@schlenkr/ragents 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/apps/server/src/config-definition.ts +24 -2
  2. package/apps/server/src/config.ts +0 -6
  3. package/apps/server/src/host-api.json +3 -10
  4. package/apps/server/src/host-api.ts +6 -7
  5. package/apps/server/src/plugin-build/build.ts +28 -5
  6. package/apps/server/src/plugin-build/plugin-description.ts +1 -1
  7. package/apps/server/src/plugin-build/typecheck.ts +2 -2
  8. package/apps/server/src/plugin-support/language-server/plugin.ts +8 -7
  9. package/apps/server/src/plugin-support/language-server/tools.ts +7 -6
  10. package/apps/server/src/plugin-support/model-aliases.ts +55 -25
  11. package/apps/server/src/plugin-support/product-relay.ts +10 -2
  12. package/apps/server/src/plugin-support/workspace-sandbox-host.ts +18 -9
  13. package/apps/server/src/profile/bundle-manifest.ts +7 -3
  14. package/apps/server/src/profile/compose.ts +6 -0
  15. package/apps/server/src/profile/plugin-discovery.ts +6 -1
  16. package/apps/server/src/profile/provisioning.ts +6 -5
  17. package/apps/server/src/ragents/engine.ts +0 -1
  18. package/apps/server/src/ragents/host-services.ts +4 -0
  19. package/apps/server/src/ragents/session.ts +1 -1
  20. package/apps/server/src/server.ts +1 -0
  21. package/apps/server/tsconfig.json +2 -0
  22. package/apps/web/dist/help/guide-clients.html +14 -6
  23. package/apps/web/dist/help/guide-clients.md +14 -6
  24. package/apps/web/dist/help/guide-distributed.html +12 -4
  25. package/apps/web/dist/help/guide-distributed.md +12 -4
  26. package/apps/web/dist/help/guide-getting-started.html +12 -8
  27. package/apps/web/dist/help/guide-getting-started.md +12 -8
  28. package/apps/web/dist/help/guide-plugins.html +6 -2
  29. package/apps/web/dist/help/guide-plugins.md +6 -2
  30. package/apps/web/dist/help/guide-runtime.html +2 -1
  31. package/apps/web/dist/help/guide-runtime.md +2 -1
  32. package/apps/web/dist/help/index.html +3328 -1135
  33. package/apps/web/dist/host-web.json +3 -3
  34. package/bundles/ragents.activity/ragents-bundle.json +3 -3
  35. package/bundles/ragents.actor-programs/ragents-bundle.json +3 -3
  36. package/bundles/ragents.ask/ragents-bundle.json +4 -4
  37. package/bundles/ragents.ask/server/index.js +42 -11
  38. package/bundles/ragents.ask/server/index.js.map +3 -3
  39. package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js +2 -0
  40. package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js.map +7 -0
  41. package/bundles/ragents.ask/web/classes.json +1 -1
  42. package/bundles/ragents.ask/web/exports/ask-payload.js +1 -1
  43. package/bundles/ragents.ask/web/index.js +1 -1
  44. package/bundles/ragents.ask/web/index.js.map +3 -3
  45. package/bundles/ragents.browser/executor/index.mjs +357 -0
  46. package/bundles/ragents.browser/executor/index.mjs.map +7 -0
  47. package/bundles/ragents.browser/ragents-bundle.json +5 -8
  48. package/bundles/ragents.browser/server/chunks/{chunk-2GKLLCBE.js → chunk-CWLX4VA5.js} +1 -1
  49. package/bundles/ragents.browser/server/chunks/chunk-CWLX4VA5.js.map +7 -0
  50. package/bundles/ragents.browser/server/exports/server/contract.js +1 -1
  51. package/bundles/ragents.browser/server/index.js +20 -9
  52. package/bundles/ragents.browser/server/index.js.map +4 -4
  53. package/bundles/ragents.documents/ragents-bundle.json +3 -3
  54. package/bundles/ragents.lsp-fsharp/executor/index.mjs +68 -0
  55. package/bundles/ragents.lsp-fsharp/executor/index.mjs.map +7 -0
  56. package/bundles/ragents.lsp-fsharp/ragents-bundle.json +5 -9
  57. package/bundles/ragents.lsp-fsharp/server/index.js +21 -6
  58. package/bundles/ragents.lsp-fsharp/server/index.js.map +4 -4
  59. package/bundles/ragents.lsp-roslyn/executor/index.mjs +68 -0
  60. package/bundles/ragents.lsp-roslyn/executor/index.mjs.map +7 -0
  61. package/bundles/ragents.lsp-roslyn/ragents-bundle.json +6 -8
  62. package/bundles/ragents.lsp-roslyn/server/index.js +54 -15
  63. package/bundles/ragents.lsp-roslyn/server/index.js.map +4 -4
  64. package/bundles/ragents.lsp-typescript/executor/index.mjs +49 -0
  65. package/bundles/ragents.lsp-typescript/executor/index.mjs.map +7 -0
  66. package/bundles/ragents.lsp-typescript/ragents-bundle.json +5 -7
  67. package/bundles/ragents.lsp-typescript/server/index.js +22 -2
  68. package/bundles/ragents.lsp-typescript/server/index.js.map +3 -3
  69. package/bundles/ragents.model-relay/ragents-bundle.json +4 -4
  70. package/bundles/ragents.model-relay/server/index.js +3 -2
  71. package/bundles/ragents.model-relay/server/index.js.map +2 -2
  72. package/bundles/ragents.orchestration/ragents-bundle.json +3 -3
  73. package/bundles/ragents.overseer/ragents-bundle.json +3 -3
  74. package/bundles/ragents.processes/ragents-bundle.json +3 -3
  75. package/bundles/ragents.product/ragents-bundle.json +3 -3
  76. package/bundles/ragents.profile-distribution/ragents-bundle.json +4 -4
  77. package/bundles/ragents.profile-distribution/server/index.js +1 -1
  78. package/bundles/ragents.profile-distribution/server/index.js.map +2 -2
  79. package/bundles/ragents.reference/ragents-bundle.json +3 -3
  80. package/bundles/ragents.todo/ragents-bundle.json +3 -3
  81. package/bundles/ragents.transcript/ragents-bundle.json +3 -3
  82. package/bundles/ragents.watch/ragents-bundle.json +3 -3
  83. package/bundles/ragents.workspace/ragents-bundle.json +11 -5
  84. package/bundles/ragents.workspace/server/index.js +101 -28
  85. package/bundles/ragents.workspace/server/index.js.map +3 -3
  86. package/bundles/ragents.workspace/web/classes.json +1 -1
  87. package/bundles/ragents.workspace/web/index.js +1 -1
  88. package/bundles/ragents.workspace/web/index.js.map +3 -3
  89. package/package.json +2 -2
  90. package/packages/agent/dist/core/agent-settings.d.ts +1 -7
  91. package/packages/agent/dist/core/compaction/compaction.d.ts +17 -13
  92. package/packages/agent/dist/core/model-runtime.d.ts +5 -4
  93. package/packages/agent/dist/core/provider-composer.d.ts +2 -1
  94. package/packages/agent/dist/core/tools/bash.d.ts +9 -3
  95. package/packages/agent/dist/index.d.ts +1 -1
  96. package/packages/agent/src/core/agent-settings.ts +1 -4
  97. package/packages/agent/src/core/compaction/compaction.ts +68 -30
  98. package/packages/agent/src/core/model-runtime.ts +12 -5
  99. package/packages/agent/src/core/provider-composer.ts +5 -0
  100. package/packages/agent/src/core/tools/bash.ts +36 -30
  101. package/packages/agent/src/index.ts +3 -0
  102. package/packages/ai/dist/types.d.ts +11 -0
  103. package/packages/ai/src/types.ts +12 -0
  104. package/packages/ragents/src/domain/event-semantics.ts +3 -0
  105. package/packages/ragents/src/domain/event-validation.ts +15 -2
  106. package/packages/ragents/src/domain/events.ts +3 -0
  107. package/packages/ragents/src/domain/model.ts +2 -0
  108. package/packages/ragents/src/domain/projection.ts +1 -0
  109. package/packages/ragents/src/drivers/agent-runtime.ts +1 -1
  110. package/packages/ragents/src/drivers/agent-turn.ts +8 -6
  111. package/packages/ragents/src/runtime/decisions/inputs.ts +6 -2
  112. package/packages/ragents/src/runtime/journal-storage.ts +5 -4
  113. package/packages/workspace-executor/package.json +0 -1
  114. package/packages/workspace-executor/src/bash-launch.ts +56 -10
  115. package/packages/workspace-executor/src/context.ts +4 -0
  116. package/packages/workspace-executor/src/contributions.ts +150 -0
  117. package/packages/workspace-executor/src/executor.ts +1 -1
  118. package/packages/workspace-executor/src/index.ts +16 -19
  119. package/packages/workspace-executor/src/language-server/host.ts +3 -0
  120. package/packages/workspace-executor/src/language-server/module.ts +12 -2
  121. package/packages/workspace-executor/src/language-server/roots.ts +0 -5
  122. package/packages/workspace-executor/src/modules.ts +7 -6
  123. package/packages/workspace-executor/src/sandbox-tools.ts +3 -9
  124. package/packages/workspace-executor/src/shell-platform.ts +14 -3
  125. package/packages/workspace-executor/src/tools.ts +0 -3
  126. package/plugins/ragents.ask/ask-payload.ts +9 -0
  127. package/plugins/ragents.ask/server/ask-service.ts +53 -13
  128. package/plugins/ragents.ask/web/AskActionView.tsx +3 -2
  129. package/plugins/ragents.browser/executor/index.ts +5 -0
  130. package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/module.ts +25 -31
  131. package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/playwright.ts +9 -23
  132. package/plugins/ragents.browser/provision.ts +1 -1
  133. package/plugins/ragents.browser/server/browser.ts +3 -3
  134. package/plugins/ragents.browser/server/contract.ts +2 -2
  135. package/plugins/ragents.browser/server/index.ts +1 -1
  136. package/plugins/ragents.lsp-fsharp/executor.ts +80 -0
  137. package/plugins/ragents.lsp-fsharp/provision.ts +1 -1
  138. package/plugins/ragents.lsp-fsharp/server/index.ts +2 -2
  139. package/plugins/ragents.lsp-roslyn/executor.ts +64 -0
  140. package/plugins/ragents.lsp-roslyn/provision.ts +1 -1
  141. package/plugins/ragents.lsp-roslyn/server/index.ts +28 -5
  142. package/plugins/ragents.lsp-roslyn/server/solution-on-start.ts +18 -6
  143. package/plugins/ragents.lsp-typescript/executor.ts +50 -0
  144. package/plugins/ragents.lsp-typescript/server/index.ts +2 -2
  145. package/plugins/ragents.model-relay/server/config.ts +1 -1
  146. package/plugins/ragents.model-relay/server/relay.ts +4 -2
  147. package/plugins/ragents.profile-distribution/server/archive.ts +2 -1
  148. package/plugins/ragents.workspace/client/workspace-client.ts +111 -28
  149. package/plugins/ragents.workspace/contract.ts +33 -2
  150. package/plugins/ragents.workspace/server/binding.ts +10 -4
  151. package/plugins/ragents.workspace/server/clients.ts +56 -9
  152. package/plugins/ragents.workspace/server/config.ts +9 -0
  153. package/plugins/ragents.workspace/server/index.ts +13 -4
  154. package/plugins/ragents.workspace/server/runtime.ts +10 -0
  155. package/plugins/ragents.workspace/server/shell-platform.ts +6 -5
  156. package/plugins/ragents.workspace/web/WorkspaceBinding.tsx +1 -1
  157. package/ragents.config.core.ts +4 -1
  158. package/ragents.config.showcase.ts +4 -1
  159. package/scripts/agent/journal.ts +3 -1
  160. package/scripts/package/build-package.ts +3 -1
  161. package/scripts/provision/run-provision.ts +2 -2
  162. package/scripts/workspace-client/run-workspace-client.ts +7 -4
  163. package/apps/web/dist/help/screenshots/release-checklist-vscode.png +0 -0
  164. package/apps/web/dist/help/screenshots/release-checklist-web.png +0 -0
  165. package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js +0 -2
  166. package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js.map +0 -7
  167. package/bundles/ragents.browser/server/chunks/chunk-2GKLLCBE.js.map +0 -7
  168. package/packages/workspace-executor/src/language-server/adapters/fsharp.ts +0 -60
  169. package/packages/workspace-executor/src/language-server/adapters/index.ts +0 -7
  170. package/packages/workspace-executor/src/language-server/adapters/roslyn.ts +0 -56
  171. package/packages/workspace-executor/src/language-server/adapters/typescript.ts +0 -74
  172. package/packages/workspace-executor/src/language-server/solution.ts +0 -13
  173. /package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/contract.ts +0 -0
  174. /package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/pages.ts +0 -0
