@intentic/sandbox-contract 1.343.0 → 1.345.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 (325) hide show
  1. package/README.md +10 -0
  2. package/dist/contracts/accounts.contract.d.ts +24 -0
  3. package/dist/contracts/accounts.contract.d.ts.map +1 -1
  4. package/dist/contracts/agent.contract.d.ts +214 -6
  5. package/dist/contracts/agent.contract.d.ts.map +1 -1
  6. package/dist/contracts/agent.contract.js +13 -2
  7. package/dist/contracts/agent.contract.js.map +1 -1
  8. package/dist/contracts/agents.contract.d.ts +656 -0
  9. package/dist/contracts/agents.contract.d.ts.map +1 -1
  10. package/dist/contracts/agents.contract.js +5 -3
  11. package/dist/contracts/agents.contract.js.map +1 -1
  12. package/dist/contracts/device.contract.d.ts +259 -0
  13. package/dist/contracts/device.contract.d.ts.map +1 -1
  14. package/dist/contracts/device.contract.js +5 -1
  15. package/dist/contracts/device.contract.js.map +1 -1
  16. package/dist/contracts/endpoints.contract.d.ts +2 -0
  17. package/dist/contracts/endpoints.contract.d.ts.map +1 -1
  18. package/dist/contracts/endpoints.contract.js +1 -0
  19. package/dist/contracts/endpoints.contract.js.map +1 -1
  20. package/dist/contracts/push.contract.d.ts +11 -0
  21. package/dist/contracts/push.contract.d.ts.map +1 -1
  22. package/dist/contracts/push.contract.js +10 -1
  23. package/dist/contracts/push.contract.js.map +1 -1
  24. package/dist/contracts/runner.contract.d.ts +18 -2
  25. package/dist/contracts/runner.contract.d.ts.map +1 -1
  26. package/dist/contracts/secrets.contract.d.ts +169 -0
  27. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  28. package/dist/contracts/secrets.contract.js +35 -0
  29. package/dist/contracts/secrets.contract.js.map +1 -1
  30. package/dist/contracts/sessions.contract.d.ts +2 -0
  31. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  32. package/dist/contracts/settings.contract.d.ts +37 -2
  33. package/dist/contracts/settings.contract.d.ts.map +1 -1
  34. package/dist/contracts/system.contract.d.ts +185 -35
  35. package/dist/contracts/system.contract.d.ts.map +1 -1
  36. package/dist/contracts/translator.contract.d.ts +24 -0
  37. package/dist/contracts/translator.contract.d.ts.map +1 -1
  38. package/dist/contracts/workspace.contract.d.ts +11 -0
  39. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  40. package/dist/contracts/workspace.contract.js +11 -1
  41. package/dist/contracts/workspace.contract.js.map +1 -1
  42. package/dist/events/agent-events.d.ts +18 -0
  43. package/dist/events/agent-events.d.ts.map +1 -1
  44. package/dist/events/agent-events.js +3 -1
  45. package/dist/events/agent-events.js.map +1 -1
  46. package/dist/events/agent-transcript.d.ts +727 -0
  47. package/dist/events/agent-transcript.d.ts.map +1 -0
  48. package/dist/events/agent-transcript.js +34 -0
  49. package/dist/events/agent-transcript.js.map +1 -0
  50. package/dist/events/agent-words.d.ts +5 -0
  51. package/dist/events/agent-words.d.ts.map +1 -1
  52. package/dist/events/agent-words.js +9 -0
  53. package/dist/events/agent-words.js.map +1 -1
  54. package/dist/events/requests.d.ts +143 -0
  55. package/dist/events/requests.d.ts.map +1 -1
  56. package/dist/events/requests.js +35 -1
  57. package/dist/events/requests.js.map +1 -1
  58. package/dist/events/sandbox-notice.d.ts +7 -0
  59. package/dist/events/sandbox-notice.d.ts.map +1 -1
  60. package/dist/events/sandbox-notice.js +1 -0
  61. package/dist/events/sandbox-notice.js.map +1 -1
  62. package/dist/events/system-events.d.ts +88 -0
  63. package/dist/events/system-events.d.ts.map +1 -1
  64. package/dist/events/transcript.d.ts +20 -720
  65. package/dist/events/transcript.d.ts.map +1 -1
  66. package/dist/events/transcript.js +17 -19
  67. package/dist/events/transcript.js.map +1 -1
  68. package/dist/events/undelivered.d.ts +4 -0
  69. package/dist/events/undelivered.d.ts.map +1 -1
  70. package/dist/events/undelivered.js +13 -0
  71. package/dist/events/undelivered.js.map +1 -1
  72. package/dist/ids/conversation-ids.d.ts +2 -0
  73. package/dist/ids/conversation-ids.d.ts.map +1 -1
  74. package/dist/ids/conversation-ids.js +2 -0
  75. package/dist/ids/conversation-ids.js.map +1 -1
  76. package/dist/ids/row-ids.d.ts +3 -0
  77. package/dist/ids/row-ids.d.ts.map +1 -0
  78. package/dist/ids/row-ids.js +11 -0
  79. package/dist/ids/row-ids.js.map +1 -0
  80. package/dist/index.d.ts +1339 -48
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/models/agent-lanes.d.ts.map +1 -1
  83. package/dist/models/agent-lanes.js +3 -2
  84. package/dist/models/agent-lanes.js.map +1 -1
  85. package/dist/models/agent-standing.d.ts +4 -0
  86. package/dist/models/agent-standing.d.ts.map +1 -1
  87. package/dist/models/agent-standing.js +2 -1
  88. package/dist/models/agent-standing.js.map +1 -1
  89. package/dist/models/plan-pools.d.ts +5 -1
  90. package/dist/models/plan-pools.d.ts.map +1 -1
  91. package/dist/models/plan-pools.js +57 -21
  92. package/dist/models/plan-pools.js.map +1 -1
  93. package/dist/netd/generated/wire.d.ts +5 -0
  94. package/dist/netd/generated/wire.d.ts.map +1 -1
  95. package/dist/policy/command-classes.d.ts +1 -1
  96. package/dist/policy/command-classes.d.ts.map +1 -1
  97. package/dist/policy/command-classes.js +2 -2
  98. package/dist/policy/command-classes.js.map +1 -1
  99. package/dist/policy/keyring-origins.d.ts +27 -0
  100. package/dist/policy/keyring-origins.d.ts.map +1 -0
  101. package/dist/policy/keyring-origins.js +213 -0
  102. package/dist/policy/keyring-origins.js.map +1 -0
  103. package/dist/policy/keyring-seal.d.ts +9 -0
  104. package/dist/policy/keyring-seal.d.ts.map +1 -1
  105. package/dist/policy/keyring-seal.js +18 -6
  106. package/dist/policy/keyring-seal.js.map +1 -1
  107. package/dist/policy/safety-policy.d.ts +1 -1
  108. package/dist/policy/safety-policy.d.ts.map +1 -1
  109. package/dist/policy/safety-policy.js +1 -1
  110. package/dist/policy/safety-policy.js.map +1 -1
  111. package/dist/policy/turned-away.d.ts +1 -0
  112. package/dist/policy/turned-away.d.ts.map +1 -1
  113. package/dist/policy/turned-away.js +11 -0
  114. package/dist/policy/turned-away.js.map +1 -1
  115. package/dist/protocol/doors/host-protocol.d.ts +1 -0
  116. package/dist/protocol/doors/host-protocol.d.ts.map +1 -1
  117. package/dist/protocol/doors/host-protocol.js +1 -0
  118. package/dist/protocol/doors/host-protocol.js.map +1 -1
  119. package/dist/protocol/doors/webext-links.d.ts +1 -0
  120. package/dist/protocol/doors/webext-links.d.ts.map +1 -1
  121. package/dist/protocol/doors/webext-links.js +1 -0
  122. package/dist/protocol/doors/webext-links.js.map +1 -1
  123. package/dist/protocol/peer-dial.d.ts +2 -0
  124. package/dist/protocol/peer-dial.d.ts.map +1 -1
  125. package/dist/protocol/peer-dial.js +62 -29
  126. package/dist/protocol/peer-dial.js.map +1 -1
  127. package/dist/protocol/raw/raw-json-routes.d.ts +193 -0
  128. package/dist/protocol/raw/raw-json-routes.d.ts.map +1 -1
  129. package/dist/protocol/raw/raw-json-routes.js +5 -0
  130. package/dist/protocol/raw/raw-json-routes.js.map +1 -1
  131. package/dist/protocol/raw/raw-routes.d.ts +31 -0
  132. package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
  133. package/dist/protocol/raw/raw-routes.js +8 -0
  134. package/dist/protocol/raw/raw-routes.js.map +1 -1
  135. package/dist/schemas/agent-place.d.ts +10 -0
  136. package/dist/schemas/agent-place.d.ts.map +1 -0
  137. package/dist/schemas/agent-place.js +15 -0
  138. package/dist/schemas/agent-place.js.map +1 -0
  139. package/dist/schemas/agent.d.ts +116 -83
  140. package/dist/schemas/agent.d.ts.map +1 -1
  141. package/dist/schemas/agent.js +26 -39
  142. package/dist/schemas/agent.js.map +1 -1
  143. package/dist/schemas/agents-list.d.ts +38 -0
  144. package/dist/schemas/agents-list.d.ts.map +1 -1
  145. package/dist/schemas/agents.d.ts +117 -16
  146. package/dist/schemas/agents.d.ts.map +1 -1
  147. package/dist/schemas/agents.js +19 -21
  148. package/dist/schemas/agents.js.map +1 -1
  149. package/dist/schemas/attach.d.ts +8 -0
  150. package/dist/schemas/attach.d.ts.map +1 -0
  151. package/dist/schemas/attach.js +14 -0
  152. package/dist/schemas/attach.js.map +1 -0
  153. package/dist/schemas/command-admission.d.ts +71 -0
  154. package/dist/schemas/command-admission.d.ts.map +1 -0
  155. package/dist/schemas/command-admission.js +27 -0
  156. package/dist/schemas/command-admission.js.map +1 -0
  157. package/dist/schemas/conversation-links.d.ts +44 -0
  158. package/dist/schemas/conversation-links.d.ts.map +1 -0
  159. package/dist/schemas/conversation-links.js +35 -0
  160. package/dist/schemas/conversation-links.js.map +1 -0
  161. package/dist/schemas/data-protection.d.ts +5 -2
  162. package/dist/schemas/data-protection.d.ts.map +1 -1
  163. package/dist/schemas/data-protection.js +17 -1
  164. package/dist/schemas/data-protection.js.map +1 -1
  165. package/dist/schemas/device-folder.d.ts +61 -0
  166. package/dist/schemas/device-folder.d.ts.map +1 -0
  167. package/dist/schemas/device-folder.js +24 -0
  168. package/dist/schemas/device-folder.js.map +1 -0
  169. package/dist/schemas/devices.d.ts +112 -39
  170. package/dist/schemas/devices.d.ts.map +1 -1
  171. package/dist/schemas/devices.js +8 -10
  172. package/dist/schemas/devices.js.map +1 -1
  173. package/dist/schemas/hosts.d.ts +2 -0
  174. package/dist/schemas/hosts.d.ts.map +1 -1
  175. package/dist/schemas/hosts.js +2 -0
  176. package/dist/schemas/hosts.js.map +1 -1
  177. package/dist/schemas/keyring-native.d.ts +137 -0
  178. package/dist/schemas/keyring-native.d.ts.map +1 -0
  179. package/dist/schemas/keyring-native.js +69 -0
  180. package/dist/schemas/keyring-native.js.map +1 -0
  181. package/dist/schemas/keyring.d.ts +413 -0
  182. package/dist/schemas/keyring.d.ts.map +1 -1
  183. package/dist/schemas/keyring.js +139 -4
  184. package/dist/schemas/keyring.js.map +1 -1
  185. package/dist/schemas/metrics.d.ts +11 -0
  186. package/dist/schemas/metrics.d.ts.map +1 -1
  187. package/dist/schemas/metrics.js +17 -0
  188. package/dist/schemas/metrics.js.map +1 -1
  189. package/dist/schemas/phone-passwords.d.ts +186 -0
  190. package/dist/schemas/phone-passwords.d.ts.map +1 -0
  191. package/dist/schemas/phone-passwords.js +25 -0
  192. package/dist/schemas/phone-passwords.js.map +1 -0
  193. package/dist/schemas/providers/plan-limits.d.ts +74 -0
  194. package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
  195. package/dist/schemas/providers/plan-limits.js +31 -0
  196. package/dist/schemas/providers/plan-limits.js.map +1 -1
  197. package/dist/schemas/providers/provider-oauth.d.ts +24 -0
  198. package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
  199. package/dist/schemas/providers/usage.d.ts +1 -0
  200. package/dist/schemas/providers/usage.d.ts.map +1 -1
  201. package/dist/schemas/providers/usage.js +1 -0
  202. package/dist/schemas/providers/usage.js.map +1 -1
  203. package/dist/schemas/push.d.ts +13 -0
  204. package/dist/schemas/push.d.ts.map +1 -1
  205. package/dist/schemas/push.js +11 -1
  206. package/dist/schemas/push.js.map +1 -1
  207. package/dist/schemas/sandbox-passwords.d.ts +288 -0
  208. package/dist/schemas/sandbox-passwords.d.ts.map +1 -0
  209. package/dist/schemas/sandbox-passwords.js +66 -0
  210. package/dist/schemas/sandbox-passwords.js.map +1 -0
  211. package/dist/schemas/secrets.d.ts +2 -0
  212. package/dist/schemas/secrets.d.ts.map +1 -1
  213. package/dist/schemas/secrets.js +4 -0
  214. package/dist/schemas/secrets.js.map +1 -1
  215. package/dist/schemas/settings-history.d.ts +1 -1
  216. package/dist/schemas/settings-history.d.ts.map +1 -1
  217. package/dist/schemas/settings-history.js +15 -0
  218. package/dist/schemas/settings-history.js.map +1 -1
  219. package/dist/schemas/settings-savings.d.ts +709 -0
  220. package/dist/schemas/settings-savings.d.ts.map +1 -0
  221. package/dist/schemas/settings-savings.js +53 -0
  222. package/dist/schemas/settings-savings.js.map +1 -0
  223. package/dist/schemas/settings.d.ts +7 -673
  224. package/dist/schemas/settings.d.ts.map +1 -1
  225. package/dist/schemas/settings.js +7 -58
  226. package/dist/schemas/settings.js.map +1 -1
  227. package/dist/schemas/terminal.d.ts +5 -0
  228. package/dist/schemas/terminal.d.ts.map +1 -1
  229. package/dist/schemas/terminal.js +4 -0
  230. package/dist/schemas/terminal.js.map +1 -1
  231. package/dist/schemas/work-assurance.d.ts +24 -0
  232. package/dist/schemas/work-assurance.d.ts.map +1 -0
  233. package/dist/schemas/work-assurance.js +23 -0
  234. package/dist/schemas/work-assurance.js.map +1 -0
  235. package/dist/schemas/workspace/workspace-tree.d.ts +23 -0
  236. package/dist/schemas/workspace/workspace-tree.d.ts.map +1 -1
  237. package/dist/schemas/workspace/workspace-tree.js +20 -0
  238. package/dist/schemas/workspace/workspace-tree.js.map +1 -1
  239. package/dist/state/auth-state.d.ts +4 -0
  240. package/dist/state/auth-state.d.ts.map +1 -0
  241. package/dist/state/auth-state.js +37 -0
  242. package/dist/state/auth-state.js.map +1 -0
  243. package/dist/state/definition.d.ts +12 -4
  244. package/dist/state/definition.d.ts.map +1 -1
  245. package/dist/state/history-state.d.ts.map +1 -1
  246. package/dist/state/history-state.js +17 -1
  247. package/dist/state/history-state.js.map +1 -1
  248. package/dist/state/runtime-state.d.ts.map +1 -1
  249. package/dist/state/runtime-state.js.map +1 -1
  250. package/dist/text/transcript-fold.js +5 -5
  251. package/dist/text/transcript-fold.js.map +1 -1
  252. package/dist/webext/index.d.ts +1 -0
  253. package/dist/webext/index.d.ts.map +1 -1
  254. package/dist/webext/index.js +1 -0
  255. package/dist/webext/index.js.map +1 -1
  256. package/package.json +6 -5
  257. package/src/contracts/agent.contract.ts +13 -1
  258. package/src/contracts/agents.contract.ts +4 -3
  259. package/src/contracts/device.contract.ts +16 -0
  260. package/src/contracts/endpoints.contract.ts +3 -0
  261. package/src/contracts/push.contract.ts +23 -1
  262. package/src/contracts/secrets.contract.ts +39 -0
  263. package/src/contracts/workspace.contract.ts +16 -0
  264. package/src/events/agent-events.ts +6 -1
  265. package/src/events/agent-transcript.ts +48 -0
  266. package/src/events/agent-words.ts +13 -0
  267. package/src/events/requests.ts +41 -3
  268. package/src/events/sandbox-notice.ts +3 -0
  269. package/src/events/transcript.ts +30 -28
  270. package/src/events/undelivered.ts +18 -0
  271. package/src/ids/conversation-ids.ts +7 -0
  272. package/src/ids/row-ids.ts +20 -0
  273. package/src/models/agent-lanes.ts +9 -3
  274. package/src/models/agent-standing.ts +9 -1
  275. package/src/models/plan-pools.test.ts +128 -20
  276. package/src/models/plan-pools.ts +114 -31
  277. package/src/netd/generated/netd-wire.json +25 -0
  278. package/src/netd/generated/wire.ts +1 -1
  279. package/src/policy/command-classes.ts +2 -2
  280. package/src/policy/keyring-origins.test.ts +132 -0
  281. package/src/policy/keyring-origins.ts +287 -0
  282. package/src/policy/keyring-seal.test.ts +37 -2
  283. package/src/policy/keyring-seal.ts +33 -7
  284. package/src/policy/safety-policy.ts +1 -1
  285. package/src/policy/tests/safety-policy.test.ts +1 -1
  286. package/src/policy/turned-away.ts +16 -0
  287. package/src/protocol/doors/host-protocol.ts +3 -0
  288. package/src/protocol/doors/webext-links.ts +3 -0
  289. package/src/protocol/peer-dial.test.ts +90 -4
  290. package/src/protocol/peer-dial.ts +83 -39
  291. package/src/protocol/raw/raw-json-routes.ts +13 -0
  292. package/src/protocol/raw/raw-routes.ts +12 -1
  293. package/src/schemas/agent-place.ts +22 -0
  294. package/src/schemas/agent.ts +42 -78
  295. package/src/schemas/agents.ts +41 -46
  296. package/src/schemas/attach.ts +23 -0
  297. package/src/schemas/command-admission.ts +57 -0
  298. package/src/schemas/conversation-links.ts +62 -0
  299. package/src/schemas/data-protection.ts +36 -10
  300. package/src/schemas/device-folder.ts +59 -0
  301. package/src/schemas/devices.ts +17 -42
  302. package/src/schemas/hosts.ts +4 -0
  303. package/src/schemas/keyring-native.test.ts +13 -0
  304. package/src/schemas/keyring-native.ts +101 -0
  305. package/src/schemas/keyring.ts +202 -4
  306. package/src/schemas/metrics.ts +28 -3
  307. package/src/schemas/phone-passwords.ts +51 -0
  308. package/src/schemas/providers/plan-limits.ts +49 -2
  309. package/src/schemas/providers/usage.ts +2 -0
  310. package/src/schemas/push.ts +19 -3
  311. package/src/schemas/sandbox-passwords.ts +88 -0
  312. package/src/schemas/secrets.ts +4 -0
  313. package/src/schemas/settings-history.ts +20 -0
  314. package/src/schemas/settings-savings.ts +125 -0
  315. package/src/schemas/settings.test.ts +38 -0
  316. package/src/schemas/settings.ts +20 -128
  317. package/src/schemas/terminal.ts +8 -0
  318. package/src/schemas/work-assurance.ts +36 -0
  319. package/src/schemas/workspace/workspace-tree.ts +32 -0
  320. package/src/state/auth-state.ts +89 -0
  321. package/src/state/history-state.ts +44 -1
  322. package/src/state/runtime-state.ts +5 -4
  323. package/src/text/transcript-fold.test.ts +67 -0
  324. package/src/text/transcript-fold.ts +6 -6
  325. package/src/webext/index.ts +2 -0
