@intentic/sandbox-contract 1.226.0 → 1.226.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 (165) hide show
  1. package/dist/contracts/activity.contract.d.ts.map +1 -1
  2. package/dist/contracts/activity.contract.js +17 -2
  3. package/dist/contracts/activity.contract.js.map +1 -1
  4. package/dist/contracts/agent.contract.d.ts.map +1 -1
  5. package/dist/contracts/agent.contract.js +71 -8
  6. package/dist/contracts/agent.contract.js.map +1 -1
  7. package/dist/contracts/agents.contract.d.ts +1 -0
  8. package/dist/contracts/agents.contract.d.ts.map +1 -1
  9. package/dist/contracts/agents.contract.js +164 -19
  10. package/dist/contracts/agents.contract.js.map +1 -1
  11. package/dist/contracts/automations.contract.d.ts.map +1 -1
  12. package/dist/contracts/automations.contract.js +78 -9
  13. package/dist/contracts/automations.contract.js.map +1 -1
  14. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  15. package/dist/contracts/capabilities.contract.js +98 -11
  16. package/dist/contracts/capabilities.contract.js.map +1 -1
  17. package/dist/contracts/chores.contract.d.ts.map +1 -1
  18. package/dist/contracts/chores.contract.js +26 -3
  19. package/dist/contracts/chores.contract.js.map +1 -1
  20. package/dist/contracts/ci.contract.d.ts.map +1 -1
  21. package/dist/contracts/ci.contract.js +52 -6
  22. package/dist/contracts/ci.contract.js.map +1 -1
  23. package/dist/contracts/claude.contract.d.ts.map +1 -1
  24. package/dist/contracts/claude.contract.js +44 -5
  25. package/dist/contracts/claude.contract.js.map +1 -1
  26. package/dist/contracts/drafts.contract.d.ts.map +1 -1
  27. package/dist/contracts/drafts.contract.js +26 -3
  28. package/dist/contracts/drafts.contract.js.map +1 -1
  29. package/dist/contracts/endpoints.contract.d.ts.map +1 -1
  30. package/dist/contracts/endpoints.contract.js +17 -2
  31. package/dist/contracts/endpoints.contract.js.map +1 -1
  32. package/dist/contracts/exit.contract.d.ts.map +1 -1
  33. package/dist/contracts/exit.contract.js +62 -7
  34. package/dist/contracts/exit.contract.js.map +1 -1
  35. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  36. package/dist/contracts/extensions.contract.js +124 -15
  37. package/dist/contracts/extensions.contract.js.map +1 -1
  38. package/dist/contracts/git.contract.d.ts.map +1 -1
  39. package/dist/contracts/git.contract.js +384 -43
  40. package/dist/contracts/git.contract.js.map +1 -1
  41. package/dist/contracts/grok.contract.d.ts.map +1 -1
  42. package/dist/contracts/grok.contract.js +25 -3
  43. package/dist/contracts/grok.contract.js.map +1 -1
  44. package/dist/contracts/history.contract.d.ts.map +1 -1
  45. package/dist/contracts/history.contract.js +35 -4
  46. package/dist/contracts/history.contract.js.map +1 -1
  47. package/dist/contracts/intentic.contract.d.ts.map +1 -1
  48. package/dist/contracts/intentic.contract.js +25 -3
  49. package/dist/contracts/intentic.contract.js.map +1 -1
  50. package/dist/contracts/inventory.contract.d.ts.map +1 -1
  51. package/dist/contracts/inventory.contract.js +26 -3
  52. package/dist/contracts/inventory.contract.js.map +1 -1
  53. package/dist/contracts/logs.contract.d.ts.map +1 -1
  54. package/dist/contracts/logs.contract.js +17 -2
  55. package/dist/contracts/logs.contract.js.map +1 -1
  56. package/dist/contracts/loops.contract.d.ts.map +1 -1
  57. package/dist/contracts/loops.contract.js +52 -6
  58. package/dist/contracts/loops.contract.js.map +1 -1
  59. package/dist/contracts/panels.contract.d.ts.map +1 -1
  60. package/dist/contracts/panels.contract.js +26 -3
  61. package/dist/contracts/panels.contract.js.map +1 -1
  62. package/dist/contracts/personas.contract.d.ts.map +1 -1
  63. package/dist/contracts/personas.contract.js +71 -8
  64. package/dist/contracts/personas.contract.js.map +1 -1
  65. package/dist/contracts/ports.contract.d.ts.map +1 -1
  66. package/dist/contracts/ports.contract.js +26 -3
  67. package/dist/contracts/ports.contract.js.map +1 -1
  68. package/dist/contracts/prepush.contract.d.ts.map +1 -1
  69. package/dist/contracts/prepush.contract.js +24 -3
  70. package/dist/contracts/prepush.contract.js.map +1 -1
  71. package/dist/contracts/providers.contract.d.ts.map +1 -1
  72. package/dist/contracts/providers.contract.js +9 -1
  73. package/dist/contracts/providers.contract.js.map +1 -1
  74. package/dist/contracts/public.contract.d.ts.map +1 -1
  75. package/dist/contracts/public.contract.js +26 -3
  76. package/dist/contracts/public.contract.js.map +1 -1
  77. package/dist/contracts/push.contract.d.ts.map +1 -1
  78. package/dist/contracts/push.contract.js +35 -4
  79. package/dist/contracts/push.contract.js.map +1 -1
  80. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  81. package/dist/contracts/secrets.contract.js +43 -5
  82. package/dist/contracts/secrets.contract.js.map +1 -1
  83. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  84. package/dist/contracts/sessions.contract.js +15 -2
  85. package/dist/contracts/sessions.contract.js.map +1 -1
  86. package/dist/contracts/settings.contract.d.ts.map +1 -1
  87. package/dist/contracts/settings.contract.js +43 -5
  88. package/dist/contracts/settings.contract.js.map +1 -1
  89. package/dist/contracts/share.contract.d.ts.map +1 -1
  90. package/dist/contracts/share.contract.js +35 -4
  91. package/dist/contracts/share.contract.js.map +1 -1
  92. package/dist/contracts/skills.contract.d.ts.map +1 -1
  93. package/dist/contracts/skills.contract.js +35 -4
  94. package/dist/contracts/skills.contract.js.map +1 -1
  95. package/dist/contracts/system.contract.d.ts.map +1 -1
  96. package/dist/contracts/system.contract.js +107 -14
  97. package/dist/contracts/system.contract.js.map +1 -1
  98. package/dist/contracts/translator.contract.d.ts.map +1 -1
  99. package/dist/contracts/translator.contract.js +29 -4
  100. package/dist/contracts/translator.contract.js.map +1 -1
  101. package/dist/contracts/usage.contract.d.ts.map +1 -1
  102. package/dist/contracts/usage.contract.js +9 -1
  103. package/dist/contracts/usage.contract.js.map +1 -1
  104. package/dist/contracts/vpn.contract.d.ts.map +1 -1
  105. package/dist/contracts/vpn.contract.js +32 -4
  106. package/dist/contracts/vpn.contract.js.map +1 -1
  107. package/dist/contracts/workflows.contract.d.ts.map +1 -1
  108. package/dist/contracts/workflows.contract.js +70 -8
  109. package/dist/contracts/workflows.contract.js.map +1 -1
  110. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  111. package/dist/contracts/workspace.contract.js +216 -25
  112. package/dist/contracts/workspace.contract.js.map +1 -1
  113. package/dist/events.d.ts.map +1 -1
  114. package/dist/events.js +179 -88
  115. package/dist/events.js.map +1 -1
  116. package/dist/history-state.d.ts.map +1 -1
  117. package/dist/history-state.js +1 -0
  118. package/dist/history-state.js.map +1 -1
  119. package/dist/index.d.ts +1 -0
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/schemas.d.ts +1 -0
  122. package/dist/schemas.d.ts.map +1 -1
  123. package/dist/schemas.js +2856 -1276
  124. package/dist/schemas.js.map +1 -1
  125. package/package.json +4 -4
  126. package/src/contracts/activity.contract.ts +18 -2
  127. package/src/contracts/agent.contract.ts +78 -8
  128. package/src/contracts/agents.contract.ts +180 -19
  129. package/src/contracts/automations.contract.ts +80 -9
  130. package/src/contracts/capabilities.contract.ts +106 -11
  131. package/src/contracts/chores.contract.ts +29 -3
  132. package/src/contracts/ci.contract.ts +55 -6
  133. package/src/contracts/claude.contract.ts +47 -5
  134. package/src/contracts/drafts.contract.ts +26 -3
  135. package/src/contracts/endpoints.contract.ts +19 -2
  136. package/src/contracts/exit.contract.ts +69 -7
  137. package/src/contracts/extensions.contract.ts +134 -15
  138. package/src/contracts/git.contract.ts +421 -43
  139. package/src/contracts/grok.contract.ts +26 -3
  140. package/src/contracts/history.contract.ts +37 -4
  141. package/src/contracts/intentic.contract.ts +28 -3
  142. package/src/contracts/inventory.contract.ts +27 -3
  143. package/src/contracts/logs.contract.ts +18 -2
  144. package/src/contracts/loops.contract.ts +58 -6
  145. package/src/contracts/panels.contract.ts +26 -3
  146. package/src/contracts/personas.contract.ts +77 -8
  147. package/src/contracts/ports.contract.ts +27 -3
  148. package/src/contracts/prepush.contract.ts +26 -3
  149. package/src/contracts/providers.contract.ts +10 -1
  150. package/src/contracts/public.contract.ts +29 -3
  151. package/src/contracts/push.contract.ts +37 -4
  152. package/src/contracts/secrets.contract.ts +46 -5
  153. package/src/contracts/sessions.contract.ts +16 -2
  154. package/src/contracts/settings.contract.ts +46 -5
  155. package/src/contracts/share.contract.ts +37 -4
  156. package/src/contracts/skills.contract.ts +39 -4
  157. package/src/contracts/system.contract.ts +119 -14
  158. package/src/contracts/translator.contract.ts +32 -4
  159. package/src/contracts/usage.contract.ts +10 -1
  160. package/src/contracts/vpn.contract.ts +35 -4
  161. package/src/contracts/workflows.contract.ts +77 -8
  162. package/src/contracts/workspace.contract.ts +231 -25
  163. package/src/events.ts +207 -88
  164. package/src/history-state.ts +6 -0
  165. package/src/schemas.ts +3506 -1260
package/dist/schemas.js CHANGED
@@ -3,22 +3,39 @@ import { ExtensionManifestSchema } from "@intentic/extension-manifest";
3
3
  import { RegistryEntrySchema } from "@intentic/registry";
4
4
  import { z } from "zod";
5
5
  import { OutputFieldsSchema } from "./output-fields.js";
6
- export const OkSchema = z.object({ ok: z.literal(true) });
6
+ export const OkSchema = z.object({
7
+ ok: z
8
+ .literal(true)
9
+ .describe("Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."),
10
+ });
7
11
  export const MemberRoleSchema = z.enum(["viewer", "collaborator", "maintainer", "owner"]);
8
12
  export const GrantedRoleSchema = z.enum(["viewer", "collaborator", "maintainer"]);
9
13
  const MEMBER_ROLE_RANK = { viewer: 0, collaborator: 1, maintainer: 2, owner: 3 };
10
14
  export const roleAtLeast = (role, floor) => MEMBER_ROLE_RANK[role] >= MEMBER_ROLE_RANK[floor];
11
- export const RepoParamSchema = z.object({ repo: z.string() });
15
+ export const RepoParamSchema = z.object({
16
+ repo: z
17
+ .string()
18
+ .describe('Which repository. "root" is the workspace itself; anything else is a repository\'s folder relative to the workspace root, URL-encoded.'),
19
+ });
12
20
  export const NATIVE_PROVIDERS = ["claude", "codex", "grok", "kimi", "gemini"];
13
21
  export const AgentProviderSchema = z.string().min(1);
14
22
  export const NativeProviderParamSchema = z.object({ provider: z.enum(NATIVE_PROVIDERS) });
15
23
  export const AgentHarnessSchema = z.enum(["native", "claude-code"]);
16
24
  export const RepoBaseSchema = z.object({ repo: z.string(), base: z.string().min(1) });
17
25
  export const EditorContextSchema = z.object({
18
- file: z.string().min(1),
19
- startLine: z.number().int().min(1).optional(),
20
- endLine: z.number().int().min(1).optional(),
21
- selection: z.string().max(20_000).optional(),
26
+ file: z.string().min(1).describe("The file open in the editor, as a workspace path."),
27
+ startLine: z
28
+ .number()
29
+ .int()
30
+ .min(1)
31
+ .optional()
32
+ .describe("First line of the selection, counting from one. Leave both out when the whole file is the context."),
33
+ endLine: z.number().int().min(1).optional().describe("Last line of the selection, counting from one."),
34
+ selection: z
35
+ .string()
36
+ .max(20_000)
37
+ .optional()
38
+ .describe("The selected text itself. Cut it down before sending if it is long: this is context, not an upload."),
22
39
  });
23
40
  export const ConversationIdSchema = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/);
24
41
  const entryId = z
@@ -57,35 +74,70 @@ export const ForkedFromSchema = z.object({
57
74
  });
58
75
  export const AgentTurnSchema = z
59
76
  .object({
60
- prompt: z.string(),
61
- title: z.string().max(80).optional(),
62
- attachments: z.array(z.string().min(1)).max(20).optional(),
63
- agent: AgentProviderSchema.optional(),
64
- harness: AgentHarnessSchema.optional(),
65
- account: z.string().optional(),
66
- actsAs: entryId.optional(),
67
- sessionId: z.string().optional(),
68
- conversationId: ConversationIdSchema.optional(),
69
- isolated: z.boolean().optional(),
70
- worktreeBase: z.array(RepoBaseSchema).min(1).max(50).optional(),
71
- autoLand: z.boolean().optional(),
72
- origin: AgentOriginSchema.optional(),
77
+ prompt: z.string().describe("What to say to the agent. May be empty if you are only attaching files."),
78
+ title: z
79
+ .string()
80
+ .max(80)
81
+ .optional()
82
+ .describe("A title for a conversation this turn is opening. Ignored for a conversation that already has one."),
83
+ attachments: z
84
+ .array(z.string().min(1))
85
+ .max(20)
86
+ .optional()
87
+ .describe("Files to hand the agent along with the prompt, as workspace paths. Upload them first."),
88
+ agent: AgentProviderSchema.optional().describe("Which model provider serves this turn. Leave it out for Claude."),
89
+ harness: AgentHarnessSchema.optional().describe("Which agentic loop runs the turn. Leave it out to use each provider's own."),
90
+ account: z.string().optional().describe("Which of that provider's connected accounts pays for the turn. Leave it out for the first one."),
91
+ actsAs: entryId.optional().describe("Which persona the turn speaks as out in the world. Not the same as which account pays for it."),
92
+ sessionId: z.string().optional().describe("Resume this provider session instead of starting a fresh one."),
93
+ conversationId: ConversationIdSchema.optional().describe("The conversation this turn belongs to. You choose it, it survives model switches, and it is how you address the conversation later. Naming one that does not exist opens it."),
94
+ isolated: z
95
+ .boolean()
96
+ .optional()
97
+ .describe("Work in this conversation's own private copy of the repos rather than the shared tree, so several agents can work at once. Needs a conversation id."),
98
+ worktreeBase: z
99
+ .array(RepoBaseSchema)
100
+ .min(1)
101
+ .max(50)
102
+ .optional()
103
+ .describe("Pin a new private copy to these exact commits instead of today's workspace. Used when several agents must start from identical files."),
104
+ autoLand: z
105
+ .boolean()
106
+ .optional()
107
+ .describe("Whether this turn's work merges into the workspace when it finishes. Overrides the conversation's own setting for this turn only."),
108
+ origin: AgentOriginSchema.optional().describe("Set by the sandbox alone: this turn opened a conversation on behalf of a message from outside rather than a person."),
73
109
  forkOf: z
74
110
  .object({
75
- conversationId: ConversationIdSchema,
76
- keep: z.number().int().nonnegative(),
77
- files: z.enum(["then", "now"]),
111
+ conversationId: ConversationIdSchema.describe("The conversation this one was cut from."),
112
+ keep: z.number().int().nonnegative().describe("How many of that conversation's messages to copy in before this turn runs."),
113
+ files: z
114
+ .enum(["then", "now"])
115
+ .describe('Which files the fork opens on: "now" is the workspace as it stands, "then" is the files as they were at the cut, which needs a private copy.'),
78
116
  })
79
- .optional(),
80
- model: z.string().optional(),
81
- unattended: z.boolean().optional(),
82
- outsideWake: z.string().min(1).optional(),
83
- permissionMode: PermissionModeSchema.optional(),
84
- allowedTools: z.array(z.string().min(1)).optional(),
85
- effort: z.string().optional(),
86
- thinking: z.boolean().optional(),
87
- fast: z.boolean().optional(),
88
- editorContext: EditorContextSchema.optional(),
117
+ .optional()
118
+ .describe("Where this conversation was cut from, on its first turn only. Only the client knows this, so only the client can say it."),
119
+ model: z.string().optional().describe("Which model to use. Leave it out for the provider's default."),
120
+ unattended: z
121
+ .boolean()
122
+ .optional()
123
+ .describe("Nobody chose a model for this turn because a screen started it rather than a person. The sandbox then fills in the model its owner picked for unwatched work."),
124
+ outsideWake: z
125
+ .string()
126
+ .min(1)
127
+ .optional()
128
+ .describe("Content from outside caused this turn, and what to call the source. It is what makes the sandbox treat the turn as carrying somebody else's words."),
129
+ permissionMode: PermissionModeSchema.optional().describe("How tool calls are gated: ask each time, accept file edits, propose a plan first, or run everything. The agent can move itself between these mid-turn."),
130
+ allowedTools: z
131
+ .array(z.string().min(1))
132
+ .optional()
133
+ .describe("Narrow the turn to these tools. Leave it out for everything the runtime has. For a turn driven by an outside message this list is the real boundary, because prompt wording is only advice."),
134
+ effort: z.string().optional().describe("How hard the model should think, where the provider offers a choice."),
135
+ thinking: z.boolean().optional().describe("Whether to show the model's reasoning as it works."),
136
+ fast: z
137
+ .boolean()
138
+ .optional()
139
+ .describe("Ask for the same work at a higher rate for a higher price. A request rather than a promise: the answer says what actually happened."),
140
+ editorContext: EditorContextSchema.optional().describe('What the user has open in their editor, folded into the prompt so that pointing words like "this" resolve.'),
89
141
  })
90
142
  .refine((turn) => turn.prompt.trim().length > 0 || (turn.attachments?.length ?? 0) > 0, {
91
143
  message: "prompt or attachments required",
@@ -105,86 +157,189 @@ export const AgentTurnSchema = z
105
157
  .refine((turn) => turn.forkOf?.files !== "then" || turn.isolated === true, {
106
158
  message: 'forkOf.files "then" requires isolated',
107
159
  });
108
- export const AgentRunPickSchema = z.object({ agent: z.string().min(1), model: z.string().min(1) }).optional();
109
- export const StartedTurnSchema = z.object({ run: z.string() });
160
+ export const AgentRunPickSchema = z
161
+ .object({
162
+ agent: z.string().min(1).describe("Which provider."),
163
+ model: z
164
+ .string()
165
+ .min(1)
166
+ .describe("Which of its models. Both or neither, because a model name only means anything to the provider that serves it."),
167
+ })
168
+ .optional();
169
+ export const StartedTurnSchema = z.object({
170
+ 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."),
171
+ });
110
172
  export const AttachTurnSchema = z.object({
111
- conversationId: ConversationIdSchema,
112
- run: z.string().optional(),
113
- after: z.number().int().min(0).optional(),
173
+ conversationId: ConversationIdSchema.describe("Which conversation to watch."),
174
+ run: z
175
+ .string()
176
+ .optional()
177
+ .describe("The run you were watching. If a newer turn has started since, the stream replays that one from its beginning instead."),
178
+ after: z
179
+ .number()
180
+ .int()
181
+ .min(0)
182
+ .optional()
183
+ .describe("The last frame you already have. Everything after it replays, then the stream goes live. Leave it out to start from the beginning."),
114
184
  });
115
185
  export const LoopContextSchema = z.enum(["fresh", "continue"]);
116
186
  export const LoopOutputSchema = z.discriminatedUnion("kind", [
117
- z.object({ kind: z.literal("none") }),
118
- z.object({ kind: z.literal("claim") }),
119
- z.object({ kind: z.literal("json"), fields: OutputFieldsSchema }),
187
+ z.object({
188
+ kind: z
189
+ .literal("none")
190
+ .describe("It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."),
191
+ }),
192
+ z.object({
193
+ kind: z
194
+ .literal("claim")
195
+ .describe("Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."),
196
+ }),
197
+ z.object({
198
+ kind: z
199
+ .literal("json")
200
+ .describe("Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."),
201
+ fields: OutputFieldsSchema.describe("The shape that answer has to match."),
202
+ }),
120
203
  ]);
121
204
  export const LoopCheckSchema = z.discriminatedUnion("kind", [
122
- z.object({ kind: z.literal("command"), command: z.string().min(1) }),
123
- z.object({ kind: z.literal("judge"), rubric: z.string().min(1), model: z.string().optional() }),
205
+ z.object({
206
+ kind: z
207
+ .literal("command")
208
+ .describe("Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."),
209
+ command: z.string().min(1).describe("The command to run in the conversation's own tree. Exiting cleanly means satisfied."),
210
+ }),
211
+ z.object({
212
+ kind: z
213
+ .literal("judge")
214
+ .describe("Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."),
215
+ rubric: z.string().min(1).describe("What that judge is asked."),
216
+ model: z.string().optional().describe("Which model judges. Leave it out for the cheap one the other small jobs use."),
217
+ }),
124
218
  ]);
125
219
  export const LoopDocumentSchema = z.object({
126
- done: z.boolean(),
127
- reason: z.string(),
128
- evidence: z.string().optional(),
129
- data: z.record(z.string(), z.unknown()).optional(),
220
+ done: z.boolean().describe("Whether the goal is met. Reading this is the whole point of the file."),
221
+ reason: z.string().describe("Why, in one line. The most-read sentence in the feature: the next round reads it first and the history shows it."),
222
+ evidence: z
223
+ .string()
224
+ .optional()
225
+ .describe("What was checked to know that. Optional, so a round with nothing to point at says so by leaving it out rather than by inventing a sentence."),
226
+ data: z
227
+ .record(z.string(), z.unknown())
228
+ .optional()
229
+ .describe("The declared answer, for a loop that asked for one, checked against the shape it declared."),
130
230
  });
131
231
  const LOOP_ITERATIONS_MAX = 50;
132
232
  export const LoopSchema = z.object({
133
- conversationId: ConversationIdSchema,
134
- goal: z.string().min(1),
135
- prompt: z.string().min(1),
136
- context: LoopContextSchema,
233
+ conversationId: ConversationIdSchema.describe("The conversation to loop. It need not exist yet: naming a fresh one opens it, which is what lets run this until it passes be the first thing you ever say."),
234
+ goal: z
235
+ .string()
236
+ .min(1)
237
+ .describe("What done means, in your words. It goes into every round's instructions and into the judge's question, so the model is told the bar rather than left to infer it."),
238
+ prompt: z
239
+ .string()
240
+ .min(1)
241
+ .describe("What each round is asked to do. The suite passes is the goal; run the tests, take the top failure, fix it is the instruction."),
242
+ context: LoopContextSchema.describe("How each round meets the last. Starting fresh makes the files the memory rather than the conversation, so the twentieth round reads the tree as clearly as the first, and costs a re-read each time. Carrying on is cheaper and keeps the reasoning, which suits a short polish-this loop and degrades on long ones: a session that has spent eleven rounds arguing for its own approach is the worst available judge of whether that approach is finished."),
137
243
  output: LoopOutputSchema,
138
- checks: z.array(LoopCheckSchema),
139
- maxIterations: z.number().int().min(1).max(LOOP_ITERATIONS_MAX),
140
- maxSpendUsd: z.number().positive().optional(),
141
- stallLimit: z.number().int().min(1),
142
- isolated: z.boolean(),
143
- agent: AgentProviderSchema.optional(),
144
- harness: AgentHarnessSchema.optional(),
145
- account: z.string().optional(),
146
- model: z.string().optional(),
147
- actsAs: entryId.optional(),
148
- worktreeBase: z.array(RepoBaseSchema).min(1).max(50).optional(),
149
- autoLand: z.boolean().optional(),
244
+ checks: z
245
+ .array(LoopCheckSchema)
246
+ .describe("What else has to be true, all of them together. A list because the suite passes and the report is written is a real bar, and running it as two loops would do the work twice."),
247
+ maxIterations: z
248
+ .number()
249
+ .int()
250
+ .min(1)
251
+ .max(LOOP_ITERATIONS_MAX)
252
+ .describe("How many rounds before it gives up. A loop that has not got there in fifty is not one round short of it."),
253
+ maxSpendUsd: z
254
+ .number()
255
+ .positive()
256
+ .optional()
257
+ .describe("A ceiling on what the whole loop may spend, in dollars. Optional for a short loop somebody is watching, and strongly wanted otherwise: this is the first thing here that can keep spending with nobody pressing anything between rounds."),
258
+ stallLimit: z
259
+ .number()
260
+ .int()
261
+ .min(1)
262
+ .describe("Stop after this many rounds in a row that changed nothing on disk. The guard that matters most: a loop's failure is not runaway success, it is an agent re-reading the same three files, restating the same plan and declaring more work remains, eleven times. Every one of those rounds succeeds, so only the tree not moving catches it."),
263
+ isolated: z
264
+ .boolean()
265
+ .describe("Whether it works in the conversation's own private copy or in the shared tree. It also decides where a check runs: testing the shared tree would be testing code this loop has not merged yet."),
266
+ agent: AgentProviderSchema.optional().describe("Which provider the rounds run on. Absent falls back to the conversation's own last choice."),
267
+ harness: AgentHarnessSchema.optional().describe("Which agentic loop they run on."),
268
+ account: z.string().optional().describe("Which account pays."),
269
+ model: z.string().optional().describe("Which model."),
270
+ actsAs: entryId
271
+ .optional()
272
+ .describe("Which persona the rounds act as. It matters here: every round is unwatched, and an unwatched turn naming no persona reaches no signed-in account at all, so pinning one is how a loop gets hands."),
273
+ worktreeBase: z
274
+ .array(RepoBaseSchema)
275
+ .min(1)
276
+ .max(50)
277
+ .optional()
278
+ .describe("Pin the private copy to these exact commits, so a restart cannot quietly change what the loop is working on."),
279
+ autoLand: z.boolean().optional().describe("Whether the work merges as it goes."),
150
280
  });
151
281
  export const loopCanConverge = (loop) => loop.output.kind !== "none" || loop.checks.length > 0;
152
282
  export const LOOP_DIR = `${STATE_DIR}/records/artifacts/loops`;
153
283
  export const LoopIterationSchema = z.object({
154
- n: z.number().int().min(1),
155
- at: z.number(),
156
- outcome: z.enum(["continue", "done", "error"]),
157
- detail: z.string().optional(),
158
- costUsd: z.number().optional(),
159
- changed: z.boolean(),
160
- sessionId: z.string().optional(),
284
+ n: z.number().int().min(1).describe("Which round this was."),
285
+ at: z.number().describe("When it ran, in milliseconds."),
286
+ outcome: z
287
+ .enum(["continue", "done", "error"])
288
+ .describe("How the round ended, which is not the same question as how the loop did. A round that errored does not end the loop by itself: a failing turn is often exactly what the next round is meant to fix."),
289
+ detail: z
290
+ .string()
291
+ .optional()
292
+ .describe("What the check said, in its own words. What a run history is actually read for: why it kept going, and why it stopped."),
293
+ costUsd: z.number().optional().describe("What the round cost, in dollars."),
294
+ changed: z.boolean().describe("Whether anything on disk moved. Three unchanged rounds in a row is the shape of a loop that is not working."),
295
+ sessionId: z.string().optional().describe("The session it ran on, and the way from a history row to a readable record."),
161
296
  });
162
297
  export const LoopStateSchema = z.enum(["running", "done", "exhausted", "stalled", "overspent", "stopped", "error"]);
163
298
  export const LoopRecordSchema = LoopSchema.extend({
164
- state: LoopStateSchema,
165
- startedAt: z.number(),
166
- endedAt: z.number().optional(),
167
- resumed: z.number().int().min(0),
168
- detail: z.string().optional(),
169
- iterations: z.array(LoopIterationSchema),
170
- });
171
- export const LoopsListSchema = z.object({ loops: z.array(LoopRecordSchema) });
172
- export const LoopIdParamSchema = z.object({ conversationId: ConversationIdSchema });
299
+ state: LoopStateSchema.describe("How it ended, and each of these is a different thing to be told. Out of rounds says give it more room; stalled says it is not making progress and more room will not help. Overspent, stopped by a person, and the loop itself failing are all their own answers."),
300
+ startedAt: z.number().describe("When it began, in milliseconds."),
301
+ endedAt: z.number().optional().describe("When it ended, in milliseconds."),
302
+ resumed: z
303
+ .number()
304
+ .int()
305
+ .min(0)
306
+ .describe("How many times the sandbox restarted under it and picked it back up. Counted rather than flagged, so a loop whose round reliably kills the sandbox is not resurrected on every boot for ever."),
307
+ detail: z.string().optional().describe("Why it ended, for the endings whose reason is not in their name."),
308
+ iterations: z
309
+ .array(LoopIterationSchema)
310
+ .describe("Every round, in order. Why it stopped at the fourth is the question a loop gets read for, and this is the answer."),
311
+ });
312
+ export const LoopsListSchema = z.object({
313
+ loops: z.array(LoopRecordSchema).describe("Every loop this workspace has run, newest first, kept after they end."),
314
+ });
315
+ export const LoopIdParamSchema = z.object({ conversationId: ConversationIdSchema.describe("Which conversation's loop.") });
173
316
  export const LoopDesignSchema = z.object({
174
- id: entryId,
175
- name: z.string().min(1).max(60),
176
- description: z.string().max(280).optional(),
177
- prompt: z.string().optional(),
178
- context: LoopContextSchema,
179
- output: LoopOutputSchema,
180
- checks: z.array(LoopCheckSchema),
181
- maxIterations: z.number().int().min(1).max(LOOP_ITERATIONS_MAX),
182
- maxSpendUsd: z.number().positive().optional(),
183
- stallLimit: z.number().int().min(1),
184
- });
185
- export const LoopDesignsListSchema = z.object({ designs: z.array(LoopDesignSchema) });
186
- export const LoopDesignSaveSchema = z.object({ design: LoopDesignSchema, create: z.boolean() });
187
- export const LoopDesignIdParamSchema = z.object({ id: entryId });
317
+ id: entryId.describe("The design's id."),
318
+ name: z.string().min(1).max(60).describe("What to call it. Short, because it has to be readable on a small badge."),
319
+ description: z.string().max(280).optional().describe("What it is for, in one line. Optional, because a well-named loop has already said it."),
320
+ prompt: z
321
+ .string()
322
+ .optional()
323
+ .describe("What each round is asked to do, when that is worth saying separately from the goal. Absent means each round works towards the goal however it sees fit."),
324
+ context: LoopContextSchema.describe("How each round meets the last: starting clean, or carrying on."),
325
+ output: LoopOutputSchema.describe("What it has to produce."),
326
+ checks: z.array(LoopCheckSchema).describe("What else has to be true."),
327
+ maxIterations: z.number().int().min(1).max(LOOP_ITERATIONS_MAX).describe("How many rounds before it gives up."),
328
+ maxSpendUsd: z.number().positive().optional().describe("A ceiling on what it may spend, in dollars."),
329
+ stallLimit: z.number().int().min(1).describe("Stop after this many rounds in a row that changed nothing."),
330
+ });
331
+ export const LoopDesignsListSchema = z.object({
332
+ designs: z
333
+ .array(LoopDesignSchema)
334
+ .describe("Saved loops: the machinery with the goal left out, so one design can be pointed at a different job every time."),
335
+ });
336
+ export const LoopDesignSaveSchema = z.object({
337
+ design: LoopDesignSchema.describe("The design to write."),
338
+ create: z
339
+ .boolean()
340
+ .describe("Whether you mean to make a new one or replace an existing one, so an id that happens to collide cannot silently overwrite the one you had."),
341
+ });
342
+ export const LoopDesignIdParamSchema = z.object({ id: entryId.describe("Which saved loop.") });
188
343
  export const loopFromDesign = (design, aim) => ({
189
344
  conversationId: aim.conversationId,
190
345
  goal: aim.goal,
@@ -223,125 +378,274 @@ export const AgentStatusSchema = z.enum([
223
378
  "interrupted",
224
379
  ]);
225
380
  export const AgentActivitySchema = z.object({
226
- tool: z.string().optional(),
227
- target: z.string().optional(),
228
- todo: z.string().optional(),
381
+ tool: z.string().optional().describe("The last tool it reached for."),
382
+ target: z.string().optional().describe("What it reached for that tool with: a file, a command, a URL."),
383
+ todo: z.string().optional().describe("The item on its own list that it is working through."),
229
384
  });
230
385
  export const AgentAttentionSchema = z.object({
231
- plan: z.boolean(),
232
- question: z.boolean(),
233
- permission: z.boolean(),
234
- service: z.boolean(),
235
- capability: z.boolean(),
236
- conflict: z.boolean(),
386
+ plan: z.boolean().describe("It has proposed a plan and is waiting for a yes."),
387
+ question: z.boolean().describe("It has asked you something."),
388
+ permission: z.boolean().describe("It wants to use a tool it needs permission for."),
389
+ service: z
390
+ .boolean()
391
+ .describe("It wants to spend money on a paid service and is waiting for approval. The one pause where waiting costs it the whole call."),
392
+ capability: z.boolean().describe("It needs something connected that is not connected yet."),
393
+ conflict: z.boolean().describe("Its work cannot be merged without somebody resolving a clash."),
237
394
  });
238
395
  export const LandedMessageSchema = z.object({
239
- subject: z.string(),
240
- note: z.string().optional(),
241
- breaking: z.string().optional(),
396
+ subject: z
397
+ .string()
398
+ .describe("One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."),
399
+ note: z
400
+ .string()
401
+ .optional()
402
+ .describe("The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice."),
403
+ breaking: z
404
+ .string()
405
+ .optional()
406
+ .describe("What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions."),
242
407
  });
243
408
  export const LandedMessageStepSchema = z.object({
244
- provider: z.string().min(1),
245
- model: z.string().min(1),
246
- status: z.enum(["asking", "answered", "refused", "skipped"]),
247
- at: z.number().optional(),
248
- ms: z.number().optional(),
249
- reason: z.string().optional(),
409
+ provider: z.string().min(1).describe("Which provider was asked."),
410
+ model: z.string().min(1).describe("Which of its models."),
411
+ status: z
412
+ .enum(["asking", "answered", "refused", "skipped"])
413
+ .describe("How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."),
414
+ at: z.number().optional().describe("When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time."),
415
+ ms: z.number().optional().describe("How long it took. Absent while it is still being asked."),
416
+ reason: z.string().optional().describe("Why it refused, in its own words."),
250
417
  });
251
418
  export const LandedMessageDraftSchema = z.object({
252
- startedAt: z.number(),
253
- steps: z.array(LandedMessageStepSchema),
254
- outcome: z.enum(["written", "failed"]).optional(),
255
- reason: z.string().optional(),
256
- finishedAt: z.number().optional(),
419
+ startedAt: z.number().describe("When the drafting began, in milliseconds."),
420
+ steps: z
421
+ .array(LandedMessageStepSchema)
422
+ .describe("Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."),
423
+ outcome: z.enum(["written", "failed"]).optional().describe("How it ended. Absent means it is still going."),
424
+ reason: z
425
+ .string()
426
+ .optional()
427
+ .describe("The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words."),
428
+ finishedAt: z.number().optional().describe("When it ended, in milliseconds."),
257
429
  });
258
430
  export const AgentSummarySchema = z.object({
259
- id: z.string(),
260
- sessionId: z.string().optional(),
261
- title: z.string().optional(),
262
- status: AgentStatusSchema,
263
- failure: z.string().optional(),
264
- provider: AgentProviderSchema,
265
- harness: AgentHarnessSchema,
266
- model: z.string().optional(),
267
- effort: z.string().optional(),
268
- thinking: z.boolean().optional(),
269
- fast: z.boolean().optional(),
270
- account: z.string().optional(),
271
- branch: z.string().optional(),
272
- autoLand: z.boolean().optional(),
431
+ id: z.string().describe("The conversation id, which is how every other call addresses it."),
432
+ sessionId: z.string().optional().describe("The provider session behind the last turn. It is retired whenever the model or account changes."),
433
+ title: z.string().optional().describe("What to call it: the first prompt cut to one line, unless somebody renamed it."),
434
+ status: AgentStatusSchema.describe("What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; resuming means the sandbox is already putting right whatever killed the turn."),
435
+ failure: z
436
+ .string()
437
+ .optional()
438
+ .describe("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."),
439
+ provider: AgentProviderSchema.describe("Which model provider it runs on."),
440
+ harness: AgentHarnessSchema.describe("Which agentic loop it runs on."),
441
+ model: z
442
+ .string()
443
+ .optional()
444
+ .describe("What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked."),
445
+ effort: z.string().optional().describe("How hard that turn was told to think."),
446
+ thinking: z.boolean().optional().describe("Whether that turn showed its reasoning."),
447
+ fast: z.boolean().optional().describe("Whether that turn asked for higher speed. What was asked for, not what was served."),
448
+ account: z.string().optional().describe("Which connected account paid for it."),
449
+ branch: z.string().optional().describe("The branch its private copy works on. Absent for a conversation that works directly in the shared tree."),
450
+ autoLand: z
451
+ .boolean()
452
+ .optional()
453
+ .describe("This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case."),
273
454
  resumeAfterOutage: z.boolean().optional(),
274
- landRequested: z.object({ email: z.string(), name: z.string().optional(), at: z.number() }).optional(),
275
- origin: AgentOriginSchema.optional(),
276
- forkedFrom: ForkedFromSchema.optional(),
277
- base: z.string().optional(),
278
- costUsd: z.number().optional(),
279
- inputTokens: z.number().optional(),
280
- outputTokens: z.number().optional(),
281
- contextTokens: z.number().optional(),
282
- contextWindow: z.number().optional(),
283
- activity: AgentActivitySchema.optional(),
284
- landedMessageDraft: LandedMessageDraftSchema.optional(),
285
- landedMessage: LandedMessageSchema.optional(),
286
- startedAt: z.number().optional(),
287
- updatedAt: z.number(),
288
- seenAt: z.number().optional(),
289
- attention: AgentAttentionSchema,
290
- turns: z.number().optional(),
291
- toolUses: z.number().optional(),
292
- subagents: z.object({ running: z.number(), total: z.number() }).optional(),
293
- diff: z.object({ files: z.number(), insertions: z.number(), deletions: z.number() }).optional(),
294
- landedPresence: z.object({ landed: z.number(), present: z.number() }).optional(),
455
+ landRequested: z
456
+ .object({
457
+ email: z.string().describe("Who asked."),
458
+ name: z.string().optional().describe("Their display name."),
459
+ at: z.number().describe("When they asked, in milliseconds."),
460
+ })
461
+ .optional()
462
+ .describe("A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting."),
463
+ origin: AgentOriginSchema.optional().describe("Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it."),
464
+ forkedFrom: ForkedFromSchema.optional().describe("The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state."),
465
+ base: z.string().optional().describe("The commit its private copy started from, shortened."),
466
+ costUsd: z.number().optional().describe("What it has cost so far, in dollars. A helper agent's spend is its own and is not folded in here."),
467
+ inputTokens: z.number().optional().describe("Tokens sent."),
468
+ outputTokens: z.number().optional().describe("Tokens received."),
469
+ contextTokens: z.number().optional().describe("How much of the window the conversation currently fills."),
470
+ contextWindow: z.number().optional().describe("How large that window is."),
471
+ activity: AgentActivitySchema.optional().describe("What it is doing at this moment."),
472
+ landedMessageDraft: LandedMessageDraftSchema.optional().describe("The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes."),
473
+ landedMessage: LandedMessageSchema.optional().describe("What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together."),
474
+ startedAt: z.number().optional().describe("When the running turn started, in milliseconds. Absent when none is running."),
475
+ updatedAt: z.number().describe("When it last did something, in milliseconds. Reading it does not count."),
476
+ seenAt: z
477
+ .number()
478
+ .optional()
479
+ .describe("When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge."),
480
+ attention: AgentAttentionSchema.describe("Which kinds of waiting-for-you it is doing."),
481
+ turns: z.number().optional().describe("Turns it has finished."),
482
+ toolUses: z.number().optional().describe("Tools it has used, over its whole life."),
483
+ subagents: z
484
+ .object({
485
+ running: z.number().describe("Helpers working right now."),
486
+ total: z.number().describe("Helpers it has started over its whole life."),
487
+ })
488
+ .optional()
489
+ .describe("Helper agents this one delegated to. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost."),
490
+ diff: z
491
+ .object({
492
+ files: z.number().describe("Files touched."),
493
+ insertions: z.number().describe("Lines added."),
494
+ deletions: z.number().describe("Lines removed."),
495
+ })
496
+ .optional()
497
+ .describe("Everything it has written, measured from where it started. Independent of how much has been merged."),
498
+ landedPresence: z
499
+ .object({
500
+ landed: z.number().describe("Paths this conversation merged in."),
501
+ present: z.number().describe("How many of them are still there, either pending or committed."),
502
+ })
503
+ .optional()
504
+ .describe("Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it."),
295
505
  loop: z
296
- .object({ state: LoopStateSchema, iteration: z.number().int().min(0), maxIterations: z.number().int().min(1), goal: z.string() })
297
- .optional(),
506
+ .object({
507
+ state: LoopStateSchema.describe("How the loop is going."),
508
+ iteration: z.number().int().min(0).describe("Which round it is on."),
509
+ maxIterations: z.number().int().min(1).describe("How many rounds it will attempt before giving up."),
510
+ goal: z.string().describe("What it is looping towards."),
511
+ })
512
+ .optional()
513
+ .describe("The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them."),
298
514
  workflow: z
299
515
  .object({
300
- runId: z.string(),
301
- name: z.string(),
302
- step: z.string(),
303
- index: z.number().int().min(1),
304
- total: z.number().int().min(1),
516
+ runId: z.string().describe("The run this belongs to, which is how a board groups its steps together."),
517
+ name: z.string().describe("The workflow's name."),
518
+ step: z.string().describe("Which step this conversation is on now. It moves when steps are chained."),
519
+ index: z.number().int().min(1).describe("This step's place in the workflow, counting from one."),
520
+ total: z.number().int().min(1).describe("How many steps the workflow has."),
305
521
  })
306
- .optional(),
307
- archivedAt: z.number().optional(),
308
- });
309
- export const AgentIdSchema = z.object({ id: z.string().min(1) });
310
- export const AgentArchiveSchema = z.object({ ids: z.array(z.string().min(1)).max(500).optional() });
311
- export const AgentIdsSchema = z.object({ ids: z.array(z.string().min(1)).min(1).max(500) });
312
- export const AgentsMovedSchema = z.object({ moved: z.array(AgentSummarySchema), rev: z.number() });
313
- export const AgentsRemovedSchema = z.object({ removed: z.array(z.string()) });
314
- export const AgentSearchQuerySchema = z.object({ query: z.string().trim().min(2), caseSensitive: z.stringbool().optional() });
522
+ .optional()
523
+ .describe("The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together."),
524
+ archivedAt: z
525
+ .number()
526
+ .optional()
527
+ .describe("When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board."),
528
+ });
529
+ export const AgentIdSchema = z.object({ id: z.string().min(1).describe("Which conversation.") });
530
+ export const AgentArchiveSchema = z.object({
531
+ ids: z
532
+ .array(z.string().min(1))
533
+ .max(500)
534
+ .optional()
535
+ .describe("Which conversations to put away. Leave it out for every finished one that can be archived right now."),
536
+ });
537
+ export const AgentIdsSchema = z.object({ ids: z.array(z.string().min(1)).min(1).max(500).describe("Which conversations.") });
538
+ export const AgentsMovedSchema = z.object({
539
+ moved: z
540
+ .array(AgentSummarySchema)
541
+ .describe("What actually moved, whole, rather than the fleet afterwards. Two archives finishing at once would each carry a snapshot from a different instant, and swapping one in wholesale would let the slower answer resurrect what the faster one just filed away."),
542
+ rev: z
543
+ .number()
544
+ .describe("The version of the fleet that includes this move, so a caller can hold its own optimistic change until it sees a list at least that new."),
545
+ });
546
+ export const AgentsRemovedSchema = z.object({
547
+ removed: z
548
+ .array(z.string())
549
+ .describe("Which conversations were deleted, as ids. Ids rather than whole cards, because these no longer exist anywhere: there is nothing left to show and nothing to put back."),
550
+ });
551
+ export const AgentSearchQuerySchema = z.object({
552
+ query: z
553
+ .string()
554
+ .trim()
555
+ .min(2)
556
+ .describe("What to look for. Searched against what was said, both sides of the conversation, and nothing else: not the thinking, not the tool output, which between them name nearly every identifier in the workspace and would return most of the board."),
557
+ caseSensitive: z.stringbool().optional().describe("Whether capitals matter."),
558
+ });
315
559
  export const SpeakerSchema = z.enum(["user", "agent"]);
