@fungi.computer/oyster 0.6.0 → 0.7.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 (163) hide show
  1. package/README.md +132 -78
  2. package/dist/agent-archive-projection.d.ts +8 -0
  3. package/dist/agent-archive-projection.js +49 -0
  4. package/dist/agent-schedules.d.ts +50 -0
  5. package/dist/agent-schedules.js +17 -0
  6. package/dist/agents.d.ts +35 -14
  7. package/dist/agents.js +93 -144
  8. package/dist/app-builds.d.ts +7 -2
  9. package/dist/app-builds.js +19 -2
  10. package/dist/approval-capability.d.ts +1 -1
  11. package/dist/approval-capability.js +11 -16
  12. package/dist/approval-home.d.ts +2 -1
  13. package/dist/approval-home.js +5 -3
  14. package/dist/apps/catalog.js +1 -1
  15. package/dist/apps/gate.d.ts +4 -7
  16. package/dist/apps/gate.js +14 -29
  17. package/dist/apps/index.d.ts +1 -1
  18. package/dist/apps/installation.d.ts +4 -42
  19. package/dist/apps/installation.js +7 -75
  20. package/dist/apps/operations.js +0 -10
  21. package/dist/apps/permissions.d.ts +18 -8
  22. package/dist/apps/permissions.js +7 -1
  23. package/dist/auth.d.ts +1 -24
  24. package/dist/auth.js +9 -39
  25. package/dist/authorization.d.ts +307 -0
  26. package/dist/authorization.js +542 -0
  27. package/dist/boot-host-script.js +1 -1
  28. package/dist/build/build-jobs.d.ts +2 -2
  29. package/dist/build/candidate-builder.d.ts +2 -0
  30. package/dist/build/candidate-builder.js +5 -1
  31. package/dist/build/candidate-cli.js +3 -0
  32. package/dist/build/candidate-cli.mjs +59 -21
  33. package/dist/build/job-log.d.ts +23 -0
  34. package/dist/build/job-log.js +88 -0
  35. package/dist/build/local-build.d.ts +2 -0
  36. package/dist/build/local-build.js +11 -9
  37. package/dist/cloudflare.d.ts +4 -1
  38. package/dist/cloudflare.js +1 -1
  39. package/dist/files.js +9 -8
  40. package/dist/git-admission.d.ts +3 -3
  41. package/dist/git-admission.js +4 -6
  42. package/dist/git-door.d.ts +8 -7
  43. package/dist/git-door.js +18 -17
  44. package/dist/git.d.ts +1 -1
  45. package/dist/host.d.ts +33 -40
  46. package/dist/host.js +153 -272
  47. package/dist/installation-execution.js +10 -5
  48. package/dist/installation-operations.d.ts +4 -180
  49. package/dist/installation-operations.js +1 -5
  50. package/dist/installation-store-generation.d.ts +4 -1
  51. package/dist/installation-store-generation.js +59 -6
  52. package/dist/internal/delivery.js +0 -1
  53. package/dist/internal/host-gate.d.ts +3 -2
  54. package/dist/internal/host-gate.js +5 -18
  55. package/dist/internal/invocation-timing.d.ts +16 -0
  56. package/dist/internal/invocation-timing.js +23 -0
  57. package/dist/internal/registry-installations.js +1 -0
  58. package/dist/invocation.d.ts +15 -14
  59. package/dist/invocation.js +58 -26
  60. package/dist/keys.d.ts +2 -19
  61. package/dist/keys.js +1 -37
  62. package/dist/mcp.d.ts +15 -27
  63. package/dist/mcp.js +105 -155
  64. package/dist/page/agent-browser.d.ts +15 -0
  65. package/dist/page/agent-browser.js +162 -0
  66. package/dist/page/app-permissions-port.d.ts +3 -0
  67. package/dist/page/app-permissions-port.js +43 -0
  68. package/dist/page/app-permissions.d.ts +11 -0
  69. package/dist/page/app-permissions.js +296 -0
  70. package/dist/page/isolated-page-theme.d.ts +2 -0
  71. package/dist/page/isolated-page-theme.js +11 -0
  72. package/dist/page/mount.d.ts +2 -13
  73. package/dist/page/mount.js +7 -93
  74. package/dist/page-script.d.ts +0 -3
  75. package/dist/page-script.js +3 -4
  76. package/dist/standalone/agent-archive-owner.d.ts +42 -0
  77. package/dist/standalone/agent-archive-owner.js +116 -0
  78. package/dist/standalone/agent-archive-schedules.d.ts +6 -0
  79. package/dist/standalone/agent-archive-schedules.js +76 -0
  80. package/dist/standalone/agent-archive.d.ts +31 -0
  81. package/dist/standalone/agent-archive.js +67 -0
  82. package/dist/standalone/agent-computers.d.ts +7 -6
  83. package/dist/standalone/agent-computers.js +4 -7
  84. package/dist/standalone/agent-mail.d.ts +2 -2
  85. package/dist/standalone/agents.d.ts +157 -20
  86. package/dist/standalone/agents.js +234 -30
  87. package/dist/standalone/app-builds-binding.js +1 -1
  88. package/dist/standalone/app-set.js +2 -54
  89. package/dist/standalone/app-source.d.ts +3 -4
  90. package/dist/standalone/app-source.js +12 -9
  91. package/dist/standalone/builds.d.ts +2 -14
  92. package/dist/standalone/builds.js +15 -13
  93. package/dist/standalone/computer-fleet.d.ts +4 -4
  94. package/dist/standalone/computer-fleet.js +13 -42
  95. package/dist/standalone/computer.d.ts +4 -3
  96. package/dist/standalone/computer.js +52 -53
  97. package/dist/standalone/dashboard-approval.d.ts +10 -0
  98. package/dist/standalone/dashboard-approval.js +66 -0
  99. package/dist/standalone/machine-link.d.ts +4 -221
  100. package/dist/standalone/machine-link.js +21 -15
  101. package/dist/standalone/media.d.ts +1 -0
  102. package/dist/standalone/media.js +20 -14
  103. package/dist/standalone/owner-routes.d.ts +16 -0
  104. package/dist/standalone/owner-routes.js +21 -0
  105. package/dist/standalone/permission-resources.d.ts +5 -0
  106. package/dist/standalone/permission-resources.js +31 -0
  107. package/dist/standalone/registry.d.ts +7 -0
  108. package/dist/standalone/registry.js +3 -0
  109. package/dist/standalone/tenancy.d.ts +0 -3
  110. package/dist/standalone/tenancy.js +2 -5
  111. package/dist/standalone/worker-routes.d.ts +19 -0
  112. package/dist/standalone/worker-routes.js +9 -0
  113. package/dist/standalone/worker.d.ts +1 -1
  114. package/dist/standalone/worker.js +27 -35
  115. package/dist/standalone/workflow-binding.d.ts +8 -3
  116. package/dist/standalone/workflow-binding.js +14 -5
  117. package/dist/standalone/workflow-git-capture.js +2 -1
  118. package/dist/standalone/workflow-host.d.ts +8 -103
  119. package/dist/standalone/workflow-host.js +1 -1
  120. package/dist/standalone/workflow-node-capture.js +2 -2
  121. package/dist/standalone/workflow-root-authority.d.ts +1 -1
  122. package/dist/standalone/workflow-root-authority.js +1 -1
  123. package/dist/standalone/workflow-schedules.d.ts +14 -5
  124. package/dist/standalone/workflow-schedules.js +11 -5
  125. package/dist/standalone/workflow-webhooks.d.ts +4 -18
  126. package/dist/standalone/workflow-webhooks.js +5 -5
  127. package/dist/standalone/workflows.d.ts +36 -28
  128. package/dist/standalone/workflows.js +6 -3
  129. package/dist/storage-frame.d.ts +6 -6
  130. package/dist/testing/capabilities.d.ts +7 -6
  131. package/dist/testing/capabilities.js +3 -6
  132. package/dist/testing/installation.d.ts +0 -1
  133. package/dist/testing/installation.js +2 -5
  134. package/dist/workers-git.d.ts +12 -4
  135. package/dist/workers-git.js +38 -35
  136. package/dist/workers.d.ts +9 -1
  137. package/dist/workers.js +44 -8
  138. package/dist/workflows.d.ts +2 -0
  139. package/dist/workflows.js +1 -0
  140. package/docs/api/README.md +7 -0
  141. package/docs/api/agent-schedules.md +175 -0
  142. package/docs/api/agents.md +34 -13
  143. package/docs/api/app-storage.md +1 -1
  144. package/docs/api/apps.md +323 -225
  145. package/docs/api/auth.md +0 -55
  146. package/docs/api/authorization.md +1300 -0
  147. package/docs/api/build.md +40 -37
  148. package/docs/api/cloudflare.md +3 -3
  149. package/docs/api/git.md +20 -11
  150. package/docs/api/host.md +64 -192
  151. package/docs/api/index.md +1 -1
  152. package/docs/api/invocation.md +14 -14
  153. package/docs/api/keys.md +1 -98
  154. package/docs/api/mcp.md +43 -40
  155. package/docs/api/modules.md +2 -0
  156. package/docs/api/page/mount.md +16 -19
  157. package/docs/api/workers-git.md +46 -12
  158. package/docs/api/workers.md +124 -235
  159. package/package.json +39 -20
  160. package/dist/page/computer-target.d.ts +0 -3
  161. package/dist/page/computer-target.js +0 -9
  162. package/dist/standalone/channel-session-door.d.ts +0 -4
  163. package/dist/standalone/channel-session-door.js +0 -72
