@fungi.computer/oyster 0.2.0 → 0.3.1

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 (291) hide show
  1. package/README.md +396 -72
  2. package/dist/agents.d.ts +3 -2
  3. package/dist/agents.js +4 -2
  4. package/dist/app-builds.d.ts +81 -8
  5. package/dist/app-builds.js +213 -60
  6. package/dist/app-storage.d.ts +3 -3
  7. package/dist/approval-capability.d.ts +5 -0
  8. package/dist/approval-capability.js +46 -0
  9. package/dist/approval-home.d.ts +51 -0
  10. package/dist/approval-home.js +226 -0
  11. package/dist/apps/catalog.d.ts +335 -0
  12. package/dist/apps/catalog.js +46 -0
  13. package/dist/apps/gate.d.ts +20 -6
  14. package/dist/apps/gate.js +42 -16
  15. package/dist/apps/index.d.ts +3 -1
  16. package/dist/apps/index.js +3 -1
  17. package/dist/apps/installation.d.ts +132 -51
  18. package/dist/apps/installation.js +176 -70
  19. package/dist/apps/permissions.d.ts +247 -0
  20. package/dist/apps/permissions.js +255 -0
  21. package/dist/boot-capability-route.d.ts +15 -0
  22. package/dist/boot-capability-route.js +196 -0
  23. package/dist/boot-capability.d.ts +103 -0
  24. package/dist/boot-capability.js +141 -0
  25. package/dist/boot-construction.d.ts +12 -0
  26. package/dist/boot-construction.js +175 -0
  27. package/dist/boot-gate.d.ts +267 -0
  28. package/dist/boot-gate.js +236 -0
  29. package/dist/boot-host-script.d.ts +2 -0
  30. package/dist/boot-host-script.js +1 -0
  31. package/dist/boot-mount.d.ts +21 -0
  32. package/dist/boot-mount.js +65 -0
  33. package/dist/browser/boot.d.ts +73 -0
  34. package/dist/browser/boot.js +502 -0
  35. package/dist/browser/browser.d.ts +104 -0
  36. package/dist/browser/browser.js +374 -0
  37. package/dist/browser/capability-source.d.ts +76 -0
  38. package/dist/browser/capability-source.js +338 -0
  39. package/dist/browser/cdp-client.d.ts +56 -0
  40. package/dist/browser/cdp-client.js +184 -0
  41. package/dist/browser/errors.d.ts +79 -0
  42. package/dist/browser/errors.js +32 -0
  43. package/dist/browser/index.d.ts +16 -0
  44. package/dist/browser/index.js +14 -0
  45. package/dist/browser/lease-registry.d.ts +61 -0
  46. package/dist/browser/lease-registry.js +101 -0
  47. package/dist/browser/operations.d.ts +225 -0
  48. package/dist/browser/operations.js +137 -0
  49. package/dist/browser/page.d.ts +25 -0
  50. package/dist/browser/page.js +172 -0
  51. package/dist/browser/quick-reads.d.ts +29 -0
  52. package/dist/browser/quick-reads.js +160 -0
  53. package/dist/browser/sessions.d.ts +52 -0
  54. package/dist/browser/sessions.js +118 -0
  55. package/dist/browser/url-policy.d.ts +18 -0
  56. package/dist/browser/url-policy.js +58 -0
  57. package/dist/build/app-imports.d.ts +12 -0
  58. package/dist/build/app-imports.js +105 -0
  59. package/dist/build/build-policy.d.ts +5 -3
  60. package/dist/build/build-policy.js +5 -3
  61. package/dist/build/candidate-builder.d.ts +1 -1
  62. package/dist/build/candidate-cli.mjs +8375 -334
  63. package/dist/build/local-build.d.ts +2 -0
  64. package/dist/build/local-build.js +28 -6
  65. package/dist/build/snapshot-package-store.d.ts +1 -1
  66. package/dist/build/snapshot-package-store.js +7 -2
  67. package/dist/build/workspace-build.d.ts +92 -5
  68. package/dist/build/workspace-build.js +93 -19
  69. package/dist/build.d.ts +1 -1
  70. package/dist/build.js +1 -1
  71. package/dist/catalog-frame.js +1 -1
  72. package/dist/cloudflare.d.ts +13 -3
  73. package/dist/cloudflare.js +102 -4
  74. package/dist/frame-binding-wire-script.js +1 -1
  75. package/dist/frame-binding-wire.js +4 -2
  76. package/dist/git-auth.d.ts +0 -2
  77. package/dist/git-auth.js +0 -2
  78. package/dist/git.d.ts +0 -1
  79. package/dist/git.js +0 -2
  80. package/dist/host.d.ts +39 -13
  81. package/dist/host.js +268 -142
  82. package/dist/index.d.ts +25 -15
  83. package/dist/index.js +39 -16
  84. package/dist/install-release-cli.d.ts +2 -0
  85. package/dist/install-release-cli.js +46 -0
  86. package/dist/installation-client.d.ts +43 -0
  87. package/dist/installation-client.js +178 -0
  88. package/dist/installation-execution.d.ts +11 -0
  89. package/dist/installation-execution.js +184 -0
  90. package/dist/installation-operations.d.ts +1437 -0
  91. package/dist/installation-operations.js +664 -0
  92. package/dist/installation-store-generation.d.ts +3 -0
  93. package/dist/installation-store-generation.js +16 -0
  94. package/dist/instance-id.d.ts +10 -0
  95. package/dist/instance-id.js +10 -0
  96. package/dist/instance-refusal.d.ts +11 -0
  97. package/dist/instance-refusal.js +21 -0
  98. package/dist/internal/delivery.js +4 -2
  99. package/dist/internal/host-gate.js +7 -1
  100. package/dist/internal/registry-installations.js +4 -4
  101. package/dist/keys.d.ts +5 -1
  102. package/dist/keys.js +10 -2
  103. package/dist/machine-link/authority.d.ts +89 -0
  104. package/dist/machine-link/authority.js +37 -0
  105. package/dist/machine-link/client.d.ts +66 -0
  106. package/dist/machine-link/client.js +94 -0
  107. package/dist/machine-link/delivery.d.ts +18 -0
  108. package/dist/machine-link/delivery.js +55 -0
  109. package/dist/machine-link/enrollment.d.ts +111 -0
  110. package/dist/machine-link/enrollment.js +286 -0
  111. package/dist/machine-link/fences.d.ts +37 -0
  112. package/dist/machine-link/fences.js +166 -0
  113. package/dist/machine-link/home.d.ts +20 -0
  114. package/dist/machine-link/home.js +24 -0
  115. package/dist/machine-link/host.d.ts +43 -0
  116. package/dist/machine-link/host.js +189 -0
  117. package/dist/machine-link/index.d.ts +15 -0
  118. package/dist/machine-link/index.js +15 -0
  119. package/dist/machine-link/link.d.ts +48 -0
  120. package/dist/machine-link/link.js +223 -0
  121. package/dist/machine-link/presence.d.ts +10 -0
  122. package/dist/machine-link/presence.js +25 -0
  123. package/dist/machine-link/workers.d.ts +6 -0
  124. package/dist/machine-link/workers.js +6 -0
  125. package/dist/mcp-owner.d.ts +2 -2
  126. package/dist/mcp-owner.js +41 -33
  127. package/dist/mcp.d.ts +15 -3
  128. package/dist/mcp.js +146 -25
  129. package/dist/older-oyster-store-refusal.d.ts +4 -0
  130. package/dist/older-oyster-store-refusal.js +18 -0
  131. package/dist/older-oyster-store.d.ts +21 -0
  132. package/dist/older-oyster-store.js +43 -0
  133. package/dist/pack.d.ts +14 -3
  134. package/dist/pack.js +44 -19
  135. package/dist/page/app-access.d.ts +7 -0
  136. package/dist/page/app-access.js +75 -0
  137. package/dist/page/boot-host.js +57 -0
  138. package/dist/page/capabilities.d.ts +3 -3
  139. package/dist/page/capabilities.js +16 -12
  140. package/dist/page/computer-handles.d.ts +3 -3
  141. package/dist/page/computer-target.d.ts +3 -0
  142. package/dist/page/computer-target.js +9 -0
  143. package/dist/page/mount.d.ts +23 -3
  144. package/dist/page/mount.js +152 -16
  145. package/dist/page-assets.d.ts +22 -0
  146. package/dist/page-assets.js +28 -0
  147. package/dist/page-script.d.ts +3 -0
  148. package/dist/page-script.js +5 -4
  149. package/dist/release.d.ts +13 -6
  150. package/dist/release.js +10 -7
  151. package/dist/root-data.d.ts +16 -6
  152. package/dist/root-data.js +2 -2
  153. package/dist/source/index.d.ts +1 -1
  154. package/dist/source/team-source.d.ts +20 -8
  155. package/dist/source/team-source.js +39 -10
  156. package/dist/source/template-seed.d.ts +2 -2
  157. package/dist/standalone/agent-mail.d.ts +24 -0
  158. package/dist/standalone/agent-mail.js +62 -0
  159. package/dist/standalone/agent-store.d.ts +9 -0
  160. package/dist/standalone/agent-store.js +30 -0
  161. package/dist/standalone/agents.d.ts +68 -9
  162. package/dist/standalone/agents.js +428 -56
  163. package/dist/standalone/app-builds-binding.d.ts +20 -2
  164. package/dist/standalone/app-builds-binding.js +147 -23
  165. package/dist/standalone/app-set.d.ts +5 -2
  166. package/dist/standalone/app-set.js +186 -144
  167. package/dist/standalone/browser.d.ts +11 -0
  168. package/dist/standalone/browser.js +72 -0
  169. package/dist/standalone/builds.d.ts +13 -4
  170. package/dist/standalone/builds.js +28 -44
  171. package/dist/standalone/computer-egress.d.ts +2 -0
  172. package/dist/standalone/computer-egress.js +13 -4
  173. package/dist/standalone/computer-fleet.d.ts +43 -0
  174. package/dist/standalone/computer-fleet.js +200 -0
  175. package/dist/standalone/computer.d.ts +44 -9
  176. package/dist/standalone/computer.js +129 -22
  177. package/dist/standalone/install-apps-cli.js +43 -6
  178. package/dist/standalone/instance.d.ts +2 -2
  179. package/dist/standalone/instance.js +8 -11
  180. package/dist/standalone/machine-link.d.ts +354 -0
  181. package/dist/standalone/machine-link.js +368 -0
  182. package/dist/standalone/media.d.ts +2 -2
  183. package/dist/standalone/media.js +19 -16
  184. package/dist/standalone/preview.d.ts +2 -2
  185. package/dist/standalone/preview.js +37 -10
  186. package/dist/standalone/registry.d.ts +151 -9
  187. package/dist/standalone/registry.js +305 -8
  188. package/dist/standalone/worker.d.ts +19 -5
  189. package/dist/standalone/worker.js +90 -54
  190. package/dist/standalone/workflow-binding.d.ts +18 -22
  191. package/dist/standalone/workflow-binding.js +18 -18
  192. package/dist/standalone/workflow-control.d.ts +4 -0
  193. package/dist/standalone/workflow-control.js +40 -0
  194. package/dist/standalone/workflow-events.d.ts +2 -2
  195. package/dist/standalone/workflow-events.js +9 -10
  196. package/dist/standalone/workflow-git-capture.js +4 -3
  197. package/dist/standalone/workflow-host.d.ts +162 -161
  198. package/dist/standalone/workflow-host.js +101 -21
  199. package/dist/standalone/workflow-node-capture.js +11 -4
  200. package/dist/standalone/workflow-schedules.d.ts +5 -2
  201. package/dist/standalone/workflow-schedules.js +9 -10
  202. package/dist/standalone/workflow-trigger-host.d.ts +65 -5
  203. package/dist/standalone/workflow-trigger-host.js +2 -2
  204. package/dist/standalone/workflow-webhooks.d.ts +22 -0
  205. package/dist/standalone/workflow-webhooks.js +39 -0
  206. package/dist/standalone/workflows.d.ts +140 -43
  207. package/dist/standalone/workflows.js +198 -39
  208. package/dist/storage-frame.d.ts +6 -6
  209. package/dist/testing/capabilities.js +14 -3
  210. package/dist/testing/installation.js +26 -2
  211. package/dist/testing/worker.js +1 -1
  212. package/dist/testing/workers-runtime.d.ts +8 -2
  213. package/dist/testing/workers-runtime.js +20 -2
  214. package/dist/webhook-abuse.d.ts +25 -0
  215. package/dist/webhook-abuse.js +54 -0
  216. package/dist/webhook-custody.d.ts +144 -0
  217. package/dist/webhook-custody.js +222 -0
  218. package/dist/webhook-ingress.d.ts +36 -0
  219. package/dist/webhook-ingress.js +146 -0
  220. package/dist/webhooks.d.ts +111 -0
  221. package/dist/webhooks.js +189 -0
  222. package/dist/workers-git.d.ts +12 -17
  223. package/dist/workers-git.js +64 -52
  224. package/dist/workers.d.ts +226 -13
  225. package/dist/workers.js +363 -51
  226. package/dist/workflow-agent-authority.d.ts +8 -0
  227. package/dist/workflow-agent-authority.js +42 -0
  228. package/dist/workflows.d.ts +66 -0
  229. package/dist/workflows.js +97 -0
  230. package/docs/api/README.md +63 -37
  231. package/docs/api/agents.md +17 -0
  232. package/docs/api/app-storage.md +1 -1
  233. package/docs/api/apps.md +1394 -543
  234. package/docs/api/browser/boot.md +132 -0
  235. package/docs/api/browser.md +1440 -0
  236. package/docs/api/build-contract.md +38 -25
  237. package/docs/api/build.md +263 -84
  238. package/docs/api/cloudflare.md +93 -7
  239. package/docs/api/git.md +3 -13
  240. package/docs/api/host.md +944 -118
  241. package/docs/api/index.md +320 -18
  242. package/docs/api/keys.md +24 -1
  243. package/docs/api/mcp.md +59 -12
  244. package/docs/api/modules.md +33 -40
  245. package/docs/api/pack.md +57 -8
  246. package/docs/api/page/mount.md +92 -4
  247. package/docs/api/workers-git.md +103 -24
  248. package/docs/api/workers.md +1594 -244
  249. package/docs/site/agent-quickstart.mdx +28 -14
  250. package/examples/standalone/worker.ts +3 -1
  251. package/examples/standalone/wrangler.jsonc +2 -0
  252. package/package.json +73 -71
  253. package/dist/installation-consent.d.ts +0 -81
  254. package/dist/installation-consent.js +0 -84
  255. package/dist/shell/app-directory.d.ts +0 -542
  256. package/dist/shell/app-directory.js +0 -196
  257. package/dist/shell/browser-actions.d.ts +0 -7
  258. package/dist/shell/browser-actions.js +0 -56
  259. package/dist/shell/command-host.d.ts +0 -112
  260. package/dist/shell/command-host.js +0 -502
  261. package/dist/shell/commands.d.ts +0 -25
  262. package/dist/shell/commands.js +0 -78
  263. package/dist/shell/index.d.ts +0 -8
  264. package/dist/shell/index.js +0 -8
  265. package/dist/shell/keys.d.ts +0 -41
  266. package/dist/shell/keys.js +0 -134
  267. package/dist/shell/shell-module.d.ts +0 -55
  268. package/dist/shell/shell-module.js +0 -86
  269. package/dist/shell/window-manager.d.ts +0 -128
  270. package/dist/shell/window-manager.js +0 -730
  271. package/dist/shell/workbench-contract.d.ts +0 -68
  272. package/dist/shell/workbench-directory.d.ts +0 -332
  273. package/dist/shell/workbench-directory.js +0 -35
  274. package/dist/shell/workbench-host-types.d.ts +0 -96
  275. package/dist/shell/workbench-host-types.js +0 -1
  276. package/dist/shell/workbench-host.d.ts +0 -34
  277. package/dist/shell/workbench-host.js +0 -352
  278. package/dist/shell/workbench-model.d.ts +0 -194
  279. package/dist/shell/workbench-model.js +0 -53
  280. package/dist/shell/workbench.d.ts +0 -4
  281. package/dist/shell/workbench.js +0 -2
  282. package/docs/api/shell/app-directory.md +0 -599
  283. package/docs/api/shell/command-host.md +0 -224
  284. package/docs/api/shell/commands.md +0 -197
  285. package/docs/api/shell/keys.md +0 -183
  286. package/docs/api/shell/shell-module.md +0 -204
  287. package/docs/api/shell/window-manager.md +0 -267
  288. package/docs/api/shell/workbench-directory.md +0 -119
  289. package/docs/api/shell/workbench.md +0 -205
  290. package/docs/api/shell.md +0 -1915
  291. /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,76 @@ 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
