@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schlenkr/ragents",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "RAgents-Host: Server, Engine, Plugins und Werkzeuge ohne Quell-Checkout",
5
5
  "keywords": [
6
6
  "agents",
@@ -70,6 +70,6 @@
70
70
  "yaml": "2.9.0"
71
71
  },
72
72
  "ragents": {
73
- "hostVersion": "765802e9cb0252b2a312b2b1b09a557ef8bf13cc"
73
+ "hostVersion": "f865fbf276b702ad64bbfa1af75e91d7effdcf41"
74
74
  }
75
75
  }
@@ -1,10 +1,5 @@
1
- /** The few knobs of an agent's runtime; everything else is fixed. */
1
+ /** The few knobs of an agent's runtime; compaction belongs to the model, everything else is fixed. */
2
2
  export interface AgentSettings {
3
- compaction: {
4
- enabled: boolean;
5
- reserveTokens: number;
6
- keepRecentTokens: number;
7
- };
8
3
  /** Retries after a retryable model error, with exponential backoff. */
9
4
  retry: {
10
5
  enabled: boolean;
@@ -19,7 +14,6 @@ export interface AgentSettings {
19
14
  };
20
15
  }
21
16
  export interface AgentSettingsInput {
22
- compaction?: Partial<AgentSettings["compaction"]>;
23
17
  retry?: Partial<AgentSettings["retry"]>;
24
18
  providerRequest?: Partial<AgentSettings["providerRequest"]>;
25
19
  }
@@ -1,6 +1,6 @@
1
1
  /** Context compaction over the context log of one agent; pure functions, the caller persists the result. */
2
2
  import type { AgentMessage, StreamFn, ThinkingLevel } from "../../loop/index.ts";
3
- import type { Model, Usage } from "@ragents/ai";
3
+ import type { Model, ModelCompaction, Usage } from "@ragents/ai";
4
4
  import { type ContextLogEntry } from "../context-log.ts";
5
5
  import { type FileOperations } from "./utils.ts";
6
6
  /** Result of compact(); the caller records it as a compaction entry. */
@@ -12,11 +12,17 @@ export interface CompactionResult {
12
12
  readFiles: string[];
13
13
  modifiedFiles: string[];
14
14
  }
15
- export interface CompactionSettings {
16
- enabled: boolean;
17
- reserveTokens: number;
18
- keepRecentTokens: number;
19
- }
15
+ /** The catalog standard of a model without own values: compact 16384 tokens below its context window. */
16
+ export declare function catalogCompaction(model: Pick<Model<any>, "contextWindow">): ModelCompaction;
17
+ /** Where the compaction values of a model come from. */
18
+ export type CompactionSource = "model" | "catalog";
19
+ /** The compaction values an agent applies on a model: its own, else the catalog standard. */
20
+ export declare function compactionOf(model: Model<any>): {
21
+ values: ModelCompaction;
22
+ source: CompactionSource;
23
+ };
24
+ /** Why a value is no valid set of compaction values, or undefined; with a model also whether they fit its context window and output limit. */
25
+ export declare function compactionProblem(value: unknown, model?: Pick<Model<any>, "contextWindow" | "maxTokens">): string | undefined;
20
26
  /**
21
27
  * Calculate total context tokens from usage.
22
28
  * Uses the native totalTokens field when available, falls back to computing from components.
@@ -33,10 +39,8 @@ export interface ContextUsageEstimate {
33
39
  * If there are messages after the last usage, estimate their tokens with estimateTokens.
34
40
  */
35
41
  export declare function estimateContextTokens(messages: AgentMessage[]): ContextUsageEstimate;
36
- /**
37
- * Check if compaction should trigger based on context usage.
38
- */
39
- export declare function shouldCompact(contextTokens: number, contextWindow: number, settings: CompactionSettings): boolean;
42
+ /** Whether a context of this size lies above the compaction threshold. */
43
+ export declare function shouldCompact(contextTokens: number, compaction: ModelCompaction): boolean;
40
44
  /**
41
45
  * Estimate token count for a message using chars/4 heuristic.
42
46
  * This is conservative (overestimates tokens).
@@ -58,7 +62,7 @@ export declare function findCutPoint(entries: ContextLogEntry[], startIndex: num
58
62
  * Generate a summary of the conversation using the LLM.
59
63
  * If previousSummary is provided, uses the update prompt to merge.
60
64
  */
61
- export declare function generateSummary(currentMessages: AgentMessage[], model: Model<any>, reserveTokens: number, apiKey: string | undefined, headers?: Record<string, string>, signal?: AbortSignal, customInstructions?: string, previousSummary?: string, thinkingLevel?: ThinkingLevel, streamFn?: StreamFn, env?: Record<string, string>): Promise<string>;
65
+ export declare function generateSummary(currentMessages: AgentMessage[], model: Model<any>, summaryTokens: number, apiKey: string | undefined, headers?: Record<string, string>, signal?: AbortSignal, customInstructions?: string, previousSummary?: string, thinkingLevel?: ThinkingLevel, streamFn?: StreamFn, env?: Record<string, string>): Promise<string>;
62
66
  export interface CompactionPreparation {
63
67
  /** Id of the first entry to keep */
64
68
  firstKeptId: string;
@@ -73,10 +77,10 @@ export interface CompactionPreparation {
73
77
  previousSummary?: string;
74
78
  /** File operations extracted from messagesToSummarize */
75
79
  fileOps: FileOperations;
76
- settings: CompactionSettings;
80
+ compaction: ModelCompaction;
77
81
  }
78
82
  /** Prepares a compaction of the whole context log; undefined when there is nothing to summarize. */
79
- export declare function prepareCompaction(entries: ContextLogEntry[], settings: CompactionSettings): CompactionPreparation | undefined;
83
+ export declare function prepareCompaction(entries: ContextLogEntry[], compaction: ModelCompaction): CompactionPreparation | undefined;
80
84
  /** Summarizes a prepared compaction; the caller records the result. */
81
85
  export declare function compact(preparation: CompactionPreparation, model: Model<any>, apiKey: string | undefined, headers?: Record<string, string>, customInstructions?: string, signal?: AbortSignal, thinkingLevel?: ThinkingLevel, streamFn?: StreamFn, env?: Record<string, string>): Promise<CompactionResult>;
82
86
  //# sourceMappingURL=compaction.d.ts.map
@@ -1,13 +1,14 @@
1
- import { type Api, type AssistantMessage, type AssistantMessageEventStream, type AuthCheck, type AuthResult, type Context, type Model, type SimpleStreamOptions } from "@ragents/ai";
1
+ import { type Api, type AssistantMessage, type AssistantMessageEventStream, type AuthCheck, type AuthResult, type Context, type Model, type ModelCompaction, type SimpleStreamOptions } from "@ragents/ai";
2
2
  import { type ProviderConfigInput } from "./provider-composer.ts";
3
- /** A name under which a model of another provider is offered; only the alias is visible to callers. */
3
+ /** A name under which a model of another provider is offered, with its own compaction values; only the alias is visible to callers. */
4
4
  export interface ModelAlias {
5
5
  readonly alias: string;
6
6
  readonly upstream: string;
7
7
  readonly model: string;
8
+ readonly compaction: ModelCompaction;
8
9
  }
9
- /** The metadata of the target under the alias as id and name, with the alias provider as provider. */
10
- export declare function aliasedModel(providerId: string, alias: string, target: Model<Api>): Model<Api>;
10
+ /** The metadata of the target under the alias as id and name, with the alias provider as provider and the alias's compaction values. */
11
+ export declare function aliasedModel(providerId: string, entry: ModelAlias, target: Model<Api>): Model<Api>;
11
12
  /** The built-in providers plus the ones registered at runtime; a key comes from the registration or the environment. */
12
13
  export declare class ModelRuntime {
13
14
  private readonly models;
@@ -1,4 +1,4 @@
1
- import { type Api, type InputModality, type Model, type Provider } from "@ragents/ai";
1
+ import { type Api, type InputModality, type Model, type ModelCompaction, type Provider } from "@ragents/ai";
2
2
  /** A provider registered at runtime: its models and a literal API key, over the built-in provider of the same id. */
3
3
  export interface ProviderConfigInput {
4
4
  name?: string;
@@ -17,6 +17,7 @@ export interface ProviderConfigInput {
17
17
  cost: Model<Api>["cost"];
18
18
  contextWindow: number;
19
19
  maxTokens: number;
20
+ compaction?: ModelCompaction;
20
21
  compat?: Model<Api>["compat"];
21
22
  }>;
22
23
  }
@@ -3,11 +3,14 @@ import type { ToolDefinition } from "../tool-definition.ts";
3
3
  import { type TruncationResult } from "./truncate.ts";
4
4
  export declare const BASH_MAX_BYTES: number;
5
5
  export declare const BASH_MAX_LINE_CHARS = 1000;
6
- declare const bashSchema: Type.TObject<{
6
+ export declare const BASH_DEFAULT_TIMEOUT_SECONDS = 120;
7
+ export declare const BASH_MAX_TIMEOUT_SECONDS = 600;
8
+ declare const bashSchemaFor: (defaultTimeoutSeconds: number) => Type.TObject<{
7
9
  command: Type.TString;
8
10
  timeout: Type.TOptional<Type.TNumber>;
9
11
  cwd: Type.TOptional<Type.TString>;
10
12
  }>;
13
+ type BashSchema = ReturnType<typeof bashSchemaFor>;
11
14
  export interface BashToolDetails {
12
15
  truncation?: TruncationResult;
13
16
  fullOutputPath?: string;
@@ -27,7 +30,8 @@ export interface BashOperations {
27
30
  exec: (command: string, cwd: string, options: {
28
31
  onData: (data: Buffer) => void;
29
32
  signal?: AbortSignal;
30
- timeout?: number;
33
+ /** Seconds until the command is stopped. */
34
+ timeout: number;
31
35
  env?: NodeJS.ProcessEnv;
32
36
  }) => Promise<{
33
37
  exitCode: number | null;
@@ -52,7 +56,9 @@ export interface BashToolOptions {
52
56
  shellPath?: string;
53
57
  /** Hook to adjust command, cwd, or env before execution */
54
58
  spawnHook?: BashSpawnHook;
59
+ /** Timeout in seconds for a call without one, at most BASH_MAX_TIMEOUT_SECONDS. Default: BASH_DEFAULT_TIMEOUT_SECONDS */
60
+ defaultTimeoutSeconds?: number;
55
61
  }
56
- export declare function createBashToolDefinition(cwd: string, options?: BashToolOptions): ToolDefinition<typeof bashSchema, BashToolDetails | undefined>;
62
+ export declare function createBashToolDefinition(cwd: string, options?: BashToolOptions): ToolDefinition<BashSchema, BashToolDetails | undefined>;
57
63
  export {};
58
64
  //# sourceMappingURL=bash.d.ts.map
@@ -1,7 +1,7 @@
1
1
  export { aliasedModel, type ModelAlias, ModelRuntime } from "./core/model-runtime.ts";
2
2
  export { type AgentSettings, type AgentSettingsInput, agentSettings } from "./core/agent-settings.ts";
3
3
  export { activeContextEntries, type ContextCompaction, type ContextLogEntry, contextMessages, latestCompaction, } from "./core/context-log.ts";
4
- export { calculateContextTokens, compact, estimateContextTokens, prepareCompaction, shouldCompact, } from "./core/compaction/index.ts";
4
+ export { calculateContextTokens, compact, compactionOf, compactionProblem, type CompactionSource, estimateContextTokens, prepareCompaction, shouldCompact, } from "./core/compaction/index.ts";
5
5
  export { convertToLlm } from "./core/messages.ts";
6
6
  export { formatSkillsForPrompt, type Skill } from "./core/skills.ts";
7
7
  export { defineTool, type ToolDefinition } from "./core/tool-definition.ts";
@@ -1,6 +1,5 @@
1
- /** The few knobs of an agent's runtime; everything else is fixed. */
1
+ /** The few knobs of an agent's runtime; compaction belongs to the model, everything else is fixed. */
2
2
  export interface AgentSettings {
3
- compaction: { enabled: boolean; reserveTokens: number; keepRecentTokens: number };
4
3
  /** Retries after a retryable model error, with exponential backoff. */
5
4
  retry: { enabled: boolean; maxRetries: number; baseDelayMs: number };
6
5
  /** Timeout and retries of a single provider request; unset values leave the SDK defaults. */
@@ -8,13 +7,11 @@ export interface AgentSettings {
8
7
  }
9
8
 
10
9
  export interface AgentSettingsInput {
11
- compaction?: Partial<AgentSettings["compaction"]>;
12
10
  retry?: Partial<AgentSettings["retry"]>;
13
11
  providerRequest?: Partial<AgentSettings["providerRequest"]>;
14
12
  }
15
13
 
16
14
  export const agentSettings = (input: AgentSettingsInput = {}): AgentSettings => ({
17
- compaction: { enabled: true, reserveTokens: 16384, keepRecentTokens: 20000, ...input.compaction },
18
15
  retry: { enabled: true, maxRetries: 3, baseDelayMs: 2000, ...input.retry },
19
16
  providerRequest: { timeoutMs: 300_000, maxRetryDelayMs: 60_000, ...input.providerRequest },
20
17
  });
@@ -1,7 +1,7 @@
1
1
  /** Context compaction over the context log of one agent; pure functions, the caller persists the result. */
2
2
 
3
3
  import type { AgentMessage, StreamFn, ThinkingLevel } from "../../loop/index.ts";
4
- import type { AssistantMessage, Context, Model, SimpleStreamOptions, Usage } from "@ragents/ai";
4
+ import type { AssistantMessage, Context, Model, ModelCompaction, SimpleStreamOptions, Usage } from "@ragents/ai";
5
5
  import { completeSimple } from "@ragents/ai";
6
6
  import { type ContextCompaction, type ContextLogEntry, contextEntryMessages, contextMessages } from "../context-log.ts";
7
7
  import { convertToLlm } from "../messages.ts";
@@ -42,15 +42,62 @@ export interface CompactionResult {
42
42
  }
43
43
 
44
44
  // ============================================================================
45
- // Types
45
+ // Compaction values of a model
46
46
  // ============================================================================
47
47
 
48
- export interface CompactionSettings {
49
- enabled: boolean;
50
- reserveTokens: number;
51
- keepRecentTokens: number;
48
+ const CATALOG_RESERVE_TOKENS = 16384;
49
+
50
+ /** The catalog standard of a model without own values: compact 16384 tokens below its context window. */
51
+ export function catalogCompaction(model: Pick<Model<any>, "contextWindow">): ModelCompaction {
52
+ return {
53
+ threshold: model.contextWindow - CATALOG_RESERVE_TOKENS,
54
+ keepRecentTokens: 20000,
55
+ summaryTokens: Math.floor(0.8 * CATALOG_RESERVE_TOKENS),
56
+ };
57
+ }
58
+
59
+ /** Where the compaction values of a model come from. */
60
+ export type CompactionSource = "model" | "catalog";
61
+
62
+ /** The compaction values an agent applies on a model: its own, else the catalog standard. */
63
+ export function compactionOf(model: Model<any>): { values: ModelCompaction; source: CompactionSource } {
64
+ return model.compaction
65
+ ? { values: model.compaction, source: "model" }
66
+ : { values: catalogCompaction(model), source: "catalog" };
67
+ }
68
+
69
+ const COMPACTION_KEYS: readonly string[] = ["threshold", "keepRecentTokens", "summaryTokens"];
70
+
71
+ /** Why a value is no valid set of compaction values, or undefined; with a model also whether they fit its context window and output limit. */
72
+ export function compactionProblem(
73
+ value: unknown,
74
+ model?: Pick<Model<any>, "contextWindow" | "maxTokens">,
75
+ ): string | undefined {
76
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
77
+ return "compaction needs threshold, keepRecentTokens and summaryTokens";
78
+ }
79
+ const fields = value as Record<string, unknown>;
80
+ const unknown = Object.keys(fields).find((key) => !COMPACTION_KEYS.includes(key));
81
+ if (unknown) return `compaction.${unknown} is not supported`;
82
+ const invalid = COMPACTION_KEYS.find((key) => !Number.isSafeInteger(fields[key]) || (fields[key] as number) < 1);
83
+ if (invalid) return `compaction.${invalid} must be a positive integer`;
84
+ const { threshold, keepRecentTokens, summaryTokens } = value as ModelCompaction;
85
+ if (keepRecentTokens + summaryTokens >= threshold) {
86
+ return `compaction: keepRecentTokens plus summaryTokens (${keepRecentTokens + summaryTokens}) must stay below threshold (${threshold})`;
87
+ }
88
+ if (!model) return undefined;
89
+ if (threshold + summaryTokens >= model.contextWindow) {
90
+ return `compaction: threshold plus summaryTokens (${threshold + summaryTokens}) must stay below the context window (${model.contextWindow})`;
91
+ }
92
+ if (model.maxTokens > 0 && summaryTokens > model.maxTokens) {
93
+ return `compaction.summaryTokens (${summaryTokens}) exceeds the output limit (${model.maxTokens})`;
94
+ }
95
+ return undefined;
52
96
  }
53
97
 
98
+ /** The summary of a split turn's beginning gets five eighths of the summary budget, the ratio the forked runtime used. */
99
+ const turnPrefixTokens = (summaryTokens: number): number => Math.round((summaryTokens * 5) / 8);
100
+
54
101
  // ============================================================================
55
102
  // Token calculation
56
103
  // ============================================================================
@@ -131,12 +178,9 @@ export function estimateContextTokens(messages: AgentMessage[]): ContextUsageEst
131
178
  };
132
179
  }
133
180
 
134
- /**
135
- * Check if compaction should trigger based on context usage.
136
- */
137
- export function shouldCompact(contextTokens: number, contextWindow: number, settings: CompactionSettings): boolean {
138
- if (!settings.enabled) return false;
139
- return contextTokens > contextWindow - settings.reserveTokens;
181
+ /** Whether a context of this size lies above the compaction threshold. */
182
+ export function shouldCompact(contextTokens: number, compaction: ModelCompaction): boolean {
183
+ return contextTokens > compaction.threshold;
140
184
  }
141
185
 
142
186
  // ============================================================================
@@ -420,7 +464,7 @@ async function completeSummarization(
420
464
  export async function generateSummary(
421
465
  currentMessages: AgentMessage[],
422
466
  model: Model<any>,
423
- reserveTokens: number,
467
+ summaryTokens: number,
424
468
  apiKey: string | undefined,
425
469
  headers?: Record<string, string>,
426
470
  signal?: AbortSignal,
@@ -430,10 +474,7 @@ export async function generateSummary(
430
474
  streamFn?: StreamFn,
431
475
  env?: Record<string, string>,
432
476
  ): Promise<string> {
433
- const maxTokens = Math.min(
434
- Math.floor(0.8 * reserveTokens),
435
- model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY,
436
- );
477
+ const maxTokens = Math.min(summaryTokens, model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY);
437
478
 
438
479
  // Use update prompt if we have a previous summary, otherwise initial prompt
439
480
  let basePrompt = previousSummary ? UPDATE_SUMMARIZATION_PROMPT : SUMMARIZATION_PROMPT;
@@ -500,13 +541,13 @@ export interface CompactionPreparation {
500
541
  previousSummary?: string;
501
542
  /** File operations extracted from messagesToSummarize */
502
543
  fileOps: FileOperations;
503
- settings: CompactionSettings;
544
+ compaction: ModelCompaction;
504
545
  }
505
546
 
506
547
  /** Prepares a compaction of the whole context log; undefined when there is nothing to summarize. */
507
548
  export function prepareCompaction(
508
549
  entries: ContextLogEntry[],
509
- settings: CompactionSettings,
550
+ compaction: ModelCompaction,
510
551
  ): CompactionPreparation | undefined {
511
552
  if (entries.length > 0 && entries[entries.length - 1].kind === "compaction") {
512
553
  return undefined;
@@ -530,7 +571,7 @@ export function prepareCompaction(
530
571
 
531
572
  const tokensBefore = estimateContextTokens(contextMessages(entries)).tokens;
532
573
 
533
- const cutPoint = findCutPoint(entries, boundaryStart, boundaryEnd, settings.keepRecentTokens);
574
+ const cutPoint = findCutPoint(entries, boundaryStart, boundaryEnd, compaction.keepRecentTokens);
534
575
  const firstKeptEntry = entries[cutPoint.firstKeptEntryIndex];
535
576
  if (!firstKeptEntry) {
536
577
  return undefined;
@@ -571,7 +612,7 @@ export function prepareCompaction(
571
612
  tokensBefore,
572
613
  previousSummary: previous?.summary,
573
614
  fileOps,
574
- settings,
615
+ compaction,
575
616
  };
576
617
  }
577
618
 
@@ -614,7 +655,7 @@ export async function compact(
614
655
  tokensBefore,
615
656
  previousSummary,
616
657
  fileOps,
617
- settings,
658
+ compaction,
618
659
  } = preparation;
619
660
 
620
661
  // Generate summaries and merge into one
@@ -626,7 +667,7 @@ export async function compact(
626
667
  ? await generateSummary(
627
668
  messagesToSummarize,
628
669
  model,
629
- settings.reserveTokens,
670
+ compaction.summaryTokens,
630
671
  apiKey,
631
672
  headers,
632
673
  signal,
@@ -640,7 +681,7 @@ export async function compact(
640
681
  const turnPrefixResult = await generateTurnPrefixSummary(
641
682
  turnPrefixMessages,
642
683
  model,
643
- settings.reserveTokens,
684
+ turnPrefixTokens(compaction.summaryTokens),
644
685
  apiKey,
645
686
  headers,
646
687
  env,
@@ -655,7 +696,7 @@ export async function compact(
655
696
  summary = await generateSummary(
656
697
  messagesToSummarize,
657
698
  model,
658
- settings.reserveTokens,
699
+ compaction.summaryTokens,
659
700
  apiKey,
660
701
  headers,
661
702
  signal,
@@ -679,7 +720,7 @@ export async function compact(
679
720
  async function generateTurnPrefixSummary(
680
721
  messages: AgentMessage[],
681
722
  model: Model<any>,
682
- reserveTokens: number,
723
+ budgetTokens: number,
683
724
  apiKey: string | undefined,
684
725
  headers?: Record<string, string>,
685
726
  env?: Record<string, string>,
@@ -687,10 +728,7 @@ async function generateTurnPrefixSummary(
687
728
  thinkingLevel?: ThinkingLevel,
688
729
  streamFn?: StreamFn,
689
730
  ): Promise<string> {
690
- const maxTokens = Math.min(
691
- Math.floor(0.5 * reserveTokens),
692
- model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY,
693
- ); // Smaller budget for turn prefix
731
+ const maxTokens = Math.min(budgetTokens, model.maxTokens > 0 ? model.maxTokens : Number.POSITIVE_INFINITY);
694
732
  const llmMessages = convertToLlm(messages);
695
733
  const conversationText = serializeConversation(llmMessages);
696
734
  const promptText = `<conversation>\n${conversationText}\n</conversation>\n\n${TURN_PREFIX_SUMMARIZATION_PROMPT}`;
@@ -9,18 +9,21 @@ import {
9
9
  createModels,
10
10
  lazyStream,
11
11
  type Model,
12
+ type ModelCompaction,
12
13
  type MutableModels,
13
14
  type Provider,
14
15
  type SimpleStreamOptions,
15
16
  } from "@ragents/ai";
16
17
  import { builtinProviders } from "@ragents/ai/providers/all";
18
+ import { compactionProblem } from "./compaction/compaction.ts";
17
19
  import { composeModelProvider, type ProviderConfigInput } from "./provider-composer.ts";
18
20
 
19
- /** A name under which a model of another provider is offered; only the alias is visible to callers. */
21
+ /** A name under which a model of another provider is offered, with its own compaction values; only the alias is visible to callers. */
20
22
  export interface ModelAlias {
21
23
  readonly alias: string;
22
24
  readonly upstream: string;
23
25
  readonly model: string;
26
+ readonly compaction: ModelCompaction;
24
27
  }
25
28
 
26
29
  interface AliasRegistration {
@@ -28,9 +31,9 @@ interface AliasRegistration {
28
31
  readonly aliases: ReadonlyMap<string, ModelAlias>;
29
32
  }
30
33
 
31
- /** The metadata of the target under the alias as id and name, with the alias provider as provider. */
32
- export function aliasedModel(providerId: string, alias: string, target: Model<Api>): Model<Api> {
33
- return { ...target, id: alias, name: alias, provider: providerId };
34
+ /** The metadata of the target under the alias as id and name, with the alias provider as provider and the alias's compaction values. */
35
+ export function aliasedModel(providerId: string, entry: ModelAlias, target: Model<Api>): Model<Api> {
36
+ return { ...target, id: entry.alias, name: entry.alias, provider: providerId, compaction: entry.compaction };
34
37
  }
35
38
 
36
39
  /** The built-in providers plus the ones registered at runtime; a key comes from the registration or the environment. */
@@ -102,6 +105,10 @@ export class ModelRuntime {
102
105
  if (duplicate) throw new Error(`Alias ${duplicate.alias} is registered twice.`);
103
106
  const missing = aliases.find((entry) => !this.models.getModel(entry.upstream, entry.model));
104
107
  if (missing) throw new Error(`Alias ${missing.alias}: model ${missing.upstream}/${missing.model} is not configured.`);
108
+ for (const entry of aliases) {
109
+ const problem = compactionProblem(entry.compaction, this.models.getModel(entry.upstream, entry.model));
110
+ if (problem) throw new Error(`Alias ${entry.alias}: ${problem}.`);
111
+ }
105
112
  this.aliasRegistration = { provider: providerId, aliases: new Map(aliases.map((entry) => [entry.alias, entry])) };
106
113
  }
107
114
 
@@ -110,7 +117,7 @@ export class ModelRuntime {
110
117
  if (!registration) return [];
111
118
  return [...registration.aliases.values()].flatMap((entry) => {
112
119
  const target = this.models.getModel(entry.upstream, entry.model);
113
- return target ? [aliasedModel(registration.provider, entry.alias, target)] : [];
120
+ return target ? [aliasedModel(registration.provider, entry, target)] : [];
114
121
  });
115
122
  }
116
123
 
@@ -7,10 +7,12 @@ import {
7
7
  type InputModality,
8
8
  lazyStream,
9
9
  type Model,
10
+ type ModelCompaction,
10
11
  type Provider,
11
12
  type SimpleStreamOptions,
12
13
  type StreamOptions,
13
14
  } from "@ragents/ai";
15
+ import { compactionProblem } from "./compaction/compaction.ts";
14
16
 
15
17
  /** A provider registered at runtime: its models and a literal API key, over the built-in provider of the same id. */
16
18
  export interface ProviderConfigInput {
@@ -30,6 +32,7 @@ export interface ProviderConfigInput {
30
32
  cost: Model<Api>["cost"];
31
33
  contextWindow: number;
32
34
  maxTokens: number;
35
+ compaction?: ModelCompaction;
33
36
  compat?: Model<Api>["compat"];
34
37
  }>;
35
38
  }
@@ -52,6 +55,8 @@ function registeredModels(
52
55
  }
53
56
  const baseUrl = definition.baseUrl ?? config.baseUrl ?? defaults?.baseUrl;
54
57
  if (!baseUrl) throw new Error(`Provider ${providerId}: "baseUrl" is required when defining custom models.`);
58
+ const problem = definition.compaction === undefined ? undefined : compactionProblem(definition.compaction, definition);
59
+ if (problem) throw new Error(`Provider ${providerId}, model ${definition.id}: ${problem}.`);
55
60
  return {
56
61
  ...definition,
57
62
  api,
@@ -11,29 +11,36 @@ import { DEFAULT_MAX_LINES, formatSize, type TruncationResult } from "./truncate
11
11
 
12
12
  export const BASH_MAX_BYTES = 20 * 1024;
13
13
  export const BASH_MAX_LINE_CHARS = 1000;
14
+ export const BASH_DEFAULT_TIMEOUT_SECONDS = 120;
15
+ export const BASH_MAX_TIMEOUT_SECONDS = 600;
14
16
 
15
- const MAX_TIMEOUT_MS = 2_147_483_647;
16
- const MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;
17
-
18
- function resolveTimeoutMs(timeout: number | undefined): number | undefined {
19
- if (timeout === undefined) return undefined;
17
+ function checkedTimeoutSeconds(timeout: number, label: string): number {
20
18
  if (!Number.isFinite(timeout) || timeout <= 0) {
21
- throw new Error("Invalid timeout: must be a finite number of seconds");
19
+ throw new Error(`Invalid ${label} ${timeout}: must be a positive number of seconds`);
22
20
  }
23
-
24
- const timeoutMs = timeout * 1000;
25
- if (timeoutMs > MAX_TIMEOUT_MS) {
26
- throw new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);
21
+ if (timeout > BASH_MAX_TIMEOUT_SECONDS) {
22
+ throw new Error(`Invalid ${label} ${timeout}: the maximum is ${BASH_MAX_TIMEOUT_SECONDS} seconds`);
27
23
  }
28
- return timeoutMs;
24
+ return timeout;
29
25
  }
30
26
 
31
- const bashSchema = Type.Object({
27
+ const bashSchemaFor = (defaultTimeoutSeconds: number) => Type.Object({
32
28
  command: Type.String({ description: "Bash command to execute" }),
33
- timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (optional, no default timeout)" })),
29
+ timeout: Type.Optional(Type.Number({
30
+ exclusiveMinimum: 0,
31
+ maximum: BASH_MAX_TIMEOUT_SECONDS,
32
+ default: defaultTimeoutSeconds,
33
+ description: `Timeout in seconds (default ${defaultTimeoutSeconds}, maximum ${BASH_MAX_TIMEOUT_SECONDS})`,
34
+ })),
34
35
  cwd: Type.Optional(Type.String({ description: "Folder to run the command in: relative to the working directory or starting with a workspace alias such as @name; defaults to the working directory" })),
35
36
  });
36
37
 
38
+ type BashSchema = ReturnType<typeof bashSchemaFor>;
39
+
40
+ const timeoutNotice = (seconds: number): string => seconds < BASH_MAX_TIMEOUT_SECONDS
41
+ ? `Command stopped after ${seconds} seconds (timeout). Narrow the command, for example search with rg instead of grep -r, or pass a larger timeout, up to ${BASH_MAX_TIMEOUT_SECONDS} seconds.`
42
+ : `Command stopped after ${seconds} seconds, the maximum timeout. Narrow the command, for example search with rg instead of grep -r, or split it into shorter steps.`;
43
+
37
44
 
38
45
  export interface BashToolDetails {
39
46
  truncation?: TruncationResult;
@@ -58,7 +65,8 @@ export interface BashOperations {
58
65
  options: {
59
66
  onData: (data: Buffer) => void;
60
67
  signal?: AbortSignal;
61
- timeout?: number;
68
+ /** Seconds until the command is stopped. */
69
+ timeout: number;
62
70
  env?: NodeJS.ProcessEnv;
63
71
  },
64
72
  ) => Promise<{ exitCode: number | null }>;
@@ -68,7 +76,6 @@ export interface BashOperations {
68
76
  export function createLocalBashOperations(options?: { shellPath?: string }): BashOperations {
69
77
  return {
70
78
  exec: async (command, cwd, { onData, signal, timeout, env }) => {
71
- const timeoutMs = resolveTimeoutMs(timeout);
72
79
  if (signal?.aborted) {
73
80
  throw new Error("aborted");
74
81
  }
@@ -87,19 +94,15 @@ export function createLocalBashOperations(options?: { shellPath?: string }): Bas
87
94
  windowsHide: true,
88
95
  });
89
96
  let timedOut = false;
90
- let timeoutHandle: NodeJS.Timeout | undefined;
91
97
  const onAbort = () => {
92
98
  if (child.pid) killProcessTree(child.pid);
93
99
  };
100
+ const timeoutHandle = setTimeout(() => {
101
+ timedOut = true;
102
+ if (child.pid) killProcessTree(child.pid);
103
+ }, timeout * 1000);
94
104
 
95
105
  try {
96
- // Set timeout if provided.
97
- if (timeoutMs !== undefined) {
98
- timeoutHandle = setTimeout(() => {
99
- timedOut = true;
100
- if (child.pid) killProcessTree(child.pid);
101
- }, timeoutMs);
102
- }
103
106
  // Stream stdout and stderr.
104
107
  child.stdout?.on("data", onData);
105
108
  child.stderr?.on("data", onData);
@@ -119,7 +122,7 @@ export function createLocalBashOperations(options?: { shellPath?: string }): Bas
119
122
  }
120
123
  return { exitCode };
121
124
  } finally {
122
- if (timeoutHandle) clearTimeout(timeoutHandle);
125
+ clearTimeout(timeoutHandle);
123
126
  if (signal) signal.removeEventListener("abort", onAbort);
124
127
  }
125
128
  },
@@ -148,6 +151,8 @@ export interface BashToolOptions {
148
151
  shellPath?: string;
149
152
  /** Hook to adjust command, cwd, or env before execution */
150
153
  spawnHook?: BashSpawnHook;
154
+ /** Timeout in seconds for a call without one, at most BASH_MAX_TIMEOUT_SECONDS. Default: BASH_DEFAULT_TIMEOUT_SECONDS */
155
+ defaultTimeoutSeconds?: number;
151
156
  }
152
157
 
153
158
  const BASH_UPDATE_THROTTLE_MS = 100;
@@ -155,21 +160,23 @@ const BASH_UPDATE_THROTTLE_MS = 100;
155
160
  export function createBashToolDefinition(
156
161
  cwd: string,
157
162
  options?: BashToolOptions,
158
- ): ToolDefinition<typeof bashSchema, BashToolDetails | undefined> {
163
+ ): ToolDefinition<BashSchema, BashToolDetails | undefined> {
159
164
  const ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });
160
165
  const commandPrefix = options?.commandPrefix;
161
166
  const spawnHook = options?.spawnHook;
167
+ const defaultTimeoutSeconds = checkedTimeoutSeconds(options?.defaultTimeoutSeconds ?? BASH_DEFAULT_TIMEOUT_SECONDS, "default timeout");
162
168
  return {
163
169
  name: "bash",
164
170
  label: "bash",
165
- description: `Execute a bash command in the working directory, or in the folder given as cwd. Returns stdout and stderr; a nonzero exit code is reported at the end of the result (for example grep without a match), not as a tool error. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${BASH_MAX_BYTES / 1024}KB (whichever is hit first), and lines longer than ${BASH_MAX_LINE_CHARS} characters are shortened. If anything was cut, the full output is saved to a temp file. Optionally provide a timeout in seconds.`,
166
- parameters: bashSchema,
171
+ description: `Execute a bash command in the working directory, or in the folder given as cwd. Returns stdout and stderr; a nonzero exit code is reported at the end of the result (for example grep without a match), not as a tool error. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${BASH_MAX_BYTES / 1024}KB (whichever is hit first), and lines longer than ${BASH_MAX_LINE_CHARS} characters are shortened. If anything was cut, the full output is saved to a temp file. A command is stopped after ${defaultTimeoutSeconds} seconds unless you pass a larger timeout (at most ${BASH_MAX_TIMEOUT_SECONDS} seconds); builds, test runs, installs and other long commands need one.`,
172
+ parameters: bashSchemaFor(defaultTimeoutSeconds),
167
173
  async execute(
168
174
  _toolCallId,
169
175
  { command, timeout, cwd: folder }: { command: string; timeout?: number; cwd?: string },
170
176
  signal?: AbortSignal,
171
177
  onUpdate?,
172
178
  ) {
179
+ const timeoutSeconds = timeout === undefined ? defaultTimeoutSeconds : checkedTimeoutSeconds(timeout, "timeout");
173
180
  const resolvedCommand = commandPrefix ? `${commandPrefix}\n${command}` : command;
174
181
  const spawnContext = resolveSpawnContext(resolvedCommand, folder === undefined ? cwd : resolvePath(folder, cwd), spawnHook);
175
182
  const output = new OutputAccumulator({ maxBytes: BASH_MAX_BYTES, maxLineChars: BASH_MAX_LINE_CHARS, tempFilePrefix: "agent-bash" });
@@ -265,7 +272,7 @@ export function createBashToolDefinition(
265
272
  const result = await ops.exec(spawnContext.command, spawnContext.cwd, {
266
273
  onData: handleData,
267
274
  signal,
268
- timeout,
275
+ timeout: timeoutSeconds,
269
276
  env: spawnContext.env,
270
277
  });
271
278
  exitCode = result.exitCode;
@@ -276,8 +283,7 @@ export function createBashToolDefinition(
276
283
  throw new Error(appendStatus(text, "Command aborted"));
277
284
  }
278
285
  if (err instanceof Error && err.message.startsWith("timeout:")) {
279
- const timeoutSecs = err.message.split(":")[1];
280
- throw new Error(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`));
286
+ throw new Error(appendStatus(text, timeoutNotice(timeoutSeconds)));
281
287
  }
282
288
  throw err;
283
289
  }
@@ -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,