@schlenkr/ragents 0.1.8 → 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.
Files changed (218) hide show
  1. package/apps/server/package.json +1 -1
  2. package/apps/server/src/access-projection.ts +6 -36
  3. package/apps/server/src/api/contracts.ts +5 -2
  4. package/apps/server/src/api/core-methods.ts +5 -3
  5. package/apps/server/src/config-definition.ts +24 -2
  6. package/apps/server/src/config-file.ts +1 -1
  7. package/apps/server/src/config.ts +0 -6
  8. package/apps/server/src/host-api.json +5 -10
  9. package/apps/server/src/host-api.ts +6 -7
  10. package/apps/server/src/plugin-build/build.ts +28 -5
  11. package/apps/server/src/plugin-build/plugin-description.ts +1 -1
  12. package/apps/server/src/plugin-build/typecheck.ts +2 -2
  13. package/apps/server/src/plugin-support/language-server/plugin.ts +8 -7
  14. package/apps/server/src/plugin-support/language-server/tools.ts +7 -6
  15. package/apps/server/src/plugin-support/model-aliases.ts +55 -25
  16. package/apps/server/src/plugin-support/process-sandbox.ts +16 -15
  17. package/apps/server/src/plugin-support/product-relay.ts +10 -2
  18. package/apps/server/src/plugin-support/workspace-sandbox-host.ts +18 -9
  19. package/apps/server/src/profile/bundle-manifest.ts +7 -3
  20. package/apps/server/src/profile/compose.ts +6 -0
  21. package/apps/server/src/profile/plugin-discovery.ts +6 -1
  22. package/apps/server/src/profile/provisioning.ts +6 -5
  23. package/apps/server/src/provider.ts +2 -2
  24. package/apps/server/src/ragents/engine.ts +2 -2
  25. package/apps/server/src/ragents/global-chat.ts +7 -1
  26. package/apps/server/src/ragents/host-services.ts +4 -0
  27. package/apps/server/src/ragents/session.ts +1 -1
  28. package/apps/server/src/server.ts +4 -1
  29. package/apps/server/tsconfig.json +2 -0
  30. package/apps/web/dist/assets/{highlighted-body-KPVGNVTW-xXXhTDbh.js → highlighted-body-KPVGNVTW-BO4wLQtE.js} +1 -1
  31. package/apps/web/dist/assets/main-DnnqWHkH.js +11 -0
  32. package/apps/web/dist/assets/mermaid-HWGCJPDP-BLLeth5_.js +568 -0
  33. package/apps/web/dist/assets/{runPanel-ZT6TPDLM.js → runPanel-DPm7s3KM.js} +2 -2
  34. package/apps/web/dist/help/guide-clients.html +26 -8
  35. package/apps/web/dist/help/guide-clients.md +27 -8
  36. package/apps/web/dist/help/guide-distributed.html +12 -4
  37. package/apps/web/dist/help/guide-distributed.md +12 -4
  38. package/apps/web/dist/help/guide-getting-started.html +12 -8
  39. package/apps/web/dist/help/guide-getting-started.md +12 -8
  40. package/apps/web/dist/help/guide-plugins.html +6 -2
  41. package/apps/web/dist/help/guide-plugins.md +6 -2
  42. package/apps/web/dist/help/guide-runtime.html +2 -1
  43. package/apps/web/dist/help/guide-runtime.md +2 -1
  44. package/apps/web/dist/help/index.html +4328 -1132
  45. package/apps/web/dist/help/mini-app.css +24 -0
  46. package/apps/web/dist/host-web.json +4 -4
  47. package/apps/web/dist/index.html +2 -2
  48. package/apps/web/dist/run-panel.html +2 -2
  49. package/apps/web/package.json +1 -1
  50. package/apps/web/src/panel/ConnectionsPage.tsx +3 -0
  51. package/apps/web/src/panel/StartPage.tsx +3 -0
  52. package/apps/web/src/panel/contract.ts +7 -0
  53. package/bundles/ragents.activity/ragents-bundle.json +3 -3
  54. package/bundles/ragents.actor-programs/ragents-bundle.json +4 -4
  55. package/bundles/ragents.actor-programs/server/index.js +31 -3
  56. package/bundles/ragents.actor-programs/server/index.js.map +4 -4
  57. package/bundles/ragents.actor-programs/web/index.js.map +1 -1
  58. package/bundles/ragents.ask/ragents-bundle.json +4 -6
  59. package/bundles/ragents.ask/server/index.js +42 -11
  60. package/bundles/ragents.ask/server/index.js.map +3 -3
  61. package/bundles/ragents.ask/web/chunks/{chunk-V7W3QDVS.js → chunk-MBRPS477.js} +2 -2
  62. package/bundles/ragents.ask/web/chunks/{chunk-V7W3QDVS.js.map → chunk-MBRPS477.js.map} +4 -4
  63. package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js +2 -0
  64. package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js.map +7 -0
  65. package/bundles/ragents.ask/web/classes.json +1 -1
  66. package/bundles/ragents.ask/web/exports/ask-payload.js +1 -1
  67. package/bundles/ragents.ask/web/exports/web/QuestionCard.js +1 -1
  68. package/bundles/ragents.ask/web/index.js +1 -1
  69. package/bundles/ragents.ask/web/index.js.map +4 -4
  70. package/bundles/ragents.browser/executor/index.mjs +357 -0
  71. package/bundles/ragents.browser/executor/index.mjs.map +7 -0
  72. package/bundles/ragents.browser/ragents-bundle.json +5 -8
  73. package/bundles/ragents.browser/server/chunks/{chunk-2GKLLCBE.js → chunk-CWLX4VA5.js} +1 -1
  74. package/bundles/ragents.browser/server/chunks/chunk-CWLX4VA5.js.map +7 -0
  75. package/bundles/ragents.browser/server/exports/server/contract.js +1 -1
  76. package/bundles/ragents.browser/server/index.js +20 -9
  77. package/bundles/ragents.browser/server/index.js.map +4 -4
  78. package/bundles/ragents.documents/ragents-bundle.json +3 -3
  79. package/bundles/ragents.lsp-fsharp/executor/index.mjs +68 -0
  80. package/bundles/ragents.lsp-fsharp/executor/index.mjs.map +7 -0
  81. package/bundles/ragents.lsp-fsharp/ragents-bundle.json +5 -9
  82. package/bundles/ragents.lsp-fsharp/server/index.js +21 -6
  83. package/bundles/ragents.lsp-fsharp/server/index.js.map +4 -4
  84. package/bundles/ragents.lsp-roslyn/executor/index.mjs +68 -0
  85. package/bundles/ragents.lsp-roslyn/executor/index.mjs.map +7 -0
  86. package/bundles/ragents.lsp-roslyn/ragents-bundle.json +6 -8
  87. package/bundles/ragents.lsp-roslyn/server/index.js +54 -15
  88. package/bundles/ragents.lsp-roslyn/server/index.js.map +4 -4
  89. package/bundles/ragents.lsp-typescript/executor/index.mjs +49 -0
  90. package/bundles/ragents.lsp-typescript/executor/index.mjs.map +7 -0
  91. package/bundles/ragents.lsp-typescript/ragents-bundle.json +5 -7
  92. package/bundles/ragents.lsp-typescript/server/index.js +22 -2
  93. package/bundles/ragents.lsp-typescript/server/index.js.map +3 -3
  94. package/bundles/ragents.model-relay/ragents-bundle.json +4 -4
  95. package/bundles/ragents.model-relay/server/index.js +3 -2
  96. package/bundles/ragents.model-relay/server/index.js.map +2 -2
  97. package/bundles/ragents.orchestration/ragents-bundle.json +4 -4
  98. package/bundles/ragents.orchestration/web/classes.json +1 -1
  99. package/bundles/ragents.orchestration/web/index.js +6 -6
  100. package/bundles/ragents.orchestration/web/index.js.map +3 -3
  101. package/bundles/ragents.overseer/ragents-bundle.json +4 -4
  102. package/bundles/ragents.overseer/server/index.js +1 -1
  103. package/bundles/ragents.overseer/server/index.js.map +2 -2
  104. package/bundles/ragents.processes/ragents-bundle.json +3 -3
  105. package/bundles/ragents.product/ragents-bundle.json +3 -3
  106. package/bundles/ragents.profile-distribution/ragents-bundle.json +4 -4
  107. package/bundles/ragents.profile-distribution/server/index.js +1 -1
  108. package/bundles/ragents.profile-distribution/server/index.js.map +2 -2
  109. package/bundles/ragents.reference/ragents-bundle.json +3 -3
  110. package/bundles/ragents.todo/ragents-bundle.json +3 -3
  111. package/bundles/ragents.transcript/ragents-bundle.json +3 -3
  112. package/bundles/ragents.watch/ragents-bundle.json +3 -3
  113. package/bundles/ragents.workspace/ragents-bundle.json +11 -5
  114. package/bundles/ragents.workspace/server/index.js +105 -30
  115. package/bundles/ragents.workspace/server/index.js.map +3 -3
  116. package/bundles/ragents.workspace/web/classes.json +1 -1
  117. package/bundles/ragents.workspace/web/index.js +1 -1
  118. package/bundles/ragents.workspace/web/index.js.map +3 -3
  119. package/package.json +3 -3
  120. package/packages/agent/dist/core/agent-settings.d.ts +1 -7
  121. package/packages/agent/dist/core/compaction/compaction.d.ts +17 -13
  122. package/packages/agent/dist/core/model-runtime.d.ts +5 -4
  123. package/packages/agent/dist/core/provider-composer.d.ts +2 -1
  124. package/packages/agent/dist/core/tools/bash.d.ts +9 -3
  125. package/packages/agent/dist/index.d.ts +1 -1
  126. package/packages/agent/src/core/agent-settings.ts +1 -4
  127. package/packages/agent/src/core/compaction/compaction.ts +68 -30
  128. package/packages/agent/src/core/model-runtime.ts +12 -5
  129. package/packages/agent/src/core/provider-composer.ts +5 -0
  130. package/packages/agent/src/core/tools/bash.ts +36 -30
  131. package/packages/agent/src/index.ts +3 -0
  132. package/packages/ai/dist/types.d.ts +11 -0
  133. package/packages/ai/src/types.ts +12 -0
  134. package/packages/ragents/src/domain/event-semantics.ts +3 -0
  135. package/packages/ragents/src/domain/event-validation.ts +15 -2
  136. package/packages/ragents/src/domain/events.ts +3 -0
  137. package/packages/ragents/src/domain/model.ts +2 -0
  138. package/packages/ragents/src/domain/projection.ts +1 -0
  139. package/packages/ragents/src/drivers/agent-runtime.ts +1 -1
  140. package/packages/ragents/src/drivers/agent-turn.ts +8 -6
  141. package/packages/ragents/src/index.ts +1 -0
  142. package/packages/ragents/src/plugin-host.ts +46 -0
  143. package/packages/ragents/src/plugin-types.ts +19 -0
  144. package/packages/ragents/src/runtime/decisions/inputs.ts +6 -2
  145. package/packages/ragents/src/runtime/journal-storage.ts +5 -4
  146. package/packages/workspace-executor/package.json +0 -1
  147. package/packages/workspace-executor/src/bash-launch.ts +56 -10
  148. package/packages/workspace-executor/src/context.ts +4 -0
  149. package/packages/workspace-executor/src/contributions.ts +150 -0
  150. package/packages/workspace-executor/src/executor.ts +1 -1
  151. package/packages/workspace-executor/src/index.ts +16 -19
  152. package/packages/workspace-executor/src/language-server/host.ts +3 -0
  153. package/packages/workspace-executor/src/language-server/module.ts +12 -2
  154. package/packages/workspace-executor/src/language-server/roots.ts +0 -5
  155. package/packages/workspace-executor/src/managed-process.ts +28 -0
  156. package/packages/workspace-executor/src/modules.ts +7 -6
  157. package/packages/workspace-executor/src/sandbox-tools.ts +12 -12
  158. package/packages/workspace-executor/src/shell-platform.ts +14 -3
  159. package/packages/workspace-executor/src/tools.ts +0 -3
  160. package/plugins/ragents.actor-programs/server/access-projections.ts +31 -0
  161. package/plugins/ragents.actor-programs/server/index.ts +2 -0
  162. package/plugins/ragents.ask/ask-payload.ts +9 -0
  163. package/plugins/ragents.ask/server/ask-service.ts +53 -13
  164. package/plugins/ragents.ask/web/AskActionView.tsx +3 -2
  165. package/plugins/ragents.ask/web/index.tsx +0 -6
  166. package/plugins/ragents.browser/executor/index.ts +5 -0
  167. package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/module.ts +25 -31
  168. package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/playwright.ts +9 -23
  169. package/plugins/ragents.browser/provision.ts +1 -1
  170. package/plugins/ragents.browser/server/browser.ts +3 -3
  171. package/plugins/ragents.browser/server/contract.ts +2 -2
  172. package/plugins/ragents.browser/server/index.ts +1 -1
  173. package/plugins/ragents.lsp-fsharp/executor.ts +80 -0
  174. package/plugins/ragents.lsp-fsharp/provision.ts +1 -1
  175. package/plugins/ragents.lsp-fsharp/server/index.ts +2 -2
  176. package/plugins/ragents.lsp-roslyn/executor.ts +64 -0
  177. package/plugins/ragents.lsp-roslyn/provision.ts +1 -1
  178. package/plugins/ragents.lsp-roslyn/server/index.ts +28 -5
  179. package/plugins/ragents.lsp-roslyn/server/solution-on-start.ts +18 -6
  180. package/plugins/ragents.lsp-typescript/executor.ts +50 -0
  181. package/plugins/ragents.lsp-typescript/server/index.ts +2 -2
  182. package/plugins/ragents.model-relay/server/config.ts +1 -1
  183. package/plugins/ragents.model-relay/server/relay.ts +4 -2
  184. package/plugins/ragents.orchestration/web/ActorTile.tsx +1 -1
  185. package/plugins/ragents.orchestration/web/run-panel/RunPanel.tsx +8 -7
  186. package/plugins/ragents.orchestration/web/run-panel/sheet-status.ts +3 -3
  187. package/plugins/ragents.overseer/server/api.ts +2 -2
  188. package/plugins/ragents.profile-distribution/server/archive.ts +2 -1
  189. package/plugins/ragents.workspace/client/workspace-client.ts +128 -32
  190. package/plugins/ragents.workspace/contract.ts +33 -2
  191. package/plugins/ragents.workspace/server/binding.ts +10 -4
  192. package/plugins/ragents.workspace/server/clients.ts +56 -9
  193. package/plugins/ragents.workspace/server/config.ts +12 -0
  194. package/plugins/ragents.workspace/server/index.ts +15 -5
  195. package/plugins/ragents.workspace/server/runtime.ts +10 -0
  196. package/plugins/ragents.workspace/server/shell-platform.ts +6 -5
  197. package/plugins/ragents.workspace/web/WorkspaceBinding.tsx +1 -1
  198. package/ragents.config.core.ts +4 -1
  199. package/ragents.config.showcase.ts +4 -1
  200. package/scripts/agent/journal.ts +3 -1
  201. package/scripts/package/build-package.ts +3 -1
  202. package/scripts/provision/run-provision.ts +2 -2
  203. package/scripts/workspace-client/run-workspace-client.ts +7 -4
  204. package/apps/web/dist/assets/main-C7JeZFjs.js +0 -6
  205. package/apps/web/dist/assets/mermaid-HWGCJPDP-DfsU5ofC.js +0 -478
  206. package/apps/web/dist/help/screenshots/release-checklist-vscode.png +0 -0
  207. package/apps/web/dist/help/screenshots/release-checklist-web.png +0 -0
  208. package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js +0 -2
  209. package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js.map +0 -7
  210. package/bundles/ragents.browser/server/chunks/chunk-2GKLLCBE.js.map +0 -7
  211. package/packages/workspace-executor/src/language-server/adapters/fsharp.ts +0 -60
  212. package/packages/workspace-executor/src/language-server/adapters/index.ts +0 -7
  213. package/packages/workspace-executor/src/language-server/adapters/roslyn.ts +0 -56
  214. package/packages/workspace-executor/src/language-server/adapters/typescript.ts +0 -74
  215. package/packages/workspace-executor/src/language-server/solution.ts +0 -13
  216. package/plugins/ragents.ask/web/QuestionSection.tsx +0 -48
  217. /package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/contract.ts +0 -0
  218. /package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/pages.ts +0 -0