+ ### Upgrading from Oyster 0.2 or earlier
176
+
177
+ Oyster 0.3 does not read data created by Oyster 0.1 or 0.2, and it does not
178
+ convert it. An Oyster deployed on 0.2 and redeployed on 0.3 answers every
179
+ request with `409` and `{"error": "store_from_older_oyster", ...}` (a short
180
+ explanation page in a browser), and `oyster install-apps` prints the same
181
+ message. Old data is left untouched, so redeploying 0.2.x brings the Oyster back
182
+ exactly as it was.
183
+
184
+ To move to 0.3, start from a fresh store. The smallest way is to deploy under
185
+ new names, leaving the old Oyster in place until you no longer need it. In
186
+ `wrangler.jsonc`, rename `name`, both `bucket_name` values,
187
+ `artifacts.namespace` and every `workflows[].name`; create the new release
188
+ bucket; then run the quick start again. To reuse the names instead, tear the old
189
+ Oyster down as in the teardown steps above (delete the Worker, which deletes its
190
+ Durable Object data; empty and delete the release bucket; delete the Computer's
191
+ container application, the Workflow, and the Artifacts repositories and
192
+ namespace), then deploy 0.3 and reinstall the Apps. Data in the old Oyster, such
193
+ as Apps' stored data, is not carried over.
194
+
195
+ ### Set the Oyster owner key
196
+
197
+ Choose an owner key (separate from the Cloudflare API token) and store it as a
198
+ secret:
109
199
 