316
- export const MatchSnippetSchema = z.object({ text: z.string(), speaker: SpeakerSchema });
317
- export const AgentMatchSchema = z.object({ id: z.string(), snippet: MatchSnippetSchema.optional() });
318
- export const AgentSearchResultSchema = z.object({ matches: z.array(AgentMatchSchema), scanned: z.number() });
319
- export const AgentRenameSchema = z.object({ id: z.string().min(1), title: z.string().trim().min(1).max(80) });
320
- export const AgentPlaceSchema = z.object({ id: z.string().min(1), text: z.string().trim().min(1).max(8_000) });
321
- export const AgentAutoLandSchema = z.object({ id: z.string().min(1), autoLand: z.boolean().nullable() });
322
- export const AgentResumeAfterOutageSchema = z.object({ id: z.string().min(1), resumeAfterOutage: z.boolean().nullable() });
323
- export const AgentFileDiffQuerySchema = z.object({ id: z.string().min(1), repo: z.string().min(1), path: z.string().min(1) });
560
+ export const MatchSnippetSchema = z.object({
561
+ text: z.string().describe("The matching line, with a little either side of it."),
562
+ speaker: SpeakerSchema.describe("Who said it. Carried with the words rather than beside them, because a line of the agent's prose under a card reads as something you typed until the row says otherwise."),
563
+ });
564
+ export const AgentMatchSchema = z.object({
565
+ id: z.string().describe("Which conversation matched."),
566
+ snippet: MatchSnippetSchema.optional().describe("Why, in its own words. Absent when the title was the match, which the card already shows: repeating it underneath is noise where evidence was wanted."),
567
+ });
568
+ export const AgentSearchResultSchema = z.object({
569
+ matches: z.array(AgentMatchSchema).describe("What matched, from the live fleet and the archive together."),
570
+ scanned: z
571
+ .number()
572
+ .describe("How many conversations were actually read, so a screen can say when a search saw less than everything rather than implying it saw all of it."),
573
+ indexing: z
574
+ .boolean()
575
+ .describe("Whether what was said is still being read in the background. True means this answer can still grow, so a screen must say it is incomplete rather than presenting it as the whole list."),
576
+ });
577
+ export const AgentRenameSchema = z.object({
578
+ id: z.string().min(1).describe("Which conversation."),
579
+ title: z.string().trim().min(1).max(80).describe("What to call it from now on."),
580
+ });
581
+ export const AgentPlaceSchema = z.object({
582
+ id: z.string().min(1).describe("Which conversation."),
583
+ text: z
584
+ .string()
585
+ .trim()
586
+ .min(1)
587
+ .max(8_000)
588
+ .describe("The words to put in the agent's mouth. Bounded just above what the next turn can carry whole, because a line too long to be handed over intact would reach the agent truncated and quietly break the very thing this is for."),
589
+ });
590
+ export const AgentAutoLandSchema = z.object({
591
+ id: z.string().min(1).describe("Which conversation."),
592
+ autoLand: z
593
+ .boolean()
594
+ .nullable()
595
+ .describe("Whether its work merges automatically when a turn finishes. Null clears the override and goes back to following the sandbox-wide setting, so a conversation does not sit holding a frozen copy of a default it has quietly stopped following."),
596
+ });
597
+ export const AgentResumeAfterOutageSchema = z.object({
598
+ id: z.string().min(1).describe("Which conversation."),
599
+ resumeAfterOutage: z
600
+ .boolean()
601
+ .nullable()
602
+ .describe("Whether it retries by itself when the model provider was what failed. Null clears the override back to the sandbox-wide setting."),
603
+ });
604
+ export const AgentFileDiffQuerySchema = z.object({
605
+ id: z.string().min(1).describe("Which conversation."),
606
+ repo: z.string().min(1).describe("Which repository."),
607
+ path: z.string().min(1).describe("Which file, relative to that repository."),
608
+ });
324
609
  export const LandConflictReasonSchema = z.enum(["workspace", "diverged", "binary"]);
325
- export const LandConflictPathSchema = z.object({ path: z.string(), reason: LandConflictReasonSchema });
610
+ export const LandConflictPathSchema = z.object({
611
+ path: z.string().describe("Which file."),
612
+ reason: LandConflictReasonSchema.describe("Why it would not merge, and the three have nothing in common but the symptom. Your own uncommitted edits on that path, where yours is the copy at risk. The shared tree having moved under the conversation since it started, where nothing of yours is at risk. Or a file git cannot merge at all, where no automatic answer exists."),
613
+ });
326
614
  export const LandConflictSchema = z.object({
327
- repo: z.string(),
328
- paths: z.array(LandConflictPathSchema),
329
- clean: z.number(),
330
- mainBranch: z.string().optional(),
615
+ repo: z.string().describe("Which repository."),
616
+ paths: z
617
+ .array(LandConflictPathSchema)
618
+ .describe("The files that genuinely would not apply. Not the whole change: reporting everything whenever the cause could not be pinned down turned four real conflicts into a wall of fourteen."),
619
+ clean: z
620
+ .number()
621
+ .describe("How many files would apply regardless, so a screen can say how much is being held back by how little and offer to take it. Zero alongside an empty list means the repository could not be reached at all."),
622
+ mainBranch: z
623
+ .string()
624
+ .optional()
625
+ .describe("The branch your own checkout is on, which is what the conversation has to rebase onto. Carried because only the sandbox can see it. Absent where there is no name to give."),
331
626
  });
332
627
  export const LandResultSchema = z.object({
333
- landed: z.boolean(),
334
- conflicts: z.array(LandConflictSchema).optional(),
335
- resolving: z.array(z.object({ repo: z.string(), paths: z.array(z.string()) })).optional(),
336
- held: z.boolean().optional(),
628
+ landed: z.boolean().describe("Whether anything was applied."),
629
+ conflicts: z.array(LandConflictSchema).optional().describe("What stopped it, per repository."),
630
+ resolving: z
631
+ .array(z.object({
632
+ repo: z.string().describe("Which repository."),
633
+ paths: z.array(z.string()).describe("Which files now hold conflict markers to sort out by hand."),
634
+ }))
635
+ .optional()
636
+ .describe("Files left half-merged, when you asked for the mode that lands what it can and leaves the rest marked up."),
637
+ held: z
638
+ .boolean()
639
+ .optional()
640
+ .describe("Nothing was applied and nothing failed: there is work waiting on the branch for a deliberate merge. Not merged on its own cannot say that, because on its own it means refused."),
337
641
  });
338
642
  export const LandModeSchema = z.enum(["check", "merge", "measure"]);
339
643
  export const AgentSpanSchema = z.enum(["cumulative", "outstanding"]);
340
644
  export const AgentLandSchema = z.object({
341
- id: z.string().min(1),
342
- mode: LandModeSchema.optional(),
343
- span: AgentSpanSchema.optional(),
344
- force: z.boolean().optional(),
645
+ id: z.string().min(1).describe("Which conversation's work to merge."),
646
+ mode: LandModeSchema.optional().describe("How to apply it. The default applies all of it or none, so a refusal leaves the workspace exactly as it was. The other lands every clean file and leaves the rest with conflict markers to resolve by hand."),
647
+ span: AgentSpanSchema.optional().describe("How much of the work to take. Leave it out for everything not yet merged."),
648
+ force: z.boolean().optional().describe("Go ahead despite a check that would otherwise refuse."),
345
649
  });
346
650
  export const KeyedProviderSchema = z.enum(["codex", "grok", "kimi", "gemini"]);