@@ -0,0 +1,101 @@
1
+ // Named imports rather than the `z` namespace, and nothing from schemas/keyring.ts: this module is bundled into the
2
+ // browser extension, where the namespace keeps zod's locales (~330 kB) that esbuild can otherwise drop
3
+ // (_devices/webext/scripts/size-budget.mjs holds the ceiling). The three schemas it shares with keyring.ts are spelled
4
+ // again below, and keyring-native.test.ts holds them equal.
5
+ import { array, boolean, discriminatedUnion, enum as zEnum, literal, number, object, string, union } from "zod";
6
+ import type * as z from "zod";
7
+
8
+ // What the browser extension and `intentic-keyring native-host` say to each other over Chrome's native messaging: one
9
+ // JSON message each way per request, length-prefixed by the browser, correlated by `id`. The native host is a process
10
+ // the browser starts as the person's user; it seals a captured value to the vault's inbox key before it reaches the
11
+ // keyring's pipe, and asks the keyring (which asks the person, with Windows Hello or polkit) before any value goes back
12
+ // to the page. A site's host, never a full address, is all the extension ever names.
13
+ //
14
+ // `@intentic/sandbox-contract/webext` re-exports it.
15
+
16
+ /** The name the extension connects to (`chrome.runtime.connectNative`), and the native host's manifest declares. */
17
+ export const KEYRING_NATIVE_HOST = "dev.intentic.keyring";
18
+
19
+ const id = string().min(1).max(64);
20
+ const host = string().min(1).max(253);
21
+
22
+ /** keyring.ts's secret name, refusal code and capture outcome, as the extension sees them. */
23
+ export const KeyringNativeNameSchema = string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,127}$/u);
24
+ export const KeyringNativeRefusalCodeSchema = zEnum([
25
+ "no-keyring",
26
+ "locked",
27
+ "declined",
28
+ "unknown",
29
+ "not-shared",
30
+ "bad-grant",
31
+ "expired",
32
+ "replayed",
33
+ "guard",
34
+ "unavailable",
35
+ "busy",
36
+ "pending",
37
+ ]);
38
+ export const KeyringNativeOutcomeSchema = zEnum(["saved", "updated", "unchanged", "pending", "skipped"]);
39
+
40
+ export const KeyringNativeRequestSchema = discriminatedUnion("type", [
41
+ // What the extension needs before it shows anything: whether remembering is on for browsers, and the vault's state.
42
+ object({
43
+ type: literal("hello"),
44
+ id,
45
+ browser: string().min(1).max(100).describe("The browser's own name, `Brave`, for the title line."),
46
+ }).strict(),
47
+ // A sign-in, sign-up or password change the page just submitted.
48
+ object({
49
+ type: literal("capture"),
50
+ id,
51
+ browser: string().min(1).max(100),
52
+ host,
53
+ username: string().max(256).optional(),
54
+ password: string().min(1).max(4096),
55
+ title: string().max(200).optional().describe("The site's own name (`og:site_name`, the title), when it has one."),
56
+ failed: boolean().optional().describe("The form came back for the same user: the last value saved for it did not work."),
57
+ }).strict(),
58
+ // What the keyring holds for this site: names, titles and users, never values.
59
+ object({ type: literal("candidates"), id, host }).strict(),
60
+ // One entry's value for the focused field, after the person's yes at the computer.
61
+ object({ type: literal("fill"), id, host, name: KeyringNativeNameSchema }).strict(),
62
+ // Take back a capture made moments ago (the toast's Undo).
63
+ object({ type: literal("undo"), id, name: KeyringNativeNameSchema, at: number().int() }).strict(),
64
+ // Never save for this site (the toast's "Never for this site").
65
+ object({ type: literal("never"), id, host }).strict(),
66
+ ]);
67
+ export type KeyringNativeRequest = z.infer<typeof KeyringNativeRequestSchema>;
68
+
69
+ const refused = object({ ok: literal(false), code: KeyringNativeRefusalCodeSchema, refusal: string() });
70
+
71
+ export const KeyringNativeCandidateSchema = object({ name: KeyringNativeNameSchema, title: string(), username: string().optional() });
72
+ export type KeyringNativeCandidate = z.infer<typeof KeyringNativeCandidateSchema>;
73
+
74
+ // A plain union: a refusal shares its `type` with the answer it stands in for, which a discriminated union cannot hold.
75
+ export const KeyringNativeResponseSchema = union([
76
+ object({
77
+ type: literal("hello"),
78
+ id,
79
+ ok: literal(true),
80
+ setUp: boolean(),
81
+ locked: boolean(),
82
+ remember: boolean().describe("Whether the person turned on remembering for browsers."),
83
+ version: string(),
84
+ }),
85
+ object({
86
+ type: literal("capture"),
87
+ id,
88
+ ok: literal(true),
89
+ outcome: KeyringNativeOutcomeSchema,
90
+ name: KeyringNativeNameSchema.optional(),
91
+ title: string().optional(),
92
+ at: number().int(),
93
+ }),
94
+ object({ type: literal("candidates"), id, ok: literal(true), items: array(KeyringNativeCandidateSchema) }),
95
+ object({ type: literal("fill"), id, ok: literal(true), username: string().optional(), password: string() }),
96
+ object({ type: literal("undo"), id, ok: literal(true) }),
97
+ object({ type: literal("never"), id, ok: literal(true) }),
98
+ // Any request refused, under the type it was asked as.
99
+ refused.extend({ type: zEnum(["hello", "capture", "candidates", "fill", "undo", "never"]), id }),
100
+ ]);
101
+ export type KeyringNativeResponse = z.infer<typeof KeyringNativeResponseSchema>;
@@ -82,15 +82,28 @@ export const KeyringSandboxKeysSchema = z.object({
82
82
  });
