@schlenkr/ragents 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/server/src/config-definition.ts +24 -2
- package/apps/server/src/config.ts +0 -6
- package/apps/server/src/host-api.json +3 -10
- package/apps/server/src/host-api.ts +6 -7
- package/apps/server/src/plugin-build/build.ts +28 -5
- package/apps/server/src/plugin-build/plugin-description.ts +1 -1
- package/apps/server/src/plugin-build/typecheck.ts +2 -2
- package/apps/server/src/plugin-support/language-server/plugin.ts +8 -7
- package/apps/server/src/plugin-support/language-server/tools.ts +7 -6
- package/apps/server/src/plugin-support/model-aliases.ts +55 -25
- package/apps/server/src/plugin-support/plugin-folder.ts +2 -1
- package/apps/server/src/plugin-support/product-relay.ts +10 -2
- package/apps/server/src/plugin-support/workspace-sandbox-host.ts +18 -9
- package/apps/server/src/profile/bundle-manifest.ts +7 -3
- package/apps/server/src/profile/compose.ts +6 -0
- package/apps/server/src/profile/plugin-discovery.ts +6 -1
- package/apps/server/src/profile/provisioning.ts +6 -5
- package/apps/server/src/ragents/engine.ts +18 -12
- package/apps/server/src/ragents/host-services.ts +4 -0
- package/apps/server/src/ragents/session.ts +1 -1
- package/apps/server/src/server.ts +1 -0
- package/apps/server/tsconfig.json +2 -0
- package/apps/web/dist/assets/{highlighted-body-KPVGNVTW-CFhTJEmH.js → highlighted-body-KPVGNVTW-xXXhTDbh.js} +1 -1
- package/apps/web/dist/assets/{main-DlzW4t4c.js → main-C7JeZFjs.js} +1 -1
- package/apps/web/dist/assets/{mermaid-HWGCJPDP-kxkB991i.js → mermaid-HWGCJPDP-DfsU5ofC.js} +2 -2
- package/apps/web/dist/assets/{runPanel-BJEFQ__w.js → runPanel-ZT6TPDLM.js} +1 -1
- package/apps/web/dist/help/guide-clients.html +14 -6
- package/apps/web/dist/help/guide-clients.md +14 -6
- package/apps/web/dist/help/guide-distributed.html +12 -4
- package/apps/web/dist/help/guide-distributed.md +12 -4
- package/apps/web/dist/help/guide-getting-started.html +12 -8
- package/apps/web/dist/help/guide-getting-started.md +12 -8
- package/apps/web/dist/help/guide-plugins.html +6 -2
- package/apps/web/dist/help/guide-plugins.md +6 -2
- package/apps/web/dist/help/guide-runtime.html +2 -1
- package/apps/web/dist/help/guide-runtime.md +2 -1
- package/apps/web/dist/help/index.html +3328 -1135
- package/apps/web/dist/host-web.json +4 -4
- package/apps/web/dist/index.html +2 -2
- package/apps/web/dist/run-panel.html +2 -2
- package/apps/web/src/StartTiles.tsx +1 -1
- package/bundles/ragents.activity/ragents-bundle.json +3 -3
- package/bundles/ragents.actor-programs/ragents-bundle.json +3 -3
- package/bundles/ragents.ask/ragents-bundle.json +4 -4
- package/bundles/ragents.ask/server/index.js +42 -11
- package/bundles/ragents.ask/server/index.js.map +3 -3
- package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js +2 -0
- package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js.map +7 -0
- package/bundles/ragents.ask/web/classes.json +1 -1
- package/bundles/ragents.ask/web/exports/ask-payload.js +1 -1
- package/bundles/ragents.ask/web/index.js +1 -1
- package/bundles/ragents.ask/web/index.js.map +3 -3
- package/bundles/ragents.browser/executor/index.mjs +357 -0
- package/bundles/ragents.browser/executor/index.mjs.map +7 -0
- package/bundles/ragents.browser/ragents-bundle.json +5 -8
- package/bundles/ragents.browser/server/chunks/{chunk-2GKLLCBE.js → chunk-CWLX4VA5.js} +1 -1
- package/bundles/ragents.browser/server/chunks/chunk-CWLX4VA5.js.map +7 -0
- package/bundles/ragents.browser/server/exports/server/contract.js +1 -1
- package/bundles/ragents.browser/server/index.js +20 -9
- package/bundles/ragents.browser/server/index.js.map +4 -4
- package/bundles/ragents.documents/ragents-bundle.json +3 -3
- package/bundles/ragents.lsp-fsharp/executor/index.mjs +68 -0
- package/bundles/ragents.lsp-fsharp/executor/index.mjs.map +7 -0
- package/bundles/ragents.lsp-fsharp/ragents-bundle.json +5 -9
- package/bundles/ragents.lsp-fsharp/server/index.js +21 -6
- package/bundles/ragents.lsp-fsharp/server/index.js.map +4 -4
- package/bundles/ragents.lsp-roslyn/executor/index.mjs +68 -0
- package/bundles/ragents.lsp-roslyn/executor/index.mjs.map +7 -0
- package/bundles/ragents.lsp-roslyn/ragents-bundle.json +6 -8
- package/bundles/ragents.lsp-roslyn/server/index.js +54 -15
- package/bundles/ragents.lsp-roslyn/server/index.js.map +4 -4
- package/bundles/ragents.lsp-typescript/executor/index.mjs +49 -0
- package/bundles/ragents.lsp-typescript/executor/index.mjs.map +7 -0
- package/bundles/ragents.lsp-typescript/ragents-bundle.json +5 -7
- package/bundles/ragents.lsp-typescript/server/index.js +22 -2
- package/bundles/ragents.lsp-typescript/server/index.js.map +3 -3
- package/bundles/ragents.model-relay/ragents-bundle.json +4 -4
- package/bundles/ragents.model-relay/server/index.js +3 -2
- package/bundles/ragents.model-relay/server/index.js.map +2 -2
- package/bundles/ragents.orchestration/ragents-bundle.json +3 -3
- package/bundles/ragents.overseer/ragents-bundle.json +3 -3
- package/bundles/ragents.processes/ragents-bundle.json +3 -3
- package/bundles/ragents.product/ragents-bundle.json +3 -3
- package/bundles/ragents.profile-distribution/ragents-bundle.json +4 -4
- package/bundles/ragents.profile-distribution/server/index.js +1 -1
- package/bundles/ragents.profile-distribution/server/index.js.map +2 -2
- package/bundles/ragents.reference/ragents-bundle.json +3 -3
- package/bundles/ragents.todo/ragents-bundle.json +3 -3
- package/bundles/ragents.transcript/ragents-bundle.json +3 -3
- package/bundles/ragents.watch/ragents-bundle.json +3 -3
- package/bundles/ragents.workspace/ragents-bundle.json +11 -5
- package/bundles/ragents.workspace/server/index.js +101 -28
- package/bundles/ragents.workspace/server/index.js.map +3 -3
- package/bundles/ragents.workspace/web/classes.json +1 -1
- package/bundles/ragents.workspace/web/index.js +1 -1
- package/bundles/ragents.workspace/web/index.js.map +3 -3
- package/package.json +2 -2
- package/packages/agent/dist/core/agent-settings.d.ts +1 -7
- package/packages/agent/dist/core/compaction/compaction.d.ts +17 -13
- package/packages/agent/dist/core/model-runtime.d.ts +5 -4
- package/packages/agent/dist/core/provider-composer.d.ts +2 -1
- package/packages/agent/dist/core/tools/bash.d.ts +9 -3
- package/packages/agent/dist/index.d.ts +1 -1
- package/packages/agent/src/core/agent-settings.ts +1 -4
- package/packages/agent/src/core/compaction/compaction.ts +68 -30
- package/packages/agent/src/core/model-runtime.ts +12 -5
- package/packages/agent/src/core/provider-composer.ts +5 -0
- package/packages/agent/src/core/tools/bash.ts +36 -30
- package/packages/agent/src/index.ts +3 -0
- package/packages/ai/dist/types.d.ts +11 -0
- package/packages/ai/src/types.ts +12 -0
- package/packages/ragents/src/agents/plugins.ts +5 -0
- package/packages/ragents/src/domain/event-semantics.ts +3 -0
- package/packages/ragents/src/domain/event-validation.ts +15 -2
- package/packages/ragents/src/domain/events.ts +3 -0
- package/packages/ragents/src/domain/model.ts +2 -0
- package/packages/ragents/src/domain/projection.ts +1 -0
- package/packages/ragents/src/drivers/agent-runtime.ts +1 -1
- package/packages/ragents/src/drivers/agent-turn.ts +8 -6
- package/packages/ragents/src/index.ts +1 -0
- package/packages/ragents/src/plugin-host.ts +58 -10
- package/packages/ragents/src/plugin-types.ts +7 -2
- package/packages/ragents/src/runtime/decisions/inputs.ts +6 -2
- package/packages/ragents/src/runtime/journal-storage.ts +5 -4
- package/packages/workspace-executor/package.json +0 -1
- package/packages/workspace-executor/src/bash-launch.ts +56 -10
- package/packages/workspace-executor/src/context.ts +4 -0
- package/packages/workspace-executor/src/contributions.ts +150 -0
- package/packages/workspace-executor/src/executor.ts +1 -1
- package/packages/workspace-executor/src/index.ts +16 -19
- package/packages/workspace-executor/src/language-server/host.ts +3 -0
- package/packages/workspace-executor/src/language-server/module.ts +12 -2
- package/packages/workspace-executor/src/language-server/roots.ts +0 -5
- package/packages/workspace-executor/src/modules.ts +7 -6
- package/packages/workspace-executor/src/sandbox-tools.ts +3 -9
- package/packages/workspace-executor/src/shell-platform.ts +14 -3
- package/packages/workspace-executor/src/tools.ts +0 -3
- package/plugins/ragents.ask/ask-payload.ts +9 -0
- package/plugins/ragents.ask/server/ask-service.ts +53 -13
- package/plugins/ragents.ask/web/AskActionView.tsx +3 -2
- package/plugins/ragents.browser/executor/index.ts +5 -0
- package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/module.ts +25 -31
- package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/playwright.ts +9 -23
- package/plugins/ragents.browser/provision.ts +1 -1
- package/plugins/ragents.browser/server/browser.ts +3 -3
- package/plugins/ragents.browser/server/contract.ts +2 -2
- package/plugins/ragents.browser/server/index.ts +1 -1
- package/plugins/ragents.lsp-fsharp/executor.ts +80 -0
- package/plugins/ragents.lsp-fsharp/provision.ts +1 -1
- package/plugins/ragents.lsp-fsharp/server/index.ts +2 -2
- package/plugins/ragents.lsp-roslyn/executor.ts +64 -0
- package/plugins/ragents.lsp-roslyn/provision.ts +1 -1
- package/plugins/ragents.lsp-roslyn/server/index.ts +28 -5
- package/plugins/ragents.lsp-roslyn/server/solution-on-start.ts +18 -6
- package/plugins/ragents.lsp-typescript/executor.ts +50 -0
- package/plugins/ragents.lsp-typescript/server/index.ts +2 -2
- package/plugins/ragents.model-relay/server/config.ts +1 -1
- package/plugins/ragents.model-relay/server/relay.ts +4 -2
- package/plugins/ragents.profile-distribution/server/archive.ts +2 -1
- package/plugins/ragents.workspace/client/workspace-client.ts +111 -28
- package/plugins/ragents.workspace/contract.ts +33 -2
- package/plugins/ragents.workspace/server/binding.ts +10 -4
- package/plugins/ragents.workspace/server/clients.ts +56 -9
- package/plugins/ragents.workspace/server/config.ts +9 -0
- package/plugins/ragents.workspace/server/index.ts +13 -4
- package/plugins/ragents.workspace/server/runtime.ts +10 -0
- package/plugins/ragents.workspace/server/shell-platform.ts +6 -5
- package/plugins/ragents.workspace/web/WorkspaceBinding.tsx +1 -1
- package/ragents.config.core.ts +4 -1
- package/ragents.config.showcase.ts +4 -1
- package/scripts/agent/journal.ts +3 -1
- package/scripts/package/build-package.ts +3 -1
- package/scripts/package/publish-package.ts +4 -3
- package/scripts/provision/run-provision.ts +2 -2
- package/scripts/workspace-client/run-workspace-client.ts +7 -4
- package/apps/web/dist/help/screenshots/release-checklist-vscode.png +0 -0
- package/apps/web/dist/help/screenshots/release-checklist-web.png +0 -0
- package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js +0 -2
- package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js.map +0 -7
- package/bundles/ragents.browser/server/chunks/chunk-2GKLLCBE.js.map +0 -7
- package/packages/workspace-executor/src/language-server/adapters/fsharp.ts +0 -60
- package/packages/workspace-executor/src/language-server/adapters/index.ts +0 -7
- package/packages/workspace-executor/src/language-server/adapters/roslyn.ts +0 -56
- package/packages/workspace-executor/src/language-server/adapters/typescript.ts +0 -74
- package/packages/workspace-executor/src/language-server/solution.ts +0 -13
- /package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/contract.ts +0 -0
- /package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/pages.ts +0 -0
|
@@ -2,20 +2,30 @@ import { realpath } from "node:fs/promises";
|
|
|
2
2
|
import { homedir, tmpdir } from "node:os";
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import {
|
|
5
|
+
EXECUTOR_CONTRIBUTION_FILE,
|
|
5
6
|
WORKSPACE_EXECUTOR_VERSION,
|
|
6
7
|
containsWorkspacePath,
|
|
8
|
+
hostDataDirectory,
|
|
9
|
+
loadExecutorContribution,
|
|
10
|
+
pluginToolsDirectory,
|
|
11
|
+
prepareExecutorContribution,
|
|
12
|
+
ripgrepAvailable,
|
|
7
13
|
WorkspaceOperationError,
|
|
8
14
|
WorkspaceOperationExecutor,
|
|
9
15
|
workspaceDataDirectory,
|
|
10
16
|
workspaceExecutorModules,
|
|
11
17
|
workspaceProcessContext,
|
|
18
|
+
type ExecutorContributionStand,
|
|
19
|
+
type PreparedExecutorContribution,
|
|
12
20
|
type WorkspaceProcessContext,
|
|
13
21
|
} from "@ragents/workspace-executor";
|
|
14
22
|
import type { RpcClient } from "../../../apps/web/src/rpc/client";
|
|
15
23
|
import type { OperationInput } from "../../../packages/ragents/src/rpc/contract";
|
|
16
24
|
import type { RpcHandlerContext } from "../../../packages/ragents/src/rpc/peer";
|
|
25
|
+
import { RPC_ERROR_CODES, RpcError } from "../../../packages/ragents/src/rpc/protocol";
|
|
17
26
|
import { DomainError } from "../../../packages/ragents/src/runtime/domain-error";
|
|
18
27
|
import {
|
|
28
|
+
PLUGIN_ID_PATTERN,
|
|
19
29
|
WORKSPACE_CLIENT_STOP_OPERATION,
|
|
20
30
|
workspaceClientContracts,
|
|
21
31
|
workspaceContracts,
|
|
@@ -28,7 +38,8 @@ export type WorkspaceClientStatus =
|
|
|
28
38
|
| { kind: "registered" }
|
|
29
39
|
| { kind: "failed"; message: string };
|
|
30
40
|
|
|
31
|
-
|
|
41
|
+
/** Was der Arbeitsplatz über sich sagt; ob seine Bash rg findet, ermittelt er selbst bei jeder Anmeldung. */
|
|
42
|
+
export interface WorkspaceClientIdentity extends Omit<WorkspaceClientDescription, "ripgrep"> {
|
|
32
43
|
id: string;
|
|
33
44
|
}
|
|
34
45
|
|
|
@@ -41,10 +52,12 @@ export interface WorkspaceClientExecution {
|
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
export interface WorkspaceClientOptions {
|
|
44
|
-
/** Die Host-Wurzel dieses Rechners, aus der
|
|
55
|
+
/** Die Host-Wurzel dieses Rechners mit den Bundles, aus denen der Executor seine Beiträge lädt; sie kann erst später entstehen. */
|
|
45
56
|
hostRoot: () => string | undefined;
|
|
46
57
|
/** Die Bash dieses Rechners für das Werkzeug bash; unter Windows die mitgebrachte und Pflicht, sonst ohne Angabe die des Systems. */
|
|
47
58
|
bash?: string | undefined;
|
|
59
|
+
/** Das mitgebrachte rg, dessen Ordner die Bash vorn im PATH hat; ohne Angabe gilt eines im PATH dieses Rechners. */
|
|
60
|
+
rg?: string | undefined;
|
|
48
61
|
onExecuted?: (execution: WorkspaceClientExecution) => void;
|
|
49
62
|
}
|
|
50
63
|
|
|
@@ -93,9 +106,44 @@ const contextOf = (runs: ReadonlyMap<string, WorkspaceProcessContext>, runId: st
|
|
|
93
106
|
return Promise.resolve(context);
|
|
94
107
|
};
|
|
95
108
|
|
|
109
|
+
const sameContributions = (left: readonly ExecutorContributionStand[], right: readonly ExecutorContributionStand[]): boolean =>
|
|
110
|
+
left.length === right.length && left.every((entry) => right.some((other) => other.plugin === entry.plugin && other.stand === entry.stand));
|
|
111
|
+
|
|
112
|
+
/** Die Bundles eines Arbeitsplatzes sind die eingebauten seines Hosts; ein Plugin, das dort fehlt, kann er nicht ausführen. */
|
|
113
|
+
const contributionFileOf = (hostRoot: string, plugin: string): string => join(hostRoot, "bundles", plugin, EXECUTOR_CONTRIBUTION_FILE);
|
|
114
|
+
|
|
115
|
+
/** Lädt die Beiträge, die der Server verlangt, aus den Bundles des Hosts dieser Maschine; ein fehlendes Bundle oder ein anderer Stand ist ein Fehler mit Ursache. */
|
|
116
|
+
const loadedContributions = async (
|
|
117
|
+
hostRoot: string | undefined,
|
|
118
|
+
wanted: readonly ExecutorContributionStand[],
|
|
119
|
+
): Promise<readonly PreparedExecutorContribution[]> => {
|
|
120
|
+
if (wanted.length === 0) return [];
|
|
121
|
+
if (!hostRoot) {
|
|
122
|
+
throw new Error(`Der Server verlangt die Executor-Beiträge von ${wanted.map(({ plugin }) => plugin).join(", ")}; dieser Arbeitsplatz `
|
|
123
|
+
+ "kennt keinen Host, aus dessen Bundles er sie laden könnte. Er bekommt ihn mit der ersten Verbindung zu einem verteilenden "
|
|
124
|
+
+ "Server oder über die Einstellung ragents.hostPath");
|
|
125
|
+
}
|
|
126
|
+
return Promise.all(wanted.map(async ({ plugin, stand }) => {
|
|
127
|
+
if (!PLUGIN_ID_PATTERN.test(plugin)) throw new Error(`Der Server nennt ${plugin} als Plugin; das ist keine Kennung eines Plugins`);
|
|
128
|
+
const loaded = await loadExecutorContribution(plugin, contributionFileOf(hostRoot, plugin), stand).catch((cause: unknown) => {
|
|
129
|
+
throw new Error(`${messageOf(cause)}. Der Host dieses Arbeitsplatzes (${hostRoot}) muss dieselben Bundles tragen wie der Server: `
|
|
130
|
+
+ "die RAgents-Erweiterung beziehungsweise das Paket @schlenkr/ragents in der Fassung des Servers, im Checkout pnpm build:plugins");
|
|
131
|
+
});
|
|
132
|
+
return prepareExecutorContribution(loaded, pluginToolsDirectory(hostDataDirectory(), plugin));
|
|
133
|
+
}));
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/** Ein Executor aus den Beiträgen, die der Server bei der Anmeldung verlangt hat. */
|
|
137
|
+
interface BuiltExecutor {
|
|
138
|
+
readonly executor: WorkspaceOperationExecutor;
|
|
139
|
+
readonly contributions: readonly ExecutorContributionStand[];
|
|
140
|
+
}
|
|
141
|
+
|
|
96
142
|
/** Was eine Anmeldung auf diesem Rechner hält: ihren eigenen Executor, die Aufträge ihrer Runs und ihre Handler an der Nachrichtenschicht. */
|
|
97
143
|
interface Attachment {
|
|
98
|
-
|
|
144
|
+
/** Entsteht mit der Antwort des Servers, welche Beiträge er verlangt; verlangt er nach einer Neuverbindung andere, ersetzt ein neuer ihn. */
|
|
145
|
+
built: BuiltExecutor | undefined;
|
|
146
|
+
readonly runsDirectory: string;
|
|
99
147
|
readonly runs: Map<string, WorkspaceProcessContext>;
|
|
100
148
|
readonly release: () => void;
|
|
101
149
|
}
|
|
@@ -172,7 +220,7 @@ export class WorkspaceClient {
|
|
|
172
220
|
this.#set({ kind: "idle" });
|
|
173
221
|
const signingOff = Promise.allSettled([this.#signingOff, this.#announcing]).then(() => this.#signOff());
|
|
174
222
|
this.#signingOff = signingOff;
|
|
175
|
-
await Promise.all([signingOff, attachment?.executor.shutdown()]);
|
|
223
|
+
await Promise.all([signingOff, attachment?.built?.executor.shutdown()]);
|
|
176
224
|
}
|
|
177
225
|
|
|
178
226
|
/** Der Arbeitsplatz bindet immer sich selbst; wo der Server läuft, ändert daran nichts. */
|
|
@@ -183,19 +231,44 @@ export class WorkspaceClient {
|
|
|
183
231
|
/** Jede Anmeldung baut ihren eigenen Executor; einer, den eine Abmeldung beendet hat, wird nie wieder benutzt. */
|
|
184
232
|
#attach(): Attachment {
|
|
185
233
|
if (this.#attachment) return this.#attachment;
|
|
186
|
-
const runs = new Map<string, WorkspaceProcessContext>();
|
|
187
|
-
const runsDirectory = this.#identity.runsDirectory;
|
|
188
|
-
const executor = new WorkspaceOperationExecutor({
|
|
189
|
-
contextFor: (runId) => contextOf(runs, runId),
|
|
190
|
-
modules: workspaceExecutorModules({ runFolder: (runId) => runFolderOf(runsDirectory, runId) }),
|
|
191
|
-
});
|
|
192
234
|
const rpc = this.transport.rpc;
|
|
193
|
-
const releases = [
|
|
194
|
-
|
|
235
|
+
const releases: Array<() => void> = [];
|
|
236
|
+
const attachment: Attachment = {
|
|
237
|
+
built: undefined,
|
|
238
|
+
runsDirectory: this.#identity.runsDirectory,
|
|
239
|
+
runs: new Map(),
|
|
240
|
+
release: () => { for (const release of releases) release(); },
|
|
241
|
+
};
|
|
242
|
+
releases.push(
|
|
243
|
+
rpc.handle(workspaceClientContracts.execute, (input, context) => this.#execute(attachment, input, context)),
|
|
195
244
|
rpc.onConnected(() => void this.#announce()),
|
|
196
|
-
|
|
197
|
-
this.#attachment =
|
|
198
|
-
return
|
|
245
|
+
);
|
|
246
|
+
this.#attachment = attachment;
|
|
247
|
+
return attachment;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Behält den Executor, solange der Server dieselben Beiträge verlangt; sonst baut es einen neuen aus den Bundles dieses Rechners und beendet den alten. */
|
|
251
|
+
async #built(attachment: Attachment, wanted: readonly ExecutorContributionStand[]): Promise<BuiltExecutor> {
|
|
252
|
+
const current = attachment.built;
|
|
253
|
+
if (current && sameContributions(current.contributions, wanted)) return current;
|
|
254
|
+
const contributions = await loadedContributions(this.options.hostRoot(), wanted);
|
|
255
|
+
const built: BuiltExecutor = {
|
|
256
|
+
executor: new WorkspaceOperationExecutor({
|
|
257
|
+
contextFor: (runId) => contextOf(attachment.runs, runId),
|
|
258
|
+
modules: workspaceExecutorModules({
|
|
259
|
+
runFolder: (runId) => runFolderOf(attachment.runsDirectory, runId),
|
|
260
|
+
contributions: contributions.map((contribution) => contribution.parts),
|
|
261
|
+
}),
|
|
262
|
+
}),
|
|
263
|
+
contributions: contributions.map(({ plugin, stand }) => ({ plugin, stand })),
|
|
264
|
+
};
|
|
265
|
+
if (attachment !== this.#attachment) {
|
|
266
|
+
await built.executor.shutdown();
|
|
267
|
+
throw new Error("Der Arbeitsplatz hat sich inzwischen abgemeldet");
|
|
268
|
+
}
|
|
269
|
+
attachment.built = built;
|
|
270
|
+
await current?.executor.shutdown();
|
|
271
|
+
return built;
|
|
199
272
|
}
|
|
200
273
|
|
|
201
274
|
/** Wartet auf den Ereignisstrom; ohne ihn nimmt der Server die Anmeldung nicht an. */
|
|
@@ -219,10 +292,20 @@ export class WorkspaceClient {
|
|
|
219
292
|
return this.#announcing;
|
|
220
293
|
}
|
|
221
294
|
|
|
222
|
-
/**
|
|
295
|
+
/** Erst die Beiträge, die der Server verlangt, dann die Anmeldung mit dem Executor daraus; eine Antwort nach einer Abmeldung ändert den Zustand nicht mehr. */
|
|
223
296
|
async #send(): Promise<void> {
|
|
224
297
|
const attachment = this.#attachment;
|
|
298
|
+
if (!attachment) return;
|
|
225
299
|
try {
|
|
300
|
+
const wanted = await this.transport.rpc.call(workspaceContracts.clients.contributions, {
|
|
301
|
+
label: this.#identity.label,
|
|
302
|
+
executor: WORKSPACE_EXECUTOR_VERSION,
|
|
303
|
+
}, { timeoutMs: REGISTER_TIMEOUT_MS }).catch((cause: unknown) => {
|
|
304
|
+
if (!(cause instanceof RpcError) || cause.code !== RPC_ERROR_CODES.methodNotFound) throw cause;
|
|
305
|
+
throw new Error(`Der Server kennt ${workspaceContracts.clients.contributions.id} nicht; er ist älter als dieser Arbeitsplatz `
|
|
306
|
+
+ `mit dem Executor ${WORKSPACE_EXECUTOR_VERSION}. Den Server auf die Fassung des Arbeitsplatzes aktualisieren.`);
|
|
307
|
+
});
|
|
308
|
+
const { contributions } = await this.#built(attachment, wanted);
|
|
226
309
|
await this.transport.rpc.call(workspaceContracts.clients.register, {
|
|
227
310
|
id: this.#identity.id,
|
|
228
311
|
label: this.#identity.label,
|
|
@@ -230,7 +313,9 @@ export class WorkspaceClient {
|
|
|
230
313
|
platform: this.#identity.platform,
|
|
231
314
|
folders: [...this.#identity.folders],
|
|
232
315
|
runsDirectory: this.#identity.runsDirectory,
|
|
316
|
+
ripgrep: ripgrepAvailable(this.options.rg, process.env),
|
|
233
317
|
executor: WORKSPACE_EXECUTOR_VERSION,
|
|
318
|
+
contributions: contributions.map((contribution) => ({ ...contribution })),
|
|
234
319
|
}, { timeoutMs: REGISTER_TIMEOUT_MS });
|
|
235
320
|
if (attachment === this.#attachment) this.#set({ kind: "registered" });
|
|
236
321
|
} catch (cause) {
|
|
@@ -247,17 +332,14 @@ export class WorkspaceClient {
|
|
|
247
332
|
}
|
|
248
333
|
|
|
249
334
|
/** Der Entwickler arbeitet auf seinem Arbeitsplatz mit seinen eigenen Zugangsdaten und seiner ganzen Umgebung; HOME bleibt sein Home. */
|
|
250
|
-
async #execute(
|
|
251
|
-
|
|
252
|
-
runs: Map<string, WorkspaceProcessContext>,
|
|
253
|
-
input: ExecuteInput,
|
|
254
|
-
context: RpcHandlerContext,
|
|
255
|
-
): Promise<{ value: unknown }> {
|
|
256
|
-
if (input.operation === WORKSPACE_CLIENT_STOP_OPERATION) return this.#stop(executor, runs, input.runId);
|
|
335
|
+
async #execute(attachment: Attachment, input: ExecuteInput, context: RpcHandlerContext): Promise<{ value: unknown }> {
|
|
336
|
+
if (input.operation === WORKSPACE_CLIENT_STOP_OPERATION) return this.#stop(attachment, input.runId);
|
|
257
337
|
const cwd = await this.#inside(input.cwd, input.runId);
|
|
258
338
|
const root = await realPathOf(cwd);
|
|
259
|
-
if (
|
|
260
|
-
|
|
339
|
+
if (attachment !== this.#attachment) throw new Error("Der Arbeitsplatz hat sich inzwischen abgemeldet");
|
|
340
|
+
const executor = attachment.built?.executor;
|
|
341
|
+
if (!executor) throw new Error("Der Arbeitsplatz ist beim Server noch nicht angemeldet");
|
|
342
|
+
attachment.runs.set(input.runId, workspaceProcessContext({
|
|
261
343
|
runId: input.runId,
|
|
262
344
|
cwd,
|
|
263
345
|
root,
|
|
@@ -267,6 +349,7 @@ export class WorkspaceClient {
|
|
|
267
349
|
additions: input.env,
|
|
268
350
|
baseEnvironment: "inherited",
|
|
269
351
|
...(this.options.bash === undefined ? {} : { bash: this.options.bash }),
|
|
352
|
+
...(this.options.rg === undefined ? {} : { rg: this.options.rg }),
|
|
270
353
|
}));
|
|
271
354
|
const startedAt = Date.now();
|
|
272
355
|
let failure: string | undefined;
|
|
@@ -293,11 +376,11 @@ export class WorkspaceClient {
|
|
|
293
376
|
}
|
|
294
377
|
|
|
295
378
|
/** Der Stopp braucht keinen Ordner: er gibt frei, was der Run hält, auch wenn sein Ordner nicht mehr angeboten wird. */
|
|
296
|
-
async #stop(
|
|
379
|
+
async #stop(attachment: Attachment, runId: string): Promise<{ value: null }> {
|
|
297
380
|
try {
|
|
298
|
-
await executor.stopRun(runId);
|
|
381
|
+
await attachment.built?.executor.stopRun(runId);
|
|
299
382
|
} finally {
|
|
300
|
-
runs.delete(runId);
|
|
383
|
+
attachment.runs.delete(runId);
|
|
301
384
|
}
|
|
302
385
|
return { value: null };
|
|
303
386
|
}
|
|
@@ -66,6 +66,8 @@ export interface WorkspaceClientDescription {
|
|
|
66
66
|
folders: string[];
|
|
67
67
|
/** Wo der Arbeitsplatz die neuen Ordner je Run anlegt, je Run ein Unterordner mit dessen Kennung. */
|
|
68
68
|
runsDirectory: string;
|
|
69
|
+
/** Ob die Bash des Arbeitsplatzes rg findet; der Prompt richtet die Suche danach. */
|
|
70
|
+
ripgrep: boolean;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
export interface WorkspaceClientInfo extends WorkspaceClientDescription {
|
|
@@ -95,12 +97,21 @@ const clientId = Type.String({ pattern: "^[A-Za-z0-9_-]{8,64}$", description: "S
|
|
|
95
97
|
|
|
96
98
|
const executorVersion = Type.String({ minLength: 1, maxLength: 64, description: "Stand des Executors, den der Arbeitsplatz mitbringt" });
|
|
97
99
|
|
|
100
|
+
/** Die Kennung eines Plugins; ein Arbeitsplatz sucht dessen Bundle unter genau diesem Namen, ein Pfad kann darin nicht stecken. */
|
|
101
|
+
export const PLUGIN_ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
|
|
102
|
+
|
|
103
|
+
const executorContributions = Type.Array(Type.Object({
|
|
104
|
+
plugin: Type.String({ pattern: PLUGIN_ID_PATTERN.source, maxLength: 128, description: "Kennung des Plugins" }),
|
|
105
|
+
stand: Type.String({ pattern: "^[0-9a-f]{64}$", description: "SHA-256 der Datei des Beitrags" }),
|
|
106
|
+
}, { additionalProperties: false }), { maxItems: 64, description: "Die Beiträge der Plugins zum Executor, in der Reihenfolge der Pluginliste des Servers" });
|
|
107
|
+
|
|
98
108
|
const clientDescription = {
|
|
99
109
|
label: Type.String({ minLength: 1, maxLength: 120 }),
|
|
100
110
|
hostname: Type.String({ minLength: 1 }),
|
|
101
111
|
platform: Type.String({ minLength: 1 }),
|
|
102
112
|
folders: Type.Array(Type.String({ minLength: 1 }), { maxItems: 32 }),
|
|
103
113
|
runsDirectory: Type.String({ minLength: 1, description: "Absoluter Ordner, unter dem der Arbeitsplatz die neuen Ordner je Run anlegt" }),
|
|
114
|
+
ripgrep: Type.Boolean({ description: "Ob die Bash des Arbeitsplatzes rg findet" }),
|
|
104
115
|
};
|
|
105
116
|
|
|
106
117
|
export const clientInfoSchema = Type.Object({
|
|
@@ -108,6 +119,19 @@ export const clientInfoSchema = Type.Object({
|
|
|
108
119
|
...clientDescription,
|
|
109
120
|
}, { additionalProperties: false });
|
|
110
121
|
|
|
122
|
+
/** Die Anmeldung, wie ein Arbeitsplatz mit dem Executor des Servers sie sendet. */
|
|
123
|
+
export const clientRegistrationSchema = Type.Object({
|
|
124
|
+
id: clientId,
|
|
125
|
+
...clientDescription,
|
|
126
|
+
executor: executorVersion,
|
|
127
|
+
contributions: executorContributions,
|
|
128
|
+
}, { additionalProperties: false });
|
|
129
|
+
|
|
130
|
+
/** Ein Arbeitsplatz mit einem anderen Executor kennt die Form dieses Stands nicht; der Server lehnt ihn an seinem Stand ab, nicht an der Form. */
|
|
131
|
+
const otherExecutorRegistration = Type.Object({ label: clientDescription.label, executor: executorVersion }, {
|
|
132
|
+
description: "Anmeldung mit einem anderen Executor-Stand in beliebiger Form; der Server lehnt sie mit workspace-executor-version ab",
|
|
133
|
+
});
|
|
134
|
+
|
|
111
135
|
const absolutePath = Type.String({ minLength: 1, description: "Absoluter Pfad im gebundenen Ordner" });
|
|
112
136
|
|
|
113
137
|
/** Keine Operation eines Moduls: gibt frei, was der Executor des Arbeitsplatzes für den Run hält. */
|
|
@@ -172,11 +196,18 @@ export const workspaceContracts = {
|
|
|
172
196
|
input: Type.Object({}, { additionalProperties: false }),
|
|
173
197
|
result: Type.Array(clientInfoSchema),
|
|
174
198
|
}),
|
|
199
|
+
contributions: defineOperation({
|
|
200
|
+
id: "ragents.workspace.clients.contributions",
|
|
201
|
+
description: "Die Beiträge der Plugins zum Executor, die ein Arbeitsplatz vor der Anmeldung lädt und mit ihr zurückmeldet. Ein Arbeitsplatz mit anderem Executor-Stand scheitert an seinem Stand; weitere Felder zählen nicht.",
|
|
202
|
+
rights: ["runs.write"],
|
|
203
|
+
input: Type.Object({ label: clientDescription.label, executor: executorVersion }),
|
|
204
|
+
result: executorContributions,
|
|
205
|
+
}),
|
|
175
206
|
register: defineOperation({
|
|
176
207
|
id: "ragents.workspace.clients.register",
|
|
177
|
-
description: "Einen Arbeitsplatz anmelden oder seine Ordner erneuern; die Verbindung dieser Anfrage wird sein Rückweg und braucht einen Ereignisstrom.",
|
|
208
|
+
description: "Einen Arbeitsplatz anmelden oder seine Ordner erneuern; die Verbindung dieser Anfrage wird sein Rückweg und braucht einen Ereignisstrom. Ein Arbeitsplatz mit anderem Executor-Stand scheitert an seinem Stand, bevor die übrige Form zählt.",
|
|
178
209
|
rights: ["runs.write"],
|
|
179
|
-
input: Type.
|
|
210
|
+
input: Type.Union([clientRegistrationSchema, otherExecutorRegistration]),
|
|
180
211
|
result: clientInfoSchema,
|
|
181
212
|
}),
|
|
182
213
|
unregister: defineOperation({
|
|
@@ -2,6 +2,7 @@ import { statSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
4
|
import { DomainError, type JsonValue, type RunState, type StartOptionContribution } from "@ragents/engine";
|
|
5
|
+
import type { ShellTools } from "@ragents/workspace-executor";
|
|
5
6
|
import { shellPlatformChapter } from "./shell-platform.js";
|
|
6
7
|
import { storedStartOption } from "@ragents/host/ragents/start-option-state.js";
|
|
7
8
|
import type { WorkspaceResolver } from "@ragents/host/ragents/workspace-runtime.js";
|
|
@@ -87,13 +88,18 @@ export const serverDirectoryFolder = (directory: string): ExistingWorkspaceFolde
|
|
|
87
88
|
return { path: resolved };
|
|
88
89
|
};
|
|
89
90
|
|
|
90
|
-
/** Die Bash eines Runs läuft auf dem Executor seiner Bindung; der Prompt nennt deshalb dessen Plattform, nicht die des Servers. */
|
|
91
|
-
export const executorShellChapter = (
|
|
91
|
+
/** Die Bash eines Runs läuft auf dem Executor seiner Bindung; der Prompt nennt deshalb dessen Plattform und Werkzeuge, nicht die des Servers. */
|
|
92
|
+
export const executorShellChapter = (
|
|
93
|
+
registry: WorkspaceClientRegistry,
|
|
94
|
+
owner: string | null,
|
|
95
|
+
binding: WorkspaceBinding,
|
|
96
|
+
serverTools: ShellTools,
|
|
97
|
+
): string => {
|
|
92
98
|
const { machine } = binding;
|
|
93
|
-
if (machine === "server") return shellPlatformChapter(process.platform);
|
|
99
|
+
if (machine === "server") return shellPlatformChapter(process.platform, serverTools);
|
|
94
100
|
const client = registry.info(owner, machine.client);
|
|
95
101
|
return client
|
|
96
|
-
? shellPlatformChapter(client.platform as NodeJS.Platform)
|
|
102
|
+
? shellPlatformChapter(client.platform as NodeJS.Platform, { ripgrep: client.ripgrep })
|
|
97
103
|
: `## Shell platform\n\nThe workstation ${machine.label} that holds this project is not registered right now, `
|
|
98
104
|
+ "so the platform of its shell is unknown; the workspace tools fail until it connects again.";
|
|
99
105
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { Value } from "typebox/value";
|
|
1
2
|
import {
|
|
2
3
|
DomainError,
|
|
3
4
|
RPC_ERROR_CODES,
|
|
4
5
|
RpcError,
|
|
5
6
|
implement,
|
|
7
|
+
schemaComplaints,
|
|
6
8
|
type AccessContext,
|
|
7
9
|
type MethodConnection,
|
|
8
10
|
type MethodContribution,
|
|
@@ -10,11 +12,13 @@ import {
|
|
|
10
12
|
import {
|
|
11
13
|
WORKSPACE_EXECUTOR_VERSION,
|
|
12
14
|
sandboxRunEnvironment,
|
|
15
|
+
type ExecutorContributionStand,
|
|
13
16
|
type WorkspaceExecuteOptions,
|
|
14
17
|
type WorkspaceExecutor,
|
|
15
18
|
} from "@ragents/workspace-executor";
|
|
16
19
|
import {
|
|
17
20
|
WORKSPACE_CLIENT_STOP_OPERATION,
|
|
21
|
+
clientRegistrationSchema,
|
|
18
22
|
workspaceClientContracts,
|
|
19
23
|
workspaceContracts,
|
|
20
24
|
type WorkspaceClientDescription,
|
|
@@ -75,6 +79,35 @@ const messageOf = (cause: unknown): string => cause instanceof Error ? cause.mes
|
|
|
75
79
|
/** Ein Arbeitsplatz ist erst durch Besitzer und Kennung bestimmt; zwei Benutzer mit derselben Kennung teilen nichts. */
|
|
76
80
|
const keyOf = (owner: string | null, id: string): string => JSON.stringify([owner, id]);
|
|
77
81
|
|
|
82
|
+
/** Server und Arbeitsplatz brauchen denselben Executor; die Meldung sagt, wer von beiden zu alt ist. */
|
|
83
|
+
const assertExecutor = (label: string, executor: string): void => {
|
|
84
|
+
if (executor === WORKSPACE_EXECUTOR_VERSION) return;
|
|
85
|
+
const action = Number(executor) > Number(WORKSPACE_EXECUTOR_VERSION)
|
|
86
|
+
? "Den Server auf die Fassung des Arbeitsplatzes aktualisieren."
|
|
87
|
+
: "Die RAgents-Erweiterung in VS Code beziehungsweise das Paket @schlenkr/ragents auf dem Arbeitsplatz aktualisieren.";
|
|
88
|
+
throw new DomainError(
|
|
89
|
+
"workspace-executor-version",
|
|
90
|
+
`Der Arbeitsplatz ${label} bringt den Executor ${executor} mit, der Server verlangt ${WORKSPACE_EXECUTOR_VERSION}. ${action}`,
|
|
91
|
+
409,
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const described = (contributions: readonly ExecutorContributionStand[]): string =>
|
|
96
|
+
contributions.map(({ plugin, stand }) => `${plugin} (${stand.slice(0, 12)})`).join(", ") || "keine";
|
|
97
|
+
|
|
98
|
+
/** Ein Arbeitsplatz führt aus, was die Plugins des Servers beitragen; mit anderen Beiträgen liefen dieselben Werkzeuge dort anders als auf dem Server. */
|
|
99
|
+
const assertContributions = (label: string, expected: readonly ExecutorContributionStand[], given: readonly ExecutorContributionStand[]): void => {
|
|
100
|
+
const key = (entry: ExecutorContributionStand): string => `${entry.plugin}\0${entry.stand}`;
|
|
101
|
+
const same = expected.length === given.length && expected.every((entry) => given.some((other) => key(other) === key(entry)));
|
|
102
|
+
if (same) return;
|
|
103
|
+
throw new DomainError(
|
|
104
|
+
"workspace-executor-contributions",
|
|
105
|
+
`Der Arbeitsplatz ${label} bringt die Executor-Beiträge ${described(given)} mit, der Server verlangt ${described(expected)}. `
|
|
106
|
+
+ "Der Arbeitsplatz fragt sie mit ragents.workspace.clients.contributions ab und meldet sich danach erneut an.",
|
|
107
|
+
409,
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
78
111
|
/** Über das Netz nimmt der Server Arbeitsplätze nur von angemeldeten Benutzern an; ohne Benutzer gäbe es nur einen Besitzer für alle Zugänge. */
|
|
79
112
|
export const assertMayRegister = (access: AccessContext, local: boolean): void => {
|
|
80
113
|
if (local || (access.enabled && access.user !== null)) return;
|
|
@@ -88,27 +121,29 @@ export const assertMayRegister = (access: AccessContext, local: boolean): void =
|
|
|
88
121
|
|
|
89
122
|
/** Kennt die angemeldeten Arbeitsplätze je Besitzer, hält je Client die Verbindung, über die der Server ihn zurückruft, und die Stopps, die ihn nicht erreicht haben. */
|
|
90
123
|
export class WorkspaceClientRegistry {
|
|
124
|
+
/** Was die Plugins des Servers zum Executor beitragen; jeder Arbeitsplatz muss genau diese Beiträge tragen. */
|
|
125
|
+
readonly contributions: readonly ExecutorContributionStand[];
|
|
91
126
|
readonly #clients = new Map<string, ClientEntry>();
|
|
92
127
|
/** Stopps, die den Arbeitsplatz noch nicht erreicht haben, je Arbeitsplatz und Run mit dem Ordner der Bindung. */
|
|
93
128
|
readonly #pendingStops = new Map<string, Map<string, string>>();
|
|
94
129
|
readonly #stopRetries = new Map<string, NodeJS.Timeout>();
|
|
95
130
|
|
|
131
|
+
constructor(contributions: readonly ExecutorContributionStand[]) {
|
|
132
|
+
this.contributions = contributions.map(({ plugin, stand }) => ({ plugin, stand }));
|
|
133
|
+
}
|
|
134
|
+
|
|
96
135
|
async register(
|
|
97
136
|
id: string,
|
|
98
137
|
description: WorkspaceClientDescription,
|
|
99
138
|
executor: string,
|
|
139
|
+
contributions: readonly ExecutorContributionStand[],
|
|
100
140
|
connection: MethodConnection,
|
|
101
141
|
): Promise<WorkspaceClientInfo> {
|
|
102
142
|
if (connection.streamless) {
|
|
103
143
|
throw new DomainError("stream-required", "Die Anmeldung eines Arbeitsplatzes braucht einen Ereignisstrom.", 409);
|
|
104
144
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
"workspace-executor-version",
|
|
108
|
-
`Der Arbeitsplatz ${description.label} bringt den Executor ${executor} mit, der Server verlangt ${WORKSPACE_EXECUTOR_VERSION}.`,
|
|
109
|
-
409,
|
|
110
|
-
);
|
|
111
|
-
}
|
|
145
|
+
assertExecutor(description.label, executor);
|
|
146
|
+
assertContributions(description.label, this.contributions, contributions);
|
|
112
147
|
const owner = connection.userId;
|
|
113
148
|
const key = keyOf(owner, id);
|
|
114
149
|
const previous = this.#clients.get(key);
|
|
@@ -318,9 +353,21 @@ export class WorkspaceClientRegistry {
|
|
|
318
353
|
|
|
319
354
|
export const clientMethods = (registry: WorkspaceClientRegistry): MethodContribution[] => [
|
|
320
355
|
implement(workspaceContracts.clients.list, (_input, { access }) => registry.list(ownerOf(access))),
|
|
321
|
-
implement(workspaceContracts.clients.
|
|
356
|
+
implement(workspaceContracts.clients.contributions, ({ label, executor }, { access, local }) => {
|
|
322
357
|
assertMayRegister(access, local);
|
|
323
|
-
|
|
358
|
+
assertExecutor(label, executor);
|
|
359
|
+
return registry.contributions.map((entry) => ({ ...entry }));
|
|
360
|
+
}),
|
|
361
|
+
implement(workspaceContracts.clients.register, (input, { access, connection, local }) => {
|
|
362
|
+
assertMayRegister(access, local);
|
|
363
|
+
// Erst der Stand: ein Arbeitsplatz mit einem anderen kennt die Form dieses Stands nicht, mit diesem gilt sie ganz.
|
|
364
|
+
assertExecutor(input.label, input.executor);
|
|
365
|
+
if (!Value.Check(clientRegistrationSchema, input)) {
|
|
366
|
+
throw new RpcError(RPC_ERROR_CODES.invalidParams,
|
|
367
|
+
`Ungültige Eingabe für ${workspaceContracts.clients.register.id}: ${schemaComplaints(clientRegistrationSchema, input, "params")}`);
|
|
368
|
+
}
|
|
369
|
+
const { id, executor, contributions, ...description } = input;
|
|
370
|
+
return registry.register(id, description, executor, contributions, connection);
|
|
324
371
|
}),
|
|
325
372
|
implement(workspaceContracts.clients.unregister, ({ id }, { access, connection }) => {
|
|
326
373
|
registry.unregister(ownerOf(access), id, connection);
|
|
@@ -5,6 +5,8 @@ export const workspaceConfigDescriptors = [
|
|
|
5
5
|
{ key: "PROCESS_SANDBOX", source: "environment" },
|
|
6
6
|
{ key: "PROCESS_SANDBOX_NETWORK", source: "environment" },
|
|
7
7
|
{ key: "RAGENTS_BASH", source: "environment" },
|
|
8
|
+
{ key: "RAGENTS_RG", source: "environment" },
|
|
9
|
+
{ key: "RAGENTS_BASH_TIMEOUT_SECONDS", source: "environment" },
|
|
8
10
|
] as const;
|
|
9
11
|
|
|
10
12
|
const env = declaredEnvironment(workspaceConfigDescriptors);
|
|
@@ -28,3 +30,10 @@ export const processSandboxSetting = (): ProcessSandboxSetting => {
|
|
|
28
30
|
|
|
29
31
|
/** Die Bash, mit der der Executor dieses Servers das Werkzeug bash startet; unter Windows setzt sie die VS-Code-Erweiterung für ihren lokalen Host. */
|
|
30
32
|
export const bashSetting = (): string | undefined => env.optional("RAGENTS_BASH") || undefined;
|
|
33
|
+
|
|
34
|
+
/** Das rg, das der Executor dieses Servers vorn in den PATH der Bash setzt; die VS-Code-Erweiterung setzt es für ihren lokalen Host. */
|
|
35
|
+
export const rgSetting = (): string | undefined => env.optional("RAGENTS_RG") || undefined;
|
|
36
|
+
|
|
37
|
+
/** Die Zeitgrenze von bash in Sekunden für Aufrufe ohne eigene; mehr als die größte, die ein Aufruf nennen darf, lehnt das Werkzeug beim Start ab. */
|
|
38
|
+
export const bashTimeoutSetting = (): number | undefined =>
|
|
39
|
+
env.optional("RAGENTS_BASH_TIMEOUT_SECONDS") ? env.positiveNumber("RAGENTS_BASH_TIMEOUT_SECONDS", "") : undefined;
|
|
@@ -4,6 +4,7 @@ import { documentStoreToken } from "@ragents/host/ragents/document-store.js";
|
|
|
4
4
|
import { workspaceResolverToken, workspaceRuntimeToken, type WorkspaceResolver } from "@ragents/host/ragents/workspace-runtime.js";
|
|
5
5
|
import { sandboxServicesToken } from "@ragents/host/plugin-support/workspace-sandbox-host.js";
|
|
6
6
|
import {
|
|
7
|
+
executorContributionsToken,
|
|
7
8
|
hostAddressToken,
|
|
8
9
|
runtimeProviderToken,
|
|
9
10
|
runGuardToken,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
} from "@ragents/host/ragents/host-services.js";
|
|
13
14
|
import { pluginAsset } from "@ragents/host/plugin-support/plugin-folder.js";
|
|
14
15
|
import { ServerProcessSandbox } from "@ragents/host/plugin-support/process-sandbox.js";
|
|
16
|
+
import { ripgrepAvailable } from "@ragents/workspace-executor";
|
|
15
17
|
import type { PluginModule } from "@ragents/host/plugin-support/plugin-module.js";
|
|
16
18
|
import { boundToTools, handlebarsPrompt } from "@ragents/host/plugin-support/prompt.js";
|
|
17
19
|
import { shellPlatformPrompt } from "./shell-platform.js";
|
|
@@ -21,7 +23,7 @@ import { WORKSPACE_BINDING_OPTION_ID, WORKSPACE_METADATA_ID } from "../contract.
|
|
|
21
23
|
import { bindingOf, executorShellChapter, sessionMetadataOf, workspaceBindingOption, workspaceLocation, workspaceOwnerOf } from "./binding.js";
|
|
22
24
|
import { createBrowseChannel, createBrowseMethods } from "./browse-route.js";
|
|
23
25
|
import { clientMethods, WorkspaceClientRegistry } from "./clients.js";
|
|
24
|
-
import { bashSetting, processSandboxSetting, workspaceConfigDescriptors } from "./config.js";
|
|
26
|
+
import { bashSetting, bashTimeoutSetting, processSandboxSetting, rgSetting, workspaceConfigDescriptors } from "./config.js";
|
|
25
27
|
import { RunWorkspaceRuntime } from "./runtime.js";
|
|
26
28
|
|
|
27
29
|
const warnWithoutSandbox = (): void => {
|
|
@@ -48,7 +50,8 @@ const ragentsWorkspacePlugin = (skillPaths: () => Promise<readonly string[]>): R
|
|
|
48
50
|
if (!directory) throw new Error("In diesem Profil gibt es keine Dateiablage; das Plugin ragents.documents fehlt");
|
|
49
51
|
return directory;
|
|
50
52
|
};
|
|
51
|
-
const
|
|
53
|
+
const contributions = host.service(executorContributionsToken);
|
|
54
|
+
const clients = new WorkspaceClientRegistry(contributions);
|
|
52
55
|
const contribution = (): WorkspaceResolver | undefined => host.optionalService(workspaceResolverToken);
|
|
53
56
|
host.config(...workspaceConfigDescriptors);
|
|
54
57
|
const sandboxSetting = processSandboxSetting();
|
|
@@ -60,10 +63,16 @@ const ragentsWorkspacePlugin = (skillPaths: () => Promise<readonly string[]>): R
|
|
|
60
63
|
})
|
|
61
64
|
: undefined;
|
|
62
65
|
const bash = bashSetting();
|
|
66
|
+
const rg = rgSetting();
|
|
67
|
+
const bashTimeoutSeconds = bashTimeoutSetting();
|
|
68
|
+
const serverTools = { ripgrep: ripgrepAvailable(rg, process.env) };
|
|
63
69
|
const runtime = new RunWorkspaceRuntime({
|
|
64
70
|
clients,
|
|
71
|
+
contributions: contributions.map((contribution) => contribution.parts),
|
|
65
72
|
...(processSandbox ? { processSandbox } : {}),
|
|
66
73
|
...(bash === undefined ? {} : { bash }),
|
|
74
|
+
...(rg === undefined ? {} : { rg }),
|
|
75
|
+
...(bashTimeoutSeconds === undefined ? {} : { bashTimeoutSeconds }),
|
|
67
76
|
globalDirectory: host.storage.root(),
|
|
68
77
|
sessionDirectory: (runId, ...segments) => host.storage.session(runId, ...segments),
|
|
69
78
|
storageRootFor: (runId) => path.join(host.storage.sessionsRoot, runId),
|
|
@@ -92,9 +101,9 @@ const ragentsWorkspacePlugin = (skillPaths: () => Promise<readonly string[]>): R
|
|
|
92
101
|
// Die Regeln gelten für die Arbeitsbereiche des Hosts; eine beigesteuerte Art beschreibt ihre eigenen.
|
|
93
102
|
render: (context) => contribution()?.kind ? "" : rules.render(context),
|
|
94
103
|
}, ...agentWorkspaceToolNames),
|
|
95
|
-
shellPlatformPrompt("ragents.workspace.shell.prompt", 102, (runId) => {
|
|
104
|
+
shellPlatformPrompt("ragents.workspace.shell.prompt", 102, serverTools, (runId) => {
|
|
96
105
|
const state = runState(runId);
|
|
97
|
-
return executorShellChapter(clients, workspaceOwnerOf(state), bindingOf(state));
|
|
106
|
+
return executorShellChapter(clients, workspaceOwnerOf(state), bindingOf(state), serverTools);
|
|
98
107
|
}),
|
|
99
108
|
);
|
|
100
109
|
const browseOptions = {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type SandboxHomeEnvironment,
|
|
9
9
|
type WorkspaceExecuteOptions,
|
|
10
10
|
type WorkspaceExecutor,
|
|
11
|
+
type WorkspaceExecutorParts,
|
|
11
12
|
} from "@ragents/workspace-executor";
|
|
12
13
|
import type {
|
|
13
14
|
SessionWorkspace,
|
|
@@ -46,10 +47,16 @@ export interface RunWorkspaceRuntimeOptions {
|
|
|
46
47
|
runState: (runId: string) => RunState | null;
|
|
47
48
|
storeBinding: (runId: string, binding: WorkspaceBinding) => void;
|
|
48
49
|
clients: WorkspaceClientRegistry;
|
|
50
|
+
/** Was die Plugins des Profils zum Executor des Servers beitragen; die Arbeitsplätze laden dieselben Beiträge selbst. */
|
|
51
|
+
contributions: readonly WorkspaceExecutorParts[];
|
|
49
52
|
/** Die Prozess-Sandbox des Servers; ohne sie laufen die Prozesse des Servers ohne. */
|
|
50
53
|
processSandbox?: RunProcessSandboxes;
|
|
51
54
|
/** Die Bash des Servers aus RAGENTS_BASH; unter Windows Pflicht für das Werkzeug bash. */
|
|
52
55
|
bash?: string;
|
|
56
|
+
/** Das rg des Servers aus RAGENTS_RG; ohne Angabe gilt eines im PATH. */
|
|
57
|
+
rg?: string;
|
|
58
|
+
/** Die Zeitgrenze von bash aus RAGENTS_BASH_TIMEOUT_SECONDS; ohne Angabe die des Werkzeugs. */
|
|
59
|
+
bashTimeoutSeconds?: number;
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
const runNotStarted = (): DomainError => new DomainError(
|
|
@@ -141,6 +148,7 @@ export class RunWorkspaceRuntime implements WorkspaceRuntime {
|
|
|
141
148
|
this.#nugetCacheDirectory = path.join(options.globalDirectory, "nuget-cache");
|
|
142
149
|
this.sandbox = new WorkspaceSandboxHost({
|
|
143
150
|
contributorName: "ragents.workspace.sandbox",
|
|
151
|
+
contributions: options.contributions,
|
|
144
152
|
workspaceFor: options.sessionWorkspaceFor,
|
|
145
153
|
identFor: () => Promise.resolve(undefined),
|
|
146
154
|
skillPaths: options.skillPaths,
|
|
@@ -150,6 +158,8 @@ export class RunWorkspaceRuntime implements WorkspaceRuntime {
|
|
|
150
158
|
serverDirectoryFor: (runId) => this.#serverDirectoryFor(runId),
|
|
151
159
|
...(options.processSandbox ? { processSandbox: options.processSandbox } : {}),
|
|
152
160
|
...(options.bash === undefined ? {} : { bash: options.bash }),
|
|
161
|
+
...(options.rg === undefined ? {} : { rg: options.rg }),
|
|
162
|
+
...(options.bashTimeoutSeconds === undefined ? {} : { bashTimeoutSeconds: options.bashTimeoutSeconds }),
|
|
153
163
|
});
|
|
154
164
|
}
|
|
155
165
|
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import type { PromptContribution } from "@ragents/engine";
|
|
2
|
-
import { shellPlatformText } from "@ragents/workspace-executor";
|
|
2
|
+
import { shellPlatformText, type ShellTools } from "@ragents/workspace-executor";
|
|
3
3
|
import { boundToTools } from "@ragents/host/plugin-support/prompt.js";
|
|
4
4
|
|
|
5
|
-
export const shellPlatformChapter = (platform: NodeJS.Platform): string =>
|
|
6
|
-
`## Shell platform\n\n${shellPlatformText(platform)}`;
|
|
5
|
+
export const shellPlatformChapter = (platform: NodeJS.Platform, tools: ShellTools): string =>
|
|
6
|
+
`## Shell platform\n\n${shellPlatformText(platform, tools)}`;
|
|
7
7
|
|
|
8
|
-
/** Ohne Angabe
|
|
8
|
+
/** Ohne Angabe gelten Plattform und Werkzeuge des Servers; `chapterFor` nennt die des Executors, der den Run ausführt. */
|
|
9
9
|
export const shellPlatformPrompt = (
|
|
10
10
|
id: string,
|
|
11
11
|
order: number,
|
|
12
|
+
serverTools: ShellTools,
|
|
12
13
|
chapterFor?: (runId: string) => string,
|
|
13
14
|
): PromptContribution => boundToTools({
|
|
14
15
|
id,
|
|
15
16
|
order,
|
|
16
17
|
delivery: "initial",
|
|
17
|
-
render: () => shellPlatformChapter(process.platform),
|
|
18
|
+
render: () => shellPlatformChapter(process.platform, serverTools),
|
|
18
19
|
...(chapterFor ? { renderForRun: chapterFor } : {}),
|
|
19
20
|
}, "bash");
|
|
@@ -42,7 +42,7 @@ const isClientInfo = (value: unknown): value is WorkspaceClientInfo => {
|
|
|
42
42
|
&& typeof raw.id === "string" && typeof raw.label === "string"
|
|
43
43
|
&& typeof raw.hostname === "string" && typeof raw.platform === "string"
|
|
44
44
|
&& Array.isArray(raw.folders) && raw.folders.every((folder) => typeof folder === "string")
|
|
45
|
-
&& typeof raw.runsDirectory === "string";
|
|
45
|
+
&& typeof raw.runsDirectory === "string" && typeof raw.ripgrep === "boolean";
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
const isFreshLabels = (value: unknown): value is FreshWorkspaceLabels => {
|
package/ragents.config.core.ts
CHANGED
|
@@ -6,7 +6,10 @@ export const config = {
|
|
|
6
6
|
PRODUCT_PROFILE: "core",
|
|
7
7
|
COMPACTION_MODEL: "google/gemma-4-26b-a4b-it",
|
|
8
8
|
// Aliasse für andere RAgents-Server, die ihre Modelle über ragents.model-relay von hier beziehen.
|
|
9
|
-
MODEL_ALIASES: [
|
|
9
|
+
MODEL_ALIASES: [
|
|
10
|
+
{ alias: "core-standard", model: "openrouter/z-ai/glm-5.3-flash", compaction: { threshold: 160_000, keepRecentTokens: 24_000, summaryTokens: 12_000 } },
|
|
11
|
+
{ alias: "core-coordinator", model: "openrouter/deepseek/deepseek-v4-flash-0731", compaction: { threshold: 300_000, keepRecentTokens: 40_000, summaryTokens: 16_000 } },
|
|
12
|
+
],
|
|
10
13
|
PRODUCT_ID: "ragents",
|
|
11
14
|
PRODUCT_TITLE: "RAgents",
|
|
12
15
|
PLUGINS: [
|