347
651
  export const UsageWindowSchema = z.object({
@@ -355,12 +659,18 @@ export const AccountUsageSchema = z.object({
355
659
  measuredAt: z.number(),
356
660
  });
357
661
  export const ProviderRefusalSchema = z.object({
358
- at: z.number(),
359
- kind: z.enum(["limit", "auth", "entitlement"]),
360
- message: z.string(),
361
- account: z.string().optional(),
662
+ at: z.number().describe("When it refused, in milliseconds."),
663
+ kind: z
664
+ .enum(["limit", "auth", "entitlement"])
665
+ .describe("Three different noes, kept apart because what fixes each is different. A spent allowance is answered by waiting; a refused credential by signing in again; and an entitlement refusal, where somebody has switched this off for your seat, by neither of those. That last one authenticates fine and reports healthy limits the whole time it refuses everything."),
666
+ message: z.string().describe("The provider's own words, verbatim. The only part that says which limit or which credential."),
667
+ account: z.string().optional().describe("Which account was serving, where that is known."),
668
+ });
669
+ export const ProviderRefusalsSchema = z.object({
670
+ refusals: z
671
+ .record(z.string(), ProviderRefusalSchema)
672
+ .describe("The most recent refusal per provider. Read alongside an account's usage: that says how full it was when last checked, this says whether it has since started saying no."),
362
673
  });
363
- export const ProviderRefusalsSchema = z.object({ refusals: z.record(z.string(), ProviderRefusalSchema) });
364
674
  export const TranslatorAccountSchema = z.object({
365
675
  name: z.string(),
366
676
  label: z.string(),
@@ -374,62 +684,90 @@ export const TranslatorAccountsSchema = z.object({
374
684
  });
375
685
  export const AgentReplySchema = z.discriminatedUnion("kind", [
376
686
  z.object({
377
- kind: z.literal("plan"),
378
- requestId: z.string().min(1),
379
- approve: z.boolean(),
380
- feedback: z.string().optional(),
687
+ kind: z.literal("plan").describe("Answering a plan the agent proposed."),
688
+ requestId: z.string().min(1).describe("Which card you are answering, from the frame that raised it."),
689
+ approve: z
690
+ .boolean()
691
+ .describe("Whether to go ahead. Approving means the plan then runs without asking again per tool, because being asked whether a plan you just approved may run its first command is not a question worth having."),
692
+ feedback: z.string().optional().describe("Why not, which goes back to the model as the reason."),
381
693
  }),
382
694
  z.object({
383
- kind: z.literal("question"),
384
- requestId: z.string().min(1),
385
- answers: z.record(z.string(), z.array(z.string())).optional(),
386
- cancelled: z.boolean().optional(),
695
+ kind: z.literal("question").describe("Answering a question the agent asked."),
696
+ requestId: z.string().min(1).describe("Which card you are answering."),
697
+ answers: z
698
+ .record(z.string(), z.array(z.string()))
699
+ .optional()
700
+ .describe("What you chose, keyed by the question, with the chosen labels or your own words."),
701
+ cancelled: z
702
+ .boolean()
703
+ .optional()
704
+ .describe("Dismissing it instead, which tells the agent to carry on using sensible defaults rather than leaving it waiting."),
387
705
  }),
388
706
  z.object({
389
- kind: z.literal("permission"),
390
- requestId: z.string().min(1),
391
- decision: z.enum(["once", "always", "deny"]),
392
- feedback: z.string().optional(),
707
+ kind: z.literal("permission").describe("Answering a request to use a tool."),
708
+ requestId: z.string().min(1).describe("Which card you are answering."),
709
+ decision: z
710
+ .enum(["once", "always", "deny"])
711
+ .describe("Once allows this call alone; always allows that whole tool for the rest of the conversation; no blocks it."),
712
+ feedback: z.string().optional().describe("Why not, which goes back to the model as the reason."),
393
713
  }),
394
714
  z.object({
395
- kind: z.literal("browser_help"),
396
- requestId: z.string().min(1),
397
- helped: z.boolean(),
398
- note: z.string().optional(),
715
+ kind: z
716
+ .literal("browser_help")
717
+ .describe("Answering a request for help in the agent's browser: a captcha, a password it does not hold, a check on your phone."),
718
+ requestId: z.string().min(1).describe("Which card you are answering."),
719
+ helped: z
720
+ .boolean()
721
+ .describe("Whether you cleared it. Yes means the turn carries on from the page as you left it; no tells the agent so, and it moves on rather than waiting for ever."),
722
+ note: z.string().optional().describe("Anything the agent should know, which goes back to it either way."),
399
723
  }),
400
724
  z.object({
401
- kind: z.literal("terminal_help"),
402
- requestId: z.string().min(1),
403
- helped: z.boolean(),
404
- note: z.string().optional(),
725
+ kind: z
726
+ .literal("terminal_help")
727
+ .describe("Answering a request for help at a terminal: a code to type, a confirmation only a person can give."),
728
+ requestId: z.string().min(1).describe("Which card you are answering."),
729
+ helped: z
730
+ .boolean()
731
+ .describe("Whether you did it. Yes also hands the agent what the terminal now says, because a person answering a prompt is exactly the moment the agent cannot see."),
732
+ note: z.string().optional().describe("Anything the agent should know, which goes back to it either way."),
405
733
  }),
406
734
  z.object({
407
- kind: z.literal("service_offer"),
408
- requestId: z.string().min(1),
409
- approve: z.boolean(),
735
+ kind: z.literal("service_offer").describe("Answering a request to spend on a paid service."),
736
+ requestId: z.string().min(1).describe("Which card you are answering."),
737
+ approve: z
738
+ .boolean()
739
+ .describe("Yes releases exactly one run. Anything else charges nothing. This click is the only way the spend can happen."),
410
740
  }),
411
741
  z.object({
412
- kind: z.literal("capability_offer"),
413
- requestId: z.string().min(1),
414
- connect: z.boolean(),
742
+ kind: z.literal("capability_offer").describe("Answering a request to connect something the agent needs."),
743
+ requestId: z.string().min(1).describe("Which card you are answering."),
744
+ connect: z
745
+ .boolean()
746
+ .describe("Yes keeps the agent waiting while you set it up, and it carries on the moment the connection comes alive. No tells it to continue without. The reply itself connects nothing: setting it up is still your own doing."),
415
747
  }),
416
748
  z.object({
417
- kind: z.literal("payment_offer"),
418
- requestId: z.string().min(1),
419
- approve: z.boolean(),
749
+ kind: z.literal("payment_offer").describe("Answering a request to pay for something."),
750
+ requestId: z.string().min(1).describe("Which card you are answering."),
751
+ approve: z
752
+ .boolean()
753
+ .describe("Yes releases exactly one payment. Anything else spends nothing. This click is the only way the money can move."),
420
754
  }),
421
755
  ]);
422
756
  export const SteerSchema = z
423
757
  .object({
424
- conversationId: z.string().min(1),
425
- text: z.string().max(20_000),
426
- attachments: z.array(z.string().min(1)).max(20).optional(),
427
- editorContext: EditorContextSchema.optional(),
758
+ conversationId: z.string().min(1).describe("Which running conversation to interrupt."),
759
+ text: z.string().max(20_000).describe("What to say to it. It arrives mid-turn without stopping the turn."),
760
+ attachments: z
761
+ .array(z.string().min(1))
762
+ .max(20)
763
+ .optional()
764
+ .describe("Files to send with it, as workspace paths. A screenshot dropped in mid-turn with no words is a legitimate thing to send."),
765
+ editorContext: EditorContextSchema.optional().describe("What you have open, folded in so that pointing words resolve."),
428
766
  })
429
767
  .refine((steer) => steer.text.trim().length > 0 || (steer.attachments?.length ?? 0) > 0, {
430
768
  message: "text or attachments required",
431
769
  });
432
- export const StopTurnSchema = z.object({ conversationId: z.string().min(1) });
770
+ export const StopTurnSchema = z.object({ conversationId: z.string().min(1).describe("Which conversation's running turn to cancel.") });
433
771
  export const RateLimitInfoSchema = z.object({
434
772
  status: z.enum(["allowed", "allowed_warning", "rejected"]),
435
773
  resetsAt: z.number().optional(),
@@ -438,56 +776,91 @@ export const RateLimitInfoSchema = z.object({
438
776
  });
439
777
  export const FastModeStateSchema = z.enum(["off", "cooldown", "on"]);
440
778
  export const OauthAccountSchema = z.object({
441
- id: z.string(),
442
- label: z.string(),
443
- email: z.string().optional(),
444
- organization: z.string().optional(),
445
- scope: z.string().optional(),
446
- connectedAt: z.number(),
447
- needsReauth: z.boolean().optional(),
448
- detail: z.string().optional(),
449
- usage: AccountUsageSchema.optional(),
779
+ id: z.string().describe("The account's id, which is what a turn names to spend on it and what disconnecting takes."),
780
+ label: z.string().describe("What it is called here, which somebody can change."),
781
+ email: z
782
+ .string()
783
+ .optional()
784
+ .describe("Who it signs in as, in the provider's own words. Kept beside the label rather than folded into it, so a renamed account can still say whose it is. Absent when the provider says nothing, which is exactly when renaming is the only answer."),
785
+ organization: z.string().optional().describe("Which organisation it belongs to, where the provider says."),
786
+ scope: z.string().optional().describe("What the credential is permitted to do, in the provider's terms."),
787
+ connectedAt: z.number().describe("When it was connected, in milliseconds."),
788
+ needsReauth: z
789
+ .boolean()
790
+ .optional()
791
+ .describe("Its stored credential can no longer be renewed and somebody has to sign in again. Absent means healthy, or not checked yet."),
792
+ detail: z.string().optional().describe("Why, in words a person can act on."),
793
+ usage: AccountUsageSchema.optional().describe("How full its plan limits were when last measured, so a picker can show what is left before committing work to it. Absent until a reading exists, which reads as unknown rather than as nothing left."),
794
+ });
795
+ export const OauthAccountListSchema = z.object({
796
+ accounts: z
797
+ .array(OauthAccountSchema)
798
+ .describe("The connected accounts. Tokens never travel in this shape: being in this list is what connected means."),
799
+ });
800
+ export const AccountListQuerySchema = z.object({
801
+ force: z
802
+ .stringbool()
803
+ .default(false)
804
+ .describe("Measure the plan limits again before answering, rather than serving a recent reading. Slower, and the right thing when somebody has just changed a plan and is asking whether what they can see is still true."),
805
+ });
806
+ export const AccountIdSchema = z.object({ id: z.string().min(1).describe("Which account.") });
807
+ export const AccountRenameSchema = z.object({
808
+ id: z.string().min(1).describe("Which account."),
809
+ label: z.string().max(80).describe("The new name. Blank restores the one derived from the sign-in, rather than leaving a nameless row."),
450
810
  });
451
- export const OauthAccountListSchema = z.object({ accounts: z.array(OauthAccountSchema) });
452
- export const AccountListQuerySchema = z.object({ force: z.stringbool().default(false) });
453
- export const AccountIdSchema = z.object({ id: z.string().min(1) });
454
- export const AccountRenameSchema = z.object({ id: z.string().min(1), label: z.string().max(80) });
455
811
  export const OauthExchangeSchema = z.object({
456
- code: z.string().min(1),
457
- verifier: z.string().min(1),
458
- state: z.string().min(1),
459
- label: z.string().optional(),
812
+ code: z.string().min(1).describe("The code the sign-in handed back."),
813
+ verifier: z.string().min(1).describe("The proof from the start of the handshake, which is what stops somebody else's code being redeemed here."),
814
+ state: z.string().min(1).describe("The handshake this belongs to. A mismatch is refused."),
815
+ label: z.string().optional().describe("What to call the account. Blank derives one from the sign-in."),
816
+ });
817
+ export const AuthorizeChallengeSchema = z.object({
818
+ authorizeUrl: z.string().describe("Where to send somebody to sign in."),
819
+ verifier: z.string().describe("Keep this and send it back when finishing. It is what proves the code that comes back belongs to this handshake."),
820
+ state: z.string().describe("The handshake's own id, sent back with it."),
821
+ });
822
+ export const DeviceStartSchema = z.object({
823
+ url: z.string().describe("The page to open, which already has the code in it."),
824
+ code: z
825
+ .string()
826
+ .describe("The one-time code, shown as well so the page and the card say the same thing. Nothing is pasted back: the sandbox waits for the sign-in to complete on its own."),
460
827
  });
461
- export const AuthorizeChallengeSchema = z.object({ authorizeUrl: z.string(), verifier: z.string(), state: z.string() });
462
- export const DeviceStartSchema = z.object({ url: z.string(), code: z.string() });
463
828
  export const TranslatorStartSchema = z.object({
464
- url: z.string(),
465
- code: z.string(),
466
- state: z.string(),
467
- flow: z.enum(["device", "redirect"]),
829
+ url: z.string().describe("The page to open."),
830
+ code: z.string().describe("The one-time code, where the provider uses one."),
831
+ state: z.string().describe("The handshake's id, which the finishing call sends back."),
832
+ flow: z
833
+ .enum(["device", "redirect"])
834
+ .describe("Which shape this is. A device sign-in finishes by itself and you poll the account list; a redirect needs the address it landed on handed back. Said outright rather than guessed at from whether a code happens to exist."),
468
835
  });
469
836
  export const TranslatorCompleteSchema = z.object({
470
- provider: KeyedProviderSchema,
471
- redirectUrl: z.string().min(1),
472
- state: z.string().min(1),
837
+ provider: KeyedProviderSchema.describe("Which provider."),
838
+ redirectUrl: z.string().min(1).describe("The address the browser was sent to, whole. The grant is inside it."),
839
+ state: z.string().min(1).describe("The handshake this belongs to. A mismatch is refused."),
473
840
  });
474
841
  export const ModelBadgeSchema = z.enum(["reasoning", "fast"]);
475
842
  export const ModelSchema = z.object({
476
- id: z.string(),
477
- label: z.string(),
478
- efforts: z.array(z.string()).optional(),
479
- description: z.string().optional(),
480
- badges: z.array(ModelBadgeSchema).optional(),
843
+ id: z.string().describe("What to name when asking for this model."),
844
+ label: z.string().describe("What to call it on screen."),
845
+ efforts: z.array(z.string()).optional().describe("The thinking levels it accepts, where the provider says. Empty means use your own defaults."),
846
+ description: z
847
+ .string()
848
+ .optional()
849
+ .describe("What it is good for, in the provider's own words. Absent where the provider publishes only ids, which is the honest answer rather than something to paper over with a hand-written table."),
850
+ badges: z.array(ModelBadgeSchema).optional().describe("What it is known for, where the provider says so."),
851
+ });
852
+ export const ModelsSchema = z.object({
853
+ models: z.array(ModelSchema).describe("What this provider serves, in its own preference order, which is not rearranged here. Never empty."),
854
+ default: z.string().describe("Which one a fresh conversation starts on. Always present."),
481
855
  });
482
- export const ModelsSchema = z.object({ models: z.array(ModelSchema), default: z.string() });
483
- export const SessionIdParamSchema = z.object({ id: z.string() });
856
+ export const SessionIdParamSchema = z.object({ id: z.string().describe("Which past conversation.") });
484
857
  export const SessionSummarySchema = z.object({
485
- id: z.string(),
486
- title: z.string(),
487
- updatedAt: z.number(),
488
- snippet: MatchSnippetSchema.optional(),
858
+ id: z.string().describe("Its id."),
859
+ title: z.string().describe("What it is called."),
860
+ updatedAt: z.number().describe("When it last moved, in milliseconds."),
861
+ snippet: MatchSnippetSchema.optional().describe("Why a search matched: the line it hit, with a little around it, and who said it. Absent on an unfiltered list, and on a match the title already shows, where repeating it would be noise rather than evidence."),
489
862
  });
490
- export const SessionsListSchema = z.object({ sessions: z.array(SessionSummarySchema) });
863
+ export const SessionsListSchema = z.object({ sessions: z.array(SessionSummarySchema).describe("Past conversations, newest first.") });
491
864
  export const SystemPromptModeSchema = z.enum(["intentic", "claude", "custom"]);
492
865
  export const BuiltinPromptSchema = z.object({ base: z.enum(["intentic", "claude"]) });
493
866
  export const RuleMomentSchema = z.enum([
@@ -533,59 +906,158 @@ export const RuleFiringsSchema = z.record(z.string(), z.number());
533
906
  export const SkillOriginSchema = z.enum(["builtin", "own", "capability", "extension", "plugin", "persona", "dropped"]);
534
907
  export const SkillNameSchema = z.string().regex(/^[a-z0-9][a-z0-9-]*$/, "a skill name is lowercase letters, digits and dashes");
535
908
  export const SkillSummarySchema = z.object({
536
- id: z.string(),
537
- name: z.string(),
538
- description: z.string(),
539
- origin: SkillOriginSchema,
540
- owner: z.string().optional(),
541
- enabled: z.boolean(),
542
- switchable: z.boolean(),
543
- editable: z.boolean(),
909
+ id: z
910
+ .string()
911
+ .describe("Its handle, which reading and deleting take. A skill of your own is simply its name; one belonging to something else is qualified, because two packages may each ship a review."),
912
+ name: z.string().describe("Its name."),
913
+ description: z
914
+ .string()
915
+ .describe("What it is for, which is the line the agent reads to decide whether to reach for it. Empty when the skill declares none, which is worth showing as the blank it is: a skill with no description is rarely picked."),
916
+ origin: SkillOriginSchema.describe("Where it came from."),
917
+ owner: z.string().optional().describe("Who ships it, as the row would name them."),
918
+ enabled: z.boolean().describe("Whether the agent can reach it."),
919
+ switchable: z
920
+ .boolean()
921
+ .describe("Whether this surface can switch it. Everything else is on because its extension or its plugin is, and a switch here that silently did nothing would be worse than none, so the row names its owner instead."),
922
+ editable: z
923
+ .boolean()
924
+ .describe("Whether it can be rewritten here. Your own only: editing somebody else's in place would be undone the next time the thing that ships it catches up."),
544
925
  removable: z.boolean(),
545
926
  });
546
927
  export const SkillsListSchema = z.array(SkillSummarySchema);
547
928
  export const SkillBodySchema = z.object({
548
- id: z.string(),
549
- name: z.string(),
550
- body: z.string(),
929
+ id: z.string().describe("The skill's id, which can carry the owner it came from."),
930
+ name: z.string().describe("Its name."),
931
+ body: z.string().describe("The instructions themselves, as written."),
932
+ });
933
+ export const SkillIdSchema = z.object({
934
+ id: z
935
+ .string()
936
+ .min(1)
937
+ .describe("Which skill. It travels in the query rather than the address, because an id can name the owner it came from and that will not fit in a path."),
551
938
  });
552
- export const SkillIdSchema = z.object({ id: z.string().min(1) });
553
939
  export const SkillDraftSchema = z.object({
554
- name: SkillNameSchema,
555
- description: z.string().min(1).max(1024),
556
- body: z.string().min(1),
940
+ name: SkillNameSchema.describe("What to call it. Saving over an existing name rewrites it, which is also how one is renamed."),
941
+ description: z.string().min(1).max(1024).describe("What it is for, which is what the agent reads to decide whether to reach for it."),
942
+ body: z.string().min(1).describe("The skill itself."),
943
+ });
944
+ export const SkillRemoveSchema = z.object({
945
+ name: SkillNameSchema.describe("Which skill to delete. The text and the enabled list are both updated, so nothing is left half done."),
557
946
  });
558
- export const SkillRemoveSchema = z.object({ name: SkillNameSchema });
559
947
  export const SandboxSettingsSchema = z.object({
560
- stableSystemPrompt: z.boolean().default(false),
561
- skills: z.array(z.string()).default(["lsp"]),
562
- hashlineEdits: z.boolean().default(false),
563
- terseOutput: z.boolean().default(false),
564
- terseHoldout: z.number().min(0).max(1).default(0),
565
- systemPromptMode: SystemPromptModeSchema.default("intentic"),
566
- systemPrompt: z.string().max(20000).default(""),
567
- iqSearch: z.boolean().default(false),
568
- iqSearchHoldout: z.number().min(0).max(1).default(0),
569
- workspaceMap: z.boolean().default(false),
570
- outputCleaners: z.string().default("off"),
571
- outputHoldout: z.number().min(0).max(1).default(0),
572
- quickModel: z.array(z.string()).max(10).default([]),
573
- changelogRepos: z.array(z.string()).max(50).default([]),
574
- agentRunModels: z.array(z.string()).max(10).default([]),
575
- agentRunEffort: z.string().default(""),
576
- autoTier: z.enum(["off", "shadow", "on"]).default("shadow"),
577
- autoFastModels: z.array(z.string()).max(10).default([]),
578
- agentRetentionDays: z.number().min(0).max(365).default(3),
579
- resumeAfterOutage: z.boolean().default(false),
580
- autoResumeOnRestart: z.boolean().default(false),
581
- rules: z.array(RuleSchema).max(50).default([]),
582
- automationFailureLimit: z.number().min(0).max(20).default(0),
583
- admission: AdmissionPolicySchema.prefault({}),
584
- actionRules: z.record(z.string(), AdmissionRuleSchema).default({}),
585
- commandRules: z.partialRecord(CommandClassSchema, AdmissionRuleSchema).default({}),
586
- subagentsAtOnce: z.number().min(1).max(200).default(20),
587
- subagentsPerTurn: z.number().min(1).max(2000).default(200),
588
- subagentDepth: z.number().min(1).max(10).default(3),
948
+ stableSystemPrompt: z
949
+ .boolean()
950
+ .default(false)
951
+ .describe("Keep the instructions identical between turns so the provider can cache them, moving anything that varies into the message instead. Cheaper, at the cost of some flexibility."),
952
+ skills: z.array(z.string()).default(["lsp"]).describe("Which skills are switched on."),
953
+ hashlineEdits: z
954
+ .boolean()
955
+ .default(false)
956
+ .describe("Have the agent edit files by line number rather than by quoting the text it wants replaced. Cheaper on large files, and less forgiving of a stale read."),
957
+ terseOutput: z.boolean().default(false).describe("Ask the agent to say less. It changes how much it narrates, not how much it does."),
958
+ terseHoldout: z
959
+ .number()
960
+ .min(0)
961
+ .max(1)
962
+ .default(0)
963
+ .describe("What share of turns to run without that instruction, so the two can be compared honestly. It has to be measured this way, because a turn cannot be re-run to see what it would have said. Zero means no measurement, which is the default, since the comparison costs the very tokens it is measuring."),
964
+ systemPromptMode: SystemPromptModeSchema.default("intentic").describe("Which instructions the agent starts from: intentic's own, the ones the installed Claude Code carries, or your own. The first two both get this product's own guidance added on top; your own gets nothing added, which is the point of it."),
965
+ systemPrompt: z
966
+ .string()
967
+ .max(20000)
968
+ .default("")
969
+ .describe("Your own instructions, used only when the mode above says custom. Then it is the whole of them: both built-in bases go, and so does everything this product would otherwise add, including the guidance the chat's own cards are driven by. That is the price of total control."),
970
+ iqSearch: z
971
+ .boolean()
972
+ .default(false)
973
+ .describe("Teach the agent how to use this workspace's own search tool, rather than leaving it to grep around."),
974
+ iqSearchHoldout: z
975
+ .number()
976
+ .min(0)
977
+ .max(1)
978
+ .default(0)
979
+ .describe("What share of conversations to run without that teaching, so the two can be compared. Whole conversations rather than individual turns, because once the teaching is in a session, withholding it from the next request does not make the model forget it."),
980
+ workspaceMap: z
981
+ .boolean()
982
+ .default(false)
983
+ .describe("Open every conversation with a map of the project it starts in: what is in it, what each part is for, and where the agent is standing. Worked out fresh each time rather than written down anywhere, because a written layout is wrong within a fortnight. Off by default, since it spends tokens on the first message of every conversation."),
984
+ outputCleaners: z
985
+ .string()
986
+ .default("off")
987
+ .describe("Which command outputs to trim before the agent reads them, cutting the noise a build tool prints without cutting what it said."),
988
+ outputHoldout: z
989
+ .number()
990
+ .min(0)
991
+ .max(1)
992
+ .default(0)
993
+ .describe("What share of commands to leave untrimmed, so the saving can be measured against a real comparison rather than estimated."),
994
+ quickModel: z
995
+ .array(z.string())
996
+ .max(10)
997
+ .default([])
998
+ .describe("Which models do the small automatic jobs that are not a conversation, such as writing a commit message. A list rather than one pick, tried in order, because the interesting failure is a model that is connected and simply will not answer today. Empty means work it out from whatever is connected, which improves by itself as accounts are added."),
999
+ changelogRepos: z
1000
+ .array(z.string())
1001
+ .max(50)
1002
+ .default([])
1003
+ .describe("Which repositories keep a changelog, and so get a user-facing note written alongside each merge. A list rather than a switch, and empty by default, because the commit writer's standing rule is to copy the house style rather than impose one, and a repository that has never written such a note gives it nothing to copy."),
1004
+ agentRunModels: z
1005
+ .array(z.string())
1006
+ .max(10)
1007
+ .default([])
1008
+ .describe("Which models run the work a screen starts rather than a person: fixing a red pipeline, a maintenance chore, an acceptance run. Tried in order, so one spent account does not take every such run down. Empty falls back to whatever the chat would have used, which is the honest floor because it is the model you already chose to work with."),
1009
+ agentRunEffort: z.string().default("").describe("How hard those runs should think."),
1010
+ autoTier: z
1011
+ .enum(["off", "shadow", "on"])
1012
+ .default("shadow")
1013
+ .describe("Whether an easy-looking turn may run on a cheaper model from the same provider. Three states rather than a switch, because the middle one is the only honest road to the third: it scores every turn and routes nothing, so the guess can become a measurement before it changes anything. It can only ever route down, so the worst case is one turn's quality rather than a bill nobody asked for."),
1014
+ autoFastModels: z
1015
+ .array(z.string())
1016
+ .max(10)
1017
+ .default([])
1018
+ .describe("Which cheaper model a downgraded turn lands on. A list so a sandbox spanning providers can name a rung on each, but not a fallback ladder: an entry naming a different provider than the turn is on is skipped rather than tried, because switching provider retires the conversation and starting over to save a fraction of a penny is not a saving. Empty picks the cheapest the turn's own provider publishes."),
1019
+ agentRetentionDays: z
1020
+ .number()
1021
+ .min(0)
1022
+ .max(365)
1023
+ .default(3)
1024
+ .describe("How many days a finished conversation stays on the board before being put away. Zero means never. The one setting here that defaults on, because each card left behind is a real working copy on disk, not just a row."),
1025
+ resumeAfterOutage: z
1026
+ .boolean()
1027
+ .default(false)
1028
+ .describe("Whether a turn killed by the model provider failing is re-run automatically, backing off between attempts. The sandbox-wide default; any one conversation can say otherwise. Off to begin with, because a retry spends your allowance on a turn you sent once and only you can say whether it was worth paying for twice. Worth turning on for a sandbox whose work mostly happens with nobody in the room."),
1029
+ autoResumeOnRestart: z
1030
+ .boolean()
1031
+ .default(false)
1032
+ .describe("Whether a turn killed by the sandbox restarting is re-run once it comes back. Off to begin with, for the same reason: it would spend your allowance on work you are not watching and edit files while you are still waiting for the sandbox to return. Either way the interruption is recorded rather than silently lost."),
1033
+ rules: z
1034
+ .array(RuleSchema)
1035
+ .max(50)
1036
+ .default([])
1037
+ .describe("Standing instructions you give the sandbox about its own work: ask for proof before a turn ends, run something before a push, hold or release finished work. Empty is the default and is exactly the behaviour of a fresh sandbox, because each of those defaults is what no rule matched means at its own moment."),
1038
+ automationFailureLimit: z
1039
+ .number()
1040
+ .min(0)
1041
+ .max(20)
1042
+ .default(0)
1043
+ .describe("How many failures in a row before an automation switches itself off. Zero means never, which is the default, because the failure is not always the automation's fault and a job disabled at three in the morning is one nobody re-enables. Only real errors count: a guard deciding there was nothing to do, or the sandbox dying mid-run, say nothing about the automation."),
1044
+ admission: AdmissionPolicySchema.prefault({}).describe("Whether work started from outside may run, per kind of trigger: let it, hold it for approval, or refuse it. Composes with each automation's own setting, and the stricter of the two wins, so holding every visitor's message needs no edit to each automation."),
1045
+ actionRules: z
1046
+ .record(z.string(), AdmissionRuleSchema)
1047
+ .default({})
1048
+ .describe("What an agent may do out in the world, per kind of action: go ahead, ask first, or never."),
1049
+ commandRules: z
1050
+ .partialRecord(CommandClassSchema, AdmissionRuleSchema)
1051
+ .default({})
1052
+ .describe("What an agent may run inside the sandbox, for the five kinds of command that are hard to take back: rewriting git history, deleting recursively, reading credential files, publishing a package, reaching out to the network. Everything else is recoverable in a container that is itself disposable, and gating it would be friction bought with nothing."),
1053
+ subagentsAtOnce: z.number().min(1).max(200).default(20).describe("How many helper agents may work at the same time."),
1054
+ subagentsPerTurn: z.number().min(1).max(2000).default(200).describe("How many a single turn may start in total."),
1055
+ subagentDepth: z
1056
+ .number()
1057
+ .min(1)
1058
+ .max(10)
1059
+ .default(3)
1060
+ .describe("How many levels deep the delegation may go, since a helper can start helpers of its own."),
589
1061
  });
590
1062
  export const BuiltinPromptTextSchema = z.object({ text: z.string(), version: z.string() });
591
1063
  export const SavingsStageSchema = z.object({ id: z.string(), commands: z.number(), savedTokens: z.number() });
@@ -622,314 +1094,603 @@ export const SavingsReportSchema = z.object({
622
1094
  });
623
1095
  export const IntenticRunSchema = z.object({ args: z.array(z.string()) });
624
1096
  export const CommitSchema = RepoParamSchema.extend({
625
- message: z.string().min(1),
626
- all: z.boolean().optional(),
627
- paths: z.array(z.string().min(1)).max(500).optional(),
1097
+ message: z.string().min(1).describe("The commit message."),
1098
+ all: z
1099
+ .boolean()
1100
+ .optional()
1101
+ .describe("Stage every change in the repository first, then commit. An alternative to naming paths, not a companion to it."),
1102
+ paths: z
1103
+ .array(z.string().min(1))
1104
+ .max(500)
1105
+ .optional()
1106
+ .describe("Stage exactly these paths, then commit everything staged. Leave this and `all` out to commit whatever is already staged."),
628
1107
  });
629
1108
  export const DiscardSchema = RepoParamSchema.extend({
630
- paths: z.array(z.string().min(1)).max(500).optional(),
1109
+ paths: z
1110
+ .array(z.string().min(1))
1111
+ .max(500)
1112
+ .optional()
1113
+ .describe("Which paths to throw away. Leave it out to discard every uncommitted change in the repository."),
1114
+ });
1115
+ export const GitStageSchema = RepoParamSchema.extend({
1116
+ paths: z.array(z.string().min(1)).max(500).describe("The paths to move. Nothing on disk changes, so this is always safe and always reversible."),
1117
+ });
1118
+ export const PushSchema = RepoParamSchema.extend({
1119
+ branch: z
1120
+ .string()
1121
+ .min(1)
1122
+ .optional()
1123
+ .describe("Which branch to push. Leave it out for the checked-out one. A branch with no upstream yet gets one set on this push."),
1124
+ });
1125
+ export const GitFileQuerySchema = RepoParamSchema.extend({ path: z.string().min(1).describe("The file to read, relative to the repository root.") });
1126
+ export const GitFileWriteSchema = RepoParamSchema.extend({
1127
+ path: z.string().min(1).describe("Where to write, relative to the repository root. Missing folders are created."),
1128
+ content: z.string().describe("The file's whole new contents."),
631
1129
  });
632
- export const GitStageSchema = RepoParamSchema.extend({ paths: z.array(z.string().min(1)).max(500) });
633
- export const PushSchema = RepoParamSchema.extend({ branch: z.string().min(1).optional() });
634
- export const GitFileQuerySchema = RepoParamSchema.extend({ path: z.string().min(1) });
635
- export const GitFileWriteSchema = RepoParamSchema.extend({ path: z.string().min(1), content: z.string() });
636
1130
  export const GitDiffSideSchema = z.enum(["staged", "unstaged", "conflicted"]);
637
- export const GitFileDiffQuerySchema = RepoParamSchema.extend({ path: z.string().min(1), side: GitDiffSideSchema });
638
- export const GitStatusSchema = z.object({ branch: z.string(), dirty: z.boolean(), files: z.array(z.string()) });
639
- export const GitFilesSchema = z.object({ files: z.array(z.string()) });
640
- export const GitFileSchema = z.object({ path: z.string(), content: z.string() });
641
- export const RepoPathsSchema = z.object({ repo: z.string().min(1), paths: z.array(z.string().min(1)).max(500).optional() });
1131
+ export const GitFileDiffQuerySchema = RepoParamSchema.extend({
1132
+ path: z.string().min(1).describe("The file, relative to the repository root."),
1133
+ side: GitDiffSideSchema.describe("Which comparison you want. A file that is staged and then edited again has genuinely different answers for each, which is why this is required rather than assumed."),
1134
+ });
1135
+ export const GitStatusSchema = z.object({
1136
+ branch: z.string().describe("The checked-out branch."),
1137
+ dirty: z.boolean().describe("Whether anything is uncommitted."),
1138
+ files: z.array(z.string()).describe("Every path with something pending, staged or not."),
1139
+ });
1140
+ export const GitFilesSchema = z.object({
1141
+ files: z.array(z.string()).describe("Every path git tracks, relative to the repository root. Ignored and untracked files are not here."),
1142
+ });
1143
+ export const GitFileSchema = z.object({
1144
+ path: z.string().describe("The path, as asked for."),
1145
+ content: z.string().describe("The file's contents as they stand on disk."),
1146
+ });
1147
+ export const RepoPathsSchema = z.object({
1148
+ repo: z.string().min(1).describe("Which repository."),
1149
+ paths: z.array(z.string().min(1)).max(500).optional().describe("Which of its paths. Leave it out for the whole repository."),
1150
+ });
642
1151
  export const GitChangeSchema = z.object({
643
- path: z.string(),
644
- status: z.enum(["added", "modified", "deleted", "renamed", "type-changed", "conflicted"]),
645
- from: z.string().optional(),
646
- additions: z.number().optional(),
647
- deletions: z.number().optional(),
1152
+ path: z.string().describe("The path, relative to the repository root. For a rename this is the new one."),
1153
+ status: z
1154
+ .enum(["added", "modified", "deleted", "renamed", "type-changed", "conflicted"])
1155
+ .describe("What happened to it. Conflicted is not a kind of edit: nothing can be committed anywhere in the repository while one exists."),
1156
+ from: z.string().optional().describe("Where a renamed file came from."),
1157
+ additions: z
1158
+ .number()
1159
+ .optional()
1160
+ .describe("Lines added. Absent for a binary file, and for an untracked one, which has nothing to compare against."),
1161
+ deletions: z.number().optional().describe("Lines removed. Absent for the same reasons additions is."),
648
1162
  });
649
1163
  export const GitRemoteStateSchema = z.object({
650
- remote: z.string().optional(),
651
- branch: z.string().optional(),
652
- upstream: z.string().optional(),
653
- ahead: z.number(),
654
- behind: z.number(),
1164
+ remote: z
1165
+ .string()
1166
+ .optional()
1167
+ .describe("The remote this branch pushes to. Absent means none is configured. In a fork with two remotes, pushing to the wrong one succeeds and leaves the count stuck, which is why this says which."),
1168
+ branch: z.string().optional().describe("The checked-out branch. Absent when the repository is on a bare commit, or has no commits yet."),
1169
+ upstream: z.string().optional().describe("The branch on the remote this one follows. Absent means the next push will publish it."),
1170
+ ahead: z.number().describe("Commits you have that the remote does not."),
1171
+ behind: z.number().describe("Commits the remote has that you do not, as of the last fetch. Fetch before trusting it."),
655
1172
  });
656
1173
  const RefNameSchema = z
657
1174
  .string()
658
1175
  .regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/)
659
1176
  .max(200);
660
1177
  export const GitBranchSchema = z.object({
661
- name: z.string(),
662
- current: z.boolean(),
663
- upstream: z.string().optional(),
664
- ahead: z.number(),
665
- behind: z.number(),
666
- gone: z.boolean().optional(),
667
- at: z.number(),
1178
+ name: z.string().describe("The branch name."),
1179
+ current: z.boolean().describe("Whether this is the one checked out."),
1180
+ upstream: z.string().optional().describe("The branch on the remote it follows, if any."),
1181
+ ahead: z.number().describe("Commits this branch has that its remote counterpart does not."),
1182
+ behind: z.number().describe("Commits its remote counterpart has that it does not."),
1183
+ gone: z
1184
+ .boolean()
1185
+ .optional()
1186
+ .describe("The branch it followed no longer exists on the remote, usually because a merged pull request deleted it. The signal that this one is safe to delete."),
1187
+ at: z.number().describe("When its tip was committed, in milliseconds. Lists are newest first."),
1188
+ });
1189
+ export const GitRemoteBranchSchema = z.object({
1190
+ name: z.string().describe("The full name, such as origin/main."),
1191
+ remote: z.string().describe("Just the remote part, so a picker can group by it without re-parsing."),
1192
+ branch: z.string().describe("Just the branch part."),
1193
+ at: z.number().describe("When its tip was committed, in milliseconds, as this repository last saw it."),
1194
+ });
1195
+ export const GitBranchesSchema = z.object({
1196
+ branches: z.array(GitBranchSchema).describe("Branches in this repository."),
1197
+ remotes: z
1198
+ .array(GitRemoteBranchSchema)
1199
+ .describe("Branches on its remotes, as last seen. Sent together with the locals so a switcher never draws a half-filled list."),
668
1200
  });
669
- export const GitRemoteBranchSchema = z.object({ name: z.string(), remote: z.string(), branch: z.string(), at: z.number() });
670
- export const GitBranchesSchema = z.object({ branches: z.array(GitBranchSchema), remotes: z.array(GitRemoteBranchSchema) });
671
1201
  export const GitBranchCreateAtSchema = RepoParamSchema.extend({
672
- name: RefNameSchema,
673
- start: z.string().min(1).optional(),
674
- checkout: z.boolean().optional(),
1202
+ name: RefNameSchema.describe("The new branch's name."),
1203
+ start: z.string().min(1).optional().describe("Where to start it: a commit or another branch. Leave it out to start from where you are."),
1204
+ checkout: z.boolean().optional().describe("Switch to it as well as creating it."),
1205
+ });
1206
+ export const GitBranchDeleteSchema = RepoParamSchema.extend({
1207
+ name: RefNameSchema.describe("The branch to delete."),
1208
+ force: z
1209
+ .boolean()
1210
+ .optional()
1211
+ .describe("Delete it even though it holds work that was never merged. The deliberate retry after the first attempt refuses."),
675
1212
  });
676
- export const GitBranchDeleteSchema = RepoParamSchema.extend({ name: RefNameSchema, force: z.boolean().optional() });
677
1213
  export const GitOperationSchema = z.enum(["merge", "rebase", "cherry-pick", "revert"]);
678
- export const GitOperationStateSchema = z.object({ repo: z.string(), operation: GitOperationSchema.optional() });
1214
+ export const GitOperationStateSchema = z.object({
1215
+ repo: z.string().describe("The repository asked about."),
1216
+ operation: GitOperationSchema.optional().describe("Which operation the working tree is stuck inside. Absent means it is not stuck at all, which is almost always. While one is present git refuses nearly everything else, and abandoning it is the only way out."),
1217
+ });
679
1218
  export const RepoChangesSchema = z.object({
680
1219
  repo: z.string(),
681
- branch: z.string().optional(),
682
- conflicted: z.array(GitChangeSchema),
683
- operation: GitOperationSchema.optional(),
684
- staged: z.array(GitChangeSchema),
685
- unstaged: z.array(GitChangeSchema),
686
- truncated: z.number().optional(),
687
- remote: GitRemoteStateSchema.optional(),
688
- origins: z.record(z.string(), z.array(z.string())).optional(),
689
- error: z.string().optional(),
1220
+ branch: z.string().optional().describe("The checked-out branch. Absent in a repository that has no commits yet."),
1221
+ conflicted: z
1222
+ .array(GitChangeSchema)
1223
+ .describe("Paths a merge or rebase could not finish. First, because nothing anywhere in this repository can be committed until they are resolved. Held apart from the two lists below, because staged or not is not a question one of these has an answer to."),
1224
+ operation: GitOperationSchema.optional().describe("What halted, when something did. This is the sentence that explains the conflicts above and names the way out of them."),
1225
+ staged: z.array(GitChangeSchema).describe("What a plain commit would record right now."),
1226
+ unstaged: z
1227
+ .array(GitChangeSchema)
1228
+ .describe("Edits on disk that are not staged, plus untracked files. A path can be in both lists at once with different line counts, which is why they are separate."),
1229
+ truncated: z
1230
+ .number()
1231
+ .optional()
1232
+ .describe("How many changes were cut from the two lists above. A freshly cloned monorepo or a mass delete runs to six figures, which no screen can draw, so past a budget the lists arrive short and this says by how much. Absent means they are complete."),
1233
+ remote: GitRemoteStateSchema.optional().describe("Where this repository stands against its remote."),
1234
+ origins: z
1235
+ .record(z.string(), z.array(z.string()))
1236
+ .optional()
1237
+ .describe("Which conversation put each path here, newest first, keyed by path. Only work that went through a merge can appear: edits made in the shared tree, in a terminal, or by a person are simply absent rather than guessed at."),
1238
+ error: z
1239
+ .string()
1240
+ .optional()
1241
+ .describe("Why the repository could not be read at all, in git's own words. A repository left broken by a failed import arrives with empty lists and this set, rather than vanishing from the answer with nothing to act on."),
690
1242
  });
691
1243
  export const OriginAgentSchema = z.object({
692
- title: z.string().optional(),
693
- provider: AgentProviderSchema,
694
- landedMessage: LandedMessageSchema.optional(),
1244
+ title: z.string().optional().describe("The conversation's title. Absent for one that never got as far as having a title."),
1245
+ provider: AgentProviderSchema.describe("Which model provider it ran on."),
1246
+ landedMessage: LandedMessageSchema.optional().describe("What the merged work did, drafted by the conversation itself. Carried here as well as on its card, because merged lines outlive the card: archiving a finished conversation does not uncommit its work."),
695
1247
  });
696
1248
  export const GitChangesSchema = z.object({
697
- repos: z.array(RepoChangesSchema),
698
- originAgents: z.record(z.string(), OriginAgentSchema).optional(),
699
- committing: z.array(z.string()).optional(),
1249
+ repos: z
1250
+ .array(RepoChangesSchema)
1251
+ .describe("One entry per repository that has something pending, is out of step with its remote, or could not be read. A clean repository is simply absent."),
1252
+ originAgents: z
1253
+ .record(z.string(), OriginAgentSchema)
1254
+ .optional()
1255
+ .describe("Who each conversation named above is, keyed by id, so a caller need not look them up. Absent when nothing in the review can be attributed."),
1256
+ committing: z
1257
+ .array(z.string())
1258
+ .optional()
1259
+ .describe("Repositories with a commit running right now. The sandbox's answer rather than any one tab's, so a reload, a second window and another device all know. Absent means nothing is committing."),
700
1260
  });
701
1261
  export const CommitResultSchema = z.object({
702
- committed: z.boolean(),
703
- changes: RepoChangesSchema.optional(),
704
- originAgents: z.record(z.string(), OriginAgentSchema).optional(),
705
- });
706
- export const WorkspaceModuleSchema = z.object({ dir: z.string(), name: z.string() });
707
- export const RepoModulesSchema = z.object({ repo: z.string(), modules: z.array(WorkspaceModuleSchema) });
708
- export const WorkspaceModulesSchema = z.object({ repos: z.array(RepoModulesSchema) });
709
- export const AgentChangeSchema = GitChangeSchema.extend({ landed: z.boolean() });
1262
+ committed: z.boolean().describe("Whether a commit was actually recorded."),
1263
+ changes: RepoChangesSchema.optional().describe("What this repository looks like now, read in the same breath as the commit so a caller can redraw from here instead of asking for a fresh scan. Absent means there is nothing left to show."),
1264
+ originAgents: z
1265
+ .record(z.string(), OriginAgentSchema)
1266
+ .optional()
1267
+ .describe("Who the conversations named in those changes are. Merge it over what you already hold rather than replacing: other repositories still name their own."),
1268
+ });
1269
+ export const WorkspaceModuleSchema = z.object({
1270
+ dir: z.string().describe("Where the package lives, relative to its repository. Empty when the repository is itself one package."),
1271
+ name: z.string().describe("The name the package declares for itself."),
1272
+ });
1273
+ export const RepoModulesSchema = z.object({
1274
+ repo: z.string().describe("Which repository."),
1275
+ modules: z.array(WorkspaceModuleSchema).describe("Its packages."),
1276
+ });
1277
+ export const WorkspaceModulesSchema = z.object({ repos: z.array(RepoModulesSchema).describe("Every repository with the packages inside it.") });
1278
+ export const AgentChangeSchema = GitChangeSchema.extend({
1279
+ landed: z
1280
+ .boolean()
1281
+ .describe("Whether this change is already in the shared tree. The list is everything the conversation wrote, not just what is left over, because a clean turn merges in milliseconds and a list of leftovers would show an empty panel for work nobody had looked at yet."),
1282
+ });
710
1283
  export const AgentRepoChangesSchema = z.object({
711
- repo: z.string(),
712
- branch: z.string().optional(),
713
- changes: z.array(AgentChangeSchema),
714
- modules: z.array(WorkspaceModuleSchema),
1284
+ repo: z.string().describe("Which repository."),
1285
+ branch: z.string().optional().describe("The branch this conversation's work sits on."),
1286
+ changes: z.array(AgentChangeSchema).describe("What it changed there."),
1287
+ modules: z
1288
+ .array(WorkspaceModuleSchema)
1289
+ .describe("The packages of the tree these changes came from, so a review can group by package. Carried with the changes rather than looked up separately, because a package the conversation has just created exists only in its own copy and the shared tree has never heard of it."),
1290
+ });
1291
+ export const AgentChangesSchema = z.object({
1292
+ repos: z.array(AgentRepoChangesSchema).describe("One entry per repository the conversation touched."),
1293
+ conflicts: z
1294
+ .array(LandConflictSchema)
1295
+ .optional()
1296
+ .describe("Why the last merge refused, when one did. Carried here as well as in the merge's own answer, because a conflict is found the moment a turn ends and dealt with hours later on this surface, which would otherwise open with nothing to explain what it promised to resolve."),
715
1297
  });
716
- export const AgentChangesSchema = z.object({ repos: z.array(AgentRepoChangesSchema), conflicts: z.array(LandConflictSchema).optional() });
717
1298
  const ShaSchema = z.string().regex(/^[0-9a-f]{4,64}$/);
718
1299
  export const GitCommitSchema = z.object({
719
- sha: z.string(),
720
- short: z.string(),
721
- parents: z.array(z.string()),
722
- subject: z.string(),
723
- body: z.string(),
724
- author: z.string(),
725
- email: z.string(),
726
- at: z.number(),
727
- refs: z.array(z.string()),
728
- head: z.boolean(),
1300
+ sha: z.string().describe("The commit, in full."),
1301
+ short: z.string().describe("The abbreviated form, for showing."),
1302
+ parents: z
1303
+ .array(z.string())
1304
+ .describe("What it came from. None means the first commit, one is ordinary, two or more is a merge, which is what a graph draws its lanes from."),
1305
+ subject: z.string().describe("Its first line."),
1306
+ body: z.string().describe("Everything after that."),
1307
+ author: z.string().describe("Who wrote it."),
1308
+ email: z.string().describe("Their address."),
1309
+ at: z.number().describe("When they wrote it, in milliseconds."),
1310
+ refs: z.array(z.string()).describe("Branches and tags sitting on it."),
1311
+ head: z.boolean().describe("Whether this is where the repository currently stands."),
729
1312
  });
730
1313
  export const GitLogSchema = z.object({
731
- repo: z.string(),
732
- branch: z.string().optional(),
733
- commits: z.array(GitCommitSchema),
734
- hasMore: z.boolean(),
1314
+ repo: z.string().describe("Which repository."),
1315
+ branch: z.string().optional().describe("Which branch these are from."),
1316
+ commits: z.array(GitCommitSchema).describe("The commits, newest first."),
1317
+ hasMore: z
1318
+ .boolean()
1319
+ .describe("There are older ones behind this page. It is also what stops the last row being drawn as the beginning of history, which is how a truncated log used to claim it started where the page happened to stop."),
735
1320
  });
736
1321
  export const GitLogQuerySchema = RepoParamSchema.extend({
737
- limit: z.coerce.number().int().positive().max(2000).optional(),
738
- skip: z.coerce.number().int().nonnegative().max(1_000_000).optional(),
1322
+ limit: z.coerce.number().int().positive().max(2000).optional().describe("How many commits to return."),
1323
+ skip: z.coerce
1324
+ .number()
1325
+ .int()
1326
+ .nonnegative()
1327
+ .max(1_000_000)
1328
+ .optional()
1329
+ .describe("How many newer commits to step over, which is how you page further back. Paged rather than read whole, because a large repository's history is tens of thousands of rows."),
1330
+ });
1331
+ export const GitReposSchema = z.object({
1332
+ repos: z.array(z.string()).describe('Every repository\'s id. The workspace itself is always present as "root".'),
1333
+ });
1334
+ export const GitRemoteRepoSchema = z.object({
1335
+ repo: z.string().describe("The workspace repository."),
1336
+ host: z.string().describe("Which forge its remote points at."),
1337
+ project: z.string().describe("Which project there, as owner and name."),
1338
+ });
1339
+ export const GitRemoteReposSchema = z.object({
1340
+ repos: z.array(GitRemoteRepoSchema).describe("Each repository matched to the project its remote points at."),
1341
+ });
1342
+ export const GitPublishFileSchema = RepoParamSchema.extend({
1343
+ path: z.string().min(1).describe("Which file, relative to the repository."),
1344
+ content: z.string().describe("Its whole new contents."),
1345
+ message: z.string().min(1).describe("The commit message."),
739
1346
  });
740
- export const GitReposSchema = z.object({ repos: z.array(z.string()) });
741
- export const GitRemoteRepoSchema = z.object({ repo: z.string(), host: z.string(), project: z.string() });
742
- export const GitRemoteReposSchema = z.object({ repos: z.array(GitRemoteRepoSchema) });
743
- export const GitPublishFileSchema = RepoParamSchema.extend({ path: z.string().min(1), content: z.string(), message: z.string().min(1) });
744
1347
  export const GitPublishFileResultSchema = z.object({
745
- ok: z.boolean(),
746
- wrote: z.boolean(),
747
- committed: z.boolean(),
748
- pushed: z.boolean(),
749
- branch: z.string().optional(),
750
- defaultBranch: z.string().optional(),
751
- reason: z.string().optional(),
752
- });
753
- export const GitCommitDiffQuerySchema = RepoParamSchema.extend({ sha: ShaSchema });
754
- export const GitCommitDiffSchema = z.object({ files: z.array(GitChangeSchema) });
755
- export const GitCommitFileDiffQuerySchema = RepoParamSchema.extend({ sha: ShaSchema, path: z.string().min(1) });
756
- export const GitBranchCreateSchema = RepoParamSchema.extend({ sha: ShaSchema, name: RefNameSchema });
757
- export const GitTagCreateSchema = RepoParamSchema.extend({ sha: ShaSchema, name: RefNameSchema });
758
- export const GitCheckoutSchema = RepoParamSchema.extend({ ref: RefNameSchema });
759
- export const GitTagDeleteSchema = RepoParamSchema.extend({ name: RefNameSchema, remote: RefNameSchema.optional() });
760
- export const GitTagPushSchema = RepoParamSchema.extend({ name: RefNameSchema, remote: RefNameSchema });
761
- export const GitResetSchema = RepoParamSchema.extend({ sha: ShaSchema, mode: z.enum(["soft", "mixed", "hard"]) });
762
- export const GitCommitActionSchema = RepoParamSchema.extend({ sha: ShaSchema });
763
- export const GitActionResultSchema = z.object({ ok: z.boolean(), reason: z.string().optional() });
1348
+ ok: z.boolean().describe("Whether the whole thing went through."),
1349
+ wrote: z.boolean().describe("The file was written."),
1350
+ committed: z.boolean().describe("The commit was recorded."),
1351
+ pushed: z.boolean().describe("It reached the remote."),
1352
+ branch: z.string().optional().describe("Which branch it happened on."),
1353
+ defaultBranch: z.string().optional().describe("Which branch the repository considers its main one, so a caller can see it was on a side branch."),
1354
+ reason: z
1355
+ .string()
1356
+ .optional()
1357
+ .describe("Why it stopped where it did. Being on a side branch, having no remote and having no credentials are all reported here rather than raised."),
1358
+ });
1359
+ export const GitCommitDiffQuerySchema = RepoParamSchema.extend({ sha: ShaSchema.describe("Which commit.") });
1360
+ export const GitCommitDiffSchema = z.object({
1361
+ files: z
1362
+ .array(GitChangeSchema)
1363
+ .describe("Which files it touched, with counts but not contents. Fetch any one file's contents separately, so a commit with a thousand files stays one cheap answer."),
1364
+ });
1365
+ export const GitCommitFileDiffQuerySchema = RepoParamSchema.extend({
1366
+ sha: ShaSchema.describe("Which commit."),
1367
+ path: z.string().min(1).describe("Which file in it."),
1368
+ });
1369
+ export const GitBranchCreateSchema = RepoParamSchema.extend({
1370
+ sha: ShaSchema.describe("Which commit to start it at."),
1371
+ name: RefNameSchema.describe("The new branch's name."),
1372
+ });
1373
+ export const GitTagCreateSchema = RepoParamSchema.extend({
1374
+ sha: ShaSchema.describe("Which commit to tag."),
1375
+ name: RefNameSchema.describe("The tag's name."),
1376
+ });
1377
+ export const GitCheckoutSchema = RepoParamSchema.extend({ ref: RefNameSchema.describe("Where to switch to: a branch, a tag, or a commit.") });
1378
+ export const GitTagDeleteSchema = RepoParamSchema.extend({
1379
+ name: RefNameSchema.describe("Which tag."),
1380
+ remote: RefNameSchema.optional().describe("Also delete it there. Leave it out to remove it locally only."),
1381
+ });
1382
+ export const GitTagPushSchema = RepoParamSchema.extend({
1383
+ name: RefNameSchema.describe("Which tag."),
1384
+ remote: RefNameSchema.describe("Which remote to send it to."),
1385
+ });
1386
+ export const GitResetSchema = RepoParamSchema.extend({
1387
+ sha: ShaSchema.describe("Which commit to move the branch to."),
1388
+ mode: z
1389
+ .enum(["soft", "mixed", "hard"])
1390
+ .describe("How much to take with it: move the branch alone, also unstage, or also throw away what is on disk. The last one takes a checkpoint first."),
1391
+ });
1392
+ export const GitCommitActionSchema = RepoParamSchema.extend({ sha: ShaSchema.describe("Which commit to act on.") });
1393
+ export const GitActionResultSchema = z.object({
1394
+ ok: z.boolean().describe("Whether it worked."),
1395
+ reason: z
1396
+ .string()
1397
+ .optional()
1398
+ .describe("Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages."),
1399
+ });
764
1400
  export const StashEntrySchema = z.object({
765
- ref: z.string(),
766
- sha: z.string(),
767
- short: z.string(),
768
- subject: z.string(),
769
- branch: z.string().optional(),
770
- at: z.number(),
771
- parents: z.array(z.string()),
1401
+ ref: z.string().describe("How to address it, which applying and dropping take."),
1402
+ sha: z.string().describe("The commit behind it, because a stash entry is a commit."),
1403
+ short: z.string().describe("The abbreviated form, for showing."),
1404
+ subject: z.string().describe("What it was set aside as, with git's own scaffolding stripped off."),
1405
+ branch: z.string().optional().describe("Which branch it was set aside from."),
1406
+ at: z.number().describe("When, in milliseconds."),
1407
+ parents: z.array(z.string()).describe("What it sits on, so a graph can draw it like any other commit."),
1408
+ });
1409
+ export const StashListSchema = z.object({
1410
+ repo: z.string().describe("Which repository."),
1411
+ stashes: z.array(StashEntrySchema).describe("What is set aside, newest first."),
772
1412
  });
773
- export const StashListSchema = z.object({ repo: z.string(), stashes: z.array(StashEntrySchema) });
774
1413
  const StashRefSchema = z.string().regex(/^stash@\{\d{1,4}\}$/);
775
- export const StashPushSchema = RepoParamSchema.extend({ message: z.string().max(500).optional(), includeUntracked: z.boolean().optional() });
776
- export const StashApplySchema = RepoParamSchema.extend({ ref: StashRefSchema, pop: z.boolean().optional() });
777
- export const StashRefParamSchema = RepoParamSchema.extend({ ref: StashRefSchema });
778
- export const StashDiffQuerySchema = RepoParamSchema.extend({ ref: StashRefSchema });
1414
+ export const StashPushSchema = RepoParamSchema.extend({
1415
+ message: z.string().max(500).optional().describe("What to call it, so you know what it was later."),
1416
+ includeUntracked: z.boolean().optional().describe("Also set aside files git is not yet tracking, which are otherwise left where they are."),
1417
+ });
1418
+ export const StashApplySchema = RepoParamSchema.extend({
1419
+ ref: StashRefSchema.describe("Which entry."),
1420
+ pop: z.boolean().optional().describe("Remove it from the stash once it has been applied cleanly."),
1421
+ });
1422
+ export const StashRefParamSchema = RepoParamSchema.extend({ ref: StashRefSchema.describe("Which entry.") });
1423
+ export const StashDiffQuerySchema = RepoParamSchema.extend({ ref: StashRefSchema.describe("Which entry.") });
779
1424
  export const UndoKindSchema = z.enum(["commit", "amend", "merge", "rebase", "cherry-pick", "revert", "reset", "pull", "other"]);
780
1425
  export const UndoableActionSchema = z.object({
781
- kind: UndoKindSchema,
782
- description: z.string(),
783
- branch: z.string(),
784
- sha: z.string(),
785
- previousSha: z.string(),
786
- changesWorkingTree: z.boolean(),
1426
+ kind: UndoKindSchema.describe("What the last action was."),
1427
+ description: z.string().describe("What undoing it would do, in words."),
1428
+ branch: z.string().describe("Which branch would move."),
1429
+ sha: z.string().describe("Where it stands now."),
1430
+ previousSha: z
1431
+ .string()
1432
+ .describe("Where it would go back to. Send this with the undo as proof you looked, so one prepared against a view that has since moved is refused rather than landing somewhere unexamined."),
1433
+ changesWorkingTree: z
1434
+ .boolean()
1435
+ .describe("Undoing would rewrite files as well as moving the branch, so anything offering it should warn about losing work."),
1436
+ });
1437
+ export const GitUndoStateSchema = z.object({
1438
+ repo: z.string().describe("Which repository."),
1439
+ action: UndoableActionSchema.optional().describe("What undoing would reverse. Absent means there is nothing to go back from."),
1440
+ });
1441
+ export const GitUndoSchema = RepoParamSchema.extend({
1442
+ previousSha: ShaSchema.describe("Where to go back to, from the matching read. It is also proof you looked: one prepared against a stale view is refused."),
1443
+ discardChanges: z.boolean().optional().describe("Also rewrite the files, rather than only moving the branch."),
787
1444
  });
788
- export const GitUndoStateSchema = z.object({ repo: z.string(), action: UndoableActionSchema.optional() });
789
- export const GitUndoSchema = RepoParamSchema.extend({ previousSha: ShaSchema, discardChanges: z.boolean().optional() });
790
1445
  export const SnapshotTriggerSchema = z.enum(["turn", "interval", "pre-restore", "restore", "user"]);
791
1446
  export const SnapshotSchema = z.object({
792
- id: z.string(),
793
- at: z.number(),
794
- trigger: SnapshotTriggerSchema,
795
- label: z.string().optional(),
1447
+ id: z.string().describe("The saved point's id, which is what restoring and diffing take."),
1448
+ at: z.number().describe("When it was taken, in milliseconds."),
1449
+ trigger: SnapshotTriggerSchema.describe("What caused it. The automatic between-turn captures are a safety net and are not listed; they dissolve into the next visible point's differences."),
1450
+ label: z.string().optional().describe("What to call it. For one taken before a turn, that turn's prompt."),
796
1451
  });
797
- export const SnapshotsListSchema = z.object({ snapshots: z.array(SnapshotSchema) });
1452
+ export const SnapshotsListSchema = z.object({ snapshots: z.array(SnapshotSchema).describe("Every point you can go back to, newest first.") });
798
1453
  export const RewindTurnSchema = z.object({
799
- conversationId: z.string().min(1),
800
- index: z.number().int().nonnegative(),
1454
+ conversationId: z.string().min(1).describe("Which conversation to rewind."),
1455
+ index: z
1456
+ .number()
1457
+ .int()
1458
+ .nonnegative()
1459
+ .describe("Which message to go back to, counting from the start. It is also how many messages survive: rewinding to the first keeps none of them and puts the files back to before it ran."),
801
1460
  });
802
1461
  export const RewindResultSchema = z.object({
803
- snapshot: z.string().optional(),
804
- dropped: z.number().int().nonnegative(),
1462
+ snapshot: z
1463
+ .string()
1464
+ .optional()
1465
+ .describe("The saved point the files were put back to. Absent for a conversation working in its own copy, whose rewind moved a branch rather than the shared timeline."),
1466
+ dropped: z.number().int().nonnegative().describe("How many messages were removed."),
805
1467
  });
806
- export const SnapshotIdSchema = z.object({ id: z.string().min(1) });
1468
+ export const SnapshotIdSchema = z.object({ id: z.string().min(1).describe("Which saved point.") });
807
1469
  export const SnapshotChangeSchema = z.object({
808
- scope: z.string(),
809
- path: z.string(),
810
- status: z.enum(["added", "modified", "deleted", "type-changed"]),
1470
+ scope: z.string().describe("Which part of the workspace the path belongs to: the workspace root, or one of the repositories inside it."),
1471
+ path: z.string().describe("The path, relative to that scope."),
1472
+ status: z.enum(["added", "modified", "deleted", "type-changed"]).describe("What happened to it."),
1473
+ });
1474
+ export const SnapshotDiffSchema = z.object({
1475
+ changes: z.array(SnapshotChangeSchema).describe("Everything that differs between this saved point and the one before it."),
811
1476
  });
812
- export const SnapshotDiffSchema = z.object({ changes: z.array(SnapshotChangeSchema) });
813
1477
  export const SnapshotFileDiffQuerySchema = z.object({
814
- id: z.string().min(1),
815
- scope: z.string().min(1),
816
- path: z.string().min(1),
1478
+ id: z.string().min(1).describe("Which saved point."),
1479
+ scope: z.string().min(1).describe("Which part of the workspace the path belongs to."),
1480
+ path: z.string().min(1).describe("The file, relative to that scope."),
817
1481
  });
818
1482
  export const FileDiffSchema = z.object({
819
- before: z.string().optional(),
820
- after: z.string().optional(),
821
- binary: z.boolean().optional(),
822
- truncated: z.boolean().optional(),
1483
+ before: z.string().optional().describe("The whole file as it was. Absent when it did not exist yet."),
1484
+ after: z.string().optional().describe("The whole file as it is now. Absent when it was deleted."),
1485
+ binary: z.boolean().optional().describe("The file is not text, so neither side is sent."),
1486
+ truncated: z.boolean().optional().describe("The file was too large to send whole, so what you have is the start of it."),
1487
+ });
1488
+ export const WorkspaceScopeSchema = z.object({
1489
+ agent: ConversationIdSchema.optional().describe("Read a conversation's own private copy of the workspace rather than the shared tree. Leave it out for the shared tree. A conversation that is not working privately resolves back to the shared tree rather than failing, so a link need not know which mode it runs in."),
823
1490
  });
824
- export const WorkspaceScopeSchema = z.object({ agent: ConversationIdSchema.optional() });
825
1491
  export const WorkspaceLinkSchema = z.object({
826
- to: z.string(),
827
- state: z.enum(["broken", "outside"]).optional(),
1492
+ to: z
1493
+ .string()
1494
+ .describe("What the link says, verbatim, rather than where it ends up. That is what the person who made it wrote, and what they would edit."),
1495
+ state: z
1496
+ .enum(["broken", "outside"])
1497
+ .optional()
1498
+ .describe("Absent for an ordinary link. Broken means there is nothing at the other end, and it is listed anyway because a dangling link is worth seeing. Outside means it leads out of the workspace, so it is shown and refused."),
828
1499
  });
829
1500
  export const WorkspaceTreeEntrySchema = z.object({
830
- name: z.string(),
831
- path: z.string(),
832
- type: z.enum(["file", "dir"]),
833
- size: z.number().optional(),
834
- ignored: z.boolean().optional(),
835
- link: WorkspaceLinkSchema.optional(),
1501
+ name: z.string().describe("Just this entry's own name."),
1502
+ path: z.string().describe("Its full path from the workspace root, which feeds straight back into the file routes."),
1503
+ type: z.enum(["file", "dir"]).describe("What it is. For a link, what it points at, so a link to a folder opens like a folder."),
1504
+ size: z.number().optional().describe("Size in bytes, for a file."),
1505
+ ignored: z
1506
+ .boolean()
1507
+ .optional()
1508
+ .describe("Tooling ignores it: installed packages, git internals, anything the ignore rules exclude. Usually drawn greyed out."),
1509
+ link: WorkspaceLinkSchema.optional().describe("Present when this entry is a link."),
836
1510
  get children() {
837
- return z.array(WorkspaceTreeEntrySchema).optional();
1511
+ return z
1512
+ .array(WorkspaceTreeEntrySchema)
1513
+ .optional()
1514
+ .describe("What is inside a folder. Absent means it was not opened, either because it is ignored or because the walk ran out of budget above it, so ask for it separately. An empty list means it really is empty.");
838
1515
  },
839
1516
  });
840
1517
  export const WorkspaceTreeSchema = z.object({
841
- root: z.string(),
842
- tree: z.array(WorkspaceTreeEntrySchema),
843
- hidden: z.number(),
1518
+ root: z.string().describe("The path everything below is relative to."),
1519
+ tree: z.array(WorkspaceTreeEntrySchema).describe("The workspace, one entry per file and folder."),
1520
+ hidden: z.number().describe("How many entries at the top level were cut for size. Zero means the listing is complete."),
1521
+ });
1522
+ export const WorkspaceChildrenQuerySchema = WorkspaceScopeSchema.extend({
1523
+ path: z.string().min(1).describe("The folder to open, as a workspace path."),
844
1524
  });
845
- export const WorkspaceChildrenQuerySchema = WorkspaceScopeSchema.extend({ path: z.string().min(1) });
846
1525
  export const WorkspaceChildrenSchema = z.object({
847
- entries: z.array(WorkspaceTreeEntrySchema),
848
- hidden: z.number(),
1526
+ entries: z
1527
+ .array(WorkspaceTreeEntrySchema)
1528
+ .describe("What is directly inside it. Folders in here carry no contents of their own, so they open the same way."),
1529
+ hidden: z.number().describe("How many entries were cut for size. Zero means the listing is complete."),
1530
+ });
1531
+ export const WorkspaceFileQuerySchema = z.object({ path: z.string().min(1).describe("The file or folder, as a workspace path.") });
1532
+ export const WorkspaceMediaTicketQuerySchema = WorkspaceScopeSchema.extend({
1533
+ path: z.string().min(1).describe("The media file the ticket should cover."),
1534
+ });
1535
+ export const WorkspaceMediaTicketSchema = z.object({
1536
+ ticket: z.string().describe("Hand this to the streaming route in the query string. It buys exactly the one file it was minted for."),
1537
+ expiresAt: z.number().describe("When it stops working, in milliseconds, so a player can tell a dead ticket from a dead file."),
849
1538
  });
850
- export const WorkspaceFileQuerySchema = z.object({ path: z.string().min(1) });
851
- export const WorkspaceMediaTicketQuerySchema = WorkspaceScopeSchema.extend({ path: z.string().min(1) });
852
- export const WorkspaceMediaTicketSchema = z.object({ ticket: z.string(), expiresAt: z.number() });
853
1539
  export const WorkspaceFileReadQuerySchema = WorkspaceScopeSchema.extend({
854
- path: z.string().min(1),
855
- offset: z.coerce.number().int().optional(),
856
- limit: z.coerce.number().int().min(1).optional(),
1540
+ path: z.string().min(1).describe("The file to read, as a workspace path."),
1541
+ offset: z.coerce
1542
+ .number()
1543
+ .int()
1544
+ .optional()
1545
+ .describe("Which byte to start at. A negative number reads that many bytes from the end, which is how you follow a growing log without knowing its size first."),
1546
+ limit: z.coerce
1547
+ .number()
1548
+ .int()
1549
+ .min(1)
1550
+ .optional()
1551
+ .describe("How many bytes to read. Capped by the sandbox, so leaving it out or asking for too much gives you the cap rather than the whole file."),
857
1552
  });
858
1553
  export const WorkspaceFilePresentSchema = z.object({
859
- present: z.literal(true),
860
- path: z.string(),
861
- content: z.string(),
862
- size: z.number(),
863
- offset: z.number(),
864
- bytes: z.number(),
865
- shared: z.boolean(),
1554
+ present: z.literal(true).describe("There is something at that path."),
1555
+ path: z.string().describe("The path, as asked for."),
1556
+ content: z.string().describe("The bytes of the window you asked for, as text."),
1557
+ size: z.number().describe("How large the whole file is. Compare it with the window below to know whether there is more."),
1558
+ offset: z.number().describe("Which byte the window starts at."),
1559
+ bytes: z.number().describe("How many bytes the window holds."),
1560
+ shared: z
1561
+ .boolean()
1562
+ .describe("Which tree answered. True when no conversation was named, and also when one was but its own copy has no such file, which is the case a reader has to be told about rather than left to assume."),
1563
+ });
1564
+ export const WorkspaceFileAbsentSchema = z.object({
1565
+ present: z
1566
+ .literal(false)
1567
+ .describe("Nothing there. An answer, not a failure: reading a file that may not exist yet is the ordinary case for half the reads in this product."),
1568
+ path: z.string().describe("The path, as asked for."),
866
1569
  });
867
- export const WorkspaceFileAbsentSchema = z.object({ present: z.literal(false), path: z.string() });
868
1570
  export const WorkspaceFileSchema = z.discriminatedUnion("present", [WorkspaceFilePresentSchema, WorkspaceFileAbsentSchema]);
869
- export const WorkspaceResolveQuerySchema = WorkspaceScopeSchema.extend({ path: z.string().min(1).max(512) });
870
- export const WorkspaceResolveSchema = z.object({ path: z.string().optional() });
871
- export const WorkspaceDirSchema = z.object({ path: z.string().min(1) });
872
- export const WorkspaceMoveSchema = z.object({ from: z.string().min(1), to: z.string().min(1) });
1571
+ export const WorkspaceResolveQuerySchema = WorkspaceScopeSchema.extend({
1572
+ path: z
1573
+ .string()
1574
+ .min(1)
1575
+ .max(512)
1576
+ .describe("The reference as somebody wrote it. Often only the tail of the real path, which is why this is matched against the tree rather than read as-is."),
1577
+ });
1578
+ export const WorkspaceResolveSchema = z.object({
1579
+ path: z.string().optional().describe("The real path it means. Absent when nothing in the workspace ends that way."),
1580
+ });
1581
+ export const WorkspaceDirSchema = z.object({ path: z.string().min(1).describe("The folder to create. Missing folders above it are created too.") });
1582
+ export const WorkspaceMoveSchema = z.object({
1583
+ from: z.string().min(1).describe("What to move or copy, as a workspace path."),
1584
+ to: z.string().min(1).describe("Where it should end up. Changing only the last part is how you rename something."),
1585
+ });
873
1586
  export const WorkspaceBucketSchema = z.enum(["repositories", "documents", "media", "archives", "other"]);
874
1587
  export const WorkspaceClassificationSchema = z.object({
875
- classifications: z.array(z.object({ path: z.string(), bucket: WorkspaceBucketSchema, reason: z.string() })),
1588
+ classifications: z
1589
+ .array(z.object({
1590
+ path: z.string().describe("What was looked at."),
1591
+ bucket: WorkspaceBucketSchema.describe("Which bucket it was sorted into."),
1592
+ reason: z.string().describe("The signal that decided it, so the proposal can be argued with rather than trusted."),
1593
+ }))
1594
+ .describe("One entry per repository folder and loose file at the top of the workspace. A read-only proposal: nothing moves until you apply it."),
876
1595
  });
877
1596
  export const WorkspaceSearchQuerySchema = z.object({
878
- query: z.string().min(2).max(512),
879
- mode: z.enum(["q", "find", "files", "def", "refs", "sym", "ast"]).optional(),
880
- includeIgnored: z.stringbool().optional(),
881
- literal: z.stringbool().optional(),
882
- word: z.stringbool().optional(),
883
- caseSensitive: z.stringbool().optional(),
884
- include: z.string().max(512).optional(),
885
- limit: z.coerce.number().int().positive().optional(),
886
- after: z.string().optional(),
1597
+ query: z.string().min(2).max(512).describe("What to look for. Plain words, a pattern, a symbol name, or a question."),
1598
+ mode: z
1599
+ .enum(["q", "find", "files", "def", "refs", "sym", "ast"])
1600
+ .optional()
1601
+ .describe("Narrow the search to one kind: plain text, filenames, definitions, references, symbols, or code structure. Leave it out to blend them, which also answers a question asked in words."),
1602
+ includeIgnored: z.stringbool().optional().describe("Search inside installed packages and other ignored folders too."),
1603
+ literal: z.stringbool().optional().describe("Treat the query as fixed text rather than a pattern."),
1604
+ word: z.stringbool().optional().describe("Match whole words only."),
1605
+ caseSensitive: z.stringbool().optional().describe("Whether capitals matter. Off means they do not, rather than being guessed at from the query."),
1606
+ include: z
1607
+ .string()
1608
+ .max(512)
1609
+ .optional()
1610
+ .describe("Which files to ask, in the same grammar an editor's files-to-include box takes: comma-separated patterns, matched at any depth unless anchored, a leading exclamation mark excluding instead."),
1611
+ limit: z.coerce.number().int().positive().optional().describe("How many results to return."),
1612
+ after: z.string().optional().describe("Resume from the cursor a previous answer handed back."),
887
1613
  });
888
1614
  export const WorkspaceSearchTagSchema = z.object({
889
- kind: z.enum(["def", "text", "sem", "bm25", "rerank", "path", "import", "call", "type", "write", "fuzzy", "heuristic"]),
890
- score: z.number().optional(),
1615
+ kind: z
1616
+ .enum(["def", "text", "sem", "bm25", "rerank", "path", "import", "call", "type", "write", "fuzzy", "heuristic"])
1617
+ .describe("Why this line matched: the literal text, its meaning, the path, a definition, a call, and so on. Several kinds can agree on one line."),
1618
+ score: z.number().optional().describe("How strongly that reason applied."),
1619
+ });
1620
+ export const WorkspaceSearchSpanSchema = z.object({
1621
+ start: z.number().describe("First character of the match within the line."),
1622
+ end: z.number().describe("One past the last."),
891
1623
  });
892
- export const WorkspaceSearchSpanSchema = z.object({ start: z.number(), end: z.number() });
893
1624
  export const WorkspaceSearchHitSchema = z.object({
894
- line: z.number(),
895
- text: z.string(),
896
- spans: z.array(WorkspaceSearchSpanSchema),
897
- tags: z.array(WorkspaceSearchTagSchema),
898
- context: z.string().optional(),
1625
+ line: z.number().describe("Which line, counting from one."),
1626
+ text: z.string().describe("The line itself."),
1627
+ spans: z
1628
+ .array(WorkspaceSearchSpanSchema)
1629
+ .describe("Where in the line the matches are, so you can highlight without searching again. Empty when the whole line is the match rather than part of it."),
1630
+ tags: z.array(WorkspaceSearchTagSchema).describe("Why it matched."),
1631
+ context: z
1632
+ .string()
1633
+ .optional()
1634
+ .describe("What it sits inside: the function, the class, the heading. Often enough that you need not open the file."),
899
1635
  });
900
1636
  export const WorkspaceSearchGroupSchema = z.object({
901
- path: z.string(),
902
- score: z.number(),
903
- hits: z.array(WorkspaceSearchHitSchema),
904
- capped: z.boolean().optional(),
1637
+ path: z.string().describe("The file."),
1638
+ score: z.number().describe("How well it matched. Groups arrive best first, never in path order."),
1639
+ hits: z.array(WorkspaceSearchHitSchema).describe("The matching lines in it."),
1640
+ capped: z
1641
+ .boolean()
1642
+ .optional()
1643
+ .describe("This file had more matches than are kept per file, so the count is a floor. Say fifty-plus rather than fifty."),
905
1644
  });
906
1645
  export const WorkspaceSearchFreshnessSchema = z.object({
907
- state: z.enum(["fresh", "building", "stale"]),
908
- ageMs: z.number().optional(),
909
- progress: z.number().optional(),
910
- behind: z.number().optional(),
1646
+ state: z.enum(["fresh", "building", "stale"]).describe("Whether the index matches what is on disk, is still filling, or has fallen behind."),
1647
+ ageMs: z.number().optional().describe("How long since it last matched the disk, in milliseconds."),
1648
+ progress: z.number().optional().describe("How far through building it is, from zero to one."),
1649
+ behind: z
1650
+ .number()
1651
+ .optional()
1652
+ .describe("How many files it has not caught up with. Worth showing, because the word stale on its own reads as a warning about the answer, which it almost never is."),
911
1653
  });
912
1654
  export const WorkspaceSearchResultSchema = z.object({
913
- mode: z.string(),
914
- total: z.number(),
915
- files: z.number(),
916
- shown: z.number(),
917
- groups: z.array(WorkspaceSearchGroupSchema),
918
- freshness: WorkspaceSearchFreshnessSchema,
919
- truncated: z.boolean(),
920
- partial: z.boolean().optional(),
921
- cursor: z.string().optional(),
922
- hint: z.string().optional(),
923
- note: z.string().optional(),
924
- related: z.array(z.string()).optional(),
925
- candidates: z.array(z.string()).optional(),
926
- features: z.array(z.string()).optional(),
1655
+ mode: z.string().describe("Which kind of search actually ran, which matters when you let it choose."),
1656
+ total: z.number().describe("Matching lines across the whole workspace, not just this page."),
1657
+ files: z.number().describe("Files the query matched in total."),
1658
+ shown: z.number().describe("How many of those lines are on this page."),
1659
+ groups: z.array(WorkspaceSearchGroupSchema).describe("The results, grouped by file, best first."),
1660
+ freshness: WorkspaceSearchFreshnessSchema.describe("Whether the index behind the answer is up to date."),
1661
+ truncated: z.boolean().describe("This page is not all of it. Use the cursor."),
1662
+ partial: z
1663
+ .boolean()
1664
+ .optional()
1665
+ .describe("At least one file had more matches than are kept per file, so the total is a floor. Different from the page being truncated: a complete page can still count partially."),
1666
+ cursor: z.string().optional().describe("Pass this back as `after` to get the next page."),
1667
+ hint: z.string().optional().describe("A suggestion for getting a better answer out of this query."),
1668
+ note: z
1669
+ .string()
1670
+ .optional()
1671
+ .describe("What the engine did that you did not ask for: a pattern rerun as plain text because it was not valid, escapes rewritten, a language filter that matched nothing."),
1672
+ related: z.array(z.string()).optional().describe("Places next door to the best results: where each is defined, and whatever calls it most."),
1673
+ candidates: z
1674
+ .array(z.string())
1675
+ .optional()
1676
+ .describe("Ranked places that scored but did not make the page, best first. The answer often sits at rank five to thirteen, so this saves paging through to find out."),
1677
+ features: z.array(z.string()).optional().describe("Which stages of the search were switched off for this run. Absent means all of them ran."),
927
1678
  });
928
1679
  export const HEALTH_LIMIT = 20;
929
1680
  export const WorkspaceHealthQuerySchema = z.object({
930
- repo: z.string().min(1),
931
- since: z.string().max(16).optional(),
932
- limit: z.coerce.number().int().positive().max(200).optional(),
1681
+ repo: z.string().min(1).describe("Which repository, using the same ids the git routes take."),
1682
+ since: z
1683
+ .string()
1684
+ .max(16)
1685
+ .optional()
1686
+ .describe("How far back to count changes, written as a span such as 2d, 12h, 1w or 3m. Leave it out for all of history."),
1687
+ limit: z.coerce
1688
+ .number()
1689
+ .int()
1690
+ .positive()
1691
+ .max(200)
1692
+ .optional()
1693
+ .describe("How many files and modules to rank. A leaderboard rather than an inventory: past a screenful the ranking stops being the point."),
933
1694
  });
934
1695
  export const WorkspaceHotspotSchema = z.object({
935
1696
  path: z.string(),
@@ -942,78 +1703,127 @@ export const WorkspaceHotspotSchema = z.object({
942
1703
  });
943
1704
  export const WorkspaceKeyModuleSchema = z.object({ path: z.string(), exports: z.number() });
944
1705
  export const WorkspaceHealthSchema = z.object({
945
- repo: z.string(),
946
- totals: z.object({
947
- files: z.number(),
948
- symbols: z.number(),
949
- complexity: z.number(),
950
- hotspots: z.number(),
951
- }),
952
- hotspots: z.array(WorkspaceHotspotSchema),
953
- modules: z.array(WorkspaceKeyModuleSchema),
954
- freshness: WorkspaceSearchFreshnessSchema,
1706
+ repo: z.string().describe("Which repository this describes."),
1707
+ totals: z
1708
+ .object({
1709
+ files: z.number().describe("Files counted."),
1710
+ symbols: z.number().describe("Named things they export."),
1711
+ complexity: z.number().describe("Branch points across all of them added up."),
1712
+ hotspots: z.number().describe("How many files qualify as hotspots at all. The list below is capped; this is not."),
1713
+ })
1714
+ .describe("Counts anybody could recount in the files themselves. Deliberately no single maintainability grade: those cannot be checked and are not comparable between projects."),
1715
+ hotspots: z.array(WorkspaceHotspotSchema).describe("Files that change often and are complicated at the same time, worst first."),
1716
+ modules: z.array(WorkspaceKeyModuleSchema).describe("The parts of the codebase the rest of it leans on most."),
1717
+ freshness: WorkspaceSearchFreshnessSchema.describe("Whether the index these numbers were read from is up to date."),
955
1718
  });
956
1719
  export const ProjectSetupSchema = z.object({
957
- dir: z.string(),
958
- ecosystem: z.enum(["node", "python"]),
959
- manager: z.string(),
960
- command: z.string(),
961
- evidence: z.string(),
962
- state: z.enum(["ready", "installing", "needs-setup", "unsupported", "stale"]),
963
- missing: z.number().optional(),
964
- });
965
- export const WorkspaceSetupSchema = z.object({ projects: z.array(ProjectSetupSchema) });
966
- export const WorkspaceInstallSchema = z.object({ dirs: z.array(z.string().max(500)).min(1).max(50) });
967
- export const WorkspaceInstallResultSchema = z.object({ queued: z.array(z.string()) });
968
- export const ReposListSchema = z.object({ repos: z.array(z.string()) });
969
- export const CloneRepoSchema = z.object({ name: z.string().min(1), cloneUrl: z.string().min(1), branch: z.string().optional() });
970
- export const CloneResultSchema = z.object({ name: z.string(), path: z.string() });
971
- export const RepoSyncSchema = z.object({
972
- repo: z.string(),
973
- status: z.enum(["updated", "current", "dirty", "diverged", "no-remote", "skipped", "error"]),
974
- behind: z.number().optional(),
975
- ahead: z.number().optional(),
976
- head: z.string().optional(),
977
- message: z.string().optional(),
1720
+ dir: z.string().describe("Where the project is, relative to the workspace root. Empty means the root itself."),
1721
+ ecosystem: z.enum(["node", "python"]).describe("Which language's tooling it uses."),
1722
+ manager: z.string().describe("The tool that would do the installing."),
1723
+ command: z.string().describe("The exact command that would run."),
1724
+ evidence: z.string().describe("The file that decided all of the above, so the answer can be checked rather than trusted."),
1725
+ state: z
1726
+ .enum(["ready", "installing", "needs-setup", "unsupported", "stale"])
1727
+ .describe("Ready means its dependencies are really there. Stale means it was installed once and has since outgrown that, which is what an agent leaves behind when it adds a dependency without installing it. Unsupported means this sandbox has no such tool."),
1728
+ missing: z.number().optional().describe("How many declared dependencies cannot be found on disk. What separates never-installed from outgrown."),
1729
+ });
1730
+ export const WorkspaceSetupSchema = z.object({
1731
+ projects: z.array(ProjectSetupSchema).describe("Every project the sandbox found, and whether each is usable."),
1732
+ });
1733
+ export const WorkspaceInstallSchema = z.object({
1734
+ dirs: z
1735
+ .array(z.string().max(500))
1736
+ .min(1)
1737
+ .max(50)
1738
+ .describe("Which projects to install, by folder. Ones already ready, already installing, or with no tool to install them are skipped rather than refused."),
1739
+ });
1740
+ export const WorkspaceInstallResultSchema = z.object({
1741
+ queued: z.array(z.string()).describe("Which of them actually started, which is not necessarily what you asked for."),
978
1742
  });
979
- export const WorkspaceSyncSchema = z.object({ repos: z.array(RepoSyncSchema) });
1743
+ export const ReposListSchema = z.object({
1744
+ repos: z
1745
+ .array(z.string())
1746
+ .describe('Every repository\'s id, sorted. An id is its folder relative to the workspace root, and "root" is the workspace itself.'),
1747
+ });
1748
+ export const CloneRepoSchema = z.object({
1749
+ name: z.string().min(1).describe("What to call it in the workspace."),
1750
+ cloneUrl: z.string().min(1).describe("Where to clone it from."),
1751
+ branch: z.string().optional().describe("Which branch to check out. Leave it out for the repository's default."),
1752
+ });
1753
+ export const CloneResultSchema = z.object({
1754
+ name: z.string().describe("What it ended up called."),
1755
+ path: z.string().describe("Where it landed."),
1756
+ });
1757
+ export const RepoSyncSchema = z.object({
1758
+ repo: z.string().describe("Which repository."),
1759
+ status: z
1760
+ .enum(["updated", "current", "dirty", "diverged", "no-remote", "skipped", "error"])
1761
+ .describe("What happened to it. Dirty and diverged are why a repository was left alone: it had uncommitted work, or it had moved in a way that cannot be fast-forwarded."),
1762
+ behind: z.number().optional().describe("How many commits it was behind."),
1763
+ ahead: z.number().optional().describe("How many commits it was ahead."),
1764
+ head: z.string().optional().describe("The commit it ended up on."),
1765
+ message: z.string().optional().describe("What went wrong, when something did."),
1766
+ });
1767
+ export const WorkspaceSyncSchema = z.object({ repos: z.array(RepoSyncSchema).describe("One entry per repository, saying what happened to it.") });
980
1768
  export const AppInstanceInputSchema = z.object({
981
- template: z.string().min(1),
1769
+ template: z.string().min(1).describe("Which kind of app to scaffold, by its key in the template list."),
982
1770
  name: z
983
1771
  .string()
984
1772
  .min(1)
985
- .regex(/^[a-z][a-z0-9-]*$/),
1773
+ .regex(/^[a-z][a-z0-9-]*$/)
1774
+ .describe("What to call this one."),
986
1775
  });
987
1776
  export const AddAppsSchema = z.object({
988
- repo: z.string(),
989
- apps: z.array(AppInstanceInputSchema).min(1),
1777
+ repo: z.string().describe("Which repository to scaffold into."),
1778
+ apps: z.array(AppInstanceInputSchema).min(1).describe("The apps to add."),
990
1779
  });
991
1780
  export const RunTestsSchema = z.object({
992
- repo: z.string(),
993
- session: z.string(),
994
- dirs: z.array(z.string()).min(1),
1781
+ repo: z.string().describe("Which repository."),
1782
+ session: z.string().describe("What to call the terminal this runs in, so you can find it again."),
1783
+ dirs: z.array(z.string()).min(1).describe("Which projects to test, as folders relative to the repository. Empty targets the repository root."),
1784
+ });
1785
+ export const TemplateSummarySchema = z.object({
1786
+ key: z.string().describe("The id to name when scaffolding one."),
1787
+ label: z.string().describe("What to call it on screen."),
1788
+ description: z.string().describe("What you get."),
1789
+ });
1790
+ export const TemplatesListSchema = z.object({
1791
+ templates: z.array(TemplateSummarySchema).describe("The kinds of app the configured source repository knows how to scaffold."),
995
1792
  });
996
- export const TemplateSummarySchema = z.object({ key: z.string(), label: z.string(), description: z.string() });
997
- export const TemplatesListSchema = z.object({ templates: z.array(TemplateSummarySchema) });
998
1793
  export const RepoAppSchema = z.object({
999
- app: z.string(),
1000
- kind: z.string().optional(),
1001
- previewUrl: z.string().optional(),
1002
- running: z.boolean(),
1003
- healthy: z.boolean(),
1794
+ app: z.string().describe("The app's name, which is also its folder."),
1795
+ kind: z
1796
+ .string()
1797
+ .optional()
1798
+ .describe("What sort of app it is: the template it came from, or the framework worked out from its dependencies. Absent when it was found purely by having a dev script."),
1799
+ previewUrl: z.string().optional().describe("Where to open it. Absent when this sandbox has no outside address."),
1800
+ running: z.boolean().describe("Whether its dev server is up."),
1801
+ healthy: z.boolean().describe("Whether it is actually answering."),
1802
+ });
1803
+ export const AppsListSchema = z.object({ apps: z.array(RepoAppSchema).describe("The apps in this repository.") });
1804
+ export const WorkspacePackageSchema = z.object({
1805
+ name: z.string().describe("The name the package declares."),
1806
+ dir: z.string().describe("Where it lives, relative to the repository."),
1807
+ group: z.string().describe("The top-level folder it sits under, which is what a diagram colours by."),
1004
1808
  });
1005
- export const AppsListSchema = z.object({ apps: z.array(RepoAppSchema) });
1006
- export const WorkspacePackageSchema = z.object({ name: z.string(), dir: z.string(), group: z.string() });
1007
1809
  export const WorkspaceDepTypeSchema = z.enum(["prod", "dev", "peer"]);
1008
- export const WorkspaceDepEdgeSchema = z.object({ from: z.string(), to: z.string(), type: WorkspaceDepTypeSchema });
1009
- export const WorkspaceGraphSchema = z.object({ packages: z.array(WorkspacePackageSchema), edges: z.array(WorkspaceDepEdgeSchema) });
1010
- export const RepoAppsParamSchema = z.object({ repo: z.string() });
1810
+ export const WorkspaceDepEdgeSchema = z.object({
1811
+ from: z.string().describe("The package that depends."),
1812
+ to: z.string().describe("The package it depends on."),
1813
+ type: WorkspaceDepTypeSchema.describe("Which kind of dependency declared it."),
1814
+ });
1815
+ export const WorkspaceGraphSchema = z.object({
1816
+ packages: z.array(WorkspacePackageSchema).describe("Every package in the repository."),
1817
+ edges: z.array(WorkspaceDepEdgeSchema).describe("Which of them use which. Pure data: how to lay it out is yours to decide."),
1818
+ });
1819
+ export const RepoAppsParamSchema = z.object({ repo: z.string().describe("Which repository.") });
1011
1820
  export const AppParamSchema = z.object({
1012
- repo: z.string(),
1821
+ repo: z.string().describe("Which repository."),
1013
1822
  app: z
1014
1823
  .string()
1015
1824
  .min(1)
1016
- .regex(/^[a-z][a-z0-9-]*$/),
1825
+ .regex(/^[a-z][a-z0-9-]*$/)
1826
+ .describe("Which app inside it."),
1017
1827
  });
1018
1828
  export const InventoryProviderSchema = z.enum(["host", "cloudflare", "github", "gitlab", "stripe"]);
1019
1829
  export const ServiceKindSchema = z.enum(["signoz", "outline", "paperless", "openproject", "invoiceninja", "infisical"]);
@@ -1024,25 +1834,25 @@ const inventoryName = z
1024
1834
  .max(60)
1025
1835
  .regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/);
1026
1836
  export const BackendEntrySchema = z.object({
1027
- kind: z.literal("backend"),
1028
- provider: InventoryProviderSchema,
1029
- name: z.string(),
1030
- values: InventoryValuesSchema,
1837
+ kind: z.literal("backend").describe("Something you already have: a machine, an account with a hosting provider."),
1838
+ provider: InventoryProviderSchema.describe("Which provider it is with."),
1839
+ name: z.string().describe("What to call it, which is also how everything else refers to it."),
1840
+ values: InventoryValuesSchema.describe("Its settings. Anything secret is stored separately and referred to here, never written in."),
1031
1841
  });
1032
1842
  export const ServiceEntrySchema = z.object({
1033
- kind: z.literal("service"),
1034
- service: ServiceKindSchema,
1035
- name: z.string(),
1036
- values: InventoryValuesSchema,
1037
- on: z.string(),
1038
- expose: z.string(),
1843
+ kind: z.literal("service").describe("Something you want provisioned."),
1844
+ service: ServiceKindSchema.describe("Which service."),
1845
+ name: z.string().describe("What to call it."),
1846
+ values: InventoryValuesSchema.describe("Its settings."),
1847
+ on: z.string().describe("Which of your machines to put it on."),
1848
+ expose: z.string().describe("How it should be reachable."),
1039
1849
  });
1040
1850
  export const AppEntrySchema = z.object({
1041
- kind: z.literal("app"),
1042
- name: z.string(),
1043
- values: InventoryValuesSchema,
1044
- on: z.string(),
1045
- expose: z.string(),
1851
+ kind: z.literal("app").describe("An app of your own, built from source and deployed."),
1852
+ name: z.string().describe("What to call it."),
1853
+ values: InventoryValuesSchema.describe("Its settings, including the address it should answer on."),
1854
+ on: z.string().describe("Which of your machines to put it on."),
1855
+ expose: z.string().describe("How it should be reachable."),
1046
1856
  });
1047
1857
  export const InventoryEntrySchema = z.discriminatedUnion("kind", [BackendEntrySchema, ServiceEntrySchema, AppEntrySchema]);
1048
1858
  export const AddInventoryInputSchema = z.discriminatedUnion("kind", [
@@ -1050,8 +1860,10 @@ export const AddInventoryInputSchema = z.discriminatedUnion("kind", [
1050
1860
  ServiceEntrySchema.extend({ name: inventoryName }),
1051
1861
  AppEntrySchema.extend({ name: inventoryName }),
1052
1862
  ]);
1053
- export const InventoryNameParamSchema = z.object({ name: z.string() });
1054
- export const InventoryListSchema = z.object({ entries: z.array(InventoryEntrySchema) });
1863
+ export const InventoryNameParamSchema = z.object({ name: z.string().describe("Which entry, by name.") });
1864
+ export const InventoryListSchema = z.object({
1865
+ entries: z.array(InventoryEntrySchema).describe("Everything declared: what you have, and what you want provisioned."),
1866
+ });
1055
1867
  export const EnrollHostInputSchema = z.object({
1056
1868
  name: inventoryName,
1057
1869
  user: z.string().min(1),
@@ -1084,51 +1896,74 @@ export const CapabilityKindSchema = z.enum([
1084
1896
  "wallet",
1085
1897
  ]);
1086
1898
  export const CapabilityStateSchema = z.enum(["active", "pending", "error", "inactive"]);
1087
- export const McpConfigSchema = z.object({ url: z.url(), token: z.string().optional() });
1899
+ export const McpConfigSchema = z.object({
1900
+ url: z.url().describe("Where the tool server answers."),
1901
+ token: z.string().optional().describe("The credential it needs, if any. Stored, never echoed back."),
1902
+ });
1088
1903
  export const ServiceConfigSchema = z.object({
1089
- service: ServiceKindSchema,
1090
- domain: z.string().min(1),
1091
- on: z.string().min(1),
1092
- expose: z.string().min(1),
1904
+ service: ServiceKindSchema.describe("Which service to provision."),
1905
+ domain: z.string().min(1).describe("The address it should answer on."),
1906
+ on: z.string().min(1).describe("Which machine to put it on."),
1907
+ expose: z.string().min(1).describe("How it should be reachable."),
1908
+ });
1909
+ export const IntegrationConfigSchema = z.object({
1910
+ provider: z.literal("stripe").describe("Which outside service's credential to make available to deployed apps."),
1093
1911
  });
1094
- export const IntegrationConfigSchema = z.object({ provider: z.literal("stripe") });
1095
- export const CliConfigSchema = z.object({ provider: z.string().min(1) }).catchall(z.string());
1912
+ export const CliConfigSchema = z
1913
+ .object({
1914
+ provider: z
1915
+ .string()
1916
+ .min(1)
1917
+ .describe("Which tool to give the agent. The rest of the fields are whatever that tool's own card declares it needs, and are checked against it when you connect."),
1918
+ })
1919
+ .catchall(z.string());
1096
1920
  export const PluginConfigSchema = z.object({
1097
- url: z.url(),
1098
- ref: z.string().min(1).optional(),
1921
+ url: z.url().describe("The repository to take the plugin from."),
1922
+ ref: z.string().min(1).optional().describe("A branch, tag or commit to pin to. Leave it out to follow the default branch."),
1099
1923
  path: z
1100
1924
  .string()
1101
1925
  .min(1)
1102
1926
  .refine((value) => !value.split("/").includes(".."), { message: "path must stay inside the checkout" })
1103
- .optional(),
1104
- token: z.string().min(1).optional(),
1927
+ .optional()
1928
+ .describe("Where inside the repository the plugin lives, for one that sits in a larger checkout."),
1929
+ token: z.string().min(1).optional().describe("A credential for a private repository. Stored, never echoed back."),
1105
1930
  });
1106
1931
  export const ExtensionConfigSchema = z.object({
1107
- url: z.url(),
1108
- ref: z.string().regex(/^[0-9a-f]{40}$/, "ref must be a full 40-character commit sha"),
1932
+ url: z.url().describe("The repository to take the extension from."),
1933
+ ref: z
1934
+ .string()
1935
+ .regex(/^[0-9a-f]{40}$/, "ref must be a full 40-character commit sha")
1936
+ .describe("The exact commit to install, in full. Required rather than optional because extension code runs with your browser's trust: the owner approves precisely the code that runs, and an update is a deliberate re-install at a new commit."),
1109
1937
  path: z
1110
1938
  .string()
1111
1939
  .min(1)
1112
1940
  .refine((value) => !value.split("/").includes(".."), { message: "path must stay inside the checkout" })
1113
- .optional(),
1114
- token: z.string().min(1).optional(),
1115
- tier: z.enum(["free", "premium"]).optional(),
1116
- registry: z.url().optional(),
1941
+ .optional()
1942
+ .describe("Where inside the repository the extension lives, for one that sits in a larger checkout."),
1943
+ token: z.string().min(1).optional().describe("A credential for a private repository. Stored, never echoed back."),
1944
+ tier: z
1945
+ .enum(["free", "premium"])
1946
+ .optional()
1947
+ .describe("Whether installing this donates credits to its publisher. Absent means free, which donates nothing and asks for nothing. Taken from the listing rather than checked against it, which is the honesty an open-source posture accepts."),
1948
+ registry: z
1949
+ .url()
1950
+ .optional()
1951
+ .describe("Which registry this install came from, which is what update checks and security advisories are read against. Absent falls back to the official one."),
1117
1952
  });
1118
1953
  export const SshConfigSchema = z.discriminatedUnion("auth", [
1119
1954
  z.object({
1120
- auth: z.literal("key"),
1121
- host: z.string().min(1),
1122
- port: z.coerce.number().default(22),
1123
- user: z.string().min(1),
1124
- privateKey: z.string().min(1),
1955
+ auth: z.literal("key").describe("Sign in with a key."),
1956
+ host: z.string().min(1).describe("The machine's address."),
1957
+ port: z.coerce.number().default(22).describe("Which port it listens on."),
1958
+ user: z.string().min(1).describe("Which user to connect as."),
1959
+ privateKey: z.string().min(1).describe("The private key, whole. Stored with tight permissions and never echoed back."),
1125
1960
  }),
1126
1961
  z.object({
1127
- auth: z.literal("password"),
1128
- host: z.string().min(1),
1129
- port: z.coerce.number().default(22),
1130
- user: z.string().min(1),
1131
- password: z.string().min(1),
1962
+ auth: z.literal("password").describe("Sign in with a password."),
1963
+ host: z.string().min(1).describe("The machine's address."),
1964
+ port: z.coerce.number().default(22).describe("Which port it listens on."),
1965
+ user: z.string().min(1).describe("Which user to connect as."),
1966
+ password: z.string().min(1).describe("The password. Stored, never echoed back."),
1132
1967
  }),
1133
1968
  ]);
1134
1969
  export const VpnProviderSchema = z.enum(["wireguard", "fortinet", "ipsec"]);
@@ -1286,10 +2121,19 @@ export const CapabilitySchema = z.discriminatedUnion("kind", [
1286
2121
  z.object({ id: entryId, kind: z.literal("wallet"), config: WalletConfigSchema }),
1287
2122
  ]);
1288
2123
  export const PersonaPowersSchema = z.object({
1289
- files: z.enum(["none", "read", "write"]).default("write"),
1290
- shell: z.boolean().default(true),
1291
- code: z.boolean().default(true),
1292
- web: z.boolean().default(true),
2124
+ files: z
2125
+ .enum(["none", "read", "write"])
2126
+ .default("write")
2127
+ .describe("What it may do with files: nothing, look and search, or also create and change."),
2128
+ shell: z
2129
+ .boolean()
2130
+ .default(true)
2131
+ .describe("Whether it may run commands, and with them the terminals, the test runs and every tool on the image. The switch the strength of the others depends on."),
2132
+ code: z
2133
+ .boolean()
2134
+ .default(true)
2135
+ .describe("Whether it may write and run a script rather than a command line. Its fence is real where the shell's is not: reads and writes follow the files answer, and it can start no other program unless commands are allowed too. The one stated gap is that the fence cannot cut the network."),
2136
+ web: z.boolean().default(true).describe("Whether it may fetch a page or run a search."),
1293
2137
  browser: z.boolean().default(true),
1294
2138
  delegate: z.boolean().default(true),
1295
2139
  sandbox: z.boolean().default(true),
@@ -1298,16 +2142,23 @@ export const PersonaPowersSchema = z.object({
1298
2142
  mcp: z.array(entryId).max(50).optional(),
1299
2143
  });
1300
2144
  export const PersonaWorkspaceSchema = z.object({
1301
- startIn: z.string().max(200).optional(),
1302
- folders: z.array(z.string().min(1)).max(50).optional(),
2145
+ startIn: z.string().max(200).optional().describe("Which folder a conversation opens in."),
2146
+ folders: z.array(z.string().min(1)).max(50).optional().describe("Which folders it may touch at all. Absent means the whole workspace."),
1303
2147
  });
1304
2148
  export const PersonaSchema = z.object({
1305
- id: entryId,
1306
- label: z.string().max(60).optional(),
1307
- capabilities: z.array(entryId).max(50),
1308
- repos: z.array(z.string().min(1)).max(50).optional(),
1309
- powers: PersonaPowersSchema.optional(),
1310
- workspace: PersonaWorkspaceSchema.optional(),
2149
+ id: entryId.describe("The persona's id."),
2150
+ label: z.string().max(60).optional().describe("What to call it on screen. Absent falls back to the id, which somebody chose anyway."),
2151
+ capabilities: z
2152
+ .array(entryId)
2153
+ .max(50)
2154
+ .describe("Which connected accounts are its hands. Named individually rather than by site, because two accounts on one site is the whole problem this solves. Naming one that is not connected yet is not an error: it is a card describing an account this sandbox has still to sign into."),
2155
+ repos: z
2156
+ .array(z.string().min(1))
2157
+ .max(50)
2158
+ .optional()
2159
+ .describe("Which repositories prefer this persona, so a conversation opened on one starts with the right choice already made. A preference rather than a fence."),
2160
+ powers: PersonaPowersSchema.optional().describe("What a conversation wearing it may do. Absent means the full toolbox, so a card written before this existed behaves exactly as it did."),
2161
+ workspace: PersonaWorkspaceSchema.optional().describe("Where it works. Absent means the whole workspace."),
1311
2162
  systemPromptMode: SystemPromptModeSchema.optional(),
1312
2163
  });
1313
2164
  export const FRONT_DESK_PERSONA = "front-desk";
@@ -1336,49 +2187,82 @@ export const personaBounds = (persona) => {
1336
2187
  ].filter(Boolean).length;
1337
2188
  return limits === 0 ? "Full powers" : `${limits} limit${limits === 1 ? "" : "s"}`;
1338
2189
  };
1339
- export const PersonaIdParamSchema = z.object({ id: entryId });
2190
+ export const PersonaIdParamSchema = z.object({ id: entryId.describe("Which persona.") });
1340
2191
  export const PersonasListSchema = z.object({
1341
- personas: z.array(PersonaSchema),
1342
- connected: z.array(z.string()),
2192
+ personas: z.array(PersonaSchema).describe("The characters an agent can wear."),
2193
+ connected: z
2194
+ .array(z.string())
2195
+ .describe("Which accounts are actually connected right now, so a persona naming one that has since been disconnected can be shown as broken rather than as working."),
1343
2196
  });
1344
2197
  export const PersonaKitSchema = z.object({
1345
- prompt: z.string(),
1346
- skills: z.array(z.object({ name: z.string(), description: z.string() })),
2198
+ prompt: z
2199
+ .string()
2200
+ .describe("What this persona is told, on top of everything else. Empty means it simply follows the sandbox's own instructions."),
2201
+ skills: z
2202
+ .array(z.object({
2203
+ name: z.string().describe("The skill's name."),
2204
+ description: z.string().describe("What it is for."),
2205
+ }))
2206
+ .describe("Skills only this persona's conversations can reach. A different question from what the agent knows generally, with a different answer."),
2207
+ });
2208
+ export const PersonaPromptSchema = PersonaIdParamSchema.extend({
2209
+ prompt: z
2210
+ .string()
2211
+ .max(20000)
2212
+ .describe("What to tell this persona. Sending an empty one removes it entirely rather than storing a blank, so the persona falls back to the sandbox's own instructions."),
1347
2213
  });
1348
- export const PersonaPromptSchema = PersonaIdParamSchema.extend({ prompt: z.string().max(20000) });
1349
2214
  export const PersonaSkillSchema = PersonaIdParamSchema.extend(SkillDraftSchema.shape);
1350
- export const PersonaSkillNameSchema = PersonaIdParamSchema.extend({ name: SkillNameSchema });
1351
- export const PersonaSkillBodySchema = z.object({ name: z.string(), description: z.string(), body: z.string() });
2215
+ export const PersonaSkillNameSchema = PersonaIdParamSchema.extend({ name: SkillNameSchema.describe("Which skill.") });
2216
+ export const PersonaSkillBodySchema = z.object({
2217
+ name: z.string().describe("The skill's name."),
2218
+ description: z.string().describe("What it is for."),
2219
+ body: z.string().describe("The skill itself, in full."),
2220
+ });
1352
2221
  export const CapabilityStatusSchema = z.object({
1353
- state: CapabilityStateSchema,
1354
- detail: z.string().optional(),
1355
- code: z.string().optional(),
2222
+ state: CapabilityStateSchema.describe("Whether it is live, still coming up, broken, or switched off."),
2223
+ detail: z.string().optional().describe("What is wrong, in words a person can act on."),
2224
+ code: z.string().optional().describe("A short marker for that reason, for anything deciding what to do about it."),
1356
2225
  });
1357
2226
  export const CapabilitySummarySchema = z.object({
1358
- id: z.string(),
1359
- kind: CapabilityKindSchema,
1360
- status: CapabilityStatusSchema,
1361
- config: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])),
1362
- secrets: z.array(z.string()).default([]),
2227
+ id: z.string().describe("The connection's id."),
2228
+ kind: CapabilityKindSchema.describe("What sort of thing it is."),
2229
+ status: CapabilityStatusSchema.describe("Whether it is working."),
2230
+ config: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).describe("Its settings, minus anything secret."),
2231
+ secrets: z
2232
+ .array(z.string())
2233
+ .default([])
2234
+ .describe("Which credentials it holds, by name. The values are on one route only, and it is not this one."),
1363
2235
  });
1364
2236
  export const CapabilityRecommendationSchema = z.object({
1365
- card: z.string(),
1366
- evidence: z.string(),
1367
- reason: z.string(),
1368
- prefill: z.record(z.string(), z.string()),
2237
+ card: z.string().describe("Which connection is being suggested."),
2238
+ evidence: z
2239
+ .string()
2240
+ .describe("What was seen that prompted it: a file, a remote, printed verbatim so the claim can be checked rather than believed."),
2241
+ reason: z.string().describe("The same claim in words, without repeating the evidence into it."),
2242
+ prefill: z
2243
+ .record(z.string(), z.string())
2244
+ .describe("Settings the scan could read, to fill the form so you supply only the credential. Never a secret, even when one is sitting in a checked-in file: the suggestion points at such a file, it does not absorb what is in it."),
1369
2245
  });
1370
2246
  export const CapabilitiesListSchema = z.object({
1371
- capabilities: z.array(CapabilitySummarySchema),
1372
- recommendations: z.array(CapabilityRecommendationSchema).default([]),
2247
+ capabilities: z.array(CapabilitySummarySchema).describe("What this sandbox is connected to."),
2248
+ recommendations: z
2249
+ .array(CapabilityRecommendationSchema)
2250
+ .default([])
2251
+ .describe("Things worth connecting, worked out from what is actually in the workspace rather than from anything you configured. Re-derived on every read, so one whose evidence has moved simply stops being suggested."),
1373
2252
  });
1374
- export const CapabilityIdParamSchema = z.object({ id: z.string() });
2253
+ export const CapabilityIdParamSchema = z.object({ id: z.string().describe("Which connection.") });
1375
2254
  export const CapabilityConnectionSchema = z.object({
1376
- id: z.string(),
1377
- kind: z.string(),
1378
- config: z.record(z.string(), z.string()),
2255
+ id: z.string().describe("The connection's id."),
2256
+ kind: z.string().describe("What sort of thing it is."),
2257
+ config: z
2258
+ .record(z.string(), z.string())
2259
+ .describe("Its settings exactly as stored, credentials included. The field names are its own kind's, which the caller already knows."),
2260
+ });
2261
+ export const CapabilityCardParamSchema = z.object({ card: z.string().describe("Which suggestion to stop making.") });
2262
+ export const CapabilitySecretInputSchema = z.object({
2263
+ id: z.string().describe("Which connection."),
2264
+ value: z.string().min(1).describe("The new credential. Its other settings are left alone."),
1379
2265
  });
1380
- export const CapabilityCardParamSchema = z.object({ card: z.string() });
1381
- export const CapabilitySecretInputSchema = z.object({ id: z.string(), value: z.string().min(1) });
1382
2266
  export const CapabilityRenameSchema = z.object({
1383
2267
  id: z.string(),
1384
2268
  to: z
@@ -1387,8 +2271,13 @@ export const CapabilityRenameSchema = z.object({
1387
2271
  .max(60)
1388
2272
  .regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/),
1389
2273
  });
1390
- export const CapabilityLoginSchema = z.object({ session: z.string() });
1391
- export const CapabilityOtpSchema = z.object({ code: z.string(), secondsRemaining: z.number() });
2274
+ export const CapabilityLoginSchema = z.object({ session: z.string().describe("The terminal the sign-in is happening in. Attach to it to type.") });
2275
+ export const CapabilityOtpSchema = z.object({
2276
+ code: z.string().describe("The code."),
2277
+ secondsRemaining: z
2278
+ .number()
2279
+ .describe("How long it lasts. Its expiring is what makes handing one to an agent safe, since the seed behind it is never revealed."),
2280
+ });
1392
2281
  export const HostFactsSchema = z.object({
1393
2282
  os: z.string(),
1394
2283
  arch: z.string(),
@@ -1413,37 +2302,66 @@ export const VpnStateSchema = z.enum([
1413
2302
  "failed",
1414
2303
  ]);
1415
2304
  export const VpnLinkSchema = z.object({
1416
- id: z.string(),
1417
- provider: VpnProviderSchema,
1418
- state: VpnStateSchema,
1419
- gateway: z.string().optional(),
1420
- interface: z.string().optional(),
1421
- address: z.string().optional(),
1422
- routes: z.array(z.string()).default([]),
1423
- dns: z.array(z.string()).default([]),
1424
- since: z.number().optional(),
1425
- autoConnect: z.boolean(),
1426
- detail: z.string().optional(),
2305
+ id: z.string().describe("Which tunnel."),
2306
+ provider: VpnProviderSchema.describe("What kind of tunnel it is."),
2307
+ state: VpnStateSchema.describe("Whether it is up, dialling, resting, failed, or not installable yet because its client needs a rebuild to arrive."),
2308
+ gateway: z.string().optional().describe("What it dials. For display only, and never a credential."),
2309
+ interface: z.string().optional().describe("The network interface carrying it, once one exists."),
2310
+ address: z
2311
+ .string()
2312
+ .optional()
2313
+ .describe("The address the far end gave this sandbox, which is the single most useful answer to whether you are on the VPN."),
2314
+ routes: z
2315
+ .array(z.string())
2316
+ .default([])
2317
+ .describe("What goes through it. Everything, when the range covers the whole internet. Empty until it is up."),
2318
+ dns: z.array(z.string()).default([]).describe("Name servers it pushed, when it pushed any."),
2319
+ since: z.number().optional().describe("When it came up, in milliseconds. Absent unless it is."),
2320
+ autoConnect: z.boolean().describe("Whether it dials itself when the sandbox starts."),
2321
+ detail: z.string().optional().describe("Why it failed, or a note about a healthy one. Never a credential."),
2322
+ });
2323
+ export const VpnListSchema = z.object({
2324
+ links: z
2325
+ .array(VpnLinkSchema)
2326
+ .describe("Every configured tunnel with its live state, read back from the operating system each time rather than remembered."),
2327
+ });
2328
+ export const VpnConnectInputSchema = z.object({
2329
+ id: z.string().describe("Which tunnel to dial."),
2330
+ otp: z
2331
+ .string()
2332
+ .min(1)
2333
+ .optional()
2334
+ .describe("A one-time code, where the gateway wants one. Supplied per dial and never stored; without it such a gateway refuses and says so."),
2335
+ });
2336
+ export const VpnIdParamSchema = z.object({ id: z.string().describe("Which tunnel.") });
2337
+ export const ForticlientImportInputSchema = z.object({
2338
+ xml: z.string().min(1).describe("The exported configuration file, whole. Nothing is stored: it is read and thrown away."),
1427
2339
  });
1428
- export const VpnListSchema = z.object({ links: z.array(VpnLinkSchema) });
1429
- export const VpnConnectInputSchema = z.object({ id: z.string(), otp: z.string().min(1).optional() });
1430
- export const VpnIdParamSchema = z.object({ id: z.string() });
1431
- export const ForticlientImportInputSchema = z.object({ xml: z.string().min(1) });
1432
2340
  export const ForticlientConnectionSchema = z.object({
1433
- id: z.string(),
1434
- label: z.string(),
1435
- provider: VpnProviderSchema,
1436
- server: z.string(),
1437
- port: z.number(),
1438
- username: z.string().optional(),
1439
- description: z.string().optional(),
1440
- localId: z.string().optional(),
1441
- aggressive: z.boolean().optional(),
1442
- pfs: z.boolean().optional(),
1443
- dhGroup: z.string().optional(),
1444
- needs: z.array(z.string()),
1445
- });
1446
- export const ForticlientImportSchema = z.object({ connections: z.array(ForticlientConnectionSchema) });
2341
+ id: z.string().describe("The id it would be added under."),
2342
+ label: z.string().describe("Its name as the file has it, so somebody recognises the connection they are picking."),
2343
+ provider: VpnProviderSchema.describe("What kind of tunnel it is."),
2344
+ server: z.string().describe("Where it dials."),
2345
+ port: z.number().describe("On which port."),
2346
+ username: z
2347
+ .string()
2348
+ .optional()
2349
+ .describe("The username, but only when the file stored it in the clear. An encrypted one is dropped rather than guessed at."),
2350
+ description: z.string().optional().describe("Whatever the file said about it."),
2351
+ localId: z.string().optional().describe("An identity some tunnel types need, when the file stored it readably."),
2352
+ aggressive: z.boolean().optional().describe("Which negotiation mode it used."),
2353
+ pfs: z.boolean().optional().describe("Whether it asked for forward secrecy."),
2354
+ dhGroup: z
2355
+ .string()
2356
+ .optional()
2357
+ .describe("Which key-exchange group it used. Together with the setting above, this is what decides whether the connection can complete at all."),
2358
+ needs: z
2359
+ .array(z.string())
2360
+ .describe("What you still have to type in before it can dial. Always at least the password, because the export wraps credentials in encryption that cannot be undone here."),
2361
+ });
2362
+ export const ForticlientImportSchema = z.object({
2363
+ connections: z.array(ForticlientConnectionSchema).describe("The connections found in the file, ready to be added one at a time."),
2364
+ });
1447
2365
  export const ExitStateSchema = z.enum([
1448
2366
  "up",
1449
2367
  "starting",
@@ -1452,35 +2370,50 @@ export const ExitStateSchema = z.enum([
1452
2370
  "failed",
1453
2371
  ]);
1454
2372
  export const ExitObservationSchema = z.object({
1455
- ip: z.string(),
1456
- country: z.string().optional(),
1457
- countryName: z.string().optional(),
2373
+ ip: z.string().describe("The address the world sees, looked up through the exit's own proxy rather than assumed."),
2374
+ country: z
2375
+ .string()
2376
+ .optional()
2377
+ .describe("Which country that address is in. Absent when the lookup gave an address and no country, in which case a switch is judged on the address having changed instead."),
2378
+ countryName: z.string().optional().describe("That country's name, spelled out."),
1458
2379
  });
1459
2380
  export const ExitPointSchema = z.object({
1460
- country: z.string(),
1461
- countryName: z.string(),
1462
- servers: z.number(),
1463
- share: z.number().optional(),
2381
+ country: z.string().describe("The country's code."),
2382
+ countryName: z.string().describe("Its name, spelled out."),
2383
+ servers: z.number().describe("How many servers this provider has there."),
2384
+ share: z
2385
+ .number()
2386
+ .optional()
2387
+ .describe("How much of the provider's actual capacity is there, from zero to one. This is what a list should be sorted by: a third of the countries on offer are one overloaded machine behind a flag, and a count of servers would rank them first."),
1464
2388
  });
1465
2389
  export const ExitCountriesSchema = z.object({
1466
- countries: z.array(ExitPointSchema),
1467
- live: z.boolean(),
2390
+ countries: z.array(ExitPointSchema).describe("Where this exit can put you, best-supplied first."),
2391
+ live: z
2392
+ .boolean()
2393
+ .describe("Whether the provider answered, or this came from a built-in list. Said out loud rather than presenting an old list as current."),
1468
2394
  });
1469
2395
  export const ExitLinkSchema = z.object({
1470
- id: z.string(),
1471
- provider: ExitProviderSchema,
1472
- state: ExitStateSchema,
1473
- proxy: z.string(),
1474
- country: z.string().optional(),
1475
- observedCountry: z.string().optional(),
1476
- ip: z.string().optional(),
1477
- checkedAt: z.number().optional(),
1478
- interface: z.string().optional(),
1479
- since: z.number().optional(),
1480
- autoStart: z.boolean(),
1481
- detail: z.string().optional(),
2396
+ id: z.string().describe("Which exit."),
2397
+ provider: ExitProviderSchema.describe("What it runs on."),
2398
+ state: ExitStateSchema.describe("Whether it is carrying traffic, coming up, resting, failed, or not installable yet because its client needs a rebuild to arrive."),
2399
+ proxy: z
2400
+ .string()
2401
+ .describe("Where to point traffic that should go through it. Fixed per exit and unchanged by a country switch, which is what lets a long job move country halfway through without reconfiguring anything."),
2402
+ country: z.string().optional().describe("Where it was asked to come out. Absent means the provider chose."),
2403
+ observedCountry: z
2404
+ .string()
2405
+ .optional()
2406
+ .describe("Where it actually comes out, as last checked. Kept separate from what was asked for, because those two disagreeing is the most useful fault signal this whole feature has."),
2407
+ ip: z.string().optional().describe("The address behind that observation."),
2408
+ checkedAt: z.number().optional().describe("When that was checked, in milliseconds, so an old reading can be shown as old."),
2409
+ interface: z.string().optional().describe("The network interface, for the kinds that have one."),
2410
+ since: z.number().optional().describe("When it came up, in milliseconds."),
2411
+ autoStart: z.boolean().describe("Whether it starts itself when the sandbox does."),
2412
+ detail: z.string().optional().describe("Why it failed, or a note about a healthy one."),
2413
+ });
2414
+ export const ExitListSchema = z.object({
2415
+ links: z.array(ExitLinkSchema).describe("Every configured exit, with where it was asked to come out and where it actually does."),
1482
2416
  });
1483
- export const ExitListSchema = z.object({ links: z.array(ExitLinkSchema) });
1484
2417
  export const TOR_EXIT_COUNTRIES = [
1485
2418
  { country: "NL", countryName: "Netherlands", servers: 607, share: 0.304 },
1486
2419
  { country: "DE", countryName: "Germany", servers: 415, share: 0.242 },
@@ -1514,10 +2447,25 @@ export const VPNGATE_EXIT_COUNTRIES = [
1514
2447
  { country: "CN", countryName: "China", servers: 1, share: 0.01 },
1515
2448
  { country: "BY", countryName: "Belarus", servers: 1, share: 0.01 },
1516
2449
  ];
1517
- export const ExitIdParamSchema = z.object({ id: z.string() });
1518
- export const ExitUseInputSchema = z.object({ id: z.string(), country: CountryCodeSchema.optional() });
1519
- export const MarketplaceRequestSchema = z.object({ url: z.url(), token: z.string().min(1).optional() });
1520
- export const MarketplaceSchema = z.object({ name: z.string(), plugins: z.array(RegistryEntrySchema) });
2450
+ export const ExitIdParamSchema = z.object({ id: z.string().describe("Which exit.") });
2451
+ export const ExitUseInputSchema = z.object({
2452
+ id: z.string().describe("Which exit."),
2453
+ country: CountryCodeSchema.optional().describe("Where to come out. Leaving it out means letting the provider choose, so clearing a country is something you can actually say rather than only setting one."),
2454
+ });
2455
+ export const MarketplaceRequestSchema = z.object({
2456
+ url: z.url().describe("The registry to read."),
2457
+ token: z
2458
+ .string()
2459
+ .min(1)
2460
+ .optional()
2461
+ .describe("A credential for a private one. Sent as a body rather than in the address, so it never lands in a log."),
2462
+ });
2463
+ export const MarketplaceSchema = z.object({
2464
+ name: z.string().describe("What the registry calls itself."),
2465
+ plugins: z
2466
+ .array(RegistryEntrySchema)
2467
+ .describe("What it lists, each with the curated decision, the resolved pointer and what a scan found upstream."),
2468
+ });
1521
2469
  const extensionId = z
1522
2470
  .string()
1523
2471
  .min(1)
@@ -1528,100 +2476,189 @@ export const ExtensionUpdatePolicySchema = z.object({
1528
2476
  advisories: z.enum(["auto-disable", "notify"]),
1529
2477
  });
1530
2478
  export const ExtensionUpdateSchema = z.object({
1531
- ref: z.string(),
1532
- version: z.string().optional(),
1533
- url: z.string(),
1534
- path: z.string().optional(),
1535
- trust: z.enum(["verified", "listed"]),
1536
- securityFix: z.boolean().optional(),
1537
- registry: z.string(),
1538
- at: z.string(),
1539
- needsReview: z.string().optional(),
1540
- review: z.object({ conversationId: z.string(), at: z.string() }).optional(),
2479
+ ref: z.string().describe("The commit being offered."),
2480
+ version: z.string().optional().describe("What it calls itself."),
2481
+ url: z.string().describe("Where it comes from."),
2482
+ path: z.string().optional().describe("Where inside that repository it lives."),
2483
+ trust: z.enum(["verified", "listed"]).describe("Whether anybody vouched for it, or it is merely listed."),
2484
+ securityFix: z
2485
+ .boolean()
2486
+ .optional()
2487
+ .describe("This release fixes a security problem in earlier ones, so here the old version is the dangerous one."),
2488
+ registry: z.string().describe("Which registry said so."),
2489
+ at: z.string().describe("When it was published."),
2490
+ needsReview: z
2491
+ .string()
2492
+ .optional()
2493
+ .describe("Why this one was not taken automatically and is asking for a person instead: it wants more than it used to, or nobody has vouched for it."),
2494
+ review: z
2495
+ .object({
2496
+ conversationId: z.string().describe("Where to read what it found."),
2497
+ at: z.string().describe("When it looked."),
2498
+ })
2499
+ .optional()
2500
+ .describe("An agent has already read the difference between what is installed and this, so the card can link to what it found rather than offer to start looking."),
1541
2501
  });
1542
2502
  export const ExtensionAdvisorySchema = z.object({
1543
- reason: z.string(),
1544
- registry: z.string(),
1545
- at: z.string(),
1546
- autoDisabled: z.boolean(),
2503
+ reason: z
2504
+ .string()
2505
+ .describe("Why the registry pulled the listing, in its own words. Delisting protects people browsing; this record is for the person already running it."),
2506
+ registry: z.string().describe("Which registry said so."),
2507
+ at: z.string().describe("When."),
2508
+ autoDisabled: z.boolean().describe("Whether the sandbox has already switched it off."),
1547
2509
  });
1548
2510
  export const ExtensionHealthSchema = z.object({
1549
- state: z.enum(["watching", "healthy", "unhealthy"]),
1550
- detail: z.string().optional(),
1551
- fromRef: z.string().optional(),
1552
- at: z.string(),
1553
- autoReverted: z.boolean().optional(),
2511
+ state: z
2512
+ .enum(["watching", "healthy", "unhealthy"])
2513
+ .describe("How it has behaved since the last update. Checks catch broken, not wrong, so for a while after a swap it is simply watched."),
2514
+ detail: z.string().optional().describe("What is going wrong, when something is."),
2515
+ fromRef: z.string().optional().describe("Which version it was updated from, which is what going back would return to."),
2516
+ at: z.string().describe("When the watching started."),
2517
+ autoReverted: z
2518
+ .boolean()
2519
+ .optional()
2520
+ .describe("The update was already rolled back without anybody asking. The record stays rather than pretending the attempt never happened."),
2521
+ });
2522
+ export const PowersDiffSchema = z.object({
2523
+ added: z.array(z.string()).describe("What the new version asks for that the running one does not. The whole point of the comparison."),
2524
+ removed: z.array(z.string()).describe("What it no longer asks for."),
2525
+ unchanged: z.array(z.string()).describe("What stays the same."),
1554
2526
  });
1555
- export const PowersDiffSchema = z.object({ added: z.array(z.string()), removed: z.array(z.string()), unchanged: z.array(z.string()) });
1556
2527
  export const ExtensionUpdateActionSchema = z.object({
1557
- id: extensionId,
2528
+ id: extensionId.describe("Which extension."),
1558
2529
  ref: z
1559
2530
  .string()
1560
2531
  .regex(/^[0-9a-f]{40}$/)
1561
- .optional(),
2532
+ .optional()
2533
+ .describe("Which commit, in full. Leave it out for whatever the last check found, which is what most callers mean."),
1562
2534
  });
1563
2535
  export const ExtensionUpdatePreviewSchema = z.object({
1564
- ref: z.string(),
1565
- version: z.string(),
1566
- installedVersion: z.string(),
1567
- engines: z.string(),
1568
- compatible: z.boolean(),
1569
- powers: PowersDiffSchema,
1570
- });
1571
- export const ExtensionUpdateAppliedSchema = z.object({ ok: z.literal(true), ref: z.string(), rebuildNeeded: z.boolean().optional() });
2536
+ ref: z.string().describe("The commit this would install."),
2537
+ version: z.string().describe("What that version calls itself."),
2538
+ installedVersion: z.string().describe("What is running now."),
2539
+ engines: z.string().describe("Which sandbox versions the new one says it needs."),
2540
+ compatible: z.boolean().describe("Whether this sandbox is one of them."),
2541
+ powers: PowersDiffSchema.describe("Exactly what the new code asks for that the running one does not. This is what approving an update is approving."),
2542
+ });
2543
+ export const ExtensionUpdateAppliedSchema = z.object({
2544
+ ok: z.literal(true).describe("It went through."),
2545
+ ref: z.string().describe("Which commit is now running."),
2546
+ rebuildNeeded: z
2547
+ .boolean()
2548
+ .optional()
2549
+ .describe("The new version changes what the sandbox image contains, so a one-time rebuild is still pending and the update is not wholly landed yet."),
2550
+ });
1572
2551
  export const ExtensionUpdatePolicyInputSchema = z.object({
1573
- id: extensionId,
1574
- updates: z.enum(["notify", "agent", "auto"]).optional(),
1575
- advisories: z.enum(["auto-disable", "notify"]).optional(),
2552
+ id: extensionId.describe("Which extension."),
2553
+ updates: z
2554
+ .enum(["notify", "agent", "auto"])
2555
+ .optional()
2556
+ .describe("What to do about a newer version: tell you, have an agent read the difference first, or just take it."),
2557
+ advisories: z.enum(["auto-disable", "notify"]).optional().describe("What to do about a security warning: switch it off at once, or tell you."),
2558
+ });
2559
+ export const ExtensionUpdatesCheckedSchema = z.object({
2560
+ ok: z.literal(true).describe("The check ran."),
2561
+ checkedAt: z.string().describe("When, so a screen can date the answer."),
1576
2562
  });
1577
- export const ExtensionUpdatesCheckedSchema = z.object({ ok: z.literal(true), checkedAt: z.string() });
1578
2563
  export const ExtensionSummarySchema = z.object({
1579
- id: extensionId,
1580
- manifest: ExtensionManifestSchema,
1581
- commit: z.string(),
1582
- source: z.enum(["builtin", "installed", "workspace"]),
1583
- enabled: z.boolean(),
1584
- essential: z.boolean().optional(),
1585
- usage: z.record(z.string(), z.object({ calls: z.number().int().nonnegative(), last: z.string() })).optional(),
2564
+ id: extensionId.describe("The extension's id."),
2565
+ manifest: ExtensionManifestSchema.describe("What it declares about itself: what it contributes, what it needs, and what it may reach."),
2566
+ commit: z.string().describe("Exactly which commit is installed."),
2567
+ source: z
2568
+ .enum(["builtin", "installed", "workspace"])
2569
+ .describe("Where the code comes from: baked into the sandbox image and not removable, installed from a repository at a pinned commit, or written in this workspace and edited in place."),
2570
+ enabled: z
2571
+ .boolean()
2572
+ .describe("The owner's switch. A switched-off extension is still listed, which is what makes it switchable back on, but nothing it contributes is wired up."),
2573
+ essential: z
2574
+ .boolean()
2575
+ .optional()
2576
+ .describe("Its switch is fixed on, because it is the only way to see or stop an engine the sandbox runs regardless. Hiding that page would not stop the spending, only your ability to notice it. Declared by the core about its own surfaces, never by an extension about itself, which would be a pack making itself un-removable."),
2577
+ usage: z
2578
+ .record(z.string(), z.object({ calls: z.number().int().nonnegative().describe("How many times."), last: z.string().describe("When, most recently.") }))
2579
+ .optional()
2580
+ .describe("How much of the reach it asked for it has actually used, keyed by what it declared. Absent means never observed doing anything, which is a different claim from uses none of them, and the two have to stay tellable apart: reading either as these permissions are unnecessary turns evidence into a guess with a number on it."),
1586
2581
  backend: z
1587
2582
  .object({
1588
- state: z.enum(["running", "error", "absent", "incompatible", "starting", "stopped"]),
1589
- detail: z.string().optional(),
2583
+ state: z
2584
+ .enum(["running", "error", "absent", "incompatible", "starting", "stopped"])
2585
+ .describe("How its server half is doing. Absent means the code is not in this image at all; incompatible means it needs a different sandbox version."),
2586
+ detail: z
2587
+ .string()
2588
+ .optional()
2589
+ .describe("What went wrong, so a backend that failed to start is a sentence rather than an address that answers nothing."),
1590
2590
  })
1591
- .optional(),
1592
- update: ExtensionUpdateSchema.optional(),
1593
- advisory: ExtensionAdvisorySchema.optional(),
1594
- health: ExtensionHealthSchema.optional(),
1595
- previous: z.object({ ref: z.string(), version: z.string().optional() }).optional(),
1596
- updatePolicy: ExtensionUpdatePolicySchema.optional(),
2591
+ .optional()
2592
+ .describe("Present only for an extension that ships a server half."),
2593
+ update: ExtensionUpdateSchema.optional().describe("A newer version waiting. All five of these exist only for one installed from a repository: a built-in updates with the image and one written here is edited live."),
2594
+ advisory: ExtensionAdvisorySchema.optional().describe("A security warning about the installed version."),
2595
+ health: ExtensionHealthSchema.optional().describe("How it has behaved since the last update, which is what decides whether that update sticks."),
2596
+ previous: z
2597
+ .object({
2598
+ ref: z.string().describe("The commit that was running before."),
2599
+ version: z.string().optional().describe("What it called itself."),
2600
+ })
2601
+ .optional()
2602
+ .describe("The version kept one step back, which is what going back means."),
2603
+ updatePolicy: ExtensionUpdatePolicySchema.optional().describe("The owner's standing answer for this one: tell me, have an agent look, or just do it."),
2604
+ });
2605
+ export const InvalidWorkspaceExtensionSchema = z.object({
2606
+ dir: z.string().describe("Which folder."),
2607
+ error: z.string().describe("Why it could not be read."),
1597
2608
  });
1598
- export const InvalidWorkspaceExtensionSchema = z.object({ dir: z.string(), error: z.string() });
1599
2609
  export const ExtensionsListSchema = z.object({
1600
- extensions: z.array(ExtensionSummarySchema),
1601
- invalid: z.array(InvalidWorkspaceExtensionSchema),
1602
- updatesCheckedAt: z.string().optional(),
2610
+ extensions: z.array(ExtensionSummarySchema).describe("What is installed."),
2611
+ invalid: z
2612
+ .array(InvalidWorkspaceExtensionSchema)
2613
+ .describe("Extensions written here that could not be read at all. Listed rather than dropped, because there is no install moment at which to reject a broken one, so this is its only way of saying anything."),
2614
+ updatesCheckedAt: z
2615
+ .string()
2616
+ .optional()
2617
+ .describe("When updates were last looked for. Absent until the first check has run. Sent so a screen can say checked an hour ago rather than presenting staleness as certainty."),
1603
2618
  });
1604
2619
  export const ExtensionSettingsSchema = z.object({
1605
- settings: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])),
1606
- secretsSet: z.array(z.string()),
2620
+ settings: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).describe("The values, minus anything marked secret."),
2621
+ secretsSet: z
2622
+ .array(z.string())
2623
+ .describe("Which of its secret settings actually hold a value. Names only: the values themselves never come back."),
1607
2624
  });
1608
2625
  export const ExtensionSettingsInputSchema = z.object({
1609
- id: z.string(),
1610
- settings: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])),
2626
+ id: z.string().describe("Which extension."),
2627
+ settings: z
2628
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
2629
+ .describe("The values to write. A key the extension never declared is refused rather than quietly stored."),
2630
+ });
2631
+ export const ExtensionEnabledInputSchema = z.object({
2632
+ id: z.string().describe("Which extension."),
2633
+ enabled: z.boolean().describe("On or off."),
1611
2634
  });
