@intentic/sandbox-contract 1.337.0 → 1.339.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 (316) hide show
  1. package/README.md +41 -0
  2. package/dist/chores/probes.d.ts.map +1 -1
  3. package/dist/chores/probes.js +4 -7
  4. package/dist/chores/probes.js.map +1 -1
  5. package/dist/contracts/agent.contract.d.ts +32 -23
  6. package/dist/contracts/agent.contract.d.ts.map +1 -1
  7. package/dist/contracts/agents.contract.d.ts +6020 -5994
  8. package/dist/contracts/agents.contract.d.ts.map +1 -1
  9. package/dist/contracts/agents.contract.js +13 -1
  10. package/dist/contracts/agents.contract.js.map +1 -1
  11. package/dist/contracts/approvals.contract.d.ts +8 -0
  12. package/dist/contracts/approvals.contract.d.ts.map +1 -1
  13. package/dist/contracts/automations.contract.d.ts +3 -3
  14. package/dist/contracts/capabilities.contract.d.ts +28 -0
  15. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  16. package/dist/contracts/capabilities.contract.js +11 -1
  17. package/dist/contracts/capabilities.contract.js.map +1 -1
  18. package/dist/contracts/device.contract.d.ts +225 -0
  19. package/dist/contracts/device.contract.d.ts.map +1 -1
  20. package/dist/contracts/device.contract.js +10 -0
  21. package/dist/contracts/device.contract.js.map +1 -1
  22. package/dist/contracts/extensions.contract.d.ts +16 -0
  23. package/dist/contracts/extensions.contract.d.ts.map +1 -1
  24. package/dist/contracts/needs.contract.d.ts +22 -7
  25. package/dist/contracts/needs.contract.d.ts.map +1 -1
  26. package/dist/contracts/org.contract.d.ts +35 -0
  27. package/dist/contracts/org.contract.d.ts.map +1 -0
  28. package/dist/contracts/org.contract.js +14 -0
  29. package/dist/contracts/org.contract.js.map +1 -0
  30. package/dist/contracts/phone.contract.d.ts +1 -0
  31. package/dist/contracts/phone.contract.d.ts.map +1 -1
  32. package/dist/contracts/privacy.contract.d.ts +115 -0
  33. package/dist/contracts/privacy.contract.d.ts.map +1 -1
  34. package/dist/contracts/privacy.contract.js +11 -2
  35. package/dist/contracts/privacy.contract.js.map +1 -1
  36. package/dist/contracts/runner.contract.d.ts +11 -7
  37. package/dist/contracts/runner.contract.d.ts.map +1 -1
  38. package/dist/contracts/secrets.contract.d.ts +85 -0
  39. package/dist/contracts/secrets.contract.d.ts.map +1 -1
  40. package/dist/contracts/secrets.contract.js +48 -1
  41. package/dist/contracts/secrets.contract.js.map +1 -1
  42. package/dist/contracts/sessions.contract.d.ts +4 -1
  43. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  44. package/dist/contracts/settings.contract.d.ts +11 -3
  45. package/dist/contracts/settings.contract.d.ts.map +1 -1
  46. package/dist/contracts/system.contract.d.ts +23 -2
  47. package/dist/contracts/system.contract.d.ts.map +1 -1
  48. package/dist/events/agent-events.d.ts +17 -4
  49. package/dist/events/agent-events.d.ts.map +1 -1
  50. package/dist/events/requests.d.ts +4 -0
  51. package/dist/events/requests.d.ts.map +1 -1
  52. package/dist/events/requests.js +1 -1
  53. package/dist/events/requests.js.map +1 -1
  54. package/dist/events/system-events.d.ts +4 -0
  55. package/dist/events/system-events.d.ts.map +1 -1
  56. package/dist/events/system-events.js +2 -0
  57. package/dist/events/system-events.js.map +1 -1
  58. package/dist/events/transcript.d.ts +27 -6
  59. package/dist/events/transcript.d.ts.map +1 -1
  60. package/dist/index.d.ts +7562 -7426
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +31 -257
  63. package/dist/index.js.map +1 -1
  64. package/dist/models/agent-lanes.d.ts +30 -0
  65. package/dist/models/agent-lanes.d.ts.map +1 -0
  66. package/dist/models/agent-lanes.js +67 -0
  67. package/dist/models/agent-lanes.js.map +1 -0
  68. package/dist/models/agent-standing.d.ts +55 -0
  69. package/dist/models/agent-standing.d.ts.map +1 -0
  70. package/dist/models/agent-standing.js +82 -0
  71. package/dist/models/agent-standing.js.map +1 -0
  72. package/dist/models/agent-starter.d.ts +10 -0
  73. package/dist/models/agent-starter.d.ts.map +1 -0
  74. package/dist/models/agent-starter.js +16 -0
  75. package/dist/models/agent-starter.js.map +1 -0
  76. package/dist/netd/generated/wire.d.ts +1 -1
  77. package/dist/netd/generated/wire.d.ts.map +1 -1
  78. package/dist/policy/identity-assertion.d.ts.map +1 -1
  79. package/dist/policy/identity-assertion.js +2 -8
  80. package/dist/policy/identity-assertion.js.map +1 -1
  81. package/dist/policy/keyring-seal.d.ts +48 -0
  82. package/dist/policy/keyring-seal.d.ts.map +1 -0
  83. package/dist/policy/keyring-seal.js +94 -0
  84. package/dist/policy/keyring-seal.js.map +1 -0
  85. package/dist/policy/message-limits.d.ts +2 -0
  86. package/dist/policy/message-limits.d.ts.map +1 -1
  87. package/dist/policy/message-limits.js +2 -0
  88. package/dist/policy/message-limits.js.map +1 -1
  89. package/dist/policy/org-locks.d.ts +58 -0
  90. package/dist/policy/org-locks.d.ts.map +1 -0
  91. package/dist/policy/org-locks.js +113 -0
  92. package/dist/policy/org-locks.js.map +1 -0
  93. package/dist/policy/persona-capability.d.ts +14 -0
  94. package/dist/policy/persona-capability.d.ts.map +1 -0
  95. package/dist/policy/persona-capability.js +23 -0
  96. package/dist/policy/persona-capability.js.map +1 -0
  97. package/dist/policy/reserved-servers.d.ts.map +1 -1
  98. package/dist/policy/reserved-servers.js +1 -1
  99. package/dist/policy/reserved-servers.js.map +1 -1
  100. package/dist/protocol/doors/phone-links.d.ts +5 -0
  101. package/dist/protocol/doors/phone-links.d.ts.map +1 -1
  102. package/dist/protocol/doors/phone-links.js +3 -1
  103. package/dist/protocol/doors/phone-links.js.map +1 -1
  104. package/dist/protocol/doors/webext-links.d.ts +3 -0
  105. package/dist/protocol/doors/webext-links.d.ts.map +1 -1
  106. package/dist/protocol/doors/webext-links.js +3 -0
  107. package/dist/protocol/doors/webext-links.js.map +1 -1
  108. package/dist/protocol/raw/raw-json-routes.d.ts +270 -0
  109. package/dist/protocol/raw/raw-json-routes.d.ts.map +1 -1
  110. package/dist/protocol/raw/raw-json-routes.js +14 -1
  111. package/dist/protocol/raw/raw-json-routes.js.map +1 -1
  112. package/dist/protocol/raw/raw-routes.d.ts +70 -0
  113. package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
  114. package/dist/protocol/raw/raw-routes.js +18 -0
  115. package/dist/protocol/raw/raw-routes.js.map +1 -1
  116. package/dist/protocol/route-meta.d.ts.map +1 -1
  117. package/dist/protocol/route-meta.js.map +1 -1
  118. package/dist/protocol/routes.d.ts +4 -0
  119. package/dist/protocol/routes.d.ts.map +1 -1
  120. package/dist/protocol/routes.js +12 -0
  121. package/dist/protocol/routes.js.map +1 -1
  122. package/dist/schemas/agents-list.d.ts +5 -0
  123. package/dist/schemas/agents-list.d.ts.map +1 -1
  124. package/dist/schemas/agents-list.js +8 -0
  125. package/dist/schemas/agents-list.js.map +1 -1
  126. package/dist/schemas/agents.d.ts +1 -0
  127. package/dist/schemas/agents.d.ts.map +1 -1
  128. package/dist/schemas/agents.js +1 -0
  129. package/dist/schemas/agents.js.map +1 -1
  130. package/dist/schemas/approvals.d.ts +20 -0
  131. package/dist/schemas/approvals.d.ts.map +1 -1
  132. package/dist/schemas/approvals.js +10 -0
  133. package/dist/schemas/approvals.js.map +1 -1
  134. package/dist/schemas/capabilities.d.ts +26 -0
  135. package/dist/schemas/capabilities.d.ts.map +1 -1
  136. package/dist/schemas/capabilities.js +17 -0
  137. package/dist/schemas/capabilities.js.map +1 -1
  138. package/dist/schemas/data-protection.d.ts +5 -0
  139. package/dist/schemas/data-protection.d.ts.map +1 -1
  140. package/dist/schemas/data-protection.js +29 -0
  141. package/dist/schemas/data-protection.js.map +1 -1
  142. package/dist/schemas/devices.d.ts +20 -0
  143. package/dist/schemas/devices.d.ts.map +1 -1
  144. package/dist/schemas/devices.js +5 -1
  145. package/dist/schemas/devices.js.map +1 -1
  146. package/dist/schemas/extension-updates.d.ts +32 -0
  147. package/dist/schemas/extension-updates.d.ts.map +1 -1
  148. package/dist/schemas/git/git.d.ts +19 -0
  149. package/dist/schemas/git/git.d.ts.map +1 -1
  150. package/dist/schemas/git/git.js +12 -1
  151. package/dist/schemas/git/git.js.map +1 -1
  152. package/dist/schemas/hosts.d.ts +2 -0
  153. package/dist/schemas/hosts.d.ts.map +1 -1
  154. package/dist/schemas/hosts.js +11 -1
  155. package/dist/schemas/hosts.js.map +1 -1
  156. package/dist/schemas/intentic-os.d.ts +218 -0
  157. package/dist/schemas/intentic-os.d.ts.map +1 -1
  158. package/dist/schemas/intentic-os.js +166 -0
  159. package/dist/schemas/intentic-os.js.map +1 -1
  160. package/dist/schemas/keyring.d.ts +421 -0
  161. package/dist/schemas/keyring.d.ts.map +1 -0
  162. package/dist/schemas/keyring.js +176 -0
  163. package/dist/schemas/keyring.js.map +1 -0
  164. package/dist/schemas/links.d.ts +208 -0
  165. package/dist/schemas/links.d.ts.map +1 -0
  166. package/dist/schemas/links.js +116 -0
  167. package/dist/schemas/links.js.map +1 -0
  168. package/dist/schemas/maintenance.d.ts.map +1 -1
  169. package/dist/schemas/maintenance.js.map +1 -1
  170. package/dist/schemas/members.d.ts.map +1 -1
  171. package/dist/schemas/members.js +2 -0
  172. package/dist/schemas/members.js.map +1 -1
  173. package/dist/schemas/needs.d.ts +25 -8
  174. package/dist/schemas/needs.d.ts.map +1 -1
  175. package/dist/schemas/needs.js +15 -3
  176. package/dist/schemas/needs.js.map +1 -1
  177. package/dist/schemas/organisation.d.ts +106 -0
  178. package/dist/schemas/organisation.d.ts.map +1 -0
  179. package/dist/schemas/organisation.js +52 -0
  180. package/dist/schemas/organisation.js.map +1 -0
  181. package/dist/schemas/phone.d.ts +3 -0
  182. package/dist/schemas/phone.d.ts.map +1 -1
  183. package/dist/schemas/phone.js +1 -0
  184. package/dist/schemas/phone.js.map +1 -1
  185. package/dist/schemas/privacy.d.ts +232 -0
  186. package/dist/schemas/privacy.d.ts.map +1 -1
  187. package/dist/schemas/privacy.js +154 -4
  188. package/dist/schemas/privacy.js.map +1 -1
  189. package/dist/schemas/secrets.d.ts +151 -0
  190. package/dist/schemas/secrets.d.ts.map +1 -1
  191. package/dist/schemas/secrets.js +71 -8
  192. package/dist/schemas/secrets.js.map +1 -1
  193. package/dist/schemas/settings.d.ts +8 -0
  194. package/dist/schemas/settings.d.ts.map +1 -1
  195. package/dist/schemas/settings.js +4 -0
  196. package/dist/schemas/settings.js.map +1 -1
  197. package/dist/schemas/speech.d.ts +12 -0
  198. package/dist/schemas/speech.d.ts.map +1 -1
  199. package/dist/schemas/speech.js +23 -0
  200. package/dist/schemas/speech.js.map +1 -1
  201. package/dist/schemas/system.d.ts +2 -0
  202. package/dist/schemas/system.d.ts.map +1 -1
  203. package/dist/schemas/system.js +1 -1
  204. package/dist/schemas/system.js.map +1 -1
  205. package/dist/schemas/terminal.d.ts +15 -0
  206. package/dist/schemas/terminal.d.ts.map +1 -1
  207. package/dist/schemas/terminal.js +11 -2
  208. package/dist/schemas/terminal.js.map +1 -1
  209. package/dist/schemas/voice.d.ts +3 -0
  210. package/dist/schemas/voice.d.ts.map +1 -1
  211. package/dist/schemas/voice.js +7 -0
  212. package/dist/schemas/voice.js.map +1 -1
  213. package/dist/schemas/webext.d.ts +17 -0
  214. package/dist/schemas/webext.d.ts.map +1 -1
  215. package/dist/schemas/webext.js +7 -1
  216. package/dist/schemas/webext.js.map +1 -1
  217. package/dist/state/contract-lock.d.ts +3 -1
  218. package/dist/state/contract-lock.d.ts.map +1 -1
  219. package/dist/state/contract-lock.js +34 -9
  220. package/dist/state/contract-lock.js.map +1 -1
  221. package/dist/state/contract-modules.d.ts +4 -0
  222. package/dist/state/contract-modules.d.ts.map +1 -0
  223. package/dist/state/contract-modules.js +28 -0
  224. package/dist/state/contract-modules.js.map +1 -0
  225. package/dist/state/daemon-floor.d.ts +4 -0
  226. package/dist/state/daemon-floor.d.ts.map +1 -0
  227. package/dist/state/daemon-floor.js +8 -0
  228. package/dist/state/daemon-floor.js.map +1 -0
  229. package/dist/state/definition.d.ts +34 -0
  230. package/dist/state/definition.d.ts.map +1 -1
  231. package/dist/state/history-state.d.ts.map +1 -1
  232. package/dist/state/history-state.js +8 -0
  233. package/dist/state/history-state.js.map +1 -1
  234. package/dist/text/documents.d.ts +3 -1
  235. package/dist/text/documents.d.ts.map +1 -1
  236. package/dist/text/documents.js +30 -0
  237. package/dist/text/documents.js.map +1 -1
  238. package/dist/text/pages.d.ts.map +1 -1
  239. package/dist/text/pages.js +3 -7
  240. package/dist/text/pages.js.map +1 -1
  241. package/package.json +27 -5
  242. package/src/chores/probes.ts +4 -6
  243. package/src/contracts/agents.contract.ts +18 -1
  244. package/src/contracts/capabilities.contract.ts +14 -0
  245. package/src/contracts/device.contract.ts +30 -0
  246. package/src/contracts/org.contract.ts +19 -0
  247. package/src/contracts/privacy.contract.ts +13 -1
  248. package/src/contracts/secrets.contract.ts +64 -0
  249. package/src/events/requests.ts +3 -1
  250. package/src/events/system-events.ts +9 -0
  251. package/src/index.ts +41 -279
  252. package/src/models/agent-lanes.ts +152 -0
  253. package/src/models/agent-standing.ts +279 -0
  254. package/src/models/agent-starter.ts +26 -0
  255. package/src/netd/generated/netd-wire.json +2 -1
  256. package/src/netd/generated/wire.ts +1 -1
  257. package/src/policy/identity-assertion.ts +2 -8
  258. package/src/policy/keyring-seal.test.ts +93 -0
  259. package/src/policy/keyring-seal.ts +148 -0
  260. package/src/policy/message-limits.ts +3 -0
  261. package/src/policy/org-locks.ts +173 -0
  262. package/src/policy/persona-capability.ts +57 -0
  263. package/src/policy/reserved-servers.ts +3 -2
  264. package/src/policy/tests/org-locks.test.ts +92 -0
  265. package/src/policy/tests/reserved-servers.test.ts +3 -2
  266. package/src/protocol/doors/listener-protocol.ts +1 -1
  267. package/src/protocol/doors/phone-links.ts +11 -2
  268. package/src/protocol/doors/phone-protocol.test.ts +8 -1
  269. package/src/protocol/doors/webext-links.ts +13 -0
  270. package/src/protocol/ingress-contract.fixture.json +2 -0
  271. package/src/protocol/pairing-redeem.ts +1 -1
  272. package/src/protocol/peer-dial.test.ts +6 -6
  273. package/src/protocol/peer-mcp-server.test.ts +2 -1
  274. package/src/protocol/raw/raw-json-routes.ts +31 -1
  275. package/src/protocol/raw/raw-routes.ts +35 -1
  276. package/src/protocol/route-meta.ts +2 -1
  277. package/src/protocol/routes.test.ts +33 -1
  278. package/src/protocol/routes.ts +18 -0
  279. package/src/protocol/sse.ts +1 -1
  280. package/src/schemas/agent.ts +1 -1
  281. package/src/schemas/agents-list.ts +13 -0
  282. package/src/schemas/agents.ts +1 -0
  283. package/src/schemas/approvals.ts +14 -0
  284. package/src/schemas/capabilities.ts +25 -0
  285. package/src/schemas/data-protection.ts +47 -0
  286. package/src/schemas/devices.ts +15 -1
  287. package/src/schemas/git/git.ts +22 -2
  288. package/src/schemas/hosts.ts +14 -1
  289. package/src/schemas/intentic-os.test.ts +148 -0
  290. package/src/schemas/intentic-os.ts +259 -0
  291. package/src/schemas/keyring.ts +277 -0
  292. package/src/schemas/links.ts +180 -0
  293. package/src/schemas/maintenance.ts +1 -0
  294. package/src/schemas/members.ts +4 -0
  295. package/src/schemas/needs.ts +21 -5
  296. package/src/schemas/organisation.ts +110 -0
  297. package/src/schemas/phone.ts +3 -0
  298. package/src/schemas/privacy.test.ts +63 -0
  299. package/src/schemas/privacy.ts +213 -5
  300. package/src/schemas/secrets.ts +96 -8
  301. package/src/schemas/settings.ts +8 -0
  302. package/src/schemas/speech.ts +38 -1
  303. package/src/schemas/system.ts +5 -2
  304. package/src/schemas/terminal.ts +13 -2
  305. package/src/schemas/voice.ts +11 -0
  306. package/src/schemas/webext.ts +16 -1
  307. package/src/state/contract-lock.test.ts +12 -12
  308. package/src/state/contract-lock.ts +43 -12
  309. package/src/state/contract-modules.ts +41 -0
  310. package/src/state/daemon-floor.test.ts +81 -0
  311. package/src/state/daemon-floor.ts +32 -0
  312. package/src/state/history-state.ts +17 -0
  313. package/src/state/workspace-state.ts +1 -1
  314. package/src/text/documents.test.ts +30 -1
  315. package/src/text/documents.ts +43 -1
  316. package/src/text/pages.ts +5 -7
