@fungi.computer/oyster 0.1.0 → 0.3.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 (292) hide show
  1. package/README.md +376 -72
  2. package/dist/agent-avatars.d.ts +103 -0
  3. package/dist/agent-avatars.js +66 -0
  4. package/dist/agents.d.ts +15 -4
  5. package/dist/agents.js +43 -4
  6. package/dist/app-builds.d.ts +81 -8
  7. package/dist/app-builds.js +210 -60
  8. package/dist/app-storage.d.ts +3 -3
  9. package/dist/approval-capability.d.ts +5 -0
  10. package/dist/approval-capability.js +46 -0
  11. package/dist/approval-home.d.ts +51 -0
  12. package/dist/approval-home.js +226 -0
  13. package/dist/apps/catalog.d.ts +335 -0
  14. package/dist/apps/catalog.js +46 -0
  15. package/dist/apps/gate.d.ts +26 -12
  16. package/dist/apps/gate.js +48 -22
  17. package/dist/apps/index.d.ts +3 -1
  18. package/dist/apps/index.js +3 -1
  19. package/dist/apps/installation.d.ts +147 -66
  20. package/dist/apps/installation.js +193 -86
  21. package/dist/apps/permissions.d.ts +247 -0
  22. package/dist/apps/permissions.js +255 -0
  23. package/dist/boot-capability-route.d.ts +15 -0
  24. package/dist/boot-capability-route.js +196 -0
  25. package/dist/boot-capability.d.ts +103 -0
  26. package/dist/boot-capability.js +141 -0
  27. package/dist/boot-construction.d.ts +12 -0
  28. package/dist/boot-construction.js +175 -0
  29. package/dist/boot-gate.d.ts +267 -0
  30. package/dist/boot-gate.js +236 -0
  31. package/dist/boot-host-script.d.ts +2 -0
  32. package/dist/boot-host-script.js +1 -0
  33. package/dist/boot-mount.d.ts +21 -0
  34. package/dist/boot-mount.js +65 -0
  35. package/dist/browser/boot.d.ts +73 -0
  36. package/dist/browser/boot.js +502 -0
  37. package/dist/browser/browser.d.ts +104 -0
  38. package/dist/browser/browser.js +374 -0
  39. package/dist/browser/capability-source.d.ts +76 -0
  40. package/dist/browser/capability-source.js +338 -0
  41. package/dist/browser/cdp-client.d.ts +56 -0
  42. package/dist/browser/cdp-client.js +184 -0
  43. package/dist/browser/errors.d.ts +79 -0
  44. package/dist/browser/errors.js +32 -0
  45. package/dist/browser/index.d.ts +16 -0
  46. package/dist/browser/index.js +14 -0
  47. package/dist/browser/lease-registry.d.ts +61 -0
  48. package/dist/browser/lease-registry.js +101 -0
  49. package/dist/browser/operations.d.ts +225 -0
  50. package/dist/browser/operations.js +137 -0
  51. package/dist/browser/page.d.ts +25 -0
  52. package/dist/browser/page.js +172 -0
  53. package/dist/browser/quick-reads.d.ts +29 -0
  54. package/dist/browser/quick-reads.js +160 -0
  55. package/dist/browser/sessions.d.ts +52 -0
  56. package/dist/browser/sessions.js +118 -0
  57. package/dist/browser/url-policy.d.ts +18 -0
  58. package/dist/browser/url-policy.js +58 -0
  59. package/dist/build/app-imports.d.ts +12 -0
  60. package/dist/build/app-imports.js +105 -0
  61. package/dist/build/build-policy.d.ts +5 -3
  62. package/dist/build/build-policy.js +5 -3
  63. package/dist/build/candidate-builder.d.ts +1 -1
  64. package/dist/build/candidate-cli.mjs +8428 -337
  65. package/dist/build/local-build.d.ts +2 -0
  66. package/dist/build/local-build.js +28 -6
  67. package/dist/build/snapshot-package-store.d.ts +1 -1
  68. package/dist/build/snapshot-package-store.js +7 -2
  69. package/dist/build/workspace-build.d.ts +92 -5
  70. package/dist/build/workspace-build.js +93 -19
  71. package/dist/build-egress.js +4 -4
  72. package/dist/build.d.ts +1 -1
  73. package/dist/build.js +1 -1
  74. package/dist/catalog-frame.js +1 -1
  75. package/dist/cloudflare.d.ts +13 -3
  76. package/dist/cloudflare.js +106 -8
  77. package/dist/frame-binding-wire-script.js +1 -1
  78. package/dist/frame-binding-wire.d.ts +1 -1
  79. package/dist/frame-binding-wire.js +5 -3
  80. package/dist/git-auth.d.ts +0 -2
  81. package/dist/git-auth.js +0 -2
  82. package/dist/git-journal.js +4 -4
  83. package/dist/git.d.ts +0 -1
  84. package/dist/git.js +0 -2
  85. package/dist/host.d.ts +39 -13
  86. package/dist/host.js +268 -142
  87. package/dist/index.d.ts +26 -15
  88. package/dist/index.js +40 -16
  89. package/dist/install-release-cli.d.ts +2 -0
  90. package/dist/install-release-cli.js +46 -0
  91. package/dist/installation-client.d.ts +43 -0
  92. package/dist/installation-client.js +178 -0
  93. package/dist/installation-execution.d.ts +11 -0
  94. package/dist/installation-execution.js +184 -0
  95. package/dist/installation-operations.d.ts +1437 -0
  96. package/dist/installation-operations.js +664 -0
  97. package/dist/installation-store-generation.d.ts +3 -0
  98. package/dist/installation-store-generation.js +16 -0
  99. package/dist/instance-id.d.ts +10 -0
  100. package/dist/instance-id.js +10 -0
  101. package/dist/instance-refusal.d.ts +11 -0
  102. package/dist/instance-refusal.js +21 -0
  103. package/dist/internal/delivery.js +4 -2
  104. package/dist/internal/host-gate.js +7 -1
  105. package/dist/internal/registry-installations.js +4 -4
  106. package/dist/keys.d.ts +5 -1
  107. package/dist/keys.js +10 -2
  108. package/dist/machine-link/authority.d.ts +89 -0
  109. package/dist/machine-link/authority.js +37 -0
  110. package/dist/machine-link/client.d.ts +66 -0
  111. package/dist/machine-link/client.js +94 -0
  112. package/dist/machine-link/delivery.d.ts +18 -0
  113. package/dist/machine-link/delivery.js +55 -0
  114. package/dist/machine-link/enrollment.d.ts +111 -0
  115. package/dist/machine-link/enrollment.js +286 -0
  116. package/dist/machine-link/fences.d.ts +37 -0
  117. package/dist/machine-link/fences.js +166 -0
  118. package/dist/machine-link/home.d.ts +20 -0
  119. package/dist/machine-link/home.js +24 -0
  120. package/dist/machine-link/host.d.ts +43 -0
  121. package/dist/machine-link/host.js +189 -0
  122. package/dist/machine-link/index.d.ts +15 -0
  123. package/dist/machine-link/index.js +15 -0
  124. package/dist/machine-link/link.d.ts +48 -0
  125. package/dist/machine-link/link.js +223 -0
  126. package/dist/machine-link/presence.d.ts +10 -0
  127. package/dist/machine-link/presence.js +25 -0
  128. package/dist/machine-link/workers.d.ts +6 -0
  129. package/dist/machine-link/workers.js +6 -0
  130. package/dist/mcp-owner.d.ts +2 -2
  131. package/dist/mcp-owner.js +41 -33
  132. package/dist/mcp.d.ts +15 -3
  133. package/dist/mcp.js +146 -25
  134. package/dist/pack.d.ts +14 -3
  135. package/dist/pack.js +44 -19
  136. package/dist/page/app-access.d.ts +7 -0
  137. package/dist/page/app-access.js +75 -0
  138. package/dist/page/boot-host.js +57 -0
  139. package/dist/page/capabilities.d.ts +3 -3
  140. package/dist/page/capabilities.js +16 -12
  141. package/dist/page/computer-handles.d.ts +3 -3
  142. package/dist/page/computer-target.d.ts +3 -0
  143. package/dist/page/computer-target.js +9 -0
  144. package/dist/page/mount.d.ts +23 -7
  145. package/dist/page/mount.js +152 -16
  146. package/dist/page-assets.d.ts +22 -0
  147. package/dist/page-assets.js +28 -0
  148. package/dist/page-script.d.ts +3 -0
  149. package/dist/page-script.js +5 -4
  150. package/dist/release.d.ts +13 -6
  151. package/dist/release.js +10 -7
  152. package/dist/root-data.d.ts +16 -6
  153. package/dist/root-data.js +2 -2
  154. package/dist/source/index.d.ts +1 -1
  155. package/dist/source/team-source.d.ts +20 -8
  156. package/dist/source/team-source.js +39 -10
  157. package/dist/source/template-seed.d.ts +2 -2
  158. package/dist/standalone/agent-mail.d.ts +24 -0
  159. package/dist/standalone/agent-mail.js +62 -0
  160. package/dist/standalone/agent-store.d.ts +9 -0
  161. package/dist/standalone/agent-store.js +30 -0
  162. package/dist/standalone/agents.d.ts +78 -12
  163. package/dist/standalone/agents.js +470 -59
  164. package/dist/standalone/app-builds-binding.d.ts +20 -2
  165. package/dist/standalone/app-builds-binding.js +151 -27
  166. package/dist/standalone/app-set.d.ts +5 -2
  167. package/dist/standalone/app-set.js +174 -143
  168. package/dist/standalone/browser.d.ts +11 -0
  169. package/dist/standalone/browser.js +72 -0
  170. package/dist/standalone/builds.d.ts +13 -4
  171. package/dist/standalone/builds.js +28 -44
  172. package/dist/standalone/computer-egress.d.ts +2 -0
  173. package/dist/standalone/computer-egress.js +13 -4
  174. package/dist/standalone/computer-fleet.d.ts +43 -0
  175. package/dist/standalone/computer-fleet.js +200 -0
  176. package/dist/standalone/computer.d.ts +44 -9
  177. package/dist/standalone/computer.js +132 -25
  178. package/dist/standalone/install-apps-cli.js +43 -6
  179. package/dist/standalone/instance.d.ts +2 -2
  180. package/dist/standalone/instance.js +8 -11
  181. package/dist/standalone/machine-link.d.ts +354 -0
  182. package/dist/standalone/machine-link.js +368 -0
  183. package/dist/standalone/media.d.ts +2 -2
  184. package/dist/standalone/media.js +19 -16
  185. package/dist/standalone/preview.d.ts +2 -2
  186. package/dist/standalone/preview.js +39 -12
  187. package/dist/standalone/registry.d.ts +149 -9
  188. package/dist/standalone/registry.js +299 -8
  189. package/dist/standalone/worker.d.ts +19 -5
  190. package/dist/standalone/worker.js +90 -54
  191. package/dist/standalone/workflow-binding.d.ts +28 -32
  192. package/dist/standalone/workflow-binding.js +20 -20
  193. package/dist/standalone/workflow-control.d.ts +4 -0
  194. package/dist/standalone/workflow-control.js +40 -0
  195. package/dist/standalone/workflow-events.d.ts +2 -2
  196. package/dist/standalone/workflow-events.js +9 -10
  197. package/dist/standalone/workflow-git-capture.js +4 -3
  198. package/dist/standalone/workflow-host.d.ts +177 -152
  199. package/dist/standalone/workflow-host.js +137 -30
  200. package/dist/standalone/workflow-node-capture.js +11 -4
  201. package/dist/standalone/workflow-schedules.d.ts +5 -2
  202. package/dist/standalone/workflow-schedules.js +9 -10
  203. package/dist/standalone/workflow-trigger-host.d.ts +88 -6
  204. package/dist/standalone/workflow-trigger-host.js +2 -2
  205. package/dist/standalone/workflow-webhooks.d.ts +22 -0
  206. package/dist/standalone/workflow-webhooks.js +39 -0
  207. package/dist/standalone/workflows.d.ts +203 -50
  208. package/dist/standalone/workflows.js +198 -39
  209. package/dist/storage-frame.d.ts +6 -6
  210. package/dist/testing/capabilities.js +14 -3
  211. package/dist/testing/installation.js +26 -2
  212. package/dist/testing/worker.js +1 -1
  213. package/dist/testing/workers-runtime.d.ts +8 -2
  214. package/dist/testing/workers-runtime.js +20 -2
  215. package/dist/webhook-abuse.d.ts +25 -0
  216. package/dist/webhook-abuse.js +54 -0
  217. package/dist/webhook-custody.d.ts +144 -0
  218. package/dist/webhook-custody.js +222 -0
  219. package/dist/webhook-ingress.d.ts +36 -0
  220. package/dist/webhook-ingress.js +146 -0
  221. package/dist/webhooks.d.ts +111 -0
  222. package/dist/webhooks.js +189 -0
  223. package/dist/workers-git.d.ts +12 -17
  224. package/dist/workers-git.js +60 -55
  225. package/dist/workers.d.ts +224 -13
  226. package/dist/workers.js +351 -51
  227. package/dist/workflow-agent-authority.d.ts +8 -0
  228. package/dist/workflow-agent-authority.js +42 -0
  229. package/dist/workflows.d.ts +66 -0
  230. package/dist/workflows.js +97 -0
  231. package/docs/api/README.md +63 -37
  232. package/docs/api/agents.md +17 -0
  233. package/docs/api/app-storage.md +1 -1
  234. package/docs/api/apps.md +1394 -543
  235. package/docs/api/browser/boot.md +132 -0
  236. package/docs/api/browser.md +1440 -0
  237. package/docs/api/build-contract.md +38 -25
  238. package/docs/api/build.md +263 -84
  239. package/docs/api/cloudflare.md +93 -7
  240. package/docs/api/git.md +3 -13
  241. package/docs/api/host.md +944 -118
  242. package/docs/api/index.md +320 -18
  243. package/docs/api/keys.md +24 -1
  244. package/docs/api/mcp.md +59 -12
  245. package/docs/api/modules.md +33 -40
  246. package/docs/api/pack.md +57 -8
  247. package/docs/api/page/mount.md +92 -4
  248. package/docs/api/workers-git.md +103 -24
  249. package/docs/api/workers.md +1581 -244
  250. package/docs/site/agent-quickstart.mdx +28 -14
  251. package/examples/standalone/worker.ts +3 -1
  252. package/examples/standalone/wrangler.jsonc +2 -0
  253. package/package.json +73 -69
  254. package/dist/installation-consent.d.ts +0 -79
  255. package/dist/installation-consent.js +0 -82
  256. package/dist/shell/app-directory.d.ts +0 -542
  257. package/dist/shell/app-directory.js +0 -199
  258. package/dist/shell/browser-actions.d.ts +0 -7
  259. package/dist/shell/browser-actions.js +0 -56
  260. package/dist/shell/command-host.d.ts +0 -112
  261. package/dist/shell/command-host.js +0 -502
  262. package/dist/shell/commands.d.ts +0 -25
  263. package/dist/shell/commands.js +0 -78
  264. package/dist/shell/index.d.ts +0 -8
  265. package/dist/shell/index.js +0 -8
  266. package/dist/shell/keys.d.ts +0 -41
  267. package/dist/shell/keys.js +0 -134
  268. package/dist/shell/shell-module.d.ts +0 -55
  269. package/dist/shell/shell-module.js +0 -86
  270. package/dist/shell/window-manager.d.ts +0 -124
  271. package/dist/shell/window-manager.js +0 -711
  272. package/dist/shell/workbench-contract.d.ts +0 -68
  273. package/dist/shell/workbench-directory.d.ts +0 -332
  274. package/dist/shell/workbench-directory.js +0 -35
  275. package/dist/shell/workbench-host-types.d.ts +0 -96
  276. package/dist/shell/workbench-host-types.js +0 -1
  277. package/dist/shell/workbench-host.d.ts +0 -34
  278. package/dist/shell/workbench-host.js +0 -347
  279. package/dist/shell/workbench-model.d.ts +0 -192
  280. package/dist/shell/workbench-model.js +0 -53
  281. package/dist/shell/workbench.d.ts +0 -4
  282. package/dist/shell/workbench.js +0 -2
  283. package/docs/api/shell/app-directory.md +0 -599
  284. package/docs/api/shell/command-host.md +0 -224
  285. package/docs/api/shell/commands.md +0 -197
  286. package/docs/api/shell/keys.md +0 -183
  287. package/docs/api/shell/shell-module.md +0 -204
  288. package/docs/api/shell/window-manager.md +0 -267
  289. package/docs/api/shell/workbench-directory.md +0 -119
  290. package/docs/api/shell/workbench.md +0 -205
  291. package/docs/api/shell.md +0 -1915
  292. /package/dist/{shell/workbench-contract.js → page/boot-host.d.ts} +0 -0
