agent-relay-server 0.91.4 → 0.93.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 (179) hide show
  1. package/docs/openapi.json +29 -1
  2. package/package.json +2 -2
  3. package/public/assets/MessageBubble-DX16Hbb5.js +3 -0
  4. package/public/assets/MessageBubble-DX16Hbb5.js.map +1 -0
  5. package/public/assets/{PlanGraphPanel-BwEOOqdj.js → PlanGraphPanel-BuWerKhs.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-BwEOOqdj.js.map → PlanGraphPanel-BuWerKhs.js.map} +1 -1
  7. package/public/assets/activity-tuTEn3vn.js +2 -0
  8. package/public/assets/{activity-DD9bl5uf.js.map → activity-tuTEn3vn.js.map} +1 -1
  9. package/public/assets/{agent-profiles-CLJNeVxc.js → agent-profiles-CAwmjdDx.js} +2 -2
  10. package/public/assets/{agent-profiles-CLJNeVxc.js.map → agent-profiles-CAwmjdDx.js.map} +1 -1
  11. package/public/assets/agents-B-BSBrM5.js +2 -0
  12. package/public/assets/{agents-DJgqCIqi.js.map → agents-B-BSBrM5.js.map} +1 -1
  13. package/public/assets/{analytics-C1F28oqr.js → analytics-aI_oGHtR.js} +3 -3
  14. package/public/assets/{analytics-C1F28oqr.js.map → analytics-aI_oGHtR.js.map} +1 -1
  15. package/public/assets/{automation-Vbei8P9W.js → automation-BAQM1dBh.js} +2 -2
  16. package/public/assets/{automation-Vbei8P9W.js.map → automation-BAQM1dBh.js.map} +1 -1
  17. package/public/assets/{badge-CK6ejiOh.js → badge-fjSgjnDc.js} +2 -2
  18. package/public/assets/{badge-CK6ejiOh.js.map → badge-fjSgjnDc.js.map} +1 -1
  19. package/public/assets/branch-state-badge-DaafW7sN.js +2 -0
  20. package/public/assets/{branch-state-badge-Q4W68jxj.js.map → branch-state-badge-DaafW7sN.js.map} +1 -1
  21. package/public/assets/{button-BAwKuRIq.js → button-DidMOp8n.js} +2 -2
  22. package/public/assets/{button-BAwKuRIq.js.map → button-DidMOp8n.js.map} +1 -1
  23. package/public/assets/card-CpLnx1JJ.js +2 -0
  24. package/public/assets/{card-DDxxo-Fo.js.map → card-CpLnx1JJ.js.map} +1 -1
  25. package/public/assets/{channels-BQbNtrtF.js → channels-DHjTCcE8.js} +2 -2
  26. package/public/assets/{channels-BQbNtrtF.js.map → channels-DHjTCcE8.js.map} +1 -1
  27. package/public/assets/chat-uWKl3MpS.js +2 -0
  28. package/public/assets/{chat-Dw5xpc5W.js.map → chat-uWKl3MpS.js.map} +1 -1
  29. package/public/assets/{connectors-CmARSKoV.js → connectors-CI97PYOl.js} +2 -2
  30. package/public/assets/{connectors-CmARSKoV.js.map → connectors-CI97PYOl.js.map} +1 -1
  31. package/public/assets/copy-button-CKAwTG9g.js +2 -0
  32. package/public/assets/{copy-button-D3B_s7J7.js.map → copy-button-CKAwTG9g.js.map} +1 -1
  33. package/public/assets/display-CWMHll1J.js.map +1 -1
  34. package/public/assets/{dist-Dmiv1tSO.js → dist-BEpGArlE.js} +2 -2
  35. package/public/assets/{dist-Dmiv1tSO.js.map → dist-BEpGArlE.js.map} +1 -1
  36. package/public/assets/dist-BZ-CpDXy.js +2 -0
  37. package/public/assets/{dist-CIbvPDQV.js.map → dist-BZ-CpDXy.js.map} +1 -1
  38. package/public/assets/{dist-DLsSqk6h.js → dist-BaDWyAAG.js} +2 -2
  39. package/public/assets/{dist-DLsSqk6h.js.map → dist-BaDWyAAG.js.map} +1 -1
  40. package/public/assets/{dist-CAFUEpK2.js → dist-BivRSJg7.js} +2 -2
  41. package/public/assets/{dist-CAFUEpK2.js.map → dist-BivRSJg7.js.map} +1 -1
  42. package/public/assets/{dist-CL2npVDe.js → dist-C9ntDbUp.js} +2 -2
  43. package/public/assets/{dist-CL2npVDe.js.map → dist-C9ntDbUp.js.map} +1 -1
  44. package/public/assets/dist-CEb4q1gZ.js +2 -0
  45. package/public/assets/{dist-DyqEeyBI.js.map → dist-CEb4q1gZ.js.map} +1 -1
  46. package/public/assets/dist-DBDYIgS5.js +2 -0
  47. package/public/assets/{dist-CoRABSO3.js.map → dist-DBDYIgS5.js.map} +1 -1
  48. package/public/assets/{dist-ERYvDMdt.js → dist-DNxK5Ibj.js} +2 -2
  49. package/public/assets/{dist-ERYvDMdt.js.map → dist-DNxK5Ibj.js.map} +1 -1
  50. package/public/assets/{dist-YjP8Gd1C.js → dist-DayffsX1.js} +2 -2
  51. package/public/assets/{dist-YjP8Gd1C.js.map → dist-DayffsX1.js.map} +1 -1
  52. package/public/assets/{dist-YQVQrtnc.js → dist-L3vQoV3T.js} +2 -2
  53. package/public/assets/{dist-YQVQrtnc.js.map → dist-L3vQoV3T.js.map} +1 -1
  54. package/public/assets/{dist-CnRv0y2x.js → dist-uFOP-AfS.js} +2 -2
  55. package/public/assets/{dist-CnRv0y2x.js.map → dist-uFOP-AfS.js.map} +1 -1
  56. package/public/assets/{es2015-Yto-4EBw.js → es2015-CJr6bLcU.js} +2 -2
  57. package/public/assets/{es2015-Yto-4EBw.js.map → es2015-CJr6bLcU.js.map} +1 -1
  58. package/public/assets/{formatted-body-impl-BisZ0FtO.js → formatted-body-impl-DuqZunxL.js} +2 -2
  59. package/public/assets/{formatted-body-impl-BisZ0FtO.js.map → formatted-body-impl-DuqZunxL.js.map} +1 -1
  60. package/public/assets/index-DLjkMB-b.css +2 -0
  61. package/public/assets/index-dMB8klzy.js +23 -0
  62. package/public/assets/index-dMB8klzy.js.map +1 -0
  63. package/public/assets/{input-CLW2gDUM.js → input-BbH2Jq-f.js} +2 -2
  64. package/public/assets/{input-CLW2gDUM.js.map → input-BbH2Jq-f.js.map} +1 -1
  65. package/public/assets/{insights-D9ytYZyE.js → insights-C0lZ7n8D.js} +2 -2
  66. package/public/assets/{insights-D9ytYZyE.js.map → insights-C0lZ7n8D.js.map} +1 -1
  67. package/public/assets/integrations-DXh00Ss-.js +2 -0
  68. package/public/assets/{integrations-D9Cx-7ma.js.map → integrations-DXh00Ss-.js.map} +1 -1
  69. package/public/assets/{lib-DdCM7GfN.js → lib-B0r9alsu.js} +2 -2
  70. package/public/assets/{lib-DdCM7GfN.js.map → lib-B0r9alsu.js.map} +1 -1
  71. package/public/assets/{lucide-react-CsPXvXMC.js → lucide-react-BCarrGVa.js} +2 -2
  72. package/public/assets/{lucide-react-CsPXvXMC.js.map → lucide-react-BCarrGVa.js.map} +1 -1
  73. package/public/assets/{maintenance-BA93FvLR.js → maintenance-Dh9nzjYV.js} +2 -2
  74. package/public/assets/{maintenance-BA93FvLR.js.map → maintenance-Dh9nzjYV.js.map} +1 -1
  75. package/public/assets/{managed-agents-DGwdEIzA.js → managed-agents-Bsveljn4.js} +4 -4
  76. package/public/assets/{managed-agents-DGwdEIzA.js.map → managed-agents-Bsveljn4.js.map} +1 -1
  77. package/public/assets/{markdown-preview-impl-C7FDXM5c.js → markdown-preview-impl-CYpMFis9.js} +2 -2
  78. package/public/assets/{markdown-preview-impl-C7FDXM5c.js.map → markdown-preview-impl-CYpMFis9.js.map} +1 -1
  79. package/public/assets/{memory-CHzkRJqf.js → memory-DMFQMXrp.js} +2 -2
  80. package/public/assets/{memory-CHzkRJqf.js.map → memory-DMFQMXrp.js.map} +1 -1
  81. package/public/assets/messages-Cnzc7j50.js +2 -0
  82. package/public/assets/{messages-B7CJ5tgT.js.map → messages-Cnzc7j50.js.map} +1 -1
  83. package/public/assets/{orchestrators-DOy9xIx2.js → orchestrators-DSoDPNVS.js} +2 -2
  84. package/public/assets/{orchestrators-DOy9xIx2.js.map → orchestrators-DSoDPNVS.js.map} +1 -1
  85. package/public/assets/{overview-DLbv1BWa.js → overview-OsRxo5XQ.js} +2 -2
  86. package/public/assets/{overview-DLbv1BWa.js.map → overview-OsRxo5XQ.js.map} +1 -1
  87. package/public/assets/{pairs-BDQo-k3F.js → pairs-D4UiUhyW.js} +2 -2
  88. package/public/assets/{pairs-BDQo-k3F.js.map → pairs-D4UiUhyW.js.map} +1 -1
  89. package/public/assets/{projects-hU5KGW_T.js → projects-CYzU-Cf3.js} +2 -2
  90. package/public/assets/{projects-hU5KGW_T.js.map → projects-CYzU-Cf3.js.map} +1 -1
  91. package/public/assets/{react-dom-CEl58oxP.js → react-dom-CYpnckci.js} +2 -2
  92. package/public/assets/{react-dom-CEl58oxP.js.map → react-dom-CYpnckci.js.map} +1 -1
  93. package/public/assets/{security-9WrCcrWs.js → security-CC2F6xdd.js} +2 -2
  94. package/public/assets/{security-9WrCcrWs.js.map → security-CC2F6xdd.js.map} +1 -1
  95. package/public/assets/{select-CRjeGZ9a.js → select-CD38f1WQ.js} +2 -2
  96. package/public/assets/{select-CRjeGZ9a.js.map → select-CD38f1WQ.js.map} +1 -1
  97. package/public/assets/settings-T3B40Amb.js +6 -0
  98. package/public/assets/settings-T3B40Amb.js.map +1 -0
  99. package/public/assets/{store-DsqUEZOf.js → store-CJeafbP1.js} +3 -3
  100. package/public/assets/{store-DsqUEZOf.js.map → store-CJeafbP1.js.map} +1 -1
  101. package/public/assets/{tasks-86St2vpn.js → tasks-BkrhWMw7.js} +2 -2
  102. package/public/assets/{tasks-86St2vpn.js.map → tasks-BkrhWMw7.js.map} +1 -1
  103. package/public/assets/{teams-C9WUxzud.js → teams-DyzaREAg.js} +2 -2
  104. package/public/assets/{teams-C9WUxzud.js.map → teams-DyzaREAg.js.map} +1 -1
  105. package/public/assets/{terminal-viewer-impl-yXWyt2fm.js → terminal-viewer-impl-B5JeHof3.js} +3 -3
  106. package/public/assets/{terminal-viewer-impl-yXWyt2fm.js.map → terminal-viewer-impl-B5JeHof3.js.map} +1 -1
  107. package/public/assets/{use-keyboard-viewport-DABeIMEJ.js → use-keyboard-viewport-BYCJkZCN.js} +2 -2
  108. package/public/assets/{use-keyboard-viewport-DABeIMEJ.js.map → use-keyboard-viewport-BYCJkZCN.js.map} +1 -1
  109. package/public/assets/user-avatar-C7i11_ju.js +2 -0
  110. package/public/assets/{user-avatar-tomKla2H.js.map → user-avatar-C7i11_ju.js.map} +1 -1
  111. package/public/assets/{work-queue-BtLMzf0g.js → work-queue-DHp4p1Zk.js} +2 -2
  112. package/public/assets/{work-queue-BtLMzf0g.js.map → work-queue-DHp4p1Zk.js.map} +1 -1
  113. package/public/assets/workspaces-BzwZvbsq.js +3 -0
  114. package/public/assets/{workspaces-C740m1ys.js.map → workspaces-BzwZvbsq.js.map} +1 -1
  115. package/public/index.html +21 -21
  116. package/src/agent-lifecycle-events.ts +0 -10
  117. package/src/branch-landed.ts +17 -29
  118. package/src/bus.ts +9 -2
  119. package/src/config-store.ts +13 -26
  120. package/src/config.ts +1 -0
  121. package/src/context-advisory.ts +20 -10
  122. package/src/db/agent-predicates.ts +21 -0
  123. package/src/db/agents.ts +3 -3
  124. package/src/db/delivery.ts +38 -0
  125. package/src/db/messages.ts +18 -1
  126. package/src/db/migrations.ts +17 -0
  127. package/src/db/scheduled-timers.ts +33 -12
  128. package/src/db/schema.ts +4 -0
  129. package/src/db/workspaces.ts +44 -2
  130. package/src/maintenance/constants.ts +2 -0
  131. package/src/maintenance/jobs.ts +21 -8
  132. package/src/maintenance/workspaces.ts +76 -0
  133. package/src/mcp/index.ts +1 -1
  134. package/src/mcp/tools-messaging.ts +13 -3
  135. package/src/mcp/tools-spawn.ts +30 -2
  136. package/src/mcp-notification-subscription-tools.ts +55 -2
  137. package/src/notification-router.ts +163 -9
  138. package/src/notification-rules.ts +7 -1
  139. package/src/notification-settings-store.ts +47 -0
  140. package/src/notification-settings.ts +155 -0
  141. package/src/notification-types.ts +2 -0
  142. package/src/quota-advisory.ts +0 -3
  143. package/src/routes/agents-spawn.ts +15 -2
  144. package/src/routes/agents.ts +17 -2
  145. package/src/routes/commands.ts +5 -0
  146. package/src/routes/index.ts +2 -0
  147. package/src/routes/notifications.ts +36 -0
  148. package/src/routes/settings.ts +6 -0
  149. package/src/security.ts +1 -0
  150. package/src/services/managed-running.ts +18 -1
  151. package/src/services/rate-limit-resume.ts +3 -0
  152. package/src/services/register-agent.ts +7 -1
  153. package/src/services/scheduler.ts +29 -1
  154. package/src/services/spawn-agent.ts +62 -2
  155. package/src/settings/registry.ts +30 -44
  156. package/src/spawn-command.ts +10 -0
  157. package/src/sse.ts +1 -0
  158. package/src/validation.ts +7 -2
  159. package/src/workspace-auto-merge.ts +53 -9
  160. package/public/assets/MessageBubble-BORB9AKu.js +0 -3
  161. package/public/assets/MessageBubble-BORB9AKu.js.map +0 -1
  162. package/public/assets/activity-DD9bl5uf.js +0 -2
  163. package/public/assets/agents-DJgqCIqi.js +0 -2
  164. package/public/assets/branch-state-badge-Q4W68jxj.js +0 -2
  165. package/public/assets/card-DDxxo-Fo.js +0 -2
  166. package/public/assets/chat-Dw5xpc5W.js +0 -2
  167. package/public/assets/copy-button-D3B_s7J7.js +0 -2
  168. package/public/assets/dist-CIbvPDQV.js +0 -2
  169. package/public/assets/dist-CoRABSO3.js +0 -2
  170. package/public/assets/dist-DyqEeyBI.js +0 -2
  171. package/public/assets/index-Bsi2NH0v.js +0 -23
  172. package/public/assets/index-Bsi2NH0v.js.map +0 -1
  173. package/public/assets/index-umexz3cs.css +0 -2
  174. package/public/assets/integrations-D9Cx-7ma.js +0 -2
  175. package/public/assets/messages-B7CJ5tgT.js +0 -2
  176. package/public/assets/settings-Durch6xW.js +0 -4
  177. package/public/assets/settings-Durch6xW.js.map +0 -1
  178. package/public/assets/user-avatar-tomKla2H.js +0 -2
  179. package/public/assets/workspaces-C740m1ys.js +0 -3
