agent-relay 1.2.3 → 1.3.1
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/.trajectories/agent-relay-322-324.md +17 -0
- package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.json +49 -0
- package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.md +31 -0
- package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.json +125 -0
- package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.md +62 -0
- package/.trajectories/completed/2026-01/traj_33iuy72sezbk.json +49 -0
- package/.trajectories/completed/2026-01/traj_33iuy72sezbk.md +31 -0
- package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.json +77 -0
- package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.md +42 -0
- package/.trajectories/completed/2026-01/traj_6mieijqyvaag.json +77 -0
- package/.trajectories/completed/2026-01/traj_6mieijqyvaag.md +42 -0
- package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.json +77 -0
- package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.md +42 -0
- package/.trajectories/completed/2026-01/traj_94gnp3k30goq.json +66 -0
- package/.trajectories/completed/2026-01/traj_94gnp3k30goq.md +36 -0
- package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.json +40 -0
- package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.md +22 -0
- package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.json +121 -0
- package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.md +29 -0
- package/.trajectories/completed/2026-01/traj_fhx9irlckht6.json +53 -0
- package/.trajectories/completed/2026-01/traj_fhx9irlckht6.md +32 -0
- package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.json +101 -0
- package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.md +52 -0
- package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.json +49 -0
- package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.md +31 -0
- package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.json +65 -0
- package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.md +37 -0
- package/.trajectories/completed/2026-01/traj_lq450ly148uw.json +49 -0
- package/.trajectories/completed/2026-01/traj_lq450ly148uw.md +31 -0
- package/.trajectories/completed/2026-01/traj_multi_server_arch.md +101 -0
- package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.json +27 -0
- package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.md +14 -0
- package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.json +53 -0
- package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.md +32 -0
- package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.json +186 -0
- package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.md +86 -0
- package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.json +77 -0
- package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.md +42 -0
- package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.json +89 -0
- package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.md +47 -0
- package/.trajectories/completed/2026-01/traj_xy9vifpqet80.json +65 -0
- package/.trajectories/completed/2026-01/traj_xy9vifpqet80.md +37 -0
- package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.json +49 -0
- package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.md +31 -0
- package/.trajectories/consolidate-settings-panel.md +24 -0
- package/.trajectories/gh-cli-user-token.md +26 -0
- package/.trajectories/index.json +155 -1
- package/deploy/workspace/codex.config.toml +15 -0
- package/deploy/workspace/entrypoint.sh +167 -7
- package/deploy/workspace/git-credential-relay +17 -2
- package/dist/bridge/spawner.d.ts +7 -0
- package/dist/bridge/spawner.js +40 -9
- package/dist/bridge/types.d.ts +2 -0
- package/dist/cli/index.js +210 -168
- package/dist/cloud/api/admin.d.ts +8 -0
- package/dist/cloud/api/admin.js +212 -0
- package/dist/cloud/api/auth.js +8 -0
- package/dist/cloud/api/billing.d.ts +0 -10
- package/dist/cloud/api/billing.js +248 -58
- package/dist/cloud/api/codex-auth-helper.d.ts +10 -4
- package/dist/cloud/api/codex-auth-helper.js +215 -8
- package/dist/cloud/api/coordinators.js +402 -0
- package/dist/cloud/api/daemons.js +15 -11
- package/dist/cloud/api/git.js +104 -17
- package/dist/cloud/api/github-app.js +42 -8
- package/dist/cloud/api/nango-auth.js +297 -16
- package/dist/cloud/api/onboarding.js +97 -33
- package/dist/cloud/api/providers.js +12 -16
- package/dist/cloud/api/repos.js +200 -124
- package/dist/cloud/api/test-helpers.js +40 -0
- package/dist/cloud/api/usage.js +13 -0
- package/dist/cloud/api/webhooks.js +1 -1
- package/dist/cloud/api/workspaces.d.ts +18 -0
- package/dist/cloud/api/workspaces.js +945 -15
- package/dist/cloud/config.d.ts +8 -0
- package/dist/cloud/config.js +15 -0
- package/dist/cloud/db/drizzle.d.ts +5 -2
- package/dist/cloud/db/drizzle.js +27 -20
- package/dist/cloud/db/schema.d.ts +19 -51
- package/dist/cloud/db/schema.js +5 -4
- package/dist/cloud/index.d.ts +0 -1
- package/dist/cloud/index.js +0 -1
- package/dist/cloud/provisioner/index.d.ts +93 -1
- package/dist/cloud/provisioner/index.js +608 -63
- package/dist/cloud/server.js +156 -16
- package/dist/cloud/services/compute-enforcement.d.ts +57 -0
- package/dist/cloud/services/compute-enforcement.js +175 -0
- package/dist/cloud/services/index.d.ts +2 -0
- package/dist/cloud/services/index.js +4 -0
- package/dist/cloud/services/intro-expiration.d.ts +55 -0
- package/dist/cloud/services/intro-expiration.js +211 -0
- package/dist/cloud/services/nango.d.ts +14 -0
- package/dist/cloud/services/nango.js +74 -14
- package/dist/cloud/services/ssh-security.d.ts +31 -0
- package/dist/cloud/services/ssh-security.js +63 -0
- package/dist/continuity/manager.d.ts +5 -0
- package/dist/continuity/manager.js +56 -2
- package/dist/daemon/api.d.ts +2 -0
- package/dist/daemon/api.js +214 -5
- package/dist/daemon/cli-auth.d.ts +13 -1
- package/dist/daemon/cli-auth.js +166 -47
- package/dist/daemon/connection.d.ts +7 -1
- package/dist/daemon/connection.js +15 -0
- package/dist/daemon/orchestrator.d.ts +2 -0
- package/dist/daemon/orchestrator.js +26 -0
- package/dist/daemon/repo-manager.d.ts +116 -0
- package/dist/daemon/repo-manager.js +384 -0
- package/dist/daemon/router.d.ts +60 -1
- package/dist/daemon/router.js +281 -20
- package/dist/daemon/user-directory.d.ts +111 -0
- package/dist/daemon/user-directory.js +233 -0
- package/dist/dashboard/out/404.html +1 -1
- package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +9 -0
- package/dist/dashboard/out/_next/static/chunks/766-b54f0853794b78c3.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/83-b51836037078006c.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/891-6cd50de1224f70bb.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/899-fc02ed79e3de4302.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/{page-3fdfa60e53f2810d.js → page-8553743baca53a00.js} +1 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/page-c617745b81344f4f.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-f829604fb75a831a.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/{page-77e9c65420a06cfb.js → page-dc786c183425c2ac.js} +1 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-84322991d7244499.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-05606941a8e2be83.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/{main-ed4e1fb6f29c34cf.js → main-2ee6beb2ae96d210.js} +1 -1
- package/dist/dashboard/out/_next/static/css/48a8fbe3e659080e.css +1 -0
- package/dist/dashboard/out/_next/static/css/fe4b28883eeff359.css +1 -0
- package/dist/dashboard/out/_next/static/sDcbGRTYLcpPvyTs_rsNb/_ssgManifest.js +1 -0
- package/dist/dashboard/out/app/onboarding.html +1 -1
- package/dist/dashboard/out/app/onboarding.txt +3 -3
- package/dist/dashboard/out/app.html +1 -1
- package/dist/dashboard/out/app.txt +3 -3
- package/dist/dashboard/out/apple-icon.png +0 -0
- package/dist/dashboard/out/connect-repos.html +1 -1
- package/dist/dashboard/out/connect-repos.txt +2 -2
- package/dist/dashboard/out/history.html +1 -1
- package/dist/dashboard/out/history.txt +2 -2
- package/dist/dashboard/out/index.html +1 -1
- package/dist/dashboard/out/index.txt +3 -3
- package/dist/dashboard/out/login.html +2 -2
- package/dist/dashboard/out/login.txt +2 -2
- package/dist/dashboard/out/metrics.html +1 -1
- package/dist/dashboard/out/metrics.txt +3 -3
- package/dist/dashboard/out/pricing.html +2 -2
- package/dist/dashboard/out/pricing.txt +3 -3
- package/dist/dashboard/out/providers/setup/claude.html +1 -0
- package/dist/dashboard/out/providers/setup/claude.txt +8 -0
- package/dist/dashboard/out/providers/setup/codex.html +1 -0
- package/dist/dashboard/out/providers/setup/codex.txt +8 -0
- package/dist/dashboard/out/providers.html +1 -1
- package/dist/dashboard/out/providers.txt +3 -3
- package/dist/dashboard/out/signup.html +2 -2
- package/dist/dashboard/out/signup.txt +2 -2
- package/dist/dashboard-server/server.js +316 -12
- package/dist/dashboard-server/user-bridge.d.ts +103 -0
- package/dist/dashboard-server/user-bridge.js +189 -0
- package/dist/protocol/channels.d.ts +205 -0
- package/dist/protocol/channels.js +154 -0
- package/dist/protocol/types.d.ts +13 -1
- package/dist/resiliency/provider-context.js +2 -0
- package/dist/shared/cli-auth-config.d.ts +19 -0
- package/dist/shared/cli-auth-config.js +58 -2
- package/dist/utils/agent-config.js +1 -1
- package/dist/wrapper/auth-detection.d.ts +49 -0
- package/dist/wrapper/auth-detection.js +192 -0
- package/dist/wrapper/base-wrapper.d.ts +153 -0
- package/dist/wrapper/base-wrapper.js +393 -0
- package/dist/wrapper/client.d.ts +7 -1
- package/dist/wrapper/client.js +3 -0
- package/dist/wrapper/index.d.ts +1 -0
- package/dist/wrapper/index.js +4 -3
- package/dist/wrapper/pty-wrapper.d.ts +62 -84
- package/dist/wrapper/pty-wrapper.js +154 -180
- package/dist/wrapper/tmux-wrapper.d.ts +41 -66
- package/dist/wrapper/tmux-wrapper.js +90 -134
- package/package.json +4 -2
- package/scripts/postinstall.js +11 -155
- package/scripts/test-interactive-terminal.sh +248 -0
- package/dist/cloud/vault/index.d.ts +0 -76
- package/dist/cloud/vault/index.js +0 -219
- package/dist/dashboard/out/_next/static/chunks/699-3b1cd6618a45d259.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/724-2dae7627550ab88f.js +0 -9
- package/dist/dashboard/out/_next/static/chunks/766-1f2dd8cb7f766b0b.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/page-e6381e5a6e1fbcfd.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-67a3e98d9a43a6ed.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-e88bc117ef7671c3.js +0 -1
- package/dist/dashboard/out/_next/static/css/29852f26181969a0.css +0 -1
- package/dist/dashboard/out/_next/static/css/7c3ae9e8617d42a5.css +0 -1
- package/dist/dashboard/out/_next/static/wPgKJtcOmTFLpUncDg16A/_ssgManifest.js +0 -1
- /package/dist/dashboard/out/_next/static/{wPgKJtcOmTFLpUncDg16A → sDcbGRTYLcpPvyTs_rsNb}/_buildManifest.js +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Bridge - Bridges dashboard WebSocket users to the relay daemon.
|
|
3
|
+
*
|
|
4
|
+
* This module allows human users connected via WebSocket to:
|
|
5
|
+
* - Register as "user" entities in the relay daemon
|
|
6
|
+
* - Join/leave channels
|
|
7
|
+
* - Send/receive messages through the relay daemon
|
|
8
|
+
* - Communicate with agents and other users
|
|
9
|
+
*/
|
|
10
|
+
import type { WebSocket } from 'ws';
|
|
11
|
+
/**
|
|
12
|
+
* Relay client interface (subset of RelayClient for dependency injection)
|
|
13
|
+
*/
|
|
14
|
+
export interface IRelayClient {
|
|
15
|
+
connect(): Promise<void>;
|
|
16
|
+
disconnect(): void;
|
|
17
|
+
state: string;
|
|
18
|
+
sendMessage(to: string, body: string, kind?: string, data?: unknown, thread?: string): boolean;
|
|
19
|
+
onMessage?: (from: string, payload: any, messageId: string, meta?: any, originalTo?: string) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Factory function type for creating relay clients
|
|
23
|
+
*/
|
|
24
|
+
export type RelayClientFactory = (options: {
|
|
25
|
+
socketPath: string;
|
|
26
|
+
agentName: string;
|
|
27
|
+
entityType: 'user';
|
|
28
|
+
displayName?: string;
|
|
29
|
+
avatarUrl?: string;
|
|
30
|
+
}) => Promise<IRelayClient>;
|
|
31
|
+
/**
|
|
32
|
+
* Options for creating a UserBridge
|
|
33
|
+
*/
|
|
34
|
+
export interface UserBridgeOptions {
|
|
35
|
+
socketPath: string;
|
|
36
|
+
createRelayClient: RelayClientFactory;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Message options for sending
|
|
40
|
+
*/
|
|
41
|
+
export interface SendMessageOptions {
|
|
42
|
+
thread?: string;
|
|
43
|
+
data?: Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* UserBridge manages the connection between dashboard WebSocket users
|
|
47
|
+
* and the relay daemon.
|
|
48
|
+
*/
|
|
49
|
+
export declare class UserBridge {
|
|
50
|
+
private readonly socketPath;
|
|
51
|
+
private readonly createRelayClient;
|
|
52
|
+
private readonly users;
|
|
53
|
+
constructor(options: UserBridgeOptions);
|
|
54
|
+
/**
|
|
55
|
+
* Register a user with the relay daemon.
|
|
56
|
+
* Creates a relay client connection for the user.
|
|
57
|
+
*/
|
|
58
|
+
registerUser(username: string, webSocket: WebSocket, options?: {
|
|
59
|
+
avatarUrl?: string;
|
|
60
|
+
displayName?: string;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Unregister a user and disconnect their relay client.
|
|
64
|
+
*/
|
|
65
|
+
unregisterUser(username: string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Check if a user is registered.
|
|
68
|
+
*/
|
|
69
|
+
isUserRegistered(username: string): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Get list of all registered users.
|
|
72
|
+
*/
|
|
73
|
+
getRegisteredUsers(): string[];
|
|
74
|
+
/**
|
|
75
|
+
* Join a channel.
|
|
76
|
+
*/
|
|
77
|
+
joinChannel(username: string, channel: string): Promise<boolean>;
|
|
78
|
+
/**
|
|
79
|
+
* Leave a channel.
|
|
80
|
+
*/
|
|
81
|
+
leaveChannel(username: string, channel: string): Promise<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Get channels a user has joined.
|
|
84
|
+
*/
|
|
85
|
+
getUserChannels(username: string): string[];
|
|
86
|
+
/**
|
|
87
|
+
* Send a message to a channel.
|
|
88
|
+
*/
|
|
89
|
+
sendChannelMessage(username: string, channel: string, body: string, options?: SendMessageOptions): Promise<boolean>;
|
|
90
|
+
/**
|
|
91
|
+
* Send a direct message to another user or agent.
|
|
92
|
+
*/
|
|
93
|
+
sendDirectMessage(fromUsername: string, toName: string, body: string, options?: SendMessageOptions): Promise<boolean>;
|
|
94
|
+
/**
|
|
95
|
+
* Handle incoming message from relay daemon.
|
|
96
|
+
*/
|
|
97
|
+
private handleIncomingMessage;
|
|
98
|
+
/**
|
|
99
|
+
* Dispose of all user sessions.
|
|
100
|
+
*/
|
|
101
|
+
dispose(): void;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=user-bridge.d.ts.map
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Bridge - Bridges dashboard WebSocket users to the relay daemon.
|
|
3
|
+
*
|
|
4
|
+
* This module allows human users connected via WebSocket to:
|
|
5
|
+
* - Register as "user" entities in the relay daemon
|
|
6
|
+
* - Join/leave channels
|
|
7
|
+
* - Send/receive messages through the relay daemon
|
|
8
|
+
* - Communicate with agents and other users
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* UserBridge manages the connection between dashboard WebSocket users
|
|
12
|
+
* and the relay daemon.
|
|
13
|
+
*/
|
|
14
|
+
export class UserBridge {
|
|
15
|
+
socketPath;
|
|
16
|
+
createRelayClient;
|
|
17
|
+
users = new Map();
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.socketPath = options.socketPath;
|
|
20
|
+
this.createRelayClient = options.createRelayClient;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Register a user with the relay daemon.
|
|
24
|
+
* Creates a relay client connection for the user.
|
|
25
|
+
*/
|
|
26
|
+
async registerUser(username, webSocket, options) {
|
|
27
|
+
// If user already registered, unregister first
|
|
28
|
+
if (this.users.has(username)) {
|
|
29
|
+
this.unregisterUser(username);
|
|
30
|
+
}
|
|
31
|
+
// Create relay client for this user
|
|
32
|
+
const relayClient = await this.createRelayClient({
|
|
33
|
+
socketPath: this.socketPath,
|
|
34
|
+
agentName: username,
|
|
35
|
+
entityType: 'user',
|
|
36
|
+
displayName: options?.displayName,
|
|
37
|
+
avatarUrl: options?.avatarUrl,
|
|
38
|
+
});
|
|
39
|
+
// Connect to daemon
|
|
40
|
+
await relayClient.connect();
|
|
41
|
+
// Set up message handler to forward messages to WebSocket
|
|
42
|
+
relayClient.onMessage = (from, payload, _messageId, _meta, _originalTo) => {
|
|
43
|
+
const body = typeof payload === 'object' && payload !== null && 'body' in payload
|
|
44
|
+
? payload.body
|
|
45
|
+
: String(payload);
|
|
46
|
+
this.handleIncomingMessage(username, from, body, payload);
|
|
47
|
+
};
|
|
48
|
+
// Create session
|
|
49
|
+
const session = {
|
|
50
|
+
username,
|
|
51
|
+
relayClient,
|
|
52
|
+
webSocket,
|
|
53
|
+
channels: new Set(),
|
|
54
|
+
avatarUrl: options?.avatarUrl,
|
|
55
|
+
};
|
|
56
|
+
this.users.set(username, session);
|
|
57
|
+
// Set up WebSocket close handler
|
|
58
|
+
webSocket.on('close', () => {
|
|
59
|
+
this.unregisterUser(username);
|
|
60
|
+
});
|
|
61
|
+
console.log(`[user-bridge] User ${username} registered with relay daemon`);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Unregister a user and disconnect their relay client.
|
|
65
|
+
*/
|
|
66
|
+
unregisterUser(username) {
|
|
67
|
+
const session = this.users.get(username);
|
|
68
|
+
if (!session)
|
|
69
|
+
return;
|
|
70
|
+
session.relayClient.disconnect();
|
|
71
|
+
this.users.delete(username);
|
|
72
|
+
console.log(`[user-bridge] User ${username} unregistered from relay daemon`);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Check if a user is registered.
|
|
76
|
+
*/
|
|
77
|
+
isUserRegistered(username) {
|
|
78
|
+
return this.users.has(username);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get list of all registered users.
|
|
82
|
+
*/
|
|
83
|
+
getRegisteredUsers() {
|
|
84
|
+
return Array.from(this.users.keys());
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Join a channel.
|
|
88
|
+
*/
|
|
89
|
+
async joinChannel(username, channel) {
|
|
90
|
+
const session = this.users.get(username);
|
|
91
|
+
if (!session) {
|
|
92
|
+
console.warn(`[user-bridge] Cannot join channel - user ${username} not registered`);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
// Send channel join via relay client
|
|
96
|
+
session.relayClient.sendMessage(channel, '', 'channel_join');
|
|
97
|
+
// Track membership
|
|
98
|
+
session.channels.add(channel);
|
|
99
|
+
console.log(`[user-bridge] User ${username} joined channel ${channel}`);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Leave a channel.
|
|
104
|
+
*/
|
|
105
|
+
async leaveChannel(username, channel) {
|
|
106
|
+
const session = this.users.get(username);
|
|
107
|
+
if (!session) {
|
|
108
|
+
console.warn(`[user-bridge] Cannot leave channel - user ${username} not registered`);
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
// Send channel leave via relay client
|
|
112
|
+
session.relayClient.sendMessage(channel, '', 'channel_leave');
|
|
113
|
+
// Update membership
|
|
114
|
+
session.channels.delete(channel);
|
|
115
|
+
console.log(`[user-bridge] User ${username} left channel ${channel}`);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get channels a user has joined.
|
|
120
|
+
*/
|
|
121
|
+
getUserChannels(username) {
|
|
122
|
+
const session = this.users.get(username);
|
|
123
|
+
return session ? Array.from(session.channels) : [];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Send a message to a channel.
|
|
127
|
+
*/
|
|
128
|
+
async sendChannelMessage(username, channel, body, options) {
|
|
129
|
+
const session = this.users.get(username);
|
|
130
|
+
if (!session) {
|
|
131
|
+
console.warn(`[user-bridge] Cannot send - user ${username} not registered`);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
return session.relayClient.sendMessage(channel, body, 'message', options?.data, options?.thread);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Send a direct message to another user or agent.
|
|
138
|
+
*/
|
|
139
|
+
async sendDirectMessage(fromUsername, toName, body, options) {
|
|
140
|
+
const session = this.users.get(fromUsername);
|
|
141
|
+
if (!session) {
|
|
142
|
+
console.warn(`[user-bridge] Cannot send DM - user ${fromUsername} not registered`);
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
return session.relayClient.sendMessage(toName, body, 'message', options?.data, options?.thread);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Handle incoming message from relay daemon.
|
|
149
|
+
*/
|
|
150
|
+
handleIncomingMessage(username, from, body, envelope) {
|
|
151
|
+
const session = this.users.get(username);
|
|
152
|
+
if (!session)
|
|
153
|
+
return;
|
|
154
|
+
const ws = session.webSocket;
|
|
155
|
+
if (ws.readyState !== 1)
|
|
156
|
+
return; // Not OPEN
|
|
157
|
+
// Determine message type from envelope
|
|
158
|
+
const env = envelope;
|
|
159
|
+
if (env.type === 'CHANNEL_MESSAGE') {
|
|
160
|
+
// Channel message
|
|
161
|
+
ws.send(JSON.stringify({
|
|
162
|
+
type: 'channel_message',
|
|
163
|
+
channel: env.payload?.channel,
|
|
164
|
+
from,
|
|
165
|
+
body: env.payload?.body || body,
|
|
166
|
+
timestamp: new Date().toISOString(),
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
// Direct message (DELIVER)
|
|
171
|
+
ws.send(JSON.stringify({
|
|
172
|
+
type: 'direct_message',
|
|
173
|
+
from,
|
|
174
|
+
body: env.payload?.body || body,
|
|
175
|
+
timestamp: new Date().toISOString(),
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Dispose of all user sessions.
|
|
181
|
+
*/
|
|
182
|
+
dispose() {
|
|
183
|
+
for (const [username] of this.users) {
|
|
184
|
+
this.unregisterUser(username);
|
|
185
|
+
}
|
|
186
|
+
console.log('[user-bridge] Disposed all user sessions');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=user-bridge.js.map
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel and User Protocol Types
|
|
3
|
+
*
|
|
4
|
+
* Extends the Agent Relay Protocol to support:
|
|
5
|
+
* - First-class user entities (humans, not just AI agents)
|
|
6
|
+
* - Channels for group communication
|
|
7
|
+
* - Direct messaging between any combination of users and agents
|
|
8
|
+
*/
|
|
9
|
+
import { PROTOCOL_VERSION, type Envelope } from './types.js';
|
|
10
|
+
export { PROTOCOL_VERSION };
|
|
11
|
+
/**
|
|
12
|
+
* Entity types in the relay system.
|
|
13
|
+
* - 'agent': AI agent (Claude, GPT, etc.)
|
|
14
|
+
* - 'user': Human user (via dashboard)
|
|
15
|
+
*/
|
|
16
|
+
export type EntityType = 'agent' | 'user';
|
|
17
|
+
/**
|
|
18
|
+
* Extended message types for channels.
|
|
19
|
+
*/
|
|
20
|
+
export type ChannelMessageType = 'CHANNEL_JOIN' | 'CHANNEL_LEAVE' | 'CHANNEL_MESSAGE' | 'CHANNEL_INFO' | 'CHANNEL_MEMBERS' | 'CHANNEL_TYPING';
|
|
21
|
+
/**
|
|
22
|
+
* Check if an entity type represents a user.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isUserEntity(entityType: EntityType | undefined): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Check if an entity type represents an agent.
|
|
27
|
+
* Undefined defaults to agent for backwards compatibility.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isAgentEntity(entityType: EntityType | undefined): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Attachment metadata for messages.
|
|
32
|
+
*/
|
|
33
|
+
export interface MessageAttachment {
|
|
34
|
+
id: string;
|
|
35
|
+
filename: string;
|
|
36
|
+
mimeType: string;
|
|
37
|
+
size: number;
|
|
38
|
+
url?: string;
|
|
39
|
+
data?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Payload for CHANNEL_JOIN message.
|
|
43
|
+
* Sent when a user or agent joins a channel.
|
|
44
|
+
*/
|
|
45
|
+
export interface ChannelJoinPayload {
|
|
46
|
+
/** The channel to join (e.g., '#general', 'dm:alice:bob') */
|
|
47
|
+
channel: string;
|
|
48
|
+
/** Optional display name for the channel member list */
|
|
49
|
+
displayName?: string;
|
|
50
|
+
/** Optional avatar URL */
|
|
51
|
+
avatarUrl?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Payload for CHANNEL_LEAVE message.
|
|
55
|
+
* Sent when a user or agent leaves a channel.
|
|
56
|
+
*/
|
|
57
|
+
export interface ChannelLeavePayload {
|
|
58
|
+
/** The channel to leave */
|
|
59
|
+
channel: string;
|
|
60
|
+
/** Optional reason for leaving */
|
|
61
|
+
reason?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Payload for CHANNEL_MESSAGE.
|
|
65
|
+
* Sent when posting a message to a channel.
|
|
66
|
+
*/
|
|
67
|
+
export interface ChannelMessagePayload {
|
|
68
|
+
/** The target channel */
|
|
69
|
+
channel: string;
|
|
70
|
+
/** Message content */
|
|
71
|
+
body: string;
|
|
72
|
+
/** Optional thread ID for threaded replies */
|
|
73
|
+
thread?: string;
|
|
74
|
+
/** Optional attachments */
|
|
75
|
+
attachments?: MessageAttachment[];
|
|
76
|
+
/** Optional metadata */
|
|
77
|
+
data?: Record<string, unknown>;
|
|
78
|
+
/** Optional list of mentioned users/agents */
|
|
79
|
+
mentions?: string[];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Channel member info.
|
|
83
|
+
*/
|
|
84
|
+
export interface ChannelMember {
|
|
85
|
+
name: string;
|
|
86
|
+
entityType: EntityType;
|
|
87
|
+
displayName?: string;
|
|
88
|
+
avatarUrl?: string;
|
|
89
|
+
status?: 'online' | 'away' | 'offline';
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Payload for CHANNEL_INFO.
|
|
93
|
+
* Contains metadata about a channel.
|
|
94
|
+
*/
|
|
95
|
+
export interface ChannelInfoPayload {
|
|
96
|
+
/** Channel identifier */
|
|
97
|
+
channel: string;
|
|
98
|
+
/** Human-readable channel name */
|
|
99
|
+
name: string;
|
|
100
|
+
/** Optional description */
|
|
101
|
+
description?: string;
|
|
102
|
+
/** Optional current topic */
|
|
103
|
+
topic?: string;
|
|
104
|
+
/** List of members */
|
|
105
|
+
members: ChannelMember[];
|
|
106
|
+
/** When the channel was created */
|
|
107
|
+
createdAt: string;
|
|
108
|
+
/** Whether the channel is private */
|
|
109
|
+
isPrivate?: boolean;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Payload for CHANNEL_MEMBERS.
|
|
113
|
+
* List of members in a channel.
|
|
114
|
+
*/
|
|
115
|
+
export interface ChannelMembersPayload {
|
|
116
|
+
channel: string;
|
|
117
|
+
members: ChannelMember[];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Payload for CHANNEL_TYPING.
|
|
121
|
+
* Indicates someone is typing in a channel.
|
|
122
|
+
*/
|
|
123
|
+
export interface ChannelTypingPayload {
|
|
124
|
+
channel: string;
|
|
125
|
+
isTyping: boolean;
|
|
126
|
+
}
|
|
127
|
+
export type ChannelJoinEnvelope = Envelope<ChannelJoinPayload> & {
|
|
128
|
+
type: 'CHANNEL_JOIN';
|
|
129
|
+
};
|
|
130
|
+
export type ChannelLeaveEnvelope = Envelope<ChannelLeavePayload> & {
|
|
131
|
+
type: 'CHANNEL_LEAVE';
|
|
132
|
+
};
|
|
133
|
+
export type ChannelMessageEnvelope = Envelope<ChannelMessagePayload> & {
|
|
134
|
+
type: 'CHANNEL_MESSAGE';
|
|
135
|
+
};
|
|
136
|
+
export type ChannelInfoEnvelope = Envelope<ChannelInfoPayload> & {
|
|
137
|
+
type: 'CHANNEL_INFO';
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Type guard to check if an envelope is a channel message.
|
|
141
|
+
*/
|
|
142
|
+
export declare function isChannelMessage(envelope: {
|
|
143
|
+
type: string;
|
|
144
|
+
}): envelope is ChannelMessageEnvelope;
|
|
145
|
+
/**
|
|
146
|
+
* Type guard to check if an envelope is a channel join.
|
|
147
|
+
*/
|
|
148
|
+
export declare function isChannelJoin(envelope: {
|
|
149
|
+
type: string;
|
|
150
|
+
}): envelope is ChannelJoinEnvelope;
|
|
151
|
+
/**
|
|
152
|
+
* Type guard to check if an envelope is a channel leave.
|
|
153
|
+
*/
|
|
154
|
+
export declare function isChannelLeave(envelope: {
|
|
155
|
+
type: string;
|
|
156
|
+
}): envelope is ChannelLeaveEnvelope;
|
|
157
|
+
/**
|
|
158
|
+
* Create a CHANNEL_JOIN envelope.
|
|
159
|
+
*/
|
|
160
|
+
export declare function createChannelJoinEnvelope(from: string, channel: string, options?: {
|
|
161
|
+
displayName?: string;
|
|
162
|
+
avatarUrl?: string;
|
|
163
|
+
}): ChannelJoinEnvelope;
|
|
164
|
+
/**
|
|
165
|
+
* Create a CHANNEL_LEAVE envelope.
|
|
166
|
+
*/
|
|
167
|
+
export declare function createChannelLeaveEnvelope(from: string, channel: string, reason?: string): ChannelLeaveEnvelope;
|
|
168
|
+
/**
|
|
169
|
+
* Create a CHANNEL_MESSAGE envelope.
|
|
170
|
+
*/
|
|
171
|
+
export declare function createChannelMessageEnvelope(from: string, channel: string, body: string, options?: {
|
|
172
|
+
thread?: string;
|
|
173
|
+
mentions?: string[];
|
|
174
|
+
attachments?: MessageAttachment[];
|
|
175
|
+
data?: Record<string, unknown>;
|
|
176
|
+
}): ChannelMessageEnvelope;
|
|
177
|
+
/**
|
|
178
|
+
* Parse a DM channel name to extract participants.
|
|
179
|
+
* DM channels follow the format: dm:<participant1>:<participant2>:...
|
|
180
|
+
*/
|
|
181
|
+
export declare function parseDmChannel(channel: string): string[] | null;
|
|
182
|
+
/**
|
|
183
|
+
* Create a DM channel name from participants.
|
|
184
|
+
* Participants are sorted alphabetically for consistency.
|
|
185
|
+
*/
|
|
186
|
+
export declare function createDmChannelName(...participants: string[]): string;
|
|
187
|
+
/**
|
|
188
|
+
* Check if a channel is a DM (direct message).
|
|
189
|
+
*/
|
|
190
|
+
export declare function isDmChannel(channel: string): boolean;
|
|
191
|
+
/**
|
|
192
|
+
* Check if a channel is private.
|
|
193
|
+
*/
|
|
194
|
+
export declare function isPrivateChannel(channel: string): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Check if a channel is a public channel (starts with #).
|
|
197
|
+
*/
|
|
198
|
+
export declare function isPublicChannel(channel: string): boolean;
|
|
199
|
+
/**
|
|
200
|
+
* Normalize channel name for storage/lookup.
|
|
201
|
+
* - Removes leading # from public channels
|
|
202
|
+
* - Sorts participants in DM channels
|
|
203
|
+
*/
|
|
204
|
+
export declare function normalizeChannelName(channel: string): string;
|
|
205
|
+
//# sourceMappingURL=channels.d.ts.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel and User Protocol Types
|
|
3
|
+
*
|
|
4
|
+
* Extends the Agent Relay Protocol to support:
|
|
5
|
+
* - First-class user entities (humans, not just AI agents)
|
|
6
|
+
* - Channels for group communication
|
|
7
|
+
* - Direct messaging between any combination of users and agents
|
|
8
|
+
*/
|
|
9
|
+
import { v4 as uuid } from 'uuid';
|
|
10
|
+
import { PROTOCOL_VERSION } from './types.js';
|
|
11
|
+
// Re-export PROTOCOL_VERSION for convenience
|
|
12
|
+
export { PROTOCOL_VERSION };
|
|
13
|
+
/**
|
|
14
|
+
* Check if an entity type represents a user.
|
|
15
|
+
*/
|
|
16
|
+
export function isUserEntity(entityType) {
|
|
17
|
+
return entityType === 'user';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if an entity type represents an agent.
|
|
21
|
+
* Undefined defaults to agent for backwards compatibility.
|
|
22
|
+
*/
|
|
23
|
+
export function isAgentEntity(entityType) {
|
|
24
|
+
return entityType === 'agent' || entityType === undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Type guard to check if an envelope is a channel message.
|
|
28
|
+
*/
|
|
29
|
+
export function isChannelMessage(envelope) {
|
|
30
|
+
return envelope.type === 'CHANNEL_MESSAGE';
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Type guard to check if an envelope is a channel join.
|
|
34
|
+
*/
|
|
35
|
+
export function isChannelJoin(envelope) {
|
|
36
|
+
return envelope.type === 'CHANNEL_JOIN';
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Type guard to check if an envelope is a channel leave.
|
|
40
|
+
*/
|
|
41
|
+
export function isChannelLeave(envelope) {
|
|
42
|
+
return envelope.type === 'CHANNEL_LEAVE';
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create a CHANNEL_JOIN envelope.
|
|
46
|
+
*/
|
|
47
|
+
export function createChannelJoinEnvelope(from, channel, options) {
|
|
48
|
+
return {
|
|
49
|
+
v: PROTOCOL_VERSION,
|
|
50
|
+
type: 'CHANNEL_JOIN',
|
|
51
|
+
id: uuid(),
|
|
52
|
+
ts: Date.now(),
|
|
53
|
+
from,
|
|
54
|
+
payload: {
|
|
55
|
+
channel,
|
|
56
|
+
displayName: options?.displayName,
|
|
57
|
+
avatarUrl: options?.avatarUrl,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Create a CHANNEL_LEAVE envelope.
|
|
63
|
+
*/
|
|
64
|
+
export function createChannelLeaveEnvelope(from, channel, reason) {
|
|
65
|
+
return {
|
|
66
|
+
v: PROTOCOL_VERSION,
|
|
67
|
+
type: 'CHANNEL_LEAVE',
|
|
68
|
+
id: uuid(),
|
|
69
|
+
ts: Date.now(),
|
|
70
|
+
from,
|
|
71
|
+
payload: {
|
|
72
|
+
channel,
|
|
73
|
+
reason,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Create a CHANNEL_MESSAGE envelope.
|
|
79
|
+
*/
|
|
80
|
+
export function createChannelMessageEnvelope(from, channel, body, options) {
|
|
81
|
+
return {
|
|
82
|
+
v: PROTOCOL_VERSION,
|
|
83
|
+
type: 'CHANNEL_MESSAGE',
|
|
84
|
+
id: uuid(),
|
|
85
|
+
ts: Date.now(),
|
|
86
|
+
from,
|
|
87
|
+
payload: {
|
|
88
|
+
channel,
|
|
89
|
+
body,
|
|
90
|
+
thread: options?.thread,
|
|
91
|
+
mentions: options?.mentions,
|
|
92
|
+
attachments: options?.attachments,
|
|
93
|
+
data: options?.data,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Parse a DM channel name to extract participants.
|
|
99
|
+
* DM channels follow the format: dm:<participant1>:<participant2>:...
|
|
100
|
+
*/
|
|
101
|
+
export function parseDmChannel(channel) {
|
|
102
|
+
if (!channel.startsWith('dm:')) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const parts = channel.split(':').slice(1);
|
|
106
|
+
return parts.length >= 2 ? parts : null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Create a DM channel name from participants.
|
|
110
|
+
* Participants are sorted alphabetically for consistency.
|
|
111
|
+
*/
|
|
112
|
+
export function createDmChannelName(...participants) {
|
|
113
|
+
if (participants.length < 2) {
|
|
114
|
+
throw new Error('DM requires at least 2 participants');
|
|
115
|
+
}
|
|
116
|
+
const sorted = [...participants].sort();
|
|
117
|
+
return `dm:${sorted.join(':')}`;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Check if a channel is a DM (direct message).
|
|
121
|
+
*/
|
|
122
|
+
export function isDmChannel(channel) {
|
|
123
|
+
return channel.startsWith('dm:');
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check if a channel is private.
|
|
127
|
+
*/
|
|
128
|
+
export function isPrivateChannel(channel) {
|
|
129
|
+
return channel.startsWith('private:');
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if a channel is a public channel (starts with #).
|
|
133
|
+
*/
|
|
134
|
+
export function isPublicChannel(channel) {
|
|
135
|
+
return channel.startsWith('#');
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Normalize channel name for storage/lookup.
|
|
139
|
+
* - Removes leading # from public channels
|
|
140
|
+
* - Sorts participants in DM channels
|
|
141
|
+
*/
|
|
142
|
+
export function normalizeChannelName(channel) {
|
|
143
|
+
if (channel.startsWith('#')) {
|
|
144
|
+
return channel.slice(1);
|
|
145
|
+
}
|
|
146
|
+
if (channel.startsWith('dm:')) {
|
|
147
|
+
const participants = parseDmChannel(channel);
|
|
148
|
+
if (participants) {
|
|
149
|
+
return createDmChannelName(...participants);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return channel;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=channels.js.map
|
package/dist/protocol/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Version 1.0
|
|
4
4
|
*/
|
|
5
5
|
export declare const PROTOCOL_VERSION = 1;
|
|
6
|
-
export type MessageType = 'HELLO' | 'WELCOME' | 'SEND' | 'DELIVER' | 'ACK' | 'NACK' | 'PING' | 'PONG' | 'ERROR' | 'BUSY' | 'RESUME' | 'BYE' | 'STATE' | 'SYNC' | 'SYNC_SNAPSHOT' | 'SYNC_DELTA' | 'SUBSCRIBE' | 'UNSUBSCRIBE' | 'SHADOW_BIND' | 'SHADOW_UNBIND' | 'LOG';
|
|
6
|
+
export type MessageType = 'HELLO' | 'WELCOME' | 'SEND' | 'DELIVER' | 'ACK' | 'NACK' | 'PING' | 'PONG' | 'ERROR' | 'BUSY' | 'RESUME' | 'BYE' | 'STATE' | 'SYNC' | 'SYNC_SNAPSHOT' | 'SYNC_DELTA' | 'SUBSCRIBE' | 'UNSUBSCRIBE' | 'SHADOW_BIND' | 'SHADOW_UNBIND' | 'LOG' | 'CHANNEL_JOIN' | 'CHANNEL_LEAVE' | 'CHANNEL_MESSAGE' | 'CHANNEL_INFO' | 'CHANNEL_MEMBERS' | 'CHANNEL_TYPING';
|
|
7
7
|
export type PayloadKind = 'message' | 'action' | 'state' | 'thinking';
|
|
8
8
|
export interface Envelope<T = unknown> {
|
|
9
9
|
v: number;
|
|
@@ -15,6 +15,12 @@ export interface Envelope<T = unknown> {
|
|
|
15
15
|
topic?: string;
|
|
16
16
|
payload: T;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Entity type distinguishes between AI agents and human users.
|
|
20
|
+
* - 'agent': AI agent (Claude, GPT, custom agents)
|
|
21
|
+
* - 'user': Human user (via dashboard WebSocket)
|
|
22
|
+
*/
|
|
23
|
+
export type EntityType = 'agent' | 'user';
|
|
18
24
|
export interface HelloPayload {
|
|
19
25
|
agent: string;
|
|
20
26
|
capabilities: {
|
|
@@ -23,6 +29,8 @@ export interface HelloPayload {
|
|
|
23
29
|
max_inflight: number;
|
|
24
30
|
supports_topics: boolean;
|
|
25
31
|
};
|
|
32
|
+
/** Entity type: 'agent' (default) or 'user' for human users */
|
|
33
|
+
entityType?: EntityType;
|
|
26
34
|
/** Optional hint about which CLI the agent is using (claude, codex, gemini, etc.) */
|
|
27
35
|
cli?: string;
|
|
28
36
|
/** Optional program identifier (e.g., 'claude', 'gpt-4o') */
|
|
@@ -33,6 +41,10 @@ export interface HelloPayload {
|
|
|
33
41
|
task?: string;
|
|
34
42
|
/** Optional working directory hint for registry/dashboard */
|
|
35
43
|
workingDirectory?: string;
|
|
44
|
+
/** Display name for human users */
|
|
45
|
+
displayName?: string;
|
|
46
|
+
/** Avatar URL for human users */
|
|
47
|
+
avatarUrl?: string;
|
|
36
48
|
session?: {
|
|
37
49
|
resume_token?: string;
|
|
38
50
|
};
|
|
@@ -197,6 +197,8 @@ export class CodexContextHandler extends ProviderContextHandler {
|
|
|
197
197
|
config.history = config.history || {};
|
|
198
198
|
config.history.save_history = true;
|
|
199
199
|
config.history.max_history_size = 1000;
|
|
200
|
+
// Disable Codex auto-update checks to keep container-stable version
|
|
201
|
+
config.check_for_updates = false;
|
|
200
202
|
// Point to our system prompt if using custom context
|
|
201
203
|
if (this.codexConfig.systemPromptPath) {
|
|
202
204
|
config.system_prompt_file = this.codexConfig.systemPromptPath;
|