@intentic/sandbox-contract 1.316.0 → 1.318.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (362) hide show
  1. package/README.md +12 -4
  2. package/dist/chores/chores.js +1 -1
  3. package/dist/chores/chores.js.map +1 -1
  4. package/dist/chores/probes.js +1 -1
  5. package/dist/chores/probes.js.map +1 -1
  6. package/dist/contracts/accounts.contract.d.ts +75 -0
  7. package/dist/contracts/accounts.contract.d.ts.map +1 -1
  8. package/dist/contracts/accounts.contract.js +10 -1
  9. package/dist/contracts/accounts.contract.js.map +1 -1
  10. package/dist/contracts/agent.contract.d.ts +616 -1
  11. package/dist/contracts/agent.contract.d.ts.map +1 -1
  12. package/dist/contracts/agents.contract.d.ts +1134 -29
  13. package/dist/contracts/agents.contract.d.ts.map +1 -1
  14. package/dist/contracts/agents.contract.js +12 -12
  15. package/dist/contracts/agents.contract.js.map +1 -1
  16. package/dist/contracts/capabilities.contract.d.ts +16 -0
  17. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  18. package/dist/contracts/capabilities.contract.js +10 -1
  19. package/dist/contracts/capabilities.contract.js.map +1 -1
  20. package/dist/contracts/ci.contract.d.ts +22 -4
  21. package/dist/contracts/ci.contract.d.ts.map +1 -1
  22. package/dist/contracts/device.contract.d.ts +39 -0
  23. package/dist/contracts/device.contract.d.ts.map +1 -1
  24. package/dist/contracts/device.contract.js +2 -0
  25. package/dist/contracts/device.contract.js.map +1 -1
  26. package/dist/contracts/endpoints.contract.d.ts +23 -0
  27. package/dist/contracts/endpoints.contract.d.ts.map +1 -1
  28. package/dist/contracts/endpoints.contract.js +6 -1
  29. package/dist/contracts/endpoints.contract.js.map +1 -1
  30. package/dist/contracts/extensions.contract.d.ts +12 -0
  31. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  32. package/dist/contracts/needs.contract.d.ts +534 -0
  33. package/dist/contracts/needs.contract.d.ts.map +1 -0
  34. package/dist/contracts/needs.contract.js +80 -0
  35. package/dist/contracts/needs.contract.js.map +1 -0
  36. package/dist/contracts/providers.contract.d.ts +4 -0
  37. package/dist/contracts/providers.contract.d.ts.map +1 -1
  38. package/dist/contracts/runner.contract.d.ts +193 -2
  39. package/dist/contracts/runner.contract.d.ts.map +1 -1
  40. package/dist/contracts/secrets.contract.d.ts +53 -0
  41. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  42. package/dist/contracts/secrets.contract.js +31 -2
  43. package/dist/contracts/secrets.contract.js.map +1 -1
  44. package/dist/contracts/sessions.contract.d.ts +83 -0
  45. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  46. package/dist/contracts/settings.contract.d.ts +33 -2
  47. package/dist/contracts/settings.contract.d.ts.map +1 -1
  48. package/dist/contracts/settings.contract.js +20 -1
  49. package/dist/contracts/settings.contract.js.map +1 -1
  50. package/dist/contracts/system.contract.d.ts +177 -356
  51. package/dist/contracts/system.contract.d.ts.map +1 -1
  52. package/dist/contracts/system.contract.js +13 -12
  53. package/dist/contracts/system.contract.js.map +1 -1
  54. package/dist/contracts/translator.contract.d.ts +7 -0
  55. package/dist/contracts/translator.contract.d.ts.map +1 -1
  56. package/dist/contracts/translator.contract.js.map +1 -1
  57. package/dist/contracts/webext.contract.d.ts +38 -0
  58. package/dist/contracts/webext.contract.d.ts.map +1 -1
  59. package/dist/contracts/webext.contract.js +3 -0
  60. package/dist/contracts/webext.contract.js.map +1 -1
  61. package/dist/contracts/workspace.contract.d.ts +1 -80
  62. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  63. package/dist/contracts/workspace.contract.js +0 -27
  64. package/dist/contracts/workspace.contract.js.map +1 -1
  65. package/dist/events/agent-events.d.ts +330 -0
  66. package/dist/events/agent-events.d.ts.map +1 -1
  67. package/dist/events/agent-events.js +13 -0
  68. package/dist/events/agent-events.js.map +1 -1
  69. package/dist/events/agent-words.d.ts +1 -0
  70. package/dist/events/agent-words.d.ts.map +1 -1
  71. package/dist/events/agent-words.js +3 -2
  72. package/dist/events/agent-words.js.map +1 -1
  73. package/dist/events/land-breakage.d.ts.map +1 -1
  74. package/dist/events/land-breakage.js.map +1 -1
  75. package/dist/events/need-wake.d.ts +17 -0
  76. package/dist/events/need-wake.d.ts.map +1 -0
  77. package/dist/events/need-wake.js +34 -0
  78. package/dist/events/need-wake.js.map +1 -0
  79. package/dist/events/resume.d.ts +6 -1
  80. package/dist/events/resume.d.ts.map +1 -1
  81. package/dist/events/resume.js +17 -10
  82. package/dist/events/resume.js.map +1 -1
  83. package/dist/events/sandbox-notice.d.ts +183 -0
  84. package/dist/events/sandbox-notice.d.ts.map +1 -0
  85. package/dist/events/sandbox-notice.js +56 -0
  86. package/dist/events/sandbox-notice.js.map +1 -0
  87. package/dist/events/system-events.d.ts +84 -0
  88. package/dist/events/system-events.d.ts.map +1 -1
  89. package/dist/events/system-events.js +2 -0
  90. package/dist/events/system-events.js.map +1 -1
  91. package/dist/events/transcript.d.ts +508 -0
  92. package/dist/events/transcript.d.ts.map +1 -1
  93. package/dist/events/transcript.js +12 -1
  94. package/dist/events/transcript.js.map +1 -1
  95. package/dist/ids/conversation-ids.d.ts +1 -3
  96. package/dist/ids/conversation-ids.d.ts.map +1 -1
  97. package/dist/ids/conversation-ids.js +1 -3
  98. package/dist/ids/conversation-ids.js.map +1 -1
  99. package/dist/ids/project-dir.d.ts +6 -0
  100. package/dist/ids/project-dir.d.ts.map +1 -0
  101. package/dist/ids/project-dir.js +43 -0
  102. package/dist/ids/project-dir.js.map +1 -0
  103. package/dist/index.d.ts +3474 -1163
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +13 -4
  106. package/dist/index.js.map +1 -1
  107. package/dist/models/agent-runtimes.d.ts.map +1 -1
  108. package/dist/models/agent-runtimes.js +2 -2
  109. package/dist/models/agent-runtimes.js.map +1 -1
  110. package/dist/models/model-roles.d.ts +1 -9
  111. package/dist/models/model-roles.d.ts.map +1 -1
  112. package/dist/models/model-roles.js +1 -9
  113. package/dist/models/model-roles.js.map +1 -1
  114. package/dist/policy/command-classes.d.ts.map +1 -1
  115. package/dist/policy/command-classes.js +52 -3
  116. package/dist/policy/command-classes.js.map +1 -1
  117. package/dist/policy/safety-policy.d.ts +7 -1
  118. package/dist/policy/safety-policy.d.ts.map +1 -1
  119. package/dist/policy/safety-policy.js +2 -0
  120. package/dist/policy/safety-policy.js.map +1 -1
  121. package/dist/policy/secret-hosts.d.ts +13 -0
  122. package/dist/policy/secret-hosts.d.ts.map +1 -0
  123. package/dist/policy/secret-hosts.js +41 -0
  124. package/dist/policy/secret-hosts.js.map +1 -0
  125. package/dist/policy/turned-away.d.ts.map +1 -1
  126. package/dist/policy/turned-away.js +1 -0
  127. package/dist/policy/turned-away.js.map +1 -1
  128. package/dist/protocol/raw-routes.d.ts +7 -5
  129. package/dist/protocol/raw-routes.d.ts.map +1 -1
  130. package/dist/protocol/raw-routes.js +4 -1
  131. package/dist/protocol/raw-routes.js.map +1 -1
  132. package/dist/protocol/routes.d.ts +2 -6
  133. package/dist/protocol/routes.d.ts.map +1 -1
  134. package/dist/protocol/routes.js +2 -3
  135. package/dist/protocol/routes.js.map +1 -1
  136. package/dist/protocol/stream-of.d.ts +6 -0
  137. package/dist/protocol/stream-of.d.ts.map +1 -0
  138. package/dist/protocol/stream-of.js +4 -0
  139. package/dist/protocol/stream-of.js.map +1 -0
  140. package/dist/schemas/activity.js +1 -1
  141. package/dist/schemas/activity.js.map +1 -1
  142. package/dist/schemas/agent.d.ts +19 -2
  143. package/dist/schemas/agent.d.ts.map +1 -1
  144. package/dist/schemas/agent.js +2 -1
  145. package/dist/schemas/agent.js.map +1 -1
  146. package/dist/schemas/agents.d.ts +119 -0
  147. package/dist/schemas/agents.d.ts.map +1 -1
  148. package/dist/schemas/agents.js +45 -0
  149. package/dist/schemas/agents.js.map +1 -1
  150. package/dist/schemas/automations.d.ts +37 -0
  151. package/dist/schemas/automations.d.ts.map +1 -1
  152. package/dist/schemas/capabilities.d.ts +21 -0
  153. package/dist/schemas/capabilities.d.ts.map +1 -1
  154. package/dist/schemas/capabilities.js +50 -4
  155. package/dist/schemas/capabilities.js.map +1 -1
  156. package/dist/schemas/ci.d.ts +69 -9
  157. package/dist/schemas/ci.d.ts.map +1 -1
  158. package/dist/schemas/ci.js +29 -8
  159. package/dist/schemas/ci.js.map +1 -1
  160. package/dist/schemas/context-trim.d.ts +5 -1
  161. package/dist/schemas/context-trim.d.ts.map +1 -1
  162. package/dist/schemas/context-trim.js +8 -4
  163. package/dist/schemas/context-trim.js.map +1 -1
  164. package/dist/schemas/devices.d.ts +91 -0
  165. package/dist/schemas/devices.d.ts.map +1 -1
  166. package/dist/schemas/devices.js +23 -1
  167. package/dist/schemas/devices.js.map +1 -1
  168. package/dist/schemas/environment.d.ts +48 -0
  169. package/dist/schemas/environment.d.ts.map +1 -1
  170. package/dist/schemas/environment.js +15 -0
  171. package/dist/schemas/environment.js.map +1 -1
  172. package/dist/schemas/extension-updates.d.ts +24 -0
  173. package/dist/schemas/extension-updates.d.ts.map +1 -1
  174. package/dist/schemas/git/git.d.ts +13 -0
  175. package/dist/schemas/git/git.d.ts.map +1 -1
  176. package/dist/schemas/git/git.js +11 -1
  177. package/dist/schemas/git/git.js.map +1 -1
  178. package/dist/schemas/hosts.d.ts +4 -0
  179. package/dist/schemas/hosts.d.ts.map +1 -1
  180. package/dist/schemas/hosts.js +3 -1
  181. package/dist/schemas/hosts.js.map +1 -1
  182. package/dist/schemas/keep-warm.d.ts +4 -0
  183. package/dist/schemas/keep-warm.d.ts.map +1 -1
  184. package/dist/schemas/keep-warm.js +15 -5
  185. package/dist/schemas/keep-warm.js.map +1 -1
  186. package/dist/schemas/loopback-catch.d.ts +33 -0
  187. package/dist/schemas/loopback-catch.d.ts.map +1 -0
  188. package/dist/schemas/loopback-catch.js +20 -0
  189. package/dist/schemas/loopback-catch.js.map +1 -0
  190. package/dist/schemas/maintenance.js +1 -1
  191. package/dist/schemas/maintenance.js.map +1 -1
  192. package/dist/schemas/needs.d.ts +664 -0
  193. package/dist/schemas/needs.d.ts.map +1 -0
  194. package/dist/schemas/needs.js +203 -0
  195. package/dist/schemas/needs.js.map +1 -0
  196. package/dist/schemas/personas.d.ts.map +1 -1
  197. package/dist/schemas/personas.js +6 -3
  198. package/dist/schemas/personas.js.map +1 -1
  199. package/dist/schemas/providers/provider-oauth.d.ts +87 -0
  200. package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
  201. package/dist/schemas/providers/provider-oauth.js +20 -0
  202. package/dist/schemas/providers/provider-oauth.js.map +1 -1
  203. package/dist/schemas/secrets.d.ts +84 -0
  204. package/dist/schemas/secrets.d.ts.map +1 -1
  205. package/dist/schemas/secrets.js +70 -0
  206. package/dist/schemas/secrets.js.map +1 -1
  207. package/dist/schemas/settings-history.d.ts +1 -1
  208. package/dist/schemas/settings-history.d.ts.map +1 -1
  209. package/dist/schemas/settings-history.js +1 -0
  210. package/dist/schemas/settings-history.js.map +1 -1
  211. package/dist/schemas/settings.d.ts +44 -2
  212. package/dist/schemas/settings.d.ts.map +1 -1
  213. package/dist/schemas/settings.js +30 -4
  214. package/dist/schemas/settings.js.map +1 -1
  215. package/dist/schemas/state-plan.d.ts +1 -0
  216. package/dist/schemas/state-plan.d.ts.map +1 -1
  217. package/dist/schemas/state-plan.js +3 -1
  218. package/dist/schemas/state-plan.js.map +1 -1
  219. package/dist/schemas/system.d.ts +20 -0
  220. package/dist/schemas/system.d.ts.map +1 -1
  221. package/dist/schemas/system.js +7 -0
  222. package/dist/schemas/system.js.map +1 -1
  223. package/dist/schemas/terminal.d.ts +2 -3
  224. package/dist/schemas/terminal.d.ts.map +1 -1
  225. package/dist/schemas/terminal.js +12 -4
  226. package/dist/schemas/terminal.js.map +1 -1
  227. package/dist/schemas/updates.d.ts +39 -0
  228. package/dist/schemas/updates.d.ts.map +1 -0
  229. package/dist/schemas/updates.js +31 -0
  230. package/dist/schemas/updates.js.map +1 -0
  231. package/dist/schemas/webext.d.ts +3 -0
  232. package/dist/schemas/webext.d.ts.map +1 -1
  233. package/dist/schemas/webext.js +1 -0
  234. package/dist/schemas/webext.js.map +1 -1
  235. package/dist/schemas/workflows.js +3 -3
  236. package/dist/schemas/workflows.js.map +1 -1
  237. package/dist/schemas/workspace/workspace-tree.d.ts +2 -0
  238. package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
  239. package/dist/schemas/workspace/workspace-tree.js +4 -0
  240. package/dist/schemas/workspace/workspace-tree.js.map +1 -1
  241. package/dist/state/definition.d.ts +28 -4
  242. package/dist/state/definition.d.ts.map +1 -1
  243. package/dist/state/history-state.d.ts.map +1 -1
  244. package/dist/state/history-state.js +10 -0
  245. package/dist/state/history-state.js.map +1 -1
  246. package/dist/state/runtime-state.d.ts +0 -3
  247. package/dist/state/runtime-state.d.ts.map +1 -1
  248. package/dist/state/runtime-state.js +0 -1
  249. package/dist/state/runtime-state.js.map +1 -1
  250. package/dist/state/workspace-state.d.ts +4 -5
  251. package/dist/state/workspace-state.d.ts.map +1 -1
  252. package/dist/state/workspace-state.js +1 -6
  253. package/dist/state/workspace-state.js.map +1 -1
  254. package/dist/text/documents.d.ts +3 -0
  255. package/dist/text/documents.d.ts.map +1 -1
  256. package/dist/text/documents.js +14 -0
  257. package/dist/text/documents.js.map +1 -1
  258. package/dist/text/transcript-fold.d.ts +4 -3
  259. package/dist/text/transcript-fold.d.ts.map +1 -1
  260. package/dist/text/transcript-fold.js +140 -31
  261. package/dist/text/transcript-fold.js.map +1 -1
  262. package/dist/webext/index.d.ts +1 -0
  263. package/dist/webext/index.d.ts.map +1 -1
  264. package/dist/webext/index.js +1 -0
  265. package/dist/webext/index.js.map +1 -1
  266. package/package.json +5 -5
  267. package/src/chores/chores.ts +2 -2
  268. package/src/chores/probes.ts +1 -1
  269. package/src/contracts/accounts.contract.ts +12 -0
  270. package/src/contracts/agents.contract.ts +13 -13
  271. package/src/contracts/capabilities.contract.ts +14 -1
  272. package/src/contracts/device.contract.ts +5 -0
  273. package/src/contracts/endpoints.contract.ts +20 -4
  274. package/src/contracts/needs.contract.ts +103 -0
  275. package/src/contracts/secrets.contract.ts +45 -3
  276. package/src/contracts/settings.contract.ts +29 -0
  277. package/src/contracts/system.contract.ts +19 -14
  278. package/src/contracts/translator.contract.ts +2 -1
  279. package/src/contracts/webext.contract.ts +7 -1
  280. package/src/contracts/workspace.contract.ts +0 -37
  281. package/src/events/agent-events.ts +22 -1
  282. package/src/events/agent-words.ts +6 -2
  283. package/src/events/land-breakage.ts +3 -4
  284. package/src/events/need-wake.test.ts +55 -0
  285. package/src/events/need-wake.ts +71 -0
  286. package/src/events/resume.test.ts +13 -2
  287. package/src/events/resume.ts +25 -12
  288. package/src/events/sandbox-notice.ts +92 -0
  289. package/src/events/system-events.ts +7 -0
  290. package/src/events/transcript.ts +23 -1
  291. package/src/events/verify-nudge.ts +3 -3
  292. package/src/ids/conversation-ids.test.ts +2 -26
  293. package/src/ids/conversation-ids.ts +6 -12
  294. package/src/ids/project-dir.fixture.json +65 -0
  295. package/src/ids/project-dir.test.ts +60 -0
  296. package/src/ids/project-dir.ts +70 -0
  297. package/src/index.ts +18 -4
  298. package/src/models/agent-catalog.test.ts +7 -1
  299. package/src/models/agent-runtimes.ts +7 -5
  300. package/src/models/model-roles.ts +1 -9
  301. package/src/policy/command-classes.test.ts +39 -0
  302. package/src/policy/command-classes.ts +70 -3
  303. package/src/policy/safety-policy.ts +10 -0
  304. package/src/policy/secret-hosts.test.ts +89 -0
  305. package/src/policy/secret-hosts.ts +73 -0
  306. package/src/policy/turned-away.ts +1 -0
  307. package/src/protocol/raw-routes.ts +6 -3
  308. package/src/protocol/routes.test.ts +6 -6
  309. package/src/protocol/routes.ts +2 -9
  310. package/src/protocol/stream-of.ts +13 -0
  311. package/src/schemas/activity.ts +1 -1
  312. package/src/schemas/agent.ts +4 -1
  313. package/src/schemas/agents.ts +72 -5
  314. package/src/schemas/automations.ts +2 -2
  315. package/src/schemas/capabilities.ts +72 -7
  316. package/src/schemas/ci.ts +56 -11
  317. package/src/schemas/context-trim.ts +11 -6
  318. package/src/schemas/devices.ts +55 -2
  319. package/src/schemas/environment.ts +39 -0
  320. package/src/schemas/git/git.ts +20 -2
  321. package/src/schemas/hosts.ts +7 -1
  322. package/src/schemas/keep-warm.ts +19 -6
  323. package/src/schemas/loopback-catch.ts +45 -0
  324. package/src/schemas/maintenance.ts +1 -1
  325. package/src/schemas/needs.ts +301 -0
  326. package/src/schemas/personas.ts +8 -3
  327. package/src/schemas/providers/provider-oauth.ts +35 -0
  328. package/src/schemas/secrets.ts +93 -0
  329. package/src/schemas/settings-history.ts +2 -0
  330. package/src/schemas/settings.test.ts +19 -1
  331. package/src/schemas/settings.ts +66 -9
  332. package/src/schemas/speaker.ts +5 -5
  333. package/src/schemas/state-plan.ts +8 -1
  334. package/src/schemas/system.ts +15 -0
  335. package/src/schemas/terminal.ts +19 -6
  336. package/src/schemas/updates.ts +56 -0
  337. package/src/schemas/webext.ts +3 -0
  338. package/src/schemas/workflows.ts +5 -5
  339. package/src/schemas/workspace/workspace-tree.ts +7 -0
  340. package/src/state/fix-attempt-plan.test.ts +1 -1
  341. package/src/state/fix-stance.ts +1 -1
  342. package/src/state/history-state.ts +19 -0
  343. package/src/state/runtime-state.ts +0 -3
  344. package/src/state/workspace-state.ts +4 -6
  345. package/src/text/documents.test.ts +25 -1
  346. package/src/text/documents.ts +24 -0
  347. package/src/text/title.ts +1 -1
  348. package/src/text/transcript-fold.test.ts +218 -25
  349. package/src/text/transcript-fold.ts +170 -45
  350. package/src/webext/index.ts +1 -0
  351. package/dist/schemas/workspace/push-checks.d.ts +0 -222
  352. package/dist/schemas/workspace/push-checks.d.ts.map +0 -1
  353. package/dist/schemas/workspace/push-checks.js +0 -97
  354. package/dist/schemas/workspace/push-checks.js.map +0 -1
  355. package/dist/state/red-streak.d.ts +0 -7
  356. package/dist/state/red-streak.d.ts.map +0 -1
  357. package/dist/state/red-streak.js +0 -11
  358. package/dist/state/red-streak.js.map +0 -1
  359. package/src/schemas/workspace/push-checks.test.ts +0 -28
  360. package/src/schemas/workspace/push-checks.ts +0 -155
  361. package/src/state/red-streak.test.ts +0 -28
  362. package/src/state/red-streak.ts +0 -28
