@nordbyte/nordrelay 0.8.1 → 0.8.3

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 (179) hide show
  1. package/.env.example +9 -0
  2. package/README.md +84 -1205
  3. package/dist/{access-control.js → access/access-control.js} +1 -1
  4. package/dist/{audit-log.js → access/audit-log.js} +32 -15
  5. package/dist/{session-locks.js → access/session-locks.js} +1 -1
  6. package/dist/{user-management.js → access/user-management.js} +1 -1
  7. package/dist/{claude-code-cli.js → agents/claude-code/claude-code-cli.js} +2 -2
  8. package/dist/{claude-code-session.js → agents/claude-code/claude-code-session.js} +1 -1
  9. package/dist/{codex-cli.js → agents/codex/codex-cli.js} +14 -5
  10. package/dist/{codex-session.js → agents/codex/codex-session.js} +2 -4
  11. package/dist/{hermes-cli.js → agents/hermes/hermes-cli.js} +2 -2
  12. package/dist/{hermes-launch.js → agents/hermes/hermes-launch.js} +1 -1
  13. package/dist/{hermes-session.js → agents/hermes/hermes-session.js} +1 -1
  14. package/dist/{openclaw-cli.js → agents/openclaw/openclaw-cli.js} +2 -2
  15. package/dist/{openclaw-launch.js → agents/openclaw/openclaw-launch.js} +1 -1
  16. package/dist/{openclaw-session.js → agents/openclaw/openclaw-session.js} +1 -1
  17. package/dist/{pi-cli.js → agents/pi/pi-cli.js} +2 -2
  18. package/dist/{pi-launch.js → agents/pi/pi-launch.js} +1 -1
  19. package/dist/{pi-session.js → agents/pi/pi-session.js} +1 -1
  20. package/dist/{adapter-conformance.js → agents/shared/adapter-conformance.js} +2 -2
  21. package/dist/{agent-activity.js → agents/shared/agent-activity.js} +5 -5
  22. package/dist/agents/shared/agent-auth-commands.js +30 -0
  23. package/dist/{agent-factory.js → agents/shared/agent-factory.js} +5 -5
  24. package/dist/{agent-feature-matrix.js → agents/shared/agent-feature-matrix.js} +2 -2
  25. package/dist/{agent-updates.js → agents/shared/agent-updates.js} +7 -7
  26. package/dist/{discord-artifacts.js → channels/discord/discord-artifacts.js} +4 -4
  27. package/dist/{discord-bot.js → channels/discord/discord-bot.js} +176 -451
  28. package/dist/{discord-channel-runtime.js → channels/discord/discord-channel-runtime.js} +2 -2
  29. package/dist/{discord-command-surface.js → channels/discord/discord-command-surface.js} +3 -3
  30. package/dist/{bot-rendering.js → channels/shared/bot-rendering.js} +6 -6
  31. package/dist/{channel-actions.js → channels/shared/channel-actions.js} +4 -4
  32. package/dist/channels/shared/channel-bridge-controller.js +69 -0
  33. package/dist/channels/shared/channel-cli-artifacts.js +51 -0
  34. package/dist/{channel-command-service.js → channels/shared/channel-command-service.js} +51 -28
  35. package/dist/channels/shared/channel-external-mirror-controller.js +193 -0
  36. package/dist/channels/shared/channel-external-monitor.js +52 -0
  37. package/dist/{channel-mirror-registry.js → channels/shared/channel-mirror-registry.js} +14 -6
  38. package/dist/{channel-peer-prompt.js → channels/shared/channel-peer-prompt.js} +3 -3
  39. package/dist/channels/shared/channel-prompt-queue.js +37 -0
  40. package/dist/{channel-turn-service.js → channels/shared/channel-turn-service.js} +25 -11
  41. package/dist/{context-key.js → channels/shared/context-key.js} +1 -1
  42. package/dist/{session-format.js → channels/shared/session-format.js} +2 -2
  43. package/dist/{slack-artifacts.js → channels/slack/slack-artifacts.js} +4 -4
  44. package/dist/{slack-bot.js → channels/slack/slack-bot.js} +171 -309
  45. package/dist/{slack-channel-runtime.js → channels/slack/slack-channel-runtime.js} +2 -2
  46. package/dist/{slack-command-surface.js → channels/slack/slack-command-surface.js} +2 -2
  47. package/dist/{slack-diagnostics.js → channels/slack/slack-diagnostics.js} +2 -2
  48. package/dist/{bot-ui.js → channels/telegram/bot-ui.js} +1 -1
  49. package/dist/{bot.js → channels/telegram/bot.js} +195 -430
  50. package/dist/{telegram-access-commands.js → channels/telegram/telegram-access-commands.js} +3 -3
  51. package/dist/{telegram-access-middleware.js → channels/telegram/telegram-access-middleware.js} +4 -4
  52. package/dist/{telegram-agent-commands.js → channels/telegram/telegram-agent-commands.js} +9 -9
  53. package/dist/{telegram-artifact-commands.js → channels/telegram/telegram-artifact-commands.js} +4 -4
  54. package/dist/{telegram-channel-runtime.js → channels/telegram/telegram-channel-runtime.js} +2 -2
  55. package/dist/{telegram-command-menu.js → channels/telegram/telegram-command-menu.js} +1 -1
  56. package/dist/{telegram-diagnostics-command.js → channels/telegram/telegram-diagnostics-command.js} +7 -7
  57. package/dist/{telegram-general-commands.js → channels/telegram/telegram-general-commands.js} +4 -4
  58. package/dist/{telegram-operational-commands.js → channels/telegram/telegram-operational-commands.js} +5 -5
  59. package/dist/{telegram-output.js → channels/telegram/telegram-output.js} +2 -2
  60. package/dist/{telegram-preference-commands.js → channels/telegram/telegram-preference-commands.js} +3 -3
  61. package/dist/{telegram-queue-commands.js → channels/telegram/telegram-queue-commands.js} +6 -6
  62. package/dist/{telegram-support-command.js → channels/telegram/telegram-support-command.js} +4 -4
  63. package/dist/{telegram-update-commands.js → channels/telegram/telegram-update-commands.js} +5 -5
  64. package/dist/{config-metadata.js → core/config-metadata.js} +8 -0
  65. package/dist/{config.js → core/config.js} +11 -3
  66. package/dist/core/pagination.js +22 -0
  67. package/dist/index.js +27 -23
  68. package/dist/peers/peer-discovery-jobs.js +206 -0
  69. package/dist/peers/peer-discovery.js +223 -0
  70. package/dist/peers/peer-health-monitor.js +49 -0
  71. package/dist/{peer-identity.js → peers/peer-identity.js} +50 -1
  72. package/dist/{peer-runtime-service.js → peers/peer-runtime-service.js} +29 -7
  73. package/dist/{peer-server.js → peers/peer-server.js} +3 -2
  74. package/dist/{peer-store.js → peers/peer-store.js} +96 -9
  75. package/dist/{peer-types.js → peers/peer-types.js} +28 -0
  76. package/dist/peers/peer-web-proxy-contract.js +129 -0
  77. package/dist/{metrics.js → runtime/metrics.js} +5 -3
  78. package/dist/{relay-artifact-service.js → runtime/relay-artifact-service.js} +1 -1
  79. package/dist/runtime/relay-auth-service.js +63 -0
  80. package/dist/runtime/relay-dashboard-service.js +139 -0
  81. package/dist/{relay-external-activity-monitor.js → runtime/relay-external-activity-monitor.js} +155 -53
  82. package/dist/{relay-queue-service.js → runtime/relay-queue-service.js} +1 -0
  83. package/dist/runtime/relay-runtime-active-sessions.js +387 -0
  84. package/dist/runtime/relay-runtime-dashboard.js +204 -0
  85. package/dist/{relay-runtime-helpers.js → runtime/relay-runtime-helpers.js} +3 -0
  86. package/dist/runtime/relay-runtime-prompt-queue-artifacts.js +311 -0
  87. package/dist/runtime/relay-runtime-sessions.js +631 -0
  88. package/dist/runtime/relay-runtime-trace.js +92 -0
  89. package/dist/runtime/relay-runtime-types.js +1 -0
  90. package/dist/runtime/relay-runtime-updates-jobs.js +366 -0
  91. package/dist/runtime/relay-runtime.js +461 -0
  92. package/dist/runtime/runtime-cache.js +117 -0
  93. package/dist/{prompt-store.js → state/prompt-store.js} +13 -1
  94. package/dist/{session-registry.js → state/session-registry.js} +3 -3
  95. package/dist/{operations.js → support/operations.js} +7 -7
  96. package/dist/{support-bundle.js → support/support-bundle.js} +1 -1
  97. package/dist/{web-api-contract.js → web/web-api-contract.js} +19 -3
  98. package/dist/web/web-api-types.js +1 -0
  99. package/dist/{web-dashboard-access-routes.js → web/web-dashboard-access-routes.js} +17 -14
  100. package/dist/{web-dashboard-artifact-routes.js → web/web-dashboard-artifact-routes.js} +6 -2
  101. package/dist/{web-dashboard-assets.js → web/web-dashboard-assets.js} +25 -2
  102. package/dist/{web-dashboard-http.js → web/web-dashboard-http.js} +41 -5
  103. package/dist/{web-dashboard-pages.js → web/web-dashboard-pages.js} +95 -30
  104. package/dist/{web-dashboard-peer-routes.js → web/web-dashboard-peer-routes.js} +121 -7
  105. package/dist/{web-dashboard-runtime-routes.js → web/web-dashboard-runtime-routes.js} +8 -1
  106. package/dist/web/web-dashboard-security.js +14 -0
  107. package/dist/{web-dashboard-session-routes.js → web/web-dashboard-session-routes.js} +29 -13
  108. package/dist/web/web-dashboard-ui.js +56 -0
  109. package/dist/{web-dashboard.js → web/web-dashboard.js} +132 -48
  110. package/dist/web/web-performance.js +62 -0
  111. package/dist/web/web-rate-limit.js +19 -0
  112. package/dist/{web-state.js → web/web-state.js} +107 -9
  113. package/dist/webui-assets/dashboard.css +398 -49
  114. package/dist/webui-assets/dashboard.js +1239 -103
  115. package/dist/webui-assets/favicon.ico +0 -0
  116. package/dist/webui-assets/favicon.png +0 -0
  117. package/dist/webui-assets/logo.png +0 -0
  118. package/package.json +6 -3
  119. package/plugins/nordrelay/scripts/nordrelay.mjs +346 -12
  120. package/plugins/nordrelay/scripts/service-installer.mjs +183 -0
  121. package/{launchd/start.sh → scripts/launchd-start.sh} +1 -1
  122. package/scripts/postinstall.mjs +122 -0
  123. package/dist/relay-runtime.js +0 -1916
  124. package/dist/runtime-cache.js +0 -57
  125. package/dist/web-dashboard-ui.js +0 -20
  126. /package/dist/{user-management-crypto.js → access/user-management-crypto.js} +0 -0
  127. /package/dist/{user-management-normalize.js → access/user-management-normalize.js} +0 -0
  128. /package/dist/{user-management-types.js → access/user-management-types.js} +0 -0
  129. /package/dist/{claude-code-auth.js → agents/claude-code/claude-code-auth.js} +0 -0
  130. /package/dist/{claude-code-launch.js → agents/claude-code/claude-code-launch.js} +0 -0
  131. /package/dist/{claude-code-state.js → agents/claude-code/claude-code-state.js} +0 -0
  132. /package/dist/{codex-auth.js → agents/codex/codex-auth.js} +0 -0
  133. /package/dist/{codex-config.js → agents/codex/codex-config.js} +0 -0
  134. /package/dist/{codex-launch.js → agents/codex/codex-launch.js} +0 -0
  135. /package/dist/{codex-state.js → agents/codex/codex-state.js} +0 -0
  136. /package/dist/{hermes-api.js → agents/hermes/hermes-api.js} +0 -0
  137. /package/dist/{hermes-auth.js → agents/hermes/hermes-auth.js} +0 -0
  138. /package/dist/{hermes-state.js → agents/hermes/hermes-state.js} +0 -0
  139. /package/dist/{openclaw-auth.js → agents/openclaw/openclaw-auth.js} +0 -0
  140. /package/dist/{openclaw-gateway.js → agents/openclaw/openclaw-gateway.js} +0 -0
  141. /package/dist/{openclaw-state.js → agents/openclaw/openclaw-state.js} +0 -0
  142. /package/dist/{pi-auth.js → agents/pi/pi-auth.js} +0 -0
  143. /package/dist/{pi-rpc.js → agents/pi/pi-rpc.js} +0 -0
  144. /package/dist/{pi-state.js → agents/pi/pi-state.js} +0 -0
  145. /package/dist/{agent-adapter.js → agents/shared/agent-adapter.js} +0 -0
  146. /package/dist/{agent.js → agents/shared/agent.js} +0 -0
  147. /package/dist/{artifacts.js → artifacts/artifacts.js} +0 -0
  148. /package/dist/{attachments.js → artifacts/attachments.js} +0 -0
  149. /package/dist/{voice.js → artifacts/voice.js} +0 -0
  150. /package/dist/{discord-rate-limit.js → channels/discord/discord-rate-limit.js} +0 -0
  151. /package/dist/{channel-adapter.js → channels/shared/channel-adapter.js} +0 -0
  152. /package/dist/{relay-runtime-types.js → channels/shared/channel-bridge-state.js} +0 -0
  153. /package/dist/{channel-command-catalog.js → channels/shared/channel-command-catalog.js} +0 -0
  154. /package/dist/{channel-command-core.js → channels/shared/channel-command-core.js} +0 -0
  155. /package/dist/{channel-prompt-engine.js → channels/shared/channel-prompt-engine.js} +0 -0
  156. /package/dist/{channel-runtime.js → channels/shared/channel-runtime.js} +0 -0
  157. /package/dist/{channel-turn-lifecycle.js → channels/shared/channel-turn-lifecycle.js} +0 -0
  158. /package/dist/{slack-rate-limit.js → channels/slack/slack-rate-limit.js} +0 -0
  159. /package/dist/{telegram-command-types.js → channels/telegram/telegram-command-types.js} +0 -0
  160. /package/dist/{telegram-rate-limit.js → channels/telegram/telegram-rate-limit.js} +0 -0
  161. /package/dist/{activity-events.js → core/activity-events.js} +0 -0
  162. /package/dist/{error-messages.js → core/error-messages.js} +0 -0
  163. /package/dist/{format.js → core/format.js} +0 -0
  164. /package/dist/{logger.js → core/logger.js} +0 -0
  165. /package/dist/{redaction.js → core/redaction.js} +0 -0
  166. /package/dist/{settings-service.js → core/settings-service.js} +0 -0
  167. /package/dist/{settings-wizard-test.js → core/settings-wizard-test.js} +0 -0
  168. /package/dist/{workspace-policy.js → core/workspace-policy.js} +0 -0
  169. /package/dist/{peer-auth.js → peers/peer-auth.js} +0 -0
  170. /package/dist/{peer-client.js → peers/peer-client.js} +0 -0
  171. /package/dist/{peer-context.js → peers/peer-context.js} +0 -0
  172. /package/dist/{peer-readiness.js → peers/peer-readiness.js} +0 -0
  173. /package/dist/{web-api-types.js → runtime/relay-runtime-delegate.js} +0 -0
  174. /package/dist/{remote-prompt.js → runtime/remote-prompt.js} +0 -0
  175. /package/dist/{bot-preferences.js → state/bot-preferences.js} +0 -0
  176. /package/dist/{job-store.js → state/job-store.js} +0 -0
  177. /package/dist/{persistence.js → state/persistence.js} +0 -0
  178. /package/dist/{state-backend.js → state/state-backend.js} +0 -0
  179. /package/dist/{zip-writer.js → support/zip-writer.js} +0 -0
