@schlenkr/ragents 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/apps/server/src/config-definition.ts +24 -2
  2. package/apps/server/src/config.ts +0 -6
  3. package/apps/server/src/host-api.json +3 -10
  4. package/apps/server/src/host-api.ts +6 -7
  5. package/apps/server/src/plugin-build/build.ts +28 -5
  6. package/apps/server/src/plugin-build/plugin-description.ts +1 -1
  7. package/apps/server/src/plugin-build/typecheck.ts +2 -2
  8. package/apps/server/src/plugin-support/language-server/plugin.ts +8 -7
  9. package/apps/server/src/plugin-support/language-server/tools.ts +7 -6
  10. package/apps/server/src/plugin-support/model-aliases.ts +55 -25
  11. package/apps/server/src/plugin-support/plugin-folder.ts +2 -1
  12. package/apps/server/src/plugin-support/product-relay.ts +10 -2
  13. package/apps/server/src/plugin-support/workspace-sandbox-host.ts +18 -9
  14. package/apps/server/src/profile/bundle-manifest.ts +7 -3
  15. package/apps/server/src/profile/compose.ts +6 -0
  16. package/apps/server/src/profile/plugin-discovery.ts +6 -1
  17. package/apps/server/src/profile/provisioning.ts +6 -5
  18. package/apps/server/src/ragents/engine.ts +18 -12
  19. package/apps/server/src/ragents/host-services.ts +4 -0
  20. package/apps/server/src/ragents/session.ts +1 -1
  21. package/apps/server/src/server.ts +1 -0
  22. package/apps/server/tsconfig.json +2 -0
  23. package/apps/web/dist/assets/{highlighted-body-KPVGNVTW-CFhTJEmH.js → highlighted-body-KPVGNVTW-xXXhTDbh.js} +1 -1
  24. package/apps/web/dist/assets/{main-DlzW4t4c.js → main-C7JeZFjs.js} +1 -1
  25. package/apps/web/dist/assets/{mermaid-HWGCJPDP-kxkB991i.js → mermaid-HWGCJPDP-DfsU5ofC.js} +2 -2
  26. package/apps/web/dist/assets/{runPanel-BJEFQ__w.js → runPanel-ZT6TPDLM.js} +1 -1
  27. package/apps/web/dist/help/guide-clients.html +14 -6
  28. package/apps/web/dist/help/guide-clients.md +14 -6
  29. package/apps/web/dist/help/guide-distributed.html +12 -4
  30. package/apps/web/dist/help/guide-distributed.md +12 -4
  31. package/apps/web/dist/help/guide-getting-started.html +12 -8
  32. package/apps/web/dist/help/guide-getting-started.md +12 -8
  33. package/apps/web/dist/help/guide-plugins.html +6 -2
  34. package/apps/web/dist/help/guide-plugins.md +6 -2
  35. package/apps/web/dist/help/guide-runtime.html +2 -1
  36. package/apps/web/dist/help/guide-runtime.md +2 -1
  37. package/apps/web/dist/help/index.html +3328 -1135
  38. package/apps/web/dist/host-web.json +4 -4
  39. package/apps/web/dist/index.html +2 -2
  40. package/apps/web/dist/run-panel.html +2 -2
  41. package/apps/web/src/StartTiles.tsx +1 -1
  42. package/bundles/ragents.activity/ragents-bundle.json +3 -3
  43. package/bundles/ragents.actor-programs/ragents-bundle.json +3 -3
  44. package/bundles/ragents.ask/ragents-bundle.json +4 -4
  45. package/bundles/ragents.ask/server/index.js +42 -11
  46. package/bundles/ragents.ask/server/index.js.map +3 -3
  47. package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js +2 -0
  48. package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js.map +7 -0
  49. package/bundles/ragents.ask/web/classes.json +1 -1
  50. package/bundles/ragents.ask/web/exports/ask-payload.js +1 -1
  51. package/bundles/ragents.ask/web/index.js +1 -1
  52. package/bundles/ragents.ask/web/index.js.map +3 -3
  53. package/bundles/ragents.browser/executor/index.mjs +357 -0
  54. package/bundles/ragents.browser/executor/index.mjs.map +7 -0
  55. package/bundles/ragents.browser/ragents-bundle.json +5 -8
  56. package/bundles/ragents.browser/server/chunks/{chunk-2GKLLCBE.js → chunk-CWLX4VA5.js} +1 -1
  57. package/bundles/ragents.browser/server/chunks/chunk-CWLX4VA5.js.map +7 -0
  58. package/bundles/ragents.browser/server/exports/server/contract.js +1 -1
  59. package/bundles/ragents.browser/server/index.js +20 -9
  60. package/bundles/ragents.browser/server/index.js.map +4 -4
  61. package/bundles/ragents.documents/ragents-bundle.json +3 -3
  62. package/bundles/ragents.lsp-fsharp/executor/index.mjs +68 -0
  63. package/bundles/ragents.lsp-fsharp/executor/index.mjs.map +7 -0
  64. package/bundles/ragents.lsp-fsharp/ragents-bundle.json +5 -9
  65. package/bundles/ragents.lsp-fsharp/server/index.js +21 -6
  66. package/bundles/ragents.lsp-fsharp/server/index.js.map +4 -4
  67. package/bundles/ragents.lsp-roslyn/executor/index.mjs +68 -0
  68. package/bundles/ragents.lsp-roslyn/executor/index.mjs.map +7 -0
  69. package/bundles/ragents.lsp-roslyn/ragents-bundle.json +6 -8
  70. package/bundles/ragents.lsp-roslyn/server/index.js +54 -15
  71. package/bundles/ragents.lsp-roslyn/server/index.js.map +4 -4
  72. package/bundles/ragents.lsp-typescript/executor/index.mjs +49 -0
  73. package/bundles/ragents.lsp-typescript/executor/index.mjs.map +7 -0
  74. package/bundles/ragents.lsp-typescript/ragents-bundle.json +5 -7
  75. package/bundles/ragents.lsp-typescript/server/index.js +22 -2
  76. package/bundles/ragents.lsp-typescript/server/index.js.map +3 -3
  77. package/bundles/ragents.model-relay/ragents-bundle.json +4 -4
  78. package/bundles/ragents.model-relay/server/index.js +3 -2
  79. package/bundles/ragents.model-relay/server/index.js.map +2 -2
  80. package/bundles/ragents.orchestration/ragents-bundle.json +3 -3
  81. package/bundles/ragents.overseer/ragents-bundle.json +3 -3
  82. package/bundles/ragents.processes/ragents-bundle.json +3 -3
  83. package/bundles/ragents.product/ragents-bundle.json +3 -3
  84. package/bundles/ragents.profile-distribution/ragents-bundle.json +4 -4
  85. package/bundles/ragents.profile-distribution/server/index.js +1 -1
  86. package/bundles/ragents.profile-distribution/server/index.js.map +2 -2
  87. package/bundles/ragents.reference/ragents-bundle.json +3 -3
  88. package/bundles/ragents.todo/ragents-bundle.json +3 -3
  89. package/bundles/ragents.transcript/ragents-bundle.json +3 -3
  90. package/bundles/ragents.watch/ragents-bundle.json +3 -3
  91. package/bundles/ragents.workspace/ragents-bundle.json +11 -5
  92. package/bundles/ragents.workspace/server/index.js +101 -28
  93. package/bundles/ragents.workspace/server/index.js.map +3 -3
  94. package/bundles/ragents.workspace/web/classes.json +1 -1
  95. package/bundles/ragents.workspace/web/index.js +1 -1
  96. package/bundles/ragents.workspace/web/index.js.map +3 -3
  97. package/package.json +2 -2
  98. package/packages/agent/dist/core/agent-settings.d.ts +1 -7
  99. package/packages/agent/dist/core/compaction/compaction.d.ts +17 -13
  100. package/packages/agent/dist/core/model-runtime.d.ts +5 -4
  101. package/packages/agent/dist/core/provider-composer.d.ts +2 -1
  102. package/packages/agent/dist/core/tools/bash.d.ts +9 -3
  103. package/packages/agent/dist/index.d.ts +1 -1
  104. package/packages/agent/src/core/agent-settings.ts +1 -4
  105. package/packages/agent/src/core/compaction/compaction.ts +68 -30
  106. package/packages/agent/src/core/model-runtime.ts +12 -5
  107. package/packages/agent/src/core/provider-composer.ts +5 -0
  108. package/packages/agent/src/core/tools/bash.ts +36 -30
  109. package/packages/agent/src/index.ts +3 -0
  110. package/packages/ai/dist/types.d.ts +11 -0
  111. package/packages/ai/src/types.ts +12 -0
  112. package/packages/ragents/src/agents/plugins.ts +5 -0
  113. package/packages/ragents/src/domain/event-semantics.ts +3 -0
  114. package/packages/ragents/src/domain/event-validation.ts +15 -2
  115. package/packages/ragents/src/domain/events.ts +3 -0
  116. package/packages/ragents/src/domain/model.ts +2 -0
  117. package/packages/ragents/src/domain/projection.ts +1 -0
  118. package/packages/ragents/src/drivers/agent-runtime.ts +1 -1
  119. package/packages/ragents/src/drivers/agent-turn.ts +8 -6
  120. package/packages/ragents/src/index.ts +1 -0
  121. package/packages/ragents/src/plugin-host.ts +58 -10
  122. package/packages/ragents/src/plugin-types.ts +7 -2
  123. package/packages/ragents/src/runtime/decisions/inputs.ts +6 -2
  124. package/packages/ragents/src/runtime/journal-storage.ts +5 -4
  125. package/packages/workspace-executor/package.json +0 -1
  126. package/packages/workspace-executor/src/bash-launch.ts +56 -10
  127. package/packages/workspace-executor/src/context.ts +4 -0
  128. package/packages/workspace-executor/src/contributions.ts +150 -0
  129. package/packages/workspace-executor/src/executor.ts +1 -1
  130. package/packages/workspace-executor/src/index.ts +16 -19
  131. package/packages/workspace-executor/src/language-server/host.ts +3 -0
  132. package/packages/workspace-executor/src/language-server/module.ts +12 -2
  133. package/packages/workspace-executor/src/language-server/roots.ts +0 -5
  134. package/packages/workspace-executor/src/modules.ts +7 -6
  135. package/packages/workspace-executor/src/sandbox-tools.ts +3 -9
  136. package/packages/workspace-executor/src/shell-platform.ts +14 -3
  137. package/packages/workspace-executor/src/tools.ts +0 -3
  138. package/plugins/ragents.ask/ask-payload.ts +9 -0
  139. package/plugins/ragents.ask/server/ask-service.ts +53 -13
  140. package/plugins/ragents.ask/web/AskActionView.tsx +3 -2
  141. package/plugins/ragents.browser/executor/index.ts +5 -0
  142. package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/module.ts +25 -31
  143. package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/playwright.ts +9 -23
  144. package/plugins/ragents.browser/provision.ts +1 -1
  145. package/plugins/ragents.browser/server/browser.ts +3 -3
  146. package/plugins/ragents.browser/server/contract.ts +2 -2
  147. package/plugins/ragents.browser/server/index.ts +1 -1
  148. package/plugins/ragents.lsp-fsharp/executor.ts +80 -0
  149. package/plugins/ragents.lsp-fsharp/provision.ts +1 -1
  150. package/plugins/ragents.lsp-fsharp/server/index.ts +2 -2
  151. package/plugins/ragents.lsp-roslyn/executor.ts +64 -0
  152. package/plugins/ragents.lsp-roslyn/provision.ts +1 -1
  153. package/plugins/ragents.lsp-roslyn/server/index.ts +28 -5
  154. package/plugins/ragents.lsp-roslyn/server/solution-on-start.ts +18 -6
  155. package/plugins/ragents.lsp-typescript/executor.ts +50 -0
  156. package/plugins/ragents.lsp-typescript/server/index.ts +2 -2
  157. package/plugins/ragents.model-relay/server/config.ts +1 -1
  158. package/plugins/ragents.model-relay/server/relay.ts +4 -2
  159. package/plugins/ragents.profile-distribution/server/archive.ts +2 -1
  160. package/plugins/ragents.workspace/client/workspace-client.ts +111 -28
  161. package/plugins/ragents.workspace/contract.ts +33 -2
  162. package/plugins/ragents.workspace/server/binding.ts +10 -4
  163. package/plugins/ragents.workspace/server/clients.ts +56 -9
  164. package/plugins/ragents.workspace/server/config.ts +9 -0
  165. package/plugins/ragents.workspace/server/index.ts +13 -4
  166. package/plugins/ragents.workspace/server/runtime.ts +10 -0
  167. package/plugins/ragents.workspace/server/shell-platform.ts +6 -5
  168. package/plugins/ragents.workspace/web/WorkspaceBinding.tsx +1 -1
  169. package/ragents.config.core.ts +4 -1
  170. package/ragents.config.showcase.ts +4 -1
  171. package/scripts/agent/journal.ts +3 -1
  172. package/scripts/package/build-package.ts +3 -1
  173. package/scripts/package/publish-package.ts +4 -3
  174. package/scripts/provision/run-provision.ts +2 -2
  175. package/scripts/workspace-client/run-workspace-client.ts +7 -4
  176. package/apps/web/dist/help/screenshots/release-checklist-vscode.png +0 -0
  177. package/apps/web/dist/help/screenshots/release-checklist-web.png +0 -0
  178. package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js +0 -2
  179. package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js.map +0 -7
  180. package/bundles/ragents.browser/server/chunks/chunk-2GKLLCBE.js.map +0 -7
  181. package/packages/workspace-executor/src/language-server/adapters/fsharp.ts +0 -60
  182. package/packages/workspace-executor/src/language-server/adapters/index.ts +0 -7
  183. package/packages/workspace-executor/src/language-server/adapters/roslyn.ts +0 -56
  184. package/packages/workspace-executor/src/language-server/adapters/typescript.ts +0 -74
  185. package/packages/workspace-executor/src/language-server/solution.ts +0 -13
  186. /package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/contract.ts +0 -0
  187. /package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/pages.ts +0 -0
