@fungi.computer/oyster 0.0.0-stage → 0.1.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 (339) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1134 -2
  3. package/dist/agents.d.ts +114 -0
  4. package/dist/agents.js +322 -0
  5. package/dist/app-builds.d.ts +210 -0
  6. package/dist/app-builds.js +499 -0
  7. package/dist/app-sandbox.d.ts +2 -0
  8. package/dist/app-sandbox.js +2 -0
  9. package/dist/app-sources.d.ts +31 -0
  10. package/dist/app-sources.js +370 -0
  11. package/dist/app-storage.d.ts +141 -0
  12. package/dist/app-storage.js +281 -0
  13. package/dist/apps/gate.d.ts +105 -0
  14. package/dist/apps/gate.js +273 -0
  15. package/dist/apps/index.d.ts +5 -0
  16. package/dist/apps/index.js +5 -0
  17. package/dist/apps/installation.d.ts +248 -0
  18. package/dist/apps/installation.js +307 -0
  19. package/dist/apps/operations.d.ts +8 -0
  20. package/dist/apps/operations.js +98 -0
  21. package/dist/apps/tools.d.ts +33 -0
  22. package/dist/apps/tools.js +17 -0
  23. package/dist/auth.d.ts +11 -0
  24. package/dist/auth.js +93 -0
  25. package/dist/backend-request.d.ts +3 -0
  26. package/dist/backend-request.js +53 -0
  27. package/dist/build/artifact-writer.d.ts +40 -0
  28. package/dist/build/artifact-writer.js +181 -0
  29. package/dist/build/build-jobs.d.ts +128 -0
  30. package/dist/build/build-jobs.js +203 -0
  31. package/dist/build/build-policy.d.ts +10 -0
  32. package/dist/build/build-policy.js +10 -0
  33. package/dist/build/build-step.d.ts +37 -0
  34. package/dist/build/build-step.js +28 -0
  35. package/dist/build/bundled-html.d.ts +7 -0
  36. package/dist/build/bundled-html.js +64 -0
  37. package/dist/build/candidate-builder.d.ts +68 -0
  38. package/dist/build/candidate-builder.js +91 -0
  39. package/dist/build/candidate-cli.d.ts +1 -0
  40. package/dist/build/candidate-cli.js +119 -0
  41. package/dist/build/candidate-cli.mjs +28170 -0
  42. package/dist/build/content-objects.d.ts +37 -0
  43. package/dist/build/content-objects.js +53 -0
  44. package/dist/build/definition-emission.d.ts +34 -0
  45. package/dist/build/definition-emission.js +201 -0
  46. package/dist/build/effect.d.ts +249 -0
  47. package/dist/build/effect.js +436 -0
  48. package/dist/build/git-environment.d.ts +9 -0
  49. package/dist/build/git-environment.js +60 -0
  50. package/dist/build/html-recipe.d.ts +20 -0
  51. package/dist/build/html-recipe.js +51 -0
  52. package/dist/build/local-build.d.ts +67 -0
  53. package/dist/build/local-build.js +1128 -0
  54. package/dist/build/snapshot-package-store.d.ts +12 -0
  55. package/dist/build/snapshot-package-store.js +36 -0
  56. package/dist/build/snapshot-program-scripts.d.ts +6 -0
  57. package/dist/build/snapshot-program-scripts.js +2 -0
  58. package/dist/build/snapshot-programs/snapshot-path.d.ts +2 -0
  59. package/dist/build/snapshot-programs/snapshot-path.js +6 -0
  60. package/dist/build/submission-contract.d.ts +828 -0
  61. package/dist/build/submission-contract.js +180 -0
  62. package/dist/build/workspace-build.d.ts +55 -0
  63. package/dist/build/workspace-build.js +200 -0
  64. package/dist/build-contract.d.ts +7 -0
  65. package/dist/build-contract.js +7 -0
  66. package/dist/build-egress.d.ts +62 -0
  67. package/dist/build-egress.js +148 -0
  68. package/dist/build-jobs.d.ts +2 -0
  69. package/dist/build-jobs.js +2 -0
  70. package/dist/build.d.ts +12 -0
  71. package/dist/build.js +14 -0
  72. package/dist/capability-descriptor.d.ts +46 -0
  73. package/dist/capability-descriptor.js +51 -0
  74. package/dist/catalog-frame.d.ts +39 -0
  75. package/dist/catalog-frame.js +72 -0
  76. package/dist/cause-class.d.ts +19 -0
  77. package/dist/cause-class.js +54 -0
  78. package/dist/cloudflare.d.ts +123 -0
  79. package/dist/cloudflare.js +391 -0
  80. package/dist/computer-terminal.d.ts +38 -0
  81. package/dist/computer-terminal.js +410 -0
  82. package/dist/computer.d.ts +60 -0
  83. package/dist/computer.js +33 -0
  84. package/dist/event-recipients.d.ts +57 -0
  85. package/dist/event-recipients.js +144 -0
  86. package/dist/facet-invocation.d.ts +4 -0
  87. package/dist/facet-invocation.js +45 -0
  88. package/dist/files.d.ts +42 -0
  89. package/dist/files.js +123 -0
  90. package/dist/frame-binding-wire-script.d.ts +2 -0
  91. package/dist/frame-binding-wire-script.js +1 -0
  92. package/dist/frame-binding-wire.d.ts +28 -0
  93. package/dist/frame-binding-wire.js +54 -0
  94. package/dist/git-admission.d.ts +52 -0
  95. package/dist/git-admission.js +188 -0
  96. package/dist/git-auth.d.ts +11 -0
  97. package/dist/git-auth.js +27 -0
  98. package/dist/git-door.d.ts +35 -0
  99. package/dist/git-door.js +108 -0
  100. package/dist/git-journal.d.ts +31 -0
  101. package/dist/git-journal.js +188 -0
  102. package/dist/git.d.ts +31 -0
  103. package/dist/git.js +64 -0
  104. package/dist/guest-policy.d.ts +9 -0
  105. package/dist/guest-policy.js +13 -0
  106. package/dist/host.d.ts +244 -0
  107. package/dist/host.js +1043 -0
  108. package/dist/index.d.ts +166 -0
  109. package/dist/index.js +320 -0
  110. package/dist/installation-consent.d.ts +79 -0
  111. package/dist/installation-consent.js +82 -0
  112. package/dist/internal/background-origins.d.ts +3 -0
  113. package/dist/internal/background-origins.js +8 -0
  114. package/dist/internal/delivery.d.ts +12 -0
  115. package/dist/internal/delivery.js +167 -0
  116. package/dist/internal/host-gate.d.ts +14 -0
  117. package/dist/internal/host-gate.js +62 -0
  118. package/dist/internal/registry-installations.d.ts +6 -0
  119. package/dist/internal/registry-installations.js +12 -0
  120. package/dist/invocation.d.ts +251 -0
  121. package/dist/invocation.js +377 -0
  122. package/dist/keys.d.ts +48 -0
  123. package/dist/keys.js +52 -0
  124. package/dist/mcp-owner.d.ts +27 -0
  125. package/dist/mcp-owner.js +155 -0
  126. package/dist/mcp.d.ts +89 -0
  127. package/dist/mcp.js +429 -0
  128. package/dist/media.d.ts +23 -0
  129. package/dist/media.js +12 -0
  130. package/dist/pack.d.ts +79 -0
  131. package/dist/pack.js +151 -0
  132. package/dist/page/capabilities.d.ts +23 -0
  133. package/dist/page/capabilities.js +256 -0
  134. package/dist/page/computer-handles.d.ts +79 -0
  135. package/dist/page/computer-handles.js +44 -0
  136. package/dist/page/gill-observation.d.ts +4 -0
  137. package/dist/page/gill-observation.js +31 -0
  138. package/dist/page/lifetime.d.ts +6 -0
  139. package/dist/page/lifetime.js +19 -0
  140. package/dist/page/mount.d.ts +163 -0
  141. package/dist/page/mount.js +390 -0
  142. package/dist/page/storage.d.ts +3 -0
  143. package/dist/page/storage.js +82 -0
  144. package/dist/page/terminal.d.ts +5 -0
  145. package/dist/page/terminal.js +7 -0
  146. package/dist/page/window-launch.d.ts +18 -0
  147. package/dist/page/window-launch.js +11 -0
  148. package/dist/page-script.d.ts +14 -0
  149. package/dist/page-script.js +5 -0
  150. package/dist/r2-files.d.ts +3 -0
  151. package/dist/r2-files.js +79 -0
  152. package/dist/release.d.ts +83 -0
  153. package/dist/release.js +167 -0
  154. package/dist/root-data.d.ts +86 -0
  155. package/dist/root-data.js +69 -0
  156. package/dist/shell/app-directory.d.ts +542 -0
  157. package/dist/shell/app-directory.js +199 -0
  158. package/dist/shell/browser-actions.d.ts +7 -0
  159. package/dist/shell/browser-actions.js +56 -0
  160. package/dist/shell/command-host.d.ts +112 -0
  161. package/dist/shell/command-host.js +502 -0
  162. package/dist/shell/commands.d.ts +25 -0
  163. package/dist/shell/commands.js +78 -0
  164. package/dist/shell/index.d.ts +8 -0
  165. package/dist/shell/index.js +8 -0
  166. package/dist/shell/keys.d.ts +41 -0
  167. package/dist/shell/keys.js +134 -0
  168. package/dist/shell/shell-module.d.ts +55 -0
  169. package/dist/shell/shell-module.js +86 -0
  170. package/dist/shell/window-manager.d.ts +124 -0
  171. package/dist/shell/window-manager.js +711 -0
  172. package/dist/shell/workbench-contract.d.ts +68 -0
  173. package/dist/shell/workbench-contract.js +1 -0
  174. package/dist/shell/workbench-directory.d.ts +332 -0
  175. package/dist/shell/workbench-directory.js +35 -0
  176. package/dist/shell/workbench-host-types.d.ts +96 -0
  177. package/dist/shell/workbench-host-types.js +1 -0
  178. package/dist/shell/workbench-host.d.ts +34 -0
  179. package/dist/shell/workbench-host.js +347 -0
  180. package/dist/shell/workbench-model.d.ts +192 -0
  181. package/dist/shell/workbench-model.js +53 -0
  182. package/dist/shell/workbench.d.ts +4 -0
  183. package/dist/shell/workbench.js +2 -0
  184. package/dist/source/fork.d.ts +27 -0
  185. package/dist/source/fork.js +46 -0
  186. package/dist/source/index.d.ts +10 -0
  187. package/dist/source/index.js +10 -0
  188. package/dist/source/seed-receipt.d.ts +53 -0
  189. package/dist/source/seed-receipt.js +18 -0
  190. package/dist/source/team-source.d.ts +32 -0
  191. package/dist/source/team-source.js +42 -0
  192. package/dist/source/template-push.d.ts +33 -0
  193. package/dist/source/template-push.js +223 -0
  194. package/dist/source/template-repository.d.ts +4 -0
  195. package/dist/source/template-repository.js +21 -0
  196. package/dist/source/template-seed.d.ts +68 -0
  197. package/dist/source/template-seed.js +80 -0
  198. package/dist/standalone/agent-directory.d.ts +4 -0
  199. package/dist/standalone/agent-directory.js +4 -0
  200. package/dist/standalone/agents.d.ts +220 -0
  201. package/dist/standalone/agents.js +795 -0
  202. package/dist/standalone/app-builds-binding.d.ts +36 -0
  203. package/dist/standalone/app-builds-binding.js +119 -0
  204. package/dist/standalone/app-set.d.ts +91 -0
  205. package/dist/standalone/app-set.js +395 -0
  206. package/dist/standalone/app-source.d.ts +41 -0
  207. package/dist/standalone/app-source.js +59 -0
  208. package/dist/standalone/builds.d.ts +18 -0
  209. package/dist/standalone/builds.js +101 -0
  210. package/dist/standalone/computer-egress.d.ts +37 -0
  211. package/dist/standalone/computer-egress.js +111 -0
  212. package/dist/standalone/computer.d.ts +122 -0
  213. package/dist/standalone/computer.js +714 -0
  214. package/dist/standalone/install-apps-cli.d.ts +2 -0
  215. package/dist/standalone/install-apps-cli.js +126 -0
  216. package/dist/standalone/instance.d.ts +35 -0
  217. package/dist/standalone/instance.js +74 -0
  218. package/dist/standalone/media.d.ts +76 -0
  219. package/dist/standalone/media.js +227 -0
  220. package/dist/standalone/preview.d.ts +51 -0
  221. package/dist/standalone/preview.js +167 -0
  222. package/dist/standalone/registry.d.ts +102 -0
  223. package/dist/standalone/registry.js +193 -0
  224. package/dist/standalone/tenancy.d.ts +42 -0
  225. package/dist/standalone/tenancy.js +241 -0
  226. package/dist/standalone/worker.d.ts +55 -0
  227. package/dist/standalone/worker.js +147 -0
  228. package/dist/standalone/workflow-authority-readers.d.ts +16 -0
  229. package/dist/standalone/workflow-authority-readers.js +35 -0
  230. package/dist/standalone/workflow-binding.d.ts +216 -0
  231. package/dist/standalone/workflow-binding.js +217 -0
  232. package/dist/standalone/workflow-events.d.ts +46 -0
  233. package/dist/standalone/workflow-events.js +39 -0
  234. package/dist/standalone/workflow-git-capture.d.ts +7 -0
  235. package/dist/standalone/workflow-git-capture.js +62 -0
  236. package/dist/standalone/workflow-host.d.ts +931 -0
  237. package/dist/standalone/workflow-host.js +751 -0
  238. package/dist/standalone/workflow-node-capture.d.ts +13 -0
  239. package/dist/standalone/workflow-node-capture.js +72 -0
  240. package/dist/standalone/workflow-root-authority.d.ts +27 -0
  241. package/dist/standalone/workflow-root-authority.js +87 -0
  242. package/dist/standalone/workflow-schedules.d.ts +52 -0
  243. package/dist/standalone/workflow-schedules.js +40 -0
  244. package/dist/standalone/workflow-trigger-host.d.ts +1694 -0
  245. package/dist/standalone/workflow-trigger-host.js +52 -0
  246. package/dist/standalone/workflows.d.ts +657 -0
  247. package/dist/standalone/workflows.js +554 -0
  248. package/dist/storage-frame.d.ts +64 -0
  249. package/dist/storage-frame.js +28 -0
  250. package/dist/tenancy/auth0.d.ts +64 -0
  251. package/dist/tenancy/auth0.js +198 -0
  252. package/dist/tenancy/cloudflare/rate-limit.d.ts +8 -0
  253. package/dist/tenancy/cloudflare/rate-limit.js +19 -0
  254. package/dist/tenancy/cloudflare/registry.d.ts +50 -0
  255. package/dist/tenancy/cloudflare/registry.js +301 -0
  256. package/dist/tenancy/cloudflare.d.ts +2 -0
  257. package/dist/tenancy/cloudflare.js +2 -0
  258. package/dist/tenancy/cold-directory.d.ts +13 -0
  259. package/dist/tenancy/cold-directory.js +43 -0
  260. package/dist/tenancy/contracts.d.ts +195 -0
  261. package/dist/tenancy/contracts.js +1 -0
  262. package/dist/tenancy/index.d.ts +19 -0
  263. package/dist/tenancy/index.js +156 -0
  264. package/dist/tenancy/owner-key.d.ts +26 -0
  265. package/dist/tenancy/owner-key.js +63 -0
  266. package/dist/tenancy/oyster.d.ts +4 -0
  267. package/dist/tenancy/oyster.js +25 -0
  268. package/dist/tenancy/registry-record.d.ts +7 -0
  269. package/dist/tenancy/registry-record.js +44 -0
  270. package/dist/tenancy/registry.d.ts +27 -0
  271. package/dist/tenancy/registry.js +138 -0
  272. package/dist/tenancy/resolve.d.ts +7 -0
  273. package/dist/tenancy/resolve.js +64 -0
  274. package/dist/tenant.d.ts +81 -0
  275. package/dist/tenant.js +71 -0
  276. package/dist/testing/capabilities.d.ts +29 -0
  277. package/dist/testing/capabilities.js +130 -0
  278. package/dist/testing/catalog.d.ts +3 -0
  279. package/dist/testing/catalog.js +53 -0
  280. package/dist/testing/index.d.ts +10 -0
  281. package/dist/testing/index.js +10 -0
  282. package/dist/testing/installation.d.ts +11 -0
  283. package/dist/testing/installation.js +25 -0
  284. package/dist/testing/native-worker.d.ts +21 -0
  285. package/dist/testing/native-worker.js +106 -0
  286. package/dist/testing/worker.d.ts +18 -0
  287. package/dist/testing/worker.js +67 -0
  288. package/dist/testing/workers-runtime.d.ts +24 -0
  289. package/dist/testing/workers-runtime.js +33 -0
  290. package/dist/webhook-delivery.d.ts +14 -0
  291. package/dist/webhook-delivery.js +17 -0
  292. package/dist/workers-git.d.ts +285 -0
  293. package/dist/workers-git.js +1174 -0
  294. package/dist/workers.d.ts +352 -0
  295. package/dist/workers.js +868 -0
  296. package/docs/api/README.md +82 -0
  297. package/docs/api/agents.md +332 -0
  298. package/docs/api/app-storage.md +209 -0
  299. package/docs/api/apps.md +1375 -0
  300. package/docs/api/auth.md +63 -0
  301. package/docs/api/build-contract.md +1304 -0
  302. package/docs/api/build.md +1712 -0
  303. package/docs/api/cloudflare.md +421 -0
  304. package/docs/api/computer.md +203 -0
  305. package/docs/api/files.md +117 -0
  306. package/docs/api/git.md +470 -0
  307. package/docs/api/host.md +1328 -0
  308. package/docs/api/index.md +430 -0
  309. package/docs/api/invocation.md +738 -0
  310. package/docs/api/keys.md +175 -0
  311. package/docs/api/mcp.md +301 -0
  312. package/docs/api/media.md +40 -0
  313. package/docs/api/modules.md +50 -0
  314. package/docs/api/pack.md +141 -0
  315. package/docs/api/page/mount.md +298 -0
  316. package/docs/api/root-data.md +111 -0
  317. package/docs/api/shell/app-directory.md +599 -0
  318. package/docs/api/shell/command-host.md +224 -0
  319. package/docs/api/shell/commands.md +197 -0
  320. package/docs/api/shell/keys.md +183 -0
  321. package/docs/api/shell/shell-module.md +204 -0
  322. package/docs/api/shell/window-manager.md +267 -0
  323. package/docs/api/shell/workbench-directory.md +119 -0
  324. package/docs/api/shell/workbench.md +205 -0
  325. package/docs/api/shell.md +1915 -0
  326. package/docs/api/tenancy/auth0.md +110 -0
  327. package/docs/api/tenancy/cloudflare.md +221 -0
  328. package/docs/api/tenancy/cold-directory.md +46 -0
  329. package/docs/api/tenancy/owner-key.md +63 -0
  330. package/docs/api/tenancy/registry.md +77 -0
  331. package/docs/api/tenancy/resolve.md +69 -0
  332. package/docs/api/tenancy.md +430 -0
  333. package/docs/api/tenant.md +194 -0
  334. package/docs/api/workers-git.md +1646 -0
  335. package/docs/api/workers.md +1691 -0
  336. package/docs/site/agent-quickstart.mdx +346 -0
  337. package/examples/standalone/worker.ts +7 -0
  338. package/examples/standalone/wrangler.jsonc +82 -0
  339. package/package.json +300 -3
