@intentic/sandbox-contract 1.317.0 → 1.319.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 (348) hide show
  1. package/README.md +16 -6
  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 +62 -1
  11. package/dist/contracts/agent.contract.d.ts.map +1 -1
  12. package/dist/contracts/agents.contract.d.ts +830 -93
  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/ci.contract.d.ts +22 -4
  17. package/dist/contracts/ci.contract.d.ts.map +1 -1
  18. package/dist/contracts/device.contract.d.ts +38 -0
  19. package/dist/contracts/device.contract.d.ts.map +1 -1
  20. package/dist/contracts/device.contract.js +2 -0
  21. package/dist/contracts/device.contract.js.map +1 -1
  22. package/dist/contracts/endpoints.contract.d.ts +23 -0
  23. package/dist/contracts/endpoints.contract.d.ts.map +1 -1
  24. package/dist/contracts/endpoints.contract.js +6 -1
  25. package/dist/contracts/endpoints.contract.js.map +1 -1
  26. package/dist/contracts/extensions.contract.d.ts +12 -0
  27. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  28. package/dist/contracts/needs.contract.d.ts +2 -0
  29. package/dist/contracts/needs.contract.d.ts.map +1 -1
  30. package/dist/contracts/providers.contract.d.ts +4 -0
  31. package/dist/contracts/providers.contract.d.ts.map +1 -1
  32. package/dist/contracts/runner.contract.d.ts +143 -124
  33. package/dist/contracts/runner.contract.d.ts.map +1 -1
  34. package/dist/contracts/secrets.contract.d.ts +37 -0
  35. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  36. package/dist/contracts/secrets.contract.js +20 -1
  37. package/dist/contracts/secrets.contract.js.map +1 -1
  38. package/dist/contracts/sessions.contract.d.ts +4 -0
  39. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  40. package/dist/contracts/settings.contract.d.ts +31 -4
  41. package/dist/contracts/settings.contract.d.ts.map +1 -1
  42. package/dist/contracts/settings.contract.js +20 -1
  43. package/dist/contracts/settings.contract.js.map +1 -1
  44. package/dist/contracts/system.contract.d.ts +102 -464
  45. package/dist/contracts/system.contract.d.ts.map +1 -1
  46. package/dist/contracts/system.contract.js +2 -12
  47. package/dist/contracts/system.contract.js.map +1 -1
  48. package/dist/contracts/translator.contract.d.ts +7 -0
  49. package/dist/contracts/translator.contract.d.ts.map +1 -1
  50. package/dist/contracts/translator.contract.js.map +1 -1
  51. package/dist/contracts/webext.contract.d.ts +38 -0
  52. package/dist/contracts/webext.contract.d.ts.map +1 -1
  53. package/dist/contracts/webext.contract.js +3 -0
  54. package/dist/contracts/webext.contract.js.map +1 -1
  55. package/dist/contracts/workspace.contract.d.ts +1 -133
  56. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  57. package/dist/contracts/workspace.contract.js +1 -37
  58. package/dist/contracts/workspace.contract.js.map +1 -1
  59. package/dist/events/agent-events.d.ts +21 -0
  60. package/dist/events/agent-events.d.ts.map +1 -1
  61. package/dist/events/agent-events.js +11 -0
  62. package/dist/events/agent-events.js.map +1 -1
  63. package/dist/events/agent-words.d.ts +1 -0
  64. package/dist/events/agent-words.d.ts.map +1 -1
  65. package/dist/events/agent-words.js +1 -1
  66. package/dist/events/agent-words.js.map +1 -1
  67. package/dist/events/land-breakage.d.ts.map +1 -1
  68. package/dist/events/land-breakage.js.map +1 -1
  69. package/dist/events/resume.d.ts +6 -1
  70. package/dist/events/resume.d.ts.map +1 -1
  71. package/dist/events/resume.js +17 -10
  72. package/dist/events/resume.js.map +1 -1
  73. package/dist/events/sandbox-notice.d.ts +183 -0
  74. package/dist/events/sandbox-notice.d.ts.map +1 -0
  75. package/dist/events/sandbox-notice.js +56 -0
  76. package/dist/events/sandbox-notice.js.map +1 -0
  77. package/dist/events/system-events.d.ts +50 -18
  78. package/dist/events/system-events.d.ts.map +1 -1
  79. package/dist/events/system-events.js +4 -2
  80. package/dist/events/system-events.js.map +1 -1
  81. package/dist/events/transcript.d.ts +24 -0
  82. package/dist/events/transcript.d.ts.map +1 -1
  83. package/dist/events/transcript.js +3 -1
  84. package/dist/events/transcript.js.map +1 -1
  85. package/dist/front/generated/browser-wire.d.ts +13 -0
  86. package/dist/front/generated/browser-wire.d.ts.map +1 -1
  87. package/dist/front/generated/wire.d.ts +4 -0
  88. package/dist/front/generated/wire.d.ts.map +1 -1
  89. package/dist/ids/conversation-ids.d.ts +1 -3
  90. package/dist/ids/conversation-ids.d.ts.map +1 -1
  91. package/dist/ids/conversation-ids.js +1 -3
  92. package/dist/ids/conversation-ids.js.map +1 -1
  93. package/dist/ids/project-dir.d.ts +6 -0
  94. package/dist/ids/project-dir.d.ts.map +1 -0
  95. package/dist/ids/project-dir.js +43 -0
  96. package/dist/ids/project-dir.js.map +1 -0
  97. package/dist/index.d.ts +11543 -11039
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +8 -4
  100. package/dist/index.js.map +1 -1
  101. package/dist/models/agent-runtimes.d.ts.map +1 -1
  102. package/dist/models/agent-runtimes.js +2 -2
  103. package/dist/models/agent-runtimes.js.map +1 -1
  104. package/dist/models/model-roles.d.ts +1 -9
  105. package/dist/models/model-roles.d.ts.map +1 -1
  106. package/dist/models/model-roles.js +1 -9
  107. package/dist/models/model-roles.js.map +1 -1
  108. package/dist/policy/command-classes.d.ts.map +1 -1
  109. package/dist/policy/command-classes.js +52 -3
  110. package/dist/policy/command-classes.js.map +1 -1
  111. package/dist/policy/safety-policy.d.ts +7 -1
  112. package/dist/policy/safety-policy.d.ts.map +1 -1
  113. package/dist/policy/safety-policy.js +2 -0
  114. package/dist/policy/safety-policy.js.map +1 -1
  115. package/dist/policy/secret-hosts.d.ts +13 -0
  116. package/dist/policy/secret-hosts.d.ts.map +1 -0
  117. package/dist/policy/secret-hosts.js +41 -0
  118. package/dist/policy/secret-hosts.js.map +1 -0
  119. package/dist/policy/turned-away.d.ts.map +1 -1
  120. package/dist/policy/turned-away.js +1 -0
  121. package/dist/policy/turned-away.js.map +1 -1
  122. package/dist/protocol/raw-routes.d.ts +12 -0
  123. package/dist/protocol/raw-routes.d.ts.map +1 -1
  124. package/dist/protocol/raw-routes.js +5 -0
  125. package/dist/protocol/raw-routes.js.map +1 -1
  126. package/dist/protocol/routes.d.ts +2 -6
  127. package/dist/protocol/routes.d.ts.map +1 -1
  128. package/dist/protocol/routes.js +2 -3
  129. package/dist/protocol/routes.js.map +1 -1
  130. package/dist/protocol/stream-of.d.ts +6 -0
  131. package/dist/protocol/stream-of.d.ts.map +1 -0
  132. package/dist/protocol/stream-of.js +4 -0
  133. package/dist/protocol/stream-of.js.map +1 -0
  134. package/dist/protocol/vitals.d.ts +8 -0
  135. package/dist/protocol/vitals.d.ts.map +1 -0
  136. package/dist/protocol/vitals.js +16 -0
  137. package/dist/protocol/vitals.js.map +1 -0
  138. package/dist/schemas/activity.js +1 -1
  139. package/dist/schemas/activity.js.map +1 -1
  140. package/dist/schemas/agent.d.ts +25 -2
  141. package/dist/schemas/agent.d.ts.map +1 -1
  142. package/dist/schemas/agent.js +14 -3
  143. package/dist/schemas/agent.js.map +1 -1
  144. package/dist/schemas/agents.d.ts +67 -0
  145. package/dist/schemas/agents.d.ts.map +1 -1
  146. package/dist/schemas/agents.js +36 -0
  147. package/dist/schemas/agents.js.map +1 -1
  148. package/dist/schemas/automations.d.ts +20 -0
  149. package/dist/schemas/automations.d.ts.map +1 -1
  150. package/dist/schemas/capabilities.d.ts +2 -0
  151. package/dist/schemas/capabilities.d.ts.map +1 -1
  152. package/dist/schemas/capabilities.js +34 -4
  153. package/dist/schemas/capabilities.js.map +1 -1
  154. package/dist/schemas/ci.d.ts +69 -9
  155. package/dist/schemas/ci.d.ts.map +1 -1
  156. package/dist/schemas/ci.js +29 -8
  157. package/dist/schemas/ci.js.map +1 -1
  158. package/dist/schemas/context-trim.d.ts +5 -1
  159. package/dist/schemas/context-trim.d.ts.map +1 -1
  160. package/dist/schemas/context-trim.js +8 -4
  161. package/dist/schemas/context-trim.js.map +1 -1
  162. package/dist/schemas/devices.d.ts +23 -0
  163. package/dist/schemas/devices.d.ts.map +1 -1
  164. package/dist/schemas/devices.js +16 -1
  165. package/dist/schemas/devices.js.map +1 -1
  166. package/dist/schemas/environment.d.ts +48 -0
  167. package/dist/schemas/environment.d.ts.map +1 -1
  168. package/dist/schemas/environment.js +15 -0
  169. package/dist/schemas/environment.js.map +1 -1
  170. package/dist/schemas/extension-updates.d.ts +24 -0
  171. package/dist/schemas/extension-updates.d.ts.map +1 -1
  172. package/dist/schemas/git/git.d.ts +13 -0
  173. package/dist/schemas/git/git.d.ts.map +1 -1
  174. package/dist/schemas/git/git.js +11 -1
  175. package/dist/schemas/git/git.js.map +1 -1
  176. package/dist/schemas/hosts.d.ts +2 -0
  177. package/dist/schemas/hosts.d.ts.map +1 -1
  178. package/dist/schemas/hosts.js +2 -1
  179. package/dist/schemas/hosts.js.map +1 -1
  180. package/dist/schemas/keep-warm.d.ts +4 -0
  181. package/dist/schemas/keep-warm.d.ts.map +1 -1
  182. package/dist/schemas/keep-warm.js +15 -5
  183. package/dist/schemas/keep-warm.js.map +1 -1
  184. package/dist/schemas/loopback-catch.d.ts +33 -0
  185. package/dist/schemas/loopback-catch.d.ts.map +1 -0
  186. package/dist/schemas/loopback-catch.js +20 -0
  187. package/dist/schemas/loopback-catch.js.map +1 -0
  188. package/dist/schemas/maintenance.js +1 -1
  189. package/dist/schemas/maintenance.js.map +1 -1
  190. package/dist/schemas/needs.d.ts +3 -0
  191. package/dist/schemas/needs.d.ts.map +1 -1
  192. package/dist/schemas/needs.js +10 -3
  193. package/dist/schemas/needs.js.map +1 -1
  194. package/dist/schemas/personas.d.ts.map +1 -1
  195. package/dist/schemas/personas.js +6 -3
  196. package/dist/schemas/personas.js.map +1 -1
  197. package/dist/schemas/providers/provider-oauth.d.ts +87 -0
  198. package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
  199. package/dist/schemas/providers/provider-oauth.js +20 -0
  200. package/dist/schemas/providers/provider-oauth.js.map +1 -1
  201. package/dist/schemas/secrets.d.ts +66 -0
  202. package/dist/schemas/secrets.d.ts.map +1 -1
  203. package/dist/schemas/secrets.js +50 -0
  204. package/dist/schemas/secrets.js.map +1 -1
  205. package/dist/schemas/settings-history.d.ts +2 -2
  206. package/dist/schemas/settings-history.d.ts.map +1 -1
  207. package/dist/schemas/settings-history.js +2 -0
  208. package/dist/schemas/settings-history.js.map +1 -1
  209. package/dist/schemas/settings.d.ts +42 -4
  210. package/dist/schemas/settings.d.ts.map +1 -1
  211. package/dist/schemas/settings.js +26 -8
  212. package/dist/schemas/settings.js.map +1 -1
  213. package/dist/schemas/terminal.d.ts +2 -3
  214. package/dist/schemas/terminal.d.ts.map +1 -1
  215. package/dist/schemas/terminal.js +12 -4
  216. package/dist/schemas/terminal.js.map +1 -1
  217. package/dist/schemas/webext.d.ts +3 -0
  218. package/dist/schemas/webext.d.ts.map +1 -1
  219. package/dist/schemas/webext.js +1 -0
  220. package/dist/schemas/webext.js.map +1 -1
  221. package/dist/schemas/workflows.js +3 -3
  222. package/dist/schemas/workflows.js.map +1 -1
  223. package/dist/schemas/workspace/workspace-tree.d.ts +2 -56
  224. package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
  225. package/dist/schemas/workspace/workspace-tree.js +7 -15
  226. package/dist/schemas/workspace/workspace-tree.js.map +1 -1
  227. package/dist/state/definition.d.ts +12 -8
  228. package/dist/state/definition.d.ts.map +1 -1
  229. package/dist/state/history-state.d.ts.map +1 -1
  230. package/dist/state/history-state.js +7 -0
  231. package/dist/state/history-state.js.map +1 -1
  232. package/dist/state/runtime-state.d.ts +0 -3
  233. package/dist/state/runtime-state.d.ts.map +1 -1
  234. package/dist/state/runtime-state.js +0 -1
  235. package/dist/state/runtime-state.js.map +1 -1
  236. package/dist/state/workspace-state.d.ts +0 -5
  237. package/dist/state/workspace-state.d.ts.map +1 -1
  238. package/dist/state/workspace-state.js +0 -6
  239. package/dist/state/workspace-state.js.map +1 -1
  240. package/dist/text/documents.d.ts +3 -0
  241. package/dist/text/documents.d.ts.map +1 -1
  242. package/dist/text/documents.js +14 -0
  243. package/dist/text/documents.js.map +1 -1
  244. package/dist/text/transcript-fold.d.ts +4 -3
  245. package/dist/text/transcript-fold.d.ts.map +1 -1
  246. package/dist/text/transcript-fold.js +137 -31
  247. package/dist/text/transcript-fold.js.map +1 -1
  248. package/dist/webext/index.d.ts +1 -0
  249. package/dist/webext/index.d.ts.map +1 -1
  250. package/dist/webext/index.js +1 -0
  251. package/dist/webext/index.js.map +1 -1
  252. package/package.json +5 -5
  253. package/src/chores/chores.ts +2 -2
  254. package/src/chores/probes.ts +1 -1
  255. package/src/contracts/accounts.contract.ts +12 -0
  256. package/src/contracts/agents.contract.ts +13 -13
  257. package/src/contracts/device.contract.ts +5 -0
  258. package/src/contracts/endpoints.contract.ts +20 -4
  259. package/src/contracts/secrets.contract.ts +28 -0
  260. package/src/contracts/settings.contract.ts +29 -0
  261. package/src/contracts/system.contract.ts +5 -14
  262. package/src/contracts/translator.contract.ts +2 -1
  263. package/src/contracts/webext.contract.ts +7 -1
  264. package/src/contracts/workspace.contract.ts +0 -49
  265. package/src/events/agent-events.ts +18 -1
  266. package/src/events/agent-words.ts +4 -1
  267. package/src/events/land-breakage.ts +3 -4
  268. package/src/events/resume.test.ts +13 -2
  269. package/src/events/resume.ts +25 -12
  270. package/src/events/sandbox-notice.ts +92 -0
  271. package/src/events/system-events.ts +12 -5
  272. package/src/events/transcript.ts +7 -1
  273. package/src/events/verify-nudge.ts +3 -3
  274. package/src/front/generated/browser-wire.json +95 -0
  275. package/src/front/generated/browser-wire.ts +36 -0
  276. package/src/front/generated/front-wire.json +25 -0
  277. package/src/front/generated/wire.ts +2 -2
  278. package/src/front/wire-manifests.test.ts +7 -0
  279. package/src/ids/conversation-ids.test.ts +2 -26
  280. package/src/ids/conversation-ids.ts +6 -12
  281. package/src/ids/project-dir.fixture.json +65 -0
  282. package/src/ids/project-dir.test.ts +60 -0
  283. package/src/ids/project-dir.ts +70 -0
  284. package/src/index.ts +13 -4
  285. package/src/models/agent-catalog.test.ts +7 -1
  286. package/src/models/agent-runtimes.ts +7 -5
  287. package/src/models/model-roles.ts +1 -9
  288. package/src/policy/command-classes.test.ts +39 -0
  289. package/src/policy/command-classes.ts +70 -3
  290. package/src/policy/safety-policy.ts +10 -0
  291. package/src/policy/secret-hosts.test.ts +89 -0
  292. package/src/policy/secret-hosts.ts +73 -0
  293. package/src/policy/turned-away.ts +1 -0
  294. package/src/protocol/raw-routes.ts +8 -1
  295. package/src/protocol/routes.test.ts +6 -6
  296. package/src/protocol/routes.ts +2 -9
  297. package/src/protocol/stream-of.ts +13 -0
  298. package/src/protocol/vitals.test.ts +46 -0
  299. package/src/protocol/vitals.ts +38 -0
  300. package/src/schemas/activity.ts +1 -1
  301. package/src/schemas/agent.ts +20 -3
  302. package/src/schemas/agents.ts +58 -4
  303. package/src/schemas/automations.ts +2 -2
  304. package/src/schemas/capabilities.ts +44 -7
  305. package/src/schemas/ci.ts +56 -11
  306. package/src/schemas/context-trim.ts +11 -6
  307. package/src/schemas/devices.ts +38 -2
  308. package/src/schemas/environment.ts +39 -0
  309. package/src/schemas/git/git.ts +20 -2
  310. package/src/schemas/hosts.ts +4 -1
  311. package/src/schemas/keep-warm.ts +19 -6
  312. package/src/schemas/loopback-catch.ts +45 -0
  313. package/src/schemas/maintenance.ts +1 -1
  314. package/src/schemas/needs.ts +12 -3
  315. package/src/schemas/personas.ts +8 -3
  316. package/src/schemas/providers/provider-oauth.ts +35 -0
  317. package/src/schemas/secrets.ts +70 -0
  318. package/src/schemas/settings-history.ts +3 -0
  319. package/src/schemas/settings.test.ts +19 -1
  320. package/src/schemas/settings.ts +59 -16
  321. package/src/schemas/speaker.ts +5 -5
  322. package/src/schemas/terminal.ts +19 -6
  323. package/src/schemas/webext.ts +3 -0
  324. package/src/schemas/workflows.ts +5 -5
  325. package/src/schemas/workspace/workspace-tree.ts +12 -20
  326. package/src/state/fix-attempt-plan.test.ts +1 -1
  327. package/src/state/fix-stance.ts +1 -1
  328. package/src/state/history-state.ts +14 -0
  329. package/src/state/runtime-state.ts +0 -3
  330. package/src/state/workspace-state.ts +0 -6
  331. package/src/text/documents.test.ts +25 -1
  332. package/src/text/documents.ts +24 -0
  333. package/src/text/title.ts +1 -1
  334. package/src/text/transcript-fold.test.ts +218 -25
  335. package/src/text/transcript-fold.ts +166 -45
  336. package/src/webext/index.ts +1 -0
  337. package/dist/schemas/workspace/push-checks.d.ts +0 -222
  338. package/dist/schemas/workspace/push-checks.d.ts.map +0 -1
  339. package/dist/schemas/workspace/push-checks.js +0 -97
  340. package/dist/schemas/workspace/push-checks.js.map +0 -1
  341. package/dist/state/red-streak.d.ts +0 -7
  342. package/dist/state/red-streak.d.ts.map +0 -1
  343. package/dist/state/red-streak.js +0 -11
  344. package/dist/state/red-streak.js.map +0 -1
  345. package/src/schemas/workspace/push-checks.test.ts +0 -28
  346. package/src/schemas/workspace/push-checks.ts +0 -155
  347. package/src/state/red-streak.test.ts +0 -28
  348. 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.",
