@clinebot/core 0.0.0 → 0.0.2
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 +1 -1
- package/dist/default-tools/index.d.ts +1 -0
- 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.node.d.ts +1 -0
- package/dist/index.node.js +220 -0
- package/dist/server/index.d.ts +5 -3
- package/dist/server/index.js +244 -192
- 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 +19 -20
- package/src/default-tools/definitions.test.ts +130 -1
- package/src/default-tools/definitions.ts +6 -2
- 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/index.ts +5 -0
- 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/index.node.ts +1 -0
- package/src/index.ts +41 -2
- package/src/input/file-indexer.ts +28 -2
- 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/server/index.ts +40 -1
- 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/index.js +0 -220
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { CoreAgentMode } from "../types/config";
|
|
2
|
+
import type { DefaultToolName, DefaultToolsConfig } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export interface ToolRoutingRule {
|
|
5
|
+
/**
|
|
6
|
+
* Optional rule label for debugging and logs.
|
|
7
|
+
*/
|
|
8
|
+
name?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Which mode the rule applies to.
|
|
11
|
+
* @default "any"
|
|
12
|
+
*/
|
|
13
|
+
mode?: CoreAgentMode | "any";
|
|
14
|
+
/**
|
|
15
|
+
* Case-insensitive substrings that must match the model ID.
|
|
16
|
+
* When omitted/empty, the rule is not constrained by model ID.
|
|
17
|
+
*/
|
|
18
|
+
modelIdIncludes?: string[];
|
|
19
|
+
/**
|
|
20
|
+
* Case-insensitive substrings that must match the provider ID.
|
|
21
|
+
* When omitted/empty, the rule is not constrained by provider ID.
|
|
22
|
+
*/
|
|
23
|
+
providerIdIncludes?: string[];
|
|
24
|
+
/**
|
|
25
|
+
* Enable these tools when the rule matches.
|
|
26
|
+
*/
|
|
27
|
+
enableTools?: DefaultToolName[];
|
|
28
|
+
/**
|
|
29
|
+
* Disable these tools when the rule matches.
|
|
30
|
+
*/
|
|
31
|
+
disableTools?: DefaultToolName[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const TOOL_NAME_TO_FLAG: Record<
|
|
35
|
+
DefaultToolName,
|
|
36
|
+
keyof Pick<
|
|
37
|
+
DefaultToolsConfig,
|
|
38
|
+
| "enableReadFiles"
|
|
39
|
+
| "enableSearch"
|
|
40
|
+
| "enableBash"
|
|
41
|
+
| "enableWebFetch"
|
|
42
|
+
| "enableApplyPatch"
|
|
43
|
+
| "enableEditor"
|
|
44
|
+
| "enableSkills"
|
|
45
|
+
| "enableAskQuestion"
|
|
46
|
+
>
|
|
47
|
+
> = {
|
|
48
|
+
read_files: "enableReadFiles",
|
|
49
|
+
search_codebase: "enableSearch",
|
|
50
|
+
run_commands: "enableBash",
|
|
51
|
+
fetch_web_content: "enableWebFetch",
|
|
52
|
+
apply_patch: "enableApplyPatch",
|
|
53
|
+
editor: "enableEditor",
|
|
54
|
+
skills: "enableSkills",
|
|
55
|
+
ask_question: "enableAskQuestion",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const DEFAULT_MODEL_TOOL_ROUTING_RULES: ToolRoutingRule[] = [
|
|
59
|
+
{
|
|
60
|
+
name: "openai-native-use-apply-patch",
|
|
61
|
+
mode: "act",
|
|
62
|
+
providerIdIncludes: ["openai-native"],
|
|
63
|
+
enableTools: ["apply_patch"],
|
|
64
|
+
disableTools: ["editor"],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "codex-and-gpt-use-apply-patch",
|
|
68
|
+
mode: "act",
|
|
69
|
+
modelIdIncludes: ["codex", "gpt"],
|
|
70
|
+
enableTools: ["apply_patch"],
|
|
71
|
+
disableTools: ["editor"],
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
function matchesModelId(
|
|
76
|
+
modelId: string,
|
|
77
|
+
includes: string[] | undefined,
|
|
78
|
+
): boolean {
|
|
79
|
+
if (!includes || includes.length === 0) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
const normalizedModelId = modelId.toLowerCase();
|
|
83
|
+
return includes.some((value) =>
|
|
84
|
+
normalizedModelId.includes(value.toLowerCase()),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function matchesRule(
|
|
89
|
+
rule: ToolRoutingRule,
|
|
90
|
+
providerId: string,
|
|
91
|
+
modelId: string,
|
|
92
|
+
mode: CoreAgentMode,
|
|
93
|
+
): boolean {
|
|
94
|
+
if (rule.mode && rule.mode !== "any" && rule.mode !== mode) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return (
|
|
98
|
+
matchesModelId(providerId, rule.providerIdIncludes) &&
|
|
99
|
+
matchesModelId(modelId, rule.modelIdIncludes)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function resolveToolRoutingConfig(
|
|
104
|
+
providerId: string,
|
|
105
|
+
modelId: string,
|
|
106
|
+
mode: CoreAgentMode,
|
|
107
|
+
rules: ToolRoutingRule[] | undefined,
|
|
108
|
+
): Partial<DefaultToolsConfig> {
|
|
109
|
+
if (!rules || rules.length === 0) {
|
|
110
|
+
return {};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const toggles = new Map<DefaultToolName, boolean>();
|
|
114
|
+
|
|
115
|
+
for (const rule of rules) {
|
|
116
|
+
if (!matchesRule(rule, providerId, modelId, mode)) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
for (const toolName of rule.disableTools ?? []) {
|
|
120
|
+
toggles.set(toolName, false);
|
|
121
|
+
}
|
|
122
|
+
for (const toolName of rule.enableTools ?? []) {
|
|
123
|
+
toggles.set(toolName, true);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const config: Partial<DefaultToolsConfig> = {};
|
|
128
|
+
for (const [toolName, enabled] of toggles.entries()) {
|
|
129
|
+
config[TOOL_NAME_TO_FLAG[toolName]] = enabled;
|
|
130
|
+
}
|
|
131
|
+
return config;
|
|
132
|
+
}
|
|
@@ -18,8 +18,16 @@ const AbsolutePath = z
|
|
|
18
18
|
* Schema for read_files tool input
|
|
19
19
|
*/
|
|
20
20
|
export const ReadFilesInputSchema = z.object({
|
|
21
|
-
file_paths: z
|
|
21
|
+
file_paths: z
|
|
22
|
+
.array(AbsolutePath)
|
|
23
|
+
.describe(
|
|
24
|
+
"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.",
|
|
25
|
+
),
|
|
22
26
|
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Union schema for read_files tool input, allowing either a single string, an array of strings, or the full object schema
|
|
30
|
+
*/
|
|
23
31
|
export const ReadFilesInputUnionSchema = z.union([
|
|
24
32
|
ReadFilesInputSchema,
|
|
25
33
|
z.array(z.string()),
|
|
@@ -42,8 +50,12 @@ const CommandInputSchema = z.string();
|
|
|
42
50
|
export const RunCommandsInputSchema = z.object({
|
|
43
51
|
commands: z
|
|
44
52
|
.array(CommandInputSchema)
|
|
45
|
-
.describe("Array of shell commands to execute"),
|
|
53
|
+
.describe("Array of shell commands to execute."),
|
|
46
54
|
});
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Union schema for run_commands tool input. More flexible.
|
|
58
|
+
*/
|
|
47
59
|
export const RunCommandsInputUnionSchema = z.union([
|
|
48
60
|
RunCommandsInputSchema,
|
|
49
61
|
z.array(CommandInputSchema),
|
|
@@ -54,7 +66,7 @@ export const RunCommandsInputUnionSchema = z.union([
|
|
|
54
66
|
* Schema for a single web fetch request
|
|
55
67
|
*/
|
|
56
68
|
export const WebFetchRequestSchema = z.object({
|
|
57
|
-
url: z.
|
|
69
|
+
url: z.string().describe("The URL to fetch"),
|
|
58
70
|
prompt: z.string().min(2).describe("Analysis prompt for the fetched content"),
|
|
59
71
|
});
|
|
60
72
|
|
|
@@ -64,7 +76,7 @@ export const WebFetchRequestSchema = z.object({
|
|
|
64
76
|
export const FetchWebContentInputSchema = z.object({
|
|
65
77
|
requests: z
|
|
66
78
|
.array(WebFetchRequestSchema)
|
|
67
|
-
.describe("Array of web fetch requests"),
|
|
79
|
+
.describe("Array of the URLs for the web fetch requests"),
|
|
68
80
|
});
|
|
69
81
|
|
|
70
82
|
/**
|
|
@@ -74,62 +86,43 @@ export const EditFileInputSchema = z
|
|
|
74
86
|
.object({
|
|
75
87
|
command: z
|
|
76
88
|
.enum(["create", "str_replace", "insert"])
|
|
77
|
-
.describe(
|
|
78
|
-
"Editor command to execute: create, str_replace, insert, or undo_edit",
|
|
79
|
-
),
|
|
89
|
+
.describe("Editor command to execute: create, str_replace, insert"),
|
|
80
90
|
path: z.string().min(1).describe("Absolute file path"),
|
|
81
91
|
file_text: z
|
|
82
92
|
.string()
|
|
83
|
-
.
|
|
84
|
-
.describe("Full file content
|
|
93
|
+
.nullish()
|
|
94
|
+
.describe("Full file content required for 'create' command"),
|
|
85
95
|
old_str: z
|
|
86
96
|
.string()
|
|
87
|
-
.
|
|
88
|
-
.describe(
|
|
89
|
-
|
|
97
|
+
.nullish()
|
|
98
|
+
.describe(
|
|
99
|
+
"Exact text to replace (must match exactly once) for 'str_replace' command",
|
|
100
|
+
),
|
|
101
|
+
new_str: z
|
|
102
|
+
.string()
|
|
103
|
+
.nullish()
|
|
104
|
+
.describe("Replacement text for 'str_replace' or 'insert' commands"),
|
|
90
105
|
insert_line: z
|
|
91
106
|
.number()
|
|
92
107
|
.int()
|
|
93
|
-
.
|
|
94
|
-
.describe("
|
|
108
|
+
.nullish()
|
|
109
|
+
.describe("Optional one-based line index for 'insert' command"),
|
|
95
110
|
})
|
|
96
|
-
.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
path: ["old_str"],
|
|
112
|
-
message: "old_str is required for command=str_replace",
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
break;
|
|
116
|
-
case "insert":
|
|
117
|
-
if (value.insert_line === undefined) {
|
|
118
|
-
ctx.addIssue({
|
|
119
|
-
code: z.ZodIssueCode.custom,
|
|
120
|
-
path: ["insert_line"],
|
|
121
|
-
message: "insert_line is required for command=insert",
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
if (value.new_str === undefined) {
|
|
125
|
-
ctx.addIssue({
|
|
126
|
-
code: z.ZodIssueCode.custom,
|
|
127
|
-
path: ["new_str"],
|
|
128
|
-
message: "new_str is required for command=insert",
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
111
|
+
.refine((v) => v.command !== "create" || v.file_text != null, {
|
|
112
|
+
path: ["file_text"],
|
|
113
|
+
message: "file_text is required for command=create",
|
|
114
|
+
})
|
|
115
|
+
.refine((v) => v.command !== "str_replace" || v.old_str != null, {
|
|
116
|
+
path: ["old_str"],
|
|
117
|
+
message: "old_str is required for command=str_replace",
|
|
118
|
+
})
|
|
119
|
+
.refine((v) => v.command !== "insert" || v.insert_line != null, {
|
|
120
|
+
path: ["insert_line"],
|
|
121
|
+
message: "insert_line is required for command=insert",
|
|
122
|
+
})
|
|
123
|
+
.refine((v) => v.command !== "insert" || v.new_str != null, {
|
|
124
|
+
path: ["new_str"],
|
|
125
|
+
message: "new_str is required for command=insert",
|
|
133
126
|
});
|
|
134
127
|
|
|
135
128
|
/**
|
|
@@ -156,7 +149,11 @@ export const SkillsInputSchema = z.object({
|
|
|
156
149
|
.describe(
|
|
157
150
|
'The skill name. E.g., "commit", "review-pr", "pdf", or "ms-office-suite:pdf"',
|
|
158
151
|
),
|
|
159
|
-
args: z
|
|
152
|
+
args: z
|
|
153
|
+
.string()
|
|
154
|
+
.nullable()
|
|
155
|
+
.optional()
|
|
156
|
+
.describe("Arguments for the skill; use null when omitted"),
|
|
160
157
|
});
|
|
161
158
|
|
|
162
159
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./index";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./index";
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,47 @@
|
|
|
4
4
|
* Runtime-agnostic core contracts and shared state utilities.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
// Shared contracts and path helpers re-exported for app consumers.
|
|
8
|
+
export type {
|
|
9
|
+
AgentMode,
|
|
10
|
+
BasicLogger,
|
|
11
|
+
ConnectorHookEvent,
|
|
12
|
+
HookSessionContext,
|
|
13
|
+
RpcAddProviderActionRequest,
|
|
14
|
+
RpcChatMessage,
|
|
15
|
+
RpcChatRunTurnRequest,
|
|
16
|
+
RpcChatRuntimeConfigBase,
|
|
17
|
+
RpcChatRuntimeLoggerConfig,
|
|
18
|
+
RpcChatStartSessionArtifacts,
|
|
19
|
+
RpcChatStartSessionRequest,
|
|
20
|
+
RpcChatTurnResult,
|
|
21
|
+
RpcClineAccountActionRequest,
|
|
22
|
+
RpcOAuthProviderId,
|
|
23
|
+
RpcProviderActionRequest,
|
|
24
|
+
RpcProviderCapability,
|
|
25
|
+
RpcProviderCatalogResponse,
|
|
26
|
+
RpcProviderListItem,
|
|
27
|
+
RpcProviderModel,
|
|
28
|
+
RpcProviderOAuthLoginResponse,
|
|
29
|
+
RpcSaveProviderSettingsActionRequest,
|
|
30
|
+
SessionLineage,
|
|
31
|
+
TeamProgressProjectionEvent,
|
|
32
|
+
ToolPolicy,
|
|
33
|
+
} from "@clinebot/shared";
|
|
34
|
+
export {
|
|
35
|
+
normalizeUserInput,
|
|
36
|
+
RPC_TEAM_LIFECYCLE_EVENT_TYPE,
|
|
37
|
+
RPC_TEAM_PROGRESS_EVENT_TYPE,
|
|
38
|
+
resolveHookLogPath,
|
|
39
|
+
} from "@clinebot/shared";
|
|
40
|
+
export {
|
|
41
|
+
ensureHookLogDir,
|
|
42
|
+
ensureParentDir,
|
|
43
|
+
resolveClineDataDir,
|
|
44
|
+
resolveSessionDataDir,
|
|
45
|
+
setHomeDir,
|
|
46
|
+
setHomeDirIfUnset,
|
|
47
|
+
} from "@clinebot/shared/storage";
|
|
7
48
|
export {
|
|
8
49
|
type ClineAccountBalance,
|
|
9
50
|
type ClineAccountOperations,
|
|
@@ -76,7 +117,6 @@ export {
|
|
|
76
117
|
resolveDefaultMcpSettingsPath,
|
|
77
118
|
resolveMcpServerRegistrations,
|
|
78
119
|
} from "./mcp";
|
|
79
|
-
|
|
80
120
|
export { ProviderSettingsManager } from "./storage/provider-settings-manager";
|
|
81
121
|
export {
|
|
82
122
|
SqliteTeamStore,
|
|
@@ -86,7 +126,6 @@ export {
|
|
|
86
126
|
buildTeamProgressSummary,
|
|
87
127
|
toTeamProgressLifecycleEvent,
|
|
88
128
|
} from "./team";
|
|
89
|
-
|
|
90
129
|
// Compatibility barrel (legacy imports).
|
|
91
130
|
export type { RuntimeEnvironment, SessionEvent, StoredMessages } from "./types";
|
|
92
131
|
export type { SessionStatus } from "./types/common";
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { isMainThread, parentPort, Worker } from "node:worker_threads";
|
|
5
5
|
|
|
6
6
|
const DEFAULT_INDEX_TTL_MS = 15_000;
|
|
7
|
+
const WORKER_INDEX_REQUEST_TIMEOUT_MS = 1_000;
|
|
7
8
|
const DEFAULT_EXCLUDE_DIRS = new Set([
|
|
8
9
|
".git",
|
|
9
10
|
"node_modules",
|
|
@@ -196,7 +197,21 @@ class FileIndexWorkerClient {
|
|
|
196
197
|
requestIndex(cwd: string): Promise<string[]> {
|
|
197
198
|
const requestId = ++this.nextRequestId;
|
|
198
199
|
const result = new Promise<string[]>((resolve, reject) => {
|
|
199
|
-
|
|
200
|
+
const timeout = setTimeout(() => {
|
|
201
|
+
this.pending.delete(requestId);
|
|
202
|
+
reject(new Error("Timed out waiting for file index worker response"));
|
|
203
|
+
}, WORKER_INDEX_REQUEST_TIMEOUT_MS);
|
|
204
|
+
timeout.unref();
|
|
205
|
+
this.pending.set(requestId, {
|
|
206
|
+
resolve: (files) => {
|
|
207
|
+
clearTimeout(timeout);
|
|
208
|
+
resolve(files);
|
|
209
|
+
},
|
|
210
|
+
reject: (reason) => {
|
|
211
|
+
clearTimeout(timeout);
|
|
212
|
+
reject(reason);
|
|
213
|
+
},
|
|
214
|
+
});
|
|
200
215
|
});
|
|
201
216
|
|
|
202
217
|
const message: IndexRequestMessage = {
|
|
@@ -218,9 +233,20 @@ class FileIndexWorkerClient {
|
|
|
218
233
|
|
|
219
234
|
startWorkerServer();
|
|
220
235
|
|
|
221
|
-
|
|
236
|
+
let workerClient: FileIndexWorkerClient | null | undefined;
|
|
237
|
+
|
|
238
|
+
function getWorkerClient(): FileIndexWorkerClient | null {
|
|
239
|
+
if (!isMainThread) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
if (workerClient === undefined) {
|
|
243
|
+
workerClient = new FileIndexWorkerClient();
|
|
244
|
+
}
|
|
245
|
+
return workerClient;
|
|
246
|
+
}
|
|
222
247
|
|
|
223
248
|
async function buildIndexInBackground(cwd: string): Promise<Set<string>> {
|
|
249
|
+
const workerClient = getWorkerClient();
|
|
224
250
|
if (!workerClient) {
|
|
225
251
|
return buildIndex(cwd);
|
|
226
252
|
}
|
|
@@ -74,6 +74,75 @@ describe("DefaultRuntimeBuilder", () => {
|
|
|
74
74
|
expect(names).not.toContain("editor");
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
+
it("uses apply_patch instead of editor for codex/gpt model IDs in act mode", () => {
|
|
78
|
+
const runtime = new DefaultRuntimeBuilder().build({
|
|
79
|
+
config: {
|
|
80
|
+
providerId: "openai",
|
|
81
|
+
modelId: "openai/gpt-5.4",
|
|
82
|
+
apiKey: "key",
|
|
83
|
+
systemPrompt: "test",
|
|
84
|
+
cwd: process.cwd(),
|
|
85
|
+
mode: "act",
|
|
86
|
+
enableTools: true,
|
|
87
|
+
enableSpawnAgent: false,
|
|
88
|
+
enableAgentTeams: false,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const names = runtime.tools.map((tool) => tool.name);
|
|
93
|
+
expect(names).toContain("apply_patch");
|
|
94
|
+
expect(names).not.toContain("editor");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("keeps editor for non-codex/non-gpt model IDs in act mode", () => {
|
|
98
|
+
const runtime = new DefaultRuntimeBuilder().build({
|
|
99
|
+
config: {
|
|
100
|
+
providerId: "anthropic",
|
|
101
|
+
modelId: "claude-sonnet-4-6",
|
|
102
|
+
apiKey: "key",
|
|
103
|
+
systemPrompt: "test",
|
|
104
|
+
cwd: process.cwd(),
|
|
105
|
+
mode: "act",
|
|
106
|
+
enableTools: true,
|
|
107
|
+
enableSpawnAgent: false,
|
|
108
|
+
enableAgentTeams: false,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const names = runtime.tools.map((tool) => tool.name);
|
|
113
|
+
expect(names).toContain("editor");
|
|
114
|
+
expect(names).not.toContain("apply_patch");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("applies custom tool routing rules from session config", () => {
|
|
118
|
+
const runtime = new DefaultRuntimeBuilder().build({
|
|
119
|
+
config: {
|
|
120
|
+
providerId: "anthropic",
|
|
121
|
+
modelId: "claude-sonnet-4-6",
|
|
122
|
+
apiKey: "key",
|
|
123
|
+
systemPrompt: "test",
|
|
124
|
+
cwd: process.cwd(),
|
|
125
|
+
mode: "act",
|
|
126
|
+
enableTools: true,
|
|
127
|
+
enableSpawnAgent: false,
|
|
128
|
+
enableAgentTeams: false,
|
|
129
|
+
toolRoutingRules: [
|
|
130
|
+
{
|
|
131
|
+
mode: "act",
|
|
132
|
+
providerIdIncludes: ["anthropic"],
|
|
133
|
+
modelIdIncludes: ["claude"],
|
|
134
|
+
enableTools: ["apply_patch"],
|
|
135
|
+
disableTools: ["editor"],
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const names = runtime.tools.map((tool) => tool.name);
|
|
142
|
+
expect(names).toContain("apply_patch");
|
|
143
|
+
expect(names).not.toContain("editor");
|
|
144
|
+
});
|
|
145
|
+
|
|
77
146
|
it("omits builtin tools when disabled", () => {
|
|
78
147
|
const runtime = new DefaultRuntimeBuilder().build({
|
|
79
148
|
config: {
|
|
@@ -16,9 +16,12 @@ import {
|
|
|
16
16
|
} from "../agents";
|
|
17
17
|
import {
|
|
18
18
|
createBuiltinTools,
|
|
19
|
+
DEFAULT_MODEL_TOOL_ROUTING_RULES,
|
|
20
|
+
resolveToolRoutingConfig,
|
|
19
21
|
type SkillsExecutor,
|
|
20
22
|
type ToolExecutors,
|
|
21
23
|
ToolPresets,
|
|
24
|
+
type ToolRoutingRule,
|
|
22
25
|
} from "../default-tools";
|
|
23
26
|
import { SqliteTeamStore } from "../storage/sqlite-team-store";
|
|
24
27
|
import type { CoreAgentMode, CoreSessionConfig } from "../types/config";
|
|
@@ -47,15 +50,26 @@ export function createTeamName(): string {
|
|
|
47
50
|
|
|
48
51
|
function createBuiltinToolsList(
|
|
49
52
|
cwd: string,
|
|
53
|
+
providerId: string,
|
|
50
54
|
mode: CoreAgentMode,
|
|
55
|
+
modelId: string,
|
|
56
|
+
toolRoutingRules: ToolRoutingRule[] | undefined,
|
|
51
57
|
skillsExecutor?: SkillsExecutorWithMetadata,
|
|
52
58
|
executorOverrides?: Partial<ToolExecutors>,
|
|
53
59
|
): Tool[] {
|
|
54
60
|
const preset =
|
|
55
61
|
mode === "plan" ? ToolPresets.readonly : ToolPresets.development;
|
|
62
|
+
const toolRoutingConfig = resolveToolRoutingConfig(
|
|
63
|
+
providerId,
|
|
64
|
+
modelId,
|
|
65
|
+
mode,
|
|
66
|
+
toolRoutingRules ?? DEFAULT_MODEL_TOOL_ROUTING_RULES,
|
|
67
|
+
);
|
|
68
|
+
|
|
56
69
|
return createBuiltinTools({
|
|
57
70
|
cwd,
|
|
58
71
|
...preset,
|
|
72
|
+
...toolRoutingConfig,
|
|
59
73
|
enableSkills: !!skillsExecutor,
|
|
60
74
|
executors: {
|
|
61
75
|
...(skillsExecutor
|
|
@@ -338,7 +352,10 @@ export class DefaultRuntimeBuilder implements RuntimeBuilder {
|
|
|
338
352
|
tools.push(
|
|
339
353
|
...createBuiltinToolsList(
|
|
340
354
|
config.cwd,
|
|
355
|
+
config.providerId,
|
|
341
356
|
normalized.mode,
|
|
357
|
+
config.modelId,
|
|
358
|
+
config.toolRoutingRules,
|
|
342
359
|
skillsExecutor,
|
|
343
360
|
defaultToolExecutors,
|
|
344
361
|
),
|
|
@@ -416,7 +433,10 @@ export class DefaultRuntimeBuilder implements RuntimeBuilder {
|
|
|
416
433
|
? () =>
|
|
417
434
|
createBuiltinToolsList(
|
|
418
435
|
config.cwd,
|
|
436
|
+
config.providerId,
|
|
419
437
|
normalized.mode,
|
|
438
|
+
config.modelId,
|
|
439
|
+
config.toolRoutingRules,
|
|
420
440
|
skillsExecutor,
|
|
421
441
|
defaultToolExecutors,
|
|
422
442
|
)
|
|
@@ -45,6 +45,13 @@ function legacyBuildRuntimeEnvironment(
|
|
|
45
45
|
return tools;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
function normalizeParityToolNames(toolNames: string[]): string[] {
|
|
49
|
+
// Skills are discovered from user/workspace config and can appear in tests
|
|
50
|
+
// depending on the machine state. They are intentionally excluded from
|
|
51
|
+
// strict legacy parity checks.
|
|
52
|
+
return toolNames.filter((toolName) => toolName !== "skills");
|
|
53
|
+
}
|
|
54
|
+
|
|
48
55
|
function makeEmptyWorkspaceCwd(): string {
|
|
49
56
|
return mkdtempSync(join(tmpdir(), "runtime-parity-"));
|
|
50
57
|
}
|
|
@@ -71,8 +78,10 @@ describe("runtime tool parity", () => {
|
|
|
71
78
|
enableAgentTeams: false,
|
|
72
79
|
};
|
|
73
80
|
const createSpawnTool = makeSpawnTool;
|
|
74
|
-
const expected =
|
|
75
|
-
(
|
|
81
|
+
const expected = normalizeParityToolNames(
|
|
82
|
+
legacyBuildRuntimeEnvironment(config, createSpawnTool).map(
|
|
83
|
+
(tool) => tool.name,
|
|
84
|
+
),
|
|
76
85
|
);
|
|
77
86
|
const actual = new DefaultRuntimeBuilder()
|
|
78
87
|
.build({
|
|
@@ -81,7 +90,7 @@ describe("runtime tool parity", () => {
|
|
|
81
90
|
})
|
|
82
91
|
.tools.map((tool) => tool.name);
|
|
83
92
|
|
|
84
|
-
expect(actual).toEqual(expected);
|
|
93
|
+
expect(normalizeParityToolNames(actual)).toEqual(expected);
|
|
85
94
|
});
|
|
86
95
|
|
|
87
96
|
it("matches legacy tool list when only spawn is enabled", () => {
|
|
@@ -96,8 +105,10 @@ describe("runtime tool parity", () => {
|
|
|
96
105
|
enableAgentTeams: false,
|
|
97
106
|
};
|
|
98
107
|
const createSpawnTool = makeSpawnTool;
|
|
99
|
-
const expected =
|
|
100
|
-
(
|
|
108
|
+
const expected = normalizeParityToolNames(
|
|
109
|
+
legacyBuildRuntimeEnvironment(config, createSpawnTool).map(
|
|
110
|
+
(tool) => tool.name,
|
|
111
|
+
),
|
|
101
112
|
);
|
|
102
113
|
const actual = new DefaultRuntimeBuilder()
|
|
103
114
|
.build({
|
|
@@ -106,7 +117,7 @@ describe("runtime tool parity", () => {
|
|
|
106
117
|
})
|
|
107
118
|
.tools.map((tool) => tool.name);
|
|
108
119
|
|
|
109
|
-
expect(actual).toEqual(expected);
|
|
120
|
+
expect(normalizeParityToolNames(actual)).toEqual(expected);
|
|
110
121
|
});
|
|
111
122
|
|
|
112
123
|
it("matches legacy tool list when tools+spawn are disabled", () => {
|
|
@@ -120,13 +131,13 @@ describe("runtime tool parity", () => {
|
|
|
120
131
|
enableSpawnAgent: false,
|
|
121
132
|
enableAgentTeams: false,
|
|
122
133
|
};
|
|
123
|
-
const expected =
|
|
124
|
-
(tool) => tool.name,
|
|
134
|
+
const expected = normalizeParityToolNames(
|
|
135
|
+
legacyBuildRuntimeEnvironment(config).map((tool) => tool.name),
|
|
125
136
|
);
|
|
126
137
|
const actual = new DefaultRuntimeBuilder()
|
|
127
138
|
.build({ config })
|
|
128
139
|
.tools.map((tool) => tool.name);
|
|
129
140
|
|
|
130
|
-
expect(actual).toEqual(expected);
|
|
141
|
+
expect(normalizeParityToolNames(actual)).toEqual(expected);
|
|
131
142
|
});
|
|
132
143
|
});
|
package/src/server/index.ts
CHANGED
|
@@ -2,8 +2,42 @@
|
|
|
2
2
|
* @clinebot/core/server
|
|
3
3
|
*
|
|
4
4
|
* Node/runtime services for host applications (CLI, desktop runtime, servers).
|
|
5
|
+
* Includes RPC server/client bindings — requires @clinebot/rpc at runtime.
|
|
5
6
|
*/
|
|
6
7
|
|
|
8
|
+
export {
|
|
9
|
+
getRpcServerDefaultAddress,
|
|
10
|
+
getRpcServerHealth,
|
|
11
|
+
type RpcRuntimeBridgeCommand,
|
|
12
|
+
type RpcRuntimeBridgeCommandOutputLine,
|
|
13
|
+
type RpcRuntimeBridgeControlLine,
|
|
14
|
+
type RpcRuntimeBridgeOutputLine,
|
|
15
|
+
type RpcRuntimeBridgeRequestEnvelope,
|
|
16
|
+
type RpcRuntimeBridgeResponseEnvelope,
|
|
17
|
+
RpcRuntimeChatClient,
|
|
18
|
+
type RpcRuntimeEvent,
|
|
19
|
+
type RpcRuntimeHandlers,
|
|
20
|
+
type RpcRuntimeStreamStop,
|
|
21
|
+
type RpcScheduleExecution,
|
|
22
|
+
type RpcScheduleExecutionStatus,
|
|
23
|
+
type RpcScheduleMode,
|
|
24
|
+
type RpcScheduleRecord,
|
|
25
|
+
type RpcServerHandle,
|
|
26
|
+
type RpcServerOptions,
|
|
27
|
+
type RpcSessionBackend,
|
|
28
|
+
RpcSessionClient,
|
|
29
|
+
type RpcSessionRow,
|
|
30
|
+
type RpcSessionStatus,
|
|
31
|
+
type RpcSessionUpdateInput,
|
|
32
|
+
type RpcSpawnQueueItem,
|
|
33
|
+
type RpcStreamTeamProgressHandlers,
|
|
34
|
+
registerRpcClient,
|
|
35
|
+
requestRpcServerShutdown,
|
|
36
|
+
runRpcRuntimeCommandBridge,
|
|
37
|
+
runRpcRuntimeEventBridge,
|
|
38
|
+
startRpcServer,
|
|
39
|
+
stopRpcServer,
|
|
40
|
+
} from "@clinebot/rpc";
|
|
7
41
|
export {
|
|
8
42
|
type ClineAccountBalance,
|
|
9
43
|
type ClineAccountOperations,
|
|
@@ -229,11 +263,16 @@ export {
|
|
|
229
263
|
} from "../session/session-graph";
|
|
230
264
|
export type {
|
|
231
265
|
CreateSessionHostOptions,
|
|
266
|
+
SessionBackend,
|
|
232
267
|
SessionHost,
|
|
233
268
|
} from "../session/session-host";
|
|
234
|
-
export {
|
|
269
|
+
export {
|
|
270
|
+
createSessionHost,
|
|
271
|
+
resolveSessionBackend,
|
|
272
|
+
} from "../session/session-host";
|
|
235
273
|
export type {
|
|
236
274
|
SendSessionInput,
|
|
275
|
+
SessionAccumulatedUsage,
|
|
237
276
|
SessionManager,
|
|
238
277
|
StartSessionInput,
|
|
239
278
|
StartSessionResult,
|