@fungi.computer/oyster 0.3.1 → 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 (75) hide show
  1. package/README.md +2 -1
  2. package/dist/agent-computer-source.d.ts +42 -0
  3. package/dist/agent-computer-source.js +88 -0
  4. package/dist/agent-computers.d.ts +104 -0
  5. package/dist/agent-computers.js +227 -0
  6. package/dist/agents.d.ts +10 -0
  7. package/dist/agents.js +33 -0
  8. package/dist/boot-capability.d.ts +1 -1
  9. package/dist/boot-capability.js +1 -1
  10. package/dist/browser/boot.d.ts +2 -2
  11. package/dist/browser/boot.js +10 -3
  12. package/dist/browser/browser.d.ts +1 -1
  13. package/dist/browser/browser.js +2 -1
  14. package/dist/browser/cdp-client.d.ts +2 -0
  15. package/dist/browser/errors.d.ts +2 -2
  16. package/dist/browser/errors.js +2 -2
  17. package/dist/browser/index.d.ts +3 -2
  18. package/dist/browser/index.js +3 -2
  19. package/dist/browser/page.js +11 -4
  20. package/dist/browser/sessions.d.ts +7 -4
  21. package/dist/browser/sessions.js +159 -0
  22. package/dist/browser/url-policy.d.ts +4 -2
  23. package/dist/browser/url-policy.js +4 -2
  24. package/dist/computer-control-port.d.ts +26 -0
  25. package/dist/computer-control-port.js +1 -0
  26. package/dist/host.d.ts +3 -0
  27. package/dist/machine-link/delivery.d.ts +1 -1
  28. package/dist/machine-link/delivery.js +2 -3
  29. package/dist/machine-link/host.d.ts +37 -6
  30. package/dist/machine-link/host.js +209 -14
  31. package/dist/machine-link/link.d.ts +13 -1
  32. package/dist/machine-link/link.js +20 -7
  33. package/dist/mcp.d.ts +152 -11
  34. package/dist/mcp.js +131 -13
  35. package/dist/page/computer-handles.d.ts +3 -3
  36. package/dist/page-script.js +1 -1
  37. package/dist/standalone/agent-computers.d.ts +29 -0
  38. package/dist/standalone/agent-computers.js +101 -0
  39. package/dist/standalone/agent-mail.d.ts +41 -18
  40. package/dist/standalone/agent-model-choice.d.ts +26 -0
  41. package/dist/standalone/agent-model-choice.js +72 -0
  42. package/dist/standalone/agents.d.ts +50 -5
  43. package/dist/standalone/agents.js +505 -53
  44. package/dist/standalone/app-builds-binding.js +1 -1
  45. package/dist/standalone/computer-control.d.ts +4 -0
  46. package/dist/standalone/computer-control.js +62 -0
  47. package/dist/standalone/computer-fleet.d.ts +1 -0
  48. package/dist/standalone/computer-fleet.js +1 -1
  49. package/dist/standalone/computer.d.ts +21 -2
  50. package/dist/standalone/computer.js +12 -0
  51. package/dist/standalone/machine-link.d.ts +10 -0
  52. package/dist/standalone/machine-link.js +6 -0
  53. package/dist/standalone/registry.d.ts +12 -0
  54. package/dist/standalone/registry.js +18 -0
  55. package/dist/standalone/tenancy.d.ts +14 -1
  56. package/dist/standalone/tenancy.js +37 -3
  57. package/dist/standalone/worker.js +36 -9
  58. package/dist/standalone/workflow-binding.d.ts +2 -2
  59. package/dist/standalone/workflow-host.d.ts +11 -11
  60. package/dist/standalone/workflows.d.ts +22 -22
  61. package/dist/storage-frame.d.ts +3 -3
  62. package/dist/workers-git.d.ts +2 -0
  63. package/dist/workers-git.js +11 -1
  64. package/dist/workers.d.ts +8 -8
  65. package/docs/api/agents.md +66 -0
  66. package/docs/api/apps.md +104 -104
  67. package/docs/api/build-contract.md +35 -35
  68. package/docs/api/build.md +12 -12
  69. package/docs/api/host.md +61 -61
  70. package/docs/api/mcp.md +141 -13
  71. package/docs/api/root-data.md +1 -1
  72. package/docs/api/tenancy.md +1 -1
  73. package/docs/api/workers-git.md +3 -2
  74. package/docs/api/workers.md +81 -83
  75. package/package.json +25 -17
package/dist/mcp.js CHANGED
@@ -15,8 +15,86 @@ import { appToolSchemas, appToolFamilySchemas } from "@fungi.computer/forage";
15
15
  import { Knapsack } from "@fungi.computer/knapsack";
16
16
  import * as Schema from "effect/Schema";
17
17
  import { defineIdFamily } from "@fungi.computer/cairn/id";
18
+ import { computerCreateRequestSchema, computerPageSchema, } from "@fungi.computer/computer/fleet/inventory-wire";
19
+ import { computerStateSchema } from "@fungi.computer/computer/fleet/wire";
20
+ import { teamComputerDirectorySchema } from "@fungi.computer/nacre/workbench";
18
21
  import { keyAppAccess } from "./keys.js";