@@ -2,7 +2,7 @@
2
2
  // itself, which those settings adopt.
3
3
  import { STATE_DIR } from "@intentic/constants";
4
4
  import { z } from "zod";
5
- import { CommandJudgeModeSchema } from "../policy/safety-policy.js";
5
+ import { CommandJudgeModeSchema, ProjectInstallModeSchema } from "../policy/safety-policy.js";
6
6
  import { ModelRoleSchema } from "../models/model-roles.js";
7
7
  import { AdmissionPolicySchema, AdmissionRuleSchema, ModelPinSchema } from "./agent.js";
8
8
  import { LimitPolicySchema, RetryPolicySchema } from "./turn-break.js";
@@ -13,10 +13,15 @@ export const SystemPromptModeSchema = z.enum(["intentic", "claude", "custom"]);
13
13
  export type SystemPromptMode = z.infer<typeof SystemPromptModeSchema>;
14
14
  // Excludes "custom": there is nothing to fetch, it's whatever the owner already typed into the settings field.
15
15
  export const BuiltinPromptSchema = z.object({ base: z.enum(["intentic", "claude"]) });
16
+ // Longest an owner's own system prompt may be, the sandbox's or a persona's; named so an editor can refuse a save the
17
+ // daemon would refuse, rather than copy the number.
18
+ export const SYSTEM_PROMPT_MAX = 20_000;
19
+ // Longest the guidance the New chat routing job reads may be (`autoModelGuidance` below).
20
+ export const AUTO_MODEL_GUIDANCE_MAX = 2000;
16
21
  // Rules: "at this moment, if this is true, do this". The owner's rules decide (land, hold, version); every command a