@@ -0,0 +1,180 @@
1
+ import { z } from "zod";
2
+ import { AskQuestionSchema } from "../events/requests.js";
3
+ import { entryId } from "./internal.js";
4
+
5
+ // LINKED SANDBOXES: one of the owner's sandboxes handing tasks to another. The sandbox that hands work over holds a
6
+ // `link` capability card (schemas/capabilities.ts LinkConfigSchema); the one that takes it holds an inbound link, minted
7
+ // by its owner, whose token is that card's credential. Everything the taking side does with a task is decided there, by
8
+ // the terms on its own link: which persona the work wears, whether each task waits for its owner, how many turns an hour.
9
+ // The handing side only asks, reads and stops.
10
+
11
+ // What a link token looks like: issuer-prefixed like a control token (`ict_`), and never the same credential.
12
+ export const LINK_TOKEN_PREFIX = "ilk_";
13
+ // The header the taking sandbox's link doors read it from.
14
+ export const LINK_HEADER = "x-intentic-link";
15
+ // How many links a task may have crossed before the one it is asking across. A task a person started is at 0; a sandbox
16
+ // that took a task through a link and hands part of it on asks at 1. Past this the asking sandbox refuses before it
17
+ // calls, and the taking sandbox refuses again, so two sandboxes cannot hand one task back and forth for ever.
18
+ export const LINK_MAX_HOPS = 2;
19
+ // The ceiling a link's own hourly allowance may be set to, and what one is minted with when its owner names none.
20
+ export const LINK_TURNS_PER_HOUR_MAX = 120;
21
+ export const LINK_TURNS_PER_HOUR_DEFAULT = 20;
22
+
23
+ // THE TERMS, set on the taking side and enforced there: what the asking side is told, never what it decides.
24
+ export const LinkTermsSchema = z.object({
25
+ actsAs: entryId
26
+ .optional()
27
+ .describe(
28
+ "The persona every task through this link wears: its accounts, its shelves, its folders, its model. Absent, the task runs with no signed-in account and the full toolbox, as any unattended wake does.",
29
+ ),
30
+ approve: z
31
+ .boolean()
32
+ .default(false)
33
+ .describe("Each new task waits for this sandbox's owner to allow it before anything runs. Follow-up messages to a task already allowed do not."),
34
+ turnsPerHour: z
35
+ .number()
36
+ .int()
37
+ .min(1)
38
+ .max(LINK_TURNS_PER_HOUR_MAX)
39
+ .default(LINK_TURNS_PER_HOUR_DEFAULT)
40
+ .describe("How many turns the link may start in an hour, new tasks and follow-ups alike. Words said into a turn already running are not counted."),
41
+ });
42
+ export type LinkTerms = z.infer<typeof LinkTermsSchema>;
43
+
44
+ // Where the link's holder says it is, as the owner's editor named it at pairing; informational, never a credential.
45
+ export const LinkPeerSchema = z.object({
46
+ sandboxId: z.string().max(100).optional().describe("The asking sandbox's id on the owner's account."),
47
+ url: z.string().max(500).optional().describe("The asking sandbox's own address."),
48
+ });
49
+ export type LinkPeer = z.infer<typeof LinkPeerSchema>;
50
+
51
+ // A task waiting for the taking sandbox's owner, when the link's terms say every task waits.
52
+ export const LinkRequestSchema = z.object({
53
+ id: z.string().describe("The task's own id, the one the asking sandbox gave it."),
54
+ conversationId: z.string().describe("The conversation the task opens here once it is allowed."),
55
+ task: z.string().describe("What it asks, cut to a readable length."),
56
+ title: z.string().optional().describe("Its one-line name."),
57
+ askedAt: z.number().describe("When it arrived, in epoch milliseconds."),
58
+ });
59
+ export type LinkRequest = z.infer<typeof LinkRequestSchema>;
60
+
61
+ // One inbound link as the owner's Access page lists it: everything but the token, which is kept only as a hash.
62
+ export const InboundLinkSummarySchema = z.object({
63
+ id: z.string(),
64
+ label: z.string().describe("What the asking sandbox is called here: its name at pairing, or what the owner typed."),
65
+ from: LinkPeerSchema.optional(),
66
+ terms: LinkTermsSchema,
67
+ createdAt: z.number(),
68
+ createdBy: z.string().optional().describe("The owner who minted it."),
69
+ lastUsedAt: z.number().optional().describe("The last time it was used, coarse on purpose; absent means never."),
70
+ conversations: z.number().int().nonnegative().describe("How many conversations it has opened here."),
71
+ requests: z.array(LinkRequestSchema).describe("Tasks waiting for the owner's yes, oldest first."),
72
+ });
73
+ export type InboundLinkSummary = z.infer<typeof InboundLinkSummarySchema>;
74
+ export const InboundLinksListSchema = z.object({ links: z.array(InboundLinkSummarySchema) });
75
+ export type InboundLinksList = z.infer<typeof InboundLinksListSchema>;
76
+
77
+ // POST /system/links: what the owner (or their editor, pairing two of their sandboxes) mints a link with.
78
+ export const InboundLinkMintSchema = z.object({
79
+ label: z.string().trim().min(1).max(60),
80
+ from: LinkPeerSchema.optional(),
81
+ terms: LinkTermsSchema.partial().optional(),
82
+ });
83
+ export type InboundLinkMint = z.infer<typeof InboundLinkMintSchema>;
84
+ // The raw token, shown exactly once.
85
+ export const InboundLinkMintedSchema = z.object({ id: z.string(), token: z.string() });
86
+ export type InboundLinkMinted = z.infer<typeof InboundLinkMintedSchema>;
87
+ // POST /system/links/{id}: new terms, whole fields replaced, the rest kept.
88
+ export const InboundLinkTermsUpdateSchema = LinkTermsSchema.partial().extend({ label: z.string().trim().min(1).max(60).optional() });
89
+ export type InboundLinkTermsUpdate = z.infer<typeof InboundLinkTermsUpdateSchema>;
90
+ // POST /system/links/{id}/requests/{request}: the owner's answer to one waiting task.
91
+ export const LinkRequestDecisionSchema = z.object({ approve: z.boolean() });
92
+ export type LinkRequestDecision = z.infer<typeof LinkRequestDecisionSchema>;
93
+
94
+ /* THE LINK DOORS, called by the asking sandbox's daemon with the link token, never by a browser. */
95
+
96
+ // GET /link: the link still works, and what it is held to.
97
+ export const LinkHelloSchema = z.object({
98
+ sandbox: z.string().describe("What the taking sandbox calls itself."),
99
+ link: z.object({ id: z.string(), label: z.string() }),
100
+ terms: z.object({
101
+ approve: z.boolean(),
102
+ turnsPerHour: z.number().int(),
103
+ persona: z.string().optional().describe("The name of the persona its tasks wear, when they wear one."),
104
+ }),
105
+ maxHops: z.number().int(),
106
+ });
107
+ export type LinkHello = z.infer<typeof LinkHelloSchema>;
108
+
109
+ // POST /link/conversations: one task, opening one conversation. `requestId` is the asking side's own id for it: asking
110
+ // again under the same id answers with what became of the first ask, so a lost answer never opens a second conversation.
111
+ export const LinkTaskSchema = z.object({
112
+ requestId: z
113
+ .string()
114
+ .min(1)
115
+ .max(100)
116
+ .regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/),
117
+ task: z.string().min(1).max(60_000).describe("The whole task, self-contained: the taking agent sees nothing else."),
118
+ title: z.string().max(80).optional().describe("A one-line name for the conversation it opens."),
119
+ hops: z.number().int().min(0).max(10).default(0).describe("How many links the task already crossed before this one."),
120
+ tainted: z
121
+ .string()
122
+ .max(500)
123
+ .optional()
124
+ .describe("What outside content the asking turn had taken in, when it had: the taking turn carries it on."),
125
+ from: z
126
+ .object({
127
+ conversation: z.string().max(100).optional(),
128
+ title: z.string().max(200).optional(),
129
+ })
130
+ .optional()
131
+ .describe("The asking conversation, for the taking sandbox's own record."),
132
+ });
133
+ export type LinkTask = z.infer<typeof LinkTaskSchema>;
134
+
135
+ // Where a linked task stands, as the asking side reads it.
136
+ // held: waiting for the taking sandbox's owner to allow it. running: a turn is working on it. blocked: parked on a card;
137
+ // `question` carries one the asking agent may answer, anything else waits for the taking sandbox's owner. finished: its
138
+ // last turn ended, `report` is what it said. failed: its last turn failed. declined: the owner said no. stopped: the
139
+ // asking side stopped it, or its owner did.
140
+ export const LinkTaskStateSchema = z.enum(["held", "running", "blocked", "finished", "failed", "declined", "stopped"]);
141
+ export type LinkTaskState = z.infer<typeof LinkTaskStateSchema>;
142
+
143
+ export const LinkQuestionSchema = z.object({
144
+ requestId: z.string(),
145
+ questions: z.array(AskQuestionSchema),
146
+ });
147
+ export type LinkQuestion = z.infer<typeof LinkQuestionSchema>;
148
+
149
+ export const LinkTaskStatusSchema = z.object({
150
+ conversationId: z.string(),
151
+ state: LinkTaskStateSchema,
152
+ title: z.string().optional(),
153
+ report: z.string().optional().describe("The last settled turn's closing message, whole."),
154
+ question: LinkQuestionSchema.optional(),
155
+ waitingOn: z.string().optional().describe("What a blocked task waits on when it is not a question the asking agent may answer."),
156
+ error: z.string().optional(),
157
+ // Moves every time anything above changes, so a long poll can wait for a version it has not seen.
158
+ version: z.number().int().nonnegative(),
159
+ updatedAt: z.number(),
160
+ });
161
+ export type LinkTaskStatus = z.infer<typeof LinkTaskStatusSchema>;
162
+
163
+ // POST /link/conversations/{id}/messages: follow-up words, said into the running turn or opening the next one.
164
+ export const LinkMessageSchema = z.object({
165
+ message: z.string().min(1).max(60_000),
166
+ messageId: z.string().min(1).max(128).optional(),
167
+ tainted: z.string().max(500).optional(),
168
+ });
169
+ export type LinkMessage = z.infer<typeof LinkMessageSchema>;
170
+ export const LinkMessageReceiptSchema = z.object({
171
+ delivered: z.enum(["started", "steered", "queued"]),
172
+ });
173
+ export type LinkMessageReceipt = z.infer<typeof LinkMessageReceiptSchema>;
174
+
175
+ // POST /link/conversations/{id}/answer: picks for the question the task is parked on, and only a question.
176
+ export const LinkAnswerSchema = z.object({
177
+ requestId: z.string().min(1),
178
+ answers: z.record(z.string(), z.array(z.string())),
179
+ });
180
+ export type LinkAnswer = z.infer<typeof LinkAnswerSchema>;
@@ -341,6 +341,7 @@ export const ChoreProbeRequestSchema = z.object({
341
341
  });