110
200
  ```sh
111
201
  export OYSTER_OWNER_KEY=$(openssl rand -hex 24)
@@ -532,13 +622,16 @@ returns an `AdmittedGit`; the door forwards reads to `.read` and pushes to
532
622
  builds these ports from `host.auth`, `host.installations` and `host.git`, so
533
623
  most hosts set `host.git` and never call `createGitDoor` directly.
534
624
 
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.
625
+ A host whose Workflow run owner issues Workflow Git credentials supplies
626
+ `workflowGitPass` in `oysterGitRoot` or `oysterWorkerGit` options. The run owner
627
+ issues one credential per native command of an admitted Git step and gives it
628
+ only to that command's Git environment. It travels only as an
629
+ `Authorization: Bearer` header starting with Braid's `workflowGitPassPrefix`
630
+ (`wgp.`) on the ordinary credential-free door URL; it is never part of a URL.
631
+ That header is admitted only by the port, for one repository and scope, as the
632
+ arming member under the Workflow's branch grant, and never as a session or key.
633
+ It stops working when its command closes or its run ends. Pushes outside those
634
+ branches are refused before the App's push policy runs.
542
635
 
543
636
  ### Provide App capabilities
544
637
 
@@ -684,6 +777,33 @@ disconnects one. These routes are owner-direct and reject an App installation
684
777
  header. A server that is down or refuses the token is left out of that run and
685
778
  the others stay available. OAuth sign-in is not offered yet.
686
779
 
780
+ ### Give an Agent a browser
781
+
782
+ Declare a Cloudflare Browser Rendering binding and every Agent gets a
783
+ `cloudBrowser` tool for public pages: `open` a driven session (optionally at a
784
+ URL), `navigate`, read it with `snapshot` (a bounded accessibility-tree
785
+ summary), `click`, `type`, `press`, `scroll`, and `close` it.
786
+
787
+ ```jsonc
788
+ // wrangler.jsonc
789
+ "browser": { "binding": "BROWSER" }
790
+ ```
791
+
792
+ Every URL the Agent supplies must be http(s), carry no credentials and resolve
793
+ to a public address; anything else is refused before the browser sees it. A
794
+ standalone Oyster allows two open sessions at once, closes one after 5 idle
795
+ minutes or 30 minutes in all, and is billed by Cloudflare to the owner's
796
+ account. Without the binding the tool is absent.
797
+
798
+ Hosts compose the same module, `@fungi.computer/oyster/browser`, behind one port
799
+ (`BrowserHost`): a provider (`bindingSessions(env.BROWSER)`, or
800
+ `browserRunSessions` with a Browser Run account token, which also enables the
801
+ `markdown`/`links`/`scrape`/`extract` Quick Reads), a required metering owner
802
+ and an event sink. `createBrowser` owns lease ownership, the URL policy, reaping
803
+ and billing order; `browserCapabilitySource` is the Agent tool. The Agent never
804
+ reaches raw CDP or runs script in the page. See the
805
+ [API reference](docs/api/browser.md).
806
+
687
807
  ### Pack an App
688
808
 
689
809
  `packRelease` composes `oyster/build`'s source capture, candidate preparer,
@@ -748,50 +868,35 @@ install-or-select owner installs it (201) or, when the installation already
748
868
  exists, switches it to that Release (200); the App keeps its state, and every
749
869
  other refusal is returned unchanged.
750
870
 
871
+ Pending installation commands retain their exact baseline, reduced plan and
872
+ preparation evidence for recovery. Settlement replaces those payloads with the
873
+ request's input digest, correlation, settlement time and recorded outcome. The
874
+ existing Registry alarm deletes superseded settled journal entries in bounded
875
+ batches after 90 days from their own settlement. The latest settled entry per
876
+ effective installation is always pinned, including while newer work is pending.
877
+ Indexed durable deadlines re-arm the same alarm when idle; pending recovery is
878
+ never age-deleted. Expiry removes App journal history, not canonical
879
+ installations, Releases, artifact bytes or Watchdog's private job history. An
880
+ expired operation is no longer readable; re-admitting its ID is refused as
881
+ `unavailable` when Watchdog still retains that job, rather than creating pending
882
+ work that cannot execute. A new intent needs a new request ID.
883
+
884
+ A retained successful receipt keeps its original generation, instance and
885
+ authority-version reference. Its `installation` is resolved only from the exact
886
+ current canonical selection; after removal, generation change or selection drift
887
+ it is `null`, while the original success remains recorded. Completion helpers
888
+ return `409 installation_result_unavailable` in that case, and the standalone
889
+ installer does not approve defaults from historical success. Settled history is
890
+ never an executable recovery command. Stores containing superseded preview
891
+ journal shapes require separately authorized current-contract republishing, not
892
+ an in-place conversion.
893
+
751
894
  ## Build a shell
752
895
 
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.
896
+ The shell client lives in its own package,
897
+ [`@fungi.computer/nacre`](../nacre/README.md): workbench host, command host,
898
+ keys, commands, window manager, App directory rows and the shell-module
899
+ contract. Oyster's page uses it; a shell imports it directly.
795
900
 
796
901
  `@fungi.computer/oyster/page` mounts one installed App's page and connects its
797
902
  sandboxed frame with that installation's admitted slots. That page is only the
@@ -894,6 +999,57 @@ table names.
894
999
 
895
1000
  ### Durable Object composition
896
1001
 
1002
+ Activation is host-gated before selection writes or aborting the old version.
1003
+ The candidate backend loads in a unique isolate over one bounded disposable
1004
+ sibling facet, `candidate`, with denied test bindings and no outbound network.
1005
+ The lifecycle supplies its already-read prior state schema: first activation
1006
+ uses empty SQLite; an initialized Instance requires the native facets API to
1007
+ copy `app` into `candidate` before construction. A host without clone support
1008
+ refuses that check rather than substituting fresh storage. Cleanup aborts and
1009
+ deletes the sibling, and Instance purge also deletes that known name. The copy
1010
+ is a construction test, not a backup or data rollback. It cannot become the live
1011
+ facet and contains no supervisor selection, authority, Team App storage, or live
1012
+ bindings. Native copying and isolation require the qualified runtime proof; the
1013
+ fresh-SQLite Node adapter is not that proof. The host also checks the immutable
1014
+ UI entry in a zero-grant browser session (`reportBootPage` with
1015
+ `checkBootPage`): the page opens the pinned `/_boot/<release>/` route on the
1016
+ host's own origin (https, or http only on loopback) with a short-lived
1017
+ capability, and every request to any other origin is blocked. The UI result is
1018
+ reported, never authority: a page that did not mount is `failed`, and a missing
1019
+ browser, a timeout, unconfirmed cleanup or an unavailable runner is
1020
+ `not_checked`. An App manifest, callback or healthy flag cannot supply the
1021
+ verdict. This is construction and UI-mount qualification, not an HTTP request
1022
+ smoke, execution against live storage, or a data rewind.
1023
+
1024
+ Each check owns one provider session and confirms it closed before reporting;
1025
+ unconfirmed closure reports `boot_cleanup_failed`, and Browser Run's
1026
+ `keep_alive` remains the backstop.
1027
+
1028
+ `pnpm run test:recovery-copy:celld` exercises the actual construction helper
1029
+ with synthetic App state, a successful copy and a constructor refusal. It checks
1030
+ live state preservation, denied bindings/outbound access, supervisor separation
1031
+ and empty storage on reopening the cleaned sibling. It does not prove physical
1032
+ file/stream deletion, restart recovery, or owner selection.
1033
+
1034
+ The disposable both-host proof is
1035
+ `bash apps/hub/scripts/check-app-boot-native.sh` from the repository root, after
1036
+ the affected dependency and Oyster builds. It requires Linux network namespaces,
1037
+ passwordless local `sudo`, the pinned celld binary and provisioned Playwright
1038
+ Chromium. Inside an unnamed private network it drives a local Chromium through
1039
+ the same `checkBootPage` both hosts use (a test-only provider in Browser Run's
1040
+ devtools API shape) and fresh local fixture storage; it does not install a
1041
+ service, contact a live host or exercise an App's HTTP handler. It checks real
1042
+ construction and ordinary Agar UI mounting through each host's `/_boot/` route,
1043
+ native facet cleanup, closed browser sessions, retained selections on refusal,
1044
+ forged verdicts, wrapper tampering and browser unavailability.
1045
+
1046
+ The Hub's real Team default-provisioning selection path is qualified separately
1047
+ with `bash apps/hub/scripts/check-app-boot-native.sh --publication`, under the
1048
+ same prerequisites. Its five host-dependent cases use fresh native resources,
1049
+ the maintained artifact writer/readback and immutable R2 object versions. Other
1050
+ domain-test metadata seeds are not host proof. This leg qualifies selection
1051
+ admission, not downstream projection delivery or deployment.
1052
+
897
1053
  `createCloudflareAppInstance` keeps supervisor state in a SQLite Durable Object
898
1054
  and runs the backend in a named `app` facet loaded through the Worker Loader.
899
1055
  Supervisor metadata and the backend's database are separate, and a new Release
@@ -1050,14 +1206,133 @@ capture does not rebase paths to the App directory. The nearest `package.json`
1050
1206
  above the entry supplies `fungi.app`, plus `fungi.backendDefinition` for an API
1051
1207
  derived from the compiled backend.
1052
1208
 
1209
+ An App owns its recipe in `package.json#fungi.build`, alongside its existing
1210
+ page policy (`react`, `stylesheets`, and dependency build declarations). Recipe
1211
+ paths in that metadata are relative to the App's own directory. The Node-only
1212
+ `readBuildManifest(packageJson)` decodes that one strict contract;
1213
+ `appBuildRecipe(manifest, directory)` roots the recipe for a captured checkout,
1214
+ or accepts `""` for an App at the repository root. Dependency-only metadata is
1215
+ still valid but cannot supply an App recipe. Unknown build fields are refused.
1216
+
1217
+ The starter installer reads metadata with `git show` from the selected commit,
1218
+ not from dirty checkout files. Its roster owns only App identity and source
1219
+ locations. The scaffold's `npm run build` deliberately reads saved working
1220
+ metadata before capturing those same saved files. Its template declares the
1221
+ ordinary npm recipe as `kind: "source-html"`, including the backend entry and
1222
+ asset bounds; the command does not carry another copy of that recipe.
1223
+
1224
+ **Breaking recipe contract:** `@fungi.computer/oyster/standalone/app-set` no
1225
+ longer exposes `StarterApp.recipe` or recipe values on `standaloneAppSet`.
1226
+ Callers read the selected source's `package.json` with `readBuildManifest` and
1227
+ materialize it with `appBuildRecipe`, both exported from
1228
+ `@fungi.computer/oyster/build`. The workspace metadata policy advances from
1229
+ `package-json-fungi-build-v3` to `package-json-fungi-build-v4`; builders refuse
1230
+ missing or superseded policy metadata rather than guessing a recipe.
1231
+
1232
+ **Breaking repository-registration contract:** the public
1233
+ `PUT /api/apps/:appId/build-source` route, which accepted an App identity in the
1234
+ path and caller-supplied installation, repository, and recipe fields, has been
1235
+ removed. Use repo-only `APPS.register({ repo })`, exposed by Bonsai as
1236
+ `apps.register({ repo })`. The host reads App identity and `fungi.build` recipe
1237
+ from the immutable main commit's root `package.json` through the shared manifest
1238
+ reader; callers cannot override identity or recipe. An existing repository
1239
+ registration retains its App identity on replay. Registration returns the App ID
1240
+ and exact commit without starting a build or installing a Release. No
1241
+ compatibility route or shim remains.
1242
+
1243
+ ### Local Release installation
1244
+
1245
+ ```sh
1246
+ export OYSTER_URL=https://your-oyster.example
1247
+ export OYSTER_OWNER_KEY='<your owner key>'
1248
+ oyster install ./dist --installation-id my-app
1249
+ # Or name the same Release's manifest:
1250
+ oyster install ./dist/release.json --installation-id my-app
1251
+ ```
1252
+
1253
+ The CLI delegates to `installRelease` from `@fungi.computer/oyster/pack`, using
1254
+ the same uploader as programmatic clients. Artifacts must upload before
1255
+ `POST /api/installations`. Upload refusal, missing files, and non-OK
1256
+ installation responses exit nonzero. No key is discovered from your home
1257
+ directory, and this command does not provision a Computer or deploy an Oyster.
1258
+
1259
+ For repository authoring, use Bonsai's repository Settings **Make this an App**
1260
+ (or `apps.register({ repo })`). Commit the root `package.json#fungi.build`
1261
+ recipe first; dirty metadata is not registration input. The readonly App ID and
1262
+ editable revision/name come from registration or the latest recorded build.
1263
+ Status can be read using `APPS.buildStatus({ repo })` in the captured Git
1264
+ installation; Settings uses that selector after reload. No job returns `null`.
1265
+ Workflow callers use `APPS.buildStatus({ jobId })` for the particular job they
1266
+ started.
1267
+
1268
+ ### Accepted main builds and approval-bounded installation
1269
+
1270
+ The standalone Instance's accepted-ref journal delivers the host-attested main
1271
+ push to Bonsai. For a registered repository, Bonsai requests the retained
1272
+ Trellis build-on-merge template through WORKFLOWS. Its published run owns the
1273
+ build, exact-source verification and installation steps and their result cards.
1274
+ An App's change/merge payload is not push authority. The host's Workflow-node
1275
+ idempotency key reuses a build job on retry or restart. A local-only Computer
1276
+ returns typed no_backend before creating a new job or starting a machine.
1277
+
1278
+ Only an admitted Workflow-node APPS call can install its matching successful
1279
+ build's recorded Release at the recorded target. Ordinary App-origin
1280
+ installation remains refused. Existing grants may be reused, but new permissions
1281
+ or anonymous exposure return needs_consent without changing the running Release.
1282
+ Registering a repository as an App (**Make this an App**) is the owner's consent
1283
+ for its merge Workflow to create that App's installation when none exists yet. A
1284
+ Workflow-created installation is private, with no grants and no anonymous
1285
+ methods. Later Workflows reuse exactly that approval and can never widen it;
1286
+ every grant still needs the owner's explicit consent. The build row records the
1287
+ outcome; status reads are read-only and no native alarm resumes installation.
1288
+ Workflow execution owns retries. This template has no review step, review
1289
+ configuration or separate consent ledger.
1290
+
1291
+ **Breaking installation contract:** Bonsai apps.install and APPS.select require
1292
+ the successful build's jobId alongside installationId and release. The host
1293
+ compares all recorded Release metadata and the target before installation.
1294
+
1295
+ **Breaking build-port contract:** consumers of
1296
+ `@fungi.computer/oyster/app-builds` must provide
1297
+ `OysterBuildComputer.buildBackend()`, returning `{ kind: "hosted" }` or
1298
+ `{ kind: "no_backend" }`, alongside `runBuild`. The preflight reads routing
1299
+ without starting a machine. The build owner's `install(jobId, decide)` invokes
1300
+ the host decision only for a successful job with a verified Release and pending
1301
+ installation target; it records that decision on the same job. Terminal
1302
+ installation decisions are not invoked again on replay.
1303
+
1304
+ For a source HTML build, that nearest package must itself be a pnpm workspace
1305
+ member, or the root package of a standalone App repository. A nested package
1306
+ outside the workspace inventory fails with `WorkspaceBuildError` code
1307
+ `non_workspace_package`; it never inherits the host package's dependency
1308
+ closure. Notes is an explicit workspace member with its own Agar and Zod
1309
+ dependencies and the builder's pinned Vite toolchain, separate from Oyster's
1310
+ host runtime.
1311
+
1312
+ The other tracked nested manifests are separate source inputs, not App HTML
1313
+ builds inside their parent workspace:
1314
+
1315
+ - `apps/bonsai/demo/koi-pond`: copied into its own demo Git repository.
1316
+ - `packages/create-fungi-app/template`: copied to the generated App's root.
1317
+ - `services/app-publisher/test/fixtures/size-hello`: a backend-only emission
1318
+ fixture, not a workspace HTML build.
1319
+ - `apps/tui/test/fixtures/fairy-ring-extension` and
1320
+ `runtimes/local/test/fixtures/{extension-compactor,extension-counter}`:
1321
+ installed extension fixtures, not App source builds.
1322
+ - `packages/computer/container` and
1323
+ `packages/cordyceps/upstream/{computerd,dofs,rpc}`: container/vendored package
1324
+ inputs, not App source entries.
1325
+ - `templates/vishnu-starter`: a standalone project template outside the pnpm
1326
+ workspace.
1327
+
1053
1328
  A captured UI bundle uses `kind: "bundled-html"`, an `entry` ending in
