agent-relay-server 0.8.1 → 0.10.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 +12 -14
- package/package.json +18 -1
- package/public/index.html +979 -2575
- package/public/manifest.webmanifest +6 -6
- package/public/sw.js +16 -10
- package/recipes/code-review.yaml +26 -0
- package/recipes/debug.yaml +20 -0
- package/recipes/feature.yaml +26 -0
- package/recipes/refactor.yaml +20 -0
- package/recipes/test.yaml +20 -0
- package/runner/src/adapter.ts +69 -0
- package/runner/src/config.ts +144 -0
- package/scripts/orchestrator-spawn-smoke.ts +2 -9
- package/src/agent-spawn.ts +2 -94
- package/src/automations.ts +774 -0
- package/src/bus-outbox.ts +75 -0
- package/src/bus.ts +439 -0
- package/src/cli.ts +251 -5
- package/src/commands-db.ts +160 -0
- package/src/config.ts +2 -1
- package/src/connectors.ts +29 -9
- package/src/daemon.ts +1 -0
- package/src/db.ts +363 -36
- package/src/events.ts +33 -0
- package/src/index.ts +100 -5
- package/src/recipe-db.ts +163 -0
- package/src/recipe-loader.ts +100 -0
- package/src/recipe-runner.ts +206 -0
- package/src/recipe-validator.ts +85 -0
- package/src/routes.ts +661 -158
- package/src/security.ts +128 -2
- package/src/sse.ts +45 -28
- package/src/token-db.ts +96 -0
- package/src/types.ts +1 -488
- package/src/upgrade.ts +14 -28
- package/public/dashboard/actions.js +0 -819
- package/public/dashboard/api.js +0 -336
- package/public/dashboard/app.js +0 -34
- package/public/dashboard/charts.js +0 -128
- package/public/dashboard/computed.js +0 -693
- package/public/dashboard/constants.js +0 -28
- package/public/dashboard/display.js +0 -345
- package/public/dashboard/state.js +0 -129
- package/public/dashboard/utils.js +0 -207
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { HUMAN_AGENT_ID, AGENT_TYPE_ICONS, AGENT_TYPE_TITLES, WAITING_TASK_STATUSES } from "./constants.js";
|
|
2
|
-
|
|
3
|
-
export function indexAgents(agents) {
|
|
4
|
-
const byId = {};
|
|
5
|
-
for (const agent of agents) byId[agent.id] = agent;
|
|
6
|
-
return byId;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function upsertById(list, item) {
|
|
10
|
-
const idx = list.findIndex((existing) => existing.id === item.id);
|
|
11
|
-
if (idx >= 0) list.splice(idx, 1, item);
|
|
12
|
-
else list.push(item);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function toTimestamp(value) {
|
|
16
|
-
const ts = typeof value === "number" ? value : new Date(value || 0).getTime();
|
|
17
|
-
return Number.isFinite(ts) ? ts : 0;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function isBuiltInAgent(agent) {
|
|
21
|
-
return agent?.meta?.builtin === true || agent?.id === HUMAN_AGENT_ID || agent?.id === "system";
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function isChannelAgent(agent) {
|
|
25
|
-
return agent?.kind === "channel" || agent?.meta?.kind === "channel" || agent?.tags?.includes("channel");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function agentType(agent) {
|
|
29
|
-
if (agent?.id === HUMAN_AGENT_ID) return "user";
|
|
30
|
-
if (agent?.id === "system") return "system";
|
|
31
|
-
if (isChannelAgent(agent)) return "channel";
|
|
32
|
-
|
|
33
|
-
const values = [
|
|
34
|
-
...(agent?.tags || []),
|
|
35
|
-
agent?.meta?.provider,
|
|
36
|
-
agent?.meta?.client,
|
|
37
|
-
agent?.meta?.runtime,
|
|
38
|
-
agent?.meta?.agentType,
|
|
39
|
-
agent?.id,
|
|
40
|
-
agent?.name,
|
|
41
|
-
]
|
|
42
|
-
.filter((value) => typeof value === "string")
|
|
43
|
-
.map((value) => value.toLowerCase());
|
|
44
|
-
|
|
45
|
-
if (values.some((value) => value.includes("codex"))) return "codex";
|
|
46
|
-
if (values.some((value) => value.includes("claude"))) return "claude";
|
|
47
|
-
return "agent";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function agentTypeIcon(agent) {
|
|
51
|
-
return AGENT_TYPE_ICONS[agentType(agent)] || AGENT_TYPE_ICONS.agent;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function agentTypeTitle(agent) {
|
|
55
|
-
return AGENT_TYPE_TITLES[agentType(agent)] || AGENT_TYPE_TITLES.agent;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function isAgentStale(vm, agent) {
|
|
59
|
-
if (!agent?.lastSeen || agent.status === "offline") return false;
|
|
60
|
-
if (agent.id === "user" || agent.id === "system") return false;
|
|
61
|
-
const lastSeenMs = new Date(agent.lastSeen).getTime();
|
|
62
|
-
if (!Number.isFinite(lastSeenMs)) return false;
|
|
63
|
-
return (vm.now || Date.now()) - lastSeenMs > 60_000;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function agentSupportsControlActions(agent) {
|
|
67
|
-
return Boolean(agent && !isBuiltInAgent(agent) && !isChannelAgent(agent));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function visibleAgents(vm) {
|
|
71
|
-
const nonChannelAgents = vm.agents.filter((agent) => !isChannelAgent(agent));
|
|
72
|
-
return vm.showBuiltIns ? nonChannelAgents : nonChannelAgents.filter((agent) => !isBuiltInAgent(agent));
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function messageBody(msg) {
|
|
76
|
-
if (!msg) return "";
|
|
77
|
-
const payload = msg.payload || {};
|
|
78
|
-
const channelMessage = payload.message;
|
|
79
|
-
if (channelMessage && typeof channelMessage === "object" && typeof channelMessage.text === "string" && channelMessage.text.trim()) {
|
|
80
|
-
return channelMessage.text;
|
|
81
|
-
}
|
|
82
|
-
const interaction = payload.interaction;
|
|
83
|
-
if (interaction && typeof interaction === "object") {
|
|
84
|
-
const title = typeof interaction.title === "string" ? interaction.title.trim() : "";
|
|
85
|
-
const description = typeof interaction.description === "string" ? interaction.description.trim() : "";
|
|
86
|
-
if (title && description) return title + "\n" + description;
|
|
87
|
-
if (title) return title;
|
|
88
|
-
if (description) return description;
|
|
89
|
-
}
|
|
90
|
-
const reaction = payload.reaction;
|
|
91
|
-
if (reaction && typeof reaction === "object") {
|
|
92
|
-
const name = typeof reaction.name === "string" ? reaction.name : "";
|
|
93
|
-
const emoji = typeof reaction.emoji === "string" ? reaction.emoji : "";
|
|
94
|
-
const value = typeof reaction.value === "string" ? reaction.value : "";
|
|
95
|
-
return ["Reaction", emoji || name || value].filter(Boolean).join(": ");
|
|
96
|
-
}
|
|
97
|
-
const activity = payload.activity;
|
|
98
|
-
if (activity && typeof activity === "object") {
|
|
99
|
-
const kind = typeof activity.kind === "string" ? activity.kind : "activity";
|
|
100
|
-
const state = typeof activity.state === "string" ? activity.state : "";
|
|
101
|
-
return [kind, state].filter(Boolean).join(" ");
|
|
102
|
-
}
|
|
103
|
-
if (typeof payload.text === "string" && payload.text.trim()) return payload.text;
|
|
104
|
-
if (typeof payload.message === "string" && payload.message.trim()) return payload.message;
|
|
105
|
-
return msg.body || "";
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function messageLooksLikeQuestion(msg) {
|
|
109
|
-
return /\?/.test(`${msg.subject || ""}\n${messageBody(msg)}`);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function inboxPeer(msg) {
|
|
113
|
-
if (msg.from === HUMAN_AGENT_ID && msg.to) return msg.to;
|
|
114
|
-
if (msg.to === HUMAN_AGENT_ID && msg.from) return msg.from;
|
|
115
|
-
return "";
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function isHumanInboundMessage(msg) {
|
|
119
|
-
return msg.to === HUMAN_AGENT_ID && msg.from !== HUMAN_AGENT_ID;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function isUnreadHumanMessage(vm, peer, msg) {
|
|
123
|
-
if (!isHumanInboundMessage(msg)) return false;
|
|
124
|
-
if ((msg.readBy || []).includes(HUMAN_AGENT_ID)) return false;
|
|
125
|
-
return msg.id > readCursorForPeer(vm, peer);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function maxMessageId(messages, predicate) {
|
|
129
|
-
let max = 0;
|
|
130
|
-
for (const msg of messages) {
|
|
131
|
-
if (predicate(msg) && msg.id > max) max = msg.id;
|
|
132
|
-
}
|
|
133
|
-
return max;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export function readCursorForPeer(vm, peer) {
|
|
137
|
-
const value = Number(vm.inboxReadCursors?.[peer] || 0);
|
|
138
|
-
return Number.isFinite(value) ? value : 0;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function draftForPeer(vm, peer) {
|
|
142
|
-
return typeof vm.inboxDrafts?.[peer] === "string" ? vm.inboxDrafts[peer] : "";
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function isClaimableTaskWaiting(task) {
|
|
146
|
-
return WAITING_TASK_STATUSES.has(task.status) && !task.claimedBy;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function isClaimableMessageWaiting(msg) {
|
|
150
|
-
return Boolean(msg.claimable && !msg.claimedBy && !(msg.kind === "task" && Number.isSafeInteger(msg.payload?.taskId)));
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export function targetMatchesAgent(target, agent) {
|
|
154
|
-
if (!target || !agent) return false;
|
|
155
|
-
if (target === "broadcast" || target === agent.id) return true;
|
|
156
|
-
if (target.startsWith("tag:")) return (agent.tags || []).includes(target.slice(4));
|
|
157
|
-
if (target.startsWith("cap:")) return (agent.capabilities || []).includes(target.slice(4));
|
|
158
|
-
if (target.startsWith("label:")) return agent.label === target.slice(6);
|
|
159
|
-
return false;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function messageMatchesChannel(message, channel) {
|
|
163
|
-
if (!message || !channel) return false;
|
|
164
|
-
const channelKeys = [channel.id, channel.type, ...(channel.topicChannels || [])].filter(Boolean);
|
|
165
|
-
if (message.channel && channelKeys.includes(message.channel)) return true;
|
|
166
|
-
const payloadChannel = message.payload?.channel;
|
|
167
|
-
if (payloadChannel && typeof payloadChannel === "object") {
|
|
168
|
-
const payloadKeys = [payloadChannel.agentId, payloadChannel.provider, payloadChannel.accountId].filter(Boolean);
|
|
169
|
-
if (payloadKeys.includes(channel.id) || payloadKeys.includes(channel.type) || payloadKeys.includes(channel.accountId)) return true;
|
|
170
|
-
}
|
|
171
|
-
if (channel.agentId && (message.from === channel.agentId || message.to === channel.agentId)) return true;
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function activityItem(input) {
|
|
176
|
-
return {
|
|
177
|
-
id: input.id,
|
|
178
|
-
kind: input.kind || "state",
|
|
179
|
-
ts: Number(input.ts) || 0,
|
|
180
|
-
icon: input.icon || "ti-activity",
|
|
181
|
-
title: input.title || "Activity",
|
|
182
|
-
body: input.body || "",
|
|
183
|
-
meta: input.meta || "",
|
|
184
|
-
view: input.view || "",
|
|
185
|
-
peer: input.peer || "",
|
|
186
|
-
clientId: input.clientId || "",
|
|
187
|
-
messageId: input.messageId,
|
|
188
|
-
pairId: input.pairId,
|
|
189
|
-
taskId: input.taskId,
|
|
190
|
-
agentId: input.agentId,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export function emptyAttention() {
|
|
195
|
-
return {
|
|
196
|
-
unread: 0,
|
|
197
|
-
agentQuestion: false,
|
|
198
|
-
pendingPairInvite: false,
|
|
199
|
-
claimableTasks: 0,
|
|
200
|
-
total: 0,
|
|
201
|
-
score: 0,
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export function safeFilename(value) {
|
|
206
|
-
return String(value || "export").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "export";
|
|
207
|
-
}
|