17
22
  // moment runs is a repository's own check (`<repo>/.intentic/checks.json`), compiled into this same table by the daemon,
18
- // so no command lives in settings. Nothing verifies inside a turn or after a land: CI checks what the owner pushes, and
19
- // nothing here holds anything (schemas/workspace/push-checks.ts).
23
+ // so no command lives in settings. Nothing verifies inside a turn, after a land or on the way to a push: CI checks what
24
+ // the owner pushes, and nothing here holds anything (schemas/ci.ts).
20
25
  export const RuleMomentSchema = z.enum([
21
26
  // A command here runs on the just-written file (`{file}` is its path); the cheapest moment to catch a defect.
22
27
  "file.edited",
@@ -50,7 +55,7 @@ export const RuleActionSchema = z.discriminatedUnion("kind", [
50
55
  z.object({ kind: z.literal("builtin"), name: RuleBuiltinSchema }),
51
56
  ]);
52
57
  export type RuleAction = z.infer<typeof RuleActionSchema>;
53
- // `checks-failed` was a clean turn whose `turn.ending` check went red. Nothing produces it now that no check runs inside a
58
+ // `checks-failed` was a clean turn whose `turn.ending` check failed. Nothing produces it now that no check runs inside a
54
59
  // turn, and a check never holds work; it stays so older rules naming it still read.
55
60
  export const RuleOutcomeSchema = z.enum(["clean", "error", "conflict", "checks-failed"]);
56
61
  export type RuleOutcome = z.infer<typeof RuleOutcomeSchema>;
@@ -226,6 +231,19 @@ export const KeepWarmSettingsSchema = z.object({
226
231
  });
227
232
  export type KeepWarmSettings = z.infer<typeof KeepWarmSettingsSchema>;
228
233
 
234
+ // Who the editor's screens are written for: `developer` reads git's own words (branch, land, commit), `maker` plain ones
235
+ // (draft, accept, save) over the same mechanisms. The editor's vocabulary, never anything an agent does. A person's own
236
+ // answer, kept by the sandbox per member rather than in each browser, so the desktop app and a browser tab name one
237
+ // button one way while two people on one sandbox can still read it in their own words.
238
+ export const AudienceSchema = z.enum(["developer", "maker"]);
239
+ export type Audience = z.infer<typeof AudienceSchema>;
240
+
241
+ // The caller's own answer as the sandbox keeps it; absent until they have given one here.
242
+ export const MemberAudienceSchema = z.object({
243
+ audience: AudienceSchema.optional().describe("developer for git's own words, maker for plain ones. Absent until this person has chosen here."),
244
+ });
245
+ export type MemberAudience = z.infer<typeof MemberAudienceSchema>;
246
+
229
247
  export const SandboxSettingsSchema = z.object({
230
248
  // The zone every WALL-CLOCK RULE in this sandbox is meant in — an automation's cron, and anything else that says
231
249
  // "at 09:00" instead of naming an instant. It is not a display preference: nothing formats through it, and an
@@ -276,7 +294,7 @@ export const SandboxSettingsSchema = z.object({
276
294
  // every turn pays for it.
277
295
  systemPrompt: z
278
296
  .string()
279
- .max(20000)
297
+ .max(SYSTEM_PROMPT_MAX)
280
298
  .default("")
281
299
  .describe(
282
300
  "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.",
@@ -385,7 +403,7 @@ export const SandboxSettingsSchema = z.object({
385
403
  // purpose: the reading runs under a 5s deadline, and this text is paid for on every chat that opens on Auto.
386
404
  autoModelGuidance: z
387
405
  .string()
388
- .max(2000)
406
+ .max(AUTO_MODEL_GUIDANCE_MAX)
389
407
  .default("")
390
408
  .describe(
391
409
  "What you would tell somebody choosing the model for a new chat on your behalf: which model you want the cheap work on, which account to leave alone, when to reach for the strongest one. Read once per chat, alongside the models and allowances this sandbox can actually run, and it overrides the product's own advice where the two disagree. It cannot invent a model: the answer is still a choice from that list.",
@@ -436,14 +454,22 @@ export const SandboxSettingsSchema = z.object({
436
454
  ),
437
455
  // Worth it since the container is recreated on every update or environment approval — otherwise approving a
438
456
  // Dockerfile change costs the run that asked for it.
439
- // What happens when main's CI goes red (ci/main-fixer.ts): its first failed job starts one fix agent, which is sent
440
- // every later failure on main until main is green, within a few turns, and a failure on the CI fleet itself is re-run
457
+ // What happens when main's CI fails (ci/main-fixer.ts): its first failed job starts one fix agent, which is sent
458
+ // every later failure on main until main passes, within a few turns, and a failure on the CI fleet itself is re-run
441
459
  // once instead. Off, it is only reported.
442
460
  autoRepair: z
443
461
  .boolean()
444
462
  .default(true)
445
463
  .describe(
446
- "Whether main's CI going red is repaired without asking. The first job that fails on main starts one fix agent, without waiting for the rest of the run, and every later failure on main goes to that same agent until a run passes. It gets a few turns; when they are spent, or it finishes without changing anything (a failure that is not in the code), the red waits for you. A failure on the CI fleet itself is re-run once instead. Off, all of it is only reported.",
464
+ "Whether main's CI failing is repaired without asking. The first job that fails on main starts one fix agent, without waiting for the rest of the run, and every later failure on main goes to that same agent until a run passes. It gets a few turns; when they are spent, or it finishes without changing anything (a failure that is not in the code), the failure waits for you. A failure on the CI fleet itself is re-run once instead. Off, all of it is only reported.",
465
+ ),
466
+ // What another writer pushes reaches the main tree within minutes instead of at the owner's next pull
467
+ // (git/remote/follow-origin.ts), so an agent whose work clashes with it meets the clash at its own land.
468
+ followOrigin: z
469
+ .boolean()
470
+ .default(true)
471
+ .describe(
472
+ "Whether each workspace repo keeps up with the remote branch it tracks. Every couple of minutes it is fetched, and what arrived is brought into the main tree: a fast-forward when you have no commits of your own, else a merge commit. Only while it is quiet (no turn working in the main tree, no merge or rebase of yours open), and never half-way: a conflict, or an uncommitted file in the way, leaves the repo exactly as it was until the next try. Nothing is ever pushed. Off, nothing is fetched.",
447
473
  ),
448
474
  // Where heavy work runs: a runner (the same image, on one of the owner's machines) instead of this sandbox. Keyed by
449
475
  // the heavy-command rule an agent's command matched (system/resources/heavy-commands.ts), each naming a runner id. A
@@ -457,6 +483,14 @@ export const SandboxSettingsSchema = z.object({
457
483
  .describe(
458
484
  "Which heavy work runs on a runner on one of your machines instead of this sandbox: agents' commands by the kind the heavy-command rules sort them into (tests, typechecks, verify…). The code travels as it stands, uncommitted work included; the output streams back, and any file the command changed comes back with it. A machine that is offline, outdated or busy hands the work back to this sandbox, and the output says so.",
459
485
  ),
486
+ // A person answering a need is exactly the moment they want the work to go on (docs/architecture/needs.md), unlike a
487
+ // restart nobody watched, so this one is on to begin with.
488
+ continueWhenNeedMet: z
489
+ .boolean()
490
+ .default(true)
491
+ .describe(
492
+ "Whether a conversation carries on by itself once something it asked a person for arrives: a connection made, a secret given, access allowed, a tool built into the image. Off leaves the answer on the conversation's card until someone sends a message.",
493
+ ),
460
494
  autoResumeOnRestart: z
461
495
  .boolean()
462
496
  .default(false)
@@ -510,6 +544,11 @@ export const SandboxSettingsSchema = z.object({
510
544
  commandJudge: CommandJudgeModeSchema.default("on").describe(
511
545
  "Whether a model reads your safety policy before a flagged command runs. Off judges nothing and asks about nothing; Watch judges everything and records it without ever interrupting you, which is how you find out what your policy actually does before you let it stop anything; On lets the verdict decide. Wiping a disk or deleting under /history asks at every setting — that rule is typed rather than judged, and cannot be turned off.",
512
546
  ),
547
+ // Only whether a person is asked: where the install lands (the conversation's own copy, or the daemon's install lane
548
+ // for a main-tree turn) is the sandbox's business, so no setting names it.
549
+ projectInstalls: ProjectInstallModeSchema.default("automatic").describe(
550
+ "What happens when an agent installs a project's packages itself (pnpm add, npm install, uv sync). Automatic lets it run and keep working; Ask first stops for your answer in the chat, once or for the whole conversation; Never refuses, and a dependency the agent added to a manifest is installed when you land its work. A conversation in its own worktree installs into its own copy, and the land review lists every dependency its work adds.",
551
+ ),
513
552
  // Three separate ceilings since they stop different things: width (parallel fan-out), lifetime (per conversation),
514
553
  // depth (how far a child may delegate) — raising width alone just hits the lifetime cap sooner. Defaults mirror the
515
554
  // CLI's own unset behavior; hitting one stops the agent rather than retrying.
@@ -532,6 +571,24 @@ export const SandboxSettingsWriteSchema = SandboxSettingsSchema.refine((settings
532
571
  path: ["rules"],
533
572
  });
534
573
 
574
+ // A browser's answer to who the screens are written for, for the person signed in to it. `offer` is the one that must
575
+ // not overwrite: a browser handing over the answer it kept on its own, taken only while the sandbox keeps none for them.
576
+ export const AudienceAnswerSchema = z.object({
577
+ audience: AudienceSchema.describe("developer for git's own words, maker for plain ones."),
578
+ offer: z
579
+ .boolean()
580
+ .optional()
581
+ .describe("Take it only while this person has no answer kept here yet. Absent or false replaces whatever is kept."),
582
+ });
583
+ export type AudienceAnswer = z.infer<typeof AudienceAnswerSchema>;
584
+
585
+ // What the sandbox keeps for this person after the answer, and whether this call set it: false is an offer it declined.
586
+ export const AudienceStateSchema = z.object({
587
+ audience: AudienceSchema.describe("The words this sandbox's editor now uses."),
588
+ adopted: z.boolean().describe("Whether this call set it. False means an offer met an answer already kept, which stands."),
589
+ });
590
+ export type AudienceState = z.infer<typeof AudienceStateSchema>;
591
+
535
592
  // A browser telling the sandbox which clock IT is on. An offer, not an instruction: see `adoptTimezone`.
536
593
  export const TimezoneOfferSchema = z.object({
537
594
  timezone: ZoneSchema.describe("The zone the offering machine is in, as an IANA name like Europe/Warsaw."),
@@ -69,11 +69,11 @@ export const speakerOwner = (speaker: TurnSpeaker): Pick<SessionOwner, "email" |
69
69
  export const spokenByPerson = (speaker: TurnSpeaker | undefined): boolean => speaker?.kind === "person";
70
70
 
71
71
  // WHAT A COMPOSED PROMPT IS FOR, when the sandbox or the app wrote it rather than whoever is named as speaking: the brief
72
- // a fix agent is handed (what a push left, main's red CI, each later failure on main) and its nudges, a land conflict to
73
- // resolve. Carried on the turn and the row it opens, so a reader shows it as the sandbox's words, not the owner's, and
74
- // never has to recognise the prompt by its opening; the four openings older rows are recognised by stay what they are
75
- // (events/errands.ts, errandOfPrompt). `verify-nudge` and the `land-*` errands are retired with the checks that sent them
76
- // and stay only because records hold them.
72
+ // a fix agent is handed (main's first failed job, each later failure on main) and its nudges, a land conflict to resolve. Carried
73
+ // on the turn and the row it opens, so a reader shows it as the sandbox's words, not the owner's, and never has to
74
+ // recognise the prompt by its opening; the four openings older rows are recognised by stay what they are
75
+ // (events/errands.ts, errandOfPrompt). `verify-nudge`, the `land-*` errands and the `push-fix` pair (the hand-over of
76
+ // what the pre-push hook found) are retired with what sent them and stay only because records hold them.
77
77
  export const TurnErrandSchema = z.enum([
78
78
  "land-conflict",
79
79
  "verify-nudge",
@@ -40,8 +40,15 @@ export type StatePlan = z.infer<typeof StatePlanSchema>;
40
40
 
41
41
  // The `state` of `/health`: whether an update's state changes are still uncommitted. The host rolls an update back when
42
42
  // the journal stays open, so this rides the same probe as the boot's progress.
43
+ // `failed`: a conversion or structural step threw partway through this boot; the daemon put back every file the episode
44
+ // had touched and keeps running on read-time conversions, and a host rolls the update back as it would an open journal
45
+ // that never commits (this version could not bring the files to its shape, the one before it can read them as they are).
43
46
  export const StateStatusSchema = z.object({
44
- journal: z.enum(["open", "none"]).describe("Open from the start of a boot that changed stored files until that boot has converged."),
47
+ journal: z
48
+ .enum(["open", "none", "failed"])
49
+ .describe(
50
+ "Open from the start of a boot that changed stored files until that boot has converged. Failed when a conversion threw partway: the files were put back as they were, and a host takes that as the update not having taken.",
51
+ ),
45
52
  engine: z.number().describe("The running build's conversion count."),
46
53
  });
47
54
  export type StateStatus = z.infer<typeof StateStatusSchema>;
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { StatePlanSchema } from "./state-plan.js";
3
+ import { UpdateOutcomeSchema, WithdrawnReleaseSchema } from "./updates.js";
3
4
  // version is this build's baked-in version; latest/updateAvailable come from comparing it to the published stable
4
5
  // release.
5
6
  // Whether a runtime can serve a turn, probed off the turn path. "unknown" reads as available-but-unverified, never as
@@ -82,6 +83,20 @@ export const InfoSchema = z.object({
82
83
  staged: StagedUpdateSchema.optional().describe(
83
84
  "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.",
84
85
  ),
86
+ // What the host last did about this sandbox's version (update-outcome.json on /history, written by ic); absent
87
+ // when no host has said, which is every hosted sandbox and every host older than the file.
88
+ lastUpdate: UpdateOutcomeSchema.optional().describe(
89
+ "What the machine running this sandbox last did about its version: an update that took, one it gave up on and why, and until when the previous version stays ready. Absent when that machine has never said.",
90
+ ),
91
+ // The running version was taken back after it shipped; absent for every version still standing.
92
+ withdrawn: WithdrawnReleaseSchema.optional().describe(
93
+ "Set when the version this sandbox runs was withdrawn after it shipped, which is the moment to go back to the one before it. Absent for every version still standing.",
94
+ ),
95
+ // The owner's "not this one" (POST /system/update/skip); updateAvailable reads false while `latest` is it.
96
+ skippedVersion: z
97
+ .string()
98
+ .optional()
99
+ .describe("A release the owner chose to skip. While it is the newest, no update is offered; a newer one is. Absent when nothing is skipped."),
85
100
  });
86
101
  export type Info = z.infer<typeof InfoSchema>;
87
102
  // What the daemon could not read in its own `.intentic/` state files, and in the files on its volume that
@@ -139,8 +139,9 @@ export const BrowserNameParamSchema = z.object({ name: z.string().describe("Whic
139
139
  // only where its record is read from and what else can be done with it, never how it looks.
140
140
  export const SubagentKindSchema = z.enum(["subagent", "spawned"]);
141
141
  export type SubagentKind = z.infer<typeof SubagentKindSchema>;
142
- // running/pending/blocked are live, the rest terminal. Uses the SDK's own task vocabulary rather than AgentStatus.
143
- // `blocked` is the one addition: a spawned child's question/permission/plan card raises it.
142
+ // pending/running/blocked/paused are live, the rest terminal. Uses the SDK's own task vocabulary rather than AgentStatus.
143
+ // `blocked` is one addition: a spawned child's question/permission/plan card raises it. `paused` the other: a spawned
144
+ // child whose turn stopped at a wall the sandbox re-runs it past by itself, its report still to come.
144
145
  export const SubagentStatusSchema = z.enum(["pending", "running", "blocked", "completed", "failed", "killed", "paused"]);
145
146
  export type SubagentStatus = z.infer<typeof SubagentStatusSchema>;
146
147
  // Whether anything checked what the subagent did, computed from its edits against the checks that ran after them; works
@@ -177,7 +178,16 @@ export const SubagentSessionSchema = z.object({
177
178
  // Subagent type (Explore, general-purpose) or a child's provider label; description is the one-line ask.
178
179
  agentType: z.string().optional().describe("What kind of subagent it is."),
179
180
  description: z.string().optional().describe("What it was asked to do, in one line."),
180
- model: z.string().optional().describe("Which model it runs on."),
181
+ model: z
182
+ .string()
183
+ .optional()
184
+ .describe(
185
+ "Which model it runs on: the exact id its provider served once its own record says so, where the call that started it named only an alias or nothing.",
186
+ ),
187
+ effort: z
188
+ .string()
189
+ .optional()
190
+ .describe("How hard it was told to think: its own definition's tier, else the one it inherited from its parent's turn."),
181
191
  // Which provider serves a spawned child; an SDK subagent implies its own (its parent's).
182
192
  provider: z.string().optional().describe("Which provider serves it, for a subagent spawned across providers."),
183
193
  // How deep in the spawn tree; 1 means the turn itself started it. A subagent can itself delegate further.
@@ -192,7 +202,7 @@ export const SubagentSessionSchema = z.object({
192
202
  .boolean()
193
203
  .optional()
194
204
  .describe(
195
- "The parent carried on working instead of waiting for it. This is the whole reason the list exists: such a subagent used to be invisible until its result landed, sometimes minutes later.",
205
+ "The parent carried on working instead of waiting for it. Such a subagent is otherwise invisible until its result lands, sometimes minutes later.",
196
206
  ),
197
207
  status: SubagentStatusSchema.describe(
198
208
  "How it is going. Blocked means it needs an answer, which a parent and an operator act on differently from it simply working.",
@@ -218,7 +228,10 @@ export const SubagentSessionSchema = z.object({
218
228
  });
219
229
  export type SubagentSession = z.infer<typeof SubagentSessionSchema>;
220
230
  export const SubagentsListSchema = z.object({
221
- sessions: z.array(SubagentSessionSchema).describe("Every subagent this sandbox's conversations have started, in-process and spawned alike."),
231
+ sessions: z
232
+ .array(SubagentSessionSchema)
233
+ .describe(
234
+ "The subagents conversations the caller can see have started, in-process and spawned alike: every one still working, and the most recent that have settled. Working ones first, then the most recently active.",
235
+ ),
222
236
  });
223
237
  export type SubagentsList = z.infer<typeof SubagentsListSchema>;
224
- export const SubagentIdParamSchema = z.object({ id: z.string() });
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+
3
+ // WHAT THE HOST LAST DID ABOUT THIS SANDBOX'S VERSION, in the host's own words. Written by `ic` (the swap, its health
4
+ // check and the probation that follows it: _sandbox/ic/src/sandbox/outcome.rs) onto /history as `update-outcome.json`,
5
+ // the same volume whichever container runs, so the version that ends up running reads what happened to the other one.
6
+ // Advisory: the swap and its undoing are the host's; this is only how the sandbox and its owner hear about them.
7
+ export const UpdateOutcomeResultSchema = z.enum(["updated", "kept", "restored", "rolled-back"]);
8
+ export type UpdateOutcomeResult = z.infer<typeof UpdateOutcomeResultSchema>;
9
+
10
+ export const UpdateOutcomeSchema = z.object({
11
+ result: UpdateOutcomeResultSchema.describe(
12
+ "What happened. Updated: the new version passed its first health check and runs, with the previous one kept ready until keepUntil. Kept: that probation ended and the new version stays. Restored: the new version never came up, so the previous container was put back at once. Rolled back: the new version came up and then failed its probation (it kept crashing, never became ready, or lost its tunnel), so the host went back to the previous one by itself.",
13
+ ),
14
+ // What the person or program asked for. A string rather than an enum: a newer ic's verb must not make an older
15
+ // daemon drop the whole outcome.
16
+ verb: z.string().optional().describe("What was asked for: update, rollback, rebuild, dev, reshape, or the probation watch acting on its own."),
17
+ at: z.number().describe("When it happened, in milliseconds."),
18
+ from: z.string().optional().describe("The version (or, when it would not say, the image) that ran before."),
19
+ to: z.string().optional().describe("The version (or image) that was moved onto, or that was tried and given up on."),
20
+ // Plain words for the owner: why a version was given up on. Absent for an update that simply worked.
21
+ reason: z.string().optional().describe("Why the host gave up on the new version, in plain words. Absent when nothing went wrong."),
22
+ // A path on the machine that runs the sandbox, which only means something to a person on that machine.
23
+ log: z.string().optional().describe("Where the host kept the full log of the swap, as a path on the machine that runs the sandbox."),
24
+ keepUntil: z
25
+ .number()
26
+ .optional()
27
+ .describe(
28
+ "Until when the previous version stays parked and ready, in milliseconds. While it does, going back takes seconds and nothing is downloaded or rebuilt; after it, going back uses the pinned image.",
29
+ ),
30
+ });
31
+ export type UpdateOutcome = z.infer<typeof UpdateOutcomeSchema>;
32
+
33
+ // A published release that was taken back after it shipped (the release pipeline's rollback-stable.sh marks it on its
34
+ // GitHub release). A sandbox running one is told, and offered the way back.
35
+ export const WithdrawnReleaseSchema = z.object({
36
+ version: z.string().describe("The withdrawn version, which is the one this sandbox is running."),
37
+ reason: z.string().optional().describe("Why it was withdrawn, as the people who withdrew it put it."),
38
+ });
39
+ export type WithdrawnRelease = z.infer<typeof WithdrawnReleaseSchema>;
40
+
41
+ // The owner's "not this one": no update is offered while the newest release is the skipped one. `null` clears it.
42
+ export const SkipUpdateInputSchema = z.object({
43
+ version: z
44
+ .string()
45
+ .min(1)
46
+ .nullable()
47
+ .describe("The release to stop offering, or null to offer the newest release again. A newer release than the skipped one is always offered."),
48
+ });
49
+ export type SkipUpdateInput = z.infer<typeof SkipUpdateInputSchema>;
50
+
51
+ // One image a sandbox can go back to, as `ic sandbox versions` knows it: the pinned local image and what it says it is.
52
+ export const RollbackTargetSchema = z.object({
53
+ image: z.string().describe("The local image a rollback would run, pinned under a tag no other flow writes."),
54
+ version: z.string().optional().describe("What that image says it is. Absent when it would not say."),
55
+ });
56
+ export type RollbackTarget = z.infer<typeof RollbackTargetSchema>;
@@ -44,6 +44,9 @@ export const WebExtFactsSchema = object({
44
44
  grants: array(WebExtGrantSchema),
45
45
  // Kill switch from the extension's popup; true means every tool refuses and reports why.
46
46
  paused: boolean(),
47
+ // The optional procedures this build answers (`loopback-catch`); strings, so a newer build's never fails a read.
48
+ // Absent from a build older than the field, which answers none of them.
49
+ features: array(string()).optional(),
47
50
  });
48
51
  export type WebExtFacts = z.infer<typeof WebExtFactsSchema>;
49
52
  export const WebExtSummarySchema = object({
@@ -37,7 +37,7 @@ export const WorkflowStepSchema = z.object({
37
37
  .min(1)
38
38
  .optional()
39
39
  .describe(
40
- "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.",
40
+ "What the step is told to do. The goal is the suite passes; 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.",
41
41
  ),
42
42
  // Steps that must finish first; empty means a root. Must be acyclic with every id existing, checked on save.
43
43
  needs: z
@@ -116,7 +116,7 @@ export type GuardOutcome = z.infer<typeof GuardOutcomeSchema>;
116
116
  // value is the field as the step wrote it; absent means most blocked verdicts had nothing to read.
117
117
  export const GateVerdictSchema = z.object({
118
118
  outcome: GuardOutcomeSchema.describe(
119
- "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.",
119
+ "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 failed one.",
120
120
  ),
121
121
  // Why, in one line; realistically the only part of this a pipeline log will show.
122
122
  reason: z.string().describe("Why, in one line. Realistically the only part of this a build log will ever show."),
@@ -156,14 +156,14 @@ export const WorkflowSchema = z.object({
156
156
  });
157
157
  export type Workflow = z.infer<typeof WorkflowSchema>;
158
158
 
159
- // skipped means the step never ran because something it waited on didn't finish; that's why a failed run shows one red
160
- // node and a trail of grey ones.
159
+ // skipped means the step never ran because something it waited on didn't finish; that's why a failed run shows one
160
+ // failed node and a trail of skipped ones.
161
161
  export const WorkflowStepStateSchema = z.enum(["pending", "running", "done", "failed", "skipped", "stopped"]);
162
162
  export type WorkflowStepState = z.infer<typeof WorkflowStepStateSchema>;
163
163
  export const WorkflowStepRunSchema = z.object({
164
164
  stepId: StepIdSchema.describe("Which step this is."),
165
165
  state: WorkflowStepStateSchema.describe(
166
- "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.",
166
+ "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 failed step and a trail of skipped ones.",
167
167
  ),
168
168
  // The conversation this step ran on; shared with its predecessor under continue, making them one card.
169
169
  conversationId: z
@@ -182,6 +182,13 @@ export const WorkspaceFilePresentSchema = z.object({
182
182
  .describe(
183
183
  "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.",
184
184
  ),
185
+ // Absent means the window decoded cleanly. Set by a server that checks (the desktop app's folder server does).
186
+ lossy: z
187
+ .literal(true)
188
+ .optional()
189
+ .describe(
190
+ "The bytes are not valid UTF-8, so `content` holds replacement characters where they failed to decode. Saving that text back would change the file, so treat it as read-only.",
191
+ ),
185
192
  });
186
193
  // Nothing there is an answer, not a failure: most reads here are read-it-if-there, and a 404 would spam the browser's
187
194
  // network log per miss. A refused read (an escape, a denylisted path) is still an error.
@@ -24,7 +24,7 @@ test("an archived attempt is skipped over, not resurrected", () => {
24
24
  expect(planFixAttempt(BASE, [], [BASE])).toEqual({ kind: `new`, conversationId: fixAttemptId(BASE, 2), attempt: 2 });
25
25
  });
26
26
 
27
- test("a landed attempt is closed: the same gates red again get a fresh attempt", () => {
27
+ test("a landed attempt is closed: the same gates failing again get a fresh attempt", () => {
28
28
  const plan = planFixAttempt(BASE, [agent(BASE, { status: `landed` })], []);
29
29
  expect(plan).toEqual({ kind: `new`, conversationId: fixAttemptId(BASE, 2), attempt: 2 });
30
30
  });
@@ -1,7 +1,7 @@
1
1
  import { turnedAwayCode } from "../policy/turned-away.js";
2
2
  import { type AgentSummary, awaitsWake } from "../schemas/agents.js";
3
3
 
4
- /* WHAT BECAME OF THE AGENT A SURFACE SENT AFTER A FAILURE — a red pipeline row, a refused push. */
4
+ /* WHAT BECAME OF THE AGENT A SURFACE SENT AFTER A FAILURE — a failed pipeline row, a refused push. */
5
5
 
6
6
  export type FixStanceKind =
7
7
  // A turn is in flight. Nothing is owed by the reader.
@@ -27,6 +27,10 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
27
27
  { path: "blobs/", portability: "carry" },
28
28
  // Index of published-page conversations; the pages travel with /work anyway, this makes one withdrawable.
29
29
  { path: "shares.json", portability: "carry" },
30
+ // Each person's own answer to which words their editor uses here (developer or maker), keyed by sign-in address
31
+ // (settings/member-audience.ts). Carried: it is theirs, and asking again after a move would be a question already
32
+ // answered.
33
+ { path: "member-audience.json", portability: "carry" },
30
34
  { path: "activity.jsonl", portability: "carry" },
31
35
  { path: "usage.jsonl", portability: "carry" },
32
36
  { path: "account-usage.json", portability: "carry" },
@@ -78,6 +82,14 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
78
82
  { path: "engines/", portability: "derived" },
79
83
  // An update the host downloaded and built for this sandbox (`ic sandbox prepare`); about this machine only.
80
84
  { path: "update-staged.json", portability: "derived" },
85
+ // What the host last did about this sandbox's version (ic writes it), the owner's skipped release, and why the
86
+ // last boot could not start (the daemon writes it, the host's probation reads it): all rebuilt by whoever next says.
87
+ { path: "update-outcome.json", portability: "derived" },
88
+ { path: "update-skipped.json", portability: "derived" },
89
+ { path: "boot-failure.json", portability: "derived" },
90
+ // A rebuild's ask that the next boot resume the turns it cuts (restart-resume.ts): about this machine's next boot only,
91
+ // read once by it, so a moved sandbox starts without one.
92
+ { path: "restart-resume.json", portability: "derived" },
81
93
 
82
94
  /* ---- credentials ---- */
83
95
 
@@ -87,6 +99,13 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
87
99
  portability: "secret",
88
100
  note: "Re-add each ssh host on the Capabilities view, its key does not travel.",
89
101
  },
102
+ // The container sshd's host key (docker-entrypoint.sh generates it once): the sandbox's SSH identity, which desktop
103
+ // sync pins. A private key, so it stays; the target makes its own on first boot.
104
+ {
105
+ path: "ssh-host-keys/",
106
+ portability: "secret",
107
+ note: "Clear this sandbox's old host key from your computer's known_hosts before syncing it again.",
108
+ },
90
109
  // The cli-proxy's config, which holds the routed subscriptions' provider tokens.
91
110
  {
92
111
  path: "translator/",
@@ -58,9 +58,6 @@ const RUNTIME_DOMAINS = [
58
58
 
59
59
  // The audit log grew or a gateway's status moved or aged out; the log lives outside /work, so no file push carries it.
60
60
  { domain: "activity", invalidates: [["activity"], ["activity-status"]] },
61
-
62
- // What the pre-push hook left behind: a push filed, a finding measured gone, dismissed or handed to an agent.
63
- { domain: "pushes", invalidates: [["push-checks"]] },
64
61
  ] as const satisfies readonly RuntimeDomainBinding[];
65
62
 
66
63
  export const RUNTIME_DOMAIN_BINDINGS: readonly RuntimeDomainBinding[] = RUNTIME_DOMAINS;
@@ -132,6 +132,10 @@ const STATE_FILES = [
132
132
  // needing, not what one machine did. The drift snapshot self-expires on a move since its `bornAt` won't match the
133
133
  // new container.
134
134
  { path: ".intentic/records/runtime-installs.json", invalidates: ["environment"], portability: "carry" },
135
+ // What agents are waiting on people for (docs/architecture/needs.md): every open need and the recent closed ones.
136
+ // Holds no credential (a secret need names the secret, its value goes to the secret store), so it may `carry`: a
137
+ // question a moved sandbox still has to ask. `needs` is the chat cards', the board's and the inbox's shared read.
138
+ { path: ".intentic/records/needs.json", invalidates: ["needs"], portability: "carry" },
135
139
  // Where each agent engine's version comes from: blessed list, upstream newest, a pin, or the image
136
140
  // (schemas/engines.ts). The versions themselves are machine state on the daemon's volume (architecture-specific
137
141
  // binaries); the policy here travels. `versioned`: a human decision worth reviewing. Own key, not `environment`'s:
@@ -379,12 +383,6 @@ const STATE_FILES = [
379
383
  portability: "derived",
380
384
  note: "The target stamps its own daemon version on first boot.",
381
385
  },
382
- {
383
- path: ".intentic/records/push-checks.json",
384
- invalidates: [],
385
- why: "What each push check found and let through, per project, and what became of every finding: still open, measured gone, or dismissed. Filed from the report the pre-push hook leaves in the repository's git dir; read through GET /workspace/push-checks, which the daemon pushes itself (the `pushes` runtime domain).",
386
- portability: "carry",
387
- },
388
386
  // What an update converted before it booted all the way (the sandbox daemon's state-journal.ts): a copy of every
389
387
  // file the new version changed, kept until it commits and for a grace window after. `secret` whatever the source
390
388
  // was, since a vault's copy is a vault; locked below for the same reason.
@@ -1,4 +1,4 @@
1
- import { documentOf, documentTitle, isDocumentPath, isPlanDocumentPath } from "./documents.js";
1
+ import { deliverableKindOf, documentOf, documentTitle, isDocumentPath, isPlanDocumentPath } from "./documents.js";
2
2
  import { PLAN_DOCUMENTS_DIR } from "../state/workspace-state.js";
3
3
 
4
4
  const write = (path: string, newText: string, extra: { oldText?: string; truncated?: boolean } = {}) => [
@@ -64,3 +64,27 @@ describe("paths", () => {
64
64
  expect(isPlanDocumentPath("docs/plans/anything.md")).toBe(false);
65
65
  });
66
66
  });
67
+
68
+ describe("deliverableKindOf", () => {
69
+ test("names each kind by its extension, in any case, `.htm` a web page too", () => {
70
+ expect(["q3/report.docx", "deck.PPTX", "numbers.xlsx", "scan.pdf", "site/index.html", "old/page.HTM"].map(deliverableKindOf)).toEqual([
71
+ "docx",
72
+ "pptx",
73
+ "xlsx",
74
+ "pdf",
75
+ "html",
76
+ "html",
77
+ ]);
78
+ });
79
+
80
+ test("anything else is none: source, markdown, the old binary formats, a dotfile, a directory's dot", () => {
81
+ expect(["src/app.ts", "notes.md", "letter.doc", ".pdf", "release.v2/README", "docx"].map(deliverableKindOf)).toEqual([
82
+ undefined,
83
+ undefined,
84
+ undefined,
85
+ undefined,
86
+ undefined,
87
+ undefined,
88
+ ]);
89
+ });
90
+ });
@@ -14,6 +14,30 @@ export const isDocumentPath = (path: string): boolean => {
14
14
  return DOCUMENT_EXTENSIONS.some((extension) => lower.endsWith(extension));
15
15
  };
16
16
 
17
+ // A deliverable is a file a turn hands a person to open rather than read as code: an office document, a PDF, a web
18
+ // page. The daemon names the ones a command wrote on its card (agent/tools/produced-documents.ts) and the chat lists a
19
+ // turn's under its answer, so the two ask this one table. Markdown stays out: the chat already draws it as a document.
20
+ export type DeliverableKind = "docx" | "pptx" | "xlsx" | "pdf" | "html";
21
+
22
+ const DELIVERABLE_BY_EXTENSION = new Map<string, DeliverableKind>([
23
+ ["docx", "docx"],
24
+ ["pptx", "pptx"],
25
+ ["xlsx", "xlsx"],
26
+ ["pdf", "pdf"],
27
+ ["html", "html"],
28
+ ["htm", "html"],
29
+ ]);
30
+
31
+ // The kind of deliverable a path names by its extension, or undefined for any other file (a dotfile included).
32
+ export const deliverableKindOf = (path: string): DeliverableKind | undefined => {
33
+ const name = path.slice(path.lastIndexOf("/") + 1).toLowerCase();
34
+ const dot = name.lastIndexOf(".");
35
+ return dot > 0 ? DELIVERABLE_BY_EXTENSION.get(name.slice(dot + 1)) : undefined;
36
+ };
37
+
38
+ // Every extension a deliverable is written with, for a reader that has to find the names in free text.
39
+ export const DELIVERABLE_EXTENSIONS = [...DELIVERABLE_BY_EXTENSION.keys()];
40
+
17
41
  // Matched by directory, not name (a mint-fresh phrase like `map-of-this-wiggly-spring.md` carries no signal). The
18
42
  // directory is declared in workspace-state.ts, the one exception to its lock.
19
43
  export const isPlanDocumentPath = (path: string): boolean => path.startsWith(`${PLAN_DOCUMENTS_DIR}/`);
package/src/text/title.ts CHANGED
@@ -24,7 +24,7 @@ const OPENERS: readonly RegExp[] = [
24
24
  /^(?:quick\s+(?:one|question|q)|one\s+more\s+thing)\b[\s,.:;!—–-]*/i,
25
25
  ];
26
26
 
27
- // Terminal punctuation followed by a break; keeps `v1.2` and `foo.ts` intact while still ending `Why is it red?`.
27
+ // Terminal punctuation followed by a break; keeps `v1.2` and `foo.ts` intact while still ending `Why does it fail?`.
28
28
  const SENTENCE_END = /[.!?](?=\s|$)/;
29
29
 
30
30
  // Matches a first sentence reporting past work (`We just landed…`); that is scene-setting, not the ask.