@fungi.computer/oyster 0.1.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/agent-avatars.d.ts +103 -0
- package/dist/agent-avatars.js +66 -0
- package/dist/agents.d.ts +15 -4
- package/dist/agents.js +43 -4
- 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 +26 -12
- package/dist/apps/gate.js +48 -22
- package/dist/apps/index.d.ts +3 -1
- package/dist/apps/index.js +3 -1
- package/dist/apps/installation.d.ts +147 -66
- package/dist/apps/installation.js +193 -86
- 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 +8428 -337
- 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-egress.js +4 -4
- 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 +106 -8
- package/dist/frame-binding-wire-script.js +1 -1
- package/dist/frame-binding-wire.d.ts +1 -1
- package/dist/frame-binding-wire.js +5 -3
- package/dist/git-auth.d.ts +0 -2
- package/dist/git-auth.js +0 -2
- package/dist/git-journal.js +4 -4
- 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 +26 -15
- package/dist/index.js +40 -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 -7
- 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 +78 -12
- package/dist/standalone/agents.js +470 -59
- package/dist/standalone/app-builds-binding.d.ts +20 -2
- package/dist/standalone/app-builds-binding.js +151 -27
- 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 +132 -25
- 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 +39 -12
- 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 +28 -32
- package/dist/standalone/workflow-binding.js +20 -20
- 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 +177 -152
- package/dist/standalone/workflow-host.js +137 -30
- 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 +88 -6
- 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 +203 -50
- 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 -55
- 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 -69
- package/dist/installation-consent.d.ts +0 -79
- package/dist/installation-consent.js +0 -82
- package/dist/shell/app-directory.d.ts +0 -542
- package/dist/shell/app-directory.js +0 -199
- 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 -124
- package/dist/shell/window-manager.js +0 -711
- 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 -347
- package/dist/shell/workbench-model.d.ts +0 -192
- 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
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { seededAvatar } from "@fungi.computer/sprite/avatar";
|
|
2
|
+
import { avatarSchema } from "@fungi.computer/sprite-zdog/avatar";
|
|
3
|
+
import { avatarCatalog } from "@fungi.computer/sprite-zdog/catalog";
|
|
4
|
+
import { renderStyleSchema } from "@fungi.computer/sprite";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
const storedAvatarRowSchema = z.strictObject({ avatar: z.string() });
|
|
7
|
+
/** The catalog-valid avatar an owner may choose for an Agent. */
|
|
8
|
+
export const agentAvatarSchema = avatarSchema;
|
|
9
|
+
const appearanceValue = z.union([z.string(), z.number(), z.boolean()]);
|
|
10
|
+
/** One character's avatar with its appearance closed to that character's controls. */
|
|
11
|
+
function closedCharacterAvatar(character) {
|
|
12
|
+
return z.strictObject({
|
|
13
|
+
v: z.literal(1),
|
|
14
|
+
character: z.literal(character),
|
|
15
|
+
appearance: z.strictObject(Object.fromEntries(Object.keys(avatarCatalog[character].controls).map((control) => [
|
|
16
|
+
control,
|
|
17
|
+
appearanceValue.optional(),
|
|
18
|
+
]))),
|
|
19
|
+
expression: z.string().min(1),
|
|
20
|
+
renderStyle: renderStyleSchema,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The avatar as a closed record, for contracts that refuse open maps (Hub
|
|
25
|
+
* projections): each character's appearance names only its own controls,
|
|
26
|
+
* then the catalog refines it exactly as `agentAvatarSchema` does.
|
|
27
|
+
*/
|
|
28
|
+
export const agentAvatarProjectionSchema = z
|
|
29
|
+
.discriminatedUnion("character", [
|
|
30
|
+
closedCharacterAvatar("mushroom"),
|
|
31
|
+
closedCharacterAvatar("cat"),
|
|
32
|
+
closedCharacterAvatar("burger"),
|
|
33
|
+
])
|
|
34
|
+
.superRefine((avatar, context) => {
|
|
35
|
+
for (const issue of agentAvatarSchema.safeParse(avatar).error?.issues ?? [])
|
|
36
|
+
context.addIssue({
|
|
37
|
+
code: "custom",
|
|
38
|
+
path: issue.path,
|
|
39
|
+
message: issue.message,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
/** A projected avatar as the Avatar descriptor Sprite draws. */
|
|
43
|
+
export function avatarFromProjection(projected) {
|
|
44
|
+
return agentAvatarSchema.parse(projected);
|
|
45
|
+
}
|
|
46
|
+
/** Open the chosen-avatar table in the Agent directory's own SQLite. */
|
|
47
|
+
export function createAgentAvatarStore(sql) {
|
|
48
|
+
sql.exec("CREATE TABLE IF NOT EXISTS agent_avatars (agent_id TEXT PRIMARY KEY, avatar TEXT NOT NULL) STRICT");
|
|
49
|
+
return {
|
|
50
|
+
read(agentId) {
|
|
51
|
+
const row = sql
|
|
52
|
+
.exec("SELECT avatar FROM agent_avatars WHERE agent_id = ?", agentId)
|
|
53
|
+
.toArray()[0];
|
|
54
|
+
if (row === undefined)
|
|
55
|
+
return seededAvatar(agentId);
|
|
56
|
+
const stored = storedAvatarRowSchema.parse(row);
|
|
57
|
+
return agentAvatarSchema.parse(JSON.parse(stored.avatar));
|
|
58
|
+
},
|
|
59
|
+
write(agentId, avatar) {
|
|
60
|
+
sql.exec("INSERT INTO agent_avatars(agent_id, avatar) VALUES(?, ?) ON CONFLICT(agent_id) DO UPDATE SET avatar = excluded.avatar", agentId, JSON.stringify(agentAvatarSchema.parse(avatar)));
|
|
61
|
+
},
|
|
62
|
+
remove(agentId) {
|
|
63
|
+
sql.exec("DELETE FROM agent_avatars WHERE agent_id = ?", agentId);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
package/dist/agents.d.ts
CHANGED
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
* the App page calls. Apps reach agents through Agar's `agent.list`,
|
|
4
4
|
* `agent.manage`, `agent.read` and `agent.send` contracts, so an App written
|
|
5
5
|
* for the Hub (Grove) runs unchanged. Each agent's own transport is
|
|
6
|
-
* Shiitake's HTTP routes (`/sessions…`, `/acp`)
|
|
6
|
+
* Shiitake's HTTP routes (`/sessions…`, `/acp`) and Knapsack's Session browser
|
|
7
|
+
* transport (`/browser/…`), served by the adapter behind the same App grants.
|
|
7
8
|
*/
|
|
8
9
|
import { Hono } from "hono";
|
|
9
10
|
import type { AppToolPolicy } from "./apps/tools.js";
|
|
10
11
|
import * as Schema from "effect/Schema";
|
|
11
12
|
import { type OysterCapability, type OysterEnv, type OysterPrincipal } from "./host.js";
|
|
12
13
|
import type { HostActor } from "@fungi.computer/puffball/app";
|
|
14
|
+
import type { Avatar } from "@fungi.computer/sprite/avatar";
|
|
13
15
|
import type { MemberInboxItem, MemberInboxSession } from "@fungi.computer/puffball/inbox";
|
|
14
16
|
/** One agent as Apps see it, in Agar's `appAgentSchema` shape. */
|
|
15
17
|
export type OysterAgent = Readonly<{
|
|
16
18
|
agentId: string;
|
|
17
19
|
name: string;
|
|
20
|
+
/** The Agent's own avatar: the owner's choice, else its seeded default. */
|
|
21
|
+
avatar: Avatar;
|
|
18
22
|
sessionOperations: readonly ("agent.read" | "agent.send" | "agent.manage")[];
|
|
19
23
|
}>;
|
|
20
24
|
/** An Agent owner's Session facts before the member identity and visit are added. */
|
|
@@ -70,12 +74,19 @@ export type AgentToolPolicyStatus = Readonly<{
|
|
|
70
74
|
export type OysterAgents = Readonly<{
|
|
71
75
|
/** Every agent, oldest first. */
|
|
72
76
|
list(): Promise<readonly OysterAgent[]>;
|
|
73
|
-
/**
|
|
74
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Create an agent; retrying with the same `requestId` returns the same one.
|
|
79
|
+
* With no `avatar` it wears its seeded default until the owner picks one.
|
|
80
|
+
*/
|
|
81
|
+
create(name: string, requestId: string, principal: OysterPrincipal, actor: AgentPolicyActor, avatar?: Avatar): Promise<OysterAgent>;
|
|
75
82
|
/** Owner-only rename; refuses a name already held by another Agent. */
|
|
76
83
|
rename(agentId: string, name: string, principal: OysterPrincipal): Promise<OysterAgent | Readonly<{
|
|
77
84
|
error: "not_found" | "name_taken" | "not_granted";
|
|
78
85
|
}>>;
|
|
86
|
+
/** Owner-only avatar change for an existing Agent. */
|
|
87
|
+
setAvatar(agentId: string, avatar: Avatar, principal: OysterPrincipal): Promise<OysterAgent | Readonly<{
|
|
88
|
+
error: "not_found" | "not_granted";
|
|
89
|
+
}>>;
|
|
79
90
|
/** This Agent's App-tool policy status, or `null` for no such Agent. */
|
|
80
91
|
policyStatus(agentId: string): Promise<AgentToolPolicyStatus | null>;
|
|
81
92
|
/** Read this Agent's stored App-tool approval; absence requires re-approval. A key's App access is its own grants (Ear Tag), not this policy. */
|
|
@@ -94,7 +105,7 @@ export type OysterAgents = Readonly<{
|
|
|
94
105
|
addMcpServer(agentId: string, input: AgentMcpServerInput): Promise<AgentMcpServer | AgentMcpServerRefusal>;
|
|
95
106
|
/** Disconnect one MCP server; `false` when the Agent or server is unknown. */
|
|
96
107
|
removeMcpServer(agentId: string, serverId: string): Promise<boolean>;
|
|
97
|
-
/** Serve
|
|
108
|
+
/** Serve this Agent's Shiitake and Knapsack Session browser transports. */
|
|
98
109
|
fetch(agentId: string, request: Request): Promise<Response | null>;
|
|
99
110
|
/** This member's Session and Workflow review rows, admitted by the host. */
|
|
100
111
|
inbox(): Promise<OysterInbox>;
|
package/dist/agents.js
CHANGED
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
* the App page calls. Apps reach agents through Agar's `agent.list`,
|
|
4
4
|
* `agent.manage`, `agent.read` and `agent.send` contracts, so an App written
|
|
5
5
|
* for the Hub (Grove) runs unchanged. Each agent's own transport is
|
|
6
|
-
* Shiitake's HTTP routes (`/sessions…`, `/acp`)
|
|
6
|
+
* Shiitake's HTTP routes (`/sessions…`, `/acp`) and Knapsack's Session browser
|
|
7
|
+
* transport (`/browser/…`), served by the adapter behind the same App grants.
|
|
7
8
|
*/
|
|
8
9
|
import { Hono } from "hono";
|
|
9
10
|
import * as Schema from "effect/Schema";
|
|
11
|
+
import { z } from "zod";
|
|
10
12
|
import { oysterPrincipalIsOwner, } from "./host.js";
|
|
11
|
-
import {
|
|
13
|
+
import { agentAvatarSchema } from "./agent-avatars.js";
|
|
14
|
+
import { agentAvatar, agentList, agentManage, agentRename, agentSessionRead, agentSessionWrite, agentToolPolicy, } from "@fungi.computer/agar/agents";
|
|
12
15
|
/** At most this many MCP servers per Agent. */
|
|
13
16
|
export const maxAgentMcpServers = 16;
|
|
14
17
|
/** Owner input for one MCP server: a guest identifier, an `https:` endpoint and an optional bearer. */
|
|
@@ -30,10 +33,12 @@ export const agentMcpServerInputSchema = Schema.Struct({
|
|
|
30
33
|
const createSchema = Schema.Struct({
|
|
31
34
|
name: Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(256)),
|
|
32
35
|
requestId: Schema.String.check(Schema.isPattern(/^[A-Za-z0-9_.:-]{1,256}$/u)),
|
|
36
|
+
avatar: Schema.optionalKey(Schema.Unknown),
|
|
33
37
|
});
|
|
34
38
|
const renameSchema = Schema.Struct({
|
|
35
39
|
name: Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(256)),
|
|
36
40
|
});
|
|
41
|
+
const avatarInputSchema = z.object({ avatar: agentAvatarSchema });
|
|
37
42
|
const visitSchema = Schema.Struct({
|
|
38
43
|
agentId: Schema.String,
|
|
39
44
|
sessionId: Schema.String,
|
|
@@ -76,6 +81,19 @@ export function agentCapabilities(agents) {
|
|
|
76
81
|
rename: { kind: "json", method: "PUT", path: "agent-names/:agentId" },
|
|
77
82
|
},
|
|
78
83
|
},
|
|
84
|
+
{
|
|
85
|
+
...shared,
|
|
86
|
+
id: "agent.avatar",
|
|
87
|
+
permission: "agent.avatar",
|
|
88
|
+
version: agentAvatar.version,
|
|
89
|
+
methods: {
|
|
90
|
+
setAvatar: {
|
|
91
|
+
kind: "json",
|
|
92
|
+
method: "PUT",
|
|
93
|
+
path: "agent-avatars/:agentId",
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
79
97
|
{
|
|
80
98
|
...shared,
|
|
81
99
|
id: "agent.toolPolicy",
|
|
@@ -126,9 +144,14 @@ function createAgentRoutes(agents) {
|
|
|
126
144
|
const body = Schema.decodeUnknownOption(createSchema)(await context.req.json());
|
|
127
145
|
if (body._tag === "None")
|
|
128
146
|
return refusal(400, "Name the agent");
|
|
147
|
+
const avatar = body.value.avatar === undefined
|
|
148
|
+
? undefined
|
|
149
|
+
: agentAvatarSchema.safeParse(body.value.avatar);
|
|
150
|
+
if (avatar?.success === false)
|
|
151
|
+
return refusal(400, "Choose a valid avatar");
|
|
129
152
|
// The owner's own authenticated request creates this Agent; its App tools
|
|
130
153
|
// are approved the same way direct owner approval is, with no second path.
|
|
131
|
-
return Response.json(await agents.create(body.value.name, body.value.requestId, principal, "owner-direct"));
|
|
154
|
+
return Response.json(await agents.create(body.value.name, body.value.requestId, principal, "owner-direct", avatar?.data));
|
|
132
155
|
});
|
|
133
156
|
routes.put("/agent-names/:agentId", async (context) => {
|
|
134
157
|
const { principal } = context.env;
|
|
@@ -149,6 +172,21 @@ function createAgentRoutes(agents) {
|
|
|
149
172
|
: 403, result.error);
|
|
150
173
|
return Response.json(result);
|
|
151
174
|
});
|
|
175
|
+
routes.put("/agent-avatars/:agentId", async (context) => {
|
|
176
|
+
const { principal } = context.env;
|
|
177
|
+
if (!oysterPrincipalIsOwner(principal))
|
|
178
|
+
return refusal(403, "Only the owner changes avatars");
|
|
179
|
+
const agentId = context.req.param("agentId");
|
|
180
|
+
if (!agentIdPattern.test(agentId))
|
|
181
|
+
return refusal(404, "No such agent");
|
|
182
|
+
const body = avatarInputSchema.safeParse(await context.req.json());
|
|
183
|
+
if (!body.success)
|
|
184
|
+
return refusal(400, "Choose a valid avatar");
|
|
185
|
+
const result = await agents.setAvatar(agentId, body.data.avatar, principal);
|
|
186
|
+
if ("error" in result)
|
|
187
|
+
return refusal(result.error === "not_found" ? 404 : 403, result.error);
|
|
188
|
+
return Response.json(result);
|
|
189
|
+
});
|
|
152
190
|
routes.get("/agent-policies/:agentId", async (context) => {
|
|
153
191
|
const agentId = context.req.param("agentId");
|
|
154
192
|
if (!agentIdPattern.test(agentId))
|
|
@@ -168,7 +206,8 @@ function createAgentRoutes(agents) {
|
|
|
168
206
|
const path = url.pathname.slice(`/agents/${agentId}`.length);
|
|
169
207
|
if (!(path === "/acp" ||
|
|
170
208
|
path === "/sessions" ||
|
|
171
|
-
path.startsWith("/sessions/")
|
|
209
|
+
path.startsWith("/sessions/") ||
|
|
210
|
+
path.startsWith("/browser/")))
|
|
172
211
|
return refusal(404, "Not found");
|
|
173
212
|
const length = Number(incoming.headers.get("content-length") ?? "0");
|
|
174
213
|
if (length > maxAgentRequestBytes)
|
package/dist/app-builds.d.ts
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import * as Schema from "effect/Schema";
|
|
20
20
|
import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
|
|
21
21
|
import { type Release } from "@fungi.computer/puffball";
|
|
22
|
+
import { type AppPublisherCoreConfigInput } from "./build/effect.js";
|
|
22
23
|
import { type TeamAppCandidateManifest } from "./build/submission-contract.js";
|
|
23
24
|
import { type ArtifactHeads, type ArtifactStore } from "./release.js";
|
|
24
25
|
import type { PublisherTemplateSeedReceipts } from "./source/seed-receipt.js";
|
|
@@ -77,6 +78,19 @@ export declare const oysterBuildSourceSchema: Schema.Struct<{
|
|
|
77
78
|
readonly buildRecipeDigest: Schema.refine<import("@fungi.computer/puffball").Sha256Digest, Schema.String>;
|
|
78
79
|
}>;
|
|
79
80
|
export type OysterBuildSource = Schema.Schema.Type<typeof oysterBuildSourceSchema>;
|
|
81
|
+
/** Installation is a build outcome, never a pending consent/approval record. */
|
|
82
|
+
export declare const oysterBuildInstallationSchema: Schema.Union<readonly [Schema.Struct<{
|
|
83
|
+
readonly kind: Schema.Literal<"pending">;
|
|
84
|
+
readonly installationId: Schema.String;
|
|
85
|
+
}>, Schema.Struct<{
|
|
86
|
+
readonly kind: Schema.Literals<readonly ["installed", "needs_consent"]>;
|
|
87
|
+
readonly installationId: Schema.String;
|
|
88
|
+
}>, Schema.Struct<{
|
|
89
|
+
readonly kind: Schema.Literal<"failed">;
|
|
90
|
+
readonly installationId: Schema.String;
|
|
91
|
+
readonly detail: Schema.String;
|
|
92
|
+
}>]>;
|
|
93
|
+
export type OysterBuildInstallation = Schema.Schema.Type<typeof oysterBuildInstallationSchema>;
|
|
80
94
|
/** What an owner sees of one build. */
|
|
81
95
|
export type OysterBuildView = Readonly<{
|
|
82
96
|
jobId: string;
|
|
@@ -86,8 +100,42 @@ export type OysterBuildView = Readonly<{
|
|
|
86
100
|
status: "running" | "succeeded" | "failed" | "revoked";
|
|
87
101
|
detail: string | null;
|
|
88
102
|
release: Release | null;
|
|
103
|
+
installation: OysterBuildInstallation | null;
|
|
104
|
+
}>;
|
|
105
|
+
/** Known preflight refusals, before a job or Computer run exists. */
|
|
106
|
+
export declare const oysterBuildStartReasonSchema: Schema.Literals<readonly ["invalid_revision", "no_source", "not_template_seed", "no_backend"]>;
|
|
107
|
+
export type OysterBuildStartReason = Schema.Schema.Type<typeof oysterBuildStartReasonSchema>;
|
|
108
|
+
export declare const oysterBuildStartMessages: {
|
|
109
|
+
invalid_revision: string;
|
|
110
|
+
no_source: string;
|
|
111
|
+
no_backend: string;
|
|
112
|
+
not_template_seed: string;
|
|
113
|
+
};
|
|
114
|
+
declare const OysterBuildStartError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
115
|
+
readonly _tag: "OysterBuildStartError";
|
|
116
|
+
} & Readonly<A>;
|
|
117
|
+
/** Local typed failure. The registry turns it into data before a DO RPC. */
|
|
118
|
+
export declare class OysterBuildStartError extends OysterBuildStartError_base<{
|
|
119
|
+
readonly reason: OysterBuildStartReason;
|
|
120
|
+
readonly message: string;
|
|
121
|
+
}> {
|
|
122
|
+
constructor(reason: OysterBuildStartReason);
|
|
123
|
+
}
|
|
124
|
+
export type OysterBuildStartResult = Readonly<{
|
|
125
|
+
kind: "started";
|
|
126
|
+
build: OysterBuildView;
|
|
127
|
+
}> | Readonly<{
|
|
128
|
+
kind: "refused";
|
|
129
|
+
reason: OysterBuildStartReason;
|
|
130
|
+
}>;
|
|
131
|
+
export type OysterBuildBackend = Readonly<{
|
|
132
|
+
kind: "hosted";
|
|
133
|
+
}> | Readonly<{
|
|
134
|
+
kind: "no_backend";
|
|
89
135
|
}>;
|
|
90
136
|
export type OysterBuildComputer = Readonly<{
|
|
137
|
+
/** Read the configured hosted route without starting a machine. Local machines never build Apps. */
|
|
138
|
+
buildBackend(): Promise<OysterBuildBackend>;
|
|
91
139
|
/** Run the fixed build command; the Computer re-admits the job first.
|
|
92
140
|
* A missing `output` means no terminal result was established: the
|
|
93
141
|
* command may still be running, so it is never read as an exit. */
|
|
@@ -118,17 +166,28 @@ export type OysterAppBuildOptions = Readonly<{
|
|
|
118
166
|
templates: Pick<PublisherTemplateSeedReceipts, "read">;
|
|
119
167
|
now?: () => number;
|
|
120
168
|
}>;
|
|
121
|
-
/**
|
|
122
|
-
* The SQLite tables one Oyster build owner keeps. Creation and any carrying
|
|
123
|
-
* forward of retained facts run as one transaction of the owner's storage, so
|
|
124
|
-
* an interrupted conversion leaves the facts as they were for a retry.
|
|
125
|
-
*/
|
|
169
|
+
/** Create the current build tables and index atomically; never convert old stores. */
|
|
126
170
|
export declare function migrateOysterAppBuilds(owner: WatchdogSqliteOwner): void;
|
|
127
171
|
/** The build owner over one tenant's SQLite, Computer and artifact store. */
|
|
128
172
|
export declare function createOysterAppBuilds(options: OysterAppBuildOptions): Readonly<{
|
|
129
173
|
/** Record (or replace) an App's build source; the caller is the owner. */
|
|
130
|
-
registerSource(source: OysterBuildSource)
|
|
174
|
+
registerSource: (source: OysterBuildSource) => OysterBuildSource;
|
|
175
|
+
/** Resolve repository identity and replace its current recipe as one transition.
|
|
176
|
+
* A fork's existing identity wins over the copied manifest's App id. */
|
|
177
|
+
registerRepositorySource(input: {
|
|
178
|
+
appId: string;
|
|
179
|
+
location: Extract<OysterBuildLocation, {
|
|
180
|
+
kind: "installation";
|
|
181
|
+
}>;
|
|
182
|
+
recipe: AppPublisherCoreConfigInput["recipe"];
|
|
183
|
+
}): {
|
|
184
|
+
kind: "registered";
|
|
185
|
+
source: OysterBuildSource;
|
|
186
|
+
} | {
|
|
187
|
+
kind: "conflict";
|
|
188
|
+
};
|
|
131
189
|
readSource: (appId: string) => OysterBuildSource | null;
|
|
190
|
+
readRepositorySource: (installationId: string, repository: string) => OysterBuildSource | null;
|
|
132
191
|
/**
|
|
133
192
|
* Mint one build job for an App's registered source at one exact commit.
|
|
134
193
|
* A template source builds only a seed revision recorded for its pinned
|
|
@@ -136,15 +195,28 @@ export declare function createOysterAppBuilds(options: OysterAppBuildOptions): R
|
|
|
136
195
|
* Answers the job and its one run, which the caller keeps alive (the
|
|
137
196
|
* upload grant exists only inside that run's fixed command).
|
|
138
197
|
*/
|
|
139
|
-
start(input: {
|
|
198
|
+
start: (input: {
|
|
140
199
|
appId: string;
|
|
141
200
|
sourceRevision: string;
|
|
142
201
|
requestedBy: string;
|
|
143
|
-
|
|
202
|
+
/** The Workflow's installation target. Absence leaves manual builds manual. */
|
|
203
|
+
installAt?: string;
|
|
204
|
+
/** One host-owned Workflow node reuses its job, including after restart. */
|
|
205
|
+
requestId?: string;
|
|
206
|
+
}) => Promise<Readonly<{
|
|
144
207
|
build: OysterBuildView;
|
|
145
208
|
run: () => Promise<OysterBuildView>;
|
|
146
209
|
}>>;
|
|
147
210
|
read(jobId: string): OysterBuildView | null;
|
|
211
|
+
/** Discover the current source's latest job from the caller's Git owner,
|
|
212
|
+
* never a job id remembered in a browser or another installation's repo. */
|
|
213
|
+
latestRepositoryBuild(installationId: string, repository: string): OysterBuildView | null;
|
|
214
|
+
/** One bounded host installation at a time. Only a verified successful
|
|
215
|
+
* build may reach the installer; the same existing row owns its outcome.
|
|
216
|
+
* A Workflow retry may resume a pending decision, never a terminal one. */
|
|
217
|
+
install(jobId: string, decide: (release: Release, installationId: string) => Promise<Exclude<OysterBuildInstallation, {
|
|
218
|
+
kind: "pending";
|
|
219
|
+
}>>): Promise<boolean>;
|
|
148
220
|
/** A built Release by id, for an owner to install or select. */
|
|
149
221
|
release(releaseId: string): Release | null;
|
|
150
222
|
/** May this Computer execute this job now? Re-checked by the Computer. */
|
|
@@ -208,3 +280,4 @@ export declare function createOysterAppBuilds(options: OysterAppBuildOptions): R
|
|
|
208
280
|
submitCandidate(jobId: string, manifest: TeamAppCandidateManifest, grant: string, credentialId: string): Promise<Response>;
|
|
209
281
|
}>;
|
|
210
282
|
export type OysterAppBuilds = ReturnType<typeof createOysterAppBuilds>;
|
|
283
|
+
export {};
|