@schlenkr/ragents 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/apps/server/package.json +1 -1
  2. package/apps/server/src/access-projection.ts +6 -36
  3. package/apps/server/src/api/contracts.ts +5 -2
  4. package/apps/server/src/api/core-methods.ts +5 -3
  5. package/apps/server/src/config-definition.ts +24 -2
  6. package/apps/server/src/config-file.ts +1 -1
  7. package/apps/server/src/config.ts +0 -6
  8. package/apps/server/src/host-api.json +5 -10
  9. package/apps/server/src/host-api.ts +6 -7
  10. package/apps/server/src/plugin-build/build.ts +28 -5
  11. package/apps/server/src/plugin-build/plugin-description.ts +1 -1
  12. package/apps/server/src/plugin-build/typecheck.ts +2 -2
  13. package/apps/server/src/plugin-support/language-server/plugin.ts +8 -7
  14. package/apps/server/src/plugin-support/language-server/tools.ts +7 -6
  15. package/apps/server/src/plugin-support/model-aliases.ts +55 -25
  16. package/apps/server/src/plugin-support/process-sandbox.ts +16 -15
  17. package/apps/server/src/plugin-support/product-relay.ts +10 -2
  18. package/apps/server/src/plugin-support/workspace-sandbox-host.ts +18 -9
  19. package/apps/server/src/profile/bundle-manifest.ts +7 -3
  20. package/apps/server/src/profile/compose.ts +6 -0
  21. package/apps/server/src/profile/plugin-discovery.ts +6 -1
  22. package/apps/server/src/profile/provisioning.ts +6 -5
  23. package/apps/server/src/provider.ts +2 -2
  24. package/apps/server/src/ragents/engine.ts +2 -2
  25. package/apps/server/src/ragents/global-chat.ts +7 -1
  26. package/apps/server/src/ragents/host-services.ts +4 -0
  27. package/apps/server/src/ragents/session.ts +1 -1
  28. package/apps/server/src/server.ts +4 -1
  29. package/apps/server/tsconfig.json +2 -0
  30. package/apps/web/dist/assets/{highlighted-body-KPVGNVTW-xXXhTDbh.js → highlighted-body-KPVGNVTW-BO4wLQtE.js} +1 -1
  31. package/apps/web/dist/assets/main-DnnqWHkH.js +11 -0
  32. package/apps/web/dist/assets/mermaid-HWGCJPDP-BLLeth5_.js +568 -0
  33. package/apps/web/dist/assets/{runPanel-ZT6TPDLM.js → runPanel-DPm7s3KM.js} +2 -2
  34. package/apps/web/dist/help/guide-clients.html +26 -8
  35. package/apps/web/dist/help/guide-clients.md +27 -8
  36. package/apps/web/dist/help/guide-distributed.html +12 -4
  37. package/apps/web/dist/help/guide-distributed.md +12 -4
  38. package/apps/web/dist/help/guide-getting-started.html +12 -8
  39. package/apps/web/dist/help/guide-getting-started.md +12 -8
  40. package/apps/web/dist/help/guide-plugins.html +6 -2
  41. package/apps/web/dist/help/guide-plugins.md +6 -2
  42. package/apps/web/dist/help/guide-runtime.html +2 -1
  43. package/apps/web/dist/help/guide-runtime.md +2 -1
  44. package/apps/web/dist/help/index.html +4328 -1132
  45. package/apps/web/dist/help/mini-app.css +24 -0
  46. package/apps/web/dist/host-web.json +4 -4
  47. package/apps/web/dist/index.html +2 -2
  48. package/apps/web/dist/run-panel.html +2 -2
  49. package/apps/web/package.json +1 -1
  50. package/apps/web/src/panel/ConnectionsPage.tsx +3 -0
  51. package/apps/web/src/panel/StartPage.tsx +3 -0
  52. package/apps/web/src/panel/contract.ts +7 -0
  53. package/bundles/ragents.activity/ragents-bundle.json +3 -3
  54. package/bundles/ragents.actor-programs/ragents-bundle.json +4 -4
  55. package/bundles/ragents.actor-programs/server/index.js +31 -3
  56. package/bundles/ragents.actor-programs/server/index.js.map +4 -4
  57. package/bundles/ragents.actor-programs/web/index.js.map +1 -1
  58. package/bundles/ragents.ask/ragents-bundle.json +4 -6
  59. package/bundles/ragents.ask/server/index.js +42 -11
  60. package/bundles/ragents.ask/server/index.js.map +3 -3
  61. package/bundles/ragents.ask/web/chunks/{chunk-V7W3QDVS.js → chunk-MBRPS477.js} +2 -2
  62. package/bundles/ragents.ask/web/chunks/{chunk-V7W3QDVS.js.map → chunk-MBRPS477.js.map} +4 -4
  63. package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js +2 -0
  64. package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js.map +7 -0
  65. package/bundles/ragents.ask/web/classes.json +1 -1
  66. package/bundles/ragents.ask/web/exports/ask-payload.js +1 -1
  67. package/bundles/ragents.ask/web/exports/web/QuestionCard.js +1 -1
  68. package/bundles/ragents.ask/web/index.js +1 -1
  69. package/bundles/ragents.ask/web/index.js.map +4 -4
  70. package/bundles/ragents.browser/executor/index.mjs +357 -0
  71. package/bundles/ragents.browser/executor/index.mjs.map +7 -0
  72. package/bundles/ragents.browser/ragents-bundle.json +5 -8
  73. package/bundles/ragents.browser/server/chunks/{chunk-2GKLLCBE.js → chunk-CWLX4VA5.js} +1 -1
  74. package/bundles/ragents.browser/server/chunks/chunk-CWLX4VA5.js.map +7 -0
  75. package/bundles/ragents.browser/server/exports/server/contract.js +1 -1
  76. package/bundles/ragents.browser/server/index.js +20 -9
  77. package/bundles/ragents.browser/server/index.js.map +4 -4
  78. package/bundles/ragents.documents/ragents-bundle.json +3 -3
  79. package/bundles/ragents.lsp-fsharp/executor/index.mjs +68 -0
  80. package/bundles/ragents.lsp-fsharp/executor/index.mjs.map +7 -0
  81. package/bundles/ragents.lsp-fsharp/ragents-bundle.json +5 -9
  82. package/bundles/ragents.lsp-fsharp/server/index.js +21 -6
  83. package/bundles/ragents.lsp-fsharp/server/index.js.map +4 -4
  84. package/bundles/ragents.lsp-roslyn/executor/index.mjs +68 -0
  85. package/bundles/ragents.lsp-roslyn/executor/index.mjs.map +7 -0
  86. package/bundles/ragents.lsp-roslyn/ragents-bundle.json +6 -8
  87. package/bundles/ragents.lsp-roslyn/server/index.js +54 -15
  88. package/bundles/ragents.lsp-roslyn/server/index.js.map +4 -4
  89. package/bundles/ragents.lsp-typescript/executor/index.mjs +49 -0
  90. package/bundles/ragents.lsp-typescript/executor/index.mjs.map +7 -0
  91. package/bundles/ragents.lsp-typescript/ragents-bundle.json +5 -7
  92. package/bundles/ragents.lsp-typescript/server/index.js +22 -2
  93. package/bundles/ragents.lsp-typescript/server/index.js.map +3 -3
  94. package/bundles/ragents.model-relay/ragents-bundle.json +4 -4
  95. package/bundles/ragents.model-relay/server/index.js +3 -2
  96. package/bundles/ragents.model-relay/server/index.js.map +2 -2
  97. package/bundles/ragents.orchestration/ragents-bundle.json +4 -4
  98. package/bundles/ragents.orchestration/web/classes.json +1 -1
  99. package/bundles/ragents.orchestration/web/index.js +6 -6
  100. package/bundles/ragents.orchestration/web/index.js.map +3 -3
  101. package/bundles/ragents.overseer/ragents-bundle.json +4 -4
  102. package/bundles/ragents.overseer/server/index.js +1 -1
  103. package/bundles/ragents.overseer/server/index.js.map +2 -2
  104. package/bundles/ragents.processes/ragents-bundle.json +3 -3
  105. package/bundles/ragents.product/ragents-bundle.json +3 -3
  106. package/bundles/ragents.profile-distribution/ragents-bundle.json +4 -4
  107. package/bundles/ragents.profile-distribution/server/index.js +1 -1
  108. package/bundles/ragents.profile-distribution/server/index.js.map +2 -2
  109. package/bundles/ragents.reference/ragents-bundle.json +3 -3
  110. package/bundles/ragents.todo/ragents-bundle.json +3 -3
  111. package/bundles/ragents.transcript/ragents-bundle.json +3 -3
  112. package/bundles/ragents.watch/ragents-bundle.json +3 -3
  113. package/bundles/ragents.workspace/ragents-bundle.json +11 -5
  114. package/bundles/ragents.workspace/server/index.js +105 -30
  115. package/bundles/ragents.workspace/server/index.js.map +3 -3
  116. package/bundles/ragents.workspace/web/classes.json +1 -1
  117. package/bundles/ragents.workspace/web/index.js +1 -1
  118. package/bundles/ragents.workspace/web/index.js.map +3 -3
  119. package/package.json +3 -3
  120. package/packages/agent/dist/core/agent-settings.d.ts +1 -7
  121. package/packages/agent/dist/core/compaction/compaction.d.ts +17 -13
  122. package/packages/agent/dist/core/model-runtime.d.ts +5 -4
  123. package/packages/agent/dist/core/provider-composer.d.ts +2 -1
  124. package/packages/agent/dist/core/tools/bash.d.ts +9 -3
  125. package/packages/agent/dist/index.d.ts +1 -1
  126. package/packages/agent/src/core/agent-settings.ts +1 -4
  127. package/packages/agent/src/core/compaction/compaction.ts +68 -30
  128. package/packages/agent/src/core/model-runtime.ts +12 -5
  129. package/packages/agent/src/core/provider-composer.ts +5 -0
  130. package/packages/agent/src/core/tools/bash.ts +36 -30
  131. package/packages/agent/src/index.ts +3 -0
  132. package/packages/ai/dist/types.d.ts +11 -0
  133. package/packages/ai/src/types.ts +12 -0
  134. package/packages/ragents/src/domain/event-semantics.ts +3 -0
  135. package/packages/ragents/src/domain/event-validation.ts +15 -2
  136. package/packages/ragents/src/domain/events.ts +3 -0
  137. package/packages/ragents/src/domain/model.ts +2 -0
  138. package/packages/ragents/src/domain/projection.ts +1 -0
  139. package/packages/ragents/src/drivers/agent-runtime.ts +1 -1
  140. package/packages/ragents/src/drivers/agent-turn.ts +8 -6
  141. package/packages/ragents/src/index.ts +1 -0
  142. package/packages/ragents/src/plugin-host.ts +46 -0
  143. package/packages/ragents/src/plugin-types.ts +19 -0
  144. package/packages/ragents/src/runtime/decisions/inputs.ts +6 -2
  145. package/packages/ragents/src/runtime/journal-storage.ts +5 -4
  146. package/packages/workspace-executor/package.json +0 -1
  147. package/packages/workspace-executor/src/bash-launch.ts +56 -10
  148. package/packages/workspace-executor/src/context.ts +4 -0
  149. package/packages/workspace-executor/src/contributions.ts +150 -0
  150. package/packages/workspace-executor/src/executor.ts +1 -1
  151. package/packages/workspace-executor/src/index.ts +16 -19
  152. package/packages/workspace-executor/src/language-server/host.ts +3 -0
  153. package/packages/workspace-executor/src/language-server/module.ts +12 -2
  154. package/packages/workspace-executor/src/language-server/roots.ts +0 -5
  155. package/packages/workspace-executor/src/managed-process.ts +28 -0
  156. package/packages/workspace-executor/src/modules.ts +7 -6
  157. package/packages/workspace-executor/src/sandbox-tools.ts +12 -12
  158. package/packages/workspace-executor/src/shell-platform.ts +14 -3
  159. package/packages/workspace-executor/src/tools.ts +0 -3
  160. package/plugins/ragents.actor-programs/server/access-projections.ts +31 -0
  161. package/plugins/ragents.actor-programs/server/index.ts +2 -0
  162. package/plugins/ragents.ask/ask-payload.ts +9 -0
  163. package/plugins/ragents.ask/server/ask-service.ts +53 -13
  164. package/plugins/ragents.ask/web/AskActionView.tsx +3 -2
  165. package/plugins/ragents.ask/web/index.tsx +0 -6
  166. package/plugins/ragents.browser/executor/index.ts +5 -0
  167. package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/module.ts +25 -31
  168. package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/playwright.ts +9 -23
  169. package/plugins/ragents.browser/provision.ts +1 -1
  170. package/plugins/ragents.browser/server/browser.ts +3 -3
  171. package/plugins/ragents.browser/server/contract.ts +2 -2
  172. package/plugins/ragents.browser/server/index.ts +1 -1
  173. package/plugins/ragents.lsp-fsharp/executor.ts +80 -0
  174. package/plugins/ragents.lsp-fsharp/provision.ts +1 -1
  175. package/plugins/ragents.lsp-fsharp/server/index.ts +2 -2
  176. package/plugins/ragents.lsp-roslyn/executor.ts +64 -0
  177. package/plugins/ragents.lsp-roslyn/provision.ts +1 -1
  178. package/plugins/ragents.lsp-roslyn/server/index.ts +28 -5
  179. package/plugins/ragents.lsp-roslyn/server/solution-on-start.ts +18 -6
  180. package/plugins/ragents.lsp-typescript/executor.ts +50 -0
  181. package/plugins/ragents.lsp-typescript/server/index.ts +2 -2
  182. package/plugins/ragents.model-relay/server/config.ts +1 -1
  183. package/plugins/ragents.model-relay/server/relay.ts +4 -2
  184. package/plugins/ragents.orchestration/web/ActorTile.tsx +1 -1
  185. package/plugins/ragents.orchestration/web/run-panel/RunPanel.tsx +8 -7
  186. package/plugins/ragents.orchestration/web/run-panel/sheet-status.ts +3 -3
  187. package/plugins/ragents.overseer/server/api.ts +2 -2
  188. package/plugins/ragents.profile-distribution/server/archive.ts +2 -1
  189. package/plugins/ragents.workspace/client/workspace-client.ts +128 -32
  190. package/plugins/ragents.workspace/contract.ts +33 -2
  191. package/plugins/ragents.workspace/server/binding.ts +10 -4
  192. package/plugins/ragents.workspace/server/clients.ts +56 -9
  193. package/plugins/ragents.workspace/server/config.ts +12 -0
  194. package/plugins/ragents.workspace/server/index.ts +15 -5
  195. package/plugins/ragents.workspace/server/runtime.ts +10 -0
  196. package/plugins/ragents.workspace/server/shell-platform.ts +6 -5
  197. package/plugins/ragents.workspace/web/WorkspaceBinding.tsx +1 -1
  198. package/ragents.config.core.ts +4 -1
  199. package/ragents.config.showcase.ts +4 -1
  200. package/scripts/agent/journal.ts +3 -1
  201. package/scripts/package/build-package.ts +3 -1
  202. package/scripts/provision/run-provision.ts +2 -2
  203. package/scripts/workspace-client/run-workspace-client.ts +7 -4
  204. package/apps/web/dist/assets/main-C7JeZFjs.js +0 -6
  205. package/apps/web/dist/assets/mermaid-HWGCJPDP-DfsU5ofC.js +0 -478
  206. package/apps/web/dist/help/screenshots/release-checklist-vscode.png +0 -0
  207. package/apps/web/dist/help/screenshots/release-checklist-web.png +0 -0
  208. package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js +0 -2
  209. package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js.map +0 -7
  210. package/bundles/ragents.browser/server/chunks/chunk-2GKLLCBE.js.map +0 -7
  211. package/packages/workspace-executor/src/language-server/adapters/fsharp.ts +0 -60
  212. package/packages/workspace-executor/src/language-server/adapters/index.ts +0 -7
  213. package/packages/workspace-executor/src/language-server/adapters/roslyn.ts +0 -56
  214. package/packages/workspace-executor/src/language-server/adapters/typescript.ts +0 -74
  215. package/packages/workspace-executor/src/language-server/solution.ts +0 -13
  216. package/plugins/ragents.ask/web/QuestionSection.tsx +0 -48
  217. /package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/contract.ts +0 -0
  218. /package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/pages.ts +0 -0
