agent-relay 1.0.20 → 1.0.21
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/bridge/teams-config.d.ts +47 -0
- package/dist/bridge/teams-config.d.ts.map +1 -0
- package/dist/bridge/teams-config.js +100 -0
- package/dist/bridge/teams-config.js.map +1 -0
- package/dist/cli/index.js +53 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/daemon/auth.d.ts +94 -0
- package/dist/daemon/auth.d.ts.map +1 -0
- package/dist/daemon/auth.js +197 -0
- package/dist/daemon/auth.js.map +1 -0
- package/dist/daemon/connection.d.ts +2 -0
- package/dist/daemon/connection.d.ts.map +1 -1
- package/dist/daemon/connection.js +10 -2
- package/dist/daemon/connection.js.map +1 -1
- package/dist/daemon/router.d.ts +51 -1
- package/dist/daemon/router.d.ts.map +1 -1
- package/dist/daemon/router.js +178 -0
- package/dist/daemon/router.js.map +1 -1
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +23 -1
- package/dist/daemon/server.js.map +1 -1
- package/dist/dashboard/out/404.html +1 -1
- package/dist/dashboard/out/_next/static/chunks/app/page-49055e5d2b5e34ec.js +1 -0
- package/dist/dashboard/out/_next/static/css/50ed6996e3df7bdd.css +1 -0
- package/dist/dashboard/out/index.html +1 -1
- package/dist/dashboard/out/index.txt +2 -2
- package/dist/dashboard/out/metrics.html +2 -2
- package/dist/dashboard/out/metrics.txt +1 -1
- package/dist/dashboard-server/server.d.ts.map +1 -1
- package/dist/dashboard-server/server.js +13 -0
- package/dist/dashboard-server/server.js.map +1 -1
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/types.d.ts +143 -0
- package/dist/hooks/types.d.ts.map +1 -0
- package/dist/hooks/types.js +8 -0
- package/dist/hooks/types.js.map +1 -0
- package/dist/protocol/types.d.ts +28 -1
- package/dist/protocol/types.d.ts.map +1 -1
- package/dist/wrapper/client.d.ts +20 -1
- package/dist/wrapper/client.d.ts.map +1 -1
- package/dist/wrapper/client.js +39 -0
- package/dist/wrapper/client.js.map +1 -1
- package/dist/wrapper/parser.d.ts.map +1 -1
- package/dist/wrapper/parser.js +35 -6
- package/dist/wrapper/parser.js.map +1 -1
- package/dist/wrapper/pty-wrapper.d.ts +0 -6
- package/dist/wrapper/pty-wrapper.d.ts.map +1 -1
- package/dist/wrapper/pty-wrapper.js +0 -27
- package/dist/wrapper/pty-wrapper.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard/out/_next/static/chunks/app/page-28a7722e809c2573.js +0 -1
- package/dist/dashboard/out/_next/static/css/34bb8e70353694d8.css +0 -1
- /package/dist/dashboard/out/_next/static/{15pBupHgVjgmNaba43jnO → gZXwjIKGDKJ0hiTH-HMeJ}/_buildManifest.js +0 -0
- /package/dist/dashboard/out/_next/static/{15pBupHgVjgmNaba43jnO → gZXwjIKGDKJ0hiTH-HMeJ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication module for Agent Relay
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - SO_PEERCRED extraction for Unix socket peer credentials
|
|
6
|
+
* - Team/UID-based agent name validation
|
|
7
|
+
* - TLS configuration for network deployments
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
/**
|
|
13
|
+
* Default auth config (disabled for backward compatibility)
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_AUTH_CONFIG = {
|
|
16
|
+
enabled: false,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Config file locations (searched in order)
|
|
20
|
+
*/
|
|
21
|
+
const AUTH_CONFIG_PATHS = [
|
|
22
|
+
path.join(os.homedir(), '.agent-relay', 'auth.json'),
|
|
23
|
+
path.join(os.homedir(), '.config', 'agent-relay', 'auth.json'),
|
|
24
|
+
'/etc/agent-relay/auth.json',
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* Load auth config from file
|
|
28
|
+
*/
|
|
29
|
+
export function loadAuthConfig(configPath) {
|
|
30
|
+
const paths = configPath ? [configPath] : AUTH_CONFIG_PATHS;
|
|
31
|
+
for (const p of paths) {
|
|
32
|
+
if (fs.existsSync(p)) {
|
|
33
|
+
try {
|
|
34
|
+
const content = fs.readFileSync(p, 'utf-8');
|
|
35
|
+
const config = JSON.parse(content);
|
|
36
|
+
console.log(`[auth] Loaded config from ${p}`);
|
|
37
|
+
return { ...DEFAULT_AUTH_CONFIG, ...config };
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error(`[auth] Failed to parse ${p}:`, err);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return DEFAULT_AUTH_CONFIG;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Extract peer credentials from Unix socket using SO_PEERCRED
|
|
48
|
+
*
|
|
49
|
+
* Note: This only works on Linux. On macOS, use LOCAL_PEERCRED.
|
|
50
|
+
* On Windows, Unix sockets don't support peer credentials.
|
|
51
|
+
*/
|
|
52
|
+
export function getPeerCredentials(socket) {
|
|
53
|
+
try {
|
|
54
|
+
// Node.js doesn't expose SO_PEERCRED directly, but we can use the
|
|
55
|
+
// underlying file descriptor with a native binding or fall back to
|
|
56
|
+
// a best-effort approach using socket properties.
|
|
57
|
+
// For now, we use a platform-specific approach:
|
|
58
|
+
// On Linux, we need to use getsockopt with SO_PEERCRED
|
|
59
|
+
// This requires native bindings or a child process call
|
|
60
|
+
const fd = socket._handle?.fd;
|
|
61
|
+
if (fd === undefined || fd < 0) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
// Use synchronous exec to get credentials via /proc on Linux
|
|
65
|
+
if (process.platform === 'linux') {
|
|
66
|
+
return getPeerCredentialsLinux(fd);
|
|
67
|
+
}
|
|
68
|
+
else if (process.platform === 'darwin') {
|
|
69
|
+
return getPeerCredentialsMacOS(fd);
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
console.error('[auth] Failed to get peer credentials:', err);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get peer credentials on Linux using /proc filesystem
|
|
80
|
+
*
|
|
81
|
+
* Note: Proper SO_PEERCRED extraction requires native bindings (e.g., unix-dgram).
|
|
82
|
+
* This implementation falls back to current process credentials for safety.
|
|
83
|
+
* For multi-tenant isolation, consider using a native module.
|
|
84
|
+
*/
|
|
85
|
+
function getPeerCredentialsLinux(_fd) {
|
|
86
|
+
try {
|
|
87
|
+
// SO_PEERCRED requires native bindings which we don't have.
|
|
88
|
+
// Return current process credentials as a safe fallback.
|
|
89
|
+
// This doesn't provide multi-tenant isolation but avoids shell command risks.
|
|
90
|
+
return {
|
|
91
|
+
uid: process.getuid?.() ?? 0,
|
|
92
|
+
gid: process.getgid?.() ?? 0,
|
|
93
|
+
pid: process.pid,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get peer credentials on macOS using LOCAL_PEERCRED
|
|
102
|
+
*/
|
|
103
|
+
function getPeerCredentialsMacOS(_fd) {
|
|
104
|
+
// macOS uses LOCAL_PEERCRED which also requires native bindings
|
|
105
|
+
// Fall back to process credentials for now
|
|
106
|
+
return {
|
|
107
|
+
uid: process.getuid?.() ?? 0,
|
|
108
|
+
gid: process.getgid?.() ?? 0,
|
|
109
|
+
pid: process.pid,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Validate agent name against team configuration
|
|
114
|
+
*/
|
|
115
|
+
export function validateAgentName(agentName, credentials, config) {
|
|
116
|
+
// Auth disabled - allow all
|
|
117
|
+
if (!config.enabled) {
|
|
118
|
+
return { valid: true };
|
|
119
|
+
}
|
|
120
|
+
// No credentials available - check if we have a default team
|
|
121
|
+
if (!credentials) {
|
|
122
|
+
if (config.defaultTeam) {
|
|
123
|
+
const team = config.teams?.find(t => t.name === config.defaultTeam);
|
|
124
|
+
if (!team) {
|
|
125
|
+
return { valid: false, error: `Default team "${config.defaultTeam}" not found in config` };
|
|
126
|
+
}
|
|
127
|
+
if (team.allowAnyName) {
|
|
128
|
+
return { valid: true, team: team.name };
|
|
129
|
+
}
|
|
130
|
+
if (team.agentPrefix && !agentName.startsWith(team.agentPrefix)) {
|
|
131
|
+
return {
|
|
132
|
+
valid: false,
|
|
133
|
+
error: `Agent name must start with "${team.agentPrefix}" for team ${team.name}`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return { valid: true, team: team.name };
|
|
137
|
+
}
|
|
138
|
+
return { valid: false, error: 'No credentials available and no default team configured' };
|
|
139
|
+
}
|
|
140
|
+
// Find matching team by UID or GID
|
|
141
|
+
const matchingTeam = config.teams?.find(team => {
|
|
142
|
+
if (team.uids?.includes(credentials.uid))
|
|
143
|
+
return true;
|
|
144
|
+
if (team.gids?.includes(credentials.gid))
|
|
145
|
+
return true;
|
|
146
|
+
return false;
|
|
147
|
+
});
|
|
148
|
+
if (matchingTeam) {
|
|
149
|
+
// Check agent name prefix
|
|
150
|
+
if (matchingTeam.allowAnyName) {
|
|
151
|
+
return { valid: true, team: matchingTeam.name };
|
|
152
|
+
}
|
|
153
|
+
if (matchingTeam.agentPrefix && !agentName.startsWith(matchingTeam.agentPrefix)) {
|
|
154
|
+
return {
|
|
155
|
+
valid: false,
|
|
156
|
+
error: `Agent name must start with "${matchingTeam.agentPrefix}" for team ${matchingTeam.name}`,
|
|
157
|
+
team: matchingTeam.name,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return { valid: true, team: matchingTeam.name };
|
|
161
|
+
}
|
|
162
|
+
// No matching team - check default
|
|
163
|
+
if (config.defaultTeam) {
|
|
164
|
+
const defaultTeam = config.teams?.find(t => t.name === config.defaultTeam);
|
|
165
|
+
if (defaultTeam?.allowAnyName) {
|
|
166
|
+
return { valid: true, team: defaultTeam.name };
|
|
167
|
+
}
|
|
168
|
+
if (defaultTeam?.agentPrefix && !agentName.startsWith(defaultTeam.agentPrefix)) {
|
|
169
|
+
return {
|
|
170
|
+
valid: false,
|
|
171
|
+
error: `Agent name must start with "${defaultTeam.agentPrefix}" for default team`,
|
|
172
|
+
team: defaultTeam.name,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return { valid: true, team: config.defaultTeam };
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
valid: false,
|
|
179
|
+
error: `UID ${credentials.uid} / GID ${credentials.gid} not authorized for any team`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Load TLS credentials
|
|
184
|
+
*/
|
|
185
|
+
export function loadTlsCredentials(config) {
|
|
186
|
+
try {
|
|
187
|
+
const cert = fs.readFileSync(config.certPath);
|
|
188
|
+
const key = fs.readFileSync(config.keyPath);
|
|
189
|
+
const ca = config.caPath ? fs.readFileSync(config.caPath) : undefined;
|
|
190
|
+
return { cert, key, ca };
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
console.error('[auth] Failed to load TLS credentials:', err);
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/daemon/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -28,6 +28,8 @@ export interface ConnectionConfig {
|
|
|
28
28
|
seq: number;
|
|
29
29
|
}>;
|
|
30
30
|
} | null>;
|
|
31
|
+
/** Optional callback to check if agent is currently processing (exempts from heartbeat timeout) */
|
|
32
|
+
isProcessing?: (agentName: string) => boolean;
|
|
31
33
|
}
|
|
32
34
|
export declare const DEFAULT_CONFIG: ConnectionConfig;
|
|
33
35
|
export declare class Connection {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/daemon/connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,OAAO,EACL,KAAK,QAAQ,EAMb,KAAK,UAAU,EAEhB,MAAM,sBAAsB,CAAC;AAG9B,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEvG,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,gHAAgH;IAChH,0BAA0B,EAAE,MAAM,CAAC;IACnC,2EAA2E;IAC3E,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;QAC1E,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,KAAK,CAAC;YAAE,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACtE,GAAG,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/daemon/connection.ts"],"names":[],"mappings":"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"}
|
|
@@ -217,8 +217,16 @@ export class Connection {
|
|
|
217
217
|
// Check for missed pong - use configurable timeout multiplier
|
|
218
218
|
const timeoutMs = this.config.heartbeatMs * this.config.heartbeatTimeoutMultiplier;
|
|
219
219
|
if (this.lastPongReceived && now - this.lastPongReceived > timeoutMs) {
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
// Exempt agents that are actively processing (long tool calls, thinking, etc.)
|
|
221
|
+
if (this._agentName && this.config.isProcessing?.(this._agentName)) {
|
|
222
|
+
// Agent is processing - reset the pong timer to avoid timeout
|
|
223
|
+
// but don't kill the connection
|
|
224
|
+
console.log(`[connection] Heartbeat timeout exemption for ${this._agentName} (processing)`);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
this.handleError(new Error(`Heartbeat timeout (no pong in ${timeoutMs}ms)`));
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
222
230
|
}
|
|
223
231
|
// Send ping
|
|
224
232
|
const nonce = uuid();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/daemon/connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAQL,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/daemon/connection.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon/router.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Message router for the agent relay daemon.
|
|
3
3
|
* Handles routing messages between agents, topic subscriptions, and broadcast.
|
|
4
4
|
*/
|
|
5
|
-
import { type Envelope, type SendEnvelope, type AckPayload } from '../protocol/types.js';
|
|
5
|
+
import { type Envelope, type SendEnvelope, type AckPayload, type ShadowConfig, type SpeakOnTrigger } from '../protocol/types.js';
|
|
6
6
|
import type { StorageAdapter } from '../storage/adapter.js';
|
|
7
7
|
import type { AgentRegistry } from './agent-registry.js';
|
|
8
8
|
export interface RoutableConnection {
|
|
@@ -26,6 +26,10 @@ export interface DeliveryReliabilityOptions {
|
|
|
26
26
|
/** How long to keep retrying before dropping (ms) */
|
|
27
27
|
deliveryTtlMs: number;
|
|
28
28
|
}
|
|
29
|
+
/** Internal shadow relationship with resolved defaults */
|
|
30
|
+
interface ShadowRelationship extends ShadowConfig {
|
|
31
|
+
shadowAgent: string;
|
|
32
|
+
}
|
|
29
33
|
export declare class Router {
|
|
30
34
|
private storage?;
|
|
31
35
|
private connections;
|
|
@@ -35,6 +39,10 @@ export declare class Router {
|
|
|
35
39
|
private processingAgents;
|
|
36
40
|
private deliveryOptions;
|
|
37
41
|
private registry?;
|
|
42
|
+
/** Shadow relationships: primaryAgent -> list of shadow configs */
|
|
43
|
+
private shadowsByPrimary;
|
|
44
|
+
/** Reverse lookup: shadowAgent -> primaryAgent (for cleanup) */
|
|
45
|
+
private primaryByShadow;
|
|
38
46
|
/** Default timeout for processing indicator (30 seconds) */
|
|
39
47
|
private static readonly PROCESSING_TIMEOUT_MS;
|
|
40
48
|
constructor(options?: {
|
|
@@ -58,10 +66,51 @@ export declare class Router {
|
|
|
58
66
|
* Unsubscribe an agent from a topic.
|
|
59
67
|
*/
|
|
60
68
|
unsubscribe(agentName: string, topic: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* Bind a shadow agent to a primary agent.
|
|
71
|
+
* The shadow will receive copies of messages to/from the primary.
|
|
72
|
+
*/
|
|
73
|
+
bindShadow(shadowAgent: string, primaryAgent: string, options?: {
|
|
74
|
+
speakOn?: SpeakOnTrigger[];
|
|
75
|
+
receiveIncoming?: boolean;
|
|
76
|
+
receiveOutgoing?: boolean;
|
|
77
|
+
}): void;
|
|
78
|
+
/**
|
|
79
|
+
* Unbind a shadow agent from its primary.
|
|
80
|
+
*/
|
|
81
|
+
unbindShadow(shadowAgent: string): void;
|
|
82
|
+
/**
|
|
83
|
+
* Get all shadows for a primary agent.
|
|
84
|
+
*/
|
|
85
|
+
getShadowsForPrimary(primaryAgent: string): ShadowRelationship[];
|
|
86
|
+
/**
|
|
87
|
+
* Get the primary agent for a shadow, if any.
|
|
88
|
+
*/
|
|
89
|
+
getPrimaryForShadow(shadowAgent: string): string | undefined;
|
|
90
|
+
/**
|
|
91
|
+
* Emit a trigger event for an agent's shadows.
|
|
92
|
+
* Shadows configured to speakOn this trigger will receive a notification.
|
|
93
|
+
* @param primaryAgent The agent whose shadows should be notified
|
|
94
|
+
* @param trigger The trigger event that occurred
|
|
95
|
+
* @param context Optional context data about the trigger
|
|
96
|
+
*/
|
|
97
|
+
emitShadowTrigger(primaryAgent: string, trigger: SpeakOnTrigger, context?: Record<string, unknown>): void;
|
|
98
|
+
/**
|
|
99
|
+
* Check if a shadow should speak based on a specific trigger.
|
|
100
|
+
*/
|
|
101
|
+
shouldShadowSpeak(shadowAgent: string, trigger: SpeakOnTrigger): boolean;
|
|
61
102
|
/**
|
|
62
103
|
* Route a SEND message to its destination(s).
|
|
63
104
|
*/
|
|
64
105
|
route(from: RoutableConnection, envelope: SendEnvelope): void;
|
|
106
|
+
/**
|
|
107
|
+
* Route a copy of a message to shadows of an agent.
|
|
108
|
+
* @param primaryAgent The primary agent whose shadows should receive the message
|
|
109
|
+
* @param envelope The original message envelope
|
|
110
|
+
* @param direction Whether this is an 'incoming' or 'outgoing' message for the primary
|
|
111
|
+
* @param actualFrom Override the 'from' field (for incoming messages, use original sender)
|
|
112
|
+
*/
|
|
113
|
+
private routeToShadows;
|
|
65
114
|
/**
|
|
66
115
|
* Send a direct message to a specific agent.
|
|
67
116
|
*/
|
|
@@ -129,4 +178,5 @@ export declare class Router {
|
|
|
129
178
|
*/
|
|
130
179
|
replayPending(connection: RoutableConnection): Promise<void>;
|
|
131
180
|
}
|
|
181
|
+
export {};
|
|
132
182
|
//# sourceMappingURL=router.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/daemon/router.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,YAAY,EAEjB,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/daemon/router.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon/router.js
CHANGED
|
@@ -18,6 +18,10 @@ export class Router {
|
|
|
18
18
|
processingAgents = new Map(); // agentName -> processing state
|
|
19
19
|
deliveryOptions;
|
|
20
20
|
registry;
|
|
21
|
+
/** Shadow relationships: primaryAgent -> list of shadow configs */
|
|
22
|
+
shadowsByPrimary = new Map();
|
|
23
|
+
/** Reverse lookup: shadowAgent -> primaryAgent (for cleanup) */
|
|
24
|
+
primaryByShadow = new Map();
|
|
21
25
|
/** Default timeout for processing indicator (30 seconds) */
|
|
22
26
|
static PROCESSING_TIMEOUT_MS = 30_000;
|
|
23
27
|
constructor(options = {}) {
|
|
@@ -62,6 +66,8 @@ export class Router {
|
|
|
62
66
|
for (const subscribers of this.subscriptions.values()) {
|
|
63
67
|
subscribers.delete(connection.agentName);
|
|
64
68
|
}
|
|
69
|
+
// Clean up shadow relationships
|
|
70
|
+
this.unbindShadow(connection.agentName);
|
|
65
71
|
// Clear processing state
|
|
66
72
|
this.clearProcessing(connection.agentName);
|
|
67
73
|
}
|
|
@@ -90,6 +96,127 @@ export class Router {
|
|
|
90
96
|
}
|
|
91
97
|
}
|
|
92
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Bind a shadow agent to a primary agent.
|
|
101
|
+
* The shadow will receive copies of messages to/from the primary.
|
|
102
|
+
*/
|
|
103
|
+
bindShadow(shadowAgent, primaryAgent, options = {}) {
|
|
104
|
+
// Clean up any existing shadow binding for this shadow
|
|
105
|
+
this.unbindShadow(shadowAgent);
|
|
106
|
+
const relationship = {
|
|
107
|
+
shadowAgent,
|
|
108
|
+
primaryAgent,
|
|
109
|
+
speakOn: options.speakOn ?? ['EXPLICIT_ASK'],
|
|
110
|
+
receiveIncoming: options.receiveIncoming ?? true,
|
|
111
|
+
receiveOutgoing: options.receiveOutgoing ?? true,
|
|
112
|
+
};
|
|
113
|
+
// Add to primary's shadow list
|
|
114
|
+
let shadows = this.shadowsByPrimary.get(primaryAgent);
|
|
115
|
+
if (!shadows) {
|
|
116
|
+
shadows = [];
|
|
117
|
+
this.shadowsByPrimary.set(primaryAgent, shadows);
|
|
118
|
+
}
|
|
119
|
+
shadows.push(relationship);
|
|
120
|
+
// Set reverse lookup
|
|
121
|
+
this.primaryByShadow.set(shadowAgent, primaryAgent);
|
|
122
|
+
console.log(`[router] Shadow bound: ${shadowAgent} -> ${primaryAgent} (speakOn: ${relationship.speakOn.join(', ')})`);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Unbind a shadow agent from its primary.
|
|
126
|
+
*/
|
|
127
|
+
unbindShadow(shadowAgent) {
|
|
128
|
+
const primaryAgent = this.primaryByShadow.get(shadowAgent);
|
|
129
|
+
if (!primaryAgent)
|
|
130
|
+
return;
|
|
131
|
+
// Remove from primary's shadow list
|
|
132
|
+
const shadows = this.shadowsByPrimary.get(primaryAgent);
|
|
133
|
+
if (shadows) {
|
|
134
|
+
const updatedShadows = shadows.filter(s => s.shadowAgent !== shadowAgent);
|
|
135
|
+
if (updatedShadows.length === 0) {
|
|
136
|
+
this.shadowsByPrimary.delete(primaryAgent);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
this.shadowsByPrimary.set(primaryAgent, updatedShadows);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Remove reverse lookup
|
|
143
|
+
this.primaryByShadow.delete(shadowAgent);
|
|
144
|
+
console.log(`[router] Shadow unbound: ${shadowAgent} from ${primaryAgent}`);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get all shadows for a primary agent.
|
|
148
|
+
*/
|
|
149
|
+
getShadowsForPrimary(primaryAgent) {
|
|
150
|
+
return this.shadowsByPrimary.get(primaryAgent) ?? [];
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get the primary agent for a shadow, if any.
|
|
154
|
+
*/
|
|
155
|
+
getPrimaryForShadow(shadowAgent) {
|
|
156
|
+
return this.primaryByShadow.get(shadowAgent);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Emit a trigger event for an agent's shadows.
|
|
160
|
+
* Shadows configured to speakOn this trigger will receive a notification.
|
|
161
|
+
* @param primaryAgent The agent whose shadows should be notified
|
|
162
|
+
* @param trigger The trigger event that occurred
|
|
163
|
+
* @param context Optional context data about the trigger
|
|
164
|
+
*/
|
|
165
|
+
emitShadowTrigger(primaryAgent, trigger, context) {
|
|
166
|
+
const shadows = this.shadowsByPrimary.get(primaryAgent);
|
|
167
|
+
if (!shadows || shadows.length === 0)
|
|
168
|
+
return;
|
|
169
|
+
for (const shadow of shadows) {
|
|
170
|
+
// Check if this shadow is configured to speak on this trigger
|
|
171
|
+
if (!shadow.speakOn.includes(trigger) && !shadow.speakOn.includes('ALL_MESSAGES')) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const target = this.agents.get(shadow.shadowAgent);
|
|
175
|
+
if (!target)
|
|
176
|
+
continue;
|
|
177
|
+
// Create a trigger notification envelope
|
|
178
|
+
const triggerEnvelope = {
|
|
179
|
+
v: PROTOCOL_VERSION,
|
|
180
|
+
type: 'SEND',
|
|
181
|
+
id: uuid(),
|
|
182
|
+
ts: Date.now(),
|
|
183
|
+
from: primaryAgent,
|
|
184
|
+
to: shadow.shadowAgent,
|
|
185
|
+
payload: {
|
|
186
|
+
kind: 'action',
|
|
187
|
+
body: `SHADOW_TRIGGER:${trigger}`,
|
|
188
|
+
data: {
|
|
189
|
+
_shadowTrigger: trigger,
|
|
190
|
+
_shadowOf: primaryAgent,
|
|
191
|
+
_triggerContext: context,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
const deliver = this.createDeliverEnvelope(primaryAgent, shadow.shadowAgent, triggerEnvelope, target);
|
|
196
|
+
const sent = target.send(deliver);
|
|
197
|
+
if (sent) {
|
|
198
|
+
this.trackDelivery(target, deliver);
|
|
199
|
+
console.log(`[router] Shadow trigger ${trigger} sent to ${shadow.shadowAgent} (primary: ${primaryAgent})`);
|
|
200
|
+
// Set processing state for triggered shadows - they're expected to respond
|
|
201
|
+
this.setProcessing(shadow.shadowAgent, deliver.id);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Check if a shadow should speak based on a specific trigger.
|
|
207
|
+
*/
|
|
208
|
+
shouldShadowSpeak(shadowAgent, trigger) {
|
|
209
|
+
const primaryAgent = this.primaryByShadow.get(shadowAgent);
|
|
210
|
+
if (!primaryAgent)
|
|
211
|
+
return true; // Not a shadow, can always speak
|
|
212
|
+
const shadows = this.shadowsByPrimary.get(primaryAgent);
|
|
213
|
+
if (!shadows)
|
|
214
|
+
return true;
|
|
215
|
+
const relationship = shadows.find(s => s.shadowAgent === shadowAgent);
|
|
216
|
+
if (!relationship)
|
|
217
|
+
return true;
|
|
218
|
+
return relationship.speakOn.includes(trigger) || relationship.speakOn.includes('ALL_MESSAGES');
|
|
219
|
+
}
|
|
93
220
|
/**
|
|
94
221
|
* Route a SEND message to its destination(s).
|
|
95
222
|
*/
|
|
@@ -113,6 +240,57 @@ export class Router {
|
|
|
113
240
|
// Direct message
|
|
114
241
|
this.sendDirect(senderName, to, envelope);
|
|
115
242
|
}
|
|
243
|
+
// Route copies to shadows of the sender (outgoing messages)
|
|
244
|
+
this.routeToShadows(senderName, envelope, 'outgoing');
|
|
245
|
+
// Route copies to shadows of the recipient (incoming messages)
|
|
246
|
+
if (to && to !== '*') {
|
|
247
|
+
this.routeToShadows(to, envelope, 'incoming', senderName);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Route a copy of a message to shadows of an agent.
|
|
252
|
+
* @param primaryAgent The primary agent whose shadows should receive the message
|
|
253
|
+
* @param envelope The original message envelope
|
|
254
|
+
* @param direction Whether this is an 'incoming' or 'outgoing' message for the primary
|
|
255
|
+
* @param actualFrom Override the 'from' field (for incoming messages, use original sender)
|
|
256
|
+
*/
|
|
257
|
+
routeToShadows(primaryAgent, envelope, direction, actualFrom) {
|
|
258
|
+
const shadows = this.shadowsByPrimary.get(primaryAgent);
|
|
259
|
+
if (!shadows || shadows.length === 0)
|
|
260
|
+
return;
|
|
261
|
+
for (const shadow of shadows) {
|
|
262
|
+
// Check if shadow wants this direction
|
|
263
|
+
if (direction === 'incoming' && shadow.receiveIncoming === false)
|
|
264
|
+
continue;
|
|
265
|
+
if (direction === 'outgoing' && shadow.receiveOutgoing === false)
|
|
266
|
+
continue;
|
|
267
|
+
// Don't send to self
|
|
268
|
+
if (shadow.shadowAgent === (actualFrom ?? primaryAgent))
|
|
269
|
+
continue;
|
|
270
|
+
const target = this.agents.get(shadow.shadowAgent);
|
|
271
|
+
if (!target)
|
|
272
|
+
continue;
|
|
273
|
+
// Create a shadow copy envelope with metadata indicating it's a shadow copy
|
|
274
|
+
const shadowEnvelope = {
|
|
275
|
+
...envelope,
|
|
276
|
+
payload: {
|
|
277
|
+
...envelope.payload,
|
|
278
|
+
data: {
|
|
279
|
+
...envelope.payload.data,
|
|
280
|
+
_shadowCopy: true,
|
|
281
|
+
_shadowOf: primaryAgent,
|
|
282
|
+
_shadowDirection: direction,
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
const deliver = this.createDeliverEnvelope(actualFrom ?? primaryAgent, shadow.shadowAgent, shadowEnvelope, target);
|
|
287
|
+
const sent = target.send(deliver);
|
|
288
|
+
if (sent) {
|
|
289
|
+
this.trackDelivery(target, deliver);
|
|
290
|
+
console.log(`[router] Shadow copy to ${shadow.shadowAgent} (${direction} from ${primaryAgent})`);
|
|
291
|
+
// Note: Don't set processing state for shadow copies - shadow stays passive
|
|
292
|
+
}
|
|
293
|
+
}
|
|
116
294
|
}
|
|
117
295
|
/**
|
|
118
296
|
* Send a direct message to a specific agent.
|