agent-relay 1.2.3 → 1.3.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.
Files changed (200) hide show
  1. package/.trajectories/agent-relay-322-324.md +17 -0
  2. package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.json +49 -0
  3. package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.md +31 -0
  4. package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.json +125 -0
  5. package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.md +62 -0
  6. package/.trajectories/completed/2026-01/traj_33iuy72sezbk.json +49 -0
  7. package/.trajectories/completed/2026-01/traj_33iuy72sezbk.md +31 -0
  8. package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.json +77 -0
  9. package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.md +42 -0
  10. package/.trajectories/completed/2026-01/traj_6mieijqyvaag.json +77 -0
  11. package/.trajectories/completed/2026-01/traj_6mieijqyvaag.md +42 -0
  12. package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.json +77 -0
  13. package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.md +42 -0
  14. package/.trajectories/completed/2026-01/traj_94gnp3k30goq.json +66 -0
  15. package/.trajectories/completed/2026-01/traj_94gnp3k30goq.md +36 -0
  16. package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.json +40 -0
  17. package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.md +22 -0
  18. package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.json +121 -0
  19. package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.md +29 -0
  20. package/.trajectories/completed/2026-01/traj_fhx9irlckht6.json +53 -0
  21. package/.trajectories/completed/2026-01/traj_fhx9irlckht6.md +32 -0
  22. package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.json +101 -0
  23. package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.md +52 -0
  24. package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.json +49 -0
  25. package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.md +31 -0
  26. package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.json +65 -0
  27. package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.md +37 -0
  28. package/.trajectories/completed/2026-01/traj_lq450ly148uw.json +49 -0
  29. package/.trajectories/completed/2026-01/traj_lq450ly148uw.md +31 -0
  30. package/.trajectories/completed/2026-01/traj_multi_server_arch.md +101 -0
  31. package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.json +27 -0
  32. package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.md +14 -0
  33. package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.json +53 -0
  34. package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.md +32 -0
  35. package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.json +186 -0
  36. package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.md +86 -0
  37. package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.json +77 -0
  38. package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.md +42 -0
  39. package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.json +89 -0
  40. package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.md +47 -0
  41. package/.trajectories/completed/2026-01/traj_xy9vifpqet80.json +65 -0
  42. package/.trajectories/completed/2026-01/traj_xy9vifpqet80.md +37 -0
  43. package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.json +49 -0
  44. package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.md +31 -0
  45. package/.trajectories/consolidate-settings-panel.md +24 -0
  46. package/.trajectories/gh-cli-user-token.md +26 -0
  47. package/.trajectories/index.json +155 -1
  48. package/deploy/workspace/codex.config.toml +15 -0
  49. package/deploy/workspace/entrypoint.sh +167 -7
  50. package/deploy/workspace/git-credential-relay +17 -2
  51. package/dist/bridge/spawner.d.ts +7 -0
  52. package/dist/bridge/spawner.js +40 -9
  53. package/dist/bridge/types.d.ts +2 -0
  54. package/dist/cli/index.js +210 -168
  55. package/dist/cloud/api/admin.d.ts +8 -0
  56. package/dist/cloud/api/admin.js +212 -0
  57. package/dist/cloud/api/auth.js +8 -0
  58. package/dist/cloud/api/billing.d.ts +0 -10
  59. package/dist/cloud/api/billing.js +248 -58
  60. package/dist/cloud/api/codex-auth-helper.d.ts +10 -4
  61. package/dist/cloud/api/codex-auth-helper.js +215 -8
  62. package/dist/cloud/api/coordinators.js +402 -0
  63. package/dist/cloud/api/daemons.js +15 -11
  64. package/dist/cloud/api/git.js +104 -17
  65. package/dist/cloud/api/github-app.js +42 -8
  66. package/dist/cloud/api/nango-auth.js +297 -16
  67. package/dist/cloud/api/onboarding.js +97 -33
  68. package/dist/cloud/api/providers.js +12 -16
  69. package/dist/cloud/api/repos.js +200 -124
  70. package/dist/cloud/api/test-helpers.js +40 -0
  71. package/dist/cloud/api/usage.js +13 -0
  72. package/dist/cloud/api/webhooks.js +1 -1
  73. package/dist/cloud/api/workspaces.d.ts +18 -0
  74. package/dist/cloud/api/workspaces.js +945 -15
  75. package/dist/cloud/config.d.ts +8 -0
  76. package/dist/cloud/config.js +15 -0
  77. package/dist/cloud/db/drizzle.d.ts +5 -2
  78. package/dist/cloud/db/drizzle.js +27 -20
  79. package/dist/cloud/db/schema.d.ts +19 -51
  80. package/dist/cloud/db/schema.js +5 -4
  81. package/dist/cloud/index.d.ts +0 -1
  82. package/dist/cloud/index.js +0 -1
  83. package/dist/cloud/provisioner/index.d.ts +93 -1
  84. package/dist/cloud/provisioner/index.js +608 -63
  85. package/dist/cloud/server.js +156 -16
  86. package/dist/cloud/services/compute-enforcement.d.ts +57 -0
  87. package/dist/cloud/services/compute-enforcement.js +175 -0
  88. package/dist/cloud/services/index.d.ts +2 -0
  89. package/dist/cloud/services/index.js +4 -0
  90. package/dist/cloud/services/intro-expiration.d.ts +55 -0
  91. package/dist/cloud/services/intro-expiration.js +211 -0
  92. package/dist/cloud/services/nango.d.ts +14 -0
  93. package/dist/cloud/services/nango.js +74 -14
  94. package/dist/cloud/services/ssh-security.d.ts +31 -0
  95. package/dist/cloud/services/ssh-security.js +63 -0
  96. package/dist/continuity/manager.d.ts +5 -0
  97. package/dist/continuity/manager.js +56 -2
  98. package/dist/daemon/api.d.ts +2 -0
  99. package/dist/daemon/api.js +214 -5
  100. package/dist/daemon/cli-auth.d.ts +13 -1
  101. package/dist/daemon/cli-auth.js +166 -47
  102. package/dist/daemon/connection.d.ts +7 -1
  103. package/dist/daemon/connection.js +15 -0
  104. package/dist/daemon/orchestrator.d.ts +2 -0
  105. package/dist/daemon/orchestrator.js +26 -0
  106. package/dist/daemon/repo-manager.d.ts +116 -0
  107. package/dist/daemon/repo-manager.js +384 -0
  108. package/dist/daemon/router.d.ts +60 -1
  109. package/dist/daemon/router.js +281 -20
  110. package/dist/daemon/user-directory.d.ts +111 -0
  111. package/dist/daemon/user-directory.js +233 -0
  112. package/dist/dashboard/out/404.html +1 -1
  113. package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_ssgManifest.js +1 -0
  114. package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +9 -0
  115. package/dist/dashboard/out/_next/static/chunks/766-b54f0853794b78c3.js +1 -0
  116. package/dist/dashboard/out/_next/static/chunks/83-b51836037078006c.js +1 -0
  117. package/dist/dashboard/out/_next/static/chunks/891-6cd50de1224f70bb.js +1 -0
  118. package/dist/dashboard/out/_next/static/chunks/899-bb19a9b3d9b39ea6.js +1 -0
  119. package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-8939b0fc700f7eca.js +1 -0
  120. package/dist/dashboard/out/_next/static/chunks/app/app/page-5af1b6b439858aa6.js +1 -0
  121. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-f45ecbc3e06134fc.js +1 -0
  122. package/dist/dashboard/out/_next/static/chunks/app/history/{page-abb9ab2d329f56e9.js → page-8c8bed33beb2bf1c.js} +1 -1
  123. package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +1 -0
  124. package/dist/dashboard/out/_next/static/chunks/app/login/{page-c22d080201cbd9fb.js → page-16f3b49e55b1e0ed.js} +1 -1
  125. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-ac39dc0cc3c26fa7.js +1 -0
  126. package/dist/dashboard/out/_next/static/chunks/app/{page-77e9c65420a06cfb.js → page-4a5938c18a11a654.js} +1 -1
  127. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-982a7000fee44014.js +1 -0
  128. package/dist/dashboard/out/_next/static/chunks/app/providers/page-ac3a6ac433fd6001.js +1 -0
  129. package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-09f9caae98a18c09.js +1 -0
  130. package/dist/dashboard/out/_next/static/chunks/app/signup/{page-68d34f50baa8ab6b.js → page-547dd0ca55ecd0ba.js} +1 -1
  131. package/dist/dashboard/out/_next/static/chunks/{main-ed4e1fb6f29c34cf.js → main-2ee6beb2ae96d210.js} +1 -1
  132. package/dist/dashboard/out/_next/static/chunks/{main-app-6e8e8d3ef4e0192a.js → main-app-5d692157a8eb1fd9.js} +1 -1
  133. package/dist/dashboard/out/_next/static/css/85d2af9c7ac74d62.css +1 -0
  134. package/dist/dashboard/out/_next/static/css/fe4b28883eeff359.css +1 -0
  135. package/dist/dashboard/out/app/onboarding.html +1 -1
  136. package/dist/dashboard/out/app/onboarding.txt +3 -3
  137. package/dist/dashboard/out/app.html +1 -1
  138. package/dist/dashboard/out/app.txt +3 -3
  139. package/dist/dashboard/out/apple-icon.png +0 -0
  140. package/dist/dashboard/out/connect-repos.html +1 -1
  141. package/dist/dashboard/out/connect-repos.txt +3 -3
  142. package/dist/dashboard/out/history.html +1 -1
  143. package/dist/dashboard/out/history.txt +3 -3
  144. package/dist/dashboard/out/index.html +1 -1
  145. package/dist/dashboard/out/index.txt +3 -3
  146. package/dist/dashboard/out/login.html +2 -2
  147. package/dist/dashboard/out/login.txt +3 -3
  148. package/dist/dashboard/out/metrics.html +1 -1
  149. package/dist/dashboard/out/metrics.txt +3 -3
  150. package/dist/dashboard/out/pricing.html +2 -2
  151. package/dist/dashboard/out/pricing.txt +3 -3
  152. package/dist/dashboard/out/providers/setup/claude.html +1 -0
  153. package/dist/dashboard/out/providers/setup/claude.txt +8 -0
  154. package/dist/dashboard/out/providers/setup/codex.html +1 -0
  155. package/dist/dashboard/out/providers/setup/codex.txt +8 -0
  156. package/dist/dashboard/out/providers.html +1 -1
  157. package/dist/dashboard/out/providers.txt +3 -3
  158. package/dist/dashboard/out/signup.html +2 -2
  159. package/dist/dashboard/out/signup.txt +3 -3
  160. package/dist/dashboard-server/server.js +316 -12
  161. package/dist/dashboard-server/user-bridge.d.ts +103 -0
  162. package/dist/dashboard-server/user-bridge.js +189 -0
  163. package/dist/protocol/channels.d.ts +205 -0
  164. package/dist/protocol/channels.js +154 -0
  165. package/dist/protocol/types.d.ts +13 -1
  166. package/dist/resiliency/provider-context.js +2 -0
  167. package/dist/shared/cli-auth-config.d.ts +19 -0
  168. package/dist/shared/cli-auth-config.js +58 -2
  169. package/dist/utils/agent-config.js +1 -1
  170. package/dist/wrapper/auth-detection.d.ts +49 -0
  171. package/dist/wrapper/auth-detection.js +192 -0
  172. package/dist/wrapper/base-wrapper.d.ts +153 -0
  173. package/dist/wrapper/base-wrapper.js +393 -0
  174. package/dist/wrapper/client.d.ts +7 -1
  175. package/dist/wrapper/client.js +3 -0
  176. package/dist/wrapper/index.d.ts +1 -0
  177. package/dist/wrapper/index.js +4 -3
  178. package/dist/wrapper/pty-wrapper.d.ts +62 -84
  179. package/dist/wrapper/pty-wrapper.js +154 -180
  180. package/dist/wrapper/tmux-wrapper.d.ts +41 -66
  181. package/dist/wrapper/tmux-wrapper.js +90 -134
  182. package/package.json +4 -2
  183. package/scripts/postinstall.js +11 -155
  184. package/scripts/test-interactive-terminal.sh +248 -0
  185. package/dist/cloud/vault/index.d.ts +0 -76
  186. package/dist/cloud/vault/index.js +0 -219
  187. package/dist/dashboard/out/_next/static/chunks/699-3b1cd6618a45d259.js +0 -1
  188. package/dist/dashboard/out/_next/static/chunks/724-2dae7627550ab88f.js +0 -9
  189. package/dist/dashboard/out/_next/static/chunks/766-1f2dd8cb7f766b0b.js +0 -1
  190. package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-3fdfa60e53f2810d.js +0 -1
  191. package/dist/dashboard/out/_next/static/chunks/app/app/page-e6381e5a6e1fbcfd.js +0 -1
  192. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-3538dfe0ffe984b8.js +0 -1
  193. package/dist/dashboard/out/_next/static/chunks/app/layout-c0d118c0f92d969c.js +0 -1
  194. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-67a3e98d9a43a6ed.js +0 -1
  195. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-b08ed1c34d14434a.js +0 -1
  196. package/dist/dashboard/out/_next/static/chunks/app/providers/page-e88bc117ef7671c3.js +0 -1
  197. package/dist/dashboard/out/_next/static/css/29852f26181969a0.css +0 -1
  198. package/dist/dashboard/out/_next/static/css/7c3ae9e8617d42a5.css +0 -1
  199. package/dist/dashboard/out/_next/static/wPgKJtcOmTFLpUncDg16A/_ssgManifest.js +0 -1
  200. /package/dist/dashboard/out/_next/static/{wPgKJtcOmTFLpUncDg16A → T1tgCqVWHFIkV7ClEtzD7}/_buildManifest.js +0 -0