1612
- export const ExtensionEnabledInputSchema = z.object({ id: z.string(), enabled: z.boolean() });
1613
2635
  export const WorkspaceExtensionCreateSchema = z.object({
1614
- publisher: z.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1615
- name: z.string().regex(/^[a-z0-9][a-z0-9-]*$/),
2636
+ publisher: z
2637
+ .string()
2638
+ .regex(/^[a-z0-9][a-z0-9-]*$/)
2639
+ .describe("Who it is by, which together with the name makes its id."),
2640
+ name: z
2641
+ .string()
2642
+ .regex(/^[a-z0-9][a-z0-9-]*$/)
2643
+ .describe("What it is called."),
2644
+ });
2645
+ export const WorkspaceExtensionCreatedSchema = z.object({
2646
+ id: z.string().describe("The id it was given."),
2647
+ dir: z.string().describe("Where its files are, so you can open them."),
2648
+ });
2649
+ export const ExtensionUsageInputSchema = z.object({
2650
+ id: z.string().describe("Which extension."),
2651
+ used: z.record(z.string(), z.number().int().positive()).describe("Which of its declared powers it exercised, and how many times."),
2652
+ });
2653
+ export const ExtensionProcessParamSchema = z.object({
2654
+ id: z.string().describe("Which extension."),
2655
+ name: z.string().describe("Which of its declared processes."),
1616
2656
  });