83
83
  export type KeyringSandboxKeys = z.infer<typeof KeyringSandboxKeysSchema>;
84
84
 
85
+ // The one-time "let Intentic remember the passwords you type", per source. All off until the person turns them on;
86
+ // each switches off on its own. Imports, an agent's `secrets ask` and adding by hand are deliberate acts and need none.
87
+ export const KeyringRememberSchema = z.object({
88
+ browser: z.boolean().describe("Sign-ins in a browser with the Intentic extension."),
89
+ terminal: z.boolean().describe("ssh, sudo and git prompts on this computer, and git's credential helper."),
90
+ apps: z.boolean().describe("Password fields in programs on this computer, typed once into the keyring's own box."),
91
+ phone: z.boolean().describe("The paired phone's autofill."),
92
+ sandbox: z.boolean().describe("Prompts that hide what is typed, in a sandbox terminal the person types into."),
93
+ });
94
+ export type KeyringRemember = z.infer<typeof KeyringRememberSchema>;
95
+
85
96
  export const KeyringStateSchema = z.object({
86
97
  version: z.string().describe("The keyring's own release."),
87
98
  locked: z.boolean().describe("Whether the vault is locked. Locked, `while-here` entries ask like `each-use` ones."),
88
99
  unlockedUntil: z.number().optional().describe("When the current unlock ends, in milliseconds. Absent while locked."),
89
100
  unlock: z
90
- .array(z.enum(["hello", "passphrase"]))
91
- .describe("How a person unlocks it here: Windows Hello, or the keyring's passphrase."),
101
+ .array(z.enum(["hello", "passphrase", "polkit"]))
102
+ .describe("How a person unlocks it here: Windows Hello, the keyring's passphrase (its recovery key), or polkit's own check on Linux."),
92
103
  keys: KeyringDeviceKeysSchema.optional().describe("Absent until the keyring is set up: there is no vault, so no inbox to seal a value to."),
93
104
  pendingInbox: z.number().int().nonnegative().describe("Values sent to be kept that wait for the next unlock."),
105
+ pendingCaptures: z.number().int().nonnegative().optional().describe("Credentials seen in use while locked, kept sealed until the next unlock. Absent from a keyring older than 2026-10-19."),
106
+ remember: KeyringRememberSchema.optional().describe("Which sources the person let the keyring remember passwords from, so a collector knows whether to send one. Absent before setup and from an older keyring."),
94
107
  });