@@ -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}
@@ -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,
@@ -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
  }),
@@ -0,0 +1,64 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import type { LanguageServerDescription, WorkspaceExecutorContribution } from "@ragents/workspace-executor";
5
+
6
+ export const ROSLYN_SERVER_VARIABLE = "ROSLYN_LANGUAGE_SERVER";
7
+ export const ROSLYN_SERVER_FILE = "roslyn/Microsoft.CodeAnalysis.LanguageServer.dll";
8
+
9
+ const languages = { ".cs": "csharp" } as const;
10
+
11
+ export const roslynLanguageServer: LanguageServerDescription = {
12
+ id: "roslyn",
13
+ label: "Roslyn",
14
+ languages,
15
+ rootDescription: "the .sln file (or a single .csproj)",
16
+ solutionExtensions: [".sln", ".slnx"],
17
+ };
18
+
19
+ const dotnetCommand = (server: string, args: readonly string[]): { command: string; args: string[] } =>
20
+ server.toLowerCase().endsWith(".dll")
21
+ ? { command: "dotnet", args: [server, ...args] }
22
+ : { command: server, args: [...args] };
23
+
24
+ const serverPath = (toolsDirectory: string): string => {
25
+ const configured = process.env[ROSLYN_SERVER_VARIABLE];
26
+ if (configured) return configured;
27
+ const provisioned = path.join(toolsDirectory, ...ROSLYN_SERVER_FILE.split("/"));
28
+ if (existsSync(provisioned)) return provisioned;
29
+ throw new Error("Microsoft.CodeAnalysis.LanguageServer gibt es auf diesem Rechner nicht: weder "
30
+ + `${ROSLYN_SERVER_VARIABLE} gesetzt noch ${provisioned} vorhanden. Ein Arbeitsplatz holt ihn mit `
31
+ + `pnpm provision --workspace, ein Server über pnpm provision <profil>; ${ROSLYN_SERVER_VARIABLE} übersteuert beides`);
32
+ };
33
+
34
+ export const executor: WorkspaceExecutorContribution = (machine) => ({
35
+ languageServers: [{
36
+ ...roslynLanguageServer,
37
+ resolveRoot: (workspaceRoot, root) => machine.resolveRootFile(workspaceRoot, root, [".sln", ".slnx", ".csproj"]),
38
+ rootDirectory: (root) => path.dirname(root),
39
+ launch: async (context, root) => ({
40
+ label: "Roslyn",
41
+ ...dotnetCommand(serverPath(machine.toolsDirectory), [
42
+ "--stdio",
43
+ "--logLevel", "Warning",
44
+ "--extensionLogDirectory", path.join(context.logDirectory, "roslyn-logs"),
45
+ ]),
46
+ cwd: path.dirname(root),
47
+ env: context.env,
48
+ uid: context.uid,
49
+ gid: context.gid,
50
+ rootUri: pathToFileURL(path.dirname(root)).href,
51
+ languages,
52
+ newFileSettleMs: 2_000,
53
+ }),
54
+ open: async (session, root, timeoutMs) => {
55
+ const uri = pathToFileURL(root).href;
56
+ const loaded = session.waitForNotification(timeoutMs, "Projekte geladen",
57
+ (method) => method === "workspace/projectInitializationComplete");
58
+ if (root.toLowerCase().endsWith(".csproj")) session.notify("project/open", { projects: [uri] });
59
+ else session.notify("solution/open", { solution: uri });
60
+ await loaded;
61
+ return `${path.basename(root)} geladen`;
62
+ },
63
+ }],
64
+ });
@@ -1,6 +1,5 @@
1
1
  import { stat } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { ROSLYN_SERVER_FILE } from "@ragents/workspace-executor";