1617
- export const WorkspaceExtensionCreatedSchema = z.object({ id: z.string(), dir: z.string() });
1618
- export const ExtensionUsageInputSchema = z.object({ id: z.string(), used: z.record(z.string(), z.number().int().positive()) });
1619
- export const ExtensionProcessParamSchema = z.object({ id: z.string(), name: z.string() });
1620
2657
  export const ExtensionProcessStatusSchema = z.object({
1621
- name: z.string(),
1622
- running: z.boolean(),
1623
- port: z.number().optional(),
1624
- previewUrl: z.string().optional(),
2658
+ name: z.string().describe("Which process."),
2659
+ running: z.boolean().describe("Whether it is up."),
2660
+ port: z.number().optional().describe("The port it was given."),
2661
+ previewUrl: z.string().optional().describe("Where to open it, when it has an address."),
1625
2662
  });
1626
2663
  export const WorkspaceEventKindSchema = z.enum(["turn.settled", "agent.landed", "deps.broken", "deps.fixed"]);
1627
2664
  export const WorkspaceEventSchema = z.object({
@@ -1642,26 +2679,56 @@ export const WorkspaceEventSchema = z.object({
1642
2679
  .optional(),
1643
2680
  });
1644
2681
  export const TriggerSchema = z.discriminatedUnion("kind", [
1645
- z.object({ kind: z.literal("schedule"), cron: z.string().min(1) }),
1646
- z.object({ kind: z.literal("event"), token: z.string().min(1).optional() }),
1647
2682
  z.object({
1648
- kind: z.literal("listener"),
1649
- provider: z.string().min(1),
1650
- channelId: z.string().min(1).optional(),
1651
- eventType: z.string().min(1).optional(),
1652
- mentioned: z.boolean().optional(),
1653
- branch: z.string().min(1).optional(),
1654
- allowedOrigins: z.array(z.string()).optional(),
2683
+ kind: z.literal("schedule").describe("On a clock."),
2684
+ cron: z.string().min(1).describe("When, in cron notation."),
2685
+ }),
2686
+ z.object({
2687
+ kind: z.literal("event").describe("When something calls its webhook."),
2688
+ token: z
2689
+ .string()
2690
+ .min(1)
2691
+ .optional()
2692
+ .describe("The credential a caller presents. It is the only one in the exchange, because an outside sender has no identity here."),
2693
+ }),
2694
+ z.object({
2695
+ kind: z.literal("listener").describe("When a message arrives from somewhere outside."),
2696
+ provider: z.string().min(1).describe("Which service to listen to."),
2697
+ channelId: z.string().min(1).optional().describe("Narrow it to one channel or thread."),
2698
+ eventType: z.string().min(1).optional().describe("Narrow it to one kind of event."),
2699
+ mentioned: z.boolean().optional().describe("Only when the agent is actually addressed, rather than on everything said in earshot."),
2700
+ branch: z
2701
+ .string()
2702
+ .min(1)
2703
+ .optional()
2704
+ .describe("Narrow it to one branch, for the sources that have branches. Absent means every branch of the repositories it matches."),
2705
+ allowedOrigins: z.array(z.string()).optional().describe("Which websites may reach the chat widget. Absent or empty admits nobody."),
2706
+ }),
2707
+ z.object({
2708
+ kind: z.literal("workspace").describe("When something happens to the files or the repositories."),
2709
+ event: WorkspaceEventKindSchema.describe("Which happening."),
2710
+ repo: z.string().min(1).optional().describe("Narrow it to one repository. Absent means any of them."),
1655
2711
  }),
1656
- z.object({ kind: z.literal("workspace"), event: WorkspaceEventKindSchema, repo: z.string().min(1).optional() }),
1657
2712
  ]);
1658
2713
  export const WebchatConfigSchema = z.object({
1659
- access: z.enum(["public", "google"]).optional(),
1660
- requireName: z.boolean().optional(),
1661
- antiBot: z.enum(["turnstile", "pow"]).optional(),
1662
- turnstileSiteKey: z.string().optional(),
1663
- turnstileSecret: z.string().optional(),
1664
- googleClientId: z.string().optional(),
2714
+ access: z
2715
+ .enum(["public", "google"])
2716
+ .optional()
2717
+ .describe("Who may write to it. Absent means anyone, which is the anonymous support box it looks like."),
2718
+ requireName: z
2719
+ .boolean()
2720
+ .optional()
2721
+ .describe("Ask a visitor for a name first. Cosmetic: the name is typed, so it reaches the model as something a stranger said, never as identity."),
2722
+ antiBot: z
2723
+ .enum(["turnstile", "pow"])
2724
+ .optional()
2725
+ .describe("How to keep bots out: a third-party check that needs the site's own keys, or a puzzle the sandbox sets and the widget solves, so a site with no such account still has something. Absent leaves the site allowlist and the rate limit as the whole boundary."),
2726
+ turnstileSiteKey: z.string().optional().describe("The public half of those keys, which ships to the visitor's browser."),
2727
+ turnstileSecret: z.string().optional().describe("The private half, which the sandbox keeps and the widget never sees."),
2728
+ googleClientId: z
2729
+ .string()
2730
+ .optional()
2731
+ .describe("The site's own sign-in client id. It cannot be ours: a sign-in is only issued to an approved origin, and no single client can list every customer's domain."),
1665
2732
  title: z.string().max(80).optional(),
1666
2733
  greeting: z.string().max(500).optional(),
1667
2734
  accent: z
@@ -1700,40 +2767,64 @@ export const WebchatMessageSchema = z.object({
1700
2767
  .optional(),
1701
2768
  });
1702
2769
  export const AutomationSchema = z.object({
1703
- id: entryId,
1704
- trigger: TriggerSchema,
1705
- guard: z.string().min(1).optional(),
1706
- prompt: z.string().min(1),
1707
- webchat: WebchatConfigSchema.optional(),
1708
- allowedTools: z.array(z.string().min(1)).optional(),
1709
- agent: AgentProviderSchema.optional(),
1710
- account: z.string().optional(),
1711
- actsAs: entryId.optional(),
1712
- harness: AgentHarnessSchema.optional(),
1713
- model: z.string().optional(),
1714
- requireApproval: z.boolean().optional(),
1715
- holdForSeconds: z.number().optional(),
1716
- chore: z.boolean().optional(),
1717
- enabled: z.boolean(),
2770
+ id: entryId.describe("The automation's id."),
2771
+ trigger: TriggerSchema.describe("What sets it off: a schedule, an event in the workspace, a message arriving from outside, or a webhook."),
2772
+ guard: z
2773
+ .string()
2774
+ .min(1)
2775
+ .optional()
2776
+ .describe("A command run before the wake that decides whether there is anything to do. Skipped by the guard is often the most useful thing an automation can report."),
2777
+ prompt: z.string().min(1).describe("What the woken agent is told."),
2778
+ webchat: WebchatConfigSchema.optional().describe("Settings for the public chat widget, for an automation that answers visitors."),
2779
+ allowedTools: z
2780
+ .array(z.string().min(1))
2781
+ .optional()
2782
+ .describe("Narrow the woken turn to these tools. For one driven by an outside message this list is the real boundary, because prompt wording is only advice and an empty toolbox is not."),
2783
+ agent: AgentProviderSchema.optional().describe("Which provider serves the wake."),
2784
+ account: z.string().optional().describe("Which account pays for it."),
2785
+ actsAs: entryId.optional().describe("Which persona it speaks as. An unwatched turn naming none reaches no signed-in account at all."),
2786
+ harness: AgentHarnessSchema.optional().describe("Which agentic loop runs it."),
2787
+ model: z.string().optional().describe("Which model runs it."),
2788
+ requireApproval: z.boolean().optional().describe("Hold every fire for a person instead of running it. Only a person can release one of those."),
2789
+ holdForSeconds: z.number().optional().describe("Hold each fire this long before running it anyway, which is a delay rather than a decision."),
2790
+ chore: z
2791
+ .boolean()
2792
+ .optional()
2793
+ .describe("This automation is a maintenance job, which is what files it under chores rather than among ordinary automations."),
2794
+ enabled: z.boolean().describe("Whether it fires at all."),
1718
2795
  });
1719
2796
  export const AutomationApprovalSchema = z.object({
1720
- id: entryId,
1721
- automationId: z.string(),
1722
- payload: z.string().optional(),
1723
- origin: AgentOriginSchema.optional(),
1724
- title: z.string().optional(),
1725
- conversationId: z.string().optional(),
1726
- sessionId: z.string().optional(),
1727
- createdAt: z.number(),
1728
- autoRunAt: z.number().optional(),
2797
+ id: entryId.describe("This waiting item's own id, which approving and rejecting take."),
2798
+ automationId: z.string().describe("Which automation it came from."),
2799
+ payload: z
2800
+ .string()
2801
+ .optional()
2802
+ .describe("What set it off, kept whole so an approved wake carries the same thing it would have had. Absent for one on a schedule, which carries nothing."),
2803
+ origin: AgentOriginSchema.optional().describe("Where the message came from, kept alongside the payload so an approved wake appears on the board exactly as an automatic one would have."),
2804
+ title: z.string().optional().describe("What the conversation would be called."),
2805
+ conversationId: z
2806
+ .string()
2807
+ .optional()
2808
+ .describe("The thread this belongs to, when it has one, so approving continues that conversation rather than opening a new one. Without it, one visitor's chat becomes a card per approved message and an agent that meets them again every turn."),
2809
+ sessionId: z.string().optional().describe("The provider session that thread last ran on."),
2810
+ createdAt: z.number().describe("When it started waiting, in milliseconds."),
2811
+ autoRunAt: z
2812
+ .number()
2813
+ .optional()
2814
+ .describe("When it goes ahead on its own, in milliseconds, for a hold that is only a delay. Absent for one that genuinely waits on a person."),
1729
2815
  });
1730
2816
  export const AgentsListSchema = z.object({
1731
- agents: z.array(AgentSummarySchema),
1732
- rev: z.number(),
1733
- held: z.array(AutomationApprovalSchema).default([]),
1734
- });
1735
- export const AutomationApprovalsListSchema = z.object({ approvals: z.array(AutomationApprovalSchema) });
1736
- export const AutomationApprovalIdParamSchema = z.object({ id: z.string() });
2817
+ agents: z.array(AgentSummarySchema).describe("The conversations."),
2818
+ rev: z
2819
+ .number()
2820
+ .describe("Which version of the fleet this is. The fleet is published as whole snapshots, so without a version a list read before a change but delivered after it would silently undo that change. Drop any list older than the newest you have already applied."),
2821
+ held: z
2822
+ .array(AutomationApprovalSchema)
2823
+ .default([])
2824
+ .describe("Automations waiting at the door for a yes, put alongside the running conversations so needs-you sits beside working rather than on a page nobody opens."),
2825
+ });
2826
+ export const AutomationApprovalsListSchema = z.object({ approvals: z.array(AutomationApprovalSchema).describe("Everything waiting for a yes.") });
2827
+ export const AutomationApprovalIdParamSchema = z.object({ id: z.string().describe("Which waiting item.") });
1737
2828
  export const AutomationRunSchema = z.object({
1738
2829
  at: z.number(),
1739
2830
  outcome: z.enum(["completed", "skipped", "error", "interrupted"]),
@@ -1790,197 +2881,379 @@ const StepIdSchema = z
1790
2881
  export const WorkflowHandoffSchema = z.enum(["fresh", "continue"]);
1791
2882
  const WORKFLOW_STEPS_MAX = 24;
1792
2883
  export const WorkflowStepSchema = z.object({
1793
- id: StepIdSchema,
1794
- title: z.string().min(1).max(60),
1795
- goal: z.string().min(1).optional(),
1796
- prompt: z.string().min(1).optional(),
1797
- needs: z.array(StepIdSchema),
1798
- handoff: WorkflowHandoffSchema,
1799
- output: LoopOutputSchema,
1800
- checks: z.array(LoopCheckSchema),
1801
- context: LoopContextSchema,
1802
- maxSpendUsd: z.number().positive().optional(),
1803
- agent: AgentProviderSchema.optional(),
1804
- harness: AgentHarnessSchema.optional(),
1805
- account: z.string().optional(),
1806
- model: z.string().optional(),
1807
- actsAs: entryId.optional(),
2884
+ id: StepIdSchema.describe("This step's own name, which other steps use to say they wait on it."),
2885
+ title: z.string().min(1).max(60).describe("What to call it on screen. Short: the instruction below is where the detail goes."),
2886
+ goal: z
2887
+ .string()
2888
+ .min(1)
2889
+ .optional()
2890
+ .describe("What done means for this step, in your words. It is what the step is judged against, and a different sentence from what it is told to do."),
2891
+ prompt: z
2892
+ .string()
2893
+ .min(1)
2894
+ .optional()
2895
+ .describe("What the step is told to do. The goal is the suite is green; this is run the tests, take the top failure, fix it. Leaving it out hands over the run's own request untouched, which is right for a step whose whole job is do what was asked."),
2896
+ needs: z
2897
+ .array(StepIdSchema)
2898
+ .describe("Which steps must finish first. Empty means it starts when the run does. Naming a step that does not exist, or a loop between steps, is refused when the workflow is saved."),
2899
+ handoff: WorkflowHandoffSchema.describe("How it meets what came before: a fresh conversation handed the previous step's result, or the same conversation carried on."),
2900
+ output: LoopOutputSchema.describe("What it has to produce for the step to count."),
2901
+ checks: z.array(LoopCheckSchema).describe("What has to pass before it counts as done."),
2902
+ context: LoopContextSchema.describe("How the step's own repeats meet each other. A long-running step wants to start clean each round; a short polish-this step wants to carry on."),
2903
+ maxSpendUsd: z
2904
+ .number()
2905
+ .positive()
2906
+ .optional()
2907
+ .describe("A ceiling on what this step may spend. The one resource that cannot be recovered after an unattended fan-out, which is why it is here and iteration limits are not. Absent is uncapped."),
2908
+ agent: AgentProviderSchema.optional().describe("Which provider runs it."),
2909
+ harness: AgentHarnessSchema.optional().describe("Which agentic loop runs it."),
2910
+ account: z.string().optional().describe("Which account pays for it."),
2911
+ model: z.string().optional().describe("Which model runs it."),
2912
+ actsAs: entryId
2913
+ .optional()
2914
+ .describe("Which persona it acts as. Unpinned, a step gets the strict unwatched default: every tool, and no signed-in accounts at all. Pinning one is how a release check gets a voice, a folder to work in, or the single account it may post from."),
1808
2915
  });
1809
2916
  export const WorkflowGateSchema = z.object({
1810
- step: StepIdSchema,
1811
- field: z.string().min(1),
1812
- pass: z.array(z.string().min(1)).min(1),
1813
- token: z.string().optional(),
1814
- dailyMax: z.number().int().positive().optional(),
2917
+ step: StepIdSchema.describe("Which step's answer carries the decision. Usually a last step that weighs up the ones before it, though nothing requires that."),
2918
+ field: z
2919
+ .string()
2920
+ .min(1)
2921
+ .describe("Which of that step's declared answers to read. A declared field is the one part of a step's answer that was checked rather than fished out of prose, which is the whole rule here. Checked when the workflow is saved."),
2922
+ pass: z
2923
+ .array(z.string().min(1))
2924
+ .min(1)
2925
+ .describe("Which values mean ship it. Everything else fails. A list of what passes rather than what fails, because a step answering mostly-pass or pass-with-notes must not ship, and this gets that right without anybody having had to enumerate the ways a model can hedge."),
2926
+ token: z
2927
+ .string()
2928
+ .optional()
2929
+ .describe("The credential the calling pipeline presents. It is the only one in the exchange, because a build runner has no identity of its own here."),
2930
+ dailyMax: z
2931
+ .number()
2932
+ .int()
2933
+ .positive()
2934
+ .optional()
2935
+ .describe("How many runs a day, across every caller. A gate is a paid door with nobody in the loop: one wired into a push-triggered pipeline is a fan-out of conversations per commit. Absent is a small default rather than unlimited."),
1815
2936
  });
1816
2937
  export const GATE_DAILY_MAX_DEFAULT = 20;
1817
2938
  export const GateOutcomeSchema = z.enum(["pass", "fail", "blocked"]);
1818
2939
  export const GateVerdictSchema = z.object({
1819
- outcome: GateOutcomeSchema,
1820
- reason: z.string(),
1821
- runId: z.string(),
1822
- value: z.string().optional(),
2940
+ outcome: GateOutcomeSchema.describe("Ship it, do not, or we could not tell. That third answer exists because could not reach a judgement is not the product is broken: a gate that reported its own outages as failures is one a team switches off, so it should be the honest answer far more often than the convenient one, and it means a neutral build rather than a red one."),
2941
+ reason: z.string().describe("Why, in one line. Realistically the only part of this a build log will ever show."),
2942
+ runId: z.string().describe("The run behind the verdict, so somebody can go and read it."),
2943
+ value: z
2944
+ .string()
2945
+ .optional()
2946
+ .describe("What the step actually answered. Absent when there was nothing to read, which is most of the could-not-tell cases."),
1823
2947
  });
1824
2948
  export const WorkflowSchema = z.object({
1825
- id: entryId,
1826
- name: z.string().min(1).max(80),
1827
- description: z.string().max(400).optional(),
1828
- steps: z.array(WorkflowStepSchema).min(1).max(WORKFLOW_STEPS_MAX),
1829
- gate: WorkflowGateSchema.optional(),
1830
- maxParallel: z.number().int().min(1).max(8),
2949
+ id: entryId.describe("The workflow's id."),
2950
+ name: z.string().min(1).max(80).describe("What to call it."),
2951
+ description: z.string().max(400).optional().describe("What it is for."),
2952
+ steps: z
2953
+ .array(WorkflowStepSchema)
2954
+ .min(1)
2955
+ .max(WORKFLOW_STEPS_MAX)
2956
+ .describe("The steps, each with what it waits on. Every one runs in its own private copy of the repos, always, because parallel steps sharing a tree collide."),
2957
+ gate: WorkflowGateSchema.optional().describe("Present means a machine can run this design and get a ship-it answer back. Absent means an ordinary workflow, started by a person, with no outside door onto it at all."),
2958
+ maxParallel: z
2959
+ .number()
2960
+ .int()
2961
+ .min(1)
2962
+ .max(8)
2963
+ .describe("How many steps may run at once. Bounded, because a fan-out of twelve is twelve model sessions, twelve working copies and twelve times the burn rate, on one machine."),
1831
2964
  });
1832
2965
  export const WorkflowStepStateSchema = z.enum(["pending", "running", "done", "failed", "skipped", "stopped"]);
1833
2966
  export const WorkflowStepRunSchema = z.object({
1834
- stepId: StepIdSchema,
1835
- state: WorkflowStepStateSchema,
1836
- conversationId: z.string(),
1837
- startedAt: z.number().optional(),
1838
- endedAt: z.number().optional(),
1839
- iterations: z.number().int().min(0),
1840
- costUsd: z.number().optional(),
1841
- loopState: LoopStateSchema.optional(),
1842
- detail: z.string().optional(),
1843
- document: LoopDocumentSchema.optional(),
1844
- report: z.string().optional(),
1845
- reportPath: z.string().optional(),
2967
+ stepId: StepIdSchema.describe("Which step this is."),
2968
+ state: WorkflowStepStateSchema.describe("How it went. Skipped carries what the others cannot: it never ran, because something it was waiting on did not finish. That is why a failed run shows one red step and a trail of grey ones."),
2969
+ conversationId: z
2970
+ .string()
2971
+ .describe("The conversation it ran on, and the way from a node on the graph to a real record. Shared with the step before it when they were chained, which is what makes those two one card."),
2972
+ startedAt: z.number().optional().describe("When it began, in milliseconds."),
2973
+ endedAt: z.number().optional().describe("When it ended, in milliseconds."),
2974
+ iterations: z.number().int().min(0).describe("How many rounds it took."),
2975
+ costUsd: z.number().optional().describe("What it cost, in dollars."),
2976
+ loopState: LoopStateSchema.optional().describe("How its repeating ended. Out of rounds and stuck both come out as a failed step, and the difference between them is the difference between give it more room and more room will not help."),
2977
+ detail: z.string().optional().describe("What went wrong, when something did."),
2978
+ document: LoopDocumentSchema.optional().describe("What it produced, once it has produced something that passes its own declared shape. This is what the steps after it are handed."),
2979
+ report: z
2980
+ .string()
2981
+ .optional()
2982
+ .describe("The start of its closing words. Bounded, so a long answer is not silently cut down to its last few thousand characters and the record stays a sensible size."),
2983
+ reportPath: z
2984
+ .string()
2985
+ .optional()
2986
+ .describe("Where the whole answer is, as a workspace path. Every step can read it, so a long handoff need not be copied into anybody's prompt."),
1846
2987
  });
1847
2988
  export const WorkflowRunStateSchema = z.enum(["running", "done", "failed", "stopped", "overspent", "error"]);
1848
2989
  export const WorkflowRunSchema = z.object({
1849
- runId: z.string().min(1),
1850
- workflow: WorkflowSchema,
1851
- repos: z.array(RepoBaseSchema).min(1).max(50),
1852
- request: z.string().optional(),
1853
- state: WorkflowRunStateSchema,
1854
- startedAt: z.number(),
1855
- endedAt: z.number().optional(),
1856
- resumed: z.number().int().min(0),
1857
- detail: z.string().optional(),
1858
- steps: z.array(WorkflowStepRunSchema),
1859
- archivedAt: z.number().optional(),
1860
- });
1861
- export const WorkflowSummarySchema = WorkflowSchema.extend({ runs: z.array(WorkflowRunSchema) });
1862
- export const WorkflowsListSchema = z.object({ workflows: z.array(WorkflowSummarySchema) });
1863
- export const WorkflowRunsListSchema = z.object({ runs: z.array(WorkflowRunSchema) });
1864
- export const WorkflowIdParamSchema = z.object({ id: z.string() });
1865
- export const WorkflowRunIdParamSchema = z.object({ runId: z.string() });
1866
- export const WorkflowRunStartSchema = WorkflowIdParamSchema.extend({ request: z.string().min(1).max(20_000).optional() });
1867
- export const WorkflowSaveSchema = z.object({ workflow: WorkflowSchema, create: z.boolean() });
2990
+ runId: z.string().min(1).describe("This run's id."),
2991
+ workflow: WorkflowSchema.describe("The design as it stood when the run started, copied rather than looked up. The run has to keep showing the graph it actually ran, not the one edited twice since, and a run of a deleted workflow has to stay readable."),
2992
+ repos: z
2993
+ .array(RepoBaseSchema)
2994
+ .min(1)
2995
+ .max(50)
2996
+ .describe("The workspace as this run began, one exact commit per repository. Every step branches from these, even if the shared tree moves while a wide fan-out is still opening its copies, so the steps can be compared with each other afterwards."),
2997
+ request: z
2998
+ .string()
2999
+ .optional()
3000
+ .describe("What this run was asked to do, handed to every step on top of its own instructions. It is what makes one saved design worth keeping: two models, one task is a shape, and the task is different every time. Absent for a run started with nowhere to type one."),
3001
+ state: WorkflowRunStateSchema.describe("How the run is going. Finished means every step that ran got there; a run with skipped steps counts as failed, because a graph that never reached its end did not do what it was asked whatever the survivors managed."),
3002
+ startedAt: z.number().describe("When it began, in milliseconds."),
3003
+ endedAt: z.number().optional().describe("When it ended, in milliseconds."),
3004
+ resumed: z.number().int().min(0).describe("How many times the sandbox restarted under it and picked it back up."),
3005
+ detail: z.string().optional().describe("What went wrong, when something did."),
3006
+ steps: z
3007
+ .array(WorkflowStepRunSchema)
3008
+ .describe("One entry per step, in the design's own order. Every one is written down as waiting when the run starts, so the picture is complete from the first frame and a missing step never has to mean two things."),
3009
+ archivedAt: z
3010
+ .number()
3011
+ .optional()
3012
+ .describe("When it was put away, in milliseconds. The record stays readable and every step's branch, transcript and counters are untouched. Its conversations are put away with it, and brought back with it. Absent means live on the board."),
3013
+ });
3014
+ export const WorkflowSummarySchema = WorkflowSchema.extend({ runs: z.array(WorkflowRunSchema).describe("Its runs, newest first.") });
3015
+ export const WorkflowsListSchema = z.object({ workflows: z.array(WorkflowSummarySchema).describe("Every saved design with its own run history.") });
3016
+ export const WorkflowRunsListSchema = z.object({
3017
+ runs: z.array(WorkflowRunSchema).describe("Every run across every workflow, newest first, including runs of workflows since deleted."),
3018
+ });
3019
+ export const WorkflowIdParamSchema = z.object({ id: z.string().describe("Which workflow.") });
3020
+ export const WorkflowRunIdParamSchema = z.object({ runId: z.string().describe("Which run.") });
3021
+ export const WorkflowRunStartSchema = WorkflowIdParamSchema.extend({
3022
+ request: z
3023
+ .string()
3024
+ .min(1)
3025
+ .max(20_000)
3026
+ .optional()
3027
+ .describe("What to point it at. Optional, because a design whose steps already say what they want is complete on its own; only one written as a shape needs today's sentence."),
3028
+ });
3029
+ export const WorkflowSaveSchema = z.object({
3030
+ workflow: WorkflowSchema.describe("The design to write."),
3031
+ create: z
3032
+ .boolean()
3033
+ .describe("Whether you mean to make a new one or replace an existing one. Said outright rather than inferred, so an id that happens to collide is a refusal instead of one saved design quietly overwriting another."),
3034
+ });
1868
3035
  export const CiHostSchema = z.enum(["github", "gitlab"]);
1869
3036
  export const PipelineStatusSchema = z.enum(["running", "success", "failed", "canceled", "skipped"]);
1870
3037
  export const PipelineRunSchema = z.object({
1871
- repo: z.string(),
1872
- host: CiHostSchema,
1873
- project: z.string(),
1874
- runId: z.number(),
1875
- title: z.string().optional(),
1876
- authorName: z.string().optional(),
1877
- authorAvatarUrl: z.string().optional(),
1878
- trigger: z.string().optional(),
1879
- branch: z.string(),
1880
- sha: z.string(),
1881
- status: PipelineStatusSchema,
1882
- url: z.string(),
1883
- createdAt: z.number(),
1884
- durationSeconds: z.number().optional(),
1885
- failedJobs: z.array(z.string()).optional(),
3038
+ repo: z.string().describe("Which workspace repository it belongs to."),
3039
+ host: CiHostSchema.describe("Which forge is running it."),
3040
+ project: z.string().describe("The project there, as that forge names it."),
3041
+ runId: z.number().describe("The forge's own id for the run, which is what re-running and cancelling take."),
3042
+ title: z
3043
+ .string()
3044
+ .optional()
3045
+ .describe("The run's headline, usually the commit subject or the pull request's title. Absent means falling back to the branch and commit."),
3046
+ authorName: z.string().optional().describe("Who the forge credits for setting it off."),
3047
+ authorAvatarUrl: z.string().optional().describe("Their picture, hosted by the forge. Absent means drawing their initials instead."),
3048
+ trigger: z
3049
+ .string()
3050
+ .optional()
3051
+ .describe("What set it off, in the forge's own word rather than flattened into a shared vocabulary, because the forge's word is the precise one."),
3052
+ branch: z.string().describe("Which branch."),
3053
+ sha: z.string().describe("Which commit."),
3054
+ status: PipelineStatusSchema.describe("How it is going. Running covers everything still moving, since the only distinction that matters is that against the three ways it can stop."),
3055
+ url: z.string().describe("Its page on the forge."),
3056
+ createdAt: z.number().describe("When it started, in milliseconds."),
3057
+ durationSeconds: z.number().optional().describe("How long it took."),
3058
+ failedJobs: z
3059
+ .array(z.string())
3060
+ .optional()
3061
+ .describe("What broke, by name. Fetched only for failed runs, so that a notification or a screen can say what went wrong rather than just that something did."),
1886
3062
  });
1887
3063
  export const PipelineJobSchema = z.object({
1888
- name: z.string(),
1889
- status: PipelineStatusSchema,
1890
- stage: z.string().optional(),
1891
- needs: z.array(z.string()).optional(),
1892
- startedAt: z.number().optional(),
1893
- finishedAt: z.number().optional(),
1894
- durationSeconds: z.number().optional(),
1895
- webUrl: z.string().optional(),
3064
+ name: z.string().describe("The job's name."),
3065
+ status: PipelineStatusSchema.describe("How it went."),
3066
+ stage: z.string().optional().describe("Which stage it belongs to, where the pipeline groups its jobs that way."),
3067
+ needs: z
3068
+ .array(z.string())
3069
+ .optional()
3070
+ .describe("Which jobs in this run it declared it waits on: the real shape of the pipeline. Absent means nothing could be read, which is different from an empty list, which is the claim that it waits on nothing."),
3071
+ startedAt: z.number().optional().describe("When it began, in milliseconds. Absent while it is queued."),
3072
+ finishedAt: z.number().optional().describe("When it ended, in milliseconds."),
3073
+ durationSeconds: z.number().optional().describe("How long it took."),
3074
+ webUrl: z.string().optional().describe("Its page on the forge, which is the shortest path from this step failed to the log that says why."),
1896
3075
  });
1897
3076
  export const CiJobsResponseSchema = z.object({
1898
- jobs: z.array(PipelineJobSchema),
3077
+ jobs: z.array(PipelineJobSchema).describe("The steps inside one run. Fetched separately from the run list, so that list stays cheap."),
1899
3078
  });
1900
3079
  export const CiRepoSchema = z.object({
1901
- repo: z.string(),
1902
- host: CiHostSchema,
1903
- project: z.string(),
1904
- url: z.string(),
1905
- hookWarning: z.string().optional(),
3080
+ repo: z.string().describe("Which workspace repository."),
3081
+ host: CiHostSchema.describe("Which forge it lives on."),
3082
+ project: z.string().describe("The project there."),
3083
+ url: z.string().describe("Its page on the forge."),
3084
+ hookWarning: z
3085
+ .string()
3086
+ .optional()
3087
+ .describe("Present when the sandbox could not register for instant notifications, with what happened and what to paste in by hand. Without them the sandbox polls instead, so this costs a couple of minutes' delay rather than the feature."),
1906
3088
  });
1907
3089
  export const CI_POLL_INTERVAL_MS = 2 * 60_000;
1908
3090
  export const CiRunsResponseSchema = z.object({
1909
- repos: z.array(CiRepoSchema),
1910
- runs: z.array(PipelineRunSchema),
1911
- seenAt: z.number().optional(),
1912
- });
1913
- export const CiSeenResponseSchema = z.object({ seenAt: z.number() });
1914
- export const CiRunParamSchema = z.object({ repo: z.string(), runId: z.number() });
1915
- export const CiFixParamSchema = CiRunParamSchema.extend({ pick: AgentRunPickSchema });
1916
- export const CiFixResponseSchema = z.object({ conversationId: z.string() });
3091
+ repos: z.array(CiRepoSchema).describe("Which workspace repositories are wired to a forge, and how each one's notifications are set up."),
3092
+ runs: z.array(PipelineRunSchema).describe("Runs across all of them, newest first."),
3093
+ seenAt: z
3094
+ .number()
3095
+ .optional()
3096
+ .describe("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."),
3097
+ });
3098
+ export const CiSeenResponseSchema = z.object({
3099
+ seenAt: z.number().describe("The timestamp that was written, handed back so a caller can update without asking again."),
3100
+ });
3101
+ export const CiRunParamSchema = z.object({
3102
+ repo: z
3103
+ .string()
3104
+ .describe("Which workspace repository. The project behind it is resolved fresh each call, so a stale screen cannot act on one the workspace no longer maps to."),
3105
+ runId: z.number().describe("Which run, by the forge's own id."),
3106
+ });
3107
+ export const CiFixParamSchema = CiRunParamSchema.extend({
3108
+ pick: AgentRunPickSchema.describe("Which model to open the conversation on, when somebody chose one. Leave it out for the sandbox's own choice, which is the ordinary path."),
3109
+ });
3110
+ export const CiFixResponseSchema = z.object({
3111
+ conversationId: z.string().describe("The conversation that was opened, already holding the failure. Open it to watch, or attach to its turn."),
3112
+ });
1917
3113
  export const PrepushStatusSchema = z.enum(["idle", "running", "passed", "failed", "error", "cancelled"]);