package/src/security.ts CHANGED
@@ -151,6 +151,7 @@ export function requiredScopeFor(method: string, pathname: string): string | nul
151
151
  if (pathname === "/api/health") return "health:read";
152
152
  if (pathname === "/api/events") return "events:read";
153
153
  if (pathname === "/api/route") return "agent:read";
154
+ if (pathname === "/api/notifications/preview") return "notifications:read";
154
155
  if (pathname === "/api/mcp") return "mcp:use";
155
156
  if (pathname === "/api/orchestrators/bootstrap/exchange") return "token:write";
156
157
  if (pathname === "/api/orchestrators/bootstrap") return "token:write";
@@ -12,7 +12,7 @@
12
12
  // Composed by `registerAgent` (HTTP + bus registration) and called directly by the
13
13
  // orchestrator-report projection in lifecycle-manager. It does NOT upsert the agent
14
14
  // row — the agent registers itself over HTTP/bus; this only reconciles managed state.
15
- import { createActivityEvent, resolveQueuedPolicyMessages } from "../db";
15
+ import { createActivityEvent, resolveQueuedDirectMessages, resolveQueuedPolicyMessages } from "../db";
16
16
  import { getManagedAgentState, updateManagedAgentState } from "../config-store";
17
17
  import { emitRelayEvent } from "../events";
18
18
  import { emitMessageAvailable, emitMessageDeliveryUpdated, emitNewMessage } from "../sse";
@@ -129,3 +129,20 @@ function recordManagedTransition(
129
129
  },
130
130
  });
131
131
  }
132
+
133
+ /**
134
+ * Flush queued messages addressed directly to `agentId` (#416). Called when a registered agent
135
+ * transitions back to available: heartbeat stale→idle, re-registration after restart/compact, or
136
+ * rate-limit resume. Emits the same `message.available` / `message.new` / `message.delivery_updated`
137
+ * events that the policy flush emits so SSE subscribers and the dashboard react identically.
138
+ * No-op when there are no queued direct messages.
139
+ */
140
+ export function flushQueuedDirectMessages(agentId: string): void {
141
+ const available = resolveQueuedDirectMessages(agentId);
142
+ if (!available.length) return;
143
+ emitMessageAvailable("direct", agentId, available);
144
+ for (const message of available) {
145
+ emitNewMessage(message);
146
+ emitMessageDeliveryUpdated(message);
147
+ }
148
+ }
@@ -1,5 +1,6 @@
1
1
  import { createActivityEvent, listAgents, mergeAgentMeta } from "../db";
2
2
  import { routeNotification } from "../notification-router";
