@schlenkr/ragents 0.1.9 → 0.1.10
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/package.json +1 -1
- package/apps/server/src/access-projection.ts +6 -36
- package/apps/server/src/api/contracts.ts +5 -2
- package/apps/server/src/api/core-methods.ts +5 -3
- package/apps/server/src/config-file.ts +1 -1
- package/apps/server/src/host-api.json +2 -0
- package/apps/server/src/plugin-support/process-sandbox.ts +16 -15
- package/apps/server/src/provider.ts +2 -2
- package/apps/server/src/ragents/engine.ts +2 -1
- package/apps/server/src/ragents/global-chat.ts +7 -1
- package/apps/server/src/server.ts +3 -1
- package/apps/web/dist/assets/{highlighted-body-KPVGNVTW-xXXhTDbh.js → highlighted-body-KPVGNVTW-BO4wLQtE.js} +1 -1
- package/apps/web/dist/assets/main-DnnqWHkH.js +11 -0
- package/apps/web/dist/assets/mermaid-HWGCJPDP-BLLeth5_.js +568 -0
- package/apps/web/dist/assets/{runPanel-ZT6TPDLM.js → runPanel-DPm7s3KM.js} +2 -2
- package/apps/web/dist/help/guide-clients.html +17 -7
- package/apps/web/dist/help/guide-clients.md +18 -7
- package/apps/web/dist/help/index.html +1911 -908
- package/apps/web/dist/help/mini-app.css +24 -0
- package/apps/web/dist/host-web.json +2 -2
- package/apps/web/dist/index.html +2 -2
- package/apps/web/dist/run-panel.html +2 -2
- package/apps/web/package.json +1 -1
- package/apps/web/src/panel/ConnectionsPage.tsx +3 -0
- package/apps/web/src/panel/StartPage.tsx +3 -0
- package/apps/web/src/panel/contract.ts +7 -0
- package/bundles/ragents.activity/ragents-bundle.json +1 -1
- package/bundles/ragents.actor-programs/ragents-bundle.json +2 -2
- package/bundles/ragents.actor-programs/server/index.js +31 -3
- package/bundles/ragents.actor-programs/server/index.js.map +4 -4
- package/bundles/ragents.actor-programs/web/index.js.map +1 -1
- package/bundles/ragents.ask/ragents-bundle.json +2 -4
- package/bundles/ragents.ask/web/chunks/{chunk-V7W3QDVS.js → chunk-MBRPS477.js} +2 -2
- package/bundles/ragents.ask/web/chunks/{chunk-V7W3QDVS.js.map → chunk-MBRPS477.js.map} +4 -4
- package/bundles/ragents.ask/web/classes.json +1 -1
- package/bundles/ragents.ask/web/exports/web/QuestionCard.js +1 -1
- package/bundles/ragents.ask/web/index.js +1 -1
- package/bundles/ragents.ask/web/index.js.map +4 -4
- package/bundles/ragents.browser/ragents-bundle.json +1 -1
- package/bundles/ragents.documents/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-fsharp/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-roslyn/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-typescript/ragents-bundle.json +1 -1
- package/bundles/ragents.model-relay/ragents-bundle.json +1 -1
- package/bundles/ragents.orchestration/ragents-bundle.json +2 -2
- package/bundles/ragents.orchestration/web/classes.json +1 -1
- package/bundles/ragents.orchestration/web/index.js +6 -6
- package/bundles/ragents.orchestration/web/index.js.map +3 -3
- package/bundles/ragents.overseer/ragents-bundle.json +2 -2
- package/bundles/ragents.overseer/server/index.js +1 -1
- package/bundles/ragents.overseer/server/index.js.map +2 -2
- package/bundles/ragents.processes/ragents-bundle.json +1 -1
- package/bundles/ragents.product/ragents-bundle.json +1 -1
- package/bundles/ragents.profile-distribution/ragents-bundle.json +1 -1
- package/bundles/ragents.reference/ragents-bundle.json +1 -1
- package/bundles/ragents.todo/ragents-bundle.json +1 -1
- package/bundles/ragents.transcript/ragents-bundle.json +1 -1
- package/bundles/ragents.watch/ragents-bundle.json +1 -1
- package/bundles/ragents.workspace/ragents-bundle.json +2 -2
- package/bundles/ragents.workspace/server/index.js +4 -2
- package/bundles/ragents.workspace/server/index.js.map +2 -2
- package/package.json +3 -3
- package/packages/agent/dist/core/tools/bash.d.ts +1 -1
- package/packages/agent/src/core/tools/bash.ts +1 -1
- package/packages/ragents/src/index.ts +1 -0
- package/packages/ragents/src/plugin-host.ts +46 -0
- package/packages/ragents/src/plugin-types.ts +19 -0
- package/packages/workspace-executor/src/managed-process.ts +28 -0
- package/packages/workspace-executor/src/sandbox-tools.ts +9 -3
- package/plugins/ragents.actor-programs/server/access-projections.ts +31 -0
- package/plugins/ragents.actor-programs/server/index.ts +2 -0
- package/plugins/ragents.ask/web/index.tsx +0 -6
- package/plugins/ragents.orchestration/web/ActorTile.tsx +1 -1
- package/plugins/ragents.orchestration/web/run-panel/RunPanel.tsx +8 -7
- package/plugins/ragents.orchestration/web/run-panel/sheet-status.ts +3 -3
- package/plugins/ragents.overseer/server/api.ts +2 -2
- package/plugins/ragents.workspace/client/workspace-client.ts +19 -6
- package/plugins/ragents.workspace/server/config.ts +3 -0
- package/plugins/ragents.workspace/server/index.ts +3 -2
- package/apps/web/dist/assets/main-C7JeZFjs.js +0 -6
- package/apps/web/dist/assets/mermaid-HWGCJPDP-DfsU5ofC.js +0 -478
- package/plugins/ragents.ask/web/QuestionSection.tsx +0 -48
|
@@ -11,7 +11,9 @@ import type { AgentProfile, CatalogModel } from "./agents/catalog.ts";
|
|
|
11
11
|
import type { ToolContributor } from "./agents/plugins.ts";
|
|
12
12
|
import { describeToolAvailability, type RunFunction } from "./agents/tools.ts";
|
|
13
13
|
import type { ChannelContribution, ChannelDescriptor, MethodContribution, MethodDescriptor } from "./rpc/contribution.ts";
|
|
14
|
+
import type { PluginState } from "./domain/model.ts";
|
|
14
15
|
import type {
|
|
16
|
+
AccessProjectionContribution,
|
|
15
17
|
AgentAudience,
|
|
16
18
|
HttpRouteContribution,
|
|
17
19
|
OperationContext,
|
|
@@ -19,6 +21,7 @@ import type {
|
|
|
19
21
|
RegisteredOperationDescriptor,
|
|
20
22
|
AgentContribution,
|
|
21
23
|
AgentContributionContext,
|
|
24
|
+
PluginChatEvent,
|
|
22
25
|
PluginConfigDescriptor,
|
|
23
26
|
PluginManifest,
|
|
24
27
|
PluginRegistration,
|
|
@@ -977,6 +980,47 @@ export class StartOptionContributionRegistry {
|
|
|
977
980
|
}
|
|
978
981
|
}
|
|
979
982
|
|
|
983
|
+
/** Was ein Zugang ohne runs.inspect von Plugin-Zuständen sieht: eine Startoption nur mit ihren Rechten, sonst die Projektion ihres Plugins, ohne sie alles. */
|
|
984
|
+
export class AccessProjectionRegistry {
|
|
985
|
+
readonly #projections = new ContributionRegistry<AccessProjectionContribution>("Zugriffsprojektion");
|
|
986
|
+
readonly #startOptions: StartOptionContributionRegistry;
|
|
987
|
+
|
|
988
|
+
constructor(startOptions = new StartOptionContributionRegistry()) {
|
|
989
|
+
this.#startOptions = startOptions;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
register(owner: string, contributions: readonly AccessProjectionContribution[]): void {
|
|
993
|
+
for (const contribution of contributions) {
|
|
994
|
+
for (const name of ["state", "chatEvent"] as const) {
|
|
995
|
+
if (typeof contribution[name] !== "function") throw new Error(`Zugriffsprojektion ${contribution.id} hat kein ${name}`);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
this.#projections.register(owner, contributions);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
state(entry: PluginState, access: Pick<AccessContext, "can">): PluginState | undefined {
|
|
1002
|
+
if (access.can("runs.inspect")) return entry;
|
|
1003
|
+
if (this.#startOptions.missingRight(entry.pluginId, access) !== undefined) return undefined;
|
|
1004
|
+
const projection = this.#find(entry.pluginId);
|
|
1005
|
+
if (!projection) return entry;
|
|
1006
|
+
const state = projection.state(entry);
|
|
1007
|
+
return state === undefined ? undefined : { ...entry, state };
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
chatEvent<T extends PluginChatEvent>(pluginId: string, event: T, access: Pick<AccessContext, "can">): T | undefined {
|
|
1011
|
+
if (access.can("runs.inspect")) return event;
|
|
1012
|
+
if (this.#startOptions.missingRight(pluginId, access) !== undefined) return undefined;
|
|
1013
|
+
const projection = this.#find(pluginId);
|
|
1014
|
+
if (!projection) return event;
|
|
1015
|
+
const projected = projection.chatEvent(event.payload === undefined ? { type: event.type } : { type: event.type, payload: event.payload });
|
|
1016
|
+
return projected === undefined ? undefined : { ...event, type: projected.type, payload: projected.payload };
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
#find(id: string): AccessProjectionContribution | undefined {
|
|
1020
|
+
return this.#projections.entries().find(({ value }) => value.id === id)?.value;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
980
1024
|
export class ConfigContributionRegistry {
|
|
981
1025
|
readonly #entries: Array<{ owners: string[]; value: PluginConfigDescriptor }> = [];
|
|
982
1026
|
|
|
@@ -1125,6 +1169,7 @@ export class PluginHost {
|
|
|
1125
1169
|
readonly lifecycle = new LifecycleContributionRegistry();
|
|
1126
1170
|
readonly sessionMetadata = new SessionMetadataContributionRegistry();
|
|
1127
1171
|
readonly startOptions = new StartOptionContributionRegistry();
|
|
1172
|
+
readonly accessProjections = new AccessProjectionRegistry(this.startOptions);
|
|
1128
1173
|
readonly config = new ConfigContributionRegistry();
|
|
1129
1174
|
readonly storage: StorageRegistry;
|
|
1130
1175
|
readonly #product: ProductDescriptor;
|
|
@@ -1176,6 +1221,7 @@ export class PluginHost {
|
|
|
1176
1221
|
skills: (...entries) => this.skills.register(manifest.id, entries),
|
|
1177
1222
|
startEntries: (...entries) => this.startEntries.register(manifest.id, entries),
|
|
1178
1223
|
startOptions: (...entries) => this.startOptions.register(manifest.id, entries),
|
|
1224
|
+
accessProjections: (...entries) => this.accessProjections.register(manifest.id, entries),
|
|
1179
1225
|
functions: (...entries) => this.tools.registerFunctions(manifest.id, entries),
|
|
1180
1226
|
script: (...entries) => this.script.register(manifest.id, entries),
|
|
1181
1227
|
};
|
|
@@ -8,6 +8,7 @@ import type { WorkspaceToolNaming } from "./agents/workspace-tools.ts";
|
|
|
8
8
|
import type { Orchestration } from "./runtime/orchestration.ts";
|
|
9
9
|
import type { AgentDriver } from "./drivers/types.ts";
|
|
10
10
|
import type { JsonValue } from "./domain/json.ts";
|
|
11
|
+
import type { PluginState } from "./domain/model.ts";
|
|
11
12
|
import type { AccessContext } from "./access.ts";
|
|
12
13
|
import type { ChannelContribution, MethodContribution } from "./rpc/contribution.ts";
|
|
13
14
|
|
|
@@ -370,6 +371,22 @@ export interface RegisteredStartOption {
|
|
|
370
371
|
option: StartOptionContribution;
|
|
371
372
|
}
|
|
372
373
|
|
|
374
|
+
/** Ein Chat-Ereignis zu einem Plugin-Zustand, ohne die Kennung, die die Projektion schon nennt. */
|
|
375
|
+
export interface PluginChatEvent {
|
|
376
|
+
type: string;
|
|
377
|
+
payload?: unknown;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** Was ein Zugang ohne runs.inspect von einem Plugin-Zustand und dessen Chat-Ereignissen sieht; ohne Projektion sieht er beides unverändert. */
|
|
381
|
+
export interface AccessProjectionContribution {
|
|
382
|
+
/** Die Kennung des Zustands, wie sie im Journal (`pluginId`) und im Chat-Ereignis steht. */
|
|
383
|
+
id: string;
|
|
384
|
+
/** Der sichtbare Zustand; undefined nimmt ihn aus der Run-Ansicht. */
|
|
385
|
+
state: (entry: PluginState) => JsonValue | undefined;
|
|
386
|
+
/** Das sichtbare Chat-Ereignis; undefined unterdrückt es. */
|
|
387
|
+
chatEvent: (event: PluginChatEvent) => PluginChatEvent | undefined;
|
|
388
|
+
}
|
|
389
|
+
|
|
373
390
|
export interface SessionMetadataContribution {
|
|
374
391
|
id: string;
|
|
375
392
|
/** Ob `describe` den Arbeitsbereich des Runs erreicht; dann ruft der Host es nur für Aufrufer, die ihn erreichen dürfen. */
|
|
@@ -432,6 +449,8 @@ export interface PluginRegistration {
|
|
|
432
449
|
skills: (...contributions: readonly SkillContribution[]) => void;
|
|
433
450
|
startEntries: (...contributions: readonly StartEntryContribution[]) => void;
|
|
434
451
|
startOptions: (...contributions: readonly StartOptionContribution[]) => void;
|
|
452
|
+
/** Wie Zugänge ohne runs.inspect die Zustände dieses Plugins und ihre Chat-Ereignisse sehen, je Kennung eines Zustands. */
|
|
453
|
+
accessProjections: (...contributions: readonly AccessProjectionContribution[]) => void;
|
|
435
454
|
functions: (...functions: readonly (RunFunction | ToolContributor)[]) => void;
|
|
436
455
|
script: (...contributions: readonly ScriptContribution[]) => void;
|
|
437
456
|
}
|
|
@@ -2,6 +2,7 @@ import { execFile, spawn, type ChildProcess } from "node:child_process";
|
|
|
2
2
|
import { readdir, readFile } from "node:fs/promises";
|
|
3
3
|
import type { Writable } from "node:stream";
|
|
4
4
|
import { killProcessTree } from "@ragents/agent";
|
|
5
|
+
import { bashLaunch } from "./bash-launch.js";
|
|
5
6
|
|
|
6
7
|
const PROCESS_GROUP_JOIN_TIMEOUT_MS = 5_000;
|
|
7
8
|
const PROCESS_GROUP_POLL_MS = 25;
|
|
@@ -77,6 +78,33 @@ export const stopProcessTree = (pid: number): void => {
|
|
|
77
78
|
if (processExists(pid)) killProcessTree(pid);
|
|
78
79
|
};
|
|
79
80
|
|
|
81
|
+
/** Markiert unter Windows die Prozesse eines bash-Aufrufs: MSYS hängt die Kinder der Bash nicht an deren Windows-Prozessbaum, taskkill /T erreicht sie nicht. */
|
|
82
|
+
export const MSYS_CALL_ENV = "RAGENTS_BASH_CALL";
|
|
83
|
+
|
|
84
|
+
/** Die Windows-Kennungen aller MSYS-Prozesse in den Prozessgruppen, in denen ein Prozess die Markierung trägt, auch nach dem Ende der Bash selbst. */
|
|
85
|
+
const msysGroupScript = (marker: string): string => [
|
|
86
|
+
"groups=' '",
|
|
87
|
+
`for d in /proc/[0-9]*; do tr '\\0' '\\n' < "$d/environ" 2>/dev/null | grep -qxF '${MSYS_CALL_ENV}=${marker}' && groups="$groups$(cat "$d/pgid") "; done`,
|
|
88
|
+
`for d in /proc/[0-9]*; do case "$groups" in *" $(cat "$d/pgid" 2>/dev/null) "*) cat "$d/winpid" 2>/dev/null;; esac; done`,
|
|
89
|
+
].join("\n");
|
|
90
|
+
|
|
91
|
+
const execFileText = (file: string, args: readonly string[], env?: NodeJS.ProcessEnv): Promise<{ stdout: string; error: Error | null }> =>
|
|
92
|
+
new Promise((resolve) => {
|
|
93
|
+
execFile(file, [...args], { env, windowsHide: true, timeout: 15_000 }, (error, stdout) => resolve({ stdout, error }));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
/** Beendet unter Windows alles, was ein bash-Aufruf mit dieser Markierung gestartet hat, samt den nativen Bäumen darunter. */
|
|
97
|
+
export const stopMsysCall = async (bash: string, env: NodeJS.ProcessEnv, marker: string): Promise<void> => {
|
|
98
|
+
const launch = bashLaunch({ bash, rg: undefined }, msysGroupScript(marker), env, "win32");
|
|
99
|
+
const listing = await execFileText(launch.command, launch.args, launch.env);
|
|
100
|
+
if (listing.error) throw new Error(`Die Prozesse des bash-Aufrufs ließen sich nicht ermitteln: ${listing.error.message}`);
|
|
101
|
+
const winpids = [...new Set(listing.stdout.split(/\s+/).filter((pid) => /^\d+$/.test(pid)))];
|
|
102
|
+
if (winpids.length === 0) return;
|
|
103
|
+
await execFileText("taskkill", ["/F", "/T", ...winpids.flatMap((pid) => ["/PID", pid])]);
|
|
104
|
+
const alive = winpids.filter((pid) => processExists(Number(pid)));
|
|
105
|
+
if (alive.length > 0) throw new Error(`Die Prozesse ${alive.join(", ")} des bash-Aufrufs laufen nach taskkill weiter`);
|
|
106
|
+
};
|
|
107
|
+
|
|
80
108
|
const darwinProcessGroupExists = (pid: number): Promise<boolean> =>
|
|
81
109
|
new Promise((resolve, reject) => {
|
|
82
110
|
execFile("/bin/ps", ["-axo", "pgid=,stat="], { timeout: 5_000, maxBuffer: 4 * 1024 * 1024 }, (error, stdout) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
3
|
import { readFile, stat } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import {
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import { bashLaunch } from "./bash-launch.js";
|
|
13
13
|
import type { WorkspaceProcessContext } from "./context.js";
|
|
14
14
|
import { WorkspaceOperationError } from "./errors.js";
|
|
15
|
-
import { processGroupExists, stopProcessTree } from "./managed-process.js";
|
|
15
|
+
import { MSYS_CALL_ENV, processGroupExists, stopMsysCall, stopProcessTree } from "./managed-process.js";
|
|
16
16
|
import type { WorkspaceModuleFactory, WorkspaceOperation } from "./module.js";
|
|
17
17
|
import { sandboxedLaunch } from "./process-sandbox.js";
|
|
18
18
|
import { stopUidProcesses } from "./session-ident.js";
|
|
@@ -125,10 +125,14 @@ export const createSandboxTools = async (
|
|
|
125
125
|
let toolOperation: Promise<void> = Promise.resolve();
|
|
126
126
|
let shuttingDown = false;
|
|
127
127
|
const processGroups = new Set<number>();
|
|
128
|
+
const msysCalls = new Map<number, { readonly bash: string; readonly env: NodeJS.ProcessEnv; readonly marker: string }>();
|
|
128
129
|
|
|
129
130
|
const killProcessGroup = async (pid: number): Promise<void> => {
|
|
130
131
|
if (onWindows) {
|
|
131
132
|
stopProcessTree(pid);
|
|
133
|
+
const call = msysCalls.get(pid);
|
|
134
|
+
msysCalls.delete(pid);
|
|
135
|
+
if (call) await stopMsysCall(call.bash, call.env, call.marker);
|
|
132
136
|
processGroups.delete(pid);
|
|
133
137
|
return;
|
|
134
138
|
}
|
|
@@ -268,17 +272,19 @@ export const createSandboxTools = async (
|
|
|
268
272
|
const launch = await sandboxedLaunch(context, bash);
|
|
269
273
|
if (shuttingDown) throw new Error("Die Werkzeuge werden killed");
|
|
270
274
|
options.signal?.throwIfAborted();
|
|
275
|
+
const marker = onWindows ? randomUUID() : undefined;
|
|
271
276
|
return new Promise((resolve, reject) => {
|
|
272
277
|
const child = spawn(launch.command, [...launch.args], {
|
|
273
278
|
cwd: commandCwd,
|
|
274
279
|
detached: !onWindows,
|
|
275
280
|
stdio: ["ignore", "pipe", "pipe"],
|
|
276
|
-
env: bash.env,
|
|
281
|
+
env: marker === undefined ? bash.env : { ...bash.env, [MSYS_CALL_ENV]: marker },
|
|
277
282
|
uid: context.uid,
|
|
278
283
|
gid: context.gid,
|
|
279
284
|
windowsHide: true,
|
|
280
285
|
});
|
|
281
286
|
if (child.pid) processGroups.add(child.pid);
|
|
287
|
+
if (child.pid && marker !== undefined && context.bash !== undefined) msysCalls.set(child.pid, { bash: context.bash, env: context.env, marker });
|
|
282
288
|
child.stdout?.on("data", options.onData);
|
|
283
289
|
child.stderr?.on("data", options.onData);
|
|
284
290
|
let killed = false;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AccessProjectionContribution, JsonValue } from "@ragents/engine";
|
|
2
|
+
import { ACTOR_INVOCATIONS_STATE_ID, ACTOR_PROGRAMS_STATE_ID } from "@ragents/host/plugin-support/actor-programs/contract.js";
|
|
3
|
+
|
|
4
|
+
const record = (value: JsonValue | undefined): Record<string, JsonValue | undefined> | undefined =>
|
|
5
|
+
value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
6
|
+
|
|
7
|
+
/** Ohne runs.inspect bleiben von einem Programm Name, Actor und Ansichten; Quellen, Funktionen und Verzeichnisse fehlen. */
|
|
8
|
+
const visibleProgram = (state: JsonValue): JsonValue => {
|
|
9
|
+
const program = record(record(state)?.program);
|
|
10
|
+
if (!program) return state;
|
|
11
|
+
const views = Array.isArray(program.views) ? program.views.map((value) => {
|
|
12
|
+
const view = record(value);
|
|
13
|
+
return view ? { id: view.id, key: view.key, title: view.title, visible: view.visible, placements: view.placements } : value;
|
|
14
|
+
}) : [];
|
|
15
|
+
return {
|
|
16
|
+
version: 1,
|
|
17
|
+
program: {
|
|
18
|
+
name: program.name,
|
|
19
|
+
title: program.title,
|
|
20
|
+
actorId: program.actorId,
|
|
21
|
+
actorHandle: program.actorHandle,
|
|
22
|
+
revision: program.revision,
|
|
23
|
+
views,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const actorProgramAccessProjections: readonly AccessProjectionContribution[] = [
|
|
29
|
+
{ id: ACTOR_PROGRAMS_STATE_ID, state: (entry) => visibleProgram(entry.state), chatEvent: () => undefined },
|
|
30
|
+
{ id: ACTOR_INVOCATIONS_STATE_ID, state: (entry) => ({ version: 1, revision: entry.updatedAt }), chatEvent: () => undefined },
|
|
31
|
+
];
|
|
@@ -13,6 +13,7 @@ import { actorProgramSummary } from "./prompts.js";
|
|
|
13
13
|
import { sandboxServicesToken } from "@ragents/host/plugin-support/workspace-sandbox-host.js";
|
|
14
14
|
import { actorProgramsToken } from "@ragents/host/plugin-support/actor-programs/service.js";
|
|
15
15
|
import { createProjectDiagnostics } from "./project-diagnostics.js";
|
|
16
|
+
import { actorProgramAccessProjections } from "./access-projections.js";
|
|
16
17
|
const actorProgramsPlugin = (pluginHost: PluginHost): RAgentsPlugin => ({
|
|
17
18
|
manifest: { id: "ragents.actor-programs" },
|
|
18
19
|
register: (host) => {
|
|
@@ -45,6 +46,7 @@ const actorProgramsPlugin = (pluginHost: PluginHost): RAgentsPlugin => ({
|
|
|
45
46
|
deleteSession: async ({ runId }) => { diagnostics.forget(runId); await runtime.deleteSession(runId); }, shutdown: () => runtime.shutdown(),
|
|
46
47
|
});
|
|
47
48
|
host.prompts(actorProgramSummary);
|
|
49
|
+
host.accessProjections(...actorProgramAccessProjections);
|
|
48
50
|
host.functions(createControlsToolContributor(), ...createActorProgramToolContributors(runtime, diagnostics));
|
|
49
51
|
},
|
|
50
52
|
});
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
import { runViewFrom, type RunView } from "@ragents/web/run-view";
|
|
7
7
|
import { askPayloadOf, ASK_PLUGIN_ID } from "../ask-payload";
|
|
8
8
|
import { AskActionView } from "./AskActionView";
|
|
9
|
-
import { QuestionSection } from "./QuestionSection";
|
|
10
9
|
|
|
11
10
|
const pendingQuestions = (view: RunView) =>
|
|
12
11
|
view.actions.filter((action) => action.owner === ASK_PLUGIN_ID && action.status === "pending");
|
|
@@ -34,11 +33,6 @@ const configuredPlugin = (descriptor: WebPluginDescriptor): WebPlugin => ({
|
|
|
34
33
|
owner: ASK_PLUGIN_ID,
|
|
35
34
|
View: AskActionView,
|
|
36
35
|
}],
|
|
37
|
-
cardSections: [{
|
|
38
|
-
id: "ragents.ask.questions",
|
|
39
|
-
order: 100,
|
|
40
|
-
Section: QuestionSection,
|
|
41
|
-
}],
|
|
42
36
|
attention: [{
|
|
43
37
|
id: "ragents.ask.pending-questions",
|
|
44
38
|
assess: attentionFor,
|
|
@@ -12,7 +12,7 @@ export function ActorTile({ actor, view, cardSections, chatInput, session, navig
|
|
|
12
12
|
navigation: SessionNavigation;
|
|
13
13
|
onSelect: (selection: EntityReference) => void;
|
|
14
14
|
}) {
|
|
15
|
-
return <div className="flex flex-col overflow-hidden has-[>[data-slot=card-sections]:not(:empty)]:min-h-[260px]">
|
|
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
16
|
<ActorChatPreview actor={actor} view={view} chatInput={chatInput} running={actor.lifecycle?.kind === "running"}
|
|
17
17
|
conversation={session.actorConversations?.[actor.id]} historyError={session.conversationError}
|
|
18
18
|
onNavigate={onSelect} primaryMessages={actor.id === view.primaryActorId ? session.messages : undefined} />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type KeyboardEvent, type MouseEvent, type PointerEvent, type ReactNode, type RefObject } from "react";
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type CSSProperties, type KeyboardEvent, type MouseEvent, type PointerEvent, type ReactNode, type RefObject } from "react";
|
|
2
2
|
import { createPortal } from "react-dom";
|
|
3
3
|
import { ArrowUpRightIcon, LayoutGridIcon, Maximize2Icon, MessageSquareIcon, PanelBottomIcon, PanelRightIcon } from "lucide-react";
|
|
4
4
|
import { useAccess } from "@ragents/web/AccessContext";
|
|
@@ -181,7 +181,7 @@ function RunPanel({ surfaceElements, cardSections, navigation, renderChat, sessi
|
|
|
181
181
|
/>}
|
|
182
182
|
<section
|
|
183
183
|
aria-label="Chat"
|
|
184
|
-
className={cn(chatLayoutClass[layout], layout === "floating" && "[&:not([data-expanded])_[data-slot=card-sections]]:hidden [&[data-sheet-resizing]_[data-slot=card-sections]]:hidden")}
|
|
184
|
+
className={cn(chatLayoutClass[layout], layout === "floating" && "[&_[data-chat=composer]]:[--qsl-panel-height:var(--sheet-available-height)] [&:not([data-expanded])_[data-slot=card-sections]]:hidden [&[data-sheet-resizing]_[data-slot=card-sections]]:hidden")}
|
|
185
185
|
data-expanded={layout === "floating" && sheet.expanded ? true : undefined}
|
|
186
186
|
data-sheet-resizing={resize.height !== undefined || undefined}
|
|
187
187
|
data-view={primarySelected ? "chat" : "actor-chat"}
|
|
@@ -190,7 +190,7 @@ function RunPanel({ surfaceElements, cardSections, navigation, renderChat, sessi
|
|
|
190
190
|
onPointerEnter={layout === "floating" ? (event) => { if (event.pointerType === "mouse") sheet.openLater(); } : undefined}
|
|
191
191
|
onPointerLeave={layout === "floating" ? () => sheet.closeLater(settings.closeDelay) : undefined}
|
|
192
192
|
ref={sheetRef}
|
|
193
|
-
style={layout === "floating" ? { height: resize.height ?? (sheet.expanded ? expandedHeight : geometry.minimum), transition: resize.height !== undefined ? "none" : undefined } : undefined}
|
|
193
|
+
style={layout === "floating" ? { height: resize.height ?? (sheet.expanded ? expandedHeight : geometry.minimum), transition: resize.height !== undefined ? "none" : undefined, "--sheet-available-height": `${geometry.available}px` } as CSSProperties : undefined}
|
|
194
194
|
>
|
|
195
195
|
{layout === "floating" && <button
|
|
196
196
|
aria-expanded={sheet.expanded}
|
|
@@ -307,9 +307,9 @@ function useSheet(active: boolean, sheetRef: RefObject<HTMLElement | null>, grab
|
|
|
307
307
|
return { expanded, held, open, openLater, closeLater, close, toggle, pauseResize, finishResize };
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
/** Die Höhe des zugeschobenen Sheets: Griff, Statuszeile und Eingabe, gemessen an der Eingabe des Chat-Rahmens. */
|
|
310
|
+
/** Die Höhe des zugeschobenen Sheets: Griff, Statuszeile und Eingabe samt offener Aktionen darüber, gemessen an der Eingabe des Chat-Rahmens. */
|
|
311
311
|
function useSheetPeek(active: boolean, sheetRef: RefObject<HTMLElement | null>, grabRef: RefObject<HTMLButtonElement | null>, statusRef: RefObject<HTMLButtonElement | null>, actorId: string | undefined) {
|
|
312
|
-
const [geometry, setGeometry] = useState({ minimum: 0, maximum: 0, scale: 1 });
|
|
312
|
+
const [geometry, setGeometry] = useState({ minimum: 0, maximum: 0, available: 0, scale: 1 });
|
|
313
313
|
useLayoutEffect(() => {
|
|
314
314
|
if (!active) return;
|
|
315
315
|
const sheet = sheetRef.current;
|
|
@@ -325,8 +325,9 @@ function useSheetPeek(active: boolean, sheetRef: RefObject<HTMLElement | null>,
|
|
|
325
325
|
const scale = width / Number.parseFloat(style.width);
|
|
326
326
|
const minimum = Math.ceil((grab.getBoundingClientRect().height + (status?.getBoundingClientRect().height ?? 0)
|
|
327
327
|
+ composer.getBoundingClientRect().height) / scale + Number.parseFloat(style.borderTopWidth) + Number.parseFloat(style.borderBottomWidth));
|
|
328
|
-
const
|
|
329
|
-
|
|
328
|
+
const available = Math.floor(container.getBoundingClientRect().height / scale * 0.9);
|
|
329
|
+
const maximum = Math.max(minimum, available);
|
|
330
|
+
setGeometry((previous) => previous.minimum === minimum && previous.maximum === maximum && previous.available === available && previous.scale === scale ? previous : { minimum, maximum, available, scale });
|
|
330
331
|
};
|
|
331
332
|
const observer = new ResizeObserver(measure);
|
|
332
333
|
observer.observe(composer);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Message } from "quassel/events";
|
|
2
2
|
|
|
3
|
-
/** Die eine Zeile des zugeschobenen Sheets: was der Chat gerade tut oder zuletzt gesagt hat. */
|
|
3
|
+
/** Die eine Zeile des zugeschobenen Sheets: was der Chat gerade tut oder zuletzt gesagt hat; offene Aktionen selbst stehen im Dock darunter. */
|
|
4
4
|
export interface SheetStatus {
|
|
5
5
|
text: string;
|
|
6
6
|
kind: "idle" | "working" | "waiting";
|
|
@@ -16,8 +16,8 @@ const senderOf = (message: Message, handle: string): string =>
|
|
|
16
16
|
|
|
17
17
|
export function sheetStatus(messages: readonly Message[], working: boolean, handle: string): SheetStatus {
|
|
18
18
|
const last = messages[messages.length - 1];
|
|
19
|
-
const waiting =
|
|
20
|
-
if (waiting) return { kind: "waiting", text:
|
|
19
|
+
const waiting = messages.filter((message) => message.role === "action" && message.action?.status === undefined).length;
|
|
20
|
+
if (waiting > 0) return { kind: "waiting", text: waiting === 1 ? "Wartet auf Eingabe" : `Wartet auf ${waiting} Eingaben` };
|
|
21
21
|
if (working) {
|
|
22
22
|
if (last?.role === "thinking" && !last.closed) return { kind: "working", text: `@${handle} denkt ...` };
|
|
23
23
|
if (last?.role === "tool" && last.tool && last.tool.result === undefined) return { kind: "working", text: `@${handle} nutzt ${last.tool.name} ...` };
|
|
@@ -3,7 +3,7 @@ import type { Static } from "typebox";
|
|
|
3
3
|
import { DomainError, implement, type AccessContext, type JsonValue, type MethodContribution, type PluginHost } from "@ragents/engine";
|
|
4
4
|
import { methodReference, openRpcDocument, type ApiAuthentication } from "@ragents/host/api/reference.js";
|
|
5
5
|
import type { RunManagement } from "@ragents/host/ragents/global-chat.js";
|
|
6
|
-
import { overseerContracts } from "../contract.js";
|
|
6
|
+
import { OVERSEER_PLUGIN_ID, overseerContracts } from "../contract.js";
|
|
7
7
|
import { coordinatorRunIdOf } from "./coordinator.js";
|
|
8
8
|
import type { RunDirectory } from "./run-directory.js";
|
|
9
9
|
|
|
@@ -36,7 +36,7 @@ const create = async (context: ManagementContext, body: CreateInput, access: Acc
|
|
|
36
36
|
const runId = await (async () => {
|
|
37
37
|
if ("packageDirectory" in body) {
|
|
38
38
|
if (!path.isAbsolute(body.packageDirectory)) throw new DomainError("invalid-package-directory", "packageDirectory muss ein absoluter Serverpfad sein", 400);
|
|
39
|
-
return context.management().create({ ...common, kind: "package", directory: body.packageDirectory, input: (body.input ?? null) as JsonValue });
|
|
39
|
+
return context.management().create({ ...common, kind: "package", directory: body.packageDirectory, input: (body.input ?? null) as JsonValue, owner: OVERSEER_PLUGIN_ID });
|
|
40
40
|
}
|
|
41
41
|
if ("script" in body) {
|
|
42
42
|
const entries = context.root.startEntries.describe().filter((entry) => entry.action === "script");
|
|
@@ -33,10 +33,11 @@ import {
|
|
|
33
33
|
type WorkspaceClientDescription,
|
|
34
34
|
} from "../contract";
|
|
35
35
|
|
|
36
|
+
/** mismatch: Arbeitsplatz und Server tragen einen anderen Stand von Executor oder Bundles; nur ein Update einer Seite behebt es. */
|
|
36
37
|
export type WorkspaceClientStatus =
|
|
37
38
|
| { kind: "idle" }
|
|
38
39
|
| { kind: "registered" }
|
|
39
|
-
| { kind: "failed"; message: string };
|
|
40
|
+
| { kind: "failed"; message: string; mismatch: boolean };
|
|
40
41
|
|
|
41
42
|
/** Was der Arbeitsplatz über sich sagt; ob seine Bash rg findet, ermittelt er selbst bei jeder Anmeldung. */
|
|
42
43
|
export interface WorkspaceClientIdentity extends Omit<WorkspaceClientDescription, "ripgrep"> {
|
|
@@ -76,6 +77,18 @@ const SIGN_OFF_TIMEOUT_MS = 3_000;
|
|
|
76
77
|
|
|
77
78
|
const messageOf = (cause: unknown): string => cause instanceof Error ? cause.message : String(cause);
|
|
78
79
|
|
|
80
|
+
/** Ein Stand dieses Arbeitsplatzes, der nicht zu dem des Servers passt. */
|
|
81
|
+
class StandMismatch extends Error {}
|
|
82
|
+
|
|
83
|
+
const STAND_REFUSALS = ["workspace-executor-version", "workspace-executor-contributions"];
|
|
84
|
+
|
|
85
|
+
/** Nach dem Fachcode statt nach der Klasse: RpcError kann in einem Prozess zweimal geladen sein. */
|
|
86
|
+
const isStandMismatch = (cause: unknown): boolean => {
|
|
87
|
+
if (cause instanceof StandMismatch) return true;
|
|
88
|
+
const code = typeof cause === "object" && cause !== null ? (cause as { domainCode?: unknown }).domainCode : undefined;
|
|
89
|
+
return typeof code === "string" && STAND_REFUSALS.includes(code);
|
|
90
|
+
};
|
|
91
|
+
|
|
79
92
|
/** Der echte Pfad des nächsten vorhandenen Elternordners, um die noch fehlenden Namen ergänzt. */
|
|
80
93
|
const realPathOf = async (target: string): Promise<string> => {
|
|
81
94
|
try {
|
|
@@ -126,7 +139,7 @@ const loadedContributions = async (
|
|
|
126
139
|
return Promise.all(wanted.map(async ({ plugin, stand }) => {
|
|
127
140
|
if (!PLUGIN_ID_PATTERN.test(plugin)) throw new Error(`Der Server nennt ${plugin} als Plugin; das ist keine Kennung eines Plugins`);
|
|
128
141
|
const loaded = await loadExecutorContribution(plugin, contributionFileOf(hostRoot, plugin), stand).catch((cause: unknown) => {
|
|
129
|
-
throw new
|
|
142
|
+
throw new StandMismatch(`${messageOf(cause)}. Der Host dieses Arbeitsplatzes (${hostRoot}) muss dieselben Bundles tragen wie der Server: `
|
|
130
143
|
+ "die RAgents-Erweiterung beziehungsweise das Paket @schlenkr/ragents in der Fassung des Servers, im Checkout pnpm build:plugins");
|
|
131
144
|
});
|
|
132
145
|
return prepareExecutorContribution(loaded, pluginToolsDirectory(hostDataDirectory(), plugin));
|
|
@@ -196,7 +209,7 @@ export class WorkspaceClient {
|
|
|
196
209
|
try {
|
|
197
210
|
await this.#connected();
|
|
198
211
|
} catch (cause) {
|
|
199
|
-
if (attachment === this.#attachment) this.#set({ kind: "failed", message: messageOf(cause) });
|
|
212
|
+
if (attachment === this.#attachment) this.#set({ kind: "failed", message: messageOf(cause), mismatch: false });
|
|
200
213
|
return;
|
|
201
214
|
}
|
|
202
215
|
if (attachment === this.#attachment) await this.#announce();
|
|
@@ -302,7 +315,7 @@ export class WorkspaceClient {
|
|
|
302
315
|
executor: WORKSPACE_EXECUTOR_VERSION,
|
|
303
316
|
}, { timeoutMs: REGISTER_TIMEOUT_MS }).catch((cause: unknown) => {
|
|
304
317
|
if (!(cause instanceof RpcError) || cause.code !== RPC_ERROR_CODES.methodNotFound) throw cause;
|
|
305
|
-
throw new
|
|
318
|
+
throw new StandMismatch(`Der Server kennt ${workspaceContracts.clients.contributions.id} nicht; er ist älter als dieser Arbeitsplatz `
|
|
306
319
|
+ `mit dem Executor ${WORKSPACE_EXECUTOR_VERSION}. Den Server auf die Fassung des Arbeitsplatzes aktualisieren.`);
|
|
307
320
|
});
|
|
308
321
|
const { contributions } = await this.#built(attachment, wanted);
|
|
@@ -319,7 +332,7 @@ export class WorkspaceClient {
|
|
|
319
332
|
}, { timeoutMs: REGISTER_TIMEOUT_MS });
|
|
320
333
|
if (attachment === this.#attachment) this.#set({ kind: "registered" });
|
|
321
334
|
} catch (cause) {
|
|
322
|
-
if (attachment === this.#attachment) this.#set({ kind: "failed", message: messageOf(cause) });
|
|
335
|
+
if (attachment === this.#attachment) this.#set({ kind: "failed", message: messageOf(cause), mismatch: isStandMismatch(cause) });
|
|
323
336
|
}
|
|
324
337
|
}
|
|
325
338
|
|
|
@@ -327,7 +340,7 @@ export class WorkspaceClient {
|
|
|
327
340
|
try {
|
|
328
341
|
await this.transport.rpc.call(workspaceContracts.clients.unregister, { id: this.id }, { timeoutMs: SIGN_OFF_TIMEOUT_MS });
|
|
329
342
|
} catch (cause) {
|
|
330
|
-
this.#set({ kind: "failed", message: messageOf(cause) });
|
|
343
|
+
this.#set({ kind: "failed", message: messageOf(cause), mismatch: false });
|
|
331
344
|
}
|
|
332
345
|
}
|
|
333
346
|
|
|
@@ -16,6 +16,9 @@ export interface ProcessSandboxSetting {
|
|
|
16
16
|
readonly network: readonly string[];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/** So schaltet der Betreiber die Prozess-Sandbox in der Profildatei ab. */
|
|
20
|
+
export const PROCESS_SANDBOX_OFF = 'PROCESS_SANDBOX: "off" in der Sektion ragents.workspace';
|
|
21
|
+
|
|
19
22
|
/** Ohne Angabe ist die Sandbox an; abschalten geht nur ausdrücklich mit "off". */
|
|
20
23
|
export const processSandboxSetting = (): ProcessSandboxSetting => {
|
|
21
24
|
const mode = env.optional("PROCESS_SANDBOX") ?? "on";
|
|
@@ -23,11 +23,11 @@ import { WORKSPACE_BINDING_OPTION_ID, WORKSPACE_METADATA_ID } from "../contract.
|
|
|
23
23
|
import { bindingOf, executorShellChapter, sessionMetadataOf, workspaceBindingOption, workspaceLocation, workspaceOwnerOf } from "./binding.js";
|
|
24
24
|
import { createBrowseChannel, createBrowseMethods } from "./browse-route.js";
|
|
25
25
|
import { clientMethods, WorkspaceClientRegistry } from "./clients.js";
|
|
26
|
-
import { bashSetting, bashTimeoutSetting, processSandboxSetting, rgSetting, workspaceConfigDescriptors } from "./config.js";
|
|
26
|
+
import { bashSetting, bashTimeoutSetting, PROCESS_SANDBOX_OFF, processSandboxSetting, rgSetting, workspaceConfigDescriptors } from "./config.js";
|
|
27
27
|
import { RunWorkspaceRuntime } from "./runtime.js";
|
|
28
28
|
|
|
29
29
|
const warnWithoutSandbox = (): void => {
|
|
30
|
-
console.warn(
|
|
30
|
+
console.warn(`Prozess-Sandbox ausgeschaltet (${PROCESS_SANDBOX_OFF}): Prozesse der Runs laufen auf dem Server ohne Sandbox.`);
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
const ragentsWorkspacePlugin = (skillPaths: () => Promise<readonly string[]>): RAgentsPlugin => ({
|
|
@@ -60,6 +60,7 @@ const ragentsWorkspacePlugin = (skillPaths: () => Promise<readonly string[]>): R
|
|
|
60
60
|
network: sandboxSetting.network,
|
|
61
61
|
serverAddress: host.service(hostAddressToken)(),
|
|
62
62
|
dataDirectory: path.dirname(host.storage.sessionsRoot),
|
|
63
|
+
disableSetting: PROCESS_SANDBOX_OFF,
|
|
63
64
|
})
|
|
64
65
|
: undefined;
|
|
65
66
|
const bash = bashSetting();
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import{a as De,r as t,j as e,D as oe,b as ce,d as G,B as b,X as Le,M as Pe,C as de,e as Me,f as Oe,g as He,h as Ie,u as Be,i as _e,k as Fe,l as Ue,m as Ke,A as Te,n as qe,o as We,p as $e,U as Qe,P as Ve,W as Ge,R as Xe,q as ae,s as V,t as Je,v as Ye,w as Ze,x as et,y as tt,z as st,F as nt,Q as rt,G as at,H as it}from"./mermaid-HWGCJPDP-DfsU5ofC.js";/**
|
|
2
|
-
* @license lucide-react v1.46.0 - ISC
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the ISC license.
|
|
5
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/const ue={name:"circle-question-mark",size:24,node:[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],aliases:["help-circle","circle-help"]};ue.node;const lt=De(ue);function ot(n,r,a,u){if(!r||n.source!==r||n.origin!==a)return;const i=n.data;if(!(!i||typeof i!="object"||Array.isArray(i))&&"type"in i){if(i.type==="ragents:help-ready")return{type:"ready"};if(i.type==="ragents:start-sample"&&"entry"in i&&typeof i.entry=="string"&&u.includes(i.entry))return{type:"start",entry:i.entry}}}function ct({onClose:n,sampleEntries:r,onStartSample:a}){const u=t.useRef(null),i=t.useRef(null),h=t.useRef(void 0),x=t.useRef(!1);t.useEffect(()=>()=>{var l;return(l=h.current)==null?void 0:l.call(h)},[]),t.useEffect(()=>{var j;const l=(j=i.current)==null?void 0:j.contentWindow;if(!l)return;const o=()=>l.postMessage({type:"ragents:help-context",canStartSamples:r.length>0,entries:r},window.location.origin),p=C=>{const c=ot(C,l,window.location.origin,r);(c==null?void 0:c.type)==="ready"&&o(),(c==null?void 0:c.type)==="start"&&!x.current&&(x.current=!0,a(c.entry))};return window.addEventListener("message",p),o(),()=>window.removeEventListener("message",p)},[a,r]);const m=()=>{var C;(C=h.current)==null||C.call(h);const l=i.current,o=l==null?void 0:l.contentDocument,p=o==null?void 0:o.defaultView;if(!l||!o||!p)return;o.documentElement.dataset.helpEmbedded="true";for(const c of o.querySelectorAll("a[href]"))new URL(c.href).origin!==window.location.origin&&(c.target="_blank",c.rel="noopener noreferrer");const j=c=>{var v;if(!c.defaultPrevented){if(c.key==="Escape")c.preventDefault(),n();else if(c.key==="Tab"){const g=[...o.querySelectorAll("a[href], button, input, select, textarea, summary, [tabindex]")].filter(y=>y.tabIndex>=0&&!y.matches(":disabled")&&y.getClientRects().length>0),O=c.shiftKey?g[0]:g.at(-1);if(!g.length||o.activeElement===O||o.activeElement===o.body){if(!c.shiftKey&&o.activeElement===o.body&&g.length)return;c.preventDefault(),(v=u.current)==null||v.focus()}}}};p.addEventListener("keydown",j),h.current=()=>p.removeEventListener("keydown",j)};return e.jsx(oe,{open:!0,onOpenChange:l=>{l||n()},children:e.jsxs(ce,{className:"gap-0 p-0",initialFocus:u,scope:"page",showCloseButton:!1,size:"full",children:[e.jsx(G,{className:"sr-only",children:"Hilfe"}),e.jsx(b,{"aria-label":"Hilfe schließen",className:"absolute top-3 right-4.5 z-1 rounded-full",onClick:n,ref:u,size:"icon",variant:"outline",children:e.jsx(Le,{})}),e.jsx("iframe",{className:"min-h-0 w-full min-w-0 flex-[1_1_0] bg-background",onLoad:m,ref:i,src:"/help/index.html",tabIndex:0,title:"RAgents: Homepage und Dokumentation"})]})})}const dt="button:not([disabled]), a[href], input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])",fe="pointer-events-auto shadow-pop";function ut({onBusy:n,onClose:r,onOpen:a,open:u,panels:i,registry:h,runs:x}){const m=t.useRef(null);return t.useEffect(()=>{if(!u)return;const l=requestAnimationFrame(()=>{var p;const o=m.current;!o||o.contains(document.activeElement)||(p=o.querySelector(dt))==null||p.focus({preventScroll:!0})});return()=>cancelAnimationFrame(l)},[u]),e.jsxs(Pe,{className:"pointer-events-none flex h-[min(900px,100%)] w-[min(1440px,100%)] flex-col rounded-none bg-transparent p-0 ring-0",keepMounted:!0,onClose:r,open:u,scope:"workspace",size:"full",children:[e.jsx(G,{className:"sr-only",children:"Übersicht"}),e.jsxs("div",{className:"flex min-h-0 min-w-0 flex-1 gap-4 max-[900px]:flex-col",id:"app-overview",ref:m,children:[i.map(({id:l,Panel:o})=>e.jsx(ft,{id:l,onBusy:n,onClose:r,onOpen:a,open:u,Panel:o,registry:h},l)),x&&e.jsx(ht,{...x})]})]})}function ft({id:n,onBusy:r,onClose:a,onOpen:u,open:i,Panel:h,registry:x}){const m=t.useCallback(l=>r(n,l),[n,r]);return t.useEffect(()=>()=>r(n,!1),[n,r]),e.jsx(de,{className:`${fe} min-h-0 min-w-0 flex-1 gap-0 p-0`,children:e.jsx(h,{onBusy:m,onClose:a,onOpen:u,open:i,registry:x})})}function ht({activeId:n,canCreate:r,canDelete:a,onCreate:u,onDelete:i,onOpen:h,registry:x,sessions:m,seenRevisions:l,unreachable:o}){const[p,j]=t.useState(),[C,c]=t.useState(!1),[v,g]=t.useState(()=>new Set),[O,y]=t.useState(!1),[N,B]=t.useState(!1),_=()=>{c(!1),g(new Set)},w=f=>{g(z=>{const H=new Set(z);return H.delete(f)||H.add(f),H})},E=async f=>{B(!0),j(void 0);try{await i(f)}catch(z){j(z instanceof Error?z.message:String(z))}finally{B(!1)}},S=async()=>{await E(m.filter(f=>v.has(f.id)).map(f=>f.id)),y(!1),_()};return e.jsx("section",{"aria-labelledby":"overview-runs-title",className:"flex min-h-0 min-w-0 flex-1 flex-col max-[900px]:flex-[1_1_50%]",children:e.jsxs(de,{className:`${fe} min-h-0 flex-1 gap-workspace-inset overflow-y-auto overscroll-contain pt-workspace-inset pr-6 pb-6 pl-6 max-[900px]:px-3 max-[900px]:pt-3.5 max-[900px]:pb-4`,children:[e.jsxs("header",{className:"flex flex-wrap items-center gap-x-3.5 gap-y-2.5",children:[e.jsxs("h2",{className:"text-[0.95rem] font-semibold",id:"overview-runs-title",children:["Runs ",e.jsx("span",{className:"ml-1 text-[0.8rem] font-medium tabular-nums text-muted-foreground",children:m.length})]}),o&&e.jsx("span",{className:"text-[0.72rem] font-medium text-destructive",role:"alert",children:"Server nicht erreichbar"}),a&&m.length>0&&e.jsx("div",{className:"ml-auto flex flex-wrap items-center gap-1",children:C?e.jsxs(e.Fragment,{children:[e.jsx(b,{onClick:()=>g(new Set(m.map(f=>f.id))),size:"sm",variant:"ghost",children:"Alle"}),e.jsx(b,{onClick:()=>g(new Set),size:"sm",variant:"ghost",children:"Keine"}),e.jsxs(b,{className:"ml-auto",disabled:v.size===0||N,onClick:()=>y(!0),size:"sm",variant:"destructive",children:["Löschen (",v.size,")"]}),e.jsx(b,{onClick:_,size:"sm",variant:"ghost",children:"Fertig"})]}):e.jsx(b,{onClick:()=>c(!0),size:"sm",variant:"ghost",children:"Auswählen"})})]}),p&&e.jsx("p",{className:"text-[0.72rem] font-medium text-destructive",role:"alert",children:p}),e.jsx("p",{className:"mb-5.5 text-[0.75rem] text-muted-foreground",children:"Nach letzter Aktivität im Run. Neue Aktivität ist seit Deinem letzten Ansehen hinzugekommen."}),e.jsx(Me,{seenRevisions:l,activeId:n,onCreate:r?u:void 0,onDelete:a?f=>void E([f]):void 0,onSelect:h,onToggleSelected:w,registry:x,selectMode:C,selectedIds:v,sessions:m}),O&&e.jsx(oe,{open:!0,onOpenChange:f=>{!f&&!N&&y(!1)},children:e.jsxs(ce,{scope:"page",showCloseButton:!1,size:"small",children:[e.jsxs(Oe,{children:[e.jsx(G,{children:"Runs löschen"}),e.jsx(He,{children:v.size===1?"Ein Run wird endgültig gelöscht.":`${v.size} Runs werden endgültig gelöscht.`})]}),e.jsxs(Ie,{children:[e.jsx(b,{disabled:N,onClick:()=>y(!1),variant:"outline",children:"Abbrechen"}),e.jsx(b,{disabled:N,onClick:()=>void S(),children:N?"Löscht ...":`Löschen (${v.size})`})]})]})})]})})}const ie="flex min-h-0 min-w-0 flex-1 flex-col",mt="inline-flex w-header min-h-header items-center justify-center border-r border-border text-primary transition-colors duration-100 not-disabled:cursor-pointer hover:not-disabled:bg-accent focus-visible:outline-2 focus-visible:outline-ring/60 focus-visible:-outline-offset-3 aria-expanded:bg-primary aria-expanded:text-primary-foreground aria-pressed:bg-primary aria-pressed:text-primary-foreground disabled:opacity-45";function xt(){var re;const n=Be(),r=_e((re=n.user)==null?void 0:re.id),a=n.can("runs.read"),u=a&&n.can("runs.write"),i=n.can("runs.inspect"),h=a&&n.can("runs.delete"),x=Fe(),m=t.useRef(null),l=t.useRef(null),[o,p]=t.useState(null),[j,C]=t.useState(null),[c,v]=t.useState(null),[g,O]=t.useState(null),[y,N]=t.useState(null),[B,_]=t.useState([]),[w,E]=t.useState(),[S,f]=t.useState(),[z,H]=t.useState(),[U,X]=t.useState(!1),[K,T]=t.useState(!1),[A,J]=t.useState(!1),[Y,D]=t.useState(),[me,xe]=t.useState(!1),[pe,ve]=t.useState(()=>new Set),[be,Z]=t.useState(!1),I=t.useRef(void 0),q=t.useRef(!1),P=t.useCallback(()=>{if(!a)return Promise.resolve();if(I.current)return q.current=!0,I.current;const s=async()=>{do{q.current=!1;try{_(await Ye()),Z(!1)}catch{Z(!0)}}while(q.current)};return I.current=s().finally(()=>{I.current=void 0}),I.current},[a]);t.useEffect(()=>{if(!a||w)return;P();const s=Ue.subscribe(Ke.channels.runs,{},()=>void P()),d=setInterval(()=>void P(),5e3);return()=>{s(),clearInterval(d)}},[w,a,P]);const L=t.useCallback((s=!1)=>{var d;J(!1),s&&((d=m.current)==null||d.focus({preventScroll:!0}))},[]),W=t.useCallback(()=>{D(void 0),xe(!0),J(!0)},[]),$=t.useCallback(()=>{A?L(!0):W()},[L,W,A]);t.useEffect(()=>{const s=d=>{if(d.defaultPrevented||d.repeat||d.altKey||d.shiftKey||d.key.toLowerCase()!=="i"||d.metaKey===d.ctrlKey)return;const R=m.current;!R||R.closest("[inert]")||(d.preventDefault(),$())};return window.addEventListener("keydown",s),()=>window.removeEventListener("keydown",s)},[$]);const ee=t.useCallback((s,d)=>ve(R=>{if(R.has(s)===d)return R;const M=new Set(R);return d?M.add(s):M.delete(s),M}),[]),te=B,ge=te.find(s=>s.id===S),we=t.useCallback(()=>{D(void 0),X(!0)},[]),je=t.useCallback(()=>T(!1),[]),ye=t.useCallback(()=>{X(!1),requestAnimationFrame(()=>{var s;return(s=l.current)==null?void 0:s.focus()})},[]),Se=e.jsx(b,{"aria-label":"Einstellungen öffnen",onClick:we,ref:l,size:"icon-lg",title:"Einstellungen",variant:"ghost",children:e.jsx(Ze,{})}),ke=s=>{f(s),L(!0)},Ce=()=>{D(void 0),L(!0),E({id:crypto.randomUUID(),title:"Neuer Run",updatedAt:Date.now()})},Re=async s=>{const d=await Promise.allSettled(s.map(F=>et(F))),R=s.filter((F,Ae)=>d[Ae].status==="fulfilled");S!==void 0&&R.includes(S)&&f(void 0),await P();const M=d.find(F=>F.status==="rejected");if(M)throw M.reason};if(x.status==="loading")return e.jsx("div",{className:"m-auto p-6 text-muted-foreground",children:"Profil wird geladen."});if(x.status==="failed")return e.jsx("div",{className:"flex h-full",children:e.jsx("main",{className:"relative z-1 flex min-h-0 min-w-0 flex-1 bg-[image:var(--surface-backdrop)]",children:e.jsxs("div",{className:ie,children:[e.jsx("header",{className:"flex min-h-[42px] flex-none items-center gap-3 border-b border-border-soft pr-0.5 pb-2 pl-2.5",children:e.jsx("div",{className:"flex min-w-0 flex-1 items-center gap-3 text-[0.82rem] font-semibold text-foreground",children:"Profil nicht verfügbar"})}),e.jsxs(Te,{className:"m-auto max-w-[560px] gap-3 p-6",variant:"destructive",children:[e.jsx("p",{children:x.error}),e.jsx(b,{variant:"outline",onClick:()=>window.location.reload(),children:"Neu laden"})]})]})})});const k=x.registry,se=k.overviewPanels.filter(s=>s.readRight===void 0||n.can(s.readRight)),Q=se.filter(s=>s.placement!=="toolbar"),Ne=se.filter(s=>s.placement==="toolbar"),Ee=a||Q.length>0,ne=Q.some(s=>pe.has(s.id)),ze=qe(a?S:void 0,A,z);return e.jsxs("div",{className:"flex h-full flex-col",children:[e.jsxs("header",{className:"relative z-[80] flex h-header flex-none items-stretch border-b border-border bg-shell shadow-bar",children:[Ee&&e.jsx("div",{className:"relative flex flex-none self-stretch",children:e.jsx("button",{"aria-controls":"app-overview","aria-expanded":A,"aria-haspopup":"dialog","aria-keyshortcuts":"Meta+I Control+I","aria-label":A?"Übersicht schließen":"Übersicht öffnen",className:mt,onClick:$,ref:m,title:`Übersicht (Cmd+I / Ctrl+I)${ne?": Bearbeitung läuft":""}`,type:"button",children:ne?e.jsx(We,{"aria-hidden":!0,className:"size-[18px]"}):e.jsx($e,{size:22})})}),Ne.map(({id:s,Panel:d})=>e.jsx(pt,{id:s,onBusy:ee,onClose:()=>D(void 0),onOpen:()=>{!U&&!K&&!w&&(L(),D(s))},open:Y===s,Panel:d,registry:k,userLocation:ze},s)),e.jsx("div",{"aria-label":"Run-Titelleiste",className:"flex min-w-0 flex-1 items-stretch overflow-x-auto no-scrollbar focus-visible:outline-2 focus-visible:outline-ring/60 focus-visible:-outline-offset-3 max-md:in-[header:has([data-slot=overseer-toolbar][data-open=true])]:flex-[0_1_0]",ref:p,role:"region",tabIndex:0}),e.jsx(Qe,{}),e.jsxs("div",{className:"flex min-w-0 flex-[0_1_auto] items-stretch bg-shell has-data-[workspace-state=expanded]:w-[var(--workspace-panel-width,560px)] has-data-[workspace-state=expanded]:border-l has-data-[workspace-state=expanded]:border-border",children:[e.jsx("div",{className:"flex min-w-0 flex-1 empty:hidden",ref:C}),e.jsxs("div",{className:"flex flex-none items-center gap-1.5 px-2 max-md:gap-0.5 max-md:px-1",children:[n.can("settings.read")&&Se,i&&e.jsx(b,{"aria-haspopup":"dialog","aria-label":"Hilfe öffnen",onClick:()=>{D(void 0),T(!0)},size:"icon-lg",title:"Hilfe",variant:"ghost",children:e.jsx(lt,{})})]})]})]}),e.jsx(Ve,{failures:x.failures}),e.jsx(Ge.Provider,{value:g,children:e.jsx("div",{className:"relative flex min-h-0 flex-1 flex-col",ref:O,children:e.jsxs(Xe.Provider,{value:y,children:[e.jsxs("div",{className:"relative flex h-auto min-h-0 flex-1",ref:N,children:[e.jsx("main",{className:"relative z-1 flex min-h-0 min-w-0 flex-1 bg-[image:var(--surface-backdrop)] @container/chat-content",children:a&&S?e.jsx(ae,{headerContainer:o,workspaceHeaderContainer:j,statusContainer:c,onLocationChange:H,onViewed:r.markViewed,viewing:!A&&!U&&!K&&!w&&!Y,registry:k,session:ge??{id:S,title:"Neuer Run",updatedAt:Date.now()}},S):e.jsx("div",{className:ie,children:e.jsx("div",{className:"m-auto text-center text-muted-foreground",children:a?u?"Öffne oben links die Übersicht, um einen Run zu wählen oder einen neuen zu starten.":"Öffne oben links die Übersicht, um einen Run zu wählen. Du hast Lesezugriff.":"Für dieses Benutzerkonto sind keine Runs freigegeben."})})}),me&&e.jsx(ut,{onOpen:W,onBusy:ee,onClose:()=>L(!0),open:A,panels:Q,registry:k,runs:a?{activeId:S,canCreate:u&&(n.can("runs.create")||k.scriptEntries.some(s=>V(n,s.id))),canDelete:h,onCreate:Ce,onDelete:Re,onOpen:ke,registry:k,sessions:te,seenRevisions:r.revisions,unreachable:be}:void 0}),w&&e.jsx(ae,{onStarted:()=>{f(w.id),E(void 0),P()},registry:k,session:w,startDialog:{onClose:()=>E(void 0),initialEntryId:w.startEntryId}},w.id),U&&e.jsx(Je,{onClose:ye,registry:k}),i&&K&&e.jsx(ct,{onClose:je,sampleEntries:k.scriptEntries.filter(s=>V(n,s.id)).map(s=>s.id),onStartSample:s=>{!V(n,s)||!k.scriptEntries.some(d=>d.id===s)||(T(!1),D(void 0),L(),E({id:crypto.randomUUID(),title:"Neuer Run",updatedAt:Date.now(),startEntryId:s}))}})]}),e.jsx("footer",{"aria-label":"Run-Statusleiste",className:"relative flex h-statusbar w-full flex-none items-stretch pl-3 before:pointer-events-none before:absolute before:inset-0 before:z-[2] before:border-t before:border-border before:bg-shell before:shadow-status before:backdrop-blur-[16px] before:content-['']",ref:v})]})})})]})}function pt({id:n,onBusy:r,Panel:a,...u}){const i=t.useCallback(h=>r(n,h),[n,r]);return t.useEffect(()=>()=>r(n,!1),[n,r]),e.jsx(a,{...u,onBusy:i})}tt();const he=document.getElementById("root");if(!he)throw new Error("Root-Element fehlt");const le=st.createRoot(he);try{const n=nt(window);le.render(e.jsx(t.StrictMode,{children:e.jsx(rt,{children:e.jsx(at,{children:e.jsx(xt,{})})})}))}catch(n){le.render(e.jsxs(it,{children:[e.jsx("h1",{className:"my-3 text-[1.5rem]",children:"Darstellung konnte nicht geladen werden"}),e.jsx("p",{className:"mb-6 leading-normal",role:"alert",children:n instanceof Error?n.message:String(n)}),e.jsx(b,{onClick:()=>window.location.reload(),variant:"outline",children:"Erneut laden"})]}))}
|