1918
3114
  export const PrepushRunSchema = z.object({
1919
- status: PrepushStatusSchema,
1920
- command: z.string(),
1921
- startedAt: z.number().optional(),
1922
- finishedAt: z.number().optional(),
1923
- exitCode: z.number().optional(),
1924
- timedOut: z.boolean().optional(),
1925
- session: z.string().optional(),
1926
- output: z.string(),
3115
+ status: PrepushStatusSchema.describe("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."),
3116
+ command: z
3117
+ .string()
3118
+ .describe("What actually ran, echoed here rather than read back from the settings, so a result looked at after the setting changed still says what produced it."),
3119
+ startedAt: z.number().optional().describe("When it began, in milliseconds."),
3120
+ finishedAt: z.number().optional().describe("When it ended, in milliseconds."),
3121
+ exitCode: z.number().optional().describe("How the command exited."),
3122
+ timedOut: z.boolean().optional().describe("It was killed for taking too long rather than finishing."),
3123
+ session: z
3124
+ .string()
3125
+ .optional()
3126
+ .describe("The terminal it runs in, which is where to watch it. Absent where the sandbox has no terminals, in which case there is nothing to attach to."),
3127
+ output: z
3128
+ .string()
3129
+ .describe("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."),
1927
3130
  });
1928
3131
  export const DraftStatusSchema = z.enum(["proposed", "approved", "posting", "posted", "failed"]);
