@fungi.computer/oyster 0.3.2 → 0.4.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 (69) hide show
  1. package/dist/agent-computer-source.d.ts +42 -0
  2. package/dist/agent-computer-source.js +88 -0
  3. package/dist/agent-computers.d.ts +104 -0
  4. package/dist/agent-computers.js +227 -0
  5. package/dist/agents.d.ts +10 -0
  6. package/dist/agents.js +33 -0
  7. package/dist/browser/boot.d.ts +2 -2
  8. package/dist/browser/boot.js +10 -3
  9. package/dist/browser/browser.d.ts +1 -1
  10. package/dist/browser/browser.js +2 -1
  11. package/dist/browser/cdp-client.d.ts +2 -0
  12. package/dist/browser/errors.d.ts +2 -2
  13. package/dist/browser/errors.js +2 -2
  14. package/dist/browser/index.d.ts +3 -2
  15. package/dist/browser/index.js +3 -2
  16. package/dist/browser/page.js +11 -4
  17. package/dist/browser/sessions.d.ts +7 -4
  18. package/dist/browser/sessions.js +159 -0
  19. package/dist/browser/url-policy.d.ts +4 -2
  20. package/dist/browser/url-policy.js +4 -2
  21. package/dist/computer-control-port.d.ts +26 -0
  22. package/dist/computer-control-port.js +1 -0
  23. package/dist/host.d.ts +3 -0
  24. package/dist/machine-link/delivery.d.ts +1 -1
  25. package/dist/machine-link/delivery.js +2 -3
  26. package/dist/machine-link/host.d.ts +37 -6
  27. package/dist/machine-link/host.js +209 -14
  28. package/dist/machine-link/link.d.ts +13 -1
  29. package/dist/machine-link/link.js +20 -7
  30. package/dist/mcp.d.ts +144 -10
  31. package/dist/mcp.js +130 -13
  32. package/dist/page/computer-handles.d.ts +3 -3
  33. package/dist/page-script.js +1 -1
  34. package/dist/standalone/agent-computers.d.ts +29 -0
  35. package/dist/standalone/agent-computers.js +101 -0
  36. package/dist/standalone/agent-mail.d.ts +41 -18
  37. package/dist/standalone/agent-model-choice.d.ts +26 -0
  38. package/dist/standalone/agent-model-choice.js +72 -0
  39. package/dist/standalone/agents.d.ts +44 -4
  40. package/dist/standalone/agents.js +502 -46
  41. package/dist/standalone/computer-control.d.ts +4 -0
  42. package/dist/standalone/computer-control.js +62 -0
  43. package/dist/standalone/computer-fleet.d.ts +1 -0
  44. package/dist/standalone/computer-fleet.js +1 -1
  45. package/dist/standalone/computer.d.ts +21 -2
  46. package/dist/standalone/computer.js +12 -0
  47. package/dist/standalone/machine-link.d.ts +10 -0
  48. package/dist/standalone/machine-link.js +6 -0
  49. package/dist/standalone/registry.d.ts +9 -0
  50. package/dist/standalone/registry.js +9 -0
  51. package/dist/standalone/worker.js +12 -0
  52. package/dist/standalone/workflow-binding.d.ts +2 -2
  53. package/dist/standalone/workflow-host.d.ts +11 -11
  54. package/dist/standalone/workflows.d.ts +22 -22
  55. package/dist/storage-frame.d.ts +3 -3
  56. package/dist/workers-git.d.ts +2 -0
  57. package/dist/workers-git.js +2 -0
  58. package/dist/workers.d.ts +8 -8
  59. package/docs/api/agents.md +66 -0
  60. package/docs/api/apps.md +104 -104
  61. package/docs/api/build-contract.md +35 -35
  62. package/docs/api/build.md +12 -12
  63. package/docs/api/host.md +61 -61
  64. package/docs/api/mcp.md +127 -13
  65. package/docs/api/root-data.md +1 -1
  66. package/docs/api/tenancy.md +1 -1
  67. package/docs/api/workers-git.md +3 -2
  68. package/docs/api/workers.md +81 -83
  69. package/package.json +25 -17
