@schlenkr/ragents 0.1.8 → 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.
- package/apps/server/src/config-definition.ts +24 -2
- package/apps/server/src/config.ts +0 -6
- package/apps/server/src/host-api.json +3 -10
- package/apps/server/src/host-api.ts +6 -7
- package/apps/server/src/plugin-build/build.ts +28 -5
- package/apps/server/src/plugin-build/plugin-description.ts +1 -1
- package/apps/server/src/plugin-build/typecheck.ts +2 -2
- package/apps/server/src/plugin-support/language-server/plugin.ts +8 -7
- package/apps/server/src/plugin-support/language-server/tools.ts +7 -6
- package/apps/server/src/plugin-support/model-aliases.ts +55 -25
- package/apps/server/src/plugin-support/product-relay.ts +10 -2
- package/apps/server/src/plugin-support/workspace-sandbox-host.ts +18 -9
- package/apps/server/src/profile/bundle-manifest.ts +7 -3
- package/apps/server/src/profile/compose.ts +6 -0
- package/apps/server/src/profile/plugin-discovery.ts +6 -1
- package/apps/server/src/profile/provisioning.ts +6 -5
- package/apps/server/src/ragents/engine.ts +0 -1
- package/apps/server/src/ragents/host-services.ts +4 -0
- package/apps/server/src/ragents/session.ts +1 -1
- package/apps/server/src/server.ts +1 -0
- package/apps/server/tsconfig.json +2 -0
- package/apps/web/dist/help/guide-clients.html +14 -6
- package/apps/web/dist/help/guide-clients.md +14 -6
- package/apps/web/dist/help/guide-distributed.html +12 -4
- package/apps/web/dist/help/guide-distributed.md +12 -4
- package/apps/web/dist/help/guide-getting-started.html +12 -8
- package/apps/web/dist/help/guide-getting-started.md +12 -8
- package/apps/web/dist/help/guide-plugins.html +6 -2
- package/apps/web/dist/help/guide-plugins.md +6 -2
- package/apps/web/dist/help/guide-runtime.html +2 -1
- package/apps/web/dist/help/guide-runtime.md +2 -1
- package/apps/web/dist/help/index.html +3328 -1135
- package/apps/web/dist/host-web.json +3 -3
- package/bundles/ragents.activity/ragents-bundle.json +3 -3
- package/bundles/ragents.actor-programs/ragents-bundle.json +3 -3
- package/bundles/ragents.ask/ragents-bundle.json +4 -4
- package/bundles/ragents.ask/server/index.js +42 -11
- package/bundles/ragents.ask/server/index.js.map +3 -3
- package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js +2 -0
- package/bundles/ragents.ask/web/chunks/chunk-WFN4374U.js.map +7 -0
- package/bundles/ragents.ask/web/classes.json +1 -1
- package/bundles/ragents.ask/web/exports/ask-payload.js +1 -1
- package/bundles/ragents.ask/web/index.js +1 -1
- package/bundles/ragents.ask/web/index.js.map +3 -3
- package/bundles/ragents.browser/executor/index.mjs +357 -0
- package/bundles/ragents.browser/executor/index.mjs.map +7 -0
- package/bundles/ragents.browser/ragents-bundle.json +5 -8
- package/bundles/ragents.browser/server/chunks/{chunk-2GKLLCBE.js → chunk-CWLX4VA5.js} +1 -1
- package/bundles/ragents.browser/server/chunks/chunk-CWLX4VA5.js.map +7 -0
- package/bundles/ragents.browser/server/exports/server/contract.js +1 -1
- package/bundles/ragents.browser/server/index.js +20 -9
- package/bundles/ragents.browser/server/index.js.map +4 -4
- package/bundles/ragents.documents/ragents-bundle.json +3 -3
- package/bundles/ragents.lsp-fsharp/executor/index.mjs +68 -0
- package/bundles/ragents.lsp-fsharp/executor/index.mjs.map +7 -0
- package/bundles/ragents.lsp-fsharp/ragents-bundle.json +5 -9
- package/bundles/ragents.lsp-fsharp/server/index.js +21 -6
- package/bundles/ragents.lsp-fsharp/server/index.js.map +4 -4
- package/bundles/ragents.lsp-roslyn/executor/index.mjs +68 -0
- package/bundles/ragents.lsp-roslyn/executor/index.mjs.map +7 -0
- package/bundles/ragents.lsp-roslyn/ragents-bundle.json +6 -8
- package/bundles/ragents.lsp-roslyn/server/index.js +54 -15
- package/bundles/ragents.lsp-roslyn/server/index.js.map +4 -4
- package/bundles/ragents.lsp-typescript/executor/index.mjs +49 -0
- package/bundles/ragents.lsp-typescript/executor/index.mjs.map +7 -0
- package/bundles/ragents.lsp-typescript/ragents-bundle.json +5 -7
- package/bundles/ragents.lsp-typescript/server/index.js +22 -2
- package/bundles/ragents.lsp-typescript/server/index.js.map +3 -3
- package/bundles/ragents.model-relay/ragents-bundle.json +4 -4
- package/bundles/ragents.model-relay/server/index.js +3 -2
- package/bundles/ragents.model-relay/server/index.js.map +2 -2
- package/bundles/ragents.orchestration/ragents-bundle.json +3 -3
- package/bundles/ragents.overseer/ragents-bundle.json +3 -3
- package/bundles/ragents.processes/ragents-bundle.json +3 -3
- package/bundles/ragents.product/ragents-bundle.json +3 -3
- package/bundles/ragents.profile-distribution/ragents-bundle.json +4 -4
- package/bundles/ragents.profile-distribution/server/index.js +1 -1
- package/bundles/ragents.profile-distribution/server/index.js.map +2 -2
- package/bundles/ragents.reference/ragents-bundle.json +3 -3
- package/bundles/ragents.todo/ragents-bundle.json +3 -3
- package/bundles/ragents.transcript/ragents-bundle.json +3 -3
- package/bundles/ragents.watch/ragents-bundle.json +3 -3
- package/bundles/ragents.workspace/ragents-bundle.json +11 -5
- package/bundles/ragents.workspace/server/index.js +101 -28
- package/bundles/ragents.workspace/server/index.js.map +3 -3
- package/bundles/ragents.workspace/web/classes.json +1 -1
- package/bundles/ragents.workspace/web/index.js +1 -1
- package/bundles/ragents.workspace/web/index.js.map +3 -3
- package/package.json +2 -2
- package/packages/agent/dist/core/agent-settings.d.ts +1 -7
- package/packages/agent/dist/core/compaction/compaction.d.ts +17 -13
- package/packages/agent/dist/core/model-runtime.d.ts +5 -4
- package/packages/agent/dist/core/provider-composer.d.ts +2 -1
- package/packages/agent/dist/core/tools/bash.d.ts +9 -3
- package/packages/agent/dist/index.d.ts +1 -1
- package/packages/agent/src/core/agent-settings.ts +1 -4
- package/packages/agent/src/core/compaction/compaction.ts +68 -30
- package/packages/agent/src/core/model-runtime.ts +12 -5
- package/packages/agent/src/core/provider-composer.ts +5 -0
- package/packages/agent/src/core/tools/bash.ts +36 -30
- package/packages/agent/src/index.ts +3 -0
- package/packages/ai/dist/types.d.ts +11 -0
- package/packages/ai/src/types.ts +12 -0
- package/packages/ragents/src/domain/event-semantics.ts +3 -0
- package/packages/ragents/src/domain/event-validation.ts +15 -2
- package/packages/ragents/src/domain/events.ts +3 -0
- package/packages/ragents/src/domain/model.ts +2 -0
- package/packages/ragents/src/domain/projection.ts +1 -0
- package/packages/ragents/src/drivers/agent-runtime.ts +1 -1
- package/packages/ragents/src/drivers/agent-turn.ts +8 -6
- package/packages/ragents/src/runtime/decisions/inputs.ts +6 -2
- package/packages/ragents/src/runtime/journal-storage.ts +5 -4
- package/packages/workspace-executor/package.json +0 -1
- package/packages/workspace-executor/src/bash-launch.ts +56 -10
- package/packages/workspace-executor/src/context.ts +4 -0
- package/packages/workspace-executor/src/contributions.ts +150 -0
- package/packages/workspace-executor/src/executor.ts +1 -1
- package/packages/workspace-executor/src/index.ts +16 -19
- package/packages/workspace-executor/src/language-server/host.ts +3 -0
- package/packages/workspace-executor/src/language-server/module.ts +12 -2
- package/packages/workspace-executor/src/language-server/roots.ts +0 -5
- package/packages/workspace-executor/src/modules.ts +7 -6
- package/packages/workspace-executor/src/sandbox-tools.ts +3 -9
- package/packages/workspace-executor/src/shell-platform.ts +14 -3
- package/packages/workspace-executor/src/tools.ts +0 -3
- package/plugins/ragents.ask/ask-payload.ts +9 -0
- package/plugins/ragents.ask/server/ask-service.ts +53 -13
- package/plugins/ragents.ask/web/AskActionView.tsx +3 -2
- package/plugins/ragents.browser/executor/index.ts +5 -0
- package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/module.ts +25 -31
- package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/playwright.ts +9 -23
- package/plugins/ragents.browser/provision.ts +1 -1
- package/plugins/ragents.browser/server/browser.ts +3 -3
- package/plugins/ragents.browser/server/contract.ts +2 -2
- package/plugins/ragents.browser/server/index.ts +1 -1
- package/plugins/ragents.lsp-fsharp/executor.ts +80 -0
- package/plugins/ragents.lsp-fsharp/provision.ts +1 -1
- package/plugins/ragents.lsp-fsharp/server/index.ts +2 -2
- package/plugins/ragents.lsp-roslyn/executor.ts +64 -0
- package/plugins/ragents.lsp-roslyn/provision.ts +1 -1
- package/plugins/ragents.lsp-roslyn/server/index.ts +28 -5
- package/plugins/ragents.lsp-roslyn/server/solution-on-start.ts +18 -6
- package/plugins/ragents.lsp-typescript/executor.ts +50 -0
- package/plugins/ragents.lsp-typescript/server/index.ts +2 -2
- package/plugins/ragents.model-relay/server/config.ts +1 -1
- package/plugins/ragents.model-relay/server/relay.ts +4 -2
- package/plugins/ragents.profile-distribution/server/archive.ts +2 -1
- package/plugins/ragents.workspace/client/workspace-client.ts +111 -28
- package/plugins/ragents.workspace/contract.ts +33 -2
- package/plugins/ragents.workspace/server/binding.ts +10 -4
- package/plugins/ragents.workspace/server/clients.ts +56 -9
- package/plugins/ragents.workspace/server/config.ts +9 -0
- package/plugins/ragents.workspace/server/index.ts +13 -4
- package/plugins/ragents.workspace/server/runtime.ts +10 -0
- package/plugins/ragents.workspace/server/shell-platform.ts +6 -5
- package/plugins/ragents.workspace/web/WorkspaceBinding.tsx +1 -1
- package/ragents.config.core.ts +4 -1
- package/ragents.config.showcase.ts +4 -1
- package/scripts/agent/journal.ts +3 -1
- package/scripts/package/build-package.ts +3 -1
- package/scripts/provision/run-provision.ts +2 -2
- package/scripts/workspace-client/run-workspace-client.ts +7 -4
- package/apps/web/dist/help/screenshots/release-checklist-vscode.png +0 -0
- package/apps/web/dist/help/screenshots/release-checklist-web.png +0 -0
- package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js +0 -2
- package/bundles/ragents.ask/web/chunks/chunk-HGFDGTNC.js.map +0 -7
- package/bundles/ragents.browser/server/chunks/chunk-2GKLLCBE.js.map +0 -7
- package/packages/workspace-executor/src/language-server/adapters/fsharp.ts +0 -60
- package/packages/workspace-executor/src/language-server/adapters/index.ts +0 -7
- package/packages/workspace-executor/src/language-server/adapters/roslyn.ts +0 -56
- package/packages/workspace-executor/src/language-server/adapters/typescript.ts +0 -74
- package/packages/workspace-executor/src/language-server/solution.ts +0 -13
- /package/{packages/workspace-executor/src/browser → plugins/ragents.browser/executor}/contract.ts +0 -0
- /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.
|
|
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": "
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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>,
|
|
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
|
-
|
|
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[],
|
|
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,
|
|
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
|
|
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
|
-
|
|
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<
|
|
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
|
-
//
|
|
45
|
+
// Compaction values of a model
|
|
46
46
|
// ============================================================================
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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(
|
|
19
|
+
throw new Error(`Invalid ${label} ${timeout}: must be a positive number of seconds`);
|
|
22
20
|
}
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
24
|
+
return timeout;
|
|
29
25
|
}
|
|
30
26
|
|
|
31
|
-
const
|
|
27
|
+
const bashSchemaFor = (defaultTimeoutSeconds: number) => Type.Object({
|
|
32
28
|
command: Type.String({ description: "Bash command to execute" }),
|
|
33
|
-
timeout: Type.Optional(Type.Number({
|
|
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
|
-
|
|
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
|
-
|
|
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<
|
|
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.
|
|
166
|
-
parameters:
|
|
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
|
-
|
|
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
|
}
|