@@ -354,13 +372,6 @@ export const SandboxSettingsSchema = z.object({
354
372
  .describe(
355
373
  "What share of conversations to run without the brief, so the two can be compared. Whole conversations rather than individual turns, because the brief sits in the prompt for the whole session and withholding it from one turn would not take it back.",
356
374
  ),
357
- // Only the eager background pass; the `fileq` CLI itself is always on PATH regardless, gated only by its own skill.
358
- sidecars: z
359
- .boolean()
360
- .default(false)
361
- .describe(
362
- "Keep an up-to-date markdown rendering of every document, image and audio file in the workspace, made in the background as files land, so the agent reads a pre-derived text instead of paying to parse the file mid-task. Costs background CPU on a document-heavy workspace, so it is a switch rather than a default.",
363
- ),
364
375
  outputCleaners: z
365
376
  .string()
366
377
  .default("")
@@ -385,7 +396,7 @@ export const SandboxSettingsSchema = z.object({
385
396
  // purpose: the reading runs under a 5s deadline, and this text is paid for on every chat that opens on Auto.
386
397
  autoModelGuidance: z
387
398
  .string()
388
- .max(2000)
399
+ .max(AUTO_MODEL_GUIDANCE_MAX)
389
400
  .default("")
390
401
  .describe(
391
402
  "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 +447,22 @@ export const SandboxSettingsSchema = z.object({
436
447
  ),
437
448
  // Worth it since the container is recreated on every update or environment approval — otherwise approving a
438
449
  // 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
450
+ // What happens when main's CI fails (ci/main-fixer.ts): its first failed job starts one fix agent, which is sent
451
+ // every later failure on main until main passes, within a few turns, and a failure on the CI fleet itself is re-run
441
452
  // once instead. Off, it is only reported.
442
453
  autoRepair: z
443
454
  .boolean()
444
455
  .default(true)
445
456
  .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.",
457
+ "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.",
458
+ ),
459
+ // What another writer pushes reaches the main tree within minutes instead of at the owner's next pull
460
+ // (git/remote/follow-origin.ts), so an agent whose work clashes with it meets the clash at its own land.
461
+ followOrigin: z
462
+ .boolean()
463
+ .default(true)
464
+ .describe(
465
+ "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
466
  ),
448
467
  // Where heavy work runs: a runner (the same image, on one of the owner's machines) instead of this sandbox. Keyed by
449
468
  // the heavy-command rule an agent's command matched (system/resources/heavy-commands.ts), each naming a runner id. A
@@ -518,6 +537,11 @@ export const SandboxSettingsSchema = z.object({
518
537
  commandJudge: CommandJudgeModeSchema.default("on").describe(
519
538
  "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.",
520
539
  ),
540
+ // Only whether a person is asked: where the install lands (the conversation's own copy, or the daemon's install lane
541
+ // for a main-tree turn) is the sandbox's business, so no setting names it.
542
+ projectInstalls: ProjectInstallModeSchema.default("automatic").describe(
543
+ "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.",
544
+ ),
521
545
  // Three separate ceilings since they stop different things: width (parallel fan-out), lifetime (per conversation),
522
546
  // depth (how far a child may delegate) — raising width alone just hits the lifetime cap sooner. Defaults mirror the
523
547
  // CLI's own unset behavior; hitting one stops the agent rather than retrying.
@@ -540,6 +564,24 @@ export const SandboxSettingsWriteSchema = SandboxSettingsSchema.refine((settings
540
564
  path: ["rules"],
541
565
  });
542
566
 
567
+ // A browser's answer to who the screens are written for, for the person signed in to it. `offer` is the one that must
568
+ // not overwrite: a browser handing over the answer it kept on its own, taken only while the sandbox keeps none for them.
569
+ export const AudienceAnswerSchema = z.object({
570
+ audience: AudienceSchema.describe("developer for git's own words, maker for plain ones."),
571
+ offer: z
572
+ .boolean()
573
+ .optional()
574
+ .describe("Take it only while this person has no answer kept here yet. Absent or false replaces whatever is kept."),
575
+ });
576
+ export type AudienceAnswer = z.infer<typeof AudienceAnswerSchema>;
577
+
578
+ // What the sandbox keeps for this person after the answer, and whether this call set it: false is an offer it declined.
579
+ export const AudienceStateSchema = z.object({
580
+ audience: AudienceSchema.describe("The words this sandbox's editor now uses."),
581
+ adopted: z.boolean().describe("Whether this call set it. False means an offer met an answer already kept, which stands."),
582
+ });
583
+ export type AudienceState = z.infer<typeof AudienceStateSchema>;
584
+
543
585
  // A browser telling the sandbox which clock IT is on. An offer, not an instruction: see `adoptTimezone`.
544
586
  export const TimezoneOfferSchema = z.object({
545
587
  timezone: ZoneSchema.describe("The zone the offering machine is in, as an IANA name like Europe/Warsaw."),
@@ -581,6 +623,7 @@ export const InputSavingsSchema = z.object({
581
623
  });
582
624
  export type InputSavings = z.infer<typeof InputSavingsSchema>;
583
625
  // One arm of a turn-level experiment; mean is per turn, since the two arms never hold the same count.
626
+ // `mean` is over samples capped at both arms' pooled 95th percentile, so one runaway conversation cannot carry an arm.
584
627
  export const SavingsArmSchema = z.object({ turns: z.number(), mean: z.number() });
585
628
  // One metric's reading of a turn-level experiment (the two arms, plus the arithmetic over them). `metric` says what
586
629
  // `mean`/`deltaPct` count:
@@ -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",
@@ -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() });
@@ -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.
@@ -203,29 +210,14 @@ export const WorkspaceDerivedQuerySchema = z.object({
203
210
  .min(1)
204
211
  .describe("The file you want the text of, as a workspace path. The real file, not its shadow: where the text is kept is this route's business."),
205
212
  });
206
- // Where the background pass stands, workspace-wide. Nothing about a file on disk can say this, and without it an
207
- // unrendered file and a file queued behind forty others look identical to a reader.
208
- export const SidecarStatusSchema = z.object({
209
- enabled: z.boolean().describe("Whether the background pass is on (the `sidecars` setting). Off means a shadow exists only where someone asked for one."),
210
- queued: z.number().describe("Files waiting for a shadow, not counting the batch being rendered right now."),
211
- deriving: z
212
- .array(z.string())
213
- .describe("The files being rendered at this moment, as workspace paths. One batch at a time, because derivation shares the box with the agent it serves."),
214
- sweeping: z.boolean().describe("Whether a whole-tree pass is running, which is what a freshly enabled setting or an unlistably large batch triggers."),
215
- broken: z.boolean().describe("Whether the `fileq` binary is missing, in which case nothing renders in the background until this sandbox restarts."),
216
- shadows: z.number().optional().describe("How many shadows the last whole-tree pass counted. Absent until one has run in this daemon's lifetime."),
217
- sweptAt: z.string().optional().describe("When that pass finished, as an ISO timestamp."),
218
- });
219
- export type SidecarStatus = z.infer<typeof SidecarStatusSchema>;
220
- // Where one file stands with that pass, which is the question a reader looking at a missing or stale shadow is
221
- // actually asking. `idle` means the pass is on and this file is not waiting for anything: what it has is what it gets.
222
- export const DerivedStateSchema = z.enum(["off", "queued", "deriving", "idle", "broken", "undeliverable"]);
213
+ // Where one file stands, which is the question a reader looking at a missing or stale rendering is actually asking.
214
+ // Nothing renders in the background: a file has text once someone asks for it, and fileq keeps it until the file changes.
215
+ export const DerivedStateSchema = z.enum(["deriving", "idle", "broken", "undeliverable"]);
223
216
  export type DerivedState = z.infer<typeof DerivedStateSchema>;
224
217
  const DerivedStandingShape = {
225
218
  state: DerivedStateSchema.describe(
226
- "Where this file stands with the background pass: switched off, waiting its turn, being read right now, settled, or unreachable because the renderer is missing. `undeliverable` is a format nothing here reads.",
219
+ "Where this file stands: being read right now, settled (what it has is what it gets until someone asks), or unreachable because the renderer is missing. `undeliverable` is a format nothing here reads.",
227
220
  ),
228
- queue: SidecarStatusSchema.describe("How the background pass as a whole is doing, so a wait can be reported as a queue rather than as nothing happening."),
229
221
  };
230
222
  export const WorkspaceDerivedPresentSchema = z.object({
231
223
  ...DerivedStandingShape,
@@ -250,7 +242,7 @@ export const WorkspaceDerivedPresentSchema = z.object({
250
242
  });
251
243
  export const WorkspaceDerivedAbsentSchema = z.object({
252
244
  ...DerivedStandingShape,
253
- present: z.literal(false).describe("There is no derived text for that file. Read `state` before saying so to anyone: absent and queued are different answers."),
245
+ present: z.literal(false).describe("There is no derived text for that file. Read `state` before saying so to anyone: absent and being read are different answers."),
254
246
  path: z.string().describe("The file, as asked for."),
255
247
  derivable: z
256
248
  .boolean()
@@ -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" },
@@ -83,6 +87,9 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
83
87
  { path: "update-outcome.json", portability: "derived" },
84
88
  { path: "update-skipped.json", portability: "derived" },
85
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" },
86
93
 
87
94
  /* ---- credentials ---- */
88
95
 
@@ -92,6 +99,13 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
92
99
  portability: "secret",
93
100
  note: "Re-add each ssh host on the Capabilities view, its key does not travel.",
94
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
+ },
95
109
  // The cli-proxy's config, which holds the routed subscriptions' provider tokens.
96
110
  {
97
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;
@@ -383,12 +383,6 @@ const STATE_FILES = [
383
383
  portability: "derived",
384
384
  note: "The target stamps its own daemon version on first boot.",
385
385
  },
386
- {
387
- path: ".intentic/records/push-checks.json",
388
- invalidates: [],
389
- 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).",
390
- portability: "carry",
391
- },
392
386
  // What an update converted before it booted all the way (the sandbox daemon's state-journal.ts): a copy of every
393
387
  // file the new version changed, kept until it commits and for a grace window after. `secret` whatever the source
394
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.