agent-relay-server 0.127.2 → 0.127.4

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 (156) hide show
  1. package/docs/openapi.json +1 -1
  2. package/package.json +7 -5
  3. package/public/assets/{MessageBubble-DnXnkt4a.js → MessageBubble-D2Wzxied.js} +2 -2
  4. package/public/assets/{MessageBubble-DnXnkt4a.js.map → MessageBubble-D2Wzxied.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-D6CPEpQD.js → PlanGraphPanel-DH_xoWZi.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-D6CPEpQD.js.map → PlanGraphPanel-DH_xoWZi.js.map} +1 -1
  7. package/public/assets/{activity-ZPwHcPLK.js → activity-B-ozOwNq.js} +2 -2
  8. package/public/assets/{activity-ZPwHcPLK.js.map → activity-B-ozOwNq.js.map} +1 -1
  9. package/public/assets/{agent-profiles-CT0E9LV1.js → agent-profiles-DZgjO25A.js} +2 -2
  10. package/public/assets/{agent-profiles-CT0E9LV1.js.map → agent-profiles-DZgjO25A.js.map} +1 -1
  11. package/public/assets/{agent-type-icon-BmBR-9CH.js → agent-type-icon-B9cLaOoz.js} +2 -2
  12. package/public/assets/{agent-type-icon-BmBR-9CH.js.map → agent-type-icon-B9cLaOoz.js.map} +1 -1
  13. package/public/assets/agents-mXBLSY6O.js +2 -0
  14. package/public/assets/{agents-Dt5TncjC.js.map → agents-mXBLSY6O.js.map} +1 -1
  15. package/public/assets/{analytics-BZtRycyL.js → analytics-5Ize_UL5.js} +3 -3
  16. package/public/assets/{analytics-BZtRycyL.js.map → analytics-5Ize_UL5.js.map} +1 -1
  17. package/public/assets/{automation-yzUJbvT2.js → automation-CidMvW2e.js} +2 -2
  18. package/public/assets/{automation-yzUJbvT2.js.map → automation-CidMvW2e.js.map} +1 -1
  19. package/public/assets/{badge-BCvp7LK3.js → badge-BkNNvFNa.js} +2 -2
  20. package/public/assets/{badge-BCvp7LK3.js.map → badge-BkNNvFNa.js.map} +1 -1
  21. package/public/assets/{branch-state-badge-Cscjqf-E.js → branch-state-badge-DnS40KCb.js} +2 -2
  22. package/public/assets/{branch-state-badge-Cscjqf-E.js.map → branch-state-badge-DnS40KCb.js.map} +1 -1
  23. package/public/assets/{button-DyHjsHY3.js → button-DUaC2wSQ.js} +2 -2
  24. package/public/assets/{button-DyHjsHY3.js.map → button-DUaC2wSQ.js.map} +1 -1
  25. package/public/assets/{card-BoqYxpVW.js → card-r-yAV4cH.js} +2 -2
  26. package/public/assets/{card-BoqYxpVW.js.map → card-r-yAV4cH.js.map} +1 -1
  27. package/public/assets/{channels-DAtOnA6o.js → channels-MPO2FOBv.js} +2 -2
  28. package/public/assets/{channels-DAtOnA6o.js.map → channels-MPO2FOBv.js.map} +1 -1
  29. package/public/assets/chat-Ca4ERso5.js +2 -0
  30. package/public/assets/chat-Ca4ERso5.js.map +1 -0
  31. package/public/assets/{connectors-B2grC-hl.js → connectors-BbYdtRkW.js} +2 -2
  32. package/public/assets/{connectors-B2grC-hl.js.map → connectors-BbYdtRkW.js.map} +1 -1
  33. package/public/assets/copy-button-u463GPZR.js +2 -0
  34. package/public/assets/{copy-button-C67FUG3E.js.map → copy-button-u463GPZR.js.map} +1 -1
  35. package/public/assets/{display-aTnwsqQh.js → display-BNI-7rdq.js} +2 -2
  36. package/public/assets/{display-aTnwsqQh.js.map → display-BNI-7rdq.js.map} +1 -1
  37. package/public/assets/{dist-D5w04z0l.js → dist-5e_is3Xr.js} +2 -2
  38. package/public/assets/{dist-D5w04z0l.js.map → dist-5e_is3Xr.js.map} +1 -1
  39. package/public/assets/{dist-BD4Xc13v.js → dist-ActI2qb5.js} +2 -2
  40. package/public/assets/{dist-BD4Xc13v.js.map → dist-ActI2qb5.js.map} +1 -1
  41. package/public/assets/dist-B-FQ0-X3.js +2 -0
  42. package/public/assets/{dist-G8YBD-wp.js.map → dist-B-FQ0-X3.js.map} +1 -1
  43. package/public/assets/{dist-f6MW74eX.js → dist-BduURKro.js} +2 -2
  44. package/public/assets/{dist-f6MW74eX.js.map → dist-BduURKro.js.map} +1 -1
  45. package/public/assets/{dist-BFDfbfNC.js → dist-Bp0-OBcd.js} +2 -2
  46. package/public/assets/{dist-BFDfbfNC.js.map → dist-Bp0-OBcd.js.map} +1 -1
  47. package/public/assets/{dist-C_VOBSaK.js → dist-CJ7e7uqU.js} +2 -2
  48. package/public/assets/{dist-C_VOBSaK.js.map → dist-CJ7e7uqU.js.map} +1 -1
  49. package/public/assets/{dist-BNB3fTO-.js → dist-CYzOsD_n.js} +2 -2
  50. package/public/assets/{dist-BNB3fTO-.js.map → dist-CYzOsD_n.js.map} +1 -1
  51. package/public/assets/{dist-CQMQ5iku.js → dist-CpcQJrKV.js} +2 -2
  52. package/public/assets/{dist-CQMQ5iku.js.map → dist-CpcQJrKV.js.map} +1 -1
  53. package/public/assets/dist-DFgmDjLv.js +2 -0
  54. package/public/assets/{dist-CHKra2JV.js.map → dist-DFgmDjLv.js.map} +1 -1
  55. package/public/assets/{dist-DG75bvPp.js → dist-DrpSpMKL.js} +2 -2
  56. package/public/assets/{dist-DG75bvPp.js.map → dist-DrpSpMKL.js.map} +1 -1
  57. package/public/assets/dist-DzWoiN2z.js +2 -0
  58. package/public/assets/{dist-BJKxqIQd.js.map → dist-DzWoiN2z.js.map} +1 -1
  59. package/public/assets/{es2015-a6fysEVz.js → es2015-DNuudVtQ.js} +2 -2
  60. package/public/assets/{es2015-a6fysEVz.js.map → es2015-DNuudVtQ.js.map} +1 -1
  61. package/public/assets/formatted-body-BEI1zP7i.js +3 -0
  62. package/public/assets/{formatted-body-D5-1aiyB.js.map → formatted-body-BEI1zP7i.js.map} +1 -1
  63. package/public/assets/{formatted-body-impl-Do5NbJUA.js → formatted-body-impl-CNQ5sqwK.js} +2 -2
  64. package/public/assets/{formatted-body-impl-Do5NbJUA.js.map → formatted-body-impl-CNQ5sqwK.js.map} +1 -1
  65. package/public/assets/index-D2Mpzpft.js +26 -0
  66. package/public/assets/{index-BdNafiPz.js.map → index-D2Mpzpft.js.map} +1 -1
  67. package/public/assets/{input-Rc4oWv4q.js → input-BvNUEkDd.js} +2 -2
  68. package/public/assets/{input-Rc4oWv4q.js.map → input-BvNUEkDd.js.map} +1 -1
  69. package/public/assets/{insights-i6AEZT_a.js → insights-5rkMFRgf.js} +2 -2
  70. package/public/assets/{insights-i6AEZT_a.js.map → insights-5rkMFRgf.js.map} +1 -1
  71. package/public/assets/{integrations-9c6IdRU4.js → integrations-BEaqjfP4.js} +2 -2
  72. package/public/assets/{integrations-9c6IdRU4.js.map → integrations-BEaqjfP4.js.map} +1 -1
  73. package/public/assets/{lib-BZ6WPg2t.js → lib-BvrbNA2I.js} +2 -2
  74. package/public/assets/{lib-BZ6WPg2t.js.map → lib-BvrbNA2I.js.map} +1 -1
  75. package/public/assets/{lucide-react-NXHPIMsX.js → lucide-react-DD_zIava.js} +2 -2
  76. package/public/assets/{lucide-react-NXHPIMsX.js.map → lucide-react-DD_zIava.js.map} +1 -1
  77. package/public/assets/maintenance-CEiURaYZ.js +2 -0
  78. package/public/assets/{maintenance-CJ6wikpg.js.map → maintenance-CEiURaYZ.js.map} +1 -1
  79. package/public/assets/{managed-agents-JZn0xNJR.js → managed-agents-CetHyuQW.js} +4 -4
  80. package/public/assets/{managed-agents-JZn0xNJR.js.map → managed-agents-CetHyuQW.js.map} +1 -1
  81. package/public/assets/{markdown-preview-impl-BsvAkpR4.js → markdown-preview-impl-ZwGYvObh.js} +2 -2
  82. package/public/assets/{markdown-preview-impl-BsvAkpR4.js.map → markdown-preview-impl-ZwGYvObh.js.map} +1 -1
  83. package/public/assets/memory-BTm8XzCg.js +2 -0
  84. package/public/assets/{memory-BE9IwCdC.js.map → memory-BTm8XzCg.js.map} +1 -1
  85. package/public/assets/{messages-vXgdc3H4.js → messages-fJd17pzv.js} +2 -2
  86. package/public/assets/{messages-vXgdc3H4.js.map → messages-fJd17pzv.js.map} +1 -1
  87. package/public/assets/{orchestrators-CribhLg-.js → orchestrators-C5XQ2UYy.js} +2 -2
  88. package/public/assets/{orchestrators-CribhLg-.js.map → orchestrators-C5XQ2UYy.js.map} +1 -1
  89. package/public/assets/overview-Bhk-tqKc.js +2 -0
  90. package/public/assets/{overview-q3O9K9VK.js.map → overview-Bhk-tqKc.js.map} +1 -1
  91. package/public/assets/{pairs-BNOnd0IE.js → pairs-CBrszSZh.js} +2 -2
  92. package/public/assets/{pairs-BNOnd0IE.js.map → pairs-CBrszSZh.js.map} +1 -1
  93. package/public/assets/{projects-iDkxAGBW.js → projects-DXy9JviE.js} +2 -2
  94. package/public/assets/{projects-iDkxAGBW.js.map → projects-DXy9JviE.js.map} +1 -1
  95. package/public/assets/prompt-settings-DEQhAt2X.js +2 -0
  96. package/public/assets/{prompt-settings-Bdz_xCgD.js.map → prompt-settings-DEQhAt2X.js.map} +1 -1
  97. package/public/assets/{react-dom-8yaD9iJG.js → react-dom-CADlISeR.js} +2 -2
  98. package/public/assets/{react-dom-8yaD9iJG.js.map → react-dom-CADlISeR.js.map} +1 -1
  99. package/public/assets/{registry-DPcyGypi.js → registry-DsZILce1.js} +4 -4
  100. package/public/assets/{registry-DPcyGypi.js.map → registry-DsZILce1.js.map} +1 -1
  101. package/public/assets/{security-Z3l3u-Pl.js → security-C-wVZm5k.js} +2 -2
  102. package/public/assets/{security-Z3l3u-Pl.js.map → security-C-wVZm5k.js.map} +1 -1
  103. package/public/assets/{select-BX_lW49D.js → select-DsEGBLJk.js} +2 -2
  104. package/public/assets/{select-BX_lW49D.js.map → select-DsEGBLJk.js.map} +1 -1
  105. package/public/assets/settings-D3_r5IGA.js +6 -0
  106. package/public/assets/{settings-Y381jZt7.js.map → settings-D3_r5IGA.js.map} +1 -1
  107. package/public/assets/{store-D-Frk_EC.js → store-DKTb9O0v.js} +5 -5
  108. package/public/assets/{store-D-Frk_EC.js.map → store-DKTb9O0v.js.map} +1 -1
  109. package/public/assets/{task-chip-CeujtXYT.js → task-chip-DwdauC08.js} +2 -2
  110. package/public/assets/{task-chip-CeujtXYT.js.map → task-chip-DwdauC08.js.map} +1 -1
  111. package/public/assets/{tasks-Di6d0_Ch.js → tasks-DnEzBku4.js} +2 -2
  112. package/public/assets/{tasks-Di6d0_Ch.js.map → tasks-DnEzBku4.js.map} +1 -1
  113. package/public/assets/{teams-DJv4l-_l.js → teams-DpAbZ3ye.js} +2 -2
  114. package/public/assets/{teams-DJv4l-_l.js.map → teams-DpAbZ3ye.js.map} +1 -1
  115. package/public/assets/{terminal-viewer-impl-3zBLpRel.js → terminal-viewer-impl-_IU5o8ya.js} +3 -3
  116. package/public/assets/{terminal-viewer-impl-3zBLpRel.js.map → terminal-viewer-impl-_IU5o8ya.js.map} +1 -1
  117. package/public/assets/types-BqeSkWrv.js.map +1 -1
  118. package/public/assets/{use-keyboard-viewport-cGEmrGE7.js → use-keyboard-viewport-wTh5CR4-.js} +2 -2
  119. package/public/assets/{use-keyboard-viewport-cGEmrGE7.js.map → use-keyboard-viewport-wTh5CR4-.js.map} +1 -1
  120. package/public/assets/user-avatar-CZ3XO8ml.js +2 -0
  121. package/public/assets/{user-avatar-qbo-B3dQ.js.map → user-avatar-CZ3XO8ml.js.map} +1 -1
  122. package/public/assets/{work-queue-D73-hP0c.js → work-queue-DKcLZWRl.js} +2 -2
  123. package/public/assets/{work-queue-D73-hP0c.js.map → work-queue-DKcLZWRl.js.map} +1 -1
  124. package/public/assets/workspaces-Cs2IZZOI.js +3 -0
  125. package/public/assets/{workspaces-CoDz-iZd.js.map → workspaces-Cs2IZZOI.js.map} +1 -1
  126. package/public/index.html +24 -24
  127. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  128. package/runner/plugins/claude/monitors/relay-monitor.provisioned.mjs +4 -1
  129. package/runner/src/adapter.ts +5 -1
  130. package/runner/src/adapters/claude-delivery.ts +5 -1
  131. package/runner/src/adapters/claude-transcript.ts +52 -3
  132. package/runner/src/control-server.ts +34 -17
  133. package/runner/src/runner-core.ts +73 -32
  134. package/scripts/orchestrator-spawn-smoke.ts +81 -15
  135. package/src/bus.ts +6 -41
  136. package/src/db/messages.ts +20 -8
  137. package/src/prompt-resolver.ts +12 -12
  138. package/src/self-resume.ts +50 -8
  139. package/src/services/bus-command-update.ts +59 -0
  140. package/src/services/task-delivery.ts +30 -3
  141. package/public/assets/agents-Dt5TncjC.js +0 -2
  142. package/public/assets/chat-DhYX9BDG.js +0 -2
  143. package/public/assets/chat-DhYX9BDG.js.map +0 -1
  144. package/public/assets/copy-button-C67FUG3E.js +0 -2
  145. package/public/assets/dist-BJKxqIQd.js +0 -2
  146. package/public/assets/dist-CHKra2JV.js +0 -2
  147. package/public/assets/dist-G8YBD-wp.js +0 -2
  148. package/public/assets/formatted-body-D5-1aiyB.js +0 -3
  149. package/public/assets/index-BdNafiPz.js +0 -26
  150. package/public/assets/maintenance-CJ6wikpg.js +0 -2
  151. package/public/assets/memory-BE9IwCdC.js +0 -2
  152. package/public/assets/overview-q3O9K9VK.js +0 -2
  153. package/public/assets/prompt-settings-Bdz_xCgD.js +0 -2
  154. package/public/assets/settings-Y381jZt7.js +0 -6
  155. package/public/assets/user-avatar-qbo-B3dQ.js +0 -2
  156. package/public/assets/workspaces-CoDz-iZd.js +0 -3