342
342
  // POST /chores/ledger: record a run, or snooze. Daemon-written so a turn started from anywhere (panel, automation,
343
343
  // agent) lands in one ledger.
344
+ /** @alias */
344
345
  export const ChoreLedgerWriteSchema = ChoreLedgerEntrySchema;
345
346
  // `warn` is a real third state, not a soft failure: reporting "pass" when nothing has been exercised yet would be the
346
347
  // check lying.
@@ -30,6 +30,10 @@ export const REACHABLE_CREDENTIAL_KINDS: readonly CapabilityKind[] = [
30
30
  "webext",
31
31
  "plugin",
32
32
  "extension",
33
+ // The owner's account: a person this sandbox works for may be kept from making sandboxes on it.
34
+ "fleet",
35
+ // Another of the owner's sandboxes: a person may be kept from handing it work.
36
+ "link",
33
37
  ];
34
38
 
35
39
  // WHAT ONE PERSON MAY DO, as three independent answers rather than one rung of a ladder: whether their conversations
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { KeyringShareModeSchema } from "./keyring.js";
2
3
  import { AutomationSchema } from "./automations.js";
3
4
 
4
5
  // A need: something an agent cannot finish its task without that only a person can provide (a connection, a secret's
@@ -29,8 +30,10 @@ export const isOpenNeed = (need: { readonly status: NeedStatus }): boolean => OP
29
30
  const FieldValuesSchema = z.record(z.string(), z.string());