95
108
  export type KeyringState = z.infer<typeof KeyringStateSchema>;
96
109
 
@@ -173,9 +186,9 @@ export const SealedBoxSchema = z
173
186
  export type SealedBox = z.infer<typeof SealedBoxSchema>;
174
187
 
175
188
  export const KeyringRefusalCodeSchema = z
176
- .enum(["no-keyring", "locked", "declined", "unknown", "not-shared", "bad-grant", "expired", "replayed", "guard", "unavailable", "busy"])
189
+ .enum(["no-keyring", "locked", "declined", "unknown", "not-shared", "bad-grant", "expired", "replayed", "guard", "unavailable", "busy", "pending"])
177
190
  .describe(
178
- "Why: no keyring here; locked with nobody to unlock it; the person said no; no such entry; not shared with this sandbox; a grant that does not verify; one past its time; one already spent; the focused window or field is not one it may go to; its password manager did not answer; another prompt is open.",
191
+ "Why: no keyring here; locked with nobody to unlock it; the person said no; no such entry; not shared with this sandbox; a grant that does not verify; one past its time; one already spent; the focused window or field is not one it may go to; its password manager did not answer; another prompt is open; nobody has answered yet, and the request waits in the Passwords app.",
179
192
  );
180
193
  export type KeyringRefusalCode = z.infer<typeof KeyringRefusalCodeSchema>;