19
22
  import { defaultAssetPolicy, sha256, verifyRelease, } from "./release.js";
23
+ const fleetIds = defineIdFamily({
24
+ family: "mcp-computer-control",
25
+ version: 1,
26
+ kinds: { request: { prefix: "computer_request" } },
27
+ });
28
+ const fleetComputerId = z.strictObject({
29
+ computerId: z.string().min(1).max(256),
30
+ });
31
+ const fleetCreate = z.strictObject({
32
+ name: computerCreateRequestSchema.shape.name
33
+ .unwrap()
34
+ .nullable()
35
+ .default(null),
36
+ requestId: computerCreateRequestSchema.shape.requestId
37
+ .nullable()
38
+ .default(null),
39
+ });
40
+ const fleetComputer = z.strictObject({
41
+ computerId: z.string(),
42
+ name: z.string(),
43
+ });
44
+ const fleetCreation = z.discriminatedUnion("kind", [
45
+ z.strictObject({
46
+ kind: z.literal("created"),
47
+ requestId: z.string(),
48
+ computer: fleetComputer,
49
+ }),
50
+ z.strictObject({
51
+ kind: z.enum(["uncertain", "denied", "conflict", "invalid"]),
52
+ requestId: z.string(),
53
+ }),
54
+ ]);
55
+ /** One set of schemas and operations for MCP and direct broker invocation. */
56
+ export function computerControlSpecs(port) {
57
+ return {
58
+ list: {
59
+ replay: "safe",
60
+ access: "read",
61
+ description: "List a page of permitted Computers. Follow nextOffset for another page.",
62
+ input: computerPageSchema,
63
+ output: teamComputerDirectorySchema,
64
+ /** Return an admitted inventory page to MCP or the retained broker. */
65
+ execute: (input, context) => port.list(input, context),
66
+ },
67
+ state: {
68
+ replay: "safe",
69
+ access: "read",
70
+ description: "Read runtime state without waking a Computer, including its live admission queue position.",
71
+ input: fleetComputerId,
72
+ output: computerStateSchema,
73
+ /** Return liveness without waking the runtime. */
74
+ execute: (input, context) => port.state(input.computerId, context),
75
+ },
76
+ start: {
77
+ replay: "unsafe",
78
+ description: "Start a hosted Computer through ordinary admission. Waits until connected; poll computers.state while queued. Safe to retry.",
79
+ input: fleetComputerId,
80
+ output: computerStateSchema,
81
+ /** Admit and start this Computer. */
82
+ execute: (input, context) => port.start(input.computerId, context),
83
+ },
84
+ create: {
85
+ replay: "unsafe",
86
+ description: "Create a Computer with an optional friendly name. Request identity is generated automatically. If the outcome is uncertain, retry with its returned requestId and the original name input; do not start a second creation.",
87
+ input: fleetCreate,
88
+ output: fleetCreation,
89
+ /** Mint one identity or preserve the requestId for an uncertain retry. */
90
+ execute: async (input, context) => {
91
+ const requestId = input.requestId ?? fleetIds.request.generate();
92
+ const result = await port.create({ name: input.name ?? undefined, requestId }, context);
93
+ return { ...result, requestId };
94
+ },
95
+ },
96
+ };
97
+ }
20
98
  /** Mints the requestId a tool family tool sends on the caller's behalf to its
21
99
  * declared `call` method: each MCP call is its own independent run, never a
22
100
  * replay the caller controls. */
@@ -372,9 +450,9 @@ export async function oysterAppMethods(host, principal, access, surfaces, preloa
372
450
  }
373
451
  /**
374
452
  * Capture the host's fixed Computer for one caller as a Knapsack source.
375
- * The host decides whether the caller holds Computer access; the Computer
376
- * owner accepts every call. Null when the host has no Computer or the caller
377
- * has no access. Grants name exactly the one captured module.
453
+ * The host decides whether the caller holds Computer access; the ComputerDO
454
+ * still checks its host-issued grant on every call. Null when the host has no
455
+ * Computer or the caller has no access. Grants name the captured module.
378
456
  */
379
457
  export async function oysterComputerTools(host, principal) {
380
458
  if (host.computerId === undefined || host.computer === undefined)
@@ -388,6 +466,7 @@ export async function oysterComputerTools(host, principal) {
388
466
  id,
389
467
  name: "workspace",
390
468
  namespace: ["computers"],
469
+ confirmCalls: true,
391
470
  computer,
392
471
  }),
393
472
  grants: { [id]: "write" },
