@fungi.computer/oyster 0.5.0 → 0.6.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 +46 -8
- package/dist/agents.js +4 -0
- package/dist/auth.d.ts +24 -1
- package/dist/auth.js +38 -0
- package/dist/boot-host-script.js +1 -1
- package/dist/build/candidate-cli.mjs +8 -1
- package/dist/channels/binding.d.ts +8 -1
- package/dist/channels/binding.js +12 -0
- package/dist/channels/conversations.d.ts +16 -5
- package/dist/channels/visitor-channel.d.ts +53 -0
- package/dist/channels/visitor-channel.js +124 -0
- package/dist/channels/visitor-edge.d.ts +115 -0
- package/dist/channels/visitor-edge.js +582 -0
- package/dist/channels/visitor-request.d.ts +7 -0
- package/dist/channels/visitor-request.js +17 -0
- package/dist/channels/visitor-stream-slots.d.ts +2 -0
- package/dist/channels/visitor-stream-slots.js +33 -0
- package/dist/channels/visitors.d.ts +47 -0
- package/dist/channels/visitors.js +181 -0
- package/dist/cloudflare.d.ts +8 -2
- package/dist/cloudflare.js +27 -1
- package/dist/computer-target.d.ts +5 -0
- package/dist/computer-target.js +11 -0
- package/dist/host.d.ts +6 -3
- package/dist/host.js +75 -13
- package/dist/internal/host-gate.d.ts +6 -4
- package/dist/internal/host-gate.js +14 -7
- package/dist/invocation.d.ts +11 -4
- package/dist/invocation.js +134 -91
- package/dist/page/capabilities.js +1 -1
- package/dist/page/child-selection.d.ts +27 -0
- package/dist/page/child-selection.js +38 -0
- package/dist/page/mount.js +1 -1
- package/dist/page-script.js +4 -4
- package/dist/release.d.ts +13 -0
- package/dist/release.js +10 -0
- package/dist/request-origin.d.ts +4 -0
- package/dist/request-origin.js +10 -0
- package/dist/standalone/agents.js +11 -11
- package/dist/standalone/channel-session-door.d.ts +2 -2
- package/dist/standalone/channel-session-door.js +35 -5
- package/dist/standalone/computer-fleet.js +7 -1
- package/dist/standalone/tenancy.d.ts +3 -0
- package/dist/standalone/tenancy.js +7 -2
- package/dist/turnstile.d.ts +27 -0
- package/dist/turnstile.js +51 -0
- package/dist/workers-git.js +20 -1
- package/dist/workers.js +15 -12
- package/docs/api/auth.md +55 -0
- package/docs/api/build-contract.md +2 -2
- package/docs/api/cloudflare.md +26 -1
- package/docs/api/host.md +22 -13
- package/docs/api/invocation.md +7 -3
- package/docs/api/workers.md +17 -16
- package/docs/site/agent-quickstart.mdx +6 -2
- package/examples/standalone/wrangler.celld.jsonc +66 -0
- package/package.json +40 -16
|
@@ -21745,6 +21745,12 @@ var BirdDogSystemIdentitySchema = Struct({
|
|
|
21745
21745
|
systemId: Literal2("hub")
|
|
21746
21746
|
});
|
|
21747
21747
|
var BIRD_DOG_DIAGNOSTIC_MAX_LENGTH = 200;
|
|
21748
|
+
var BIRD_DOG_WAITING_MAX_ITEMS = 8;
|
|
21749
|
+
var BirdDogWaitingItemSchema = Struct({
|
|
21750
|
+
itemId: Identifier,
|
|
21751
|
+
kind: String4.check(isMinLength(1), isMaxLength(64)),
|
|
21752
|
+
summary: String4.check(isMinLength(1), isMaxLength(200))
|
|
21753
|
+
});
|
|
21748
21754
|
var SettledBirdDogReplySchema = Struct({
|
|
21749
21755
|
commandId: Identifier,
|
|
21750
21756
|
outcome: Literals([
|
|
@@ -21754,7 +21760,8 @@ var SettledBirdDogReplySchema = Struct({
|
|
|
21754
21760
|
"cancelled",
|
|
21755
21761
|
"outcome_unknown"
|
|
21756
21762
|
]),
|
|
21757
|
-
diagnostic: optionalKey2(String4.check(isMinLength(1), isMaxLength(BIRD_DOG_DIAGNOSTIC_MAX_LENGTH)))
|
|
21763
|
+
diagnostic: optionalKey2(String4.check(isMinLength(1), isMaxLength(BIRD_DOG_DIAGNOSTIC_MAX_LENGTH))),
|
|
21764
|
+
waitingOnYou: optionalKey2(ArraySchema(BirdDogWaitingItemSchema).check(isMinLength(1), isMaxLength(BIRD_DOG_WAITING_MAX_ITEMS)))
|
|
21758
21765
|
});
|
|
21759
21766
|
var BirdDogUndeliverableReasonSchema = Literals([
|
|
21760
21767
|
"recipient_offline",
|
|
@@ -9,11 +9,18 @@ export type ConversationBinding = Readonly<{
|
|
|
9
9
|
verifier: "fungi-v1" | "github" | "secret-url";
|
|
10
10
|
eventsPerMinute: number;
|
|
11
11
|
}> | Readonly<{
|
|
12
|
-
provider: "web"
|
|
12
|
+
provider: "web";
|
|
13
|
+
siteKey: string;
|
|
14
|
+
origins: readonly string[];
|
|
15
|
+
accent?: string | undefined;
|
|
16
|
+
}> | Readonly<{
|
|
17
|
+
provider: "discord" | "email" | "game";
|
|
13
18
|
}>;
|
|
14
19
|
/** Zod's optional binding projection may carry an explicit undefined. */
|
|
15
20
|
limits?: BindingLimits | undefined;
|
|
16
21
|
}>;
|
|
22
|
+
/** Does one allowlisted origin admit this exact browser origin? */
|
|
23
|
+
export declare function originAllowed(allowed: readonly string[], origin: string): boolean;
|
|
17
24
|
/** Per-party edge limits applied before any work is accepted. */
|
|
18
25
|
export declare const bindingLimitsSchema: z.ZodObject<{
|
|
19
26
|
messagesPerPartyPerDay: z.ZodNumber;
|
package/dist/channels/binding.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { channelIds } from "./ids.js";
|
|
3
|
+
/** Does one allowlisted origin admit this exact browser origin? */
|
|
4
|
+
export function originAllowed(allowed, origin) {
|
|
5
|
+
return allowed.some((entry) => {
|
|
6
|
+
if (!entry.startsWith("https://*."))
|
|
7
|
+
return entry === origin;
|
|
8
|
+
const suffix = entry.slice("https://*".length);
|
|
9
|
+
if (!origin.startsWith("https://") || !origin.endsWith(suffix))
|
|
10
|
+
return false;
|
|
11
|
+
const label = origin.slice("https://".length, -suffix.length);
|
|
12
|
+
return label.length > 0 && !label.includes(".");
|
|
13
|
+
});
|
|
14
|
+
}
|
|
3
15
|
/** Per-party edge limits applied before any work is accepted. */
|
|
4
16
|
export const bindingLimitsSchema = z.strictObject({
|
|
5
17
|
messagesPerPartyPerDay: z.number().int().min(1).max(10_000),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { type FungiAuthor, type AuthorMeta
|
|
2
|
+
import { type FungiAuthor, type AuthorMeta } from "@fungi.computer/agar/agents";
|
|
3
3
|
import { type Handoff } from "@fungi.computer/agar/channels";
|
|
4
4
|
import { type ConversationBinding, type ChannelParty } from "./binding.js";
|
|
5
5
|
export type HandoffEvent = Readonly<{
|
|
@@ -124,12 +124,11 @@ export type ChannelWithdrawResult = Readonly<{
|
|
|
124
124
|
commandId: string;
|
|
125
125
|
sessionId?: string;
|
|
126
126
|
}>;
|
|
127
|
-
/**
|
|
128
|
-
export type
|
|
127
|
+
/** A host-projected intent contains no private metadata or internal request key. */
|
|
128
|
+
export type VisitorPendingPrompt = Readonly<{
|
|
129
129
|
requestId: string;
|
|
130
130
|
commandId: string;
|
|
131
131
|
text: string;
|
|
132
|
-
meta?: MessageMetaValue | undefined;
|
|
133
132
|
delivery: "steer" | "followUp";
|
|
134
133
|
images?: readonly Readonly<{
|
|
135
134
|
type: "image";
|
|
@@ -137,10 +136,22 @@ export type ChannelPendingPrompt = Readonly<{
|
|
|
137
136
|
mimeType: string;
|
|
138
137
|
}>[] | undefined;
|
|
139
138
|
}>;
|
|
139
|
+
/** Only public baseline pagination crosses the visitor Session port. */
|
|
140
|
+
export type VisitorReadOptions = Readonly<{
|
|
141
|
+
limit?: number;
|
|
142
|
+
before?: string;
|
|
143
|
+
}>;
|
|
140
144
|
/** The channel's narrow Session port; hosts translate to their durable client. */
|
|
141
145
|
export type SessionDoor = Readonly<{
|
|
142
146
|
withdraw: (sessionId: string, requestId: string) => Promise<ChannelWithdrawResult>;
|
|
143
|
-
latestPending: (sessionId: string, visitor: FungiAuthor) => Promise<
|
|
147
|
+
latestPending: (sessionId: string, visitor: FungiAuthor) => Promise<VisitorPendingPrompt | null>;
|
|
148
|
+
participantDocument: (sessionId: string, visitorId: string, options: VisitorReadOptions) => Promise<string>;
|
|
149
|
+
participantStream: (sessionId: string, visitorId: string, options: Readonly<{
|
|
150
|
+
expiresAt: number;
|
|
151
|
+
now: () => number;
|
|
152
|
+
release: () => void;
|
|
153
|
+
canRead: () => Promise<boolean>;
|
|
154
|
+
}>) => Promise<ReadableStream<Uint8Array>>;
|
|
144
155
|
createSession: (requestId: string) => Promise<string>;
|
|
145
156
|
/** Deliver this conversation Session's tool grants to the runtime. */
|
|
146
157
|
deliverGrants: (sessionId: string) => void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type ConversationBinding } from "./binding.js";
|
|
2
|
+
import { type ChannelConversations, type ChannelWithdrawResult, type InboundEvent, type IntakeResult, type SessionDoor, type VisitorPendingPrompt, type VisitorReadOptions } from "./conversations.js";
|
|
3
|
+
import type { ChannelVisitors, VisitorGrant } from "./visitors.js";
|
|
4
|
+
/**
|
|
5
|
+
* Web visitor sequencing only. The host owns authorization, its admission
|
|
6
|
+
* receipt and the Session implementation; identity and conversations can live
|
|
7
|
+
* in different stores (and, in standalone, different Agents).
|
|
8
|
+
*/
|
|
9
|
+
export declare function createVisitorChannel(options: Readonly<{
|
|
10
|
+
binding: (bindingId: string) => ConversationBinding | null;
|
|
11
|
+
visitors: () => ChannelVisitors;
|
|
12
|
+
conversations: () => ChannelConversations;
|
|
13
|
+
sessionDoor: () => SessionDoor;
|
|
14
|
+
intake: (event: InboundEvent) => Promise<IntakeResult>;
|
|
15
|
+
acquireStream: (bindingId: string, visitorId: string, addressHash: string) => (() => void) | null;
|
|
16
|
+
now: () => number;
|
|
17
|
+
}>): {
|
|
18
|
+
mint: (bindingId: string, siteKey: string, addressHash: string) => Promise<VisitorGrant>;
|
|
19
|
+
refresh: (bindingId: string, secret: string) => Promise<VisitorGrant>;
|
|
20
|
+
rotate: (bindingId: string, siteKey: string, token: string, addressHash: string) => Promise<VisitorGrant | Readonly<{
|
|
21
|
+
kind: "unauthorized";
|
|
22
|
+
}>>;
|
|
23
|
+
send: (bindingId: string, token: string, requestId: string, text: string) => Promise<IntakeResult | Readonly<{
|
|
24
|
+
kind: "unauthorized";
|
|
25
|
+
}>>;
|
|
26
|
+
withdraw: (bindingId: string, token: string, rawRequestId: string) => Promise<ChannelWithdrawResult | Readonly<{
|
|
27
|
+
kind: "unauthorized";
|
|
28
|
+
}> | Readonly<{
|
|
29
|
+
kind: "denied";
|
|
30
|
+
reason: "event_conflict" | "new_party_limit" | "banned";
|
|
31
|
+
}>>;
|
|
32
|
+
latestPending: (bindingId: string, token: string) => Promise<VisitorPendingPrompt | null | Readonly<{
|
|
33
|
+
kind: "unauthorized";
|
|
34
|
+
}>>;
|
|
35
|
+
open: (bindingId: string, token: string, read: VisitorReadOptions) => Promise<Readonly<{
|
|
36
|
+
kind: "session";
|
|
37
|
+
snapshot: string;
|
|
38
|
+
}> | Readonly<{
|
|
39
|
+
kind: "unauthorized";
|
|
40
|
+
}> | Readonly<{
|
|
41
|
+
kind: "denied";
|
|
42
|
+
reason: "new_party_limit" | "banned";
|
|
43
|
+
}>>;
|
|
44
|
+
watch: (bindingId: string, token: string, canRead: (visitorId: string) => Promise<boolean>) => Promise<Readonly<{
|
|
45
|
+
kind: "stream";
|
|
46
|
+
body: ReadableStream<Uint8Array>;
|
|
47
|
+
}> | Readonly<{
|
|
48
|
+
kind: "empty" | "unauthorized";
|
|
49
|
+
}> | Readonly<{
|
|
50
|
+
kind: "denied";
|
|
51
|
+
reason: "suspended" | "stream_limit";
|
|
52
|
+
}>>;
|
|
53
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { partyLabel } from "./binding.js";
|
|
2
|
+
import { channelRequestId, } from "./conversations.js";
|
|
3
|
+
import { visitorMessageId, visitorRequestSchema } from "./visitor-request.js";
|
|
4
|
+
/**
|
|
5
|
+
* Web visitor sequencing only. The host owns authorization, its admission
|
|
6
|
+
* receipt and the Session implementation; identity and conversations can live
|
|
7
|
+
* in different stores (and, in standalone, different Agents).
|
|
8
|
+
*/
|
|
9
|
+
export function createVisitorChannel(options) {
|
|
10
|
+
const active = (bindingId) => {
|
|
11
|
+
const binding = options.binding(bindingId);
|
|
12
|
+
return binding?.lifecycle === "active" &&
|
|
13
|
+
binding.selector.provider === "web"
|
|
14
|
+
? binding
|
|
15
|
+
: null;
|
|
16
|
+
};
|
|
17
|
+
const authenticate = (bindingId, token) => options.visitors().authenticate(bindingId, token);
|
|
18
|
+
return {
|
|
19
|
+
mint: (bindingId, siteKey, addressHash) => options.visitors().mint(options.binding(bindingId), siteKey, addressHash),
|
|
20
|
+
refresh: (bindingId, secret) => options.visitors().refresh(options.binding(bindingId), secret),
|
|
21
|
+
rotate: async (bindingId, siteKey, token, addressHash) => {
|
|
22
|
+
const visitors = options.visitors();
|
|
23
|
+
const visitor = await visitors.authenticate(bindingId, token);
|
|
24
|
+
if (visitor === null)
|
|
25
|
+
return { kind: "unauthorized" };
|
|
26
|
+
visitors.forget(visitor.visitorId);
|
|
27
|
+
return await visitors.mint(options.binding(bindingId), siteKey, addressHash);
|
|
28
|
+
},
|
|
29
|
+
send: async (bindingId, token, requestId, text) => {
|
|
30
|
+
const visitor = await authenticate(bindingId, token);
|
|
31
|
+
if (visitor === null)
|
|
32
|
+
return { kind: "unauthorized" };
|
|
33
|
+
const { visitorId } = visitor;
|
|
34
|
+
return options.intake({
|
|
35
|
+
bindingId,
|
|
36
|
+
conversation: visitorId,
|
|
37
|
+
eventId: await visitorMessageId(requestId),
|
|
38
|
+
party: { kind: "visitor", visitorId },
|
|
39
|
+
text,
|
|
40
|
+
sourceRequestId: requestId,
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
withdraw: async (bindingId, token, rawRequestId) => {
|
|
44
|
+
const binding = active(bindingId);
|
|
45
|
+
const visitor = await authenticate(bindingId, token);
|
|
46
|
+
if (visitor === null || binding === null)
|
|
47
|
+
return { kind: "unauthorized" };
|
|
48
|
+
const { requestId } = visitorRequestSchema.parse({
|
|
49
|
+
requestId: rawRequestId,
|
|
50
|
+
});
|
|
51
|
+
const eventId = await visitorMessageId(requestId);
|
|
52
|
+
const conversations = options.conversations();
|
|
53
|
+
if (conversations.visitorEventConflict(bindingId, visitor.visitorId, eventId))
|
|
54
|
+
return { kind: "denied", reason: "event_conflict" };
|
|
55
|
+
const opened = await conversations.open(binding, visitor.visitorId, {
|
|
56
|
+
kind: "visitor",
|
|
57
|
+
visitorId: visitor.visitorId,
|
|
58
|
+
});
|
|
59
|
+
if (opened.kind === "denied")
|
|
60
|
+
return opened;
|
|
61
|
+
return options
|
|
62
|
+
.sessionDoor()
|
|
63
|
+
.withdraw(opened.conversation.sessionId, channelRequestId(binding.bindingId, eventId));
|
|
64
|
+
},
|
|
65
|
+
latestPending: async (bindingId, token) => {
|
|
66
|
+
const binding = active(bindingId);
|
|
67
|
+
const visitor = await authenticate(bindingId, token);
|
|
68
|
+
if (visitor === null || binding === null)
|
|
69
|
+
return { kind: "unauthorized" };
|
|
70
|
+
const { visitorId } = visitor;
|
|
71
|
+
const conversation = options.conversations().find(bindingId, visitorId);
|
|
72
|
+
if (conversation === null)
|
|
73
|
+
return null;
|
|
74
|
+
return await options.sessionDoor().latestPending(conversation.sessionId, {
|
|
75
|
+
id: visitorId,
|
|
76
|
+
name: partyLabel({ kind: "visitor", visitorId }),
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
open: async (bindingId, token, read) => {
|
|
80
|
+
const binding = active(bindingId);
|
|
81
|
+
const visitor = await authenticate(bindingId, token);
|
|
82
|
+
if (visitor === null || binding === null)
|
|
83
|
+
return { kind: "unauthorized" };
|
|
84
|
+
const { visitorId } = visitor;
|
|
85
|
+
const opened = await options.conversations().open(binding, visitorId, {
|
|
86
|
+
kind: "visitor",
|
|
87
|
+
visitorId,
|
|
88
|
+
});
|
|
89
|
+
if (opened.kind === "denied")
|
|
90
|
+
return opened;
|
|
91
|
+
return {
|
|
92
|
+
kind: "session",
|
|
93
|
+
snapshot: await options
|
|
94
|
+
.sessionDoor()
|
|
95
|
+
.participantDocument(opened.conversation.sessionId, visitorId, read),
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
watch: async (bindingId, token, canRead) => {
|
|
99
|
+
const visitor = await authenticate(bindingId, token);
|
|
100
|
+
if (visitor === null)
|
|
101
|
+
return { kind: "unauthorized" };
|
|
102
|
+
if (options.binding(bindingId)?.lifecycle !== "active")
|
|
103
|
+
return { kind: "denied", reason: "suspended" };
|
|
104
|
+
const { visitorId } = visitor;
|
|
105
|
+
const conversation = options.conversations().find(bindingId, visitorId);
|
|
106
|
+
if (conversation === null)
|
|
107
|
+
return { kind: "empty" };
|
|
108
|
+
const release = options.acquireStream(bindingId, visitorId, visitor.addressHash);
|
|
109
|
+
if (release === null)
|
|
110
|
+
return { kind: "denied", reason: "stream_limit" };
|
|
111
|
+
return {
|
|
112
|
+
kind: "stream",
|
|
113
|
+
body: await options
|
|
114
|
+
.sessionDoor()
|
|
115
|
+
.participantStream(conversation.sessionId, visitorId, {
|
|
116
|
+
expiresAt: visitor.expiresAt,
|
|
117
|
+
now: options.now,
|
|
118
|
+
release,
|
|
119
|
+
canRead: () => canRead(visitorId),
|
|
120
|
+
}),
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Hono, type Context, type Env as HonoEnv } from "hono";
|
|
2
|
+
import type { ChannelWithdrawResult, IntakeResult, VisitorPendingPrompt, VisitorReadOptions } from "./conversations.js";
|
|
3
|
+
import { type VisitorGrant } from "./visitors.js";
|
|
4
|
+
import type { Avatar } from "@fungi.computer/sprite/avatar";
|
|
5
|
+
/** Public address facts the host supplies after looking up an embed site. */
|
|
6
|
+
export type VisitorSiteAddress = Readonly<{
|
|
7
|
+
bindingId: string;
|
|
8
|
+
}>;
|
|
9
|
+
/** Embed-safe site chrome returned by the host after binding admission. */
|
|
10
|
+
export type VisitorSiteFacts = Readonly<{
|
|
11
|
+
accent?: string;
|
|
12
|
+
name: string;
|
|
13
|
+
avatar: Avatar;
|
|
14
|
+
origins: readonly string[];
|
|
15
|
+
}>;
|
|
16
|
+
/** An opened host door, disposed after the edge has returned a response. */
|
|
17
|
+
export type VisitorEdgeChannel = Readonly<{
|
|
18
|
+
[Symbol.dispose]: () => void;
|
|
19
|
+
readChannelSite: (input: Readonly<{
|
|
20
|
+
bindingId: string;
|
|
21
|
+
siteKey: string;
|
|
22
|
+
}>) => Promise<VisitorSiteFacts | null>;
|
|
23
|
+
mintChannelVisitor: (input: Readonly<{
|
|
24
|
+
bindingId: string;
|
|
25
|
+
siteKey: string;
|
|
26
|
+
addressHash: string;
|
|
27
|
+
}>) => Promise<VisitorGrant>;
|
|
28
|
+
refreshChannelVisitor: (input: Readonly<{
|
|
29
|
+
bindingId: string;
|
|
30
|
+
refreshSecret: string;
|
|
31
|
+
}>) => Promise<VisitorGrant>;
|
|
32
|
+
rotateChannelVisitor: (input: Readonly<{
|
|
33
|
+
bindingId: string;
|
|
34
|
+
siteKey: string;
|
|
35
|
+
accessToken: string;
|
|
36
|
+
addressHash: string;
|
|
37
|
+
}>) => Promise<VisitorGrant | Readonly<{
|
|
38
|
+
kind: "unauthorized";
|
|
39
|
+
}>>;
|
|
40
|
+
sendAsChannelVisitor: (input: Readonly<{
|
|
41
|
+
bindingId: string;
|
|
42
|
+
accessToken: string;
|
|
43
|
+
requestId: string;
|
|
44
|
+
text: string;
|
|
45
|
+
}>) => Promise<IntakeResult | Readonly<{
|
|
46
|
+
kind: "unauthorized";
|
|
47
|
+
}>>;
|
|
48
|
+
withdrawAsChannelVisitor: (input: Readonly<{
|
|
49
|
+
bindingId: string;
|
|
50
|
+
accessToken: string;
|
|
51
|
+
requestId: string;
|
|
52
|
+
}>) => Promise<ChannelWithdrawResult | Readonly<{
|
|
53
|
+
kind: "unauthorized";
|
|
54
|
+
}> | Readonly<{
|
|
55
|
+
kind: "denied";
|
|
56
|
+
reason: "event_conflict" | "new_party_limit" | "banned";
|
|
57
|
+
}>>;
|
|
58
|
+
latestPendingAsChannelVisitor: (input: Readonly<{
|
|
59
|
+
bindingId: string;
|
|
60
|
+
accessToken: string;
|
|
61
|
+
}>) => Promise<VisitorPendingPrompt | null | Readonly<{
|
|
62
|
+
kind: "unauthorized";
|
|
63
|
+
}>>;
|
|
64
|
+
watchAsChannelVisitor: (input: Readonly<{
|
|
65
|
+
bindingId: string;
|
|
66
|
+
accessToken: string;
|
|
67
|
+
}>) => Promise<Readonly<{
|
|
68
|
+
kind: "stream";
|
|
69
|
+
body: ReadableStream<Uint8Array>;
|
|
70
|
+
}> | Readonly<{
|
|
71
|
+
kind: "empty";
|
|
72
|
+
}> | Readonly<{
|
|
73
|
+
kind: "unauthorized";
|
|
74
|
+
}> | Readonly<{
|
|
75
|
+
kind: "denied";
|
|
76
|
+
reason: "suspended" | "stream_limit";
|
|
77
|
+
}>>;
|
|
78
|
+
openVisitorSession: (input: Readonly<{
|
|
79
|
+
bindingId: string;
|
|
80
|
+
accessToken: string;
|
|
81
|
+
options: VisitorReadOptions;
|
|
82
|
+
}>) => Promise<Readonly<{
|
|
83
|
+
kind: "session";
|
|
84
|
+
snapshot: string;
|
|
85
|
+
}> | Readonly<{
|
|
86
|
+
kind: "unauthorized";
|
|
87
|
+
}> | Readonly<{
|
|
88
|
+
kind: "denied";
|
|
89
|
+
reason: "new_party_limit" | "banned";
|
|
90
|
+
}>>;
|
|
91
|
+
readAsChannelVisitor: (input: Readonly<{
|
|
92
|
+
bindingId: string;
|
|
93
|
+
accessToken: string;
|
|
94
|
+
}>) => Promise<Readonly<{
|
|
95
|
+
kind: "view";
|
|
96
|
+
view: unknown;
|
|
97
|
+
}> | Readonly<{
|
|
98
|
+
kind: "unauthorized";
|
|
99
|
+
}>>;
|
|
100
|
+
}>;
|
|
101
|
+
/**
|
|
102
|
+
* Address lookup and mint/refresh use the site owner; conversation work must
|
|
103
|
+
* receive a verified visitor first. A host may route different visitors to
|
|
104
|
+
* different Agents without teaching the conversation core about its registry.
|
|
105
|
+
*/
|
|
106
|
+
export type VisitorEdgeHost<E extends HonoEnv, Site extends VisitorSiteAddress, Visitor extends object> = Readonly<{
|
|
107
|
+
resolveSite: (context: Context<E>, siteKey: string) => Promise<Site | null>;
|
|
108
|
+
openSite: (context: Context<E>, site: Site) => Promise<VisitorEdgeChannel>;
|
|
109
|
+
authenticateVisitor: (context: Context<E>, site: Site, token: string) => Promise<Visitor | null>;
|
|
110
|
+
openChannel: (context: Context<E>, site: Site, visitor: Visitor) => Promise<VisitorEdgeChannel>;
|
|
111
|
+
addressHash: (context: Context<E>) => Promise<string>;
|
|
112
|
+
assetPath: (kind: "widget" | "styles") => string;
|
|
113
|
+
onError: (error: Error, context: Context<E>) => Response | Promise<Response>;
|
|
114
|
+
}>;
|
|
115
|
+
export declare function createVisitorEdge<E extends HonoEnv, Site extends VisitorSiteAddress, Visitor extends object>(host: VisitorEdgeHost<E, Site, Visitor>): Hono<E>;
|