@fungi.computer/oyster 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/README.md +376 -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 +210 -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/pack.d.ts +14 -3
  130. package/dist/pack.js +44 -19
  131. package/dist/page/app-access.d.ts +7 -0
  132. package/dist/page/app-access.js +75 -0
  133. package/dist/page/boot-host.js +57 -0
  134. package/dist/page/capabilities.d.ts +3 -3
  135. package/dist/page/capabilities.js +16 -12
  136. package/dist/page/computer-handles.d.ts +3 -3
  137. package/dist/page/computer-target.d.ts +3 -0
  138. package/dist/page/computer-target.js +9 -0
  139. package/dist/page/mount.d.ts +23 -3
  140. package/dist/page/mount.js +152 -16
  141. package/dist/page-assets.d.ts +22 -0
  142. package/dist/page-assets.js +28 -0
  143. package/dist/page-script.d.ts +3 -0
  144. package/dist/page-script.js +5 -4
  145. package/dist/release.d.ts +13 -6
  146. package/dist/release.js +10 -7
  147. package/dist/root-data.d.ts +16 -6
  148. package/dist/root-data.js +2 -2
  149. package/dist/source/index.d.ts +1 -1
  150. package/dist/source/team-source.d.ts +20 -8
  151. package/dist/source/team-source.js +39 -10
  152. package/dist/source/template-seed.d.ts +2 -2
  153. package/dist/standalone/agent-mail.d.ts +24 -0
  154. package/dist/standalone/agent-mail.js +62 -0
  155. package/dist/standalone/agent-store.d.ts +9 -0
  156. package/dist/standalone/agent-store.js +30 -0
  157. package/dist/standalone/agents.d.ts +68 -9
  158. package/dist/standalone/agents.js +428 -56
  159. package/dist/standalone/app-builds-binding.d.ts +20 -2
  160. package/dist/standalone/app-builds-binding.js +147 -23
  161. package/dist/standalone/app-set.d.ts +5 -2
  162. package/dist/standalone/app-set.js +174 -143
  163. package/dist/standalone/browser.d.ts +11 -0
  164. package/dist/standalone/browser.js +72 -0
  165. package/dist/standalone/builds.d.ts +13 -4
  166. package/dist/standalone/builds.js +28 -44
  167. package/dist/standalone/computer-egress.d.ts +2 -0
  168. package/dist/standalone/computer-egress.js +13 -4
  169. package/dist/standalone/computer-fleet.d.ts +43 -0
  170. package/dist/standalone/computer-fleet.js +200 -0
  171. package/dist/standalone/computer.d.ts +44 -9
  172. package/dist/standalone/computer.js +129 -22
  173. package/dist/standalone/install-apps-cli.js +43 -6
  174. package/dist/standalone/instance.d.ts +2 -2
  175. package/dist/standalone/instance.js +8 -11
  176. package/dist/standalone/machine-link.d.ts +354 -0
  177. package/dist/standalone/machine-link.js +368 -0
  178. package/dist/standalone/media.d.ts +2 -2
  179. package/dist/standalone/media.js +19 -16
  180. package/dist/standalone/preview.d.ts +2 -2
  181. package/dist/standalone/preview.js +37 -10
  182. package/dist/standalone/registry.d.ts +149 -9
  183. package/dist/standalone/registry.js +299 -8
  184. package/dist/standalone/worker.d.ts +19 -5
  185. package/dist/standalone/worker.js +90 -54
  186. package/dist/standalone/workflow-binding.d.ts +18 -22
  187. package/dist/standalone/workflow-binding.js +18 -18
  188. package/dist/standalone/workflow-control.d.ts +4 -0
  189. package/dist/standalone/workflow-control.js +40 -0
  190. package/dist/standalone/workflow-events.d.ts +2 -2
  191. package/dist/standalone/workflow-events.js +9 -10
  192. package/dist/standalone/workflow-git-capture.js +4 -3
  193. package/dist/standalone/workflow-host.d.ts +162 -161
  194. package/dist/standalone/workflow-host.js +101 -21
  195. package/dist/standalone/workflow-node-capture.js +11 -4
  196. package/dist/standalone/workflow-schedules.d.ts +5 -2
  197. package/dist/standalone/workflow-schedules.js +9 -10
  198. package/dist/standalone/workflow-trigger-host.d.ts +65 -5
  199. package/dist/standalone/workflow-trigger-host.js +2 -2
  200. package/dist/standalone/workflow-webhooks.d.ts +22 -0
  201. package/dist/standalone/workflow-webhooks.js +39 -0
  202. package/dist/standalone/workflows.d.ts +140 -43
  203. package/dist/standalone/workflows.js +198 -39
  204. package/dist/storage-frame.d.ts +6 -6
  205. package/dist/testing/capabilities.js +14 -3
  206. package/dist/testing/installation.js +26 -2
  207. package/dist/testing/worker.js +1 -1
  208. package/dist/testing/workers-runtime.d.ts +8 -2
  209. package/dist/testing/workers-runtime.js +20 -2
  210. package/dist/webhook-abuse.d.ts +25 -0
  211. package/dist/webhook-abuse.js +54 -0
  212. package/dist/webhook-custody.d.ts +144 -0
  213. package/dist/webhook-custody.js +222 -0
  214. package/dist/webhook-ingress.d.ts +36 -0
  215. package/dist/webhook-ingress.js +146 -0
  216. package/dist/webhooks.d.ts +111 -0
  217. package/dist/webhooks.js +189 -0
  218. package/dist/workers-git.d.ts +12 -17
  219. package/dist/workers-git.js +60 -52
  220. package/dist/workers.d.ts +224 -13
  221. package/dist/workers.js +351 -51
  222. package/dist/workflow-agent-authority.d.ts +8 -0
  223. package/dist/workflow-agent-authority.js +42 -0
  224. package/dist/workflows.d.ts +66 -0
  225. package/dist/workflows.js +97 -0
  226. package/docs/api/README.md +63 -37
  227. package/docs/api/agents.md +17 -0
  228. package/docs/api/app-storage.md +1 -1
  229. package/docs/api/apps.md +1394 -543
  230. package/docs/api/browser/boot.md +132 -0
  231. package/docs/api/browser.md +1440 -0
  232. package/docs/api/build-contract.md +38 -25
  233. package/docs/api/build.md +263 -84
  234. package/docs/api/cloudflare.md +93 -7
  235. package/docs/api/git.md +3 -13
  236. package/docs/api/host.md +944 -118
  237. package/docs/api/index.md +320 -18
  238. package/docs/api/keys.md +24 -1
  239. package/docs/api/mcp.md +59 -12
  240. package/docs/api/modules.md +33 -40
  241. package/docs/api/pack.md +57 -8
  242. package/docs/api/page/mount.md +92 -4
  243. package/docs/api/workers-git.md +103 -24
  244. package/docs/api/workers.md +1581 -244
  245. package/docs/site/agent-quickstart.mdx +28 -14
  246. package/examples/standalone/worker.ts +3 -1
  247. package/examples/standalone/wrangler.jsonc +2 -0
  248. package/package.json +73 -71
  249. package/dist/installation-consent.d.ts +0 -81
  250. package/dist/installation-consent.js +0 -84
  251. package/dist/shell/app-directory.d.ts +0 -542
  252. package/dist/shell/app-directory.js +0 -196
  253. package/dist/shell/browser-actions.d.ts +0 -7
  254. package/dist/shell/browser-actions.js +0 -56
  255. package/dist/shell/command-host.d.ts +0 -112
  256. package/dist/shell/command-host.js +0 -502
  257. package/dist/shell/commands.d.ts +0 -25
  258. package/dist/shell/commands.js +0 -78
  259. package/dist/shell/index.d.ts +0 -8
  260. package/dist/shell/index.js +0 -8
  261. package/dist/shell/keys.d.ts +0 -41
  262. package/dist/shell/keys.js +0 -134
  263. package/dist/shell/shell-module.d.ts +0 -55
  264. package/dist/shell/shell-module.js +0 -86
  265. package/dist/shell/window-manager.d.ts +0 -128
  266. package/dist/shell/window-manager.js +0 -730
  267. package/dist/shell/workbench-contract.d.ts +0 -68
  268. package/dist/shell/workbench-directory.d.ts +0 -332
  269. package/dist/shell/workbench-directory.js +0 -35
  270. package/dist/shell/workbench-host-types.d.ts +0 -96
  271. package/dist/shell/workbench-host-types.js +0 -1
  272. package/dist/shell/workbench-host.d.ts +0 -34
  273. package/dist/shell/workbench-host.js +0 -352
  274. package/dist/shell/workbench-model.d.ts +0 -194
  275. package/dist/shell/workbench-model.js +0 -53
  276. package/dist/shell/workbench.d.ts +0 -4
  277. package/dist/shell/workbench.js +0 -2
  278. package/docs/api/shell/app-directory.md +0 -599
  279. package/docs/api/shell/command-host.md +0 -224
  280. package/docs/api/shell/commands.md +0 -197
  281. package/docs/api/shell/keys.md +0 -183
  282. package/docs/api/shell/shell-module.md +0 -204
  283. package/docs/api/shell/window-manager.md +0 -267
  284. package/docs/api/shell/workbench-directory.md +0 -119
  285. package/docs/api/shell/workbench.md +0 -205
  286. package/docs/api/shell.md +0 -1915
  287. /package/dist/{shell/workbench-contract.js → page/boot-host.d.ts} +0 -0