@@ -22,8 +22,3 @@ export const resolveRootDirectory = async (workspaceRoot: string, requested: str
22
22
  if (!info?.isDirectory()) throw new Error(`${requested} ist kein Verzeichnis im Arbeitsverzeichnis`);
23
23
  return absolute;
24
24
  };
25
-
26
- export const dotnetCommand = (server: string, args: readonly string[]): { command: string; args: string[] } =>
27
- server.toLowerCase().endsWith(".dll")
28
- ? { command: "dotnet", args: [server, ...args] }
29
- : { command: server, args: [...args] };
@@ -1,7 +1,6 @@
1
- import { browserModule } from "./browser/module.js";
2
1
  import { commandModule } from "./commands.js";
2
+ import type { WorkspaceExecutorParts } from "./contributions.js";
3
3
  import { fileModule } from "./files.js";
4
- import { workspaceLanguageServers } from "./language-server/adapters/index.js";
5
4
  import { languageServerModule } from "./language-server/module.js";
6
5
  import type { WorkspaceModuleFactory } from "./module.js";
7
6
  import { processModule } from "./processes/module.js";
@@ -11,15 +10,17 @@ import { sandboxToolsModule } from "./sandbox-tools.js";
11
10
  export interface WorkspaceExecutorModuleOptions {
12
11
  /** Der neue Ordner eines Runs auf dieser Maschine; nur ein Arbeitsplatz kennt ihn, auf dem Server legt ihn der Host an. */
13
12
  runFolder?: (runId: string) => string;
13
+ /** Die Beiträge der Plugins, gebaut für diese Maschine; Server und Arbeitsplatz bekommen dieselben. */
14
+ contributions: readonly WorkspaceExecutorParts[];
14
15
  }