@@ -0,0 +1,4 @@
1
+ import type { ComputerControlPort } from "../mcp.js";
2
+ import type { StandaloneRegistryRpc } from "./registry.js";
3
+ /** MCP control speaks to the same owner-admitted durable fleet as HTTP. */
4
+ export declare function standaloneComputerControl(registry: Pick<StandaloneRegistryRpc, "computerFleet">, memberId: string): ComputerControlPort;
@@ -0,0 +1,62 @@
1
+ import { z } from "zod";
2
+ import { computerStateSchema, } from "@fungi.computer/computer/fleet";
3
+ import { teamComputerDirectorySchema } from "@fungi.computer/nacre/workbench";
4
+ const inventoryComputer = z.strictObject({
5
+ computerId: z.string(),
6
+ name: z.string(),
7
+ createdAt: z.number(),
8
+ updatedAt: z.number(),
9
+ });
10
+ const inventoryPage = z.strictObject({
11
+ computers: z.array(inventoryComputer),
12
+ nextOffset: z.number().int().nonnegative().nullable(),
13
+ });
14
+ const inventoryError = z.strictObject({
15
+ error: z.string(),
16
+ });
17
+ /** MCP control speaks to the same owner-admitted durable fleet as HTTP. */
18
+ export function standaloneComputerControl(registry, memberId) {
19
+ const fleet = (request) => registry.computerFleet(memberId, request);
20
+ const available = (result) => {
21
+ if (result === null || inventoryError.safeParse(result).success)
22
+ throw new Error("Computer fleet refused the request");
23
+ return result;
24
+ };
25
+ return {
26
+ list: async (page) => teamComputerDirectorySchema.parse(inventoryPage.parse(available(await fleet({ operation: "list", page })))),
27
+ state: async (computerId) => computerStateSchema.parse(available(await fleet({ operation: "state", computerId }))),
28
+ start: async (computerId) => computerStateSchema.parse(available(await fleet({ operation: "start", computerId }))),
29
+ create: async (input) => {
30
+ let result;
31
+ try {
32
+ result = await fleet({ operation: "create", request: input });
33
+ }
34
+ catch {
35
+ // Creation may have persisted before the RPC failed. Surface its
36
+ // identity so the caller can retry, never silently mint another one.
37
+ return { kind: "uncertain" };
38
+ }
39
+ if (result === null)
40
+ return { kind: "denied" };
41
+ const error = inventoryError.safeParse(result);
42
+ if (error.success) {
43
+ switch (error.data.error) {
44
+ case "owner_required":
45
+ return { kind: "denied" };
46
+ case "name_taken":
47
+ case "request_conflict":
48
+ return { kind: "conflict" };
49
+ case "partial_init":
50
+ return { kind: "uncertain" };
51
+ default:
52
+ return { kind: "invalid" };
53
+ }
54
+ }
55
+ const computer = inventoryComputer.parse(result);
56
+ return {
57
+ kind: "created",
58
+ computer: { computerId: computer.computerId, name: computer.name },
59
+ };
60
+ },
61
+ };
62
+ }
@@ -23,6 +23,7 @@ export declare const standaloneFleetRequestSchema: z.ZodDiscriminatedUnion<[z.Zo
23
23
  resolve: "resolve";
24
24
  select: "select";
25
25
  start: "start";
26
+ state: "state";
26
27
  }>;
27
28
  computerId: z.ZodString;
