@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.
Files changed (150) hide show
  1. package/apps/server/src/chat-handler.ts +2 -0
  2. package/apps/server/src/config.ts +6 -0
  3. package/apps/server/src/host-api.json +1 -0
  4. package/apps/server/src/layout.ts +0 -15
  5. package/apps/server/src/plugin-support/agent-tool.ts +2 -3
  6. package/apps/server/src/plugin-support/plugin-folder.ts +2 -1
  7. package/apps/server/src/provider.ts +34 -11
  8. package/apps/server/src/ragents/actor-chat-history.ts +1 -0
  9. package/apps/server/src/ragents/engine.ts +23 -24
  10. package/apps/server/src/ragents/journal-chat-events.ts +2 -0
  11. package/apps/server/src/ragents/run-owner.ts +3 -2
  12. package/apps/server/src/ragents/session.ts +3 -3
  13. package/apps/server/src/run-preparation.ts +14 -18
  14. package/apps/server/src/run-transfer.ts +65 -4
  15. package/apps/server/src/settings.ts +2 -4
  16. package/apps/web/dist/assets/{highlighted-body-KPVGNVTW-B9qYBARS.js → highlighted-body-KPVGNVTW-xXXhTDbh.js} +1 -1
  17. package/apps/web/dist/assets/{main-BCjvD2Hh.js → main-C7JeZFjs.js} +1 -1
  18. package/apps/web/dist/assets/{mermaid-HWGCJPDP-CvYYFK_n.js → mermaid-HWGCJPDP-DfsU5ofC.js} +69 -69
  19. package/apps/web/dist/assets/{runPanel-CG7kRMJh.js → runPanel-ZT6TPDLM.js} +1 -1
  20. package/apps/web/dist/help/guide-clients.html +13 -4
  21. package/apps/web/dist/help/guide-clients.md +14 -4
  22. package/apps/web/dist/help/guide-distributed.html +8 -3
  23. package/apps/web/dist/help/guide-distributed.md +8 -3
  24. package/apps/web/dist/help/guide-runtime.html +18 -12
  25. package/apps/web/dist/help/guide-runtime.md +19 -12
  26. package/apps/web/dist/help/mini-app.css +23 -3
  27. package/apps/web/dist/host-web.json +4 -4
  28. package/apps/web/dist/index.html +2 -2
  29. package/apps/web/dist/run-panel.html +2 -2
  30. package/apps/web/src/SessionList.tsx +16 -8
  31. package/apps/web/src/StartTiles.tsx +1 -1
  32. package/apps/web/src/api.ts +2 -0
  33. package/apps/web/src/panel/PanelPage.tsx +1 -1
  34. package/apps/web/src/panel/RunLine.tsx +4 -2
  35. package/apps/web/src/panel/contract.ts +2 -0
  36. package/bundles/ragents.activity/ragents-bundle.json +1 -1
  37. package/bundles/ragents.actor-programs/ragents-bundle.json +1 -1
  38. package/bundles/ragents.ask/ragents-bundle.json +1 -1
  39. package/bundles/ragents.browser/ragents-bundle.json +1 -1
  40. package/bundles/ragents.documents/ragents-bundle.json +1 -1
  41. package/bundles/ragents.lsp-fsharp/ragents-bundle.json +1 -1
  42. package/bundles/ragents.lsp-roslyn/ragents-bundle.json +1 -1
  43. package/bundles/ragents.lsp-typescript/ragents-bundle.json +1 -1
  44. package/bundles/ragents.model-relay/ragents-bundle.json +1 -1
  45. package/bundles/ragents.orchestration/ragents-bundle.json +1 -1
  46. package/bundles/ragents.overseer/ragents-bundle.json +1 -1
  47. package/bundles/ragents.processes/ragents-bundle.json +1 -1
  48. package/bundles/ragents.product/ragents-bundle.json +1 -1
  49. package/bundles/ragents.profile-distribution/ragents-bundle.json +1 -1
  50. package/bundles/ragents.reference/ragents-bundle.json +2 -2
  51. package/bundles/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
  52. package/bundles/ragents.reference/run-scripts/word-game/tests/program.test.ts +3 -3
  53. package/bundles/ragents.todo/ragents-bundle.json +1 -1
  54. package/bundles/ragents.transcript/ragents-bundle.json +1 -1
  55. package/bundles/ragents.watch/ragents-bundle.json +1 -1
  56. package/bundles/ragents.workspace/ragents-bundle.json +1 -1
  57. package/package.json +2 -2
  58. package/packages/agent/dist/core/agent-settings.d.ts +2 -2
  59. package/packages/agent/dist/core/compaction/compaction.d.ts +16 -50
  60. package/packages/agent/dist/core/context-log.d.ts +25 -0
  61. package/packages/agent/dist/core/messages.d.ts +3 -10
  62. package/packages/agent/dist/core/tool-definition.d.ts +17 -0
  63. package/packages/agent/dist/core/tools/bash.d.ts +2 -7
  64. package/packages/agent/dist/core/tools/edit.d.ts +1 -1
  65. package/packages/agent/dist/core/tools/read.d.ts +1 -1
  66. package/packages/agent/dist/core/tools/write.d.ts +1 -1
  67. package/packages/agent/dist/index.d.ts +7 -8
  68. package/packages/agent/dist/loop/agent.d.ts +2 -6
  69. package/packages/agent/package.json +1 -1
  70. package/packages/agent/src/core/agent-settings.ts +2 -2
  71. package/packages/agent/src/core/compaction/compaction.ts +49 -175
  72. package/packages/agent/src/core/context-log.ts +49 -0
  73. package/packages/agent/src/core/messages.ts +5 -11
  74. package/packages/agent/src/core/tool-definition.ts +29 -0
  75. package/packages/agent/src/core/tools/bash.ts +2 -9
  76. package/packages/agent/src/core/tools/edit.ts +2 -11
  77. package/packages/agent/src/core/tools/read.ts +3 -18
  78. package/packages/agent/src/core/tools/write.ts +1 -5
  79. package/packages/agent/src/index.ts +28 -14
  80. package/packages/agent/src/loop/agent-loop.ts +1 -2
  81. package/packages/agent/src/loop/agent.ts +8 -2
  82. package/packages/ai/dist/api/ai-sdk.d.ts +2 -0
  83. package/packages/ai/dist/types.d.ts +2 -0
  84. package/packages/ai/src/api/ai-sdk-messages.ts +15 -10
  85. package/packages/ai/src/api/ai-sdk.ts +11 -0
  86. package/packages/ai/src/api/transform-messages.ts +1 -1
  87. package/packages/ai/src/types.ts +2 -0
  88. package/packages/ai/src/utils/retry.ts +14 -0
  89. package/packages/ragents/src/agents/actor-input.ts +15 -0
  90. package/packages/ragents/src/agents/model-context.ts +360 -0
  91. package/packages/ragents/src/agents/plugins.ts +5 -0
  92. package/packages/ragents/src/agents/scheduler.ts +3 -7
  93. package/packages/ragents/src/agents/tools.ts +2 -2
  94. package/packages/ragents/src/agents/toolset.ts +47 -20
  95. package/packages/ragents/src/agents/workspaces.ts +0 -6
  96. package/packages/ragents/src/domain/event-semantics.ts +51 -0
  97. package/packages/ragents/src/domain/event-validation.ts +166 -0
  98. package/packages/ragents/src/domain/events.ts +72 -0
  99. package/packages/ragents/src/domain/model.ts +4 -2
  100. package/packages/ragents/src/domain/projection.ts +9 -0
  101. package/packages/ragents/src/domain/tool-result-text.ts +9 -0
  102. package/packages/ragents/src/drivers/agent-hooks.ts +25 -38
  103. package/packages/ragents/src/drivers/agent-runtime.ts +118 -933
  104. package/packages/ragents/src/drivers/agent-turn.ts +735 -0
  105. package/packages/ragents/src/drivers/agent.ts +9 -7
  106. package/packages/ragents/src/drivers/skill-preload.ts +53 -63
  107. package/packages/ragents/src/drivers/types.ts +21 -5
  108. package/packages/ragents/src/index.ts +8 -6
  109. package/packages/ragents/src/plugin-host.ts +60 -13
  110. package/packages/ragents/src/plugin-types.ts +7 -2
  111. package/packages/ragents/src/runtime/decisions/actors.ts +10 -0
  112. package/packages/ragents/src/runtime/decisions/turns.ts +66 -7
  113. package/packages/ragents/src/runtime/journal-storage.ts +7 -4
  114. package/packages/ragents/src/runtime/journal.ts +35 -0
  115. package/packages/ragents/src/runtime/orchestration.ts +140 -9
  116. package/packages/workspace-executor/src/sandbox-tools.ts +10 -11
  117. package/plugins/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
  118. package/plugins/ragents.reference/run-scripts/word-game/tests/program.test.ts +3 -3
  119. package/scripts/agent/journal.ts +5 -3
  120. package/scripts/package/publish-package.ts +4 -3
  121. package/packages/agent/dist/core/agent-session-runtime.d.ts +0 -33
  122. package/packages/agent/dist/core/agent-session-services.d.ts +0 -35
  123. package/packages/agent/dist/core/agent-session.d.ts +0 -234
  124. package/packages/agent/dist/core/auth-guidance.d.ts +0 -4
  125. package/packages/agent/dist/core/defaults.d.ts +0 -3
  126. package/packages/agent/dist/core/extensions/index.d.ts +0 -11
  127. package/packages/agent/dist/core/extensions/loader.d.ts +0 -12
  128. package/packages/agent/dist/core/extensions/runner.d.ts +0 -39
  129. package/packages/agent/dist/core/extensions/types.d.ts +0 -156
  130. package/packages/agent/dist/core/extensions/wrapper.d.ts +0 -12
  131. package/packages/agent/dist/core/resource-loader.d.ts +0 -39
  132. package/packages/agent/dist/core/sdk.d.ts +0 -38
  133. package/packages/agent/dist/core/session-manager.d.ts +0 -189
  134. package/packages/agent/dist/core/source-info.d.ts +0 -16
  135. package/packages/agent/dist/core/system-prompt.d.ts +0 -21
  136. package/packages/agent/src/core/agent-session-runtime.ts +0 -94
  137. package/packages/agent/src/core/agent-session-services.ts +0 -74
  138. package/packages/agent/src/core/agent-session.ts +0 -1108
  139. package/packages/agent/src/core/auth-guidance.ts +0 -14
  140. package/packages/agent/src/core/defaults.ts +0 -3
  141. package/packages/agent/src/core/extensions/index.ts +0 -38
  142. package/packages/agent/src/core/extensions/loader.ts +0 -125
  143. package/packages/agent/src/core/extensions/runner.ts +0 -211
  144. package/packages/agent/src/core/extensions/types.ts +0 -197
  145. package/packages/agent/src/core/extensions/wrapper.ts +0 -24
  146. package/packages/agent/src/core/resource-loader.ts +0 -142
  147. package/packages/agent/src/core/sdk.ts +0 -123
  148. package/packages/agent/src/core/session-manager.ts +0 -743
  149. package/packages/agent/src/core/source-info.ts +0 -28
  150. 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"),