181
194
 
@@ -275,3 +288,188 @@ export const KeyringSyncPairResultSchema = z.discriminatedUnion("ok", [z.object(
275
288
  export const KeyringSyncOutboxSchema = z.object({ items: z.array(KeyringSyncItemSchema) });
276
289
  export const KeyringSyncDeliverSchema = z.object({ items: z.array(KeyringSyncItemSchema).max(64) }).strict();
277
290
  export const KeyringSyncDeliverResultSchema = z.object({ accepted: z.number().int().nonnegative().describe("How many it took; the rest were not from a paired keyring or did not open.") });
291
+
292
+ // ---- origins: where a credential is used, and where it was seen ----
293
+
294
+ // One place a credential belongs to. An entry's rules (programs, packages, hosts, askpass matchers) are the keyring's
295
+ // own record of the same thing; policy/keyring-origins.ts maps one onto the other, so a captured credential gets its
296
+ // rules without anyone typing them and the Passwords app can say "Works on github.com, git https://github.com".
297
+ export const KeyringOriginSchema = z.discriminatedUnion("kind", [
298
+ z.object({ kind: z.literal("site"), host: z.string().min(1).max(253).describe("The page's host, `github.com`; never a path.") }).strict(),
299
+ z
300
+ .object({
301
+ kind: z.literal("program"),
302
+ program: KeyringProgramSchema,
303
+ label: z.string().max(200).optional().describe("The program's own name for people, `FortiClient VPN`, when the window says it."),
304
+ })
305
+ .strict(),
306
+ z.object({ kind: z.literal("package"), package: KeyringPackageSchema, label: z.string().max(200).optional() }).strict(),
307
+ z.object({ kind: z.literal("ssh"), target: z.string().min(1).max(300).describe("`user@host`, as ssh's prompt names it.") }).strict(),
308
+ z.object({ kind: z.literal("ssh-key"), path: z.string().min(1).max(1000) }).strict(),
309
+ z.object({ kind: z.literal("sudo"), user: z.string().max(64).optional() }).strict(),
310
+ z.object({ kind: z.literal("git"), url: z.string().min(1).max(500).describe("`https://host`, as git's credential helper or askpass names it.") }).strict(),
311
+ z
312
+ .object({
313
+ kind: z.literal("prompt"),
314
+ text: z.string().min(1).max(300).describe("The prompt's own words, `Password for npm:`."),
315
+ command: z.string().max(200).optional().describe("The command that asked, `npm login`, when known."),
316
+ sandbox: z.string().url().optional().describe("The sandbox whose terminal asked, for a prompt seen there rather than on this computer."),
317
+ })
318
+ .strict(),
319
+ ]);
320
+ export type KeyringOrigin = z.infer<typeof KeyringOriginSchema>;
321
+
322
+ // Where a capture came from, for its title line ("saved from Brave") and its record.
323
+ export const KeyringCaptureSourceSchema = z
324
+ .enum(["browser", "askpass", "git", "app", "phone", "terminal", "import", "agent", "manual"])
325
+ .describe(
326
+ "What saw it: the browser extension, an ssh/sudo/git prompt answered through the keyring, git's credential helper, a password field in an app on this computer, the phone's autofill, a sandbox terminal's prompt, an import, an agent's `secrets ask`, or the person adding it by hand.",
327
+ );
328
+ export type KeyringCaptureSource = z.infer<typeof KeyringCaptureSourceSchema>;
329
+
330
+ // A credential seen in use, sent to be kept. The value is sealed to the vault's inbox key for `{ kind: "capture" }`
331
+ // (policy/keyring-seal.ts), so whoever carries it (the native-messaging host, the sandbox for a phone or a terminal)
332
+ // holds ciphertext. Taken in without asking once the person turned remembering on for its source: bringing a value IN
333
+ // reveals nothing, and an update keeps the old value in the entry's history, so a capture can be undone.
334
+ export const KEYRING_CAPTURE_BATCH_MAX = 5000;
335
+ export const KeyringCaptureItemSchema = z
336
+ .object({
337
+ origin: KeyringOriginSchema,
338
+ username: z.string().max(256).optional().describe("The account it signs in as, when the form or prompt said. Not a secret."),
339
+ sealed: SealedBoxSchema,
340
+ source: KeyringCaptureSourceSchema,
341
+ via: z.string().min(1).max(200).describe("Who saw it, in words for the title line: `Brave`, `Pixel 8`, `intentic-dev`. Also bound into the seal."),
342
+ kind: z.enum(["password", "text"]).default("password"),
343
+ title: z.string().max(200).optional().describe("The site's or app's own name, when it said one; the keyring derives one otherwise."),
344
+ at: z.number().int().describe("When it was seen, in milliseconds. Also bound into the seal."),
345
+ failed: z
346
+ .boolean()
347
+ .optional()
348
+ .describe("The last value captured for this origin and user did not work (ssh asked again, git erased it, the form came back): replace it rather than keep it as history."),
349
+ })
350
+ .strict();
351
+ export type KeyringCaptureItem = z.infer<typeof KeyringCaptureItemSchema>;
352
+
353
+ export const KeyringCaptureOutcomeSchema = z
354
+ .enum(["saved", "updated", "unchanged", "pending", "skipped"])
355
+ .describe(
356
+ "A new entry; a new value for an existing one (the old kept in its history); the same value it already had; kept sealed until the next unlock; not kept, because remembering is off for its source or its origin is on the never-save list.",
357
+ );
358
+ export type KeyringCaptureOutcome = z.infer<typeof KeyringCaptureOutcomeSchema>;
359
+
360
+ export const KeyringCaptureResultSchema = z.discriminatedUnion("ok", [
361
+ z.object({
362
+ ok: z.literal(true),
363
+ outcome: KeyringCaptureOutcomeSchema,
364
+ name: KeyringSecretNameSchema.optional().describe("The entry it went to. Absent while pending or skipped."),
365
+ title: z.string().optional().describe("That entry's title, for the 'Saved github.com' notice."),
366
+ }),
367
+ refusal,
368
+ ]);
369
+ export type KeyringCaptureResult = z.infer<typeof KeyringCaptureResultSchema>;
370
+
371
+ // ---- an agent's need: asked for by what it is for, not by name ----
372
+
373
+ // A sandbox asking for a credential by where it is to be used. The keyring matches its entries by origin and asks the
374
+ // person (with their OS's own check: Windows Hello, polkit, a phone's biometric signature) whether this sandbox may use
375
+ // the one it found, and on what terms. Nothing about entries that do not match, nor about one the person declines,
376
+ // reaches the sandbox. A match nobody answers waits in the Passwords app's Requests, and the sandbox learns of the yes
377
+ // from its next `shares` read.
378
+ export const KeyringNeedSchema = z
379
+ .object({
380
+ origin: KeyringOriginSchema,
381
+ username: z.string().max(256).optional().describe("The account wanted, when the agent knows it."),
382
+ detail: z.string().min(1).max(300).describe("What it is for, in words for the prompt: `sign in to push the release branch`."),
383
+ conversation: z.string().max(200).optional(),
384
+ purpose: z.enum(["agent", "owner"]).default("agent"),
385
+ typeOnce: z
386
+ .boolean()
387
+ .default(false)
388
+ .describe(
389
+ "Nothing matched on any of the owner's computers: offer the person here the keyring's type-once box. Without it, a keyring with no match answers `unknown` at once, so the sandbox can ask the next computer without a box opening on each.",
390
+ ),
391
+ })
392
+ .strict();
393
+ export type KeyringNeed = z.infer<typeof KeyringNeedSchema>;
394
+
395
+ export const KeyringNeedAnswerSchema = z
396
+ .enum(["once", "while-here", "always", "deny"])
397
+ .describe("The person's answer: this one use (an `each-use` share), unasked while unlocked, a copy in the sandbox, or no.");
398
+ export type KeyringNeedAnswer = z.infer<typeof KeyringNeedAnswerSchema>;
399
+
400
+ export const KeyringNeedResultSchema = z.discriminatedUnion("ok", [
401
+ z.object({
402
+ ok: z.literal(true),
403
+ pointer: KeyringPointerSchema.describe("The entry now shared with the asking sandbox, as its next `shares` read will also list it."),
404
+ asked: z.boolean(),
405
+ saved: z.boolean().describe("Nothing matched, and the person typed it once into the keyring's own box: a new entry, kept here and shared."),
406
+ }),
407
+ refusal,
408
+ ]);
409
+ export type KeyringNeedResult = z.infer<typeof KeyringNeedResultSchema>;
410
+
411
+ // A need that waits for an answer, as the Passwords app lists it.
412
+ export const KeyringPendingNeedSchema = z.object({
413
+ id: z.string().regex(KEYRING_NONCE_RE),
414
+ sandbox: z.string().url(),
415
+ need: KeyringNeedSchema,
416
+ matches: z.array(KeyringSecretNameSchema).describe("The entries that fit, best first. Empty: none yet, the app offers to type one once."),
417
+ at: z.number().int(),
418
+ });
419
+ export type KeyringPendingNeed = z.infer<typeof KeyringPendingNeedSchema>;
420
+
421
+ // ---- the owner's phone as a place to save and fill ----
422
+
423
+ // A phone paired with the keyring holds two keys made in its Android Keystore: `seal` (X25519, kept in software, which
424
+ // Android's Keystore cannot do natively) that a value released for autofill is sealed to, and `presence` (ECDSA P-256,
425
+ // `setUserAuthenticationRequired`, so it signs only after the person's fingerprint, face or screen lock). A signature by
426
+ // `presence` over a fill request is the person's yes, checked by the keyring against the key it pinned at pairing, the
427
+ // way a Windows Hello signature is. Pairing is carried by a sandbox both are linked to, with both fingerprints shown.
428
+ export const KeyringPhoneKeysSchema = z
429
+ .object({
430
+ device: z.string().min(1).max(128).describe("The phone, by its device card."),
431
+ label: z.string().min(1).max(200).describe("The phone's name for people, `Pixel 8`."),
432
+ seal: KeyringPublicKeySchema,
433
+ presence: z.string().min(1).max(512).describe("The presence key, X.509 SubjectPublicKeyInfo DER, base64."),
434
+ })
435
+ .strict();
436
+ export type KeyringPhoneKeys = z.infer<typeof KeyringPhoneKeysSchema>;
437
+
438
+ export const KeyringPhonePairResultSchema = z.discriminatedUnion("ok", [
439
+ z.object({ ok: z.literal(true), fingerprint: z.string().describe("The keyring's own fingerprint, for the phone to show beside its own.") }),
440
+ refusal,
441
+ ]);
442
+ export type KeyringPhonePairResult = z.infer<typeof KeyringPhonePairResultSchema>;
443
+
444
+ export const KeyringPhoneFillSchema = z
445
+ .object({
446
+ v: z.literal(1),
447
+ device: z.string().min(1).max(128),
448
+ origin: KeyringOriginSchema.describe("Where the phone is filling: a `package`, or a `site` for a browser on the phone."),
449
+ username: z.string().max(256).optional(),
450
+ nonce: z.string().regex(KEYRING_NONCE_RE),
451
+ issuedAt: z.number().int(),
452
+ expiresAt: z.number().int(),
453
+ })
454
+ .strict();
455
+ export type KeyringPhoneFill = z.infer<typeof KeyringPhoneFillSchema>;
456
+
457
+ export const SignedKeyringPhoneFillSchema = z
458
+ .object({
459
+ payload: z.string().min(1).max(4096).describe("The fill request's JSON, base64url."),
460
+ sig: z.string().min(1).max(256).describe("The presence key's ECDSA P-256 signature (DER) over the payload, base64url."),
461
+ })
462
+ .strict();
463
+ export type SignedKeyringPhoneFill = z.infer<typeof SignedKeyringPhoneFillSchema>;
464
+
465
+ export const KeyringPhoneFillResultSchema = z.discriminatedUnion("ok", [
466
+ z.object({
467
+ ok: z.literal(true),
468
+ username: z.string().max(256).optional(),
469
+ sealed: SealedBoxSchema.describe("The value, sealed to the phone's `seal` key for `{ kind: \"phone\" }`."),
470
+ name: KeyringSecretNameSchema,
471
+ title: z.string(),
472
+ }),
473
+ refusal,
474
+ ]);
475
+ export type KeyringPhoneFillResult = z.infer<typeof KeyringPhoneFillResultSchema>;
@@ -97,7 +97,9 @@ export const SandboxUsageSchema = z.object({
97
97
  countedFileCacheBytes: z
98
98
  .number()
99
99
  .optional()
100
- .describe("File cache counted in resident usage (memory.stat file less inactive_file), in bytes. Absent unless both counters are valid."),
100
+ .describe(
101
+ "File cache counted in resident usage (memory.stat file less inactive_file), in bytes. Absent unless both counters are valid.",
102
+ ),
101
103
  kernelBytes: z
102
104
  .number()
103
105
  .optional()
@@ -163,7 +165,9 @@ export const SandboxMetricsSchema = z.object({
163
165
  .optional()
164
166
  .describe("How long the CPU figures were measured over, in milliseconds. Absent on a first reading, and then so is every CPU figure."),
165
167
  sandbox: SandboxUsageSchema.describe("The sandbox as a whole, including the daemon."),
166
- daemon: DaemonUsageSchema.describe("The daemon's own usage, included in the sandbox total and process count but excluded from roles and session sums."),
168
+ daemon: DaemonUsageSchema.describe(
169
+ "The daemon's own usage, included in the sandbox total and process count but excluded from roles and session sums.",
170
+ ),
167
171
  sessions: z
168
172
  .record(z.string(), SessionMetricsSchema)
169
173
  .describe(
@@ -273,8 +277,29 @@ export const StorageScanSchema = z.object({
273
277
  export type StorageScan = z.infer<typeof StorageScanSchema>;
274
278
 
275
279
  export const StorageReportSchema = z.object({
276
- scan: StorageScanSchema.optional().describe("The last scan that finished, kept across restarts; its `finishedAt` says how old it is. Absent until one has."),
280
+ scan: StorageScanSchema.optional().describe(
281
+ "The last scan that finished, kept across restarts; its `finishedAt` says how old it is. Absent until one has.",
282
+ ),
277
283
  scanning: z.boolean().describe("Whether a scan is running now."),
284
+ historyDisk: z
285
+ .object({
286
+ freeBytes: z.number().describe("Space available to the sandbox on the history volume, measured now."),
287
+ totalBytes: z.number().describe("The history volume's total size."),
288
+ floorBytes: z.number().describe("Space kept free before a new checkout: 2% of this volume clamped to 5–20 GiB."),
289
+ low: z.boolean().describe("Whether available history space is below the checkout floor."),
290
+ })
291
+ .optional()
292
+ .describe("The filesystem holding conversation checkouts, independent of the workspace volume. Absent if unreadable."),
293
+ repositoryCheckouts: z
294
+ .array(
295
+ z.object({
296
+ repo: z.string().describe("The live repository's workspace-relative id, or root for the workspace repository."),
297
+ head: z.string().describe("The commit whose tracked blobs were measured."),
298
+ bytes: z.number().describe("The sum of blob sizes at this HEAD: the estimated bytes for one full checkout."),
299
+ }),
300
+ )
301
+ .optional()
302
+ .describe("Live repositories' checkout costs, cached per repository and commit. Repositories without a readable HEAD are omitted."),
278
303
  });
279
304
  export type StorageReport = z.infer<typeof StorageReportSchema>;
280
305
 
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ import {
3
+ KeyringCaptureItemSchema,
4
+ KeyringCaptureResultSchema,
5
+ KeyringPhoneFillResultSchema,
6
+ KeyringPhoneKeysSchema,
7
+ KeyringPhonePairResultSchema,
8
+ KeyringPublicKeySchema,
9
+ SignedKeyringPhoneFillSchema,
10
+ } from "./keyring.js";
11
+
12
+ // THE PHONE'S WAY TO THE OWNER'S PASSWORDS (2026-10-19): the Intentic Device app as Android's autofill service, saving
13
+ // what the person signs in with and filling it back, against the keyrings on the owner's computers. The phone door is
14
+ // the sandbox asking and the phone answering (phone-protocol.ts); these go the other way, so they are plain HTTPS
15
+ // requests from the app to its sandbox, authorised by the phone's enrollment token (`Authorization: Bearer <token>`),
16
+ // and the sandbox relays each to the named computer's keyring over that computer's device link. What rides is sealed
17
+ // end to end: a capture to the keyring's inbox key, a fill's answer to the phone's own seal key.
18
+
19
+ export const PHONE_PASSWORDS_PATHS = {
20
+ keyrings: "/system/phones/passwords/keyrings",
21
+ pair: "/system/phones/passwords/pair",
22
+ capture: "/system/phones/passwords/capture",
23
+ fill: "/system/phones/passwords/fill",
24
+ } as const;
25
+
26
+ /** One of the owner's computers whose keyring the phone can save to and fill from. */
27
+ export const PhoneKeyringSchema = z.object({
28
+ device: z.string().min(1).max(128).describe("The computer, by its device card."),
29
+ label: z.string().describe("The computer's name for people."),
30
+ online: z.boolean(),
31
+ locked: z.boolean().optional(),
32
+ inbox: KeyringPublicKeySchema.optional().describe("What a capture is sealed to. Absent until the keyring is set up."),
33
+ remember: z.boolean().describe("Whether the person turned on remembering from the phone on that computer."),
34
+ paired: z.boolean().describe("Whether this phone's keys are pinned there, so it can fill."),
35
+ });
36
+ export type PhoneKeyring = z.infer<typeof PhoneKeyringSchema>;
37
+
38
+ // GET keyrings → the owner's keyrings this sandbox can reach.
39
+ export const PhoneKeyringsResultSchema = z.object({ keyrings: z.array(PhoneKeyringSchema) });
40
+
41
+ // POST pair → the computer's keyring asks its person, showing the phone's fingerprint; the phone shows the keyring's.
42
+ export const PhonePasswordsPairSchema = z.object({ keyring: z.string().min(1).max(128), phone: KeyringPhoneKeysSchema }).strict();
43
+ export const PhonePasswordsPairResultSchema = KeyringPhonePairResultSchema;
44
+
45
+ // POST capture → onSaveRequest's credential, sealed on the phone for `{ kind: "capture", via, at }`.
46
+ export const PhonePasswordsCaptureSchema = z.object({ keyring: z.string().min(1).max(128), item: KeyringCaptureItemSchema }).strict();
47
+ export const PhonePasswordsCaptureResultSchema = KeyringCaptureResultSchema;
48
+
49
+ // POST fill → after the person's fingerprint signed the request with the presence key.
50
+ export const PhonePasswordsFillSchema = z.object({ keyring: z.string().min(1).max(128), fill: SignedKeyringPhoneFillSchema }).strict();
51
+ export const PhonePasswordsFillResultSchema = KeyringPhoneFillResultSchema;
@@ -36,6 +36,12 @@ export const UsageWindowSchema = z.object({
36
36
  .describe(
37
37
  "When the window this pool was last read in closed, in epoch seconds, while nothing has read the pool since. Its `utilization` is then 0, the floor every new window starts from, not a measurement, and `resetsAt` is absent because the next reset is unknown.",
38
38
  ),
39
+ missingSince: z
40
+ .number()
41
+ .optional()
42
+ .describe(
43
+ "When the provider stopped reporting this pool, in epoch seconds: a later reading of the account named every other pool but not this one. Its figure is the last one read, still a valid floor until its window closes, and the pool is dropped once it has.",
44
+ ),
39
45
  });
40
46
  export type UsageWindow = z.infer<typeof UsageWindowSchema>;
41
47
  // A failed re-read, whatever the cause: a 429 park, a 403 asking for verification, a 5xx, a timeout. Recorded as the fact
@@ -50,6 +56,12 @@ export const UsageUnreadSchema = z.object({
50
56
  ),
51
57
  });
52
58
  export type UsageUnread = z.infer<typeof UsageUnreadSchema>;
59
+ // A turn answered against a reading that said its model could not run: the provider's own word that the reading is wrong.
60
+ export const UsageContradictionSchema = z.object({
61
+ at: z.number().describe("When the turn was answered, in milliseconds."),
62
+ model: z.string().describe("The model the answered turn ran on."),
63
+ });
64
+ export type UsageContradiction = z.infer<typeof UsageContradictionSchema>;
53
65
  // Every window kept, not just the binding one, since which pool binds changes between turns. Utilization only climbs
54
66
  // within a window, so a stale reading is still a valid floor. `measuredAt` is epoch ms — windows are seconds.
55
67
  export const AccountUsageSchema = z.object({
@@ -58,6 +70,16 @@ export const AccountUsageSchema = z.object({
58
70
  unread: UsageUnreadSchema.optional().describe(
59
71
  "Present while re-reading this account keeps failing: these windows are the last reading that succeeded, and `measuredAt` will not move until a read succeeds again.",
60
72
  ),
73
+ uncovered: z
74
+ .array(z.string())
75
+ .optional()
76
+ .describe(
77
+ "Models this account serves that no pool in this reading gates, by id: the provider published no allowance for them, so their room is unknown, never assumed spent. Absent when every model is covered or the account's models are not known.",
78
+ ),
79
+ contradicted: UsageContradictionSchema.optional()
80
+ .describe(
81
+ "A turn on `model` was answered after this reading said every pool it spends was empty, so the reading is wrong about that model: those pools read as at the line, not spent, until a new reading replaces this one.",
82
+ ),
61
83
  });
62
84
  export type AccountUsage = z.infer<typeof AccountUsageSchema>;
63
85
  // Who has to act for a blocked account, which is the only thing a surface chooses its instruction by: the reason is the
@@ -148,8 +170,8 @@ export const ProviderRefusalSchema = z.object({
148
170
  .describe(
149
171
  "When the provider said to try again, in epoch seconds, for a spent allowance where it named one. Until then the refusal stands whatever a reading says; after it, it is over.",
150
172
  ),
151
- // Codex's "To get more access now, send a request to your admin": a team's credit, which no usage endpoint reads,
152
- // so a weekly pool at 57% says nothing about it (2026-10-17).
173
+ // Cursor's "ask your admin to increase your limit": a team's spend limit, which no usage endpoint reads. Not Codex's
174
+ // "send a request to your admin", which names a window its reading measures (failure-sentences.ts ADMIN_GRANTED).
153
175
  unmetered: z
154
176
  .literal(true)
155
177
  .optional()
@@ -408,6 +430,8 @@ export const ResumeRoutingSchema = z.object({
408
430
  ),
409
431
  // A client whose catalog hasn't loaded has nothing to send; the turn's own model is the better fallback.
410
432
  model: z.string().optional().describe("Which model. Leave it out to keep the one the refused turn named."),
433
+ // A tier named for one model may mean nothing on another, so a move onto a ladder rung carries that rung's own.
434
+ effort: z.string().optional().describe("How hard the model should think on the re-run. Leave it out to keep the refused turn's."),
411
435
  // A session is a file the daemon keeps; a credential is an env var. That's what lets a same-provider account switch
412
436
  // resume the existing session instead of opening a fresh one.
413
437
  carry: z
@@ -458,6 +482,19 @@ export const QueueResumeSchema = z.object({
458
482
  ),
459
483
  });
460
484
  export type QueueResume = z.infer<typeof QueueResumeSchema>;
485
+ // Points a person's waiting messages at another provider, model or account without letting them go: what a pick in the
486
+ // model picker does to the words already waiting, which would otherwise go by themselves on the pick they were sent under.
487
+ export const QueueRerouteSchema = z.object({
488
+ conversationId: z.string().min(1).describe("Whose queue."),
489
+ routing: ResumeRoutingSchema.describe(
490
+ "Who serves the turn each message starts from now on. A model or effort left out keeps each message's own; an account left out goes on the conversation's own when it goes.",
491
+ ),
492
+ ids: z
493
+ .array(z.string().min(1))
494
+ .optional()
495
+ .describe("Which waiting messages to move. Leave it out to move every person's waiting message, held or booked; each keeps its own booking."),
496
+ });
497
+ export type QueueReroute = z.infer<typeof QueueRerouteSchema>;
461
498
  // Re-times what a conversation's queue holds: the same messages (or only those `ids` names), booked for another instant
462
499
  // or for after another conversation's work lands. One of the two, never both, since a booking waits on exactly one thing.
463
500
  export const QueueScheduleSchema = z
@@ -472,6 +509,12 @@ export const QueueScheduleSchema = z
472
509
  sendAfter: ConversationIdSchema.optional().describe(
473
510
  "Send what waits once that conversation has finished and all of its work has landed in the workspace, instead. Sent now when it has nothing running and nothing left to land.",
474
511
  ),
512
+ sendAtReopen: z
513
+ .literal(true)
514
+ .optional()
515
+ .describe(
516
+ "Says `sendAt` is the reopen of the allowance what waits would be refused by, not a time you chose; the queue records it as `reopenOf`. Only with `sendAt`.",
517
+ ),
475
518
  ids: z
476
519
  .array(z.string().min(1))
477
520
  .optional()
@@ -497,6 +540,10 @@ export const QueueContinueSchema = z
497
540
  .optional()
498
541
  .describe("Carry on at this instant (epoch milliseconds). At most a month ahead; an instant already past carries on now."),
499
542
  sendAfter: ConversationIdSchema.optional().describe("Carry on once that conversation has finished and all of its work has landed, instead."),
543
+ sendAtReopen: z
544
+ .literal(true)
545
+ .optional()
546
+ .describe("Says `sendAt` is the reopen of a spent allowance, not a time you chose; the queue records it as `reopenOf`. Only with `sendAt`."),
500
547
  routing: ResumeRoutingSchema.optional().describe(
501
548
  "Who serves the turn it starts. Leave it out to carry on where the conversation runs when it goes.",
502
549
  ),
@@ -74,6 +74,8 @@ export const UsageTurnSchema = z.object({
74
74
  iqSearchCohort: z.string().optional(),
75
75
  // Every tool call that searched, including CLI searches like `iq q`; absent means unmeasured, not zero.
76
76
  searchCalls: z.number().optional(),
77
+ // Of searchCalls, the ones that ran iq: the search teaching's uptake. Absent on rows written before 2026-10-19.
78
+ iqSearches: z.number().optional(),
77
79
  // Search calls before the first file touch; absent as searchCalls; a turn touching nothing counts them all.
78
80
  openingSearches: z.number().optional(),
79
81
  // Directory listings (ls, tree, LS tool) before the turn's first file touch, same window as openingSearches.
@@ -3,8 +3,9 @@ import { z } from "zod";
3
3
  // The daemon is the only tier that knows what the agent is doing, so it is the sender. A registration is per-device, in
4
4
  // two kinds:
5
5
  // webpush: a browser (including the Android TWA); the daemon sends to its push service directly, end-to-end encrypted.
6
- // relay: a native app (iOS); its OS push service only accepts sends from the vendor, so the daemon posts through a
7
- // relay, which can read the payload.
6
+ // relay: a native app (the iPhone app, the Android editor app, the Intentic Device app); its OS push service only accepts
7
+ // sends from the vendor, so the daemon posts through a relay, which can read the payload. Only Apple is handed the words:
8
+ // an Android app gets a data-only message and fetches them from the daemon itself.
8
9
 
9
10
  // Exact shape `web-push` consumes, from `PushManager.subscribe()`; posted back verbatim.
10
11
  export const WebPushChannelSchema = z.object({
@@ -26,7 +27,12 @@ export const RelayChannelSchema = z.object({
26
27
  ),
27
28
  url: z.url().describe("Where to post a send. Recorded rather than assumed, so the sandbox need not know any platform by name."),
28
29
  deviceId: z.string().min(1).describe("The device's id, which also identifies this registration everywhere else in this group."),
29
- secret: z.string().min(1).describe("Proof that this sandbox may notify this device. The relay never learns which sandbox is calling."),
30
+ secret: z
31
+ .string()
32
+ .min(1)
33
+ .describe(
34
+ "Proof that this sandbox may notify this device. A send names no sandbox; the relay knows which sandbox a device belongs to only when the editor app registered it for that one sandbox, so the app can be told which sandbox to ask.",
35
+ ),
30
36
  });
31
37
  export type RelayChannel = z.infer<typeof RelayChannelSchema>;
32
38
  export const PushChannelSchema = z.discriminatedUnion("kind", [WebPushChannelSchema, RelayChannelSchema]);
@@ -61,6 +67,16 @@ export const PushNotificationSchema = z.object({
61
67
  ),
62
68
  });
63
69
  export type PushNotification = z.infer<typeof PushNotificationSchema>;
70
+ export const PushSentQuerySchema = z.object({
71
+ tag: z.string().min(1).describe("The tag the push carried, which is all a native app's push says about what it was."),
72
+ });
73
+ // What a native app's data-only push leaves out, read back over the app's own session so the words never pass through
74
+ // the push service or the relay.
75
+ export const PushSentNotificationSchema = PushNotificationSchema.extend({
76
+ tag: z.string().min(1).describe("The tag it was sent under, the one asked for."),
77
+ sentAt: z.string().describe("When the sandbox sent it (ISO)."),
78
+ });
79
+ export type PushSentNotification = z.infer<typeof PushSentNotificationSchema>;
64
80
  export const PushConfigSchema = z.object({
65
81
  publicKey: z.string().describe("The key a browser needs in order to subscribe. Native apps ignore it."),
66
82
  subscribed: z