@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,51 +0,0 @@
1
- import { handleKey } from "@ragents/engine/src/http/contracts";
2
- import { surfaceTileNodeOf } from "./tiled-layout.js";
3
-
4
- export const ORCHESTRATION_PLUGIN_ID = "ragents.orchestration";
5
-
6
- export type SurfaceEntity =
7
- | { kind: "actor"; handle: string }
8
- | { kind: "app"; id: string };
9
-
10
- export type SurfaceTileNode =
11
- | { entity: string; chatInput?: boolean }
12
- | { direction: "horizontal" | "vertical"; weights: [number, number]; children: [SurfaceTileNode, SurfaceTileNode] };
13
-
14
- export type SurfaceLayout = {
15
- root: SurfaceTileNode | null;
16
- };
17
-
18
- const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === "object" && value !== null && !Array.isArray(value);
19
-
20
- export const surfaceEntityOf = (text: string): SurfaceEntity => {
21
- const trimmed = text.trim();
22
- if (trimmed.startsWith("@")) {
23
- const handle = handleKey(trimmed);
24
- if (!handle || /\s/.test(handle)) throw new Error(`Entity ${JSON.stringify(text)}: a handle is missing after the @`);
25
- return { kind: "actor", handle };
26
- }
27
- const match = /^app:(.+)$/s.exec(trimmed);
28
- if (!match) {
29
- throw new Error(`Entity ${JSON.stringify(text)}: expected @handle or app:<id>`);
30
- }
31
- const id = match[1].trim();
32
- if (!id) throw new Error(`Entity ${JSON.stringify(text)}: the ID after the colon is empty`);
33
- return { kind: "app", id };
34
- };
35
-
36
- export const surfaceEntityKey = (entity: SurfaceEntity): string =>
37
- entity.kind === "actor" ? `@${entity.handle}` : `app:${entity.id}`;
38
-
39
- const LEGACY_KEYS = ["nodes", "shapes", "lines", "mode"] as const;
40
-
41
- /** Parses and validates a tile layout; throws an error naming the first violation. */
42
- export const surfaceLayoutOf = (value: unknown): SurfaceLayout => {
43
- if (!isRecord(value)) throw new Error("The arrangement must be an object with root");
44
- const legacy = LEGACY_KEYS.filter((key) => key in value);
45
- if (legacy.length > 0) {
46
- throw new Error(`The program arrangement comes from the removed free surface (${legacy.join(", ")}); `
47
- + "canvas_layout_replace with root sets it anew as a tile layout");
48
- }
49
- if (!("root" in value)) throw new Error("root is missing; expected a tile, a split or null");
50
- return { root: value.root === null ? null : surfaceTileNodeOf(value.root) };
51
- };
@@ -1,14 +0,0 @@
1
- import { serviceToken, type CommandContext, type ServiceToken } from "@ragents/engine";
2
-
3
- export interface SurfacePlacementRequest {
4
- entity: string;
5
- direction?: "horizontal" | "vertical";
6
- weight?: number;
7
- }
8
-
9
- /** Places an entity next to a run's surface layout, as canvas_layout_place does; an entity the layout shows already stays. */
10
- export interface SurfacePlacement {
11
- place: (context: CommandContext, runId: string, request: SurfacePlacementRequest) => { placed: boolean };
12
- }
13
-
14
- export const surfacePlacementToken: ServiceToken<SurfacePlacement> = serviceToken("ragents.orchestration.surface-placement");
@@ -1,198 +0,0 @@
1
- import { Type } from "typebox";
2
- import {
3
- DomainError,
4
- actorByHandle,
5
- defineRunFunction,
6
- defineToolAvailability,
7
- holdsUsable,
8
- type CommandContext,
9
- type Orchestration,
10
- type RunFunction,
11
- type RunView,
12
- type ToolContributor,
13
- } from "@ragents/engine";
14
- import { toolDescriptorFrom } from "@ragents/host/plugin-support/agent-tool.js";
15
- import { ACTOR_PROGRAMS_STATE_ID, resolveActorView, type ActorProgramState } from "@ragents/host/plugin-support/actor-programs/contract.js";
16
- import {
17
- ORCHESTRATION_PLUGIN_ID,
18
- surfaceEntityOf,
19
- surfaceLayoutOf,
20
- type SurfaceLayout,
21
- } from "../contract.js";
22
- import { surfaceTileEntities, mapSurfaceTileEntities } from "../tiled-layout.js";
23
- import type { SurfacePlacement, SurfacePlacementRequest } from "./contract.js";
24
-
25
- export const surfaceToolMetadata = {
26
- name: "canvas_layout_replace",
27
- label: "Replace Surface Layout",
28
- description: "Arrange actors and mini-apps as tiles on the viewport-filling work surface, using a binary tree of weighted splits.",
29
- longDescription: "root is the whole arrangement and replaces the stored one. A tile is {entity:'@helper'} or "
30
- + "{entity:'app:@workspace/main'}; a split is {direction:'horizontal',weights:[1,1],children:[tile,tile]}. "
31
- + "horizontal means left/right, vertical means top/bottom, and weights give the ratio of the two children. "
32
- + "App left, chat right at 50:50: {root:{direction:'horizontal',weights:[1,1],children:["
33
- + "{entity:'app:@workspace/main'},{entity:'@helper'}]}}. One tile above two tiles at 2:1: "
34
- + "{root:{direction:'vertical',weights:[2,1],children:[{entity:'@lead'},"
35
- + "{direction:'horizontal',weights:[1,1],children:[{entity:'@first'},{entity:'@second'}]}]}}. "
36
- + "root:null clears the surface. Dividers are draggable, so do not resend a layout to fight a personal arrangement. "
37
- + "Participants you do not place stay reachable through the run header.",
38
- } as const;
39
-
40
- export const placeToolMetadata = {
41
- name: "canvas_layout_place",
42
- label: "Place on Surface",
43
- description: "Place one actor or mini-app next to the current surface layout instead of replacing it.",
44
- longDescription: "entity is {entity} of a tile: '@helper' or 'app:@workspace/main'. The current arrangement becomes one side of a new split "
45
- + "and the entity the other: direction 'horizontal' (default) puts it to the right, 'vertical' below. weight is its share against "
46
- + "the existing arrangement's weight 1, default 1. An entity the layout already shows stays where it is; placed is then false. "
47
- + "On an empty surface the entity becomes the only tile. Use it when you join a running run, such as a run script started with "
48
- + "start.embedded, so that the tiles others arranged stay.",
49
- } as const;
50
-
51
- export const canDesignSurface = defineToolAvailability({
52
- availability: "conditional",
53
- availabilityDetail: "Only for agents and TypeScript actors with the capability plugin.state.write.",
54
- requiredCapabilities: ["plugin.state.write"],
55
- }, (actor) => actor.kind !== "human" && holdsUsable(actor, "plugin.state.write"));
56
-
57
- const tileSchema = Type.Cyclic({
58
- SurfaceTile: Type.Union([
59
- Type.Object({
60
- entity: Type.String({ minLength: 1, description: "Actor @handle or activated mini-app app:@handle/view-key or app:program-name/view-key; the server resolves the view ID" }),
61
- chatInput: Type.Optional(Type.Boolean({ description: "Actor tiles only: false hides the chat composer in the tile; default true. Does not change permissions or the inspector" })),
62
- }, { additionalProperties: false }),
63
- Type.Object({
64
- direction: Type.Union([Type.Literal("horizontal"), Type.Literal("vertical")]),
65
- weights: Type.Tuple([Type.Number({ exclusiveMinimum: 0 }), Type.Number({ exclusiveMinimum: 0 })]),
66
- children: Type.Tuple([Type.Ref("SurfaceTile"), Type.Ref("SurfaceTile")]),
67
- }, { additionalProperties: false }),
68
- ]),
69
- }, "SurfaceTile");
70
-
71
- const surfaceLayoutSchema = Type.Object({
72
- root: Type.Union([tileSchema, Type.Null()], { description: "The whole arrangement: a tile or a binary split. Maximum 64 unique tiles and 16 nested splits. null clears the surface" }),
73
- });
74
-
75
- const invalid = (message: string) => new DomainError("invalid-surface-layout", message, 400);
76
-
77
- const parsedLayout = (input: unknown): SurfaceLayout => {
78
- try {
79
- return surfaceLayoutOf(input);
80
- } catch (caught) {
81
- throw invalid(caught instanceof Error ? caught.message : String(caught));
82
- }
83
- };
84
-
85
- const isStopped = (actor: RunView["actors"][number]) => actor.kind !== "human" && actor.lifecycle.kind === "stopped";
86
-
87
- const surfaceActorOf = (view: RunView, reference: string, where: string) => {
88
- const entity = surfaceEntityOf(reference);
89
- if (entity.kind !== "actor") throw invalid(`${where}: ${reference} is not an actor`);
90
- const handle = entity.handle;
91
- const found = actorByHandle(view.actors, handle);
92
- if (!found) {
93
- const known = view.actors.filter((actor) => actor.kind !== "human").map((actor) => `@${actor.handle}`);
94
- throw invalid(`${where}: @${handle} is not an actor of this run. Present: ${known.join(", ") || "none"}`);
95
- }
96
- if (found.kind === "human") {
97
- throw invalid(`${where}: @${handle} is the human in the chat; the chat is fixed to the left of the surface`);
98
- }
99
- return found;
100
- };
101
-
102
- const isActorReference = (reference: string) => surfaceEntityOf(reference).kind === "actor";
103
-
104
- const resolveAppReferences = (view: RunView, layout: SurfaceLayout): SurfaceLayout => {
105
- const programs = view.pluginStates.flatMap((entry) => {
106
- if (entry.pluginId !== ACTOR_PROGRAMS_STATE_ID || entry.scope.kind !== "actor") return [];
107
- const actorId = entry.scope.actorId;
108
- const actor = view.actors.find((candidate) => candidate.id === actorId);
109
- if (!actor || actor.kind === "human" || isStopped(actor)) return [];
110
- const { program } = entry.state as unknown as ActorProgramState;
111
- return program ? [program] : [];
112
- });
113
- const resolve = (reference: string, where: string): string => {
114
- const entity = surfaceEntityOf(reference);
115
- if (entity.kind !== "app") return reference;
116
- try {
117
- return `app:${resolveActorView(programs, entity.id).view.id}`;
118
- } catch (error) {
119
- throw invalid(`${where}: ${reference}: ${error instanceof Error ? error.message : String(error)} Surface entities get the prefix app:.`);
120
- }
121
- };
122
- return parsedLayout({ root: layout.root ? mapSurfaceTileEntities(layout.root, (entity) => resolve(entity, "root")) : null });
123
- };
124
-
125
- /** Resolve app names and validate the placed actors before any layout is stored. */
126
- export const checkLayoutAgainstRun = (view: RunView, input: SurfaceLayout): SurfaceLayout => {
127
- const layout = resolveAppReferences(view, input);
128
- for (const reference of surfaceTileEntities(layout.root)) {
129
- if (isActorReference(reference)) surfaceActorOf(view, reference, "root");
130
- }
131
- return layout;
132
- };
133
-
134
- const currentLayout = (view: RunView): SurfaceLayout => {
135
- const state = view.pluginStates.find((entry) => entry.pluginId === ORCHESTRATION_PLUGIN_ID && entry.scope.kind === "run")?.state;
136
- return state === undefined || state === null ? { root: null } : surfaceLayoutOf(state);
137
- };
138
-
139
- /** Splits the root once: the existing arrangement keeps weight 1, the placed tile gets its own. */
140
- const placed = (view: RunView, request: SurfacePlacementRequest): SurfaceLayout | undefined => {
141
- const current = currentLayout(view);
142
- const [tile] = surfaceTileEntities(checkLayoutAgainstRun(view, parsedLayout({ root: { entity: request.entity } })).root);
143
- if (surfaceTileEntities(current.root).includes(tile!)) return undefined;
144
- const root = current.root === null ? { entity: tile! } : {
145
- direction: request.direction ?? "horizontal",
146
- weights: [1, request.weight ?? 1],
147
- children: [current.root, { entity: tile! }],
148
- };
149
- return parsedLayout({ root });
150
- };
151
-
152
- export const placeOnSurface = (runtime: () => Orchestration): SurfacePlacement => ({
153
- place: (context: CommandContext, runId: string, request: SurfacePlacementRequest) => {
154
- const layout = placed(runtime().view(runId), request);
155
- if (!layout) return { placed: false };
156
- runtime().replacePluginState(context, runId, { pluginId: ORCHESTRATION_PLUGIN_ID, scope: { kind: "run" }, state: layout });
157
- return { placed: true };
158
- },
159
- });
160
-
161
- const placeSchema = Type.Object({
162
- entity: Type.String({ minLength: 1, description: "Actor @handle or activated mini-app app:@handle/view-key or app:program-name/view-key" }),
163
- direction: Type.Optional(Type.Union([Type.Literal("horizontal"), Type.Literal("vertical")], { description: "horizontal: to the right of the current layout (default); vertical: below it" })),
164
- weight: Type.Optional(Type.Number({ exclusiveMinimum: 0, description: "Share of the placed tile against the existing arrangement's weight 1; default 1" })),
165
- }, { additionalProperties: false });
166
-
167
- export const createPlaceTool = (): RunFunction =>
168
- defineRunFunction({
169
- ...placeToolMetadata,
170
- schema: placeSchema,
171
- resultSchema: Type.Object({ placed: Type.Boolean() }, { additionalProperties: false }),
172
- available: canDesignSurface,
173
- run: ({ runtime, caller, context }, toolCallId, input) => placeOnSurface(() => runtime).place(context(toolCallId), caller.runId, input),
174
- });
175
-
176
- export const createSurfaceTool = (): RunFunction =>
177
- defineRunFunction({
178
- ...surfaceToolMetadata,
179
- schema: surfaceLayoutSchema,
180
- resultSchema: Type.Null(),
181
- available: canDesignSurface,
182
- run: ({ runtime, caller, context }, toolCallId, input) => {
183
- const view = runtime.view(caller.runId);
184
- const layout = checkLayoutAgainstRun(view, parsedLayout(input));
185
- runtime.replacePluginState(context(toolCallId), caller.runId, {
186
- pluginId: ORCHESTRATION_PLUGIN_ID,
187
- scope: { kind: "run" },
188
- state: layout,
189
- });
190
- return null;
191
- },
192
- });
193
-
194
- export const createSurfaceToolContributor = (): ToolContributor => ({
195
- name: "ragents.orchestration.surface",
196
- descriptors: [toolDescriptorFrom(surfaceToolMetadata, canDesignSurface), toolDescriptorFrom(placeToolMetadata, canDesignSurface)],
197
- tools: () => [createSurfaceTool(), createPlaceTool()],
198
- });
@@ -1,39 +0,0 @@
1
- ## Arranging the tiled work surface
2
-
3
- The work surface of a run is a grid of tiles that fills the viewport. Each tile shows one actor or
4
- one activated mini-app. There is no other arrangement and no pan, zoom or free placement. Use
5
- `context.functions.canvas_layout_replace` from TypeScript to set the arrangement, and read its exact
6
- input type with `typescript_api` before the first call.
7
-
8
- `root` is the whole arrangement and replaces the stored one; it is the only parameter. A tile is
9
- {entity:"@helper"} or {entity:"app:@workspace/main"}. A split is {direction, weights, children} with
10
- direction "horizontal" (left/right) or "vertical" (top/bottom), two positive weights and exactly two
11
- children, each of which may be a split again. `root:null` clears the surface.
12
-
13
- App left, chat right at 50:50:
14
- {root:{direction:"horizontal",weights:[1,1],children:[
15
- {entity:"app:@workspace/main"},{entity:"@helper"}
16
- ]}}. One tile above two tiles at 2:1:
17
- {root:{direction:"vertical",weights:[2,1],children:[{entity:"@lead"},
18
- {direction:"horizontal",weights:[1,1],children:[{entity:"@first"},{entity:"@second"}]}
19
- ]}}. Resolve the actual actor and app names before calling; the example names are not existing
20
- participants.
21
-
22
- To add one tile without replacing what is arranged, `context.functions.canvas_layout_place({ entity })`
23
- splits the current layout once and puts the entity to its right, or below with `direction: "vertical"`;
24
- `weight` is its share against the existing arrangement's weight 1. An entity the layout shows already
25
- stays where it is. A run script started inside a running run (`start.embedded`) uses place, not replace.
26
-
27
- An actor tile carries {entity:"@helper",chatInput:false} when its chat composer should stay hidden.
28
- That changes neither permissions nor the inspector. Mini-app tiles have no chatInput.
29
-
30
- Activate actor programs before placing their views. Reference a view as `app:@handle/view-key` or
31
- `app:program-name/view-key`, using the handle, program name and view key you chose. The server
32
- resolves the actual view ID and rejects unknown views before storing the layout. Do not invent or
33
- copy generated view IDs. `actor_view_set_visibility` uses the same names without the `app:` prefix.
34
-
35
- Place what the user needs to see side by side and update the arrangement when the work changes.
36
- Participants you do not place stay reachable through the run header and can be dragged onto a tile
37
- there. Users can move, resize and remove tiles themselves; their personal arrangement takes
38
- precedence until they reset it, so do not resend a layout to fight it. The user takes part through
39
- the chat and needs no tile of their own.
@@ -1,55 +0,0 @@
1
- import { surfaceEntityKey, surfaceEntityOf, type SurfaceTileNode } from "./contract.js";
2
-
3
- export type { SurfaceTileNode } from "./contract.js";
4
-
5
- export const MAX_SURFACE_TILE_DEPTH = 16;
6
- export const MAX_SURFACE_TILES = 64;
7
-
8
- export const surfaceTileNodeOf = (input: unknown): SurfaceTileNode => {
9
- const entities = new Set<string>();
10
- const parse = (value: unknown, where: string, depth: number): SurfaceTileNode => {
11
- if (depth > MAX_SURFACE_TILE_DEPTH) throw new Error(`${where}: at most ${MAX_SURFACE_TILE_DEPTH} nested splits are allowed`);
12
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${where} must be a tile or a split`);
13
- const node = value as Record<string, unknown>;
14
- if (Object.hasOwn(node, "entity")) {
15
- if (Object.keys(node).some((key) => key !== "entity" && key !== "chatInput")) throw new Error(`${where}: a tile carries only entity and chatInput; a split direction, weights and children`);
16
- if (typeof node.entity !== "string") throw new Error(`${where}.entity must be @handle or app:<name>/<view>`);
17
- const entity = surfaceEntityOf(node.entity);
18
- const key = surfaceEntityKey(entity);
19
- if (node.chatInput !== undefined && (entity.kind !== "actor" || typeof node.chatInput !== "boolean")) {
20
- throw new Error(`${where}.chatInput is only allowed for actors, as a boolean`);
21
- }
22
- if (entities.has(key)) throw new Error(`${where}: ${key} is placed as a tile more than once`);
23
- entities.add(key);
24
- if (entities.size > MAX_SURFACE_TILES) throw new Error(`At most ${MAX_SURFACE_TILES} tiles are allowed`);
25
- return node.chatInput === undefined ? { entity: key } : { entity: key, chatInput: node.chatInput as boolean };
26
- }
27
- if (Object.keys(node).some((key) => !["direction", "weights", "children"].includes(key))) throw new Error(`${where}: a split carries only direction, weights and children`);
28
- if (node.direction !== "horizontal" && node.direction !== "vertical") throw new Error(`${where}.direction must be horizontal or vertical`);
29
- if (!Array.isArray(node.weights) || node.weights.length !== 2 || node.weights.some((weight) => typeof weight !== "number" || !Number.isFinite(weight) || weight <= 0)) {
30
- throw new Error(`${where}.weights must contain exactly two finite positive numbers`);
31
- }
32
- if (!Array.isArray(node.children) || node.children.length !== 2) throw new Error(`${where}.children must contain exactly two tiles or splits`);
33
- return {
34
- direction: node.direction,
35
- weights: [node.weights[0], node.weights[1]],
36
- children: [parse(node.children[0], `${where}.children[0]`, depth + 1), parse(node.children[1], `${where}.children[1]`, depth + 1)],
37
- };
38
- };
39
- return parse(input, "root", 0);
40
- };
41
-
42
- /** A tile hides the chat composer of its actor only when its leaf says chatInput: false. */
43
- export const surfaceTileChatInput = (root: SurfaceTileNode | null | undefined, entity: string): boolean =>
44
- !root ? true : "entity" in root
45
- ? root.entity !== entity || root.chatInput !== false
46
- : surfaceTileChatInput(root.children[0], entity) && surfaceTileChatInput(root.children[1], entity);
47
-
48
- export const surfaceTileEntities = (root: SurfaceTileNode | null | undefined): string[] =>
49
- !root ? [] : "entity" in root ? [root.entity] : root.children.flatMap(surfaceTileEntities);
50
-
51
- export const mapSurfaceTileEntities = (root: SurfaceTileNode, resolve: (entity: string) => string): SurfaceTileNode =>
52
- "entity" in root ? { ...root, entity: resolve(root.entity) } : {
53
- ...root,
54
- children: [mapSurfaceTileEntities(root.children[0], resolve), mapSurfaceTileEntities(root.children[1], resolve)],
55
- };
@@ -1,29 +0,0 @@
1
- import { ActorChat } from "./ActorChat";
2
- import { ActorChatControls } from "./ActorChatControls";
3
- import { ChatPanel, type Message } from "quassel";
4
- import { memo, useCallback } from "react";
5
- import type { FlowSelection } from "./FlowInspector";
6
- import type { RunActor, RunView } from "@ragents/web/run-view";
7
-
8
- export const ActorChatPreview = memo(function ActorChatPreview({ actor, view, primaryMessages, conversation, historyError, chatInput = true, running = false, onNavigate }: {
9
- actor: RunActor;
10
- view: RunView;
11
- primaryMessages?: readonly Message[];
12
- conversation?: readonly Message[];
13
- historyError?: string;
14
- chatInput?: boolean;
15
- running?: boolean;
16
- onNavigate: (selection: FlowSelection) => void;
17
- }) {
18
- const focusableScroller = useCallback((element: HTMLDivElement | null) => {
19
- if (!element) return;
20
- element.tabIndex = 0;
21
- element.setAttribute("role", "region");
22
- element.setAttribute("aria-label", `Chat history of @${actor.handle}`);
23
- }, [actor.handle]);
24
- return <div className="flex min-h-[106px] w-full min-w-0 flex-1 flex-col overflow-hidden rounded-b-[16px] text-left" data-surface-scroll="true">
25
- <ChatPanel className="flex-1" composer={<ActorChatControls actor={actor} view={view} composerVisible={chatInput} presentation="surface" running={running} />}>
26
- <ActorChat actor={actor} view={view} presentation="surface" primaryMessages={primaryMessages} conversation={conversation} historyError={historyError} running={running} onNavigate={onNavigate} scrollerRef={focusableScroller} />
27
- </ChatPanel>
28
- </div>;
29
- });
@@ -1,74 +0,0 @@
1
- import { useAccess } from "@ragents/web/AccessContext";
2
- import { useCallback, useId, useRef, useState } from "react";
3
- import { useSurfaceController, type SessionContext } from "@ragents/web/PluginRegistry";
4
- import { CodeIcon, UsersIcon } from "lucide-react";
5
- import { ToolbarCopy, ToolbarItem, ToolbarLabel, ToolbarText } from "@ragents/web/Toolbar";
6
- import { cn } from "@ragents/web/ui";
7
- import { FlowInspector, type FlowSelection } from "./FlowInspector";
8
- import { ActorPopout } from "./ActorPopout";
9
- import { actorTone, type RunActor, type RunView } from "@ragents/web/run-view";
10
- import { useSurfaceEntities } from "./surface-entities";
11
- import { SURFACE_TILE_DRAG_TYPE } from "./tile-docking";
12
-
13
- const shortcutIconClass = "grid size-[30px] flex-none place-items-center rounded-lg border border-foreground/15 text-foreground";
14
-
15
- const shortcutFillClass: Readonly<Record<string, string>> = {
16
- agent: "bg-glass-agent",
17
- primary: "bg-glass-primary",
18
- script: "bg-glass-script",
19
- app: "bg-glass-app",
20
- };
21
-
22
- export function ActorShortcut({ actor, view, session, open, onToggle, onClose, visible }: {
23
- actor: RunActor;
24
- view: RunView;
25
- session: SessionContext;
26
- open: boolean;
27
- onToggle: () => void;
28
- onClose: () => void;
29
- visible: boolean;
30
- }) {
31
- const staged = useSurfaceEntities(session.session.id).has(`@${actor.handle}`);
32
- const technical = useAccess().can("runs.inspect");
33
- const [visited, setVisited] = useState(false);
34
- const buttonRef = useRef<HTMLButtonElement>(null);
35
- const panelId = useId();
36
- const surface = useSurfaceController();
37
- const close = useCallback((restoreFocus = false) => {
38
- onClose();
39
- if (restoreFocus) buttonRef.current?.focus({ preventScroll: true });
40
- }, [onClose]);
41
-
42
- const inspect = (selection: FlowSelection) => {
43
- if (!surface) throw new Error("The actor shortcut needs the surface controller.");
44
- surface.acceptSelection(selection);
45
- close();
46
- };
47
- const tone = actorTone(view, actor);
48
- const label = actor.kind === "agent" ? `Chat with @${actor.handle}` : `Actor view of @${actor.handle}`;
49
- const type = !technical ? "Agent" : actor.kind === "agent" ? "LLM agent" : "TypeScript actor";
50
-
51
- return <div className="flex items-stretch [&[hidden]]:hidden" hidden={!visible}>
52
- <ToolbarItem as="button" aria-controls={visited ? panelId : undefined} aria-expanded={open} aria-haspopup="dialog"
53
- aria-label={`${actor.displayName || actor.handle}. ${type}: open ${label}`} className="font-bold"
54
- data-tone={tone} data-staged={staged || undefined} onClick={() => { setVisited(true); onToggle(); }}
55
- draggable={technical} onDragStart={(event) => {
56
- if (!technical) { event.preventDefault(); return; }
57
- event.dataTransfer.setData(SURFACE_TILE_DRAG_TYPE, `@${actor.handle}`);
58
- event.dataTransfer.effectAllowed = "move";
59
- close();
60
- }}
61
- ref={buttonRef} title={`${type}: open ${label}${technical ? ". Drag onto a tile to dock." : ""}`} type="button">
62
- <span className={cn(shortcutIconClass, staged && shortcutFillClass[tone])}>{actor.kind === "agent" ? <UsersIcon size={20} /> : <CodeIcon size={20} />}</span>
63
- <ToolbarCopy><ToolbarLabel>@{actor.handle}</ToolbarLabel><ToolbarText>{actor.displayName && actor.displayName !== actor.handle ? actor.displayName : type}</ToolbarText></ToolbarCopy>
64
- </ToolbarItem>
65
- {visited && <ActorPopout open={open} id={panelId} label={label} closeLabel={`Close ${label}`}
66
- buttonRef={buttonRef} keepMounted onClose={close} width={actor.kind === "agent" ? 784 : 560} height={640} role="dialog">
67
- <div className="min-h-0 min-w-0 flex-1 overflow-auto overscroll-contain">
68
- <FlowInspector actorConversations={session.actorConversations} canGoBack={false} chatDisplay="popout" composerVisible
69
- conversationError={session.conversationError} onBack={() => {}} onNavigate={inspect}
70
- primaryMessages={session.messages} primaryRunning={session.running} selection={{ type: "actor", id: actor.id }} view={view} />
71
- </div>
72
- </ActorPopout>}
73
- </div>;
74
- }
@@ -1,130 +0,0 @@
1
- import { useAccess } from "@ragents/web/AccessContext";
2
- import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
3
- import type { SessionContext, SessionHeaderContext } from "@ragents/web/PluginRegistry";
4
- import { UsersIcon } from "lucide-react";
5
- import { Input } from "@ragents/web/ui";
6
- import { ToolbarCopy, ToolbarItem, ToolbarLabel, ToolbarText } from "@ragents/web/Toolbar";
7
- import { actorVisibleOnSurface, type SurfaceViewPreferences } from "./surface-view-settings";
8
- import { chatPrimaryId, runViewFrom, type RunActor } from "@ragents/web/run-view";
9
- import { ActorPopout } from "./ActorPopout";
10
- import { ActorShortcut } from "./ActorShortcut";
11
- import { ACTOR_HEADER_MODES, ACTOR_HEADER_MODE_LABELS, actorOnStage, actorVisibleInHeader, saveActorHeaderMode, useActorHeaderMode, type ActorHeaderMode } from "./actor-header-settings";
12
- import { useSurfaceEntities } from "./surface-entities";
13
-
14
- export interface ActorSurfaceControlsProps {
15
- session: SessionContext;
16
- appActorIds: ReadonlySet<string>;
17
- preferences: SurfaceViewPreferences;
18
- onPreferencesChange: (preferences: SurfaceViewPreferences) => void;
19
- }
20
-
21
- const nameClass = "min-w-0 max-w-full text-left font-semibold [overflow-wrap:anywhere]";
22
-
23
- const actorType = (actor: RunActor) => actor.kind === "human" ? "User" : actor.kind === "agent" ? "LLM agent" : "TypeScript actor";
24
- const actorStatus = (actor: RunActor) => actor.lifecycle?.kind === "running" ? "Working" : actor.lifecycle?.kind === "stopped" ? "Stopped" : actor.lifecycle?.kind === "idle" ? "Ready" : "";
25
-
26
- export function filterSurfaceActors(actors: readonly RunActor[], appActorIds: ReadonlySet<string>, query: string): readonly RunActor[] {
27
- const words = query.trim().toLocaleLowerCase("en-US").split(/\s+/).filter(Boolean);
28
- return actors.filter((actor) => {
29
- const text = [`@${actor.handle}`, actor.displayName, actor.kind, actorType(actor), actor.lifecycle?.kind, actorStatus(actor), appActorIds.has(actor.id) ? "Mini-app" : ""].join(" ").toLocaleLowerCase("en-US");
30
- return words.every((word) => text.includes(word));
31
- });
32
- }
33
-
34
- export function ActorShortcuts({ session }: SessionHeaderContext) {
35
- const inspect = useAccess().can("runs.inspect");
36
- const [openKey, setOpenKey] = useState<string>();
37
- const close = useCallback(() => setOpenKey(undefined), []);
38
- const mode = useActorHeaderMode(session.session.id);
39
- const stage = useSurfaceEntities(session.session.id);
40
- const view = runViewFrom(session.runView);
41
- const actors = useMemo(() => view?.actors.filter((actor) => actor.kind !== "human" && (inspect || actor.kind === "agent")) ?? [], [inspect, view]);
42
- const primaryId = view ? chatPrimaryId(view) : undefined;
43
- const shown = useCallback((actor: RunActor) => actorVisibleInHeader(actor, mode, actorOnStage(actor, primaryId, stage)), [mode, stage, primaryId]);
44
- useEffect(() => {
45
- if (openKey && !actors.some((actor) => `${session.session.id}:${actor.id}` === openKey && shown(actor))) close();
46
- }, [actors, close, openKey, session.session.id, shown]);
47
- if (!view || actors.length === 0) return null;
48
-
49
- return <nav aria-label="Actors of the run" className="flex min-w-0 flex-none items-stretch">
50
- {actors.map((actor) => {
51
- const key = `${session.session.id}:${actor.id}`;
52
- return <ActorShortcut actor={actor} key={key} onClose={close}
53
- onToggle={() => setOpenKey((current) => current === key ? undefined : key)}
54
- open={openKey === key} session={session} view={view} visible={shown(actor)} />;
55
- })}
56
- </nav>;
57
- }
58
-
59
- const PUBLIC_ACTOR_HEADER_MODES: readonly ActorHeaderMode[] = ["active", "visible", "all"];
60
-
61
- export function ActorHeaderModeControl({ runId }: { runId: string }) {
62
- const inspect = useAccess().can("runs.inspect");
63
- const mode = useActorHeaderMode(runId);
64
- const modes = inspect ? ACTOR_HEADER_MODES : PUBLIC_ACTOR_HEADER_MODES;
65
- const next = modes[(modes.indexOf(mode) + 1) % modes.length];
66
- const label = `Actor display: ${ACTOR_HEADER_MODE_LABELS[mode]}. Switch to ${ACTOR_HEADER_MODE_LABELS[next]}`;
67
- return <ToolbarItem as="button" aria-label={label} className="w-[120px] max-w-[120px] min-w-[120px] flex-[0_0_120px]" onClick={() => saveActorHeaderMode(runId, next)} title={label} type="button">
68
- <ToolbarCopy><ToolbarLabel>Display</ToolbarLabel><ToolbarText>{ACTOR_HEADER_MODE_LABELS[mode]}</ToolbarText></ToolbarCopy>
69
- </ToolbarItem>;
70
- }
71
-
72
- export function ActorSurfaceControls({ session, appActorIds, preferences, onPreferencesChange }: ActorSurfaceControlsProps) {
73
- const [open, setOpen] = useState(false);
74
- const [query, setQuery] = useState("");
75
- const buttonRef = useRef<HTMLButtonElement>(null);
76
- const panelId = useId();
77
- const runView = runViewFrom(session.runView);
78
- const actors = runView?.actors;
79
- const filtered = useMemo(() => filterSurfaceActors(actors ?? [], appActorIds, query), [actors, appActorIds, query]);
80
- const close = useCallback((restoreFocus = false) => {
81
- setOpen(false);
82
- if (restoreFocus) buttonRef.current?.focus({ preventScroll: true });
83
- }, []);
84
-
85
- useEffect(() => { close(); setQuery(""); }, [session.session.id, close]);
86
-
87
- const label = `Actors \u00b7 ${actors?.length ?? 0}`;
88
- return <div className="flex h-full min-w-0 flex-none items-stretch">
89
- <ToolbarItem as="button" ref={buttonRef} aria-controls={open ? panelId : undefined} aria-expanded={open}
90
- aria-label={label} title={label} onClick={() => setOpen((value) => !value)} type="button">
91
- <UsersIcon size={20} /><ToolbarText>Actors</ToolbarText>
92
- </ToolbarItem>
93
- {open && <ActorPopout open id={panelId} label="Actors of the run" closeLabel="Close actor list"
94
- buttonRef={buttonRef} onClose={close} width={460} height={460}
95
- placement="bottom" focusInput role="region">
96
- <div className="flex flex-none items-center gap-2.5 p-2.5">
97
- <Input className="flex-1" aria-label="Search actors" onChange={(event) => setQuery(event.target.value)} placeholder="Search handle, type or status ..." type="search" value={query} />
98
- <span className="flex-none whitespace-nowrap text-[0.68rem] text-muted-foreground" role="status">{filtered.length} of {actors?.length ?? 0}</span>
99
- </div>
100
- <ActorSurfaceList actors={filtered} appActorIds={appActorIds} primaryActorId={runView?.primaryActorId} onPreferencesChange={onPreferencesChange} preferences={preferences} />
101
- <p className="flex-none border-t border-border-soft px-2.5 py-2.5 text-[0.68rem] text-muted-foreground">Personal view of this run</p>
102
- </ActorPopout>}
103
- </div>;
104
- }
105
-
106
- export function ActorSurfaceList({ actors, appActorIds, primaryActorId, onPreferencesChange, preferences }: {
107
- actors: readonly RunActor[];
108
- appActorIds: ReadonlySet<string>;
109
- primaryActorId?: string | null;
110
- onPreferencesChange: (preferences: SurfaceViewPreferences) => void;
111
- preferences: SurfaceViewPreferences;
112
- }) {
113
- return <div className="min-h-0 flex-1 overflow-auto overscroll-contain px-2.5">
114
- {actors.length === 0 ? <p className="text-muted-foreground">No matching actors.</p> : <ul className="list-none">{actors.map((actor) => <li className="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-2.5 border-b border-border-soft py-1.5" key={actor.id}>
115
- <div className="grid min-w-0 gap-0.5 leading-[1.3]">
116
- <div className="flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-0.5">
117
- <span className={nameClass}>@{actor.handle}</span>
118
- {actor.displayName !== actor.handle && <span className="min-w-0 max-w-full text-[0.7rem] [overflow-wrap:anywhere]">{actor.displayName}</span>}
119
- </div>
120
- <span className="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-[0.66rem] text-muted-foreground">
121
- <span>{actorType(actor)}</span>{actorStatus(actor) && <span>{actorStatus(actor)}</span>}{appActorIds.has(actor.id) && <span className="text-primary">Mini-app</span>}
122
- </span>
123
- </div>
124
- {actor.kind !== "human" && <label className="flex cursor-pointer items-center gap-[5px] whitespace-nowrap text-[0.68rem]">
125
- <input aria-label={`Show @${actor.handle} on the surface`} checked={actorVisibleOnSurface(actor, appActorIds, preferences, primaryActorId)} onChange={(event) => onPreferencesChange({ ...preferences, actorVisibility: { ...preferences.actorVisibility, [actor.id]: event.target.checked } })} type="checkbox" />
126
- <span>Surface</span>
127
- </label>}
128
- </li>)}</ul>}
129
- </div>;
130
- }
@@ -1,23 +0,0 @@
1
- import type { CardSectionContribution, EntityReference, SessionContext, SessionNavigation } from "@ragents/web/PluginRegistry";
2
- import { ActorChatPreview } from "./ActorChatPreview";
3
- import { cardSectionsClass } from "./constants";
4
- import type { RunActor, RunView } from "@ragents/web/run-view";
5
-
6
- export function ActorTile({ actor, view, cardSections, chatInput, session, navigation, onSelect }: {
7
- actor: RunActor;
8
- view: RunView;
9
- cardSections: readonly CardSectionContribution[];
10
- chatInput?: boolean;
11
- session: SessionContext;
12
- navigation: SessionNavigation;
13
- onSelect: (selection: EntityReference) => void;
14
- }) {
15
- return <div className="flex flex-col overflow-hidden has-[>[data-slot=card-sections]:not(:empty)]:min-h-[260px] has-[[data-chat=actions]:not(:empty)]:min-h-[260px]">
16
- <ActorChatPreview actor={actor} view={view} chatInput={chatInput} running={actor.lifecycle?.kind === "running"}
17
- conversation={session.actorConversations?.[actor.id]} historyError={session.conversationError}
18
- onNavigate={onSelect} primaryMessages={actor.id === view.primaryActorId ? session.messages : undefined} />
19
- <div className={`${cardSectionsClass} max-h-[45%] flex-[0_1_auto] overflow-auto overscroll-contain`} data-slot="card-sections">
20
- {cardSections.map(({ id, Section }) => <Section actor={actor} key={id} navigation={navigation} session={session} />)}
21
- </div>
22
- </div>;
23
- }