30
31
 
31
32
  export const CapabilityNeedModeSchema = z
32
- .enum(["connect", "reconnect", "change"])
33
- .describe("Connect something new, give a connected one a credential that works again, or change a setting on a connected one.");
33
+ .enum(["connect", "reconnect", "change", "remove"])
34
+ .describe(
35
+ "Connect something new, give a connected one a credential that works again, change a setting on a connected one, or remove a connected one.",
36
+ );
34
37
  export type CapabilityNeedMode = z.infer<typeof CapabilityNeedModeSchema>;
35
38
 
36
39
  export const CapabilityNeedSchema = z.object({
@@ -38,7 +41,7 @@ export const CapabilityNeedSchema = z.object({
38
41
  entry: z.string().min(1).describe("The catalog entry, as the catalog names it."),
39
42
  name: z.string().describe("What the catalog calls it, never the agent's spelling."),
40
43
  mode: CapabilityNeedModeSchema,
41
- instance: z.string().optional().describe("The connection a reconnect or a change is about."),
44
+ instance: z.string().optional().describe("The connection a reconnect, a change or a removal is about."),
42
45
  target: z.string().optional().describe("The site, host or address the connection is for, when the entry can hold several."),
43
46
  prefill: FieldValuesSchema.optional().describe(
44
47
  "Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.",
@@ -192,6 +195,10 @@ export const NeedAskSchema = z.discriminatedUnion("kind", [
192
195
  .boolean()
193
196
  .optional()
194
197
  .describe("It is connected, but its credential is being refused: ask for a new one rather than being told to use it."),
198
+ remove: z
199
+ .boolean()
200
+ .optional()
201
+ .describe("Take a connection away: the person removes it in one press on the card, as the Capabilities page's Remove does."),
195
202
  }),
196
203
  SecretNeedSchema,
197
204
  z.object({
@@ -239,7 +246,7 @@ export const NeedAnswerSchema = z.discriminatedUnion("kind", [
239
246
  z.object({ kind: z.literal("decline"), note: z.string().max(500).optional().describe("Why not, passed to the agent.") }),
240
247
  // A connection's yes before its form is saved: moves the need to working, so the board keeps saying so.
241
248
  z.object({ kind: z.literal("accept") }),
242
- // A change to a connected capability's settings, applied as the card showed it.
249
+ // A change to a connected capability's settings, or its removal, applied as the card showed it.
243
250
  z.object({ kind: z.literal("apply") }),
244
251
  z.object({ kind: z.literal("grant"), scope: GrantScopeSchema }),
245
252
  z.object({ kind: z.literal("release") }),
@@ -251,6 +258,15 @@ export const NeedIdParamSchema = z.object({ id: z.string().min(1).describe("Whic
251
258
  export const NeedAnswerInputSchema = NeedIdParamSchema.extend({ answer: NeedAnswerSchema });
252
259
  export const NeedSecretInputSchema = NeedIdParamSchema.extend({
253
260
  value: z.string().min(1).max(64_000).describe("The secret's value. Stored, never echoed, never written into a transcript."),
261
+ // "Keep it on my computer": the value is sealed to that computer's keyring inbox (schemas/keyring.ts) and kept there,
262
+ // and this sandbox holds a pointer to it; nothing is stored here. Absent: stored in the sandbox, as before.
263
+ keepOn: z
264
+ .object({
265
+ device: z.string().min(1).describe("Which computer's keyring keeps it, by its device card."),
266
+ mode: KeyringShareModeSchema.describe("How that keyring answers this sandbox for it."),
267
+ })
268
+ .optional()
269
+ .describe("Keep it in the keyring on one of your computers instead of in the sandbox."),
254
270
  });
255
271
  export const NeedsQuerySchema = z.object({
256
272
  conversationId: z.string().optional().describe("One conversation's needs. Absent is every conversation's."),
@@ -355,7 +371,7 @@ export const OwnerGrantSchema = z.discriminatedUnion("kind", [
355
371
  hooks: z.array(z.string()).describe("Each hook in the set in the daemon's words: when it runs and what it runs."),
356
372
  at: z.number().describe("When it was approved, in milliseconds."),
357
373
  }),
358
- // A workspace extension approved for the powers it declared then (extensions/extension-approvals.ts).
374
+ // A workspace extension approved for the powers it declared then (capabilities/extension-inventory/extension-approvals.ts).
359
375
  z.object({
360
376
  kind: z.literal("extension"),
361
377
  id: z.string().describe("The extension, by its publisher.name id."),
@@ -0,0 +1,110 @@
1
+ import { z } from "zod";
2
+ import { PRESET_GROUPS, type PresetGroup, PresetGroupSchema } from "./compliance.js";
3
+
4
+ // An ORGANISATION'S POLICY as a sandbox holds it (docs/architecture/organisations.md). A sandbox that belongs to an
5
+ // organisation is handed this by the platform it is registered with; a personal sandbox never holds one, and nothing
6
+ // here applies to it. The daemon is the enforcing copy: a section the policy locks is refused at the daemon's own
7
+ // middleware for every caller, the owner and the agent included, and the editor only draws what the daemon says.
8
+
9
+ // The parts of a sandbox an organisation can take out of its members' hands. Each is a section of the editor's sandbox
10
+ // hub (sandboxNav.ts) except `updates`, which is the auto-update switch on Overview. Which daemon routes each one
11
+ // covers is policy/org-locks.ts, beside the refusal that reads it.
12
+ export const ORG_SECTIONS = [
13
+ "models",
14
+ "agent",
15
+ "environment",
16
+ "secrets",
17
+ "data-protection",
18
+ "extensions",
19
+ "access",
20
+ "areas",
21
+ "personas",
22
+ "updates",
23
+ ] as const;
24
+ export const OrgSectionSchema = z.enum(ORG_SECTIONS);
25
+ export type OrgSection = z.infer<typeof OrgSectionSchema>;
26
+
27
+ // `open`: as in a personal sandbox. `locked`: readable, every change refused. `hidden`: locked, and the editor does not
28
+ // show the section at all. Hidden is a fence on what people are shown, not on what the sandbox reads: the agent still
29
+ // uses the secrets of a hidden Secrets section, and a read route answers as it always did.
30
+ export const OrgSectionModeSchema = z.enum(["open", "locked", "hidden"]);
31
+ export type OrgSectionMode = z.infer<typeof OrgSectionModeSchema>;
32
+
33
+ // What an organisation admin edits. A section left out is open, so a policy written before a section existed opens it
34
+ // rather than guessing.
35
+ //
36
+ // `preset` PINS settings rather than locking a page: each named group of the public-office preset (compliance.ts
37
+ // PRESET_GROUPS; which settings a group holds is the daemon's compliance/preset.ts) is applied to what the sandbox READS,
38
+ // whatever its files say, and a change that would undo it is refused. Strings on the wire, not the enum: a group a newer
39
+ // platform names and this daemon does not know is skipped and reported (`unenforced`), never a reason to refuse the
40
+ // whole policy, which would stop every later edition reaching an older daemon. The platform validates the names an
41
+ // admin saves.
42
+ //
43
+ // `membersOnly` makes the organisation's member list the outer fence of every bound sandbox's roster: a person the
44
+ // organisation removes is taken off each sandbox's roster by its daemon at the next fetch, and a grant to someone who
45
+ // is not a member is refused. The sandbox's owner is not on a roster, so this never reaches them.
46
+ //
47
+ // Both are optional rather than defaulted, so every policy written before them (a stored row, a held file, a literal)
48
+ // still reads as the same type: absent is no pin and an open roster.
49
+ export const OrgPolicyRulesSchema = z.object({
50
+ sections: z
51
+ .partialRecord(OrgSectionSchema, OrgSectionModeSchema)
52
+ .default({})
53
+ .describe("How each section is held. A section left out is open."),
54
+ preset: z
55
+ .array(z.string().min(1).max(40))
56
+ .max(40)
57
+ .optional()
58
+ .describe(`Public-office preset groups every bound sandbox is held to (${PRESET_GROUPS.join(", ")}). Unknown names are skipped.`),
59
+ membersOnly: z
60
+ .boolean()
61
+ .optional()
62
+ .describe("Only the organisation's members may be on a bound sandbox's roster; removing someone from the organisation removes them everywhere."),
63
+ });
64
+ export type OrgPolicyRules = z.infer<typeof OrgPolicyRulesSchema>;
65
+
66
+ // The rules as one sandbox receives them: whose they are and which edition. `version` only grows on the platform; the
67
+ // daemon reports the edition it holds back, which is how the organisation's console tells applied from pending.
68
+ // `people` is computed by the platform, never edited: the organisation's members, each as the sha256 of
69
+ // orgPersonPreimage, sent only while `membersOnly` is on, so a daemon can test an address without being handed the
70
+ // staff list in clear. A policy with `membersOnly` and no `people` fences nothing: no list is not an empty list.
71
+ export const OrgPolicySchema = OrgPolicyRulesSchema.extend({
72
+ orgId: z.string().min(1).max(64),
73
+ orgName: z.string().min(1).max(120),
74
+ version: z.number().int().min(1),
75
+ people: z.array(z.string().regex(/^[0-9a-f]{64}$/u)).max(10_000).optional(),
76
+ });
77
+ export type OrgPolicy = z.infer<typeof OrgPolicySchema>;
78
+
79
+ // What the daemon answers the editor about it (GET /org/policy): unbound, or bound with the policy it enforces and
80
+ // when it last heard from the platform about it. `provisional` is a held file that could not be read: every section is
81
+ // held read-only until the platform answers, since a bound sandbox must not open up because its record broke.
82
+ // `unenforced` names preset groups the policy pins that this daemon does not know.
83
+ export const OrgPolicyStateSchema = z.discriminatedUnion("bound", [
84
+ z.object({ bound: z.literal(false) }),
85
+ z.object({
86
+ bound: z.literal(true),
87
+ policy: OrgPolicySchema,
88
+ receivedAt: z.string().describe("When this edition arrived (ISO)."),
89
+ provisional: z.boolean().optional(),
90
+ unenforced: z.array(z.string()).optional(),
91
+ }),
92
+ ]);
93
+ export type OrgPolicyState = z.infer<typeof OrgPolicyStateSchema>;
94
+
95
+ // How a policy holds one section; anything it does not name is open.
96
+ export const orgSectionMode = (policy: Pick<OrgPolicy, "sections"> | undefined, section: OrgSection): OrgSectionMode =>
97
+ policy?.sections[section] ?? "open";
98
+
99
+ // The preset groups a policy pins that this build knows, in the contract's order; none for a personal sandbox. A policy
100
+ // stored before pins existed has no `preset` at all.
101
+ export const orgPinnedGroups = (policy: Partial<Pick<OrgPolicy, "preset">> | undefined): PresetGroup[] =>
102
+ PRESET_GROUPS.filter((group) => policy?.preset?.includes(group) === true);
103
+
104
+ // The names a policy pins that this build does not know.
105
+ export const orgUnknownPins = (policy: Partial<Pick<OrgPolicy, "preset">> | undefined): string[] =>
106
+ (policy?.preset ?? []).filter((name) => !PresetGroupSchema.safeParse(name).success);
107
+
108
+ // What both sides hash (sha256, hex) to name one member of one organisation in `people`: salted with the organisation's
109
+ // id, so the same address in two organisations does not read as the same digest.
110
+ export const orgPersonPreimage = (orgId: string, email: string): string => `intentic-org-person:${orgId}:${email.trim().toLowerCase()}`;
@@ -62,6 +62,9 @@ export const PhoneFactsSchema = z.object({
62
62
  sdk: z.number().int(),
63
63
  // "direct" (downloaded from intentic.dev, can touch and type) or "play" (the store build, which cannot).
64
64
  build: z.string(),
65
+ // What kind of Android device answered: "watch" for Wear OS, which has no folders and no notification access to
66
+ // give; absent from an app older than this field, which is a phone. A string, so a newer app's "tablet" still reads.
67
+ form: z.string().optional(),
65
68
  // The pause tile on the phone; true means every tool refuses and says the person paused it.
66
69
  paused: z.boolean(),
67
70
  // What the person has switched on in Android's own settings for this app.
@@ -0,0 +1,63 @@
1
+ import { DEFAULT_PRIVACY_SHIELD, PRIVACY_PATTERN_MAX, PrivacyShieldPolicySchema, patternProblem } from "./privacy.js";
2
+
3
+ // The owner's own patterns run on every string bound for an untrusted provider, in the daemon's one thread: one that
4
+ // cannot compile, matches empty text or nests a repeat inside a repeat is refused before it is saved.
5
+
6
+ describe(`patternProblem`, () => {
7
+ it(`takes the patterns an ID or a customer number is written with`, () => {
8
+ for (const pattern of [
9
+ String.raw`EMP-\d{6}`,
10
+ String.raw`\b[A-Z]{3}\d{6}\b`,
11
+ String.raw`Customer:\s*(?<value>\d+)`,
12
+ String.raw`(?:\d{3}-){2}\d{4}`,
13
+ String.raw`[A-Z]{2}\d{2}(?:[ -]?\d{4}){3}`,
14
+ String.raw`(?<=Ref\. )[A-Z0-9]+`,
15
+ String.raw`(ab|cd)+`,
16
+ String.raw`[(+*)]+x`,
17
+ ]) {
18
+ expect([pattern, patternProblem(pattern)]).toEqual([pattern, undefined]);
19
+ }
20
+ });
21
+
22
+ it(`refuses one that does not compile, matches empty text or is too long`, () => {
23
+ expect(patternProblem(`(unclosed`)).toBe(`invalid`);
24
+ expect(patternProblem(String.raw`\p{Nope}`)).toBe(`invalid`);
25
+ expect(patternProblem(String.raw`\d*`)).toBe(`empty`);
26
+ expect(patternProblem(`a|`)).toBe(`empty`);
27
+ expect(patternProblem(` `)).toBe(`empty`);
28
+ expect(patternProblem(`a`.repeat(PRIVACY_PATTERN_MAX + 1))).toBe(`tooLong`);
29
+ });
30
+
31
+ it(`refuses a variable repeat inside a variable repeat, the shape that backtracks for minutes`, () => {
32
+ expect(patternProblem(`(a+)+b`)).toBe(`nested`);
33
+ expect(patternProblem(String.raw`(\w+\s?)*!`)).toBe(`nested`);
34
+ expect(patternProblem(String.raw`(?:x(?:\d{2,})?)+y`)).toBe(`nested`);
35
+ expect(patternProblem(String.raw`(?<id>\d+-?){2,}z`)).toBe(`nested`);
36
+ // A fixed count is no variable repeat, inside or out.
37
+ expect(patternProblem(String.raw`(\d{3}-?){3}`)).toBeUndefined();
38
+ expect(patternProblem(String.raw`(\d{3}){2,}`)).toBeUndefined();
39
+ });
40
+ });
41
+
42
+ describe(`PrivacyShieldPolicySchema`, () => {
43
+ it(`reads a policy written before the owner's lists and rules as having none`, () => {
44
+ expect(PrivacyShieldPolicySchema.parse({ mode: `on` })).toMatchObject({ dictionary: { off: [], lists: [] }, rules: [] });
45
+ expect(DEFAULT_PRIVACY_SHIELD.rules).toEqual([]);
46
+ });
47
+
48
+ it(`fills a rule's defaults and keeps a stored pattern whatever a later build judges of it`, () => {
49
+ const parsed = PrivacyShieldPolicySchema.parse({
50
+ rules: [{ id: `r1`, title: `Nested`, class: `identity-document`, match: `pattern`, pattern: `(a+)+` }],
51
+ });
52
+ expect(parsed.rules[0]).toEqual({
53
+ id: `r1`,
54
+ title: `Nested`,
55
+ on: true,
56
+ class: `identity-document`,
57
+ match: `pattern`,
58
+ terms: [],
59
+ pattern: `(a+)+`,
60
+ matchCase: false,
61
+ });
62
+ });
63
+ });