@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
@@ -0,0 +1,150 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ import { createRequire } from "node:module";
4
+ import { homedir } from "node:os";
5
+ import path from "node:path";
6
+ import { pathToFileURL } from "node:url";
7
+ import { WorkspaceOperationError } from "./errors.js";
8
+ import type { LanguageServerAdapter } from "./language-server/host.js";
9
+ import { resolveRootDirectory, resolveRootFile } from "./language-server/roots.js";
10
+ import type { WorkspaceModuleFactory } from "./module.js";
11
+ import { RUN_MARKER_ENV } from "./run-marker.js";
12
+ import { safeProcessEnvironment } from "./safe-environment.js";
13
+
14
+ /** Wo ein Bundle seinen Beitrag zum Executor trägt: eine in sich geschlossene Datei, die jeder Node-Prozess ohne Host-Auflösung lädt. */
15
+ export const EXECUTOR_CONTRIBUTION_FILE = "executor/index.mjs";
16
+
17
+ /** Was der Executor einer Maschine einem Beitrag über sie sagt; ein Beitrag importiert vom Host nur Typen, alles andere kommt hierüber. */
18
+ export interface WorkspaceExecutorMachine {
19
+ /** Der Werkzeugordner des Plugins auf dieser Maschine, wohin seine Provisionierung lädt. */
20
+ readonly toolsDirectory: string;
21
+ /** Eine Datei aus einem Paket in den node_modules des Hosts dieser Maschine; ohne Host oder Paket scheitert der Aufruf mit Ursache. */
22
+ readonly hostPackageFile: (hostRoot: string | undefined, specifier: string) => string;
23
+ /** Eine Datei mit einer der Endungen im Arbeitsbereich, aufgelöst und geprüft. */
24
+ readonly resolveRootFile: (workspaceRoot: string, requested: string, extensions: readonly string[]) => Promise<string>;
25
+ /** Ein Ordner im Arbeitsbereich, aufgelöst und geprüft. */
26
+ readonly resolveRootDirectory: (workspaceRoot: string, requested: string) => Promise<string>;
27
+ /** Ein fachlicher Fehler einer Operation mit Kennung und Status; beim Aufrufer kommt er als derselbe Fehler an wie einer des Executors. */
28
+ readonly operationError: (code: string, message: string, status: number) => Error;
29
+ /** Die Umgebung eines Prozesses, den ein Beitrag selbst startet: die sichere Auswahl dieser Maschine, ihr HOME und der Marker des Runs für die Prozessanzeige. */
30
+ readonly processEnvironment: (runId: string) => NodeJS.ProcessEnv;
31
+ }
32
+
33
+ /** Was ein Beitrag dem Executor einer Maschine hinzufügt. */
34
+ export interface WorkspaceExecutorParts {
35
+ readonly languageServers?: readonly LanguageServerAdapter[];
36
+ /** Module mit eigenen Operationen; jede Operation gehört genau einem Modul. */
37
+ readonly modules?: readonly WorkspaceModuleFactory[];
38
+ }
39
+
40
+ /** Was ein Plugin-Bundle als `executor` exportiert; es läuft in jedem Executor, im Server wie auf jedem Arbeitsplatz, und berührt beim Bauen weder Platte noch Netz. */
41
+ export type WorkspaceExecutorContribution = (machine: WorkspaceExecutorMachine) => WorkspaceExecutorParts;
42
+
43
+ /** Wessen Beitrag in welchem Stand; Server und Arbeitsplatz verlangen dieselben. */
44
+ export interface ExecutorContributionStand {
45
+ readonly plugin: string;
46
+ /** SHA-256 der Datei des Beitrags. */
47
+ readonly stand: string;
48
+ }
49
+
50
+ export interface LoadedExecutorContribution extends ExecutorContributionStand {
51
+ readonly contribution: WorkspaceExecutorContribution;
52
+ }
53
+
54
+ /** Ein Beitrag, gebaut für diese Maschine. */
55
+ export interface PreparedExecutorContribution extends ExecutorContributionStand {
56
+ readonly parts: WorkspaceExecutorParts;
57
+ }
58
+
59
+ const LANGUAGE_SERVER_ID = /^[a-z][a-z0-9]{0,39}$/;
60
+ const PART_KEYS = new Set(["languageServers", "modules"]);
61
+
62
+ const messageOf = (cause: unknown): string => cause instanceof Error ? cause.message : String(cause);
63
+
64
+ /** Der Paketname eines Bezeichners wie acme-server/lib/cli.mjs oder @scope/name/datei. */
65
+ const packageOf = (specifier: string): string =>
66
+ specifier.split("/").slice(0, specifier.startsWith("@") ? 2 : 1).join("/");
67
+
68
+ const hostPackageFile = (hostRoot: string | undefined, specifier: string): string => {
69
+ if (!hostRoot) {
70
+ throw new Error(`Auf diesem Rechner ist kein Host bekannt, aus dem sich ${packageOf(specifier)} auflösen ließe. Ein Arbeitsplatz `
71
+ + "bekommt ihn mit der ersten Verbindung zu einem verteilenden Server oder über die Einstellung ragents.hostPath");
72
+ }
73
+ try {
74
+ return createRequire(path.join(hostRoot, "package.json")).resolve(specifier);
75
+ } catch (cause) {
76
+ throw new Error(`${packageOf(specifier)} liegt nicht im Host ${hostRoot}: ${messageOf(cause)}`);
77
+ }
78
+ };
79
+
80
+ export const executorMachine = (toolsDirectory: string): WorkspaceExecutorMachine => ({
81
+ toolsDirectory,
82
+ hostPackageFile,
83
+ resolveRootFile,
84
+ resolveRootDirectory,
85
+ operationError: (code, message, status) => new WorkspaceOperationError(code, message, status),
86
+ processEnvironment: (runId) => ({ ...safeProcessEnvironment(process.env), HOME: homedir(), [RUN_MARKER_ENV]: runId }),
87
+ });
88
+
89
+ /** Lädt die Datei eines Beitrags, mit `expected` nur in diesem Stand; der Stand steht in ihrer Adresse, damit ein neu gebauter Beitrag nie aus dem Modulcache kommt. */
90
+ export const loadExecutorContribution = async (plugin: string, file: string, expected?: string): Promise<LoadedExecutorContribution> => {
91
+ const content = await readFile(file).catch((cause: unknown) => {
92
+ throw new Error(`Der Executor-Beitrag von ${plugin} fehlt unter ${file}: ${messageOf(cause)}`);
93
+ });
94
+ const stand = createHash("sha256").update(content).digest("hex");
95
+ if (expected !== undefined && stand !== expected) {
96
+ throw new Error(`Der Executor-Beitrag von ${plugin} unter ${file} hat den Stand ${stand.slice(0, 12)}, verlangt ist ${expected.slice(0, 12)}`);
97
+ }
98
+ const exported = await import(`${pathToFileURL(file).href}?stand=${stand}`).catch((cause: unknown) => {
99
+ throw new Error(`Der Executor-Beitrag von ${plugin} (${file}) lädt nicht: ${messageOf(cause)}`, { cause });
100
+ }) as Readonly<Record<string, unknown>>;
101
+ const contribution = exported.executor;
102
+ if (typeof contribution !== "function") {
103
+ throw new Error(`Das Bundle ${plugin} exportiert in ${file} keinen gültigen Executor-Beitrag; erwartet wird `
104
+ + "\"export const executor: WorkspaceExecutorContribution\" als Funktion der Maschine");
105
+ }
106
+ return { plugin, stand, contribution: contribution as WorkspaceExecutorContribution };
107
+ };
108
+
109
+ const isRecord = (value: unknown): value is Readonly<Record<string, unknown>> =>
110
+ typeof value === "object" && value !== null && !Array.isArray(value);
111
+
112
+ const isStringList = (value: unknown): value is readonly string[] =>
113
+ Array.isArray(value) && value.every((entry) => typeof entry === "string");
114
+
115
+ /** Was an einem Sprachserver fehlt oder die falsche Form hat; leer, wenn er taugt. */
116
+ const adapterProblems = (value: Readonly<Record<string, unknown>>): readonly string[] => [
117
+ ...typeof value.id === "string" && LANGUAGE_SERVER_ID.test(value.id) ? [] : ["id aus Kleinbuchstaben und Ziffern"],
118
+ ...typeof value.label === "string" ? [] : ["label"],
119
+ ...isRecord(value.languages) && Object.values(value.languages).every((entry) => typeof entry === "string") ? [] : ["languages"],
120
+ ...typeof value.rootDescription === "string" ? [] : ["rootDescription"],
121
+ ...value.solutionExtensions === undefined || isStringList(value.solutionExtensions) ? [] : ["solutionExtensions"],
122
+ ...(["resolveRoot", "rootDirectory", "launch", "open"] as const).filter((name) => typeof value[name] !== "function"),
123
+ ];
124
+
125
+ /** Baut einen geladenen Beitrag für diese Maschine und prüft, was er liefert; eine falsche Form ist ein Fehler mit Ursache. */
126
+ export const prepareExecutorContribution = (loaded: LoadedExecutorContribution, toolsDirectory: string): PreparedExecutorContribution => {
127
+ const invalid = (detail: string): Error => new Error(`Der Executor-Beitrag von ${loaded.plugin} ist ungültig: ${detail}`);
128
+ const parts = ((): unknown => {
129
+ try {
130
+ return loaded.contribution(executorMachine(toolsDirectory));
131
+ } catch (cause) {
132
+ throw invalid(`die Funktion wirft: ${messageOf(cause)}`);
133
+ }
134
+ })();
135
+ if (!isRecord(parts)) throw invalid("die Funktion liefert kein Objekt");
136
+ const unknown = Object.keys(parts).filter((key) => !PART_KEYS.has(key));
137
+ if (unknown.length > 0) throw invalid(`unbekannte Teile ${unknown.join(", ")}; erlaubt sind ${[...PART_KEYS].join(", ")}`);
138
+ const modules = parts.modules;
139
+ if (modules !== undefined && (!Array.isArray(modules) || !modules.every((module) => typeof module === "function"))) {
140
+ throw invalid("modules ist keine Liste von Modulfabriken");
141
+ }
142
+ const servers = parts.languageServers;
143
+ if (servers !== undefined && !Array.isArray(servers)) throw invalid("languageServers ist keine Liste");
144
+ for (const [index, server] of (servers ?? []).entries()) {
145
+ if (!isRecord(server)) throw invalid(`der Sprachserver ${index + 1} ist kein Objekt`);
146
+ const problems = adapterProblems(server);
147
+ if (problems.length > 0) throw invalid(`der Sprachserver ${index + 1} braucht ${problems.join(", ")}`);
148
+ }
149
+ return { plugin: loaded.plugin, stand: loaded.stand, parts: parts as WorkspaceExecutorParts };
150
+ };
@@ -4,7 +4,7 @@ import type { WorkspaceExecutorModule, WorkspaceModuleFactory, WorkspaceOperatio
4
4
  import { NO_ROOTS, type OperationFootprint } from "./paths.js";
5
5
 
6
6
  /** Der Stand des Executors; Server und Arbeitsplatz müssen denselben tragen. */
7
- export const WORKSPACE_EXECUTOR_VERSION = "5";
7
+ export const WORKSPACE_EXECUTOR_VERSION = "7";
8
8
 
9
9
  export interface WorkspaceExecuteOptions {
10
10
  toolCallId?: string;
@@ -18,6 +18,18 @@ export {
18
18
  type WorkspaceModuleFactory,
19
19
  } from "./module.js";
20
20
  export { workspaceExecutorModules, type WorkspaceExecutorModuleOptions } from "./modules.js";
21
+ export {
22
+ EXECUTOR_CONTRIBUTION_FILE,
23
+ executorMachine,
24
+ loadExecutorContribution,
25
+ prepareExecutorContribution,
26
+ type ExecutorContributionStand,
27
+ type LoadedExecutorContribution,
28
+ type PreparedExecutorContribution,
29
+ type WorkspaceExecutorContribution,
30
+ type WorkspaceExecutorMachine,
31
+ type WorkspaceExecutorParts,
32
+ } from "./contributions.js";
21
33
  export { RUN_FOLDER_OPERATIONS, runFolderModule, type RunFolderCreated } from "./run-folders.js";
22
34
  export {
23
35
  FILE_OPERATIONS,
@@ -37,18 +49,6 @@ export {
37
49
  type CommandResult,
38
50
  type CommandRunInput,
39
51
  } from "./commands.js";
40
- export {
41
- BROWSER_EXECUTABLE_VARIABLE,
42
- BROWSER_OPERATIONS,
43
- type BrowserCheck,
44
- type BrowserCheckResult,
45
- type BrowserPageState,
46
- type BrowserSnapshot,
47
- type BrowserStep,
48
- type BrowserTarget,
49
- type BrowserViewport,
50
- } from "./browser/contract.js";
51
- export { browserModule, type BrowserModuleOptions } from "./browser/module.js";
52
52
  export { processGroupExists, runManagedProcess, startManagedService, type ManagedService } from "./managed-process.js";
53
53
  export {
54
54
  containsWorkspacePath,
@@ -81,15 +81,16 @@ export { labelOf, runProcessesFrom } from "./processes/snapshot.js";
81
81
  export { RunProcessTerminator } from "./processes/terminator.js";
82
82
  export { RUN_MARKER_ENV } from "./run-marker.js";
83
83
  export { editorFreeEnvironment, inheritedProcessEnvironment, safeProcessEnvironment } from "./safe-environment.js";
84
- export { bashLaunch, type BashLaunch } from "./bash-launch.js";
84
+ export { bashLaunch, ripgrepAvailable, type BashLaunch, type BashPrograms } from "./bash-launch.js";
85
85
  export { createSandboxTools, sandboxToolsModule, withAnnotation, type SeenFile } from "./sandbox-tools.js";
86
86
  export { type SessionIdent } from "./session-ident.js";
87
- export { shellPlatformText } from "./shell-platform.js";
88
- export { ragentsDataRoot, workspaceDataDirectory } from "./tools.js";
87
+ export { shellPlatformText, type ShellTools } from "./shell-platform.js";
88
+ export { hostDataDirectory, pluginToolsDirectory, ragentsDataRoot, workspaceDataDirectory } from "./tools.js";
89
89
  export {
90
90
  LanguageServerHost,
91
91
  changedWorkspaceFiles,
92
92
  type LanguageServerAdapter,
93
+ type LanguageServerDescription,
93
94
  type LanguageServerHostOptions,
94
95
  } from "./language-server/host.js";
95
96
  export {
@@ -112,7 +113,3 @@ export {
112
113
  } from "./language-server/module.js";
113
114
  export { resolveRootDirectory } from "./language-server/roots.js";
114
115
  export { LanguageServerSession, withTimeout } from "./language-server/session.js";
115
- export { solutionProjects } from "./language-server/solution.js";
116
- export { fsharpAdapter, FSHARP_SERVER_FILE, FSHARP_SERVER_VARIABLE } from "./language-server/adapters/fsharp.js";
117
- export { roslynAdapter, ROSLYN_SERVER_FILE, ROSLYN_SERVER_VARIABLE } from "./language-server/adapters/roslyn.js";
118
- export { typescriptAdapter } from "./language-server/adapters/typescript.js";
@@ -27,6 +27,9 @@ export interface LanguageServerAdapter {
27
27
  open: (session: LanguageServerSession, root: string, timeoutMs: number) => Promise<string>;
28
28
  }
29
29
 
30
+ /** Was der Server über einen Sprachserver wissen muss, um Werkzeuge und Reiter zu beschreiben; gestartet wird er nur im Executor. */
31
+ export type LanguageServerDescription = Pick<LanguageServerAdapter, "id" | "label" | "languages" | "rootDescription" | "solutionExtensions">;
32
+
30
33
  export interface LanguageServerHostOptions {
31
34
  idleMs?: number;
32
35
  openTimeoutMs?: number;
@@ -1,5 +1,5 @@
1
1
  import { WorkspaceOperationError } from "../errors.js";
2
- import type { WorkspaceModuleFactory, WorkspaceOperation } from "../module.js";
2
+ import type { WorkspaceExecutorModule, WorkspaceModuleFactory, WorkspaceModuleHost, WorkspaceOperation } from "../module.js";
3
3
  import { rootsOfFields, type OperationFootprint } from "../paths.js";
4
4
  import { LanguageServerHost, type LanguageServerAdapter, type LanguageServerHostOptions } from "./host.js";
5
5
 
@@ -89,7 +89,17 @@ const footprintsOf = (server: LanguageServerHost): Array<readonly [string, (inpu
89
89
  export const languageServerModule = (
90
90
  adapters: readonly LanguageServerAdapter[],
91
91
  options: LanguageServerHostOptions = {},
92
- ): WorkspaceModuleFactory => (host) => {
92
+ ): WorkspaceModuleFactory => {
93
+ const duplicate = adapters.find((adapter, index) => adapters.findIndex((other) => other.id === adapter.id) !== index);
94
+ if (duplicate) throw new Error(`Der Sprachserver ${duplicate.id} kommt im Executor zweimal vor; seine Operationen gehören genau einem Beitrag`);
95
+ return (host) => languageServersOn(host, adapters, options);
96
+ };
97
+
98
+ const languageServersOn = (
99
+ host: WorkspaceModuleHost,
100
+ adapters: readonly LanguageServerAdapter[],
101
+ options: LanguageServerHostOptions,
102
+ ): WorkspaceExecutorModule => {
93
103
  const servers = adapters.map((adapter) => new LanguageServerHost(adapter, host.contextFor, options));
94
104
  return {
95
105
  operations: Object.fromEntries(servers.flatMap(operationsOf)),
@@ -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] };
@@ -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,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
  ];
@@ -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";
@@ -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. */
@@ -130,10 +125,14 @@ export const createSandboxTools = async (
130
125
  let toolOperation: Promise<void> = Promise.resolve();
131
126
  let shuttingDown = false;
132
127
  const processGroups = new Set<number>();
128
+ const msysCalls = new Map<number, { readonly bash: string; readonly env: NodeJS.ProcessEnv; readonly marker: string }>();
133
129
 
134
130
  const killProcessGroup = async (pid: number): Promise<void> => {
135
131
  if (onWindows) {
136
132
  stopProcessTree(pid);
133
+ const call = msysCalls.get(pid);
134
+ msysCalls.delete(pid);
135
+ if (call) await stopMsysCall(call.bash, call.env, call.marker);
137
136
  processGroups.delete(pid);
138
137
  return;
139
138
  }
@@ -269,21 +268,23 @@ export const createSandboxTools = async (
269
268
  const context = await contextFor();
270
269
  if (shuttingDown) throw new Error("Die Werkzeuge werden killed");
271
270
  options.signal?.throwIfAborted();
272
- const bash = bashLaunch(context.bash, command, context.env);
271
+ const bash = bashLaunch({ bash: context.bash, rg: context.rg }, command, context.env);
273
272
  const launch = await sandboxedLaunch(context, bash);
274
273
  if (shuttingDown) throw new Error("Die Werkzeuge werden killed");
275
274
  options.signal?.throwIfAborted();
275
+ const marker = onWindows ? randomUUID() : undefined;
276
276
  return new Promise((resolve, reject) => {
277
277
  const child = spawn(launch.command, [...launch.args], {
278
278
  cwd: commandCwd,
279
279
  detached: !onWindows,
280
280
  stdio: ["ignore", "pipe", "pipe"],
281
- env: bash.env,
281
+ env: marker === undefined ? bash.env : { ...bash.env, [MSYS_CALL_ENV]: marker },
282
282
  uid: context.uid,
283
283
  gid: context.gid,
284
284
  windowsHide: true,
285
285
  });
286
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 });
287
288
  child.stdout?.on("data", options.onData);
288
289
  child.stderr?.on("data", options.onData);
289
290
  let killed = false;
@@ -303,11 +304,10 @@ export const createSandboxTools = async (
303
304
  }
304
305
  });
305
306
  };
306
- const timeoutSeconds = options.timeout ?? DEFAULT_BASH_TIMEOUT_SECONDS;
307
307
  const timer = setTimeout(() => {
308
308
  timedOut = true;
309
309
  kill();
310
- }, timeoutSeconds * 1000);
310
+ }, options.timeout * 1000);
311
311
  options.signal?.addEventListener("abort", kill);
312
312
  const cleanup = () => {
313
313
  if (timer) clearTimeout(timer);
@@ -322,7 +322,7 @@ export const createSandboxTools = async (
322
322
  const finish = () => processError
323
323
  ? reject(processError)
324
324
  : timedOut
325
- ? reject(new Error(`timeout:${timeoutSeconds}`))
325
+ ? reject(new Error(`timeout:${options.timeout}`))
326
326
  : resolve({ exitCode: killed ? null : code });
327
327
  if (!child.pid) {
328
328
  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("/"));
@@ -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
  });
@@ -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
+ };