1929
3132
  export const DraftSchema = z.object({
1930
- platform: z.string().min(1),
1931
- actsAs: entryId.optional(),
1932
- content: z.string().min(1),
1933
- title: z.string().optional(),
1934
- target: z.string().optional(),
1935
- media: z.array(z.string()).optional(),
1936
- scheduledAt: z.number().optional(),
1937
- status: DraftStatusSchema.default("proposed"),
1938
- createdAt: z.number().optional(),
1939
- postingAt: z.number().optional(),
1940
- postedAt: z.number().optional(),
1941
- postedUrl: z.string().optional(),
1942
- error: z.string().optional(),
1943
- });
1944
- export const DraftSummarySchema = DraftSchema.extend({ id: entryId });
1945
- export const DraftsListSchema = z.object({ drafts: z.array(DraftSummarySchema), invalid: z.array(z.string()) });
1946
- export const DraftIdParamSchema = z.object({ id: entryId });
3133
+ platform: z
3134
+ .string()
3135
+ .min(1)
3136
+ .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."),
3137
+ actsAs: entryId
3138
+ .optional()
3139
+ .describe("Whose name it goes out under. Needed for anywhere 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."),
3140
+ content: z.string().min(1).describe("The post itself."),
3141
+ title: z.string().optional().describe("A title, where the site wants one."),
3142
+ target: z
3143
+ .string()
3144
+ .optional()
3145
+ .describe("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."),
3146
+ media: z.array(z.string()).optional().describe("Anything to attach, as workspace paths."),
3147
+ scheduledAt: z
3148
+ .number()
3149
+ .optional()
3150
+ .describe("When it should go out, in milliseconds. An agent may propose without one and you set it when approving; an approved draft with no time goes as soon as it is picked up."),
3151
+ status: DraftStatusSchema.default("proposed").describe("Where it is: proposed by the agent, approved by you, being sent, sent, or failed. Rejecting is deleting it; retrying is approving a failed one again."),
3152
+ createdAt: z.number().optional().describe("When it was written, in milliseconds."),
3153
+ postingAt: z
3154
+ .number()
3155
+ .optional()
3156
+ .describe("When sending started, in milliseconds. Needed to tell a send that is under way from one whose run died mid-flight, which the scheduled time cannot: a post due last week is not a post that has been sending since last week."),
3157
+ postedAt: z.number().optional().describe("When it went out, in milliseconds."),
3158
+ postedUrl: z
3159
+ .string()
3160
+ .optional()
3161
+ .describe("Where it landed, when the site hands back an address. The one thing a sent draft can offer that reading it cannot: the post itself, to go and look at."),
3162
+ error: z.string().optional().describe("Why it failed, written as a sentence for a person to read rather than as a code."),
3163
+ });
3164
+ export const DraftSummarySchema = DraftSchema.extend({ id: entryId.describe("The draft's id.") });
3165
+ export const DraftsListSchema = z.object({
3166
+ drafts: z.array(DraftSummarySchema).describe("The queue."),
3167
+ invalid: z
3168
+ .array(z.string())
3169
+ .describe("Drafts that could not be read at all. Listed rather than skipped, because an agent writes these files directly and a malformed one would otherwise never post and never say why."),
3170
+ });
3171
+ export const DraftIdParamSchema = z.object({ id: entryId.describe("Which draft.") });
1947
3172
  export const PanelSummarySchema = z.object({
1948
- repo: z.string(),
1949
- hasPanel: z.boolean(),
1950
- running: z.boolean(),
1951
- healthy: z.boolean(),
1952
- port: z.number().optional(),
1953
- servers: z.array(z.object({ url: z.string(), dir: z.string().optional(), session: z.string().optional() })),
1954
- previewUrl: z.string().optional(),
1955
- role: z.enum(["intent", "desired-state", "app"]).optional(),
1956
- deployConfig: z.boolean(),
1957
- desiredState: z.boolean(),
1958
- directoryUi: z.boolean(),
1959
- monorepo: z.boolean(),
1960
- vitest: z.boolean(),
1961
- userStories: z.boolean(),
1962
- docs: z.boolean(),
1963
- });
1964
- export const PanelsListSchema = z.object({ panels: z.array(PanelSummarySchema) });
1965
- export const PanelRepoParamSchema = z.object({ repo: z.string() });
3173
+ repo: z.string().describe("Which repository."),
3174
+ hasPanel: z.boolean().describe("Whether it has anything runnable at all."),
3175
+ running: z.boolean().describe("Whether the sandbox has it running."),
3176
+ healthy: z
3177
+ .boolean()
3178
+ .describe("Whether anything it owns is actually answering. A different question: a server still installing is running and not yet healthy, and one somebody started by hand is healthy without the sandbox running it."),
3179
+ port: z
3180
+ .number()
3181
+ .optional()
3182
+ .describe("The port the sandbox told it to use. What it actually bound is below, and for a repository that pins its own ports those are different numbers."),
3183
+ servers: z
3184
+ .array(z.object({
3185
+ url: z.string().describe("Where it answers, with the right scheme: a server on its own certificate is served over https."),
3186
+ dir: z
3187
+ .string()
3188
+ .optional()
3189
+ .describe("Which part of the repository it belongs to, which for a repository whose dev command fans out is the only thing telling them apart."),
3190
+ session: z
3191
+ .string()
3192
+ .optional()
3193
+ .describe("The terminal it runs in: the sandbox's when it started it, yours when you did, and absent when nothing here owns it, which is the case worth designing for."),
3194
+ }))
3195
+ .describe("Every server this repository is really serving, found by looking at what is listening. Empty when nothing answers."),
3196
+ previewUrl: z.string().optional().describe("Where to open it from outside. Absent on a sandbox with no outside address."),
3197
+ role: z
3198
+ .enum(["intent", "desired-state", "app"])
3199
+ .optional()
3200
+ .describe("Which of the workspace's three fixed roles this repository fills. Absent for one that was simply cloned in."),
3201
+ deployConfig: z.boolean().describe("It declares infrastructure."),
3202
+ desiredState: z.boolean().describe("That declaration has been resolved at least once."),
3203
+ directoryUi: z.boolean().describe("It carries a small interface of its own."),
3204
+ monorepo: z.boolean().describe("It holds several packages."),
3205
+ vitest: z.boolean().describe("It has tests that can be run."),
3206
+ userStories: z
3207
+ .boolean()
3208
+ .describe("It carries stories an agent could test the running app against. The one fact here that says nothing about the language."),
3209
+ docs: z.boolean().describe("It carries generated architecture documentation."),
3210
+ });
3211
+ export const PanelsListSchema = z.object({
3212
+ panels: z
3213
+ .array(PanelSummarySchema)
3214
+ .describe("One entry per repository, worked out in a single pass so nothing has to walk the workspace file by file."),
3215
+ });
3216
+ export const PanelRepoParamSchema = z.object({ repo: z.string().describe("Which repository.") });
1966
3217
  export const PortSummarySchema = z.object({
1967
- port: z.number(),
1968
- host: z.enum(["127.0.0.1", "::1"]),
1969
- forwardable: z.boolean(),
1970
- kind: z.enum(["workspace", "system"]),
1971
- title: z.string(),
1972
- purpose: z.string(),
1973
- origin: z.enum(["terminal", "agent", "panel", "extension", "container", "sandbox", "unknown"]),
1974
- pid: z.number().optional(),
1975
- command: z.string().optional(),
1976
- cwd: z.string().optional(),
1977
- session: z.string().optional(),
1978
- forwarded: z.boolean(),
1979
- previewUrl: z.string().optional(),
1980
- });
1981
- export const PortsListSchema = z.object({ ports: z.array(PortSummarySchema) });
1982
- export const PortParamSchema = z.object({ port: z.number().int().min(1).max(65535) });
1983
- export const PortForwardResultSchema = z.object({ previewUrl: z.string().optional() });
3218
+ port: z.number().describe("The port number."),
3219
+ host: z
3220
+ .enum(["127.0.0.1", "::1"])
3221
+ .describe("Which loopback address it actually answers on. Some tools bind only one of the two, and anything dialling it has to know which."),
3222
+ forwardable: z
3223
+ .boolean()
3224
+ .describe("Whether it can be exposed at all. Some listeners answer only at their own address and nowhere else; those are listed for honesty and refused for forwarding."),
3225
+ kind: z
3226
+ .enum(["workspace", "system"])
3227
+ .describe("Whether somebody's own work put it there, or the sandbox's own machinery did. Only the first kind is worth previewing."),
3228
+ title: z
3229
+ .string()
3230
+ .describe("What a person would call it. Always present: a listener nothing can explain is still named, because the button beside it publishes the port to the internet."),
3231
+ purpose: z.string().describe("One sentence about what it is for, including when the honest answer is that nothing could work it out."),
3232
+ origin: z
3233
+ .enum(["terminal", "agent", "panel", "extension", "container", "sandbox", "unknown"])
3234
+ .describe("Who put it there, which is the question somebody is really asking: mine, my agent's, or the box's own."),
3235
+ pid: z.number().optional().describe("The process holding it. Absent when nothing could be matched to the socket."),
3236
+ command: z.string().optional().describe("The command behind it, as it was run. Absent only when nothing could be attributed at all."),
3237
+ cwd: z.string().optional().describe("Where it is running from, which is how a port gets attributed to a repository."),
3238
+ session: z
3239
+ .string()
3240
+ .optional()
3241
+ .describe('The terminal it came from, to watch it in or stop it from. Absent when nothing in its ancestry is one, which is the honest "you cannot reach this from here".'),
3242
+ forwarded: z.boolean().describe("Whether it is currently reachable from outside."),
3243
+ previewUrl: z.string().optional().describe("Where to open it. Present only while forwarded, and only on a sandbox that has an outside address."),
3244
+ });
3245
+ export const PortsListSchema = z.object({
3246
+ ports: z
3247
+ .array(PortSummarySchema)
3248
+ .describe("Everything listening inside the sandbox right now, read fresh each time rather than from a register the sandbox keeps."),
3249
+ });
3250
+ export const PortParamSchema = z.object({ port: z.number().int().min(1).max(65535).describe("Which port.") });
3251
+ export const PortForwardResultSchema = z.object({
3252
+ previewUrl: z
3253
+ .string()
3254
+ .optional()
3255
+ .describe("Where it can now be reached. Absent on a sandbox with no outside address, where the mapping exists but has no public name."),
3256
+ });
1984
3257
  export const MachineSandboxSchema = z.object({
1985
3258
  slug: z.string(),
1986
3259
  container: z.string(),
@@ -2069,99 +3342,178 @@ export const SyncStatusSchema = z.object({
2069
3342
  machines: z.array(MachineReportSchema).optional(),
2070
3343
  });
2071
3344
  export const PublicFileSchema = z.object({
2072
- path: z.string(),
2073
- size: z.number(),
2074
- modifiedAt: z.number(),
2075
- url: z.string().optional(),
2076
- blocked: z.string().optional(),
2077
- });
2078
- export const PublicListSchema = z.object({ url: z.string().optional(), files: z.array(PublicFileSchema) });
2079
- export const PublishSchema = z.object({ path: z.string().min(1) });
2080
- export const UnpublishSchema = z.object({ path: z.string().min(1) });
2081
- export const PublishResultSchema = z.object({ path: z.string(), url: z.string().optional() });
3345
+ path: z.string().describe("Where it sits inside the outbox."),
3346
+ size: z.number().describe("Size in bytes."),
3347
+ modifiedAt: z.number().describe("When it last changed, in milliseconds."),
3348
+ url: z.string().optional().describe("Its public address. Absent when this sandbox has no outside address, or when the file is being refused."),
3349
+ blocked: z
3350
+ .string()
3351
+ .optional()
3352
+ .describe("Why a file sitting in the outbox is not being served: a hidden name, a credential-shaped name, contents that look like a token, or sheer size. Only the publisher sees this; a stranger asking for the same file gets the same nothing every other miss gets."),
3353
+ });
3354
+ export const PublicListSchema = z.object({
3355
+ url: z.string().optional().describe("Your public address, which every file's own hangs off. Absent on a sandbox with nowhere to publish to."),
3356
+ files: z.array(PublicFileSchema).describe("What the outbox holds."),
3357
+ });
3358
+ export const PublishSchema = z.object({
3359
+ path: z
3360
+ .string()
3361
+ .min(1)
3362
+ .describe("What to publish, as a workspace path. It is copied rather than moved, so a repository does not lose its build output because somebody shared it."),
3363
+ });
3364
+ export const UnpublishSchema = z.object({
3365
+ path: z.string().min(1).describe("What to withdraw, as a path inside the outbox rather than a workspace path."),
3366
+ });
3367
+ export const PublishResultSchema = z.object({
3368
+ path: z.string().describe("Where it landed inside the outbox."),
3369
+ url: z.string().optional().describe("Its public address. Absent on a sandbox with nowhere to publish to."),
3370
+ });
2082
3371
  export const ShareDetailSchema = z.enum(["messages", "everything"]);
2083
3372
  export const SharedConversationSchema = z.object({
2084
- id: z.string(),
2085
- conversationId: z.string(),
2086
- title: z.string(),
2087
- detail: ShareDetailSchema,
2088
- sharedAt: z.number(),
2089
- messages: z.number(),
2090
- url: z.string().optional(),
2091
- });
2092
- export const ShareListSchema = z.object({ shares: z.array(SharedConversationSchema) });
3373
+ id: z
3374
+ .string()
3375
+ .describe("The share's own id, minted fresh each time, so sharing one conversation twice gives two links. Deliberately not the conversation's id, which is memorable by design and would make a page's address guessable."),
3376
+ conversationId: z.string().describe("Which conversation it was taken from."),
3377
+ title: z.string().describe("The title on the page, which is the sharer's choice rather than the conversation's own."),
3378
+ detail: ShareDetailSchema.describe("How much travels: the two speakers' words alone, or the whole record including the agent's work and thinking, which necessarily publishes the code and command output in it."),
3379
+ sharedAt: z
3380
+ .number()
3381
+ .describe("When the snapshot was taken, in milliseconds. A share is frozen, so this dates what a recipient can see rather than when the conversation happened."),
3382
+ messages: z.number().describe("How many messages are behind the link."),
3383
+ url: z.string().optional().describe("The page's address. Absent on a sandbox with nowhere to publish to."),
3384
+ });
3385
+ export const ShareListSchema = z.object({ shares: z.array(SharedConversationSchema).describe("Every conversation currently published as a page.") });
2093
3386
  export const ShareCreateSchema = z.object({
2094
- conversationId: z.string().min(1),
2095
- title: z.string().min(1).max(80),
2096
- detail: ShareDetailSchema,
3387
+ conversationId: z.string().min(1).describe("Which conversation to publish."),
3388
+ title: z.string().min(1).max(80).describe("The title for the page. The conversation's own name is only what a dialog would open with."),
3389
+ detail: ShareDetailSchema.describe("How much to publish. Two levels rather than a set of switches, because every extra toggle is another thing to get wrong about a link that cannot be recalled."),
2097
3390
  });
2098
- export const ShareUpdateSchema = z.object({ id: z.string().min(1) });
2099
- export const ShareRemoveSchema = z.object({ id: z.string().min(1) });
3391
+ export const ShareUpdateSchema = z.object({
3392
+ id: z.string().min(1).describe("Which share to re-take. Its link stays the same, which matters because it has already been sent."),
3393
+ });
3394
+ export const ShareRemoveSchema = z.object({ id: z.string().min(1).describe("Which share to take down.") });
2100
3395
  export const TerminalSessionSchema = z.object({
2101
- name: z.string(),
2102
- label: z.string().optional(),
2103
- kind: z.enum(["shell", "panel", "agent", "job", "process"]),
2104
- running: z.boolean(),
2105
- activityAt: z.number(),
2106
- exitCode: z.number().optional(),
2107
- command: z.string().optional(),
2108
- extensionId: z.string().optional(),
2109
- processName: z.string().optional(),
2110
- help: z.object({ requestId: z.string(), message: z.string(), requestedAt: z.number() }).optional(),
3396
+ name: z.string().describe("Its id, and what the close route takes."),
3397
+ label: z.string().optional().describe("What to call it on screen."),
3398
+ kind: z
3399
+ .enum(["shell", "panel", "agent", "job", "process"])
3400
+ .describe("What sort of thing it is: a terminal somebody opened, a repository's dev server, where an agent's commands run, a job the sandbox started, or a background process that is watched rather than typed into."),
3401
+ running: z
3402
+ .boolean()
3403
+ .describe("Whether it is alive. A finished one-shot job leaves a dead shell behind, which reads as false and is how it gets swept up."),
3404
+ activityAt: z.number().describe("When it last produced output, in milliseconds. Zero means it did not say, which is unknown rather than 1970."),
3405
+ exitCode: z.number().optional().describe("How the last thing in it ended. Absent while that pane is still alive."),
3406
+ command: z
3407
+ .string()
3408
+ .optional()
3409
+ .describe("What is running in it right now. Absent when it is sitting at a prompt. Not a second spelling of whether it is alive: this says whether anything is happening, which is what a close button should ask about before it ends something."),
3410
+ extensionId: z.string().optional().describe("Which extension declared this process, when one did."),
3411
+ processName: z
3412
+ .string()
3413
+ .optional()
3414
+ .describe("Which of that extension's processes it is, which together with the id above addresses its start and stop routes."),
3415
+ help: z
3416
+ .object({
3417
+ requestId: z.string().describe("What to send back when you answer, through the agent reply route."),
3418
+ message: z.string().describe("What the agent needs, in its own words."),
3419
+ requestedAt: z.number().describe("When it asked, in milliseconds."),
3420
+ })
3421
+ .optional()
3422
+ .describe("The agent has stopped at something only a person can clear, and is waiting at this terminal. Present only while it is waiting."),
3423
+ });
3424
+ export const TerminalsListSchema = z.object({
3425
+ sessions: z
3426
+ .array(TerminalSessionSchema)
3427
+ .describe("Every live surface the sandbox is holding, in one list, because the question they all answer is the same one."),
2111
3428
  });
2112
- export const TerminalsListSchema = z.object({ sessions: z.array(TerminalSessionSchema) });
2113
- export const TerminalNameParamSchema = z.object({ name: z.string() });
3429
+ export const TerminalNameParamSchema = z.object({ name: z.string().describe("Which terminal.") });
2114
3430
  export const TerminalScrollbackQuerySchema = z.object({
2115
- name: z.string(),
2116
- lines: z.coerce.number().min(1).max(100_000).default(20_000),
3431
+ name: z.string().describe("Which terminal."),
3432
+ lines: z.coerce.number().min(1).max(100_000).default(20_000).describe("How far back to ask for. Clamped to the history that actually exists."),
2117
3433
  });
2118
3434
  export const TerminalScrollbackSchema = z.object({
2119
- name: z.string(),
2120
- text: z.string(),
2121
- lines: z.number(),
2122
- truncated: z.boolean(),
3435
+ name: z.string().describe("Which terminal this is from."),
3436
+ text: z.string().describe("The history, oldest line first, with wrapped lines rejoined so a copied address or path comes back whole."),
3437
+ lines: z.number().describe("How many lines you got."),
3438
+ truncated: z.boolean().describe("It stopped because you asked for that many, not because the history ran out."),
2123
3439
  });
2124
3440
  export const BrowserPageSchema = z.object({
2125
- id: z.string(),
2126
- title: z.string().optional(),
2127
- url: z.string(),
2128
- active: z.boolean(),
3441
+ id: z
3442
+ .string()
3443
+ .describe("Stable for the life of the page, which is what lets a tab survive a refresh of this list. Its address changes as the agent navigates and its position changes when a sibling closes."),
3444
+ title: z.string().optional().describe("The page's title. Absent mid-navigation, which is exactly when a tab still has to be drawn."),
3445
+ url: z.string().describe("Where it is."),
3446
+ active: z.boolean().describe("The one the agent last touched, or for a finished session, the one it ended on. Exactly one page has this."),
2129
3447
  });
2130
3448
  export const BrowserSessionSchema = z.object({
2131
- name: z.string(),
2132
- label: z.string(),
2133
- server: z.string(),
2134
- running: z.boolean(),
2135
- activityAt: z.number(),
2136
- finishedAt: z.number().optional(),
2137
- help: z.object({ requestId: z.string(), message: z.string(), requestedAt: z.number() }).optional(),
2138
- pages: z.array(BrowserPageSchema),
3449
+ name: z.string().describe("Its id, and what the close route takes."),
3450
+ label: z.string().describe("What to call it on screen: the open page's title, or its site, or which browser this is."),
3451
+ server: z
3452
+ .string()
3453
+ .describe("Which browser drives it: the credential-free one, or a signed-in account's. The difference between a throwaway page and one logged in as you, which is worth saying out loud."),
3454
+ running: z
3455
+ .boolean()
3456
+ .describe("Whether it is still open. A closed one is listed for a while with the pages it had, as the record of where the agent went."),
3457
+ activityAt: z.number().describe("When it last did anything, in milliseconds."),
3458
+ finishedAt: z.number().optional().describe("When it closed, in milliseconds. Absent while it is open."),
3459
+ help: z
3460
+ .object({
3461
+ requestId: z.string().describe("What to send back when you answer, through the agent reply route."),
3462
+ message: z.string().describe("What the agent needs, in its own words."),
3463
+ requestedAt: z.number().describe("When it asked, in milliseconds."),
3464
+ })
3465
+ .optional()
3466
+ .describe("The agent has hit something only a person can clear: a captcha, a password it does not hold, a check on your phone. Present only while it is waiting."),
3467
+ pages: z
3468
+ .array(BrowserPageSchema)
3469
+ .describe("Every page it has open. A browser holds several at once, which is the reason it is listed apart from the terminals."),
2139
3470
  });
2140
- export const BrowsersListSchema = z.object({ sessions: z.array(BrowserSessionSchema) });
2141
- export const BrowserNameParamSchema = z.object({ name: z.string() });
3471
+ export const BrowsersListSchema = z.object({
3472
+ sessions: z.array(BrowserSessionSchema).describe("Every browser the agents have running, open or recently closed."),
3473
+ });
3474
+ export const BrowserNameParamSchema = z.object({ name: z.string().describe("Which browser.") });
2142
3475
  export const SubagentKindSchema = z.enum(["subagent", "codex", "grok"]);
2143
3476
  export const SubagentStatusSchema = z.enum(["pending", "running", "blocked", "completed", "failed", "killed", "paused"]);
2144
3477
  export const SubagentSessionSchema = z.object({
2145
- id: z.string(),
2146
- kind: SubagentKindSchema,
2147
- conversationId: z.string(),
2148
- agentType: z.string().optional(),
2149
- description: z.string().optional(),
2150
- model: z.string().optional(),
2151
- spawnDepth: z.number().optional(),
2152
- background: z.boolean().optional(),
2153
- status: SubagentStatusSchema,
2154
- startedAt: z.number(),
2155
- endedAt: z.number().optional(),
2156
- activityAt: z.number(),
2157
- tokens: z.number().optional(),
2158
- toolUses: z.number().optional(),
2159
- lastTool: z.string().optional(),
2160
- summary: z.string().optional(),
2161
- error: z.string().optional(),
2162
- terminal: z.string().optional(),
3478
+ id: z
3479
+ .string()
3480
+ .describe("The id of the tool call that started it, which every side already holds, so a card links to its helper with the id it has and the helper points back the same way."),
3481
+ kind: SubagentKindSchema.describe("What sort of helper: one the runtime spawned, or a separate tool the agent drove from a shell. It changes only how you watch it."),
3482
+ conversationId: z.string().describe("The conversation whose turn started it, and the way back to the chat it belongs to."),
3483
+ agentType: z.string().optional().describe("What kind of helper it is."),
3484
+ description: z.string().optional().describe("What it was asked to do, in one line."),
3485
+ model: z.string().optional().describe("Which model it runs on."),
3486
+ spawnDepth: z
3487
+ .number()
3488
+ .optional()
3489
+ .describe("How deep in the chain it sits, where one means the turn itself started it. A helper can start helpers, and a flat list that could not say so would read as though the turn started all of them."),
3490
+ background: z
3491
+ .boolean()
3492
+ .optional()
3493
+ .describe("The parent carried on working instead of waiting for it. This is the whole reason the list exists: such a helper used to be invisible until its result landed, sometimes minutes later."),
3494
+ status: SubagentStatusSchema.describe("How it is going. Blocked means it needs an answer, which a parent and an operator act on differently from it simply working."),
3495
+ startedAt: z.number().describe("When it started, in milliseconds."),
3496
+ endedAt: z.number().optional().describe("When it finished, in milliseconds. Absent while it works."),
3497
+ activityAt: z.number().describe("When it last did anything, in milliseconds."),
3498
+ tokens: z
3499
+ .number()
3500
+ .optional()
3501
+ .describe("What it has spent. Its own, so a parent's cost and the sum of its helpers' are two different true numbers."),
3502
+ toolUses: z.number().optional().describe("How many tools it has used."),
3503
+ lastTool: z.string().optional().describe("The last one it reached for."),
3504
+ summary: z
3505
+ .string()
3506
+ .optional()
3507
+ .describe("Its report: what it concluded, without opening its record. The question a finished helper gets read for."),
3508
+ error: z.string().optional().describe("Why it failed, when it did."),
3509
+ terminal: z
3510
+ .string()
3511
+ .optional()
3512
+ .describe("The terminal its command runs in, when there is one. Absent for a helper with no process of its own, which is watched by reading its record instead."),
3513
+ });
3514
+ export const SubagentsListSchema = z.object({
3515
+ sessions: z.array(SubagentSessionSchema).describe("Every helper this sandbox's conversations have started."),
2163
3516
  });
2164
- export const SubagentsListSchema = z.object({ sessions: z.array(SubagentSessionSchema) });
2165
3517
  export const SubagentIdParamSchema = z.object({ id: z.string() });
2166
3518
  const environmentFileSchema = z.object({ content: z.string(), hash: z.string() });
2167
3519
  export const EnvironmentSchema = z.object({
@@ -2257,109 +3609,203 @@ export const SecretSetSchema = z.object({
2257
3609
  key: z
2258
3610
  .string()
2259
3611
  .regex(/^[A-Za-z_][A-Za-z0-9_]*$/)
2260
- .max(128),
2261
- value: z.string().min(1),
3612
+ .max(128)
3613
+ .describe("The name to store it under, which is the name a process will find it by."),
3614
+ value: z.string().min(1).describe("The value. It goes straight to your sandbox and never through the platform."),
2262
3615
  });
2263
- export const SecretKeysSchema = z.object({ keys: z.array(z.string()) });
2264
- export const SecretKeyParamSchema = z.object({ key: z.string() });
2265
- export const SecretRevealSchema = z.object({ value: z.string() });
3616
+ export const SecretKeysSchema = z.object({
3617
+ keys: z.array(z.string()).describe("The names that exist here. Only the names: the values never leave the sandbox."),
3618
+ });
3619
+ export const SecretKeyParamSchema = z.object({ key: z.string().describe("Which secret, by name.") });
3620
+ export const SecretRevealSchema = z.object({ value: z.string().describe("The value itself. The only place in this API one is ever returned.") });
2266
3621
  export const SecretInventoryEntrySchema = z.object({
2267
- key: z.string(),
2268
- kind: z.enum(["env", "generated", "capability", "provider"]),
2269
- label: z.string().optional(),
2270
- status: z.enum(["missing", "set", "connected"]),
2271
- requiredBy: z.array(z.object({ resourceId: z.string(), type: z.string() })),
2272
- storedAt: z.string(),
2273
- revealable: z.boolean(),
2274
- ci: z.object({ synced: z.boolean(), pushedAt: z.string().optional() }).optional(),
2275
- lastUse: z.object({ at: z.number(), lane: z.enum(["shell", "code", "browser"]), detail: z.string().optional() }).optional(),
2276
- });
2277
- export const SecretInventorySchema = z.object({ entries: z.array(SecretInventoryEntrySchema) });
3622
+ key: z.string().describe("What identifies it. Unique across the whole inventory, so several accounts of one provider each get their own entry."),
3623
+ kind: z
3624
+ .enum(["env", "generated", "capability", "provider"])
3625
+ .describe("Where it came from: you set it, the sandbox generated it, a connection needs it, or it is a model account's credential."),
3626
+ label: z.string().optional().describe("A friendlier name, for entries that have one."),
3627
+ status: z.enum(["missing", "set", "connected"]).describe("Whether it exists and, for a connection, whether it is working."),
3628
+ requiredBy: z
3629
+ .array(z.object({ resourceId: z.string().describe("Which resource."), type: z.string().describe("What kind of resource it is.") }))
3630
+ .describe("What is waiting on it. Empty for a connection's or an account's own credential."),
3631
+ storedAt: z.string().describe("Where it actually lives, in words."),
3632
+ revealable: z.boolean().describe("Whether its value can be shown at all. Everything except a model account's credential can be."),
3633
+ ci: z
3634
+ .object({
3635
+ synced: z.boolean().describe("Whether the pipeline has it."),
3636
+ pushedAt: z.string().optional().describe("When it was last sent there."),
3637
+ })
3638
+ .optional()
3639
+ .describe("Whether a copy has been given to the build pipeline."),
3640
+ lastUse: z
3641
+ .object({
3642
+ at: z.number().describe("When, in milliseconds."),
3643
+ lane: z.enum(["shell", "code", "browser"]).describe("How it was used: a command, a script, or typed into a page."),
3644
+ detail: z
3645
+ .string()
3646
+ .optional()
3647
+ .describe("Where it went: the start of the command or script, or the site. Names and destinations only, never values."),
3648
+ })
3649
+ .optional()
3650
+ .describe("The last time an agent actually spent this secret. Absent while it never has been, which most never are."),
3651
+ });
3652
+ export const SecretInventorySchema = z.object({
3653
+ entries: z
3654
+ .array(SecretInventoryEntrySchema)
3655
+ .describe("One entry per secret this sandbox knows about, from every place they live. No values, ever."),
3656
+ });
2278
3657
  export const AdapterHealthSchema = z.object({
2279
- state: z.enum(["ready", "unavailable", "unknown"]),
2280
- detail: z.string().optional(),
2281
- checkedAt: z.number(),
3658
+ state: z
3659
+ .enum(["ready", "unavailable", "unknown"])
3660
+ .describe("Whether this runtime can serve a turn. Unknown is a real answer rather than a soft no: a check that could not run must not grey out a provider you can in fact use."),
3661
+ detail: z.string().optional().describe("Why it cannot, and what to do about it. Absent when it can."),
3662
+ checkedAt: z.number().describe("When it was last checked, in milliseconds."),
2282
3663
  });
2283
3664
  export const StagedUpdateSchema = z.object({
2284
- version: z.string().optional(),
2285
- channel: z.string(),
2286
- at: z.number(),
3665
+ version: z.string().optional().describe("What the downloaded build says it is. Absent means ready but unnamed, never that nothing is ready."),
3666
+ channel: z
3667
+ .string()
3668
+ .describe("Which channel it was taken from. Not necessarily the one this sandbox follows: downloading a beta build is not the same as moving onto beta."),
3669
+ at: z.number().describe("When the download finished, in milliseconds, which answers whether this is still the update being offered."),
2287
3670
  });
2288
3671
  export const InfoSchema = z.object({
2289
- name: z.string().optional(),
2290
- image: z.string().optional(),
2291
- version: z.string().optional(),
2292
- latest: z.string().optional(),
2293
- updateAvailable: z.boolean().optional(),
2294
- runtimes: z.record(z.string(), AdapterHealthSchema).optional(),
2295
- channel: z.string().optional(),
2296
- previousImage: z.string().optional(),
2297
- updateNotes: z.array(z.string()).optional(),
2298
- moreUpdateNotes: z.number().optional(),
2299
- breakingNotes: z.array(z.string()).optional(),
2300
- staged: StagedUpdateSchema.optional(),
3672
+ name: z.string().optional().describe("What this sandbox is called."),
3673
+ image: z.string().optional().describe("The image it is running."),
3674
+ version: z.string().optional().describe("The version of that image."),
3675
+ latest: z.string().optional().describe("The newest published version on its channel."),
3676
+ updateAvailable: z.boolean().optional().describe("Whether those two differ."),
3677
+ runtimes: z
3678
+ .record(z.string(), AdapterHealthSchema)
3679
+ .optional()
3680
+ .describe("Which agent runtimes can serve a turn right now, keyed by runtime. Absent until the first check has run, which reads the same as every entry being unknown."),
3681
+ channel: z.string().optional().describe("Which release channel this sandbox follows."),
3682
+ previousImage: z
3683
+ .string()
3684
+ .optional()
3685
+ .describe("The image the last update replaced, which is what a rollback would return to. Absent means there is nothing to go back to."),
3686
+ updateNotes: z
3687
+ .array(z.string())
3688
+ .optional()
3689
+ .describe("What is in the update, in the words of the people it is for, newest first. Absent or empty whenever there is nothing worth saying, which reads on screen exactly as it did before there were notes at all."),
3690
+ moreUpdateNotes: z
3691
+ .number()
3692
+ .optional()
3693
+ .describe("How many further notes there are beyond the ones sent, for a sandbox left alone a long time. Absent or zero means you have all of them."),
3694
+ breakingNotes: z
3695
+ .array(z.string())
3696
+ .optional()
3697
+ .describe("What the update takes away, uncapped, because a warning that fell off a shortened list is a breaking update taken unwarned. Absent for the overwhelming majority, which break nothing."),
3698
+ staged: StagedUpdateSchema.optional().describe("An update already downloaded and built on the machine running this container, waiting only for the restart that applies it. That restart is seconds, where an unprepared update is minutes, which is a different decision entirely. Absent when nothing is waiting."),
2301
3699
  });
