@schlenkr/ragents 0.1.18 → 0.1.20

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 (209) hide show
  1. package/apps/server/src/host-api.json +6 -2
  2. package/apps/server/src/host-api.ts +3 -2
  3. package/apps/server/src/host-web.ts +3 -0
  4. package/apps/server/src/plugin-support/actor-programs/contract.ts +0 -3
  5. package/apps/server/src/plugin-support/removed-layout.ts +94 -0
  6. package/apps/server/src/ragents/engine.ts +2 -1
  7. package/apps/server/src/title-compactor.ts +5 -7
  8. package/apps/web/dist/assets/highlighted-body-KPVGNVTW-k67VKdbw.js +1 -0
  9. package/apps/web/dist/assets/main-CVjIz7BS.js +674 -0
  10. package/apps/web/dist/help/guide-clients.html +22 -42
  11. package/apps/web/dist/help/guide-clients.md +24 -45
  12. package/apps/web/dist/help/guide-functions.html +1 -1
  13. package/apps/web/dist/help/guide-functions.md +1 -1
  14. package/apps/web/dist/help/guide-getting-started.html +14 -22
  15. package/apps/web/dist/help/guide-getting-started.md +17 -24
  16. package/apps/web/dist/help/guide-ideas.html +1 -1
  17. package/apps/web/dist/help/guide-ideas.md +1 -1
  18. package/apps/web/dist/help/guide-plugins.html +1 -1
  19. package/apps/web/dist/help/guide-plugins.md +1 -1
  20. package/apps/web/dist/help/guide-programs.html +1 -1
  21. package/apps/web/dist/help/guide-programs.md +1 -1
  22. package/apps/web/dist/help/index.html +12 -79
  23. package/apps/web/dist/help/mini-app.css +8 -502
  24. package/apps/web/dist/host-web.json +28 -23
  25. package/apps/web/dist/index.html +2 -3
  26. package/apps/web/dist/run-panel.html +3 -4
  27. package/apps/web/src/Overview.tsx +4 -143
  28. package/apps/web/src/PluginChat.tsx +22 -116
  29. package/apps/web/src/PluginRegistry.tsx +4 -9
  30. package/apps/web/src/RunPreparationChat.tsx +4 -1
  31. package/apps/web/src/host-modules.ts +2 -0
  32. package/apps/web/src/main.tsx +23 -3
  33. package/apps/web/src/panel/RunLine.tsx +5 -2
  34. package/apps/web/src/panel/RunsPage.tsx +6 -6
  35. package/apps/web/src/panel/StartPage.tsx +6 -5
  36. package/apps/web/src/panel/contract.ts +3 -0
  37. package/apps/web/src/panel/page-props.ts +2 -0
  38. package/apps/web/src/run-apps.ts +34 -0
  39. package/apps/web/src/run-overview.ts +0 -27
  40. package/apps/web/src/run-panel/PanelContributions.tsx +41 -0
  41. package/apps/web/src/run-panel/RunPanelApp.tsx +172 -91
  42. package/apps/web/src/run-panel/RunPanelMenu.tsx +4 -3
  43. package/apps/web/src/run-panel/RunPanelWorkspace.tsx +8 -2
  44. package/apps/web/src/run-panel/clipboard.ts +17 -14
  45. package/apps/web/src/run-panel/host-contract.ts +8 -9
  46. package/apps/web/src/run-panel/host.ts +5 -31
  47. package/apps/web/src/run-panel/input-bridge.ts +15 -7
  48. package/bundles/ragents.activity/ragents-bundle.json +2 -2
  49. package/bundles/ragents.actor-programs/prompt.hbs +4 -5
  50. package/bundles/ragents.actor-programs/ragents-bundle.json +4 -14
  51. package/bundles/ragents.actor-programs/server/index.js +2 -14
  52. package/bundles/ragents.actor-programs/server/index.js.map +2 -2
  53. package/bundles/ragents.actor-programs/web/classes.json +1 -1
  54. package/bundles/ragents.actor-programs/web/index.js +1 -3
  55. package/bundles/ragents.actor-programs/web/index.js.map +4 -4
  56. package/bundles/ragents.ask/ragents-bundle.json +2 -2
  57. package/bundles/ragents.browser/ragents-bundle.json +2 -2
  58. package/bundles/ragents.documents/ragents-bundle.json +2 -2
  59. package/bundles/ragents.lsp-fsharp/ragents-bundle.json +2 -2
  60. package/bundles/ragents.lsp-roslyn/ragents-bundle.json +2 -2
  61. package/bundles/ragents.lsp-typescript/ragents-bundle.json +2 -2
  62. package/bundles/ragents.model-relay/ragents-bundle.json +2 -2
  63. package/bundles/ragents.orchestration/ragents-bundle.json +15 -41
  64. package/bundles/ragents.orchestration/server/index.js +10 -249
  65. package/bundles/ragents.orchestration/server/index.js.map +4 -4
  66. package/bundles/ragents.orchestration/web/chunks/chunk-KU7O6MEX.js +2 -0
  67. package/bundles/ragents.orchestration/web/chunks/chunk-KU7O6MEX.js.map +7 -0
  68. package/bundles/ragents.orchestration/web/classes.json +1 -1
  69. package/bundles/ragents.orchestration/web/exports/web/program-slot.js +1 -1
  70. package/bundles/ragents.orchestration/web/index.js +10 -18
  71. package/bundles/ragents.orchestration/web/index.js.map +4 -4
  72. package/bundles/ragents.overseer/ragents-bundle.json +3 -3
  73. package/bundles/ragents.overseer/server/index.js +0 -1
  74. package/bundles/ragents.overseer/server/index.js.map +2 -2
  75. package/bundles/ragents.processes/ragents-bundle.json +2 -2
  76. package/bundles/ragents.product/ragents-bundle.json +2 -2
  77. package/bundles/ragents.profile-distribution/ragents-bundle.json +2 -2
  78. package/bundles/ragents.reference/ragents-bundle.json +3 -3
  79. package/bundles/ragents.reference/run-scripts/balcony-wizard/RUN.md +1 -1
  80. package/bundles/ragents.reference/run-scripts/balcony-wizard/src/server.ts +1 -2
  81. package/bundles/ragents.reference/run-scripts/balcony-wizard/tests/program.test.ts +6 -7
  82. package/bundles/ragents.reference/run-scripts/conversation-circle/RUN.md +3 -3
  83. package/bundles/ragents.reference/run-scripts/conversation-circle/src/server.ts +3 -13
  84. package/bundles/ragents.reference/run-scripts/conversation-circle/tests/helpers.ts +1 -1
  85. package/bundles/ragents.reference/run-scripts/conversation-circle/tests/program.test.ts +1 -6
  86. package/bundles/ragents.reference/run-scripts/learning-afternoon/src/server.ts +1 -15
  87. package/bundles/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
  88. package/bundles/ragents.reference/run-scripts/moderated-round/tests/helpers.ts +1 -1
  89. package/bundles/ragents.reference/run-scripts/quick-note/RUN.md +1 -1
  90. package/bundles/ragents.reference/run-scripts/quick-note/src/server.ts +1 -2
  91. package/bundles/ragents.reference/run-scripts/quick-note/tests/program.test.ts +0 -2
  92. package/bundles/ragents.reference/run-scripts/run-roster/RUN.md +2 -2
  93. package/bundles/ragents.reference/run-scripts/run-roster/src/server.ts +1 -2
  94. package/bundles/ragents.reference/run-scripts/run-roster/tests/program.test.ts +0 -2
  95. package/bundles/ragents.reference/run-scripts/shared-actor-list/src/server.ts +1 -1
  96. package/bundles/ragents.reference/run-scripts/shared-actor-list/tests/helpers.ts +1 -1
  97. package/bundles/ragents.reference/run-scripts/word-game/src/contract.ts +1 -1
  98. package/bundles/ragents.reference/run-scripts/word-game/src/server.ts +0 -1
  99. package/bundles/ragents.reference/run-scripts/word-game/tests/program.test.ts +1 -2
  100. package/bundles/ragents.reference/skills/80-actor-text-analysis/SKILL.md +1 -1
  101. package/bundles/ragents.reference/skills/90-actor-notes/SKILL.md +1 -1
  102. package/bundles/ragents.todo/ragents-bundle.json +2 -2
  103. package/bundles/ragents.transcript/ragents-bundle.json +2 -2
  104. package/bundles/ragents.watch/ragents-bundle.json +2 -2
  105. package/bundles/ragents.workspace/ragents-bundle.json +2 -2
  106. package/package.json +8 -8
  107. package/packages/agent/dist/core/agent-settings.d.ts +1 -1
  108. package/packages/agent/src/core/agent-settings.ts +2 -2
  109. package/packages/ai/dist/types.d.ts +2 -1
  110. package/packages/ai/src/api/ai-sdk.ts +15 -2
  111. package/packages/ai/src/types.ts +2 -1
  112. package/packages/ragents/src/runtime/journal.ts +4 -0
  113. package/plugins/ragents.actor-programs/prompt.hbs +4 -5
  114. package/plugins/ragents.actor-programs/server/access-projections.ts +1 -1
  115. package/plugins/ragents.actor-programs/server/index.ts +0 -2
  116. package/plugins/ragents.actor-programs/server/runtime.ts +2 -10
  117. package/plugins/ragents.actor-programs/web/ActorViewFrame.tsx +5 -20
  118. package/plugins/ragents.actor-programs/web/AppsPanel.tsx +3 -79
  119. package/plugins/ragents.actor-programs/web/EmbeddedApps.tsx +0 -9
  120. package/plugins/ragents.actor-programs/web/api.ts +0 -20
  121. package/plugins/ragents.actor-programs/web/context.ts +0 -3
  122. package/plugins/ragents.actor-programs/web/index.tsx +0 -9
  123. package/plugins/ragents.orchestration/ragents-plugin.json +1 -2
  124. package/plugins/ragents.orchestration/server/index.ts +1 -11
  125. package/plugins/ragents.orchestration/web/ActorChat.tsx +1 -1
  126. package/plugins/ragents.orchestration/web/ActorChatControls.tsx +1 -1
  127. package/plugins/ragents.orchestration/web/FlowInspector.tsx +2 -4
  128. package/plugins/ragents.orchestration/web/InspectionPanel.tsx +21 -0
  129. package/plugins/ragents.orchestration/web/index.tsx +13 -9
  130. package/plugins/ragents.orchestration/web/program-slot.ts +0 -3
  131. package/plugins/ragents.orchestration/web/run-panel/AddresseeControl.tsx +15 -36
  132. package/plugins/ragents.orchestration/web/run-panel/RunPanel.tsx +76 -451
  133. package/plugins/ragents.orchestration/web/run-panel/run-panel-actors.ts +0 -27
  134. package/plugins/ragents.orchestration/web/run-panel/run-panel-state.ts +7 -40
  135. package/plugins/ragents.overseer/server/coordinator-prompt.ts +0 -1
  136. package/plugins/ragents.reference/examples.ts +1 -2
  137. package/plugins/ragents.reference/run-scripts/balcony-wizard/RUN.md +1 -1
  138. package/plugins/ragents.reference/run-scripts/balcony-wizard/src/server.ts +1 -2
  139. package/plugins/ragents.reference/run-scripts/balcony-wizard/tests/program.test.ts +6 -7
  140. package/plugins/ragents.reference/run-scripts/conversation-circle/RUN.md +3 -3
  141. package/plugins/ragents.reference/run-scripts/conversation-circle/src/server.ts +3 -13
  142. package/plugins/ragents.reference/run-scripts/conversation-circle/tests/helpers.ts +1 -1
  143. package/plugins/ragents.reference/run-scripts/conversation-circle/tests/program.test.ts +1 -6
  144. package/plugins/ragents.reference/run-scripts/learning-afternoon/src/server.ts +1 -15
  145. package/plugins/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
  146. package/plugins/ragents.reference/run-scripts/moderated-round/tests/helpers.ts +1 -1
  147. package/plugins/ragents.reference/run-scripts/quick-note/RUN.md +1 -1
  148. package/plugins/ragents.reference/run-scripts/quick-note/src/server.ts +1 -2
  149. package/plugins/ragents.reference/run-scripts/quick-note/tests/program.test.ts +0 -2
  150. package/plugins/ragents.reference/run-scripts/run-roster/RUN.md +2 -2
  151. package/plugins/ragents.reference/run-scripts/run-roster/src/server.ts +1 -2
  152. package/plugins/ragents.reference/run-scripts/run-roster/tests/program.test.ts +0 -2
  153. package/plugins/ragents.reference/run-scripts/shared-actor-list/src/server.ts +1 -1
  154. package/plugins/ragents.reference/run-scripts/shared-actor-list/tests/helpers.ts +1 -1
  155. package/plugins/ragents.reference/run-scripts/word-game/src/contract.ts +1 -1
  156. package/plugins/ragents.reference/run-scripts/word-game/src/server.ts +0 -1
  157. package/plugins/ragents.reference/run-scripts/word-game/tests/program.test.ts +1 -2
  158. package/plugins/ragents.reference/skills/80-actor-text-analysis/SKILL.md +1 -1
  159. package/plugins/ragents.reference/skills/90-actor-notes/SKILL.md +1 -1
  160. package/plugins/ragents.reference/walkthroughs.ts +13 -14
  161. package/apps/web/dist/assets/highlighted-body-KPVGNVTW-BwD4uYEe.js +0 -1
  162. package/apps/web/dist/assets/main-KBrc5Q77.js +0 -11
  163. package/apps/web/dist/assets/mermaid-HWGCJPDP-CSCkaArr.js +0 -608
  164. package/apps/web/dist/assets/runPanel-BjG5NBKm.js +0 -26
  165. package/apps/web/src/App.tsx +0 -318
  166. package/apps/web/src/SessionList.tsx +0 -157
  167. package/apps/web/src/SurfaceShortcuts.tsx +0 -60
  168. package/apps/web/src/WorkspacePanel.tsx +0 -309
  169. package/apps/web/src/run-panel.tsx +0 -52
  170. package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js +0 -11
  171. package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js.map +0 -7
  172. package/bundles/ragents.orchestration/server/exports/server/contract.js +0 -9
  173. package/bundles/ragents.orchestration/server/exports/server/contract.js.map +0 -7
  174. package/bundles/ragents.orchestration/surface.hbs +0 -39
  175. package/bundles/ragents.orchestration/web/chunks/chunk-CFMJD5ZI.js +0 -2
  176. package/bundles/ragents.orchestration/web/chunks/chunk-CFMJD5ZI.js.map +0 -7
  177. package/bundles/ragents.orchestration/web/chunks/chunk-HOBLI364.js +0 -2
  178. package/bundles/ragents.orchestration/web/chunks/chunk-HOBLI364.js.map +0 -7
  179. package/bundles/ragents.orchestration/web/chunks/chunk-UCIMT2R4.js +0 -2
  180. package/bundles/ragents.orchestration/web/chunks/chunk-UCIMT2R4.js.map +0 -7
  181. package/bundles/ragents.orchestration/web/chunks/chunk-XGPPQCZR.js +0 -2
  182. package/bundles/ragents.orchestration/web/chunks/chunk-XGPPQCZR.js.map +0 -7
  183. package/bundles/ragents.orchestration/web/exports/web/surface-entities.js +0 -2
  184. package/bundles/ragents.orchestration/web/exports/web/surface-entities.js.map +0 -7
  185. package/bundles/ragents.orchestration/web/exports/web/tile-docking.js +0 -2
  186. package/bundles/ragents.orchestration/web/exports/web/tile-docking.js.map +0 -7
  187. package/plugins/ragents.actor-programs/web/ExpandIcon.tsx +0 -5
  188. package/plugins/ragents.actor-programs/web/FullscreenHost.tsx +0 -31
  189. package/plugins/ragents.orchestration/contract.ts +0 -51
  190. package/plugins/ragents.orchestration/server/contract.ts +0 -14
  191. package/plugins/ragents.orchestration/server/surface-tool.ts +0 -198
  192. package/plugins/ragents.orchestration/surface.hbs +0 -39
  193. package/plugins/ragents.orchestration/tiled-layout.ts +0 -55
  194. package/plugins/ragents.orchestration/web/ActorChatPreview.tsx +0 -29
  195. package/plugins/ragents.orchestration/web/ActorShortcut.tsx +0 -74
  196. package/plugins/ragents.orchestration/web/ActorSurfaceControls.tsx +0 -130
  197. package/plugins/ragents.orchestration/web/ActorTile.tsx +0 -23
  198. package/plugins/ragents.orchestration/web/Center.tsx +0 -133
  199. package/plugins/ragents.orchestration/web/SurfaceTiles.tsx +0 -47
  200. package/plugins/ragents.orchestration/web/TiledSurface.tsx +0 -280
  201. package/plugins/ragents.orchestration/web/actor-header-settings.ts +0 -41
  202. package/plugins/ragents.orchestration/web/run-panel/RunPanelSettings.tsx +0 -50
  203. package/plugins/ragents.orchestration/web/run-panel/run-panel-settings.ts +0 -47
  204. package/plugins/ragents.orchestration/web/run-panel/sheet-status.ts +0 -30
  205. package/plugins/ragents.orchestration/web/surface-entities.ts +0 -21
  206. package/plugins/ragents.orchestration/web/surface-view-settings.ts +0 -42
  207. package/plugins/ragents.orchestration/web/tile-docking.ts +0 -105
  208. package/plugins/ragents.orchestration/web/tiled-view-settings.ts +0 -74
  209. /package/apps/web/dist/assets/{mermaid-HWGCJPDP-BPn4aV2r.css → main-BPn4aV2r.css} +0 -0