4
3
  import {
5
4
  DOTNET_INSTRUCTION,
6
5
  downloadArchive,
@@ -14,6 +13,7 @@ import {
14
13
  type PluginProvision,
15
14
  type ProvisionState,
16
15
  } from "@ragents/host/plugin-support/provision.js";
16
+ import { ROSLYN_SERVER_FILE } from "./executor.js";
17
17
 
18
18
  export const ROSLYN_VERSION = "5.4.0-2.26179.14";
19
19
 
@@ -1,15 +1,22 @@
1
- import { roslynAdapter, ROSLYN_SERVER_VARIABLE } from "@ragents/workspace-executor";
1
+ import path from "node:path";
2
2
  import { declaredEnvironment } from "@ragents/host/plugin-support/plugin-config.js";
3
3
  import { createLanguageServerPlugin } from "@ragents/host/plugin-support/language-server/plugin.js";
4
4
  import type { PluginModule } from "@ragents/host/plugin-support/plugin-module.js";
5
5
  import { sandboxServicesToken } from "@ragents/host/plugin-support/workspace-sandbox-host.js";
6
6
  import { runtimeProviderToken } from "@ragents/host/ragents/host-services.js";
7
7
  import { askServiceToken } from "@ragents/plugins/ragents.ask/server/contract.js";
8
- import { createSolutionOnStart, SOLUTION_ON_START_VARIABLE, type SolutionOnStart } from "./solution-on-start.js";
8
+ import { roslynLanguageServer, ROSLYN_SERVER_VARIABLE } from "../executor.js";
9
+ import {
10
+ createSolutionOnStart,
11
+ SOLUTION_ON_START_VARIABLE,
12
+ SOLUTION_PREFERRED_VARIABLE,
13
+ type SolutionOnStart,
14
+ } from "./solution-on-start.js";
9
15
 
10
16
  export const roslynConfigDescriptors = [
11
17
  { key: ROSLYN_SERVER_VARIABLE, source: "environment" },
12
18
  { key: SOLUTION_ON_START_VARIABLE, source: "environment" },
19
+ { key: SOLUTION_PREFERRED_VARIABLE, source: "environment" },
13
20
  ] as const;
14
21
 
15
22
  const PLUGIN_ID = "ragents.lsp-roslyn";
@@ -25,13 +32,26 @@ export const solutionOnStart = (): boolean => {
25
32
  return mode === "on";
26
33
  };
27
34
 
35
+ /** Ein Pfad relativ zur Wurzel des Arbeitsbereichs, wie ihn `roslyn_solutions` nennt; ohne Laden beim Start hätte er keine Wirkung. */
36
+ export const preferredSolution = (loadOnStart: boolean): string | undefined => {
37
+ const preferred = env.optional(SOLUTION_PREFERRED_VARIABLE);
38
+ if (preferred === undefined) return undefined;
39
+ if (!loadOnStart) {
40
+ throw new Error(`${SOLUTION_PREFERRED_VARIABLE} in der Sektion ragents.lsp-roslyn verlangt ${SOLUTION_ON_START_VARIABLE}: "on"`);
41
+ }
42
+ if (preferred === "" || preferred.includes("\\") || path.win32.isAbsolute(preferred)) {
43
+ throw new Error(`${SOLUTION_PREFERRED_VARIABLE} in der Sektion ragents.lsp-roslyn ist ein Pfad relativ zum Arbeitsbereich mit Schrägstrichen, nicht "${preferred}"`);
44
+ }
45
+ return preferred;
46
+ };
47
+
28
48
  export const plugin: PluginModule = {
29
49
  requires: ["ragents.ask"],
30
50
  create: () => {
31
51
  let startup: SolutionOnStart | undefined;
32
52
  const languageServer = createLanguageServerPlugin({
33
53
  id: PLUGIN_ID,
34
- adapter: roslynAdapter,
54
+ languageServer: roslynLanguageServer,
35
55
  configDescriptors: roslynConfigDescriptors,
36
56
  onOpened: (runId) => startup?.opened(runId),
37
57
  });
@@ -39,10 +59,13 @@ export const plugin: PluginModule = {
39
59
  manifest: languageServer.manifest,
40
60
  register: (host) => {
41
61
  languageServer.register(host);
42
- if (!solutionOnStart()) return;
62
+ const loadOnStart = solutionOnStart();
63
+ const preferred = preferredSolution(loadOnStart);
64
+ if (!loadOnStart) return;
43
65
  startup = createSolutionOnStart({
44
66
  pluginId: PLUGIN_ID,
45
- adapterId: roslynAdapter.id,
67
+ adapterId: roslynLanguageServer.id,
68
+ preferred,
46
69
  sandbox: () => host.service(sandboxServicesToken),
47
70
  runtime: () => host.service(runtimeProviderToken)(),
48
71
  ask: () => host.service(askServiceToken),