3
+ import { flushQueuedDirectMessages } from "./managed-running";
3
4
  import {
4
5
  TRANSIENT_RATE_LIMIT_BACKOFF_MS,
5
6
  agentProviderState,
@@ -134,6 +135,8 @@ export function resumeRateLimitedAgents(): Record<string, unknown> {
134
135
 
135
136
  if (transient) mergeAgentMeta(agent.id, { providerState: nextTransientState(state as Record<string, unknown>, now, attempts) });
136
137
 
138
+ // #416 — flush messages queued while the agent was rate-limited.
139
+ flushQueuedDirectMessages(agent.id);
137
140
  resumed.push(agent.id);
138
141
  createActivityEvent({
139
142
  clientId: `rate-limit-resume-${agent.id}-${now}`,
@@ -15,7 +15,8 @@ import { emitAgentStatus, emitNewMessage } from "../sse";
15
15
  import { noteAgentTimelineEvent } from "../compaction-watch";
16
16
  import { notifyAgentReady } from "../agent-lifecycle-events";
17
17
  import { resolveSpawnLineage, resolveTeamLineage } from "../security";
18
- import { markManagedAgentRunning } from "./managed-running";
18
+ import { flushQueuedDirectMessages, markManagedAgentRunning } from "./managed-running";
19
+ import { isAgentUnavailable } from "../db/agent-predicates";
19
20
  import type { AuthContext } from "./auth-context";
20
21
  import type { AgentCard, RegisterAgentInput } from "../types";
21
22
 
@@ -71,6 +72,11 @@ export function registerAgent(input: RegisterAgentInput, ctx: AuthContext): Agen
71
72
  // transport. notifyAgentReady no-ops for non-spawned agents and dedups repeats.
72
73
  if (agent.ready && !existing?.ready) notifyAgentReady(agent.id);
73
74
 
75
+ // #416 — flush queued direct messages when a re-registering agent was previously unavailable
76
+ // (stale, compacting, blocked) or a new agent becomes ready. No-op for brand-new agents
77
+ // (no queued messages yet) and when no queued messages exist.
78
+ if (existing && isAgentUnavailable(existing) && agent.ready) flushQueuedDirectMessages(agent.id);
79
+
74
80
  if (!existing) {
75
81
  // #413 — a spawned worker with a report-back contract gets a pending reply obligation to its
76
82
  // spawner so it can't idle with its verdict undelivered (vent #121). First-registration only.
@@ -39,9 +39,13 @@ export interface ScheduleTimerInput {
39
39
  everyMs?: number;
40
40
  rrule?: string;
41
41
  timezone?: string;
42
+ kind?: SendMessageInput["kind"];
43
+ channel?: string;
42
44
  subject?: string;
43
45
  body: string;
46
+ claimable?: boolean;
44
47
  payload?: Record<string, unknown>;
48
+ meta?: Record<string, unknown>;
45
49
  maxRuns?: number;
46
50
  expiresAt?: string | number;
47
51
  idempotencyKey?: string;
@@ -78,9 +82,13 @@ export function scheduleTimer(input: ScheduleTimerInput, ctx: AuthContext): Sche
78
82
  const deliveryFrom = ctx.callerAgentId ?? "system";
79
83
  const body = cleanBody(input.body);
80
84
  const payload = cleanPayload(input.payload);
85
+ const meta = cleanPayload(input.meta);
81
86
  const maxRuns = cleanPositiveInt(input.maxRuns, "maxRuns");
82
87
  const expiresAt = cleanExpiresAt(input.expiresAt, now);
83
88
  const idempotencyKey = cleanOptionalString(input.idempotencyKey, "idempotencyKey", 240);
89
+ const messageKind = cleanOptionalString(input.kind, "kind", 40) as SendMessageInput["kind"] | undefined;
90
+ const channel = cleanOptionalString(input.channel, "channel", 120);
91
+ const claimable = cleanOptionalBoolean(input.claimable, "claimable");
84
92
 
85
93
  if (schedule.kind === "interval") {
86
94
  if ((schedule.def.everyMs ?? 0) < SCHEDULER_MIN_RECURRING_INTERVAL_MS) {
@@ -100,9 +108,13 @@ export function scheduleTimer(input: ScheduleTimerInput, ctx: AuthContext): Sche
100
108
  validateSendMessageAuthorization({
101
109
  from: deliveryFrom,
102
110
  to: target,
111
+ kind: messageKind,
112
+ channel,
103
113
  body,
104
114
  subject: cleanOptionalString(input.subject, "subject", 200),
115
+ claimable,
105
116
  payload,
117
+ meta,
106
118
  idempotencyKey: idempotencyKey ? `${idempotencyKey}:probe` : undefined,
107
119
  }, ctx);
108
120
 
@@ -116,9 +128,13 @@ export function scheduleTimer(input: ScheduleTimerInput, ctx: AuthContext): Sche
116
128
  nextRunAt: schedule.nextRunAt,
117
129
  scheduleKind: schedule.kind,
118
130
  scheduleDef: schedule.def,
131
+ messageKind,
132
+ channel,
119
133
  subject: cleanOptionalString(input.subject, "subject", 200),
120
134
  body,
135
+ claimable,
121
136
  payload,
137
+ meta,
122
138
  maxRuns,
123
139
  expiresAt,
124
140
  idempotencyKey,
@@ -176,8 +192,11 @@ function fireClaimedTimer(timer: ScheduledTimer, now: number): { status: "fired"
176
192
  const result = sendMessageService({
177
193
  from: timer.deliveryFrom,
178
194
  to: timer.target,
195
+ kind: timer.messageKind as SendMessageInput["kind"] | undefined,
196
+ channel: timer.channel,
179
197
  subject: timer.subject,
180
198
  body: timer.body,
199
+ claimable: timer.claimable,
181
200
  payload: {
182
201
  ...timer.payload,
183
202
  scheduler: {
@@ -187,6 +206,7 @@ function fireClaimedTimer(timer: ScheduledTimer, now: number): { status: "fired"
187
206
  },
188
207
  },
189
208
  meta: {
209
+ ...timer.meta,
190
210
  scheduler: {
191
211
  timerId: timer.id,
192
212
  correlationId: timer.correlationId,
@@ -199,7 +219,7 @@ function fireClaimedTimer(timer: ScheduledTimer, now: number): { status: "fired"
199
219
  return { status: "fired", messageId: result.message.id };
200
220
  } catch (error) {
201
221
  const updated = recordScheduledTimerFailure({
202
- id: timer.id,
222
+ timer,
203
223
  error: errMessage(error),
204
224
  now,
205
225
  retryMs: SCHEDULER_RETRY_MS,
@@ -215,6 +235,8 @@ function timerRunIdempotencyKey(timer: ScheduledTimer, runNumber: number): strin
215
235
  }
216
236
 
217
237
  function persistedAuthContext(timer: ScheduledTimer): AuthContext {
238
+ // Timers intentionally fire with the create-time component scope snapshot. That keeps durable
239
+ // sends restart-safe; a stricter future model can revalidate a live token at fire time.
218
240
  const component: ComponentToken = {
219
241
  sub: timer.createdBy,
220
242
  role: timer.createdByKind,
@@ -287,6 +309,12 @@ function cleanPositiveInt(value: unknown, field: string): number | undefined {
287
309
  return value;
288
310
  }
289
311
 
312
+ function cleanOptionalBoolean(value: unknown, field: string): boolean | undefined {
313
+ if (value === undefined || value === null) return undefined;
314
+ if (typeof value !== "boolean") throw new ValidationError(`${field} must be a boolean`);
315
+ return value;
316
+ }
317
+
290
318
  function cleanExpiresAt(value: unknown, now: number): number | undefined {
291
319
  if (value === undefined || value === null) return undefined;
292
320
  const ms = typeof value === "number" ? value : typeof value === "string" ? Date.parse(value) : NaN;
@@ -25,7 +25,7 @@
25
25
  // deliberately out of scope here — this slice converges the two external TRANSPORTS.
26
26
  import type { SpawnProvider } from "agent-relay-sdk";
27
27
  import { errMessage, stringValue } from "agent-relay-sdk";
28
- import { countLiveSpawnedAgents, createActivityEvent, ensureTeam, getAgent, listAgents, resolveProjectForCwd, setAgentTeam, ValidationError, withResolvedProvisioning } from "../db";
28
+ import { countLiveSpawnedAgents, createActivityEvent, ensureTeam, getAgent, listAgents, listWorkspaces, resolveProjectForCwd, setAgentTeam, ValidationError, withResolvedProvisioning } from "../db";
29
29
  import { createCommand } from "../commands-db";
30
30
  import { emitCommandEvent } from "../command-events";
31
31
  import { buildSpawnCommand, generateSpawnRequestId, resolveSpawnModelParams } from "../spawn-command";
@@ -42,7 +42,9 @@ import { hasComponentScope, isComponentAuthorizedFor } from "../security";
42
42
  import { isPathWithinBase } from "../utils";
43
43
  import { McpAuthError, McpNotFoundError } from "../mcp-errors";
44
44
  import type { AuthContext } from "./auth-context";
45
- import type { AgentCard, AgentLifecycle, Command, SpawnApprovalMode, WorkspaceMode } from "../types";
45
+ import type { AgentCard, AgentLifecycle, Command, SpawnApprovalMode, WorkspaceAutoMergePolicy, WorkspaceLandingPolicy, WorkspaceMode } from "../types";
46
+ import { isOwnerAlive, liveAgentCwds, worktreeInUseByLiveAgent } from "../workspace-merge";
47
+ import { TERMINAL_WORKSPACE_STATUSES } from "../workspace-phase";
46
48
 
47
49
  /** The spawn scope every transport must hold (admin `*` / `admin:*` pass; `system` bypasses). */
48
50
  const SPAWN_SCOPE = "command:spawn";
@@ -99,6 +101,9 @@ export interface SpawnAgentInput {
99
101
  * The identity-bearing relay endpoint stays per-agent (#215), never on the shared listener.
100
102
  */
101
103
  sharedMcp?: SharedMcpSpawnOverride;
104
+ landStrategy?: WorkspaceLandingPolicy;
105
+ autoMerge?: WorkspaceAutoMergePolicy;
106
+ reviewer?: string;
102
107
  /**
103
108
  * Legacy report-back contract flag. Spawned children now report up by lineage: their
104
109
  * turn-final response is delivered to the spawner automatically, without a reply obligation.
@@ -115,6 +120,23 @@ export interface SpawnAgentInput {
115
120
  requestedBy?: string;
116
121
  /** Poll for the spawned child's registration this long (ms). 0 = fire-and-forget (HTTP). */
117
122
  waitForRegistrationMs?: number;
123
+ /**
124
+ * #635 — attach to or branch off an existing managed worktree. Relay validates existence
125
+ * + live-agent containment before enriching with the resolved worktreePath/workspaceId and
126
+ * threading to the orchestrator. User provides only `branch` + `mode`; relay fills the rest.
127
+ */
128
+ resumeWorkspace?: {
129
+ branch: string;
130
+ mode: "attach" | "branch-from";
131
+ /** Relay-enriched: resolved worktree path from the DB (set during validation, not user input). */
132
+ worktreePath?: string;
133
+ /** Relay-enriched: existing workspace id so the record is updated, not duplicated (attach only). */
134
+ workspaceId?: string;
135
+ /** Relay-enriched: existing baseRef. */
136
+ baseRef?: string;
137
+ /** Relay-enriched: existing baseSha. */
138
+ baseSha?: string;
139
+ };
118
140
  }
119
141
 
120
142
  export interface SpawnAgentResult {
@@ -236,6 +258,40 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
236
258
  // outside its cwd prefix, and MCP now honors the same bound.
237
259
  assertResourceAllowed(ctx, { scope: "agent:write", resource: { orchestratorId: orchestrator.id, cwd: resolvedCwd } });
238
260
 
261
+ // #635 — validate and enrich resumeWorkspace before dispatching to the orchestrator.
262
+ // The relay has the workspace DB records; the orchestrator has disk access. We do the
263
+ // "is this a real managed workspace and is it free?" check here, the orchestrator
264
+ // does the "does the worktree actually exist on disk?" check in resolveSpawnWorkspace.
265
+ if (input.resumeWorkspace) {
266
+ const { branch, mode } = input.resumeWorkspace;
267
+ const existingWs = listWorkspaces().find(
268
+ (ws) => ws.branch === branch && ws.mode === "isolated" && !TERMINAL_WORKSPACE_STATUSES.has(ws.status),
269
+ );
270
+ if (!existingWs || !existingWs.worktreePath) {
271
+ throw new ValidationError(`resumeWorkspace: no active isolated workspace found for branch "${branch}"`);
272
+ }
273
+ if (!isPathWithinBase(existingWs.worktreePath, orchestrator.baseDir)) {
274
+ throw new ValidationError(`resumeWorkspace: worktree "${existingWs.worktreePath}" is outside orchestrator base dir "${orchestrator.baseDir}"`);
275
+ }
276
+ if (mode === "attach") {
277
+ if (isOwnerAlive(existingWs.ownerAgentId)) {
278
+ throw new ValidationError(`resumeWorkspace: workspace owner "${existingWs.ownerAgentId}" is still alive — cannot double-attach`);
279
+ }
280
+ if (worktreeInUseByLiveAgent(existingWs.worktreePath, liveAgentCwds())) {
281
+ throw new ValidationError(`resumeWorkspace: worktree "${existingWs.worktreePath}" is still in use by a live agent`);
282
+ }
283
+ }
284
+ // Enrich with relay-resolved fields the orchestrator needs.
285
+ input.resumeWorkspace = {
286
+ branch,
287
+ mode,
288
+ worktreePath: existingWs.worktreePath,
289
+ workspaceId: existingWs.id,
290
+ baseRef: existingWs.baseRef,
291
+ baseSha: existingWs.baseSha,
292
+ };
293
+ }
294
+
239
295
  // Provenance actor: the resolved caller agent when known, else the transport's label, else
240
296
  // the raw token actor. Recorded in the command payload, the token mint, and the audit row.
241
297
  const requestedBy = callerId ?? input.requestedBy ?? ctx.actor.id;
@@ -321,6 +377,10 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
321
377
  prompt: input.prompt,
322
378
  systemPromptAppend,
323
379
  relayInjectionEvents: composedInstructions.relayInjectionEvents,
380
+ landStrategy: input.landStrategy,
381
+ autoMerge: input.autoMerge,
382
+ reviewer: input.reviewer,
383
+ resumeWorkspace: input.resumeWorkspace,
324
384
  ...(expectReply ? { expectReply } : {}),
325
385
  policyName: input.policyName,
326
386
  spawnRequestId,
@@ -28,6 +28,13 @@ import {
28
28
  type SettingScope,
29
29
  } from "agent-relay-sdk";
30
30
  import { DEFAULT_TEAMS_CONFIG } from "agent-relay-sdk/teams-config";
31
+ import {
32
+ NOTIFICATIONS_CONFIG_DEFAULTS,
33
+ NOTIFICATIONS_DEFAULT_KEY,
34
+ NOTIFICATIONS_NAMESPACE,
35
+ NOTIFICATIONS_SETTINGS_SCHEMA,
36
+ parseNotificationSettingsKey,
37
+ } from "../notification-settings";
31
38
 
32
39
  /** Fail-closed default scope — a descriptor that omits scope requires admin to
33
40
  * read and write (#385 owns the scope grammar; this layer only declares it). */
@@ -177,34 +184,6 @@ const INSIGHTS_SCHEMA: JSONSchema = {
177
184
  },
178
185
  };
179
186
 
180
- const CONTEXT_THRESHOLD_SCHEMA: JSONSchema = {
181
- type: "object",
182
- additionalProperties: false,
183
- required: ["enabled", "advisoryUtilization", "hardBackstopUtilization", "bandSize"],
184
- properties: {
185
- enabled: { type: "boolean" },
186
- advisoryUtilization: { type: "number", minimum: 0, maximum: 1 },
187
- selfCompactUtilization: { type: "number", minimum: 0, maximum: 1 },
188
- warnUtilization: { type: "number", minimum: 0, maximum: 1 },
189
- hardBackstopUtilization: { type: "number", minimum: 0, maximum: 1 },
190
- bandSize: { type: "number", minimum: 0, maximum: 1 },
191
- },
192
- };
193
-
194
- const NOTIFICATIONS_SCHEMA: JSONSchema = {
195
- type: "object",
196
- additionalProperties: false,
197
- required: ["enabled", "branchLanded", "agentReady", "agentExited", "agentSpawnFailed", "contextThreshold"],
198
- properties: {
199
- enabled: { type: "boolean" },
200
- branchLanded: { type: "boolean" },
201
- agentReady: { type: "boolean" },
202
- agentExited: { type: "boolean" },
203
- agentSpawnFailed: { type: "boolean" },
204
- contextThreshold: CONTEXT_THRESHOLD_SCHEMA,
205
- },
206
- };
207
-
208
187
  const WORKSPACE_SCHEMA: JSONSchema = {
209
188
  type: "object",
210
189
  additionalProperties: false,
@@ -362,22 +341,7 @@ function seedBuiltInSettings(): void {
362
341
  display: { group: "Insights", label: "Insights", description: "Continuous self-improvement instrumentation toggles.", order: 20 },
363
342
  });
364
343
 
365
- registerSetting({
366
- namespace: "notifications",
367
- key: "default",
368
- schema: NOTIFICATIONS_SCHEMA,
369
- defaults: {
370
- enabled: true,
371
- branchLanded: true,
372
- agentReady: true,
373
- agentExited: true,
374
- agentSpawnFailed: true,
375
- contextThreshold: { enabled: true, advisoryUtilization: 0.7, hardBackstopUtilization: 0.9, bandSize: 0.1 },
376
- },
377
- scope: { read: "settings:read:notifications", write: "settings:write:notifications" },
378
- tier: "server-runtime",
379
- display: { group: "Notifications", label: "Lifecycle notifications", description: "Relay-driven lifecycle push notifications (#239).", order: 30 },
380
- });
344
+ registerNotificationSettingDescriptor(NOTIFICATIONS_DEFAULT_KEY);
381
345
 
382
346
  registerSetting({
383
347
  namespace: "workspace",
@@ -481,6 +445,28 @@ export function registerProviderConfigSetting(input: {
481
445
  });
482
446
  }
483
447
 
448
+ export function registerNotificationSettingDescriptor(key: string = NOTIFICATIONS_DEFAULT_KEY): SettingDescriptor {
449
+ const parsed = parseNotificationSettingsKey(key);
450
+ if (!parsed) throw new Error(`invalid notification setting key "${key}"`);
451
+ const label = parsed.kind === "default"
452
+ ? "Lifecycle notifications"
453
+ : parsed.kind === "team"
454
+ ? `Team notifications: ${parsed.id}`
455
+ : `Agent notifications: ${parsed.id}`;
456
+ const description = parsed.kind === "default"
457
+ ? "Default relay-driven lifecycle push notification policy."
458
+ : "Scoped notification override; unset fields inherit from the less-specific layer.";
459
+ return registerSetting({
460
+ namespace: NOTIFICATIONS_NAMESPACE,
461
+ key,
462
+ schema: NOTIFICATIONS_SETTINGS_SCHEMA,
463
+ defaults: parsed.kind === "default" ? NOTIFICATIONS_CONFIG_DEFAULTS : {},
464
+ scope: { read: "settings:read:notifications", write: "settings:write:notifications" },
465
+ tier: "server-runtime",
466
+ display: { group: "Notifications", label, description, order: parsed.kind === "default" ? 30 : 31 },
467
+ });
468
+ }
469
+
484
470
  function ensureSeeded(): void {
485
471
  if (!seeded) seedBuiltInSettings();
486
472
  }
@@ -5,6 +5,7 @@ import { resolveProviderSelection, type ProviderEffort } from "agent-relay-sdk/p
5
5
  import { workspaceSpawnParams } from "./config-store";
6
6
  import { ValidationError } from "./db";
7
7
  import { effectiveProviderCatalogList } from "./provider-catalog-store";
8
+ import type { WorkspaceAutoMergePolicy, WorkspaceLandingPolicy } from "./types";
8
9
 
9
10
  /** Resolved provider/model/effort triple — the output shape of every model-resolution wrapper. */
10
11
  interface SpawnModelParams {
@@ -96,6 +97,9 @@ interface BuildSpawnCommandOptions {
96
97
  prompt?: string;
97
98
  systemPromptAppend?: string;
98
99
  relayInjectionEvents?: unknown[];
100
+ landStrategy?: WorkspaceLandingPolicy;
101
+ autoMerge?: WorkspaceAutoMergePolicy;
102
+ reviewer?: string;
99
103
  /**
100
104
  * #413 — when true, the child registering with this spawnRequestId gets a pending reply
101
105
  * obligation back to its spawner, so the Stop hook refuses to let it idle until it delivers
@@ -113,6 +117,8 @@ interface BuildSpawnCommandOptions {
113
117
  automationRunId?: string;
114
118
  orchestratorId?: string;
115
119
  requestedVia?: string;
120
+ /** #635 — relay-enriched resume-workspace target. Relayed verbatim to the orchestrator. */
121
+ resumeWorkspace?: { branch: string; mode: "attach" | "branch-from"; worktreePath?: string; workspaceId?: string; baseRef?: string; baseSha?: string };
116
122
 
117
123
  /** Extra params merged last (e.g. Claude-resume fields). */
118
124
  extra?: Record<string, unknown>;
@@ -151,6 +157,9 @@ export function buildSpawnCommand(opts: BuildSpawnCommandOptions): Record<string
151
157
  ...def(opts.prompt, "prompt"),
152
158
  ...def(opts.systemPromptAppend, "systemPromptAppend"),
153
159
  ...def(opts.relayInjectionEvents, "relayInjectionEvents"),
160
+ ...def(opts.landStrategy, "landStrategy"),
161
+ ...def(opts.autoMerge, "autoMerge"),
162
+ ...def(opts.reviewer, "reviewer"),
154
163
  ...def(opts.expectReply, "expectReply"),
155
164
  ...def(opts.headless, "headless"),
156
165
  ...def(opts.policyName, "policyName"),
@@ -159,6 +168,7 @@ export function buildSpawnCommand(opts: BuildSpawnCommandOptions): Record<string
159
168
  ...def(opts.orchestratorId, "orchestratorId"),
160
169
  ...def(opts.requestedVia, "requestedVia"),
161
170
  ...def(opts.spawnRequestId, "spawnRequestId"),
171
+ ...def(opts.resumeWorkspace, "resumeWorkspace"),
162
172
  env: opts.env,
163
173
  requestedBy: opts.requestedBy,
164
174
  requestedAt: opts.requestedAt ?? Date.now(),
package/src/sse.ts CHANGED
@@ -310,6 +310,7 @@ function notificationForMessage(msg: Message): RelayNotification | undefined {
310
310
  agentId: msg.from,
311
311
  threadPeer: msg.from,
312
312
  view: "chat",
313
+ ...(isRecord(msg.payload?.routing) ? { routing: msg.payload.routing as unknown as RelayNotification["routing"] } : {}),
313
314
  createdAt: Date.now(),
314
315
  };
315
316
  }
package/src/validation.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { ValidationError } from "./db";
2
- import { type TokenConstraints, type WorkspaceMetadata, type WorkspaceMode, type WorkspaceProbe, type WorkspaceStatus } from "./types";
3
- import { VALID_WORKSPACE_MODES, isRecord } from "agent-relay-sdk";
2
+ import { type TokenConstraints, type WorkspaceAutoMergePolicy, type WorkspaceLandingPolicy, type WorkspaceMetadata, type WorkspaceMode, type WorkspaceProbe, type WorkspaceStatus } from "./types";
3
+ import { AUTO_MERGE_POLICIES, VALID_WORKSPACE_MODES, isRecord } from "agent-relay-sdk";
4
+
5
+ const WORKSPACE_LAND_STRATEGIES = ["direct", "pr"] as const;
4
6
 
5
7
  /**
6
8
  * Trim + validate an optional string input. Throws `ValidationError` when the
@@ -164,6 +166,9 @@ export function cleanWorkspaceMetadata(value: unknown, field: string): Workspace
164
166
  baseSha: cleanString(value.baseSha, `${field}.baseSha`, { max: 80 }),
165
167
  status: optionalEnum(value.status, `${field}.status`, VALID_WORKSPACE_STATUSES) as WorkspaceStatus | undefined,
166
168
  stewardAgentId: cleanString(value.stewardAgentId, `${field}.stewardAgentId`, { max: 240 }),
169
+ landStrategy: optionalEnum(value.landStrategy, `${field}.landStrategy`, WORKSPACE_LAND_STRATEGIES) as WorkspaceLandingPolicy | undefined,
170
+ autoMerge: optionalEnum(value.autoMerge, `${field}.autoMerge`, AUTO_MERGE_POLICIES) as WorkspaceAutoMergePolicy | undefined,
171
+ reviewer: cleanString(value.reviewer, `${field}.reviewer`, { max: 240 }),
167
172
  probe: isRecord(value.probe) ? value.probe as unknown as WorkspaceProbe : undefined,
168
173
  };
169
174
  }
@@ -37,9 +37,56 @@ import { requestWorkspaceMerge } from "./workspace-merge";
37
37
  import { workspaceActiveClaim } from "./workspace-claim";
38
38
  import { DIRTY_WORKTREE_LAND_SKIP_REASON, READY_TO_LAND_STATUSES } from "./workspace-phase";
39
39
  import { fetchHostMergePreview, workspacePathWithinBase } from "./workspace-probe";
40
- import type { WorkspaceMergePreview, WorkspaceRecord } from "./types";
40
+ import type { WorkspaceAutoMergePolicy, WorkspaceLandingPolicy, WorkspaceMergePreview, WorkspaceMergeStrategy, WorkspaceRecord } from "./types";
41
41
  import { STEWARD_WAKE_COOLDOWN_MS, stewardFallbackTarget, workspaceAutoMergeEnabled } from "./config";
42
42
 
43
+ const WORKSPACE_LAND_STRATEGIES = ["direct", "pr"] as const;
44
+ const WORKSPACE_AUTO_MERGE_POLICIES = ["on-green", "on-approval", "manual"] as const;
45
+
46
+ type AutoMergeRequestOptions = {
47
+ requestedBy: "auto-merge";
48
+ strategy?: WorkspaceMergeStrategy;
49
+ autoMerge?: WorkspaceAutoMergePolicy;
50
+ reviewer?: string;
51
+ };
52
+
53
+ function workspaceLandStrategy(metadata: Record<string, unknown>): WorkspaceLandingPolicy | undefined {
54
+ const value = metadata.landStrategy;
55
+ return typeof value === "string" && (WORKSPACE_LAND_STRATEGIES as readonly string[]).includes(value)
56
+ ? value as WorkspaceLandingPolicy
57
+ : undefined;
58
+ }
59
+
60
+ function workspaceAutoMergePolicy(metadata: Record<string, unknown>): WorkspaceAutoMergePolicy | undefined {
61
+ const value = metadata.autoMerge;
62
+ return typeof value === "string" && (WORKSPACE_AUTO_MERGE_POLICIES as readonly string[]).includes(value)
63
+ ? value as WorkspaceAutoMergePolicy
64
+ : undefined;
65
+ }
66
+
67
+ function workspaceReviewer(metadata: Record<string, unknown>): string | undefined {
68
+ const value = metadata.reviewer;
69
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
70
+ }
71
+
72
+ function autoMergeRequestOptions(ws: WorkspaceRecord): AutoMergeRequestOptions {
73
+ const landStrategy = workspaceLandStrategy(ws.metadata);
74
+ const autoMerge = workspaceAutoMergePolicy(ws.metadata);
75
+ const reviewer = workspaceReviewer(ws.metadata);
76
+ if (landStrategy === "pr") {
77
+ return {
78
+ requestedBy: "auto-merge",
79
+ strategy: "pr",
80
+ ...(autoMerge ? { autoMerge } : {}),
81
+ ...(reviewer ? { reviewer } : {}),
82
+ };
83
+ }
84
+ if (landStrategy === "direct") return { requestedBy: "auto-merge", strategy: "rebase-ff" };
85
+ return getLandingConfig().strategy === "pr"
86
+ ? { requestedBy: "auto-merge", ...(autoMerge ? { autoMerge } : {}), ...(reviewer ? { reviewer } : {}) }
87
+ : { requestedBy: "auto-merge", strategy: "rebase-ff" };
88
+ }
89
+
43
90
  // Don't re-wake the managed steward for the same workspace more than once per this
44
91
  // window — a persistent conflict/behind row would otherwise re-ping every sweep.
45
92
  // Wake the managed per-repo steward (issue #167) for a workspace it should handle:
@@ -246,14 +293,11 @@ async function landCandidates(
246
293
  continue;
247
294
  }
248
295
 
249
- // Honor the operator's instance landing.strategy (#419-followup): when PR mode is on
250
- // (`landing.strategy = "pr"`), autonomous worker lands must open a PR too not silently
251
- // direct-merge. Passing an explicit "rebase-ff" here SHADOWS the instance config inside
252
- // requestWorkspaceMerge (explicit wins over instanceStrategy), which is the bug that made
253
- // PR mode never engage for spawned workers. For "pr" we OMIT the explicit strategy so
254
- // requestWorkspaceMerge resolves it from the config and defaults autoMerge to "on-green";
255
- // the default "direct" keeps the historical deterministic rebase-ff (no behavior change).
256
- const result = requestWorkspaceMerge(ws, getLandingConfig().strategy === "pr" ? { requestedBy: "auto-merge" } : { strategy: "rebase-ff", requestedBy: "auto-merge" });
296
+ // Honor per-spawn land metadata first (#421), then the operator's instance
297
+ // landing.strategy (#419-followup). A spawn override of `pr` becomes an explicit PR land
298
+ // and may carry autoMerge/reviewer; omitted strategy falls back to instance config.
299
+ // The default/direct path keeps the historical deterministic rebase-ff behavior.
300
+ const result = requestWorkspaceMerge(ws, autoMergeRequestOptions(ws));
257
301
  if (!result.ok) {
258
302
  // 409 = another merge holds this repo's lease this tick; the on-settle re-trigger
259
303
  // (routes/commands.ts → scheduleRepoMerge) drains it once the lease frees.
@@ -1,3 +0,0 @@
1
- import{r as e}from"./chunk-CilyBKbf.js";import{Cn as t,Ct as n,F as r,Fn as i,Hn as a,It as o,Kn as s,Kt as c,On as l,Pn as u,R as d,Wn as f,a as p,b as m,c as h,dn as g,en as _,fn as v,g as ee,gn as y,hn as b,i as x,in as te,lt as S,m as C,mn as w,ot as ne,qt as re,r as T,rn as E,rr as D,un as O,wt as ie,y as k,z as A}from"./lucide-react-CsPXvXMC.js";import{g as j,r as ae,t as M}from"./store-DsqUEZOf.js";import{E as N,H as P,N as F,P as I,R as L,S as R,V as oe,Z as z,_ as se,d as ce,f as le,k as B,lt as V,q as H,v as ue}from"./display-CWMHll1J.js";import{a as de,i as fe,o as pe,t as U,w as W}from"./index-Bsi2NH0v.js";import{t as me}from"./user-avatar-tomKla2H.js";var G=e(D(),1),he={online:`came online`,offline:`went offline`,shutdown:`shut down`,restarted:`restarted`,"compaction-requested":`compaction requested`,compacting:`compaction started`,compacted:`context compacted`,"compaction-stalled":`compaction stalled — may need manual intervention`,"clear-requested":`clear requested`,"clearing-context":`clear started`,"context-cleared":`context cleared`,"clear-stalled":`context clear stalled — may need manual intervention`,"shutting-down":`shutting down`},ge=new Set(Object.keys(he)),_e=3e3;function K(e){return new Date(e.occurredAt??e.createdAt).getTime()}function ve(e){if(e.kind!==`session`)return null;let t=e.payload?.session;return!t||t.type!==`narration`&&t.type!==`reasoning`&&t.type!==`tool`?null:{id:e.id,kind:t.type,label:typeof t.label==`string`?t.label:void 0,text:e.body,ts:K(e),turnId:typeof t.turnId==`string`?t.turnId:void 0}}function ye(e){if(e.kind!==`session`)return null;let t=e.payload?.session;if(t?.type!==`notice`)return null;let n=typeof t.label==`string`&&t.label?t.label:`compacted`;return{id:`notice-${e.id}`,status:n,timestamp:K(e)}}function be(e){if(e.kind!==`session`)return null;let t=e.payload?.session;if(t?.type!==`relay-injection`)return null;let n=t.relayInjection&&typeof t.relayInjection==`object`&&!Array.isArray(t.relayInjection)?t.relayInjection:{},r=typeof n.category==`string`&&[`memory`,`instructions`,`tools`,`comms`,`knowledge`,`context`].includes(n.category)?n.category:`context`,i=typeof n.timestamp==`number`&&Number.isFinite(n.timestamp)?n.timestamp:K(e),a=n.detail&&typeof n.detail==`object`&&!Array.isArray(n.detail)?n.detail:{};return{id:e.id,category:r,summary:typeof n.summary==`string`&&n.summary.trim()?n.summary:e.body,detail:a,agentId:typeof n.agentId==`string`&&n.agentId?n.agentId:e.from,timestamp:i}}function xe(e,t){let n=e.trim();for(let e of t){let t=e.trim();if(t){if(n===t)break;n.startsWith(`${t}\n\n`)&&(n=n.slice(t.length+2).trimStart())}}return n.trim()||e.trim()}function Se(e){return e.kind===`system`&&e.from===`system`}var Ce=new Set([`agent.ready`,`agent.exited`,`agent.spawn_failed`]);function q(e){let t=new Date(e);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,`0`)}-${String(t.getDate()).padStart(2,`0`)}`}function we(e){let t=e.filter(e=>ge.has(e.status)).slice().sort((e,t)=>e.timestamp-t.timestamp),n=[];for(let e of t)n.some(t=>t.id===e.id)||n.some(t=>t.status===e.status&&Math.abs(t.timestamp-e.timestamp)<=_e)||n.push(e);return n}function Te(e,t,n,r=[],i){let a=[];for(let e of r){let t=e.entries.reduce((e,t)=>Math.min(e,H(t.originalCreatedAt)),1/0),n=Number.isFinite(t)?t-1:e.importedAt;a.push({ts:n,entry:{type:`import-boundary`,history:e,timestamp:n}});for(let t of e.entries)a.push({ts:H(t.originalCreatedAt),entry:{type:`imported-message`,history:e,entry:t}})}if(n){let e=new Date(n).getTime();Number.isFinite(e)&&a.push({ts:e,entry:{type:`created`,timestamp:e}})}if(i?.spawnedBy){let e=n?new Date(n).getTime():0,t=Number.isFinite(e)?e:0;a.push({ts:t,entry:{type:`agent-action`,direction:`spawned-by`,agentId:i.spawnedBy.agentId,provider:i.spawnedBy.provider,status:`ready`,timestamp:t}})}let o=i?.spawnedBy?.agentId,s=null,c=1/0;for(let t of e){if(t.kind===`session`||Se(t)||N(t)||!(t.from===`user`||o!==void 0&&t.from===o)||!B(t).trim())continue;let e=K(t);e<c&&(c=e,s=t.id)}let l=new Map,u=new Map,d=new Map;for(let t of e){if(t.kind!==`session`)continue;let e=t.payload?.session;if(e?.type===`narration`&&typeof e.turnId==`string`){let n=u.get(e.turnId)??[];n.push({id:t.id,body:t.body.trim()}),u.set(e.turnId,n)}else if(e?.type===`response`&&typeof e.turnId==`string`){let n=l.get(e.turnId)??[];n.push(t.body.trim()),l.set(e.turnId,n)}}let f=new Map;for(let[e,t]of l){let n=(u.get(e)??[]).map(e=>e.body);f.set(e,t.map(e=>xe(e,n)))}for(let t of e){if(t.kind!==`session`)continue;let e=t.payload?.session;if(e?.type!==`response`||typeof e.turnId!=`string`)continue;let n=xe(t.body,(u.get(e.turnId)??[]).map(e=>e.body));n&&n!==t.body&&d.set(t.id,n)}let p=new Set;for(let[e,t]of f){let n=u.get(e)??[];for(let e of t)for(let t=n.length-1;t>=0;t--){let r=n[t];if(!p.has(r.id)&&P(e,r.body)){p.add(r.id);break}}}let m=new Map,h=[];for(let t of e){if(Se(t)){let e=typeof t.payload?.kind==`string`?t.payload.kind:``;if(Ce.has(e)){let n=t.payload,r=typeof n.agentId==`string`?n.agentId:``,i=K(t);if(!r){a.push({ts:i,entry:{type:`notice`,msg:t}});continue}let o=e===`agent.ready`?`ready`:e===`agent.exited`?`exited`:`failed`,s=m.get(r);if(s)s.entry.status=o,typeof n.reason==`string`&&(s.entry.reason=n.reason),s.entry.timestamp=i,s.ts=i;else{let e={ts:i,entry:{type:`agent-action`,direction:`spawned-child`,agentId:r,provider:typeof n.provider==`string`?n.provider:void 0,status:o,reason:typeof n.reason==`string`?n.reason:void 0,timestamp:i}};m.set(r,e),a.push(e)}continue}a.push({ts:K(t),entry:{type:`notice`,msg:t}});continue}let e=ye(t);if(e){h.push(e);continue}let n=ve(t);if(n){if(n.kind===`narration`&&p.has(n.id))continue;a.push({ts:n.ts,entry:{type:`activity`,steps:[n],timestamp:n.ts}});continue}let r=be(t);if(r){a.push({ts:r.timestamp,entry:{type:`relay-injection`,event:r}});continue}if(t.kind===`session`){let e=t.payload?.session;if(e?.type===`response`&&typeof e.turnId==`string`){let e=d.get(t.id);if(e&&e!==t.body){a.push({ts:K(t),entry:{type:`message`,msg:{...t,body:e}}});continue}}}let i=t.id===s?`initial-prompt`:`message`;a.push({ts:K(t),entry:{type:i,msg:t}})}for(let e of we([...t,...h]))a.push({ts:e.timestamp,entry:{type:`status`,event:e}});let g={created:0,date:1,"import-boundary":2,"imported-message":3,"initial-prompt":4,message:4,activity:4,notice:4,"agent-action":4,"relay-injection":4,status:5};a.sort((e,t)=>{let n=e.ts-t.ts;return Math.abs(n)<3e3&&g[e.entry.type]-g[t.entry.type]||n});let _=[],v=``;for(let{ts:e,entry:t}of a){if(t.type===`created`){_.push(t);continue}let n=q(e);if(n!==v&&(_.push({type:`date`,date:n,timestamp:e}),v=n),t.type===`activity`){let e=_[_.length-1];if(e&&e.type===`activity`&&Oe(e,t)){e.steps.push(...t.steps);continue}}_.push(t)}return _}function Ee(e){return e.type===`message`?`msg-${e.msg.id}`:e.type===`initial-prompt`?`initprompt-${e.msg.id}`:e.type===`notice`?`notice-${e.msg.id}`:e.type===`relay-injection`?`relay-injection-${e.event.id}`:e.type===`agent-action`?`agent-action-${e.direction}-${e.agentId}`:e.type===`activity`?`act-${e.steps[0]?.id??e.timestamp}`:e.type===`import-boundary`?`import-${e.history.id}`:e.type===`imported-message`?`import-msg-${e.history.id}-${e.entry.originalMessageId}-${e.entry.position}`:e.type===`status`?`status-${e.event.id}`:e.type===`date`?`date-${e.date}`:`created-${e.timestamp}`}function De(e){return e.type===`message`||e.type===`initial-prompt`||e.type===`notice`?(e.msg.subject?`${e.msg.subject} `:``)+B(e.msg):e.type===`imported-message`?B(e.entry.message):e.type===`activity`?e.steps.map(e=>[e.label,e.text].filter(Boolean).join(` `)).join(`
2
- `):e.type===`relay-injection`?`${e.event.category} ${e.event.summary}\n${JSON.stringify(e.event.detail)}`:``}function Oe(e,t){let n=e.steps[e.steps.length-1]?.turnId,r=t.steps[0]?.turnId;return n&&r?n===r:!n&&!r}function ke(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(1)} MB`}function Ae(e,t){if(t.startsWith(`/`))return t;let n=`${e.replace(/\/+$/,``)}/${t}`.split(`/`),r=[];for(let e of n)!e||e===`.`||(e===`..`?r.pop():r.push(e));return`/`+r.join(`/`)}function je(e){let t=e.replace(/\/+$/,``),n=t.lastIndexOf(`/`);return n>0?t.slice(0,n):`/`}function Me(e,t){return e.filter(e=>e.status===`online`&&L(t,e.baseDir)).sort((e,t)=>F(t.baseDir).length-F(e.baseDir).length)[0]||e.find(e=>e.status===`online`)}function Ne(){return typeof window<`u`&&window.matchMedia(`(min-width: 1280px)`).matches}function Pe(){return typeof window<`u`&&!!window.getSelection()?.toString()}function Fe(e){let t=e.payload?.attachments;return Array.isArray(t)?t.filter(e=>!!(e&&typeof e==`object`&&typeof e.artifactId==`string`)):[]}var Ie=[`👍`,`❤️`,`✅`,`👀`];function Le(e){let t=new Map;for(let n of e.reactions||[]){let e=I(n.emoji),r=t.get(e)??{emoji:e,count:0,mine:!1,actors:[]};r.count+=1,r.mine=r.mine||n.actorId===`user`,r.actors.push(n.actorId),t.set(e,r)}return[...t.values()].sort((e,t)=>t.count-e.count||e.emoji.localeCompare(t.emoji))}function J(e){return N(e)}function Re(e,t){let n=e.readBy||[],r=e.resolvedToAgent||t||e.to,i=r?n.includes(r):!1,a=n.some(e=>e!==V);if(i||a){let e=i?r:n.find(e=>e!==V);return{label:`read`,title:e?`Read by ${e}`:`Read by agent`,tone:`ok`,icon:y}}if(e.deliveryStatus===`delivered`)return{label:`delivered`,title:`Delivered to agent inbox`,tone:`ok`,icon:y};if(e.deliveryStatus===`queued`)return{label:`queued`,title:`Queued until the target agent is available`,tone:`warn`,icon:_};if(e.deliveryStatus===`failed`||e.deliveryStatus===`dead`){let t=e.deliveryPoisonReason||e.deliveryLastError||e.deliveryStatus;return{label:e.deliveryStatus,title:t,tone:`danger`,icon:O}}return{label:`sent`,title:`Accepted by Relay`,tone:`muted`,icon:b}}function ze(e){let t=H(e.createdAt);return(e.reactions||[]).reduce((e,t)=>Math.max(e,H(t.updatedAt||t.createdAt)),t)}function Be(e){return e?e.messages.reduce((e,t)=>Math.max(e,ze(t)),0):0}function Ve(e,t,n){return(e-t)*n}function He(e){if(!e)return 0;let t=0;for(let n of e.messages)R(n)&&(t=Math.max(t,H(n.createdAt)));return t}function Y(e){return new Date(e).toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`})}function Ue(e){for(let t of e)t.previewUrl&&URL.revokeObjectURL(t.previewUrl)}function We(e,t,n,r){if(e?.teamId)return{teamId:e.teamId};let i=[...t];n&&!i.some(e=>e.id===n.id)&&i.push(n);let a=i.filter(e=>e.threadId).reverse(),o=a.find(e=>!!r[ae(e.threadId,e.channel)])??a[0];return o?.threadId?{threadId:o.threadId,...o.channel?{channel:o.channel}:{}}:null}var X=f(),Ge={online:`text-emerald-400`,offline:`text-zinc-400`,shutdown:`text-zinc-400`,restarted:`text-blue-400`,"compaction-requested":`text-zinc-400`,compacting:`text-amber-400`,compacted:`text-amber-400`,"compaction-stalled":`text-red-400`,"clear-requested":`text-zinc-400`,"clearing-context":`text-amber-400`,"context-cleared":`text-amber-400`,"clear-stalled":`text-red-400`,"shutting-down":`text-zinc-400`};function Ke(e){let t=new Date,[n,r,i]=e.split(`-`).map(Number),a=new Date(n,r-1,i);if(e===q(t.getTime()))return`Today`;let o=new Date(t);return o.setDate(o.getDate()-1),e===q(o.getTime())?`Yesterday`:a.toLocaleDateString(void 0,{weekday:`short`,month:`short`,day:`numeric`,year:a.getFullYear()===t.getFullYear()?void 0:`numeric`})}function qe({event:e}){return(0,X.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,X.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-muted-foreground`,children:[(0,X.jsx)(E,{className:a(`w-2 h-2 fill-current`,Ge[e.status]||`text-zinc-400`)}),(0,X.jsx)(`span`,{children:he[e.status]||e.status}),(0,X.jsx)(`span`,{className:`text-muted-foreground/60`,children:Y(e.timestamp)})]}),(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}var Je={"branch.landed":n,"context.warning":C,"agent.context_threshold":C,"agent.resume_budget_exhausted":C,"agent.rate_limit_resume":A},Ye={"branch.landed":`branch landed`,"context.warning":`context warning`,"agent.context_threshold":`context threshold reached`};function Xe(e){return e&&(e.includes(`.`)?e.slice(e.indexOf(`.`)+1):e).replace(/[._-]+/g,` `).trim()||`system signal`}function Z({agentId:e,fallbackLabel:t,className:n,noIcon:r}){let i=M(t=>t.agentsById[e])||null,o=M(e=>e.openInboxThread),s=M(e=>e.switchView),c=i?ce(i):t||e;return(0,X.jsxs)(`button`,{type:`button`,className:a(`inline-flex max-w-full items-center gap-1 rounded px-1 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground`,n),onClick:t=>{t.stopPropagation(),o(e),s(`chat`)},title:`Open chat for ${c}`,children:[!r&&(0,X.jsx)(W,{agent:i,className:`w-4 h-4 text-[10px]`}),(0,X.jsx)(`span`,{className:`max-w-[160px] truncate`,children:c})]})}function Ze({msg:e}){let t=e.payload??{},r=typeof t.sha==`string`?t.sha:``,i=r?r.slice(0,7):``,a=typeof t.author==`string`?t.author:``,o=typeof t.branch==`string`?t.branch:``,s=typeof t.base==`string`?t.base:``,c=t.pushed;return(0,X.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,X.jsxs)(`div`,{className:`flex max-w-full flex-wrap items-center justify-center gap-x-1.5 gap-y-0.5 rounded-full border border-emerald-600/35 bg-emerald-500/10 px-2.5 py-1 text-xs text-emerald-700 dark:border-emerald-500/25 dark:text-emerald-300/90`,children:[(0,X.jsx)(n,{className:`h-3 w-3 shrink-0 text-emerald-600 dark:text-emerald-400`}),(0,X.jsx)(`span`,{className:`text-emerald-700 dark:text-emerald-300`,children:`branch landed`}),o&&s&&(0,X.jsxs)(`span`,{className:`font-mono text-emerald-700/80 dark:text-emerald-200/70`,title:`${o} → ${s}`,children:[o,(0,X.jsx)(`span`,{className:`opacity-50`,children:` → `}),s]}),i&&(0,X.jsx)(`span`,{className:`rounded bg-emerald-500/15 px-1 font-mono text-emerald-800 dark:text-emerald-200`,title:r,children:i}),a&&(0,X.jsxs)(`span`,{className:`inline-flex items-center gap-1 text-emerald-700/80 dark:text-emerald-300/80`,children:[(0,X.jsx)(`span`,{className:`opacity-70`,children:`by`}),(0,X.jsx)(Z,{agentId:a,className:`text-emerald-700 hover:text-emerald-900 dark:text-emerald-300/90 dark:hover:text-emerald-100`})]}),c===!1&&(0,X.jsx)(`span`,{className:`text-amber-600 dark:text-amber-300/80`,title:`Local-only land — not pushed to origin`,children:`· local only`}),(0,X.jsx)(`span`,{className:`text-emerald-700/60 dark:text-emerald-300/50`,children:Y(e.occurredAt??e.createdAt)})]}),(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function Qe({msg:e}){let t=typeof e.payload?.kind==`string`?e.payload.kind:``;if(t===`branch.landed`)return(0,X.jsx)(Ze,{msg:e});let n=Je[t]??r,i=Ye[t]??Xe(t);return(0,X.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,X.jsxs)(`div`,{className:`flex items-center gap-1.5 rounded-full border border-border bg-muted/30 px-2.5 py-1 text-xs text-muted-foreground`,children:[(0,X.jsx)(n,{className:`h-3 w-3`}),(0,X.jsx)(`span`,{children:i}),(0,X.jsx)(`span`,{className:`text-muted-foreground/60`,children:Y(e.occurredAt??e.createdAt)})]}),(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}var $e={"spawned-child":{ready:`spawned`,exited:`spawned · exited`,failed:`spawn failed`},"spawned-by":{ready:`spawned by`,exited:`spawned by`,failed:`spawned by`}},et={"spawned-child-ready":d,"spawned-child-exited":ne,"spawned-child-failed":O,"spawned-by-ready":i,"spawned-by-exited":i,"spawned-by-failed":i},tt={"spawned-child-ready":`text-emerald-400`,"spawned-child-exited":`text-zinc-400`,"spawned-child-failed":`text-red-400`,"spawned-by-ready":`text-blue-400`,"spawned-by-exited":`text-blue-400`,"spawned-by-failed":`text-blue-400`};function nt({entry:e}){let t=`${e.direction}-${e.status}`,n=$e[e.direction]?.[e.status]??e.direction,i=et[t]??r,o=tt[t]??`text-muted-foreground`,s=e.status===`failed`;return(0,X.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,X.jsxs)(`div`,{className:a(`flex items-center gap-1.5 rounded-full border bg-muted/30 px-2.5 py-1 text-xs`,s?`border-red-500/30`:`border-border`),children:[(0,X.jsx)(i,{className:a(`h-3 w-3 shrink-0`,o)}),(0,X.jsx)(`span`,{className:`text-muted-foreground`,children:n}),(0,X.jsx)(Z,{agentId:e.agentId}),e.reason&&s&&(0,X.jsxs)(`span`,{className:`text-red-400/80 max-w-[120px] truncate`,title:e.reason,children:[`— `,e.reason]}),(0,X.jsx)(`span`,{className:`text-muted-foreground/60 shrink-0`,children:Y(e.timestamp)})]}),(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}var rt={memory:t,instructions:o,tools:p,comms:r,knowledge:l,context:re},it={memory:`border-emerald-500/25 bg-emerald-500/10 text-emerald-300`,instructions:`border-sky-500/25 bg-sky-500/10 text-sky-300`,tools:`border-amber-500/25 bg-amber-500/10 text-amber-300`,comms:`border-violet-500/25 bg-violet-500/10 text-violet-300`,knowledge:`border-cyan-500/25 bg-cyan-500/10 text-cyan-300`,context:`border-zinc-500/25 bg-zinc-500/10 text-zinc-300`};function at(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function ot({entry:e}){let[t,n]=(0,G.useState)(!1),r=e.event,i=rt[r.category]??k;return(0,X.jsx)(`div`,{className:`flex justify-center py-2 my-1`,children:(0,X.jsxs)(`div`,{className:a(`w-full max-w-[85%] md:max-w-[75%] rounded-lg border px-2.5 py-1.5 text-xs shadow-sm`,it[r.category]??it.context),children:[(0,X.jsxs)(`button`,{type:`button`,className:`flex w-full min-w-0 items-center gap-1.5 text-left`,onClick:()=>n(e=>!e),children:[(0,X.jsx)(i,{className:`h-3.5 w-3.5 shrink-0`}),(0,X.jsx)(`span`,{className:`shrink-0 font-medium`,children:r.category}),(0,X.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-foreground/80`,children:r.summary}),(0,X.jsx)(`span`,{className:`shrink-0 text-muted-foreground/60`,children:Y(r.timestamp)}),(0,X.jsx)(v,{className:a(`h-3.5 w-3.5 shrink-0 transition-transform`,t&&`rotate-90`)})]}),t&&(0,X.jsx)(`pre`,{className:`mt-2 max-h-72 overflow-auto rounded-md border border-border/60 bg-background/75 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground whitespace-pre-wrap break-words`,children:at(r.detail)})]})})}function st({blockedLabel:e,onInterrupt:t}){return e?(0,X.jsx)(`div`,{className:`flex justify-start mb-3 animate-in fade-in slide-in-from-bottom-1 duration-150`,children:(0,X.jsxs)(`div`,{className:`flex h-9 items-center gap-2 rounded-2xl rounded-bl-sm bg-card ring-1 ring-red-500/30 px-4`,children:[(0,X.jsx)(O,{className:`w-3.5 h-3.5 text-red-400`}),(0,X.jsxs)(`span`,{className:`text-xs text-red-300`,children:[`blocked: `,e]})]})}):(0,X.jsx)(`div`,{className:`flex justify-start mb-3 animate-in fade-in slide-in-from-bottom-1 duration-150`,children:(0,X.jsxs)(`div`,{className:`flex h-9 items-center gap-2 rounded-2xl rounded-bl-sm bg-card ring-1 ring-foreground/10 px-4`,children:[(0,X.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,X.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_infinite]`}),(0,X.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_0.2s_infinite]`}),(0,X.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_0.4s_infinite]`})]}),(0,X.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`working`}),t&&(0,X.jsxs)(`button`,{onClick:t,title:`Interrupt`,className:`ml-1 flex items-center gap-1 rounded-full px-1.5 py-0.5 text-xs text-muted-foreground hover:text-red-300 hover:bg-red-500/10 transition-colors`,children:[(0,X.jsx)(te,{className:`w-3.5 h-3.5`}),`Stop`]})]})})}var Q=`agent-relay:chat-hide-tools`,$=(()=>{let e=new Set,t=(()=>{try{return window.localStorage.getItem(Q)===`on`}catch{return!1}})(),n=()=>e.forEach(e=>e());return typeof window<`u`&&window.addEventListener(`storage`,e=>{e.key===Q&&(t=e.newValue===`on`,n())}),{subscribe(t){return e.add(t),()=>e.delete(t)},get:()=>t,toggle(){t=!t;try{window.localStorage.setItem(Q,t?`on`:`off`)}catch{}n()}}})();function ct(){return[(0,G.useSyncExternalStore)($.subscribe,$.get,()=>!1),$.toggle]}function lt({steps:e,showReasoning:n,issueRepo:r}){let[i,o]=ct(),s=n?e:e.filter(e=>e.kind!==`reasoning`);if(!s.length)return null;let c=s.filter(e=>e.kind===`tool`).length;return(0,X.jsx)(`div`,{className:`flex justify-start mb-2`,children:(0,X.jsxs)(`div`,{className:`max-w-[85%] md:max-w-[75%] min-w-0 space-y-1.5`,children:[s.map(e=>e.kind===`narration`?(0,X.jsx)(U,{text:e.text,issueRepo:r,className:`text-sm leading-relaxed break-words text-foreground/90 min-w-0`},e.id):e.kind===`reasoning`?(0,X.jsxs)(`div`,{className:`flex items-start gap-1.5 text-xs leading-relaxed text-muted-foreground/80`,children:[(0,X.jsx)(t,{className:`w-3.5 h-3.5 mt-0.5 shrink-0 opacity-70`}),(0,X.jsx)(U,{text:e.text,issueRepo:r,className:`italic break-words min-w-0`})]},e.id):i?null:(0,X.jsxs)(`div`,{className:`flex items-start gap-1.5 text-xs leading-relaxed text-muted-foreground`,children:[(0,X.jsx)(ee,{className:`w-3 h-3 mt-0.5 shrink-0 opacity-70`}),(0,X.jsxs)(`span`,{className:`min-w-0 break-words`,children:[(0,X.jsx)(`span`,{className:`font-medium`,children:e.label||`tool`}),e.text&&e.text.trim()&&e.text.trim()!==(e.label??``).trim()?(0,X.jsxs)(`span`,{className:`opacity-70`,children:[` — `,e.text]}):null]})]},e.id)),c>0&&(0,X.jsxs)(`button`,{onClick:o,className:`flex items-center gap-1 text-[11px] text-muted-foreground/50 hover:text-muted-foreground transition-colors text-left`,children:[(0,X.jsx)(v,{className:a(`w-3 h-3 shrink-0 transition-transform`,!i&&`rotate-90`)}),(0,X.jsx)(`span`,{children:i?`show ${c} tool step${c===1?``:`s`}`:`hide tool steps`})]})]})})}function ut({date:e}){return(0,X.jsxs)(`div`,{className:`flex items-center justify-center gap-3 py-3 my-1`,children:[(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,X.jsx)(`span`,{className:`text-xs font-medium text-muted-foreground`,children:Ke(e)}),(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function dt({timestamp:e}){let t=new Date(e).toLocaleString(void 0,{month:`short`,day:`numeric`,year:`numeric`,hour:`2-digit`,minute:`2-digit`});return(0,X.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,X.jsxs)(`span`,{className:`text-xs text-muted-foreground/60`,children:[`created `,t]}),(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function ft({history:e}){let t=e.sourceAgentLabel||e.sourcePeerId,n=e.entries.length;return(0,X.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,X.jsxs)(`div`,{className:`flex items-center gap-1.5 rounded-full border border-dashed border-border bg-muted/30 px-2 py-1 text-[11px] text-muted-foreground`,children:[(0,X.jsx)(ie,{className:`h-3 w-3`}),(0,X.jsxs)(`span`,{children:[`forked history from `,t]}),(0,X.jsxs)(`span`,{className:`text-muted-foreground/60`,children:[n,` `,n===1?`entry`:`entries`]})]}),(0,X.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function pt({entry:e,sourceLabel:t,issueRepo:n}){let r=e.message,i=B(r),o=e.originalFrom===V;return(0,X.jsx)(`div`,{className:a(`flex mb-3 opacity-80`,o?`justify-end`:`justify-start`),children:(0,X.jsx)(`div`,{className:`max-w-[85%] md:max-w-[75%]`,children:(0,X.jsxs)(`div`,{className:a(`rounded-2xl border border-dashed px-3.5 py-2 text-sm select-text`,o?`border-primary/25 bg-primary/10 text-foreground rounded-br-sm`:`border-border bg-card/70 rounded-bl-sm`),children:[(0,X.jsxs)(`div`,{className:a(`mb-1 text-[10px] uppercase tracking-normal`,o?`text-primary/70`:`text-muted-foreground`),children:[`forked history · `,o?`you`:t||e.originalFrom]}),(0,X.jsx)(U,{text:i,rawBody:r.body,issueRepo:n,className:`leading-relaxed`}),(0,X.jsxs)(`p`,{className:a(`mt-1 flex items-center justify-end gap-1.5 text-xs`,o?`text-primary/60`:`text-muted-foreground`),children:[(0,X.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.originalMessageId]}),(0,X.jsx)(`span`,{children:Y(e.originalCreatedAt)})]})]})})})}function mt({msg:e,issueRepo:t}){let n=B(e),r=Y(e.occurredAt??e.createdAt);return(0,X.jsx)(`div`,{"data-msg-id":e.id,className:`mb-3`,children:(0,X.jsxs)(`div`,{className:`rounded-xl border border-primary/30 bg-primary/[0.06] px-3.5 py-2.5 shadow-sm`,children:[(0,X.jsxs)(`div`,{className:`mb-1.5 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-primary/80`,children:[(0,X.jsx)(k,{className:`h-3.5 w-3.5`}),(0,X.jsx)(`span`,{children:`Initial prompt`}),(0,X.jsx)(`span`,{className:`ml-auto text-[10px] font-normal normal-case text-muted-foreground/60`,children:r})]}),(0,X.jsx)(U,{text:n,rawBody:e.body,issueRepo:t,className:`text-sm leading-relaxed text-foreground`})]})})}function ht({scrollEl:e,timeline:t}){let[n,r]=(0,G.useState)(!0),[i,o]=(0,G.useState)(null),[s,c]=(0,G.useState)(null),[l,u]=(0,G.useState)(!1),d=(0,G.useMemo)(()=>{for(let e=t.length-1;e>=0;e--){let n=t[e];if(!(!n||n.type!==`message`&&n.type!==`initial-prompt`)&&n.msg.from===`user`)return{id:n.msg.id,body:B(n.msg)}}return null},[t]);if((0,G.useEffect)(()=>{if(!d||!e){r(!0);return}let t=null,n=null,i=()=>{let i=e.querySelector(`[data-msg-id="${d.id}"]`);if(!i){t?.disconnect(),t=null,n=null,r(!1);return}let a=i.getBoundingClientRect(),o=e.getBoundingClientRect();u(a.top<o.top),i!==n&&(t?.disconnect(),n=i,t=new IntersectionObserver(([e])=>{e&&r(e.isIntersecting)},{root:e,threshold:0}),t.observe(i))};return i(),e.addEventListener(`scroll`,i,{passive:!0}),()=>{t?.disconnect(),e.removeEventListener(`scroll`,i)}},[d?.id,e]),n||!d||!d.body.trim())return null;let f=i?.id===d.id?i.value:!1,p=s?.id===d.id?s.value:!1;function m(){!e||!d||e.querySelector(`[data-msg-id="${d.id}"]`)?.scrollIntoView({behavior:`smooth`,block:`center`})}let h=l?`↑`:`↓`;return p?(0,X.jsx)(`div`,{className:`flex shrink-0 bg-card/45 border-b border-border`,children:(0,X.jsxs)(`button`,{type:`button`,className:`w-full flex items-center justify-center gap-1.5 py-1 text-[11px] text-primary/60 hover:text-primary transition-colors`,onClick:()=>c({id:d.id,value:!1}),children:[(0,X.jsx)(`span`,{children:`Your prompt`}),(0,X.jsx)(w,{className:`w-3 h-3`})]})}):(0,X.jsxs)(`section`,{className:`flex shrink-0 flex-col border-b border-border bg-card/45 px-3 py-2`,children:[(0,X.jsxs)(`div`,{className:`flex items-center gap-2 text-[11px] text-muted-foreground mb-0.5`,children:[(0,X.jsx)(`span`,{className:`font-medium text-primary/70`,children:`Your prompt`}),(0,X.jsxs)(`button`,{type:`button`,className:`ml-auto hover:text-foreground transition-colors`,onClick:m,children:[h,` scroll to original`]}),(0,X.jsx)(`button`,{type:`button`,className:`hover:text-foreground transition-colors`,onClick:()=>c({id:d.id,value:!0}),title:`Minimize`,children:(0,X.jsx)(g,{className:`w-3 h-3`})})]}),(0,X.jsx)(`p`,{className:a(`text-sm text-foreground leading-snug cursor-pointer`,!f&&`line-clamp-2`),onClick:()=>o({id:d.id,value:!f}),children:d.body})]})}function gt(){return(0,G.useSyncExternalStore)(e=>j.subscribe(e),()=>j.getPlayingKey(),()=>null)}function _t({msg:e,peer:t}){return(0,X.jsx)(mt,{msg:e,issueRepo:M(e=>e.agentsById[t]?.issueRepo)})}function vt({agent:e,onClick:t}){let n=!!(t&&e);return(0,X.jsx)(`button`,{type:`button`,title:e?e.label||e.name||e.id:void 0,onClick:t,disabled:!n,className:a(`shrink-0 self-end transition-opacity`,n?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,X.jsx)(W,{agent:e})})}function yt({attachment:e,outbound:t}){let n=e.kind===`image`,r=e.title||e.artifactId,[i,l]=(0,G.useState)(null),[u,d]=(0,G.useState)(null),[f,p]=(0,G.useState)(!1),[m,h]=(0,G.useState)(!1);(0,G.useEffect)(()=>{let t=!1,n=null;async function r(){p(!0),d(null),l(null);try{let r=await s(`/artifacts/${encodeURIComponent(e.artifactId)}/content`);if(t)return;n=URL.createObjectURL(r),l(n)}catch(e){t||d(e.message)}finally{t||p(!1)}}return r(),()=>{t=!0,n&&URL.revokeObjectURL(n)}},[e.artifactId]);function g(){if(!i)return;let e=document.createElement(`a`);e.href=i,e.download=r,document.body.appendChild(e),e.click(),e.remove()}return n?(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`button`,{type:`button`,onClick:()=>i&&h(!0),disabled:!i,className:`group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative`,children:i?(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(`img`,{src:i,alt:r,loading:`lazy`,className:`block max-h-72 max-w-full object-contain`}),(0,X.jsx)(`span`,{className:`absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors`,children:(0,X.jsx)(T,{className:`w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md`})})]}):(0,X.jsxs)(`span`,{className:a(`flex min-h-20 items-center gap-2 px-3 py-2 text-xs`,u?`text-red-400`:`text-muted-foreground`),children:[f?(0,X.jsx)(S,{className:`h-4 w-4 animate-spin`}):(0,X.jsx)(O,{className:`h-4 w-4`}),u||`Loading image`]})}),i&&(0,X.jsx)(fe,{open:m,onOpenChange:h,children:(0,X.jsxs)(de,{className:`max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden`,showCloseButton:!1,children:[(0,X.jsx)(pe,{className:`sr-only`,children:r}),(0,X.jsxs)(`div`,{className:`relative flex items-center justify-center min-h-[200px]`,children:[(0,X.jsx)(`img`,{src:i,alt:r,className:`max-w-[93vw] max-h-[90vh] object-contain`}),(0,X.jsxs)(`div`,{className:`absolute top-2 right-2 flex gap-1`,children:[(0,X.jsx)(`button`,{type:`button`,onClick:g,className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Download`,children:(0,X.jsx)(c,{className:`w-4 h-4`})}),(0,X.jsx)(`button`,{type:`button`,onClick:()=>h(!1),className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Close`,children:(0,X.jsx)(x,{className:`w-4 h-4`})})]})]})]})})]}):(0,X.jsxs)(`button`,{type:`button`,onClick:g,disabled:!i,className:a(`mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70`,t?`border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15`:`border-border bg-background hover:bg-muted`),children:[f?(0,X.jsx)(S,{className:`h-4 w-4 shrink-0 animate-spin`}):u?(0,X.jsx)(O,{className:`h-4 w-4 shrink-0 text-red-400`}):(0,X.jsx)(o,{className:`h-4 w-4 shrink-0`}),(0,X.jsx)(`span`,{className:a(`min-w-0 flex-1 truncate`,u&&`text-red-400`),children:u||r}),(0,X.jsx)(c,{className:`h-3.5 w-3.5 shrink-0`})]})}function bt({open:e,onToggle:t,onReact:n}){return(0,X.jsxs)(`div`,{className:`relative shrink-0`,children:[(0,X.jsx)(`button`,{type:`button`,title:`Add reaction`,onClick:t,className:a(`inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground`,e?`opacity-100`:`opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto`),children:(0,X.jsx)(m,{className:`h-3 w-3`})}),e&&(0,X.jsx)(`div`,{className:`absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md`,children:Ie.map(e=>(0,X.jsx)(`button`,{type:`button`,title:`React ${e}`,onClick:()=>n(e),className:`inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted`,children:e},e))})]})}function xt({msg:e,peer:t}){let[n,r]=(0,G.useState)(!1),o=M(e=>e.agentsById),s=M(e=>e.openAgentDetail),c=e.from===t,l=(c?e.to:e.from)||``,d=o[c?t:l]?.issueRepo??o[t]?.issueRepo,f=!l||l===`broadcast`||l.includes(`:`),p=B(e),m=Y(e.occurredAt??e.createdAt),h=c?u:i,g=o[e.from??``]??null;function _(e){e.stopPropagation(),g&&s(g)}return(0,X.jsx)(`div`,{"data-msg-id":e.id,className:`flex justify-center mb-1.5`,children:(0,X.jsxs)(`div`,{className:`w-full max-w-[94%] rounded-md border border-border/50 bg-muted/15 px-2.5 py-1`,children:[(0,X.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs cursor-pointer select-none`,onClick:()=>r(e=>!e),children:[(0,X.jsx)(v,{className:a(`h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform`,n&&`rotate-90`)}),(0,X.jsx)(`button`,{type:`button`,title:g?g.label||g.name||g.id:void 0,disabled:!g,onClick:_,className:a(`shrink-0 transition-opacity`,g?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,X.jsx)(W,{agent:g,className:`!w-4 !h-4 !rounded-sm opacity-70`})}),(0,X.jsx)(h,{className:`h-3 w-3 shrink-0 text-muted-foreground/70`}),(0,X.jsxs)(`span`,{className:`shrink-0 text-muted-foreground`,children:[`Message `,c?`to`:`from`]}),f?(0,X.jsx)(`span`,{className:`shrink-0 font-medium text-muted-foreground`,children:le(l,o)}):(0,X.jsx)(Z,{agentId:l,className:`shrink-0 px-0.5 text-foreground/80`,noIcon:!0}),!n&&(0,X.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-muted-foreground/50`,children:p}),(0,X.jsx)(`span`,{className:`ml-auto shrink-0 text-muted-foreground/40`,children:m})]}),n&&(0,X.jsx)(`div`,{className:`mt-1.5 pl-5`,children:(0,X.jsx)(U,{text:p,rawBody:e.body,issueRepo:d,className:`text-sm leading-relaxed text-foreground/90`})})]})})}var St=(0,G.memo)(function({msg:e,peer:t,showAvatar:n=!1,onOpenReferencedPath:r,onPreviewReferencedPath:i,onPreviewReferencedPathEnd:o}){if(!z(e.from)&&!z(e.to))return(0,X.jsx)(xt,{msg:e,peer:t});let s=e.kind===`session`?e.payload?.session:void 0;if(s?.type===`prompt`&&s?.origin===`provider`)return(0,X.jsx)(_t,{msg:e,peer:t});let c=z(e.from),l=M(e=>e.reactToMessage),u=M(e=>e.voiceTtsEnabled),d=gt(),f=M(e=>{let n=e.agentsById[t]?.meta?.cwd;return typeof n==`string`?n:``}),p=M(e=>e.agentsById[t]?.issueRepo),m=M(e=>e.agentsById[t]??null),g=M(e=>e.usersById),_=M(e=>e.users.length>1),v=c?oe(e.from,g):void 0,ee=M(e=>e.openAgentDetail),[y,b]=(0,G.useState)(!1),x=(0,G.useRef)(null),S=(0,G.useRef)(null),C=(0,G.useRef)(!1),w=B(e),ne=Y(e.occurredAt??e.createdAt),re=Fe(e),T=Le(e),E=c?Re(e,t):null,D=E?.icon,O=d===String(e.id),ie=u&&!c&&!J(e)&&w.trim().length>0;function k(t){t.stopPropagation(),O?j.bargeIn():j.speak(e.body,String(e.id))}(0,G.useEffect)(()=>{if(!y)return;function e(e){Pe()||x.current&&!x.current.contains(e.target)&&b(!1)}return document.addEventListener(`mousedown`,e),document.addEventListener(`touchstart`,e),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`touchstart`,e)}},[y]);function A(e){return Ae(f||`/`,e)}function ae(e,t){let n=A(e);r?.(n,t)}function N(e,t,n){let r=A(e);i?.(r,t,n)}function P(t){l(e,t),b(!1)}function F(e){e.button===0&&(S.current={x:e.clientX,y:e.clientY},C.current=!1)}function I(e){let t=S.current;t&&(Math.abs(e.clientX-t.x)>3||Math.abs(e.clientY-t.y)>3)&&(C.current=!0)}function L(t){if(!(J(e)||c)&&!t.target.closest(`a,button,input,textarea,select,[role="button"]`)){if(C.current||Pe()){C.current=!1;return}b(e=>!e)}}let R=n&&!J(e);function ce(){!c&&m&&ee(m)}return(0,X.jsxs)(`div`,{"data-msg-id":e.id,className:a(`group/msg flex mb-3 items-end`,c?`justify-end`:`justify-start`,R&&`gap-1.5`),children:[!c&&R&&(0,X.jsx)(vt,{agent:m,onClick:ce}),(0,X.jsxs)(`div`,{ref:x,className:a(`relative`,R?`max-w-[calc(85%-2rem)] md:max-w-[calc(75%-2rem)]`:`max-w-[85%] md:max-w-[75%]`),children:[ie&&(0,X.jsx)(`button`,{type:`button`,title:O?`Stop playback`:`Play aloud`,onClick:k,className:a(`absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition`,O?`border-primary/50 text-primary opacity-100`:`border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100`),children:O?(0,X.jsx)(te,{className:`h-3.5 w-3.5 animate-pulse`}):(0,X.jsx)(h,{className:`h-3.5 w-3.5`})}),(0,X.jsxs)(`div`,{className:a(`rounded-2xl px-3.5 py-2 text-sm select-text`,!c&&!J(e)&&`cursor-pointer`,c?`bg-primary text-primary-foreground rounded-br-sm`:`bg-card ring-1 ring-foreground/10 rounded-bl-sm`),onPointerDown:F,onPointerMove:I,onClick:L,children:[c&&_&&(0,X.jsx)(`p`,{className:a(`text-[11px] font-semibold mb-0.5 text-right`,se(e.from)),children:ue(e.from,g)}),e.subject&&(0,X.jsx)(`p`,{className:a(`text-xs font-semibold mb-1`,c?`text-primary-foreground/70`:`text-muted-foreground`),children:e.subject}),(0,X.jsx)(U,{text:w,rawBody:e.body,issueRepo:p,className:`leading-relaxed`,onPrimary:c,onOpenPath:ae,onPreviewPath:N,onPreviewPathEnd:o,resolvePathTitle:(e,t)=>`${A(e)}${t?`:${t}`:``}`}),re.map(t=>(0,X.jsx)(yt,{attachment:t,outbound:c},`${e.id}-${t.artifactId}-${t.role||``}`)),(0,X.jsxs)(`p`,{className:a(`text-xs mt-1 flex items-center gap-1.5`,c?`justify-end text-primary-foreground/60`:`justify-end text-muted-foreground`),children:[(0,X.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.id]}),(0,X.jsx)(`span`,{children:ne}),E&&D&&(0,X.jsxs)(`span`,{className:a(`inline-flex items-center gap-1`,E.tone===`ok`&&`text-emerald-300`,E.tone===`warn`&&`text-amber-200`,E.tone===`danger`&&`text-red-200`),title:E.title,children:[(0,X.jsx)(D,{className:`h-3 w-3`}),E.label]})]})]}),!J(e)&&T.length>0&&(0,X.jsxs)(`div`,{className:a(`flex flex-wrap items-center gap-0.5 -mt-1 relative z-10`,c?`justify-end pr-2`:`justify-start pl-2`),children:[!c&&(0,X.jsx)(bt,{open:y,onToggle:()=>b(e=>!e),onReact:P}),T.map(e=>(0,X.jsxs)(`button`,{type:`button`,title:e.actors.join(`, `),onClick:()=>P(e.emoji),className:a(`inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors`,e.mine?`border-primary/40 bg-primary/10 text-primary`:`border-border bg-background hover:bg-muted text-foreground`),children:[(0,X.jsx)(`span`,{children:e.emoji}),e.count>1&&(0,X.jsx)(`span`,{children:e.count})]},e.emoji))]}),!J(e)&&!c&&T.length===0&&(0,X.jsx)(`div`,{className:`absolute -bottom-2.5 left-1 z-20`,children:(0,X.jsx)(bt,{open:y,onToggle:()=>b(e=>!e),onReact:P})})]}),c&&R&&(0,X.jsx)(`span`,{className:`shrink-0 self-end opacity-60`,children:(0,X.jsx)(me,{user:v,address:e.from,distinct:_})})]})});export{Te as C,Be as S,De as T,Me as _,dt as a,Ue as b,pt as c,ot as d,qe as f,He as g,ke as h,st as i,mt as l,Ve as m,lt as n,ut as o,ht as p,nt as r,ft as s,St as t,Qe as u,je as v,Ee as w,Ne as x,We as y};
3
- //# sourceMappingURL=MessageBubble-BORB9AKu.js.map