fauna-api 0.0.0-stage → 1.0.676
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/dist/brands.d.ts +2 -0
- package/dist/brands.js +2 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +121 -0
- package/dist/src/ant/chat.d.ts +130 -0
- package/dist/src/ant/chat.js +467 -0
- package/dist/src/ant/format.d.ts +40 -0
- package/dist/src/ant/format.js +143 -0
- package/dist/src/ant/graph.d.ts +130 -0
- package/dist/src/ant/graph.js +449 -0
- package/dist/src/ant/index.d.ts +16 -0
- package/dist/src/ant/index.js +61 -0
- package/dist/src/ant/persona.d.ts +151 -0
- package/dist/src/ant/persona.js +136 -0
- package/dist/src/ant/timeline.d.ts +283 -0
- package/dist/src/ant/timeline.js +1025 -0
- package/dist/src/authentification.d.ts +45 -0
- package/dist/src/authentification.js +194 -0
- package/dist/src/billing.d.ts +135 -0
- package/dist/src/billing.js +128 -0
- package/dist/src/calendar.d.ts +83 -0
- package/dist/src/calendar.js +133 -0
- package/dist/src/chats.d.ts +113 -0
- package/dist/src/chats.js +100 -0
- package/dist/src/cicada/contacts.d.ts +31 -0
- package/dist/src/cicada/contacts.js +28 -0
- package/dist/src/cicada/conversations.d.ts +73 -0
- package/dist/src/cicada/conversations.js +236 -0
- package/dist/src/cicada/inboxes.d.ts +48 -0
- package/dist/src/cicada/inboxes.js +82 -0
- package/dist/src/cicada/index.d.ts +16 -0
- package/dist/src/cicada/index.js +61 -0
- package/dist/src/cicada/phone.d.ts +48 -0
- package/dist/src/cicada/phone.js +306 -0
- package/dist/src/drive.d.ts +80 -0
- package/dist/src/drive.js +472 -0
- package/dist/src/extensions/index.d.ts +4 -0
- package/dist/src/extensions/index.js +5 -0
- package/dist/src/extensions/web/index.d.ts +6 -0
- package/dist/src/extensions/web/index.js +7 -0
- package/dist/src/extensions/web/session.d.ts +106 -0
- package/dist/src/extensions/web/session.js +469 -0
- package/dist/src/extensions/web/tools.d.ts +13 -0
- package/dist/src/extensions/web/tools.js +72 -0
- package/dist/src/friends.d.ts +39 -0
- package/dist/src/friends.js +119 -0
- package/dist/src/groups.d.ts +82 -0
- package/dist/src/groups.js +269 -0
- package/dist/src/inbox.d.ts +11 -0
- package/dist/src/inbox.js +35 -0
- package/dist/src/integrations.d.ts +52 -0
- package/dist/src/integrations.js +205 -0
- package/dist/src/magpie/explorer.d.ts +14 -0
- package/dist/src/magpie/explorer.js +36 -0
- package/dist/src/magpie/index.d.ts +16 -0
- package/dist/src/magpie/index.js +61 -0
- package/dist/src/magpie/portfolio.d.ts +52 -0
- package/dist/src/magpie/portfolio.js +179 -0
- package/dist/src/magpie/quotes.d.ts +35 -0
- package/dist/src/magpie/quotes.js +47 -0
- package/dist/src/magpie/room.d.ts +61 -0
- package/dist/src/magpie/room.js +238 -0
- package/dist/src/maps.d.ts +159 -0
- package/dist/src/maps.js +171 -0
- package/dist/src/marmot/browser.d.ts +139 -0
- package/dist/src/marmot/browser.js +389 -0
- package/dist/src/marmot/index.d.ts +20 -0
- package/dist/src/marmot/index.js +65 -0
- package/dist/src/marmot/media.d.ts +56 -0
- package/dist/src/marmot/media.js +265 -0
- package/dist/src/marmot/research.d.ts +337 -0
- package/dist/src/marmot/research.js +1138 -0
- package/dist/src/marmot/scraper.d.ts +59 -0
- package/dist/src/marmot/scraper.js +94 -0
- package/dist/src/marmot/web.d.ts +402 -0
- package/dist/src/marmot/web.js +2586 -0
- package/dist/src/marmot/workspace.d.ts +81 -0
- package/dist/src/marmot/workspace.js +263 -0
- package/dist/src/parrot/agent.d.ts +53 -0
- package/dist/src/parrot/agent.js +93 -0
- package/dist/src/parrot/chat.d.ts +84 -0
- package/dist/src/parrot/chat.js +237 -0
- package/dist/src/parrot/format.d.ts +128 -0
- package/dist/src/parrot/format.js +639 -0
- package/dist/src/parrot/generative.d.ts +261 -0
- package/dist/src/parrot/generative.js +185 -0
- package/dist/src/parrot/index.d.ts +22 -0
- package/dist/src/parrot/index.js +67 -0
- package/dist/src/parrot/room.d.ts +225 -0
- package/dist/src/parrot/room.js +466 -0
- package/dist/src/parrot/routing.d.ts +90 -0
- package/dist/src/parrot/routing.js +78 -0
- package/dist/src/parrot/share.d.ts +126 -0
- package/dist/src/parrot/share.js +261 -0
- package/dist/src/parrot/socket.d.ts +41 -0
- package/dist/src/parrot/socket.js +478 -0
- package/dist/src/payments.d.ts +73 -0
- package/dist/src/payments.js +107 -0
- package/dist/src/private/cards.css +75 -0
- package/dist/src/rooms.d.ts +76 -0
- package/dist/src/rooms.js +168 -0
- package/dist/src/shared/schema.d.ts +24 -0
- package/dist/src/shared/schema.js +1 -0
- package/dist/src/shared/server.d.ts +65 -0
- package/dist/src/shared/server.js +302 -0
- package/dist/src/termite/environment.d.ts +110 -0
- package/dist/src/termite/environment.js +263 -0
- package/dist/src/termite/index.d.ts +14 -0
- package/dist/src/termite/index.js +59 -0
- package/dist/src/termite/os.d.ts +326 -0
- package/dist/src/termite/os.js +572 -0
- package/dist/src/termite/versioning.d.ts +94 -0
- package/dist/src/termite/versioning.js +217 -0
- package/dist/src/tickets.d.ts +43 -0
- package/dist/src/tickets.js +138 -0
- package/dist/src/travel.d.ts +85 -0
- package/dist/src/travel.js +44 -0
- package/dist/src/ui/authentification.d.ts +24 -0
- package/dist/src/ui/authentification.js +177 -0
- package/dist/src/ui/brands.css +11 -0
- package/dist/src/ui/brands.d.ts +7 -0
- package/dist/src/ui/brands.js +14 -0
- package/dist/src/ui/browser.d.ts +74 -0
- package/dist/src/ui/browser.js +470 -0
- package/dist/src/ui/chat.d.ts +183 -0
- package/dist/src/ui/chat.js +2379 -0
- package/dist/src/ui/chatPanel.d.ts +109 -0
- package/dist/src/ui/chatPanel.js +768 -0
- package/dist/src/ui/chatSequence.d.ts +36 -0
- package/dist/src/ui/chatSequence.js +42 -0
- package/dist/src/ui/crown.d.ts +19 -0
- package/dist/src/ui/crown.js +199 -0
- package/dist/src/ui/cursor.d.ts +73 -0
- package/dist/src/ui/cursor.js +175 -0
- package/dist/src/ui/device.d.ts +33 -0
- package/dist/src/ui/device.js +49 -0
- package/dist/src/ui/drive.d.ts +69 -0
- package/dist/src/ui/drive.js +1243 -0
- package/dist/src/ui/generative.d.ts +39 -0
- package/dist/src/ui/generative.js +313 -0
- package/dist/src/ui/hover.d.ts +6 -0
- package/dist/src/ui/hover.js +210 -0
- package/dist/src/ui/index.d.ts +42 -0
- package/dist/src/ui/index.js +87 -0
- package/dist/src/ui/integrations.d.ts +13 -0
- package/dist/src/ui/integrations.js +175 -0
- package/dist/src/ui/maps.d.ts +87 -0
- package/dist/src/ui/maps.js +547 -0
- package/dist/src/ui/roster.d.ts +32 -0
- package/dist/src/ui/roster.js +163 -0
- package/dist/src/ui/talk.d.ts +12 -0
- package/dist/src/ui/talk.js +560 -0
- package/dist/src/ui/tickets.d.ts +13 -0
- package/dist/src/ui/tickets.js +966 -0
- package/dist/src/ui/toast.d.ts +7 -0
- package/dist/src/ui/toast.js +258 -0
- package/dist/src/ui/tokenText.d.ts +18 -0
- package/dist/src/ui/tokenText.js +96 -0
- package/dist/src/ui/tokenized.d.ts +42 -0
- package/dist/src/ui/tokenized.js +622 -0
- package/dist/src/ui/ui.css +4763 -0
- package/dist/src/ui/view.d.ts +23 -0
- package/dist/src/ui/view.js +584 -0
- package/dist/src/view.d.ts +113 -0
- package/dist/src/view.js +386 -0
- package/dist/src/webhooks.d.ts +66 -0
- package/dist/src/webhooks.js +78 -0
- package/dist/ui.d.ts +1 -0
- package/dist/ui.js +1 -0
- package/package.json +89 -6
- package/README.md +0 -3
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import { Ant } from "./index.js";
|
|
2
|
+
import { Room, connect as connectServer } from "../shared/server.js";
|
|
3
|
+
import { connectRoom } from "../parrot/room.js";
|
|
4
|
+
import * as Drive from "../drive.js";
|
|
5
|
+
import IsoWebSocket from "isomorphic-ws";
|
|
6
|
+
async function fileRef(authorization, file) {
|
|
7
|
+
let ready = file;
|
|
8
|
+
if (!ready.online) {
|
|
9
|
+
const dest = `tmp/${crypto.randomUUID()}-${ready.name}`;
|
|
10
|
+
const bytes = await ready.download();
|
|
11
|
+
ready = Drive.fromBytes(authorization, dest, ready.contentType, bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes));
|
|
12
|
+
await ready.upload();
|
|
13
|
+
}
|
|
14
|
+
const authz = ready.authorization;
|
|
15
|
+
if ("accessCode" in authz) {
|
|
16
|
+
return { type: "FILE", name: ready.name, contentType: ready.contentType, accessCode: authz.accessCode };
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
type: "FILE",
|
|
20
|
+
name: ready.name,
|
|
21
|
+
contentType: ready.contentType,
|
|
22
|
+
path: authz.path,
|
|
23
|
+
...(authz.group ? { group: authz.group } : {}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function serializeMessages(authorization, messages) {
|
|
27
|
+
return Promise.all(messages.map(async (message) => ({
|
|
28
|
+
parts: await Promise.all(message.parts.map(async (part) => part.type === "FILE" ? await fileRef(authorization, part.file) : part)),
|
|
29
|
+
role: message.role,
|
|
30
|
+
})));
|
|
31
|
+
}
|
|
32
|
+
/** Answer a persona's call to one of the caller's tools; one it doesn't have comes back declined. */
|
|
33
|
+
function answerClientTool(ws, tools, call) {
|
|
34
|
+
const tool = tools.find((t) => t.function.name === call.name);
|
|
35
|
+
void Promise.resolve(tool ? tool.evaluate(call.args ?? {}) : JSON.stringify({ declined: true }))
|
|
36
|
+
.catch(() => JSON.stringify({ declined: true }))
|
|
37
|
+
.then((result) => {
|
|
38
|
+
try {
|
|
39
|
+
ws.send(JSON.stringify({ event: "tool_result", id: call.id, result: String(result) }));
|
|
40
|
+
}
|
|
41
|
+
catch { /* closed */ }
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export async function listChats(authorization, opts) {
|
|
45
|
+
const response = await fetch(`${Ant.getURL()}/chats`, {
|
|
46
|
+
method: "GET",
|
|
47
|
+
headers: {
|
|
48
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
49
|
+
...(opts?.group ? { Group: opts.group.id } : {}),
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok)
|
|
53
|
+
throw new Error(await response.text());
|
|
54
|
+
const rows = await response.json();
|
|
55
|
+
return (Array.isArray(rows) ? rows : []).map((row) => ({
|
|
56
|
+
...row,
|
|
57
|
+
last: row.last ? { ...row.last, createdAt: new Date(row.last.createdAt) } : null,
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
/** Create a chat. `repository` (the owner's) is the computer its personas start from. */
|
|
61
|
+
export async function createChat(authorization, name, opts) {
|
|
62
|
+
const response = await fetch(`${Ant.getURL()}/chats`, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: {
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
67
|
+
...(opts?.group ? { Group: opts.group.id } : {}),
|
|
68
|
+
},
|
|
69
|
+
body: JSON.stringify({
|
|
70
|
+
name,
|
|
71
|
+
...(opts?.audience?.length ? { audience: opts.audience } : {}),
|
|
72
|
+
...(opts?.repository ? { repository: opts.repository.id } : {}),
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
|
+
if (!response.ok)
|
|
76
|
+
throw new Error(await response.text());
|
|
77
|
+
return await response.json();
|
|
78
|
+
}
|
|
79
|
+
export async function deleteChat(authorization, id, opts) {
|
|
80
|
+
const response = await fetch(`${Ant.getURL()}/chat`, {
|
|
81
|
+
method: "DELETE",
|
|
82
|
+
headers: {
|
|
83
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
84
|
+
...(opts?.group ? { Group: opts.group.id } : {}),
|
|
85
|
+
Chat: id,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
if (!response.ok)
|
|
89
|
+
throw new Error(await response.text());
|
|
90
|
+
}
|
|
91
|
+
export async function moveChatToGroup(authorization, id, groupId) {
|
|
92
|
+
const response = await fetch(`${Ant.getURL()}/chat/move`, {
|
|
93
|
+
method: "POST",
|
|
94
|
+
headers: {
|
|
95
|
+
"Content-Type": "application/json",
|
|
96
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
97
|
+
Chat: id,
|
|
98
|
+
},
|
|
99
|
+
body: JSON.stringify({ groupId }),
|
|
100
|
+
});
|
|
101
|
+
if (!response.ok)
|
|
102
|
+
throw new Error(await response.text());
|
|
103
|
+
return await response.json();
|
|
104
|
+
}
|
|
105
|
+
export async function renameChat(authorization, id, name, opts) {
|
|
106
|
+
const response = await fetch(`${Ant.getURL()}/chat/rename`, {
|
|
107
|
+
method: "POST",
|
|
108
|
+
headers: {
|
|
109
|
+
"Content-Type": "application/json",
|
|
110
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
111
|
+
...(opts?.group ? { Group: opts.group.id } : {}),
|
|
112
|
+
Chat: id,
|
|
113
|
+
},
|
|
114
|
+
body: JSON.stringify({ name }),
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok)
|
|
117
|
+
throw new Error(await response.text());
|
|
118
|
+
}
|
|
119
|
+
// ─── Domain ──────────────────────────────────────────────────────────────────
|
|
120
|
+
export class Agent {
|
|
121
|
+
constructor(parent, id, persona, repository = null) {
|
|
122
|
+
Object.defineProperty(this, "parent", {
|
|
123
|
+
enumerable: true,
|
|
124
|
+
configurable: true,
|
|
125
|
+
writable: true,
|
|
126
|
+
value: void 0
|
|
127
|
+
});
|
|
128
|
+
Object.defineProperty(this, "id", {
|
|
129
|
+
enumerable: true,
|
|
130
|
+
configurable: true,
|
|
131
|
+
writable: true,
|
|
132
|
+
value: void 0
|
|
133
|
+
});
|
|
134
|
+
Object.defineProperty(this, "persona", {
|
|
135
|
+
enumerable: true,
|
|
136
|
+
configurable: true,
|
|
137
|
+
writable: true,
|
|
138
|
+
value: void 0
|
|
139
|
+
});
|
|
140
|
+
/** A repository picked for this agent, used directly; null when it follows the chat's. */
|
|
141
|
+
Object.defineProperty(this, "repository", {
|
|
142
|
+
enumerable: true,
|
|
143
|
+
configurable: true,
|
|
144
|
+
writable: true,
|
|
145
|
+
value: void 0
|
|
146
|
+
});
|
|
147
|
+
this.parent = parent;
|
|
148
|
+
this.id = id;
|
|
149
|
+
this.persona = persona;
|
|
150
|
+
this.repository = repository;
|
|
151
|
+
}
|
|
152
|
+
/** Pick a repository for this agent (used directly), or null to follow the chat's again. */
|
|
153
|
+
setRepository(repository) {
|
|
154
|
+
this.parent.post({ event: "set-agent-repository", agentId: this.id, repository: repository ? repository.id : null });
|
|
155
|
+
}
|
|
156
|
+
async observe(content) {
|
|
157
|
+
const auth = this.parent.authorization;
|
|
158
|
+
return observeAgent(auth.authorization, this.parent.chatId, this.id, content, "accessCode" in auth
|
|
159
|
+
? { accessCode: auth.accessCode }
|
|
160
|
+
: auth.group
|
|
161
|
+
? { group: { id: auth.group } }
|
|
162
|
+
: undefined);
|
|
163
|
+
}
|
|
164
|
+
/** Stream a reply as this agent, under the room's own authorization. `tools` answer the persona's calls to them. */
|
|
165
|
+
generate(messages, opts) {
|
|
166
|
+
const auth = this.parent.authorization;
|
|
167
|
+
return generateAgent(auth.authorization, this.parent.chatId, this.id, messages, "accessCode" in auth
|
|
168
|
+
? { accessCode: auth.accessCode, signal: opts?.signal, tools: opts?.tools }
|
|
169
|
+
: { ...(auth.group ? { group: { id: auth.group } } : {}), signal: opts?.signal, tools: opts?.tools });
|
|
170
|
+
}
|
|
171
|
+
toJSON() {
|
|
172
|
+
return { id: this.id, persona: this.persona };
|
|
173
|
+
}
|
|
174
|
+
static fromJSON(json, parent) {
|
|
175
|
+
const repository = typeof json?.repository === "number" && json.repository > 0 ? json.repository : null;
|
|
176
|
+
return new Agent(parent, String(json.id), json.persona ?? {}, repository);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
export class AntChatRoom extends Room {
|
|
180
|
+
constructor(endpoint, ws, authorization, id) {
|
|
181
|
+
super(endpoint, ws, authorization, id);
|
|
182
|
+
Object.defineProperty(this, "chatId", {
|
|
183
|
+
enumerable: true,
|
|
184
|
+
configurable: true,
|
|
185
|
+
writable: true,
|
|
186
|
+
value: void 0
|
|
187
|
+
});
|
|
188
|
+
Object.defineProperty(this, "name", {
|
|
189
|
+
enumerable: true,
|
|
190
|
+
configurable: true,
|
|
191
|
+
writable: true,
|
|
192
|
+
value: void 0
|
|
193
|
+
});
|
|
194
|
+
Object.defineProperty(this, "agents", {
|
|
195
|
+
enumerable: true,
|
|
196
|
+
configurable: true,
|
|
197
|
+
writable: true,
|
|
198
|
+
value: void 0
|
|
199
|
+
});
|
|
200
|
+
/** The owner's Termite repository personas start from, by id; null when it has none. */
|
|
201
|
+
Object.defineProperty(this, "repository", {
|
|
202
|
+
enumerable: true,
|
|
203
|
+
configurable: true,
|
|
204
|
+
writable: true,
|
|
205
|
+
value: void 0
|
|
206
|
+
});
|
|
207
|
+
/** Who owns the chat: `credentials:{id}` or `group:{id}`. */
|
|
208
|
+
Object.defineProperty(this, "owner", {
|
|
209
|
+
enumerable: true,
|
|
210
|
+
configurable: true,
|
|
211
|
+
writable: true,
|
|
212
|
+
value: void 0
|
|
213
|
+
});
|
|
214
|
+
Object.defineProperty(this, "repositoryWatchers", {
|
|
215
|
+
enumerable: true,
|
|
216
|
+
configurable: true,
|
|
217
|
+
writable: true,
|
|
218
|
+
value: new Set()
|
|
219
|
+
});
|
|
220
|
+
this.chatId = id;
|
|
221
|
+
this.name = "";
|
|
222
|
+
this.agents = new Map();
|
|
223
|
+
this.repository = null;
|
|
224
|
+
this.owner = "";
|
|
225
|
+
}
|
|
226
|
+
/** The group that owns the chat, whose repositories it takes; undefined for a person's. */
|
|
227
|
+
get group() {
|
|
228
|
+
const match = /^group:(\d+)$/.exec(this.owner);
|
|
229
|
+
return match ? { id: match[1] } : undefined;
|
|
230
|
+
}
|
|
231
|
+
/** Attach one of the owner's repositories, or null to detach. Personas' forks of the old one are deleted. */
|
|
232
|
+
async setRepository(repository) {
|
|
233
|
+
this.post({ event: "set-repository", repository: repository ? repository.id : null });
|
|
234
|
+
}
|
|
235
|
+
/** Hear about every repository change, from anyone in the room. Returns a function that stops listening. */
|
|
236
|
+
watchRepository(listener) {
|
|
237
|
+
this.repositoryWatchers.add(listener);
|
|
238
|
+
return () => { this.repositoryWatchers.delete(listener); };
|
|
239
|
+
}
|
|
240
|
+
applyRepository(raw) {
|
|
241
|
+
const repository = typeof raw === "number" && raw > 0 ? raw : null;
|
|
242
|
+
if (repository === this.repository)
|
|
243
|
+
return;
|
|
244
|
+
this.repository = repository;
|
|
245
|
+
for (const listener of this.repositoryWatchers)
|
|
246
|
+
listener(repository);
|
|
247
|
+
}
|
|
248
|
+
hydrate(state) {
|
|
249
|
+
this.chatId = state?.id ?? this.chatId;
|
|
250
|
+
if (typeof state?.name === "string")
|
|
251
|
+
this.name = state.name;
|
|
252
|
+
if (typeof state?.owner === "string")
|
|
253
|
+
this.owner = state.owner;
|
|
254
|
+
this.applyRepository(state?.repository);
|
|
255
|
+
this.agents.clear();
|
|
256
|
+
for (const a of state?.agents ?? []) {
|
|
257
|
+
const agent = Agent.fromJSON(a, this);
|
|
258
|
+
this.agents.set(agent.id, agent);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
handleEvent(packet) {
|
|
262
|
+
switch (packet?.event) {
|
|
263
|
+
case "upsert-agent": {
|
|
264
|
+
const agent = Agent.fromJSON(packet.data?.agent ?? packet.agent, this);
|
|
265
|
+
this.agents.set(agent.id, agent);
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
case "remove-agent": {
|
|
269
|
+
const id = String(packet.data?.id ?? packet.id ?? "");
|
|
270
|
+
if (id)
|
|
271
|
+
this.agents.delete(id);
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
case "update-chat": {
|
|
275
|
+
const chat = packet.data?.chat ?? packet.chat;
|
|
276
|
+
if (typeof chat?.name === "string")
|
|
277
|
+
this.name = chat.name;
|
|
278
|
+
if (chat && "repository" in chat)
|
|
279
|
+
this.applyRepository(chat.repository);
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
default:
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
upsertAgent(identity) {
|
|
287
|
+
const preferred = String(identity?.id ?? "").trim();
|
|
288
|
+
let id = preferred && !this.agents.has(preferred) ? preferred : "";
|
|
289
|
+
if (!id) {
|
|
290
|
+
let n = 0;
|
|
291
|
+
while (this.agents.has(String(n)))
|
|
292
|
+
n++;
|
|
293
|
+
id = String(n);
|
|
294
|
+
}
|
|
295
|
+
const agent = new Agent(this, id, identity);
|
|
296
|
+
this.agents.set(agent.id, agent);
|
|
297
|
+
this.post({ event: "upsert-agent", agent: agent.toJSON() });
|
|
298
|
+
return agent;
|
|
299
|
+
}
|
|
300
|
+
removeAgent(id) {
|
|
301
|
+
this.agents.delete(id);
|
|
302
|
+
this.post({ event: "remove-agent", id });
|
|
303
|
+
}
|
|
304
|
+
/** Lazily mint/open the architect Parrot ChatRoom. */
|
|
305
|
+
async getChat(hooks) {
|
|
306
|
+
const auth = this.authorization;
|
|
307
|
+
const response = await fetch(`${Ant.getURL()}/chat/ensure`, {
|
|
308
|
+
method: "POST",
|
|
309
|
+
headers: {
|
|
310
|
+
"Content-Type": "application/json",
|
|
311
|
+
Authorization: "email" in auth.authorization ? `Token ${auth.authorization.token}` : `ApiKey ${auth.authorization.id}`,
|
|
312
|
+
...("accessCode" in auth
|
|
313
|
+
? { "Access-Code": auth.accessCode }
|
|
314
|
+
: { Chat: this.chatId, ...(auth.group ? { Group: auth.group } : {}) }),
|
|
315
|
+
},
|
|
316
|
+
body: JSON.stringify({}),
|
|
317
|
+
});
|
|
318
|
+
if (!response.ok)
|
|
319
|
+
throw new Error(await response.text());
|
|
320
|
+
const { chatId, reference, code } = await response.json();
|
|
321
|
+
if (!chatId || !code)
|
|
322
|
+
throw new Error("Invalid ensure response.");
|
|
323
|
+
const guest = "accessCode" in this.authorization;
|
|
324
|
+
const target = guest
|
|
325
|
+
? { code }
|
|
326
|
+
: { reference: reference || `parrot:chat:${chatId}` };
|
|
327
|
+
const { room, self } = await connectRoom(this.authorization.authorization, target, hooks ?? {});
|
|
328
|
+
return { room, self, chatId };
|
|
329
|
+
}
|
|
330
|
+
static async connect(authorization, target, opts) {
|
|
331
|
+
const server = opts?.server ?? target.id;
|
|
332
|
+
let room;
|
|
333
|
+
const user = opts?.hooks ?? {};
|
|
334
|
+
const self = await connectServer({ authorization, server, ...(opts?.group ? { group: opts.group.id } : {}) }, `${Ant.getURL()}/chat`, {
|
|
335
|
+
...user,
|
|
336
|
+
createRoom: (endpoint, ws, a, id, state) => {
|
|
337
|
+
room = (user.createRoom?.(endpoint, ws, a, id, state)
|
|
338
|
+
?? new AntChatRoom(endpoint, ws, a, id));
|
|
339
|
+
return room;
|
|
340
|
+
},
|
|
341
|
+
onConnection: (r, state) => {
|
|
342
|
+
r.hydrate(state);
|
|
343
|
+
user.onConnection?.(r, state);
|
|
344
|
+
},
|
|
345
|
+
onEvent: (r, packet) => {
|
|
346
|
+
const handled = r.handleEvent(packet);
|
|
347
|
+
return user.onEvent?.(r, packet) ?? Boolean(handled);
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
return { room, self };
|
|
351
|
+
}
|
|
352
|
+
static async join(authorization, target, hooks) {
|
|
353
|
+
let room;
|
|
354
|
+
const user = hooks ?? {};
|
|
355
|
+
const self = await connectServer({ authorization, accessCode: target.accessCode }, `${Ant.getURL()}/chat`, {
|
|
356
|
+
...user,
|
|
357
|
+
createRoom: (endpoint, ws, a, id, state) => {
|
|
358
|
+
room = (user.createRoom?.(endpoint, ws, a, id, state)
|
|
359
|
+
?? new AntChatRoom(endpoint, ws, a, id));
|
|
360
|
+
return room;
|
|
361
|
+
},
|
|
362
|
+
onConnection: (r, state) => {
|
|
363
|
+
r.hydrate(state);
|
|
364
|
+
user.onConnection?.(r, state);
|
|
365
|
+
},
|
|
366
|
+
onEvent: (r, packet) => {
|
|
367
|
+
const handled = r.handleEvent(packet);
|
|
368
|
+
return user.onEvent?.(r, packet) ?? Boolean(handled);
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
return { room, self };
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/** Write a standalone fact onto this agent's Parrot graph (`ant/chats/…/memory`). */
|
|
375
|
+
export async function observeAgent(authorization, chatId, agentId, content, opts) {
|
|
376
|
+
const response = await fetch(`${Ant.getURL()}/chat/agent/observe`, {
|
|
377
|
+
method: "POST",
|
|
378
|
+
headers: {
|
|
379
|
+
"Content-Type": "application/json",
|
|
380
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
381
|
+
...(opts?.accessCode
|
|
382
|
+
? { "Access-Code": opts.accessCode }
|
|
383
|
+
: { Chat: chatId, ...(opts?.group ? { Group: opts.group.id } : {}) }),
|
|
384
|
+
},
|
|
385
|
+
body: JSON.stringify({ agentId, content }),
|
|
386
|
+
});
|
|
387
|
+
if (!response.ok)
|
|
388
|
+
throw new Error(await response.text());
|
|
389
|
+
return await response.json();
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Stream a persona reply for one chat agent (memories under ant/chats/…), with
|
|
393
|
+
* `tool` and `tool-result` events for its own tools. `tools` are answered here
|
|
394
|
+
* when the persona calls them, like `use_desktop` asking for a computer.
|
|
395
|
+
*/
|
|
396
|
+
export async function* generateAgent(authorization, chatId, agentId, messages, opts) {
|
|
397
|
+
const wsURL = Ant.getURL().replace("https://", "wss://").replace("http://", "ws://");
|
|
398
|
+
const authHeaders = {
|
|
399
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
400
|
+
...(opts?.accessCode
|
|
401
|
+
? { "Access-Code": opts.accessCode }
|
|
402
|
+
: { Chat: chatId, ...(opts?.group ? { Group: opts.group.id } : {}) }),
|
|
403
|
+
};
|
|
404
|
+
const ws = new IsoWebSocket(`${wsURL}/chat/agent/generate?authorization=${encodeURIComponent(JSON.stringify(authHeaders))}`);
|
|
405
|
+
const onAbort = () => { try {
|
|
406
|
+
ws.close();
|
|
407
|
+
}
|
|
408
|
+
catch { /* noop */ } };
|
|
409
|
+
if (opts?.signal) {
|
|
410
|
+
if (opts.signal.aborted)
|
|
411
|
+
onAbort();
|
|
412
|
+
else
|
|
413
|
+
opts.signal.addEventListener("abort", onAbort);
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
await new Promise((resolve, reject) => {
|
|
417
|
+
ws.onopen = () => resolve();
|
|
418
|
+
ws.onerror = () => reject(new Error("WebSocket connection failed"));
|
|
419
|
+
});
|
|
420
|
+
if (opts?.signal?.aborted)
|
|
421
|
+
return;
|
|
422
|
+
const serializedMessages = await serializeMessages(authorization, messages);
|
|
423
|
+
const clientTools = opts?.tools ?? [];
|
|
424
|
+
ws.send(JSON.stringify({ event: "generate", agentId, messages: serializedMessages, tools: clientTools.map((t) => t.function) }));
|
|
425
|
+
ws.send(JSON.stringify({ event: "start" }));
|
|
426
|
+
const eventQueue = [];
|
|
427
|
+
let resolveNext;
|
|
428
|
+
let done = false;
|
|
429
|
+
ws.onmessage = (msg) => {
|
|
430
|
+
try {
|
|
431
|
+
const event = JSON.parse(String(msg.data));
|
|
432
|
+
if (event?.event === "tool_call") {
|
|
433
|
+
answerClientTool(ws, clientTools, event);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
eventQueue.push(event);
|
|
437
|
+
resolveNext?.();
|
|
438
|
+
}
|
|
439
|
+
catch { /* ignore */ }
|
|
440
|
+
};
|
|
441
|
+
ws.onclose = () => { done = true; resolveNext?.(); };
|
|
442
|
+
ws.onerror = () => { done = true; resolveNext?.(); };
|
|
443
|
+
while (true) {
|
|
444
|
+
if (eventQueue.length > 0) {
|
|
445
|
+
const event = eventQueue.shift();
|
|
446
|
+
yield event;
|
|
447
|
+
if (event.event === "done" || event.event === "error")
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
else if (done) {
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
await new Promise((resolve) => { resolveNext = resolve; });
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
finally {
|
|
459
|
+
opts?.signal?.removeEventListener("abort", onAbort);
|
|
460
|
+
try {
|
|
461
|
+
ws.close();
|
|
462
|
+
}
|
|
463
|
+
catch { /* noop */ }
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
// Alias for plan naming.
|
|
467
|
+
export { AntChatRoom as ChatRoom };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CategoricalFormat, Format } from "../parrot/format.js";
|
|
2
|
+
import type { Stimuli } from "./graph.js";
|
|
3
|
+
export declare const X_ACTIONS: readonly ["Did not dwell", "Dwell", "Like", "Reply", "Repost", "Quote", "Photo expand", "Video open", "Video quality view", "Click", "Open link", "Quoted click", "Quoted video view", "Profile", "Follow", "Share", "Share via DM", "Copy link", "Not interested", "Mute", "Block", "Report"];
|
|
4
|
+
/** Phoenix default weights (param.rs). Copy link is the tile max. */
|
|
5
|
+
export declare const X_WEIGHTS: Record<string, number>;
|
|
6
|
+
export declare const LINKEDIN_ACTIONS: readonly ["Skip", "Click", "Long dwell", "Like", "Comment", "Share"];
|
|
7
|
+
/** Not official. Feed SR does not publish numbers. Share is the tile max. */
|
|
8
|
+
export declare const LINKEDIN_WEIGHTS: Record<string, number>;
|
|
9
|
+
export declare abstract class AntFormat extends CategoricalFormat {
|
|
10
|
+
abstract readonly weights: Record<string, number>;
|
|
11
|
+
maxWeight(): number;
|
|
12
|
+
/** Phoenix style: Σ w_a * (count_a / n). Signed. Empty bucket is NaN. */
|
|
13
|
+
score(counts: Record<string, number>): number;
|
|
14
|
+
static fromJSON(json: any): Format<any>;
|
|
15
|
+
static forKind(kind: Stimuli): AntFormat[];
|
|
16
|
+
}
|
|
17
|
+
export declare class XFormat extends AntFormat {
|
|
18
|
+
readonly weights: Record<string, number>;
|
|
19
|
+
constructor();
|
|
20
|
+
clone(): XFormat;
|
|
21
|
+
toJSON(): {
|
|
22
|
+
type: string;
|
|
23
|
+
weights: Record<string, number>;
|
|
24
|
+
instruction: string;
|
|
25
|
+
categories: string[];
|
|
26
|
+
answer: string | undefined;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export declare class LinkedInFormat extends AntFormat {
|
|
30
|
+
readonly weights: Record<string, number>;
|
|
31
|
+
constructor();
|
|
32
|
+
clone(): LinkedInFormat;
|
|
33
|
+
toJSON(): {
|
|
34
|
+
type: string;
|
|
35
|
+
weights: Record<string, number>;
|
|
36
|
+
instruction: string;
|
|
37
|
+
categories: string[];
|
|
38
|
+
answer: string | undefined;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { CategoricalFormat, Format } from "../parrot/format.js";
|
|
2
|
+
// Phoenix discrete heads from home-mixer/params/param.rs (product words).
|
|
3
|
+
// Field names in comments. Skip post_unexplored and continuous dwell seconds.
|
|
4
|
+
const X_INSTRUCTION = "You are a viewer on X For You. Phoenix predicts the probability of each action below. Pick the one action you would take. Favorite is Like. Copy link, reply, quote, and share via DM are the strong positive heads. Report, mute, block, and not interested are the negative heads. Did not dwell means you skip the card.";
|
|
5
|
+
export const X_ACTIONS = [
|
|
6
|
+
"Did not dwell",
|
|
7
|
+
"Dwell",
|
|
8
|
+
"Like",
|
|
9
|
+
"Reply",
|
|
10
|
+
"Repost",
|
|
11
|
+
"Quote",
|
|
12
|
+
"Photo expand",
|
|
13
|
+
"Video open",
|
|
14
|
+
"Video quality view",
|
|
15
|
+
"Click",
|
|
16
|
+
"Open link",
|
|
17
|
+
"Quoted click",
|
|
18
|
+
"Quoted video view",
|
|
19
|
+
"Profile",
|
|
20
|
+
"Follow",
|
|
21
|
+
"Share",
|
|
22
|
+
"Share via DM",
|
|
23
|
+
"Copy link",
|
|
24
|
+
"Not interested",
|
|
25
|
+
"Mute",
|
|
26
|
+
"Block",
|
|
27
|
+
"Report",
|
|
28
|
+
];
|
|
29
|
+
/** Phoenix default weights (param.rs). Copy link is the tile max. */
|
|
30
|
+
export const X_WEIGHTS = {
|
|
31
|
+
"Did not dwell": -0.02, // not_dwelled
|
|
32
|
+
"Dwell": 0.05, // dwell
|
|
33
|
+
"Like": 0.5, // favorite
|
|
34
|
+
"Reply": 5, // reply
|
|
35
|
+
"Repost": 1, // retweet
|
|
36
|
+
"Quote": 5, // quote
|
|
37
|
+
"Photo expand": 0.05, // photo_expand
|
|
38
|
+
"Video open": 0.07, // video_open
|
|
39
|
+
"Video quality view": 0, // vqv
|
|
40
|
+
"Click": 0.4, // click
|
|
41
|
+
"Open link": 0.2, // open_link
|
|
42
|
+
"Quoted click": 0.05, // quoted_click
|
|
43
|
+
"Quoted video view": 0, // quoted_vqv
|
|
44
|
+
"Profile": 0, // profile_click
|
|
45
|
+
"Follow": 4, // follow_author
|
|
46
|
+
"Share": 2, // share
|
|
47
|
+
"Share via DM": 5, // share_via_dm
|
|
48
|
+
"Copy link": 20, // share_via_copy_link
|
|
49
|
+
"Not interested": -43.2, // not_interested
|
|
50
|
+
"Mute": -58.8, // mute_author
|
|
51
|
+
"Block": -31.2, // block_author
|
|
52
|
+
"Report": -234, // report
|
|
53
|
+
};
|
|
54
|
+
const LINKEDIN_INSTRUCTION = "You are a member in the LinkedIn feed. Feed SR predicts skip, click, and long dwell on one tower and like, comment, and share on the other. Those scores are blended to rank the post. Pick the one action you would take. Long dwell means you stay past the type threshold. Skip means you scroll past. Weights below are ours, not LinkedIn production numbers.";
|
|
55
|
+
export const LINKEDIN_ACTIONS = [
|
|
56
|
+
"Skip",
|
|
57
|
+
"Click",
|
|
58
|
+
"Long dwell",
|
|
59
|
+
"Like",
|
|
60
|
+
"Comment",
|
|
61
|
+
"Share",
|
|
62
|
+
];
|
|
63
|
+
/** Not official. Feed SR does not publish numbers. Share is the tile max. */
|
|
64
|
+
export const LINKEDIN_WEIGHTS = {
|
|
65
|
+
"Skip": 0,
|
|
66
|
+
"Click": 1,
|
|
67
|
+
"Like": 1,
|
|
68
|
+
"Long dwell": 2,
|
|
69
|
+
"Comment": 2,
|
|
70
|
+
"Share": 3,
|
|
71
|
+
};
|
|
72
|
+
export class AntFormat extends CategoricalFormat {
|
|
73
|
+
maxWeight() {
|
|
74
|
+
return Math.max(0, ...Object.values(this.weights));
|
|
75
|
+
}
|
|
76
|
+
/** Phoenix style: Σ w_a * (count_a / n). Signed. Empty bucket is NaN. */
|
|
77
|
+
score(counts) {
|
|
78
|
+
let n = 0;
|
|
79
|
+
let sum = 0;
|
|
80
|
+
for (const [label, w] of Object.entries(this.weights)) {
|
|
81
|
+
const c = counts[label] ?? 0;
|
|
82
|
+
n += c;
|
|
83
|
+
sum += w * c;
|
|
84
|
+
}
|
|
85
|
+
return n === 0 ? Number.NaN : sum / n;
|
|
86
|
+
}
|
|
87
|
+
static fromJSON(json) {
|
|
88
|
+
if (json?.type === "x") {
|
|
89
|
+
const format = new XFormat();
|
|
90
|
+
if (json.answer != null)
|
|
91
|
+
format.answer(json.answer);
|
|
92
|
+
return format;
|
|
93
|
+
}
|
|
94
|
+
if (json?.type === "linkedin") {
|
|
95
|
+
const format = new LinkedInFormat();
|
|
96
|
+
if (json.answer != null)
|
|
97
|
+
format.answer(json.answer);
|
|
98
|
+
return format;
|
|
99
|
+
}
|
|
100
|
+
return Format.fromJSON(json);
|
|
101
|
+
}
|
|
102
|
+
static forKind(kind) {
|
|
103
|
+
if (kind === "x")
|
|
104
|
+
return [new XFormat()];
|
|
105
|
+
if (kind === "linkedin")
|
|
106
|
+
return [new LinkedInFormat()];
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export class XFormat extends AntFormat {
|
|
111
|
+
constructor() {
|
|
112
|
+
super(X_INSTRUCTION, [...X_ACTIONS]);
|
|
113
|
+
Object.defineProperty(this, "weights", {
|
|
114
|
+
enumerable: true,
|
|
115
|
+
configurable: true,
|
|
116
|
+
writable: true,
|
|
117
|
+
value: X_WEIGHTS
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
clone() {
|
|
121
|
+
return new XFormat();
|
|
122
|
+
}
|
|
123
|
+
toJSON() {
|
|
124
|
+
return { ...super.toJSON(), type: "x", weights: this.weights };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export class LinkedInFormat extends AntFormat {
|
|
128
|
+
constructor() {
|
|
129
|
+
super(LINKEDIN_INSTRUCTION, [...LINKEDIN_ACTIONS]);
|
|
130
|
+
Object.defineProperty(this, "weights", {
|
|
131
|
+
enumerable: true,
|
|
132
|
+
configurable: true,
|
|
133
|
+
writable: true,
|
|
134
|
+
value: LINKEDIN_WEIGHTS
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
clone() {
|
|
138
|
+
return new LinkedInFormat();
|
|
139
|
+
}
|
|
140
|
+
toJSON() {
|
|
141
|
+
return { ...super.toJSON(), type: "linkedin", weights: this.weights };
|
|
142
|
+
}
|
|
143
|
+
}
|