@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.
- package/README.md +376 -72
- package/dist/agents.d.ts +3 -2
- package/dist/agents.js +4 -2
- package/dist/app-builds.d.ts +81 -8
- package/dist/app-builds.js +210 -60
- package/dist/app-storage.d.ts +3 -3
- package/dist/approval-capability.d.ts +5 -0
- package/dist/approval-capability.js +46 -0
- package/dist/approval-home.d.ts +51 -0
- package/dist/approval-home.js +226 -0
- package/dist/apps/catalog.d.ts +335 -0
- package/dist/apps/catalog.js +46 -0
- package/dist/apps/gate.d.ts +20 -6
- package/dist/apps/gate.js +42 -16
- package/dist/apps/index.d.ts +3 -1
- package/dist/apps/index.js +3 -1
- package/dist/apps/installation.d.ts +132 -51
- package/dist/apps/installation.js +176 -70
- package/dist/apps/permissions.d.ts +247 -0
- package/dist/apps/permissions.js +255 -0
- package/dist/boot-capability-route.d.ts +15 -0
- package/dist/boot-capability-route.js +196 -0
- package/dist/boot-capability.d.ts +103 -0
- package/dist/boot-capability.js +141 -0
- package/dist/boot-construction.d.ts +12 -0
- package/dist/boot-construction.js +175 -0
- package/dist/boot-gate.d.ts +267 -0
- package/dist/boot-gate.js +236 -0
- package/dist/boot-host-script.d.ts +2 -0
- package/dist/boot-host-script.js +1 -0
- package/dist/boot-mount.d.ts +21 -0
- package/dist/boot-mount.js +65 -0
- package/dist/browser/boot.d.ts +73 -0
- package/dist/browser/boot.js +502 -0
- package/dist/browser/browser.d.ts +104 -0
- package/dist/browser/browser.js +374 -0
- package/dist/browser/capability-source.d.ts +76 -0
- package/dist/browser/capability-source.js +338 -0
- package/dist/browser/cdp-client.d.ts +56 -0
- package/dist/browser/cdp-client.js +184 -0
- package/dist/browser/errors.d.ts +79 -0
- package/dist/browser/errors.js +32 -0
- package/dist/browser/index.d.ts +16 -0
- package/dist/browser/index.js +14 -0
- package/dist/browser/lease-registry.d.ts +61 -0
- package/dist/browser/lease-registry.js +101 -0
- package/dist/browser/operations.d.ts +225 -0
- package/dist/browser/operations.js +137 -0
- package/dist/browser/page.d.ts +25 -0
- package/dist/browser/page.js +172 -0
- package/dist/browser/quick-reads.d.ts +29 -0
- package/dist/browser/quick-reads.js +160 -0
- package/dist/browser/sessions.d.ts +52 -0
- package/dist/browser/sessions.js +118 -0
- package/dist/browser/url-policy.d.ts +18 -0
- package/dist/browser/url-policy.js +58 -0
- package/dist/build/app-imports.d.ts +12 -0
- package/dist/build/app-imports.js +105 -0
- package/dist/build/build-policy.d.ts +5 -3
- package/dist/build/build-policy.js +5 -3
- package/dist/build/candidate-builder.d.ts +1 -1
- package/dist/build/candidate-cli.mjs +8375 -334
- package/dist/build/local-build.d.ts +2 -0
- package/dist/build/local-build.js +28 -6
- package/dist/build/snapshot-package-store.d.ts +1 -1
- package/dist/build/snapshot-package-store.js +7 -2
- package/dist/build/workspace-build.d.ts +92 -5
- package/dist/build/workspace-build.js +93 -19
- package/dist/build.d.ts +1 -1
- package/dist/build.js +1 -1
- package/dist/catalog-frame.js +1 -1
- package/dist/cloudflare.d.ts +13 -3
- package/dist/cloudflare.js +102 -4
- package/dist/frame-binding-wire-script.js +1 -1
- package/dist/frame-binding-wire.js +4 -2
- package/dist/git-auth.d.ts +0 -2
- package/dist/git-auth.js +0 -2
- package/dist/git.d.ts +0 -1
- package/dist/git.js +0 -2
- package/dist/host.d.ts +39 -13
- package/dist/host.js +268 -142
- package/dist/index.d.ts +25 -15
- package/dist/index.js +39 -16
- package/dist/install-release-cli.d.ts +2 -0
- package/dist/install-release-cli.js +46 -0
- package/dist/installation-client.d.ts +43 -0
- package/dist/installation-client.js +178 -0
- package/dist/installation-execution.d.ts +11 -0
- package/dist/installation-execution.js +184 -0
- package/dist/installation-operations.d.ts +1437 -0
- package/dist/installation-operations.js +664 -0
- package/dist/installation-store-generation.d.ts +3 -0
- package/dist/installation-store-generation.js +16 -0
- package/dist/instance-id.d.ts +10 -0
- package/dist/instance-id.js +10 -0
- package/dist/instance-refusal.d.ts +11 -0
- package/dist/instance-refusal.js +21 -0
- package/dist/internal/delivery.js +4 -2
- package/dist/internal/host-gate.js +7 -1
- package/dist/internal/registry-installations.js +4 -4
- package/dist/keys.d.ts +5 -1
- package/dist/keys.js +10 -2
- package/dist/machine-link/authority.d.ts +89 -0
- package/dist/machine-link/authority.js +37 -0
- package/dist/machine-link/client.d.ts +66 -0
- package/dist/machine-link/client.js +94 -0
- package/dist/machine-link/delivery.d.ts +18 -0
- package/dist/machine-link/delivery.js +55 -0
- package/dist/machine-link/enrollment.d.ts +111 -0
- package/dist/machine-link/enrollment.js +286 -0
- package/dist/machine-link/fences.d.ts +37 -0
- package/dist/machine-link/fences.js +166 -0
- package/dist/machine-link/home.d.ts +20 -0
- package/dist/machine-link/home.js +24 -0
- package/dist/machine-link/host.d.ts +43 -0
- package/dist/machine-link/host.js +189 -0
- package/dist/machine-link/index.d.ts +15 -0
- package/dist/machine-link/index.js +15 -0
- package/dist/machine-link/link.d.ts +48 -0
- package/dist/machine-link/link.js +223 -0
- package/dist/machine-link/presence.d.ts +10 -0
- package/dist/machine-link/presence.js +25 -0
- package/dist/machine-link/workers.d.ts +6 -0
- package/dist/machine-link/workers.js +6 -0
- package/dist/mcp-owner.d.ts +2 -2
- package/dist/mcp-owner.js +41 -33
- package/dist/mcp.d.ts +15 -3
- package/dist/mcp.js +146 -25
- package/dist/pack.d.ts +14 -3
- package/dist/pack.js +44 -19
- package/dist/page/app-access.d.ts +7 -0
- package/dist/page/app-access.js +75 -0
- package/dist/page/boot-host.js +57 -0
- package/dist/page/capabilities.d.ts +3 -3
- package/dist/page/capabilities.js +16 -12
- package/dist/page/computer-handles.d.ts +3 -3
- package/dist/page/computer-target.d.ts +3 -0
- package/dist/page/computer-target.js +9 -0
- package/dist/page/mount.d.ts +23 -3
- package/dist/page/mount.js +152 -16
- package/dist/page-assets.d.ts +22 -0
- package/dist/page-assets.js +28 -0
- package/dist/page-script.d.ts +3 -0
- package/dist/page-script.js +5 -4
- package/dist/release.d.ts +13 -6
- package/dist/release.js +10 -7
- package/dist/root-data.d.ts +16 -6
- package/dist/root-data.js +2 -2
- package/dist/source/index.d.ts +1 -1
- package/dist/source/team-source.d.ts +20 -8
- package/dist/source/team-source.js +39 -10
- package/dist/source/template-seed.d.ts +2 -2
- package/dist/standalone/agent-mail.d.ts +24 -0
- package/dist/standalone/agent-mail.js +62 -0
- package/dist/standalone/agent-store.d.ts +9 -0
- package/dist/standalone/agent-store.js +30 -0
- package/dist/standalone/agents.d.ts +68 -9
- package/dist/standalone/agents.js +428 -56
- package/dist/standalone/app-builds-binding.d.ts +20 -2
- package/dist/standalone/app-builds-binding.js +147 -23
- package/dist/standalone/app-set.d.ts +5 -2
- package/dist/standalone/app-set.js +174 -143
- package/dist/standalone/browser.d.ts +11 -0
- package/dist/standalone/browser.js +72 -0
- package/dist/standalone/builds.d.ts +13 -4
- package/dist/standalone/builds.js +28 -44
- package/dist/standalone/computer-egress.d.ts +2 -0
- package/dist/standalone/computer-egress.js +13 -4
- package/dist/standalone/computer-fleet.d.ts +43 -0
- package/dist/standalone/computer-fleet.js +200 -0
- package/dist/standalone/computer.d.ts +44 -9
- package/dist/standalone/computer.js +129 -22
- package/dist/standalone/install-apps-cli.js +43 -6
- package/dist/standalone/instance.d.ts +2 -2
- package/dist/standalone/instance.js +8 -11
- package/dist/standalone/machine-link.d.ts +354 -0
- package/dist/standalone/machine-link.js +368 -0
- package/dist/standalone/media.d.ts +2 -2
- package/dist/standalone/media.js +19 -16
- package/dist/standalone/preview.d.ts +2 -2
- package/dist/standalone/preview.js +37 -10
- package/dist/standalone/registry.d.ts +149 -9
- package/dist/standalone/registry.js +299 -8
- package/dist/standalone/worker.d.ts +19 -5
- package/dist/standalone/worker.js +90 -54
- package/dist/standalone/workflow-binding.d.ts +18 -22
- package/dist/standalone/workflow-binding.js +18 -18
- package/dist/standalone/workflow-control.d.ts +4 -0
- package/dist/standalone/workflow-control.js +40 -0
- package/dist/standalone/workflow-events.d.ts +2 -2
- package/dist/standalone/workflow-events.js +9 -10
- package/dist/standalone/workflow-git-capture.js +4 -3
- package/dist/standalone/workflow-host.d.ts +162 -161
- package/dist/standalone/workflow-host.js +101 -21
- package/dist/standalone/workflow-node-capture.js +11 -4
- package/dist/standalone/workflow-schedules.d.ts +5 -2
- package/dist/standalone/workflow-schedules.js +9 -10
- package/dist/standalone/workflow-trigger-host.d.ts +65 -5
- package/dist/standalone/workflow-trigger-host.js +2 -2
- package/dist/standalone/workflow-webhooks.d.ts +22 -0
- package/dist/standalone/workflow-webhooks.js +39 -0
- package/dist/standalone/workflows.d.ts +140 -43
- package/dist/standalone/workflows.js +198 -39
- package/dist/storage-frame.d.ts +6 -6
- package/dist/testing/capabilities.js +14 -3
- package/dist/testing/installation.js +26 -2
- package/dist/testing/worker.js +1 -1
- package/dist/testing/workers-runtime.d.ts +8 -2
- package/dist/testing/workers-runtime.js +20 -2
- package/dist/webhook-abuse.d.ts +25 -0
- package/dist/webhook-abuse.js +54 -0
- package/dist/webhook-custody.d.ts +144 -0
- package/dist/webhook-custody.js +222 -0
- package/dist/webhook-ingress.d.ts +36 -0
- package/dist/webhook-ingress.js +146 -0
- package/dist/webhooks.d.ts +111 -0
- package/dist/webhooks.js +189 -0
- package/dist/workers-git.d.ts +12 -17
- package/dist/workers-git.js +60 -52
- package/dist/workers.d.ts +224 -13
- package/dist/workers.js +351 -51
- package/dist/workflow-agent-authority.d.ts +8 -0
- package/dist/workflow-agent-authority.js +42 -0
- package/dist/workflows.d.ts +66 -0
- package/dist/workflows.js +97 -0
- package/docs/api/README.md +63 -37
- package/docs/api/agents.md +17 -0
- package/docs/api/app-storage.md +1 -1
- package/docs/api/apps.md +1394 -543
- package/docs/api/browser/boot.md +132 -0
- package/docs/api/browser.md +1440 -0
- package/docs/api/build-contract.md +38 -25
- package/docs/api/build.md +263 -84
- package/docs/api/cloudflare.md +93 -7
- package/docs/api/git.md +3 -13
- package/docs/api/host.md +944 -118
- package/docs/api/index.md +320 -18
- package/docs/api/keys.md +24 -1
- package/docs/api/mcp.md +59 -12
- package/docs/api/modules.md +33 -40
- package/docs/api/pack.md +57 -8
- package/docs/api/page/mount.md +92 -4
- package/docs/api/workers-git.md +103 -24
- package/docs/api/workers.md +1581 -244
- package/docs/site/agent-quickstart.mdx +28 -14
- package/examples/standalone/worker.ts +3 -1
- package/examples/standalone/wrangler.jsonc +2 -0
- package/package.json +73 -71
- package/dist/installation-consent.d.ts +0 -81
- package/dist/installation-consent.js +0 -84
- package/dist/shell/app-directory.d.ts +0 -542
- package/dist/shell/app-directory.js +0 -196
- package/dist/shell/browser-actions.d.ts +0 -7
- package/dist/shell/browser-actions.js +0 -56
- package/dist/shell/command-host.d.ts +0 -112
- package/dist/shell/command-host.js +0 -502
- package/dist/shell/commands.d.ts +0 -25
- package/dist/shell/commands.js +0 -78
- package/dist/shell/index.d.ts +0 -8
- package/dist/shell/index.js +0 -8
- package/dist/shell/keys.d.ts +0 -41
- package/dist/shell/keys.js +0 -134
- package/dist/shell/shell-module.d.ts +0 -55
- package/dist/shell/shell-module.js +0 -86
- package/dist/shell/window-manager.d.ts +0 -128
- package/dist/shell/window-manager.js +0 -730
- package/dist/shell/workbench-contract.d.ts +0 -68
- package/dist/shell/workbench-directory.d.ts +0 -332
- package/dist/shell/workbench-directory.js +0 -35
- package/dist/shell/workbench-host-types.d.ts +0 -96
- package/dist/shell/workbench-host-types.js +0 -1
- package/dist/shell/workbench-host.d.ts +0 -34
- package/dist/shell/workbench-host.js +0 -352
- package/dist/shell/workbench-model.d.ts +0 -194
- package/dist/shell/workbench-model.js +0 -53
- package/dist/shell/workbench.d.ts +0 -4
- package/dist/shell/workbench.js +0 -2
- package/docs/api/shell/app-directory.md +0 -599
- package/docs/api/shell/command-host.md +0 -224
- package/docs/api/shell/commands.md +0 -197
- package/docs/api/shell/keys.md +0 -183
- package/docs/api/shell/shell-module.md +0 -204
- package/docs/api/shell/window-manager.md +0 -267
- package/docs/api/shell/workbench-directory.md +0 -119
- package/docs/api/shell/workbench.md +0 -205
- package/docs/api/shell.md +0 -1915
- /package/dist/{shell/workbench-contract.js → page/boot-host.d.ts} +0 -0
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import { observeProviderStatus } from "@fungi.computer/mule/effect";
|
|
1
2
|
import { standaloneTenancy, } from "./tenancy.js";
|
|
2
3
|
import { AcceptedSchema } from "@fungi.computer/shiitake/client";
|
|
4
|
+
import { createShiitakeClient } from "@fungi.computer/shiitake/client";
|
|
5
|
+
import { AgentCreateRefused, } from "@fungi.computer/shiitake/agent-control";
|
|
6
|
+
import { AgentDelegation, AgentDisclosureRefused, machineAgentCodecs, } from "@fungi.computer/shiitake/delegation";
|
|
7
|
+
import { FungiMessageMetaSchema } from "@fungi.computer/agar/agents";
|
|
8
|
+
import { agentMail } from "./agent-mail.js";
|
|
9
|
+
import { createStandaloneMachineEnrollment, standaloneMachineAgentHome, standaloneMachineLinkAuthority, } from "./machine-link.js";
|
|
10
|
+
import { createMachineLinkFences, MachineEnrollmentRefused, MachineLinkRefused, createMachineAgentClient, } from "../machine-link/index.js";
|
|
3
11
|
import { memberInboxItemSchema } from "@fungi.computer/puffball/inbox";
|
|
4
12
|
/**
|
|
5
13
|
* Agents on a standalone Oyster Worker. Each agent is Shiitake's public
|
|
@@ -11,7 +19,8 @@ import { memberInboxItemSchema } from "@fungi.computer/puffball/inbox";
|
|
|
11
19
|
import { agentDirectoryForScope } from "./agent-directory.js";
|
|
12
20
|
import { DurableObject } from "cloudflare:workers";
|
|
13
21
|
import { ShiitakeDO } from "@fungi.computer/shiitake/cloudflare";
|
|
14
|
-
import {
|
|
22
|
+
import { agentStoreKeeping } from "./agent-store.js";
|
|
23
|
+
import { standaloneBrowserSource } from "./browser.js";
|
|
15
24
|
import { configuredProviderEgress, providerFetch, } from "@fungi.computer/veil/gatekeeper";
|
|
16
25
|
import { CLOUDFLARE_GATEWAY_BINDING_AUTH_SENTINEL, createAiBindingFetch, } from "@earendil-works/pi-ai/api/cloudflare-ai-binding";
|
|
17
26
|
import { cloudflareWorkersAIProvider } from "@earendil-works/pi-ai/providers/cloudflare-workers-ai";
|
|
@@ -21,16 +30,49 @@ import {} from "../cloudflare.js";
|
|
|
21
30
|
import { AppInstanceError } from "../index.js";
|
|
22
31
|
import { createAgentAvatarStore, } from "../agent-avatars.js";
|
|
23
32
|
import { defineIdFamily } from "@fungi.computer/cairn";
|
|
24
|
-
import { agentIds, createAgentDirectory, createFleetSessions, DirectoryError, } from "@fungi.computer/shiitake/fleet";
|
|
25
|
-
import { appSurfaceCache, oysterAppMethods, } from "../mcp.js";
|
|
33
|
+
import { agentIds, createAgentDirectory, createFleetSessions, commandSettlementSchema, DirectoryError, } from "@fungi.computer/shiitake/fleet";
|
|
26
34
|
import { appToolPolicySchema } from "../apps/index.js";
|
|
27
35
|
import { oysterPrincipalIsOwner, oysterPrincipalId, } from "../host.js";
|
|
28
36
|
import { defaultTenant, tenantIdSchema, tenantScope, } from "../tenant.js";
|
|
29
37
|
import { createCodemodeSandbox } from "@fungi.computer/mycelium/workers";
|
|
30
|
-
import { Knapsack } from "@fungi.computer/knapsack";
|
|
31
38
|
import { maxAgentMcpServers, } from "../agents.js";
|
|
32
39
|
/** Default Workers AI chat model: one that reliably calls tools. */
|
|
33
40
|
const defaultAgentModel = "@cf/moonshotai/kimi-k2.6";
|
|
41
|
+
/** Own transport evidence and exact-message decisions for one model lease. */
|
|
42
|
+
function observedModels(model, defaults) {
|
|
43
|
+
return {
|
|
44
|
+
acquire: async ({ signal }) => {
|
|
45
|
+
const streamSimple = await loadStreamSimple();
|
|
46
|
+
const retryableMessages = new WeakSet();
|
|
47
|
+
return {
|
|
48
|
+
model,
|
|
49
|
+
retryable: (message) => !signal.aborted &&
|
|
50
|
+
message.stopReason === "error" &&
|
|
51
|
+
retryableMessages.has(message),
|
|
52
|
+
stream: (selected, context, options) => {
|
|
53
|
+
const upstream = { ...options, ...defaults };
|
|
54
|
+
const observed = observeProviderStatus(upstream);
|
|
55
|
+
const response = streamSimple(selected, context, {
|
|
56
|
+
...upstream,
|
|
57
|
+
fetch: observed.fetch,
|
|
58
|
+
onProviderStreamEvent: observed.onProviderStreamEvent,
|
|
59
|
+
});
|
|
60
|
+
// Observe Pi's ordinary settled-result interface without replacing
|
|
61
|
+
// its stream, iterator, result or transport.
|
|
62
|
+
void response.result().then((message) => {
|
|
63
|
+
if (message.stopReason === "error" &&
|
|
64
|
+
!signal.aborted &&
|
|
65
|
+
!upstream.signal?.aborted &&
|
|
66
|
+
observed.rejection()?.code === "provider_unavailable")
|
|
67
|
+
retryableMessages.add(message);
|
|
68
|
+
});
|
|
69
|
+
return response;
|
|
70
|
+
},
|
|
71
|
+
release: () => Promise.resolve(),
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
34
76
|
/**
|
|
35
77
|
* Workers AI over the AI binding. Requests go through AI Gateway's
|
|
36
78
|
* `workers-ai` passthrough on the binding, so no API token is needed.
|
|
@@ -47,26 +89,16 @@ function workersAiModels(ai, options = {}) {
|
|
|
47
89
|
baseUrl: `https://workers-binding.ai/ai-gateway/gateways/${options.gateway ?? "default"}/workers-ai/v1`,
|
|
48
90
|
};
|
|
49
91
|
const fetch = createAiBindingFetch(ai);
|
|
50
|
-
return {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
apiKey: CLOUDFLARE_GATEWAY_BINDING_AUTH_SENTINEL,
|
|
59
|
-
headers: {
|
|
60
|
-
"cf-aig-authorization": `Bearer ${CLOUDFLARE_GATEWAY_BINDING_AUTH_SENTINEL}`,
|
|
61
|
-
// The binding carries identity; a request auth header would be
|
|
62
|
-
// taken as a provider key that overrides the gateway's own.
|
|
63
|
-
Authorization: null,
|
|
64
|
-
},
|
|
65
|
-
}),
|
|
66
|
-
release: () => Promise.resolve(),
|
|
67
|
-
};
|
|
92
|
+
return observedModels(model, {
|
|
93
|
+
fetch,
|
|
94
|
+
apiKey: CLOUDFLARE_GATEWAY_BINDING_AUTH_SENTINEL,
|
|
95
|
+
headers: {
|
|
96
|
+
"cf-aig-authorization": `Bearer ${CLOUDFLARE_GATEWAY_BINDING_AUTH_SENTINEL}`,
|
|
97
|
+
// The binding carries identity; a request auth header would be
|
|
98
|
+
// taken as a provider key that overrides the gateway's own.
|
|
99
|
+
Authorization: null,
|
|
68
100
|
},
|
|
69
|
-
};
|
|
101
|
+
});
|
|
70
102
|
}
|
|
71
103
|
/**
|
|
72
104
|
* Pi's dispatching `streamSimple` evaluates every provider's model catalog when
|
|
@@ -122,6 +154,179 @@ const instructions = [
|
|
|
122
154
|
export class StandaloneAgentBase extends ShiitakeDO {
|
|
123
155
|
/** Each installed Release's verified catalog surface, built once per Release for this Agent's lifetime. */
|
|
124
156
|
#appSurfaces;
|
|
157
|
+
uiOwner;
|
|
158
|
+
async machineClient() {
|
|
159
|
+
const pin = await this.policyDirectory();
|
|
160
|
+
const agentId = this.ctx.id.name;
|
|
161
|
+
const identity = () => pin === null || agentId === undefined
|
|
162
|
+
? null
|
|
163
|
+
: { teamId: pin.directoryName, agentId };
|
|
164
|
+
return createMachineAgentClient({
|
|
165
|
+
identity,
|
|
166
|
+
home: standaloneMachineAgentHome({
|
|
167
|
+
identity,
|
|
168
|
+
directory: this.env.AGENT_DIRECTORY,
|
|
169
|
+
}),
|
|
170
|
+
links: this.env.MACHINE_LINK,
|
|
171
|
+
codecs: machineAgentCodecs,
|
|
172
|
+
// Lend the client only the three hosted Agent entry points it routes to.
|
|
173
|
+
hosted: {
|
|
174
|
+
getByName: (agentId) => {
|
|
175
|
+
const agent = this.env.AGENTS.getByName(agentId);
|
|
176
|
+
return {
|
|
177
|
+
deliver: (envelope) => agent.deliver(envelope),
|
|
178
|
+
fetch: (request) => agent.fetch(request),
|
|
179
|
+
settlement: (principal, sessionId, commandId) => agent.settlement(principal, sessionId, commandId),
|
|
180
|
+
};
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
async ui() {
|
|
186
|
+
const { Knapsack } = await import("@fungi.computer/knapsack");
|
|
187
|
+
return (this.uiOwner ??= Knapsack.ui({
|
|
188
|
+
id: "oyster-ui",
|
|
189
|
+
hasSession: (sessionId) => this.hasStoredSession(sessionId),
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
hostFetch(request) {
|
|
193
|
+
return new URL(request.url).pathname.startsWith("/browser/")
|
|
194
|
+
? this.ui().then((connection) => connection.fetch(request))
|
|
195
|
+
: undefined;
|
|
196
|
+
}
|
|
197
|
+
async closeShiitake() {
|
|
198
|
+
try {
|
|
199
|
+
await (await this.ui()).close();
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
await super.closeShiitake();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
controlDirectory() {
|
|
206
|
+
const list = async (signal) => {
|
|
207
|
+
signal.throwIfAborted();
|
|
208
|
+
const pin = await this.policyDirectory();
|
|
209
|
+
if (pin === null)
|
|
210
|
+
throw new Error("Agent directory is unavailable");
|
|
211
|
+
const agents = await this.env.AGENT_DIRECTORY.getByName(pin.directoryName).list();
|
|
212
|
+
if (!agents.some((agent) => agent.agentId === this.ctx.id.name))
|
|
213
|
+
throw new Error("Agent is outside this scope");
|
|
214
|
+
signal.throwIfAborted();
|
|
215
|
+
return agents;
|
|
216
|
+
};
|
|
217
|
+
return {
|
|
218
|
+
list,
|
|
219
|
+
create: async (input, signal) => {
|
|
220
|
+
await list(signal);
|
|
221
|
+
const { assertOrdinaryAgentCreation } = await import("@fungi.computer/shiitake-extensions/roster/conversation-agents");
|
|
222
|
+
assertOrdinaryAgentCreation(input);
|
|
223
|
+
const pin = await this.policyDirectory();
|
|
224
|
+
const createdBy = this.ctx.id.name;
|
|
225
|
+
if (pin === null || createdBy === undefined)
|
|
226
|
+
throw new AgentCreateRefused("no_creator", "Agent directory is unavailable");
|
|
227
|
+
const directory = this.env.AGENT_DIRECTORY.getByName(pin.directoryName);
|
|
228
|
+
const owner = await directory.readCreator(createdBy);
|
|
229
|
+
if (owner === null)
|
|
230
|
+
throw new AgentCreateRefused("no_creator", "Agent credential owner is unavailable");
|
|
231
|
+
const entry = await directory.create(input.name ?? `Agent ${input.requestId}`, input.requestId, { actor: { kind: "member", memberId: owner }, role: "owner" }, "owner-direct", undefined, createdBy);
|
|
232
|
+
await this.env.AGENTS.getByName(entry.agentId).usePolicyDirectory(pin);
|
|
233
|
+
return entry;
|
|
234
|
+
},
|
|
235
|
+
send: (input, sessionId, signal, invocation) => this.delegation().send(input, sessionId, signal, invocation),
|
|
236
|
+
recall: (input, sessionId, signal) => this.delegation().recall(input, sessionId, signal),
|
|
237
|
+
open: async (agentId, signal) => {
|
|
238
|
+
const admit = async () => {
|
|
239
|
+
if (!(await list(signal)).some((agent) => agent.agentId === agentId))
|
|
240
|
+
throw new Error("Agent is outside this scope");
|
|
241
|
+
};
|
|
242
|
+
await admit();
|
|
243
|
+
const agent = createShiitakeClient({
|
|
244
|
+
baseUrl: "https://agent.internal",
|
|
245
|
+
fetch: async (input, init) => {
|
|
246
|
+
await admit();
|
|
247
|
+
const request = new Request(input, init);
|
|
248
|
+
if (new URL(request.url).origin !== "https://agent.internal")
|
|
249
|
+
throw new Error("Agent transport origin is invalid");
|
|
250
|
+
return (await this.machineClient()).dispatch(agentId, request);
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
return { agent, release: () => agent.close() };
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
delegationOwner;
|
|
258
|
+
delegation() {
|
|
259
|
+
return (this.delegationOwner ??= new AgentDelegation({
|
|
260
|
+
storage: this.ctx.storage,
|
|
261
|
+
expiresAt: () => Date.now() + 60_000,
|
|
262
|
+
identity: async () => {
|
|
263
|
+
const pin = await this.policyDirectory();
|
|
264
|
+
const agentId = this.ctx.id.name;
|
|
265
|
+
return pin === null || agentId === undefined
|
|
266
|
+
? null
|
|
267
|
+
: { scopeId: pin.directoryName, agentId };
|
|
268
|
+
},
|
|
269
|
+
directory: () => this.controlDirectory(),
|
|
270
|
+
deliver: (mail) => this.mail().deliver(mail),
|
|
271
|
+
prepare: () => Promise.resolve(),
|
|
272
|
+
accept: async (mail, reply) => (await this.shiitake()).acceptMail(mail, mail.meta, reply),
|
|
273
|
+
}));
|
|
274
|
+
}
|
|
275
|
+
/** Host-only mail admission; an HTTP caller never receives this binding. */
|
|
276
|
+
async deliver(envelope) {
|
|
277
|
+
return this.delegation().receive(envelope);
|
|
278
|
+
}
|
|
279
|
+
mailOwner;
|
|
280
|
+
conversationAgentsOwner;
|
|
281
|
+
/** The Agents each of this Agent's conversations created or sent work to. */
|
|
282
|
+
async conversationAgents() {
|
|
283
|
+
const { createConversationAgents } = await import("@fungi.computer/shiitake-extensions/roster/conversation-agents");
|
|
284
|
+
return (this.conversationAgentsOwner ??= createConversationAgents(this.ctx.storage));
|
|
285
|
+
}
|
|
286
|
+
mail() {
|
|
287
|
+
return (this.mailOwner ??= agentMail({
|
|
288
|
+
storage: this.ctx.storage,
|
|
289
|
+
namespace: {
|
|
290
|
+
getByName: (agentId) => ({
|
|
291
|
+
deliver: async (envelope) => (await this.machineClient()).namespace
|
|
292
|
+
.getByName(agentId)
|
|
293
|
+
.deliver(envelope),
|
|
294
|
+
}),
|
|
295
|
+
},
|
|
296
|
+
identity: async () => {
|
|
297
|
+
const pin = await this.policyDirectory();
|
|
298
|
+
const agentId = this.ctx.id.name;
|
|
299
|
+
return pin === null || agentId === undefined
|
|
300
|
+
? null
|
|
301
|
+
: { scopeId: pin.directoryName, agentId };
|
|
302
|
+
},
|
|
303
|
+
replySettlement: async (mail) => {
|
|
304
|
+
const pin = await this.policyDirectory();
|
|
305
|
+
if (pin === null || mail.reply === undefined)
|
|
306
|
+
throw new Error("Delegated reply identity is unavailable");
|
|
307
|
+
return (await this.machineClient()).settlement(mail.from.agentId, mail.from.sessionId, mail.reply.commandId, mail.to.agentId);
|
|
308
|
+
},
|
|
309
|
+
wake: () => this.wakeRuntime(),
|
|
310
|
+
}));
|
|
311
|
+
}
|
|
312
|
+
/** Undefined is disclosure refusal; unavailable evidence fails before RPC. */
|
|
313
|
+
async settlement(principal, sessionId, commandId) {
|
|
314
|
+
const pin = await this.policyDirectory();
|
|
315
|
+
if (pin?.directoryName !== principal.teamId)
|
|
316
|
+
return undefined;
|
|
317
|
+
try {
|
|
318
|
+
const facts = await this.delegation().settlement({ scopeId: principal.teamId, agentId: principal.actor.id }, sessionId, commandId, await this.shiitake());
|
|
319
|
+
if (facts === undefined)
|
|
320
|
+
throw new Error("Settled delegated command evidence is unavailable");
|
|
321
|
+
return Schema.encodeSync(Schema.fromJsonString(commandSettlementSchema))(facts);
|
|
322
|
+
}
|
|
323
|
+
catch (cause) {
|
|
324
|
+
// A disclosure refusal is data before crossing the DO RPC boundary.
|
|
325
|
+
if (cause instanceof AgentDisclosureRefused)
|
|
326
|
+
return undefined;
|
|
327
|
+
throw cause;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
125
330
|
/** The root host for this Agent's pinned policy directory; unpinned Agents have no App tools. */
|
|
126
331
|
async appHost() {
|
|
127
332
|
const stored = await this.ctx.storage.get("app-policy-directory");
|
|
@@ -140,25 +345,19 @@ export class StandaloneAgentBase extends ShiitakeDO {
|
|
|
140
345
|
const baseUrl = egress.baseUrl("ai-gateway");
|
|
141
346
|
if (baseUrl === null)
|
|
142
347
|
throw new Error("Configure the Luna AI Gateway Worker secret and gateway");
|
|
143
|
-
const provider = openaiProvider();
|
|
144
|
-
const listed = provider
|
|
145
|
-
.getModels()
|
|
146
|
-
.find((item) => item.id === "gpt-6-luna");
|
|
147
|
-
if (listed === undefined)
|
|
148
|
-
throw new Error("The OpenAI catalog has no Luna model");
|
|
149
|
-
const model = { ...listed, id: "openai/gpt-6-luna", baseUrl };
|
|
150
348
|
return {
|
|
151
|
-
acquire: async () => {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
349
|
+
acquire: async (input) => {
|
|
350
|
+
const { openaiProvider } = await import("@earendil-works/pi-ai/providers/openai");
|
|
351
|
+
const listed = openaiProvider()
|
|
352
|
+
.getModels()
|
|
353
|
+
.find((item) => item.id === "gpt-6-luna");
|
|
354
|
+
if (listed === undefined)
|
|
355
|
+
throw new Error("The OpenAI catalog has no Luna model");
|
|
356
|
+
const model = { ...listed, id: "openai/gpt-6-luna", baseUrl };
|
|
357
|
+
return observedModels(model, {
|
|
358
|
+
apiKey: "oyster-provider-egress",
|
|
359
|
+
fetch: providerFetch(egress, "ai-gateway"),
|
|
360
|
+
}).acquire(input);
|
|
162
361
|
},
|
|
163
362
|
};
|
|
164
363
|
}
|
|
@@ -233,12 +432,20 @@ export class StandaloneAgentBase extends ShiitakeDO {
|
|
|
233
432
|
await this.writeMcpServers(kept);
|
|
234
433
|
return true;
|
|
235
434
|
}
|
|
236
|
-
shiitakeOptions() {
|
|
435
|
+
async shiitakeOptions() {
|
|
436
|
+
const [{ oysterAgentExtensions }, { Knapsack }] = await Promise.all([
|
|
437
|
+
import("@fungi.computer/shiitake-extensions/roster/conversation-agents"),
|
|
438
|
+
import("@fungi.computer/knapsack"),
|
|
439
|
+
]);
|
|
440
|
+
const conversationAgents = await this.conversationAgents();
|
|
441
|
+
const keeping = agentStoreKeeping(this.ctx.storage, (request) => this.readColdSessions(request));
|
|
237
442
|
const principal = {
|
|
238
443
|
actor: { kind: "agent", agentId: this.ctx.id.name ?? "agent" },
|
|
239
444
|
};
|
|
240
445
|
return {
|
|
241
446
|
model: this.modelSource(),
|
|
447
|
+
meta: { schema: FungiMessageMetaSchema },
|
|
448
|
+
...this.mail().host,
|
|
242
449
|
instructions: {
|
|
243
450
|
load: () => Promise.resolve({ revision: "oyster-4", text: instructions }),
|
|
244
451
|
},
|
|
@@ -256,6 +463,7 @@ export class StandaloneAgentBase extends ShiitakeDO {
|
|
|
256
463
|
release: () => Promise.resolve(),
|
|
257
464
|
};
|
|
258
465
|
const policy = (await host.appToolPolicy?.(principal.actor)) ?? null;
|
|
466
|
+
const { appSurfaceCache, oysterAppMethods } = await import("../mcp.js");
|
|
259
467
|
this.#appSurfaces ??= appSurfaceCache(host);
|
|
260
468
|
const source = await oysterAppMethods(host, principal, policy === null ? null : { kind: "agent", policy }, this.#appSurfaces);
|
|
261
469
|
return source.acquire(context);
|
|
@@ -284,10 +492,18 @@ export class StandaloneAgentBase extends ShiitakeDO {
|
|
|
284
492
|
// The Worker's own egress: public internet only.
|
|
285
493
|
fetch: (url, init) => fetch(url, init),
|
|
286
494
|
}),
|
|
495
|
+
conversationAgents.control(() => this.controlDirectory()),
|
|
496
|
+
(await this.ui()).source,
|
|
497
|
+
standaloneBrowserSource(this.env.BROWSER, () => ({
|
|
498
|
+
teamId: this.env.OYSTER_ORIGIN,
|
|
499
|
+
agentId: principal.actor.agentId,
|
|
500
|
+
})),
|
|
287
501
|
],
|
|
288
502
|
sandbox: createCodemodeSandbox(this.env.LOADER, "oyster-workerd"),
|
|
289
503
|
},
|
|
290
|
-
extensions:
|
|
504
|
+
extensions: oysterAgentExtensions(conversationAgents.source(this.controlDirectory())),
|
|
505
|
+
...keeping,
|
|
506
|
+
hostWork: [...this.mail().host.hostWork, ...keeping.hostWork],
|
|
291
507
|
};
|
|
292
508
|
}
|
|
293
509
|
/** Send an installation-admitted message through fleet Session operations. */
|
|
@@ -343,11 +559,6 @@ const storedPolicySchema = Schema.Union([
|
|
|
343
559
|
actingInstallation: actingInstallationSchema,
|
|
344
560
|
}),
|
|
345
561
|
]);
|
|
346
|
-
const requestIds = defineIdFamily({
|
|
347
|
-
family: "oyster-agent",
|
|
348
|
-
version: 1,
|
|
349
|
-
kinds: { request: { prefix: "request" } },
|
|
350
|
-
});
|
|
351
562
|
const visitRow = Schema.Struct({
|
|
352
563
|
agent_id: Schema.String,
|
|
353
564
|
session_id: Schema.String,
|
|
@@ -359,6 +570,139 @@ export class AgentDirectory extends DurableObject {
|
|
|
359
570
|
directory;
|
|
360
571
|
avatars;
|
|
361
572
|
composedTenancy;
|
|
573
|
+
machineEnrollmentOwner;
|
|
574
|
+
machineFencesOwner;
|
|
575
|
+
machineScope() {
|
|
576
|
+
return tenantScope(defaultTenant, this.env.OYSTER_ORIGIN);
|
|
577
|
+
}
|
|
578
|
+
machineEnrollment() {
|
|
579
|
+
const env = this.env;
|
|
580
|
+
const scope = this.machineScope();
|
|
581
|
+
return (this.machineEnrollmentOwner ??= createStandaloneMachineEnrollment({
|
|
582
|
+
owner: this.ctx.storage,
|
|
583
|
+
scopeId: scope.durableName("agents"),
|
|
584
|
+
ownerId: async () => {
|
|
585
|
+
if (env.TENANTS === undefined)
|
|
586
|
+
return null;
|
|
587
|
+
if (this.ctx.id.name !== scope.durableName("agents"))
|
|
588
|
+
return null;
|
|
589
|
+
// An inactive/missing scope must not cold-start tenancy's seed path.
|
|
590
|
+
const tenant = await env.TENANTS.getByName("registry").get(defaultTenant);
|
|
591
|
+
if (tenant?.status !== "active")
|
|
592
|
+
return null;
|
|
593
|
+
const tenancy = await standaloneTenancy({
|
|
594
|
+
...env,
|
|
595
|
+
TENANTS: env.TENANTS,
|
|
596
|
+
});
|
|
597
|
+
return (await tenancy.owner())?.actor.memberId ?? null;
|
|
598
|
+
},
|
|
599
|
+
liveKey: (keyId) => env.KEYS.getByName(scope.durableName("keys")).live(keyId),
|
|
600
|
+
onAgentCreated: () => {
|
|
601
|
+
this.bump();
|
|
602
|
+
},
|
|
603
|
+
}));
|
|
604
|
+
}
|
|
605
|
+
/** Host-only join: consumption, enrollment and directory share one commit. */
|
|
606
|
+
async enrollMachine(keyId, input) {
|
|
607
|
+
try {
|
|
608
|
+
return {
|
|
609
|
+
kind: "enrolled",
|
|
610
|
+
value: await this.machineEnrollment().enrollWithKey(keyId, input),
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
catch (cause) {
|
|
614
|
+
if (cause instanceof MachineEnrollmentRefused)
|
|
615
|
+
return {
|
|
616
|
+
kind: "refused",
|
|
617
|
+
error: cause.reason === "request_conflict" || cause.reason === "capacity"
|
|
618
|
+
? cause.reason
|
|
619
|
+
: "not_allowed",
|
|
620
|
+
};
|
|
621
|
+
if (cause instanceof MachineLinkRefused)
|
|
622
|
+
return { kind: "refused", error: "not_allowed" };
|
|
623
|
+
if (cause instanceof DOMException && cause.name === "DataError")
|
|
624
|
+
return { kind: "refused", error: "invalid_enrollment" };
|
|
625
|
+
throw cause;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
machineDevice(id) {
|
|
629
|
+
return this.machineEnrollment().admitDevice(id);
|
|
630
|
+
}
|
|
631
|
+
machineFence(id) {
|
|
632
|
+
return this.machineEnrollment().currentFence(id);
|
|
633
|
+
}
|
|
634
|
+
machineDirectory(id) {
|
|
635
|
+
return this.machineEnrollment().publicDirectory(id);
|
|
636
|
+
}
|
|
637
|
+
machineTarget(caller, agentId, requester) {
|
|
638
|
+
return this.machineEnrollment().target(caller, agentId, requester);
|
|
639
|
+
}
|
|
640
|
+
machineKeyRetired(deviceId) {
|
|
641
|
+
return this.machineEnrollment().keyRetired(deviceId);
|
|
642
|
+
}
|
|
643
|
+
/** Catalog home only; no hosted DO is opened to discover a machine home. */
|
|
644
|
+
agentHome(agentId) {
|
|
645
|
+
if (!this.has(agentId))
|
|
646
|
+
return Promise.resolve(null);
|
|
647
|
+
if (this.ctx.id.name !==
|
|
648
|
+
tenantScope(defaultTenant, this.env.OYSTER_ORIGIN).durableName("agents"))
|
|
649
|
+
return Promise.resolve({ kind: "hosted" });
|
|
650
|
+
return Promise.resolve(this.machineEnrollment().home(agentId) ?? { kind: "hosted" });
|
|
651
|
+
}
|
|
652
|
+
machineFences() {
|
|
653
|
+
if (this.env.TENANTS === undefined)
|
|
654
|
+
throw new MachineLinkRefused("actor_not_allowed");
|
|
655
|
+
const env = { ...this.env, TENANTS: this.env.TENANTS };
|
|
656
|
+
const enrollment = this.machineEnrollment();
|
|
657
|
+
const authority = () => standaloneMachineLinkAuthority(env);
|
|
658
|
+
return (this.machineFencesOwner ??= createMachineLinkFences({
|
|
659
|
+
owner: this.ctx.storage,
|
|
660
|
+
enrollment,
|
|
661
|
+
readFact: (deviceId) => authority().currentFence({
|
|
662
|
+
teamId: this.machineScope().durableName("agents"),
|
|
663
|
+
deviceId,
|
|
664
|
+
}),
|
|
665
|
+
push: (fact) => authority().deliverControlFact(fact),
|
|
666
|
+
wakeNow: () => {
|
|
667
|
+
this.ctx.waitUntil(this.ctx.storage.setAlarm(Date.now()).catch((cause) => {
|
|
668
|
+
console.error("machine-link.first-fence-wake", cause);
|
|
669
|
+
}));
|
|
670
|
+
},
|
|
671
|
+
retryDelayMs: (failures) => Math.min(60_000, 1_000 * 2 ** Math.min(failures, 6)),
|
|
672
|
+
report: (operation, cause) => {
|
|
673
|
+
console.error(operation, cause);
|
|
674
|
+
},
|
|
675
|
+
}));
|
|
676
|
+
}
|
|
677
|
+
async armMachineFenceAlarm() {
|
|
678
|
+
const due = this.machineFences().family.nextDue();
|
|
679
|
+
if (due === null)
|
|
680
|
+
await this.ctx.storage.deleteAlarm();
|
|
681
|
+
else
|
|
682
|
+
await this.ctx.storage.setAlarm(due);
|
|
683
|
+
}
|
|
684
|
+
/** Only the current canonical owner can retire a device, before fence push. */
|
|
685
|
+
async revokeMachineDevice(memberId, deviceId) {
|
|
686
|
+
if (this.env.TENANTS === undefined)
|
|
687
|
+
throw new MachineLinkRefused("actor_not_allowed");
|
|
688
|
+
const owner = await (await standaloneTenancy({ ...this.env, TENANTS: this.env.TENANTS })).owner();
|
|
689
|
+
if (owner?.actor.memberId !== memberId)
|
|
690
|
+
throw new MachineLinkRefused("actor_not_allowed");
|
|
691
|
+
try {
|
|
692
|
+
return await this.machineFences().revoke(memberId, deviceId);
|
|
693
|
+
}
|
|
694
|
+
finally {
|
|
695
|
+
await this.armMachineFenceAlarm();
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
async alarm() {
|
|
699
|
+
try {
|
|
700
|
+
await this.machineFences().family.deliver();
|
|
701
|
+
}
|
|
702
|
+
finally {
|
|
703
|
+
await this.armMachineFenceAlarm();
|
|
704
|
+
}
|
|
705
|
+
}
|
|
362
706
|
grant() {
|
|
363
707
|
return {
|
|
364
708
|
agents: "all",
|
|
@@ -379,8 +723,9 @@ export class AgentDirectory extends DurableObject {
|
|
|
379
723
|
return this.directory
|
|
380
724
|
.list(this.grant())
|
|
381
725
|
.toReversed()
|
|
382
|
-
.map(({ agentId, name }) => ({
|
|
726
|
+
.map(({ agentId, name, createdBy }) => ({
|
|
383
727
|
agentId,
|
|
728
|
+
createdBy,
|
|
384
729
|
name,
|
|
385
730
|
avatar: this.avatars.read(agentId),
|
|
386
731
|
}));
|
|
@@ -408,6 +753,7 @@ export class AgentDirectory extends DurableObject {
|
|
|
408
753
|
this.bump();
|
|
409
754
|
return {
|
|
410
755
|
agentId: renamed.agentId,
|
|
756
|
+
createdBy: renamed.createdBy,
|
|
411
757
|
name: renamed.name,
|
|
412
758
|
avatar: this.avatars.read(renamed.agentId),
|
|
413
759
|
};
|
|
@@ -436,19 +782,24 @@ export class AgentDirectory extends DurableObject {
|
|
|
436
782
|
this.bump();
|
|
437
783
|
return {
|
|
438
784
|
agentId: agent.agentId,
|
|
785
|
+
createdBy: agent.createdBy,
|
|
439
786
|
name: agent.name,
|
|
440
787
|
avatar: this.avatars.read(agent.agentId),
|
|
441
788
|
};
|
|
442
789
|
});
|
|
443
790
|
}
|
|
444
|
-
create(name, requestId, principal, actor, avatar) {
|
|
791
|
+
create(name, requestId, principal, actor, avatar, createdBy = null) {
|
|
445
792
|
if (!oysterPrincipalIsOwner(principal))
|
|
446
793
|
throw new Error("Only the authenticated owner creates Agents");
|
|
794
|
+
if (createdBy !== null &&
|
|
795
|
+
(!this.has(createdBy) ||
|
|
796
|
+
this.readCreator(createdBy) !== oysterPrincipalId(principal)))
|
|
797
|
+
throw new AgentCreateRefused("no_creator", "Agent is outside this credential owner's scope");
|
|
447
798
|
return this.ctx.storage.transactionSync(() => {
|
|
448
799
|
const { agent, fact } = this.directory.create({
|
|
449
800
|
...this.grant(),
|
|
450
801
|
provenance: { actor: oysterPrincipalId(principal) },
|
|
451
|
-
}, { name, requestId, configuration: "none" });
|
|
802
|
+
}, { name, requestId, configuration: "none", createdBy });
|
|
452
803
|
if (fact !== null) {
|
|
453
804
|
if (avatar !== undefined)
|
|
454
805
|
this.avatars.write(agent.agentId, avatar);
|
|
@@ -463,6 +814,7 @@ export class AgentDirectory extends DurableObject {
|
|
|
463
814
|
const stored = this.storedPolicy(agent.agentId);
|
|
464
815
|
return {
|
|
465
816
|
agentId: agent.agentId,
|
|
817
|
+
createdBy: agent.createdBy,
|
|
466
818
|
name: agent.name,
|
|
467
819
|
avatar: this.avatars.read(agent.agentId),
|
|
468
820
|
policyRevision: stored === null
|
|
@@ -691,9 +1043,16 @@ export function standaloneAgents(env, scope) {
|
|
|
691
1043
|
: { error: "not_found" },
|
|
692
1044
|
removeMcpServer: async (agentId, serverId) => (await directory().has(agentId)) &&
|
|
693
1045
|
(await env.AGENTS.getByName(agentId).removeMcpServer(serverId)),
|
|
694
|
-
fetch: async (agentId, request) =>
|
|
695
|
-
|
|
696
|
-
|
|
1046
|
+
fetch: async (agentId, request) => {
|
|
1047
|
+
const home = await directory().agentHome(agentId);
|
|
1048
|
+
if (home?.kind === "machine") {
|
|
1049
|
+
const refusal = new MachineLinkRefused("actor_not_allowed");
|
|
1050
|
+
return Response.json({ error: refusal.reason }, { status: 409 });
|
|
1051
|
+
}
|
|
1052
|
+
return home?.kind === "hosted"
|
|
1053
|
+
? await env.AGENTS.getByName(agentId).fetch(request)
|
|
1054
|
+
: null;
|
|
1055
|
+
},
|
|
697
1056
|
inbox: async () => {
|
|
698
1057
|
const [agents, visits, directoryRevision] = await Promise.all([
|
|
699
1058
|
directory().list(),
|
|
@@ -752,7 +1111,15 @@ const agentBindingArgs = {
|
|
|
752
1111
|
},
|
|
753
1112
|
AGENTS: {
|
|
754
1113
|
send: z.tuple([
|
|
755
|
-
agentTarget.extend({
|
|
1114
|
+
agentTarget.extend({
|
|
1115
|
+
requestId: z
|
|
1116
|
+
.string()
|
|
1117
|
+
.min(1)
|
|
1118
|
+
.max(256)
|
|
1119
|
+
.refine((value) => new TextEncoder().encode(value).byteLength <= 256),
|
|
1120
|
+
text: z.string(),
|
|
1121
|
+
delivery: z.literal("followUp"),
|
|
1122
|
+
}),
|
|
756
1123
|
]),
|
|
757
1124
|
},
|
|
758
1125
|
};
|
|
@@ -818,9 +1185,14 @@ export async function dispatchAgentBinding(env, scope, frame, props, method, arg
|
|
|
818
1185
|
const [input] = agentBindingArgs.AGENTS.send.parse(args);
|
|
819
1186
|
if (!(await directory.has(input.agentId)))
|
|
820
1187
|
throw new Error("No such agent");
|
|
1188
|
+
const home = await directory.agentHome(input.agentId);
|
|
1189
|
+
if (home?.kind === "machine")
|
|
1190
|
+
throw new MachineLinkRefused("actor_not_allowed");
|
|
1191
|
+
if (home === null)
|
|
1192
|
+
throw new AppInstanceError("revoked");
|
|
821
1193
|
frame.signal.throwIfAborted();
|
|
822
1194
|
const accepted = await env.AGENTS.getByName(input.agentId).sendFromApp(installationId, input.sessionId, {
|
|
823
|
-
requestId:
|
|
1195
|
+
requestId: input.requestId,
|
|
824
1196
|
text: input.text,
|
|
825
1197
|
delivery: input.delivery,
|
|
826
1198
|
});
|
|
@@ -7,17 +7,34 @@ import type { StandaloneComputerEnv } from "./computer.js";
|
|
|
7
7
|
/** The Instance gate and native guest entrypoint share these argument codecs. */
|
|
8
8
|
export declare const appsFrameMethods: {
|
|
9
9
|
APPS: {
|
|
10
|
+
repository: {
|
|
11
|
+
decodeArgs: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => readonly [{
|
|
12
|
+
readonly repo: string;
|
|
13
|
+
}];
|
|
14
|
+
decodeResult: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => Schema.Json;
|
|
15
|
+
requiredOperations: () => string[];
|
|
16
|
+
};
|
|
17
|
+
register: {
|
|
18
|
+
decodeArgs: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => readonly [{
|
|
19
|
+
readonly repo: string;
|
|
20
|
+
}];
|
|
21
|
+
decodeResult: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => Schema.Json;
|
|
22
|
+
requiredOperations: () => string[];
|
|
23
|
+
};
|
|
10
24
|
build: {
|
|
11
25
|
decodeArgs: (raw: readonly unknown[]) => [{
|
|
12
26
|
appId: string;
|
|
13
27
|
sourceRevision: string;
|
|
28
|
+
installationId?: string | undefined;
|
|
14
29
|
}];
|
|
15
30
|
decodeResult: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => Schema.Json;
|
|
16
31
|
requiredOperations: () => string[];
|
|
17
32
|
};
|
|
18
33
|
buildStatus: {
|
|
19
|
-
decodeArgs: (
|
|
20
|
-
jobId: string;
|
|
34
|
+
decodeArgs: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => readonly [{
|
|
35
|
+
readonly jobId: string;
|
|
36
|
+
} | {
|
|
37
|
+
readonly repo: string;
|
|
21
38
|
}];
|
|
22
39
|
decodeResult: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => Schema.Json;
|
|
23
40
|
requiredOperations: () => string[];
|
|
@@ -25,6 +42,7 @@ export declare const appsFrameMethods: {
|
|
|
25
42
|
select: {
|
|
26
43
|
decodeArgs: (raw: readonly unknown[]) => [{
|
|
27
44
|
installationId: string;
|
|
45
|
+
jobId: string;
|
|
28
46
|
release: unknown;
|
|
29
47
|
}];
|
|
30
48
|
decodeResult: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => Schema.Json;
|