15
16
 
16
- /** Die Module jedes Executors, im Server wie auf dem Arbeitsplatz dieselben. */
17
- export const workspaceExecutorModules = (options: WorkspaceExecutorModuleOptions = {}): readonly WorkspaceModuleFactory[] => [
17
+ /** Die Module eines Executors: die eigenen und was die Beiträge der Plugins hinzufügen. */
18
+ export const workspaceExecutorModules = (options: WorkspaceExecutorModuleOptions): readonly WorkspaceModuleFactory[] => [
18
19
  sandboxToolsModule,
19
- languageServerModule(workspaceLanguageServers),
20
+ languageServerModule(options.contributions.flatMap((parts) => parts.languageServers ?? [])),
20
21
  fileModule,
21
22
  processModule(),
22
23
  commandModule(),
23
- browserModule(),
24
+ ...options.contributions.flatMap((parts) => parts.modules ?? []),
24
25
  runFolderModule(options.runFolder),
25
26
  ];
@@ -40,11 +40,6 @@ export interface SandboxTools {
40
40
  shutdown: () => Promise<void>;
41
41
  }
42
42
 
43
- export const DEFAULT_BASH_TIMEOUT_SECONDS = (() => {
44
- const value = Number(process.env.RAGENTS_BASH_TIMEOUT_SECONDS);
45
- return Number.isFinite(value) && value > 0 ? value : 600;
46
- })();
47
-
48
43
  type ToolResult = { content?: Array<{ type: string; text?: string }>; details?: unknown };
49
44
 
50
45
  /** Der Stand einer Datei, den das Modell zuletzt gesehen hat; der Host führt ihn je Actor und gibt ihn mit, das Modell nennt ihn nie. */
@@ -269,7 +264,7 @@ export const createSandboxTools = async (
269
264
  const context = await contextFor();
270
265
  if (shuttingDown) throw new Error("Die Werkzeuge werden killed");
271
266
  options.signal?.throwIfAborted();
272
- const bash = bashLaunch(context.bash, command, context.env);
267
+ const bash = bashLaunch({ bash: context.bash, rg: context.rg }, command, context.env);
273
268
  const launch = await sandboxedLaunch(context, bash);
274
269
  if (shuttingDown) throw new Error("Die Werkzeuge werden killed");
275
270
  options.signal?.throwIfAborted();
@@ -303,11 +298,10 @@ export const createSandboxTools = async (
303
298
  }
304
299
  });
305
300
  };
306
- const timeoutSeconds = options.timeout ?? DEFAULT_BASH_TIMEOUT_SECONDS;
307
301
  const timer = setTimeout(() => {
308
302
  timedOut = true;
309
303
  kill();
310
- }, timeoutSeconds * 1000);
304
+ }, options.timeout * 1000);
311
305
  options.signal?.addEventListener("abort", kill);