@@ -0,0 +1,393 @@
1
+ /**
2
+ * BaseWrapper - Abstract base class for agent wrappers
3
+ *
4
+ * Provides shared functionality between TmuxWrapper and PtyWrapper:
5
+ * - Message queue management and deduplication
6
+ * - Spawn/release command parsing and execution
7
+ * - Continuity integration (agent ID, summary saving)
8
+ * - Relay command handling
9
+ * - Line joining for multi-line commands
10
+ *
11
+ * Subclasses implement:
12
+ * - start() - Initialize and start the agent process
13
+ * - stop() - Stop the agent process
14
+ * - performInjection() - Inject content into the agent
15
+ * - getCleanOutput() - Get cleaned output for parsing
16
+ */
17
+ import { EventEmitter } from 'node:events';
18
+ import { RelayClient } from './client.js';
19
+ import { isPlaceholderTarget } from './parser.js';
20
+ import { getDefaultRelayPrefix, detectCliType, createInjectionMetrics, } from './shared.js';
21
+ import { getContinuityManager, parseContinuityCommand, hasContinuityCommand, } from '../continuity/index.js';
22
+ /**
23
+ * Abstract base class for agent wrappers
24
+ */
25
+ export class BaseWrapper extends EventEmitter {
26
+ config;
27
+ client;
28
+ relayPrefix;
29
+ cliType;
30
+ running = false;
31
+ // Message queue state
32
+ messageQueue = [];
33
+ sentMessageHashes = new Set();
34
+ isInjecting = false;
35
+ receivedMessageIds = new Set();
36
+ injectionMetrics = createInjectionMetrics();
37
+ // Spawn/release state
38
+ processedSpawnCommands = new Set();
39
+ processedReleaseCommands = new Set();
40
+ pendingFencedSpawn = null;
41
+ // Continuity state
42
+ continuity;
43
+ agentId;
44
+ processedContinuityCommands = new Set();
45
+ sessionEndProcessed = false;
46
+ sessionEndData;
47
+ lastSummaryRawContent = '';
48
+ constructor(config) {
49
+ super();
50
+ this.config = config;
51
+ this.relayPrefix = config.relayPrefix ?? getDefaultRelayPrefix();
52
+ this.cliType = config.cliType ?? detectCliType(config.command);
53
+ // Initialize relay client with full config
54
+ this.client = new RelayClient({
55
+ agentName: config.name,
56
+ socketPath: config.socketPath,
57
+ cli: this.cliType,
58
+ task: config.task,
59
+ workingDirectory: config.cwd,
60
+ quiet: true,
61
+ });
62
+ // Initialize continuity manager
63
+ this.continuity = getContinuityManager({ defaultCli: this.cliType });
64
+ // Set up message handler
65
+ this.client.onMessage = (from, payload, messageId, meta, originalTo) => {
66
+ this.handleIncomingMessage(from, payload, messageId, meta, originalTo);
67
+ };
68
+ }
69
+ // =========================================================================
70
+ // Common getters
71
+ // =========================================================================
72
+ get isRunning() {
73
+ return this.running;
74
+ }
75
+ get name() {
76
+ return this.config.name;
77
+ }
78
+ getAgentId() {
79
+ return this.agentId;
80
+ }
81
+ getInjectionMetrics() {
82
+ const total = this.injectionMetrics.total;
83
+ const successes = this.injectionMetrics.successFirstTry + this.injectionMetrics.successWithRetry;
84
+ const successRate = total > 0
85
+ ? (successes / total) * 100
86
+ : 100;
87
+ return {
88
+ ...this.injectionMetrics,
89
+ successRate,
90
+ };
91
+ }
92
+ get pendingMessageCount() {
93
+ return this.messageQueue.length;
94
+ }
95
+ // =========================================================================
96
+ // Message handling
97
+ // =========================================================================
98
+ /**
99
+ * Handle incoming message from relay
100
+ */
101
+ handleIncomingMessage(from, payload, messageId, meta, originalTo) {
102
+ // Deduplicate by message ID
103
+ if (this.receivedMessageIds.has(messageId))
104
+ return;
105
+ this.receivedMessageIds.add(messageId);
106
+ // Limit dedup set size
107
+ if (this.receivedMessageIds.size > 1000) {
108
+ const oldest = this.receivedMessageIds.values().next().value;
109
+ if (oldest)
110
+ this.receivedMessageIds.delete(oldest);
111
+ }
112
+ // Queue the message
113
+ const queuedMsg = {
114
+ from,
115
+ body: payload.body,
116
+ messageId,
117
+ thread: payload.thread,
118
+ importance: meta?.importance,
119
+ data: payload.data,
120
+ originalTo,
121
+ };
122
+ this.messageQueue.push(queuedMsg);
123
+ }
124
+ /**
125
+ * Send a relay command via the client
126
+ */
127
+ sendRelayCommand(cmd) {
128
+ // Validate target
129
+ if (isPlaceholderTarget(cmd.to))
130
+ return;
131
+ // Create hash for deduplication (use first 100 chars of body)
132
+ const hash = `${cmd.to}:${cmd.body.substring(0, 100)}`;
133
+ if (this.sentMessageHashes.has(hash))
134
+ return;
135
+ this.sentMessageHashes.add(hash);
136
+ // Limit hash set size
137
+ if (this.sentMessageHashes.size > 500) {
138
+ const oldest = this.sentMessageHashes.values().next().value;
139
+ if (oldest)
140
+ this.sentMessageHashes.delete(oldest);
141
+ }
142
+ // Only send if client ready
143
+ if (this.client.state !== 'READY')
144
+ return;
145
+ this.client.sendMessage(cmd.to, cmd.body, cmd.kind, cmd.data, cmd.thread);
146
+ }
147
+ // =========================================================================
148
+ // Spawn/release handling
149
+ // =========================================================================
150
+ /**
151
+ * Parse spawn and release commands from output
152
+ */
153
+ parseSpawnReleaseCommands(content) {
154
+ // Single-line spawn: ->relay:spawn Name cli "task"
155
+ const spawnPattern = new RegExp(`${this.relayPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}spawn\\s+(\\w+)\\s+(\\w+)\\s+"([^"]+)"`);
156
+ const spawnMatch = content.match(spawnPattern);
157
+ if (spawnMatch) {
158
+ const [, name, cli, task] = spawnMatch;
159
+ const cmdHash = `spawn:${name}:${cli}:${task}`;
160
+ if (!this.processedSpawnCommands.has(cmdHash)) {
161
+ this.processedSpawnCommands.add(cmdHash);
162
+ this.executeSpawn(name, cli, task);
163
+ }
164
+ }
165
+ // Fenced spawn: ->relay:spawn Name cli <<<\ntask\n>>>
166
+ const fencedSpawnPattern = new RegExp(`${this.relayPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}spawn\\s+(\\w+)\\s+(\\w+)\\s+<<<\\n?([\\s\\S]*?)>>>`);
167
+ const fencedSpawnMatch = content.match(fencedSpawnPattern);
168
+ if (fencedSpawnMatch) {
169
+ const [, name, cli, task] = fencedSpawnMatch;
170
+ const cmdHash = `spawn:${name}:${cli}:${task.trim()}`;
171
+ if (!this.processedSpawnCommands.has(cmdHash)) {
172
+ this.processedSpawnCommands.add(cmdHash);
173
+ this.executeSpawn(name, cli, task.trim());
174
+ }
175
+ }
176
+ // Release: ->relay:release Name
177
+ const releasePattern = new RegExp(`${this.relayPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}release\\s+(\\w+)`);
178
+ const releaseMatch = content.match(releasePattern);
179
+ if (releaseMatch) {
180
+ const name = releaseMatch[1];
181
+ const cmdHash = `release:${name}`;
182
+ if (!this.processedReleaseCommands.has(cmdHash)) {
183
+ this.processedReleaseCommands.add(cmdHash);
184
+ this.executeRelease(name);
185
+ }
186
+ }
187
+ }
188
+ /**
189
+ * Execute a spawn command
190
+ */
191
+ async executeSpawn(name, cli, task) {
192
+ // Try dashboard API first
193
+ if (this.config.dashboardPort) {
194
+ try {
195
+ const response = await fetch(`http://localhost:${this.config.dashboardPort}/api/agents/spawn`, {
196
+ method: 'POST',
197
+ headers: { 'Content-Type': 'application/json' },
198
+ body: JSON.stringify({ name, cli, task }),
199
+ });
200
+ if (response.ok)
201
+ return;
202
+ }
203
+ catch {
204
+ // Fall through to callback
205
+ }
206
+ }
207
+ // Use callback
208
+ if (this.config.onSpawn) {
209
+ await this.config.onSpawn(name, cli, task);
210
+ }
211
+ }
212
+ /**
213
+ * Execute a release command
214
+ */
215
+ async executeRelease(name) {
216
+ // Try dashboard API first
217
+ if (this.config.dashboardPort) {
218
+ try {
219
+ const response = await fetch(`http://localhost:${this.config.dashboardPort}/api/agents/${name}`, { method: 'DELETE' });
220
+ if (response.ok)
221
+ return;
222
+ }
223
+ catch {
224
+ // Fall through to callback
225
+ }
226
+ }
227
+ // Use callback
228
+ if (this.config.onRelease) {
229
+ await this.config.onRelease(name);
230
+ }
231
+ }
232
+ // =========================================================================
233
+ // Continuity handling
234
+ // =========================================================================
235
+ /**
236
+ * Initialize agent ID for continuity/resume
237
+ */
238
+ async initializeAgentId() {
239
+ if (!this.continuity)
240
+ return;
241
+ try {
242
+ let ledger;
243
+ // If resuming, try to find previous ledger
244
+ if (this.config.resumeAgentId) {
245
+ ledger = await this.continuity.findLedgerByAgentId(this.config.resumeAgentId);
246
+ if (ledger) {
247
+ console.log(`[${this.config.name}] Resuming agent ID: ${ledger.agentId}`);
248
+ }
249
+ }
250
+ // Otherwise get or create
251
+ if (!ledger) {
252
+ ledger = await this.continuity.getOrCreateLedger(this.config.name, this.cliType);
253
+ console.log(`[${this.config.name}] Agent ID: ${ledger.agentId}`);
254
+ }
255
+ this.agentId = ledger.agentId;
256
+ }
257
+ catch (err) {
258
+ console.error(`[${this.config.name}] Failed to initialize agent ID: ${err.message}`);
259
+ }
260
+ }
261
+ /**
262
+ * Parse continuity commands from output
263
+ */
264
+ async parseContinuityCommands(content) {
265
+ if (!this.continuity)
266
+ return;
267
+ if (!hasContinuityCommand(content))
268
+ return;
269
+ const command = parseContinuityCommand(content);
270
+ if (!command)
271
+ return;
272
+ // Deduplication
273
+ const cmdHash = `${command.type}:${command.content || command.query || command.item || 'no-content'}`;
274
+ if (command.content && this.processedContinuityCommands.has(cmdHash))
275
+ return;
276
+ this.processedContinuityCommands.add(cmdHash);
277
+ // Limit dedup set size
278
+ if (this.processedContinuityCommands.size > 100) {
279
+ const oldest = this.processedContinuityCommands.values().next().value;
280
+ if (oldest)
281
+ this.processedContinuityCommands.delete(oldest);
282
+ }
283
+ try {
284
+ const response = await this.continuity.handleCommand(this.config.name, command);
285
+ if (response) {
286
+ // Queue response for injection
287
+ this.messageQueue.push({
288
+ from: 'system',
289
+ body: response,
290
+ messageId: `continuity-${Date.now()}`,
291
+ thread: 'continuity-response',
292
+ });
293
+ }
294
+ }
295
+ catch (err) {
296
+ console.error(`[${this.config.name}] Continuity command error: ${err.message}`);
297
+ }
298
+ }
299
+ /**
300
+ * Save a parsed summary to the continuity ledger
301
+ */
302
+ async saveSummaryToLedger(summary) {
303
+ if (!this.continuity)
304
+ return;
305
+ const updates = {};
306
+ if (summary.currentTask) {
307
+ updates.currentTask = summary.currentTask;
308
+ }
309
+ if (summary.completedTasks && summary.completedTasks.length > 0) {
310
+ updates.completed = summary.completedTasks;
311
+ }
312
+ if (summary.context) {
313
+ updates.inProgress = [summary.context];
314
+ }
315
+ if (summary.files && summary.files.length > 0) {
316
+ updates.fileContext = summary.files.map((f) => ({ path: f }));
317
+ }
318
+ if (Object.keys(updates).length > 0) {
319
+ await this.continuity.saveLedger(this.config.name, updates);
320
+ console.log(`[${this.config.name}] Saved summary to continuity ledger`);
321
+ }
322
+ }
323
+ /**
324
+ * Reset session-specific state for wrapper reuse
325
+ */
326
+ resetSessionState() {
327
+ this.sessionEndProcessed = false;
328
+ this.lastSummaryRawContent = '';
329
+ this.sessionEndData = undefined;
330
+ }
331
+ // =========================================================================
332
+ // Utility methods
333
+ // =========================================================================
334
+ /**
335
+ * Join continuation lines for multi-line relay/continuity commands.
336
+ * TUIs like Claude Code insert real newlines in output, causing
337
+ * messages to span multiple lines. This joins indented
338
+ * continuation lines back to the command line.
339
+ */
340
+ joinContinuationLines(content) {
341
+ const lines = content.split('\n');
342
+ const result = [];
343
+ // Pattern to detect relay OR continuity command line (with optional bullet prefix)
344
+ const escapedPrefix = this.relayPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
345
+ const commandPattern = new RegExp(`^(?:\\s*(?:[>$%#→➜›»●•◦‣⁃\\-*⏺◆◇○□■]\\s*)*)?(?:${escapedPrefix}|->continuity:)`);
346
+ // Pattern to detect a continuation line (starts with spaces, no bullet/command)
347
+ const continuationPattern = /^[ \t]+[^>$%#→➜›»●•◦‣⁃\-*⏺◆◇○□■\s]/;
348
+ // Pattern to detect a new block/bullet (stops continuation)
349
+ const newBlockPattern = /^(?:\s*)?[>$%#→➜›»●•◦‣⁃\-*⏺◆◇○□■]/;
350
+ let i = 0;
351
+ while (i < lines.length) {
352
+ const line = lines[i];
353
+ // Check if this is a command line
354
+ if (commandPattern.test(line)) {
355
+ let joined = line;
356
+ let j = i + 1;
357
+ // Look ahead for continuation lines
358
+ while (j < lines.length) {
359
+ const nextLine = lines[j];
360
+ // Empty line stops continuation
361
+ if (nextLine.trim() === '')
362
+ break;
363
+ // New bullet/block stops continuation
364
+ if (newBlockPattern.test(nextLine))
365
+ break;
366
+ // Check if it looks like a continuation (indented text)
367
+ if (continuationPattern.test(nextLine)) {
368
+ // Join with newline to preserve multi-line message content
369
+ joined += '\n' + nextLine.trim();
370
+ j++;
371
+ }
372
+ else {
373
+ break;
374
+ }
375
+ }
376
+ result.push(joined);
377
+ i = j; // Skip the lines we joined
378
+ }
379
+ else {
380
+ result.push(line);
381
+ i++;
382
+ }
383
+ }
384
+ return result.join('\n');
385
+ }
386
+ /**
387
+ * Clean up resources
388
+ */
389
+ destroyClient() {
390
+ this.client.destroy();
391
+ }
392
+ }
393
+ //# sourceMappingURL=base-wrapper.js.map
@@ -2,11 +2,13 @@
2
2
  * Relay Client
3
3
  * Connects to the daemon and handles message sending/receiving.
4
4
  */
5
- import { type SendPayload, type SendMeta, type PayloadKind, type SpeakOnTrigger } from '../protocol/types.js';
5
+ import { type SendPayload, type SendMeta, type PayloadKind, type SpeakOnTrigger, type EntityType } from '../protocol/types.js';
6
6
  export type ClientState = 'DISCONNECTED' | 'CONNECTING' | 'HANDSHAKING' | 'READY' | 'BACKOFF';
7
7
  export interface ClientConfig {
8
8
  socketPath: string;
9
9
  agentName: string;
10
+ /** Entity type: 'agent' (default) or 'user' for human users */
11
+ entityType?: EntityType;
10
12
  /** Optional CLI identifier to surface to the dashboard */
11
13
  cli?: string;
12
14
  /** Optional program identifier (e.g., 'claude', 'gpt-4o') */
@@ -17,6 +19,10 @@ export interface ClientConfig {
17
19
  task?: string;
18
20
  /** Optional working directory to surface in registry/dashboard */
19
21
  workingDirectory?: string;
22
+ /** Display name for human users */
23
+ displayName?: string;
24
+ /** Avatar URL for human users */
25
+ avatarUrl?: string;
20
26
  /** Suppress client-side console logging */
21
27
  quiet?: boolean;
22
28
  reconnect: boolean;
@@ -280,11 +280,14 @@ export class RelayClient {
280
280
  ts: Date.now(),
281
281
  payload: {
282
282
  agent: this.config.agentName,
283
+ entityType: this.config.entityType,
283
284
  cli: this.config.cli,
284
285
  program: this.config.program,
285
286
  model: this.config.model,
286
287
  task: this.config.task,
287
288
  workingDirectory: this.config.workingDirectory,
289
+ displayName: this.config.displayName,
290
+ avatarUrl: this.config.avatarUrl,
288
291
  capabilities: {
289
292
  ack: true,
290
293
  resume: true,
@@ -1,3 +1,4 @@
1
1
  export * from './client.js';
2
2
  export * from './parser.js';
3
+ export * from './base-wrapper.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1,6 +1,7 @@
1
1
  export * from './client.js';
2
2
  export * from './parser.js';
3
- // Note: tmux-wrapper.ts is intentionally not exported here.
4
- // It's dynamically imported in CLI only, as it has different
5
- // runtime requirements (tmux must be installed).
3
+ export * from './base-wrapper.js';
4
+ // Note: tmux-wrapper.ts and pty-wrapper.ts are intentionally not exported here.
5
+ // They're dynamically imported in CLI only, as they have different
6
+ // runtime requirements (tmux/pty must be available).
6
7
  //# sourceMappingURL=index.js.map