@@ -11,6 +11,9 @@ export {
11
11
  export {
12
12
  calculateContextTokens,
13
13
  compact,
14
+ compactionOf,
15
+ compactionProblem,
16
+ type CompactionSource,
14
17
  estimateContextTokens,
15
18
  prepareCompaction,
16
19
  shouldCompact,
@@ -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"
@@ -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;
@@ -17,6 +17,7 @@ export {
17
17
  type LiveBusOptions,
18
18
  } from "./agents/live.ts";
19
19
  export {
20
+ AccessProjectionRegistry,
20
21
  ConfigContributionRegistry,
21
22
  HttpContributionRegistry,
22
23
  MethodContributionRegistry,
@@ -11,7 +11,9 @@ import type { AgentProfile, CatalogModel } from "./agents/catalog.ts";
11
11
  import type { ToolContributor } from "./agents/plugins.ts";
12
12
  import { describeToolAvailability, type RunFunction } from "./agents/tools.ts";
13
13
  import type { ChannelContribution, ChannelDescriptor, MethodContribution, MethodDescriptor } from "./rpc/contribution.ts";
14
+ import type { PluginState } from "./domain/model.ts";
14
15
  import type {
16
+ AccessProjectionContribution,
15
17
  AgentAudience,
16
18
  HttpRouteContribution,
17
19
  OperationContext,
@@ -19,6 +21,7 @@ import type {
19
21
  RegisteredOperationDescriptor,
20
22
  AgentContribution,
21
23
  AgentContributionContext,
24
+ PluginChatEvent,
22
25
  PluginConfigDescriptor,
23
26
  PluginManifest,
24
27
  PluginRegistration,
@@ -977,6 +980,47 @@ export class StartOptionContributionRegistry {
977
980
  }
978
981
  }
979
982
 
983
+ /** Was ein Zugang ohne runs.inspect von Plugin-Zuständen sieht: eine Startoption nur mit ihren Rechten, sonst die Projektion ihres Plugins, ohne sie alles. */
984
+ export class AccessProjectionRegistry {
985
+ readonly #projections = new ContributionRegistry<AccessProjectionContribution>("Zugriffsprojektion");
986
+ readonly #startOptions: StartOptionContributionRegistry;
987
+
988
+ constructor(startOptions = new StartOptionContributionRegistry()) {
989
+ this.#startOptions = startOptions;
990
+ }
991
+
992
+ register(owner: string, contributions: readonly AccessProjectionContribution[]): void {
993
+ for (const contribution of contributions) {
994
+ for (const name of ["state", "chatEvent"] as const) {
995
+ if (typeof contribution[name] !== "function") throw new Error(`Zugriffsprojektion ${contribution.id} hat kein ${name}`);
996
+ }
997
+ }
998
+ this.#projections.register(owner, contributions);
999
+ }
1000
+
1001
+ state(entry: PluginState, access: Pick<AccessContext, "can">): PluginState | undefined {
1002
+ if (access.can("runs.inspect")) return entry;
1003
+ if (this.#startOptions.missingRight(entry.pluginId, access) !== undefined) return undefined;
1004
+ const projection = this.#find(entry.pluginId);
1005
+ if (!projection) return entry;
1006
+ const state = projection.state(entry);
1007
+ return state === undefined ? undefined : { ...entry, state };
1008
+ }
1009
+
1010
+ chatEvent<T extends PluginChatEvent>(pluginId: string, event: T, access: Pick<AccessContext, "can">): T | undefined {
1011
+ if (access.can("runs.inspect")) return event;
1012
+ if (this.#startOptions.missingRight(pluginId, access) !== undefined) return undefined;
1013
+ const projection = this.#find(pluginId);
1014
+ if (!projection) return event;
1015
+ const projected = projection.chatEvent(event.payload === undefined ? { type: event.type } : { type: event.type, payload: event.payload });
1016
+ return projected === undefined ? undefined : { ...event, type: projected.type, payload: projected.payload };
1017
+ }
1018
+
1019
+ #find(id: string): AccessProjectionContribution | undefined {
1020
+ return this.#projections.entries().find(({ value }) => value.id === id)?.value;
1021
+ }
1022
+ }
1023
+
980
1024
  export class ConfigContributionRegistry {
981
1025
  readonly #entries: Array<{ owners: string[]; value: PluginConfigDescriptor }> = [];
982
1026
 
@@ -1125,6 +1169,7 @@ export class PluginHost {
1125
1169
  readonly lifecycle = new LifecycleContributionRegistry();
1126
1170
  readonly sessionMetadata = new SessionMetadataContributionRegistry();
1127
1171
  readonly startOptions = new StartOptionContributionRegistry();
1172
+ readonly accessProjections = new AccessProjectionRegistry(this.startOptions);
1128
1173
  readonly config = new ConfigContributionRegistry();
1129
1174
  readonly storage: StorageRegistry;
1130
1175
  readonly #product: ProductDescriptor;
@@ -1176,6 +1221,7 @@ export class PluginHost {
1176
1221
  skills: (...entries) => this.skills.register(manifest.id, entries),
1177
1222
  startEntries: (...entries) => this.startEntries.register(manifest.id, entries),
1178
1223
  startOptions: (...entries) => this.startOptions.register(manifest.id, entries),
1224
+ accessProjections: (...entries) => this.accessProjections.register(manifest.id, entries),
1179
1225
  functions: (...entries) => this.tools.registerFunctions(manifest.id, entries),
1180
1226
  script: (...entries) => this.script.register(manifest.id, entries),
1181
1227
  };
@@ -8,6 +8,7 @@ import type { WorkspaceToolNaming } from "./agents/workspace-tools.ts";
8
8
  import type { Orchestration } from "./runtime/orchestration.ts";
9
9
  import type { AgentDriver } from "./drivers/types.ts";
10
10
  import type { JsonValue } from "./domain/json.ts";
11
+ import type { PluginState } from "./domain/model.ts";
11
12
  import type { AccessContext } from "./access.ts";
12
13
  import type { ChannelContribution, MethodContribution } from "./rpc/contribution.ts";
13
14
 
@@ -370,6 +371,22 @@ export interface RegisteredStartOption {
370
371
  option: StartOptionContribution;
371
372
  }
372
373
 
374
+ /** Ein Chat-Ereignis zu einem Plugin-Zustand, ohne die Kennung, die die Projektion schon nennt. */
375
+ export interface PluginChatEvent {
376
+ type: string;
377
+ payload?: unknown;
378
+ }
379
+
380
+ /** Was ein Zugang ohne runs.inspect von einem Plugin-Zustand und dessen Chat-Ereignissen sieht; ohne Projektion sieht er beides unverändert. */
381
+ export interface AccessProjectionContribution {
382
+ /** Die Kennung des Zustands, wie sie im Journal (`pluginId`) und im Chat-Ereignis steht. */
383
+ id: string;
384
+ /** Der sichtbare Zustand; undefined nimmt ihn aus der Run-Ansicht. */
385
+ state: (entry: PluginState) => JsonValue | undefined;
386
+ /** Das sichtbare Chat-Ereignis; undefined unterdrückt es. */
387
+ chatEvent: (event: PluginChatEvent) => PluginChatEvent | undefined;
388
+ }
389
+
373
390
  export interface SessionMetadataContribution {
374
391
  id: string;
375
392
  /** Ob `describe` den Arbeitsbereich des Runs erreicht; dann ruft der Host es nur für Aufrufer, die ihn erreichen dürfen. */
@@ -432,6 +449,8 @@ export interface PluginRegistration {
432
449
  skills: (...contributions: readonly SkillContribution[]) => void;
433
450
  startEntries: (...contributions: readonly StartEntryContribution[]) => void;
434
451
  startOptions: (...contributions: readonly StartOptionContribution[]) => void;
452
+ /** Wie Zugänge ohne runs.inspect die Zustände dieses Plugins und ihre Chat-Ereignisse sehen, je Kennung eines Zustands. */
453
+ accessProjections: (...contributions: readonly AccessProjectionContribution[]) => void;
435
454
  functions: (...functions: readonly (RunFunction | ToolContributor)[]) => void;
436
455
  script: (...contributions: readonly ScriptContribution[]) => void;
437
456
  }
@@ -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
  };