@@ -114,8 +114,6 @@ export async function admitBackgroundFrame(key, frame, raw) {
114
114
  frame.host.origin = decision.kind;
115
115
  return decision;
116
116
  }
117
- /** Foreground reads one Instance runs at once; every other frame runs alone. */
118
- const concurrentReadFrames = 8;
119
117
  const frameIds = defineIdFamily({
120
118
  family: "app-invocation-frame",
121
119
  version: 1,
@@ -152,6 +150,7 @@ function immutableFacts(facts) {
152
150
  Object.freeze(facts.ancestry);
153
151
  return Object.freeze(facts);
154
152
  }
153
+ const concurrentReadFrames = 8;
155
154
  /**
156
155
  * First-come permits over one guest: a waiting exclusive frame holds back
157
156
  * every frame queued after it, so a stream of reads never starves a write.
@@ -160,15 +159,18 @@ function orderedPermits(total) {
160
159
  let available = total;
161
160
  const waiting = [];
162
161
  const admit = () => {
163
- for (let next = waiting[0]; next !== undefined && next.permits <= available; next = waiting[0]) {
162
+ for (let next = waiting[0]; next !== undefined; next = waiting[0]) {
163
+ const count = next.requested();
164
+ if (count > available)
165
+ break;
164
166
  waiting.shift();
165
- available -= next.permits;
166
- next.enter();
167
+ available -= count;
168
+ next.enter(count);
167
169
  }
168
170
  };
169
- return async (permits, work) => {
170
- await new Promise((enter) => {
171
- waiting.push({ permits, enter });
171
+ return async (requested, work) => {
172
+ const permits = await new Promise((enter) => {
173
+ waiting.push({ requested, enter });
172
174
  admit();
173
175
  });
174
176
  try {
@@ -185,31 +187,63 @@ function orderedPermits(total) {
185
187
  }
186
188
  };
187
189
  }
188
- /** A foreground call to a verified read method; only these share the guest. */
190
+ /** Only foreground reads declared by the verified API may share the guest. */
189
191
  function concurrentRead(facts) {
190
192
  return (facts.subject.kind === "call" &&
191
193
  facts.subject.replay === undefined &&
192
194
  facts.subject.method?.effect === "read");
193
195
  }
194
196
  /**
195
- * Admit frames against one guest. Verified foreground reads share it, up to
196
- * eight at once; writes, raw backend requests, Gills and replay run alone. Every guest binding packet names its frame, so a finished
197
- * frame's leftover timers cannot borrow a later frame's authority and a
198
- * normal finish keeps the guest warm. Closing authority early (revocation,
199
- * expiry, a write preempting replay) aborts the guest and so every frame
200
- * sharing it; replay only yields to a verified write.
197
+ * One Instance shares up to eight verified foreground reads; all other work
198
+ * runs alone in first-come order. Every finish fences its frame; the last live
199
+ * frame stops the native guest before closing authority and releasing admission.
200
+ * Binding checks alone cannot stop a timer mutating the facet's own SQLite.
201
+ * Revocation, expiry and a verified write preempting replay stop every live frame.
202
+ * Arrivals wait for a stopping guest. Once the opening deadline passes, new
203
+ * reads queue exclusively so the current cohort cannot be prolonged forever.
201
204
  */
202
205
  export function createInvocationGate(ports) {
203
- const withPermits = orderedPermits(concurrentReadFrames);
206
+ const permits = orderedPermits(concurrentReadFrames);
204
207
  const now = ports.now ?? Date.now;
208
+ let stopping;
209
+ let cohortDeadlineAt;
210
+ const withPermits = (read, work) => permits(() => read && (cohortDeadlineAt === undefined || now() < cohortDeadlineAt)
211
+ ? 1
212
+ : concurrentReadFrames, async () => {
213
+ while (stopping !== undefined)
214
+ await stopping;
215
+ return work();
216
+ });
217
+ const stopGuest = () => {
218
+ if (stopping !== undefined)
219
+ return stopping;
220
+ let aborted;
221
+ try {
222
+ aborted = ports.abortGuest();
223
+ }
224
+ catch (cause) {
225
+ aborted = Promise.reject(cause instanceof Error
226
+ ? cause
227
+ : new Error("App guest stop failed", { cause }));
228
+ }
229
+ const stopped = aborted.finally(() => {
230
+ cohortDeadlineAt = undefined;
231
+ stopping = undefined;
232
+ });
233
+ stopping = stopped;
234
+ return stopped;
235
+ };
205
236
  let foregroundWaiting = 0;
206
237
  const live = new Map();
207
238
  const finish = (state, reason) => {
208
239
  if (state.closing)
209
240
  return state.closing;
210
- live.delete(state.frame.frameId);
211
241
  state.controller.abort(reason);
212
- state.closing = ports.close(state.frame);
242
+ live.delete(state.frame.frameId);
243
+ state.closing =
244
+ live.size === 0
245
+ ? stopGuest().finally(() => ports.close(state.frame))
246
+ : ports.close(state.frame);
213
247
  return state.closing;
214
248
  };
215
249
  // The guest is shared: aborting it ends every live frame's guest work, and
@@ -220,7 +254,7 @@ export function createInvocationGate(ports) {
220
254
  live.delete(state.frame.frameId);
221
255
  state.controller.abort(reason);
222
256
  }
223
- const guest = ports.abortGuest();
257
+ const guest = stopGuest();
224
258
  for (const state of states)
225
259
  state.closing ??= guest.finally(() => ports.close(state.frame));
226
260
  return Promise.all([guest, ...states.map((state) => state.closing)]).then(() => undefined);
@@ -250,7 +284,7 @@ export function createInvocationGate(ports) {
250
284
  [...live.values()].some(({ frame }) => frame.facts.subject.kind === "call" &&
251
285
  frame.facts.subject.replay !== undefined))
252
286
  await abortAll(new AppInstanceError("revoked"));
253
- return await withPermits(concurrentRead(facts) ? 1 : concurrentReadFrames, async () => {
287
+ return await withPermits(concurrentRead(facts), async () => {
254
288
  if (facts.subject.kind === "call" &&
255
289
  facts.subject.replay !== undefined &&
256
290
  foregroundWaiting > 0)
@@ -271,6 +305,7 @@ export function createInvocationGate(ports) {
271
305
  signal: controller.signal,
272
306
  });
273
307
  const state = { frame, controller };
308
+ cohortDeadlineAt ??= deadlineAt;
274
309
  live.set(frameId, state);
275
310
  let timer;
276
311
  let stop = () => undefined;
@@ -312,7 +347,7 @@ export function createInvocationGate(ports) {
312
347
  };
313
348
  return {
314
349
  run,
315
- exclusive: (work) => withPermits(concurrentReadFrames, work),
350
+ exclusive: (work) => withPermits(false, work),
316
351
  active: (frameId) => {
317
352
  const state = live.get(frameId);
318
353
  if (state === undefined ||
@@ -331,14 +366,11 @@ export function createInvocationGate(ports) {
331
366
  }
332
367
  /**
333
368
  * Project an admitted principal into App attribution; only the host chooses
334
- * the fixed Computer context. The App caller grammar has no visitor actor, so
335
- * the anonymous public reader is attributed as the reserved `visitor` key
336
- * without a Computer. Host admission reads the principal, never this projection.
369
+ * the fixed Computer context. Host admission reads the principal, never this
370
+ * projection.
337
371
  */
338
372
  export function invocationCallerContext(principal, computerId) {
339
373
  const { actor } = principal;
340
- if (actor.kind === "visitor")
341
- return { computerId: null, actor: { kind: "team-key", keyId: "visitor" } };
342
374
  if (!("role" in principal))
343
375
  return { computerId, actor };
344
376
  return principal.soleMember === true
package/dist/keys.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import type { Grant, GrantVocabulary, Scope } from "@fungi.computer/ear-tag/grants";
2
2
  import type { KeyPrincipal, KeyRecord, KeyStore } from "@fungi.computer/ear-tag/keys";
3
- import type { AppToolPolicy } from "./apps/tools.js";
4
3
  /** Every capability an Oyster key may carry. */
5
4
  export type OysterKeyCapability = "apps.read" | "apps.use" | "agents.use" | "computer.use" | "machine.enroll";
6
5
  /**
@@ -28,8 +27,8 @@ export type OysterKeyStore = KeyStore<OysterKeyCapability>;
28
27
  /**
29
28
  * The minimal live facts `OysterHost`'s `keys.live` port returns: enough to
30
29
  * gate one call, never the key's name or timestamps. `null` for an unknown,
31
- * revoked, expired or suspended key -- the same closed set `keyAppAccess`
32
- * and `keyAllowsComputerUse` already treat as no authority.
30
+ * revoked, expired or suspended key, which `authorization.ts` treats as no
31
+ * authority.
33
32
  */
34
33
  export type LiveOysterKey = Readonly<{
35
34
  keyId: string;
@@ -37,19 +36,3 @@ export type LiveOysterKey = Readonly<{
37
36
  grantVersion: number;
38
37
  grants: readonly Grant<OysterKeyCapability>[];
39
38
  }>;
40
- /**
41
- * One live key's access to one installation: `write` when its grants admit
42
- * `apps.use` there, `read` when they admit `apps.read`, otherwise `null` --
43
- * including for an installation added after the key was issued, unless the
44
- * key holds "All Apps" (`targets: {kind: "all"}`). The same shape as an
45
- * Agent's stored {@link AppToolPolicy}; its caller adds `bindingName` (which
46
- * varies by caller: the plain installation id for the gate, the uppercased
47
- * binding name for MCP) to reach the full `AppToolAccess`.
48
- */
49
- export declare function keyAppAccess(key: LiveOysterKey, installationId: string): AppToolPolicy | null;
50
- /** Agent access is scoped to the granted Agent ids, never inferred from App grants. */
51
- export declare function keyAgentAccess(key: LiveOysterKey, agentId: string): boolean;
52
- /** Whether a live key's own grants turn its Computer access on. Off by default. */
53
- export declare function keyAllowsComputerUse(key: LiveOysterKey): boolean;
54
- /** Whether a live key's grants permit a one-use device enrollment. */
55
- export declare function keyAllowsMachineEnroll(key: LiveOysterKey): boolean;
package/dist/keys.js CHANGED
@@ -2,6 +2,7 @@
2
2
  * Oyster's own Ear Tag vocabulary: named keys an owner issues for selected
3
3
  * Apps and Agents, its Computer or machine enrollment. App grants never imply
4
4
  * Agent access. A key's id is its name, so name-derived identities stay stable.
5
+ * What a key reaches is decided only in `authorization.ts`.
5
6
  */
6
7
  import { defineGrants } from "@fungi.computer/ear-tag/grants";
7
8
  /**
@@ -32,40 +33,3 @@ export const computerUseGrant = {
32
33
  capability: "computer.use",
33
34
  targets: { kind: "all" },
34
35
  };
35
- /**
36
- * One live key's access to one installation: `write` when its grants admit
37
- * `apps.use` there, `read` when they admit `apps.read`, otherwise `null` --
38
- * including for an installation added after the key was issued, unless the
39
- * key holds "All Apps" (`targets: {kind: "all"}`). The same shape as an
40
- * Agent's stored {@link AppToolPolicy}; its caller adds `bindingName` (which
41
- * varies by caller: the plain installation id for the gate, the uppercased
42
- * binding name for MCP) to reach the full `AppToolAccess`.
43
- */
44
- export function keyAppAccess(key, installationId) {
45
- const level = oysterKeyVocabulary.grantsAllow(key.grants, "apps.use", installationId)
46
- ? "write"
47
- : oysterKeyVocabulary.grantsAllow(key.grants, "apps.read", installationId)
48
- ? "read"
49
- : null;
50
- if (level === null)
51
- return null;
52
- return {
53
- level,
54
- revision: `key:${key.keyId}:${String(key.grantVersion)}`,
55
- grantedBy: key.owner,
56
- };
57
- }
58
- /** Agent access is scoped to the granted Agent ids, never inferred from App grants. */
59
- export function keyAgentAccess(key, agentId) {
60
- return oysterKeyVocabulary.grantsAllow(key.grants, "agents.use", agentId);
61
- }
62
- /** Whether a live key's own grants turn its Computer access on. Off by default. */
63
- export function keyAllowsComputerUse(key) {
64
- return oysterKeyVocabulary.grantsAllow(key.grants, "computer.use");
65
- }
66
- /** Whether a live key's grants permit a one-use device enrollment. */
67
- export function keyAllowsMachineEnroll(key) {
68
- return (key.grants.length === 1 &&
69
- key.grants[0]?.capability === "machine.enroll" &&
70
- oysterKeyVocabulary.grantsAllow(key.grants, "machine.enroll"));
71
- }
package/dist/mcp.d.ts CHANGED
@@ -2,12 +2,11 @@ import { z } from "zod";
2
2
  import { type AppToolPolicy } from "./apps/tools.js";
3
3
  import type { DeclaredOperations, Installation } from "./apps/installation.js";
4
4
  import { type CapabilitySource, type Grants, type ModuleRegistration } from "@fungi.computer/mycelium";
5
- import { appToolSchemas } from "@fungi.computer/forage";
6
5
  import { computerPageSchema } from "@fungi.computer/computer/fleet/inventory-wire";
7
6
  import type { InvocationContext } from "@fungi.computer/mycelium";
8
7
  import type { ComputerControlPort } from "./computer-control-port.js";
9
8
  export type { ComputerControlPort } from "./computer-control-port.js";
10
- import type { OysterHost, OysterPrincipal } from "./host.js";
9
+ import type { OysterHost } from "./host.js";
11
10
  import type { TenantPrincipal } from "./tenant.js";
12
11
  import { type VerifiedRelease } from "./release.js";
13
12
  /** Captured capabilities and grants offered to one admitted MCP caller. */
@@ -17,6 +16,8 @@ export type OysterMcpCatalogSnapshot = Readonly<{
17
16
  /** An omitted module has no grant. */
18
17
  grants: Grants;
19
18
  }>;
19
+ /** Shared host result guidance for Agent and MCP instructions, not per method. */
20
+ export declare const appApprovalInstructions: string;
20
21
  declare const fleetComputerId: z.ZodObject<{
21
22
  computerId: z.ZodString;
22
23
  }, z.core.$strict>;
@@ -33,10 +34,10 @@ declare const fleetCreation: z.ZodDiscriminatedUnion<[z.ZodObject<{
33
34
  }, z.core.$strict>;
34
35
  }, z.core.$strict>, z.ZodObject<{
35
36
  kind: z.ZodEnum<{
37
+ invalid: "invalid";
36
38
  denied: "denied";
37
39
  uncertain: "uncertain";
38
40
  conflict: "conflict";
39
- invalid: "invalid";
40
41
  }>;
41
42
  requestId: z.ZodString;
42
43
  }, z.core.$strict>], "kind">;
@@ -135,10 +136,10 @@ export declare function computerControlSpecs(port: ComputerControlPort): {
135
136
  }, z.core.$strict>;
136
137
  }, z.core.$strict>, z.ZodObject<{
137
138
  kind: z.ZodEnum<{
139
+ invalid: "invalid";
138
140
  denied: "denied";
139
141
  uncertain: "uncertain";
140
142
  conflict: "conflict";
141
- invalid: "invalid";
142
143
  }>;
143
144
  requestId: z.ZodString;
144
145
  }, z.core.$strict>], "kind">;
@@ -159,7 +160,7 @@ export declare class AppMethodRefused extends Error {
159
160
  * The App-tool access one caller's catalog capture used, re-checked at call
160
161
  * time against a fresh read. An Agent's policy is one blanket level for
161
162
  * every installation (`host.appToolPolicy`); a key's is its own grants,
162
- * resolved per installation (`keyAppAccess`) -- an installation the key has
163
+ * resolved per installation (`appToolAccess`) -- an installation the key has
163
164
  * no grant for is captured as absent, never merely refused, so it is never
164
165
  * listed at all.
165
166
  */
@@ -171,35 +172,23 @@ export type OysterCallerAccess = Readonly<{
171
172
  keyId: string;
172
173
  byInstallation: ReadonlyMap<string, AppToolPolicy>;
173
174
  }> | null;
174
- /** What one Release contributes to every MCP catalog: its verified build,
175
- * its declared operations and its compiled tool schemas. */
175
+ /** Immutable Release data shared by captures; no validator graphs live here. */
176
176
  export type ReleaseSurface = Readonly<{
177
177
  release: VerifiedRelease;
178
178
  api: NonNullable<VerifiedRelease["api"]>;
179
179
  declaredOperations: DeclaredOperations;
180
- toolSchemas: ReturnType<typeof appToolSchemas>;
180
+ methods: NonNullable<VerifiedRelease["api"]>["methods"];
181
181
  }>;
182
182
  /**
183
- * Per-host memo of each installed Release's catalog surface. A Release's
184
- * artifacts are immutable and content-addressed, so verifying the build,
185
- * deriving its operations and compiling its tool schemas happens once per
186
- * Release ID and artifact digest, not on every catalog capture: an MCP call
187
- * captures the catalog several times (refresh, session acquire, the pre-call
188
- * authority check), and redoing this for every installed App on each one
189
- * exhausted a Durable Object's CPU and memory. Only Release-derived facts are
190
- * cached; installation authority, caller access and tool-family listings
191
- * are still read fresh on every capture. A failed build is not cached.
183
+ * Per-host verified data, keyed by immutable Release ID and artifact digest.
184
+ * Authority, caller access and tool-family listings stay fresh per capture.
185
+ * Failed verification is not cached.
192
186
  */
193
187
  export type AppSurfaceCache = (release: Installation["release"]) => Promise<ReleaseSurface | null>;
194
188
  /** Make one {@link AppSurfaceCache} for a host whose artifact store and capabilities stay fixed. */
195
189
  export declare function appSurfaceCache(host: Pick<OysterHost, "artifacts" | "capabilities">): AppSurfaceCache;
196
190
  /** Derive the current principal's admitted App modules and grants. */
197
- export declare function oysterAppSnapshot(host: Pick<OysterHost, "installations" | "instances" | "artifacts" | "guestLimits" | "backgroundOriginKey" | "computerId" | "capabilities" | "appToolPolicy" | "keys">, surfaces: AppSurfaceCache, principal: OysterPrincipal, installations: readonly Installation[], access: OysterCallerAccess, sessionId?: string,
198
- /** Shares one tool-family listing per Instance across the two
199
- * `appModules` passes one `oysterAppMethods` call can make (the eager
200
- * grants/version pass, and a session-scoped re-derivation); never shared
201
- * across separate calls, so it never serves another request's listing. */
202
- familyListingCache?: Map<string, unknown>): Promise<Readonly<{
191
+ export declare function oysterAppSnapshot(host: Pick<OysterHost, "installations" | "instances" | "artifacts" | "guestLimits" | "backgroundOriginKey" | "computerId" | "capabilities" | "appToolPolicy" | "keys" | "approvals">, surfaces: AppSurfaceCache, principal: TenantPrincipal, installations: readonly Installation[], access: OysterCallerAccess, sessionId: string | null, mode: "direct" | "approval"): Promise<Readonly<{
203
192
  modules: Readonly<Record<string, ModuleRegistration>>;
204
193
  levels: Readonly<Record<string, AppToolPolicy["level"]>>;
205
194
  fences: Readonly<Record<string, string>>;
@@ -208,14 +197,13 @@ familyListingCache?: Map<string, unknown>): Promise<Readonly<{
208
197
  /**
209
198
  * Capture installed App methods for MCP or agents as one principal-scoped source.
210
199
  * Enabled installations become `env.<APP>` modules using the Instance call path; a dotted method such as `repos.list` is `env.<APP>.repos.list()`.
211
- * Visitors see only public read methods; owners see every discoverable
212
- * method. A key sees only the installations its own grants admit, each at
200
+ * Owners see every discoverable method. A key sees only the installations its own grants admit, each at
213
201
  * the level (`read`/`write`) those grants give it; Mycelium itself refuses a
214
202
  * write call up front when the module's own grant is `read`
215
203
  * (`mycelium/src/access.ts`). Call again to capture changes to Releases,
216
204
  * installation authority or the caller's own grants.
217
205
  */
218
- export declare function oysterAppMethods(host: Pick<OysterHost, "installations" | "instances" | "artifacts" | "guestLimits" | "backgroundOriginKey" | "computerId" | "capabilities" | "appToolPolicy" | "keys" | "approvals">, principal: OysterPrincipal, access: OysterCallerAccess, surfaces: AppSurfaceCache, preloadedInstallations?: readonly Installation[]): Promise<CapabilitySource & {
206
+ export declare function oysterAppMethods(host: Pick<OysterHost, "installations" | "instances" | "artifacts" | "guestLimits" | "backgroundOriginKey" | "computerId" | "capabilities" | "appToolPolicy" | "keys" | "approvals">, principal: TenantPrincipal, access: OysterCallerAccess, surfaces: AppSurfaceCache, preloadedInstallations?: readonly Installation[]): Promise<CapabilitySource & {
219
207
  readonly grants: Grants;
220
208
  }>;
221
209
  /**
@@ -224,7 +212,7 @@ export declare function oysterAppMethods(host: Pick<OysterHost, "installations"
224
212
  * still checks its host-issued grant on every call. Null when the host has no
225
213
  * Computer or the caller has no access. Grants name the captured module.
226
214
  */
227
- export declare function oysterComputerTools(host: Pick<OysterHost, "computerId" | "computer">, principal: OysterPrincipal): Promise<(CapabilitySource & {
215
+ export declare function oysterComputerTools(host: Pick<OysterHost, "computerId" | "computer">, principal: TenantPrincipal): Promise<(CapabilitySource & {
228
216
  readonly grants: Grants;
229
217
  }) | null>;
230
218
  /**