312
306
  const cleanup = () => {
313
307
  if (timer) clearTimeout(timer);
@@ -322,7 +316,7 @@ export const createSandboxTools = async (
322
316
  const finish = () => processError
323
317
  ? reject(processError)
324
318
  : timedOut
325
- ? reject(new Error(`timeout:${timeoutSeconds}`))
319
+ ? reject(new Error(`timeout:${options.timeout}`))
326
320
  : resolve({ exitCode: killed ? null : code });
327
321
  if (!child.pid) {
328
322
  finish();
@@ -1,11 +1,22 @@
1
1
  const platformTexts: Partial<Record<NodeJS.Platform, string>> = {
2
- darwin: "`bash` runs on macOS with the BSD userland: `grep` has no `-P` (use `grep -E` or `rg`), in-place `sed` needs an explicit backup suffix (`sed -i '' ...`), `date` uses `-v` instead of `-d`, `xargs` uses `-I{}` instead of `-i`, and other GNU-only flags are unavailable. `/bin/bash` is version 3.2: no associative arrays (`declare -A`), no `mapfile`/`readarray`, no `${var,,}` case conversion.",
2
+ darwin: "`bash` runs on macOS with the BSD userland: `grep` has no `-P` (use `grep -E`), in-place `sed` needs an explicit backup suffix (`sed -i '' ...`), `date` uses `-v` instead of `-d`, `xargs` uses `-I{}` instead of `-i`, and other GNU-only flags are unavailable. `/bin/bash` is version 3.2: no associative arrays (`declare -A`), no `mapfile`/`readarray`, no `${var,,}` case conversion.",
3
3
  linux: "`bash` runs on Linux with the GNU userland: `grep -P`, `sed -i` without suffix and the usual GNU flags work.",
4
4
  win32: "`bash` runs on Windows with the bash RAgents brings along, an MSYS userland with the GNU tools (coreutils, `grep`, `sed`, `awk`, `find`, `xargs`, `diff`, `patch`, `tar`, `gzip`, `unzip`, `file`, `cygpath`, `dos2unix`): `grep -P`, `sed -i` without suffix and the usual GNU flags work. `git`, `dotnet` and `node` are the Windows programs of this machine on the `PATH`, with the user's own configuration and login; they take Windows paths, not `/c/...` (`cygpath -w` converts). The file system is the Windows one: absolute paths are `C:/project/...` or `/c/project/...`, both spellings reach the same file, and a path from another tool may use backslashes. Files often carry CRLF line endings, so anchor patterns with `\\r\\?$` and keep the line ending a file already has.",
5
5
  };
6
6
 
7
- export const shellPlatformText = (platform: NodeJS.Platform = process.platform): string => {
7
+ const searchTexts = {
8
+ ripgrep: "Search code with `rg` (ripgrep) and list files with `rg --files`: it skips hidden files and everything `.gitignore` excludes, such as `node_modules`, `bin` and `obj`, and is far faster than `grep -r` (`rg -uu` searches those too). Use `grep` only on single files or in pipes.",
9
+ grep: "`rg` (ripgrep) is not available here. `grep -r` also walks dependency and build folders, so exclude them: `grep -r --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=bin --exclude-dir=obj ...`.",
10
+ };
11
+
12
+ /** Was der Executor dem Werkzeug bash über die Plattform hinaus bietet. */
13
+ export interface ShellTools {
14
+ readonly ripgrep: boolean;
15
+ }
16
+
17
+ export const shellPlatformText = (platform: NodeJS.Platform, tools: ShellTools): string => {
8
18
  const text = platformTexts[platform];
9
19
  if (!text) throw new Error(`Für die Plattform ${platform} gibt es keine Shell-Beschreibung.`);
10
- return `${text} A nonzero exit code, for example \`grep\` without a match, comes back as an ordinary result with the code at the end, not as a tool error; only start, timeout and abort problems are tool errors.`;
20
+ const search = tools.ripgrep ? searchTexts.ripgrep : searchTexts.grep;
21
+ return `${text} ${search} A nonzero exit code, for example \`grep\` without a match, comes back as an ordinary result with the code at the end, not as a tool error; only start, timeout and abort problems are tool errors.`;
11
22
  };
@@ -23,6 +23,3 @@ export const workspaceDataDirectory = (home = homedir()): string => path.join(ra
23
23
  /** Der Datenordner dieses Prozesses: DATA_DIR wie beim Server, sonst der des Arbeitsplatzes ohne Profil. */
24
24
  export const hostDataDirectory = (environment: NodeJS.ProcessEnv = process.env): string =>
25
25
  path.resolve(environment.DATA_DIR ?? workspaceDataDirectory());
26
-
27
- export const hostToolFile = (pluginId: string, relative: string): string =>
28
- path.join(pluginToolsDirectory(hostDataDirectory(), pluginId), ...relative.split("/"));
@@ -1,5 +1,8 @@
1
1
  export const ASK_PLUGIN_ID = "ragents.ask";
2
2
 
3
+ /** What the asking tool call gets when a person's message to the asker closes its question. */
4
+ export const SUPERSEDED_ANSWER = "Not answered: the user sent a new message instead.";
5
+
3
6
  export interface AskPayload {
4
7
  question: string;
5
8
  options: string[];
@@ -23,3 +26,9 @@ export const askPayloadOf = (payload: unknown): AskPayload | undefined => {
23
26
  ...(typeof candidate.recipient === "string" ? { recipient: candidate.recipient } : {}),
24
27
  };
25
28
  };
29
+
30
+ export const supersedingInputOf = (result: unknown): string | undefined => {
31
+ if (typeof result !== "object" || result === null) return undefined;
32
+ const { supersededBy } = result as { supersededBy?: unknown };
33
+ return typeof supersededBy === "string" ? supersededBy : undefined;
34
+ };
@@ -4,20 +4,34 @@ import {
4
4
  type Action,
5
5
  type CommandContext,
6
6
  type JournalEvent,
7
+ type JsonValue,
7
8
  type Orchestration,
8
9
  } from "@ragents/engine";
9
- import { askPayloadOf, ASK_PLUGIN_ID } from "../ask-payload.js";
10
+ import { askPayloadOf, ASK_PLUGIN_ID, SUPERSEDED_ANSWER, supersedingInputOf } from "../ask-payload.js";
10
11
  import { DISMISSED_ANSWER, type AskCall, type AskRequest, type AskService } from "./contract.js";
11
12
 
12
13
  const answerOf = (decision: "approved" | "dismissed", result: unknown): string =>
13
- decision !== "approved" ? DISMISSED_ANSWER : typeof result === "string" ? result : result === null || result === undefined ? "" : JSON.stringify(result);
14
+ decision !== "approved"
15
+ ? supersedingInputOf(result) === undefined ? DISMISSED_ANSWER : SUPERSEDED_ANSWER
16
+ : typeof result === "string" ? result : result === null || result === undefined ? "" : JSON.stringify(result);
14
17
 
15
18
  const resolvedAnswerOf = (action: Action): string | null =>
16
19
  action.status === "pending" ? null : answerOf(action.status, action.result);
17
20
 
21
+ const humanInputPendingFor = (runtime: Orchestration, runId: string, actorId: string): boolean =>
22
+ [...runtime.state(runId).inputs.values()].some((input) =>
23
+ input.actorId === actorId && input.origin === "human" && input.lifecycle.kind === "pending");
24
+
25
+ interface Waiter {
26
+ runId: string;
27
+ /** The actor whose running turn waits for the answer; null for a question asked outside a turn. */
28
+ blockedActorId: string | null;
29
+ settle: (answer: string) => void;
30
+ }
31
+
18
32
  export class RuntimeAskService implements AskService {
19
33
  #runtime: Orchestration | undefined;
20
- readonly #waiters = new Map<string, (answer: string) => void>();
34
+ readonly #waiters = new Map<string, Waiter>();
21
35
  readonly #aborting = new Set<string>();
22
36
 
23
37
  bind(runtime: Orchestration): void {
@@ -28,6 +42,7 @@ export class RuntimeAskService implements AskService {
28
42
 
29
43
  ask(call: AskCall, request: AskRequest, signal: AbortSignal | undefined): Promise<string> {
30
44
  const runtime = this.#requireRuntime();
45
+ if (call.turnId && humanInputPendingFor(runtime, call.runId, call.agentId)) return Promise.resolve(SUPERSEDED_ANSWER);
31
46
  const context: CommandContext = {
32
47
  actorId: call.agentId,
33
48
  commandId: call.commandId,
@@ -51,7 +66,7 @@ export class RuntimeAskService implements AskService {
51
66
  if (proposed?.type !== "action.proposed") {
52
67
  throw new Error(`Die Frage von ${call.agentId} wurde nicht im Journal von ${call.runId} angelegt`);
53
68
  }
54
- return this.#awaitAnswer(call.runId, proposed.payload.actionId, signal);
69
+ return this.#awaitAnswer(call.runId, proposed.payload.actionId, call.turnId ? call.agentId : null, signal);
55
70
  }
56
71
 
57
72
  answer(runId: string, actionId: string, input: { answer?: string; dismiss?: boolean }): void {
@@ -72,6 +87,18 @@ export class RuntimeAskService implements AskService {
72
87
  }
73
88
 
74
89
  withdraw(runId: string, actionId: string): void {
90
+ this.#dismiss(runId, actionId, "ask-withdraw", null, DISMISSED_ANSWER);
91
+ }
92
+
93
+ #supersede(runId: string, actionId: string, inputId: string): void {
94
+ try {
95
+ this.#dismiss(runId, actionId, "ask-supersede", { supersededBy: inputId }, SUPERSEDED_ANSWER);
96
+ } catch (error) {
97
+ console.error(`Question ${actionId} could not be closed for message ${inputId}: ${error instanceof Error ? error.message : String(error)}`);
98
+ }
99
+ }
100
+
101
+ #dismiss(runId: string, actionId: string, commandPrefix: string, result: JsonValue, answer: string): void {
75
102
  const runtime = this.#requireRuntime();
76
103
  const state = runtime.state(runId);
77
104
  const action = state.actions.get(actionId);
@@ -79,18 +106,18 @@ export class RuntimeAskService implements AskService {
79
106
  this.#aborting.add(actionId);
80
107
  try {
81
108
  runtime.resolveAction(
82
- { actorId: state.ownerId, commandId: `ask-withdraw:${actionId}:${randomUUID()}` },
109
+ { actorId: state.ownerId, commandId: `${commandPrefix}:${actionId}:${randomUUID()}` },
83
110
  runId,
84
111
  actionId,
85
- { decision: "dismissed", result: null },
112
+ { decision: "dismissed", result },
86
113
  );
87
114
  } finally {
88
115
  this.#aborting.delete(actionId);
89
116
  }
90
- this.#waiters.get(actionId)?.(DISMISSED_ANSWER);
117
+ this.#waiters.get(actionId)?.settle(answer);
91
118
  }
92
119
 
93
- #awaitAnswer(runId: string, actionId: string, signal: AbortSignal | undefined): Promise<string> {
120
+ #awaitAnswer(runId: string, actionId: string, blockedActorId: string | null, signal: AbortSignal | undefined): Promise<string> {
94
121
  const runtime = this.#requireRuntime();
95
122
  const action = runtime.state(runId).actions.get(actionId);
96
123
  if (!action) throw new Error(`Frage ${actionId} existiert nicht in Run ${runId}`);
@@ -123,16 +150,29 @@ export class RuntimeAskService implements AskService {
123
150
  return;
124
151
  }
125
152
  signal?.addEventListener("abort", abort, { once: true });
126
- this.#waiters.set(actionId, (answer) => {
127
- signal?.removeEventListener("abort", abort);
128
- this.#waiters.delete(actionId);
129
- resolve(answer);
153
+ this.#waiters.set(actionId, {
154
+ runId,
155
+ blockedActorId,
156
+ settle: (answer) => {
157
+ signal?.removeEventListener("abort", abort);
158
+ this.#waiters.delete(actionId);
159
+ resolve(answer);
160
+ },
130
161
  });
131
162
  });
132
163
  }
133
164
 
134
165
  #onJournal(events: JournalEvent[]): void {
135
166
  for (const event of events) {
167
+ if (event.type === "actor.input.enqueued") {
168
+ if (event.payload.origin !== "human") continue;
169
+ const { actorId, inputId } = event.payload;
170
+ for (const [actionId, waiter] of this.#waiters) {
171
+ if (waiter.runId !== event.runId || waiter.blockedActorId !== actorId) continue;
172
+ queueMicrotask(() => this.#supersede(event.runId, actionId, inputId));
173
+ }
174
+ continue;
175
+ }
136
176
  if (event.type !== "action.resolved") continue;
137
177
  if (this.#aborting.has(event.payload.actionId)) continue;
138
178
  const resolved = this.#requireRuntime().state(event.runId).actions.get(event.payload.actionId);
@@ -140,7 +180,7 @@ export class RuntimeAskService implements AskService {
140
180
  const answer = answerOf(event.payload.decision, event.payload.result);
141
181
  const waiter = this.#waiters.get(event.payload.actionId);
142
182
  if (waiter) {
143
- waiter(answer);
183
+ waiter.settle(answer);
144
184
  continue;
145
185
  }
146
186
  queueMicrotask(() => this.#enqueueAnswer(event.runId, event.payload.actionId, answer));
@@ -1,7 +1,7 @@
1
1
  import { useAccess } from "@ragents/web/AccessContext";
2
2
  import { useState } from "react";
3
3
  import type { ActionViewContext } from "@ragents/web/PluginRegistry";
4
- import { askPayloadOf } from "../ask-payload";
4
+ import { askPayloadOf, SUPERSEDED_ANSWER, supersedingInputOf } from "../ask-payload";
5
5
  import { answerQuestion } from "./api";
6
6
  import { QuestionCard } from "./QuestionCard";
7
7
 
@@ -25,7 +25,8 @@ export function AskActionView({ action, session, text }: ActionViewContext) {
25
25
  <QuestionCard
26
26
  answer={action.status === undefined
27
27
  ? undefined
28
- : action.status === "approved" ? answerTextOf(action.result) : "Der Benutzer hat die Frage verworfen."}
28
+ : action.status === "approved" ? answerTextOf(action.result)
29
+ : supersedingInputOf(action.result) !== undefined ? SUPERSEDED_ANSWER : "Der Benutzer hat die Frage verworfen."}
29
30
  onAnswer={access.can("runs.write") ? answer : undefined}
30
31
  question={question}
31
32
  text={text}
@@ -0,0 +1,5 @@
1
+ import type { WorkspaceExecutorContribution } from "@ragents/workspace-executor";
2
+ import { browserModule } from "./module.js";
3
+
4
+ /** Der Browser läuft dort, wo der Arbeitsbereich des Runs liegt: im Executor des Servers oder in dem des Arbeitsplatzes. */
5
+ export const executor: WorkspaceExecutorContribution = (machine) => ({ modules: [browserModule(machine)] });
@@ -1,6 +1,5 @@
1
1
  import type { Browser } from "playwright-core";
2
- import { WorkspaceOperationError } from "../errors.js";
3
- import type { WorkspaceModuleFactory } from "../module.js";
2
+ import type { WorkspaceExecutorMachine, WorkspaceModuleFactory } from "@ragents/workspace-executor";
4
3
  import { BROWSER_OPERATIONS, type BrowserCheck, type BrowserTarget, type BrowserViewport } from "./contract.js";
5
4
  import { BrowserPages } from "./pages.js";
6
5
  import { launchChromium } from "./playwright.js";
@@ -14,38 +13,33 @@ export interface BrowserModuleOptions {
14
13
  checkTimeoutMs?: number;
15
14
  }
16
15
 
17
- const invalid = (message: string): WorkspaceOperationError => new WorkspaceOperationError("browser-input-invalid", message, 400);
18
-
19
- const fieldsOf = (input: unknown): Readonly<Record<string, unknown>> => {
20
- if (typeof input !== "object" || input === null || Array.isArray(input)) throw invalid("Die Eingabe einer Browseroperation ist ein Objekt");
21
- return input as Readonly<Record<string, unknown>>;
22
- };
23
-
24
- const textOf = (input: unknown, key: string): string => {
25
- const value = fieldsOf(input)[key];
26
- if (typeof value !== "string") throw invalid(`Die Browseroperation braucht ${key} als Text`);
27
- return value;
28
- };
29
-
30
- const targetOf = (input: unknown): BrowserTarget => {
31
- const value = fieldsOf(input).target;
32
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw invalid("Die Browseroperation braucht ein Ziel");
33
- return value as BrowserTarget;
34
- };
35
-
36
- const viewportOf = (input: unknown): BrowserViewport => {
37
- const { width, height } = fieldsOf(input);
38
- if (typeof width !== "number" || typeof height !== "number" || !Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0) {
39
- throw invalid("Ein Viewport braucht Breite und Höhe als positive ganze Zahlen");
40
- }
41
- return { width, height };
42
- };
43
-
44
16
  /** Der Browser eines Runs auf dieser Maschine: Seite, Aktionen, Prüfungen und Aufnahmen; Evidenz und Ablage hält der Aufrufer. */
45
- export const browserModule = (options: BrowserModuleOptions = {}): WorkspaceModuleFactory => (host) => {
17
+ export const browserModule = (machine: WorkspaceExecutorMachine, options: BrowserModuleOptions = {}): WorkspaceModuleFactory => (host) => {
18
+ const invalid = (message: string): Error => machine.operationError("browser-input-invalid", message, 400);
19
+ const fieldsOf = (input: unknown): Readonly<Record<string, unknown>> => {
20
+ if (typeof input !== "object" || input === null || Array.isArray(input)) throw invalid("Die Eingabe einer Browseroperation ist ein Objekt");
21
+ return input as Readonly<Record<string, unknown>>;
22
+ };
23
+ const textOf = (input: unknown, key: string): string => {
24
+ const value = fieldsOf(input)[key];
25
+ if (typeof value !== "string") throw invalid(`Die Browseroperation braucht ${key} als Text`);
26
+ return value;
27
+ };
28
+ const targetOf = (input: unknown): BrowserTarget => {
29
+ const value = fieldsOf(input).target;
30
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw invalid("Die Browseroperation braucht ein Ziel");
31
+ return value as BrowserTarget;
32
+ };
33
+ const viewportOf = (input: unknown): BrowserViewport => {
34
+ const { width, height } = fieldsOf(input);
35
+ if (typeof width !== "number" || typeof height !== "number" || !Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0) {
36
+ throw invalid("Ein Viewport braucht Breite und Höhe als positive ganze Zahlen");
37
+ }
38
+ return { width, height };
39
+ };
46
40
  const timeoutMs = options.timeoutMs ?? BROWSER_TIMEOUT_MS;
47
41
  const pages = new BrowserPages({
48
- launch: options.launch ?? (async (runId) => launchChromium((await host.contextFor(runId)).hostRoot, runId, timeoutMs)),
42
+ launch: options.launch ?? (async (runId) => launchChromium(machine, (await host.contextFor(runId)).hostRoot, runId, timeoutMs)),
49
43
  timeoutMs,
50
44
  checkTimeoutMs: options.checkTimeoutMs ?? Math.min(5_000, timeoutMs),
51
45
  });
@@ -1,30 +1,16 @@
1
1
  import { constants } from "node:fs";
2
2
  import { access } from "node:fs/promises";
3
- import { createRequire } from "node:module";
4
- import { homedir } from "node:os";
5
- import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
6
4
  import type { Browser } from "playwright-core";
7
- import { RUN_MARKER_ENV } from "../run-marker.js";
8
- import { safeProcessEnvironment } from "../safe-environment.js";
5
+ import type { WorkspaceExecutorMachine } from "@ragents/workspace-executor";
9
6
  import { BROWSER_EXECUTABLE_VARIABLE } from "./contract.js";
10
7
 
11
8
  type Playwright = typeof import("playwright-core");
12
9
 
13
- const messageOf = (cause: unknown): string => cause instanceof Error ? cause.message : String(cause);
14
-
15
- /** playwright-core liegt in den node_modules des Hosts dieser Maschine und wird erst im Aufruf geladen, nie mit dem Executor. */
16
- export const hostPlaywright = (hostRoot: string | undefined): Playwright => {
17
- if (!hostRoot) {
18
- throw new Error("Für die Browserprüfung ist auf diesem Rechner kein Host bekannt, aus dem sich playwright-core auflösen "
19
- + "ließe. Ein Arbeitsplatz bekommt ihn mit der ersten Verbindung zu einem verteilenden Server oder über die "
20
- + "Einstellung ragents.hostPath");
21
- }
22
- const load = createRequire(path.join(hostRoot, "package.json"));
23
- try {
24
- return load("playwright-core") as Playwright;
25
- } catch (cause) {
26
- throw new Error(`playwright-core liegt nicht im Host ${hostRoot}: ${messageOf(cause)}`);
27
- }
10
+ /** playwright-core liegt in den node_modules des Hosts dieser Maschine und wird erst im Aufruf geladen, nie mit dem Beitrag. */
11
+ export const hostPlaywright = async (machine: WorkspaceExecutorMachine, hostRoot: string | undefined): Promise<Playwright> => {
12
+ const loaded = await import(pathToFileURL(machine.hostPackageFile(hostRoot, "playwright-core")).href) as { default: Playwright };
13
+ return loaded.default;
28
14
  };
29
15
 
30
16
  /** Der Chrome dieser Maschine: der aus ihrer Umgebung, sonst das Chromium, das die Provisionierung für playwright-core holt. */
@@ -41,12 +27,12 @@ export const browserExecutable = async (playwright: Playwright, environment: Nod
41
27
  };
42
28
 
43
29
  /** Startet Chrome headless mit der Umgebung dieser Maschine und dem Marker des Runs, damit die Prozessanzeige ihn zuordnet. */
44
- export const launchChromium = async (hostRoot: string | undefined, runId: string, timeoutMs: number): Promise<Browser> => {
45
- const playwright = hostPlaywright(hostRoot);
30
+ export const launchChromium = async (machine: WorkspaceExecutorMachine, hostRoot: string | undefined, runId: string, timeoutMs: number): Promise<Browser> => {
31
+ const playwright = await hostPlaywright(machine, hostRoot);
46
32
  return playwright.chromium.launch({
47
33
  executablePath: await browserExecutable(playwright),
48
34
  headless: true,
49
35
  timeout: timeoutMs,
50
- env: { ...safeProcessEnvironment(process.env), HOME: homedir(), [RUN_MARKER_ENV]: runId },
36
+ env: machine.processEnvironment(runId),
51
37
  });
52
38
  };
@@ -2,13 +2,13 @@ import { spawn } from "node:child_process";
2
2
  import { access, constants } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { chromium } from "playwright-core";
5
- import { BROWSER_EXECUTABLE_VARIABLE } from "@ragents/workspace-executor";
6
5
  import {
7
6
  hostPackageFolder,
8
7
  provisionGap,
9
8
  provisionReady,
10
9
  type PluginProvision,
11
10
  } from "@ragents/host/plugin-support/provision.js";
11
+ import { BROWSER_EXECUTABLE_VARIABLE } from "./executor/contract.js";
12
12
 
13
13
  const executable = async (file: string): Promise<boolean> => access(file, constants.X_OK).then(() => true, () => false);
14
14
 
@@ -1,6 +1,8 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
+ import type { SandboxServices } from "@ragents/host/plugin-support/workspace-sandbox-host.js";
5
+ import { documentsApiPrefix } from "@ragents/plugins/ragents.documents/contract.js";
4
6
  import {
5
7
  BROWSER_OPERATIONS,
6
8
  type BrowserCheck,
@@ -10,9 +12,7 @@ import {
10
12
  type BrowserStep,
11
13
  type BrowserTarget,
12
14
  type BrowserViewport,
13
- } from "@ragents/workspace-executor";
14
- import type { SandboxServices } from "@ragents/host/plugin-support/workspace-sandbox-host.js";
15
- import { documentsApiPrefix } from "@ragents/plugins/ragents.documents/contract.js";
15
+ } from "../executor/contract.js";
16
16
  import type { BrowserCallOptions, BrowserEvidence, BrowserRuntime } from "./contract.js";
17
17
 
18
18
  export const DEFAULT_VIEWPORT: BrowserViewport = { width: 1920, height: 1080 };
@@ -1,7 +1,7 @@
1
1
  import { serviceToken } from "@ragents/engine";
2
- import type { BrowserSnapshot } from "@ragents/workspace-executor";
2
+ import type { BrowserSnapshot } from "../executor/contract.js";
3
3
 
4
- export type { BrowserSnapshot, BrowserTarget } from "@ragents/workspace-executor";
4
+ export type { BrowserSnapshot, BrowserTarget } from "../executor/contract.js";
5
5
 
6
6
  export interface BrowserEvidence {
7
7
  checkedAt?: string;
@@ -1,8 +1,8 @@
1
1
  import type { RAgentsPlugin } from "@ragents/engine";
2
- import { BROWSER_EXECUTABLE_VARIABLE } from "@ragents/workspace-executor";
3
2
  import type { PluginModule } from "@ragents/host/plugin-support/plugin-module.js";
4
3
  import { sandboxServicesToken } from "@ragents/host/plugin-support/workspace-sandbox-host.js";
5
4
  import { documentStoreToken } from "@ragents/host/ragents/document-store.js";
5
+ import { BROWSER_EXECUTABLE_VARIABLE } from "../executor/contract.js";
6
6
  import { RunBrowser } from "./browser.js";
7
7
  import { browserRuntimeToken } from "./contract.js";
8
8
  import { createBrowserFunctions, createBrowserImageContribution } from "./tools.js";
@@ -0,0 +1,80 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import type { LanguageServerDescription, WorkspaceExecutorContribution } from "@ragents/workspace-executor";
6
+
7
+ export const FSHARP_SERVER_VARIABLE = "FSHARP_LANGUAGE_SERVER";
8
+ export const FSHARP_SERVER_FILE = "fsautocomplete/fsautocomplete.dll";
9
+
10
+ const languages = { ".fs": "fsharp", ".fsi": "fsharp", ".fsx": "fsharp" } as const;
11
+
12
+ export const fsharpLanguageServer: LanguageServerDescription = {
13
+ id: "fsharp",
14
+ label: "FSAC",
15
+ languages,
16
+ rootDescription: "the .sln file (or a single .fsproj)",
17
+ };
18
+
19
+ const PROJECT_LINE = /^Project\("\{[^}]+\}"\)\s*=\s*"[^"]*",\s*"([^"]+)"/gm;
20
+
21
+ /** Die Projekte einer .sln, aufgelöst neben ihr; Ordnereinträge der Solution zählen nicht. */
22
+ export const solutionProjects = async (solutionFile: string): Promise<string[]> => {
23
+ const text = await readFile(solutionFile, "utf8");
24
+ const directory = path.dirname(solutionFile);
25
+ return [...text.matchAll(PROJECT_LINE)]
26
+ .map((match) => match[1].replaceAll("\\", "/"))
27
+ .filter((relative) => /\.[a-z]+proj$/i.test(relative))
28
+ .map((relative) => path.resolve(directory, relative));
29
+ };
30
+
31
+ const dotnetCommand = (server: string, args: readonly string[]): { command: string; args: string[] } =>
32
+ server.toLowerCase().endsWith(".dll")
33
+ ? { command: "dotnet", args: [server, ...args] }
34
+ : { command: server, args: [...args] };
35
+
36
+ const serverPath = (toolsDirectory: string): string => {
37
+ const configured = process.env[FSHARP_SERVER_VARIABLE];
38
+ if (configured) return configured;
39
+ const provisioned = path.join(toolsDirectory, ...FSHARP_SERVER_FILE.split("/"));
40
+ if (existsSync(provisioned)) return provisioned;
41
+ throw new Error("fsautocomplete gibt es auf diesem Rechner nicht: weder "
42
+ + `${FSHARP_SERVER_VARIABLE} gesetzt noch ${provisioned} vorhanden. Ein Arbeitsplatz holt es mit `
43
+ + `pnpm provision --workspace, ein Server über pnpm provision <profil>; ${FSHARP_SERVER_VARIABLE} übersteuert beides`);
44
+ };
45
+
46
+ const contentOf = (params: unknown): string => (params as { content?: string } | undefined)?.content ?? "";
47
+
48
+ export const executor: WorkspaceExecutorContribution = (machine) => ({
49
+ languageServers: [{
50
+ ...fsharpLanguageServer,
51
+ resolveRoot: (workspaceRoot, root) => machine.resolveRootFile(workspaceRoot, root, [".sln", ".fsproj"]),
52
+ rootDirectory: (root) => path.dirname(root),
53
+ launch: async (context, root) => ({
54
+ label: "FSAC",
55
+ ...dotnetCommand(serverPath(machine.toolsDirectory), ["--state-directory", path.join(context.logDirectory, "fsautocomplete")]),
56
+ cwd: path.dirname(root),
57
+ env: context.env,
58
+ uid: context.uid,
59
+ gid: context.gid,
60
+ rootUri: pathToFileURL(path.dirname(root)).href,
61
+ initializationOptions: { AutomaticWorkspaceInit: false },
62
+ languages,
63
+ }),
64
+ open: async (session, root, timeoutMs) => {
65
+ const projects = root.toLowerCase().endsWith(".sln")
66
+ ? (await solutionProjects(root)).filter((project) => project.toLowerCase().endsWith(".fsproj"))
67
+ : [root];
68
+ if (projects.length === 0) throw new Error(`${path.basename(root)} enthält keine F#-Projekte`);
69
+ const finished = session.waitForNotification(timeoutMs, "Projekte geladen", (method, params) =>
70
+ method === "fsharp/notifyWorkspace"
71
+ && contentOf(params).includes("\"workspaceLoad\"")
72
+ && contentOf(params).includes("\"finished\""));
73
+ await session.request("fsharp/workspaceLoad", {
74
+ textDocuments: projects.map((project) => ({ uri: pathToFileURL(project).href })),
75
+ });
76
+ await finished;
77
+ return `${projects.length} F#-Projekt${projects.length === 1 ? "" : "e"} aus ${path.basename(root)} geladen`;
78
+ },
79
+ }],
80
+ });
@@ -1,6 +1,5 @@
1
1
  import { stat } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { FSHARP_SERVER_FILE } from "@ragents/workspace-executor";
4
3
  import {
5
4
  DOTNET_INSTRUCTION,
6
5
  downloadArchive,
@@ -15,6 +14,7 @@ import {
15
14
  type PluginProvision,
16
15
  type ProvisionState,
17
16
  } from "@ragents/host/plugin-support/provision.js";
17
+ import { FSHARP_SERVER_FILE } from "./executor.js";
18
18
 
19
19
  export const FSAUTOCOMPLETE_VERSION = "0.83.0";
20
20
 
@@ -1,6 +1,6 @@
1
- import { fsharpAdapter, FSHARP_SERVER_VARIABLE } from "@ragents/workspace-executor";
2
1
  import { createLanguageServerPlugin } from "@ragents/host/plugin-support/language-server/plugin.js";
3
2
  import type { PluginModule } from "@ragents/host/plugin-support/plugin-module.js";
3
+ import { fsharpLanguageServer, FSHARP_SERVER_VARIABLE } from "../executor.js";
4
4
 
5
5
  export const fsharpConfigDescriptors = [
6
6
  { key: FSHARP_SERVER_VARIABLE, source: "environment" },
@@ -9,7 +9,7 @@ export const fsharpConfigDescriptors = [
9
9
  export const plugin: PluginModule = {
10
10
  create: () => createLanguageServerPlugin({
11
11
  id: "ragents.lsp-fsharp",
12
- adapter: fsharpAdapter,
12
+ languageServer: fsharpLanguageServer,
13
13
  tabLabel: "F#",
14
14
  configDescriptors: fsharpConfigDescriptors,
15
15
  }),