@@ -520,6 +520,15 @@ export interface ModelCost extends ModelCostRates {
520
520
  /** Request-wide pricing tiers. The highest matching input threshold applies to the full request. */
521
521
  tiers?: ModelCostTier[];
522
522
  }
523
+ /** When an agent compacts its context on a model and how much it keeps, in absolute tokens of that model. */
524
+ export interface ModelCompaction {
525
+ /** Context tokens above which the agent compacts. */
526
+ threshold: number;
527
+ /** Tokens of the newest context the compaction keeps verbatim. */
528
+ keepRecentTokens: number;
529
+ /** Output budget of the summary. */
530
+ summaryTokens: number;
531
+ }
523
532
  export interface Model<TApi extends Api> {
524
533
  id: string;
525
534
  name: string;
@@ -536,6 +545,8 @@ export interface Model<TApi extends Api> {
536
545
  cost: ModelCost;
537
546
  contextWindow: number;
538
547
  maxTokens: number;
548
+ /** The model's own compaction values; without them the agent applies the catalog standard of its context window. */
549
+ compaction?: ModelCompaction;
539
550
  headers?: Record<string, string>;
540
551
  /** Compatibility overrides for OpenAI-compatible APIs. If not set, auto-detected from baseUrl. */
541
552
  compat?: TApi extends "openai-completions" ? OpenAICompletionsCompat : TApi extends "openai-responses" | "openai-codex-responses" ? OpenAIResponsesCompat : TApi extends "anthropic-messages" ? AnthropicMessagesCompat : never;
@@ -561,6 +561,16 @@ export interface ModelCost extends ModelCostRates {
561
561
  tiers?: ModelCostTier[];
562
562
  }
563
563
 
564
+ /** When an agent compacts its context on a model and how much it keeps, in absolute tokens of that model. */
565
+ export interface ModelCompaction {
566
+ /** Context tokens above which the agent compacts. */
567
+ threshold: number;
568
+ /** Tokens of the newest context the compaction keeps verbatim. */
569
+ keepRecentTokens: number;
570
+ /** Output budget of the summary. */
571
+ summaryTokens: number;
572
+ }
573
+
564
574
  // Model interface for the unified model system
565
575
  export interface Model<TApi extends Api> {
566
576
  id: string;
@@ -578,6 +588,8 @@ export interface Model<TApi extends Api> {
578
588
  cost: ModelCost;
579
589
  contextWindow: number;
580
590
  maxTokens: number;
591
+ /** The model's own compaction values; without them the agent applies the catalog standard of its context window. */
592
+ compaction?: ModelCompaction;
581
593
  headers?: Record<string, string>;
582
594
  /** Compatibility overrides for OpenAI-compatible APIs. If not set, auto-detected from baseUrl. */
583
595
  compat?: TApi extends "openai-completions"
@@ -19,6 +19,8 @@ export type ToolContributor = {
19
19
  name: string;
20
20
  descriptors: readonly ToolDescriptor[];
21
21
  dynamic?: boolean;
22
+ /** Ob die Werkzeuge in einem Run gelten; ohne Angabe in jedem, sonst fehlen sie dort ganz. */
23
+ runCondition?: (runId: string) => boolean;
22
24
  tools: (context: PluginContext) => readonly RunFunction[] | Promise<readonly RunFunction[]>;
23
25
  };
24
26
 
@@ -74,6 +76,9 @@ export class ToolRegistry implements ToolProvider {
74
76
  const taken = new Set(agentTools.map((entry) => entry.name));
75
77
 
76
78
  for (const plugin of this.#plugins) {
79
+ if (plugin.runCondition && !plugin.runCondition(context.runId))
80
+ continue;
81
+
77
82
  const tools = await plugin.tools(context);
78
83
  this.#assertDescriptors(plugin, tools);
79
84
 
@@ -358,6 +358,9 @@ export const assertEventSemantics = (
358
358
  for (const artifactId of event.payload.artifactIds)
359
359
  assertArtifactRead(state, event.actorId, artifactId);
360
360
 
361
+ if (event.payload.origin === "human" && existingActorOf(state, event.actorId).kind !== "human")
362
+ throw new Error(`Input ${event.payload.inputId} of human origin was not enqueued by a human actor.`);
363
+
361
364
  if (event.payload.subscriptionId) {
362
365
  const subscription = state.subscriptions.get(event.payload.subscriptionId);
363
366
 
@@ -431,9 +431,13 @@ const payloadOf = (type: EventType, value: unknown, path: string) => {
431
431
  "artifactIds",
432
432
  "sourceEventIds",
433
433
  "subscriptionId",
434
- ], ["presentation"]);
434
+ ], ["presentation", "origin"]);
435
435
  if (payload.presentation !== undefined && payload.presentation !== "background")
436
436
  fail(`${path}.presentation`, "must be background");
437
+ if (payload.origin !== undefined && payload.origin !== "human")
438
+ fail(`${path}.origin`, "must be human");
439
+ if (subscriptionInput && Object.hasOwn(payload, "origin"))
440
+ fail(`${path}.origin`, "is not supported for a subscription input");
437
441
  stringOf(payload.inputId, `${path}.inputId`);
438
442
  actorIdOf(payload.actorId, `${path}.actorId`);
439
443
  stringArrayOf(payload.artifactIds, `${path}.artifactIds`);
@@ -545,7 +549,7 @@ const payloadOf = (type: EventType, value: unknown, path: string) => {
545
549
  "model",
546
550
  "readFiles",
547
551
  "modifiedFiles",
548
- ]);
552
+ ], ["threshold"]);
549
553
  stringOf(payload.turnId, `${path}.turnId`);
550
554
  nonEmptyStringOf(payload.summary, `${path}.summary`);
551
555
  nonEmptyStringOf(payload.firstKeptEventId, `${path}.firstKeptEventId`);
@@ -554,6 +558,15 @@ const payloadOf = (type: EventType, value: unknown, path: string) => {
554
558
  nonEmptyStringOf(payload.model, `${path}.model`);
555
559
  stringArrayOf(payload.readFiles, `${path}.readFiles`);
556
560
  stringArrayOf(payload.modifiedFiles, `${path}.modifiedFiles`);
561
+
562
+ if (Object.hasOwn(payload, "threshold")) {
563
+ const threshold = exactObject(payload.threshold, `${path}.threshold`, ["tokens", "source"]);
564
+ if (!Number.isSafeInteger(threshold.tokens))
565
+ fail(`${path}.threshold.tokens`, "must be a safe integer");
566
+ if (threshold.source !== "model" && threshold.source !== "catalog")
567
+ fail(`${path}.threshold.source`, "must be model or catalog");
568
+ }
569
+
557
570
  return;
558
571
  }
559
572
 
@@ -93,6 +93,7 @@ export type EventPayloads = {
93
93
  actorId: ActorId;
94
94
  artifactIds: string[];
95
95
  presentation?: "background";
96
+ origin?: "human";
96
97
  } & (
97
98
  | { subscriptionId: null; sourceEventIds: string[]; content: string }
98
99
  | { subscriptionId: SubscriptionId; sourceEventIds: [string]; content?: never }
@@ -151,6 +152,8 @@ export type EventPayloads = {
151
152
  model: string;
152
153
  readFiles: string[];
153
154
  modifiedFiles: string[];
155
+ /** The compaction threshold of the model and whether it is the model's own or the catalog standard; absent before file format 9. */
156
+ threshold?: { tokens: number; source: "model" | "catalog" };
154
157
  };
155
158
  "model.output.completed": {
156
159
  turnId: TurnId;
@@ -89,6 +89,8 @@ export type ActorInput = {
89
89
  actorId: ActorId;
90
90
  content: string;
91
91
  presentation?: "background";
92
+ /** Set only on a person's own message, because system inputs are enqueued under the owner too. */
93
+ origin?: "human";
92
94
  artifactIds: readonly ArtifactId[];
93
95
  enqueuedBy: ActorId;
94
96
  enqueuedAt: string;
@@ -222,6 +222,7 @@ export function applyEvent(
222
222
  actorId: event.payload.actorId,
223
223
  content: event.payload.subscriptionId === null ? event.payload.content : JSON.stringify(source),
224
224
  ...(event.payload.presentation ? { presentation: event.payload.presentation } : {}),
225
+ ...(event.payload.origin ? { origin: event.payload.origin } : {}),
225
226
  artifactIds: event.payload.artifactIds,
226
227
  enqueuedBy: event.actorId,
227
228
  enqueuedAt: event.occurredAt,
@@ -36,7 +36,7 @@ export type AgentRuntimeManagerOptions = {
36
36
  context: AgentRuntimeContext,
37
37
  signal: AbortSignal,
38
38
  ) => readonly AgentHook[] | Promise<readonly AgentHook[]>;
39
- /** Compaction, retry and provider request limits; unset values keep the defaults. */
39
+ /** Retry and provider request limits; unset values keep the defaults. Compaction comes from the model. */
40
40
  settings?: AgentSettingsInput;
41
41
  turnAbortTimeoutMs?: number;
42
42
  onDiagnostic?: (diagnostic: AgentRuntimeDiagnostic) => void;
@@ -10,6 +10,7 @@ import {
10
10
  type AgentTool,
11
11
  calculateContextTokens,
12
12
  compact,
13
+ compactionOf,
13
14
  contextMessages,
14
15
  convertToLlm,
15
16
  EMPTY_RESPONSE_NUDGE,
@@ -626,12 +627,11 @@ export class AgentTurn {
626
627
  return true;
627
628
  }
628
629
 
629
- /** After a context overflow compacts and retries once; over the threshold compacts without a retry. */
630
+ /** After a context overflow compacts and retries once; over the threshold of the turn's model compacts without a retry. */
630
631
  async #checkCompaction(assistant: AssistantMessage | undefined, skipAborted = true): Promise<boolean> {
631
- const settings = this.#options.settings.compaction;
632
632
  const { model } = this.#options;
633
633
 
634
- if (!assistant || !settings.enabled || (skipAborted && assistant.stopReason === "aborted"))
634
+ if (!assistant || (skipAborted && assistant.stopReason === "aborted"))
635
635
  return false;
636
636
 
637
637
  const contextWindow = model.contextWindow ?? 0;
@@ -672,12 +672,13 @@ export class AgentTurn {
672
672
  contextTokens = estimate.tokens;
673
673
  }
674
674
 
675
- return shouldCompact(contextTokens, contextWindow, settings) ? this.#compact(false) : false;
675
+ return shouldCompact(contextTokens, compactionOf(model).values) ? this.#compact(false) : false;
676
676
  }
677
677
 
678
678
  async #compact(retry: boolean): Promise<boolean> {
679
- const { model, modelRuntime, thinkingLevel, settings } = this.#options;
680
- const preparation = prepareCompaction([...this.#request.modelContext().entries], settings.compaction);
679
+ const { model, modelRuntime, thinkingLevel } = this.#options;
680
+ const compaction = compactionOf(model);
681
+ const preparation = prepareCompaction([...this.#request.modelContext().entries], compaction.values);
681
682
 
682
683
  if (!preparation)
683
684
  return false;
@@ -715,6 +716,7 @@ export class AgentTurn {
715
716
  model: model.id,
716
717
  readFiles: result.readFiles,
717
718
  modifiedFiles: result.modifiedFiles,
719
+ threshold: { tokens: compaction.values.threshold, source: compaction.source },
718
720
  },
719
721
  });
720
722
  const messages = this.#request.modelContext().messages;
@@ -27,6 +27,7 @@ export {
27
27
  PluginHost,
28
28
  ProfileContributionRegistry,
29
29
  PromptContributionRegistry,
30
+ RunConditionRegistry,
30
31
  SessionMetadataContributionRegistry,
31
32
  SkillContributionRegistry,
32
33
  StartEntryContributionRegistry,
@@ -41,6 +41,7 @@ import type {
41
41
  RAgentsPlugin,
42
42
  ServiceToken,
43
43
  RegisteredStartOption,
44
+ RunCondition,
44
45
  SessionLifecycleContribution,
45
46
  SessionMetadata,
46
47
  SessionStartedContext,
@@ -86,6 +87,24 @@ class ContributionRegistry<T extends { id: string }> {
86
87
  }
87
88
  }
88
89
 
90
+ /** Die Laufbedingung je Plugin; ein Plugin ohne gilt in jedem Run. */
91
+ export class RunConditionRegistry {
92
+ readonly #conditions = new Map<string, RunCondition>();
93
+
94
+ register(owner: string, condition: RunCondition): void {
95
+ if (this.#conditions.has(owner)) throw new Error(`Plugin ${owner} hat bereits eine Laufbedingung`);
96
+ this.#conditions.set(owner, condition);
97
+ }
98
+
99
+ has(owner: string): boolean {
100
+ return this.#conditions.has(owner);
101
+ }
102
+
103
+ applies(owner: string, runId: string): boolean {
104
+ return this.#conditions.get(owner)?.(runId) ?? true;
105
+ }
106
+ }
107
+
89
108
  export class MethodContributionRegistry {
90
109
  readonly #methods = new ContributionRegistry<{ id: string; contribution: MethodContribution }>("Methode");
91
110
 
@@ -219,6 +238,11 @@ export class OperationContributionRegistry {
219
238
 
220
239
  export class AgentContributionRegistry {
221
240
  readonly #contributions = new ContributionRegistry<AgentContribution>("Agent-Beitrag");
241
+ readonly #conditions: RunConditionRegistry;
242
+
243
+ constructor(conditions = new RunConditionRegistry()) {
244
+ this.#conditions = conditions;
245
+ }
222
246
 
223
247
  register(owner: string, contributions: readonly AgentContribution[]): void {
224
248
  for (const contribution of contributions) {
@@ -230,7 +254,9 @@ export class AgentContributionRegistry {
230
254
  }
231
255
 
232
256
  resolve(context: AgentContributionContext): readonly AgentHook[] {
233
- return this.#contributions.entries().map(({ value }) => agentHookOf(value, context));
257
+ return this.#contributions.entries()
258
+ .filter(({ owner }) => this.#conditions.applies(owner, context.runId))
259
+ .map(({ value }) => agentHookOf(value, context));
234
260
  }
235
261
 
236
262
  describe(): readonly PublicAgentHookContribution[] {
@@ -246,6 +272,11 @@ export class AgentContributionRegistry {
246
272
 
247
273
  export class ToolContributionRegistry {
248
274
  readonly #contributors: Owned<ToolContributor>[] = [];
275
+ readonly #conditions: RunConditionRegistry;
276
+
277
+ constructor(conditions = new RunConditionRegistry()) {
278
+ this.#conditions = conditions;
279
+ }
249
280
 
250
281
  register(owner: string, contributors: readonly ToolContributor[]): void {
251
282
  for (const contributor of contributors) {
@@ -278,7 +309,9 @@ export class ToolContributionRegistry {
278
309
  }
279
310
 
280
311
  entries(): readonly ToolContributor[] {
281
- return this.#contributors.map(({ value }) => value);
312
+ return this.#contributors.map(({ owner, value }) => this.#conditions.has(owner)
313
+ ? { ...value, runCondition: (runId: string) => this.#conditions.applies(owner, runId) }
314
+ : value);
282
315
  }
283
316
 
284
317
  describe(): readonly PublicToolDescriptor[] {
@@ -295,6 +328,11 @@ export class ToolContributionRegistry {
295
328
 
296
329
  export class PromptContributionRegistry {
297
330
  readonly #prompts = new ContributionRegistry<PromptContribution>("Prompt-Beitrag");
331
+ readonly #conditions: RunConditionRegistry;
332
+
333
+ constructor(conditions = new RunConditionRegistry()) {
334
+ this.#conditions = conditions;
335
+ }
298
336
 
299
337
  register(owner: string, prompts: readonly PromptContribution[]): void {
300
338
  this.#prompts.register(owner, prompts);
@@ -330,10 +368,13 @@ export class PromptContributionRegistry {
330
368
  })));
331
369
  }
332
370
 
333
- /** Die Beiträge, die für diesen Run anders lauten als im Schnappschuss; alles andere bleibt der gerenderte Text. */
371
+ /** Die Beiträge, die für diesen Run anders lauten als im Schnappschuss; ein leerer Text fehlt dort, alles andere bleibt der gerenderte Text. */
334
372
  runOverrides(runId: string): ReadonlyMap<string, string> {
335
- return new Map([...this.#prompts.entries()].flatMap(({ value }) =>
336
- value.renderForRun ? [[value.id, value.renderForRun(runId).trim()] as const] : []));
373
+ return new Map([...this.#prompts.entries()].flatMap(({ owner, value }) => {
374
+ if (!this.#conditions.applies(owner, runId)) return [[value.id, ""] as const];
375
+ const text = value.renderForRun?.(runId);
376
+ return text === undefined ? [] : [[value.id, text.trim()] as const];
377
+ }));
337
378
  }
338
379
 
339
380
  static handlebarsContext(context: PromptRenderContext): Record<string, unknown> {
@@ -343,6 +384,11 @@ export class PromptContributionRegistry {
343
384
 
344
385
  export class SkillContributionRegistry {
345
386
  readonly #skills = new ContributionRegistry<SkillContribution>("Skill-Beitrag");
387
+ readonly #conditions: RunConditionRegistry;
388
+
389
+ constructor(conditions = new RunConditionRegistry()) {
390
+ this.#conditions = conditions;
391
+ }
346
392
 
347
393
  register(owner: string, skills: readonly SkillContribution[]): void {
348
394
  for (const skill of skills) {
@@ -385,7 +431,7 @@ export class SkillContributionRegistry {
385
431
  id: value.id,
386
432
  owner,
387
433
  audiences: value.audiences ?? allAudiences,
388
- paths: await value.paths(context),
434
+ paths: context && !this.#conditions.applies(owner, context.runId) ? [] : await value.paths(context),
389
435
  })));
390
436
  }
391
437
  }
@@ -1068,10 +1114,11 @@ export class PluginHost {
1068
1114
  readonly channels = new ChannelContributionRegistry();
1069
1115
  readonly http = new HttpContributionRegistry();
1070
1116
  readonly operations = new OperationContributionRegistry();
1071
- readonly agentRuntime = new AgentContributionRegistry();
1072
- readonly tools = new ToolContributionRegistry();
1073
- readonly prompts = new PromptContributionRegistry();
1074
- readonly skills = new SkillContributionRegistry();
1117
+ readonly runConditions = new RunConditionRegistry();
1118
+ readonly agentRuntime = new AgentContributionRegistry(this.runConditions);
1119
+ readonly tools = new ToolContributionRegistry(this.runConditions);
1120
+ readonly prompts = new PromptContributionRegistry(this.runConditions);
1121
+ readonly skills = new SkillContributionRegistry(this.runConditions);
1075
1122
  readonly startEntries = new StartEntryContributionRegistry();
1076
1123
  readonly profiles = new ProfileContributionRegistry();
1077
1124
  readonly script = new ScriptContributionRegistry();
@@ -1121,6 +1168,7 @@ export class PluginHost {
1121
1168
  agentRuntime: (...entries) => this.agentRuntime.register(manifest.id, entries),
1122
1169
  profiles: (...entries) => this.profiles.register(manifest.id, entries),
1123
1170
  prompts: (...entries) => this.prompts.register(manifest.id, entries),
1171
+ runCondition: (condition) => this.runConditions.register(manifest.id, condition),
1124
1172
  provide: (token, service) => this.#services.provide(manifest.id, token, service),
1125
1173
  service: (token) => this.#services.require(token),
1126
1174
  optionalService: (token) => this.#services.optional(token),
@@ -277,10 +277,13 @@ export interface PromptContribution {
277
277
  delivery?: "initial" | "on-demand";
278
278
  requiresTools?: readonly string[];
279
279
  render: (context: PromptRenderContext) => string | Promise<string>;
280
- /** Hängt der Beitrag am einzelnen Run, ersetzt dieser Text den von `render` - synchron, damit der Turn ihn ohne Warten hat. */
281
- renderForRun?: (runId: string) => string;
280
+ /** Hängt der Beitrag am einzelnen Run, ersetzt dieser Text den von `render`, undefined behält ihn - synchron, damit der Turn ihn ohne Warten hat. */
281
+ renderForRun?: (runId: string) => string | undefined;
282
282
  }
283
283
 
284
+ /** Ob die laufbezogenen Beiträge eines Plugins in einem Run gelten; der Host fragt je Run, ein Wurf lässt dessen Turn mit dieser Ursache scheitern. */
285
+ export type RunCondition = (runId: string) => boolean;
286
+
284
287
  export type ModelProviderConfig = Parameters<ModelRuntime["registerProvider"]>[1];
285
288
 
286
289
  export interface ModelProviderRegistration {
@@ -420,6 +423,8 @@ export interface PluginRegistration {
420
423
  agentRuntime: (...contributions: readonly AgentContribution[]) => void;
421
424
  profiles: (...contributions: readonly ProfileContribution[]) => void;
422
425
  prompts: (...contributions: readonly PromptContribution[]) => void;
426
+ /** Prompts, Funktionen, Skills und Agent-Hooks dieses Plugins gelten nur in Runs, für die die Bedingung zutrifft; höchstens eine je Plugin. */
427
+ runCondition: (condition: RunCondition) => void;
423
428
  provide: <T>(token: ServiceToken<T>, service: T) => void;
424
429
  service: <T>(token: ServiceToken<T>) => T;
425
430
  optionalService: <T>(token: ServiceToken<T>) => T | undefined;
@@ -17,8 +17,8 @@ export type EnqueueInput = {
17
17
  artifactIds?: readonly string[];
18
18
  presentation?: "background";
19
19
  } & (
20
- | { content: string; sourceEventIds?: readonly string[]; subscriptionId?: null }
21
- | { content?: never; sourceEventIds: readonly [string]; subscriptionId: string }
20
+ | { content: string; sourceEventIds?: readonly string[]; subscriptionId?: null; origin?: "human" }
21
+ | { content?: never; sourceEventIds: readonly [string]; subscriptionId: string; origin?: never }
22
22
  );
23
23
 
24
24
  export const enqueueInput =
@@ -43,6 +43,9 @@ export const enqueueInput =
43
43
  assertCapability(caller, "actor.input", { kind: "run" });
44
44
  }
45
45
 
46
+ if (input.origin === "human" && caller.kind !== "human")
47
+ throw new DomainError("input-origin-invalid", "Only a human can enqueue an input of human origin.", 403);
48
+
46
49
  const artifactIds = [...new Set(input.artifactIds ?? [])];
47
50
 
48
51
  for (const artifactId of artifactIds)
@@ -55,6 +58,7 @@ export const enqueueInput =
55
58
  actorId: target.id,
56
59
  artifactIds,
57
60
  ...(input.presentation !== undefined ? { presentation: input.presentation } : {}),
61
+ ...(input.origin !== undefined ? { origin: input.origin } : {}),
58
62
  };
59
63
 
60
64
  if (subscriptionId && firstSourceEventId === undefined)
@@ -8,9 +8,10 @@ import { isRunId } from "../domain/portable-id.ts";
8
8
  import { syncDirectory } from "./durable-fs.ts";
9
9
  import type { CommandRecord, JournalCommand } from "./journal.ts";
10
10
 
11
- /** Rises whenever an older stand would reject newly written lines; the journal reads only this version. */
12
- export const journalStorageVersion = 7;
13
- const readableJournalStorageVersions: readonly unknown[] = [journalStorageVersion];
11
+ /** Rises whenever an older stand would reject newly written lines; formats 7 and 8 stay readable because they only lack the input origin and the compaction threshold. */
12
+ export const journalStorageVersion = 9;
13
+ const oldestReadableJournalStorageVersion = 7;
14
+ const readableJournalStorageVersions: readonly unknown[] = [oldestReadableJournalStorageVersion, 8, journalStorageVersion];
14
15
  export const journalPayloadThresholdBytes = 4096;
15
16
 
16
17
  type PayloadReference = { sha256: string; bytes: number };
@@ -162,7 +163,7 @@ export const parseJournalRecord = (line: string, runDirectory: string, location:
162
163
 
163
164
  if (!readableJournalStorageVersions.includes(record.formatVersion))
164
165
  throw new Error(`${location} has unsupported journal format version ${String(record.formatVersion)}.${
165
- typeof record.formatVersion === "number" && record.formatVersion < journalStorageVersion
166
+ typeof record.formatVersion === "number" && record.formatVersion < oldestReadableJournalStorageVersion
166
167
  ? " Journals before format 7 hold no model context and are not migrated."
167
168
  : ""}`);
168
169
 
@@ -15,7 +15,6 @@
15
15
  "dependencies": {
16
16
  "@ragents/agent": "workspace:*",
17
17
  "typescript": "^5.9.0",
18
- "typescript-language-server": "6.0.0",
19
18
  "vscode-jsonrpc": "^9.0.2"
20
19
  },
21
20
  "devDependencies": {
@@ -1,4 +1,4 @@
1
- import { existsSync } from "node:fs";
1
+ import { accessSync, constants, existsSync, statSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { getShellConfig } from "@ragents/agent";
4
4
 
@@ -9,6 +9,12 @@ export interface BashLaunch {
9
9
  readonly env: NodeJS.ProcessEnv;
10
10
  }
11
11
 
12
+ /** Was der Executor dem Werkzeug bash mitgibt: die Bash selbst und rg; ohne Angabe gilt jeweils das des Systems. */
13
+ export interface BashPrograms {
14
+ readonly bash: string | undefined;
15
+ readonly rg: string | undefined;
16
+ }
17
+
12
18
  /** Unter Windows sucht RAgents keine Bash; der Executor nennt die mitgebrachte. */
13
19
  const missingWindowsBash = (): Error => new Error(
14
20
  "Unter Windows arbeitet das Werkzeug bash nur mit der Bash, die RAgents mitbringt, und für diesen Executor ist keine genannt. "
@@ -16,17 +22,51 @@ const missingWindowsBash = (): Error => new Error(
16
22
  + "und reicht sie an Arbeitsplatz und lokalen Host weiter; ein Host oder Arbeitsplatz ohne Erweiterung braucht RAGENTS_BASH mit dem Pfad dieser bash.exe.",
17
23
  );
18
24
 
19
- /** Die Windows-Umgebung der Bash: ihr usr/bin vorn im PATH, damit find.exe und sort.exe aus System32 nicht gewinnen; MSYSTEM gehört einer Git-Bash-Anmeldung. */
20
- const windowsBashEnvironment = (env: NodeJS.ProcessEnv, bin: string): NodeJS.ProcessEnv => {
21
- const pathNames = Object.keys(env).filter((name) => name.toUpperCase() === "PATH");
22
- const current = pathNames.map((name) => env[name]).find((value) => value !== undefined && value !== "");
23
- const kept = Object.entries(env).filter(([name]) => !pathNames.includes(name) && name !== "MSYSTEM");
24
- return { ...Object.fromEntries(kept), PATH: current ? `${bin}${path.win32.delimiter}${current}` : bin };
25
+ const missingRipgrep = (rg: string): Error =>
26
+ new Error(`Das rg dieses Executors fehlt: ${rg}. Es gehört zur Erweiterung (dist/rg/<plattform>) oder steht in RAGENTS_RG.`);
27
+
28
+ const pathModule = (platform: NodeJS.Platform): path.PlatformPath => platform === "win32" ? path.win32 : path.posix;
29
+
30
+ /** Die Namen der PATH-Variablen; unter Windows zählt jede Schreibweise (`Path`). */
31
+ const pathNames = (env: NodeJS.ProcessEnv, platform: NodeJS.Platform): readonly string[] =>
32
+ platform === "win32" ? Object.keys(env).filter((name) => name.toUpperCase() === "PATH") : ["PATH"];
33
+
34
+ const currentPath = (env: NodeJS.ProcessEnv, platform: NodeJS.Platform): string | undefined =>
35
+ pathNames(env, platform).map((name) => env[name]).find((value) => value !== undefined && value !== "");
36
+
37
+ /** Die Ordner vorn im PATH, als eine Variable PATH; MSYSTEM gehört unter Windows einer Git-Bash-Anmeldung und fällt weg. */
38
+ const withPathFront = (env: NodeJS.ProcessEnv, directories: readonly string[], platform: NodeJS.Platform): NodeJS.ProcessEnv => {
39
+ if (directories.length === 0) return env;
40
+ const names = pathNames(env, platform);
41
+ const current = currentPath(env, platform);
42
+ const kept = Object.entries(env).filter(([name]) => !names.includes(name) && (platform !== "win32" || name !== "MSYSTEM"));
43
+ const delimiter = pathModule(platform).delimiter;
44
+ return { ...Object.fromEntries(kept), PATH: [...directories, ...(current ? [current] : [])].join(delimiter) };
45
+ };
46
+
47
+ const isExecutableFile = (file: string, platform: NodeJS.Platform): boolean => {
48
+ try {
49
+ if (platform !== "win32") accessSync(file, constants.X_OK);
50
+ return statSync(file).isFile();
51
+ } catch {
52
+ return false;
53
+ }
54
+ };
55
+
56
+ /** Ob die Bash dieses Executors rg findet: das genannte, sonst eines im PATH; ein genanntes, das fehlt, ist ein Fehler. */
57
+ export const ripgrepAvailable = (rg: string | undefined, env: NodeJS.ProcessEnv, platform: NodeJS.Platform = process.platform): boolean => {
58
+ if (rg !== undefined) {
59
+ if (!existsSync(rg)) throw missingRipgrep(rg);
60
+ return true;
61
+ }
62
+ const file = platform === "win32" ? "rg.exe" : "rg";
63
+ return (currentPath(env, platform) ?? "").split(pathModule(platform).delimiter).filter(Boolean)
64
+ .some((directory) => isExecutableFile(path.join(directory, file), platform));
25
65
  };
26
66
 
27
- /** Wie ein Befehl in der Bash dieses Executors startet; die Plattform ist für Prüfungen wählbar. */
67
+ /** Wie ein Befehl in der Bash dieses Executors startet: das Verzeichnis von rg vorn im PATH, unter Windows dahinter das usr/bin der Bash, damit find.exe und sort.exe aus System32 nicht gewinnen. */
28
68
  export const bashLaunch = (
29
- bash: string | undefined,
69
+ { bash, rg }: BashPrograms,
30
70
  command: string,
31
71
  env: NodeJS.ProcessEnv,
32
72
  platform: NodeJS.Platform = process.platform,
@@ -35,11 +75,17 @@ export const bashLaunch = (
35
75
  if (bash !== undefined && !existsSync(bash)) {
36
76
  throw new Error(`Die Bash dieses Executors fehlt: ${bash}. Unter Windows gehört sie zur Erweiterung (dist/bash/<plattform>) oder steht in RAGENTS_BASH.`);
37
77
  }
78
+ if (rg !== undefined && !existsSync(rg)) throw missingRipgrep(rg);
38
79
  const shell = getShellConfig(bash, platform);
39
80
  if (!/bash(\.exe)?$/i.test(shell.shell)) throw new Error(`${shell.shell} ist keine Bash; das Werkzeug heißt bash und der Prompt beschreibt bash`);
81
+ const paths = pathModule(platform);
82
+ const front = [
83
+ ...(rg === undefined ? [] : [paths.dirname(rg)]),
84
+ ...(platform === "win32" ? [paths.dirname(shell.shell)] : []),
85
+ ];
40
86
  return {
41
87
  command: shell.shell,
42
88
  args: [...shell.args, command],
43
- env: platform === "win32" ? windowsBashEnvironment(env, path.win32.dirname(shell.shell)) : env,
89
+ env: withPathFront(env, front, platform),
44
90
  };
45
91
  };
@@ -27,6 +27,8 @@ export interface WorkspaceProcessContext {
27
27
  sandbox?: ProcessSandbox;
28
28
  /** Die Bash, mit der das Werkzeug bash startet; unter Windows Pflicht, sonst gilt ohne Angabe die des Systems. */
29
29
  bash?: string;
30
+ /** Das rg, dessen Ordner das Werkzeug bash vorn im PATH hat; ohne Angabe gilt eines im PATH, falls es eines gibt. */
31
+ rg?: string;
30
32
  }
31
33
 
32
34
  export interface SandboxHomeEnvironment {
@@ -103,6 +105,7 @@ export interface WorkspaceContextOptions {
103
105
  source?: NodeJS.ProcessEnv;
104
106
  baseEnvironment?: BaseEnvironment;
105
107
  bash?: string;
108
+ rg?: string;
106
109
  }
107
110
 
108
111
  const namedEntries = (
@@ -151,5 +154,6 @@ export const workspaceProcessContext = (options: WorkspaceContextOptions): Works
151
154
  ...(options.runOperation === undefined ? {} : { runOperation: options.runOperation }),
152
155
  ...(options.sandbox === undefined ? {} : { sandbox: options.sandbox }),
153
156
  ...(options.bash === undefined ? {} : { bash: options.bash }),
157
+ ...(options.rg === undefined ? {} : { rg: options.rg }),
154
158
  };
155
159
  };