package/src/bus.ts CHANGED
@@ -3,9 +3,7 @@ import { ValidationError, getAgent, getDb, heartbeat, markReady, mergeAgentMeta,
3
3
  import { getOutboxCursor, hasReplayGap, replayEventsSlice, type BusEvent } from "./bus-outbox";
4
4
  import { projectAgentStatusData } from "./agent-card-projection";
5
5
  import { emitRelayEvent, subscribeRelayEvents, type RelayEvent } from "./events";
6
- import { getCommand, updateCommand } from "./commands-db";
7
- import { emitCommandEvent } from "./command-events";
8
- import { noteAgentTimelineEvent, noteCompactionCommandCompleted } from "./compaction-watch";
6
+ import { noteAgentTimelineEvent } from "./compaction-watch";
9
7
  import { reconcileTerminalProviderExit } from "./services/terminal-provider-exit";
10
8
  import { bindSpawnedTaskToReadyAgent, registerAgent } from "./services/register-agent";
11
9
  import { authContextFromBus } from "./services/auth-context";
@@ -15,9 +13,8 @@ import { agentProviderTimelineEvent, auditRunnerTimelineEvent } from "./provider
15
13
  import { ServiceAuthError } from "./services/errors";
16
14
  import { ShutdownAuthError, ShutdownTargetError, shutdownAgent, shutdownInputFromBusFrame } from "./services/shutdown-agent";
17
15
  import { flushQueuedDirectMessages } from "./services/managed-running";
16
+ import { updateCommandFromBus } from "./services/bus-command-update";
18
17
  import { isAgentUnavailable } from "./db/agent-predicates";
19
- import { applyCommandToRecipe } from "./recipe-runner";
20
- import { enqueueContinuationInjectionAfterSelfResume } from "./self-resume";
21
18
  import { messageMatchesAgent, targetMatchesAgent } from "./agent-ref";
22
19
  import { probeWorkspaceOnTurnEnd } from "./workspace-probe";
23
20
  import { busStaleGraceMs } from "./config";
@@ -31,7 +28,7 @@ import {
31
28
  } from "agent-relay-sdk/protocol";
32
29
  import { errMessage, isRecord } from "agent-relay-sdk";
33
30
  import { getComponentAuth, isComponentAuthorizedFor, isAuthorized, isOriginAllowed, unauthorized } from "./security";
34
- import type { AgentCard, Command, ComponentToken, ContextState, Message, ProviderCapabilities, QuotaState, RegisterAgentInput, Task } from "./types";
31
+ import type { AgentCard, ComponentToken, ContextState, Message, ProviderCapabilities, QuotaState, RegisterAgentInput, Task } from "./types";
35
32
 
36
33
  interface BusSocketData {
37
34
  kind: "bus";
@@ -238,30 +235,9 @@ function handleCommandFrame(
238
235
  params: Record<string, unknown>,
239
236
  ): void {
240
237
  if (commandType === "command.update") {
241
- const command = getCommand(target);
242
- if (!command) {
243
- sendCommandResult(ws, frameId, "failed", undefined, "command not found");
244
- return;
245
- }
246
- if (!busCommandAuthorized(conn, command)) {
247
- sendCommandResult(ws, frameId, "rejected", undefined, "component token lacks command scope");
248
- return;
249
- }
250
- const status = typeof params.status === "string" ? params.status : undefined;
251
- const updated = updateCommand(command.id, {
252
- status: status as any,
253
- result: isRecord(params.result) ? params.result : undefined,
254
- error: typeof params.error === "string" ? params.error : undefined,
255
- });
256
- if (updated) {
257
- applyCommandToRecipe(updated);
258
- emitCommandEvent(updated, `command.${updated.status}`);
259
- // Production command completions arrive here over the bus, not the HTTP
260
- // PATCH route — arm the stall watch for finished compact/clear commands.
261
- noteCompactionCommandCompleted(updated.type, updated.status, updated.id, updated.target);
262
- enqueueContinuationInjectionAfterSelfResume(updated);
263
- }
264
- sendCommandResult(ws, frameId, "succeeded", updated ? { command: updated } : undefined);
238
+ void updateCommandFromBus({ target, params, componentAuth: conn.componentAuth })
239
+ .then((result) => sendCommandResult(ws, frameId, result.status, result.result, result.error))
240
+ .catch((error) => sendError(ws, frameId, "FRAME_FAILED", errMessage(error)));
265
241
  return;
266
242
  }
267
243
 
@@ -303,17 +279,6 @@ function handleCommandFrame(
303
279
  }
304
280
  }
305
281
 
306
- function busCommandAuthorized(
307
- conn: BusConnection,
308
- command: Pick<Command, "target" | "params">,
309
- ): boolean {
310
- if (!conn.componentAuth) return true;
311
- return isComponentAuthorizedFor(conn.componentAuth, {
312
- scope: "command:write",
313
- resource: commandAuthorizationResource(command),
314
- });
315
- }
316
-
317
282
  function handleRegister(ws: BusWebSocket, frame: RegisterFrame): void {
318
283
  const payload = frame.payload;
319
284
  const runnerManaged = payload.meta?.runnerManaged === true || typeof payload.meta?.runnerId === "string";
@@ -138,8 +138,10 @@ function normalizeMessagePayload(payload: MessagePayload | undefined): MessagePa
138
138
  * stable `payload.session.stepId`. Instead of appending a second row (which renders as a
139
139
  * duplicate on dashboard reload), update the EXISTING row in place: same DB id, so the
140
140
  * dashboard's id-keyed merge replaces it. Gated on stepId presence + matching step, so the
141
- * Claude transcript path (no stepId) is byte-identical. Newer-event-wins via occurredAt so a
142
- * late retry of an earlier lifecycle phase can't clobber a later one.
141
+ * Claude transcript path (no stepId) is byte-identical. The row keeps the first event's
142
+ * occurredAt so long-running tools stay anchored at their start; completion time rides in
143
+ * payload.session.completedAt. Lifecycle rank still prevents a late running retry from
144
+ * clobbering an already-completed row.
143
145
  */
144
146
  function upsertSessionStepInPlace(existing: Message, input: SendMessageInput, now: number): Message | null {
145
147
  const incomingStep = sessionStepId(input.payload);
@@ -147,14 +149,24 @@ function upsertSessionStepInPlace(existing: Message, input: SendMessageInput, no
147
149
  if (existing.kind !== "session") return null;
148
150
  if (sessionStepId(existing.payload) !== incomingStep) return null;
149
151
  const incomingAt = sanitizeOccurredAt(input.occurredAt, now) ?? now;
150
- const existingAt = existing.occurredAt ?? existing.createdAt;
151
- if (incomingAt < existingAt) return null; // stale lifecycle phase — keep the newer row, no re-emit
152
- getDb()
153
- .query("UPDATE messages SET body = ?, payload = ?, occurred_at = ? WHERE id = ?")
154
- .run(input.body, JSON.stringify(input.payload ?? {}), incomingAt, existing.id);
152
+ if (sessionStepStatusRank(input.payload) < sessionStepStatusRank(existing.payload)) return null;
153
+ const payload = sessionPayloadWithCompletion(input.payload, incomingAt);
154
+ getDb().query("UPDATE messages SET body = ?, payload = ? WHERE id = ?").run(input.body, JSON.stringify(payload), existing.id);
155
155
  return getMessage(existing.id);
156
156
  }
157
157
 
158
+ function sessionStepStatusRank(payload: MessagePayload | undefined): number {
159
+ const status = stringValue(isRecord(payload?.session) ? payload.session.status : undefined);
160
+ return status === "completed" || status === "failed" ? 2 : status === "running" ? 1 : 0;
161
+ }
162
+
163
+ function sessionPayloadWithCompletion(payload: MessagePayload | undefined, completedAt: number): MessagePayload {
164
+ const original = payload ?? {};
165
+ const session = isRecord(original.session) ? original.session : undefined;
166
+ const status = stringValue(session?.status);
167
+ return status === "completed" || status === "failed" ? { ...original, session: { ...session, completedAt } } : original;
168
+ }
169
+
158
170
  function policyNameFromTarget(target: string): string | null {
159
171
  if (!target.startsWith("policy:")) return null;
160
172
  const name = target.slice("policy:".length).trim();
@@ -256,7 +268,7 @@ function lineageStreamTurnId(message: Message, session: Record<string, unknown>)
256
268
  }
257
269
 
258
270
  function lineageStreamEventId(message: Message, session: Record<string, unknown>): string {
259
- const occurredAt = message.occurredAt ?? message.createdAt;
271
+ const occurredAt = typeof session.completedAt === "number" && Number.isFinite(session.completedAt) ? Math.floor(session.completedAt) : message.occurredAt ?? message.createdAt;
260
272
  const stepId = stringValue(session.stepId);
261
273
  return stepId ? `${stepId}:${occurredAt}` : `message:${occurredAt}`;
262
274
  }
@@ -147,35 +147,35 @@ const DEFAULT_PROMPT_TEMPLATES = [
147
147
  "Objective (verbatim, relay-owned):",
148
148
  "{{objective}}",
149
149
  "",
150
- "Ruled-out ledger (append-only):",
151
- "{{ruledOut}}",
152
- "",
153
- "Decision log (append-only):",
154
- "{{decisions}}",
155
- "",
156
150
  "Working state:",
157
151
  "{{workingState}}",
158
152
  "",
159
153
  "Archive refs:",
160
154
  "{{archiveRefs}}",
161
- ),
162
- defaultTemplate: lines(
163
- "{{intro}}",
164
- "",
165
- "Objective (verbatim, relay-owned):",
166
- "{{objective}}",
167
155
  "",
168
156
  "Ruled-out ledger (append-only):",
169
157
  "{{ruledOut}}",
170
158
  "",
171
159
  "Decision log (append-only):",
172
160
  "{{decisions}}",
161
+ ),
162
+ defaultTemplate: lines(
163
+ "{{intro}}",
164
+ "",
165
+ "Objective (verbatim, relay-owned):",
166
+ "{{objective}}",
173
167
  "",
174
168
  "Working state:",
175
169
  "{{workingState}}",
176
170
  "",
177
171
  "Archive refs:",
178
172
  "{{archiveRefs}}",
173
+ "",
174
+ "Ruled-out ledger (append-only):",
175
+ "{{ruledOut}}",
176
+ "",
177
+ "Decision log (append-only):",
178
+ "{{decisions}}",
179
179
  ),
180
180
  variables: ["intro", "generation", "objective", "ruledOut", "decisions", "workingState", "archiveRefs"],
181
181
  },
@@ -9,6 +9,7 @@ import {
9
9
  findMessageByIdempotencyKey,
10
10
  getAgent,
11
11
  getContinuationEnvelope,
12
+ sendMessage,
12
13
  updateContinuationAfterCompact,
13
14
  ValidationError,
14
15
  } from "./db";
@@ -31,6 +32,7 @@ interface CompactAndResumeResult {
31
32
  generation: number;
32
33
  commandId: string;
33
34
  continuationChars?: number;
35
+ continuationMessageId?: number;
34
36
  ledgerEntries: number;
35
37
  exhausted?: boolean;
36
38
  reason?: string;
@@ -91,10 +93,12 @@ export function compactAndResume(input: CompactAndResumeInput): CompactAndResume
91
93
  now,
92
94
  });
93
95
  if (updated.droppedLedgerEntries.length) archiveDroppedLedgerEntries(agent.id, generation, updated.droppedLedgerEntries, now);
94
- const continuation = renderContinuation(updated.envelope);
96
+ const fullContinuation = renderContinuation(updated.envelope);
97
+ const continuationMessage = createContinuationRecoveryMessage(agent.id, generation, fullContinuation, now);
98
+ const continuation = insertRecoveryPointerAfterIntro(fullContinuation, continuationMessage.id);
95
99
  const command = mode === "native"
96
- ? createNativeCompact(agent.id, generation, continuation)
97
- : createClearThenInject(agent.id, generation, continuation);
100
+ ? createNativeCompact(agent.id, generation, continuation, continuationMessage.id)
101
+ : createClearThenInject(agent.id, generation, continuation, continuationMessage.id);
98
102
  emitCommandEvent(command, "command.requested");
99
103
  return {
100
104
  ok: true,
@@ -102,10 +106,45 @@ export function compactAndResume(input: CompactAndResumeInput): CompactAndResume
102
106
  generation,
103
107
  commandId: command.id,
104
108
  continuationChars: continuation.length,
109
+ continuationMessageId: continuationMessage.id,
105
110
  ledgerEntries: updated.envelope.ledger.length,
106
111
  };
107
112
  }
108
113
 
114
+ function continuationRecoveryPointer(messageId: number): string {
115
+ return `Recovery pointer: use relay_get_message with message id ${messageId} to read your full prior context.`;
116
+ }
117
+
118
+ function insertRecoveryPointerAfterIntro(continuation: string, messageId: number): string {
119
+ const pointer = continuationRecoveryPointer(messageId);
120
+ const firstNewline = continuation.indexOf("\n");
121
+ if (firstNewline === -1) return `${continuation.trimEnd()}\n\n${pointer}`;
122
+ return `${continuation.slice(0, firstNewline).trimEnd()}\n${pointer}${continuation.slice(firstNewline)}`;
123
+ }
124
+
125
+ function createContinuationRecoveryMessage(agentId: string, generation: number, continuation: string, now: number) {
126
+ const idempotencyKey = `self-resume-continuation:${agentId}:${generation}`;
127
+ const existing = findMessageByIdempotencyKey("system", idempotencyKey);
128
+ if (existing) return existing;
129
+ return sendMessage({
130
+ from: "system",
131
+ to: agentId,
132
+ kind: "system",
133
+ subject: `Self-resume continuation generation ${generation}`,
134
+ body: continuation,
135
+ replyExpected: false,
136
+ idempotencyKey,
137
+ maxAgeSeconds: null,
138
+ occurredAt: now,
139
+ payload: {
140
+ kind: "agent.self_resume_continuation",
141
+ agentId,
142
+ generation,
143
+ fullEnvelope: true,
144
+ },
145
+ });
146
+ }
147
+
109
148
  // #1023 — the ruledOut ledger bound (see updateContinuationAfterCompact) drops the
110
149
  // oldest entries once the persisted ledger grows past its cap instead of letting a
111
150
  // long-running agent render a multi-megabyte continuation. Dropped entries are not
@@ -142,6 +181,7 @@ interface PendingInjection {
142
181
  continuation: string;
143
182
  mode: string;
144
183
  generation?: number;
184
+ fullContextMessageId?: number;
145
185
  }
146
186
 
147
187
  function extractPendingInjection(command: Command): PendingInjection | null {
@@ -158,7 +198,8 @@ function extractPendingInjection(command: Command): PendingInjection | null {
158
198
  : undefined;
159
199
  if (!continuation) return null;
160
200
  const generation = typeof resume?.generation === "number" ? resume.generation : undefined;
161
- return { continuation, mode, generation };
201
+ const fullContextMessageId = typeof resume?.fullContextMessageId === "number" ? resume.fullContextMessageId : undefined;
202
+ return { continuation, mode, generation, fullContextMessageId };
162
203
  }
163
204
 
164
205
  function buildInjectionParams(command: Command, injection: PendingInjection): Record<string, unknown> {
@@ -168,6 +209,7 @@ function buildInjectionParams(command: Command, injection: PendingInjection): Re
168
209
  selfResume: {
169
210
  generation: injection.generation,
170
211
  mode: injection.mode,
212
+ fullContextMessageId: injection.fullContextMessageId,
171
213
  ...(command.type === "agent.clearContext" ? { afterClearCommandId: command.id } : {}),
172
214
  ...(command.type === "agent.compact" ? { afterCompactCommandId: command.id } : {}),
173
215
  },
@@ -376,7 +418,7 @@ function hardLimit(limit: number, multiplier: number): number {
376
418
  return Math.ceil(limit * multiplier);
377
419
  }
378
420
 
379
- function createNativeCompact(agentId: string, generation: number, continuation: string): Command {
421
+ function createNativeCompact(agentId: string, generation: number, continuation: string, fullContextMessageId: number): Command {
380
422
  return createCommand({
381
423
  type: "agent.compact",
382
424
  source: "system",
@@ -384,19 +426,19 @@ function createNativeCompact(agentId: string, generation: number, continuation:
384
426
  params: {
385
427
  reason: "self-resume",
386
428
  instructions: continuation,
387
- selfResume: { generation, mode: "native", continuation },
429
+ selfResume: { generation, mode: "native", continuation, fullContextMessageId },
388
430
  },
389
431
  });
390
432
  }
391
433
 
392
- function createClearThenInject(agentId: string, generation: number, continuation: string): Command {
434
+ function createClearThenInject(agentId: string, generation: number, continuation: string, fullContextMessageId: number): Command {
393
435
  return createCommand({
394
436
  type: "agent.clearContext",
395
437
  source: "system",
396
438
  target: agentId,
397
439
  params: {
398
440
  reason: "self-resume",
399
- selfResume: { generation, mode: "clear-inject", continuation },
441
+ selfResume: { generation, mode: "clear-inject", continuation, fullContextMessageId },
400
442
  },
401
443
  });
402
444
  }
@@ -0,0 +1,59 @@
1
+ import { isRecord } from "agent-relay-sdk";
2
+ import { getCommand, updateCommand } from "../commands-db";
3
+ import { emitCommandEvent } from "../command-events";
4
+ import { noteCompactionCommandCompleted } from "../compaction-watch";
5
+ import { applyCommandToRecipe } from "../recipe-runner";
6
+ import { enqueueContinuationInjectionAfterSelfResume } from "../self-resume";
7
+ import { isComponentAuthorizedFor } from "../security";
8
+ import type { Command, ComponentToken } from "../types";
9
+ import { commandAuthorizationResource } from "./dispatch-command";
10
+ import { prepareScheduledCommandResultForStorage, settleScheduledCommandTimer } from "./scheduler-command";
11
+
12
+ type BusCommandUpdateResult = {
13
+ status: "succeeded" | "failed" | "rejected";
14
+ result?: Record<string, unknown>;
15
+ error?: string;
16
+ };
17
+
18
+ export async function updateCommandFromBus(input: {
19
+ target: string;
20
+ params: Record<string, unknown>;
21
+ componentAuth?: ComponentToken;
22
+ }): Promise<BusCommandUpdateResult> {
23
+ const command = getCommand(input.target);
24
+ if (!command) return { status: "failed", error: "command not found" };
25
+ if (!busCommandAuthorized(input.componentAuth, command)) {
26
+ return { status: "rejected", error: "component token lacks command scope" };
27
+ }
28
+ const status = typeof input.params.status === "string" ? input.params.status : undefined;
29
+ let result = isRecord(input.params.result) ? input.params.result : undefined;
30
+ if (result && command.type === "scheduler.command") {
31
+ result = await prepareScheduledCommandResultForStorage(command, result);
32
+ }
33
+ const updated = updateCommand(command.id, {
34
+ status: status as any,
35
+ result,
36
+ error: typeof input.params.error === "string" ? input.params.error : undefined,
37
+ });
38
+ if (updated) {
39
+ applyCommandToRecipe(updated);
40
+ if (updated.type === "scheduler.command") {
41
+ await settleScheduledCommandTimer(updated);
42
+ }
43
+ emitCommandEvent(updated, `command.${updated.status}`);
44
+ noteCompactionCommandCompleted(updated.type, updated.status, updated.id, updated.target);
45
+ enqueueContinuationInjectionAfterSelfResume(updated);
46
+ }
47
+ return { status: "succeeded", result: updated ? { command: updated } : undefined };
48
+ }
49
+
50
+ function busCommandAuthorized(
51
+ auth: ComponentToken | undefined,
52
+ command: Pick<Command, "target" | "params">,
53
+ ): boolean {
54
+ if (!auth) return true;
55
+ return isComponentAuthorizedFor(auth, {
56
+ scope: "command:write",
57
+ resource: commandAuthorizationResource(command),
58
+ });
59
+ }
@@ -6,6 +6,7 @@ import {
6
6
  ownedIsolatedWorkspace,
7
7
  recordGate,
8
8
  recordTaskDeliverable,
9
+ setTaskStage,
9
10
  setTaskStatus,
10
11
  ValidationError,
11
12
  } from "../db";
@@ -62,7 +63,17 @@ function resolveTaskId(ctx: AuthContext, explicit: unknown): number {
62
63
  throw new ValidationError("caller is bound to multiple Tasks; pass taskId explicitly");
63
64
  }
64
65
 
65
- function resolveWorkspace(ctx: AuthContext, input: { workspaceId?: string }, taskId: number): WorkspaceRecord | undefined {
66
+ function canRebindToWorkspace(ctx: AuthContext, task: TaskDetail, ws: WorkspaceRecord): boolean {
67
+ const caller = ctx.callerAgentId;
68
+ if (!caller || ws.ownerAgentId !== caller) return false;
69
+ return task.ownerAgentId === caller || agentBoundTaskIds(caller).has(task.id);
70
+ }
71
+
72
+ function shouldRebindToWorkspace(task: TaskDetail, ws: WorkspaceRecord | undefined): ws is WorkspaceRecord {
73
+ return Boolean(task.branch && ws?.branch && task.branch !== ws.branch);
74
+ }
75
+
76
+ function resolveWorkspace(ctx: AuthContext, input: { workspaceId?: string }, taskId: number, opts: { allowRebind?: boolean } = {}): WorkspaceRecord | undefined {
66
77
  const caller = ctx.callerAgentId;
67
78
  const explicit = input.workspaceId?.trim();
68
79
  const ws = explicit ? getWorkspace(explicit) : caller ? ownedIsolatedWorkspace(caller) : undefined;
@@ -73,11 +84,26 @@ function resolveWorkspace(ctx: AuthContext, input: { workspaceId?: string }, tas
73
84
  const task = getTaskDetail(taskId);
74
85
  if (!task) throw new ValidationError("task not found");
75
86
  if (task.branch && ws.branch && task.branch !== ws.branch) {
87
+ if (opts.allowRebind && canRebindToWorkspace(ctx, task, ws)) return ws;
76
88
  throw new ValidationError(`task ${taskId} is bound to branch ${task.branch}, not workspace branch ${ws.branch}`);
77
89
  }
78
90
  return ws;
79
91
  }
80
92
 
93
+ function rebindTaskToWorkspaceIfNeeded(task: TaskDetail, ws: WorkspaceRecord | undefined, actor: string): TaskDetail {
94
+ if (!shouldRebindToWorkspace(task, ws)) return task;
95
+ setTaskStatus(task.id, {
96
+ status: "in_progress",
97
+ note: `fix-forward rebind to workspace ${ws.id} (${ws.branch})`,
98
+ actor,
99
+ });
100
+ return setTaskStage(task.id, {
101
+ deliverableStage: "committed-on-branch",
102
+ branch: ws.branch,
103
+ actor,
104
+ });
105
+ }
106
+
81
107
  function gateWrites(gates: TaskDeliveryGateReports | undefined, deliveryKey: string): GateWrite[] {
82
108
  const writes: GateWrite[] = [];
83
109
  if (gates?.typecheck) writes.push({ gate: "typecheck", ...gatePayload(gates.typecheck, `${deliveryKey}:typecheck`) });
@@ -207,10 +233,11 @@ export async function deliverTask(input: DeliverTaskInput, ctx: AuthContext): Pr
207
233
  if (gateProblem) throw new ValidationError(`complete delivery cannot land without passing gate block (${gateProblem})`);
208
234
  const reason = input.disposition === "blocked" || input.disposition === "failed" ? cleanReason(input.reason, input.disposition) : undefined;
209
235
  const blockedBy = input.disposition === "blocked" ? cleanBlockedBy(input.blockedBy) : input.blockedBy;
210
- const workspace = wantsLand || input.workspaceId ? resolveWorkspace(ctx, input, taskId) : undefined;
236
+ const workspace = wantsLand || input.workspaceId ? resolveWorkspace(ctx, input, taskId, { allowRebind: wantsLand }) : undefined;
211
237
  if (wantsLand && (!workspace || workspace.mode !== "isolated" || !workspace.worktreePath)) {
212
238
  throw new ValidationError("complete delivery requires a live isolated workspace");
213
239
  }
240
+ const currentTask = wantsLand ? rebindTaskToWorkspaceIfNeeded(before, workspace, actor) : before;
214
241
  const landMeta: TaskDeliverableEventMetadata["land"] | undefined = wantsLand && workspace
215
242
  ? { requested: true, triggered: false, reason: "pending", workspaceId: workspace.id, workspaceStatus: workspace.status }
216
243
  : undefined;
@@ -236,7 +263,7 @@ export async function deliverTask(input: DeliverTaskInput, ctx: AuthContext): Pr
236
263
  } else if (isVerdictOnlyComplete) {
237
264
  setTaskStatus(taskId, { status: "done", note: "complete delivery recorded without land", actor });
238
265
  }
239
- const detail = getTaskDetail(taskId);
266
+ const detail = getTaskDetail(taskId) ?? currentTask;
240
267
  if (detail) {
241
268
  emitTaskChanged(detail, "task.deliverable");
242
269
  notifyDelivery(taskId, delivery.event.id, eventMeta ?? (delivery.event.metadata as unknown as TaskDeliverableEventMetadata));
@@ -1,2 +0,0 @@
1
- import{r as e}from"./chunk-CilyBKbf.js";import{B as t,Gn as n,H as r,Ht as i,Qt as a,Xn as o,_ as s,_t as c,a as l,et as u,g as d,gr as f,q as p,rr as m,rt as h,tr as g,u as _}from"./lucide-react-NXHPIMsX.js";import{p as ee,t as v}from"./store-D-Frk_EC.js";import{_ as te,h as y}from"./types-BqeSkWrv.js";import{J as b,K as x,W as S,Y as C,a as w,d as T,i as ne,n as re}from"./display-aTnwsqQh.js";import{o as E,r as D}from"./dist-BNB3fTO-.js";import{t as O}from"./dist-BD4Xc13v.js";import{i as ie,n as k}from"./dist-BFDfbfNC.js";import{t as A}from"./dist-DG75bvPp.js";import{t as j}from"./dist-CQMQ5iku.js";import{a as M,i as N,n as P,o as F,r as I,s as ae,t as oe}from"./es2015-a6fysEVz.js";import{a as L,i as se,n as R,r as z,t as B}from"./dist-C_VOBSaK.js";import{t as V}from"./badge-BCvp7LK3.js";import{t as H}from"./button-DyHjsHY3.js";import{t as ce}from"./copy-button-C67FUG3E.js";import{t as U}from"./agent-type-icon-BmBR-9CH.js";import{Dt as W,Et as le,J as ue,Ot as de,Y as fe,Z as pe,bt as me,ct as G,et as he,kt as ge,nt as _e,q as ve,rt as ye,tt as be}from"./index-BdNafiPz.js";import{r as xe,t as Se}from"./card-BoqYxpVW.js";import{i as Ce,n as we,r as Te,t as Ee}from"./branch-state-badge-Cscjqf-E.js";var K=e(f(),1),q=m(),J=`Popover`,[De,Oe]=ie(J,[L]),Y=L(),[ke,X]=De(J),Ae=e=>{let{__scopePopover:t,children:n,open:r,defaultOpen:i,onOpenChange:a,modal:o=!1}=e,s=Y(t),c=K.useRef(null),[l,u]=K.useState(!1),[d,f]=A({prop:r,defaultProp:i??!1,onChange:a,caller:J});return(0,q.jsx)(se,{...s,children:(0,q.jsx)(ke,{scope:t,contentId:ae(),triggerRef:c,open:d,onOpenChange:f,onOpenToggle:K.useCallback(()=>f(e=>!e),[f]),hasCustomAnchor:l,onCustomAnchorAdd:K.useCallback(()=>u(!0),[]),onCustomAnchorRemove:K.useCallback(()=>u(!1),[]),modal:o,children:n})})};Ae.displayName=J;var je=`PopoverAnchor`,Me=K.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=X(je,n),a=Y(n),{onCustomAnchorAdd:o,onCustomAnchorRemove:s}=i;return K.useEffect(()=>(o(),()=>s()),[o,s]),(0,q.jsx)(B,{...a,...r,ref:t})});Me.displayName=je;var Z=`PopoverTrigger`,Ne=K.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=X(Z,n),a=Y(n),o=E(t,i.triggerRef),s=(0,q.jsx)(O.button,{type:`button`,"aria-haspopup":`dialog`,"aria-expanded":i.open,"aria-controls":i.contentId,"data-state":Ke(i.open),...r,ref:o,onClick:k(e.onClick,i.onOpenToggle)});return i.hasCustomAnchor?s:(0,q.jsx)(B,{asChild:!0,...a,children:s})});Ne.displayName=Z;var Q=`PopoverPortal`,[Pe,Fe]=De(Q,{forceMount:void 0}),Ie=e=>{let{__scopePopover:t,forceMount:n,children:r,container:i}=e,a=X(Q,t);return(0,q.jsx)(Pe,{scope:t,forceMount:n,children:(0,q.jsx)(j,{present:n||a.open,children:(0,q.jsx)(N,{asChild:!0,container:i,children:r})})})};Ie.displayName=Q;var $=`PopoverContent`,Le=K.forwardRef((e,t)=>{let n=Fe($,e.__scopePopover),{forceMount:r=n.forceMount,...i}=e,a=X($,e.__scopePopover);return(0,q.jsx)(j,{present:r||a.open,children:a.modal?(0,q.jsx)(ze,{...i,ref:t}):(0,q.jsx)(Be,{...i,ref:t})})});Le.displayName=$;var Re=D(`PopoverContent.RemoveScroll`),ze=K.forwardRef((e,t)=>{let n=X($,e.__scopePopover),r=K.useRef(null),i=E(t,r),a=K.useRef(!1);return K.useEffect(()=>{let e=r.current;if(e)return oe(e)},[]),(0,q.jsx)(P,{as:Re,allowPinchZoom:!0,children:(0,q.jsx)(Ve,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:k(e.onCloseAutoFocus,e=>{e.preventDefault(),a.current||n.triggerRef.current?.focus()}),onPointerDownOutside:k(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;a.current=t.button===2||n},{checkForDefaultPrevented:!1}),onFocusOutside:k(e.onFocusOutside,e=>e.preventDefault(),{checkForDefaultPrevented:!1})})})}),Be=K.forwardRef((e,t)=>{let n=X($,e.__scopePopover),r=K.useRef(!1),i=K.useRef(!1);return(0,q.jsx)(Ve,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})}),Ve=K.forwardRef((e,t)=>{let{__scopePopover:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,disableOutsidePointerEvents:o,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:l,onInteractOutside:u,...d}=e,f=X($,n),p=Y(n);return I(),(0,q.jsx)(M,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,q.jsx)(F,{asChild:!0,disableOutsidePointerEvents:o,onInteractOutside:u,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:l,onDismiss:()=>f.onOpenChange(!1),children:(0,q.jsx)(z,{"data-state":Ke(f.open),role:`dialog`,id:f.contentId,...p,...d,ref:t,style:{...d.style,"--radix-popover-content-transform-origin":`var(--radix-popper-transform-origin)`,"--radix-popover-content-available-width":`var(--radix-popper-available-width)`,"--radix-popover-content-available-height":`var(--radix-popper-available-height)`,"--radix-popover-trigger-width":`var(--radix-popper-anchor-width)`,"--radix-popover-trigger-height":`var(--radix-popper-anchor-height)`}})})})}),He=`PopoverClose`,Ue=K.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=X(He,n);return(0,q.jsx)(O.button,{type:`button`,...r,ref:t,onClick:k(e.onClick,()=>i.onOpenChange(!1))})});Ue.displayName=He;var We=`PopoverArrow`,Ge=K.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=Y(n);return(0,q.jsx)(R,{...i,...r,ref:t})});Ge.displayName=We;function Ke(e){return e?`open`:`closed`}var qe=Ae,Je=Ne,Ye=Ie,Xe=Le;function Ze({...e}){return(0,q.jsx)(qe,{"data-slot":`popover`,...e})}function Qe({...e}){return(0,q.jsx)(Je,{"data-slot":`popover-trigger`,...e})}function $e({className:e,align:t=`center`,sideOffset:n=4,...r}){return(0,q.jsx)(Ye,{children:(0,q.jsx)(Xe,{"data-slot":`popover-content`,align:t,sideOffset:n,className:g(`z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95`,e),...r})})}function et({className:e,...t}){return(0,q.jsx)(`div`,{"data-slot":`popover-header`,className:g(`flex flex-col gap-0.5 text-sm`,e),...t})}function tt({className:e,...t}){return(0,q.jsx)(`div`,{"data-slot":`popover-title`,className:g(`font-medium`,e),...t})}function nt({agent:e,childAgents:n=[],parentAgent:r}){let o=ee();v(e=>e.set);let l=v(e=>e.switchView),f=v(e=>e.openInboxThread),m=v(e=>e.openAgentDetail),g=v(e=>e.openPairInvite),te=v(e=>e.openRename),y=v(e=>e.openConfirm),E=v(e=>e.doAgentAction),D=v(e=>e.doDeleteAgent),O=v(e=>e.openFilesForAgent),ie=v(e=>e.workspaceAction),k=v(e=>e.openWorkspaceFocus),{terminalOpen:A,terminalTarget:j,terminalOpening:M,openTerminal:N,closeTerminal:P}=Ce(e.id),F=le(),I=ne(o,e,me(e),F[e.id]||null),ae=w(e,`shutdown`),oe=w(e,`compact`),L=w(e,`clearContext`),se=!!(e.providerCapabilities?.terminal?.live?.read||e.providerCapabilities?.terminal?.attach?.create),R=!!e.providerCapabilities?.terminal?.live?.write,z=re(e),B=r?T(r):e.spawnedBy,W=n.length>0;return(0,q.jsxs)(q.Fragment,{children:[(0,q.jsx)(Se,{className:`group hover:border-zinc-600 transition-colors cursor-pointer`,onClick:()=>m(e),children:(0,q.jsxs)(xe,{className:`p-4`,children:[(0,q.jsxs)(`div`,{className:`flex items-start gap-2.5 mb-2.5`,children:[(0,q.jsx)(G,{agent:e,now:o,className:`mt-0.5`}),(0,q.jsx)(U,{agent:e}),(0,q.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,q.jsxs)(`div`,{className:`flex items-center gap-1 min-w-0`,children:[(0,q.jsx)(we,{profile:Te(e),className:`text-sm shrink-0`}),(0,q.jsx)(`span`,{className:`text-sm font-medium truncate`,children:T(e)})]}),(0,q.jsx)(`div`,{className:`text-xs mt-0.5 ${b(I.tone)}`,children:I.label}),typeof e.meta?.cwd==`string`&&e.meta.cwd&&(0,q.jsx)(`button`,{type:`button`,className:`mt-0.5 block max-w-full truncate font-mono text-[10px] text-muted-foreground hover:text-foreground`,title:`Open ${e.meta.cwd} in Files`,onClick:t=>{t.stopPropagation(),O(e)},children:S(e.meta.cwd)}),(0,q.jsx)(pe,{agent:e,className:`mt-1`})]}),e.branchState&&e.branchWorkspaceId&&(0,q.jsx)(Ee,{state:e.branchState,className:`mt-0.5 shrink-0`,onClick:t=>{t.stopPropagation(),k(e.branchWorkspaceId)}})]}),I.badges.length>0&&(0,q.jsx)(`div`,{className:`flex flex-wrap gap-1 mb-2`,children:I.badges.map(e=>(0,q.jsx)(`span`,{className:`text-[10px] px-1.5 py-0.5 rounded-full font-medium ${e.className}`,children:e.label},e.label))}),(W||e.spawnedBy&&B)&&(0,q.jsxs)(`div`,{className:`mb-2 flex flex-wrap gap-1.5`,onClick:e=>e.stopPropagation(),children:[W&&(0,q.jsxs)(Ze,{children:[(0,q.jsx)(Qe,{asChild:!0,children:(0,q.jsxs)(`button`,{type:`button`,className:`inline-flex h-6 max-w-full items-center gap-1.5 rounded-full border border-sky-500/30 bg-sky-500/10 px-2 text-[10px] font-medium text-sky-300 transition-colors hover:bg-sky-500/15`,title:`${n.length} live child ${n.length===1?`agent`:`agents`}`,children:[(0,q.jsx)(_,{className:`h-3 w-3`}),(0,q.jsx)(`span`,{children:n.length}),(0,q.jsx)(`span`,{className:`flex -space-x-1`,children:n.slice(0,4).map(e=>(0,q.jsx)(G,{agent:e,now:o,className:`h-2 w-2 ring-1 ring-background`},e.id))})]})}),(0,q.jsxs)($e,{align:`start`,className:`w-80`,onClick:e=>e.stopPropagation(),children:[(0,q.jsx)(et,{children:(0,q.jsx)(tt,{children:`Team`})}),(0,q.jsx)(`div`,{className:`space-y-1.5`,children:n.map(e=>(0,q.jsxs)(`div`,{className:`rounded-md border border-border bg-card/60 p-2`,children:[(0,q.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,q.jsx)(G,{agent:e,now:o,className:`mt-1`}),(0,q.jsx)(U,{agent:e}),(0,q.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,q.jsx)(`div`,{className:`truncate text-xs font-medium`,children:T(e)}),(0,q.jsx)(`div`,{className:`truncate text-[10px] text-muted-foreground`,children:e.providerCapabilities?.model?.alias||e.providerCapabilities?.model?.id||e.providerCapabilities?.model?.provider||e.status})]}),(0,q.jsx)(V,{variant:`outline`,className:`h-4 px-1.5 text-[9px]`,children:e.status})]}),(0,q.jsxs)(`div`,{className:`mt-2 flex gap-1`,children:[(0,q.jsx)(H,{size:`xs`,variant:`outline`,onClick:()=>m(e),children:`Open`}),(0,q.jsxs)(H,{size:`xs`,variant:`ghost`,onClick:()=>{f(e.id),l(`chat`)},children:[(0,q.jsx)(h,{className:`h-3 w-3`}),`Chat`]})]})]},e.id))})]})]}),e.spawnedBy&&B&&(0,q.jsx)(V,{variant:`outline`,className:`max-w-full text-[10px] text-muted-foreground`,children:(0,q.jsxs)(`span`,{className:`truncate`,children:[`spawned by `,B]})})]}),(e.tags.length>0||C(e.capabilities).length>0)&&(0,q.jsxs)(`div`,{className:`flex flex-wrap gap-1 mb-2`,children:[e.tags.slice(0,3).map(e=>(0,q.jsx)(V,{variant:`outline`,className:`text-[9px] px-1 py-0 h-4`,children:e},e)),C(e.capabilities).slice(0,2).map(e=>(0,q.jsx)(V,{variant:`secondary`,className:`text-[9px] px-1 py-0 h-4`,children:e},e))]}),(0,q.jsxs)(`div`,{className:`space-y-2 mb-2`,children:[(0,q.jsx)(_e,{agent:e}),(0,q.jsx)(be,{agent:e,compact:!0}),(0,q.jsx)(ye,{agent:e})]}),(0,q.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:e.id!==`user`&&e.id!==`system`&&x(o,e.lastSeen)}),(0,q.jsxs)(`div`,{className:`flex gap-1 mt-2.5 opacity-100 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity`,onClick:e=>e.stopPropagation(),children:[(0,q.jsx)(ce,{value:e.id,label:`Copy agent ID`,size:`icon`,className:`h-7 w-7`,iconClassName:`w-3 h-3`}),(0,q.jsx)(H,{size:`icon`,variant:`ghost`,className:`h-7 w-7`,title:`Chat`,onClick:()=>{f(e.id),l(`chat`)},children:(0,q.jsx)(h,{className:`w-3 h-3`})}),(0,q.jsx)(H,{size:`icon`,variant:`ghost`,className:`h-7 w-7`,title:`Pair`,onClick:()=>g(e.id),children:(0,q.jsx)(c,{className:`w-3 h-3`})}),(0,q.jsx)(H,{size:`icon`,variant:`ghost`,className:`h-7 w-7`,title:`Rename`,onClick:()=>te(e),children:(0,q.jsx)(p,{className:`w-3 h-3`})}),se&&(0,q.jsx)(H,{size:`icon`,variant:`ghost`,className:`h-7 w-7`,title:`Terminal`,disabled:M,onClick:()=>void N(),children:(0,q.jsx)(s,{className:`w-3 h-3`})}),(e.branchState===`changes`||e.branchState===`idle`)&&e.branchWorkspaceId&&(0,q.jsx)(H,{size:`icon`,variant:`ghost`,className:`h-7 w-7 text-amber-400 hover:text-amber-300`,title:`Mark workspace ready — hand off to the auto-merge`,onClick:()=>y(`Mark Workspace Ready`,`Mark ${T(e)}'s branch ready to land? The relay auto-merge will rebase and land it.`,()=>ie(e.branchWorkspaceId,`ready`)),children:(0,q.jsx)(i,{className:`w-3 h-3`})}),oe&&(0,q.jsx)(H,{size:`icon`,variant:`ghost`,className:`h-7 w-7`,title:`Compact`,onClick:()=>y(`Compact Agent`,`Compact context for ${T(e)}?`,()=>E(e,`compact`,`agents-grid`)),children:(0,q.jsx)(u,{className:`w-3 h-3`})}),L&&(0,q.jsx)(H,{size:`icon`,variant:`ghost`,className:`h-7 w-7`,title:`Clear Context`,onClick:()=>y(`Clear Context`,`Clear context for ${T(e)}?`,()=>E(e,`clearContext`,`agents-grid`)),children:(0,q.jsx)(a,{className:`w-3 h-3`})}),ae&&(0,q.jsx)(H,{size:`icon`,variant:`ghost`,className:`h-7 w-7 text-red-400 hover:text-red-300`,title:`Shutdown`,onClick:()=>y(`Shutdown Agent`,`Shutdown ${T(e)}?`,()=>E(e,`shutdown`,`agents-grid`)),children:(0,q.jsx)(t,{className:`w-3 h-3`})}),z&&(0,q.jsx)(H,{size:`icon`,variant:`ghost`,className:`h-7 w-7 text-red-400 hover:text-red-300`,title:`Forget stale agent`,onClick:()=>y(`Forget Agent`,`Forget ${T(e)}?`,()=>D(e.id)),children:(0,q.jsx)(d,{className:`w-3 h-3`})})]})]})}),j&&(0,q.jsx)(he,{open:A,onOpenChange:P,orchestratorId:j.orchestratorId,session:j.session,interactive:j.interactive||R,canWrite:R})]})}function rt(){let e=v(e=>e.agentPresetFilter),t=v(e=>e.agentStatusFilter),i=v(e=>e.agentTagFilter),a=v(e=>e.agentHostFilter),s=v(e=>e.agentSort),c=v(e=>e.agentSortDir),u=v(e=>e.showOffline),d=v(e=>e.set),f=v(e=>e.openAgentSpawn),p=v(e=>e.agentsById),m=ee(),h=W(),g=ge(),_=de(),b=ve(h,m),x=fe(h,m),S=e||t||i||a;function C(){d({agentPresetFilter:``,agentStatusFilter:``,agentTagFilter:``,agentHostFilter:``})}function w(){d({agentSortDir:c===`asc`?`desc`:`asc`})}return(0,q.jsxs)(`div`,{className:`space-y-4`,children:[(0,q.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,q.jsxs)(`select`,{value:e,onChange:e=>d({agentPresetFilter:e.target.value}),className:`h-8 rounded-md border border-border bg-card text-sm px-2 text-foreground`,children:[(0,q.jsx)(`option`,{value:``,children:`All agents`}),(0,q.jsx)(`option`,{value:`active`,children:`Active`}),y().map(e=>(0,q.jsx)(`option`,{value:e,children:te(e)},e)),(0,q.jsx)(`option`,{value:`paired`,children:`Paired`}),(0,q.jsx)(`option`,{value:`unpaired`,children:`Unpaired`}),(0,q.jsx)(`option`,{value:`offline_stale`,children:`Offline / Stale`})]}),(0,q.jsxs)(`select`,{value:t,onChange:e=>d({agentStatusFilter:e.target.value}),className:`h-8 rounded-md border border-border bg-card text-sm px-2 text-foreground`,children:[(0,q.jsx)(`option`,{value:``,children:`Any status`}),(0,q.jsx)(`option`,{value:`online`,children:`Online`}),(0,q.jsx)(`option`,{value:`idle`,children:`Idle`}),(0,q.jsx)(`option`,{value:`busy`,children:`Busy`}),(0,q.jsx)(`option`,{value:`offline`,children:`Offline`}),(0,q.jsx)(`option`,{value:`starting`,children:`Starting`})]}),g.length>0&&(0,q.jsxs)(`select`,{value:i,onChange:e=>d({agentTagFilter:e.target.value}),className:`h-8 rounded-md border border-border bg-card text-sm px-2 text-foreground`,children:[(0,q.jsx)(`option`,{value:``,children:`Any tag`}),g.map(e=>(0,q.jsx)(`option`,{value:e,children:e},e))]}),_.length>1&&(0,q.jsxs)(`select`,{value:a,onChange:e=>d({agentHostFilter:e.target.value}),className:`h-8 rounded-md border border-border bg-card text-sm px-2 text-foreground`,children:[(0,q.jsx)(`option`,{value:``,children:`Any host`}),_.map(e=>(0,q.jsx)(`option`,{value:e,children:e},e))]}),(0,q.jsxs)(`select`,{value:s,onChange:e=>d({agentSort:e.target.value}),className:`h-8 rounded-md border border-border bg-card text-sm px-2 text-foreground`,children:[(0,q.jsx)(`option`,{value:`status`,children:`Sort: Status`}),(0,q.jsx)(`option`,{value:`name`,children:`Sort: Name`}),(0,q.jsx)(`option`,{value:`lastSeen`,children:`Sort: Last seen`}),(0,q.jsx)(`option`,{value:`created`,children:`Sort: Created`})]}),(0,q.jsx)(H,{size:`icon`,variant:`outline`,className:`h-8 w-8`,title:`Toggle sort direction`,onClick:w,children:c===`asc`?(0,q.jsx)(n,{className:`w-3.5 h-3.5`}):(0,q.jsx)(o,{className:`w-3.5 h-3.5`})}),(0,q.jsxs)(`label`,{className:`flex items-center gap-1.5 text-sm text-muted-foreground cursor-pointer select-none`,children:[(0,q.jsx)(`input`,{type:`checkbox`,checked:u,onChange:e=>d({showOffline:e.target.checked}),className:`rounded`}),`Show offline`]}),S&&(0,q.jsxs)(H,{size:`sm`,variant:`ghost`,className:`h-8 text-muted-foreground hover:text-foreground gap-1`,onClick:C,children:[(0,q.jsx)(l,{className:`w-3 h-3`}),`Clear filters`]}),(0,q.jsx)(`div`,{className:`ml-auto`,children:(0,q.jsxs)(H,{size:`sm`,onClick:f,className:`gap-1`,children:[(0,q.jsx)(r,{className:`w-3.5 h-3.5`}),`Spawn`]})})]}),x.length===0?(0,q.jsx)(`div`,{className:`text-center py-16 text-muted-foreground text-sm`,children:`No agents match the current filters.`}):(0,q.jsx)(`div`,{className:`grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-3`,children:x.map(e=>(0,q.jsx)(nt,{agent:e,parentAgent:e.spawnedBy?p[e.spawnedBy]??null:null,childAgents:ue(b.get(e.id)??[])},e.id))})]})}export{rt as AgentsView};
2
- //# sourceMappingURL=agents-Dt5TncjC.js.map