heyhank 0.1.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 +40 -0
- package/bin/cli.ts +168 -0
- package/bin/ctl.ts +528 -0
- package/bin/generate-token.ts +28 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/AgentsPage-BPhirnCe.js +7 -0
- package/dist/assets/AssistantPage-DJ-cMQfb.js +1 -0
- package/dist/assets/CronManager-DDbz-yiT.js +1 -0
- package/dist/assets/HelpPage-DMfkzERp.js +1 -0
- package/dist/assets/IntegrationsPage-CrOitCmJ.js +1 -0
- package/dist/assets/MediaPage-CE5rdvkC.js +1 -0
- package/dist/assets/PlatformDashboard-Do6F0O2p.js +1 -0
- package/dist/assets/Playground-Fc5cdc5p.js +109 -0
- package/dist/assets/ProcessPanel-CslEiZkI.js +2 -0
- package/dist/assets/PromptsPage-D2EhsdNO.js +4 -0
- package/dist/assets/RunsPage-C5BZF5Rx.js +1 -0
- package/dist/assets/SandboxManager-a1AVI5q2.js +8 -0
- package/dist/assets/SettingsPage-DirhjQrJ.js +51 -0
- package/dist/assets/SocialMediaPage-DBuM28vD.js +1 -0
- package/dist/assets/TailscalePage-CHiFhZXF.js +1 -0
- package/dist/assets/TelephonyPage-x0VV0fOo.js +1 -0
- package/dist/assets/TerminalPage-Drwyrnfd.js +1 -0
- package/dist/assets/gemini-audio-t-TSU-To.js +17 -0
- package/dist/assets/gemini-live-client-C7rqAW7G.js +166 -0
- package/dist/assets/index-C8M_PUmX.css +32 -0
- package/dist/assets/index-CEqZnThB.js +204 -0
- package/dist/assets/sw-register-LSSpj6RU.js +1 -0
- package/dist/assets/time-ago-B6r_l9u1.js +1 -0
- package/dist/assets/workbox-window.prod.es5-BIl4cyR9.js +2 -0
- package/dist/favicon-32-original.png +0 -0
- package/dist/favicon-32.png +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/favicon.svg +8 -0
- package/dist/fonts/MesloLGSNerdFontMono-Bold.woff2 +0 -0
- package/dist/fonts/MesloLGSNerdFontMono-Regular.woff2 +0 -0
- package/dist/heyhank-mascot-poster.png +0 -0
- package/dist/heyhank-mascot.mp4 +0 -0
- package/dist/heyhank-mascot.webm +0 -0
- package/dist/icon-192-original.png +0 -0
- package/dist/icon-192.png +0 -0
- package/dist/icon-512-original.png +0 -0
- package/dist/icon-512.png +0 -0
- package/dist/index.html +21 -0
- package/dist/logo-192.png +0 -0
- package/dist/logo-512.png +0 -0
- package/dist/logo-codex.svg +14 -0
- package/dist/logo-docker.svg +4 -0
- package/dist/logo-original.png +0 -0
- package/dist/logo.png +0 -0
- package/dist/logo.svg +14 -0
- package/dist/manifest.json +24 -0
- package/dist/push-sw.js +34 -0
- package/dist/sw.js +1 -0
- package/dist/workbox-d2a0910a.js +1 -0
- package/package.json +109 -0
- package/server/agent-cron-migrator.ts +85 -0
- package/server/agent-executor.ts +357 -0
- package/server/agent-store.ts +185 -0
- package/server/agent-timeout.ts +107 -0
- package/server/agent-types.ts +122 -0
- package/server/ai-validation-settings.ts +37 -0
- package/server/ai-validator.ts +181 -0
- package/server/anthropic-provider-migration.ts +48 -0
- package/server/assistant-store.ts +272 -0
- package/server/auth-manager.ts +150 -0
- package/server/auto-approve.ts +153 -0
- package/server/auto-namer.ts +36 -0
- package/server/backend-adapter.ts +54 -0
- package/server/cache-headers.ts +61 -0
- package/server/calendar-service.ts +434 -0
- package/server/claude-adapter.ts +889 -0
- package/server/claude-container-auth.ts +30 -0
- package/server/claude-session-discovery.ts +157 -0
- package/server/claude-session-history.ts +410 -0
- package/server/cli-launcher.ts +1303 -0
- package/server/codex-adapter.ts +3027 -0
- package/server/codex-container-auth.ts +24 -0
- package/server/codex-home.ts +27 -0
- package/server/codex-ws-proxy.cjs +226 -0
- package/server/commands-discovery.ts +81 -0
- package/server/constants.ts +7 -0
- package/server/container-manager.ts +1053 -0
- package/server/cost-tracker.ts +222 -0
- package/server/cron-scheduler.ts +243 -0
- package/server/cron-store.ts +148 -0
- package/server/cron-types.ts +63 -0
- package/server/email-service.ts +354 -0
- package/server/env-manager.ts +161 -0
- package/server/event-bus-types.ts +75 -0
- package/server/event-bus.ts +124 -0
- package/server/execution-store.ts +170 -0
- package/server/federation/node-connection.ts +190 -0
- package/server/federation/node-manager.ts +366 -0
- package/server/federation/node-store.ts +86 -0
- package/server/federation/node-types.ts +121 -0
- package/server/fs-utils.ts +15 -0
- package/server/git-utils.ts +421 -0
- package/server/github-pr.ts +379 -0
- package/server/google-media.ts +342 -0
- package/server/image-pull-manager.ts +279 -0
- package/server/index.ts +491 -0
- package/server/internal-ai.ts +237 -0
- package/server/kill-switch.ts +99 -0
- package/server/llm-providers.ts +342 -0
- package/server/logger.ts +259 -0
- package/server/mcp-registry.ts +401 -0
- package/server/message-bus.ts +271 -0
- package/server/message-delivery.ts +128 -0
- package/server/metrics-collector.ts +350 -0
- package/server/metrics-types.ts +108 -0
- package/server/middleware/managed-auth.ts +195 -0
- package/server/novnc-proxy.ts +99 -0
- package/server/path-resolver.ts +186 -0
- package/server/paths.ts +13 -0
- package/server/pr-poller.ts +162 -0
- package/server/prompt-manager.ts +211 -0
- package/server/protocol/claude-upstream/README.md +19 -0
- package/server/protocol/claude-upstream/sdk.d.ts.txt +1943 -0
- package/server/protocol/codex-upstream/ClientNotification.ts.txt +5 -0
- package/server/protocol/codex-upstream/ClientRequest.ts.txt +60 -0
- package/server/protocol/codex-upstream/README.md +18 -0
- package/server/protocol/codex-upstream/ServerNotification.ts.txt +41 -0
- package/server/protocol/codex-upstream/ServerRequest.ts.txt +16 -0
- package/server/protocol/codex-upstream/v2/DynamicToolCallParams.ts.txt +6 -0
- package/server/protocol/codex-upstream/v2/DynamicToolCallResponse.ts.txt +6 -0
- package/server/protocol-monitor.ts +50 -0
- package/server/provider-manager.ts +111 -0
- package/server/provider-registry.ts +393 -0
- package/server/push-notifications.ts +221 -0
- package/server/recorder.ts +374 -0
- package/server/recording-hub/compat-validator.ts +284 -0
- package/server/recording-hub/diagnostics.ts +299 -0
- package/server/recording-hub/hub-config.ts +19 -0
- package/server/recording-hub/hub-routes.ts +236 -0
- package/server/recording-hub/hub-store.ts +265 -0
- package/server/recording-hub/replay-adapter.ts +207 -0
- package/server/relay-client.ts +320 -0
- package/server/reminder-scheduler.ts +38 -0
- package/server/replay.ts +78 -0
- package/server/routes/agent-routes.ts +264 -0
- package/server/routes/assistant-routes.ts +90 -0
- package/server/routes/cron-routes.ts +103 -0
- package/server/routes/env-routes.ts +95 -0
- package/server/routes/federation-routes.ts +76 -0
- package/server/routes/fs-routes.ts +622 -0
- package/server/routes/git-routes.ts +97 -0
- package/server/routes/llm-routes.ts +166 -0
- package/server/routes/media-routes.ts +135 -0
- package/server/routes/metrics-routes.ts +13 -0
- package/server/routes/platform-routes.ts +1379 -0
- package/server/routes/prompt-routes.ts +67 -0
- package/server/routes/provider-routes.ts +109 -0
- package/server/routes/sandbox-routes.ts +127 -0
- package/server/routes/settings-routes.ts +285 -0
- package/server/routes/skills-routes.ts +100 -0
- package/server/routes/socialmedia-routes.ts +208 -0
- package/server/routes/system-routes.ts +228 -0
- package/server/routes/tailscale-routes.ts +22 -0
- package/server/routes/telephony-routes.ts +259 -0
- package/server/routes.ts +1379 -0
- package/server/sandbox-manager.ts +168 -0
- package/server/service.ts +718 -0
- package/server/session-creation-service.ts +457 -0
- package/server/session-git-info.ts +104 -0
- package/server/session-names.ts +67 -0
- package/server/session-orchestrator.ts +824 -0
- package/server/session-state-machine.ts +207 -0
- package/server/session-store.ts +146 -0
- package/server/session-types.ts +511 -0
- package/server/settings-manager.ts +149 -0
- package/server/shared-context.ts +157 -0
- package/server/socialmedia/adapter.ts +15 -0
- package/server/socialmedia/adapters/ayrshare-adapter.ts +169 -0
- package/server/socialmedia/adapters/buffer-adapter.ts +299 -0
- package/server/socialmedia/adapters/postiz-adapter.ts +298 -0
- package/server/socialmedia/manager.ts +227 -0
- package/server/socialmedia/store.ts +98 -0
- package/server/socialmedia/types.ts +89 -0
- package/server/tailscale-manager.ts +451 -0
- package/server/telephony/audio-bridge.ts +331 -0
- package/server/telephony/call-manager.ts +457 -0
- package/server/telephony/call-types.ts +108 -0
- package/server/telephony/telephony-store.ts +119 -0
- package/server/terminal-manager.ts +240 -0
- package/server/update-checker.ts +192 -0
- package/server/usage-limits.ts +225 -0
- package/server/web-push.d.ts +51 -0
- package/server/worktree-tracker.ts +84 -0
- package/server/ws-auth.ts +41 -0
- package/server/ws-bridge-browser-ingest.ts +72 -0
- package/server/ws-bridge-browser.ts +112 -0
- package/server/ws-bridge-cli-ingest.ts +81 -0
- package/server/ws-bridge-codex.ts +266 -0
- package/server/ws-bridge-controls.ts +20 -0
- package/server/ws-bridge-persist.ts +66 -0
- package/server/ws-bridge-publish.ts +79 -0
- package/server/ws-bridge-replay.ts +61 -0
- package/server/ws-bridge-types.ts +121 -0
- package/server/ws-bridge.ts +1240 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// ─── Inter-Agent Message Bus ─────────────────────────────────────────────────
|
|
2
|
+
// Ported from AgentManager/src/messages.ts — adapted for HeyHank/Bun
|
|
3
|
+
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import {
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { join, dirname } from "node:path";
|
|
13
|
+
import { HEYHANK_HOME } from "./paths.js";
|
|
14
|
+
|
|
15
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
export type MessageType =
|
|
18
|
+
| "task"
|
|
19
|
+
| "result"
|
|
20
|
+
| "question"
|
|
21
|
+
| "info"
|
|
22
|
+
| "status"
|
|
23
|
+
| "interrupt";
|
|
24
|
+
|
|
25
|
+
export const VALID_MESSAGE_TYPES: MessageType[] = [
|
|
26
|
+
"task",
|
|
27
|
+
"result",
|
|
28
|
+
"question",
|
|
29
|
+
"info",
|
|
30
|
+
"status",
|
|
31
|
+
"interrupt",
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
export interface AgentMessage {
|
|
35
|
+
id: string;
|
|
36
|
+
from: string;
|
|
37
|
+
fromName?: string;
|
|
38
|
+
to?: string;
|
|
39
|
+
channel?: string;
|
|
40
|
+
type: MessageType;
|
|
41
|
+
content: string;
|
|
42
|
+
metadata?: Record<string, unknown>;
|
|
43
|
+
createdAt: string;
|
|
44
|
+
readBy: string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface MessageQuery {
|
|
48
|
+
to?: string;
|
|
49
|
+
from?: string;
|
|
50
|
+
channel?: string;
|
|
51
|
+
type?: MessageType;
|
|
52
|
+
unreadBy?: string;
|
|
53
|
+
since?: string;
|
|
54
|
+
limit?: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type MessageListener = (msg: AgentMessage) => void;
|
|
58
|
+
|
|
59
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
const MAX_MESSAGES = 500;
|
|
62
|
+
const MESSAGES_FILE = join(HEYHANK_HOME, "messages.jsonl");
|
|
63
|
+
const DEBOUNCE_MS = 500;
|
|
64
|
+
|
|
65
|
+
// ─── MessageBus ──────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
export class MessageBus {
|
|
68
|
+
private messages: AgentMessage[] = [];
|
|
69
|
+
private listeners = new Set<MessageListener>();
|
|
70
|
+
private persistTimer: ReturnType<typeof setTimeout> | null = null;
|
|
71
|
+
private dirty = false;
|
|
72
|
+
|
|
73
|
+
constructor() {
|
|
74
|
+
this.restore();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Post a new message to the bus. */
|
|
78
|
+
post(opts: {
|
|
79
|
+
from: string;
|
|
80
|
+
fromName?: string;
|
|
81
|
+
to?: string;
|
|
82
|
+
channel?: string;
|
|
83
|
+
type: MessageType;
|
|
84
|
+
content: string;
|
|
85
|
+
metadata?: Record<string, unknown>;
|
|
86
|
+
}): AgentMessage {
|
|
87
|
+
const msg: AgentMessage = {
|
|
88
|
+
id: randomUUID(),
|
|
89
|
+
from: opts.from,
|
|
90
|
+
fromName: opts.fromName,
|
|
91
|
+
to: opts.to,
|
|
92
|
+
channel: opts.channel,
|
|
93
|
+
type: opts.type,
|
|
94
|
+
content: opts.content,
|
|
95
|
+
metadata: opts.metadata,
|
|
96
|
+
createdAt: new Date().toISOString(),
|
|
97
|
+
readBy: [],
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
this.messages.push(msg);
|
|
101
|
+
this.trim();
|
|
102
|
+
this.schedulePersist();
|
|
103
|
+
|
|
104
|
+
// Notify listeners
|
|
105
|
+
for (const listener of this.listeners) {
|
|
106
|
+
try {
|
|
107
|
+
listener(msg);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.error("[message-bus] Listener error:", err);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return msg;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Query messages with optional filters. */
|
|
117
|
+
query(opts?: MessageQuery): AgentMessage[] {
|
|
118
|
+
let result = [...this.messages];
|
|
119
|
+
if (opts?.to) {
|
|
120
|
+
result = result.filter(
|
|
121
|
+
(m) => m.to === opts.to || m.to === undefined,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (opts?.from) {
|
|
125
|
+
result = result.filter((m) => m.from === opts.from);
|
|
126
|
+
}
|
|
127
|
+
if (opts?.channel) {
|
|
128
|
+
result = result.filter((m) => m.channel === opts.channel);
|
|
129
|
+
}
|
|
130
|
+
if (opts?.type) {
|
|
131
|
+
result = result.filter((m) => m.type === opts.type);
|
|
132
|
+
}
|
|
133
|
+
if (opts?.unreadBy) {
|
|
134
|
+
const agentId = opts.unreadBy;
|
|
135
|
+
result = result.filter((m) => !m.readBy.includes(agentId));
|
|
136
|
+
}
|
|
137
|
+
if (opts?.since) {
|
|
138
|
+
result = result.filter((m) => m.createdAt > opts.since!);
|
|
139
|
+
}
|
|
140
|
+
const limit = opts?.limit ?? 100;
|
|
141
|
+
return result.slice(-limit);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Mark a message as read by an agent. */
|
|
145
|
+
markRead(messageId: string, agentId: string): void {
|
|
146
|
+
const msg = this.messages.find((m) => m.id === messageId);
|
|
147
|
+
if (msg && !msg.readBy.includes(agentId)) {
|
|
148
|
+
msg.readBy.push(agentId);
|
|
149
|
+
this.schedulePersist();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Mark all messages targeted at an agent as read. */
|
|
154
|
+
markAllRead(agentId: string): void {
|
|
155
|
+
let changed = false;
|
|
156
|
+
for (const msg of this.messages) {
|
|
157
|
+
if (
|
|
158
|
+
(msg.to === agentId || msg.to === undefined) &&
|
|
159
|
+
!msg.readBy.includes(agentId)
|
|
160
|
+
) {
|
|
161
|
+
msg.readBy.push(agentId);
|
|
162
|
+
changed = true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (changed) this.schedulePersist();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Get count of unread messages for an agent. */
|
|
169
|
+
unreadCount(agentId: string): number {
|
|
170
|
+
return this.messages.filter(
|
|
171
|
+
(m) =>
|
|
172
|
+
(m.to === agentId || m.to === undefined) &&
|
|
173
|
+
!m.readBy.includes(agentId),
|
|
174
|
+
).length;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Delete a single message. */
|
|
178
|
+
deleteMessage(id: string): boolean {
|
|
179
|
+
const idx = this.messages.findIndex((m) => m.id === id);
|
|
180
|
+
if (idx === -1) return false;
|
|
181
|
+
this.messages.splice(idx, 1);
|
|
182
|
+
this.schedulePersist();
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Clear all messages. */
|
|
187
|
+
clearAll(): void {
|
|
188
|
+
this.messages = [];
|
|
189
|
+
this.persistNow();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Remove all messages to/from a specific agent. */
|
|
193
|
+
cleanupForAgent(agentId: string): void {
|
|
194
|
+
const before = this.messages.length;
|
|
195
|
+
this.messages = this.messages.filter(
|
|
196
|
+
(m) => m.from !== agentId && m.to !== agentId,
|
|
197
|
+
);
|
|
198
|
+
if (this.messages.length !== before) {
|
|
199
|
+
this.schedulePersist();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Subscribe to new messages. Returns unsubscribe function. */
|
|
204
|
+
subscribe(listener: MessageListener): () => void {
|
|
205
|
+
this.listeners.add(listener);
|
|
206
|
+
return () => {
|
|
207
|
+
this.listeners.delete(listener);
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Get all messages (for debugging/admin). */
|
|
212
|
+
getAll(): AgentMessage[] {
|
|
213
|
+
return [...this.messages];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ─── Persistence ─────────────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
private trim(): void {
|
|
219
|
+
if (this.messages.length > MAX_MESSAGES) {
|
|
220
|
+
this.messages = this.messages.slice(-MAX_MESSAGES);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private schedulePersist(): void {
|
|
225
|
+
this.dirty = true;
|
|
226
|
+
if (this.persistTimer) return;
|
|
227
|
+
this.persistTimer = setTimeout(() => {
|
|
228
|
+
this.persistTimer = null;
|
|
229
|
+
if (this.dirty) this.persistNow();
|
|
230
|
+
}, DEBOUNCE_MS);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private persistNow(): void {
|
|
234
|
+
this.dirty = false;
|
|
235
|
+
try {
|
|
236
|
+
mkdirSync(dirname(MESSAGES_FILE), { recursive: true });
|
|
237
|
+
const data = this.messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
238
|
+
const tmpFile = MESSAGES_FILE + ".tmp";
|
|
239
|
+
writeFileSync(tmpFile, data, "utf-8");
|
|
240
|
+
renameSync(tmpFile, MESSAGES_FILE);
|
|
241
|
+
} catch (err) {
|
|
242
|
+
console.error("[message-bus] Failed to persist:", err);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private restore(): void {
|
|
247
|
+
try {
|
|
248
|
+
if (!existsSync(MESSAGES_FILE)) return;
|
|
249
|
+
const raw = readFileSync(MESSAGES_FILE, "utf-8");
|
|
250
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
251
|
+
const restored: AgentMessage[] = [];
|
|
252
|
+
for (const line of lines) {
|
|
253
|
+
try {
|
|
254
|
+
restored.push(JSON.parse(line));
|
|
255
|
+
} catch {
|
|
256
|
+
// Skip corrupt lines
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Keep only last MAX_MESSAGES
|
|
260
|
+
this.messages = restored.slice(-MAX_MESSAGES);
|
|
261
|
+
console.log(
|
|
262
|
+
`[message-bus] Restored ${this.messages.length} messages from disk`,
|
|
263
|
+
);
|
|
264
|
+
} catch (err) {
|
|
265
|
+
console.error("[message-bus] Failed to restore:", err);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Singleton instance */
|
|
271
|
+
export const messageBus = new MessageBus();
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// ─── Message Delivery System ─────────────────────────────────────────────────
|
|
2
|
+
// Auto-delivers inter-agent messages to idle agents
|
|
3
|
+
// Ported from AgentManager/src/message-delivery.ts
|
|
4
|
+
|
|
5
|
+
import { messageBus } from "./message-bus.js";
|
|
6
|
+
import type { AgentMessage } from "./message-bus.js";
|
|
7
|
+
import type { AgentExecutor } from "./agent-executor.js";
|
|
8
|
+
import type { WsBridge } from "./ws-bridge.js";
|
|
9
|
+
import { isKilled } from "./kill-switch.js";
|
|
10
|
+
|
|
11
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export interface MessageDeliveryOptions {
|
|
14
|
+
/** Delay before delivering queued messages (ms) */
|
|
15
|
+
deliverySettleMs?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ─── Delivery Prompt Formatting ──────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export function formatDeliveryPrompt(
|
|
21
|
+
fromName: string,
|
|
22
|
+
fromId: string,
|
|
23
|
+
content: string,
|
|
24
|
+
type: string,
|
|
25
|
+
): string {
|
|
26
|
+
if (type === "interrupt") {
|
|
27
|
+
return `[INTERRUPT from ${fromName}] ⚠️ Your current task has been interrupted. Read and act on this message immediately:
|
|
28
|
+
|
|
29
|
+
<message-content>
|
|
30
|
+
${content}
|
|
31
|
+
</message-content>
|
|
32
|
+
|
|
33
|
+
(Reply by sending a message to agent ID: ${fromId})`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return `[Message from ${fromName} (${fromId})] Type: ${type}
|
|
37
|
+
|
|
38
|
+
<message-content>
|
|
39
|
+
${content}
|
|
40
|
+
</message-content>
|
|
41
|
+
|
|
42
|
+
Process this message and respond appropriately. To reply, send a message to agent ID: ${fromId}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ─── Attach Auto-Delivery ────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Subscribe to the message bus and auto-deliver messages to target agent sessions.
|
|
49
|
+
* When a message arrives for a specific agent, injects it as a user message
|
|
50
|
+
* into that agent's active session.
|
|
51
|
+
*/
|
|
52
|
+
export function attachMessageDelivery(
|
|
53
|
+
agentExecutor: AgentExecutor,
|
|
54
|
+
wsBridge: WsBridge,
|
|
55
|
+
opts?: MessageDeliveryOptions,
|
|
56
|
+
): () => void {
|
|
57
|
+
const settleMs = opts?.deliverySettleMs ?? 2000;
|
|
58
|
+
|
|
59
|
+
const unsubscribe = messageBus.subscribe((msg: AgentMessage) => {
|
|
60
|
+
// Skip broadcasts (no specific target)
|
|
61
|
+
if (!msg.to) return;
|
|
62
|
+
|
|
63
|
+
// Skip status messages
|
|
64
|
+
if (msg.type === "status") return;
|
|
65
|
+
|
|
66
|
+
// Kill switch check
|
|
67
|
+
if (isKilled()) {
|
|
68
|
+
console.log(
|
|
69
|
+
`[message-delivery] Skipping delivery (kill switch active): ${msg.id}`,
|
|
70
|
+
);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Find the target agent's active session
|
|
75
|
+
const targetAgentId = msg.to;
|
|
76
|
+
|
|
77
|
+
// Delay delivery slightly to let agents settle
|
|
78
|
+
setTimeout(() => {
|
|
79
|
+
try {
|
|
80
|
+
deliverToAgent(targetAgentId, msg, wsBridge);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
console.error(
|
|
83
|
+
`[message-delivery] Failed to deliver to ${targetAgentId}:`,
|
|
84
|
+
err,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}, settleMs);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
console.log("[message-delivery] Auto-delivery attached");
|
|
91
|
+
return unsubscribe;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function deliverToAgent(
|
|
95
|
+
agentId: string,
|
|
96
|
+
msg: AgentMessage,
|
|
97
|
+
wsBridge: WsBridge,
|
|
98
|
+
): void {
|
|
99
|
+
// Find active sessions for this agent
|
|
100
|
+
const sessions = wsBridge.getSessionMemoryStats();
|
|
101
|
+
|
|
102
|
+
// Look for a session that belongs to this agent
|
|
103
|
+
// Agent sessions are tagged with agentId in their metadata
|
|
104
|
+
for (const session of sessions) {
|
|
105
|
+
// Try to inject the message
|
|
106
|
+
const prompt = formatDeliveryPrompt(
|
|
107
|
+
msg.fromName || msg.from,
|
|
108
|
+
msg.from,
|
|
109
|
+
msg.content,
|
|
110
|
+
msg.type,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
wsBridge.injectUserMessage(session.id, prompt);
|
|
115
|
+
messageBus.markRead(msg.id, agentId);
|
|
116
|
+
console.log(
|
|
117
|
+
`[message-delivery] Delivered ${msg.type} from ${msg.from} to ${agentId} (session ${session.id.slice(0, 8)})`,
|
|
118
|
+
);
|
|
119
|
+
return;
|
|
120
|
+
} catch {
|
|
121
|
+
// Session might not accept messages, try next
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log(
|
|
126
|
+
`[message-delivery] No active session for ${agentId}, message ${msg.id} queued`,
|
|
127
|
+
);
|
|
128
|
+
}
|