@clinebot/core 0.0.0 → 0.0.3
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/README.md +7 -7
- package/dist/default-tools/definitions.d.ts +1 -1
- package/dist/default-tools/executors/index.d.ts +1 -1
- package/dist/default-tools/index.d.ts +2 -1
- package/dist/default-tools/model-tool-routing.d.ts +33 -0
- package/dist/default-tools/schemas.d.ts +13 -7
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.js +220 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +47 -47
- package/dist/index.node.d.ts +36 -0
- package/dist/index.node.js +622 -0
- package/dist/providers/local-provider-service.d.ts +37 -0
- package/dist/session/default-session-manager.d.ts +3 -1
- package/dist/session/session-host.d.ts +2 -2
- package/dist/session/session-manager.d.ts +8 -0
- package/dist/session/unified-session-persistence-service.d.ts +1 -1
- package/dist/session/utils/helpers.d.ts +11 -0
- package/dist/session/utils/types.d.ts +42 -0
- package/dist/session/utils/usage.d.ts +9 -0
- package/dist/storage/provider-settings-manager.d.ts +2 -0
- package/dist/types/config.d.ts +8 -1
- package/dist/types.d.ts +1 -1
- package/package.json +11 -32
- package/src/default-tools/definitions.test.ts +130 -1
- package/src/default-tools/definitions.ts +7 -3
- package/src/default-tools/executors/editor.ts +10 -9
- package/src/default-tools/executors/file-read.test.ts +1 -1
- package/src/default-tools/executors/file-read.ts +11 -6
- package/src/default-tools/executors/index.ts +1 -1
- package/src/default-tools/index.ts +6 -1
- package/src/default-tools/model-tool-routing.test.ts +86 -0
- package/src/default-tools/model-tool-routing.ts +132 -0
- package/src/default-tools/schemas.ts +49 -52
- package/src/index.browser.ts +1 -0
- package/src/{server/index.ts → index.node.ts} +51 -109
- package/src/index.ts +41 -2
- package/src/input/file-indexer.ts +28 -2
- package/src/providers/local-provider-service.ts +591 -0
- package/src/runtime/runtime-builder.test.ts +69 -0
- package/src/runtime/runtime-builder.ts +20 -0
- package/src/runtime/runtime-parity.test.ts +20 -9
- package/src/session/default-session-manager.e2e.test.ts +11 -1
- package/src/session/default-session-manager.test.ts +270 -0
- package/src/session/default-session-manager.ts +109 -191
- package/src/session/index.ts +7 -2
- package/src/session/session-host.ts +30 -18
- package/src/session/session-manager.ts +11 -0
- package/src/session/unified-session-persistence-service.ts +11 -5
- package/src/session/utils/helpers.ts +148 -0
- package/src/session/utils/types.ts +46 -0
- package/src/session/utils/usage.ts +32 -0
- package/src/storage/provider-settings-legacy-migration.test.ts +3 -3
- package/src/storage/provider-settings-manager.test.ts +34 -0
- package/src/storage/provider-settings-manager.ts +22 -1
- package/src/types/config.ts +13 -0
- package/src/types.ts +1 -0
- package/dist/server/index.d.ts +0 -47
- package/dist/server/index.js +0 -641
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { providers as LlmsProviders } from "@clinebot/llms";
|
|
2
|
+
import type { RpcAddProviderActionRequest, RpcOAuthProviderId, RpcProviderListItem, RpcProviderModel, RpcSaveProviderSettingsActionRequest } from "@clinebot/shared";
|
|
3
|
+
import type { ProviderSettingsManager } from "../storage/provider-settings-manager";
|
|
4
|
+
export declare function ensureCustomProvidersLoaded(manager: ProviderSettingsManager): Promise<void>;
|
|
5
|
+
export declare function addLocalProvider(manager: ProviderSettingsManager, request: RpcAddProviderActionRequest): Promise<{
|
|
6
|
+
providerId: string;
|
|
7
|
+
settingsPath: string;
|
|
8
|
+
modelsPath: string;
|
|
9
|
+
modelsCount: number;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function listLocalProviders(manager: ProviderSettingsManager): Promise<{
|
|
12
|
+
providers: RpcProviderListItem[];
|
|
13
|
+
settingsPath: string;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function getLocalProviderModels(providerId: string): Promise<{
|
|
16
|
+
providerId: string;
|
|
17
|
+
models: RpcProviderModel[];
|
|
18
|
+
}>;
|
|
19
|
+
export declare function saveLocalProviderSettings(manager: ProviderSettingsManager, request: RpcSaveProviderSettingsActionRequest): {
|
|
20
|
+
providerId: string;
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
settingsPath: string;
|
|
23
|
+
};
|
|
24
|
+
export declare function normalizeOAuthProvider(provider: string): RpcOAuthProviderId;
|
|
25
|
+
export declare function loginLocalProvider(providerId: RpcOAuthProviderId, existing: LlmsProviders.ProviderSettings | undefined, openUrl: (url: string) => void): Promise<{
|
|
26
|
+
access: string;
|
|
27
|
+
refresh: string;
|
|
28
|
+
expires: number;
|
|
29
|
+
accountId?: string;
|
|
30
|
+
}>;
|
|
31
|
+
export declare function saveLocalProviderOAuthCredentials(manager: ProviderSettingsManager, providerId: RpcOAuthProviderId, existing: LlmsProviders.ProviderSettings | undefined, credentials: {
|
|
32
|
+
access: string;
|
|
33
|
+
refresh: string;
|
|
34
|
+
expires: number;
|
|
35
|
+
accountId?: string;
|
|
36
|
+
}): LlmsProviders.ProviderSettings;
|
|
37
|
+
export declare function resolveLocalClineAuthToken(settings: LlmsProviders.ProviderSettings | undefined): string | undefined;
|
|
@@ -7,7 +7,7 @@ import type { CoreSessionEvent } from "../types/events";
|
|
|
7
7
|
import type { SessionRecord } from "../types/sessions";
|
|
8
8
|
import type { RpcCoreSessionService } from "./rpc-session-service";
|
|
9
9
|
import { RuntimeOAuthTokenManager } from "./runtime-oauth-token-manager";
|
|
10
|
-
import type { SendSessionInput, SessionManager, StartSessionInput, StartSessionResult } from "./session-manager";
|
|
10
|
+
import type { SendSessionInput, SessionAccumulatedUsage, SessionManager, StartSessionInput, StartSessionResult } from "./session-manager";
|
|
11
11
|
import type { CoreSessionService } from "./session-service";
|
|
12
12
|
type SessionBackend = CoreSessionService | RpcCoreSessionService;
|
|
13
13
|
export interface DefaultSessionManagerOptions {
|
|
@@ -32,11 +32,13 @@ export declare class DefaultSessionManager implements SessionManager {
|
|
|
32
32
|
private readonly defaultRequestToolApproval?;
|
|
33
33
|
private readonly listeners;
|
|
34
34
|
private readonly sessions;
|
|
35
|
+
private readonly usageBySession;
|
|
35
36
|
constructor(options: DefaultSessionManagerOptions);
|
|
36
37
|
private resolveStoredProviderSettings;
|
|
37
38
|
private buildResolvedProviderConfig;
|
|
38
39
|
start(input: StartSessionInput): Promise<StartSessionResult>;
|
|
39
40
|
send(input: SendSessionInput): Promise<AgentResult | undefined>;
|
|
41
|
+
getAccumulatedUsage(sessionId: string): Promise<SessionAccumulatedUsage | undefined>;
|
|
40
42
|
abort(sessionId: string): Promise<void>;
|
|
41
43
|
stop(sessionId: string): Promise<void>;
|
|
42
44
|
dispose(reason?: string): Promise<void>;
|
|
@@ -3,7 +3,7 @@ import type { ToolExecutors } from "../default-tools";
|
|
|
3
3
|
import { RpcCoreSessionService } from "./rpc-session-service";
|
|
4
4
|
import type { SessionManager } from "./session-manager";
|
|
5
5
|
import { CoreSessionService } from "./session-service";
|
|
6
|
-
type SessionBackend = RpcCoreSessionService | CoreSessionService;
|
|
6
|
+
export type SessionBackend = RpcCoreSessionService | CoreSessionService;
|
|
7
7
|
export interface CreateSessionHostOptions {
|
|
8
8
|
distinctId?: string;
|
|
9
9
|
sessionService?: SessionBackend;
|
|
@@ -17,5 +17,5 @@ export interface CreateSessionHostOptions {
|
|
|
17
17
|
requestToolApproval?: (request: ToolApprovalRequest) => Promise<ToolApprovalResult>;
|
|
18
18
|
}
|
|
19
19
|
export type SessionHost = SessionManager;
|
|
20
|
+
export declare function resolveSessionBackend(options: CreateSessionHostOptions): Promise<SessionBackend>;
|
|
20
21
|
export declare function createSessionHost(options: CreateSessionHostOptions): Promise<SessionHost>;
|
|
21
|
-
export {};
|
|
@@ -34,9 +34,17 @@ export interface SendSessionInput {
|
|
|
34
34
|
userImages?: string[];
|
|
35
35
|
userFiles?: string[];
|
|
36
36
|
}
|
|
37
|
+
export interface SessionAccumulatedUsage {
|
|
38
|
+
inputTokens: number;
|
|
39
|
+
outputTokens: number;
|
|
40
|
+
cacheReadTokens: number;
|
|
41
|
+
cacheWriteTokens: number;
|
|
42
|
+
totalCost: number;
|
|
43
|
+
}
|
|
37
44
|
export interface SessionManager {
|
|
38
45
|
start(input: StartSessionInput): Promise<StartSessionResult>;
|
|
39
46
|
send(input: SendSessionInput): Promise<AgentResult | undefined>;
|
|
47
|
+
getAccumulatedUsage(sessionId: string): Promise<SessionAccumulatedUsage | undefined>;
|
|
40
48
|
abort(sessionId: string): Promise<void>;
|
|
41
49
|
stop(sessionId: string): Promise<void>;
|
|
42
50
|
dispose(reason?: string): Promise<void>;
|
|
@@ -76,7 +76,7 @@ export declare class UnifiedSessionPersistenceService {
|
|
|
76
76
|
upsertSubagentSessionFromHook(event: HookEventPayload): Promise<string | undefined>;
|
|
77
77
|
appendSubagentHookAudit(subSessionId: string, event: HookEventPayload): Promise<void>;
|
|
78
78
|
appendSubagentTranscriptLine(subSessionId: string, line: string): Promise<void>;
|
|
79
|
-
persistSessionMessages(sessionId: string, messages: LlmsProviders.Message[]): Promise<void>;
|
|
79
|
+
persistSessionMessages(sessionId: string, messages: LlmsProviders.Message[], systemPrompt?: string): Promise<void>;
|
|
80
80
|
applySubagentStatus(subSessionId: string, event: HookEventPayload): Promise<void>;
|
|
81
81
|
applySubagentStatusBySessionId(subSessionId: string, status: SessionStatus): Promise<void>;
|
|
82
82
|
applyStatusToRunningChildSessions(parentSessionId: string, status: Exclude<SessionStatus, "running">): Promise<void>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentConfig, AgentEvent, AgentResult } from "@clinebot/agents";
|
|
2
|
+
import type { providers as LlmsProviders } from "@clinebot/llms";
|
|
3
|
+
import type { SessionRecord } from "../../types/sessions";
|
|
4
|
+
import type { SessionRowShape } from "../session-service";
|
|
5
|
+
import type { StoredMessageWithMetadata } from "./types";
|
|
6
|
+
export declare function extractWorkspaceMetadataFromSystemPrompt(systemPrompt: string): string | undefined;
|
|
7
|
+
export declare function hasRuntimeHooks(hooks: AgentConfig["hooks"]): boolean;
|
|
8
|
+
export declare function mergeAgentExtensions(explicitExtensions: AgentConfig["extensions"] | undefined, loadedExtensions: AgentConfig["extensions"] | undefined): AgentConfig["extensions"];
|
|
9
|
+
export declare function serializeAgentEvent(event: AgentEvent): string;
|
|
10
|
+
export declare function withLatestAssistantTurnMetadata(messages: LlmsProviders.Message[], result: AgentResult): StoredMessageWithMetadata[];
|
|
11
|
+
export declare function toSessionRecord(row: SessionRowShape): SessionRecord;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Agent } from "@clinebot/agents";
|
|
2
|
+
import type { providers as LlmsProviders } from "@clinebot/llms";
|
|
3
|
+
import type { BuiltRuntime } from "../../runtime/session-runtime";
|
|
4
|
+
import type { SessionSource } from "../../types/common";
|
|
5
|
+
import type { CoreSessionConfig } from "../../types/config";
|
|
6
|
+
import type { SessionAccumulatedUsage } from "../session-manager";
|
|
7
|
+
import type { RootSessionArtifacts } from "../session-service";
|
|
8
|
+
export type ActiveSession = {
|
|
9
|
+
sessionId: string;
|
|
10
|
+
config: CoreSessionConfig;
|
|
11
|
+
artifacts?: RootSessionArtifacts;
|
|
12
|
+
source: SessionSource;
|
|
13
|
+
startedAt: string;
|
|
14
|
+
pendingPrompt?: string;
|
|
15
|
+
runtime: BuiltRuntime;
|
|
16
|
+
agent: Agent;
|
|
17
|
+
started: boolean;
|
|
18
|
+
aborting: boolean;
|
|
19
|
+
interactive: boolean;
|
|
20
|
+
activeTeamRunIds: Set<string>;
|
|
21
|
+
pendingTeamRunUpdates: TeamRunUpdate[];
|
|
22
|
+
teamRunWaiters: Array<() => void>;
|
|
23
|
+
pluginSandboxShutdown?: () => Promise<void>;
|
|
24
|
+
turnUsageBaseline?: SessionAccumulatedUsage;
|
|
25
|
+
};
|
|
26
|
+
export type TeamRunUpdate = {
|
|
27
|
+
runId: string;
|
|
28
|
+
agentId: string;
|
|
29
|
+
taskId?: string;
|
|
30
|
+
status: "completed" | "failed" | "cancelled" | "interrupted";
|
|
31
|
+
error?: string;
|
|
32
|
+
iterations?: number;
|
|
33
|
+
};
|
|
34
|
+
export type StoredMessageWithMetadata = LlmsProviders.MessageWithMetadata & {
|
|
35
|
+
providerId?: string;
|
|
36
|
+
modelId?: string;
|
|
37
|
+
};
|
|
38
|
+
export type PreparedTurnInput = {
|
|
39
|
+
prompt: string;
|
|
40
|
+
userImages?: string[];
|
|
41
|
+
userFiles?: string[];
|
|
42
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SessionAccumulatedUsage } from "../session-manager";
|
|
2
|
+
export declare function createInitialAccumulatedUsage(): SessionAccumulatedUsage;
|
|
3
|
+
export declare function accumulateUsageTotals(baseline: SessionAccumulatedUsage, usage: {
|
|
4
|
+
inputTokens?: number;
|
|
5
|
+
outputTokens?: number;
|
|
6
|
+
cacheReadTokens?: number;
|
|
7
|
+
cacheWriteTokens?: number;
|
|
8
|
+
totalCost?: number;
|
|
9
|
+
}): SessionAccumulatedUsage;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ProviderConfig, type ProviderSettings, type ProviderTokenSource, type StoredProviderSettings } from "../types/provider-settings";
|
|
2
2
|
export interface ProviderSettingsManagerOptions {
|
|
3
3
|
filePath?: string;
|
|
4
|
+
dataDir?: string;
|
|
4
5
|
}
|
|
5
6
|
export interface SaveProviderSettingsOptions {
|
|
6
7
|
setLastUsed?: boolean;
|
|
@@ -8,6 +9,7 @@ export interface SaveProviderSettingsOptions {
|
|
|
8
9
|
}
|
|
9
10
|
export declare class ProviderSettingsManager {
|
|
10
11
|
private readonly filePath;
|
|
12
|
+
private readonly dataDir?;
|
|
11
13
|
constructor(options?: ProviderSettingsManagerOptions);
|
|
12
14
|
getFilePath(): string;
|
|
13
15
|
read(): StoredProviderSettings;
|
package/dist/types/config.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { AgentConfig, AgentHooks, HookErrorMode, TeamEvent, Tool } from "@clinebot/agents";
|
|
1
|
+
import type { AgentConfig, AgentHooks, ConsecutiveMistakeLimitContext, ConsecutiveMistakeLimitDecision, HookErrorMode, TeamEvent, Tool } from "@clinebot/agents";
|
|
2
2
|
import type { providers as LlmsProviders } from "@clinebot/llms";
|
|
3
3
|
import type { AgentMode, BasicLogger, SessionExecutionConfig, SessionPromptConfig, SessionWorkspaceConfig } from "@clinebot/shared";
|
|
4
|
+
import type { ToolRoutingRule } from "../default-tools/model-tool-routing.js";
|
|
4
5
|
export type CoreAgentMode = AgentMode;
|
|
5
6
|
export interface CoreModelConfig {
|
|
6
7
|
providerId: string;
|
|
@@ -14,6 +15,10 @@ export interface CoreModelConfig {
|
|
|
14
15
|
* Request model-side thinking/reasoning when supported.
|
|
15
16
|
*/
|
|
16
17
|
thinking?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Explicit reasoning effort override for capable models.
|
|
20
|
+
*/
|
|
21
|
+
reasoningEffort?: LlmsProviders.ProviderConfig["reasoningEffort"];
|
|
17
22
|
}
|
|
18
23
|
export interface CoreRuntimeFeatures {
|
|
19
24
|
enableTools: boolean;
|
|
@@ -34,4 +39,6 @@ export interface CoreSessionConfig extends CoreModelConfig, CoreRuntimeFeatures,
|
|
|
34
39
|
pluginPaths?: string[];
|
|
35
40
|
extensions?: AgentConfig["extensions"];
|
|
36
41
|
onTeamEvent?: (event: TeamEvent) => void;
|
|
42
|
+
onConsecutiveMistakeLimitReached?: (context: ConsecutiveMistakeLimitContext) => Promise<ConsecutiveMistakeLimitDecision> | ConsecutiveMistakeLimitDecision;
|
|
43
|
+
toolRoutingRules?: ToolRoutingRule[];
|
|
37
44
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type { SandboxCallOptions, SubprocessSandboxOptions, } from "./runtime/sa
|
|
|
7
7
|
export { SubprocessSandbox } from "./runtime/sandbox/subprocess-sandbox";
|
|
8
8
|
export type { BuiltRuntime as RuntimeEnvironment, RuntimeBuilder, RuntimeBuilderInput, SessionRuntime, } from "./runtime/session-runtime";
|
|
9
9
|
export type { CreateSessionHostOptions, SessionHost, } from "./session/session-host";
|
|
10
|
-
export type { SendSessionInput, SessionManager, StartSessionInput, StartSessionResult, } from "./session/session-manager";
|
|
10
|
+
export type { SendSessionInput, SessionAccumulatedUsage, SessionManager, StartSessionInput, StartSessionResult, } from "./session/session-manager";
|
|
11
11
|
export type { SessionManifest } from "./session/session-manifest";
|
|
12
12
|
export type { CreateRootSessionWithArtifactsInput, RootSessionArtifacts, } from "./session/session-service";
|
|
13
13
|
export type { WorkspaceManager, WorkspaceManagerEvent, } from "./session/workspace-manager";
|
package/package.json
CHANGED
|
@@ -1,47 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clinebot/core",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"main": "dist/index.js",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@clinebot/agents": "
|
|
7
|
-
"@clinebot/llms": "
|
|
8
|
-
"
|
|
9
|
-
"@clinebot/shared": "workspace:*",
|
|
10
|
-
"nanoid": "^5.1.6",
|
|
6
|
+
"@clinebot/agents": "0.0.3",
|
|
7
|
+
"@clinebot/llms": "0.0.3",
|
|
8
|
+
"nanoid": "^5.1.7",
|
|
11
9
|
"simple-git": "^3.32.3",
|
|
12
10
|
"yaml": "^2.8.2",
|
|
13
11
|
"zod": "^4.3.6"
|
|
14
12
|
},
|
|
15
13
|
"exports": {
|
|
16
14
|
".": {
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"import": "./dist/index.js"
|
|
20
|
-
},
|
|
21
|
-
"./node": {
|
|
22
|
-
"development": "./src/index.ts",
|
|
15
|
+
"browser": "./dist/index.browser.js",
|
|
16
|
+
"development": "./dist/index.js",
|
|
23
17
|
"types": "./dist/index.d.ts",
|
|
24
18
|
"import": "./dist/index.js"
|
|
25
19
|
},
|
|
26
20
|
"./browser": {
|
|
27
|
-
"development": "./
|
|
21
|
+
"development": "./dist/index.browser.js",
|
|
28
22
|
"types": "./dist/index.browser.d.ts",
|
|
29
23
|
"import": "./dist/index.browser.js"
|
|
30
|
-
},
|
|
31
|
-
"./server": {
|
|
32
|
-
"development": "./src/server/index.ts",
|
|
33
|
-
"types": "./dist/server/index.d.ts",
|
|
34
|
-
"import": "./dist/server/index.js"
|
|
35
|
-
},
|
|
36
|
-
"./server/node": {
|
|
37
|
-
"development": "./src/server/index.ts",
|
|
38
|
-
"types": "./dist/server/index.d.ts",
|
|
39
|
-
"import": "./dist/server/index.js"
|
|
40
|
-
},
|
|
41
|
-
"./server/browser": {
|
|
42
|
-
"development": "./src/server/index.ts",
|
|
43
|
-
"types": "./dist/server.browser.d.ts",
|
|
44
|
-
"import": "./dist/server.browser.js"
|
|
45
24
|
}
|
|
46
25
|
},
|
|
47
26
|
"description": "State-aware orchestration for Cline Agent runtimes",
|
|
@@ -50,7 +29,7 @@
|
|
|
50
29
|
"src"
|
|
51
30
|
],
|
|
52
31
|
"scripts": {
|
|
53
|
-
"build": "bun run ./
|
|
32
|
+
"build": "bun run ./bun.mts && bun tsc -p tsconfig.build.json",
|
|
54
33
|
"clean": "rm -rf dist node_modules",
|
|
55
34
|
"typecheck": "bun tsc -p tsconfig.dev.json --noEmit",
|
|
56
35
|
"test": "bun run test:unit && bun run test:e2e",
|
|
@@ -59,5 +38,5 @@
|
|
|
59
38
|
"test:watch": "vitest --config vitest.config.ts"
|
|
60
39
|
},
|
|
61
40
|
"type": "module",
|
|
62
|
-
"types": "dist/index.d.ts"
|
|
63
|
-
}
|
|
41
|
+
"types": "./dist/index.d.ts"
|
|
42
|
+
}
|
|
@@ -226,8 +226,137 @@ describe("zod schema conversion", () => {
|
|
|
226
226
|
description:
|
|
227
227
|
"The absolute file path of a text file to read content from",
|
|
228
228
|
},
|
|
229
|
-
description:
|
|
229
|
+
description:
|
|
230
|
+
"Array of absolute file paths to get full content from. Prefer this tool over running terminal command to get file content for better performance and reliability.",
|
|
230
231
|
});
|
|
231
232
|
expect(inputSchema.required).toEqual(["file_paths"]);
|
|
232
233
|
});
|
|
234
|
+
|
|
235
|
+
it("exposes skills args as optional nullable in tool schemas", () => {
|
|
236
|
+
const tools = createDefaultTools({
|
|
237
|
+
executors: {
|
|
238
|
+
skills: async () => "ok",
|
|
239
|
+
},
|
|
240
|
+
enableReadFiles: false,
|
|
241
|
+
enableSearch: false,
|
|
242
|
+
enableBash: false,
|
|
243
|
+
enableWebFetch: false,
|
|
244
|
+
enableEditor: false,
|
|
245
|
+
enableApplyPatch: false,
|
|
246
|
+
enableAskQuestion: false,
|
|
247
|
+
enableSkills: true,
|
|
248
|
+
});
|
|
249
|
+
const skills = tools.find((tool) => tool.name === "skills");
|
|
250
|
+
expect(skills).toBeDefined();
|
|
251
|
+
if (!skills) {
|
|
252
|
+
throw new Error("Expected skills tool.");
|
|
253
|
+
}
|
|
254
|
+
const schema = skills.inputSchema as {
|
|
255
|
+
required?: string[];
|
|
256
|
+
properties?: Record<string, unknown>;
|
|
257
|
+
};
|
|
258
|
+
expect(schema.required).toEqual(["skill"]);
|
|
259
|
+
expect(schema.properties).toHaveProperty("args");
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe("default editor tool", () => {
|
|
264
|
+
it("accepts null for unused optional fields on str_replace", async () => {
|
|
265
|
+
const execute = vi.fn(async () => "patched");
|
|
266
|
+
const tools = createDefaultTools({
|
|
267
|
+
executors: {
|
|
268
|
+
editor: execute,
|
|
269
|
+
},
|
|
270
|
+
enableReadFiles: false,
|
|
271
|
+
enableSearch: false,
|
|
272
|
+
enableBash: false,
|
|
273
|
+
enableWebFetch: false,
|
|
274
|
+
enableSkills: false,
|
|
275
|
+
enableAskQuestion: false,
|
|
276
|
+
enableApplyPatch: false,
|
|
277
|
+
enableEditor: true,
|
|
278
|
+
});
|
|
279
|
+
const editorTool = tools.find((tool) => tool.name === "editor");
|
|
280
|
+
expect(editorTool).toBeDefined();
|
|
281
|
+
if (!editorTool) {
|
|
282
|
+
throw new Error("Expected editor tool to be defined.");
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const result = await editorTool.execute(
|
|
286
|
+
{
|
|
287
|
+
command: "str_replace",
|
|
288
|
+
path: "/tmp/example.ts",
|
|
289
|
+
old_str: "before",
|
|
290
|
+
new_str: "after",
|
|
291
|
+
file_text: null,
|
|
292
|
+
insert_line: null,
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
agentId: "agent-1",
|
|
296
|
+
conversationId: "conv-1",
|
|
297
|
+
iteration: 1,
|
|
298
|
+
},
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
expect(result).toEqual({
|
|
302
|
+
query: "str_replace:/tmp/example.ts",
|
|
303
|
+
result: "patched",
|
|
304
|
+
success: true,
|
|
305
|
+
});
|
|
306
|
+
expect(execute).toHaveBeenCalledWith(
|
|
307
|
+
expect.objectContaining({
|
|
308
|
+
command: "str_replace",
|
|
309
|
+
path: "/tmp/example.ts",
|
|
310
|
+
old_str: "before",
|
|
311
|
+
new_str: "after",
|
|
312
|
+
file_text: null,
|
|
313
|
+
insert_line: null,
|
|
314
|
+
}),
|
|
315
|
+
process.cwd(),
|
|
316
|
+
expect.objectContaining({
|
|
317
|
+
agentId: "agent-1",
|
|
318
|
+
conversationId: "conv-1",
|
|
319
|
+
iteration: 1,
|
|
320
|
+
}),
|
|
321
|
+
);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("still rejects null for required insert fields", async () => {
|
|
325
|
+
const execute = vi.fn(async () => "patched");
|
|
326
|
+
const tools = createDefaultTools({
|
|
327
|
+
executors: {
|
|
328
|
+
editor: execute,
|
|
329
|
+
},
|
|
330
|
+
enableReadFiles: false,
|
|
331
|
+
enableSearch: false,
|
|
332
|
+
enableBash: false,
|
|
333
|
+
enableWebFetch: false,
|
|
334
|
+
enableSkills: false,
|
|
335
|
+
enableAskQuestion: false,
|
|
336
|
+
enableApplyPatch: false,
|
|
337
|
+
enableEditor: true,
|
|
338
|
+
});
|
|
339
|
+
const editorTool = tools.find((tool) => tool.name === "editor");
|
|
340
|
+
expect(editorTool).toBeDefined();
|
|
341
|
+
if (!editorTool) {
|
|
342
|
+
throw new Error("Expected editor tool to be defined.");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
await expect(
|
|
346
|
+
editorTool.execute(
|
|
347
|
+
{
|
|
348
|
+
command: "insert",
|
|
349
|
+
path: "/tmp/example.ts",
|
|
350
|
+
new_str: "after",
|
|
351
|
+
insert_line: null,
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
agentId: "agent-1",
|
|
355
|
+
conversationId: "conv-1",
|
|
356
|
+
iteration: 1,
|
|
357
|
+
},
|
|
358
|
+
),
|
|
359
|
+
).rejects.toThrow(/insert_line is required for command=insert/);
|
|
360
|
+
expect(execute).not.toHaveBeenCalled();
|
|
361
|
+
});
|
|
233
362
|
});
|
|
@@ -426,7 +426,7 @@ export function createEditorTool(
|
|
|
426
426
|
name: "editor",
|
|
427
427
|
description:
|
|
428
428
|
"Edit file using absolute path with create, string replacement, and line insert operations. " +
|
|
429
|
-
"Supported commands: create, str_replace, insert
|
|
429
|
+
"Supported commands: create, str_replace, insert.",
|
|
430
430
|
inputSchema: zodToJsonSchema(EditFileInputSchema),
|
|
431
431
|
timeoutMs,
|
|
432
432
|
retryable: false, // Editing operations are stateful and should not auto-retry
|
|
@@ -487,7 +487,11 @@ export function createSkillsTool(
|
|
|
487
487
|
execute: async (input, context) => {
|
|
488
488
|
const validatedInput = validateWithZod(SkillsInputSchema, input);
|
|
489
489
|
return withTimeout(
|
|
490
|
-
executor(
|
|
490
|
+
executor(
|
|
491
|
+
validatedInput.skill,
|
|
492
|
+
validatedInput.args || undefined,
|
|
493
|
+
context,
|
|
494
|
+
),
|
|
491
495
|
timeoutMs,
|
|
492
496
|
`Skills operation timed out after ${timeoutMs}ms`,
|
|
493
497
|
);
|
|
@@ -543,7 +547,7 @@ export function createAskQuestionTool(
|
|
|
543
547
|
* @example
|
|
544
548
|
* ```typescript
|
|
545
549
|
* import { Agent } from "@clinebot/agents"
|
|
546
|
-
* import { createDefaultTools } from "@clinebot/core/
|
|
550
|
+
* import { createDefaultTools } from "@clinebot/core/node"
|
|
547
551
|
* import * as fs from "fs/promises"
|
|
548
552
|
* import { exec } from "child_process"
|
|
549
553
|
*
|
|
@@ -116,7 +116,7 @@ async function createFile(
|
|
|
116
116
|
async function replaceInFile(
|
|
117
117
|
filePath: string,
|
|
118
118
|
oldStr: string,
|
|
119
|
-
newStr: string | undefined,
|
|
119
|
+
newStr: string | null | undefined,
|
|
120
120
|
encoding: BufferEncoding,
|
|
121
121
|
maxDiffLines: number,
|
|
122
122
|
): Promise<string> {
|
|
@@ -142,23 +142,24 @@ async function replaceInFile(
|
|
|
142
142
|
|
|
143
143
|
async function insertInFile(
|
|
144
144
|
filePath: string,
|
|
145
|
-
|
|
145
|
+
insertLineOneBased: number,
|
|
146
146
|
newStr: string,
|
|
147
147
|
encoding: BufferEncoding,
|
|
148
148
|
): Promise<string> {
|
|
149
149
|
const content = await fs.readFile(filePath, encoding);
|
|
150
150
|
const lines = content.split("\n");
|
|
151
|
+
const insertLine = insertLineOneBased - 1; // Convert to zero-based index
|
|
151
152
|
|
|
152
153
|
if (insertLine < 0 || insertLine > lines.length) {
|
|
153
154
|
throw new Error(
|
|
154
|
-
`Invalid line number: ${
|
|
155
|
+
`Invalid line number: ${insertLineOneBased}. Valid range: 1-${lines.length}`,
|
|
155
156
|
);
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
lines.splice(insertLine, 0, ...newStr.split("\n"));
|
|
159
160
|
await fs.writeFile(filePath, lines.join("\n"), { encoding });
|
|
160
161
|
|
|
161
|
-
return `Inserted content at line ${
|
|
162
|
+
return `Inserted content at line ${insertLineOneBased} in ${filePath}.`;
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
/**
|
|
@@ -182,7 +183,7 @@ export function createEditorExecutor(
|
|
|
182
183
|
|
|
183
184
|
switch (input.command) {
|
|
184
185
|
case "create":
|
|
185
|
-
if (input.file_text
|
|
186
|
+
if (input.file_text == null) {
|
|
186
187
|
throw new Error(
|
|
187
188
|
"Parameter `file_text` is required for command: create",
|
|
188
189
|
);
|
|
@@ -190,7 +191,7 @@ export function createEditorExecutor(
|
|
|
190
191
|
return createFile(filePath, input.file_text, encoding);
|
|
191
192
|
|
|
192
193
|
case "str_replace":
|
|
193
|
-
if (input.old_str
|
|
194
|
+
if (input.old_str == null) {
|
|
194
195
|
throw new Error(
|
|
195
196
|
"Parameter `old_str` is required for command: str_replace",
|
|
196
197
|
);
|
|
@@ -204,19 +205,19 @@ export function createEditorExecutor(
|
|
|
204
205
|
);
|
|
205
206
|
|
|
206
207
|
case "insert":
|
|
207
|
-
if (input.insert_line
|
|
208
|
+
if (input.insert_line == null) {
|
|
208
209
|
throw new Error(
|
|
209
210
|
"Parameter `insert_line` is required for insert command.",
|
|
210
211
|
);
|
|
211
212
|
}
|
|
212
|
-
if (input.new_str
|
|
213
|
+
if (input.new_str == null) {
|
|
213
214
|
throw new Error(
|
|
214
215
|
"Parameter `new_str` is required for insert command.",
|
|
215
216
|
);
|
|
216
217
|
}
|
|
217
218
|
return insertInFile(
|
|
218
219
|
filePath,
|
|
219
|
-
input.insert_line,
|
|
220
|
+
input.insert_line, // One-based index
|
|
220
221
|
input.new_str,
|
|
221
222
|
encoding,
|
|
222
223
|
);
|
|
@@ -17,7 +17,7 @@ describe("createFileReadExecutor", () => {
|
|
|
17
17
|
conversationId: "conv-1",
|
|
18
18
|
iteration: 1,
|
|
19
19
|
});
|
|
20
|
-
expect(result).toBe("hello absolute path");
|
|
20
|
+
expect(result).toBe("1 | hello absolute path");
|
|
21
21
|
} finally {
|
|
22
22
|
await fs.rm(dir, { recursive: true, force: true });
|
|
23
23
|
}
|
|
@@ -32,6 +32,12 @@ export interface FileReadExecutorOptions {
|
|
|
32
32
|
includeLineNumbers?: boolean;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
const DEFAULT_FILE_READ_OPTIONS: Required<FileReadExecutorOptions> = {
|
|
36
|
+
maxFileSizeBytes: 10_000_000, // 10MB default limit
|
|
37
|
+
encoding: "utf-8", // Default to UTF-8 encoding
|
|
38
|
+
includeLineNumbers: true, // Include line numbers by default
|
|
39
|
+
};
|
|
40
|
+
|
|
35
41
|
/**
|
|
36
42
|
* Create a file read executor using Node.js fs module
|
|
37
43
|
*
|
|
@@ -48,11 +54,10 @@ export interface FileReadExecutorOptions {
|
|
|
48
54
|
export function createFileReadExecutor(
|
|
49
55
|
options: FileReadExecutorOptions = {},
|
|
50
56
|
): FileReadExecutor {
|
|
51
|
-
const {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} = options;
|
|
57
|
+
const { maxFileSizeBytes, encoding, includeLineNumbers } = {
|
|
58
|
+
...DEFAULT_FILE_READ_OPTIONS,
|
|
59
|
+
...options,
|
|
60
|
+
};
|
|
56
61
|
|
|
57
62
|
return async (filePath: string, _context: ToolContext): Promise<string> => {
|
|
58
63
|
const resolvedPath = path.isAbsolute(filePath)
|
|
@@ -77,7 +82,7 @@ export function createFileReadExecutor(
|
|
|
77
82
|
// Read file content
|
|
78
83
|
const content = await fs.readFile(resolvedPath, encoding);
|
|
79
84
|
|
|
80
|
-
// Optionally add line numbers
|
|
85
|
+
// Optionally add line numbers - one-based indexing for better readability
|
|
81
86
|
if (includeLineNumbers) {
|
|
82
87
|
const lines = content.split("\n");
|
|
83
88
|
const maxLineNumWidth = String(lines.length).length;
|
|
@@ -48,7 +48,7 @@ export interface DefaultExecutorsOptions {
|
|
|
48
48
|
*
|
|
49
49
|
* @example
|
|
50
50
|
* ```typescript
|
|
51
|
-
* import { createDefaultTools, createDefaultExecutors } from "@clinebot/core/
|
|
51
|
+
* import { createDefaultTools, createDefaultExecutors } from "@clinebot/core/node"
|
|
52
52
|
*
|
|
53
53
|
* const executors = createDefaultExecutors({
|
|
54
54
|
* bash: { timeoutMs: 60000 },
|
|
@@ -37,6 +37,11 @@ export {
|
|
|
37
37
|
type SearchExecutorOptions,
|
|
38
38
|
type WebFetchExecutorOptions,
|
|
39
39
|
} from "./executors/index.js";
|
|
40
|
+
export {
|
|
41
|
+
DEFAULT_MODEL_TOOL_ROUTING_RULES,
|
|
42
|
+
resolveToolRoutingConfig,
|
|
43
|
+
type ToolRoutingRule,
|
|
44
|
+
} from "./model-tool-routing.js";
|
|
40
45
|
// Presets
|
|
41
46
|
export {
|
|
42
47
|
createDefaultToolsWithPreset,
|
|
@@ -121,7 +126,7 @@ export interface CreateBuiltinToolsOptions
|
|
|
121
126
|
* @example
|
|
122
127
|
* ```typescript
|
|
123
128
|
* import { Agent } from "@clinebot/agents"
|
|
124
|
-
* import { createBuiltinTools } from "@clinebot/core/
|
|
129
|
+
* import { createBuiltinTools } from "@clinebot/core/node"
|
|
125
130
|
*
|
|
126
131
|
* const tools = createBuiltinTools({
|
|
127
132
|
* cwd: "/path/to/project",
|