1054
1329
  `/index.html`, `assetBase: "./"`, and an explicit `assets` policy with
1055
1330
  `maxManifestBytes`, `maxObjects`, `maxObjectBytes`, and `maxTotalBytes`. Set
1056
1331
  `backend: null` for UI only, or supply a repository-relative `backend.entry`
1057
1332
  with the backend runtime, export name, and compatibility date. The compiler
1058
1333
  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.
1334
+ Worker entry as `entry` and omits `backend.entry`. First-party HTML recipes
1335
+ retain their catalog-owned compilation steps.
1061
1336
 
1062
1337
  For UI source, omit `kind` and use a closed HTML recipe entry, such as
1063
1338
  `ui/index.html` for Notes at its repository root. It requires `assets` and
@@ -1077,9 +1352,19 @@ implicit install. Only the Computer build (`buildTeamCandidate`, composed with
1077
1352
  arbitrary package code, isolated by that admitted Computer. Every other
1078
1353
  composition, including `packRelease` and `create-fungi-app`, refuses such a
1079
1354
  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
1355
+ is pinned as `package-json-fungi-build-v4` in the recipe toolchain; an executor
1081
1356
  refuses a recipe pinned under another policy.
1082
1357
 
1358
+ The App's HTML module scripts, shell module and selected backend select its
1359
+ workspace compile roots. The maintained HTML parser and esbuild follow local
1360
+ runtime imports; erased type imports and authoring scripts do not pull a host
1361
+ package into that compilation. Declared package stylesheets are also roots. The
1362
+ planning pass follows code imports only; the actual Vite build retains ownership
1363
+ of asset loading, validation and emission. Each selected workspace package still
1364
+ builds its full declared products and runtime workspace dependency closure, in
1365
+ dependency order. Dependency manifests, hydration and the Computer-only
1366
+ package-script guard are unchanged.
1367
+
1083
1368
  `createLocalArtifactWriter(directory, recipe.assets)` writes the App root and
1084
1369
  all referenced UI/backend objects. It uses the supplied asset bounds and
1085
1370
  maintained MIME metadata. Bundled UI plus a backend may hydrate a captured
@@ -1116,6 +1401,33 @@ selected tenant. Canonical member roles remain distinct from owner authority.
1116
1401
  Public App reads remain available; unknown origins and policy refusals fail
1117
1402
  closed. `ownerOrKeyTenancy` is the single-tenant case.
1118
1403
 
1404
+ ## Machine link protocol
1405
+
1406
+ `@fungi.computer/seam/machine-link-protocol` exposes the bounded read/leave
1407
+ decoder and `createMachineLinkPostChannel`. The channel transports ordinary
1408
+ Carrier Post envelopes and their receiver receipts without rewriting cargo. The
1409
+ host supplies frame-byte, in-flight and acknowledgment-time limits.
1410
+
1411
+ `machineLinkDialUrl(issuer, teamId, deviceId)` and
1412
+ `machineLinkAudience(teamId, deviceId)` give both hosts the same GET path and
1413
+ Seam audience. Enrollment is separately authorized by the account owner; a
1414
+ native dial proves possession of the enrolled device key, not command
1415
+ acceptance.
1416
+
1417
+ The host still owns authenticated native sockets, currentness and connection
1418
+ lifetime. Call `close()` when a connection is lost; create a new channel for its
1419
+ successor. A closed channel, timeout or capacity failure rejects pending I/O
1420
+ without asserting command refusal or custody. Carrier remains the durable outbox
1421
+ and retry owner. In-flight state is not a persistent mailbox or a Capnweb
1422
+ capability table.
1423
+
1424
+ A Post acknowledgment is not Shiitake command acceptance. The Hub's home-aware
1425
+ Agent mail route still requires the recipient's actual Accepted receipt. Focused
1426
+ channel and route tests do not qualify native transport, reconnect, Hub restart
1427
+ or Agent execution. The rebuild now wires Local service, Team native receiver
1428
+ and hosted actual-home consumers together; their whole-bundle native
1429
+ qualification and production activation remain separate claims.
1430
+
1119
1431
  ## Approve an installed App's permissions
1120
1432
 
1121
1433
  After selecting a new Release, the owner can open **App access** beside the App
@@ -1126,10 +1438,22 @@ preserved; a newly selected capability is approved within this Oyster's scope.
1126
1438
  This updates the installation without reinstalling the App or changing its data.
1127
1439
 
1128
1440
  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.
1441
+ `storeGeneration`, `authorityVersion`, Release `artifactDigest`, and a bounded
1442
+ `{kind:"change-grants",changes:[{capability,grant}]}` command. Each grant
1443
+ contains `operations` and `resources`; `null` revokes that capability. Unnamed
1444
+ approvals retain their existing operation and resource ceilings. Agents, keys,
1445
+ visitors and App-origin requests cannot approve. A replaced store, stale
1446
+ Release/version or concurrent approval returns `409`; the page refreshes the
1447
+ facts and rebases touched edits for review. The required `InstallationStore`
1448
+ exposes its durable `generation()` and
1449
+ `compareAndPut(record, expectedVersion, expectedGeneration)`. The latter returns
1450
+ a serializable commit/refusal result across Durable Object RPC and checks both
1451
+ pins in the existing commit transaction.
1452
+
1453
+ ## Breaking App send contract
1454
+
1455
+ App `AGENTS.send` requires a caller-owned `requestId`, minted once per user
1456
+ intent and reused unchanged on retry after an UNKNOWN outcome. The standalone
1457
+ adapter no longer generates a replacement. See the
1458
+ [App Agent send API reference](docs/overview.md#app-agent-send-api--breaking-change)
1459
+ for the complete input and migration note.
package/dist/agents.d.ts CHANGED
@@ -3,7 +3,8 @@
3
3
  * the App page calls. Apps reach agents through Agar's `agent.list`,
4
4
  * `agent.manage`, `agent.read` and `agent.send` contracts, so an App written
5
5
  * for the Hub (Grove) runs unchanged. Each agent's own transport is
6
- * Shiitake's HTTP routes (`/sessions…`, `/acp`), served by the adapter.
6
+ * Shiitake's HTTP routes (`/sessions…`, `/acp`) and Knapsack's Session browser
7
+ * transport (`/browser/…`), served by the adapter behind the same App grants.
7
8
  */
8
9
  import { Hono } from "hono";
9
10
  import type { AppToolPolicy } from "./apps/tools.js";
@@ -104,7 +105,7 @@ export type OysterAgents = Readonly<{
104
105
  addMcpServer(agentId: string, input: AgentMcpServerInput): Promise<AgentMcpServer | AgentMcpServerRefusal>;
105
106
  /** Disconnect one MCP server; `false` when the Agent or server is unknown. */
106
107
  removeMcpServer(agentId: string, serverId: string): Promise<boolean>;
107
- /** Serve one Shiitake transport request (`/sessions…`, `/acp`) for an agent. */
108
+ /** Serve this Agent's Shiitake and Knapsack Session browser transports. */
108
109
  fetch(agentId: string, request: Request): Promise<Response | null>;
109
110
  /** This member's Session and Workflow review rows, admitted by the host. */
110
111
  inbox(): Promise<OysterInbox>;
package/dist/agents.js CHANGED
@@ -3,7 +3,8 @@
3
3
  * the App page calls. Apps reach agents through Agar's `agent.list`,
4
4
  * `agent.manage`, `agent.read` and `agent.send` contracts, so an App written
5
5
  * for the Hub (Grove) runs unchanged. Each agent's own transport is
6
- * Shiitake's HTTP routes (`/sessions…`, `/acp`), served by the adapter.
6
+ * Shiitake's HTTP routes (`/sessions…`, `/acp`) and Knapsack's Session browser
7
+ * transport (`/browser/…`), served by the adapter behind the same App grants.
7
8
  */
8
9
  import { Hono } from "hono";
9
10
  import * as Schema from "effect/Schema";
@@ -205,7 +206,8 @@ function createAgentRoutes(agents) {
205
206
  const path = url.pathname.slice(`/agents/${agentId}`.length);
206
207
  if (!(path === "/acp" ||
207
208
  path === "/sessions" ||
208
- path.startsWith("/sessions/")))
209
+ path.startsWith("/sessions/") ||
210
+ path.startsWith("/browser/")))
209
211
  return refusal(404, "Not found");
210
212
  const length = Number(incoming.headers.get("content-length") ?? "0");
211
213
  if (length > maxAgentRequestBytes)