@@ -1,7 +1,8 @@
1
- import {} from "./agent.js";
2
- import { getExternalSnapshotForSession } from "./agent-activity.js";
3
- import { friendlyErrorText } from "./error-messages.js";
4
- import {} from "./web-state.js";
1
+ import {} from "../agents/shared/agent.js";
2
+ import { getExternalSnapshotForSession } from "../agents/shared/agent-activity.js";
3
+ import { renderExternalMirrorEvent, renderExternalMirrorStatus, trimLine, } from "../channels/shared/bot-rendering.js";
4
+ import { friendlyErrorText } from "../core/error-messages.js";
5
+ import {} from "../web/web-state.js";
5
6
  const CLI_ACTIVITY_ACTOR = {
6
7
  channel: "cli",
7
8
  label: "CLI",
@@ -16,6 +17,9 @@ export class RelayExternalActivityMonitor {
16
17
  snapshot() {
17
18
  return this.mirror ? { ...this.mirror } : null;
18
19
  }
20
+ reset() {
21
+ this.mirror = null;
22
+ }
19
23
  task() {
20
24
  if (!this.mirror) {
21
25
  return null;
@@ -81,7 +85,7 @@ export class RelayExternalActivityMonitor {
81
85
  startedAt: snapshot.activity.startedAt?.toISOString() ?? null,
82
86
  };
83
87
  if (snapshot.activity.active) {
84
- this.startExternalTurn(snapshot, info);
88
+ await this.startExternalTurn(snapshot, info);
85
89
  }
86
90
  return;
87
91
  }
@@ -91,36 +95,53 @@ export class RelayExternalActivityMonitor {
91
95
  mirror.turnId = snapshot.activity.turnId;
92
96
  mirror.startedAt = snapshot.activity.startedAt?.toISOString() ?? null;
93
97
  mirror.latestAgentLine = undefined;
94
- this.startExternalTurn(snapshot, info);
98
+ mirror.latestStatusAt = undefined;
99
+ mirror.latestMirroredEventLine = undefined;
100
+ await this.startExternalTurn(snapshot, info);
101
+ }
102
+ const mirrorMode = this.options.mirrorMode();
103
+ const newEvents = snapshot.events.filter((event) => event.lineNumber > mirror.lastLine);
104
+ this.broadcastExternalEvents(snapshot, newEvents, info, mirrorMode === "full");
105
+ if (mirrorMode === "full") {
106
+ await this.appendExternalEventMessages(snapshot, newEvents, mirror);
95
107
  }
96
- this.broadcastExternalEvents(snapshot, snapshot.events.filter((event) => event.lineNumber > mirror.lastLine), info);
97
108
  mirror.lastLine = Math.max(mirror.lastLine, snapshot.lineCount);
98
109
  mirror.latestStatus = externalStatusLine(snapshot, this.options.queueLength());
99
- this.options.broadcastStatus(mirror.latestStatus, "info");
110
+ if (mirrorMode === "status" || mirrorMode === "full") {
111
+ await this.updateExternalStatusMessage(snapshot, mirror);
112
+ }
113
+ if (mirrorMode !== "off") {
114
+ this.options.broadcastStatus(mirror.latestStatus, "info");
115
+ }
100
116
  return;
101
117
  }
102
118
  const terminalEvent = [...snapshot.events].reverse().find((event) => event.kind === "task" && event.status && event.status !== "started");
103
119
  if (terminalEvent && terminalEvent.lineNumber > mirror.lastLine) {
120
+ const mirrorMode = this.options.mirrorMode();
104
121
  const finalAgent = snapshot.events.filter((event) => event.kind === "agent" && event.text).at(-1);
105
122
  const finalText = finalAgent?.text ?? snapshot.latestAgentMessage;
106
123
  const finalLine = finalAgent?.lineNumber ?? snapshot.lineCount;
107
- if (finalText && finalLine !== mirror.latestAgentLine) {
108
- this.options.chatStore.append({
124
+ if ((mirrorMode === "final" || mirrorMode === "full") && finalText && finalLine !== mirror.latestAgentLine) {
125
+ this.options.chatStore.appendWithResult({
109
126
  threadId: snapshot.threadId,
110
127
  role: "agent",
111
128
  text: finalText,
112
129
  source: "cli",
130
+ correlationId: externalCorrelationId(snapshot),
113
131
  turnId: terminalEvent.turnId ?? undefined,
132
+ key: externalMessageKey("final", snapshot, terminalEvent.lineNumber),
114
133
  });
115
- this.options.broadcast({ type: "text_delta", id: terminalEvent.turnId ?? "cli", delta: finalText });
116
134
  mirror.latestAgentLine = finalLine;
117
135
  }
118
136
  const externalStartedAt = mirror.startedAt ? new Date(mirror.startedAt) : snapshot.activity.startedAt;
119
- this.options.broadcast({
120
- type: "turn_complete",
121
- id: terminalEvent.turnId ?? "cli",
122
- at: terminalEvent.timestamp?.toISOString() ?? new Date().toISOString(),
123
- });
137
+ if (mirrorMode !== "off") {
138
+ this.options.broadcast({
139
+ type: "turn_complete",
140
+ id: terminalEvent.turnId ?? "cli",
141
+ at: terminalEvent.timestamp?.toISOString() ?? new Date().toISOString(),
142
+ correlationId: externalCorrelationId(snapshot),
143
+ });
144
+ }
124
145
  this.options.appendActivity({
125
146
  source: "cli",
126
147
  status: terminalEvent.status === "aborted" ? "aborted" : terminalEvent.status === "failed" ? "failed" : "completed",
@@ -129,6 +150,7 @@ export class RelayExternalActivityMonitor {
129
150
  workspace: info.workspace,
130
151
  agentId: info.agentId,
131
152
  actor: CLI_ACTIVITY_ACTOR,
153
+ correlationId: externalCorrelationId(snapshot),
132
154
  prompt: snapshot.latestUserMessage ?? undefined,
133
155
  detail: `${snapshot.agentLabel} CLI task ${terminalEvent.status ?? "finished"}.`,
134
156
  durationMs: durationFromDates(externalStartedAt, terminalEvent.timestamp),
@@ -137,29 +159,36 @@ export class RelayExternalActivityMonitor {
137
159
  await this.options.persistWorkspaceArtifactsForTurn(info.workspace, terminalEvent.turnId, externalStartedAt);
138
160
  }
139
161
  mirror.latestStatus = `${snapshot.agentLabel} CLI task ${terminalEvent.status ?? "finished"}.`;
140
- this.options.broadcastStatus(`${snapshot.agentLabel} CLI task ${terminalEvent.status ?? "finished"}.`, terminalEvent.status === "failed" ? "error" : terminalEvent.status === "aborted" ? "warn" : "info");
141
- this.options.broadcast({ type: "chat_history", messages: await this.options.chatHistory() });
162
+ if (mirrorMode === "status" || mirrorMode === "full") {
163
+ await this.updateExternalStatusMessage(snapshot, mirror, mirror.latestStatus);
164
+ }
165
+ if (mirrorMode !== "off") {
166
+ this.options.broadcastStatus(`${snapshot.agentLabel} CLI task ${terminalEvent.status ?? "finished"}.`, terminalEvent.status === "failed" ? "error" : terminalEvent.status === "aborted" ? "warn" : "info");
167
+ await this.broadcastChatHistory();
168
+ }
142
169
  await this.options.drainQueue();
143
170
  }
144
171
  mirror.lastLine = Math.max(mirror.lastLine, snapshot.lineCount);
145
172
  }
146
- startExternalTurn(snapshot, info) {
173
+ async startExternalTurn(snapshot, info) {
147
174
  const prompt = snapshot.latestUserMessage ?? `${snapshot.agentLabel} CLI task`;
148
- this.options.chatStore.append({
149
- threadId: snapshot.threadId,
150
- role: "user",
151
- text: prompt,
152
- source: "cli",
153
- turnId: snapshot.activity.turnId ?? undefined,
154
- timestamp: snapshot.activity.startedAt?.toISOString(),
155
- });
156
- this.options.broadcast({
157
- type: "turn_start",
158
- id: snapshot.activity.turnId ?? "cli",
159
- prompt,
160
- at: snapshot.activity.startedAt?.toISOString() ?? new Date().toISOString(),
161
- source: "cli",
162
- });
175
+ const mode = this.options.mirrorMode();
176
+ if (mode === "final" || mode === "full") {
177
+ this.options.chatStore.appendWithResult({
178
+ threadId: snapshot.threadId,
179
+ role: "system",
180
+ text: `Working on ${trimLine(prompt, 500)}`,
181
+ source: "cli",
182
+ correlationId: externalCorrelationId(snapshot),
183
+ turnId: snapshot.activity.turnId ?? undefined,
184
+ timestamp: snapshot.activity.startedAt?.toISOString(),
185
+ key: externalMessageKey("working", snapshot),
186
+ });
187
+ await this.broadcastChatHistory();
188
+ }
189
+ if ((mode === "status" || mode === "full") && this.mirror) {
190
+ await this.updateExternalStatusMessage(snapshot, this.mirror);
191
+ }
163
192
  this.options.appendActivity({
164
193
  source: "cli",
165
194
  status: "running",
@@ -168,19 +197,23 @@ export class RelayExternalActivityMonitor {
168
197
  workspace: info.workspace,
169
198
  agentId: info.agentId,
170
199
  actor: CLI_ACTIVITY_ACTOR,
200
+ correlationId: externalCorrelationId(snapshot),
171
201
  prompt,
172
202
  detail: `${snapshot.sourceLabel}: ${snapshot.sourcePath}`,
173
203
  });
174
204
  }
175
- broadcastExternalEvents(snapshot, events, info) {
205
+ broadcastExternalEvents(snapshot, events, info, broadcastTools) {
176
206
  for (const event of events) {
177
207
  if (event.kind === "tool" && event.status === "started") {
178
- this.options.broadcast({
179
- type: "tool_start",
180
- id: snapshot.activity.turnId ?? "cli",
181
- toolCallId: `cli-${event.lineNumber}`,
182
- toolName: event.toolName ?? "tool",
183
- });
208
+ if (broadcastTools) {
209
+ this.options.broadcast({
210
+ type: "tool_start",
211
+ id: snapshot.activity.turnId ?? "cli",
212
+ toolCallId: `cli-${event.lineNumber}`,
213
+ toolName: event.toolName ?? "tool",
214
+ correlationId: externalCorrelationId(snapshot),
215
+ });
216
+ }
184
217
  this.options.appendActivity({
185
218
  source: "cli",
186
219
  status: "running",
@@ -189,16 +222,20 @@ export class RelayExternalActivityMonitor {
189
222
  workspace: info.workspace,
190
223
  agentId: info.agentId,
191
224
  actor: CLI_ACTIVITY_ACTOR,
225
+ correlationId: externalCorrelationId(snapshot),
192
226
  detail: event.toolName ?? "tool",
193
227
  });
194
228
  }
195
229
  if (event.kind === "tool" && event.status === "finished") {
196
- this.options.broadcast({
197
- type: "tool_end",
198
- id: snapshot.activity.turnId ?? "cli",
199
- toolCallId: `cli-${event.lineNumber}`,
200
- isError: false,
201
- });
230
+ if (broadcastTools) {
231
+ this.options.broadcast({
232
+ type: "tool_end",
233
+ id: snapshot.activity.turnId ?? "cli",
234
+ toolCallId: `cli-${event.lineNumber}`,
235
+ isError: false,
236
+ correlationId: externalCorrelationId(snapshot),
237
+ });
238
+ }
202
239
  this.options.appendActivity({
203
240
  source: "cli",
204
241
  status: "completed",
@@ -207,16 +244,20 @@ export class RelayExternalActivityMonitor {
207
244
  workspace: info.workspace,
208
245
  agentId: info.agentId,
209
246
  actor: CLI_ACTIVITY_ACTOR,
247
+ correlationId: externalCorrelationId(snapshot),
210
248
  detail: event.toolName ?? "tool",
211
249
  });
212
250
  }
213
251
  if (event.kind === "tool" && event.status === "failed") {
214
- this.options.broadcast({
215
- type: "tool_end",
216
- id: snapshot.activity.turnId ?? "cli",
217
- toolCallId: `cli-${event.lineNumber}`,
218
- isError: true,
219
- });
252
+ if (broadcastTools) {
253
+ this.options.broadcast({
254
+ type: "tool_end",
255
+ id: snapshot.activity.turnId ?? "cli",
256
+ toolCallId: `cli-${event.lineNumber}`,
257
+ isError: true,
258
+ correlationId: externalCorrelationId(snapshot),
259
+ });
260
+ }
220
261
  this.options.appendActivity({
221
262
  source: "cli",
222
263
  status: "failed",
@@ -230,6 +271,67 @@ export class RelayExternalActivityMonitor {
230
271
  }
231
272
  }
232
273
  }
274
+ async appendExternalEventMessages(snapshot, events, mirror) {
275
+ let changed = false;
276
+ for (const event of events) {
277
+ if (event.lineNumber <= (mirror.latestMirroredEventLine ?? mirror.lastLine)) {
278
+ continue;
279
+ }
280
+ const rendered = renderExternalMirrorEvent(event);
281
+ if (!rendered) {
282
+ continue;
283
+ }
284
+ const stored = this.options.chatStore.appendWithResult({
285
+ threadId: snapshot.threadId,
286
+ role: event.kind === "tool" ? "tool" : "system",
287
+ text: rendered.plain,
288
+ source: "cli",
289
+ correlationId: externalCorrelationId(snapshot),
290
+ turnId: event.turnId ?? snapshot.activity.turnId ?? undefined,
291
+ timestamp: event.timestamp?.toISOString(),
292
+ key: externalMessageKey("event", snapshot, event.lineNumber),
293
+ });
294
+ changed = changed || stored.inserted;
295
+ mirror.latestMirroredEventLine = event.lineNumber;
296
+ }
297
+ if (changed) {
298
+ await this.broadcastChatHistory();
299
+ }
300
+ }
301
+ async updateExternalStatusMessage(snapshot, mirror, text) {
302
+ const now = Date.now();
303
+ const minInterval = this.options.mirrorMinUpdateMs();
304
+ if (!text && mirror.latestStatusAt && now - mirror.latestStatusAt < minInterval) {
305
+ return;
306
+ }
307
+ this.options.chatStore.upsertByKey({
308
+ threadId: snapshot.threadId,
309
+ role: "system",
310
+ text: text ?? renderExternalMirrorStatus(snapshot, this.options.queueLength()).plain,
311
+ source: "cli",
312
+ correlationId: externalCorrelationId(snapshot),
313
+ turnId: snapshot.activity.turnId ?? undefined,
314
+ key: externalMessageKey("status", snapshot),
315
+ });
316
+ mirror.latestStatusAt = now;
317
+ await this.broadcastChatHistory();
318
+ }
319
+ async broadcastChatHistory() {
320
+ this.options.broadcast({ type: "chat_history", messages: await this.options.chatHistory() });
321
+ }
322
+ }
323
+ function externalMessageKey(kind, snapshot, lineNumber) {
324
+ return [
325
+ "external",
326
+ kind,
327
+ snapshot.agentId,
328
+ snapshot.threadId,
329
+ snapshot.activity.turnId ?? "turn",
330
+ lineNumber ?? "",
331
+ ].join(":");
332
+ }
333
+ function externalCorrelationId(snapshot) {
334
+ return `cli:${snapshot.agentId}:${snapshot.activity.turnId ?? snapshot.threadId}`;
233
335
  }
234
336
  function externalStatusLine(snapshot, queueLength) {
235
337
  const elapsed = snapshot.activity.startedAt
@@ -60,6 +60,7 @@ export function queueItemDto(item) {
60
60
  description: item.description,
61
61
  createdAt: new Date(item.createdAt).toISOString(),
62
62
  attempts: item.attempts ?? 0,
63
+ correlationId: item.correlationId,
63
64
  notBefore: item.notBefore ? new Date(item.notBefore).toISOString() : undefined,
64
65
  lastError: item.lastError,
65
66
  };