package/README.md CHANGED
@@ -37,6 +37,9 @@ Computer.
37
37
  - [Prints](https://github.com/fungi-computer/prints) generates images and video
38
38
  with Workers AI.
39
39
 
40
+ Stage is not published or included in the 0.3 starter set. It appears as a
41
+ glowing ember swarm and directs animated scenes and cards as an agent answers.
42
+
40
43
  <table>
41
44
  <tr>
42
45
  <td width="50%"><img alt="Bonsai's koi-pond repository: a network graph with one lane per agent fork, its three open Changes and the live activity feed" src="https://raw.githubusercontent.com/fungi-computer/oyster/main/docs/media/bonsai-koi-pond.png"><br><b>Bonsai</b>: a repository three agents build together</td>
@@ -61,25 +64,38 @@ Workers AI, Workflows and media.
61
64
 
62
65
  You need:
63
66
 
64
- - a Cloudflare account on Workers Paid, with Containers, Artifacts, Workers AI
65
- and Workflows enabled;
67
+ - a Cloudflare account on Workers Paid, with Containers, Artifacts, Workers AI,
68
+ Workflows and Browser Rendering (now called Browser Run) enabled;
66
69
  - Docker, which Wrangler uses to build the Computer's container image;
67
- - Node 24, pnpm (npm works the same way) and Git.
70
+ - Node 24, pnpm 11 or newer and Git.
68
71
 
69
72
  The first install takes about 15 to 20 minutes, most of it the seven App builds
70
73
  on the Computer, one after another. Start a project, add Oyster, its Computer
71
- and Wrangler, and copy the example Worker out of the package:
74
+ and Wrangler, and copy the example Worker out of the package. The local pnpm
75
+ policy allows esbuild and workerd to install their native binaries and disables
76
+ unneeded optional dependency scripts, so a fresh install exits successfully:
72
77
 
73
78
  ```sh
74
79
  mkdir my-oyster && cd my-oyster
75
- pnpm add @fungi.computer/oyster @fungi.computer/computer
80
+ cat > pnpm-workspace.yaml <<'YAML'
81
+ allowBuilds:
82
+ "@google/genai": false
83
+ "@mongodb-js/zstd": false
84
+ esbuild: true
85
+ exifreader: false
86
+ node-liblzma: false
87
+ protobufjs: false
88
+ workerd: true
89
+ YAML
90
+ pnpm add @fungi.computer/oyster @fungi.computer/computer react@^19.2.0 react-dom@^19.2.0
76
91
  pnpm add -D wrangler
77
92
  cp node_modules/@fungi.computer/oyster/examples/standalone/{worker.ts,wrangler.jsonc} .
78
93
  ```
79
94
 
80
- pnpm 11 may end an `add` with `ERR_PNPM_IGNORED_BUILDS` for packages such as
81
- `esbuild` or `workerd`; they are installed, and Oyster needs none of their
82
- install scripts.
95
+ If `pnpm add` reports `ERR_PNPM_IGNORED_BUILDS`, check that the policy file
96
+ above exists before installing. If it names a new dependency not listed there,
97
+ stop and review that package before allowing its scripts. Do not treat exit 1 as
98
+ success.
83
99
 
84
100
  `worker.ts` re-exports the whole host:
85
101
 
@@ -90,10 +106,15 @@ export { default } from "@fungi.computer/oyster/standalone";
90
106
 
91
107
  `wrangler.jsonc` names the Worker `oyster`, its Release bucket `oyster-releases`
92
108
  and its Artifacts namespace `oyster`, and runs the Computer from
93
- `node_modules/@fungi.computer/computer/Dockerfile`. Create the bucket and
94
- deploy. Your Worker's address is `https://oyster.<your-subdomain>.workers.dev`;
95
- your workers.dev subdomain is on the Workers & Pages overview in the Cloudflare
96
- dashboard.
109
+ `node_modules/@fungi.computer/computer/Dockerfile`. Its
110
+ `"browser": { "binding": "BROWSER" }` enables Cloudflare Browser Rendering for
111
+ the zero-grant UI boot check and for Agents' browser tool. Without the binding
112
+ the UI of a Release is reported `not_checked` with
113
+ `code: "boot_browser_binding_missing"`; the install still proceeds.
114
+
115
+ Create the bucket and deploy. Your Worker's address is
116
+ `https://oyster.<your-subdomain>.workers.dev`; your workers.dev subdomain is on
117
+ the Workers & Pages overview in the Cloudflare dashboard.
97
118
 
98
119
  ```sh
99
120
  npx wrangler login
@@ -105,7 +126,56 @@ npx wrangler deploy --var OYSTER_ORIGIN:$OYSTER_URL
105
126
  Set `OYSTER_ORIGIN` in `wrangler.jsonc` instead if you deploy again later; every
106
127
  deploy replaces the Worker's variables.
107
128
 
108
- Choose an owner key and store it as a secret:
129
+ ### API-token permissions and teardown
130
+
131
+ For an API token instead of `wrangler login`, load `CLOUDFLARE_API_TOKEN` into
132
+ your shell without committing it. Set `CLOUDFLARE_ACCOUNT_ID` (or `account_id`
133
+ in `wrangler.jsonc`) explicitly, and skip the login command. Use Wrangler
134
+ 4.135.0 or newer; record `npx wrangler --version` for deployment and teardown.
135
+
136
+ The [Cloudflare API specification](https://github.com/cloudflare/api-schemas)
137
+ names these account-scoped permission groups for the deployment and resource
138
+ deletion operations:
139
+
140
+ | Permission group | Used for |
141
+ | ------------------------ | ----------------------------------------------------------- |
142
+ | Workers Scripts Write | Deploy, secrets, Worker deletion and Workflow deletion |
143
+ | Workers R2 Storage Write | Release bucket creation, object cleanup and bucket deletion |
144
+ | Workers Containers Write | Computer container application management |
145
+ | Artifacts Edit | Artifacts namespace and repository management |
146
+
147
+ Browser Rendering through the Workers `BROWSER` binding
148
+ [needs no additional API-token permission](https://developers.cloudflare.com/browser-run/quick-actions/).
149
+ The separate Browser Rendering Write permission is for its REST API, which this
150
+ quick start does not use. Workers Paid entitlement is still required.
151
+
152
+ For stream-rehearsal diagnostics, include **Browser Rendering (Read)** in the
153
+ account-scoped token checklist: it permits REST session inspection, separate
154
+ from the `BROWSER` binding's boot operation. Use the documented
155
+ `/browser-rendering/devtools/session` list and `devtools/session/{session_id}`
156
+ details operations; a session's `endTime`, not its connection end time, reports
157
+ closure. A plain 404 from another route does not establish a missing permission.
158
+ If inspection is unavailable, record **UNAVAILABLE**, not confirmed cleanup. Do
159
+ not broaden an existing token automatically.
160
+
161
+ With an explicit account ID, user/account discovery is unnecessary. Do not add
162
+ Workers KV Storage Read, User Details Read or Memberships Read just for that
163
+ discovery. Wrangler 4.135.0 treats denied legacy-Sites KV inspection during
164
+ `wrangler delete` as a warning, not a reason to broaden this token. AI Gateway
165
+ Read/Edit permissions are not needed for this deploy or a teardown before any
166
+ media ran; inspect that additional resource if media has created a gateway.
167
+
168
+ `npx wrangler delete` deletes the Worker, not the entire Oyster. For a throwaway
169
+ deployment, also empty and delete the Release bucket, delete the Computer's
170
+ container application, delete the Workflow, and delete the Artifacts
171
+ repositories and namespace. Use the configured resource names and confirm each
172
+ is gone through Cloudflare's dashboard or API. Capture teardown errors and the
173
+ Wrangler version rather than silently adding unrelated token permissions.
174
+
175
+ ### Set the Oyster owner key
176
+
177
+ Choose an owner key (separate from the Cloudflare API token) and store it as a
178
+ secret:
109
179
 
110
180
  ```sh
111
181
  export OYSTER_OWNER_KEY=$(openssl rand -hex 24)
@@ -532,13 +602,16 @@ returns an `AdmittedGit`; the door forwards reads to `.read` and pushes to
532
602
  builds these ports from `host.auth`, `host.installations` and `host.git`, so
533
603
  most hosts set `host.git` and never call `createGitDoor` directly.
534
604
 
535
- A host that mints Workflow Git passes supplies `workflowGitPass` in
536
- `oysterGitRoot` or `oysterWorkerGit` options. A pass travels only as an
537
- `Authorization: Bearer` header starting with `workflowGitPassPrefix` (`wgp.`) on
538
- the ordinary credential-free door URL; it is never part of a URL. That header is
539
- admitted only by the port, for one repository and scope, as the arming member
540
- under the Workflow's branch grant, and never as a session or key. Pushes outside
541
- those branches are refused before the App's push policy runs.
605
+ A host whose Workflow run owner issues Workflow Git credentials supplies
606
+ `workflowGitPass` in `oysterGitRoot` or `oysterWorkerGit` options. The run owner
607
+ issues one credential per native command of an admitted Git step and gives it
608
+ only to that command's Git environment. It travels only as an
609
+ `Authorization: Bearer` header starting with Braid's `workflowGitPassPrefix`
610
+ (`wgp.`) on the ordinary credential-free door URL; it is never part of a URL.
611
+ That header is admitted only by the port, for one repository and scope, as the
612
+ arming member under the Workflow's branch grant, and never as a session or key.
613
+ It stops working when its command closes or its run ends. Pushes outside those
614
+ branches are refused before the App's push policy runs.
542
615
 
543
616
  ### Provide App capabilities
544
617
 
@@ -684,6 +757,33 @@ disconnects one. These routes are owner-direct and reject an App installation
684
757
  header. A server that is down or refuses the token is left out of that run and
685
758
  the others stay available. OAuth sign-in is not offered yet.
686
759
 
760
+ ### Give an Agent a browser
761
+
762
+ Declare a Cloudflare Browser Rendering binding and every Agent gets a
763
+ `cloudBrowser` tool for public pages: `open` a driven session (optionally at a
764
+ URL), `navigate`, read it with `snapshot` (a bounded accessibility-tree
765
+ summary), `click`, `type`, `press`, `scroll`, and `close` it.
766
+
767
+ ```jsonc
768
+ // wrangler.jsonc
769
+ "browser": { "binding": "BROWSER" }
770
+ ```
771
+
772
+ Every URL the Agent supplies must be http(s), carry no credentials and resolve
773
+ to a public address; anything else is refused before the browser sees it. A
774
+ standalone Oyster allows two open sessions at once, closes one after 5 idle
775
+ minutes or 30 minutes in all, and is billed by Cloudflare to the owner's
776
+ account. Without the binding the tool is absent.
777
+
778
+ Hosts compose the same module, `@fungi.computer/oyster/browser`, behind one port
779
+ (`BrowserHost`): a provider (`bindingSessions(env.BROWSER)`, or
780
+ `browserRunSessions` with a Browser Run account token, which also enables the
781
+ `markdown`/`links`/`scrape`/`extract` Quick Reads), a required metering owner
782
+ and an event sink. `createBrowser` owns lease ownership, the URL policy, reaping
783
+ and billing order; `browserCapabilitySource` is the Agent tool. The Agent never
784
+ reaches raw CDP or runs script in the page. See the
785
+ [API reference](docs/api/browser.md).
786
+
687
787
  ### Pack an App
688
788
 
689
789
  `packRelease` composes `oyster/build`'s source capture, candidate preparer,
@@ -748,50 +848,35 @@ install-or-select owner installs it (201) or, when the installation already
748
848
  exists, switches it to that Release (200); the App keeps its state, and every
749
849
  other refusal is returned unchanged.
750
850
 
851
+ Pending installation commands retain their exact baseline, reduced plan and
852
+ preparation evidence for recovery. Settlement replaces those payloads with the
853
+ request's input digest, correlation, settlement time and recorded outcome. The
854
+ existing Registry alarm deletes superseded settled journal entries in bounded
855
+ batches after 90 days from their own settlement. The latest settled entry per
856
+ effective installation is always pinned, including while newer work is pending.
857
+ Indexed durable deadlines re-arm the same alarm when idle; pending recovery is
858
+ never age-deleted. Expiry removes App journal history, not canonical
859
+ installations, Releases, artifact bytes or Watchdog's private job history. An
860
+ expired operation is no longer readable; re-admitting its ID is refused as
861
+ `unavailable` when Watchdog still retains that job, rather than creating pending
862
+ work that cannot execute. A new intent needs a new request ID.
863
+
864
+ A retained successful receipt keeps its original generation, instance and
865
+ authority-version reference. Its `installation` is resolved only from the exact
866
+ current canonical selection; after removal, generation change or selection drift
867
+ it is `null`, while the original success remains recorded. Completion helpers
868
+ return `409 installation_result_unavailable` in that case, and the standalone
869
+ installer does not approve defaults from historical success. Settled history is
870
+ never an executable recovery command. Stores containing superseded preview
871
+ journal shapes require separately authorized current-contract republishing, not
872
+ an in-place conversion.
873
+
751
874
  ## Build a shell
752
875
 
753
- `@fungi.computer/oyster/shell` supplies the client side of hosting Apps. It can
754
- be imported independently of the Workers server and Node packing helpers. The
755
- same exports are available through focused entries:
756
-
757
- | Entry | Contract |
758
- | ------------------------ | ----------------------------------------------------------------------------------- |
759
- | `./shell/workbench` | Workbench host contract, shared child-window host, Gill layout and arranger shapes. |
760
- | `./shell/command-host` | A Whistle graph that admits mounted commands against verified Release declarations. |
761
- | `./shell/keys` | DOM key dispatch and shared chord matching. |
762
- | `./shell/commands` | Window, App picker and command palette declarations over host callbacks. |
763
- | `./shell/window-manager` | Window generations, visibility, navigation, cancellation and disposal. |
764
- | `./shell/app-directory` | App addresses, reusable directory rows and catalog metadata. |
765
-
766
- `./shell/shell-module` is the contract between a host and the shell it runs, and
767
- is imported on its own rather than through `./shell`. A shell Release exports
768
- `mountShell(root, input)` (`MountShell`); `input` (`ShellMountInput`) carries
769
- the workbench handle, the shell installation's `AppStorage` and
770
- `methods.call(method, input)` for its declared backend methods, which takes the
771
- input as JSON text and resolves with the decoded JSON result.
772
- `importShellModule(moduleUrl, hostOrigin)` imports the picked shell's module
773
- from the host's own origin, checks that it exports `mountShell`, and rejects
774
- with `ShellModuleError` otherwise. The host never bundles a shell.
775
-
776
- Supply your own mount adapter to `createWindowManager`. It receives the
777
- container, launch descriptor, cancellation signal and navigation callbacks; its
778
- returned handle owns visibility, reconfiguration and disposal. The manager
779
- disposes stale mounts and serializes changes to each window. It neither picks
780
- Apps nor grants capabilities.
781
-
782
- `createAppSurfaceCommandHost` owns command contribution and focus. Give
783
- `mount(id, declarations)` the verified Release's declarations and pass the
784
- returned handlers to Agar's `connectAppHost`. A mounted App cannot introduce an
785
- undeclared command or claim the reserved shell chord. Dispose its command mount
786
- when its iframe closes, and dispose the command host with the shell.
787
- `installShellKeys` attaches DOM listeners only when called; its return value
788
- removes them. Terminal shells can use the command and window owners without a
789
- DOM.
790
-
791
- Directory metadata is not launch authority. A host extends the public row schema
792
- with its own permission model and admits each mount itself. Fungi's Team
793
- directory, Team settings capability and signed Hub/host protocol live in its
794
- private consumers; the public shell imports none of them.
876
+ The shell client lives in its own package,
877
+ [`@fungi.computer/nacre`](../nacre/README.md): workbench host, command host,
878
+ keys, commands, window manager, App directory rows and the shell-module
879
+ contract. Oyster's page uses it; a shell imports it directly.
795
880
 
796
881
  `@fungi.computer/oyster/page` mounts one installed App's page and connects its
797
882
  sandboxed frame with that installation's admitted slots. That page is only the
@@ -894,6 +979,57 @@ table names.
894
979
 
895
980
  ### Durable Object composition
896
981
 
982
+ Activation is host-gated before selection writes or aborting the old version.
983
+ The candidate backend loads in a unique isolate over one bounded disposable
984
+ sibling facet, `candidate`, with denied test bindings and no outbound network.
985
+ The lifecycle supplies its already-read prior state schema: first activation
986
+ uses empty SQLite; an initialized Instance requires the native facets API to
987
+ copy `app` into `candidate` before construction. A host without clone support
988
+ refuses that check rather than substituting fresh storage. Cleanup aborts and
989
+ deletes the sibling, and Instance purge also deletes that known name. The copy
990
+ is a construction test, not a backup or data rollback. It cannot become the live
991
+ facet and contains no supervisor selection, authority, Team App storage, or live
992
+ bindings. Native copying and isolation require the qualified runtime proof; the
993
+ fresh-SQLite Node adapter is not that proof. The host also checks the immutable
994
+ UI entry in a zero-grant browser session (`reportBootPage` with
995
+ `checkBootPage`): the page opens the pinned `/_boot/<release>/` route on the
996
+ host's own origin (https, or http only on loopback) with a short-lived
997
+ capability, and every request to any other origin is blocked. The UI result is
998
+ reported, never authority: a page that did not mount is `failed`, and a missing
999
+ browser, a timeout, unconfirmed cleanup or an unavailable runner is
1000
+ `not_checked`. An App manifest, callback or healthy flag cannot supply the
1001
+ verdict. This is construction and UI-mount qualification, not an HTTP request
1002
+ smoke, execution against live storage, or a data rewind.
1003
+
1004
+ Each check owns one provider session and confirms it closed before reporting;
1005
+ unconfirmed closure reports `boot_cleanup_failed`, and Browser Run's
1006
+ `keep_alive` remains the backstop.
1007
+
1008
+ `pnpm run test:recovery-copy:celld` exercises the actual construction helper
1009
+ with synthetic App state, a successful copy and a constructor refusal. It checks
1010
+ live state preservation, denied bindings/outbound access, supervisor separation
1011
+ and empty storage on reopening the cleaned sibling. It does not prove physical
1012
+ file/stream deletion, restart recovery, or owner selection.
1013
+
1014
+ The disposable both-host proof is
1015
+ `bash apps/hub/scripts/check-app-boot-native.sh` from the repository root, after
1016
+ the affected dependency and Oyster builds. It requires Linux network namespaces,
1017
+ passwordless local `sudo`, the pinned celld binary and provisioned Playwright
1018
+ Chromium. Inside an unnamed private network it drives a local Chromium through
1019
+ the same `checkBootPage` both hosts use (a test-only provider in Browser Run's
1020
+ devtools API shape) and fresh local fixture storage; it does not install a
1021
+ service, contact a live host or exercise an App's HTTP handler. It checks real
1022
+ construction and ordinary Agar UI mounting through each host's `/_boot/` route,
1023
+ native facet cleanup, closed browser sessions, retained selections on refusal,
1024
+ forged verdicts, wrapper tampering and browser unavailability.
1025
+
1026
+ The Hub's real Team default-provisioning selection path is qualified separately
1027
+ with `bash apps/hub/scripts/check-app-boot-native.sh --publication`, under the
1028
+ same prerequisites. Its five host-dependent cases use fresh native resources,
1029
+ the maintained artifact writer/readback and immutable R2 object versions. Other
1030
+ domain-test metadata seeds are not host proof. This leg qualifies selection
1031
+ admission, not downstream projection delivery or deployment.
1032
+
897
1033
  `createCloudflareAppInstance` keeps supervisor state in a SQLite Durable Object
898
1034
  and runs the backend in a named `app` facet loaded through the Worker Loader.
899
1035
  Supervisor metadata and the backend's database are separate, and a new Release
@@ -1050,14 +1186,133 @@ capture does not rebase paths to the App directory. The nearest `package.json`
1050
1186
  above the entry supplies `fungi.app`, plus `fungi.backendDefinition` for an API
1051
1187
  derived from the compiled backend.
1052
1188
 
1189
+ An App owns its recipe in `package.json#fungi.build`, alongside its existing
1190
+ page policy (`react`, `stylesheets`, and dependency build declarations). Recipe
1191
+ paths in that metadata are relative to the App's own directory. The Node-only
1192
+ `readBuildManifest(packageJson)` decodes that one strict contract;
1193
+ `appBuildRecipe(manifest, directory)` roots the recipe for a captured checkout,
1194
+ or accepts `""` for an App at the repository root. Dependency-only metadata is
1195
+ still valid but cannot supply an App recipe. Unknown build fields are refused.
1196
+
1197
+ The starter installer reads metadata with `git show` from the selected commit,
1198
+ not from dirty checkout files. Its roster owns only App identity and source
1199
+ locations. The scaffold's `npm run build` deliberately reads saved working
1200
+ metadata before capturing those same saved files. Its template declares the
1201
+ ordinary npm recipe as `kind: "source-html"`, including the backend entry and
1202
+ asset bounds; the command does not carry another copy of that recipe.
1203
+
1204
+ **Breaking recipe contract:** `@fungi.computer/oyster/standalone/app-set` no
1205
+ longer exposes `StarterApp.recipe` or recipe values on `standaloneAppSet`.
1206
+ Callers read the selected source's `package.json` with `readBuildManifest` and
1207
+ materialize it with `appBuildRecipe`, both exported from
1208
+ `@fungi.computer/oyster/build`. The workspace metadata policy advances from
1209
+ `package-json-fungi-build-v3` to `package-json-fungi-build-v4`; builders refuse
1210
+ missing or superseded policy metadata rather than guessing a recipe.
1211
+
1212
+ **Breaking repository-registration contract:** the public
1213
+ `PUT /api/apps/:appId/build-source` route, which accepted an App identity in the
1214
+ path and caller-supplied installation, repository, and recipe fields, has been
1215
+ removed. Use repo-only `APPS.register({ repo })`, exposed by Bonsai as
1216
+ `apps.register({ repo })`. The host reads App identity and `fungi.build` recipe
1217
+ from the immutable main commit's root `package.json` through the shared manifest
1218
+ reader; callers cannot override identity or recipe. An existing repository
1219
+ registration retains its App identity on replay. Registration returns the App ID
1220
+ and exact commit without starting a build or installing a Release. No
1221
+ compatibility route or shim remains.
1222
+
1223
+ ### Local Release installation
1224
+
1225
+ ```sh
1226
+ export OYSTER_URL=https://your-oyster.example
1227
+ export OYSTER_OWNER_KEY='<your owner key>'
1228
+ oyster install ./dist --installation-id my-app
1229
+ # Or name the same Release's manifest:
1230
+ oyster install ./dist/release.json --installation-id my-app
1231
+ ```
1232
+
1233
+ The CLI delegates to `installRelease` from `@fungi.computer/oyster/pack`, using
1234
+ the same uploader as programmatic clients. Artifacts must upload before
1235
+ `POST /api/installations`. Upload refusal, missing files, and non-OK
1236
+ installation responses exit nonzero. No key is discovered from your home
1237
+ directory, and this command does not provision a Computer or deploy an Oyster.
1238
+
1239
+ For repository authoring, use Bonsai's repository Settings **Make this an App**
1240
+ (or `apps.register({ repo })`). Commit the root `package.json#fungi.build`
1241
+ recipe first; dirty metadata is not registration input. The readonly App ID and
1242
+ editable revision/name come from registration or the latest recorded build.
1243
+ Status can be read using `APPS.buildStatus({ repo })` in the captured Git
1244
+ installation; Settings uses that selector after reload. No job returns `null`.
1245
+ Workflow callers use `APPS.buildStatus({ jobId })` for the particular job they
1246
+ started.
1247
+
1248
+ ### Accepted main builds and approval-bounded installation
1249
+
1250
+ The standalone Instance's accepted-ref journal delivers the host-attested main
1251
+ push to Bonsai. For a registered repository, Bonsai requests the retained
1252
+ Trellis build-on-merge template through WORKFLOWS. Its published run owns the
1253
+ build, exact-source verification and installation steps and their result cards.
1254
+ An App's change/merge payload is not push authority. The host's Workflow-node
1255
+ idempotency key reuses a build job on retry or restart. A local-only Computer
1256
+ returns typed no_backend before creating a new job or starting a machine.
1257
+
1258
+ Only an admitted Workflow-node APPS call can install its matching successful
1259
+ build's recorded Release at the recorded target. Ordinary App-origin
1260
+ installation remains refused. Existing grants may be reused, but new permissions
1261
+ or anonymous exposure return needs_consent without changing the running Release.
1262
+ Registering a repository as an App (**Make this an App**) is the owner's consent
1263
+ for its merge Workflow to create that App's installation when none exists yet. A
1264
+ Workflow-created installation is private, with no grants and no anonymous
1265
+ methods. Later Workflows reuse exactly that approval and can never widen it;
1266
+ every grant still needs the owner's explicit consent. The build row records the
1267
+ outcome; status reads are read-only and no native alarm resumes installation.
1268
+ Workflow execution owns retries. This template has no review step, review
1269
+ configuration or separate consent ledger.
1270
+
1271
+ **Breaking installation contract:** Bonsai apps.install and APPS.select require
1272
+ the successful build's jobId alongside installationId and release. The host
1273
+ compares all recorded Release metadata and the target before installation.
1274
+
1275
+ **Breaking build-port contract:** consumers of
1276
+ `@fungi.computer/oyster/app-builds` must provide
1277
+ `OysterBuildComputer.buildBackend()`, returning `{ kind: "hosted" }` or
1278
+ `{ kind: "no_backend" }`, alongside `runBuild`. The preflight reads routing
1279
+ without starting a machine. The build owner's `install(jobId, decide)` invokes
1280
+ the host decision only for a successful job with a verified Release and pending
1281
+ installation target; it records that decision on the same job. Terminal
1282
+ installation decisions are not invoked again on replay.
1283
+
1284
+ For a source HTML build, that nearest package must itself be a pnpm workspace
1285
+ member, or the root package of a standalone App repository. A nested package
1286
+ outside the workspace inventory fails with `WorkspaceBuildError` code
1287
+ `non_workspace_package`; it never inherits the host package's dependency
1288
+ closure. Notes is an explicit workspace member with its own Agar and Zod
1289
+ dependencies and the builder's pinned Vite toolchain, separate from Oyster's
1290
+ host runtime.
1291
+
1292
+ The other tracked nested manifests are separate source inputs, not App HTML
1293
+ builds inside their parent workspace:
1294
+
1295
+ - `apps/bonsai/demo/koi-pond`: copied into its own demo Git repository.
1296
+ - `packages/create-fungi-app/template`: copied to the generated App's root.
1297
+ - `services/app-publisher/test/fixtures/size-hello`: a backend-only emission
1298
+ fixture, not a workspace HTML build.
1299
+ - `apps/tui/test/fixtures/fairy-ring-extension` and
1300
+ `runtimes/local/test/fixtures/{extension-compactor,extension-counter}`:
1301
+ installed extension fixtures, not App source builds.
1302
+ - `packages/computer/container` and
1303
+ `packages/cordyceps/upstream/{computerd,dofs,rpc}`: container/vendored package
1304
+ inputs, not App source entries.
1305
+ - `templates/vishnu-starter`: a standalone project template outside the pnpm
1306
+ workspace.
1307
+
1053
1308
  A captured UI bundle uses `kind: "bundled-html"`, an `entry` ending in
1054
1309
  `/index.html`, `assetBase: "./"`, and an explicit `assets` policy with
1055
1310
  `maxManifestBytes`, `maxObjects`, `maxObjectBytes`, and `maxTotalBytes`. Set
1056
1311
  `backend: null` for UI only, or supply a repository-relative `backend.entry`
1057
1312
  with the backend runtime, export name, and compatibility date. The compiler
1058
1313
  joins both outputs from that same snapshot. A backend-only recipe uses its
1059
- Worker entry as `entry` and omits `backend.entry`. Existing first-party HTML
1060
- recipes retain their pinned steps and digests.
1314
+ Worker entry as `entry` and omits `backend.entry`. First-party HTML recipes
1315
+ retain their catalog-owned compilation steps.
1061
1316
 
1062
1317
  For UI source, omit `kind` and use a closed HTML recipe entry, such as
1063
1318
  `ui/index.html` for Notes at its repository root. It requires `assets` and
@@ -1077,9 +1332,19 @@ implicit install. Only the Computer build (`buildTeamCandidate`, composed with
1077
1332
  arbitrary package code, isolated by that admitted Computer. Every other
1078
1333
  composition, including `packRelease` and `create-fungi-app`, refuses such a
1079
1334
  source before installing. Unknown `fungi.build` fields are refused. The policy
1080
- is pinned as `package-json-fungi-build-v2` in the recipe toolchain; an executor
1335
+ is pinned as `package-json-fungi-build-v4` in the recipe toolchain; an executor
1081
1336
  refuses a recipe pinned under another policy.
1082
1337
 
1338
+ The App's HTML module scripts, shell module and selected backend select its
1339
+ workspace compile roots. The maintained HTML parser and esbuild follow local
1340
+ runtime imports; erased type imports and authoring scripts do not pull a host
1341
+ package into that compilation. Declared package stylesheets are also roots. The
1342
+ planning pass follows code imports only; the actual Vite build retains ownership
1343
+ of asset loading, validation and emission. Each selected workspace package still
1344
+ builds its full declared products and runtime workspace dependency closure, in
1345
+ dependency order. Dependency manifests, hydration and the Computer-only
1346
+ package-script guard are unchanged.
1347
+
1083
1348
  `createLocalArtifactWriter(directory, recipe.assets)` writes the App root and
1084
1349
  all referenced UI/backend objects. It uses the supplied asset bounds and
1085
1350
  maintained MIME metadata. Bundled UI plus a backend may hydrate a captured
@@ -1116,6 +1381,33 @@ selected tenant. Canonical member roles remain distinct from owner authority.
1116
1381
  Public App reads remain available; unknown origins and policy refusals fail
1117
1382
  closed. `ownerOrKeyTenancy` is the single-tenant case.
1118
1383
 
1384
+ ## Machine link protocol
1385
+
1386
+ `@fungi.computer/seam/machine-link-protocol` exposes the bounded read/leave
1387
+ decoder and `createMachineLinkPostChannel`. The channel transports ordinary
1388
+ Carrier Post envelopes and their receiver receipts without rewriting cargo. The
1389
+ host supplies frame-byte, in-flight and acknowledgment-time limits.
1390
+
1391
+ `machineLinkDialUrl(issuer, teamId, deviceId)` and
1392
+ `machineLinkAudience(teamId, deviceId)` give both hosts the same GET path and
1393
+ Seam audience. Enrollment is separately authorized by the account owner; a
1394
+ native dial proves possession of the enrolled device key, not command
1395
+ acceptance.
1396
+
1397
+ The host still owns authenticated native sockets, currentness and connection
1398
+ lifetime. Call `close()` when a connection is lost; create a new channel for its
1399
+ successor. A closed channel, timeout or capacity failure rejects pending I/O
1400
+ without asserting command refusal or custody. Carrier remains the durable outbox
1401
+ and retry owner. In-flight state is not a persistent mailbox or a Capnweb
1402
+ capability table.
1403
+
1404
+ A Post acknowledgment is not Shiitake command acceptance. The Hub's home-aware
1405
+ Agent mail route still requires the recipient's actual Accepted receipt. Focused
1406
+ channel and route tests do not qualify native transport, reconnect, Hub restart
1407
+ or Agent execution. The rebuild now wires Local service, Team native receiver
1408
+ and hosted actual-home consumers together; their whole-bundle native
1409
+ qualification and production activation remain separate claims.
1410
+
1119
1411
  ## Approve an installed App's permissions
1120
1412
 
1121
1413
  After selecting a new Release, the owner can open **App access** beside the App
@@ -1126,10 +1418,22 @@ preserved; a newly selected capability is approved within this Oyster's scope.
1126
1418
  This updates the installation without reinstalling the App or changing its data.
1127
1419
 
1128
1420
  The page sends an owner-only `PUT` to the same path, carrying the captured
1129
- `authorityVersion`, Release `artifactDigest`, and the maintained
1130
- `{kind:"set-grants",grants}` command. Agents, keys, visitors and App-origin
1131
- requests cannot approve. A stale Release/version or concurrent approval returns
1132
- `409`; reload the page to review current permissions. The required
1133
- `InstallationStore.compareAndPut` port returns a serializable commit/refusal
1134
- result across Durable Object RPC. The registry checks the expected authority
1135
- version in its existing commit transaction.
1421
+ `storeGeneration`, `authorityVersion`, Release `artifactDigest`, and a bounded
1422
+ `{kind:"change-grants",changes:[{capability,grant}]}` command. Each grant
1423
+ contains `operations` and `resources`; `null` revokes that capability. Unnamed
1424
+ approvals retain their existing operation and resource ceilings. Agents, keys,
1425
+ visitors and App-origin requests cannot approve. A replaced store, stale
1426
+ Release/version or concurrent approval returns `409`; the page refreshes the
1427
+ facts and rebases touched edits for review. The required `InstallationStore`
1428
+ exposes its durable `generation()` and
1429
+ `compareAndPut(record, expectedVersion, expectedGeneration)`. The latter returns
1430
+ a serializable commit/refusal result across Durable Object RPC and checks both
1431
+ pins in the existing commit transaction.
1432
+
1433
+ ## Breaking App send contract
1434
+
1435
+ App `AGENTS.send` requires a caller-owned `requestId`, minted once per user
1436
+ intent and reused unchanged on retry after an UNKNOWN outcome. The standalone
1437
+ adapter no longer generates a replacement. See the
1438
+ [App Agent send API reference](docs/overview.md#app-agent-send-api--breaking-change)
1439
+ for the complete input and migration note.
@@ -0,0 +1,103 @@
1
+ /**
2
+ * An Agent's avatar is host data on the Agent, beside its directory record:
3
+ * the one the owner chose, else Sprite's `seededAvatar(agentId)`. The seeded
4
+ * default is a pure function of identity, so it is computed, never stored,
5
+ * and an Agent created before any choice needs no stored row. Conversations
6
+ * carry no avatar of their own. Both hosts (the Hub's Team store and a
7
+ * standalone Oyster's Agent directory) keep the row in the same owner-local
8
+ * SQLite as the directory, so a choice commits and erases with its Agent.
9
+ */
10
+ import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
11
+ import { type Avatar } from "@fungi.computer/sprite/avatar";
12
+ import { z } from "zod";
13
+ /** The catalog-valid avatar an owner may choose for an Agent. */
14
+ export declare const agentAvatarSchema: z.ZodObject<{
15
+ v: z.ZodLiteral<1>;
16
+ character: z.ZodEnum<{
17
+ mushroom: "mushroom";
18
+ cat: "cat";
19
+ burger: "burger";
20
+ }>;
21
+ appearance: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
22
+ expression: z.ZodString;
23
+ renderStyle: z.ZodEnum<{
24
+ plain: "plain";
25
+ pencil: "pencil";
26
+ manga: "manga";
27
+ riso: "riso";
28
+ halftone: "halftone";
29
+ blueprint: "blueprint";
30
+ crt: "crt";
31
+ watercolor: "watercolor";
32
+ }>;
33
+ }, z.core.$strict>;
34
+ /**
35
+ * The avatar as a closed record, for contracts that refuse open maps (Hub
36
+ * projections): each character's appearance names only its own controls,
37
+ * then the catalog refines it exactly as `agentAvatarSchema` does.
38
+ */
39
+ export declare const agentAvatarProjectionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
40
+ v: z.ZodLiteral<1>;
41
+ character: z.ZodLiteral<"mushroom">;
42
+ appearance: z.ZodObject<{
43
+ [x: string]: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
44
+ }, z.core.$strict>;
45
+ expression: z.ZodString;
46
+ renderStyle: z.ZodEnum<{
47
+ plain: "plain";
48
+ pencil: "pencil";
49
+ manga: "manga";
50
+ riso: "riso";
51
+ halftone: "halftone";
52
+ blueprint: "blueprint";
53
+ crt: "crt";
54
+ watercolor: "watercolor";
55
+ }>;
56
+ }, z.core.$strict>, z.ZodObject<{
57
+ v: z.ZodLiteral<1>;
58
+ character: z.ZodLiteral<"cat">;
59
+ appearance: z.ZodObject<{
60
+ [x: string]: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
61
+ }, z.core.$strict>;
62
+ expression: z.ZodString;
63
+ renderStyle: z.ZodEnum<{
64
+ plain: "plain";
65
+ pencil: "pencil";
66
+ manga: "manga";
67
+ riso: "riso";
68
+ halftone: "halftone";
69
+ blueprint: "blueprint";
70
+ crt: "crt";
71
+ watercolor: "watercolor";
72
+ }>;
73
+ }, z.core.$strict>, z.ZodObject<{
74
+ v: z.ZodLiteral<1>;
75
+ character: z.ZodLiteral<"burger">;
76
+ appearance: z.ZodObject<{
77
+ [x: string]: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
78
+ }, z.core.$strict>;
79
+ expression: z.ZodString;
80
+ renderStyle: z.ZodEnum<{
81
+ plain: "plain";
82
+ pencil: "pencil";
83
+ manga: "manga";
84
+ riso: "riso";
85
+ halftone: "halftone";
86
+ blueprint: "blueprint";
87
+ crt: "crt";
88
+ watercolor: "watercolor";
89
+ }>;
90
+ }, z.core.$strict>], "character">;
91
+ /** A projected avatar as the Avatar descriptor Sprite draws. */
92
+ export declare function avatarFromProjection(projected: z.infer<typeof agentAvatarProjectionSchema>): Avatar;
93
+ /** One owner-local store of chosen Agent avatars. */
94
+ export type AgentAvatarStore = Readonly<{
95
+ /** The Agent's avatar: its chosen one, else its seeded default. */
96
+ read(agentId: string): Avatar;
97
+ /** Record the owner's choice; the caller owns the Agent's existence. */
98
+ write(agentId: string, avatar: Avatar): void;
99
+ /** Forget a removed Agent's choice in the caller's removal transaction. */
100
+ remove(agentId: string): void;
101
+ }>;
102
+ /** Open the chosen-avatar table in the Agent directory's own SQLite. */
103
+ export declare function createAgentAvatarStore(sql: WatchdogSqliteOwner["sql"]): AgentAvatarStore;