@@ -1,6 +1,5 @@
1
1
  import { hostInputEnabled, isRunPanelKeyboardMessage, relayFrameInput } from "@ragents/web/run-panel/input-bridge";
2
2
  import { useAccess } from "@ragents/web/AccessContext";
3
- import { cn } from "@ragents/web/ui";
4
3
  import { useResolvedTheme } from "@ragents/web/theme";
5
4
  import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
6
5
  import type { RunApp, RunAppInvocation, ActorProgramsApi } from "./api";
@@ -41,7 +40,6 @@ interface ActorViewFrameProps {
41
40
  input: JsonValue,
42
41
  ) => Promise<RunAppInvocation>;
43
42
  pendingConfirmationInvocationId?: string;
44
- presentation: "embedded" | "fullscreen" | "tiled";
45
43
  runId: string;
46
44
  session: SessionContext;
47
45
  }
@@ -51,19 +49,11 @@ const REQUEST_ID_HISTORY_LIMIT = 512;
51
49
 
52
50
  const invocationSignature = (invocation: RunAppInvocation): string => JSON.stringify(invocation);
53
51
 
54
- const markTones: Record<string, string> = {
55
- success: "bg-success",
56
- running: "bg-primary shadow-[0_0_0_4px_color-mix(in_srgb,var(--primary)_12%,transparent)] animate-fade-pulse [animation-duration:1.2s] motion-reduce:animate-none",
57
- confirm: "bg-warning",
58
- error: "bg-destructive",
59
- };
60
-
61
52
  export function ActorViewFrame({
62
53
  api,
63
54
  app,
64
55
  invoke,
65
56
  pendingConfirmationInvocationId,
66
- presentation,
67
57
  runId,
68
58
  session,
69
59
  }: ActorViewFrameProps) {
@@ -435,27 +425,22 @@ export function ActorViewFrame({
435
425
  ? undefined
436
426
  : { tone: "muted", title: "Loading app", detail: "Bridge not connected yet" };
437
427
 
438
- const visibleStatus = status && (presentation === "fullscreen" ? status.tone === "error" : status.tone !== "success") ? status : undefined;
428
+ const visibleStatus = status && status.tone !== "success" ? status : undefined;
439
429
 
440
- const compact = presentation !== "fullscreen";
441
430
 
442
431
  return (
443
432
  <div className="flex min-h-0 flex-1 flex-col">
444
433
  <div aria-live="polite" role="status"
445
- className={cn("grid min-w-0 items-center overflow-hidden text-xs text-muted-foreground",
446
- compact
447
- ? "order-1 h-7 flex-[0_0_28px] grid-cols-[auto_minmax(0,1fr)] gap-1.5 px-workspace-inset py-1"
448
- : "h-[31px] flex-[0_0_31px] grid-cols-[8px_auto_minmax(0,1fr)] gap-[7px] border-b border-border-soft px-workspace-inset py-[5px]")}>
434
+ className="order-1 grid h-7 min-w-0 flex-[0_0_28px] grid-cols-[auto_minmax(0,1fr)] items-center gap-1.5 overflow-hidden px-workspace-inset py-1 text-xs text-muted-foreground">
449
435
  {visibleStatus && <>
450
- {!compact && <span className={cn("size-[7px] rounded-full bg-muted-foreground/55", markTones[visibleStatus.tone])} />}
451
- <strong className={cn("font-semibold whitespace-nowrap text-foreground", compact ? "text-[0.63rem]" : "text-[0.7rem]")}>{visibleStatus.title}</strong>
436
+ <strong className="text-[0.63rem] font-semibold whitespace-nowrap text-foreground">{visibleStatus.title}</strong>
452
437
  <span className="truncate" title={visibleStatus.detail}>{visibleStatus.detail}</span>
453
438
  </>}
454
439
  </div>
455
- <div className={cn("relative flex min-h-0 flex-1 overflow-hidden", compact ? "bg-transparent" : "bg-background")}>
440
+ <div className="relative flex min-h-0 flex-1 overflow-hidden bg-transparent">
456
441
  <iframe
457
442
  allow="camera 'none'; geolocation 'none'; microphone 'none'"
458
- className={cn("absolute inset-0 h-full w-full min-h-0 min-w-0 border-0", compact ? "bg-transparent" : "bg-background")}
443
+ className="absolute inset-0 h-full w-full min-h-0 min-w-0 border-0 bg-transparent"
459
444
  ref={iframeRef}
460
445
  referrerPolicy="no-referrer"
461
446
  sandbox="allow-scripts allow-forms allow-downloads"
@@ -1,7 +1,6 @@
1
1
  import { useAccess } from "@ragents/web/AccessContext";
2
- import { ArrowLeftIcon, LayoutGridIcon } from "lucide-react";
2
+ import { ArrowLeftIcon } from "lucide-react";
3
3
  import { Alert, Badge, Button, cn, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, Spinner } from "@ragents/web/ui";
4
- import { ToolbarCopy, ToolbarItem, ToolbarLabel, ToolbarText } from "@ragents/web/Toolbar";
5
4
  import {
6
5
  useCallback,
7
6
  useEffect,
@@ -15,14 +14,10 @@ import { answerQuestion } from "@ragents/plugins/ragents.ask/web/api";
15
14
  import { QuestionCard } from "@ragents/plugins/ragents.ask/web/QuestionCard";
16
15
  import { ACTOR_PROGRAMS_STATE_ID } from "@ragents/host/plugin-support/actor-programs/contract";
17
16
  import {
18
- useSurfaceController,
19
17
  type SessionContext,
20
- type SessionHeaderContext,
21
18
  type WorkspaceTabContext,
22
19
  } from "@ragents/web/PluginRegistry";
23
20
  import { runViewFrom, type RunAction } from "@ragents/web/run-view";
24
- import { useSurfaceEntities } from "@ragents/plugins/ragents.orchestration/web/surface-entities";
25
- import { SURFACE_TILE_DRAG_TYPE } from "@ragents/plugins/ragents.orchestration/web/tile-docking";
26
21
  import { ProgramSlotContext, type ProgramSlot } from "@ragents/plugins/ragents.orchestration/web/program-slot";
27
22
  import {
28
23
  type RunApp,
@@ -34,7 +29,6 @@ import {
34
29
  import type { JsonValue } from "./bridge";
35
30
  import { actorProgramSourceView, ProgramSource } from "./ProgramSource";
36
31
  import { FunctionForm } from "./FunctionForm";
37
- import { ExpandIcon } from "./ExpandIcon";
38
32
  import { actorProgramsRevision } from "./state-revision";
39
33
  import { actorProgramViews, currentActorListing } from "./program-state";
40
34
  import { activeActorInvocations, readActorInvocation } from "./invocations";
@@ -47,7 +41,6 @@ export const RUN_TOOLS_TAB_ID = "ragents.actor-programs.tools";
47
41
  const entryIconClass = "flex size-[34px] items-center justify-center rounded-[10px] bg-[color-mix(in_srgb,var(--primary)_12%,var(--background))] text-primary";
48
42
  const headClass = "grid flex-none grid-cols-[auto_minmax(0,1fr)] items-center gap-2.5 border-b border-border-soft px-workspace-inset py-[9px]";
49
43
  const sectionLabelClass = "mb-2 text-[0.66rem] tracking-[0.075em] text-muted-foreground uppercase";
50
- const quickIconClass = "grid size-[30px] flex-none place-items-center rounded-lg border border-info/25 bg-glass-app text-info";
51
44
 
52
45
  const ACTIVE_STATUSES = new Set(["queued", "running"]);
53
46
  const POLL_INTERVAL = 700;
@@ -79,18 +72,6 @@ export function ActorProgramsProvider({
79
72
  const [storedListing, setListing] = useState<ActorProgramsListing>();
80
73
  const listing = useMemo(() => currentActorListing(storedListing, session.runView), [storedListing, session.runView]);
81
74
  const [error, setError] = useState<string>();
82
- const [fullscreen, setFullscreen] = useState<{ runId: string; appId: string }>();
83
- const visibleApps = actorProgramApps(session).filter((app) => app.app.visible !== false);
84
- const fullscreenAppId = fullscreen?.runId === runId && visibleApps.some((app) => app.id === fullscreen.appId)
85
- ? fullscreen.appId : undefined;
86
- const closeFullscreen = useCallback(() => setFullscreen(undefined), []);
87
- const openFullscreen = useCallback((appId: string) => {
88
- if (!actorProgramApps(session).some((app) => app.id === appId && app.app.visible !== false)) {
89
- throw new Error("The actor view is not available on the surface.");
90
- }
91
- setFullscreen({ runId, appId });
92
- }, [runId, session]);
93
- useEffect(() => { if (fullscreen && !fullscreenAppId) setFullscreen(undefined); }, [fullscreen, fullscreenAppId]);
94
75
  const loadRevision = useRef(0);
95
76
 
96
77
  const refresh = useCallback(async () => {
@@ -172,23 +153,15 @@ export function ActorProgramsProvider({
172
153
  const value = useMemo<ActorProgramsContextValue>(() => ({
173
154
  api,
174
155
  error,
175
- fullscreenAppId,
176
156
  invoke,
177
157
  listing,
178
- openFullscreen,
179
- closeFullscreen,
180
158
  refresh,
181
159
  runId,
182
- }), [api, error, fullscreenAppId, invoke, listing, openFullscreen, closeFullscreen, refresh, runId]);
160
+ }), [api, error, invoke, listing, refresh, runId]);
183
161
  const slot = useMemo<ProgramSlot>(() => ({
184
162
  runId,
185
- needsAnswer: (session, appId) => {
186
- const app = listing?.apps.find((candidate) => candidate.id === appId);
187
- return app !== undefined && pendingConfirmationFor(session, app) !== undefined;
188
- },
189
- openFullscreen,
190
163
  source: (view, actorId) => actorProgramSourceView(api, view, actorId),
191
- }), [api, listing, openFullscreen, runId]);
164
+ }), [api, runId]);
192
165
  return <ActorProgramsContext.Provider value={value}>
193
166
  <ProgramSlotContext.Provider value={slot}>{children}</ProgramSlotContext.Provider>
194
167
  </ActorProgramsContext.Provider>;
@@ -376,55 +349,6 @@ export function ToolsPanel({ active, navigation, selection, session }: Workspace
376
349
  );
377
350
  }
378
351
 
379
- /** A click on a mini-app selects its tile on the surface; the surface is the surface controller of the orchestration plugin. */
380
- export function ActorProgramsHeader({ session }: SessionHeaderContext) {
381
- const canArrange = useAccess().can("runs.inspect");
382
- const surface = useSurfaceController();
383
- const stage = useSurfaceEntities(session.session.id);
384
- const { closeFullscreen, fullscreenAppId, openFullscreen } = useActorPrograms();
385
- const apps = actorProgramApps(session).filter((app) => app.app.visible !== false);
386
- if (apps.length === 0) return null;
387
-
388
- return (
389
- <nav aria-label="Actor views" className="flex min-w-0 flex-none items-stretch">
390
- {apps.map((app) => (
391
- <div role="group" aria-label={app.title} key={`app:${app.id}`}
392
- className={cn("relative flex flex-none items-stretch border-r border-border hover:bg-accent focus-within:bg-accent",
393
- fullscreenAppId === app.id && "bg-primary/12 after:pointer-events-none after:absolute after:inset-x-0 after:bottom-0 after:h-[3px] after:bg-primary after:content-['']")}>
394
- <ToolbarItem
395
- as="button"
396
- className="max-w-[214px] border-r-0 font-bold hover:not-disabled:bg-transparent"
397
- draggable={canArrange}
398
- onDragStart={(event) => {
399
- if (!canArrange) { event.preventDefault(); return; }
400
- event.dataTransfer.setData(SURFACE_TILE_DRAG_TYPE, `app:${app.id}`);
401
- event.dataTransfer.effectAllowed = "move";
402
- closeFullscreen();
403
- }}
404
- onClick={() => {
405
- if (!surface) throw new Error("The app entries need the surface controller of the orchestration plugin.");
406
- closeFullscreen();
407
- surface.acceptSelection({ type: "run-app", id: app.id });
408
- }}
409
- aria-label={`Mini-app: ${app.title} of @${app.actorHandle}`}
410
- title={`Mini-app: ${app.title} of @${app.actorHandle}${canArrange ? ". Drag onto a tile to dock." : ""}`}
411
- type="button"
412
- >
413
- <span className={cn(quickIconClass, !stage.has(`app:${app.id}`) && "bg-transparent")}><LayoutGridIcon size={20} /></span>
414
- <ToolbarCopy><ToolbarLabel>@{app.actorHandle}</ToolbarLabel><ToolbarText>{app.title}</ToolbarText></ToolbarCopy>
415
- </ToolbarItem>
416
- <ToolbarItem as="button" aria-expanded={fullscreenAppId === app.id} aria-label={`${app.title} ${fullscreenAppId === app.id ? "close full view" : "open in full view"}`}
417
- title={`${app.title} ${fullscreenAppId === app.id ? "close full view" : "open in full view"}`}
418
- className="relative w-[38px] justify-center border-r-0 p-0 hover:not-disabled:bg-transparent before:pointer-events-none before:absolute before:top-1/4 before:bottom-1/4 before:left-0 before:border-l before:border-dashed before:border-border-strong before:content-['']"
419
- onClick={() => fullscreenAppId === app.id ? closeFullscreen() : openFullscreen(app.id)} type="button">
420
- <ExpandIcon />
421
- </ToolbarItem>
422
- </div>
423
- ))}
424
- </nav>
425
- );
426
- }
427
-
428
352
  export function ToolsBadge() {
429
353
  const { listing } = useActorPrograms();
430
354
  return listing && listing.tools.length > 0
@@ -1,6 +1,5 @@
1
1
  import { useAccess } from "@ragents/web/AccessContext";
2
2
  import { Spinner } from "@ragents/web/ui";
3
- import { isRecord } from "@ragents/web/lib/guards";
4
3
  import { runActorFrom } from "@ragents/web/run-view";
5
4
  import type {
6
5
  SurfaceElementContext,
@@ -33,13 +32,6 @@ export function ActorProgramToolCardSection({ actor, session }: CardSectionConte
33
32
 
34
33
  export const actorProgramSurfaceElements = (session: SessionContext): readonly SurfaceElementDefinition[] =>
35
34
  actorProgramApps(session).flatMap((module): SurfaceElementDefinition[] => {
36
- const rawPlacements = module.app.placements;
37
- const placement = Array.isArray(rawPlacements) ? rawPlacements.find((entry) => isRecord(entry) && entry.kind === "canvas") : undefined;
38
- const surface = isRecord(placement) ? placement : undefined;
39
- if (surface && typeof surface.anchorActorId !== "string") {
40
- throw new Error(`The surface placement of the actor view ${module.title} is invalid`);
41
- }
42
- if (surface && surface.anchorActorId !== module.actorId) throw new Error(`The view ${module.title} belongs to @${module.actorHandle}.`);
43
35
  return [{
44
36
  id: module.id,
45
37
  title: module.title,
@@ -61,7 +53,6 @@ export function ActorProgramSurfaceElement({ definition, session }: SurfaceEleme
61
53
  app={app}
62
54
  invoke={invoke}
63
55
  pendingConfirmationInvocationId={confirmation?.parameters.invocationId}
64
- presentation="tiled"
65
56
  runId={runId}
66
57
  session={session}
67
58
  />
@@ -17,8 +17,6 @@ export interface RunAppAction {
17
17
  confirmation: string | null;
18
18
  }
19
19
 
20
- export type RunAppPlacement = { kind: "canvas"; anchorActorId: string };
21
-
22
20
  export type RunAppInvocation = {
23
21
  id: string;
24
22
  actorId: string;
@@ -45,7 +43,6 @@ export interface RunApp {
45
43
  description?: string;
46
44
  revision: string;
47
45
  actions: RunAppAction[];
48
- placements: RunAppPlacement[];
49
46
  state: { version: 1; revision: number; values: JsonValue };
50
47
  invocations: RunAppInvocation[];
51
48
  }
@@ -198,23 +195,10 @@ export const runAppInvocationFrom = (value: unknown): RunAppInvocation => {
198
195
  }
199
196
  };
200
197
 
201
- const runAppPlacementFrom = (value: unknown): RunAppPlacement => {
202
- if (!isRecord(value)) throw new Error("An app placement is not an object");
203
- if (value.kind === "canvas") {
204
- return {
205
- kind: "canvas",
206
- anchorActorId: requiredString(value.anchorActorId, "placement.anchorActorId"),
207
- };
208
- }
209
- throw new Error("kind of an app placement must be canvas");
210
- };
211
-
212
198
  export const runAppFrom = (value: unknown): RunApp => {
213
199
  if (!isRecord(value)) throw new Error("An app is not an object");
214
200
  if (!Array.isArray(value.actions)) throw new Error("actions of an app is not a list");
215
201
  if (!Array.isArray(value.invocations)) throw new Error("invocations of an app is not a list");
216
- const rawPlacements = value.placements ?? [];
217
- if (!Array.isArray(rawPlacements)) throw new Error("placements of an app is not a list");
218
202
  if (!isRecord(value.state) || value.state.version !== 1 || !Number.isSafeInteger(value.state.revision) || !isJsonValue(value.state.values)) throw new Error("state of a view does not match the state contract");
219
203
  const actions = unique(value.actions.map(runAppActionFrom), (action) => action.id, "actions");
220
204
  const invocations = unique(
@@ -227,12 +211,9 @@ export const runAppFrom = (value: unknown): RunApp => {
227
211
  throw new Error("An app execution refers to an unknown action");
228
212
  }
229
213
  if (value.visible !== undefined && typeof value.visible !== "boolean") throw new Error("visible of an app is not a boolean");
230
- if (rawPlacements.length > 1) throw new Error("placements of an app contains more than one placement");
231
- const placements = rawPlacements.map(runAppPlacementFrom);
232
214
  const id = requiredString(value.id, "view.id");
233
215
  if (!VIEW_ID_PATTERN.test(id)) throw new Error("view.id is not a valid identifier");
234
216
  const actorId = requiredString(value.actorId, "view.actorId");
235
- if (placements.some((placement) => placement.anchorActorId !== actorId)) throw new Error("The view must be placed at its own actor");
236
217
  return {
237
218
  id,
238
219
  actorId,
@@ -244,7 +225,6 @@ export const runAppFrom = (value: unknown): RunApp => {
244
225
  : {}),
245
226
  revision: requiredString(value.revision, "app.revision"),
246
227
  actions,
247
- placements,
248
228
  state: { version: 1, revision: value.state.revision as number, values: value.state.values },
249
229
  invocations,
250
230
  };
@@ -5,10 +5,7 @@ import type { JsonValue } from "./bridge";
5
5
  export interface ActorProgramsContextValue {
6
6
  api: ActorProgramsApi;
7
7
  error: string | undefined;
8
- fullscreenAppId: string | undefined;
9
8
  listing: ActorProgramsListing | undefined;
10
- openFullscreen: (appId: string) => void;
11
- closeFullscreen: () => void;
12
9
  refresh: () => Promise<void>;
13
10
  invoke: (
14
11
  appId: string,
@@ -7,12 +7,10 @@ import {
7
7
  import { createActorProgramsApi } from "./api";
8
8
  import {
9
9
  RUN_TOOLS_TAB_ID,
10
- ActorProgramsHeader,
11
10
  ActorProgramsProvider,
12
11
  ToolsBadge,
13
12
  ToolsPanel,
14
13
  } from "./AppsPanel";
15
- import { FullscreenHost } from "./FullscreenHost";
16
14
  import {
17
15
  ActorProgramSurfaceElement,
18
16
  ActorProgramToolCardSection,
@@ -38,7 +36,6 @@ const configuredPlugin = (descriptor: WebPluginDescriptor, routePrefix: string):
38
36
  return (
39
37
  <ActorProgramsProvider api={api} session={session}>
40
38
  {children}
41
- <FullscreenHost session={session} />
42
39
  </ActorProgramsProvider>
43
40
  );
44
41
  }
@@ -47,12 +44,6 @@ const configuredPlugin = (descriptor: WebPluginDescriptor, routePrefix: string):
47
44
  ...descriptor,
48
45
  needsRunView: true,
49
46
  SessionProvider: Provider,
50
- sessionHeaders: [{
51
- id: "ragents.actor-programs.quick-list",
52
- placement: "surface",
53
- order: 250,
54
- Header: ActorProgramsHeader,
55
- }],
56
47
  cardSections: [{
57
48
  id: "ragents.actor-programs.tools",
58
49
  order: 400,
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "id": "ragents.orchestration",
3
3
  "exports": {
4
- "server": ["server/contract"],
5
- "web": ["web/surface-entities", "web/program-slot", "web/tile-docking"]
4
+ "web": ["web/program-slot"]
6
5
  }
7
6
  }
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  actorInputSchema,
3
- agentTools,
4
3
  enqueueActorInput,
5
4
  eventResultSchemaOf,
6
5
  ScriptDriver,
@@ -10,17 +9,13 @@ import {
10
9
  import { runtimeProviderToken } from "@ragents/host/ragents/host-services.js";
11
10
  import { pluginAsset } from "@ragents/host/plugin-support/plugin-folder.js";
12
11
  import type { PluginModule } from "@ragents/host/plugin-support/plugin-module.js";
13
- import { boundToTools, handlebarsPrompt } from "@ragents/host/plugin-support/prompt.js";
14
- import { createSurfaceToolContributor, placeOnSurface } from "./surface-tool.js";
15
- import { surfacePlacementToken } from "./contract.js";
12
+ import { handlebarsPrompt } from "@ragents/host/plugin-support/prompt.js";
16
13
  import { createRunStopContributor } from "./run-stop-tool.js";
17
14
  import { runManagementToken } from "@ragents/host/ragents/global-chat.js";
18
15
 
19
16
  const orchestrationPlugin: RAgentsPlugin = {
20
17
  manifest: { id: "ragents.orchestration" },
21
18
  register: (host) => {
22
- host.functions(createSurfaceToolContributor());
23
- host.provide(surfacePlacementToken, placeOnSurface(host.service(runtimeProviderToken)));
24
19
  host.functions(createRunStopContributor(
25
20
  (runId) => host.service(runManagementToken)().stop(runId),
26
21
  (error) => console.error("Run stop by the coordinator failed", error),
@@ -52,11 +47,6 @@ const orchestrationPlugin: RAgentsPlugin = {
52
47
  });
53
48
  host.prompts(
54
49
  handlebarsPrompt("ragents.orchestration.prompt", 600, pluginAsset("ragents.orchestration", "orchestration.hbs")),
55
- boundToTools(
56
- handlebarsPrompt("ragents.orchestration.surface", 650, pluginAsset("ragents.orchestration", "surface.hbs")),
57
- "canvas_layout_replace",
58
- "canvas_layout_place",
59
- ),
60
50
  );
61
51
  },
62
52
  };
@@ -13,7 +13,7 @@ const emptyMessages: readonly Message[] = [];
13
13
  export function ActorChat({ actor, view, presentation, display = presentation, primaryMessages = emptyMessages, conversation, historyError, running = false, className, onNavigate, scrollerRef }: {
14
14
  actor: RunActor;
15
15
  view: RunView;
16
- presentation: "surface" | "inspector";
16
+ presentation: "panel" | "inspector";
17
17
  /** Key of the display surface for the remembered detail level; defaults to the presentation. */
18
18
  display?: string;
19
19
  primaryMessages?: readonly Message[];
@@ -16,7 +16,7 @@ export function ActorChatControls({ actor, view, composerVisible = true, present
16
16
  actor: RunActor;
17
17
  view: RunView;
18
18
  composerVisible?: boolean;
19
- presentation?: "surface" | "inspector" | "panel";
19
+ presentation?: "inspector" | "panel";
20
20
  display?: string;
21
21
  running?: boolean;
22
22
  toolbarLeft?: ReactNode;
@@ -42,8 +42,6 @@ interface FlowInspectorProps {
42
42
  actorConversations?: Readonly<Record<string, readonly Message[]>>;
43
43
  conversationError?: string;
44
44
  primaryRunning: boolean;
45
- /** Display surface for the remembered detail level; the popout registers separately from the side inspector. */
46
- chatDisplay?: string;
47
45
  }
48
46
 
49
47
  const inspectorClass = "flex h-full min-h-0 flex-col bg-card";
@@ -549,7 +547,7 @@ function ActorView({ actor, index, ...props }: FlowInspectorProps & { actor: Run
549
547
  actor={actor}
550
548
  view={view}
551
549
  presentation="inspector"
552
- display={props.chatDisplay}
550
+ display="inspector"
553
551
  className="min-h-0 flex-1"
554
552
  primaryMessages={props.primaryMessages}
555
553
  conversation={props.actorConversations?.[actor.id]}
@@ -557,7 +555,7 @@ function ActorView({ actor, index, ...props }: FlowInspectorProps & { actor: Run
557
555
  onNavigate={props.onNavigate}
558
556
  running={primary ? props.primaryRunning : lifecycle === "running"}
559
557
  />
560
- <ActorChatControls actor={actor} view={view} composerVisible={props.composerVisible} display={props.chatDisplay}
558
+ <ActorChatControls actor={actor} view={view} composerVisible={props.composerVisible} display="inspector"
561
559
  running={primary ? props.primaryRunning : lifecycle === "running"} />
562
560
  </SectionTabs>
563
561
  </div>
@@ -0,0 +1,21 @@
1
+ import type { WorkspaceTabContext } from "@ragents/web/PluginRegistry";
2
+ import { Button } from "@ragents/web/ui";
3
+ import { chatPrimaryId, runViewFrom } from "@ragents/web/run-view";
4
+ import { FlowInspector, type FlowSelection } from "./FlowInspector";
5
+
6
+ export const INSPECTION_TAB_ID = "ragents.orchestration.inspection";
7
+
8
+ export function InspectionPanel({ navigation, selection, session }: WorkspaceTabContext) {
9
+ const view = runViewFrom(session.runView);
10
+ const primary = view && chatPrimaryId(view);
11
+ const selected = (selection ?? undefined) as FlowSelection | undefined;
12
+ return <div className="flex h-full min-h-0 flex-col">
13
+ <nav aria-label="Inspect participants" className="flex flex-none flex-wrap gap-1 border-b border-border p-2">
14
+ {view?.actors.map((actor) => <Button key={actor.id} onClick={() => navigation.openTab(INSPECTION_TAB_ID, { type: "actor", id: actor.id })} size="xs" variant="ghost">@{actor.handle}</Button>)}
15
+ </nav>
16
+ <div className="min-h-0 flex-1"><FlowInspector actorConversations={session.actorConversations} canGoBack={selected !== undefined} composerVisible={false}
17
+ conversationError={session.conversationError} onBack={() => navigation.openTab(INSPECTION_TAB_ID, null)}
18
+ onNavigate={(next) => navigation.openTab(INSPECTION_TAB_ID, next)} primaryMessages={session.messages}
19
+ primaryRunning={session.running} selection={selected ?? (primary ? { type: "actor", id: primary } : undefined)} view={view} /></div>
20
+ </div>;
21
+ }
@@ -1,9 +1,8 @@
1
+ import { SearchIcon } from "lucide-react";
2
+ import { INSPECTION_TAB_ID, InspectionPanel } from "./InspectionPanel";
1
3
  import { useEffect, useState } from "react";
2
- import { OrchestrationCenter } from "./Center";
3
4
  import { OrchestrationRunPanel } from "./run-panel/RunPanel";
4
5
  import { DocumentsSection } from "./DocumentsSection";
5
- import { RunPanelSettings } from "./run-panel/RunPanelSettings";
6
- import { ActorShortcuts } from "./ActorSurfaceControls";
7
6
  import { JournalStatus } from "./JournalStatus";
8
7
  import { StopRunHeader } from "./StopRunButton";
9
8
  import { EXECUTIONS_TAB_ID, ExecutionsPanel, IconExecutions } from "./ExecutionsPanel";
@@ -18,7 +17,7 @@ import {
18
17
 
19
18
  export { ORCHESTRATION_PLUGIN_ID } from "./constants";
20
19
 
21
- /** The selection on the surface: an artifact opens the documents, every other entity becomes the selected tile and the reported location. */
20
+ /** The selection on the surface: an artifact opens the documents; actors and mini-apps report the current location. */
22
21
  function OrchestrationSessionProvider({ children, navigation, session }: SessionProviderProps) {
23
22
  const runView = runViewFrom(session.runView);
24
23
  const [selection, setSelection] = useState<EntityReference>();
@@ -46,12 +45,8 @@ export const webPlugin: WebPlugin = {
46
45
  sessionStatus: [{ id: "ragents.orchestration.journal", readRight: "runs.inspect", order: 20, Status: JournalStatus }],
47
46
  sessionHeaders: [
48
47
  { id: "ragents.orchestration.stop", readRight: "runs.write", order: 100, Header: StopRunHeader },
49
- { id: "ragents.orchestration.actors", order: 300, placement: "surface", Header: ActorShortcuts },
50
48
  ],
51
- settings: [
52
- { id: "ragents.orchestration.runPanel", category: "appearance", label: "Run panel", Settings: RunPanelSettings },
53
- ],
54
- surface: { Center: OrchestrationCenter, RunPanel: OrchestrationRunPanel },
49
+ surface: { RunPanel: OrchestrationRunPanel },
55
50
  cardSections: [{
56
51
  id: "ragents.orchestration.documents",
57
52
  order: 300,
@@ -59,6 +54,15 @@ export const webPlugin: WebPlugin = {
59
54
  }],
60
55
  SessionProvider: OrchestrationSessionProvider,
61
56
  workspaceTabs: [{
57
+ id: INSPECTION_TAB_ID,
58
+ readRight: "runs.inspect",
59
+ label: "Inspection",
60
+ order: 100,
61
+ Icon: SearchIcon,
62
+ Panel: InspectionPanel,
63
+ keepMounted: true,
64
+ available: (session) => runViewFrom(session.runView) !== undefined,
65
+ }, {
62
66
  id: EXECUTIONS_TAB_ID,
63
67
  readRight: "runs.inspect",
64
68
  label: "Executions",
@@ -1,12 +1,9 @@
1
1
  import { createContext, useContext, type ReactNode } from "react";
2
- import type { SessionContext } from "@ragents/web/PluginRegistry";
3
2
  import type { RunView } from "@ragents/web/run-view";
4
3
 
5
4
  /** What an actor-program extension adds to the run panel and the inspector; without one both show no programs. */
6
5
  export interface ProgramSlot {
7
6
  runId: string;
8
- needsAnswer: (session: SessionContext, appId: string) => boolean;
9
- openFullscreen: (appId: string) => void;
10
7
  source: (view: RunView, actorId: string) => ReactNode;
11
8
  }
12
9
 
@@ -1,7 +1,6 @@
1
1
  import { useCallback, useId, useMemo, useRef, useState } from "react";
2
2
  import { ChevronDownIcon } from "lucide-react";
3
- import { Badge, cn, Input, Spinner } from "@ragents/web/ui";
4
- import { ActorHeaderModeControl } from "../ActorSurfaceControls";
3
+ import { Badge, Input, Spinner } from "@ragents/web/ui";
5
4
  import { ActorPopout } from "../ActorPopout";
6
5
  import type { RunActor, RunView } from "@ragents/web/run-view";
7
6
  import { pendingInputCount } from "./run-panel-actors";
@@ -11,50 +10,38 @@ import { ActorIcon, AddresseeTree } from "./AddresseeTree";
11
10
  const chipClass = "flex h-7 min-w-[70px] max-w-[220px] flex-[0_1_auto] cursor-pointer items-center gap-1.5 rounded-full border border-border bg-background pr-2 pl-1 text-[0.72rem] font-semibold text-foreground hover:bg-accent aria-expanded:border-primary focus-visible:outline-2 focus-visible:outline-ring/60 focus-visible:outline-offset-1";
12
11
  const busyClass = "flex h-7 min-w-0 flex-[0_1_auto] cursor-pointer items-center gap-1.5 rounded-md px-1.5 text-[0.7rem] text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring/60 focus-visible:outline-offset-1";
13
12
 
14
- /** The chat's addressee as a chip in the input bar; the pop-out shows the actors as a tree by creator, the hidden ones behind their count. */
15
- export function AddresseeControl({ hidden, onOpenChange, onReveal, onSelect, runId, selected, shown, technical, view }: {
16
- hidden: readonly RunActor[];
17
- onOpenChange: (open: boolean) => void;
18
- onReveal: (actor: RunActor) => void;
13
+ /** The chat's addressee as a chip in the input bar; the pop-out shows the actors as a tree by creator, including stopped actors. */
14
+ export function AddresseeControl({ actors, onSelect, selected, technical, view }: {
15
+ actors: readonly RunActor[];
19
16
  onSelect: (actor: RunActor) => void;
20
- runId: string;
21
17
  selected: RunActor;
22
- shown: readonly RunActor[];
23
18
  technical: boolean;
24
19
  view: RunView;
25
20
  }) {
26
21
  const [open, setOpen] = useState(false);
27
22
  const [query, setQuery] = useState("");
28
- const [hiddenListed, setHiddenListed] = useState(false);
29
23
  const [toggled, setToggled] = useState<ReadonlySet<string>>(() => new Set());
30
24
  const buttonRef = useRef<HTMLButtonElement>(null);
31
25
  const panelId = useId();
32
- const change = useCallback((next: boolean) => {
33
- setOpen(next);
34
- onOpenChange(next);
35
- }, [onOpenChange]);
36
26
  const close = useCallback((restoreFocus = false) => {
37
- change(false);
27
+ setOpen(false);
38
28
  setQuery("");
39
29
  if (restoreFocus) buttonRef.current?.focus({ preventScroll: true });
40
- }, [change]);
30
+ }, []);
41
31
  const revealSelected = useCallback((element: HTMLDivElement | null) => {
42
32
  element?.querySelector("[aria-current=true]")?.scrollIntoView({ block: "nearest" });
43
33
  }, []);
44
- const working = useMemo(() => [...shown, ...hidden].filter((actor) => actor.lifecycle?.kind === "running" && actor.id !== selected.id), [hidden, selected.id, shown]);
34
+ const working = useMemo(() => actors.filter((actor) => actor.lifecycle?.kind === "running" && actor.id !== selected.id), [actors, selected.id]);
45
35
  const summary = useMemo(() => addresseeSummaries(view), [view]);
46
36
  const searching = query.trim() !== "";
47
- const shownTree = useMemo(() => addresseeTree(view, addresseeMatches(view, shown, query, summary)), [query, shown, summary, view]);
48
- const hiddenMatches = useMemo(() => addresseeMatches(view, hidden, query, summary), [hidden, query, summary, view]);
49
- const hiddenTree = useMemo(() => addresseeTree(view, hiddenMatches), [hiddenMatches, view]);
50
- const hiddenOpen = hiddenListed || (searching && hiddenMatches.length > 0);
37
+ const tree = useMemo(() => addresseeTree(view, addresseeMatches(view, actors, query, summary)), [actors, query, summary, view]);
51
38
  const toggle = useCallback((key: string) => setToggled((current) => {
52
39
  const next = new Set(current);
53
40
  if (!next.delete(key)) next.add(key);
54
41
  return next;
55
42
  }), []);
56
- const pick = (actor: RunActor, reveal: boolean) => {
57
- (reveal ? onReveal : onSelect)(actor);
43
+ const pick = (actor: RunActor) => {
44
+ onSelect(actor);
58
45
  close(true);
59
46
  };
60
47
  const busyTitle = working.map((actor) => `@${actor.handle} is working`).join(", ");
@@ -63,33 +50,25 @@ export function AddresseeControl({ hidden, onOpenChange, onReveal, onSelect, run
63
50
  const listClass = "px-1.5 py-1";
64
51
 
65
52
  return <>
66
- <button aria-controls={open ? panelId : undefined} aria-expanded={open} aria-haspopup="dialog" className={chipClass} onClick={() => change(!open)} ref={buttonRef} title={`Addressee: @${selected.handle}. Click to choose another actor`} type="button">
53
+ <button aria-controls={open ? panelId : undefined} aria-expanded={open} aria-haspopup="dialog" className={chipClass} onClick={() => setOpen(!open)} ref={buttonRef} title={`Addressee: @${selected.handle}. Click to choose another actor`} type="button">
67
54
  <ActorIcon actor={selected} className="size-5" view={view} />
68
55
  <span className="truncate">@{selected.handle}</span>
69
56
  <ChevronDownIcon aria-hidden className="size-3 flex-none text-muted-foreground" />
70
57
  </button>
71
- {working.length > 0 && <button className={busyClass} onClick={() => change(!open)} title={busyTitle} type="button">
58
+ {working.length > 0 && <button className={busyClass} onClick={() => setOpen(!open)} title={busyTitle} type="button">
72
59
  <Spinner aria-hidden className="size-3 flex-none" />
73
60
  <span className="truncate in-data-[compact=true]:hidden">@{working[0].handle}</span>
74
61
  {busyPending > 0 && <Badge className="h-4 min-w-4 px-1 text-[0.6rem]" variant="secondary">{busyPending}</Badge>}
75
62
  {working.length > 1 && <span className="flex-none">+{working.length - 1}</span>}
76
63
  </button>}
77
64
  {open && <ActorPopout buttonRef={buttonRef} closeLabel="Close addressee" height={460} id={panelId} label="Addressee" onClose={close} open placement="top" role="dialog" width="available">
78
- {shown.length + hidden.length > ADDRESSEE_SEARCH_ABOVE && <div className="flex-none border-b border-border-soft px-2.5 py-1.5">
65
+ {actors.length > ADDRESSEE_SEARCH_ABOVE && <div className="flex-none border-b border-border-soft px-2.5 py-1.5">
79
66
  <Input aria-label="Search actors" className="h-7" onChange={(event) => setQuery(event.target.value)} placeholder="Search handle or task ..." type="search" value={query} />
80
67
  </div>}
81
68
  <div className="min-h-0 flex-1 overflow-auto overscroll-contain" ref={revealSelected}>
82
- <AddresseeTree className={listClass} nodes={shownTree} onPick={(actor) => pick(actor, false)} {...treeProps} />
83
- {searching && shownTree.length === 0 && hiddenMatches.length === 0 && <p className="px-3.5 py-2 text-muted-foreground">No actor matches the search.</p>}
84
- {hidden.length > 0 && (!searching || hiddenMatches.length > 0) && <div className="border-t border-border-soft">
85
- <button aria-expanded={hiddenOpen} className="flex w-full cursor-pointer items-center justify-between gap-2 px-3.5 py-2 text-left text-muted-foreground hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring/60 focus-visible:-outline-offset-2" onClick={() => setHiddenListed(!hiddenOpen)} type="button">
86
- <span>{hidden.length} hidden actors</span>
87
- <ChevronDownIcon aria-hidden className={cn("size-3.5 flex-none transition-transform", hiddenOpen && "rotate-180")} />
88
- </button>
89
- {hiddenOpen && <AddresseeTree className={listClass} nodes={hiddenTree} onPick={(actor) => pick(actor, true)} {...treeProps} />}
90
- </div>}
69
+ <AddresseeTree className={listClass} nodes={tree} onPick={pick} {...treeProps} />
70
+ {searching && tree.length === 0 && <p className="px-3.5 py-2 text-muted-foreground">No actor matches the search.</p>}
91
71
  </div>
92
- <div className="flex flex-none items-stretch border-t border-border-soft"><ActorHeaderModeControl runId={runId} /></div>
93
72
  </ActorPopout>}
94
73
  </>;
95
74
  }