@@ -398,12 +477,12 @@ export async function oysterComputerTools(host, principal) {
398
477
  * methods, plus the host's Computer when the caller holds Computer access.
399
478
  * A key sees exactly the installations and levels its own grants admit, and
400
479
  * the Computer only with its own `computer.use` grant; each App retains its
401
- * own caller checks regardless. The owner's stored write approval of an
402
- * Agent's App tools is that principal's confirmation for App writes, so a
403
- * headless client can write; the shared MCP server asks the client to
404
- * approve every other write (an owner's own calls, the Computer). Each call
405
- * still re-admits the key, rechecks that same stored approval, and passes
406
- * the App's gate.
480
+ * own caller checks regardless. App writes use the host's approval record;
481
+ * stored approval of an Agent's App tools can permit headless App writes.
482
+ * The shared MCP server elicits consent for explicitly confirmed operations,
483
+ * including every advertised Workspace Computer call, even reads. Each call
484
+ * still re-admits the key, checks current App approval and passes the App's
485
+ * gate.
407
486
  */
408
487
  export function oysterMcpCatalogLoad(host) {
409
488
  const surfaces = appSurfaceCache(host);
@@ -434,6 +513,40 @@ export function oysterMcpCatalogLoad(host) {
434
513
  }
435
514
  const apps = await oysterAppMethods(host, principal, access, surfaces, installations);
436
515
  const computer = await oysterComputerTools(host, principal);
516
+ const fleet = host.computerControl?.(principal) ?? null;
517
+ const fleetSpecs = fleet === null ? null : computerControlSpecs(fleet);
518
+ const fleetSource = fleetSpecs === null
519
+ ? []
520
+ : [
521
+ {
522
+ id: "oyster-computer-control",
523
+ acquire: () => Promise.resolve({
524
+ snapshot: {
525
+ version: "team-computer-control",
526
+ modules: [
527
+ Mycelium.module({
528
+ id: "team-computer-control",
529
+ name: "computers",
530
+ operations: {
531
+ list: Mycelium.operation(fleetSpecs.list),
532
+ state: Mycelium.operation(fleetSpecs.state),
533
+ start: Mycelium.operation({
534
+ ...fleetSpecs.start,
535
+ confirm: true,
536
+ }),
537
+ create: Mycelium.operation({
538
+ ...fleetSpecs.create,
539
+ confirm: true,
540
+ }),
541
+ },
542
+ }),
543
+ ],
544
+ },
545
+ release: () => Promise.resolve(),
546
+ }),
547
+ },
548
+ ];
549
+ const fleetGrants = fleet === null ? {} : { "team-computer-control": "write" };
437
550
  const control = await host.workflows?.(principal);
438
551
  const workflowSources = control == null
439
552
  ? []
@@ -453,13 +566,18 @@ export function oysterMcpCatalogLoad(host) {
453
566
  if (computer === null)
454
567
  return {
455
568
  instructions: "Installed Apps on this Oyster. Each App is env.<APP>; dotted method names are nested properties, e.g. env.BONSAI.repos.list({}). Use forage.search / forage.describe to find methods.",
456
- grants: { ...apps.grants, ...workflowGrants },
457
- sources: [apps, ...workflowSources],
569
+ grants: { ...apps.grants, ...workflowGrants, ...fleetGrants },
570
+ sources: [apps, ...workflowSources, ...fleetSource],
458
571
  };
459
572
  return {
460
573
  instructions: 'Installed Apps and this Oyster\'s Computer. Each App is env.<APP>; dotted method names are nested properties, e.g. env.BONSAI.repos.list({}). The Computer is env.computers.workspace, e.g. env.computers.workspace.exec({command: "ls"}). Use forage.search / forage.describe to find methods.',
461
- grants: { ...apps.grants, ...computer.grants, ...workflowGrants },
462
- sources: [apps, computer, ...workflowSources],
574
+ grants: {
575
+ ...apps.grants,
576
+ ...computer.grants,
577
+ ...workflowGrants,
578
+ ...fleetGrants,
579
+ },
580
+ sources: [apps, computer, ...workflowSources, ...fleetSource],
463
581
  };
464
582
  };
465
583
  }
@@ -44,7 +44,7 @@ export declare class ComputerProcessesHandle extends RpcTarget implements Handle
44
44
  readonly command: string;
45
45
  readonly cwd: string | null;
46
46
  readonly startedAt: number;
47
- readonly state: "running" | "exited" | "uncertain";
47
+ readonly state: "running" | "uncertain" | "exited";
48
48
  readonly exitCode: number | null;
49
49
  readonly sync: "unknown" | "pending" | "synced";
50
50
  readonly endedAt: number | null;
@@ -56,7 +56,7 @@ export declare class ComputerProcessesHandle extends RpcTarget implements Handle
56
56
  readonly command: string;
57
57
  readonly cwd: string | null;
58
58
  readonly startedAt: number;
59
- readonly state: "running" | "exited" | "uncertain";
59
+ readonly state: "running" | "uncertain" | "exited";
60
60
  readonly exitCode: number | null;
61
61
  readonly sync: "unknown" | "pending" | "synced";
62
62
  readonly endedAt: number | null;
@@ -69,7 +69,7 @@ export declare class ComputerProcessesHandle extends RpcTarget implements Handle
69
69
  readonly command: string;
70
70
  readonly cwd: string | null;
71
71
  readonly startedAt: number;
72
- readonly state: "running" | "exited" | "uncertain";
72
+ readonly state: "running" | "uncertain" | "exited";
73
73
  readonly exitCode: number | null;
74
74
  readonly sync: "unknown" | "pending" | "synced";
75
75
  readonly endedAt: number | null;