@schlenkr/ragents 0.1.0 → 0.1.1
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.
- package/apps/server/src/plugin-support/language-server/contract.ts +6 -1
- package/apps/server/src/plugin-support/language-server/tools.ts +29 -4
- package/apps/server/src/plugin-support/run-scripts.ts +4 -1
- package/apps/server/src/plugin-support/start-entries-contract.ts +4 -3
- package/apps/server/src/plugin-support/workspace-sandbox-host.ts +2 -0
- package/apps/web/src/App.tsx +0 -10
- package/apps/web/src/PluginChat.tsx +8 -8
- package/apps/web/src/PluginRegistry.tsx +0 -12
- package/apps/web/src/StartSurface.tsx +2 -2
- package/apps/web/src/ThemeSettings.tsx +1 -1
- package/apps/web/src/column/ColumnApp.tsx +83 -7
- package/apps/web/src/column/clipboard.ts +64 -0
- package/apps/web/src/column/host-contract.ts +10 -0
- package/apps/web/src/column.tsx +2 -0
- package/apps/web/src/panel/LoginForm.tsx +9 -3
- package/apps/web/src/panel/OverviewPage.tsx +187 -62
- package/apps/web/src/panel/PanelDialogs.tsx +115 -0
- package/apps/web/src/panel/PanelPage.tsx +1 -1
- package/apps/web/src/panel/SettingsPage.tsx +75 -79
- package/apps/web/src/panel/contract.ts +17 -9
- package/apps/web/src/panel/target-state.ts +27 -2
- package/apps/web/src/ui/theme.css +4 -0
- package/package.json +2 -2
- package/packages/ragents/src/plugin-host.ts +5 -2
- package/packages/ragents/src/plugin-types.ts +3 -2
- package/packages/workspace-executor/src/context.ts +4 -0
- package/packages/workspace-executor/src/executor.ts +9 -2
- package/packages/workspace-executor/src/index.ts +2 -0
- package/packages/workspace-executor/src/language-server/adapters/fsharp.ts +1 -0
- package/packages/workspace-executor/src/language-server/adapters/roslyn.ts +1 -0
- package/packages/workspace-executor/src/language-server/adapters/typescript.ts +34 -6
- package/packages/workspace-executor/src/language-server/contract.ts +7 -3
- package/packages/workspace-executor/src/language-server/host.ts +275 -138
- package/plugins/ragents.actor-programs/contract.ts +1 -1
- package/plugins/ragents.actor-programs/prompt.hbs +4 -4
- package/plugins/ragents.actor-programs/server/app-project.ts +0 -2
- package/plugins/ragents.actor-programs/server/runtime.ts +1 -1
- package/plugins/ragents.actor-programs/web/AppsPanel.tsx +3 -5
- package/plugins/ragents.actor-programs/web/EmbeddedApps.tsx +7 -39
- package/plugins/ragents.actor-programs/web/api.ts +1 -10
- package/plugins/ragents.lsp-roslyn/web/language-server-plugin.tsx +73 -44
- package/plugins/ragents.orchestration/canvas.hbs +28 -44
- package/plugins/ragents.orchestration/contract.ts +17 -287
- package/plugins/ragents.orchestration/server/canvas-tool.ts +18 -123
- package/plugins/ragents.orchestration/tiled-layout.ts +12 -4
- package/plugins/ragents.orchestration/web/ActorCanvasControls.tsx +23 -47
- package/plugins/ragents.orchestration/web/ActorChatPreview.tsx +3 -9
- package/plugins/ragents.orchestration/web/ActorShortcut.tsx +3 -5
- package/plugins/ragents.orchestration/web/ActorTile.tsx +3 -2
- package/plugins/ragents.orchestration/web/CanvasTiles.tsx +3 -3
- package/plugins/ragents.orchestration/web/Center.tsx +28 -463
- package/plugins/ragents.orchestration/web/MaterialBody.tsx +6 -87
- package/plugins/ragents.orchestration/web/TiledCanvas.tsx +2 -3
- package/plugins/ragents.orchestration/web/canvas-view-settings.ts +7 -13
- package/plugins/ragents.orchestration/web/column/RunColumn.tsx +44 -19
- package/plugins/ragents.orchestration/web/column/column-state.ts +13 -4
- package/plugins/ragents.orchestration/web/constants.ts +0 -2
- package/plugins/ragents.orchestration/web/index.tsx +0 -6
- package/plugins/ragents.orchestration/web/tile-docking.ts +7 -1
- package/plugins/ragents.orchestration/web/tiled-view-settings.ts +7 -9
- package/plugins/ragents.overseer/server/coordinator-prompt.ts +1 -1
- package/plugins/ragents.reference/examples.ts +1 -2
- package/plugins/ragents.reference/run-scripts/balcony-wizard/RUN.md +1 -1
- package/plugins/ragents.reference/run-scripts/balcony-wizard/actors/balcony-app/package.json +1 -1
- package/plugins/ragents.reference/run-scripts/balcony-wizard/src/server.ts +1 -1
- package/plugins/ragents.reference/run-scripts/balcony-wizard/tests/program.test.ts +2 -2
- package/plugins/ragents.reference/run-scripts/conversation-circle/RUN.md +3 -4
- package/plugins/ragents.reference/run-scripts/conversation-circle/src/server.ts +9 -15
- package/plugins/ragents.reference/run-scripts/conversation-circle/tests/program.test.ts +5 -3
- package/plugins/ragents.reference/run-scripts/learning-afternoon/package.json +1 -1
- package/plugins/ragents.reference/run-scripts/learning-afternoon/src/server.ts +14 -4
- package/plugins/ragents.reference/run-scripts/word-game/src/server.ts +1 -1
- package/plugins/ragents.reference/skills/210-moderator-handover/SKILL.md +1 -1
- package/plugins/ragents.reference/walkthroughs.ts +14 -14
- package/plugins/ragents.workspace/client/workspace-client.ts +4 -1
- package/scripts/package/publish-package.ts +4 -5
- package/scripts/workspace-client/run-workspace-client.ts +3 -1
- package/apps/web/src/MaterialSettings.tsx +0 -40
- package/apps/web/src/material-settings.ts +0 -104
- package/plugins/ragents.orchestration/web/ActorNet.tsx +0 -607
- package/plugins/ragents.orchestration/web/CardSizeSettings.tsx +0 -42
- package/plugins/ragents.orchestration/web/ScriptActorCard.tsx +0 -56
- package/plugins/ragents.orchestration/web/ZoomSettings.tsx +0 -38
- package/plugins/ragents.orchestration/web/canvas-layout-storage.ts +0 -39
- package/plugins/ragents.orchestration/web/canvas-layout.ts +0 -326
- package/plugins/ragents.orchestration/web/canvas-scene.ts +0 -311
- package/plugins/ragents.orchestration/web/card-size-settings.ts +0 -36
- package/plugins/ragents.orchestration/web/keyboard-navigation.ts +0 -55
- package/plugins/ragents.orchestration/web/material-layout.ts +0 -14
- package/plugins/ragents.orchestration/web/useCanvasMeasurements.ts +0 -78
- package/plugins/ragents.orchestration/web/zoom-settings.ts +0 -28
- package/plugins/ragents.reference/skills/120-canvas-conversation-circle/SKILL.md +0 -12
- package/plugins/ragents.reference/skills/130-canvas-pipeline-with-approval/SKILL.md +0 -12
- package/plugins/ragents.reference/skills/140-canvas-team-tree-and-topic-grid/SKILL.md +0 -12
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { memo, useId, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
-
import { MATERIAL_DEPTH_PER_STEP } from "@aicontainer/web/material-settings";
|
|
3
1
|
import { cn } from "@aicontainer/web/ui";
|
|
4
2
|
|
|
5
3
|
export type MaterialSurface = "agent" | "primary" | "script" | "app";
|
|
6
4
|
|
|
7
|
-
/** Schichtwerk: matte Front
|
|
8
|
-
const materialBaseClass = "relative isolate rounded-panel border-[1.5px] border-(--material-edge) bg-(--material-face) [
|
|
5
|
+
/** Schichtwerk: matte Front mit geraden Kanten. */
|
|
6
|
+
const materialBaseClass = "relative isolate rounded-panel border-[1.5px] border-(--material-edge) bg-(--material-face) [--material-edge:#514657] dark:[--material-edge:#b7a8bd]";
|
|
9
7
|
|
|
10
8
|
const materialSurfaceClass: Readonly<Record<MaterialSurface, string>> = {
|
|
11
|
-
agent: "[--material-face:var(--color-glass-agent)]
|
|
12
|
-
primary: "[--material-face:var(--color-glass-primary)]
|
|
13
|
-
script: "[--material-face:var(--color-glass-script)]
|
|
14
|
-
app: "[--material-face:var(--color-glass-app)]
|
|
9
|
+
agent: "[--material-face:var(--color-glass-agent)]",
|
|
10
|
+
primary: "[--material-face:var(--color-glass-primary)]",
|
|
11
|
+
script: "[--material-face:var(--color-glass-script)]",
|
|
12
|
+
app: "[--material-face:var(--color-glass-app)]",
|
|
15
13
|
};
|
|
16
14
|
|
|
17
15
|
export const materialCardClass = (surface: MaterialSurface, className?: string) =>
|
|
@@ -23,82 +21,3 @@ export const materialHeadClass = "relative flex items-center gap-2 rounded-t-[15
|
|
|
23
21
|
export const materialTitleClass = "min-w-0 truncate p-0 text-[17px] font-[730] leading-[1.3] tracking-[-.5px]";
|
|
24
22
|
|
|
25
23
|
export const materialIconClass = "grid size-8 flex-none place-items-center rounded-[11px] border border-[#5146578a] bg-white/9 text-foreground shadow-glass-icon";
|
|
26
|
-
|
|
27
|
-
export const materialRuleClass = "border-[#51465738]";
|
|
28
|
-
|
|
29
|
-
export function materialExtrusion(steps: number): string {
|
|
30
|
-
if (steps === 0) return "none";
|
|
31
|
-
const depth = steps * MATERIAL_DEPTH_PER_STEP;
|
|
32
|
-
const layers: string[] = [];
|
|
33
|
-
for (let i = .5; i <= depth; i += .5) {
|
|
34
|
-
const band = Math.floor((i - .5) / MATERIAL_DEPTH_PER_STEP);
|
|
35
|
-
layers.push(`${i * .5}px ${-i * .7}px 0 hsl(var(--material-tone) var(--material-saturation) calc(var(--material-lightness) - ${8 - band * 3}%))`);
|
|
36
|
-
}
|
|
37
|
-
return layers.join(",");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function outlineOf(width: number, height: number, dx: number, dy: number): string {
|
|
41
|
-
const radius = Math.min(17, (width - 1) / 2, (height - 1) / 2);
|
|
42
|
-
const points = [[radius, radius], [radius + dx, radius + dy], [width - radius + dx, radius + dy],
|
|
43
|
-
[width - radius + dx, height - radius + dy], [width - radius, height - radius], [radius, height - radius]];
|
|
44
|
-
const normals = points.map((point, index) => {
|
|
45
|
-
const next = points[(index + 1) % points.length]!;
|
|
46
|
-
const x = next[0]! - point[0]!, y = next[1]! - point[1]!, length = Math.hypot(x, y);
|
|
47
|
-
return [y / length, -x / length];
|
|
48
|
-
});
|
|
49
|
-
return points.map((point, index) => {
|
|
50
|
-
const before = normals[(index + points.length - 1) % points.length]!, after = normals[index]!;
|
|
51
|
-
const start = [point[0]! + radius * before[0]!, point[1]! + radius * before[1]!];
|
|
52
|
-
const end = [point[0]! + radius * after[0]!, point[1]! + radius * after[1]!];
|
|
53
|
-
return `${index ? "L" : "M"}${start.join(" ")}A${radius} ${radius} 0 0 1 ${end.join(" ")}`;
|
|
54
|
-
}).join("") + "Z";
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const depthClass = "pointer-events-none absolute -top-[1.5px] -left-[1.5px] z-2 h-[calc(100%+3px)] w-[calc(100%+3px)] flex-none overflow-visible";
|
|
58
|
-
|
|
59
|
-
export const MaterialBody = memo(function MaterialBody({ steps }: { steps: number }) {
|
|
60
|
-
const depth = steps * MATERIAL_DEPTH_PER_STEP;
|
|
61
|
-
const ref = useRef<SVGSVGElement>(null);
|
|
62
|
-
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
63
|
-
const id = useId().replaceAll(":", "");
|
|
64
|
-
useLayoutEffect(() => {
|
|
65
|
-
const parent = ref.current?.parentElement;
|
|
66
|
-
if (!parent) return;
|
|
67
|
-
parent.style.setProperty("--material-extrusion", materialExtrusion(steps));
|
|
68
|
-
return () => { parent.style.removeProperty("--material-extrusion"); };
|
|
69
|
-
}, [steps]);
|
|
70
|
-
useLayoutEffect(() => {
|
|
71
|
-
const parent = ref.current?.parentElement;
|
|
72
|
-
if (!parent || steps === 0) return;
|
|
73
|
-
const measure = () => setSize((previous) => {
|
|
74
|
-
const width = parent.offsetWidth, height = parent.offsetHeight;
|
|
75
|
-
return width === previous.width && height === previous.height ? previous : { width, height };
|
|
76
|
-
});
|
|
77
|
-
measure();
|
|
78
|
-
const observer = new ResizeObserver(measure);
|
|
79
|
-
observer.observe(parent);
|
|
80
|
-
return () => observer.disconnect();
|
|
81
|
-
}, [steps]);
|
|
82
|
-
const { width, height } = size, dx = depth * .5, dy = -depth * .7;
|
|
83
|
-
if (steps === 0 || width < 2 || height < 2) return <svg aria-hidden="true" className={depthClass} ref={ref} />;
|
|
84
|
-
const outline = outlineOf(width, height, dx, dy), length = Math.hypot(dx, dy);
|
|
85
|
-
const normalX = -dy / length, normalY = dx / length, radius = Math.min(17, (width - 1) / 2, (height - 1) / 2);
|
|
86
|
-
const centerX = width - radius + radius / Math.sqrt(2), centerY = radius - radius / Math.sqrt(2);
|
|
87
|
-
return <svg aria-hidden="true" className={depthClass} ref={ref} viewBox={`0 0 ${width} ${height}`}>
|
|
88
|
-
<defs>
|
|
89
|
-
<mask id={`${id}-face`} maskUnits="userSpaceOnUse" x={-3} y={dy - 3} width={width + dx + 6} height={height - dy + 6}>
|
|
90
|
-
<rect x={-3} y={dy - 3} width={width + dx + 6} height={height - dy + 6} fill="white" stroke="none" />
|
|
91
|
-
<rect width={width} height={height} rx={radius} fill="black" stroke="black" strokeWidth={1} />
|
|
92
|
-
</mask>
|
|
93
|
-
<linearGradient id={`${id}-corner`} gradientUnits="userSpaceOnUse"
|
|
94
|
-
x1={centerX - normalX * 24} y1={centerY - normalY * 24} x2={centerX + normalX * 24} y2={centerY + normalY * 24}>
|
|
95
|
-
{[[0, 0], [.15, 0], [.15, .12], [.35, .12], [.35, .28], [.65, .28], [.65, .12], [.85, .12], [.85, 0], [1, 0]]
|
|
96
|
-
.map(([offset, opacity], index) => <stop key={index} offset={offset} stopColor="black" stopOpacity={opacity} />)}
|
|
97
|
-
</linearGradient>
|
|
98
|
-
</defs>
|
|
99
|
-
<g mask={`url(#${id}-face)`}>
|
|
100
|
-
<path d={outline} fill={`url(#${id}-corner)`} stroke="none" />
|
|
101
|
-
<path d={outline} fill="none" stroke="var(--material-edge)" strokeWidth={1.5} vectorEffect="non-scaling-stroke" />
|
|
102
|
-
</g>
|
|
103
|
-
</svg>;
|
|
104
|
-
});
|
|
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, typ
|
|
|
2
2
|
import { ArrowDown, ArrowLeft, ArrowRight, ArrowUp, CodeXml, LayoutGrid, Sparkles, GripVertical, X } from "lucide-react";
|
|
3
3
|
import { canvasTileNodeOf, type CanvasTileNode } from "../tiled-layout";
|
|
4
4
|
import { CANVAS_TILE_DRAG_TYPE, dockTile, removeTile, resizeTile, tileEntities, tileGeometry, tileMinimum, type TileDivider, type TileDockSide, type TileRect } from "./tile-docking";
|
|
5
|
-
import {
|
|
5
|
+
import { materialCardClass, materialHeadClass, materialIconClass, materialTitleClass } from "./MaterialBody";
|
|
6
6
|
|
|
7
7
|
export interface CanvasTileItem { entity: string; title: string; content: ReactNode; surface?: "agent" | "primary" | "script" | "app" }
|
|
8
8
|
interface TiledCanvasProps {
|
|
@@ -211,7 +211,6 @@ export function TiledCanvas({ root, onChange, items, canArrange, selectedEntity,
|
|
|
211
211
|
"absolute z-0 flex min-h-0 min-w-0 flex-col overflow-visible")} data-tile-entity={entity} data-surface="material"
|
|
212
212
|
style={{ ...positioned(bounds), display: compact && entity !== active ? "none" : undefined }}
|
|
213
213
|
onFocusCapture={() => { if (active !== entity) setSelected(entity); }}>
|
|
214
|
-
<MaterialBody steps={0} />
|
|
215
214
|
<div className="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden rounded-[15.5px]">
|
|
216
215
|
<header className={`${materialHeadClass} flex-[0_0_49px] min-w-0 select-none border-b border-[#51465738] text-foreground [&[draggable=true]]:cursor-grab`}
|
|
217
216
|
draggable={canArrange} onDragStart={(event) => {
|
|
@@ -275,7 +274,7 @@ export function TiledCanvas({ root, onChange, items, canArrange, selectedEntity,
|
|
|
275
274
|
{canArrange && dragEntity !== null && root && <div className="pointer-events-none absolute inset-2 z-6">{targets(null)}</div>}
|
|
276
275
|
{canArrange && dragEntity !== null && preview && <div className={materialCardClass(itemMap.get(dragEntity)?.surface ?? (dragEntity.startsWith("app:") ? "app" : "agent"),
|
|
277
276
|
"pointer-events-none absolute z-5 opacity-80")}
|
|
278
|
-
style={positioned(materialFaceRect(preview))}><
|
|
277
|
+
style={positioned(materialFaceRect(preview))}><div className="absolute inset-0 rounded-[15.5px] border-2 border-primary bg-primary/22" /></div>}
|
|
279
278
|
</div>
|
|
280
279
|
</div>;
|
|
281
280
|
}
|
|
@@ -2,26 +2,20 @@ import { createLocalStorageSetting } from "@aicontainer/web/lib/local-storage-se
|
|
|
2
2
|
import type { RunActor } from "./run-view";
|
|
3
3
|
|
|
4
4
|
export interface CanvasViewPreferences {
|
|
5
|
-
showAppActors: boolean;
|
|
6
|
-
showConnections: boolean;
|
|
7
5
|
actorVisibility: Readonly<Record<string, boolean>>;
|
|
8
6
|
}
|
|
9
7
|
|
|
10
|
-
export const DEFAULT_CANVAS_VIEW_PREFERENCES: CanvasViewPreferences = Object.freeze({
|
|
11
|
-
|
|
12
|
-
});
|
|
13
|
-
export const canvasViewStorageKey = (runId: string) => `ragents.orchestration.canvas-view:${runId}`;
|
|
8
|
+
export const DEFAULT_CANVAS_VIEW_PREFERENCES: CanvasViewPreferences = Object.freeze({ actorVisibility: Object.freeze({}) });
|
|
9
|
+
export const canvasViewStorageKey = (runId: string) => `ragents.orchestration.actor-visibility:${runId}`;
|
|
14
10
|
|
|
15
11
|
export function parseCanvasViewPreferences(raw: string | null): CanvasViewPreferences {
|
|
16
12
|
if (raw === null) return DEFAULT_CANVAS_VIEW_PREFERENCES;
|
|
17
13
|
const value: unknown = JSON.parse(raw);
|
|
18
14
|
if (!value || typeof value !== "object" || Array.isArray(value)
|
|
19
|
-
|| Object.keys(value).some((key) =>
|
|
20
|
-
|| !("showAppActors" in value) || typeof value.showAppActors !== "boolean"
|
|
21
|
-
|| !("showConnections" in value) || typeof value.showConnections !== "boolean"
|
|
15
|
+
|| Object.keys(value).some((key) => key !== "actorVisibility")
|
|
22
16
|
|| !("actorVisibility" in value) || !value.actorVisibility || typeof value.actorVisibility !== "object"
|
|
23
17
|
|| Array.isArray(value.actorVisibility) || Object.values(value.actorVisibility).some((visible) => typeof visible !== "boolean")) {
|
|
24
|
-
throw new Error("Die gespeicherte
|
|
18
|
+
throw new Error("Die gespeicherte Actor-Sichtbarkeit ist ungültig.");
|
|
25
19
|
}
|
|
26
20
|
return value as CanvasViewPreferences;
|
|
27
21
|
}
|
|
@@ -29,12 +23,12 @@ export function parseCanvasViewPreferences(raw: string | null): CanvasViewPrefer
|
|
|
29
23
|
export const actorVisibleOnCanvas = (actor: RunActor, appActorIds: ReadonlySet<string>, preferences: CanvasViewPreferences, primaryActorId?: string | null): boolean => {
|
|
30
24
|
if (actor.kind === "human") return false;
|
|
31
25
|
const override = Object.hasOwn(preferences.actorVisibility, actor.id) ? preferences.actorVisibility[actor.id] : undefined;
|
|
32
|
-
return override ?? (actor.id !== primaryActorId &&
|
|
26
|
+
return override ?? (actor.id !== primaryActorId && !appActorIds.has(actor.id));
|
|
33
27
|
};
|
|
34
28
|
|
|
35
29
|
const setting = createLocalStorageSetting({
|
|
36
|
-
changeEvent: "ragents-
|
|
37
|
-
matchesKey: (key) => key.startsWith("ragents.orchestration.
|
|
30
|
+
changeEvent: "ragents-actor-visibility-change",
|
|
31
|
+
matchesKey: (key) => key.startsWith("ragents.orchestration.actor-visibility:"),
|
|
38
32
|
parse: parseCanvasViewPreferences,
|
|
39
33
|
serialize: JSON.stringify,
|
|
40
34
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type PointerEvent, type ReactNode, type RefObject } from "react";
|
|
2
2
|
import { createPortal } from "react-dom";
|
|
3
|
-
import { ArrowUpRightIcon, LayoutGridIcon, Maximize2Icon, PanelBottomIcon, PanelRightIcon } from "lucide-react";
|
|
3
|
+
import { ArrowUpRightIcon, LayoutGridIcon, Maximize2Icon, MessageSquareIcon, PanelBottomIcon, PanelRightIcon } from "lucide-react";
|
|
4
4
|
import { useAccess } from "@aicontainer/web/AccessContext";
|
|
5
5
|
import { ChatPanel } from "@aicontainer/web/chat/ChatPanel";
|
|
6
6
|
import { runIsWorking } from "@aicontainer/web/chat/chat-target";
|
|
@@ -19,12 +19,9 @@ import { runViewFrom, type RunActor, type RunView } from "../run-view";
|
|
|
19
19
|
import { AddresseeControl } from "./AddresseeControl";
|
|
20
20
|
import { columnActors, elementNeedsAttention, partitionColumnActors } from "./column-actors";
|
|
21
21
|
import { useColumnSettings } from "./column-settings";
|
|
22
|
-
import { clampChatWidth, saveColumnState, useColumnState, type ColumnState } from "./column-state";
|
|
22
|
+
import { chatLayoutFor, clampChatWidth, saveColumnState, useColumnState, type ChatLayout, type ColumnChatMode, type ColumnState } from "./column-state";
|
|
23
23
|
import { sheetStatus } from "./sheet-status";
|
|
24
24
|
|
|
25
|
-
/** Wie der Chat zur Mini-App liegt: allein, daneben oder als Sheet darüber. */
|
|
26
|
-
type ChatLayout = "full" | "side" | "floating";
|
|
27
|
-
|
|
28
25
|
const SHEET_HEIGHT = "90%";
|
|
29
26
|
const SHEET_BLUR_DELAY = 220;
|
|
30
27
|
|
|
@@ -36,6 +33,12 @@ const chatLayoutClass: Readonly<Record<ChatLayout, string>> = {
|
|
|
36
33
|
floating: "absolute inset-x-2.5 bottom-0 z-20 flex flex-col overflow-hidden rounded-t-2xl border border-b-0 border-transparent bg-transparent transition-[height,box-shadow,background-color,border-color] duration-300 ease-[cubic-bezier(.32,.72,0,1)] data-expanded:border-border data-expanded:bg-card data-expanded:shadow-[0_-16px_60px_#00000080] motion-reduce:transition-none",
|
|
37
34
|
};
|
|
38
35
|
const chatSurfaceClass = "rounded-none border-0 bg-transparent";
|
|
36
|
+
const chatModeButtonClass = "aria-pressed:bg-accent aria-pressed:text-primary";
|
|
37
|
+
const chatModes: readonly { Icon: typeof MessageSquareIcon; label: string; mode: ColumnChatMode; title: string }[] = [
|
|
38
|
+
{ Icon: MessageSquareIcon, label: "Nur Chat", mode: "chat", title: "Nur Chat: der Chat füllt die Spalte, die Mini-App tritt zurück" },
|
|
39
|
+
{ Icon: PanelBottomIcon, label: "Chat unten", mode: "bottom", title: "Chat unten: das Sheet über der Mini-App" },
|
|
40
|
+
{ Icon: PanelRightIcon, label: "Chat rechts", mode: "side", title: "Chat rechts: neben der Mini-App" },
|
|
41
|
+
];
|
|
39
42
|
|
|
40
43
|
interface ColumnElement {
|
|
41
44
|
Element: CanvasElementContribution["Element"];
|
|
@@ -98,7 +101,7 @@ function RunColumn({ canvasElements, cardSections, navigation, renderChat, sessi
|
|
|
98
101
|
}, [actors, selectActor]);
|
|
99
102
|
const primarySelected = selectedActor === undefined || selectedActor.id === primaryId;
|
|
100
103
|
const narrow = width < settings.sideWidth;
|
|
101
|
-
const layout
|
|
104
|
+
const layout = chatLayoutFor({ chat: stored.chat, hasElement: selectedElement !== undefined, narrow });
|
|
102
105
|
const sheet = useSheet(layout === "floating", sheetRef, addresseeOpen, settings.openDelay);
|
|
103
106
|
const peek = useSheetPeek(layout === "floating", sheetRef, grabRef, statusRef, selectedActor?.id);
|
|
104
107
|
const scrollerRef = useRef<HTMLDivElement | null>(null);
|
|
@@ -129,22 +132,19 @@ function RunColumn({ canvasElements, cardSections, navigation, renderChat, sessi
|
|
|
129
132
|
technical={inspect}
|
|
130
133
|
view={view}
|
|
131
134
|
/>;
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
title={narrow ? `Chat bleibt unten, solange das Panel schmaler als ${settings.sideWidth} Pixel ist` : stored.chat === "side" ? "Chat nach unten legen" : "Chat nach rechts legen"}
|
|
139
|
-
variant="ghost"
|
|
140
|
-
>{!narrow && stored.chat === "side" ? <PanelBottomIcon /> : <PanelRightIcon />}</Button>, toolbarContainer);
|
|
135
|
+
const chatModeSwitch = selectedElement && toolbarContainer && createPortal(<ChatModeSwitch
|
|
136
|
+
narrow={narrow}
|
|
137
|
+
onChange={(chat) => save({ chat })}
|
|
138
|
+
selected={stored.chat}
|
|
139
|
+
sideWidth={settings.sideWidth}
|
|
140
|
+
/>, toolbarContainer);
|
|
141
141
|
const chat = primarySelected || !view || !selectedActor
|
|
142
142
|
? renderChat({ chatElementClassName: chatSurfaceClass, chatScrollerRef: (element) => { scrollerRef.current = element; }, toolbarLeft: addressee })
|
|
143
143
|
: <ActorColumnChat actor={selectedActor} cardSections={cardSections} navigation={navigation} onNavigate={navigate} session={session} toolbarLeft={addressee} view={view} />;
|
|
144
144
|
|
|
145
145
|
return <div className="flex min-h-0 min-w-0 flex-1 flex-col" data-column="run" ref={rootRef}>
|
|
146
|
-
{
|
|
147
|
-
{elements.length > 1 && <nav aria-label="Mini-Apps des Runs" className={chipRowClass}>
|
|
146
|
+
{chatModeSwitch}
|
|
147
|
+
{layout !== "full" && elements.length > 1 && <nav aria-label="Mini-Apps des Runs" className={chipRowClass}>
|
|
148
148
|
{elements.map(({ definition }) => <ElementChip
|
|
149
149
|
centered={centered.has(definition.id)}
|
|
150
150
|
definition={definition}
|
|
@@ -158,7 +158,7 @@ function RunColumn({ canvasElements, cardSections, navigation, renderChat, sessi
|
|
|
158
158
|
/>)}
|
|
159
159
|
</nav>}
|
|
160
160
|
<div className={cn("relative min-h-0 min-w-0 flex-1", layout === "side" ? "grid" : "flex flex-col")} data-chat-layout={layout} style={layout === "side" ? { gridTemplateColumns: `minmax(0,1fr) auto ${chatWidth}px` } : undefined}>
|
|
161
|
-
{selectedElement && <ColumnStage centered={centered.has(selectedElement.definition.id)} element={selectedElement} navigation={navigation} peek={layout === "floating" ? peek : 0} runId={runId} session={session} />}
|
|
161
|
+
{layout !== "full" && selectedElement && <ColumnStage centered={centered.has(selectedElement.definition.id)} element={selectedElement} navigation={navigation} peek={layout === "floating" ? peek : 0} runId={runId} session={session} />}
|
|
162
162
|
{layout === "side" && <Sash
|
|
163
163
|
onChange={(next) => setChatWidth(clampChatWidth(next, availableWidth()))}
|
|
164
164
|
onCommit={(next) => save({ chatWidth: clampChatWidth(next, availableWidth()) })}
|
|
@@ -207,6 +207,31 @@ function RunColumn({ canvasElements, cardSections, navigation, renderChat, sessi
|
|
|
207
207
|
</div>;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
/** Die drei Ansichten der Spalte in der Kopfzeile; "Chat rechts" bleibt gesperrt, solange die Spalte zu schmal ist. */
|
|
211
|
+
function ChatModeSwitch({ narrow, onChange, selected, sideWidth }: {
|
|
212
|
+
narrow: boolean;
|
|
213
|
+
onChange: (mode: ColumnChatMode) => void;
|
|
214
|
+
selected: ColumnChatMode;
|
|
215
|
+
sideWidth: number;
|
|
216
|
+
}) {
|
|
217
|
+
return <div aria-label="Ansicht der Spalte" className="flex flex-none items-center gap-0.5 self-center rounded-lg border border-border bg-card p-0.5" role="group">
|
|
218
|
+
{chatModes.map(({ Icon, label, mode, title }) => {
|
|
219
|
+
const blocked = mode === "side" && narrow;
|
|
220
|
+
return <Button
|
|
221
|
+
aria-label={label}
|
|
222
|
+
aria-pressed={selected === mode}
|
|
223
|
+
className={chatModeButtonClass}
|
|
224
|
+
disabled={blocked}
|
|
225
|
+
key={mode}
|
|
226
|
+
onClick={() => onChange(mode)}
|
|
227
|
+
size="icon-sm"
|
|
228
|
+
title={blocked ? `Chat bleibt unten, solange das Panel schmaler als ${sideWidth} Pixel ist` : title}
|
|
229
|
+
variant="ghost"
|
|
230
|
+
><Icon /></Button>;
|
|
231
|
+
})}
|
|
232
|
+
</div>;
|
|
233
|
+
}
|
|
234
|
+
|
|
210
235
|
/** Das Sheet: Maus, Fokus oder der Griff schieben es hoch; Escape, die Bühne oder der Griff senken es. Ein offenes Pop-out hält es oben. */
|
|
211
236
|
function useSheet(active: boolean, sheetRef: RefObject<HTMLElement | null>, blocked: boolean, openDelay: number) {
|
|
212
237
|
const [expanded, setExpanded] = useState(false);
|
|
@@ -308,7 +333,7 @@ function ColumnStage({ centered, element, navigation, peek, runId, session }: {
|
|
|
308
333
|
<span>{title} liegt in der Mitte.</span>
|
|
309
334
|
<Button onClick={() => host.returnToColumn(runId, definition.id)} size="sm" variant="outline">Zurück ins Panel</Button>
|
|
310
335
|
</div>
|
|
311
|
-
: <Element definition={definition} navigation={navigation}
|
|
336
|
+
: <Element definition={definition} navigation={navigation} session={session} />}
|
|
312
337
|
</div>
|
|
313
338
|
</section>;
|
|
314
339
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { createLocalStorageSetting } from "@aicontainer/web/lib/local-storage-setting";
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
export type ColumnChatMode = "
|
|
3
|
+
/** Die gewählte Ansicht der Spalte: nur Chat, Chat unten als Sheet oder Chat rechts, sofern die Spalte breit genug ist. */
|
|
4
|
+
export type ColumnChatMode = "chat" | "bottom" | "side";
|
|
5
|
+
|
|
6
|
+
/** Wie der Chat zur Mini-App liegt: allein, daneben oder als Sheet darüber. */
|
|
7
|
+
export type ChatLayout = "full" | "side" | "floating";
|
|
5
8
|
|
|
6
9
|
/** Persönlicher Zustand der Arbeitsspalte je Run: gewählte Mini-App, gewählter Actor, Chat-Lage und Chatbreite daneben. */
|
|
7
10
|
export interface ColumnState {
|
|
@@ -21,9 +24,11 @@ export const DEFAULT_COLUMN_STATE: ColumnState = Object.freeze({ element: null,
|
|
|
21
24
|
export const columnStorageKey = (runId: string) => `ragents.orchestration.column:${runId}`;
|
|
22
25
|
|
|
23
26
|
const isTextOrNull = (value: unknown): value is string | null => value === null || typeof value === "string";
|
|
24
|
-
/** Frühere Zustände kannten `auto`, `floating` und `docked` sowie eine Bühnenhöhe; sie werden auf die
|
|
27
|
+
/** Frühere Zustände kannten `auto`, `floating` und `docked` sowie eine Bühnenhöhe; sie werden auf die heutigen Lagen abgebildet. */
|
|
25
28
|
const chatModeOf = (value: unknown): ColumnChatMode | undefined =>
|
|
26
|
-
value === undefined || value === "side" || value === "auto" ? "side"
|
|
29
|
+
value === undefined || value === "side" || value === "auto" ? "side"
|
|
30
|
+
: value === "chat" ? "chat"
|
|
31
|
+
: value === "bottom" || value === "floating" || value === "docked" ? "bottom" : undefined;
|
|
27
32
|
|
|
28
33
|
export function parseColumnState(raw: string | null): ColumnState {
|
|
29
34
|
if (raw === null) return DEFAULT_COLUMN_STATE;
|
|
@@ -40,6 +45,10 @@ export function parseColumnState(raw: string | null): ColumnState {
|
|
|
40
45
|
return { element: stored.element, actor: stored.actor, chat: chatModeOf(stored.chat) ?? "side", chatWidth: stored.chatWidth ?? CHAT_DEFAULT_WIDTH };
|
|
41
46
|
}
|
|
42
47
|
|
|
48
|
+
/** Ohne Mini-App und in der Ansicht "Nur Chat" füllt der Chat die Spalte; sonst liegt er rechts, wenn Platz ist, und unten, wenn nicht. */
|
|
49
|
+
export const chatLayoutFor = ({ chat, hasElement, narrow }: { chat: ColumnChatMode; hasElement: boolean; narrow: boolean }): ChatLayout =>
|
|
50
|
+
!hasElement || chat === "chat" ? "full" : !narrow && chat === "side" ? "side" : "floating";
|
|
51
|
+
|
|
43
52
|
export const clampChatWidth = (width: number, available: number): number =>
|
|
44
53
|
Math.max(CHAT_MIN_WIDTH, Math.min(Math.round(width), Math.max(CHAT_MIN_WIDTH, available - STAGE_MIN_WIDTH)));
|
|
45
54
|
|
|
@@ -4,7 +4,5 @@ export const ORCHESTRATION_TAB_ID = "ragents.orchestration.inspector";
|
|
|
4
4
|
/** Knopf in der Statusleiste: volle Leistenhöhe, keine Rundung, ruhige Fläche. */
|
|
5
5
|
export const statusControlClass = "flex h-full min-w-statusbar items-center justify-center self-stretch whitespace-nowrap px-2.5 text-[0.7rem] text-foreground hover:bg-primary/12 aria-expanded:bg-primary/12 focus-visible:outline-2 focus-visible:outline-primary focus-visible:-outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed";
|
|
6
6
|
|
|
7
|
-
export const statusOutputClass = "flex h-full min-w-14 items-center justify-center self-stretch whitespace-nowrap px-2.5 font-mono text-[0.7rem] tabular-nums text-muted-foreground";
|
|
8
|
-
|
|
9
7
|
/** Rahmen für die Plugin-Beiträge einer Actor-Karte; ein Beitrag bringt keine eigenen Abstaende mit. */
|
|
10
8
|
export const cardSectionsClass = "grid gap-2.5 border-t border-border-soft px-3 pt-2.5 pb-2.5 empty:hidden";
|
|
@@ -4,11 +4,8 @@ import { FlowInspector as NetworkInspector, type FlowSelection as NetworkSelecti
|
|
|
4
4
|
import { OrchestrationCenter } from "./Center";
|
|
5
5
|
import { OrchestrationColumn } from "./column/RunColumn";
|
|
6
6
|
import { DocumentsSection } from "./DocumentsSection";
|
|
7
|
-
import { CardSizeSettings } from "./CardSizeSettings";
|
|
8
|
-
import { ZoomSettings } from "./ZoomSettings";
|
|
9
7
|
import { ColumnSettings } from "./column/ColumnSettings";
|
|
10
8
|
import { ActorShortcuts } from "./ActorCanvasControls";
|
|
11
|
-
import { MaterialSettings } from "@aicontainer/web/MaterialSettings";
|
|
12
9
|
import { JournalStatus } from "./JournalStatus";
|
|
13
10
|
import { StopRunHeader } from "./StopRunButton";
|
|
14
11
|
import { EXECUTIONS_TAB_ID, ExecutionsPanel, IconExecutions } from "./ExecutionsPanel";
|
|
@@ -149,9 +146,6 @@ export const webPlugin: WebPlugin = {
|
|
|
149
146
|
{ id: "ragents.orchestration.actors", order: 300, placement: "canvas", Header: ActorShortcuts },
|
|
150
147
|
],
|
|
151
148
|
settings: [
|
|
152
|
-
{ id: "ragents.orchestration.material", category: "appearance", label: "Schichtwerk-Material", Settings: MaterialSettings },
|
|
153
|
-
{ id: "ragents.orchestration.card-size", category: "appearance", label: "LLM-Karten", Settings: CardSizeSettings },
|
|
154
|
-
{ id: "ragents.orchestration.zoom", category: "appearance", label: "Canvas-Zoom", Settings: ZoomSettings },
|
|
155
149
|
{ id: "ragents.orchestration.column", category: "appearance", label: "Arbeitsspalte", Settings: ColumnSettings },
|
|
156
150
|
],
|
|
157
151
|
canvas: { Center: OrchestrationCenter, Column: OrchestrationColumn, tabId: ORCHESTRATION_TAB_ID },
|
|
@@ -19,6 +19,12 @@ export function tileEntities(root: CanvasTileNode | null): string[] {
|
|
|
19
19
|
return root === null ? [] : "entity" in root ? [root.entity] : root.children.flatMap(tileEntities);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function tileLeaf(root: CanvasTileNode | null, entity: string): CanvasTileNode | undefined {
|
|
23
|
+
if (root === null) return undefined;
|
|
24
|
+
if ("entity" in root) return root.entity === entity ? root : undefined;
|
|
25
|
+
return tileLeaf(root.children[0], entity) ?? tileLeaf(root.children[1], entity);
|
|
26
|
+
}
|
|
27
|
+
|
|
22
28
|
export function removeTile(root: CanvasTileNode | null, entity: string): CanvasTileNode | null {
|
|
23
29
|
if (root === null || "entity" in root) return root?.entity === entity ? null : root;
|
|
24
30
|
const first = removeTile(root.children[0], entity);
|
|
@@ -31,7 +37,7 @@ export function removeTile(root: CanvasTileNode | null, entity: string): CanvasT
|
|
|
31
37
|
export function dockTile(root: CanvasTileNode | null, entity: string, target: string | null, side: TileDockSide): CanvasTileNode | null {
|
|
32
38
|
if ((root && "entity" in root && root.entity === entity && target === null) || entity === target || (target !== null && !tileEntities(root).includes(target))) return root;
|
|
33
39
|
const remaining = removeTile(root, entity);
|
|
34
|
-
const leaf: CanvasTileNode = { entity };
|
|
40
|
+
const leaf: CanvasTileNode = tileLeaf(root, entity) ?? { entity };
|
|
35
41
|
if (!remaining) return leaf;
|
|
36
42
|
const wrap = (node: CanvasTileNode): CanvasTileNode => ({
|
|
37
43
|
direction: side === "left" || side === "right" ? "horizontal" : "vertical",
|
|
@@ -5,30 +5,28 @@ import { canvasTileEntities, canvasTileNodeOf, type CanvasTileNode } from "../ti
|
|
|
5
5
|
import type { RunView } from "./run-view";
|
|
6
6
|
|
|
7
7
|
export interface CanvasPresentation {
|
|
8
|
-
mode: "free" | "tiled";
|
|
9
8
|
root: CanvasTileNode | null;
|
|
10
9
|
programBasis?: string;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
export const canvasPresentationStorageKey = (runId: string) => `ragents.orchestration.
|
|
12
|
+
export const canvasPresentationStorageKey = (runId: string) => `ragents.orchestration.tile-presentation:${runId}`;
|
|
14
13
|
|
|
15
14
|
export function parseCanvasPresentation(raw: string | null): CanvasPresentation | null {
|
|
16
15
|
if (raw === null || raw === "null") return null;
|
|
17
16
|
const value: unknown = JSON.parse(raw);
|
|
18
17
|
if (!value || typeof value !== "object" || Array.isArray(value)
|
|
19
|
-
|| !("
|
|
20
|
-
|| !("root" in value) || Object.keys(value).some((key) => !["mode", "root", "programBasis"].includes(key))
|
|
18
|
+
|| !("root" in value) || Object.keys(value).some((key) => !["root", "programBasis"].includes(key))
|
|
21
19
|
|| ("programBasis" in value && typeof value.programBasis !== "string")) {
|
|
22
20
|
throw new Error("Die gespeicherte Kachelansicht ist ungültig.");
|
|
23
21
|
}
|
|
24
|
-
return {
|
|
22
|
+
return { root: value.root === null ? null : canvasTileNodeOf(value.root),
|
|
25
23
|
...("programBasis" in value ? { programBasis: value.programBasis as string } : {}),
|
|
26
24
|
};
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
const setting = createLocalStorageSetting({
|
|
30
|
-
changeEvent: "ragents-
|
|
31
|
-
matchesKey: (key) => key.startsWith("ragents.orchestration.
|
|
28
|
+
changeEvent: "ragents-tile-presentation-change",
|
|
29
|
+
matchesKey: (key) => key.startsWith("ragents.orchestration.tile-presentation:"),
|
|
32
30
|
parse: parseCanvasPresentation,
|
|
33
31
|
serialize: JSON.stringify,
|
|
34
32
|
});
|
|
@@ -37,7 +35,7 @@ export const useCanvasPresentationOverride = (runId: string) => setting.useValue
|
|
|
37
35
|
export const saveCanvasPresentation = (runId: string, value: CanvasPresentation | null) =>
|
|
38
36
|
setting.save(canvasPresentationStorageKey(runId), value);
|
|
39
37
|
|
|
40
|
-
export const canvasProgramBasis = (program: CanvasPresentation): string => JSON.stringify({
|
|
38
|
+
export const canvasProgramBasis = (program: CanvasPresentation): string => JSON.stringify({ root: program.root });
|
|
41
39
|
|
|
42
40
|
export function currentCanvasOverride(personal: CanvasPresentation | null, program: CanvasPresentation): CanvasPresentation | null {
|
|
43
41
|
if (!personal) return null;
|
|
@@ -53,7 +51,7 @@ export function useCanvasPresentation(runId: string, view: RunView | undefined)
|
|
|
53
51
|
try { return { layout: stateKey === undefined ? undefined : canvasLayoutOf(JSON.parse(stateKey)) }; }
|
|
54
52
|
catch (error) { return { error: error instanceof Error ? error.message : String(error) }; }
|
|
55
53
|
}, [stateKey]);
|
|
56
|
-
const program = useMemo((): CanvasPresentation => ({
|
|
54
|
+
const program = useMemo((): CanvasPresentation => ({ root: programLayout.layout?.root ?? null }), [programLayout]);
|
|
57
55
|
const programBasis = useMemo(() => canvasProgramBasis(program), [program]);
|
|
58
56
|
const storedLayout = useCanvasPresentationOverride(runId);
|
|
59
57
|
const personalLayout = view && !programLayout.error ? currentCanvasOverride(storedLayout, program) : storedLayout;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const coordinatorPrompt = `Du koordinierst Aufträge in RAgents.
|
|
2
2
|
Neue Runs dürfen mit einem fachlichen Auftrag starten. Der Auftrag beschreibt das gewünschte Ergebnis; der Run-Koordinator entdeckt die Funktionen und wählt seine technische Umsetzung selbst.
|
|
3
3
|
Prüfe bei vorhandenen Runs den Typ des Primary-Actors in der Laufansicht. Ein TypeScript-Actor ist ein Programm mit festem Eingabeprotokoll, kein Chatpartner. Die Nachrichtenroute akzeptiert nur LLM-Actors; verwende bei Programmen die dokumentierte Mini-App oder Funktionen. Eine erfolgreiche Nachrichtenannahme bestätigt nur das Einreihen und beweist keine Ausführung. Behaupte einen Neustart oder eine Erledigung erst bei entsprechendem Ergebnis.
|
|
4
|
-
|
|
4
|
+
Die Arbeitsfläche eines Runs besteht aus Kacheln. Benutzer können eine bestimmte Aufteilung verlangen, etwa Mini-App links und Chat rechts 50:50 oder eine Kachel oben und zwei unten im Verhältnis 2:1. Übernimm diese Wünsche in den Run-Auftrag; der Run setzt sie über context.functions.canvas_layout_replace mit verschachteltem root um. Bei bestehenden Runs gib den Layoutauftrag an den LLM-Koordinator weiter. Persönliche Benutzeranordnungen bleiben vorrangig, bis der Benutzer die Programmanordnung übernimmt.
|
|
5
5
|
Antworte auf Deutsch mit echten Umlauten und den nötigen Einzelheiten. Nenne Läufe mit ihren Titeln. Verwende einfache Bindestriche und normale Anführungszeichen.`;
|
|
6
6
|
|
|
7
7
|
export const preparationPrompt = `${coordinatorPrompt}
|
|
@@ -5,8 +5,7 @@ export const referenceConcepts = [
|
|
|
5
5
|
{ id: "fifo", label: "Input-Warteschlange" },
|
|
6
6
|
{ id: "stop", label: "Actor-Stopp" },
|
|
7
7
|
{ id: "artifacts", label: "Artefakte und Zugriff" },
|
|
8
|
-
{ id: "
|
|
9
|
-
{ id: "canvas-view", label: "Persönliche Canvas-Ansicht", entryKind: "walkthrough" },
|
|
8
|
+
{ id: "tiles", label: "Kachelfläche", entryKind: "walkthrough" },
|
|
10
9
|
{ id: "actor-views", label: "Mini-Apps" },
|
|
11
10
|
{ id: "actor-state", label: "Actor-Zustand" },
|
|
12
11
|
{ id: "llm-actor-view", label: "LLM-Actor mit View" },
|
|
@@ -8,7 +8,7 @@ tags: Run-Scripts, Anwendungsfall, Konzeptdemo, Mini-Apps, LLM-Actor mit View, F
|
|
|
8
8
|
|
|
9
9
|
Das TypeScript-Setup legt einen Balkon-Berater mit dem Modellprofil `standard` und ausdrücklich
|
|
10
10
|
ohne Werkzeuge an. Das mitgelieferte Programm `actors/balcony-app/` bindet eine eigenständige
|
|
11
|
-
|
|
11
|
+
Mini-App an diesen Actor. Die Fläche zeigt allein deren Kachel statt einer Chatkachel; ein Koordinator
|
|
12
12
|
wird für diesen Run nicht angelegt. Der Berater wird zum Primary-Actor und behält seinen eigenen
|
|
13
13
|
Interview-Prompt.
|
|
14
14
|
|
package/plugins/ragents.reference/run-scripts/balcony-wizard/actors/balcony-app/package.json
CHANGED
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
"ragents": {
|
|
6
6
|
"title": "Balkonberatung",
|
|
7
7
|
"description": "Fünf Fragen und eine persönliche Gestaltungsempfehlung in einer eigenen Mini-App.",
|
|
8
|
-
"views": [{ "id": "main", "client": "src/client.tsx"
|
|
8
|
+
"views": [{ "id": "main", "client": "src/client.tsx" }]
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -25,7 +25,7 @@ export default defineActor(contract, {
|
|
|
25
25
|
handle: "balcony-advisor", displayName: "Balkon-Berater", prompt, profile: profile.name, tools: [],
|
|
26
26
|
});
|
|
27
27
|
await context.functions.actor_program_activate({ name: "balcony-app", actor: `@${advisor.handle}` });
|
|
28
|
-
await context.functions.canvas_layout_replace({
|
|
28
|
+
await context.functions.canvas_layout_replace({ root: { entity: `app:@${advisor.handle}/main` } });
|
|
29
29
|
await context.functions.run_configure({ title: "Dein Balkon", primaryActor: `@${advisor.handle}` });
|
|
30
30
|
context.state.replace({ built: true });
|
|
31
31
|
},
|
|
@@ -27,7 +27,7 @@ function setup(options: { missingProfile?: boolean; failActivation?: boolean } =
|
|
|
27
27
|
return { calls, context };
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
test("richtet einen Berater ohne Werkzeuge und seine eigene
|
|
30
|
+
test("richtet einen Berater ohne Werkzeuge und seine eigene Mini-App als einzige Kachel ein", async () => {
|
|
31
31
|
const { calls, context } = setup();
|
|
32
32
|
await program.onInput!(input, context);
|
|
33
33
|
assert.deepEqual(calls.map((call) => call.name), ["model_list", "agent_spawn", "actor_program_activate", "canvas_layout_replace", "run_configure"]);
|
|
@@ -36,7 +36,7 @@ test("richtet einen Berater ohne Werkzeuge und seine eigene Canvas-App ein", asy
|
|
|
36
36
|
prompt: (calls[1]!.input as { prompt: string }).prompt,
|
|
37
37
|
});
|
|
38
38
|
assert.deepEqual(calls[2]!.input, { name: "balcony-app", actor: "@balcony-advisor" });
|
|
39
|
-
assert.deepEqual(calls[3]!.input, {
|
|
39
|
+
assert.deepEqual(calls[3]!.input, { root: { entity: "app:@balcony-advisor/main" } });
|
|
40
40
|
assert.deepEqual(calls[4]!.input, { title: "Dein Balkon", primaryActor: "@balcony-advisor" });
|
|
41
41
|
assert.deepEqual(context.state.read(), { built: true });
|
|
42
42
|
await program.onInput!(input, context);
|
|
@@ -3,12 +3,11 @@ title: Gesprächsrunde einrichten
|
|
|
3
3
|
description: "Ein vorbereitetes Setup zeigt die Parametrisierung durch einen Startleitfaden und die Anordnung einer Gesprächsrunde. Der Koordinator führt anschließend die Runden."
|
|
4
4
|
order: 120
|
|
5
5
|
guide: ragents.reference.conversation-circle
|
|
6
|
-
tags: Run-Scripts, Anwendungsfall, Konzeptdemo, Startleitfaden, Agententeams
|
|
6
|
+
tags: Run-Scripts, Anwendungsfall, Konzeptdemo, Startleitfaden, Agententeams
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
das Briefing.
|
|
9
|
+
Ein deterministisches Setup: das Script stellt mira, jon und ada als gewöhnliche LLMs auf, teilt
|
|
10
|
+
die Kachelfläche unter ihnen auf und übergibt dem Koordinator das Briefing.
|
|
12
11
|
Die Karte prüft das Modell, dieses Script prüft die Plattform.
|
|
13
12
|
|
|
14
13
|
Der Leitfaden übergibt `{ "topic": "...", "rounds": 2 }`. `topic` enthält 1 bis 160 Zeichen, `rounds` ist eine ganze Zahl von 1 bis 5. Der Startwert `null` wählt ausdrücklich das Thema "Sollten Innenstädte autofrei werden?" und zwei Runden. Andere unvollständige oder ungültige Werte werden vor dem Aufbau abgelehnt. Thema und Rundenzahl steuern die Darstellung und den Auftrag, das Thema auch den Run-Titel.
|
|
@@ -58,25 +58,19 @@ export default defineActor(contract, {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
await context.functions.canvas_layout_replace({
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
lines: [
|
|
70
|
-
{ from: participants[0]!, to: participants[1]!, arrow: "end", label: "gibt weiter" },
|
|
71
|
-
{ from: participants[1]!, to: participants[2]!, arrow: "end", label: "gibt weiter" },
|
|
72
|
-
{ from: participants[2]!, to: participants[0]!, arrow: "end", label: "gibt weiter" },
|
|
73
|
-
{ from: "shape:thema", to: participants[0]!, style: "dashed", label: "Auftakt" },
|
|
74
|
-
],
|
|
61
|
+
root: {
|
|
62
|
+
direction: "vertical",
|
|
63
|
+
weights: [1, 1],
|
|
64
|
+
children: [
|
|
65
|
+
{ entity: participants[0]! },
|
|
66
|
+
{ direction: "horizontal", weights: [1, 1], children: [{ entity: participants[1]! }, { entity: participants[2]! }] },
|
|
67
|
+
],
|
|
68
|
+
},
|
|
75
69
|
});
|
|
76
70
|
|
|
77
71
|
await context.functions.actor_input({
|
|
78
72
|
actor: "@coordinator",
|
|
79
|
-
content: `Die Runde steht: ${participants.join(", ")} sind aufgestellt, die Fläche ist
|
|
73
|
+
content: `Die Runde steht: ${participants.join(", ")} sind aufgestellt, die Fläche ist aufgeteilt. Thema: "${topic}". `
|
|
80
74
|
+ `Führe genau ${rounds} Gesprächsrunden durch: in jeder Runde ${participants.join(", ")} in dieser Reihenfolge. `
|
|
81
75
|
+ "Jeder erhält die bisherigen Beiträge. Ändere die Fläche nicht. "
|
|
82
76
|
+ "Fasse das Gespräch am Ende in drei Sätzen zusammen.",
|
|
@@ -8,9 +8,11 @@ test("Thema und Rundenzahl steuern Teilnehmer, Fläche und Auftrag", async () =>
|
|
|
8
8
|
await program.onInput!(startInput({ topic: "Teamfrühstück", rounds: 3 }), context);
|
|
9
9
|
assert.deepEqual(calls.filter((call) => call.name === "agent_spawn").map((call) => (call.input as { handle: string }).handle), ["mira", "jon", "ada"]);
|
|
10
10
|
assert.deepEqual(calls.find((call) => call.name === "run_configure")?.input, { title: "Gesprächsrunde: Teamfrühstück" });
|
|
11
|
-
const layout = calls.find((call) => call.name === "canvas_layout_replace")?.input as {
|
|
12
|
-
assert.
|
|
13
|
-
|
|
11
|
+
const layout = calls.find((call) => call.name === "canvas_layout_replace")?.input as { root: unknown };
|
|
12
|
+
assert.deepEqual(layout.root, { direction: "vertical", weights: [1, 1], children: [
|
|
13
|
+
{ entity: "@mira" },
|
|
14
|
+
{ direction: "horizontal", weights: [1, 1], children: [{ entity: "@jon" }, { entity: "@ada" }] },
|
|
15
|
+
] });
|
|
14
16
|
assert.match(JSON.stringify(calls.at(-1)), /genau 3 Gesprächsrunden/);
|
|
15
17
|
assert.deepEqual(context.state.read(), { built: true });
|
|
16
18
|
const completedCalls = calls.length;
|