@@ -7,7 +7,7 @@ import { verifyRelease, } from "../host.js";
7
7
  import { APP_INVOCATION_CEILING_MS, invocationAuthorityOf, invocationFacts, invocationFactsSchema, } from "../invocation.js";
8
8
  import { AppInstanceError } from "../index.js";
9
9
  import { appCapabilityIds } from "@fungi.computer/puffball";
10
- import { createGraphWorkflowRuns, compileForMember, graphStartRequestSchema, preparationFromRun, planNodeCall, workflowRunContextSchema, } from "@fungi.computer/braid/runtime";
10
+ import { createGraphWorkflowRuns, createTriggerRunReservations, compileForMember, graphStartRequestSchema, preparationFromRun, planNodeCall, workflowRunContextSchema, } from "@fungi.computer/braid/runtime";
11
11
  import { appMethodCallContextSchema } from "@fungi.computer/puffball/app";
12
12
  import { appEventFactSchema, } from "@fungi.computer/puffball/app-event-facts";
13
13
  import { keyAppAccess } from "../keys.js";
@@ -97,7 +97,19 @@ const summarizedEntry = (entry) => {
97
97
  };
98
98
  /** The standalone host's installation admission, above the portable engine. */
99
99
  export function createStandaloneWorkflowHost(owner, host, options) {
100
- const store = createGraphWorkflowRuns(owner);
100
+ const graph = createGraphWorkflowRuns(owner);
101
+ const reservations = createTriggerRunReservations(owner);
102
+ const store = {
103
+ ...graph,
104
+ markTerminal(...args) {
105
+ // The standalone run owner ends graph/review state and the SAME Braid
106
+ // reservation atomically, without creating a Hub Computer-cleanup queue.
107
+ owner.transactionSync(() => {
108
+ graph.markTerminal(...args);
109
+ reservations.terminalTriggerRun(args[0]);
110
+ });
111
+ },
112
+ };
101
113
  // Only the host's attested invocation context is associated here. Lifecycle,
102
114
  // events, reviews and replay are owned by the shared runtime store.
103
115
  owner.sql.exec(`CREATE TABLE IF NOT EXISTS standalone_workflow_callers (
@@ -169,14 +181,22 @@ export function createStandaloneWorkflowHost(owner, host, options) {
169
181
  if (declaration === null)
170
182
  return false;
171
183
  const snapshot = await deriveDeclaredOperations(selected.installation.release, declaration, host.capabilities ?? []);
172
- return (grantsFor(selected.installation, declaration, snapshot, "workflow.publish") !== null);
184
+ return (grantsFor({
185
+ status: selected.installation.status,
186
+ authority: selected.installation.authority,
187
+ artifactDigest: selected.installation.release.artifact.artifactDigest,
188
+ }, declaration, snapshot, "workflow.publish") !== null);
173
189
  };
174
190
  const runGranted = async (selected) => {
175
191
  const declaration = selected.verified.declaration;
176
192
  if (declaration === null)
177
193
  return false;
178
194
  const snapshot = await deriveDeclaredOperations(selected.installation.release, declaration, host.capabilities ?? []);
179
- if (grantsFor(selected.installation, declaration, snapshot, "workflow.run") === null)
195
+ if (grantsFor({
196
+ status: selected.installation.status,
197
+ authority: selected.installation.authority,
198
+ artifactDigest: selected.installation.release.artifact.artifactDigest,
199
+ }, declaration, snapshot, "workflow.run") === null)
180
200
  return false;
181
201
  return approvedBindingGrants(selected.installation, declaration).some((binding) => binding.binding === "WORKFLOWS" &&
182
202
  binding.operations.includes("runGraph"));
@@ -192,12 +212,10 @@ export function createStandaloneWorkflowHost(owner, host, options) {
192
212
  checkLibraryOperation(selected, operation);
193
213
  return selected;
194
214
  };
195
- const admitInvocation = async (raw, operation) => {
196
- const facts = S.decodeUnknownSync(invocationFactsSchema)(raw, {
197
- onExcessProperty: "error",
198
- });
199
- if (facts.subject.kind !== "call" ||
200
- facts.selection.owner.kind !== "installation")
215
+ // All frame kinds re-open the same live installation pin and grants.
216
+ // A lifecycle admission does not turn the frame into a member call.
217
+ const admitFrame = async (facts, operation) => {
218
+ if (facts.selection.owner.kind !== "installation")
201
219
  throw new AppInstanceError("revoked");
202
220
  if (facts.deadlineAt <= Date.now())
203
221
  throw new AppInstanceError("expired");
@@ -217,13 +235,54 @@ export function createStandaloneWorkflowHost(owner, host, options) {
217
235
  return {
218
236
  ...selected,
219
237
  libraryId,
220
- context: facts.subject.context,
221
238
  installationAuthority: {
222
239
  selection: current.selection,
223
240
  artifactDigest: current.artifactDigest,
224
241
  },
225
242
  };
226
243
  };
244
+ const admitInvocation = async (raw, operation) => {
245
+ const facts = S.decodeUnknownSync(invocationFactsSchema)(raw, {
246
+ onExcessProperty: "error",
247
+ });
248
+ if (facts.subject.kind !== "call")
249
+ throw new AppInstanceError("revoked");
250
+ return {
251
+ ...(await admitFrame(facts, operation)),
252
+ context: facts.subject.context,
253
+ };
254
+ };
255
+ const admitLifecycle = async (raw, operation) => {
256
+ const facts = S.decodeUnknownSync(invocationFactsSchema)(raw, {
257
+ onExcessProperty: "error",
258
+ });
259
+ if (facts.subject.kind !== "lifecycle" ||
260
+ facts.subject.name !== "onGitPush")
261
+ throw new AppInstanceError("revoked");
262
+ return admitFrame(facts, operation);
263
+ };
264
+ const admitStart = async (raw) => {
265
+ const facts = S.decodeUnknownSync(invocationFactsSchema)(raw, {
266
+ onExcessProperty: "error",
267
+ });
268
+ if (facts.subject.kind === "call")
269
+ return admitInvocation(facts, "runGraph");
270
+ const selected = await admitLifecycle(facts, "runGraph");
271
+ const member = await options.lifecycleMember();
272
+ if (member === null)
273
+ throw new AppInstanceError("revoked");
274
+ const current = await options
275
+ .authority(selected.libraryId)
276
+ .member(member.memberId);
277
+ if (current.membershipVersion !== member.membershipVersion)
278
+ throw new AppInstanceError("revoked");
279
+ const context = {
280
+ actor: { kind: "member", memberId: member.memberId },
281
+ role: "owner",
282
+ computerId: host.computerId ?? null,
283
+ };
284
+ return { ...selected, context };
285
+ };
227
286
  /** A trigger's live library pin; any missing grant is a typed revocation. */
228
287
  const triggerAuthority = async (installationId) => {
229
288
  const installation = await host.installations.get(installationId);
@@ -325,7 +384,8 @@ export function createStandaloneWorkflowHost(owner, host, options) {
325
384
  workflowRunGranted = await runGranted(selected);
326
385
  const after = await host.installations.get(current.installationId);
327
386
  if (after?.status !== "enabled" ||
328
- after.authorityVersion !== current.authorityVersion ||
387
+ after.authority.authorityVersion !==
388
+ current.authority.authorityVersion ||
329
389
  releaseKey(after) !== releaseKey(current))
330
390
  throw new AppInstanceError("revoked");
331
391
  }
@@ -357,7 +417,9 @@ export function createStandaloneWorkflowHost(owner, host, options) {
357
417
  return { apps };
358
418
  }
359
419
  const catalog = async (facts, input) => {
360
- const library = await admitInvocation(facts, "catalog");
420
+ const library = facts.subject.kind === "call"
421
+ ? await admitInvocation(facts, "catalog")
422
+ : await admitLifecycle(facts, "catalog");
361
423
  if (input?.installationId === undefined)
362
424
  return catalogForLibrary(library);
363
425
  const { apps } = await catalogForLibrary(library, {
@@ -399,12 +461,15 @@ export function createStandaloneWorkflowHost(owner, host, options) {
399
461
  // Host readers use this only after admitting the live member and library.
400
462
  catalogForLibrary,
401
463
  async prepare(facts, raw) {
402
- const selected = await admitInvocation(facts, "runGraph");
464
+ const selected = await admitStart(facts);
403
465
  const { libraryId, context } = selected;
404
466
  const request = S.decodeUnknownSync(graphStartRequestSchema)(raw, {
405
467
  onExcessProperty: "error",
406
468
  });
407
469
  const publication = request.published;
470
+ // Only immutable, published runs receive Workflow node attestations.
471
+ if (facts.subject.kind === "lifecycle" && publication === undefined)
472
+ return { error: "not_granted" };
408
473
  if (publication !== undefined) {
409
474
  if (!(await publisherGranted(selected)))
410
475
  return { error: "not_granted" };
@@ -459,8 +524,11 @@ export function createStandaloneWorkflowHost(owner, host, options) {
459
524
  const admitted = admitRunInput(compiled.plan.definition, request.input);
460
525
  if (admitted.kind === "invalid")
461
526
  return { error: "invalid_input" };
462
- const runId = "run_" +
463
- (await contentDigest([libraryId, request.requestId])).slice(7, 47);
527
+ // The host's accepted event owns lifecycle replay, not a guest request id.
528
+ const requestId = facts.subject.kind === "lifecycle" && facts.subject.name === "onGitPush"
529
+ ? ["onGitPush", facts.subject.eventId]
530
+ : request.requestId;
531
+ const runId = "run_" + (await contentDigest([libraryId, requestId])).slice(7, 47);
464
532
  // An Agent runs with the access of the person who approved its App
465
533
  // tools.
466
534
  const subject = facts.subject.kind === "call" ? facts.subject : null;
@@ -516,7 +584,7 @@ export function createStandaloneWorkflowHost(owner, host, options) {
516
584
  }
517
585
  : {
518
586
  initiatedBy: { kind: "member", memberId: actorId },
519
- source: "manual",
587
+ source: facts.subject.kind === "lifecycle" ? "event" : "manual",
520
588
  libraryInstallationId: libraryId,
521
589
  computerId: context.computerId,
522
590
  plan: compiled.plan,
@@ -532,7 +600,7 @@ export function createStandaloneWorkflowHost(owner, host, options) {
532
600
  const started = {
533
601
  teamId: libraryId,
534
602
  runId,
535
- requestId: await contentDigest([libraryId, request.requestId]),
603
+ requestId: await contentDigest([libraryId, requestId]),
536
604
  actorId,
537
605
  computerId: context.computerId,
538
606
  plan: compiled.plan,
@@ -542,7 +610,10 @@ export function createStandaloneWorkflowHost(owner, host, options) {
542
610
  initiatedBy: preparation.initiatedBy,
543
611
  };
544
612
  const record = delivery === undefined ? started : { ...started, delivery };
545
- const current = await admitInvocation(facts, "runGraph");
613
+ const current = await admitStart(facts);
614
+ if (canonicalJson(S.decodeUnknownSync(S.Json)(current.context)) !==
615
+ canonicalJson(S.decodeUnknownSync(S.Json)(context)))
616
+ return { error: "not_granted" };
546
617
  if (publication !== undefined && !(await publisherGranted(current)))
547
618
  return { error: "not_granted" };
548
619
  const callContext = {
@@ -699,6 +770,7 @@ export function createStandaloneWorkflowHost(owner, host, options) {
699
770
  // retryable fault; same for an instance the owner state marks
700
771
  // inactive.
701
772
  case "revoked":
773
+ case "disabled":
702
774
  case "instance_owner_mismatch":
703
775
  case "instance_authority_version_mismatch":
704
776
  case "instance_release_mismatch":
@@ -714,6 +786,14 @@ export function createStandaloneWorkflowHost(owner, host, options) {
714
786
  ? "App execution failed"
715
787
  : `App execution failed (${result.causeClass})`);
716
788
  case "expired":
789
+ case "backend_load_failed":
790
+ case "backend_construction_failed":
791
+ case "ui_mount_failed":
792
+ case "boot_runner_unavailable":
793
+ case "boot_browser_binding_missing":
794
+ case "boot_clone_unavailable":
795
+ case "boot_timeout":
796
+ case "boot_cleanup_failed":
717
797
  case "binding_unavailable":
718
798
  case "reentrant":
719
799
  case "conflicting_owner_state":
@@ -731,9 +811,9 @@ export function createStandaloneWorkflowHost(owner, host, options) {
731
811
  }
732
812
  }
733
813
  // The engine's closed refusal vocabulary names host decisions; an App's
734
- // own domain refusal fails the node with its code.
814
+ // own domain refusal fails the node with its code and caller-facing reason.
735
815
  if (result.value.kind === "refused")
736
- throw new Error(`App method refused (${result.value.code})`);
816
+ throw new Error(`App method refused (${result.value.code}): ${result.value.message}`);
737
817
  const value = result.value.value;
738
818
  const pending = pinned.pending === true ? pendingSchema.safeParse(value) : null;
739
819
  if (pending?.success)
@@ -31,7 +31,13 @@ export function createStandaloneWorkflowNodeCapture(options) {
31
31
  return null;
32
32
  const facts = {
33
33
  principal: { actor: caller.actor, role: caller.role ?? null },
34
- installation,
34
+ installation: {
35
+ status: installation.status,
36
+ visibility: installation.visibility,
37
+ instanceId: installation.instanceId,
38
+ authority: installation.authority,
39
+ artifactDigest: installation.release.artifact.artifactDigest,
40
+ },
35
41
  declaration: verified.declaration,
36
42
  declaredOperations: await deriveDeclaredOperations(installation.release, verified.declaration, host.capabilities ?? []),
37
43
  api: verified.api,
@@ -57,9 +63,10 @@ export function createStandaloneWorkflowNodeCapture(options) {
57
63
  const after = await readers.member(actor.memberId);
58
64
  if (latest?.status !== "enabled" ||
59
65
  latest.instanceId !== installation.instanceId ||
60
- latest.authorityVersion !== installation.authorityVersion ||
61
- latest.declaredOperationsDigest !==
62
- installation.declaredOperationsDigest ||
66
+ latest.authority.authorityVersion !==
67
+ installation.authority.authorityVersion ||
68
+ latest.authority.declaredOperationsDigest !==
69
+ installation.authority.declaredOperationsDigest ||
63
70
  latest.release.releaseId !== installation.release.releaseId ||
64
71
  latest.release.artifact.artifactDigest !==
65
72
  installation.release.artifact.artifactDigest ||
@@ -1,8 +1,10 @@
1
1
  import { createWorkflowSchedules, type WorkflowInstallationAuthority } from "@fungi.computer/braid/runtime";
2
2
  import type { AppMethodCallContext } from "@fungi.computer/puffball/app-method-runtime";
3
+ import type { workflowAgentAuthority } from "../workflow-agent-authority.js";
3
4
  import { type TriggerArmTarget, type ScheduleDisarmInput } from "./workflow-binding.js";
4
5
  /** The host supplies this result only after reopening the protected invocation. */
5
6
  export type TriggerAdmission = Readonly<{
7
+ authority: Awaited<ReturnType<typeof workflowAgentAuthority>>;
6
8
  libraryId: string;
7
9
  context: AppMethodCallContext;
8
10
  /** The admitted frame's selection and verified artifact, pinned on arm. */
@@ -26,7 +28,7 @@ export declare function createStandaloneWorkflowSchedules<Frame>(options: Parame
26
28
  } | {
27
29
  kind: "refused";
28
30
  reason: string;
29
- readonly error?: undefined;
31
+ error?: undefined;
30
32
  }>;
31
33
  disarm(frame: Frame, raw: ScheduleDisarmInput): Promise<{
32
34
  kind: "not_found";
@@ -41,12 +43,13 @@ export declare function createStandaloneWorkflowSchedules<Frame>(options: Parame
41
43
  } | {
42
44
  schedules: {
43
45
  scheduleId: string;
46
+ installationId: string;
44
47
  workflowId: string;
45
48
  triggerId: string;
46
49
  nextDueAt: number;
47
50
  state: "active" | "finished" | "paused" | "cancelled";
48
51
  lastError: string | null;
49
52
  }[];
50
- readonly error?: undefined;
53
+ error?: undefined;
51
54
  }>;
52
55
  };
@@ -10,30 +10,29 @@ export function createStandaloneWorkflowSchedules(options) {
10
10
  alarm: () => schedules.fireDue(),
11
11
  async arm(frame, raw) {
12
12
  const target = triggerArmTargetSchema.parse(raw);
13
- const { libraryId, context, installationAuthority } = await options.admitInvocation(frame, "armSchedule");
14
- if (context.actor.kind !== "member")
13
+ const { libraryId, context, installationAuthority, authority } = await options.admitInvocation(frame, "armSchedule");
14
+ if (authority === null)
15
15
  return { error: "not_granted" };
16
16
  if (context.computerId === null)
17
17
  return {
18
18
  kind: "refused",
19
19
  reason: "Select a Computer before arming a schedule",
20
20
  };
21
- // Standalone has no Agent arming source: the owner arms as themself.
22
- return schedules.arm({ armedBy: context.actor.memberId, armedVia: null }, { ...target, installationId: libraryId }, context.computerId, installationAuthority);
21
+ return schedules.arm(authority.armer, { ...target, installationId: libraryId }, context.computerId, installationAuthority);
23
22
  },
24
23
  async disarm(frame, raw) {
25
24
  const { scheduleId } = scheduleDisarmInputSchema.parse(raw);
26
- const { libraryId, context } = await options.admitInvocation(frame, "disarmSchedule");
27
- if (context.actor.kind !== "member")
25
+ const { libraryId, authority } = await options.admitInvocation(frame, "disarmSchedule");
26
+ if (authority === null)
28
27
  return { error: "not_granted" };
29
- return schedules.disarm({ kind: "member", memberId: context.actor.memberId }, libraryId, scheduleId);
28
+ return schedules.disarm(authority.principal, libraryId, scheduleId);
30
29
  },
31
30
  async list(frame) {
32
- const { libraryId, context } = await options.admitInvocation(frame, "schedules");
33
- if (context.actor.kind !== "member")
31
+ const { libraryId, authority } = await options.admitInvocation(frame, "schedules");
32
+ if (authority === null)
34
33
  return { error: "not_granted" };
35
34
  return {
36
- schedules: await schedules.list({ kind: "member", memberId: context.actor.memberId }, libraryId),
35
+ schedules: await schedules.list(authority.principal, libraryId),
37
36
  };
38
37
  },
39
38
  };
@@ -1,4 +1,4 @@
1
- import { createTeamWorkflowRuns, createTeamWorkflowTriggers, type WorkflowErrorPort, type WorkflowRunAuthority } from "@fungi.computer/braid/runtime";
1
+ import { createTeamWorkflowRuns, createTeamWorkflowTriggers, type WorkflowErrorPort, type WorkflowRunAuthority, type WorkflowTriggerAdmission } from "@fungi.computer/braid/runtime";
2
2
  /** Standalone denial is the Instance's revoked refusal; other failures retry. */
3
3
  export declare const standaloneTriggerErrors: WorkflowErrorPort;
4
4
  /**
@@ -8,7 +8,7 @@ export declare const standaloneTriggerErrors: WorkflowErrorPort;
8
8
  */
9
9
  export declare function createStandaloneWorkflowTriggerHost(options: {
10
10
  authority: WorkflowRunAuthority;
11
- runs: Pick<Parameters<typeof createTeamWorkflowRuns>[0], "sql" | "transactionSync" | "teamId">;
11
+ runs: Pick<Parameters<typeof createTeamWorkflowRuns>[0], "sql" | "transactionSync" | "teamId" | "now">;
12
12
  triggers: Omit<Parameters<typeof createTeamWorkflowTriggers>[0], "runs" | "errors">;
13
13
  /** Whether this member may have run endings reported to the Agent Session. */
14
14
  admitDelivery: Parameters<typeof createTeamWorkflowRuns>[0]["admitDelivery"];
@@ -344,7 +344,7 @@ export declare function createStandaloneWorkflowTriggerHost(options: {
344
344
  readonly operations: readonly string[];
345
345
  }[];
346
346
  readonly nodeId: string;
347
- readonly capabilities: readonly ("agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "agent.toolPolicy" | "agent.read" | "agent.send" | "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "llm.generation" | "media.generation" | "games:servers" | "git.repository" | "apps.open" | "apps.call" | "app.storage" | "app.events.emit" | "team.workbenchHost" | "marketplace.browse" | "marketplace.purchase" | "workflow.run" | "workflow.publish" | "workflow.arm" | "world" | "channels.read" | "channels.personal.manage")[];
347
+ readonly capabilities: readonly ("approvals" | "agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "agent.toolPolicy" | "agent.read" | "agent.send" | "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "llm.generation" | "media.generation" | "games:servers" | "git.repository" | "apps.open" | "apps.call" | "app.storage" | "app.events.emit" | "team.workbenchHost" | "marketplace.browse" | "marketplace.purchase" | "workflow.run" | "workflow.publish" | "workflow.arm" | "world" | "channels.read" | "channels.personal.manage")[];
348
348
  readonly graphId?: string;
349
349
  }[];
350
350
  readonly nodeGitAccess: readonly {
@@ -1242,7 +1242,7 @@ export declare function createStandaloneWorkflowTriggerHost(options: {
1242
1242
  }>;
1243
1243
  startCachedGraph(binding: import("@fungi.computer/braid/runtime").ArmedWorkflowTrigger, started: Pick<import("@fungi.computer/braid/runtime").GraphStartRequest, "requestId" | "input"> & {
1244
1244
  triggerRepository?: string;
1245
- }): Promise<{
1245
+ }, admission?: WorkflowTriggerAdmission): Promise<{
1246
1246
  kind: "conflict" | "started" | "replay";
1247
1247
  runId: string;
1248
1248
  planDigest: string;
@@ -1264,6 +1264,66 @@ export declare function createStandaloneWorkflowTriggerHost(options: {
1264
1264
  revokeInstallationInMutation(rawInstallationId: string): void;
1265
1265
  revokeReleaseInMutation(appIds: readonly string[]): void;
1266
1266
  onLifecycle(status: "active" | "suspended"): void;
1267
+ reserveWebhookDelivery(raw: import("zod").input<import("zod").ZodObject<{
1268
+ scope: import("zod").ZodLazy<import("zod").ZodObject<{
1269
+ installationId: import("zod").ZodString;
1270
+ workflowId: import("zod").ZodString;
1271
+ commit: import("zod").ZodString;
1272
+ triggerId: import("zod").ZodString;
1273
+ }, import("zod/v4/core").$strict>>;
1274
+ delivery: import("zod").ZodObject<{
1275
+ host: import("zod").ZodString;
1276
+ tenant: import("zod").ZodString;
1277
+ bindingId: import("zod").ZodString;
1278
+ eventId: import("zod").ZodString;
1279
+ }, import("zod/v4/core").$strict>;
1280
+ cargo: import("zod").ZodJSONSchema;
1281
+ limits: import("zod").ZodObject<{
1282
+ hostConcurrency: import("zod").ZodNumber;
1283
+ bindingConcurrency: import("zod").ZodNumber;
1284
+ }, import("zod/v4/core").$strict>;
1285
+ }, import("zod/v4/core").$strict>>): {
1286
+ kind: "reserved" | "replay";
1287
+ reservationId: string;
1288
+ runId: string | null;
1289
+ } | {
1290
+ kind: "refused";
1291
+ reason: "limit";
1292
+ };
1293
+ pendingWebhookDeliveries(rawOwner: import("zod").input<import("zod").ZodObject<{
1294
+ host: import("zod").ZodString;
1295
+ tenant: import("zod").ZodString;
1296
+ }, import("zod/v4/core").$strict>>): ({
1297
+ kind: "launch";
1298
+ reservationId: string;
1299
+ runId: string;
1300
+ } | {
1301
+ scope: {
1302
+ installationId: string;
1303
+ workflowId: string;
1304
+ commit: string;
1305
+ triggerId: string;
1306
+ };
1307
+ delivery: {
1308
+ host: string;
1309
+ tenant: string;
1310
+ bindingId: string;
1311
+ eventId: string;
1312
+ };
1313
+ cargo: import("zod").JSONType;
1314
+ limits: {
1315
+ hostConcurrency: number;
1316
+ bindingConcurrency: number;
1317
+ };
1318
+ kind: "start";
1319
+ reservationId: string;
1320
+ runId?: never;
1321
+ })[];
1322
+ nextWebhookWake(rawOwner: import("zod").input<import("zod").ZodObject<{
1323
+ host: import("zod").ZodString;
1324
+ tenant: import("zod").ZodString;
1325
+ }, import("zod/v4/core").$strict>>): number | null;
1326
+ completeWebhookDelivery(rawReservationId: string): void;
1267
1327
  reserveTriggerRun(raw: {
1268
1328
  scope: import("@fungi.computer/braid/runtime").TriggerRunScope;
1269
1329
  requestId: string;
@@ -1702,7 +1762,7 @@ export declare function createStandaloneWorkflowTriggerHost(options: {
1702
1762
  refusal: (binding: import("@fungi.computer/braid/runtime").ArmedWorkflowTrigger) => string | null;
1703
1763
  invalidateInstallation: (installationId: string) => void;
1704
1764
  revalidatePublished(installationId: string, git: import("@fungi.computer/agar/git").GitBinding): Promise<void>;
1705
- fire(raw: import("@fungi.computer/braid/runtime").ArmedWorkflowTrigger, scope: string, event: import("@fungi.computer/braid").TriggerEvent): Promise<Readonly<{
1765
+ fire(raw: import("@fungi.computer/braid/runtime").ArmedWorkflowTrigger, scope: string, event: import("@fungi.computer/braid").TriggerEvent, admission?: WorkflowTriggerAdmission): Promise<Readonly<{
1706
1766
  kind: "ignored";
1707
1767
  }> | Readonly<{
1708
1768
  kind: "started" | "replay" | "conflict";
@@ -37,8 +37,8 @@ export function createStandaloneWorkflowTriggerHost(options) {
37
37
  errors: standaloneTriggerErrors,
38
38
  runs: {
39
39
  ...runs,
40
- async startCachedGraph(binding, request) {
41
- const started = await runs.startCachedGraph(binding, request);
40
+ async startCachedGraph(binding, request, admission) {
41
+ const started = await runs.startCachedGraph(binding, request, admission);
42
42
  // A replay re-records the same caller after a lost reply.
43
43
  if (started.kind === "started" || started.kind === "replay")
44
44
  options.recordCaller(started.runId, binding.armedBy, binding.computerId);
@@ -0,0 +1,22 @@
1
+ import { Hono } from "hono";
2
+ import type { OysterEnv, OysterHost } from "../host.js";
3
+ import type { StandaloneWorkflowEnv } from "./workflows.js";
4
+ /** The root resolves the origin before this unauthenticated signed-delivery path. */
5
+ export declare function standaloneWorkflowWebhookRoutes(env: Pick<StandaloneWorkflowEnv, "WORKFLOW_RUNS">, hostFor: (request: Request) => Promise<Pick<OysterHost, "installations"> | null>): import("hono/hono-base").HonoBase<import("hono/types").BlankEnv, {
6
+ "/api/installations/:installationId/workflows/webhooks/:bindingId": {
7
+ $all: {
8
+ input: {
9
+ param: {
10
+ installationId: string;
11
+ } & {
12
+ bindingId: string;
13
+ };
14
+ };
15
+ output: {};
16
+ outputFormat: string;
17
+ status: import("hono/utils/http-status").StatusCode;
18
+ };
19
+ };
20
+ }, "/", "/api/installations/:installationId/workflows/webhooks/:bindingId">;
21
+ /** Mounted only under the root's authenticated owner routes; the DO re-admits. */
22
+ export declare function standaloneWorkflowWebhookControls(env: Pick<StandaloneWorkflowEnv, "WORKFLOW_RUNS">, hostFor: () => Promise<Pick<OysterHost, "installations">>): Hono<OysterEnv>;
@@ -0,0 +1,39 @@
1
+ import { Hono } from "hono";
2
+ import { bodyLimit } from "hono/body-limit";
3
+ import * as Schema from "effect/Schema";
4
+ import { workflowWebhookCommandSchema } from "../webhooks.js";
5
+ const refused = () => Response.json({ error: "refused" }, { status: 403, headers: { "cache-control": "no-store" } });
6
+ /** Address an existing library, never use an opaque binding ID as a DO name. */
7
+ async function libraryExists(host, installationId) {
8
+ const installation = await host.installations.get(installationId);
9
+ return (installation?.status === "enabled" &&
10
+ installation.authority.approvedGrants.some((grant) => grant.capability === "workflow.run"));
11
+ }
12
+ /** The root resolves the origin before this unauthenticated signed-delivery path. */
13
+ export function standaloneWorkflowWebhookRoutes(env, hostFor) {
14
+ return new Hono().all("/api/installations/:installationId/workflows/webhooks/:bindingId", async (context) => {
15
+ const host = await hostFor(context.req.raw);
16
+ const installationId = context.req.param("installationId");
17
+ if (host === null || !(await libraryExists(host, installationId)))
18
+ return refused();
19
+ const address = new URL(context.req.url);
20
+ address.pathname = `/webhooks/${encodeURIComponent(context.req.param("bindingId"))}`;
21
+ address.search = "";
22
+ return env.WORKFLOW_RUNS.getByName(installationId).fetch(new Request(address, context.req.raw));
23
+ });
24
+ }
25
+ /** Mounted only under the root's authenticated owner routes; the DO re-admits. */
26
+ export function standaloneWorkflowWebhookControls(env, hostFor) {
27
+ return new Hono().post("/api/installations/:installationId/workflows/webhooks", bodyLimit({ maxSize: 64 * 1024, onError: refused }), async (context) => {
28
+ const installationId = context.req.param("installationId");
29
+ if (!(await libraryExists(await hostFor(), installationId)))
30
+ return refused();
31
+ const body = Schema.decodeUnknownResult(Schema.fromJsonString(Schema.Json))(await context.req.text());
32
+ if (body._tag === "Failure")
33
+ return refused();
34
+ const command = workflowWebhookCommandSchema.safeParse(body.success);
35
+ if (!command.success)
36
+ return refused();
37
+ return env.WORKFLOW_RUNS.getByName(installationId).webhookControl(context.get("principal"), installationId, command.data);
38
+ });
39
+ }