@@ -899,6 +899,7 @@
899
899
  "timestampLabel",
900
900
  "useChatAction",
901
901
  "useQuasselComponents",
902
+ "useQuasselPortalContainer",
902
903
  "validateAttachmentSelection"
903
904
  ],
904
905
  "react": [
@@ -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, any> & { longDescription?: string; nativeTool?: boolean };
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, 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.map((skill) => skill.paths(context)));
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 infos = described.filter((info): info is ListedSession => info !== undefined);
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, Modellkontexte und seine Plugin-Ablagen. */
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({ places: this.transferPlaces(), runId: id, manifest });
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.journal.forget(id);
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?.journal.forget(id);
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
- AgentSessionDriver,
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, ROOT_ONLY_MODE } from "../layout.js";
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 AgentSessionDriver({
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
- resolveExtensionFactories: (context) => globalChat?.isCoordinator(context.runId) ? [] : options.plugins.agentRuntime.resolve(contributionContextFor(context)),
214
- sessions: {
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, der den Run ausführt. */
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) ?? entry.content;
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((entry) => systemPromptSelectionPromptIds.has(entry.id) ? texts.join("\n\n") : content(entry))
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
- return chapters.map((entry) => entry.content).filter(Boolean).join("\n\n");
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 agentPromptFor = (runId: string): string =>
307
- storedSystemPrompt(journal.stateOf(runId)).shareWithAgents ? textsFor(runId).join("\n\n") : "";
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: (_runId, _actor, toolNames) => chaptersFor(toolNames),
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
- return state ? state.ownerUserId : undefined;
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.actorId === primaryActorId
860
- && (event.type === "turn.finished" || event.type === "turn.interrupted")
861
- && this.#liveTurns.has(event.payload.turnId)) this.#journalFinishedTurns.add(event.payload.turnId);
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 { createAgentSession, DefaultResourceLoader, defineTool, SessionManager, type ModelRuntime } from "@ragents/agent";
1
+ import { Agent, convertToLlm, type AgentTool, type ModelRuntime } from "@ragents/agent";
2
2
  import { Type } from "typebox";
3
- import type { Message } from "@ragents/ai";
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 = defineTool({
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 { session } = await createAgentSession({ modelRuntime: runtime,
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 session.prompt(typeof content === "string" ? content : content.filter((part) => part.type === "text").map((part) => part.text).join("\n"),
118
- typeof content === "string" ? {} : { attachments: content.filter((part) => part.type !== "text") });
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 = session.messages.at(-1);
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 { existsSync } from "node:fs";
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: (entry) => !temporary(entry) }, entries)) {
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
- skillPreloadExtensionName,
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
- turnDispatcherExtensionName,
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-CvYYFK_n.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
+ 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-CvYYFK_n.js";/**
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.