@@ -0,0 +1,12 @@
1
+ import { type AppGitPushFact } from "@fungi.computer/agar/git";
2
+ import { type BackgroundWake } from "@fungi.computer/puffball/background-replay";
3
+ import type { OysterHost } from "../host.js";
4
+ /** Re-admit a retained push and deliver it directly to the current Instance.
5
+ * Durable host delivery uses the same fresh checks as initial push admission;
6
+ * this path never calls the producer's optional enqueue callback. */
7
+ export declare function deliverGitPush(host: Pick<OysterHost, "artifacts" | "installations" | "instances" | "guestLimits">, installationId: string, fact: AppGitPushFact): Promise<void>;
8
+ /** Re-admit a stored member through the same directory and method gate as a click. */
9
+ export declare function deliverBackground(host: OysterHost, raw: BackgroundWake): Promise<Readonly<{
10
+ status: "accepted" | "refused";
11
+ more: boolean;
12
+ }>>;
@@ -0,0 +1,167 @@
1
+ import { appGitPushFactSchema, } from "@fungi.computer/agar/git";
2
+ import { backgroundWakeSchema, readmitBackground, } from "@fungi.computer/puffball/background-replay";
3
+ import { z } from "zod";
4
+ import { admits } from "../apps/gate.js";
5
+ import { backgroundWakeKey } from "../git-journal.js";
6
+ import { guestLimits } from "../guest-policy.js";
7
+ import { selectionOf } from "../index.js";
8
+ import { APP_INVOCATION_CEILING_MS, invocationAuthorityOf, invocationCallerContext, invocationFacts, } from "../invocation.js";
9
+ import { defaultAssetPolicy, verifyRelease, } from "../release.js";
10
+ import { admittedBackgroundOrigins } from "./background-origins.js";
11
+ import { hostGateFacts, releaseCache } from "./host-gate.js";
12
+ async function admitGitPush(host, readRelease, installationId, raw) {
13
+ const fact = appGitPushFactSchema.parse(raw);
14
+ const installation = await host.installations.get(installationId);
15
+ if (installation?.status !== "enabled")
16
+ throw new Error("Git push installation unavailable");
17
+ const release = await readRelease(installation.release);
18
+ const { declaration } = release;
19
+ if (!declaration?.receives?.includes("git.push") ||
20
+ !declaration.capabilities.includes("git.repository"))
21
+ return null;
22
+ const current = await host.installations.get(installationId);
23
+ if (current?.status !== "enabled" ||
24
+ current.instanceId !== installation.instanceId ||
25
+ current.authorityVersion !== installation.authorityVersion ||
26
+ current.release.releaseId !== installation.release.releaseId)
27
+ throw new Error("Git push installation changed");
28
+ return { installation: current, release, fact };
29
+ }
30
+ function deliverAdmittedGitPush(host, installation, release, fact) {
31
+ return host.instances
32
+ .get(selectionOf(installation).instanceId, guestLimits(host.guestLimits))
33
+ .onGitPush({
34
+ ...invocationAuthorityOf(installation, release),
35
+ subject: {
36
+ kind: "lifecycle",
37
+ name: "onGitPush",
38
+ eventId: fact.eventId,
39
+ },
40
+ deadlineAt: Date.now() + APP_INVOCATION_CEILING_MS,
41
+ }, fact);
42
+ }
43
+ /** Re-admit a retained push and deliver it directly to the current Instance.
44
+ * Durable host delivery uses the same fresh checks as initial push admission;
45
+ * this path never calls the producer's optional enqueue callback. */
46
+ export async function deliverGitPush(host, installationId, fact) {
47
+ const admitted = await admitGitPush(host, (release) => verifyRelease(host.artifacts, release, defaultAssetPolicy, false), installationId, fact);
48
+ if (admitted !== null)
49
+ await deliverAdmittedGitPush(host, admitted.installation, admitted.release, admitted.fact);
50
+ }
51
+ /** Re-admit a stored member through the same directory and method gate as a click. */
52
+ export async function deliverBackground(host, raw) {
53
+ const wake = backgroundWakeSchema.parse(raw);
54
+ const available = async () => {
55
+ const current = await host.installations.get(wake.installationId);
56
+ return current?.status === "enabled" && current.instanceId !== null
57
+ ? current
58
+ : null;
59
+ };
60
+ if ((await available()) === null)
61
+ return { status: "refused", more: false };
62
+ const readRelease = releaseCache(host.artifacts);
63
+ let admitted;
64
+ const decision = await readmitBackground(wake, {
65
+ resolve: (actor) => host.auth.resolve(actor),
66
+ admit: async (principal) => {
67
+ const installation = await available();
68
+ if (installation === null)
69
+ return { kind: "refused", code: "unavailable" };
70
+ const release = await readRelease(installation.release);
71
+ const method = release.declaration?.backend?.sync?.method;
72
+ if (method === undefined ||
73
+ release.api?.methods[method]?.effect !== "write")
74
+ return { kind: "refused", code: "denied" };
75
+ const gate = await hostGateFacts(host, installation, release, principal);
76
+ const verdict = admits(gate, {
77
+ kind: "method",
78
+ name: method,
79
+ input: {},
80
+ via: "ui",
81
+ });
82
+ if (verdict.kind === "refused") {
83
+ switch (verdict.code) {
84
+ case "unavailable":
85
+ case "stale_authority":
86
+ case "not_approved":
87
+ case "level_too_low":
88
+ case "requires_not_granted":
89
+ return { kind: "refused", code: verdict.code };
90
+ case "not_declared":
91
+ case "approval_required":
92
+ case "invalid_input":
93
+ case "visitor_read_only":
94
+ return { kind: "refused", code: "denied" };
95
+ }
96
+ }
97
+ const origins = admittedBackgroundOrigins(gate);
98
+ admitted = {
99
+ installation,
100
+ release,
101
+ method,
102
+ grants: verdict.grants,
103
+ origins,
104
+ };
105
+ return {
106
+ kind: "admitted",
107
+ context: invocationCallerContext(principal, host.computerId ?? null),
108
+ };
109
+ },
110
+ });
111
+ if (decision.kind === "retry")
112
+ throw new Error("Background admission changed; retry");
113
+ if (decision.kind === "refused") {
114
+ const current = await available();
115
+ if (current === null || decision.code === "installation_unavailable")
116
+ return { status: "refused", more: false };
117
+ const release = await readRelease(current.release);
118
+ const authority = invocationAuthorityOf(current, release);
119
+ const refusal = { lane: wake.lane, code: decision.code };
120
+ await host.instances
121
+ .get(authority.instanceId, guestLimits(host.guestLimits))
122
+ .syncRefused({
123
+ ...authority,
124
+ grants: [],
125
+ subject: { kind: "lifecycle", name: "syncRefused", ...refusal },
126
+ deadlineAt: Date.now() + APP_INVOCATION_CEILING_MS,
127
+ }, refusal);
128
+ return { status: "accepted", more: false };
129
+ }
130
+ if (!admitted?.release.api)
131
+ throw new Error("Background continuation was not admitted");
132
+ const deadlineAt = Date.now() + APP_INVOCATION_CEILING_MS;
133
+ const facts = invocationFacts.fromCaller({
134
+ ...invocationAuthorityOf(admitted.installation, admitted.release),
135
+ grants: admitted.grants,
136
+ }, {
137
+ ...decision.context,
138
+ idempotencyKey: backgroundWakeKey(wake),
139
+ }, { deadlineAt, grantExpiresAt: deadlineAt }, { name: admitted.method, effect: "write" });
140
+ if (facts.subject.kind !== "call")
141
+ throw new Error("Background replay must be a call");
142
+ const result = await host.instances
143
+ .get(facts.instanceId, guestLimits(host.guestLimits))
144
+ .call({
145
+ ...facts,
146
+ grants: [
147
+ ...facts.grants,
148
+ { binding: "BACKGROUND", operations: ["admit"] },
149
+ ],
150
+ subject: {
151
+ ...facts.subject,
152
+ replay: {
153
+ lane: wake.lane,
154
+ epoch: wake.epoch,
155
+ admitted: admitted.origins,
156
+ },
157
+ },
158
+ }, admitted.release.api, admitted.method, {});
159
+ if (!result.ok)
160
+ throw new Error(`Background continuation failed: ${result.code}`);
161
+ if (result.value.kind === "refused")
162
+ throw new Error(`Background continuation refused: ${result.value.code}`);
163
+ return {
164
+ status: "accepted",
165
+ more: z.object({ more: z.boolean() }).parse(result.value.value).more,
166
+ };
167
+ }
@@ -0,0 +1,14 @@
1
+ /** Release verification and gate facts shared by the HTTP host and its delivery engines. */
2
+ import type { Release } from "@fungi.computer/puffball";
3
+ import type { GateFacts } from "../apps/gate.js";
4
+ import type { Installation } from "../apps/installation.js";
5
+ import type { OysterHost, OysterPrincipal } from "../host.js";
6
+ import { type ArtifactStore, type VerifiedRelease } from "../release.js";
7
+ /**
8
+ * Verify each Release once per process: a failed read is not cached, so the
9
+ * next request reads it again. Installation verified every asset; serving
10
+ * verifies each on read.
11
+ */
12
+ export declare function releaseCache(artifacts: ArtifactStore): (release: Release) => Promise<VerifiedRelease>;
13
+ /** The gate facts one principal presents to one installation. */
14
+ export declare function hostGateFacts(host: OysterHost, installation: Installation, release: VerifiedRelease, principal: OysterPrincipal): Promise<GateFacts>;
@@ -0,0 +1,62 @@
1
+ import { emptyAppDeclaration } from "@fungi.computer/puffball/app";
2
+ import { deriveDeclaredOperations } from "../apps/operations.js";
3
+ import { keyAppAccess } from "../keys.js";
4
+ import { defaultAssetPolicy, verifyRelease, } from "../release.js";
5
+ /**
6
+ * Verify each Release once per process: a failed read is not cached, so the
7
+ * next request reads it again. Installation verified every asset; serving
8
+ * verifies each on read.
9
+ */
10
+ export function releaseCache(artifacts) {
11
+ const verified = new Map();
12
+ return (release) => {
13
+ const key = `${release.releaseId}:${release.artifact.artifactDigest}`;
14
+ let entry = verified.get(key);
15
+ if (entry === undefined) {
16
+ // A failed read is not cached: the next request reads the Release again.
17
+ // Installation verified every asset; serving verifies each on read.
18
+ entry = verifyRelease(artifacts, release, defaultAssetPolicy, false).then((value) => value, (cause) => {
19
+ verified.delete(key);
20
+ throw cause;
21
+ });
22
+ verified.set(key, entry);
23
+ }
24
+ return entry;
25
+ };
26
+ }
27
+ /**
28
+ * Only Agents and keys hold tool policy; members and visitors never ask. An
29
+ * Agent keeps its one blanket policy; a key's access is its own grants for
30
+ * this one installation -- `null` when its live key is unknown, revoked,
31
+ * expired or suspended, or simply does not reach this installation, which
32
+ * then refuses `not_approved`, the same as an Agent with no approval.
33
+ */
34
+ async function hostToolPolicy(host, actor, installationId) {
35
+ if (actor.kind === "agent")
36
+ return (await host.appToolPolicy?.(actor)) ?? null;
37
+ if (actor.kind !== "team-key")
38
+ return null;
39
+ const key = (await host.keys?.live(actor.keyId)) ?? null;
40
+ return key === null ? null : keyAppAccess(key, installationId);
41
+ }
42
+ /** The gate facts one principal presents to one installation. */
43
+ export async function hostGateFacts(host, installation, release, principal) {
44
+ const { actor } = principal;
45
+ const declaration = release.declaration ?? emptyAppDeclaration;
46
+ const toolPolicy = await hostToolPolicy(host, actor, installation.installationId);
47
+ return {
48
+ installation,
49
+ declaration,
50
+ declaredOperations: await deriveDeclaredOperations(installation.release, declaration, host.capabilities ?? []),
51
+ api: release.api,
52
+ principal: { actor, role: "role" in principal ? principal.role : null },
53
+ access: toolPolicy === null
54
+ ? null
55
+ : {
56
+ level: toolPolicy.level,
57
+ bindingName: installation.installationId,
58
+ revision: toolPolicy.revision,
59
+ grantedBy: toolPolicy.grantedBy,
60
+ },
61
+ };
62
+ }
@@ -0,0 +1,6 @@
1
+ /** The standalone registry Durable Object as an `InstallationStore`. */
2
+ import type { InstallationStore } from "../host.js";
3
+ import type { TenantScope } from "../tenant.js";
4
+ import type { OysterWorkerEnv } from "../workers.js";
5
+ /** Read and commit installations in the scope's registry. */
6
+ export declare function registryInstallations(env: Pick<OysterWorkerEnv, "REGISTRY">, scope: TenantScope): InstallationStore;
@@ -0,0 +1,12 @@
1
+ /** Read and commit installations in the scope's registry. */
2
+ export function registryInstallations(env, scope) {
3
+ const registry = () => env.REGISTRY.getByName(scope.durableName("registry"));
4
+ return {
5
+ list: async () => registry().list(),
6
+ get: async (id) => registry().get(id),
7
+ put: async (installation) => {
8
+ await registry().put(installation, scope.tenantId);
9
+ },
10
+ compareAndPut: async (installation, expectedAuthorityVersion) => await registry().compareAndPut(installation, expectedAuthorityVersion, scope.tenantId),
11
+ };
12
+ }
@@ -0,0 +1,251 @@
1
+ import type { OysterHost, OysterPrincipal } from "./host.js";
2
+ import type { VerifiedRelease } from "./release.js";
3
+ import type { Installation } from "./apps/installation.js";
4
+ import { type InvocationGrants } from "./apps/gate.js";
5
+ import * as Schema from "effect/Schema";
6
+ import { type AppBackendBindingName, type AppMethodCallContext, type WorkflowAttestation } from "@fungi.computer/puffball/app";
7
+ import { type BackgroundOrigin, type ClaimedJobAdmission } from "@fungi.computer/puffball/background-replay";
8
+ import { type InstanceId } from "./index.js";
9
+ /** One App invocation may hold authority for at most the Hub grant's lifetime. */
10
+ export declare const APP_INVOCATION_CEILING_MS = 60000;
11
+ /** Host-admitted facts; shape decoding does not establish host authority. */
12
+ export declare const invocationFactsSchema: Schema.Struct<{
13
+ readonly instanceId: Schema.Codec<InstanceId, string, never, never>;
14
+ readonly selection: Schema.Struct<{
15
+ readonly instanceId: Schema.Codec<InstanceId, string, never, never>;
16
+ readonly owner: Schema.Struct<{
17
+ readonly kind: Schema.Literals<readonly ["installation", "deployment"]>;
18
+ readonly id: Schema.NonEmptyString;
19
+ }>;
20
+ readonly appId: Schema.refine<import("@fungi.computer/puffball").AppId, Schema.String>;
21
+ readonly releaseId: Schema.refine<import("@fungi.computer/puffball").ReleaseId, Schema.String>;
22
+ readonly authorityVersion: Schema.Number;
23
+ }>;
24
+ readonly artifactDigest: Schema.refine<import("@fungi.computer/puffball").Sha256Digest, Schema.String>;
25
+ readonly subject: Schema.Union<readonly [Schema.Struct<{
26
+ readonly kind: Schema.Literal<"call">;
27
+ readonly context: Schema.declareConstructor<{
28
+ computerId: string | null;
29
+ actor: {
30
+ kind: "member";
31
+ memberId: string;
32
+ } | {
33
+ kind: "agent";
34
+ agentId: string;
35
+ } | {
36
+ kind: "team-key";
37
+ keyId: string;
38
+ };
39
+ backgroundLane?: string | undefined;
40
+ backgroundOrigin?: {
41
+ method: string;
42
+ seal: string;
43
+ } | undefined;
44
+ role?: "member" | "owner" | undefined;
45
+ soleMember?: true | undefined;
46
+ workflow?: {
47
+ workflowId: string;
48
+ runId: string;
49
+ nodeId: string;
50
+ armedBy: {
51
+ kind: "member";
52
+ memberId: string;
53
+ } | {
54
+ kind: "agent";
55
+ agentId: string;
56
+ } | {
57
+ kind: "team-key";
58
+ keyId: string;
59
+ };
60
+ } | undefined;
61
+ idempotencyKey?: string | undefined;
62
+ }, {
63
+ computerId: string | null;
64
+ actor: {
65
+ kind: "member";
66
+ memberId: string;
67
+ } | {
68
+ kind: "agent";
69
+ agentId: string;
70
+ } | {
71
+ kind: "team-key";
72
+ keyId: string;
73
+ };
74
+ backgroundLane?: string | undefined;
75
+ backgroundOrigin?: {
76
+ method: string;
77
+ seal: string;
78
+ } | undefined;
79
+ role?: "member" | "owner" | undefined;
80
+ soleMember?: true | undefined;
81
+ workflow?: {
82
+ workflowId: string;
83
+ runId: string;
84
+ nodeId: string;
85
+ armedBy: {
86
+ kind: "member";
87
+ memberId: string;
88
+ } | {
89
+ kind: "agent";
90
+ agentId: string;
91
+ } | {
92
+ kind: "team-key";
93
+ keyId: string;
94
+ };
95
+ } | undefined;
96
+ idempotencyKey?: string | undefined;
97
+ }, readonly [], {
98
+ computerId: string | null;
99
+ actor: {
100
+ kind: "member";
101
+ memberId: string;
102
+ } | {
103
+ kind: "agent";
104
+ agentId: string;
105
+ } | {
106
+ kind: "team-key";
107
+ keyId: string;
108
+ };
109
+ backgroundLane?: string | undefined;
110
+ backgroundOrigin?: {
111
+ method: string;
112
+ seal: string;
113
+ } | undefined;
114
+ role?: "member" | "owner" | undefined;
115
+ soleMember?: true | undefined;
116
+ workflow?: {
117
+ workflowId: string;
118
+ runId: string;
119
+ nodeId: string;
120
+ armedBy: {
121
+ kind: "member";
122
+ memberId: string;
123
+ } | {
124
+ kind: "agent";
125
+ agentId: string;
126
+ } | {
127
+ kind: "team-key";
128
+ keyId: string;
129
+ };
130
+ } | undefined;
131
+ idempotencyKey?: string | undefined;
132
+ }>;
133
+ /** Originating Session supplied by capability acquisition, never App arguments. */
134
+ readonly sessionId: Schema.optionalKey<Schema.String>;
135
+ /** Captured Agent approval, supplied by the host after current policy admission. */
136
+ readonly agentPolicy: Schema.optionalKey<Schema.Struct<{
137
+ readonly policyRevision: Schema.NonEmptyString;
138
+ readonly grantorMemberId: Schema.NonEmptyString;
139
+ }>>;
140
+ /** Verified method metadata; raw fetch frames have no declared method. */
141
+ readonly method: Schema.optionalKey<Schema.Struct<{
142
+ readonly name: Schema.String;
143
+ readonly effect: Schema.Literals<readonly ["read", "write", "external"]>;
144
+ }>>;
145
+ /** Host-marked, freshly admitted replay; never read from guest input. */
146
+ readonly replay: Schema.optionalKey<Schema.Struct<{
147
+ readonly lane: Schema.NonEmptyString;
148
+ readonly epoch: Schema.Number;
149
+ readonly admitted: Schema.$Array<Schema.String>;
150
+ }>>;
151
+ }>, Schema.Struct<{
152
+ readonly kind: Schema.Literal<"lifecycle">;
153
+ readonly name: Schema.Literal<"onGitPush">;
154
+ readonly eventId: Schema.NonEmptyString;
155
+ }>, Schema.Struct<{
156
+ readonly kind: Schema.Literal<"lifecycle">;
157
+ readonly name: Schema.Literal<"syncRefused">;
158
+ readonly lane: Schema.String;
159
+ readonly code: Schema.Literals<("access_changed" | "installation_unavailable")[]>;
160
+ }>]>;
161
+ readonly grants: Schema.$Array<Schema.Struct<{
162
+ readonly binding: Schema.refine<"BACKGROUND" | "GIT" | "EVENTS" | "APPS" | "WORKFLOWS" | "APP_STORAGE" | "MEDIA" | "COMPUTER" | "INBOX" | "AGENTS", Schema.String>;
163
+ readonly operations: Schema.$Array<Schema.NonEmptyString>;
164
+ }>>;
165
+ readonly deadlineAt: Schema.Number;
166
+ readonly ancestry: Schema.$Array<Schema.Codec<InstanceId, string, never, never>>;
167
+ }>;
168
+ /** The immutable authority and attribution supplied at host admission. */
169
+ export type InvocationFacts = typeof invocationFactsSchema.Type;
170
+ /** Method name/effect taken from a verified API; raw backend requests have no method. */
171
+ export type InvocationMethod = NonNullable<Extract<InvocationFacts["subject"], {
172
+ kind: "call";
173
+ }>["method"]>;
174
+ /** Captured policy identity; the Agent id remains on the canonical call actor. */
175
+ export type InvocationAgentPolicy = NonNullable<Extract<InvocationFacts["subject"], {
176
+ kind: "call";
177
+ }>["agentPolicy"]>;
178
+ /** A live frame stays inside its Instance; host state and signals never cross RPC. */
179
+ export type InvocationFrame<Host = unknown> = Readonly<{
180
+ frameId: string;
181
+ facts: InvocationFacts;
182
+ host: Host;
183
+ signal: AbortSignal;
184
+ }>;
185
+ /** One mutable origin latch on the host state of one invocation, never shared across frames. */
186
+ export type BackgroundFrameState = {
187
+ /** This frame's one claimed-origin decision; never carried into another frame. */
188
+ origin: "unchecked" | "admitted" | "refused";
189
+ };
190
+ /** Block replay capability effects before job-origin admission; storage permits claiming/settling. */
191
+ export declare function assertBackgroundBinding(frame: InvocationFrame<BackgroundFrameState>, binding: AppBackendBindingName): void;
192
+ /** Consume this frame's one origin check before awaiting verification, then retain only its decision. */
193
+ export declare function admitBackgroundFrame(key: CryptoKey, frame: InvocationFrame<BackgroundFrameState>, raw: unknown): Promise<ClaimedJobAdmission>;
194
+ /** The host's native guest-abort and authority-close hooks. */
195
+ export type InvocationGatePorts<Host> = Readonly<{
196
+ /** Stop the guest when the captured invocation's authority closes. */
197
+ abortGuest: () => Promise<void>;
198
+ /** Release the host resources belonging to this completed or revoked frame. */
199
+ close: (frame: InvocationFrame<Host>) => Promise<void>;
200
+ /** Host clock used to enforce the frame deadline; defaults to Date.now. */
201
+ now?: () => number;
202
+ }>;
203
+ /** One invocation owner shared by Hub shards and standalone Instances. */
204
+ export type InvocationGate<Host> = Readonly<{
205
+ /** Run work within one immutable frame and close its authority afterward. */
206
+ run: <A>(facts: InvocationFacts, host: Host, work: (frame: InvocationFrame<Host>) => Promise<A>) => Promise<A>;
207
+ /** Resolve the sole admitted frame, refusing outside an active invocation. */
208
+ active: () => InvocationFrame<Host>;
209
+ /** Close live authority, optionally restricted to frames of a selected run. */
210
+ revoke: (reason?: Error, target?: Readonly<{
211
+ runId: string;
212
+ }>) => Promise<void>;
213
+ /** Serialize supervisor metadata/erasure without opening guest authority. */
214
+ exclusive: <A>(work: () => Promise<A>) => Promise<A>;
215
+ }>;
216
+ /** Decode only identities minted from the invocation-frame ID family. */
217
+ export declare const invocationFrameIdSchema: Schema.String;
218
+ /** Serialize authority, preempt replay only for verified writes, and close each guest before admission. */
219
+ export declare function createInvocationGate<Host>(ports: InvocationGatePorts<Host>): InvocationGate<Host>;
220
+ /** Verified selection, artifact and binding grants before caller attribution. */
221
+ export type InvocationAuthority = Omit<InvocationFacts, "subject" | "deadlineAt">;
222
+ /** The host supplies both deadlines; the frame cannot outlive either. */
223
+ export type InvocationDeadline = Readonly<{
224
+ deadlineAt: number;
225
+ grantExpiresAt: number;
226
+ }>;
227
+ /**
228
+ * Project an admitted principal into App attribution; only the host chooses
229
+ * the fixed Computer context. The App caller grammar has no visitor actor, so
230
+ * the anonymous public reader is attributed as the reserved `visitor` key
231
+ * without a Computer. Host admission reads the principal, never this projection.
232
+ */
233
+ export declare function invocationCallerContext(principal: OysterPrincipal, computerId: string | null): AppMethodCallContext;
234
+ /** Shared host-held caller/Workflow fact builders, never guest argument decoders. */
235
+ export declare const invocationFacts: {
236
+ /** Combine host-admitted caller facts with the invocation and grant deadlines. */
237
+ fromCaller: (authority: InvocationAuthority, context: AppMethodCallContext, deadline: InvocationDeadline, method?: InvocationMethod, sessionId?: string, agentPolicy?: InvocationAgentPolicy) => InvocationFacts;
238
+ /** Attribute an already host-admitted published node to its retained arming actor. */
239
+ fromWorkflowNode: (authority: InvocationAuthority, input: Readonly<{
240
+ workflow: WorkflowAttestation;
241
+ computerId: string | null;
242
+ role?: AppMethodCallContext["role"];
243
+ idempotencyKey: string;
244
+ }>, deadline: InvocationDeadline) => InvocationFacts;
245
+ };
246
+ /** Authority comes from the admitted installation and its verified backend bytes. */
247
+ export declare function invocationAuthorityOf(installation: Installation, release: VerifiedRelease, ancestry?: readonly InstanceId[]): InvocationAuthority;
248
+ /** Mint standalone facts only after host caller and Release admission. */
249
+ export declare function admittedCallerFacts(installation: Installation, release: VerifiedRelease, context: AppMethodCallContext, grants: InvocationGrants, ancestry?: readonly InstanceId[], sessionId?: string, agentPolicy?: InvocationAgentPolicy, method?: InvocationMethod, origin?: BackgroundOrigin): InvocationFacts;
250
+ /** Seal the host-verified named method after its current admission checks. */
251
+ export declare function admittedMethodCallerFacts(host: Pick<OysterHost, "backgroundOriginKey">, installation: Installation, release: VerifiedRelease, context: AppMethodCallContext, grants: InvocationGrants, method: InvocationMethod, ancestry?: readonly InstanceId[], sessionId?: string, agentPolicy?: InvocationAgentPolicy): Promise<InvocationFacts>;