2302
3700
  export const ManifestProblemSchema = z.object({
2303
- kind: z.enum(["unreadable", "unknownKey", "invalidEntry"]),
2304
- detail: z.string(),
2305
- suggestion: z.string().optional(),
3701
+ kind: z
3702
+ .enum(["unreadable", "unknownKey", "invalidEntry"])
3703
+ .describe("What to do about it. Unreadable means the whole file is being ignored and everything in it is at its default. An unknown key means only that key is ignored. An invalid entry means one item of a list was skipped and the rest is fine."),
3704
+ detail: z.string().describe("What exactly was wrong."),
3705
+ suggestion: z.string().optional().describe("The name it was probably meant to be, when one is close enough to guess honestly."),
3706
+ });
3707
+ export const ManifestProblemReportSchema = z.object({
3708
+ path: z.string().describe("The file, as a workspace path. The file is the unit somebody fixes, which is why problems are grouped by it."),
3709
+ problems: z
3710
+ .array(ManifestProblemSchema)
3711
+ .describe("Everything currently wrong with it. A file with nothing wrong is absent rather than present and empty."),
2306
3712
  });
2307
- export const ManifestProblemReportSchema = z.object({ path: z.string(), problems: z.array(ManifestProblemSchema) });
2308
3713
  export const ManifestProblemsSchema = z.array(ManifestProblemReportSchema);
2309
- export const DaemonSessionSchema = z.object({ token: z.string(), expiresAt: z.number(), email: z.string() });
3714
+ export const DaemonSessionSchema = z.object({
3715
+ token: z.string().describe("The credential every other call carries. Present it as a bearer token."),
3716
+ expiresAt: z.number().describe("When it stops working, in milliseconds, so a caller can renew ahead of it without reading the token."),
3717
+ email: z.string().describe("Who the sandbox verified you as."),
3718
+ });
2310
3719
  export const ActivityEventSchema = z.object({
2311
- id: z.string(),
2312
- at: z.number(),
2313
- provider: z.string().optional(),
2314
- account: z.string().optional(),
2315
- direction: z.enum(["in", "out", "system"]),
2316
- type: z.string(),
2317
- channelId: z.string().optional(),
2318
- author: z.string().optional(),
2319
- content: z.string().optional(),
2320
- method: z.string().optional(),
2321
- endpoint: z.string().optional(),
2322
- sessionId: z.string().optional(),
2323
- turnId: z.string().optional(),
2324
- conversationId: z.string().optional(),
2325
- title: z.string().optional(),
2326
- origin: AgentOriginSchema.optional(),
2327
- automationIds: z.array(z.string()).optional(),
2328
- outcome: z.enum(["ok", "error"]).optional(),
2329
- error: z.string().optional(),
2330
- extra: z.record(z.string(), z.unknown()).optional(),
3720
+ id: z.string().describe("The entry's own id."),
3721
+ at: z.number().describe("When it happened, in milliseconds. Also what you page by."),
3722
+ provider: z.string().optional().describe("Which outside service, when one was involved. Absent for the sandbox's own events."),
3723
+ account: z
3724
+ .string()
3725
+ .optional()
3726
+ .describe("Which account handled it. Absent for the sandbox's own events and for work run on a provider's default."),
3727
+ direction: z.enum(["in", "out", "system"]).describe("Whether something arrived, something went out, or the sandbox did it to itself."),
3728
+ type: z
3729
+ .string()
3730
+ .describe("Exactly what happened: a message received or sent, a reaction, a turn starting or ending, a rule doing something. A rule that ran and passed says nothing here, because a feed of green ticks is one the eye learns to skip."),
3731
+ channelId: z.string().optional().describe("Which channel or thread it happened in."),
3732
+ author: z.string().optional().describe("Who sent it, for something that arrived."),
3733
+ content: z.string().optional().describe("The message, in full, whichever direction it went."),
3734
+ method: z.string().optional().describe("The verb of an outgoing call."),
3735
+ endpoint: z.string().optional().describe("The address of an outgoing call. Credentials travel in headers, so they are never here."),
3736
+ sessionId: z.string().optional().describe("The provider session behind it."),
3737
+ turnId: z
3738
+ .string()
3739
+ .optional()
3740
+ .describe("Ties one turn's entries together. A turn writes several, and read as separate rows they say one thing several times, so a feed groups on this."),
3741
+ conversationId: z
3742
+ .string()
3743
+ .optional()
3744
+ .describe("Which conversation. This, rather than the provider session, is what the same agent means across a feed, because a session is retired whenever the model changes."),
3745
+ title: z
3746
+ .string()
3747
+ .optional()
3748
+ .describe("What that conversation was called at the time. Copied in rather than looked up, because an audit entry must still read as words years later, after the conversation has been renamed or pruned."),
3749
+ origin: AgentOriginSchema.optional().describe("What woke the conversation from outside, when something did. It is how a turn gets filed under the chat service that caused it rather than under the model that served it."),
3750
+ automationIds: z.array(z.string()).optional().describe("Which automations were involved."),
3751
+ outcome: z.enum(["ok", "error"]).optional().describe("How it ended."),
3752
+ error: z.string().optional().describe("What went wrong, when something did."),
3753
+ extra: z
3754
+ .record(z.string(), z.unknown())
3755
+ .optional()
3756
+ .describe("Whatever else the source had to say: attachments, participants, a recording's path. Shape varies by source."),
2331
3757
  });
2332
3758
  export const ActivityQuerySchema = z.object({
2333
- provider: z.string().optional(),
2334
- limit: z.coerce.number().min(1).max(500).default(100),
2335
- before: z.coerce.number().optional(),
3759
+ provider: z.string().optional().describe("Narrow it to one outside service."),
3760
+ limit: z.coerce.number().min(1).max(500).default(100).describe("How many entries to return."),
3761
+ before: z.coerce.number().optional().describe("Only entries older than this timestamp, so paging walks backwards through the feed."),
2336
3762
  });
2337
- export const ActivityListSchema = z.object({ events: z.array(ActivityEventSchema) });
3763
+ export const ActivityListSchema = z.object({ events: z.array(ActivityEventSchema).describe("The audit entries, newest first.") });
2338
3764
  export const ActivityConnectionSchema = z.object({
2339
- capabilityId: z.string(),
2340
- provider: z.string(),
2341
- gateway: z.enum(["ready", "connecting", "pairing", "disconnected", "idle"]),
2342
- lastError: z.string().optional(),
3765
+ capabilityId: z.string().describe("Which connection."),
3766
+ provider: z.string().describe("Which service it is."),
3767
+ gateway: z
3768
+ .enum(["ready", "connecting", "pairing", "disconnected", "idle"])
3769
+ .describe("Idle means it is up but has nothing to listen for, which is different from a connection that should be up and is not. Pairing means somebody started a sign-in and never finished it, which no amount of waiting will fix."),
3770
+ lastError: z.string().optional().describe("The most recent thing that went wrong on it."),
2343
3771
  });
2344
3772
  export const ActivityStatusSchema = z.object({
2345
- connections: z.array(ActivityConnectionSchema),
2346
- voice: z.object({ channelId: z.string(), channelName: z.string(), startedAt: z.number(), participants: z.array(z.string()) }).optional(),
3773
+ connections: z
3774
+ .array(ActivityConnectionSchema)
3775
+ .describe("Each source feeding the record, and whether it is working. Probed now rather than remembered."),
3776
+ voice: z
3777
+ .object({
3778
+ channelId: z.string().describe("Which channel."),
3779
+ channelName: z.string().describe("What it is called."),
3780
+ startedAt: z.number().describe("When it joined, in milliseconds."),
3781
+ participants: z.array(z.string()).describe("Who else is in it."),
3782
+ })
3783
+ .optional()
3784
+ .describe("A voice call the sandbox is currently in, when it is in one."),
2347
3785
  });
2348
3786
  export const UsageTurnSchema = z.object({
2349
- at: z.number(),
2350
- day: z.string(),
2351
- provider: z.string(),
2352
- account: z.string().optional(),
2353
- model: z.string().optional(),
2354
- harness: z.string(),
2355
- conversationId: z.string().optional(),
2356
- turns: z.number(),
2357
- inputTokens: z.number(),
2358
- outputTokens: z.number(),
2359
- cacheReadTokens: z.number(),
2360
- cacheCreationTokens: z.number(),
2361
- costUsd: z.number(),
2362
- durationMs: z.number(),
3787
+ at: z.number().describe("When the turn ended, in milliseconds."),
3788
+ day: z.string().describe("The day it fell in, as YYYY-MM-DD in UTC, worked out once so nothing downstream has to do timezone arithmetic."),
3789
+ provider: z.string().describe("Which model provider served it."),
3790
+ account: z.string().optional().describe("Which account paid. Absent for a turn run on a plain key, which belongs to no account."),
3791
+ model: z
3792
+ .string()
3793
+ .optional()
3794
+ .describe("The model that actually ran, past whatever was asked for and every default. Absent only when the provider's own default served it without being named."),
3795
+ harness: z.string().describe("Which agentic loop it ran on."),
3796
+ conversationId: z
3797
+ .string()
3798
+ .optional()
3799
+ .describe("Which conversation it belonged to, so spending can be traced to a card. Absent only for an internal one-off with no conversation at all."),
3800
+ turns: z
3801
+ .number()
3802
+ .describe("The provider's own count for the request, since one exchange can be several under the hood. One when it reported none."),
3803
+ inputTokens: z.number().describe("Tokens sent."),
3804
+ outputTokens: z.number().describe("Tokens received."),
3805
+ cacheReadTokens: z.number().describe("Tokens served from cache, which cost less."),
3806
+ cacheCreationTokens: z.number().describe("Tokens written to cache, which cost more up front and less afterwards."),
3807
+ costUsd: z.number().describe("What it cost, in dollars."),
3808
+ durationMs: z.number().describe("How long it took, in milliseconds."),
2363
3809
  terse: z.boolean().optional(),
2364
3810
  iqSearchArm: z.boolean().optional(),
2365
3811
  iqSearchCohort: z.string().optional(),
@@ -2371,25 +3817,32 @@ export const UsageTurnSchema = z.object({
2371
3817
  tierRouted: z.boolean().optional(),
2372
3818
  });
2373
3819
  export const UsageRollupRowSchema = z.object({
2374
- day: z.string(),
2375
- provider: z.string(),
2376
- account: z.string().optional(),
2377
- model: z.string().optional(),
2378
- harness: z.string(),
2379
- conversationId: z.string().optional(),
2380
- turns: z.number(),
2381
- inputTokens: z.number(),
2382
- outputTokens: z.number(),
2383
- cacheReadTokens: z.number(),
2384
- cacheCreationTokens: z.number(),
2385
- costUsd: z.number(),
2386
- durationMs: z.number(),
3820
+ day: z.string().describe("The day, as YYYY-MM-DD in UTC."),
3821
+ provider: z.string().describe("Which model provider."),
3822
+ account: z.string().optional().describe("Which account. Absent for work run on a plain key."),
3823
+ model: z.string().optional().describe("Which model."),
3824
+ harness: z.string().describe("Which agentic loop."),
3825
+ conversationId: z.string().optional().describe("Which conversation."),
3826
+ turns: z.number().describe("Turns in this group."),
3827
+ inputTokens: z.number().describe("Tokens sent."),
3828
+ outputTokens: z.number().describe("Tokens received."),
3829
+ cacheReadTokens: z.number().describe("Tokens served from cache."),
3830
+ cacheCreationTokens: z.number().describe("Tokens written to cache."),
3831
+ costUsd: z.number().describe("What the group cost, in dollars."),
3832
+ durationMs: z.number().describe("Time spent, in milliseconds."),
2387
3833
  });
2388
3834
  export const DayWindowQuerySchema = z.object({
2389
- from: z.string().optional(),
2390
- to: z.string().optional(),
3835
+ from: z.string().optional().describe("First day to include, as YYYY-MM-DD in UTC. Leave it out for everything up to the end day."),
3836
+ to: z
3837
+ .string()
3838
+ .optional()
3839
+ .describe("Last day to include, as YYYY-MM-DD in UTC, and it is included rather than excluded. Leave it out for everything from the start day onwards."),
3840
+ });
3841
+ export const UsageRollupSchema = z.object({
3842
+ rows: z
3843
+ .array(UsageRollupRowSchema)
3844
+ .describe("Spending grouped by day, provider, account, model and conversation. Everything a cost screen shows is a rearrangement of these rows, which is why there is no second call for any of it."),
2391
3845
  });
2392
- export const UsageRollupSchema = z.object({ rows: z.array(UsageRollupRowSchema) });
2393
3846
  export const UsageAccountSchema = z.object({
2394
3847
  provider: z.string(),
2395
3848
  account: z.string(),
@@ -2402,94 +3855,178 @@ export const UsageAccountSchema = z.object({
2402
3855
  });
2403
3856
  export const UsageSummarySchema = z.object({ accounts: z.array(UsageAccountSchema) });
2404
3857
  export const LogFileEntrySchema = z.object({
2405
- name: z.string(),
2406
- sizeBytes: z.number(),
2407
- modifiedAt: z.number(),
3858
+ name: z.string().describe("Its name, which is what the read route takes."),
3859
+ sizeBytes: z.number().describe("Size in bytes."),
3860
+ modifiedAt: z.number().describe("When it last changed, in milliseconds."),
3861
+ });
3862
+ export const LogsListSchema = z.object({
3863
+ files: z.array(LogFileEntrySchema).describe("Every log the sandbox keeps: captured terminal output, command runs, and its own log."),
2408
3864
  });
2409
- export const LogsListSchema = z.object({ files: z.array(LogFileEntrySchema) });
2410
3865
  export const LogReadQuerySchema = z.object({
2411
- name: z.string().min(1),
2412
- bytes: z.coerce.number().min(1).max(1_048_576).default(65_536),
3866
+ name: z.string().min(1).describe("Which log. It travels in the query rather than the address, because log names contain slashes."),
3867
+ bytes: z.coerce
3868
+ .number()
3869
+ .min(1)
3870
+ .max(1_048_576)
3871
+ .default(65_536)
3872
+ .describe("How much of the end to read. The newest bytes win when the file is larger."),
2413
3873
  });
2414
3874
  export const LogReadSchema = z.object({
2415
- name: z.string(),
2416
- sizeBytes: z.number(),
2417
- text: z.string(),
2418
- truncated: z.boolean(),
3875
+ name: z.string().describe("Which log this is from."),
3876
+ sizeBytes: z.number().describe("How large the whole file is."),
3877
+ text: z.string().describe("The end of it, as text."),
3878
+ truncated: z.boolean().describe("There is more before what you got."),
2419
3879
  });
2420
3880
  export const PresenceReportSchema = z.object({
2421
- clientId: z.string(),
2422
- idle: z.boolean(),
2423
- view: z.string().optional(),
2424
- sessionId: z.string().optional(),
2425
- path: z.string().optional(),
3881
+ clientId: z.string().describe("This connection's own id, the same one it gave the event stream."),
3882
+ idle: z.boolean().describe("Whether the person has stopped doing anything."),
3883
+ view: z.string().optional().describe("Which view they are on."),
3884
+ sessionId: z.string().optional().describe("Which conversation they have open."),
3885
+ path: z
3886
+ .string()
3887
+ .optional()
3888
+ .describe("Which file they are looking at. Sent whole rather than merged: leaving a field out clears it, so a tab that closes a file drops the path in the same report."),
2426
3889
  });
2427
3890
  export const WebPushChannelSchema = z.object({
2428
- kind: z.literal("webpush"),
2429
- endpoint: z.url(),
2430
- keys: z.object({
2431
- p256dh: z.string().min(1),
2432
- auth: z.string().min(1),
2433
- }),
3891
+ kind: z.literal("webpush").describe("A browser, which the sandbox can reach directly and encrypt end to end."),
3892
+ endpoint: z.url().describe("Where that browser's push service accepts sends. It also identifies the device everywhere else in this group."),
3893
+ keys: z
3894
+ .object({
3895
+ p256dh: z.string().min(1).describe("The browser's public key, for encrypting what is sent."),
3896
+ auth: z.string().min(1).describe("The browser's secret, for the same."),
3897
+ })
3898
+ .describe("What the browser handed you when it subscribed. Post it back exactly as it came; nothing reshapes it."),
2434
3899
  });
2435
3900
  export const RelayChannelSchema = z.object({
2436
- kind: z.literal("relay"),
2437
- url: z.url(),
2438
- deviceId: z.string().min(1),
2439
- secret: z.string().min(1),
3901
+ kind: z
3902
+ .literal("relay")
3903
+ .describe("A native app, whose operating system only accepts sends from the app's publisher, so the sandbox posts through a relay instead. The message passes through that relay readable, which is the price of the publisher having to be in the loop."),
3904
+ url: z.url().describe("Where to post a send. Recorded rather than assumed, so the sandbox need not know any platform by name."),
3905
+ deviceId: z.string().min(1).describe("The device's id, which also identifies this registration everywhere else in this group."),
3906
+ secret: z.string().min(1).describe("Proof that this sandbox may notify this device. The relay never learns which sandbox is calling."),
2440
3907
  });
2441
3908
  export const PushChannelSchema = z.discriminatedUnion("kind", [WebPushChannelSchema, RelayChannelSchema]);
2442
3909
  export const channelId = (channel) => (channel.kind === "webpush" ? channel.endpoint : channel.deviceId);
2443
3910
  export const PushNotificationSchema = z.object({
2444
- title: z.string().min(1),
2445
- body: z.string(),
2446
- url: z.string().optional(),
2447
- tag: z.string().optional(),
2448
- requireInteraction: z.boolean().optional(),
2449
- });
2450
- export const PushConfigSchema = z.object({ publicKey: z.string(), subscribed: z.boolean() });
2451
- export const PushChannelIdSchema = z.object({ id: z.string().min(1) });
2452
- export const PushConfigQuerySchema = z.object({ id: z.string().min(1).optional() });
2453
- export const PushTestSchema = z.object({ delivered: z.number().int().nonnegative() });
3911
+ title: z.string().min(1).describe("The headline."),
3912
+ body: z
3913
+ .string()
3914
+ .describe("The line under it. Push services cap the whole payload at a few kilobytes, which is why nothing here carries a transcript or a diff: a notification is a pointer back, not a delivery."),
3915
+ url: z.string().optional().describe("Where tapping it goes. An existing tab is focused rather than a new one opened."),
3916
+ tag: z
3917
+ .string()
3918
+ .optional()
3919
+ .describe("Collapses repeats: a second notification with the same tag replaces the first instead of stacking beside it."),
3920
+ requireInteraction: z
3921
+ .boolean()
3922
+ .optional()
3923
+ .describe("Keep it on screen until it is dismissed. Used when the agent is waiting for you, where one that fades away is a question that went unanswered in silence."),
3924
+ });
3925
+ export const PushConfigSchema = z.object({
3926
+ publicKey: z.string().describe("The key a browser needs in order to subscribe. Native apps ignore it."),
3927
+ subscribed: z
3928
+ .boolean()
3929
+ .describe("Whether the asking device is already registered, so a toggle can show its real state instead of trusting the device's own permission, which can be granted with nothing behind it."),
3930
+ });
3931
+ export const PushChannelIdSchema = z.object({
3932
+ id: z.string().min(1).describe("Which device: a browser's push address, or a native install's device id."),
3933
+ });
3934
+ export const PushConfigQuerySchema = z.object({
3935
+ id: z
3936
+ .string()
3937
+ .min(1)
3938
+ .optional()
3939
+ .describe("Which device is asking. Without it the answer can only speak for the sandbox as a whole, which is rarely the question."),
3940
+ });
3941
+ export const PushTestSchema = z.object({
3942
+ delivered: z
3943
+ .number()
3944
+ .int()
3945
+ .nonnegative()
3946
+ .describe("How many devices actually accepted it. A count rather than a yes, because this button exists to prove a chain nobody can inspect, and the sandbox having accepted the request is not the question being asked."),
3947
+ });
2454
3948
  export const PROBE_IDS = ["outdated", "audit", "knip", "jscpd", "ui", "bundle"];
2455
3949
  export const ProbeIdSchema = z.enum(PROBE_IDS);
2456
3950
  export const OutdatedPackageSchema = z.object({
2457
- name: z.string(),
2458
- current: z.string(),
2459
- latest: z.string(),
2460
- kind: z.enum(["major", "minor", "patch"]),
2461
- section: z.string(),
3951
+ name: z.string().describe("The dependency."),
3952
+ current: z.string().describe("What you are on."),
3953
+ latest: z.string().describe("What is published."),
3954
+ kind: z
3955
+ .enum(["major", "minor", "patch"])
3956
+ .describe("How far apart those are. This is not one number because forty patch releases behind is a morning's work and one major version is a project."),
3957
+ section: z
3958
+ .string()
3959
+ .describe("Which part of the manifest declares it. A major version behind on a build-time tool is a different risk from one that ships."),
2462
3960
  });
2463
3961
  export const AdvisorySchema = z.object({
2464
- name: z.string(),
2465
- severity: z.enum(["critical", "high", "moderate", "low", "info"]),
2466
- title: z.string(),
2467
- patched: z.string().optional(),
2468
- dev: z.boolean(),
3962
+ name: z.string().describe("The dependency it concerns."),
3963
+ severity: z.enum(["critical", "high", "moderate", "low", "info"]).describe("How bad it is said to be."),
3964
+ title: z
3965
+ .string()
3966
+ .describe("What it is, in one line. No scoring vector and no reference list: those are for reading on the advisory's own page, and carrying them would put a kilobyte of prose per finding on every poll."),
3967
+ patched: z
3968
+ .string()
3969
+ .optional()
3970
+ .describe("Which versions fix it. Absent means no fix has been published, which is exactly when nothing should offer to upgrade and something should say so instead."),
3971
+ dev: z.boolean().describe("Whether it only reaches build-time tooling, which is a different problem from one that reaches what you ship."),
2469
3972
  });
2470
3973
  export const DeadCodeSchema = z.object({
2471
- files: z.number().int().nonnegative(),
2472
- exports: z.number().int().nonnegative(),
2473
- types: z.number().int().nonnegative(),
2474
- dependencies: z.number().int().nonnegative(),
2475
- devDependencies: z.number().int().nonnegative(),
2476
- sample: z.array(z.string()),
3974
+ files: z.number().int().nonnegative().describe("Files nothing reaches."),
3975
+ exports: z.number().int().nonnegative().describe("Exported things nothing uses."),
3976
+ types: z.number().int().nonnegative().describe("Types nothing uses."),
3977
+ dependencies: z.number().int().nonnegative().describe("Declared dependencies nothing imports."),
3978
+ devDependencies: z.number().int().nonnegative().describe("The same, for build-time ones."),
3979
+ sample: z
3980
+ .array(z.string())
3981
+ .describe("A handful of the files, so a reader need not take the count on faith. Counts and a sample rather than the whole list, because an agent re-measures against the live tree anyway."),
2477
3982
  });
2478
3983
  export const DuplicationSchema = z.object({
2479
- percentage: z.number(),
2480
- clones: z.number().int().nonnegative(),
2481
- top: z.array(z.object({ lines: z.number().int().nonnegative(), first: z.string(), second: z.string() })),
3984
+ percentage: z
3985
+ .number()
3986
+ .describe("How much of the scanned code is duplicated. A share rather than a count, because a count grows with the repository and would mean something different every quarter."),
3987
+ clones: z.number().int().nonnegative().describe("How many duplicated stretches were found."),
3988
+ top: z
3989
+ .array(z.object({
3990
+ lines: z.number().int().nonnegative().describe("How long the duplicated stretch is."),
3991
+ first: z.string().describe("One of the two places."),
3992
+ second: z.string().describe("The other."),
3993
+ }))
3994
+ .describe("The largest of them."),
2482
3995
  });
2483
3996
  export const UiScanSchema = z.object({
2484
- components: z.array(z.string()),
2485
- bypasses: z.array(z.object({ path: z.string(), count: z.number().int().positive() })),
2486
- idioms: z.array(z.object({ id: z.string(), files: z.array(z.string()) })),
3997
+ components: z.array(z.string()).describe("The interface's own source files, with tests, stories and generated output left out."),
3998
+ bypasses: z
3999
+ .array(z.object({
4000
+ path: z.string().describe("The file."),
4001
+ count: z.number().int().positive().describe("How many times, in that file."),
4002
+ }))
4003
+ .describe("Where the design system was routed around and a value hard-coded instead. Counted per file, because a reader deciding what to open is served by a file and a number, not by eleven snippets."),
4004
+ idioms: z
4005
+ .array(z.object({
4006
+ id: z
4007
+ .string()
4008
+ .describe("Which outdated idiom. Looked up rather than listed here, so a sandbox one version behind can still report one this list has never heard of."),
4009
+ files: z.array(z.string()).describe("The files still on it."),
4010
+ }))
4011
+ .describe("Files still written the way their framework has since replaced."),
2487
4012
  });
2488
4013
  export const BundleSchema = z.object({
2489
- dir: z.string(),
2490
- totalBytes: z.number().int().nonnegative(),
2491
- totalGzip: z.number().int().nonnegative(),
2492
- assets: z.array(z.object({ path: z.string(), bytes: z.number().int().nonnegative(), gzip: z.number().int().nonnegative() })),
4014
+ dir: z
4015
+ .string()
4016
+ .describe("Which folder was measured. Read from build output already on disk rather than by building, so this is sometimes a commit behind and never leaves anything in your working tree."),
4017
+ totalBytes: z.number().int().nonnegative().describe("The whole thing, raw."),
4018
+ totalGzip: z
4019
+ .number()
4020
+ .int()
4021
+ .nonnegative()
4022
+ .describe("The whole thing, compressed. The ratio between the two is the difference between big and big-and-incompressible, which are different problems."),
4023
+ assets: z
4024
+ .array(z.object({
4025
+ path: z.string().describe("The file."),
4026
+ bytes: z.number().int().nonnegative().describe("Its raw size."),
4027
+ gzip: z.number().int().nonnegative().describe("Its compressed size."),
4028
+ }))
4029
+ .describe("What is in it, piece by piece."),
2493
4030
  });
2494
4031
  export const ProbeStateSchema = z.enum(["ok", "unavailable", "failed"]);
2495
4032
  export const ProbeFactsSchema = z.discriminatedUnion("id", [
@@ -2501,66 +4038,109 @@ export const ProbeFactsSchema = z.discriminatedUnion("id", [
2501
4038
  z.object({ id: z.literal("bundle"), bundle: BundleSchema }),
2502
4039
  ]);
2503
4040
  export const ProbeResultSchema = z.object({
2504
- id: ProbeIdSchema,
2505
- state: ProbeStateSchema,
2506
- ranAt: z.number(),
2507
- tookMs: z.number().int().nonnegative(),
2508
- facts: ProbeFactsSchema.optional(),
2509
- reason: z.string().optional(),
4041
+ id: ProbeIdSchema.describe("Which measurement this is."),
4042
+ state: ProbeStateSchema.describe("Whether the tool ran and reported, is not part of this repository at all, or broke. The middle one is not evidence of health: the check simply cannot be made here."),
4043
+ ranAt: z.number().describe("When it last finished, in milliseconds, which is what its age is measured from."),
4044
+ tookMs: z.number().int().nonnegative().describe("How long it took. Worth knowing before asking for it again: some of these run for minutes."),
4045
+ facts: ProbeFactsSchema.optional().describe("What it found, including finding nothing, which is a real answer and the one that keeps a chore quiet."),
4046
+ reason: z
4047
+ .string()
4048
+ .optional()
4049
+ .describe("Why it broke, quoted from the tool rather than summarised, or, when it never ran, what is missing. Never a sentence built from the check's own name, which would have an unmeasured check claiming there is nothing to measure."),
2510
4050
  });
2511
4051
  export const ChorePackageSchema = z.object({
2512
- dir: z.string(),
2513
- name: z.string(),
2514
- engines: z.record(z.string(), z.string()).optional(),
2515
- dependencies: z.array(z.string()),
2516
- devDependencies: z.array(z.string()),
2517
- documented: z.boolean(),
4052
+ dir: z.string().describe("Where the package lives."),
4053
+ name: z.string().describe("What it declares itself as."),
4054
+ engines: z.record(z.string(), z.string()).optional().describe("Which runtime versions it says it needs, verbatim."),
4055
+ dependencies: z.array(z.string()).describe("What it depends on."),
4056
+ devDependencies: z.array(z.string()).describe("What it needs only to build."),
4057
+ documented: z.boolean().describe("Whether it has a README, which in this workspace is what a package's own documentation is."),
2518
4058
  });
2519
4059
  export const ChoreShapeSchema = z.object({
2520
- docs: z.array(z.string()),
2521
- dockerfiles: z.array(z.string()),
2522
- ci: z.array(z.string()),
2523
- lockfile: z.boolean(),
2524
- packageManifest: z.boolean(),
2525
- deps: z.array(z.string()),
4060
+ docs: z
4061
+ .array(z.string())
4062
+ .describe("The repository's own architecture documents, when it has any. Their existence is the question: a repository with none has never been through the documentation flow at all."),
4063
+ dockerfiles: z.array(z.string()).describe("Container definitions in it."),
4064
+ ci: z.array(z.string()).describe("Pipeline definitions in it."),
4065
+ lockfile: z.boolean().describe("Whether dependencies are pinned to exact versions, which is what makes a security audit mean anything."),
4066
+ packageManifest: z
4067
+ .boolean()
4068
+ .describe("Whether it is a JavaScript project at all. A Rust or Go repository has no majors to be behind on, and offering it those checks would be this surface guessing at what it is looking at."),
4069
+ deps: z
4070
+ .array(z.string())
4071
+ .describe("Every dependency name declared anywhere in the repository. Names rather than a verdict about which framework this is, because that judgement belongs to whatever reads this, not to a sandbox baked months ago."),
2526
4072
  });
2527
4073
  export const ChoreSignalsSchema = z.object({
2528
- packages: z.array(ChorePackageSchema),
2529
- shape: ChoreShapeSchema,
2530
- hotspots: z.array(WorkspaceHotspotSchema),
2531
- keyModules: z.array(WorkspaceKeyModuleSchema),
2532
- totals: z.object({ files: z.number(), symbols: z.number(), complexity: z.number(), hotspots: z.number() }),
2533
- indexed: z.boolean(),
4074
+ packages: z.array(ChorePackageSchema).describe("Each package in the repository, as its own manifest declares it."),
4075
+ shape: ChoreShapeSchema.describe("What the repository is made of, which decides whether a given chore is even a sensible question to ask of it."),
4076
+ hotspots: z
4077
+ .array(WorkspaceHotspotSchema)
4078
+ .describe("Files that change often and are complicated at once, capped tight: a chore only asks whether something has entered the top of the ranking."),
4079
+ keyModules: z.array(WorkspaceKeyModuleSchema).describe("The parts the rest of the code leans on most, capped the same way."),
4080
+ totals: z
4081
+ .object({
4082
+ files: z.number().describe("Files counted."),
4083
+ symbols: z.number().describe("Named things they export."),
4084
+ complexity: z.number().describe("Branch points added up."),
4085
+ hotspots: z.number().describe("How many files qualify as hotspots at all."),
4086
+ })
4087
+ .describe("The repository in numbers."),
4088
+ indexed: z.boolean().describe("Whether the index these rankings came from is finished. Nothing should act on a half-built one."),
2534
4089
  });
2535
4090
  export const ChoreOutcomeSchema = z.enum(["acted", "reported", "clean"]);
2536
4091
  export const ChoreLedgerEntrySchema = z.object({
2537
- repo: z.string(),
2538
- chore: z.string(),
2539
- ranAt: z.number(),
2540
- runId: z.string(),
2541
- outcome: ChoreOutcomeSchema,
2542
- digest: z.string(),
2543
- snoozedUntil: z.number().optional(),
4092
+ repo: z.string().describe("Which repository."),
4093
+ chore: z.string().describe("Which chore."),
4094
+ ranAt: z.number().describe("When it ran, in milliseconds."),
4095
+ runId: z.string().describe("The conversation that ran it, so its whole record can be opened."),
4096
+ outcome: ChoreOutcomeSchema.describe("What it concluded: it did something, it wrote something down, or it looked and found the finding to be false. That last one matters most, or the same turn starts again for ever."),
4097
+ digest: z
4098
+ .string()
4099
+ .describe("A fingerprint of the evidence standing at the time. A chore whose evidence has since changed is due again on its own merits; one whose evidence has not stays quiet."),
4100
+ snoozedUntil: z
4101
+ .number()
4102
+ .optional()
4103
+ .describe("Not until then, in milliseconds. The chore stays visible and stays out of the badge. Different from switching it off, which is a setting."),
2544
4104
  });
2545
4105
  export const RunningProbeSchema = z.object({
2546
- repo: z.string(),
2547
- id: ProbeIdSchema,
2548
- askedAt: z.number(),
2549
- startedAt: z.number().optional(),
4106
+ repo: z.string().describe("Which repository."),
4107
+ id: ProbeIdSchema.describe("Which measurement."),
4108
+ askedAt: z.number().describe("When it was asked for, in milliseconds, so one still waiting can say how long it has waited."),
4109
+ startedAt: z
4110
+ .number()
4111
+ .optional()
4112
+ .describe("When it actually began. Absent while it is queued behind another, which is a real and common state: there is one lane for the whole sandbox."),
2550
4113
  });
2551
4114
  export const ChoresReportSchema = z.object({
2552
- repos: z.array(z.object({ repo: z.string(), probes: z.array(ProbeResultSchema), signals: ChoreSignalsSchema })),
2553
- ledger: z.array(ChoreLedgerEntrySchema),
2554
- running: z.array(RunningProbeSchema),
2555
- node: z.string(),
4115
+ repos: z
4116
+ .array(z.object({
4117
+ repo: z.string().describe("Which repository."),
4118
+ probes: z
4119
+ .array(ProbeResultSchema)
4120
+ .describe("The expensive measurements, served from a cache with an age on each rather than run on demand."),
4121
+ signals: ChoreSignalsSchema.describe("The cheap facts, worked out fresh every time."),
4122
+ }))
4123
+ .describe("Every repository's standing evidence. One answer for all of them, because a badge polls this on a timer and one request per repository is the kind of poll that shows up in a battery graph."),
4124
+ ledger: z.array(ChoreLedgerEntrySchema).describe("What has already been done about all of it."),
4125
+ running: z
4126
+ .array(RunningProbeSchema)
4127
+ .describe("What is being measured right now and what is waiting behind it. Part of this read rather than a route of its own, because a screen that had to ask twice would show the two halves disagreeing."),
4128
+ node: z
4129
+ .string()
4130
+ .describe("The runtime version this sandbox is actually running, read off the process rather than off a manifest, because what is installed is the fact that matters and a declared range is a wish."),
4131
+ });
4132
+ export const ChoreProbeRequestSchema = z.object({
4133
+ repo: z.string().min(1).describe("Which repository."),
4134
+ id: ProbeIdSchema.describe("Which measurement to retake, ahead of its usual schedule."),
2556
4135
  });
2557
- export const ChoreProbeRequestSchema = z.object({ repo: z.string().min(1), id: ProbeIdSchema });
2558
4136
  export const ChoreLedgerWriteSchema = ChoreLedgerEntrySchema;
2559
4137
  export const ReadinessCheckSchema = z.object({
2560
- id: z.string(),
2561
- label: z.string(),
2562
- status: z.enum(["pass", "warn", "fail"]),
2563
- detail: z.string(),
4138
+ id: z.string().describe("Which check."),
4139
+ label: z.string().describe("What it is called."),
4140
+ status: z.enum(["pass", "warn", "fail"]).describe("How it went. A warning is a real third answer rather than a soft failure."),
4141
+ detail: z.string().describe("What it found."),
4142
+ });
4143
+ export const ExtensionReadinessSchema = z.object({
4144
+ checks: z.array(ReadinessCheckSchema).describe("Everything that can be checked from the extension's own files, for an author about to publish."),
2564
4145
  });
2565
- export const ExtensionReadinessSchema = z.object({ checks: z.array(ReadinessCheckSchema) });
2566
4146
  //# sourceMappingURL=schemas.js.map