28
29
  }, z.core.$strict>, z.ZodObject<{
@@ -15,7 +15,7 @@ export const standaloneFleetRequestSchema = z.discriminatedUnion("operation", [
15
15
  page: computerPageSchema,
16
16
  }),
17
17
  z.strictObject({
18
- operation: z.enum(["read", "resolve", "start", "select"]),
18
+ operation: z.enum(["read", "resolve", "state", "start", "select"]),
19
19
  computerId: fleetComputerId,
20
20
  }),
21
21
  z.strictObject({
@@ -4,7 +4,8 @@ import { type PreviewSelector } from "./preview.js";
4
4
  import { type StandaloneTenancyEnv } from "./tenancy.js";
5
5
  import * as z from "zod";
6
6
  import { computerProcessSchema, computerToolResultSchema, readOutputSchema } from "@fungi.computer/computer-contract";
7
- import { ComputerDO as NativeComputerDO, type AttachedBackend, type ComputerEnv } from "@fungi.computer/computer";
7
+ import { ComputerDO as NativeComputerDO, type AttachedBackend, type ComputerEnv, type ComputerFilesRpc } from "@fungi.computer/computer";
8
+ import { patchInputSchema, patchOutputSchema } from "@fungi.computer/knapsack/files-patch";
8
9
  import { listOutputSchema, writeOutputSchema, mkdirOutputSchema, computerPortsOutputSchema, computerPortOutputSchema, computerPortRemovalOutputSchema, processListOutputSchema, processKillOutputSchema, computerOperationResultSchema, type ComputerExecutionHostHooks, type ComputerValue } from "@fungi.computer/computer-contract";
9
10
  import { type WorkflowGitCredential, type WorkflowGitPassGrant } from "@fungi.computer/braid/runtime";
10
11
  import { type ComputerAction, type ComputerGrant } from "@fungi.computer/computer/fleet";
@@ -66,8 +67,13 @@ export type StandaloneWorkflowGitIssuer = Readonly<{
66
67
  export type ComputerFetch = {
67
68
  fetch(request: Request): Promise<Response>;
68
69
  };
70
+ /** The additional native doors an attached Agent may use on this Computer. */
71
+ export type StandaloneAgentComputerRpc = ComputerFilesRpc & Readonly<{
72
+ tool(authority: ComputerGrant, capabilityId: string, input: ComputerValue): Promise<StandaloneComputerResult>;
73
+ filePatch(authority: ComputerGrant, input: z.output<typeof patchInputSchema>): Promise<z.output<typeof patchOutputSchema>>;
74
+ }>;
69
75
  /** Durable workspace and existing backings, with fleet checks at native acceptance. */
70
- export declare class ComputerDO extends NativeComputerDO implements ComputerFetch {
76
+ export declare class ComputerDO extends NativeComputerDO implements ComputerFetch, StandaloneAgentComputerRpc {
71
77
  #private;
72
78
  private readonly previewBindings;
73
79
  constructor(state: DurableObjectState, previewBindings: StandaloneComputerOwnerEnv);
@@ -77,6 +83,17 @@ export declare class ComputerDO extends NativeComputerDO implements ComputerFetc
77
83
  startHostedComputer(authority: ComputerGrant): Promise<{
78
84
  state: "running";
79
85
  }>;
86
+ /** Observe an admitted fleet Computer without starting its backend. */
87
+ readHostedComputerState(authority: ComputerGrant): Promise<{
88
+ state: "queued";
89
+ position: number;
90
+ } | {
91
+ state: "running" | "starting";
92
+ position?: never;
93
+ } | {
94
+ state: "running" | "sleeping";
95
+ position?: never;
96
+ }>;
80
97
  /** A read-only preflight over the maintained hosted-route selector. */
81
98
  buildBackend(): OysterBuildBackend;
82
99
  /**
@@ -120,6 +137,8 @@ export declare class ComputerDO extends NativeComputerDO implements ComputerFetc
120
137
  /** Binary files share the same native filesystem as commands and PTYs. */
121
138
  fileRead(authority: ComputerGrant, path: string): Promise<Uint8Array | null>;
122
139
  fileWrite(authority: ComputerGrant, path: string, bytes: Uint8Array): Promise<void>;
140
+ /** Patch is handled by the Computer's pinned native engine, not by the Agent. */
141
+ filePatch(authority: ComputerGrant, input: z.output<typeof patchInputSchema>): Promise<z.output<typeof patchOutputSchema>>;
123
142
  fileReplace(authority: ComputerGrant, path: string, expectedContent: string, content: string): Promise<{
124
143
  path: string;
125
144
  bytes: number;
@@ -11,6 +11,7 @@ import { Base64 } from "effect/encoding";
11
11
  import * as z from "zod";
12
12
  import { ComputerPortConflictError, computerByteReadReplySchema, computerProcessSchema, installationProcessScope, processStartInputSchema, processStartToolInputSchema, computerCapabilities, computerToolResultSchema, readOutputSchema, } from "@fungi.computer/computer-contract";
13
13
  import { ComputerDO as NativeComputerDO, ComputerOperationConflictError, } from "@fungi.computer/computer";
14
+ import { patchInputSchema, patchOutputSchema, } from "@fungi.computer/knapsack/files-patch";
14
15
  import { ComputerFileConflict, computerExecutionRequestSchema, computerValueSchema, computerPortInputSchema, listInputSchema, listOutputSchema, readBytesInputSchema, writeInputSchema, replaceInputSchema, writeOutputSchema, mkdirOutputSchema, computerPortsOutputSchema, computerPortOutputSchema, computerPortRemovalOutputSchema, processListOutputSchema, processKillOutputSchema, computerOperationResultSchema, mintComputerRequestId, processIdInputSchema, } from "@fungi.computer/computer-contract";
15
16
  import { workflowGitExecutionEnvironment, workflowGitPassGrantSchema, } from "@fungi.computer/braid/runtime";
16
17
  import { checkComputerGrant, computerActionSchema, computerGrantSchema, createFleetBacking, FleetGrantRefused, FleetLifecycle, } from "@fungi.computer/computer/fleet";
@@ -173,6 +174,11 @@ export class ComputerDO extends NativeComputerDO {
173
174
  await run();
174
175
  });
175
176
  }
177
+ /** Observe an admitted fleet Computer without starting its backend. */
178
+ readHostedComputerState(authority) {
179
+ this.#accept(authority, "start");
180
+ return this.#fleetLifecycle().readComputerState();
181
+ }
176
182
  #previewGrant(ownerId) {
177
183
  return {
178
184
  computerId: this.#id(),
@@ -455,6 +461,12 @@ export class ComputerDO extends NativeComputerDO {
455
461
  this.#accept(authority, "files.writeBytes");
456
462
  });
457
463
  }
464
+ /** Patch is handled by the Computer's pinned native engine, not by the Agent. */
465
+ async filePatch(authority, input) {
466
+ return this.applyPatchAfterAcceptance(input, () => {
467
+ this.#accept(authority, "files.edit");
468
+ });
469
+ }
458
470
  async fileReplace(authority, path, expectedContent, content) {
459
471
  try {
460
472
  const result = await this.replaceFileIfUnchangedAfterAcceptance({ path, expectedContent, content }, () => {
@@ -265,6 +265,7 @@ export declare function standaloneMachineLinkRoutes(env: StandaloneMachineLinkEn
265
265
  /** One device's link; directory, credentials and owner state remain elsewhere. */
266
266
  export declare class MachineLinkDO extends DurableObject<StandaloneMachineLinkEnv> {
267
267
  private readonly link;
268
+ alarm(): Promise<void>;
268
269
  fetch(request: Request): Promise<Response>;
269
270
  deliver(id: MachineLinkIdentity, caller: MachineLinkCaller, agentId: string, envelope: PostEnvelope): Promise<PostDeliveryResult>;
270
271
  operation(id: MachineLinkIdentity, caller: MachineLinkCaller, agentId: string, operation: MachineAgentOperation): Promise<import("effect/Schema").Json>;
@@ -276,6 +277,15 @@ export declare class MachineLinkDO extends DurableObject<StandaloneMachineLinkEn
276
277
  readErasure(): Promise<{
277
278
  erased: boolean;
278
279
  }>;
280
+ deadLetters(id: MachineLinkIdentity, afterKey?: string): Promise<Readonly<{
281
+ letters: readonly Readonly<{
282
+ key: string;
283
+ reason: "refused" | "exhausted" | "expired";
284
+ envelope: PostEnvelope;
285
+ attempts: number;
286
+ }>[];
287
+ nextCursor: string | undefined;
288
+ }>>;
279
289
  webSocketMessage(socket: WebSocket, message: string | ArrayBuffer): Promise<void>;
280
290
  webSocketClose(socket: WebSocket, code: number, reason: string): void;
281
291
  webSocketError(socket: WebSocket): void;
@@ -176,6 +176,9 @@ export class MachineLinkDO extends DurableObject {
176
176
  console.error(operation, cause);
177
177
  },
178
178
  });
179
+ alarm() {
180
+ return this.link.alarm();
181
+ }
179
182
  fetch(request) {
180
183
  return this.link.fetch(request);
181
184
  }
@@ -197,6 +200,9 @@ export class MachineLinkDO extends DurableObject {
197
200
  readErasure() {
198
201
  return this.link.readErasure();
199
202
  }
203
+ deadLetters(id, afterKey) {
204
+ return this.link.deadLetters(id, afterKey);
205
+ }
200
206
  webSocketMessage(socket, message) {
201
207
  return this.link.webSocketMessage(socket, message);
202
208
  }
@@ -61,6 +61,15 @@ export declare abstract class StandaloneRegistry extends OysterRegistry implemen
61
61
  }>[];
62
62
  readonly nextOffset: number | null;
63
63
  } | ({
64
+ state: "queued";
65
+ position: number;
66
+ } & Disposable) | ({
67
+ state: "running" | "starting";
68
+ position?: never | undefined;
69
+ } & Disposable) | ({
70
+ state: "running" | "sleeping";
71
+ position?: never | undefined;
72
+ } & Disposable) | ({
64
73
  state: "running";
65
74
  } & Disposable) | {
66
75
  error: string;
@@ -129,6 +129,15 @@ export class StandaloneRegistry extends OysterRegistry {
129
129
  await this.#fleetStorage.put("selected-computer", input.computerId);
130
130
  return computer;
131
131
  }
132
+ case "state":
133
+ if (this.#computers.read(input.computerId) === null)
134
+ return null;
135
+ return this.#env.COMPUTER.getByName(input.computerId).readHostedComputerState({
136
+ computerId: input.computerId,
137
+ actions: new Set(["start"]),
138
+ expiresAt: Date.now() + 60_000,
139
+ provenance: { actor: memberId },
140
+ });
132
141
  case "start":
133
142
  if (this.#computers.read(input.computerId) === null)
134
143
  return null;
@@ -5,6 +5,8 @@
5
5
  */
6
6
  import { createOysterGitWorker, createOysterGitMcpOwner, oysterGitRoot, } from "../workers-git.js";
7
7
  import { defaultTenant, tenantScope } from "../tenant.js";
8
+ import { oysterPrincipalIsOwner } from "../host.js";
9
+ import { standaloneComputerControl } from "./computer-control.js";
8
10
  import { onBoxVmIngress } from "@fungi.computer/computer";
9
11
  import { WorkerEntrypoint } from "cloudflare:workers";
10
12
  import { installationWorkerEntrypoint } from "../installation-execution.js";
@@ -28,6 +30,8 @@ import { Hono } from "hono";
28
30
  import { HTTPException } from "hono/http-exception";
29
31
  import { standaloneBuildRoutes } from "./builds.js";
30
32
  import { standaloneComputerFleetRoutes, standaloneComputerSelection, } from "./computer-fleet.js";
33
+ import { standaloneAgentComputerRoutes } from "./agent-computers.js";
34
+ import { agentDirectoryForScope } from "./agent-directory.js";
31
35
  import { serveOysterComputerEgress, } from "./computer-egress.js";
32
36
  import { appSourceOwnerRoutes, } from "./app-source.js";
33
37
  export { StandaloneGraphWorkflow } from "./workflows.js";
@@ -65,6 +69,9 @@ const standaloneRoot = (selectComputer) => {
65
69
  homeInstallationId: (env) => env.OYSTER_HOME_INSTALLATION,
66
70
  agents: standaloneAgents,
67
71
  workflows: standaloneWorkflowControl,
72
+ computerControl: (env, scope) => (principal) => oysterPrincipalIsOwner(principal)
73
+ ? standaloneComputerControl(tenantRegistry(env, scope), principal.actor.memberId)
74
+ : null,
68
75
  workspace: async (env, keysLive, scope, request) => standaloneWorkspace(env, keysLive, await selectComputer(tenantRegistry(env, scope), request)),
69
76
  capabilities: (env, workspace, keysLive) => [
70
77
  ...standaloneComputerCapabilities(env, workspace, keysLive),
@@ -97,6 +104,11 @@ const standaloneRoot = (selectComputer) => {
97
104
  ownerRoutes: (env, scope) => appSourceOwnerRoutes(env, scope)
98
105
  .route("/", standaloneBuildRoutes(() => tenantRegistry(env, scope)))
99
106
  .route("/", standaloneComputerFleetRoutes(() => tenantRegistry(env, scope)))
107
+ .route("/", standaloneAgentComputerRoutes({
108
+ attach: (principal, command) => agentDirectoryForScope(env, scope).attachComputer(command, principal, scope.tenantId),
109
+ detach: (principal, command) => agentDirectoryForScope(env, scope).detachComputer(command, principal, scope.tenantId),
110
+ read: (principal, agentId) => agentDirectoryForScope(env, scope).readAgentComputers(agentId, principal, scope.tenantId),
111
+ }))
100
112
  .route("/", standaloneWorkflowWebhookControls(env, () => root.hostFor(env, scope))),
101
113
  });
102
114
  return root;
@@ -72,10 +72,10 @@ export declare const workflowFrameMethods: {
72
72
  readonly input: {
73
73
  readonly [x: string]: S.Json;
74
74
  };
75
+ readonly requestId: string;
75
76
  readonly bindings: Record<string, string | {
76
77
  kind: "team-source";
77
78
  }>;
78
- readonly requestId: string;
79
79
  readonly definition: unknown;
80
80
  readonly delivery?: {
81
81
  readonly agentId: string;
@@ -86,10 +86,10 @@ export declare const workflowFrameMethods: {
86
86
  readonly input: {
87
87
  readonly [x: string]: S.Json;
88
88
  };
89
+ readonly requestId: string;
89
90
  readonly bindings: Record<string, string | {
90
91
  kind: "team-source";
91
92
  }>;
92
- readonly requestId: string;
93
93
  readonly definition: S.Json;
94
94
  readonly published: {
95
95
  workflowId: string;
@@ -103,7 +103,7 @@ export declare function createStandaloneWorkflowHost(owner: WatchdogSqliteOwner,
103
103
  authorityVersion: number;
104
104
  declaredOperationsDigest: import("@fungi.computer/puffball").Sha256Digest;
105
105
  approvedGrants: readonly Readonly<{
106
- capability: "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";
106
+ capability: "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "approvals" | "agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "agent.toolPolicy" | "agent.read" | "agent.send" | "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";
107
107
  capabilityApproved: true;
108
108
  operations: readonly string[];
109
109
  resources: "tenant-scope" | readonly Readonly<{
@@ -239,7 +239,7 @@ export declare function createStandaloneWorkflowHost(owner: WatchdogSqliteOwner,
239
239
  authorityVersion: number;
240
240
  declaredOperationsDigest: import("@fungi.computer/puffball").Sha256Digest;
241
241
  approvedGrants: readonly Readonly<{
242
- capability: "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";
242
+ capability: "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "approvals" | "agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "agent.toolPolicy" | "agent.read" | "agent.send" | "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";
243
243
  capabilityApproved: true;
244
244
  operations: readonly string[];
245
245
  resources: "tenant-scope" | readonly Readonly<{
@@ -325,7 +325,7 @@ export declare function createStandaloneWorkflowHost(owner: WatchdogSqliteOwner,
325
325
  authorityVersion: number;
326
326
  declaredOperationsDigest: import("@fungi.computer/puffball").Sha256Digest;
327
327
  approvedGrants: readonly Readonly<{
328
- capability: "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";
328
+ capability: "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "approvals" | "agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "agent.toolPolicy" | "agent.read" | "agent.send" | "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";
329
329
  capabilityApproved: true;
330
330
  operations: readonly string[];
331
331
  resources: "tenant-scope" | readonly Readonly<{
@@ -444,7 +444,7 @@ export declare function createStandaloneWorkflowHost(owner: WatchdogSqliteOwner,
444
444
  authorityVersion: number;
445
445
  declaredOperationsDigest: import("@fungi.computer/puffball").Sha256Digest;
446
446
  approvedGrants: readonly Readonly<{
447
- capability: "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";
447
+ capability: "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "approvals" | "agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "agent.toolPolicy" | "agent.read" | "agent.send" | "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";
448
448
  capabilityApproved: true;
449
449
  operations: readonly string[];
450
450
  resources: "tenant-scope" | readonly Readonly<{
@@ -544,7 +544,7 @@ export declare function createStandaloneWorkflowHost(owner: WatchdogSqliteOwner,
544
544
  authorityVersion: number;
545
545
  declaredOperationsDigest: import("@fungi.computer/puffball").Sha256Digest;
546
546
  approvedGrants: readonly Readonly<{
547
- capability: "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";
547
+ capability: "computer.list" | "computer.readFile" | "computer.writeFile" | "computer.execute" | "approvals" | "agent.list" | "agent.manage" | "agent.rename" | "agent.avatar" | "agent.toolPolicy" | "agent.read" | "agent.send" | "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";
548
548
  capabilityApproved: true;
549
549
  operations: readonly string[];
550
550
  resources: "tenant-scope" | readonly Readonly<{
@@ -678,9 +678,9 @@ export declare function createStandaloneWorkflowHost(owner: WatchdogSqliteOwner,
678
678
  readonly values: {
679
679
  readonly [x: string]: import("@fungi.computer/braid").Mapping;
680
680
  };
681
+ readonly id: string;
681
682
  readonly kind: "app";
682
683
  readonly method: string;
683
- readonly id: string;
684
684
  readonly app: string;
685
685
  readonly methodDigest: string;
686
686
  readonly retry?: {
@@ -696,8 +696,8 @@ export declare function createStandaloneWorkflowHost(owner: WatchdogSqliteOwner,
696
696
  access: "read";
697
697
  } | undefined;
698
698
  } | {
699
- readonly kind: "review";
700
699
  readonly id: string;
700
+ readonly kind: "review";
701
701
  readonly timeout: number;
702
702
  readonly title: string;
703
703
  readonly reviewers: "runner" | "owners";
@@ -775,9 +775,9 @@ export declare function createStandaloneWorkflowHost(owner: WatchdogSqliteOwner,
775
775
  readonly values: {
776
776
  readonly [x: string]: import("@fungi.computer/braid").Mapping;
777
777
  };
778
+ readonly id: string;
778
779
  readonly kind: "app";
779
780
  readonly method: string;
780
- readonly id: string;
781
781
  readonly app: string;
782
782
  readonly methodDigest: string;
783
783
  readonly retry?: {
@@ -793,8 +793,8 @@ export declare function createStandaloneWorkflowHost(owner: WatchdogSqliteOwner,
793
793
  access: "read";
794
794
  } | undefined;
795
795
  } | {
796
- readonly kind: "review";
797
796
  readonly id: string;
797
+ readonly kind: "review";
798
798
  readonly timeout: number;
799
799
  readonly title: string;
800
800
  readonly reviewers: "runner" | "owners";
@@ -900,9 +900,9 @@ export declare function createStandaloneWorkflowHost(owner: WatchdogSqliteOwner,
900
900
  readonly input: {
901
901
  readonly [x: string]: S.Json;
902
902
  };
903
+ readonly description: string;
903
904
  readonly digest: string;
904
905
  readonly approval: "queue" | "none" | "simulate";
905
- readonly description: string;
906
906
  readonly effect: "read" | "write" | "external";
907
907
  readonly requires: readonly string[];
908
908
  readonly pending?: true | undefined;
@@ -935,10 +935,10 @@ export declare function createStandaloneWorkflowHost(owner: WatchdogSqliteOwner,
935
935
  readonly input: {
936
936
  readonly [x: string]: S.Json;
937
937
  };
938
+ readonly description: string;
938
939
  readonly method: string;
939
940
  readonly installationId: string;
940
941
  readonly approval: "queue" | "none" | "simulate";
941
- readonly description: string;
942
942
  readonly effect: "read" | "write" | "external";
943
943
  readonly requires: readonly string[];
944
944
  readonly app: string;
@@ -295,9 +295,9 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
295
295
  readonly values: {
296
296
  readonly [x: string]: import("@fungi.computer/braid").Mapping;
297
297
  };
298
+ readonly id: string;
298
299
  readonly kind: "app";
299
300
  readonly method: string;
300
- readonly id: string;
301
301
  readonly app: string;
302
302
  readonly methodDigest: string;
303
303
  readonly retry?: {
@@ -313,8 +313,8 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
313
313
  access: "read";
314
314
  } | undefined;
315
315
  } | {
316
- readonly kind: "review";
317
316
  readonly id: string;
317
+ readonly kind: "review";
318
318
  readonly timeout: number;
319
319
  readonly title: string;
320
320
  readonly reviewers: "runner" | "owners";
@@ -392,9 +392,9 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
392
392
  readonly values: {
393
393
  readonly [x: string]: import("@fungi.computer/braid").Mapping;
394
394
  };
395
+ readonly id: string;
395
396
  readonly kind: "app";
396
397
  readonly method: string;
397
- readonly id: string;
398
398
  readonly app: string;
399
399
  readonly methodDigest: string;
400
400
  readonly retry?: {
@@ -410,8 +410,8 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
410
410
  access: "read";
411
411
  } | undefined;
412
412
  } | {
413
- readonly kind: "review";
414
413
  readonly id: string;
414
+ readonly kind: "review";
415
415
  readonly timeout: number;
416
416
  readonly title: string;
417
417
  readonly reviewers: "runner" | "owners";
@@ -517,9 +517,9 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
517
517
  readonly input: {
518
518
  readonly [x: string]: S.Json;
519
519
  };
520
+ readonly description: string;
520
521
  readonly digest: string;
521
522
  readonly approval: "queue" | "none" | "simulate";
522
- readonly description: string;
523
523
  readonly effect: "read" | "write" | "external";
524
524
  readonly requires: readonly string[];
525
525
  readonly pending?: true | undefined;
@@ -552,10 +552,10 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
552
552
  readonly input: {
553
553
  readonly [x: string]: S.Json;
554
554
  };
555
+ readonly description: string;
555
556
  readonly method: string;
556
557
  readonly installationId: string;
557
558
  readonly approval: "queue" | "none" | "simulate";
558
- readonly description: string;
559
559
  readonly effect: "read" | "write" | "external";
560
560
  readonly requires: readonly string[];
561
561
  readonly app: string;
@@ -625,17 +625,23 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
625
625
  runId: string;
626
626
  planDigest: string;
627
627
  title: string;
628
- outcome: "terminal" | "cancelled" | "active" | null;
628
+ outcome: "active" | "terminal" | "cancelled" | null;
629
629
  events: readonly {
630
630
  readonly runId: string;
631
- readonly nodeId: string;
632
631
  readonly state: "skipped" | "queued" | "running" | "failed" | "complete" | "waiting";
632
+ readonly nodeId: string;
633
633
  readonly iteration: string;
634
634
  readonly attempt: number;
635
635
  readonly output?: {
636
636
  readonly [x: string]: S.Json;
637
637
  } | undefined;
638
638
  readonly refusal?: string | undefined;
639
+ readonly position?: readonly {
640
+ readonly node: string;
641
+ readonly loop: "map" | "repeat";
642
+ readonly index: number;
643
+ readonly count: number;
644
+ }[] | undefined;
639
645
  readonly row?: {
640
646
  readonly id: string;
641
647
  readonly title: string;
@@ -658,12 +664,6 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
658
664
  } | undefined;
659
665
  readonly graphId?: string | undefined;
660
666
  readonly poll?: number | undefined;
661
- readonly position?: readonly {
662
- readonly node: string;
663
- readonly loop: "map" | "repeat";
664
- readonly index: number;
665
- readonly count: number;
666
- }[] | undefined;
667
667
  readonly recordedAt?: number | undefined;
668
668
  readonly durationMs?: number | undefined;
669
669
  readonly costMilliunits?: number | undefined;
@@ -716,17 +716,23 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
716
716
  runId: string;
717
717
  planDigest: string;
718
718
  title: string;
719
- outcome: "terminal" | "cancelled" | "active" | null;
719
+ outcome: "active" | "terminal" | "cancelled" | null;
720
720
  events: readonly {
721
721
  readonly runId: string;
722
- readonly nodeId: string;
723
722
  readonly state: "skipped" | "queued" | "running" | "failed" | "complete" | "waiting";
723
+ readonly nodeId: string;
724
724
  readonly iteration: string;
725
725
  readonly attempt: number;
726
726
  readonly output?: {
727
727
  readonly [x: string]: S.Json;
728
728
  } | undefined;
729
729
  readonly refusal?: string | undefined;
730
+ readonly position?: readonly {
731
+ readonly node: string;
732
+ readonly loop: "map" | "repeat";
733
+ readonly index: number;
734
+ readonly count: number;
735
+ }[] | undefined;
730
736
  readonly row?: {
731
737
  readonly id: string;
732
738
  readonly title: string;
@@ -749,12 +755,6 @@ export declare abstract class StandaloneWorkflowRunsBase<Env extends StandaloneW
749
755
  } | undefined;
750
756
  readonly graphId?: string | undefined;
751
757
  readonly poll?: number | undefined;
752
- readonly position?: readonly {
753
- readonly node: string;
754
- readonly loop: "map" | "repeat";
755
- readonly index: number;
756
- readonly count: number;
757
- }[] | undefined;
758
758
  readonly recordedAt?: number | undefined;
759
759
  readonly durationMs?: number | undefined;
760
760
  readonly costMilliunits?: number | undefined;
@@ -21,7 +21,7 @@ export declare const storageFrameMethods: {
21
21
  };
22
22
  } | {
23
23
  readonly kind: "rejected";
24
- readonly code: "unavailable" | "invalid" | "conflict" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
24
+ readonly code: "unavailable" | "conflict" | "invalid" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
25
25
  };
26
26
  requiredOperations: () => string[];
27
27
  };
@@ -39,7 +39,7 @@ export declare const storageFrameMethods: {
39
39
  };
40
40
  } | {
41
41
  readonly kind: "rejected";
42
- readonly code: "unavailable" | "invalid" | "conflict" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
42
+ readonly code: "unavailable" | "conflict" | "invalid" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
43
43
  };
44
44
  requiredOperations: () => string[];
45
45
  };
@@ -56,7 +56,7 @@ export declare const storageFrameMethods: {
56
56
  };
57
57
  } | {
58
58
  readonly kind: "rejected";
59
- readonly code: "unavailable" | "invalid" | "conflict" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
59
+ readonly code: "unavailable" | "conflict" | "invalid" | "undeclared_key" | "scope_not_declared" | "owner_required" | "too_large" | "quota_exceeded";
60
60
  };
61
61
  requiredOperations: () => string[];
62
62
  };
@@ -191,6 +191,8 @@ export declare abstract class OysterGitInstance<Env extends OysterGitWorkerEnv =
191
191
  * `oysterGitRoot(options).hostFor`.
192
192
  */
193
193
  export type OysterGitWorkerOptions<Env extends OysterGitWorkerEnv> = Readonly<{
194
+ /** Host-owned fleet control, admitted on every invocation by its durable owner. */
195
+ computerControl?: (env: Env, scope: TenantScope) => NonNullable<OysterHost["computerControl"]>;
194
196
  /** Workflow controls supplied by the host's existing run owner. */
195
197
  workflows?: (env: Env, host: OysterGitWorkerHost) => NonNullable<OysterHost["workflows"]>;
196
198
  /** Resolve one host-selected tenant before authentication; unknown origins refuse.
@@ -1145,6 +1145,8 @@ export async function oysterGitWorkerHost(env, options, selected, request) {
1145
1145
  host = { ...host, pageScript: options.pageScript };
1146
1146
  if (options.workflows !== undefined)
1147
1147
  host = { ...host, workflows: options.workflows(env, host) };
1148
+ if (options.computerControl !== undefined)
1149
+ host = { ...host, computerControl: options.computerControl(env, scope) };
1148
1150
  if (options.homeInstallationId !== undefined)
1149
1151
  host = { ...host, homeInstallationId: options.homeInstallationId(env) };
1150
1152
  return host;