@schlenkr/ragents 0.1.6 → 0.1.8
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/chat-handler.ts +2 -0
- package/apps/server/src/config.ts +6 -0
- package/apps/server/src/host-api.json +1 -0
- package/apps/server/src/layout.ts +0 -15
- package/apps/server/src/plugin-support/agent-tool.ts +2 -3
- package/apps/server/src/plugin-support/plugin-folder.ts +2 -1
- package/apps/server/src/provider.ts +34 -11
- package/apps/server/src/ragents/actor-chat-history.ts +1 -0
- package/apps/server/src/ragents/engine.ts +23 -24
- package/apps/server/src/ragents/journal-chat-events.ts +2 -0
- package/apps/server/src/ragents/run-owner.ts +3 -2
- package/apps/server/src/ragents/session.ts +3 -3
- package/apps/server/src/run-preparation.ts +14 -18
- package/apps/server/src/run-transfer.ts +65 -4
- package/apps/server/src/settings.ts +2 -4
- package/apps/web/dist/assets/{highlighted-body-KPVGNVTW-B9qYBARS.js → highlighted-body-KPVGNVTW-xXXhTDbh.js} +1 -1
- package/apps/web/dist/assets/{main-BCjvD2Hh.js → main-C7JeZFjs.js} +1 -1
- package/apps/web/dist/assets/{mermaid-HWGCJPDP-CvYYFK_n.js → mermaid-HWGCJPDP-DfsU5ofC.js} +69 -69
- package/apps/web/dist/assets/{runPanel-CG7kRMJh.js → runPanel-ZT6TPDLM.js} +1 -1
- package/apps/web/dist/help/guide-clients.html +13 -4
- package/apps/web/dist/help/guide-clients.md +14 -4
- package/apps/web/dist/help/guide-distributed.html +8 -3
- package/apps/web/dist/help/guide-distributed.md +8 -3
- package/apps/web/dist/help/guide-runtime.html +18 -12
- package/apps/web/dist/help/guide-runtime.md +19 -12
- package/apps/web/dist/help/mini-app.css +23 -3
- 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/SessionList.tsx +16 -8
- package/apps/web/src/StartTiles.tsx +1 -1
- package/apps/web/src/api.ts +2 -0
- package/apps/web/src/panel/PanelPage.tsx +1 -1
- package/apps/web/src/panel/RunLine.tsx +4 -2
- package/apps/web/src/panel/contract.ts +2 -0
- package/bundles/ragents.activity/ragents-bundle.json +1 -1
- package/bundles/ragents.actor-programs/ragents-bundle.json +1 -1
- package/bundles/ragents.ask/ragents-bundle.json +1 -1
- package/bundles/ragents.browser/ragents-bundle.json +1 -1
- package/bundles/ragents.documents/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-fsharp/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-roslyn/ragents-bundle.json +1 -1
- package/bundles/ragents.lsp-typescript/ragents-bundle.json +1 -1
- package/bundles/ragents.model-relay/ragents-bundle.json +1 -1
- package/bundles/ragents.orchestration/ragents-bundle.json +1 -1
- package/bundles/ragents.overseer/ragents-bundle.json +1 -1
- package/bundles/ragents.processes/ragents-bundle.json +1 -1
- package/bundles/ragents.product/ragents-bundle.json +1 -1
- package/bundles/ragents.profile-distribution/ragents-bundle.json +1 -1
- package/bundles/ragents.reference/ragents-bundle.json +2 -2
- package/bundles/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
- package/bundles/ragents.reference/run-scripts/word-game/tests/program.test.ts +3 -3
- package/bundles/ragents.todo/ragents-bundle.json +1 -1
- package/bundles/ragents.transcript/ragents-bundle.json +1 -1
- package/bundles/ragents.watch/ragents-bundle.json +1 -1
- package/bundles/ragents.workspace/ragents-bundle.json +1 -1
- package/package.json +2 -2
- package/packages/agent/dist/core/agent-settings.d.ts +2 -2
- package/packages/agent/dist/core/compaction/compaction.d.ts +16 -50
- package/packages/agent/dist/core/context-log.d.ts +25 -0
- package/packages/agent/dist/core/messages.d.ts +3 -10
- package/packages/agent/dist/core/tool-definition.d.ts +17 -0
- package/packages/agent/dist/core/tools/bash.d.ts +2 -7
- package/packages/agent/dist/core/tools/edit.d.ts +1 -1
- package/packages/agent/dist/core/tools/read.d.ts +1 -1
- package/packages/agent/dist/core/tools/write.d.ts +1 -1
- package/packages/agent/dist/index.d.ts +7 -8
- package/packages/agent/dist/loop/agent.d.ts +2 -6
- package/packages/agent/package.json +1 -1
- package/packages/agent/src/core/agent-settings.ts +2 -2
- package/packages/agent/src/core/compaction/compaction.ts +49 -175
- package/packages/agent/src/core/context-log.ts +49 -0
- package/packages/agent/src/core/messages.ts +5 -11
- package/packages/agent/src/core/tool-definition.ts +29 -0
- package/packages/agent/src/core/tools/bash.ts +2 -9
- package/packages/agent/src/core/tools/edit.ts +2 -11
- package/packages/agent/src/core/tools/read.ts +3 -18
- package/packages/agent/src/core/tools/write.ts +1 -5
- package/packages/agent/src/index.ts +28 -14
- package/packages/agent/src/loop/agent-loop.ts +1 -2
- package/packages/agent/src/loop/agent.ts +8 -2
- package/packages/ai/dist/api/ai-sdk.d.ts +2 -0
- package/packages/ai/dist/types.d.ts +2 -0
- package/packages/ai/src/api/ai-sdk-messages.ts +15 -10
- package/packages/ai/src/api/ai-sdk.ts +11 -0
- package/packages/ai/src/api/transform-messages.ts +1 -1
- package/packages/ai/src/types.ts +2 -0
- package/packages/ai/src/utils/retry.ts +14 -0
- package/packages/ragents/src/agents/actor-input.ts +15 -0
- package/packages/ragents/src/agents/model-context.ts +360 -0
- package/packages/ragents/src/agents/plugins.ts +5 -0
- package/packages/ragents/src/agents/scheduler.ts +3 -7
- package/packages/ragents/src/agents/tools.ts +2 -2
- package/packages/ragents/src/agents/toolset.ts +47 -20
- package/packages/ragents/src/agents/workspaces.ts +0 -6
- package/packages/ragents/src/domain/event-semantics.ts +51 -0
- package/packages/ragents/src/domain/event-validation.ts +166 -0
- package/packages/ragents/src/domain/events.ts +72 -0
- package/packages/ragents/src/domain/model.ts +4 -2
- package/packages/ragents/src/domain/projection.ts +9 -0
- package/packages/ragents/src/domain/tool-result-text.ts +9 -0
- package/packages/ragents/src/drivers/agent-hooks.ts +25 -38
- package/packages/ragents/src/drivers/agent-runtime.ts +118 -933
- package/packages/ragents/src/drivers/agent-turn.ts +735 -0
- package/packages/ragents/src/drivers/agent.ts +9 -7
- package/packages/ragents/src/drivers/skill-preload.ts +53 -63
- package/packages/ragents/src/drivers/types.ts +21 -5
- package/packages/ragents/src/index.ts +8 -6
- package/packages/ragents/src/plugin-host.ts +60 -13
- package/packages/ragents/src/plugin-types.ts +7 -2
- package/packages/ragents/src/runtime/decisions/actors.ts +10 -0
- package/packages/ragents/src/runtime/decisions/turns.ts +66 -7
- package/packages/ragents/src/runtime/journal-storage.ts +7 -4
- package/packages/ragents/src/runtime/journal.ts +35 -0
- package/packages/ragents/src/runtime/orchestration.ts +140 -9
- package/packages/workspace-executor/src/sandbox-tools.ts +10 -11
- package/plugins/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
- package/plugins/ragents.reference/run-scripts/word-game/tests/program.test.ts +3 -3
- package/scripts/agent/journal.ts +5 -3
- package/scripts/package/publish-package.ts +4 -3
- package/packages/agent/dist/core/agent-session-runtime.d.ts +0 -33
- package/packages/agent/dist/core/agent-session-services.d.ts +0 -35
- package/packages/agent/dist/core/agent-session.d.ts +0 -234
- package/packages/agent/dist/core/auth-guidance.d.ts +0 -4
- package/packages/agent/dist/core/defaults.d.ts +0 -3
- package/packages/agent/dist/core/extensions/index.d.ts +0 -11
- package/packages/agent/dist/core/extensions/loader.d.ts +0 -12
- package/packages/agent/dist/core/extensions/runner.d.ts +0 -39
- package/packages/agent/dist/core/extensions/types.d.ts +0 -156
- package/packages/agent/dist/core/extensions/wrapper.d.ts +0 -12
- package/packages/agent/dist/core/resource-loader.d.ts +0 -39
- package/packages/agent/dist/core/sdk.d.ts +0 -38
- package/packages/agent/dist/core/session-manager.d.ts +0 -189
- package/packages/agent/dist/core/source-info.d.ts +0 -16
- package/packages/agent/dist/core/system-prompt.d.ts +0 -21
- package/packages/agent/src/core/agent-session-runtime.ts +0 -94
- package/packages/agent/src/core/agent-session-services.ts +0 -74
- package/packages/agent/src/core/agent-session.ts +0 -1108
- package/packages/agent/src/core/auth-guidance.ts +0 -14
- package/packages/agent/src/core/defaults.ts +0 -3
- package/packages/agent/src/core/extensions/index.ts +0 -38
- package/packages/agent/src/core/extensions/loader.ts +0 -125
- package/packages/agent/src/core/extensions/runner.ts +0 -211
- package/packages/agent/src/core/extensions/types.ts +0 -197
- package/packages/agent/src/core/extensions/wrapper.ts +0 -24
- package/packages/agent/src/core/resource-loader.ts +0 -142
- package/packages/agent/src/core/sdk.ts +0 -123
- package/packages/agent/src/core/session-manager.ts +0 -743
- package/packages/agent/src/core/source-info.ts +0 -28
- package/packages/agent/src/core/system-prompt.ts +0 -108
|
@@ -8,6 +8,8 @@ export interface SessionInfo {
|
|
|
8
8
|
createdAt?: number;
|
|
9
9
|
updatedAt: number;
|
|
10
10
|
revision?: number;
|
|
11
|
+
/** Warum der Run gesperrt ist; er bleibt sichtbar und löschbar, alles andere meldet journal-unavailable. */
|
|
12
|
+
locked?: string;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
export interface ChatUser {
|
|
@@ -11,6 +11,8 @@ export const hostConfigDescriptors = [
|
|
|
11
11
|
{ key: "PORT", source: "environment" },
|
|
12
12
|
{ key: "DATA_DIR", source: "environment" },
|
|
13
13
|
{ key: "AGENT_THINKING", source: "environment" },
|
|
14
|
+
{ key: "AGENT_COMPACTION_RESERVE_TOKENS", source: "environment" },
|
|
15
|
+
{ key: "AGENT_COMPACTION_KEEP_RECENT_TOKENS", source: "environment" },
|
|
14
16
|
{ key: "COMPACTION_PROVIDER", source: "environment" },
|
|
15
17
|
{ key: "COMPACTION_MODEL", source: "environment" },
|
|
16
18
|
...modelAliasEnvDescriptors,
|
|
@@ -35,6 +37,10 @@ export const config = Object.freeze({
|
|
|
35
37
|
dataDir: path.resolve(env.value("DATA_DIR", defaultDataDirectory(env.required("PRODUCT_PROFILE")))),
|
|
36
38
|
webDistDir: hostWebDirectory(rootDir),
|
|
37
39
|
agentThinking: env.value("AGENT_THINKING", "high"),
|
|
40
|
+
agentCompaction: {
|
|
41
|
+
...(env.optional("AGENT_COMPACTION_RESERVE_TOKENS") ? { reserveTokens: env.positiveNumber("AGENT_COMPACTION_RESERVE_TOKENS", "") } : {}),
|
|
42
|
+
...(env.optional("AGENT_COMPACTION_KEEP_RECENT_TOKENS") ? { keepRecentTokens: env.positiveNumber("AGENT_COMPACTION_KEEP_RECENT_TOKENS", "") } : {}),
|
|
43
|
+
},
|
|
38
44
|
compactionProvider: env.value("COMPACTION_PROVIDER", "openrouter"),
|
|
39
45
|
compactionModel: env.value("COMPACTION_MODEL", ""),
|
|
40
46
|
productProfile: env.required("PRODUCT_PROFILE"),
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { isPortableId } from "@ragents/engine";
|
|
3
2
|
import { config } from "./config.js";
|
|
4
3
|
|
|
5
|
-
const portableId = (value: string, name: string) => {
|
|
6
|
-
if (!isPortableId(value)) throw new Error(`${name} must be a lowercase portable ID.`);
|
|
7
|
-
|
|
8
|
-
return value;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
4
|
export const layout = {
|
|
12
5
|
runsDir: path.join(config.dataDir, "runs"),
|
|
13
6
|
artifactsDir: path.join(config.dataDir, "artifacts"),
|
|
@@ -18,14 +11,6 @@ export const layout = {
|
|
|
18
11
|
serverLog: path.join(config.dataDir, "logs/server.log"),
|
|
19
12
|
|
|
20
13
|
sessionDir: (id: string) => path.join(config.dataDir, "sessions", id),
|
|
21
|
-
chatDir: (id: string) => path.join(config.dataDir, "sessions", id, "chat"),
|
|
22
|
-
agentChatDir: (id: string, agentId: string) => path.join(
|
|
23
|
-
config.dataDir,
|
|
24
|
-
"sessions",
|
|
25
|
-
portableId(id, "Run ID"),
|
|
26
|
-
"chat",
|
|
27
|
-
portableId(agentId, "Actor ID"),
|
|
28
|
-
),
|
|
29
14
|
deleteIntentFile: (id: string) => path.join(config.dataDir, "delete-intents", `${id}.json`),
|
|
30
15
|
archiveSessionDir: (id: string) => path.join(config.dataDir, "archive", id),
|
|
31
16
|
recoverySessionDir: (id: string) => path.join(config.dataDir, "recovery", id),
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
type ToolExecutionMode,
|
|
10
10
|
} from "@ragents/engine";
|
|
11
11
|
|
|
12
|
-
export type AgentToolDefinition = ToolDefinition<any, any
|
|
12
|
+
export type AgentToolDefinition = ToolDefinition<any, any> & { longDescription?: string; nativeTool?: boolean };
|
|
13
13
|
|
|
14
14
|
export interface AgentToolMetadata {
|
|
15
15
|
readonly name: string;
|
|
@@ -24,7 +24,6 @@ type BoundExecute = (
|
|
|
24
24
|
params: unknown,
|
|
25
25
|
signal: AbortSignal | undefined,
|
|
26
26
|
onUpdate: unknown,
|
|
27
|
-
ctx: unknown,
|
|
28
27
|
) => Promise<ToolOutput>;
|
|
29
28
|
|
|
30
29
|
export const textOf = (result: ToolOutput): string =>
|
|
@@ -49,7 +48,7 @@ export const agentToolFrom = (
|
|
|
49
48
|
available,
|
|
50
49
|
...(executionMode ? { executionMode } : {}),
|
|
51
50
|
run: async (scope, toolCallId, input) =>
|
|
52
|
-
textOf(await (definition.execute as BoundExecute)(toolCallId, input, scope.signal, undefined
|
|
51
|
+
textOf(await (definition.execute as BoundExecute)(toolCallId, input, scope.signal, undefined)),
|
|
53
52
|
});
|
|
54
53
|
|
|
55
54
|
export const toolDescriptorFrom = (
|
|
@@ -74,8 +74,9 @@ export const withFolderAssets = (plugin: RAgentsPlugin): RAgentsPlugin => ({
|
|
|
74
74
|
if (skillPaths.length === 0) return;
|
|
75
75
|
host.skills({
|
|
76
76
|
id: `${plugin.manifest.id}.folder-skills`,
|
|
77
|
+
// Ein ausdrücklich angemeldeter Pfad gehört in jedem Run dem ausdrücklichen Beitrag, auch wo der ihn für diesen Run weglässt.
|
|
77
78
|
paths: async (context) => {
|
|
78
|
-
const explicit = await Promise.all(explicitSkills.
|
|
79
|
+
const explicit = await Promise.all(explicitSkills.flatMap((skill) => [skill.paths(undefined), skill.paths(context)]));
|
|
79
80
|
const taken = new Set(explicit.flat());
|
|
80
81
|
return skillPaths.filter((skillPath) => !taken.has(skillPath));
|
|
81
82
|
},
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
3
|
+
import { existsSync, statSync } from "node:fs";
|
|
4
4
|
import { chmod, mkdir, open, readFile, readdir, realpath, rename, rm } from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import type { ChatSessionLike, ChatSessionProvider, RunListScope, SessionInfo } from "./chat-handler.js";
|
|
7
|
-
import { DomainError, isRunId, unrestrictedAccess, type AccessContext, type HttpRouteContribution, type MethodContribution, type PluginHost, type ServiceToken, type SessionStartedContext } from "@ragents/engine";
|
|
7
|
+
import { DomainError, isRunId, unrestrictedAccess, type AccessContext, type HttpRouteContribution, type Journal, type JournalLoadFailure, type MethodContribution, type PluginHost, type ServiceToken, type SessionStartedContext } from "@ragents/engine";
|
|
8
8
|
import { WORKSPACE_EXECUTOR_VERSION } from "@ragents/workspace-executor";
|
|
9
9
|
import { assertRunRights, assertRunWorkspaceAccess, runIdInPath, runListScope, runReachable, type GlobalRunPolicy, type RunAccessPolicy } from "./api/rights.js";
|
|
10
10
|
import { configuredAnonymousUser, configuredUsers } from "./config-file.js";
|
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
assertRunIdFree,
|
|
18
18
|
assertRunStopped,
|
|
19
19
|
assertWorkspaceReplacement,
|
|
20
|
+
contentHashesOf,
|
|
21
|
+
installContents,
|
|
20
22
|
installSessionDirectory,
|
|
21
23
|
packRunArchive,
|
|
22
24
|
runTransferStagingDirectory,
|
|
@@ -314,7 +316,10 @@ export class RunSessionProvider implements ChatSessionProvider {
|
|
|
314
316
|
...(Object.keys(metadata.unavailable).length > 0 ? { metadataUnavailable: metadata.unavailable } : {}),
|
|
315
317
|
};
|
|
316
318
|
}));
|
|
317
|
-
const
|
|
319
|
+
const locked = engine.journal.unavailableRuns()
|
|
320
|
+
.filter(({ runId }) => !isCoordinator(runId) && !this.deleteRequested.has(runId) && !this.deleted.has(runId) && visible(runId))
|
|
321
|
+
.map((failure) => lockedSession(failure, engine.journal));
|
|
322
|
+
const infos = [...described.filter((info): info is ListedSession => info !== undefined), ...locked];
|
|
318
323
|
for (const [id, session] of this.sessions) {
|
|
319
324
|
if (isCoordinator(id) || !visible(id)) continue;
|
|
320
325
|
if (this.deleteRequested.has(id) || this.deleted.has(id)) continue;
|
|
@@ -466,10 +471,6 @@ export class RunSessionProvider implements ChatSessionProvider {
|
|
|
466
471
|
this.ensureUsable(id);
|
|
467
472
|
if (!this.plugins.optionalService(globalChatToken)?.isCoordinator(id)) await this.plugins.lifecycle.prepareSession(id);
|
|
468
473
|
this.ensureUsable(id);
|
|
469
|
-
await mkdir(layout.chatDir(id), { recursive: true, mode: ROOT_ONLY_MODE });
|
|
470
|
-
this.ensureUsable(id);
|
|
471
|
-
await chmod(layout.chatDir(id), ROOT_ONLY_MODE);
|
|
472
|
-
this.ensureUsable(id);
|
|
473
474
|
}
|
|
474
475
|
|
|
475
476
|
private async startedRun(id: string, startEntry: SessionStartedContext["startEntry"]): Promise<void> {
|
|
@@ -496,7 +497,7 @@ export class RunSessionProvider implements ChatSessionProvider {
|
|
|
496
497
|
return this.deleting.get(id)?.done;
|
|
497
498
|
}
|
|
498
499
|
|
|
499
|
-
/** Ein gestoppter Run als Archiv: Journal, Payloads,
|
|
500
|
+
/** Ein gestoppter Run als Archiv: Journal, Payloads, die Inhalte, auf die er verweist, und seine Plugin-Ablagen. */
|
|
500
501
|
exportRun(id: string): Promise<RunTransferExport> {
|
|
501
502
|
this.ensureUsable(id);
|
|
502
503
|
if (this.plugins.optionalService(globalChatToken)?.isCoordinator(id)) {
|
|
@@ -524,7 +525,12 @@ export class RunSessionProvider implements ChatSessionProvider {
|
|
|
524
525
|
boundDirectory: this.workspaceTransfer().boundDirectory(id),
|
|
525
526
|
exportedAt: new Date().toISOString(),
|
|
526
527
|
};
|
|
527
|
-
const archive = await packRunArchive({
|
|
528
|
+
const archive = await packRunArchive({
|
|
529
|
+
places: this.transferPlaces(),
|
|
530
|
+
runId: id,
|
|
531
|
+
manifest,
|
|
532
|
+
contentHashes: contentHashesOf(engine.journal.load(id)),
|
|
533
|
+
});
|
|
528
534
|
return { manifest, archive: archive.toString("base64") };
|
|
529
535
|
});
|
|
530
536
|
}
|
|
@@ -543,6 +549,7 @@ export class RunSessionProvider implements ChatSessionProvider {
|
|
|
543
549
|
assertWorkspaceReplacement(manifest, workspacePath);
|
|
544
550
|
if (workspacePath !== undefined) transfer.assertDirectory(workspacePath);
|
|
545
551
|
const sessionDirectory = await installSessionDirectory({ places, runId: manifest.runId, staging, mode: SESSION_MODE });
|
|
552
|
+
await installContents({ places, staging });
|
|
546
553
|
const engine = this.requireEngine();
|
|
547
554
|
try {
|
|
548
555
|
engine.journal.adopt(unpacked.records);
|
|
@@ -639,7 +646,7 @@ export class RunSessionProvider implements ChatSessionProvider {
|
|
|
639
646
|
await settled;
|
|
640
647
|
await this.plugins.optionalService(sandboxServicesToken)?.shutdown(id);
|
|
641
648
|
await this.removeGlobalConversation(id, policy);
|
|
642
|
-
engine.
|
|
649
|
+
engine.runtime.forgetRun(id);
|
|
643
650
|
this.sessionWorkspaces.delete(id);
|
|
644
651
|
this.workspaces.forget(id);
|
|
645
652
|
});
|
|
@@ -756,7 +763,7 @@ export class RunSessionProvider implements ChatSessionProvider {
|
|
|
756
763
|
if (failures.length > 0) throw new AggregateError(failures, `Run ${id} konnte nicht gestoppt werden`);
|
|
757
764
|
await this.plugins.lifecycle.deleteSession(id);
|
|
758
765
|
await this.archiveSession(id);
|
|
759
|
-
this.engine?.
|
|
766
|
+
this.engine?.runtime.forgetRun(id);
|
|
760
767
|
this.sessions.delete(id);
|
|
761
768
|
this.sessionWorkspaces.delete(id);
|
|
762
769
|
this.workspaces.forget(id);
|
|
@@ -870,6 +877,7 @@ export class RunSessionProvider implements ChatSessionProvider {
|
|
|
870
877
|
private async archiveSession(id: string): Promise<void> {
|
|
871
878
|
const target = layout.archiveSessionDir(id);
|
|
872
879
|
await mkdir(target, { recursive: true, mode: ROOT_ONLY_MODE });
|
|
880
|
+
// Journale vor Format 7 hielten den Modellkontext unter sessions/<id>/chat; er bleibt im Archiv erhalten.
|
|
873
881
|
const moves = [
|
|
874
882
|
[path.join(layout.sessionDir(id), "chat"), "chat"],
|
|
875
883
|
[path.join(layout.runsDir, id), "run"],
|
|
@@ -893,6 +901,21 @@ export class RunSessionProvider implements ChatSessionProvider {
|
|
|
893
901
|
}
|
|
894
902
|
}
|
|
895
903
|
|
|
904
|
+
/** Ein gesperrter Run ohne Plugin-Metadaten und Arbeitsbereich; ohne geladenen Zustand zählt die Journaldatei. */
|
|
905
|
+
const lockedSession = (failure: JournalLoadFailure, journal: Journal): ListedSession => {
|
|
906
|
+
const state = journal.stateOf(failure.runId);
|
|
907
|
+
const updatedAt = state ? Date.parse(journal.updatedAt(failure.runId) ?? state.createdAt) : statSync(failure.path, { throwIfNoEntry: false })?.mtimeMs ?? 0;
|
|
908
|
+
return {
|
|
909
|
+
id: failure.runId,
|
|
910
|
+
title: state?.title ?? failure.runId,
|
|
911
|
+
...(state ? { createdAt: Date.parse(state.createdAt), revision: state.revision } : {}),
|
|
912
|
+
updatedAt,
|
|
913
|
+
running: false,
|
|
914
|
+
workspaceAccessible: false,
|
|
915
|
+
locked: failure.message,
|
|
916
|
+
};
|
|
917
|
+
};
|
|
918
|
+
|
|
896
919
|
const sessionTitle = (text: string | undefined): string | undefined => {
|
|
897
920
|
const lines = text?.split(/\r?\n/).map((line) => line.trim()).filter(Boolean) ?? [];
|
|
898
921
|
if (lines.length === 0) return undefined;
|
|
@@ -81,6 +81,7 @@ export function actorChatHistoryOf(view: RunView, events: readonly JournalEvent[
|
|
|
81
81
|
case "tool.call.completed":
|
|
82
82
|
case "tool.call.failed":
|
|
83
83
|
case "runtime.output.recorded":
|
|
84
|
+
case "context.compacted":
|
|
84
85
|
case "turn.finished":
|
|
85
86
|
case "turn.interrupted": {
|
|
86
87
|
const actorId = turnActorOf(event.payload.turnId);
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
Journal,
|
|
8
8
|
LiveBus,
|
|
9
9
|
Orchestration,
|
|
10
|
-
|
|
10
|
+
AgentLoopDriver,
|
|
11
11
|
attachmentInputKind,
|
|
12
12
|
RunStopper,
|
|
13
13
|
type PluginHost,
|
|
@@ -39,7 +39,7 @@ import { config } from "../config.js";
|
|
|
39
39
|
import { accessibleRunView } from "../access-projection.js";
|
|
40
40
|
import { assertRunRights } from "../api/rights.js";
|
|
41
41
|
import { runOwnerOf, runOwnerOnly } from "./run-owner.js";
|
|
42
|
-
import { layout
|
|
42
|
+
import { layout } from "../layout.js";
|
|
43
43
|
import { renderSystemPromptOption, systemPromptSelectionPromptIds } from "../plugin-support/prompt.js";
|
|
44
44
|
import { actorProgramsToken } from "../plugin-support/actor-programs/service.js";
|
|
45
45
|
import { checkedThinkingLevel } from "../plugin-support/thinking-level.js";
|
|
@@ -88,11 +88,6 @@ export class SessionWorkspaces implements Workspaces {
|
|
|
88
88
|
return this.#roots.get(runId)?.description;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/** Der Ordner auf diesem Server für Arbeit des Runs, die hier läuft; bei einem Arbeitsplatz nie dessen Pfad. */
|
|
92
|
-
async runtimeDirectory(runId: string): Promise<string> {
|
|
93
|
-
return (await this.#sandbox().serverProcessContextFor(runId)).cwd;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
91
|
async storeAttachment(runId: string, name: string, content: Uint8Array): Promise<string> {
|
|
97
92
|
const stored = await this.#sandbox().execute(runId, FILE_OPERATIONS.attach, { name, content: Buffer.from(content).toString("base64") });
|
|
98
93
|
const storedName = (stored as { name?: unknown } | null)?.name;
|
|
@@ -206,15 +201,12 @@ export const createEngine = async (options: EngineOptions): Promise<Engine> => {
|
|
|
206
201
|
workspace: context.workspace,
|
|
207
202
|
audience: isRunCoordinator(runtime, context.runId, context.agentId) ? "coordinator" : "agent",
|
|
208
203
|
});
|
|
209
|
-
const agentRuntime = new
|
|
204
|
+
const agentRuntime = new AgentLoopDriver({
|
|
210
205
|
modelRuntime: options.modelRuntime,
|
|
211
206
|
resolveSkills: async (context) => globalChat?.isCoordinator(context.runId) ? []
|
|
212
207
|
: (await options.plugins.skills.resolve(contributionContextFor(context))).map(skillOfDirectory),
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
directory: (runId, agentId) => layout.agentChatDir(runId, agentId),
|
|
216
|
-
directoryMode: ROOT_ONLY_MODE,
|
|
217
|
-
},
|
|
208
|
+
resolveHooks: (context) => globalChat?.isCoordinator(context.runId) ? [] : options.plugins.agentRuntime.resolve(contributionContextFor(context)),
|
|
209
|
+
settings: { compaction: config.agentCompaction },
|
|
218
210
|
});
|
|
219
211
|
const configuredModels = await Promise.all(options.plugins.profiles.models().map(async (model) => {
|
|
220
212
|
if (model.driver !== "agent") return model;
|
|
@@ -279,32 +271,39 @@ export const createEngine = async (options: EngineOptions): Promise<Engine> => {
|
|
|
279
271
|
});
|
|
280
272
|
const boundToTools = (entry: PublicPromptContribution, toolNames: readonly string[]): boolean =>
|
|
281
273
|
entry.requiresTools.some((name) => toolNames.includes(name));
|
|
282
|
-
/** Ein Beitrag darf je Run anders lauten, etwa die Shell-Plattform des Executors,
|
|
283
|
-
const contentFor = (runId: string | null): ((entry: PublicPromptContribution) => string) => {
|
|
274
|
+
/** Ein Beitrag darf je Run anders lauten oder fehlen, etwa die Shell-Plattform des Executors oder ein Plugin, dessen Laufbedingung nicht zutrifft; das gilt auch für die gewählten Systemprompts. */
|
|
275
|
+
const contentFor = (runId: string | null, texts: () => readonly string[]): ((entry: PublicPromptContribution) => string) => {
|
|
284
276
|
const overrides = runId === null ? undefined : options.plugins.prompts.runOverrides(runId);
|
|
285
|
-
return (entry) => overrides?.get(entry.id)
|
|
277
|
+
return (entry) => overrides?.get(entry.id)
|
|
278
|
+
?? (systemPromptSelectionPromptIds.has(entry.id) ? texts().join("\n\n") : entry.content);
|
|
286
279
|
};
|
|
287
280
|
const composeWith = (runId: string | null, texts: readonly string[], toolNames: readonly string[] | null): string => {
|
|
288
|
-
const content = contentFor(runId);
|
|
281
|
+
const content = contentFor(runId, () => texts);
|
|
289
282
|
return baseSnapshot
|
|
290
283
|
.contributions
|
|
291
284
|
.filter((entry) => entry.delivery === "initial")
|
|
292
285
|
.filter((entry) => toolNames === null || entry.requiresTools.length === 0 || boundToTools(entry, toolNames))
|
|
293
|
-
.map(
|
|
286
|
+
.map(content)
|
|
294
287
|
.filter(Boolean)
|
|
295
288
|
.join("\n\n");
|
|
296
289
|
};
|
|
297
|
-
const chaptersFor = async (toolNames: readonly string[]): Promise<string> => {
|
|
290
|
+
const chaptersFor = async (runId: string, toolNames: readonly string[]): Promise<string> => {
|
|
298
291
|
const chapters = await options.plugins.prompts.describe({
|
|
299
292
|
}, { delivery: "on-demand", toolNames });
|
|
300
|
-
|
|
293
|
+
const content = contentFor(runId, () => []);
|
|
294
|
+
return chapters.map(content).filter(Boolean).join("\n\n");
|
|
301
295
|
};
|
|
302
296
|
const systemPromptFor = (runId: string): string => globalChat?.isCoordinator(runId)
|
|
303
297
|
? globalChat.prompt : composeWith(runId, textsFor(runId), null);
|
|
304
298
|
const coordinatorPromptFor = (runId: string, toolNames: readonly string[]): string =>
|
|
305
299
|
composeWith(runId, textsFor(runId), toolNames);
|
|
306
|
-
const
|
|
307
|
-
|
|
300
|
+
const selectionPrompts = baseSnapshot.contributions.filter((entry) => systemPromptSelectionPromptIds.has(entry.id));
|
|
301
|
+
const agentPromptFor = (runId: string): string => {
|
|
302
|
+
if (!storedSystemPrompt(journal.stateOf(runId)).shareWithAgents) return "";
|
|
303
|
+
if (selectionPrompts.length === 0) return textsFor(runId).join("\n\n");
|
|
304
|
+
const content = contentFor(runId, () => textsFor(runId));
|
|
305
|
+
return selectionPrompts.map(content).filter(Boolean).join("\n\n");
|
|
306
|
+
};
|
|
308
307
|
const systemPrompt = composeWith(
|
|
309
308
|
null,
|
|
310
309
|
selectedSystemPrompts(promptCatalog, promptCatalog.defaultIds).map((option) => optionTexts.get(option.id) ?? ""),
|
|
@@ -317,7 +316,7 @@ export const createEngine = async (options: EngineOptions): Promise<Engine> => {
|
|
|
317
316
|
: [agentPromptFor(runId), actor.kind === "agent" ? actor.prompt : "",
|
|
318
317
|
...baseSnapshot.contributions
|
|
319
318
|
.filter((entry) => entry.delivery === "initial" && boundToTools(entry, toolNames))
|
|
320
|
-
.map(contentFor(runId)),
|
|
319
|
+
.map(contentFor(runId, () => textsFor(runId))),
|
|
321
320
|
].filter(Boolean).join("\n\n");
|
|
322
321
|
return [prompt, productRuntime.contract(role), outputContractFor(role)]
|
|
323
322
|
.filter(Boolean)
|
|
@@ -339,7 +338,7 @@ export const createEngine = async (options: EngineOptions): Promise<Engine> => {
|
|
|
339
338
|
...(workspaceToolNaming ? { workspaceToolNaming } : {}),
|
|
340
339
|
basePrompt: basePromptFor,
|
|
341
340
|
contract: (actor) => coreContractFor(actor),
|
|
342
|
-
toolChapters: (
|
|
341
|
+
toolChapters: (runId, _actor, toolNames) => chaptersFor(runId, toolNames),
|
|
343
342
|
onError: (error) => console.error(`Turn fehlgeschlagen: ${error instanceof Error ? error.stack ?? error.message : String(error)}`),
|
|
344
343
|
});
|
|
345
344
|
const stopExternal: RunStopBoundary = (runId, stopJournal) => scheduler.haltRun(runId, async (
|
|
@@ -62,6 +62,8 @@ export const journalChatEventsOf = (
|
|
|
62
62
|
];
|
|
63
63
|
case "actor.stopped":
|
|
64
64
|
return [{ kind: "system", text: event.payload.reason, at }];
|
|
65
|
+
case "context.compacted":
|
|
66
|
+
return [{ kind: "system", text: `Model context compacted: the model now sees the history before this point (about ${event.payload.tokensBefore} tokens) only as a summary.`, at }];
|
|
65
67
|
default:
|
|
66
68
|
return [];
|
|
67
69
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { Journal, StartOptionContributionRegistry } from "@ragents/engine";
|
|
2
2
|
import { storedStartOption } from "./start-option-state.js";
|
|
3
3
|
|
|
4
|
-
/** Der Benutzer eines Runs aus seinem Journal: null ohne Eigentümer, undefined für eine Kennung ohne Run. */
|
|
4
|
+
/** Der Benutzer eines Runs aus seinem Journal: null ohne Eigentümer und für ein gesperrtes Journal ohne Zustand, undefined für eine Kennung ohne Run. */
|
|
5
5
|
export const runOwnerOf = (journal: Journal, runId: string): string | null | undefined => {
|
|
6
6
|
const state = journal.stateOf(runId);
|
|
7
|
-
|
|
7
|
+
if (state) return state.ownerUserId;
|
|
8
|
+
return journal.failureOf(runId) ? null : undefined;
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
/** Nur der Eigentümer bedient einen Run, dessen gespeicherte Startoption das für ihren Wert erklärt. */
|
|
@@ -856,9 +856,9 @@ export class RunChatSession implements ChatSessionLike {
|
|
|
856
856
|
const cursor = this.#textPositions.observe(event);
|
|
857
857
|
const primaryActorId = this.#primaryActorId;
|
|
858
858
|
if (!primaryActorId) continue;
|
|
859
|
-
if (event.
|
|
860
|
-
&& (event.
|
|
861
|
-
&& this.#
|
|
859
|
+
if ((event.type === "turn.finished" || event.type === "turn.interrupted")
|
|
860
|
+
&& this.#liveTurns.has(event.payload.turnId)
|
|
861
|
+
&& this.#engine.runtime.state(this.id).turns.get(event.payload.turnId)?.actorId === primaryActorId) this.#journalFinishedTurns.add(event.payload.turnId);
|
|
862
862
|
if (live) continue;
|
|
863
863
|
for (const chatEvent of chatEventsOf(event, this.#scope(primaryActorId), cursor, this.#pluginStates)) this.#emit(chatEvent);
|
|
864
864
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Agent, convertToLlm, type AgentTool, type ModelRuntime } from "@ragents/agent";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
|
-
import type
|
|
3
|
+
import { clampThinkingLevel, type Message } from "@ragents/ai";
|
|
4
4
|
import { DomainError, type ModelSelection } from "@ragents/engine";
|
|
5
5
|
import { prepareInputAttachments } from "../../../packages/ragents/src/drivers/attachments.ts";
|
|
6
6
|
import { MAX_CHAT_REQUEST_BYTES, parseChatAttachments } from "quassel/events";
|
|
@@ -86,13 +86,8 @@ export const prepareRunMessage = async (options: {
|
|
|
86
86
|
}
|
|
87
87
|
signal.throwIfAborted();
|
|
88
88
|
const systemPrompt = [options.prompt, options.request.skillName ? `Ausgewählter Skill: ${options.request.skillName}` : ""].filter(Boolean).join("\n\n");
|
|
89
|
-
const resourceLoader = new DefaultResourceLoader({ systemPrompt });
|
|
90
|
-
await resourceLoader.reload({ signal });
|
|
91
|
-
signal.throwIfAborted();
|
|
92
|
-
const sessionManager = SessionManager.inMemory();
|
|
93
|
-
for (const message of messages.slice(0, -1)) sessionManager.appendMessage(message);
|
|
94
89
|
let startRequested = false;
|
|
95
|
-
const startRun =
|
|
90
|
+
const startRun: AgentTool = {
|
|
96
91
|
name: "start_run", label: "Run starten",
|
|
97
92
|
description: "Den besprochenen Run starten, ausschließlich nach dem ausdrücklichen sinngemäßen Go des Benutzers zur Ausführung. Keine feste Formulierung nötig. Eine Detailbestätigung, ein Zitat oder eine Fähigkeitsfrage genügt nicht. Der Host übernimmt Auftrag, Skill und Anhänge aus dem Gespräch sowie die Startoptionen.",
|
|
98
93
|
parameters: Type.Object({}),
|
|
@@ -101,23 +96,25 @@ export const prepareRunMessage = async (options: {
|
|
|
101
96
|
startRequested = true;
|
|
102
97
|
return { content: [{ type: "text", text: "Start zur Übergabe vorgemerkt. Nach Abschluss dieser Antwort übernimmt die Oberfläche den Auftrag." }], details: {} };
|
|
103
98
|
},
|
|
99
|
+
};
|
|
100
|
+
if (!(await runtime.checkAuth(model.provider))) throw new Error(`No key is configured for the provider ${model.provider}.`);
|
|
101
|
+
const agent = new Agent({
|
|
102
|
+
initialState: { systemPrompt, model, thinkingLevel: clampThinkingLevel(model, selection.thinking ?? "off"), tools: [startRun], messages: messages.slice(0, -1) },
|
|
103
|
+
convertToLlm,
|
|
104
|
+
streamFn: (streamModel, context, streamOptions) => runtime.streamSimple(streamModel, context, { ...streamOptions, maxRetries: 0, timeoutMs: PREPARATION_TIMEOUT_MS }),
|
|
104
105
|
});
|
|
105
|
-
const
|
|
106
|
-
model, thinkingLevel: selection.thinking ?? "off", resourceLoader, sessionManager,
|
|
107
|
-
settings: { compaction: { enabled: false }, retry: { enabled: false }, providerRequest: { maxRetries: 0, timeoutMs: PREPARATION_TIMEOUT_MS } },
|
|
108
|
-
tools: [startRun.name], customTools: [startRun],
|
|
109
|
-
});
|
|
110
|
-
const abort = () => { void session.abort(); };
|
|
106
|
+
const abort = () => { agent.abort(); };
|
|
111
107
|
signal.addEventListener("abort", abort, { once: true });
|
|
112
108
|
try {
|
|
113
109
|
signal.throwIfAborted();
|
|
114
110
|
const latest = messages.at(-1)!;
|
|
115
111
|
if (latest.role !== "user") throw new Error("Die letzte Nachricht muss vom Benutzer kommen.");
|
|
116
112
|
const content = latest.content;
|
|
117
|
-
await
|
|
118
|
-
|
|
113
|
+
await agent.prompt({ role: "user", timestamp: Date.now(), content: typeof content === "string"
|
|
114
|
+
? [{ type: "text", text: content }]
|
|
115
|
+
: [{ type: "text", text: content.filter((part) => part.type === "text").map((part) => part.text).join("\n") }, ...content.filter((part) => part.type !== "text")] });
|
|
119
116
|
signal.throwIfAborted();
|
|
120
|
-
const completed =
|
|
117
|
+
const completed = agent.state.messages.at(-1);
|
|
121
118
|
if (!completed || completed.role !== "assistant" || completed.stopReason !== "stop") {
|
|
122
119
|
throw new DomainError("preparation-model-failed", completed?.role === "assistant"
|
|
123
120
|
? completed.errorMessage ?? `Die Vorbereitung wurde nicht vollständig beantwortet (${completed.stopReason}).`
|
|
@@ -129,7 +126,6 @@ export const prepareRunMessage = async (options: {
|
|
|
129
126
|
return { kind: "reply", text };
|
|
130
127
|
} finally {
|
|
131
128
|
signal.removeEventListener("abort", abort);
|
|
132
|
-
session.dispose();
|
|
133
129
|
}
|
|
134
130
|
} catch (error) {
|
|
135
131
|
if (options.signal.aborted) throw new DomainError("preparation-aborted", "Die Vorbereitung wurde abgebrochen.", 499);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, readlinkSync, Stats } from "node:fs";
|
|
2
3
|
import { chmod, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import { create as createTar, extract as extractTar } from "tar";
|
|
5
|
-
import { DomainError, isPendingActorInput, isRunId, type CommandRecord, type RunState } from "@ragents/engine";
|
|
5
|
+
import { create as createTar, extract as extractTar, type ReadEntry } from "tar";
|
|
6
|
+
import { DomainError, isPendingActorInput, isRunId, type CommandRecord, type JournalEvent, type RunState } from "@ragents/engine";
|
|
6
7
|
import { parseJournalRecord } from "@ragents/engine/src/runtime/journal-storage";
|
|
7
8
|
|
|
8
9
|
export const RUN_TRANSFER_FORMAT_VERSION = 1;
|
|
@@ -100,16 +101,36 @@ export const assertRunIdFree = (options: { runId: string; known: boolean; direct
|
|
|
100
101
|
}
|
|
101
102
|
};
|
|
102
103
|
|
|
104
|
+
/** Die Inhalte unter artifacts/, auf die der Run verweist: veröffentlichte Artefakte und Medien seiner Modellkontexte. */
|
|
105
|
+
export const contentHashesOf = (events: readonly JournalEvent[]): string[] => [...new Set(events.flatMap((event) => {
|
|
106
|
+
if (event.type === "artifact.published") return [event.payload.artifact.hash];
|
|
107
|
+
const content = event.type === "model.input.presented" || event.type === "model.tool-result.presented" ? event.payload.content : undefined;
|
|
108
|
+
return Array.isArray(content) ? content.flatMap((part) => "hash" in part ? [part.hash] : []) : [];
|
|
109
|
+
}))].sort();
|
|
110
|
+
|
|
111
|
+
const sha256 = /^[0-9a-f]{64}$/;
|
|
112
|
+
|
|
103
113
|
// Halbfertige Journal- und Payload-Schreibvorgänge gehören nie ins Archiv.
|
|
104
114
|
const temporary = (entry: string): boolean => {
|
|
105
115
|
const name = path.basename(entry);
|
|
106
116
|
return name.startsWith(".journal.") || name.startsWith(".payload.");
|
|
107
117
|
};
|
|
108
118
|
|
|
119
|
+
/** A link travels only inside the run's tree; one out of it below node_modules is left for the next install, elsewhere refused. */
|
|
120
|
+
const linkDecision = (dataDirectory: string, entry: string): "keep" | "skip" | "refuse" => {
|
|
121
|
+
const file = path.join(dataDirectory, entry);
|
|
122
|
+
const target = readlinkSync(file);
|
|
123
|
+
const root = path.join(dataDirectory, ...entry.split("/").slice(0, 2));
|
|
124
|
+
const resolved = path.resolve(path.dirname(file), target);
|
|
125
|
+
if (!path.isAbsolute(target) && (resolved === root || resolved.startsWith(`${root}${path.sep}`))) return "keep";
|
|
126
|
+
return entry.split("/").includes("node_modules") ? "skip" : "refuse";
|
|
127
|
+
};
|
|
128
|
+
|
|
109
129
|
export const packRunArchive = async (options: {
|
|
110
130
|
places: RunTransferPlaces;
|
|
111
131
|
runId: string;
|
|
112
132
|
manifest: RunTransferManifest;
|
|
133
|
+
contentHashes: readonly string[];
|
|
113
134
|
}): Promise<Buffer> => {
|
|
114
135
|
const runId = assertRunId(options.runId);
|
|
115
136
|
const { dataDirectory } = options.places;
|
|
@@ -123,15 +144,36 @@ export const packRunArchive = async (options: {
|
|
|
123
144
|
RUN_TRANSFER_MANIFEST_ENTRY,
|
|
124
145
|
`runs/${runId}`,
|
|
125
146
|
...(existsSync(runTransferSessionDirectory(dataDirectory, runId)) ? [`sessions/${runId}`] : []),
|
|
147
|
+
...options.contentHashes.map((hash) => {
|
|
148
|
+
if (!sha256.test(hash) || !existsSync(path.join(dataDirectory, "artifacts", hash))) {
|
|
149
|
+
throw new DomainError("run-transfer-missing", `The content ${hash} of run ${runId} is missing under artifacts`, 404);
|
|
150
|
+
}
|
|
151
|
+
return `artifacts/${hash}`;
|
|
152
|
+
}),
|
|
126
153
|
];
|
|
127
154
|
const chunks: Buffer[] = [];
|
|
155
|
+
const refused: string[] = [];
|
|
156
|
+
const portable = (entry: string, stat: Stats | ReadEntry): boolean => {
|
|
157
|
+
if (temporary(entry)) return false;
|
|
158
|
+
if (!(stat instanceof Stats) || !stat.isSymbolicLink()) return true;
|
|
159
|
+
const decision = linkDecision(dataDirectory, entry);
|
|
160
|
+
if (decision === "refuse") refused.push(entry);
|
|
161
|
+
return decision === "keep";
|
|
162
|
+
};
|
|
128
163
|
try {
|
|
129
|
-
for await (const chunk of createTar({ gzip: true, cwd: dataDirectory, portable: true, filter:
|
|
164
|
+
for await (const chunk of createTar({ gzip: true, cwd: dataDirectory, portable: true, filter: portable }, entries)) {
|
|
130
165
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
131
166
|
}
|
|
132
167
|
} finally {
|
|
133
168
|
await rm(manifestFile, { force: true });
|
|
134
169
|
}
|
|
170
|
+
if (refused.length > 0) {
|
|
171
|
+
throw new DomainError(
|
|
172
|
+
"run-transfer-link",
|
|
173
|
+
`Run ${runId} contains links that would point nowhere on the target: ${refused.join(", ")}. Replace them with files or relative links inside the run.`,
|
|
174
|
+
409,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
135
177
|
const archive = Buffer.concat(chunks);
|
|
136
178
|
if (archive.byteLength > RUN_TRANSFER_MAX_ARCHIVE_BYTES) {
|
|
137
179
|
throw new DomainError(
|
|
@@ -234,6 +276,14 @@ export const unpackRunArchive = async (options: {
|
|
|
234
276
|
if (existsSync(path.join(staging, "sessions"))) {
|
|
235
277
|
await onlyEntry(path.join(staging, "sessions"), manifest.runId, "Der Ordner sessions des Archivs");
|
|
236
278
|
}
|
|
279
|
+
if (existsSync(path.join(staging, "artifacts"))) {
|
|
280
|
+
for (const name of await readdir(path.join(staging, "artifacts"))) {
|
|
281
|
+
const content = sha256.test(name) ? await readFile(path.join(staging, "artifacts", name)) : undefined;
|
|
282
|
+
if (!content || createHash("sha256").update(content).digest("hex") !== name) {
|
|
283
|
+
throw new DomainError("run-transfer-invalid", `The archive content artifacts/${name} does not match its name`, 400);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
237
287
|
const lines = (await readFile(journalFile, "utf8")).split("\n").filter((line) => line.trim().length > 0);
|
|
238
288
|
const records = lines.map((line, index) => parseJournalRecord(line, runDirectory, `${journalFile}:${index + 1}`));
|
|
239
289
|
if (records.length === 0) throw new DomainError("run-transfer-invalid", `Das Journal des Runs ${manifest.runId} im Archiv ist leer`, 400);
|
|
@@ -246,6 +296,17 @@ export const unpackRunArchive = async (options: {
|
|
|
246
296
|
return { manifest, staging, records };
|
|
247
297
|
};
|
|
248
298
|
|
|
299
|
+
/** Legt die Inhalte des Archivs unter artifacts/ des Ziels ab; vorhandene sind wegen ihres Hash-Namens dieselben. */
|
|
300
|
+
export const installContents = async (options: { places: RunTransferPlaces; staging: string }): Promise<void> => {
|
|
301
|
+
const source = path.join(options.staging, "artifacts");
|
|
302
|
+
if (!existsSync(source)) return;
|
|
303
|
+
const target = path.join(options.places.dataDirectory, "artifacts");
|
|
304
|
+
await mkdir(target, { recursive: true });
|
|
305
|
+
for (const name of await readdir(source)) {
|
|
306
|
+
if (!existsSync(path.join(target, name))) await rename(path.join(source, name), path.join(target, name));
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
249
310
|
/** Legt die Session-Ablage des Archivs an ihrem Platz im Ziel ab; das Journal übernimmt danach die Records. */
|
|
250
311
|
export const installSessionDirectory = async (options: {
|
|
251
312
|
places: RunTransferPlaces;
|
|
@@ -3,8 +3,7 @@ import { readdir, readFile, stat } from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import {
|
|
5
5
|
modelToolDescriptors,
|
|
6
|
-
|
|
7
|
-
turnDispatcherExtensionName,
|
|
6
|
+
skillPreloadHookName,
|
|
8
7
|
runtimeOwner,
|
|
9
8
|
type AgentProfile,
|
|
10
9
|
type CatalogModel,
|
|
@@ -104,8 +103,7 @@ const runtimeSettings = (plugins: PluginHost): SettingsRuntime => {
|
|
|
104
103
|
};
|
|
105
104
|
|
|
106
105
|
const internalAgentHooks: readonly InternalAgentHookContribution[] = [
|
|
107
|
-
|
|
108
|
-
skillPreloadExtensionName,
|
|
106
|
+
skillPreloadHookName,
|
|
109
107
|
].map((name): InternalAgentHookContribution => ({
|
|
110
108
|
id: name,
|
|
111
109
|
owner: runtimeOwner,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r,S as d,E as p,j as E,c as f}from"./mermaid-HWGCJPDP-
|
|
1
|
+
import{r,S as d,E as p,j as E,c as f}from"./mermaid-HWGCJPDP-DfsU5ofC.js";var j=({code:i,language:e,maxHeight:o,raw:a,className:u,startLine:m,lineNumbers:n,...h})=>{let{shikiTheme:l}=r.useContext(d),t=p(),[c,s]=r.useState(a);return r.useEffect(()=>{if(!t){s(a);return}let g=t.highlight({code:i,language:e,themes:l},x=>{s(x)});g&&s(g)},[i,e,l,t,a]),E.jsx(f,{className:u,language:e,lineNumbers:n,maxHeight:o,result:c,startLine:m,...h})};export{j as HighlightedCodeBlockBody};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as De,r as t,j as e,D as oe,b as ce,d as G,B as b,X as Le,M as Pe,C as de,e as Me,f as Oe,g as He,h as Ie,u as Be,i as _e,k as Fe,l as Ue,m as Ke,A as Te,n as qe,o as We,p as $e,U as Qe,P as Ve,W as Ge,R as Xe,q as ae,s as V,t as Je,v as Ye,w as Ze,x as et,y as tt,z as st,F as nt,Q as rt,G as at,H as it}from"./mermaid-HWGCJPDP-
|
|
1
|
+
import{a as De,r as t,j as e,D as oe,b as ce,d as G,B as b,X as Le,M as Pe,C as de,e as Me,f as Oe,g as He,h as Ie,u as Be,i as _e,k as Fe,l as Ue,m as Ke,A as Te,n as qe,o as We,p as $e,U as Qe,P as Ve,W as Ge,R as Xe,q as ae,s as V,t as Je,v as Ye,w as Ze,x as et,y as tt,z as st,F as nt,Q as rt,G as at,H as it}from"./mermaid-HWGCJPDP-DfsU5ofC.js";/**
|
|
2
2
|
* @license lucide-react v1.46.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|