@kimbho/kimbho-cli 0.1.4 → 0.1.5
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 +14 -3
- package/dist/index.cjs +2187 -1202
- package/dist/index.cjs.map +4 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1147,7 +1147,7 @@ var require_command = __commonJS({
|
|
|
1147
1147
|
"../../node_modules/commander/lib/command.js"(exports2) {
|
|
1148
1148
|
var EventEmitter = require("node:events").EventEmitter;
|
|
1149
1149
|
var childProcess = require("node:child_process");
|
|
1150
|
-
var
|
|
1150
|
+
var path7 = require("node:path");
|
|
1151
1151
|
var fs = require("node:fs");
|
|
1152
1152
|
var process12 = require("node:process");
|
|
1153
1153
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
@@ -2147,9 +2147,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2147
2147
|
let launchWithNode = false;
|
|
2148
2148
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2149
2149
|
function findFile(baseDir, baseName) {
|
|
2150
|
-
const localBin =
|
|
2150
|
+
const localBin = path7.resolve(baseDir, baseName);
|
|
2151
2151
|
if (fs.existsSync(localBin)) return localBin;
|
|
2152
|
-
if (sourceExt.includes(
|
|
2152
|
+
if (sourceExt.includes(path7.extname(baseName))) return void 0;
|
|
2153
2153
|
const foundExt = sourceExt.find(
|
|
2154
2154
|
(ext) => fs.existsSync(`${localBin}${ext}`)
|
|
2155
2155
|
);
|
|
@@ -2167,17 +2167,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2167
2167
|
} catch {
|
|
2168
2168
|
resolvedScriptPath = this._scriptPath;
|
|
2169
2169
|
}
|
|
2170
|
-
executableDir =
|
|
2171
|
-
|
|
2170
|
+
executableDir = path7.resolve(
|
|
2171
|
+
path7.dirname(resolvedScriptPath),
|
|
2172
2172
|
executableDir
|
|
2173
2173
|
);
|
|
2174
2174
|
}
|
|
2175
2175
|
if (executableDir) {
|
|
2176
2176
|
let localFile = findFile(executableDir, executableFile);
|
|
2177
2177
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2178
|
-
const legacyName =
|
|
2178
|
+
const legacyName = path7.basename(
|
|
2179
2179
|
this._scriptPath,
|
|
2180
|
-
|
|
2180
|
+
path7.extname(this._scriptPath)
|
|
2181
2181
|
);
|
|
2182
2182
|
if (legacyName !== this._name) {
|
|
2183
2183
|
localFile = findFile(
|
|
@@ -2188,7 +2188,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2188
2188
|
}
|
|
2189
2189
|
executableFile = localFile || executableFile;
|
|
2190
2190
|
}
|
|
2191
|
-
launchWithNode = sourceExt.includes(
|
|
2191
|
+
launchWithNode = sourceExt.includes(path7.extname(executableFile));
|
|
2192
2192
|
let proc;
|
|
2193
2193
|
if (process12.platform !== "win32") {
|
|
2194
2194
|
if (launchWithNode) {
|
|
@@ -3035,7 +3035,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3035
3035
|
* @return {Command}
|
|
3036
3036
|
*/
|
|
3037
3037
|
nameFromFilename(filename) {
|
|
3038
|
-
this._name =
|
|
3038
|
+
this._name = path7.basename(filename, path7.extname(filename));
|
|
3039
3039
|
return this;
|
|
3040
3040
|
}
|
|
3041
3041
|
/**
|
|
@@ -3049,9 +3049,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3049
3049
|
* @param {string} [path]
|
|
3050
3050
|
* @return {(string|null|Command)}
|
|
3051
3051
|
*/
|
|
3052
|
-
executableDir(
|
|
3053
|
-
if (
|
|
3054
|
-
this._executableDir =
|
|
3052
|
+
executableDir(path8) {
|
|
3053
|
+
if (path8 === void 0) return this._executableDir;
|
|
3054
|
+
this._executableDir = path8;
|
|
3055
3055
|
return this;
|
|
3056
3056
|
}
|
|
3057
3057
|
/**
|
|
@@ -3346,7 +3346,7 @@ var {
|
|
|
3346
3346
|
// package.json
|
|
3347
3347
|
var package_default = {
|
|
3348
3348
|
name: "@kimbho/kimbho-cli",
|
|
3349
|
-
version: "0.1.
|
|
3349
|
+
version: "0.1.5",
|
|
3350
3350
|
description: "Kimbho CLI is a terminal-native coding agent for planning, execution, and verification.",
|
|
3351
3351
|
type: "module",
|
|
3352
3352
|
engines: {
|
|
@@ -3414,9 +3414,12 @@ var AGENT_CATALOG = {
|
|
|
3414
3414
|
brainRole: "fast",
|
|
3415
3415
|
purpose: "Map the workspace, commands, conventions, and likely risk areas before implementation.",
|
|
3416
3416
|
defaultTools: [
|
|
3417
|
+
"file.list",
|
|
3418
|
+
"file.search",
|
|
3417
3419
|
"file.read",
|
|
3418
3420
|
"shell.exec",
|
|
3419
|
-
"git.status"
|
|
3421
|
+
"git.status",
|
|
3422
|
+
"git.diff"
|
|
3420
3423
|
],
|
|
3421
3424
|
escalationTriggers: [
|
|
3422
3425
|
"Unknown build entry points",
|
|
@@ -3429,6 +3432,8 @@ var AGENT_CATALOG = {
|
|
|
3429
3432
|
brainRole: "planner",
|
|
3430
3433
|
purpose: "Generate machine-readable plans, milestone graphs, and re-plan when reality changes.",
|
|
3431
3434
|
defaultTools: [
|
|
3435
|
+
"file.list",
|
|
3436
|
+
"file.search",
|
|
3432
3437
|
"file.read"
|
|
3433
3438
|
],
|
|
3434
3439
|
escalationTriggers: [
|
|
@@ -3442,9 +3447,12 @@ var AGENT_CATALOG = {
|
|
|
3442
3447
|
brainRole: "fast",
|
|
3443
3448
|
purpose: "Select the next ready tasks, assign them to specialists, and supervise completion.",
|
|
3444
3449
|
defaultTools: [
|
|
3450
|
+
"file.list",
|
|
3451
|
+
"file.search",
|
|
3445
3452
|
"file.read",
|
|
3446
3453
|
"shell.exec",
|
|
3447
|
-
"tests.run"
|
|
3454
|
+
"tests.run",
|
|
3455
|
+
"git.diff"
|
|
3448
3456
|
],
|
|
3449
3457
|
escalationTriggers: [
|
|
3450
3458
|
"Blocked dependency chains",
|
|
@@ -3457,8 +3465,12 @@ var AGENT_CATALOG = {
|
|
|
3457
3465
|
brainRole: "coder",
|
|
3458
3466
|
purpose: "Implement and refine user-facing flows and integration points.",
|
|
3459
3467
|
defaultTools: [
|
|
3468
|
+
"file.list",
|
|
3469
|
+
"file.search",
|
|
3460
3470
|
"file.read",
|
|
3461
|
-
"file.patch"
|
|
3471
|
+
"file.patch",
|
|
3472
|
+
"git.diff",
|
|
3473
|
+
"tests.run"
|
|
3462
3474
|
],
|
|
3463
3475
|
escalationTriggers: [
|
|
3464
3476
|
"Design-system mismatch",
|
|
@@ -3471,9 +3483,13 @@ var AGENT_CATALOG = {
|
|
|
3471
3483
|
brainRole: "coder",
|
|
3472
3484
|
purpose: "Implement backend behavior, contracts, and domain logic.",
|
|
3473
3485
|
defaultTools: [
|
|
3486
|
+
"file.list",
|
|
3487
|
+
"file.search",
|
|
3474
3488
|
"file.read",
|
|
3475
3489
|
"file.patch",
|
|
3476
|
-
"shell.exec"
|
|
3490
|
+
"shell.exec",
|
|
3491
|
+
"git.diff",
|
|
3492
|
+
"tests.run"
|
|
3477
3493
|
],
|
|
3478
3494
|
escalationTriggers: [
|
|
3479
3495
|
"Schema drift",
|
|
@@ -3486,9 +3502,13 @@ var AGENT_CATALOG = {
|
|
|
3486
3502
|
brainRole: "coder",
|
|
3487
3503
|
purpose: "Own schema design, migrations, and persistence safety.",
|
|
3488
3504
|
defaultTools: [
|
|
3505
|
+
"file.list",
|
|
3506
|
+
"file.search",
|
|
3489
3507
|
"file.read",
|
|
3490
3508
|
"file.patch",
|
|
3491
|
-
"shell.exec"
|
|
3509
|
+
"shell.exec",
|
|
3510
|
+
"git.diff",
|
|
3511
|
+
"tests.run"
|
|
3492
3512
|
],
|
|
3493
3513
|
escalationTriggers: [
|
|
3494
3514
|
"Data-destructive migration",
|
|
@@ -3501,9 +3521,13 @@ var AGENT_CATALOG = {
|
|
|
3501
3521
|
brainRole: "coder",
|
|
3502
3522
|
purpose: "Own local toolchain, packaging, CI, and environment plumbing.",
|
|
3503
3523
|
defaultTools: [
|
|
3524
|
+
"file.list",
|
|
3525
|
+
"file.search",
|
|
3504
3526
|
"file.read",
|
|
3505
3527
|
"file.patch",
|
|
3506
|
-
"shell.exec"
|
|
3528
|
+
"shell.exec",
|
|
3529
|
+
"git.diff",
|
|
3530
|
+
"tests.run"
|
|
3507
3531
|
],
|
|
3508
3532
|
escalationTriggers: [
|
|
3509
3533
|
"Dependency installation",
|
|
@@ -3516,9 +3540,12 @@ var AGENT_CATALOG = {
|
|
|
3516
3540
|
brainRole: "coder",
|
|
3517
3541
|
purpose: "Run verification, interpret failures, and produce targeted repair loops.",
|
|
3518
3542
|
defaultTools: [
|
|
3543
|
+
"file.list",
|
|
3544
|
+
"file.search",
|
|
3519
3545
|
"file.read",
|
|
3520
3546
|
"shell.exec",
|
|
3521
|
-
"tests.run"
|
|
3547
|
+
"tests.run",
|
|
3548
|
+
"git.diff"
|
|
3522
3549
|
],
|
|
3523
3550
|
escalationTriggers: [
|
|
3524
3551
|
"Flaky verification",
|
|
@@ -3531,7 +3558,10 @@ var AGENT_CATALOG = {
|
|
|
3531
3558
|
brainRole: "reviewer",
|
|
3532
3559
|
purpose: "Inspect diffs for correctness, risk, and missing coverage before integration.",
|
|
3533
3560
|
defaultTools: [
|
|
3561
|
+
"file.list",
|
|
3562
|
+
"file.search",
|
|
3534
3563
|
"file.read",
|
|
3564
|
+
"git.diff",
|
|
3535
3565
|
"git.status",
|
|
3536
3566
|
"tests.run"
|
|
3537
3567
|
],
|
|
@@ -3546,8 +3576,11 @@ var AGENT_CATALOG = {
|
|
|
3546
3576
|
brainRole: "coder",
|
|
3547
3577
|
purpose: "Combine accepted work, preserve repo consistency, and finalize the handoff state.",
|
|
3548
3578
|
defaultTools: [
|
|
3579
|
+
"file.list",
|
|
3580
|
+
"file.search",
|
|
3549
3581
|
"file.read",
|
|
3550
3582
|
"file.patch",
|
|
3583
|
+
"git.diff",
|
|
3551
3584
|
"git.status",
|
|
3552
3585
|
"tests.run"
|
|
3553
3586
|
],
|
|
@@ -3562,7 +3595,7 @@ function listAgentProfiles() {
|
|
|
3562
3595
|
return Object.values(AGENT_CATALOG).sort((left, right) => left.role.localeCompare(right.role));
|
|
3563
3596
|
}
|
|
3564
3597
|
|
|
3565
|
-
// ../agent-runtime/dist/
|
|
3598
|
+
// ../agent-runtime/dist/autonomous.js
|
|
3566
3599
|
var import_promises4 = require("node:fs/promises");
|
|
3567
3600
|
var import_node_path4 = __toESM(require("node:path"), 1);
|
|
3568
3601
|
|
|
@@ -4044,8 +4077,8 @@ function getErrorMap() {
|
|
|
4044
4077
|
|
|
4045
4078
|
// ../../node_modules/zod/v3/helpers/parseUtil.js
|
|
4046
4079
|
var makeIssue = (params) => {
|
|
4047
|
-
const { data, path:
|
|
4048
|
-
const fullPath = [...
|
|
4080
|
+
const { data, path: path7, errorMaps, issueData } = params;
|
|
4081
|
+
const fullPath = [...path7, ...issueData.path || []];
|
|
4049
4082
|
const fullIssue = {
|
|
4050
4083
|
...issueData,
|
|
4051
4084
|
path: fullPath
|
|
@@ -4161,11 +4194,11 @@ var errorUtil;
|
|
|
4161
4194
|
|
|
4162
4195
|
// ../../node_modules/zod/v3/types.js
|
|
4163
4196
|
var ParseInputLazyPath = class {
|
|
4164
|
-
constructor(parent, value,
|
|
4197
|
+
constructor(parent, value, path7, key) {
|
|
4165
4198
|
this._cachedPath = [];
|
|
4166
4199
|
this.parent = parent;
|
|
4167
4200
|
this.data = value;
|
|
4168
|
-
this._path =
|
|
4201
|
+
this._path = path7;
|
|
4169
4202
|
this._key = key;
|
|
4170
4203
|
}
|
|
4171
4204
|
get path() {
|
|
@@ -7837,6 +7870,8 @@ var SessionEventTypeSchema = external_exports.enum([
|
|
|
7837
7870
|
"task-started",
|
|
7838
7871
|
"task-completed",
|
|
7839
7872
|
"task-blocked",
|
|
7873
|
+
"task-handed-off",
|
|
7874
|
+
"task-paused",
|
|
7840
7875
|
"note"
|
|
7841
7876
|
]);
|
|
7842
7877
|
var SessionEventSchema = external_exports.object({
|
|
@@ -8164,1294 +8199,2137 @@ async function loadLatestSession(cwd = process.cwd()) {
|
|
|
8164
8199
|
return loadSession(import_node_path2.default.join(sessionsDir, latest));
|
|
8165
8200
|
}
|
|
8166
8201
|
|
|
8167
|
-
// ../
|
|
8168
|
-
var
|
|
8169
|
-
"safe",
|
|
8170
|
-
"approval-required",
|
|
8171
|
-
"destructive"
|
|
8172
|
-
]);
|
|
8173
|
-
var ToolDescriptorSchema = external_exports.object({
|
|
8174
|
-
id: external_exports.string().min(1),
|
|
8175
|
-
description: external_exports.string().min(1),
|
|
8176
|
-
permission: ToolPermissionSchema.default("safe"),
|
|
8177
|
-
timeoutMs: external_exports.number().int().positive(),
|
|
8178
|
-
retryable: external_exports.boolean().default(false),
|
|
8179
|
-
producesArtifacts: external_exports.boolean().default(false),
|
|
8180
|
-
allowedRoles: external_exports.array(AgentRoleSchema).default([])
|
|
8181
|
-
});
|
|
8182
|
-
|
|
8183
|
-
// ../tools/dist/registry.js
|
|
8184
|
-
var BUILTIN_TOOLS = [
|
|
8202
|
+
// ../brains/dist/templates.js
|
|
8203
|
+
var BUILTIN_PROVIDER_TEMPLATES = [
|
|
8185
8204
|
{
|
|
8186
|
-
id: "
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
"session-orchestrator",
|
|
8194
|
-
"repo-analyst",
|
|
8195
|
-
"planner",
|
|
8196
|
-
"execution-manager",
|
|
8197
|
-
"frontend-specialist",
|
|
8198
|
-
"backend-specialist",
|
|
8199
|
-
"database-specialist",
|
|
8200
|
-
"infra-specialist",
|
|
8201
|
-
"test-debugger",
|
|
8202
|
-
"reviewer",
|
|
8203
|
-
"integrator"
|
|
8204
|
-
]
|
|
8205
|
+
id: "openai",
|
|
8206
|
+
label: "OpenAI",
|
|
8207
|
+
driver: "openai-responses",
|
|
8208
|
+
defaultBaseUrl: "https://api.openai.com/v1",
|
|
8209
|
+
defaultApiKeyEnv: "OPENAI_API_KEY",
|
|
8210
|
+
defaultModel: "gpt-5",
|
|
8211
|
+
notes: "Uses the OpenAI Responses API."
|
|
8205
8212
|
},
|
|
8206
8213
|
{
|
|
8207
|
-
id: "
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
allowedRoles: [
|
|
8214
|
-
"frontend-specialist",
|
|
8215
|
-
"backend-specialist",
|
|
8216
|
-
"database-specialist",
|
|
8217
|
-
"infra-specialist",
|
|
8218
|
-
"test-debugger",
|
|
8219
|
-
"integrator"
|
|
8220
|
-
]
|
|
8214
|
+
id: "anthropic",
|
|
8215
|
+
label: "Anthropic",
|
|
8216
|
+
driver: "anthropic-messages",
|
|
8217
|
+
defaultBaseUrl: "https://api.anthropic.com/v1/messages",
|
|
8218
|
+
defaultApiKeyEnv: "ANTHROPIC_API_KEY",
|
|
8219
|
+
notes: "Uses the Anthropic Messages API."
|
|
8221
8220
|
},
|
|
8222
8221
|
{
|
|
8223
|
-
id: "
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
allowedRoles: [
|
|
8230
|
-
"repo-analyst",
|
|
8231
|
-
"execution-manager",
|
|
8232
|
-
"backend-specialist",
|
|
8233
|
-
"infra-specialist",
|
|
8234
|
-
"test-debugger",
|
|
8235
|
-
"reviewer",
|
|
8236
|
-
"integrator"
|
|
8237
|
-
]
|
|
8222
|
+
id: "openrouter",
|
|
8223
|
+
label: "OpenRouter",
|
|
8224
|
+
driver: "openai-compatible",
|
|
8225
|
+
defaultBaseUrl: "https://openrouter.ai/api/v1",
|
|
8226
|
+
defaultApiKeyEnv: "OPENROUTER_API_KEY",
|
|
8227
|
+
notes: "Routes through the OpenAI-compatible OpenRouter API."
|
|
8238
8228
|
},
|
|
8239
8229
|
{
|
|
8240
|
-
id: "
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
allowedRoles: [
|
|
8247
|
-
"session-orchestrator",
|
|
8248
|
-
"repo-analyst",
|
|
8249
|
-
"execution-manager",
|
|
8250
|
-
"reviewer",
|
|
8251
|
-
"integrator"
|
|
8252
|
-
]
|
|
8230
|
+
id: "ollama",
|
|
8231
|
+
label: "Ollama",
|
|
8232
|
+
driver: "ollama",
|
|
8233
|
+
defaultBaseUrl: "http://localhost:11434",
|
|
8234
|
+
defaultModel: "qwen2.5-coder:7b",
|
|
8235
|
+
notes: "Runs against a local Ollama server."
|
|
8253
8236
|
},
|
|
8254
8237
|
{
|
|
8255
|
-
id: "
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
producesArtifacts: true,
|
|
8261
|
-
allowedRoles: [
|
|
8262
|
-
"execution-manager",
|
|
8263
|
-
"test-debugger",
|
|
8264
|
-
"reviewer",
|
|
8265
|
-
"integrator"
|
|
8266
|
-
]
|
|
8238
|
+
id: "lmstudio",
|
|
8239
|
+
label: "LM Studio",
|
|
8240
|
+
driver: "openai-compatible",
|
|
8241
|
+
defaultBaseUrl: "http://localhost:1234/v1",
|
|
8242
|
+
notes: "Works with LM Studio's local OpenAI-compatible server."
|
|
8267
8243
|
}
|
|
8268
8244
|
];
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8245
|
+
function listProviderTemplates() {
|
|
8246
|
+
return BUILTIN_PROVIDER_TEMPLATES.slice().sort((left, right) => left.id.localeCompare(right.id));
|
|
8247
|
+
}
|
|
8248
|
+
function findProviderTemplate(templateId) {
|
|
8249
|
+
return BUILTIN_PROVIDER_TEMPLATES.find((template) => template.id === templateId);
|
|
8250
|
+
}
|
|
8251
|
+
function trimTrailingSlash(value) {
|
|
8252
|
+
return value.replace(/\/+$/, "");
|
|
8253
|
+
}
|
|
8254
|
+
function normalizeTemplateBaseUrl(templateId, baseUrl) {
|
|
8255
|
+
if (!baseUrl) {
|
|
8256
|
+
return void 0;
|
|
8276
8257
|
}
|
|
8277
|
-
|
|
8278
|
-
|
|
8258
|
+
const normalized = trimTrailingSlash(baseUrl);
|
|
8259
|
+
if (templateId !== "lmstudio") {
|
|
8260
|
+
return normalized;
|
|
8279
8261
|
}
|
|
8280
|
-
|
|
8281
|
-
|
|
8262
|
+
try {
|
|
8263
|
+
const parsed = new URL(normalized);
|
|
8264
|
+
if (!parsed.pathname || parsed.pathname === "/") {
|
|
8265
|
+
parsed.pathname = "/v1";
|
|
8266
|
+
return trimTrailingSlash(parsed.toString());
|
|
8267
|
+
}
|
|
8268
|
+
} catch {
|
|
8269
|
+
return normalized;
|
|
8282
8270
|
}
|
|
8283
|
-
|
|
8284
|
-
|
|
8271
|
+
return normalized;
|
|
8272
|
+
}
|
|
8273
|
+
function buildProviderFromTemplate(templateId, options = {}) {
|
|
8274
|
+
const template = findProviderTemplate(templateId);
|
|
8275
|
+
if (!template) {
|
|
8276
|
+
throw new Error(`Unknown provider template "${templateId}".`);
|
|
8285
8277
|
}
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8278
|
+
return ProviderDefinitionSchema.parse({
|
|
8279
|
+
id: options.providerId ?? template.id,
|
|
8280
|
+
label: options.label ?? template.label,
|
|
8281
|
+
driver: template.driver,
|
|
8282
|
+
...normalizeTemplateBaseUrl(templateId, options.baseUrl) ?? template.defaultBaseUrl ? {
|
|
8283
|
+
baseUrl: normalizeTemplateBaseUrl(templateId, options.baseUrl) ?? template.defaultBaseUrl
|
|
8284
|
+
} : {},
|
|
8285
|
+
...options.apiKeyEnv ?? template.defaultApiKeyEnv ? {
|
|
8286
|
+
apiKeyEnv: options.apiKeyEnv ?? template.defaultApiKeyEnv
|
|
8287
|
+
} : {},
|
|
8288
|
+
...options.model ?? template.defaultModel ? {
|
|
8289
|
+
defaultModel: options.model ?? template.defaultModel
|
|
8290
|
+
} : {},
|
|
8291
|
+
...options.models && options.models.length > 0 ? {
|
|
8292
|
+
models: options.models
|
|
8293
|
+
} : {}
|
|
8294
|
+
});
|
|
8289
8295
|
}
|
|
8290
8296
|
|
|
8291
|
-
// ../
|
|
8297
|
+
// ../brains/dist/registry.js
|
|
8292
8298
|
var import_promises3 = require("node:fs/promises");
|
|
8293
8299
|
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
8294
|
-
var
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
function truncateOutput(value) {
|
|
8299
|
-
if (!value) {
|
|
8300
|
-
return value;
|
|
8301
|
-
}
|
|
8302
|
-
if (value.length <= DEFAULT_CAPTURE_LIMIT) {
|
|
8303
|
-
return value;
|
|
8300
|
+
var import_node_url = require("node:url");
|
|
8301
|
+
function resolveApiKey(definition) {
|
|
8302
|
+
if (!definition.apiKeyEnv) {
|
|
8303
|
+
return null;
|
|
8304
8304
|
}
|
|
8305
|
-
|
|
8306
|
-
return `${value.slice(0, DEFAULT_CAPTURE_LIMIT)}
|
|
8307
|
-
... [truncated ${omitted} chars]`;
|
|
8305
|
+
return process.env[definition.apiKeyEnv] ?? null;
|
|
8308
8306
|
}
|
|
8309
|
-
function
|
|
8310
|
-
const
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8307
|
+
function makeAbortSignal(timeoutMs) {
|
|
8308
|
+
const controller = new AbortController();
|
|
8309
|
+
setTimeout(() => controller.abort(), timeoutMs).unref?.();
|
|
8310
|
+
return controller.signal;
|
|
8311
|
+
}
|
|
8312
|
+
var GENERATION_TIMEOUT_MS = 12e4;
|
|
8313
|
+
async function requestJson(url, init, timeoutMs = 3e4) {
|
|
8314
|
+
const response = await fetch(url, {
|
|
8315
|
+
...init,
|
|
8316
|
+
signal: makeAbortSignal(timeoutMs)
|
|
8317
|
+
});
|
|
8318
|
+
if (!response.ok) {
|
|
8319
|
+
throw new Error(`Request failed with ${response.status} ${response.statusText}`);
|
|
8314
8320
|
}
|
|
8315
|
-
return
|
|
8321
|
+
return response.json();
|
|
8316
8322
|
}
|
|
8317
|
-
|
|
8318
|
-
return
|
|
8319
|
-
const child = (0, import_node_child_process.spawn)(command, args, {
|
|
8320
|
-
cwd,
|
|
8321
|
-
env: import_node_process.default.env,
|
|
8322
|
-
stdio: [
|
|
8323
|
-
"ignore",
|
|
8324
|
-
"pipe",
|
|
8325
|
-
"pipe"
|
|
8326
|
-
]
|
|
8327
|
-
});
|
|
8328
|
-
const stdout = [];
|
|
8329
|
-
const stderr = [];
|
|
8330
|
-
let settled = false;
|
|
8331
|
-
let timedOut = false;
|
|
8332
|
-
const timer = setTimeout(() => {
|
|
8333
|
-
timedOut = true;
|
|
8334
|
-
child.kill("SIGTERM");
|
|
8335
|
-
setTimeout(() => child.kill("SIGKILL"), 1e3).unref();
|
|
8336
|
-
}, timeoutMs);
|
|
8337
|
-
child.stdout.on("data", (chunk) => {
|
|
8338
|
-
stdout.push(String(chunk));
|
|
8339
|
-
});
|
|
8340
|
-
child.stderr.on("data", (chunk) => {
|
|
8341
|
-
stderr.push(String(chunk));
|
|
8342
|
-
});
|
|
8343
|
-
child.on("error", (error) => {
|
|
8344
|
-
if (settled) {
|
|
8345
|
-
return;
|
|
8346
|
-
}
|
|
8347
|
-
settled = true;
|
|
8348
|
-
clearTimeout(timer);
|
|
8349
|
-
reject(error);
|
|
8350
|
-
});
|
|
8351
|
-
child.on("close", (code) => {
|
|
8352
|
-
if (settled) {
|
|
8353
|
-
return;
|
|
8354
|
-
}
|
|
8355
|
-
settled = true;
|
|
8356
|
-
clearTimeout(timer);
|
|
8357
|
-
resolve({
|
|
8358
|
-
code,
|
|
8359
|
-
stdout: stdout.join(""),
|
|
8360
|
-
stderr: stderr.join(""),
|
|
8361
|
-
timedOut
|
|
8362
|
-
});
|
|
8363
|
-
});
|
|
8364
|
-
});
|
|
8323
|
+
function trimTrailingSlash2(value) {
|
|
8324
|
+
return value.replace(/\/+$/, "");
|
|
8365
8325
|
}
|
|
8366
|
-
|
|
8367
|
-
const
|
|
8368
|
-
const
|
|
8369
|
-
|
|
8370
|
-
command
|
|
8371
|
-
], cwd, timeoutMs);
|
|
8372
|
-
const success = !result.timedOut && result.code === 0;
|
|
8373
|
-
const summary = result.timedOut ? `Command timed out after ${timeoutMs}ms.` : success ? `Command completed successfully.` : `Command exited with code ${result.code ?? "unknown"}.`;
|
|
8374
|
-
return ToolResultSchema.parse({
|
|
8375
|
-
toolId,
|
|
8376
|
-
success,
|
|
8377
|
-
summary,
|
|
8378
|
-
stdout: truncateOutput(result.stdout),
|
|
8379
|
-
stderr: truncateOutput(result.stderr)
|
|
8380
|
-
});
|
|
8326
|
+
function joinUrl(baseUrl, suffix) {
|
|
8327
|
+
const normalizedBase = trimTrailingSlash2(baseUrl);
|
|
8328
|
+
const normalizedSuffix = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
8329
|
+
return `${normalizedBase}${normalizedSuffix}`;
|
|
8381
8330
|
}
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
if (!rawPath) {
|
|
8385
|
-
throw new Error("file.read requires a string path.");
|
|
8386
|
-
}
|
|
8387
|
-
const targetPath = resolveWorkspacePath(context.cwd, rawPath);
|
|
8388
|
-
const contents = await (0, import_promises3.readFile)(targetPath, "utf8");
|
|
8389
|
-
return ToolResultSchema.parse({
|
|
8390
|
-
toolId: "file.read",
|
|
8391
|
-
success: true,
|
|
8392
|
-
summary: `Read ${import_node_path3.default.relative(context.cwd, targetPath) || import_node_path3.default.basename(targetPath)}.`,
|
|
8393
|
-
stdout: truncateOutput(contents),
|
|
8394
|
-
artifacts: [
|
|
8395
|
-
targetPath
|
|
8396
|
-
]
|
|
8397
|
-
});
|
|
8331
|
+
function isOpenRouter(definition) {
|
|
8332
|
+
return definition.baseUrl?.includes("openrouter.ai") ?? false;
|
|
8398
8333
|
}
|
|
8399
|
-
|
|
8400
|
-
const
|
|
8401
|
-
|
|
8402
|
-
|
|
8334
|
+
function buildProviderHeaders(definition, apiKey, includeJsonContentType = true) {
|
|
8335
|
+
const headers = {
|
|
8336
|
+
...definition.headers
|
|
8337
|
+
};
|
|
8338
|
+
if (includeJsonContentType) {
|
|
8339
|
+
headers["content-type"] = "application/json";
|
|
8403
8340
|
}
|
|
8404
|
-
|
|
8341
|
+
if (apiKey) {
|
|
8342
|
+
headers.authorization = `Bearer ${apiKey}`;
|
|
8343
|
+
}
|
|
8344
|
+
if (isOpenRouter(definition)) {
|
|
8345
|
+
const siteUrl = process.env.OPENROUTER_SITE_URL;
|
|
8346
|
+
const appName = process.env.OPENROUTER_APP_NAME ?? "Kimbho CLI";
|
|
8347
|
+
if (siteUrl) {
|
|
8348
|
+
headers["HTTP-Referer"] = siteUrl;
|
|
8349
|
+
}
|
|
8350
|
+
headers["X-Title"] = appName;
|
|
8351
|
+
}
|
|
8352
|
+
return headers;
|
|
8405
8353
|
}
|
|
8406
|
-
|
|
8407
|
-
|
|
8354
|
+
function filterModels(models, input = {}) {
|
|
8355
|
+
const normalized = input.search?.trim().toLowerCase();
|
|
8356
|
+
const filtered = normalized ? models.filter((model) => [model.id, model.name].filter((value) => Boolean(value)).some((value) => value.toLowerCase().includes(normalized))) : models;
|
|
8357
|
+
return typeof input.limit === "number" ? filtered.slice(0, input.limit) : filtered;
|
|
8408
8358
|
}
|
|
8409
|
-
|
|
8410
|
-
const
|
|
8411
|
-
const
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8359
|
+
function mapOpenAIStyleModels(providerId, payload, input) {
|
|
8360
|
+
const response = payload;
|
|
8361
|
+
const data = Array.isArray(response.data) ? response.data : [];
|
|
8362
|
+
const models = data.flatMap((item) => {
|
|
8363
|
+
if (typeof item !== "object" || item === null) {
|
|
8364
|
+
return [];
|
|
8365
|
+
}
|
|
8366
|
+
const record = item;
|
|
8367
|
+
const pricing = typeof record.pricing === "object" && record.pricing !== null ? record.pricing : null;
|
|
8368
|
+
const architecture = typeof record.architecture === "object" && record.architecture !== null ? record.architecture : null;
|
|
8369
|
+
return [
|
|
8370
|
+
ProviderModelSchema.parse({
|
|
8371
|
+
id: String(record.id ?? ""),
|
|
8372
|
+
...typeof record.name === "string" ? {
|
|
8373
|
+
name: record.name
|
|
8374
|
+
} : {},
|
|
8375
|
+
...typeof record.description === "string" ? {
|
|
8376
|
+
description: record.description
|
|
8377
|
+
} : {},
|
|
8378
|
+
...typeof record.context_length === "number" ? {
|
|
8379
|
+
contextLength: record.context_length
|
|
8380
|
+
} : {},
|
|
8381
|
+
...pricing && typeof pricing.prompt === "string" ? {
|
|
8382
|
+
promptPrice: pricing.prompt
|
|
8383
|
+
} : {},
|
|
8384
|
+
...pricing && typeof pricing.completion === "string" ? {
|
|
8385
|
+
completionPrice: pricing.completion
|
|
8386
|
+
} : {},
|
|
8387
|
+
...architecture && typeof architecture.modality === "string" ? {
|
|
8388
|
+
modality: architecture.modality
|
|
8389
|
+
} : {},
|
|
8390
|
+
providerId
|
|
8391
|
+
})
|
|
8392
|
+
];
|
|
8415
8393
|
});
|
|
8394
|
+
return filterModels(models, input);
|
|
8416
8395
|
}
|
|
8417
|
-
|
|
8418
|
-
const
|
|
8419
|
-
|
|
8420
|
-
|
|
8396
|
+
function mapAnthropicModels(providerId, payload, input) {
|
|
8397
|
+
const response = payload;
|
|
8398
|
+
const data = Array.isArray(response.data) ? response.data : [];
|
|
8399
|
+
const models = data.flatMap((item) => {
|
|
8400
|
+
if (typeof item !== "object" || item === null) {
|
|
8401
|
+
return [];
|
|
8402
|
+
}
|
|
8403
|
+
const record = item;
|
|
8404
|
+
return [
|
|
8405
|
+
ProviderModelSchema.parse({
|
|
8406
|
+
id: String(record.id ?? ""),
|
|
8407
|
+
...typeof record.display_name === "string" ? {
|
|
8408
|
+
name: record.display_name
|
|
8409
|
+
} : {},
|
|
8410
|
+
providerId
|
|
8411
|
+
})
|
|
8412
|
+
];
|
|
8413
|
+
});
|
|
8414
|
+
return filterModels(models, input);
|
|
8415
|
+
}
|
|
8416
|
+
function mapOllamaModels(providerId, payload, input) {
|
|
8417
|
+
const response = payload;
|
|
8418
|
+
const data = Array.isArray(response.models) ? response.models : [];
|
|
8419
|
+
const models = data.flatMap((item) => {
|
|
8420
|
+
if (typeof item !== "object" || item === null) {
|
|
8421
|
+
return [];
|
|
8422
|
+
}
|
|
8423
|
+
const record = item;
|
|
8424
|
+
const details = typeof record.details === "object" && record.details !== null ? record.details : null;
|
|
8425
|
+
const id = typeof record.model === "string" ? record.model : typeof record.name === "string" ? record.name : "";
|
|
8426
|
+
return [
|
|
8427
|
+
ProviderModelSchema.parse({
|
|
8428
|
+
id,
|
|
8429
|
+
...typeof record.name === "string" ? {
|
|
8430
|
+
name: record.name
|
|
8431
|
+
} : {},
|
|
8432
|
+
...details && typeof details.family === "string" ? {
|
|
8433
|
+
modality: details.family
|
|
8434
|
+
} : {},
|
|
8435
|
+
...details && typeof details.parameter_size === "string" ? {
|
|
8436
|
+
description: details.parameter_size
|
|
8437
|
+
} : {},
|
|
8438
|
+
providerId
|
|
8439
|
+
})
|
|
8440
|
+
];
|
|
8441
|
+
});
|
|
8442
|
+
return filterModels(models, input);
|
|
8443
|
+
}
|
|
8444
|
+
function toChatMessages(input) {
|
|
8445
|
+
const messages = [];
|
|
8446
|
+
if (input.systemPrompt) {
|
|
8447
|
+
messages.push({
|
|
8448
|
+
role: "system",
|
|
8449
|
+
content: input.systemPrompt
|
|
8450
|
+
});
|
|
8421
8451
|
}
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
"--recount",
|
|
8429
|
-
"--whitespace=nowarn",
|
|
8430
|
-
patchPath
|
|
8431
|
-
], context.cwd, timeoutMs);
|
|
8432
|
-
const success = !result.timedOut && result.code === 0;
|
|
8433
|
-
return ToolResultSchema.parse({
|
|
8434
|
-
toolId: "file.patch",
|
|
8435
|
-
success,
|
|
8436
|
-
summary: success ? "Patch applied successfully." : result.timedOut ? `Patch timed out after ${timeoutMs}ms.` : `Patch failed with code ${result.code ?? "unknown"}.`,
|
|
8437
|
-
stdout: truncateOutput(result.stdout),
|
|
8438
|
-
stderr: truncateOutput(result.stderr),
|
|
8439
|
-
artifacts: []
|
|
8452
|
+
if (input.messages && input.messages.length > 0) {
|
|
8453
|
+
messages.push(...input.messages);
|
|
8454
|
+
} else if (input.userPrompt) {
|
|
8455
|
+
messages.push({
|
|
8456
|
+
role: "user",
|
|
8457
|
+
content: input.userPrompt
|
|
8440
8458
|
});
|
|
8441
|
-
} finally {
|
|
8442
|
-
await (0, import_promises3.rm)(tempDir, { recursive: true, force: true });
|
|
8443
8459
|
}
|
|
8460
|
+
return messages;
|
|
8444
8461
|
}
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
(input, context) => executeFileRead(input, context)
|
|
8454
|
-
],
|
|
8455
|
-
[
|
|
8456
|
-
"file.patch",
|
|
8457
|
-
executeFilePatch
|
|
8458
|
-
],
|
|
8459
|
-
[
|
|
8460
|
-
"shell.exec",
|
|
8461
|
-
executeShell
|
|
8462
|
-
],
|
|
8463
|
-
[
|
|
8464
|
-
"git.status",
|
|
8465
|
-
executeGitStatus
|
|
8466
|
-
],
|
|
8467
|
-
[
|
|
8468
|
-
"tests.run",
|
|
8469
|
-
executeTests
|
|
8470
|
-
]
|
|
8471
|
-
]);
|
|
8462
|
+
function toPromptText(input) {
|
|
8463
|
+
const messages = toChatMessages(input);
|
|
8464
|
+
return messages.map((message) => `${message.role.toUpperCase()}: ${message.content}`).join("\n\n");
|
|
8465
|
+
}
|
|
8466
|
+
function requireModel(definition, input) {
|
|
8467
|
+
const model = input.model ?? definition.defaultModel;
|
|
8468
|
+
if (!model) {
|
|
8469
|
+
throw new Error(`Provider "${definition.id}" does not have a resolved model.`);
|
|
8472
8470
|
}
|
|
8473
|
-
|
|
8474
|
-
const descriptor = this.registry.get(toolId);
|
|
8475
|
-
if (!descriptor) {
|
|
8476
|
-
throw new Error(`Unknown tool "${toolId}".`);
|
|
8477
|
-
}
|
|
8478
|
-
const executor = this.executors.get(toolId);
|
|
8479
|
-
if (!executor) {
|
|
8480
|
-
throw new Error(`No executor registered for "${toolId}".`);
|
|
8481
|
-
}
|
|
8482
|
-
return executor(input, context, descriptor.timeoutMs);
|
|
8483
|
-
}
|
|
8484
|
-
};
|
|
8485
|
-
|
|
8486
|
-
// ../agent-runtime/dist/orchestrator.js
|
|
8487
|
-
function createSessionId() {
|
|
8488
|
-
return `session-${Date.now()}`;
|
|
8489
|
-
}
|
|
8490
|
-
function createEventId(type, taskId) {
|
|
8491
|
-
return `${type}-${taskId ?? "session"}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
8492
|
-
}
|
|
8493
|
-
function isTaskReady(task, completedTaskIds) {
|
|
8494
|
-
return task.status === "pending" && task.dependsOn.every((taskId) => completedTaskIds.has(taskId));
|
|
8495
|
-
}
|
|
8496
|
-
function completedTaskIdsFromPlan(plan) {
|
|
8497
|
-
return new Set(flattenPlanTasks(plan).filter((task) => task.status === "completed").map((task) => task.id));
|
|
8471
|
+
return model;
|
|
8498
8472
|
}
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
return "completed";
|
|
8504
|
-
}
|
|
8505
|
-
if (readyTasks.length > 0) {
|
|
8506
|
-
return completed > 0 ? "running" : "ready";
|
|
8507
|
-
}
|
|
8508
|
-
if (blockedTasks.length > 0) {
|
|
8509
|
-
return "blocked";
|
|
8473
|
+
var OpenAIResponsesProvider = class {
|
|
8474
|
+
definition;
|
|
8475
|
+
constructor(definition) {
|
|
8476
|
+
this.definition = ProviderDefinitionSchema.parse(definition);
|
|
8510
8477
|
}
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
status
|
|
8519
|
-
} : task)
|
|
8520
|
-
}));
|
|
8521
|
-
return {
|
|
8522
|
-
...plan,
|
|
8523
|
-
milestones
|
|
8524
|
-
};
|
|
8525
|
-
}
|
|
8526
|
-
function maybeAppendNote(notes, note) {
|
|
8527
|
-
return notes.at(-1) === note ? notes : [
|
|
8528
|
-
...notes,
|
|
8529
|
-
note
|
|
8530
|
-
];
|
|
8531
|
-
}
|
|
8532
|
-
function renderToolResultSection(results) {
|
|
8533
|
-
return results.map((result) => {
|
|
8534
|
-
const lines = [
|
|
8535
|
-
`## ${result.toolId}`,
|
|
8536
|
-
`- success: ${result.success ? "yes" : "no"}`,
|
|
8537
|
-
`- summary: ${result.summary}`
|
|
8538
|
-
];
|
|
8539
|
-
if (result.stdout) {
|
|
8540
|
-
lines.push("");
|
|
8541
|
-
lines.push("```text");
|
|
8542
|
-
lines.push(result.stdout);
|
|
8543
|
-
lines.push("```");
|
|
8478
|
+
async healthCheck() {
|
|
8479
|
+
const apiKey = resolveApiKey(this.definition);
|
|
8480
|
+
if (!apiKey) {
|
|
8481
|
+
return {
|
|
8482
|
+
ok: false,
|
|
8483
|
+
message: `Missing ${this.definition.apiKeyEnv ?? "API key env var"}`
|
|
8484
|
+
};
|
|
8544
8485
|
}
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8486
|
+
return {
|
|
8487
|
+
ok: true,
|
|
8488
|
+
message: `${this.definition.baseUrl ?? "https://api.openai.com/v1"} (${this.definition.defaultModel ?? "model not set"})`
|
|
8489
|
+
};
|
|
8490
|
+
}
|
|
8491
|
+
async listModels(input) {
|
|
8492
|
+
const apiKey = resolveApiKey(this.definition);
|
|
8493
|
+
if (!apiKey) {
|
|
8494
|
+
throw new Error(`Missing ${this.definition.apiKeyEnv ?? "API key env var"} for provider "${this.definition.id}".`);
|
|
8550
8495
|
}
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
return [];
|
|
8496
|
+
const baseUrl = this.definition.baseUrl ?? "https://api.openai.com/v1";
|
|
8497
|
+
const payload = await requestJson(joinUrl(baseUrl, "/models"), {
|
|
8498
|
+
method: "GET",
|
|
8499
|
+
headers: buildProviderHeaders(this.definition, apiKey, false)
|
|
8500
|
+
});
|
|
8501
|
+
return mapOpenAIStyleModels(this.definition.id, payload, input);
|
|
8558
8502
|
}
|
|
8559
|
-
|
|
8560
|
-
const
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8503
|
+
async generateText(input) {
|
|
8504
|
+
const apiKey = resolveApiKey(this.definition);
|
|
8505
|
+
if (!apiKey) {
|
|
8506
|
+
throw new Error(`Missing ${this.definition.apiKeyEnv ?? "API key env var"} for provider "${this.definition.id}".`);
|
|
8507
|
+
}
|
|
8508
|
+
const baseUrl = this.definition.baseUrl ?? "https://api.openai.com/v1";
|
|
8509
|
+
const model = requireModel(this.definition, input);
|
|
8510
|
+
const messages = toChatMessages(input).map((message) => ({
|
|
8511
|
+
role: message.role,
|
|
8512
|
+
content: [
|
|
8513
|
+
{
|
|
8514
|
+
type: "input_text",
|
|
8515
|
+
text: message.content
|
|
8516
|
+
}
|
|
8517
|
+
]
|
|
8518
|
+
}));
|
|
8519
|
+
const response = await requestJson(`${baseUrl}/responses`, {
|
|
8520
|
+
method: "POST",
|
|
8521
|
+
headers: {
|
|
8522
|
+
...buildProviderHeaders(this.definition, apiKey)
|
|
8523
|
+
},
|
|
8524
|
+
body: JSON.stringify({
|
|
8525
|
+
model,
|
|
8526
|
+
input: messages,
|
|
8527
|
+
...typeof input.temperature === "number" ? {
|
|
8528
|
+
temperature: input.temperature
|
|
8529
|
+
} : {},
|
|
8530
|
+
...typeof input.maxTokens === "number" ? {
|
|
8531
|
+
max_output_tokens: input.maxTokens
|
|
8532
|
+
} : {}
|
|
8533
|
+
})
|
|
8534
|
+
}, GENERATION_TIMEOUT_MS);
|
|
8535
|
+
const text = typeof response.output_text === "string" ? response.output_text : Array.isArray(response.output) ? response.output.flatMap((item) => typeof item === "object" && item !== null && "content" in item && Array.isArray(item.content) ? item.content : []).flatMap((item) => typeof item === "object" && item !== null && "text" in item ? [
|
|
8536
|
+
String(item.text)
|
|
8537
|
+
] : []).join("\n") : "";
|
|
8538
|
+
return {
|
|
8539
|
+
text,
|
|
8540
|
+
model
|
|
8541
|
+
};
|
|
8564
8542
|
}
|
|
8565
|
-
}
|
|
8566
|
-
var
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
this.toolRegistry = toolRegistry;
|
|
8571
|
-
this.toolRuntime = toolRuntime;
|
|
8543
|
+
};
|
|
8544
|
+
var AnthropicMessagesProvider = class {
|
|
8545
|
+
definition;
|
|
8546
|
+
constructor(definition) {
|
|
8547
|
+
this.definition = ProviderDefinitionSchema.parse(definition);
|
|
8572
8548
|
}
|
|
8573
|
-
|
|
8574
|
-
const
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8549
|
+
async healthCheck() {
|
|
8550
|
+
const apiKey = resolveApiKey(this.definition);
|
|
8551
|
+
if (!apiKey) {
|
|
8552
|
+
return {
|
|
8553
|
+
ok: false,
|
|
8554
|
+
message: `Missing ${this.definition.apiKeyEnv ?? "ANTHROPIC_API_KEY"}`
|
|
8555
|
+
};
|
|
8556
|
+
}
|
|
8579
8557
|
return {
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
plan,
|
|
8583
|
-
readyTasks,
|
|
8584
|
-
blockedTasks,
|
|
8585
|
-
assignedAgents
|
|
8558
|
+
ok: true,
|
|
8559
|
+
message: `${this.definition.baseUrl ?? "https://api.anthropic.com/v1/messages"} (${this.definition.defaultModel ?? "model not set"})`
|
|
8586
8560
|
};
|
|
8587
8561
|
}
|
|
8588
|
-
|
|
8589
|
-
const
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
completedTaskIds: Array.from(completedTaskIdsFromPlan(envelope.plan)),
|
|
8603
|
-
assignedAgents: envelope.assignedAgents.map((agent) => agent.role),
|
|
8604
|
-
notes: [
|
|
8605
|
-
"Initial session snapshot created from the current plan."
|
|
8606
|
-
],
|
|
8607
|
-
events: [],
|
|
8608
|
-
...overrides
|
|
8562
|
+
async listModels(input) {
|
|
8563
|
+
const apiKey = resolveApiKey(this.definition);
|
|
8564
|
+
if (!apiKey) {
|
|
8565
|
+
throw new Error(`Missing ${this.definition.apiKeyEnv ?? "ANTHROPIC_API_KEY"} for provider "${this.definition.id}".`);
|
|
8566
|
+
}
|
|
8567
|
+
const endpoint = this.definition.baseUrl ?? "https://api.anthropic.com/v1/messages";
|
|
8568
|
+
const modelsEndpoint = endpoint.endsWith("/messages") ? `${endpoint.slice(0, -"/messages".length)}/models` : joinUrl(endpoint, "/models");
|
|
8569
|
+
const payload = await requestJson(modelsEndpoint, {
|
|
8570
|
+
method: "GET",
|
|
8571
|
+
headers: {
|
|
8572
|
+
"anthropic-version": "2023-06-01",
|
|
8573
|
+
"x-api-key": apiKey,
|
|
8574
|
+
...this.definition.headers
|
|
8575
|
+
}
|
|
8609
8576
|
});
|
|
8577
|
+
return mapAnthropicModels(this.definition.id, payload, input);
|
|
8610
8578
|
}
|
|
8611
|
-
async
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
const
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8579
|
+
async generateText(input) {
|
|
8580
|
+
const apiKey = resolveApiKey(this.definition);
|
|
8581
|
+
if (!apiKey) {
|
|
8582
|
+
throw new Error(`Missing ${this.definition.apiKeyEnv ?? "ANTHROPIC_API_KEY"} for provider "${this.definition.id}".`);
|
|
8583
|
+
}
|
|
8584
|
+
const endpoint = this.definition.baseUrl ?? "https://api.anthropic.com/v1/messages";
|
|
8585
|
+
const model = requireModel(this.definition, input);
|
|
8586
|
+
const messages = toChatMessages(input);
|
|
8587
|
+
const systemMessage = messages.find((message) => message.role === "system")?.content;
|
|
8588
|
+
const conversation = messages.filter((message) => message.role !== "system").map((message) => ({
|
|
8589
|
+
role: message.role === "assistant" ? "assistant" : "user",
|
|
8590
|
+
content: message.content
|
|
8591
|
+
}));
|
|
8592
|
+
const response = await requestJson(endpoint, {
|
|
8593
|
+
method: "POST",
|
|
8594
|
+
headers: {
|
|
8595
|
+
"content-type": "application/json",
|
|
8596
|
+
"x-api-key": apiKey,
|
|
8597
|
+
"anthropic-version": "2023-06-01",
|
|
8598
|
+
...this.definition.headers
|
|
8599
|
+
},
|
|
8600
|
+
body: JSON.stringify({
|
|
8601
|
+
model,
|
|
8602
|
+
messages: conversation,
|
|
8603
|
+
...systemMessage ? {
|
|
8604
|
+
system: systemMessage
|
|
8605
|
+
} : {},
|
|
8606
|
+
max_tokens: input.maxTokens ?? 2048,
|
|
8607
|
+
...typeof input.temperature === "number" ? {
|
|
8608
|
+
temperature: input.temperature
|
|
8609
|
+
} : {}
|
|
8610
|
+
})
|
|
8611
|
+
}, GENERATION_TIMEOUT_MS);
|
|
8612
|
+
const content = Array.isArray(response.content) ? response.content : [];
|
|
8613
|
+
const text = content.flatMap((item) => typeof item === "object" && item !== null && "text" in item ? [
|
|
8614
|
+
String(item.text)
|
|
8615
|
+
] : []).join("\n");
|
|
8616
|
+
return {
|
|
8617
|
+
text,
|
|
8618
|
+
model
|
|
8619
|
+
};
|
|
8620
|
+
}
|
|
8621
|
+
};
|
|
8622
|
+
var OpenAICompatibleProvider = class {
|
|
8623
|
+
definition;
|
|
8624
|
+
constructor(definition) {
|
|
8625
|
+
this.definition = ProviderDefinitionSchema.parse(definition);
|
|
8626
|
+
}
|
|
8627
|
+
async healthCheck() {
|
|
8628
|
+
if (!this.definition.baseUrl) {
|
|
8629
|
+
return {
|
|
8630
|
+
ok: false,
|
|
8631
|
+
message: "Missing baseUrl"
|
|
8632
|
+
};
|
|
8633
|
+
}
|
|
8634
|
+
const apiKey = resolveApiKey(this.definition);
|
|
8635
|
+
if (this.definition.apiKeyEnv && !apiKey) {
|
|
8636
|
+
return {
|
|
8637
|
+
ok: false,
|
|
8638
|
+
message: `${this.definition.baseUrl} (${this.definition.defaultModel ?? "model not set"}), missing ${this.definition.apiKeyEnv}`
|
|
8639
|
+
};
|
|
8640
|
+
}
|
|
8641
|
+
try {
|
|
8642
|
+
await requestJson(joinUrl(this.definition.baseUrl, "/models"), {
|
|
8643
|
+
method: "GET",
|
|
8644
|
+
headers: buildProviderHeaders(this.definition, apiKey, false)
|
|
8645
|
+
}, 2e3);
|
|
8646
|
+
} catch (error) {
|
|
8647
|
+
return {
|
|
8648
|
+
ok: false,
|
|
8649
|
+
message: error instanceof Error ? error.message : String(error)
|
|
8650
|
+
};
|
|
8651
|
+
}
|
|
8652
|
+
return {
|
|
8653
|
+
ok: true,
|
|
8654
|
+
message: `${this.definition.baseUrl} (${this.definition.defaultModel ?? "model not set"})`
|
|
8655
|
+
};
|
|
8656
|
+
}
|
|
8657
|
+
async listModels(input) {
|
|
8658
|
+
if (!this.definition.baseUrl) {
|
|
8659
|
+
throw new Error(`Provider "${this.definition.id}" requires baseUrl.`);
|
|
8660
|
+
}
|
|
8661
|
+
const apiKey = resolveApiKey(this.definition);
|
|
8662
|
+
const payload = await requestJson(joinUrl(this.definition.baseUrl, "/models"), {
|
|
8663
|
+
method: "GET",
|
|
8664
|
+
headers: buildProviderHeaders(this.definition, apiKey, false)
|
|
8665
|
+
});
|
|
8666
|
+
return mapOpenAIStyleModels(this.definition.id, payload, input);
|
|
8667
|
+
}
|
|
8668
|
+
async generateText(input) {
|
|
8669
|
+
if (!this.definition.baseUrl) {
|
|
8670
|
+
throw new Error(`Provider "${this.definition.id}" requires baseUrl.`);
|
|
8671
|
+
}
|
|
8672
|
+
const apiKey = resolveApiKey(this.definition);
|
|
8673
|
+
const model = requireModel(this.definition, input);
|
|
8674
|
+
const response = await requestJson(`${this.definition.baseUrl}/chat/completions`, {
|
|
8675
|
+
method: "POST",
|
|
8676
|
+
headers: buildProviderHeaders(this.definition, apiKey),
|
|
8677
|
+
body: JSON.stringify({
|
|
8678
|
+
model,
|
|
8679
|
+
messages: toChatMessages(input),
|
|
8680
|
+
...typeof input.temperature === "number" ? {
|
|
8681
|
+
temperature: input.temperature
|
|
8682
|
+
} : {},
|
|
8683
|
+
...typeof input.maxTokens === "number" ? {
|
|
8684
|
+
max_tokens: input.maxTokens
|
|
8685
|
+
} : {}
|
|
8686
|
+
})
|
|
8687
|
+
}, GENERATION_TIMEOUT_MS);
|
|
8688
|
+
const choices = Array.isArray(response.choices) ? response.choices : [];
|
|
8689
|
+
const firstChoice = choices.length > 0 && typeof choices[0] === "object" && choices[0] !== null ? choices[0] : null;
|
|
8690
|
+
const message = firstChoice && typeof firstChoice.message === "object" && firstChoice.message !== null ? firstChoice.message : null;
|
|
8691
|
+
return {
|
|
8692
|
+
text: typeof message?.content === "string" ? message.content : "",
|
|
8693
|
+
model
|
|
8694
|
+
};
|
|
8695
|
+
}
|
|
8696
|
+
};
|
|
8697
|
+
var OllamaProvider = class {
|
|
8698
|
+
definition;
|
|
8699
|
+
constructor(definition) {
|
|
8700
|
+
this.definition = ProviderDefinitionSchema.parse(definition);
|
|
8701
|
+
}
|
|
8702
|
+
async healthCheck() {
|
|
8703
|
+
const baseUrl = this.definition.baseUrl ?? "http://localhost:11434";
|
|
8704
|
+
try {
|
|
8705
|
+
await requestJson(`${baseUrl}/api/tags`, { method: "GET" }, 2e3);
|
|
8706
|
+
return {
|
|
8707
|
+
ok: true,
|
|
8708
|
+
message: `${baseUrl} (${this.definition.defaultModel ?? "model not set"})`
|
|
8709
|
+
};
|
|
8710
|
+
} catch (error) {
|
|
8711
|
+
return {
|
|
8712
|
+
ok: false,
|
|
8713
|
+
message: error instanceof Error ? error.message : String(error)
|
|
8714
|
+
};
|
|
8715
|
+
}
|
|
8716
|
+
}
|
|
8717
|
+
async listModels(input) {
|
|
8718
|
+
const baseUrl = this.definition.baseUrl ?? "http://localhost:11434";
|
|
8719
|
+
const payload = await requestJson(`${baseUrl}/api/tags`, { method: "GET" }, 2e3);
|
|
8720
|
+
return mapOllamaModels(this.definition.id, payload, input);
|
|
8721
|
+
}
|
|
8722
|
+
async generateText(input) {
|
|
8723
|
+
const baseUrl = this.definition.baseUrl ?? "http://localhost:11434";
|
|
8724
|
+
const model = requireModel(this.definition, input);
|
|
8725
|
+
const response = await requestJson(`${baseUrl}/api/generate`, {
|
|
8726
|
+
method: "POST",
|
|
8727
|
+
headers: {
|
|
8728
|
+
"content-type": "application/json",
|
|
8729
|
+
...this.definition.headers
|
|
8730
|
+
},
|
|
8731
|
+
body: JSON.stringify({
|
|
8732
|
+
model,
|
|
8733
|
+
prompt: input.userPrompt ?? toPromptText(input),
|
|
8734
|
+
...input.systemPrompt ? {
|
|
8735
|
+
system: input.systemPrompt
|
|
8736
|
+
} : {},
|
|
8737
|
+
stream: false,
|
|
8738
|
+
...typeof input.temperature === "number" ? {
|
|
8739
|
+
options: {
|
|
8740
|
+
temperature: input.temperature
|
|
8741
|
+
}
|
|
8742
|
+
} : {}
|
|
8743
|
+
})
|
|
8744
|
+
}, GENERATION_TIMEOUT_MS);
|
|
8745
|
+
return {
|
|
8746
|
+
text: typeof response.response === "string" ? response.response : "",
|
|
8747
|
+
model
|
|
8748
|
+
};
|
|
8749
|
+
}
|
|
8750
|
+
};
|
|
8751
|
+
async function createCustomModuleProvider(definition, cwd) {
|
|
8752
|
+
if (!definition.modulePath) {
|
|
8753
|
+
throw new Error(`Provider "${definition.id}" requires modulePath.`);
|
|
8754
|
+
}
|
|
8755
|
+
const modulePath = import_node_path3.default.isAbsolute(definition.modulePath) ? definition.modulePath : import_node_path3.default.join(cwd, definition.modulePath);
|
|
8756
|
+
await (0, import_promises3.access)(modulePath);
|
|
8757
|
+
const module2 = await import((0, import_node_url.pathToFileURL)(modulePath).href);
|
|
8758
|
+
const createProvider = typeof module2.createProvider === "function" ? module2.createProvider : typeof module2.default === "function" ? module2.default : null;
|
|
8759
|
+
if (!createProvider) {
|
|
8760
|
+
throw new Error(`Custom provider module "${modulePath}" must export createProvider(definition).`);
|
|
8761
|
+
}
|
|
8762
|
+
return createProvider(definition);
|
|
8763
|
+
}
|
|
8764
|
+
var BUILTIN_FACTORIES = {
|
|
8765
|
+
"openai-responses": {
|
|
8766
|
+
driver: "openai-responses",
|
|
8767
|
+
create: async (definition) => new OpenAIResponsesProvider(definition)
|
|
8768
|
+
},
|
|
8769
|
+
"anthropic-messages": {
|
|
8770
|
+
driver: "anthropic-messages",
|
|
8771
|
+
create: async (definition) => new AnthropicMessagesProvider(definition)
|
|
8772
|
+
},
|
|
8773
|
+
"openai-compatible": {
|
|
8774
|
+
driver: "openai-compatible",
|
|
8775
|
+
create: async (definition) => new OpenAICompatibleProvider(definition)
|
|
8776
|
+
},
|
|
8777
|
+
ollama: {
|
|
8778
|
+
driver: "ollama",
|
|
8779
|
+
create: async (definition) => new OllamaProvider(definition)
|
|
8780
|
+
},
|
|
8781
|
+
"custom-module": {
|
|
8782
|
+
driver: "custom-module",
|
|
8783
|
+
create: (definition, cwd) => createCustomModuleProvider(definition, cwd)
|
|
8784
|
+
}
|
|
8785
|
+
};
|
|
8786
|
+
var BrainProviderRegistry = class {
|
|
8787
|
+
cwd;
|
|
8788
|
+
factories;
|
|
8789
|
+
constructor(cwd = process.cwd(), factories = Object.values(BUILTIN_FACTORIES)) {
|
|
8790
|
+
this.cwd = cwd;
|
|
8791
|
+
this.factories = new Map(factories.map((factory) => [factory.driver, factory]));
|
|
8792
|
+
}
|
|
8793
|
+
listDrivers() {
|
|
8794
|
+
return Array.from(this.factories.keys()).sort();
|
|
8795
|
+
}
|
|
8796
|
+
async createProvider(definition) {
|
|
8797
|
+
const normalized = ProviderDefinitionSchema.parse(definition);
|
|
8798
|
+
const factory = this.factories.get(normalized.driver);
|
|
8799
|
+
if (!factory) {
|
|
8800
|
+
throw new Error(`No provider factory registered for driver "${normalized.driver}".`);
|
|
8801
|
+
}
|
|
8802
|
+
return factory.create(normalized, this.cwd);
|
|
8803
|
+
}
|
|
8804
|
+
async healthCheck(definition) {
|
|
8805
|
+
const provider = await this.createProvider(definition);
|
|
8806
|
+
return provider.healthCheck();
|
|
8807
|
+
}
|
|
8808
|
+
async listModels(definition, input) {
|
|
8809
|
+
const provider = await this.createProvider(definition);
|
|
8810
|
+
if (provider.listModels) {
|
|
8811
|
+
return provider.listModels(input);
|
|
8812
|
+
}
|
|
8813
|
+
return filterModels(definition.models.map((modelId) => ProviderModelSchema.parse({
|
|
8814
|
+
id: modelId,
|
|
8815
|
+
providerId: definition.id
|
|
8816
|
+
})), input);
|
|
8817
|
+
}
|
|
8818
|
+
};
|
|
8819
|
+
function createDefaultBrainProviderRegistry(cwd = process.cwd()) {
|
|
8820
|
+
return new BrainProviderRegistry(cwd);
|
|
8821
|
+
}
|
|
8822
|
+
|
|
8823
|
+
// ../brains/dist/resolver.js
|
|
8824
|
+
var BrainResolver = class {
|
|
8825
|
+
config;
|
|
8826
|
+
registry;
|
|
8827
|
+
constructor(config, registry = createDefaultBrainProviderRegistry()) {
|
|
8828
|
+
this.config = config;
|
|
8829
|
+
this.registry = registry;
|
|
8830
|
+
}
|
|
8831
|
+
async resolve(role) {
|
|
8832
|
+
const settings = resolveBrainSettings(this.config, role);
|
|
8833
|
+
const provider = findProviderById(this.config, settings.providerId);
|
|
8834
|
+
if (!provider) {
|
|
8835
|
+
throw new Error(`Brain role "${role}" points to unknown provider "${settings.providerId}".`);
|
|
8836
|
+
}
|
|
8837
|
+
const model = resolveBrainModel(this.config, role);
|
|
8838
|
+
if (!model) {
|
|
8839
|
+
throw new Error(`Brain role "${role}" does not resolve to a model.`);
|
|
8840
|
+
}
|
|
8841
|
+
return {
|
|
8842
|
+
role,
|
|
8843
|
+
settings,
|
|
8844
|
+
provider,
|
|
8845
|
+
model,
|
|
8846
|
+
client: await this.registry.createProvider(provider)
|
|
8847
|
+
};
|
|
8848
|
+
}
|
|
8849
|
+
};
|
|
8850
|
+
|
|
8851
|
+
// ../agent-runtime/dist/autonomous.js
|
|
8852
|
+
var MAX_TOOL_OUTPUT_CHARS = 4e3;
|
|
8853
|
+
var MAX_PARSE_RETRIES = 2;
|
|
8854
|
+
var DEFAULT_MAX_REPAIR_ATTEMPTS = 2;
|
|
8855
|
+
var MODEL_ACTION_TOOLS = [
|
|
8856
|
+
"file.list",
|
|
8857
|
+
"file.search",
|
|
8858
|
+
"file.read",
|
|
8859
|
+
"file.write",
|
|
8860
|
+
"file.patch",
|
|
8861
|
+
"shell.exec",
|
|
8862
|
+
"tests.run",
|
|
8863
|
+
"git.status",
|
|
8864
|
+
"git.diff"
|
|
8865
|
+
];
|
|
8866
|
+
function createAgentActionSchema(allowedTools) {
|
|
8867
|
+
return external_exports.discriminatedUnion("type", [
|
|
8868
|
+
external_exports.object({
|
|
8869
|
+
type: external_exports.literal("tool"),
|
|
8870
|
+
tool: external_exports.enum(MODEL_ACTION_TOOLS).refine((tool) => allowedTools.includes(tool), {
|
|
8871
|
+
message: `Tool must be one of: ${allowedTools.join(", ")}`
|
|
8872
|
+
}),
|
|
8873
|
+
input: external_exports.record(external_exports.union([
|
|
8874
|
+
external_exports.string(),
|
|
8875
|
+
external_exports.number(),
|
|
8876
|
+
external_exports.boolean(),
|
|
8877
|
+
external_exports.null()
|
|
8878
|
+
])).default({}),
|
|
8879
|
+
reason: external_exports.string().min(1).optional()
|
|
8880
|
+
}),
|
|
8881
|
+
external_exports.object({
|
|
8882
|
+
type: external_exports.literal("finish"),
|
|
8883
|
+
summary: external_exports.string().min(1)
|
|
8884
|
+
}),
|
|
8885
|
+
external_exports.object({
|
|
8886
|
+
type: external_exports.literal("block"),
|
|
8887
|
+
reason: external_exports.string().min(1)
|
|
8888
|
+
})
|
|
8889
|
+
]);
|
|
8890
|
+
}
|
|
8891
|
+
function truncateForModel(value) {
|
|
8892
|
+
if (!value) {
|
|
8893
|
+
return value;
|
|
8894
|
+
}
|
|
8895
|
+
if (value.length <= MAX_TOOL_OUTPUT_CHARS) {
|
|
8896
|
+
return value;
|
|
8897
|
+
}
|
|
8898
|
+
return `${value.slice(0, MAX_TOOL_OUTPUT_CHARS)}
|
|
8899
|
+
... [truncated]`;
|
|
8900
|
+
}
|
|
8901
|
+
function extractJsonObject(raw) {
|
|
8902
|
+
const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
8903
|
+
const candidate = (fenced?.[1] ?? raw).trim();
|
|
8904
|
+
const firstBrace = candidate.indexOf("{");
|
|
8905
|
+
const lastBrace = candidate.lastIndexOf("}");
|
|
8906
|
+
if (firstBrace === -1 || lastBrace === -1 || lastBrace <= firstBrace) {
|
|
8907
|
+
throw new Error("Expected a JSON object in the model response.");
|
|
8908
|
+
}
|
|
8909
|
+
return JSON.parse(candidate.slice(firstBrace, lastBrace + 1));
|
|
8910
|
+
}
|
|
8911
|
+
function renderToolResultForModel(result) {
|
|
8912
|
+
const sections = [
|
|
8913
|
+
`tool: ${result.toolId}`,
|
|
8914
|
+
`success: ${result.success ? "true" : "false"}`,
|
|
8915
|
+
`summary: ${result.summary}`
|
|
8916
|
+
];
|
|
8917
|
+
if (result.stdout) {
|
|
8918
|
+
sections.push(`stdout:
|
|
8919
|
+
${truncateForModel(result.stdout)}`);
|
|
8920
|
+
}
|
|
8921
|
+
if (result.stderr) {
|
|
8922
|
+
sections.push(`stderr:
|
|
8923
|
+
${truncateForModel(result.stderr)}`);
|
|
8924
|
+
}
|
|
8925
|
+
if (result.artifacts.length > 0) {
|
|
8926
|
+
sections.push(`artifacts: ${result.artifacts.join(", ")}`);
|
|
8927
|
+
}
|
|
8928
|
+
return sections.join("\n");
|
|
8929
|
+
}
|
|
8930
|
+
function isReadOnlyShellCommand(command) {
|
|
8931
|
+
const normalized = command.trim().toLowerCase();
|
|
8932
|
+
return [
|
|
8933
|
+
"pwd",
|
|
8934
|
+
"ls",
|
|
8935
|
+
"find ",
|
|
8936
|
+
"rg ",
|
|
8937
|
+
"grep ",
|
|
8938
|
+
"cat ",
|
|
8939
|
+
"sed ",
|
|
8940
|
+
"head ",
|
|
8941
|
+
"tail ",
|
|
8942
|
+
"git status",
|
|
8943
|
+
"git diff"
|
|
8944
|
+
].some((prefix) => normalized === prefix || normalized.startsWith(prefix));
|
|
8945
|
+
}
|
|
8946
|
+
function isVerificationCommand(command) {
|
|
8947
|
+
const normalized = command.trim().toLowerCase();
|
|
8948
|
+
return [
|
|
8949
|
+
"npm test",
|
|
8950
|
+
"npm run test",
|
|
8951
|
+
"npm run build",
|
|
8952
|
+
"npm run lint",
|
|
8953
|
+
"pnpm test",
|
|
8954
|
+
"pnpm build",
|
|
8955
|
+
"pnpm lint",
|
|
8956
|
+
"yarn test",
|
|
8957
|
+
"yarn build",
|
|
8958
|
+
"yarn lint",
|
|
8959
|
+
"tsc",
|
|
8960
|
+
"vitest",
|
|
8961
|
+
"jest"
|
|
8962
|
+
].some((prefix) => normalized === prefix || normalized.startsWith(prefix));
|
|
8963
|
+
}
|
|
8964
|
+
function isMutatingAction(action) {
|
|
8965
|
+
if (action.type !== "tool") {
|
|
8966
|
+
return false;
|
|
8967
|
+
}
|
|
8968
|
+
if (action.tool === "file.write" || action.tool === "file.patch") {
|
|
8969
|
+
return true;
|
|
8970
|
+
}
|
|
8971
|
+
if (action.tool !== "shell.exec") {
|
|
8972
|
+
return false;
|
|
8973
|
+
}
|
|
8974
|
+
const command = typeof action.input.command === "string" ? action.input.command : "";
|
|
8975
|
+
return command.length > 0 && !isReadOnlyShellCommand(command);
|
|
8976
|
+
}
|
|
8977
|
+
function isVerificationAction(action) {
|
|
8978
|
+
if (action.type !== "tool") {
|
|
8979
|
+
return false;
|
|
8980
|
+
}
|
|
8981
|
+
if (action.tool === "tests.run") {
|
|
8982
|
+
return true;
|
|
8983
|
+
}
|
|
8984
|
+
if (action.tool !== "shell.exec") {
|
|
8985
|
+
return false;
|
|
8986
|
+
}
|
|
8987
|
+
const command = typeof action.input.command === "string" ? action.input.command : "";
|
|
8988
|
+
return command.length > 0 && isVerificationCommand(command);
|
|
8989
|
+
}
|
|
8990
|
+
function buildSystemPrompt(agent, task, request, allowedTools) {
|
|
8991
|
+
const toolShape = allowedTools.join("|");
|
|
8992
|
+
return [
|
|
8993
|
+
`You are Kimbho's ${agent.role}.`,
|
|
8994
|
+
`Purpose: ${agent.purpose}`,
|
|
8995
|
+
`Goal: ${request.goal}`,
|
|
8996
|
+
`Current task: ${task.id} - ${task.title}`,
|
|
8997
|
+
`Task description: ${task.description}`,
|
|
8998
|
+
`Acceptance criteria:`,
|
|
8999
|
+
...task.acceptanceCriteria.map((item) => `- ${item}`),
|
|
9000
|
+
`Likely files: ${task.filesLikelyTouched.join(", ") || "(not specified)"}`,
|
|
9001
|
+
`Workspace state: ${request.workspaceState}`,
|
|
9002
|
+
`Allowed tools: ${allowedTools.join(", ")}`,
|
|
9003
|
+
`Respond with exactly one JSON object and no markdown.`,
|
|
9004
|
+
`Tool action shape: {"type":"tool","tool":"${toolShape}","input":{...},"reason":"why this step matters"}`,
|
|
9005
|
+
`Finish shape: {"type":"finish","summary":"what was completed and verified"}`,
|
|
9006
|
+
`Block shape: {"type":"block","reason":"why you cannot proceed safely"}`,
|
|
9007
|
+
`Rules:`,
|
|
9008
|
+
`- Use one action per response.`,
|
|
9009
|
+
`- Use file.list and file.search to explore the workspace before editing.`,
|
|
9010
|
+
`- Prefer file.read before editing existing files.`,
|
|
9011
|
+
`- Use file.patch for existing files when possible; use file.write for new files or full replacements.`,
|
|
9012
|
+
`- Use git.diff to inspect the current patch after changes when helpful.`,
|
|
9013
|
+
`- Use shell.exec for non-interactive commands only.`,
|
|
9014
|
+
`- Keep paths relative to the workspace.`,
|
|
9015
|
+
`- After changing code, run verification with tests.run or shell.exec when appropriate.`,
|
|
9016
|
+
`- Do not claim success unless the task acceptance criteria are satisfied.`,
|
|
9017
|
+
`- If the task is underspecified, make a pragmatic implementation choice and continue.`
|
|
9018
|
+
].join("\n");
|
|
9019
|
+
}
|
|
9020
|
+
function buildInitialUserPrompt(task, request) {
|
|
9021
|
+
return [
|
|
9022
|
+
`Complete this task in the workspace.`,
|
|
9023
|
+
`Task type: ${task.type}`,
|
|
9024
|
+
`Risk level: ${task.riskLevel}`,
|
|
9025
|
+
`Outputs expected: ${task.outputs.join(", ") || "(not specified)"}`,
|
|
9026
|
+
`Constraints: ${request.constraints.join(", ") || "(none)"}`,
|
|
9027
|
+
`Choose the next single action now.`
|
|
9028
|
+
].join("\n");
|
|
9029
|
+
}
|
|
9030
|
+
function buildToolResultUserMessage(step, result) {
|
|
9031
|
+
return [
|
|
9032
|
+
`Step ${step} tool result:`,
|
|
9033
|
+
renderToolResultForModel(result),
|
|
9034
|
+
`Choose the next single action now.`
|
|
9035
|
+
].join("\n\n");
|
|
9036
|
+
}
|
|
9037
|
+
function createToolFailureResult(toolId, error) {
|
|
9038
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9039
|
+
return {
|
|
9040
|
+
toolId,
|
|
9041
|
+
success: false,
|
|
9042
|
+
summary: `${toolId} failed: ${message}`,
|
|
9043
|
+
stderr: message,
|
|
9044
|
+
artifacts: []
|
|
9045
|
+
};
|
|
9046
|
+
}
|
|
9047
|
+
async function writeTranscriptArtifact(cwd, sessionId, taskId, entries) {
|
|
9048
|
+
await ensureKimbhoDir(cwd);
|
|
9049
|
+
const outputPath = import_node_path4.default.join(resolveKimbhoDir(cwd), "logs", `${sessionId}-${taskId}-autonomous.md`);
|
|
9050
|
+
const content = [
|
|
9051
|
+
`# Autonomous Task Transcript`,
|
|
9052
|
+
``,
|
|
9053
|
+
`- session: ${sessionId}`,
|
|
9054
|
+
`- task: ${taskId}`,
|
|
9055
|
+
``,
|
|
9056
|
+
...entries.flatMap((entry) => {
|
|
9057
|
+
const lines = [
|
|
9058
|
+
`## Step ${entry.step}`,
|
|
9059
|
+
``,
|
|
9060
|
+
`### Model Response`,
|
|
9061
|
+
`\`\`\`json`,
|
|
9062
|
+
entry.response.trim(),
|
|
9063
|
+
`\`\`\``
|
|
9064
|
+
];
|
|
9065
|
+
if (entry.toolResult) {
|
|
9066
|
+
lines.push("");
|
|
9067
|
+
lines.push(`### Tool Result`);
|
|
9068
|
+
lines.push(`- tool: ${entry.toolResult.toolId}`);
|
|
9069
|
+
lines.push(`- success: ${entry.toolResult.success ? "true" : "false"}`);
|
|
9070
|
+
lines.push(`- summary: ${entry.toolResult.summary}`);
|
|
9071
|
+
if (entry.toolResult.stdout) {
|
|
9072
|
+
lines.push("");
|
|
9073
|
+
lines.push("```text");
|
|
9074
|
+
lines.push(truncateForModel(entry.toolResult.stdout) ?? "");
|
|
9075
|
+
lines.push("```");
|
|
9076
|
+
}
|
|
9077
|
+
if (entry.toolResult.stderr) {
|
|
9078
|
+
lines.push("");
|
|
9079
|
+
lines.push("```text");
|
|
9080
|
+
lines.push(truncateForModel(entry.toolResult.stderr) ?? "");
|
|
9081
|
+
lines.push("```");
|
|
9082
|
+
}
|
|
9083
|
+
}
|
|
9084
|
+
if (entry.runtimeNote) {
|
|
9085
|
+
lines.push("");
|
|
9086
|
+
lines.push(`### Runtime Note`);
|
|
9087
|
+
lines.push(entry.runtimeNote);
|
|
9088
|
+
}
|
|
9089
|
+
lines.push("");
|
|
9090
|
+
return lines;
|
|
9091
|
+
})
|
|
9092
|
+
].join("\n");
|
|
9093
|
+
await (0, import_promises4.writeFile)(outputPath, `${content}
|
|
9094
|
+
`, "utf8");
|
|
9095
|
+
return outputPath;
|
|
9096
|
+
}
|
|
9097
|
+
var AutonomousTaskExecutor = class {
|
|
9098
|
+
config;
|
|
9099
|
+
resolver;
|
|
9100
|
+
toolRegistry;
|
|
9101
|
+
toolRuntime;
|
|
9102
|
+
constructor(config, toolRegistry, toolRuntime) {
|
|
9103
|
+
this.config = config;
|
|
9104
|
+
this.resolver = new BrainResolver(config);
|
|
9105
|
+
this.toolRegistry = toolRegistry;
|
|
9106
|
+
this.toolRuntime = toolRuntime;
|
|
9107
|
+
}
|
|
9108
|
+
async execute(sessionId, task, request, options = {}) {
|
|
9109
|
+
const agent = AGENT_CATALOG[task.agentRole];
|
|
9110
|
+
const brain = await this.resolver.resolve(agent.brainRole);
|
|
9111
|
+
const allowedTools = this.toolRegistry.byRole(agent.role).map((tool) => tool.id);
|
|
9112
|
+
const actionSchema = createAgentActionSchema(allowedTools);
|
|
9113
|
+
const messages = [
|
|
9114
|
+
{
|
|
9115
|
+
role: "user",
|
|
9116
|
+
content: buildInitialUserPrompt(task, request)
|
|
9117
|
+
}
|
|
9118
|
+
];
|
|
9119
|
+
const toolResults = [];
|
|
9120
|
+
const artifacts = /* @__PURE__ */ new Set();
|
|
9121
|
+
const transcript = [];
|
|
9122
|
+
const maxSteps = options.maxSteps ?? 10;
|
|
9123
|
+
const maxRepairAttempts = options.maxRepairAttempts ?? DEFAULT_MAX_REPAIR_ATTEMPTS;
|
|
9124
|
+
let changedWorkspace = false;
|
|
9125
|
+
let verifiedAfterLatestChange = false;
|
|
9126
|
+
let repairRequiredBeforeVerification = false;
|
|
9127
|
+
let repairAppliedSinceFailure = false;
|
|
9128
|
+
let verificationFailures = 0;
|
|
9129
|
+
let lastVerificationFailure = null;
|
|
9130
|
+
for (let step = 1; step <= maxSteps; step += 1) {
|
|
9131
|
+
let responseText = "";
|
|
9132
|
+
let parsedAction = null;
|
|
9133
|
+
for (let attempt = 0; attempt <= MAX_PARSE_RETRIES; attempt += 1) {
|
|
9134
|
+
const response = await brain.client.generateText({
|
|
9135
|
+
model: brain.model,
|
|
9136
|
+
systemPrompt: buildSystemPrompt(agent, task, request, allowedTools),
|
|
9137
|
+
messages,
|
|
9138
|
+
...typeof brain.settings.temperature === "number" ? {
|
|
9139
|
+
temperature: brain.settings.temperature
|
|
9140
|
+
} : {},
|
|
9141
|
+
...typeof brain.settings.maxTokens === "number" ? {
|
|
9142
|
+
maxTokens: brain.settings.maxTokens
|
|
9143
|
+
} : {}
|
|
9144
|
+
});
|
|
9145
|
+
responseText = response.text;
|
|
9146
|
+
try {
|
|
9147
|
+
parsedAction = actionSchema.parse(extractJsonObject(response.text));
|
|
9148
|
+
break;
|
|
9149
|
+
} catch (error) {
|
|
9150
|
+
if (attempt === MAX_PARSE_RETRIES) {
|
|
9151
|
+
throw error;
|
|
9152
|
+
}
|
|
9153
|
+
messages.push({
|
|
9154
|
+
role: "assistant",
|
|
9155
|
+
content: response.text
|
|
9156
|
+
});
|
|
9157
|
+
messages.push({
|
|
9158
|
+
role: "user",
|
|
9159
|
+
content: [
|
|
9160
|
+
"Your previous response was invalid.",
|
|
9161
|
+
error instanceof Error ? error.message : String(error),
|
|
9162
|
+
"Return exactly one valid JSON object matching the required action schema."
|
|
9163
|
+
].join("\n")
|
|
9164
|
+
});
|
|
9165
|
+
}
|
|
9166
|
+
}
|
|
9167
|
+
if (!parsedAction) {
|
|
9168
|
+
throw new Error("Model did not return a valid action.");
|
|
9169
|
+
}
|
|
9170
|
+
const transcriptEntry = {
|
|
9171
|
+
step,
|
|
9172
|
+
response: responseText,
|
|
9173
|
+
parsedAction
|
|
9174
|
+
};
|
|
9175
|
+
messages.push({
|
|
9176
|
+
role: "assistant",
|
|
9177
|
+
content: JSON.stringify(parsedAction)
|
|
9178
|
+
});
|
|
9179
|
+
if (parsedAction.type === "finish") {
|
|
9180
|
+
if (changedWorkspace && !verifiedAfterLatestChange) {
|
|
9181
|
+
transcriptEntry.runtimeNote = "Finish rejected because code changed without a successful verification step.";
|
|
9182
|
+
transcript.push(transcriptEntry);
|
|
9183
|
+
messages.push({
|
|
9184
|
+
role: "user",
|
|
9185
|
+
content: [
|
|
9186
|
+
"You tried to finish after making code changes without a successful verification step.",
|
|
9187
|
+
"Run tests.run or a build/lint/test shell command, inspect failures, and only finish after verification passes."
|
|
9188
|
+
].join("\n")
|
|
9189
|
+
});
|
|
9190
|
+
continue;
|
|
9191
|
+
}
|
|
9192
|
+
const transcriptPath2 = await writeTranscriptArtifact(request.cwd, sessionId, task.id, transcript.concat(transcriptEntry));
|
|
9193
|
+
artifacts.add(transcriptPath2);
|
|
9194
|
+
return {
|
|
9195
|
+
status: "completed",
|
|
9196
|
+
summary: parsedAction.summary,
|
|
9197
|
+
toolResults,
|
|
9198
|
+
artifacts: Array.from(artifacts)
|
|
9199
|
+
};
|
|
9200
|
+
}
|
|
9201
|
+
if (parsedAction.type === "block") {
|
|
9202
|
+
const transcriptPath2 = await writeTranscriptArtifact(request.cwd, sessionId, task.id, transcript.concat(transcriptEntry));
|
|
9203
|
+
artifacts.add(transcriptPath2);
|
|
9204
|
+
return {
|
|
9205
|
+
status: "blocked",
|
|
9206
|
+
summary: parsedAction.reason,
|
|
9207
|
+
toolResults,
|
|
9208
|
+
artifacts: Array.from(artifacts)
|
|
9209
|
+
};
|
|
9210
|
+
}
|
|
9211
|
+
if (!allowedTools.includes(parsedAction.tool)) {
|
|
9212
|
+
transcriptEntry.runtimeNote = `Rejected disallowed tool "${parsedAction.tool}" for ${agent.role}.`;
|
|
9213
|
+
transcript.push(transcriptEntry);
|
|
9214
|
+
messages.push({
|
|
9215
|
+
role: "user",
|
|
9216
|
+
content: [
|
|
9217
|
+
`The tool "${parsedAction.tool}" is not allowed for ${agent.role}.`,
|
|
9218
|
+
`Choose one of the allowed tools instead: ${allowedTools.join(", ")}.`
|
|
9219
|
+
].join("\n")
|
|
9220
|
+
});
|
|
9221
|
+
continue;
|
|
9222
|
+
}
|
|
9223
|
+
if (isVerificationAction(parsedAction) && repairRequiredBeforeVerification && !repairAppliedSinceFailure) {
|
|
9224
|
+
transcriptEntry.runtimeNote = "Verification rerun rejected because no repair action succeeded after the last failed verification.";
|
|
9225
|
+
transcript.push(transcriptEntry);
|
|
9226
|
+
messages.push({
|
|
9227
|
+
role: "user",
|
|
9228
|
+
content: [
|
|
9229
|
+
"Verification already failed for this task.",
|
|
9230
|
+
"Inspect the failure output, apply a repair, and only then rerun verification.",
|
|
9231
|
+
lastVerificationFailure ? `Latest failure summary: ${lastVerificationFailure.summary}` : "Latest failure summary: unavailable."
|
|
9232
|
+
].join("\n")
|
|
9233
|
+
});
|
|
9234
|
+
continue;
|
|
9235
|
+
}
|
|
9236
|
+
const result = await this.toolRuntime.run(parsedAction.tool, parsedAction.input, { cwd: request.cwd }).catch((error) => createToolFailureResult(parsedAction.tool, error));
|
|
9237
|
+
const mutatingAction = isMutatingAction(parsedAction);
|
|
9238
|
+
const verificationAction = isVerificationAction(parsedAction);
|
|
9239
|
+
if (mutatingAction && result.success) {
|
|
9240
|
+
changedWorkspace = true;
|
|
9241
|
+
verifiedAfterLatestChange = false;
|
|
9242
|
+
if (repairRequiredBeforeVerification) {
|
|
9243
|
+
repairAppliedSinceFailure = true;
|
|
9244
|
+
}
|
|
9245
|
+
}
|
|
9246
|
+
if (verificationAction) {
|
|
9247
|
+
verifiedAfterLatestChange = result.success;
|
|
9248
|
+
if (result.success) {
|
|
9249
|
+
verificationFailures = 0;
|
|
9250
|
+
repairRequiredBeforeVerification = false;
|
|
9251
|
+
repairAppliedSinceFailure = false;
|
|
9252
|
+
lastVerificationFailure = null;
|
|
9253
|
+
} else {
|
|
9254
|
+
verificationFailures += 1;
|
|
9255
|
+
repairRequiredBeforeVerification = true;
|
|
9256
|
+
repairAppliedSinceFailure = false;
|
|
9257
|
+
lastVerificationFailure = result;
|
|
9258
|
+
}
|
|
9259
|
+
}
|
|
9260
|
+
transcriptEntry.toolResult = result;
|
|
9261
|
+
transcript.push(transcriptEntry);
|
|
9262
|
+
toolResults.push(result);
|
|
9263
|
+
for (const artifact of result.artifacts) {
|
|
9264
|
+
artifacts.add(artifact);
|
|
9265
|
+
}
|
|
9266
|
+
const followUp = [
|
|
9267
|
+
buildToolResultUserMessage(step, result)
|
|
9268
|
+
];
|
|
9269
|
+
if (mutatingAction && result.success) {
|
|
9270
|
+
followUp.push("Code or workspace state changed. Inspect the diff if needed and run verification before finishing.");
|
|
9271
|
+
}
|
|
9272
|
+
if (verificationAction && !result.success) {
|
|
9273
|
+
transcriptEntry.runtimeNote = [
|
|
9274
|
+
`Verification failed (${verificationFailures}/${maxRepairAttempts} repair attempts used).`,
|
|
9275
|
+
"Executor requires a repair action before the next verification run."
|
|
9276
|
+
].join(" ");
|
|
9277
|
+
followUp.push(`Verification failed. Inspect the failure output, repair the issue, and run verification again before finishing. Repair attempts used: ${verificationFailures}/${maxRepairAttempts}.`);
|
|
9278
|
+
if (verificationFailures >= maxRepairAttempts) {
|
|
9279
|
+
transcriptEntry.runtimeNote = [
|
|
9280
|
+
transcriptEntry.runtimeNote,
|
|
9281
|
+
"Repair budget exhausted; task escalated as blocked."
|
|
9282
|
+
].join(" ");
|
|
9283
|
+
const transcriptPath2 = await writeTranscriptArtifact(request.cwd, sessionId, task.id, transcript);
|
|
9284
|
+
artifacts.add(transcriptPath2);
|
|
9285
|
+
if (task.agentRole !== "test-debugger") {
|
|
9286
|
+
return {
|
|
9287
|
+
status: "handoff",
|
|
9288
|
+
summary: `Verification failed ${verificationFailures} time${verificationFailures === 1 ? "" : "s"} for ${task.id}; handing off to test-debugger.`,
|
|
9289
|
+
toolResults,
|
|
9290
|
+
artifacts: Array.from(artifacts)
|
|
9291
|
+
};
|
|
9292
|
+
}
|
|
9293
|
+
return {
|
|
9294
|
+
status: "blocked",
|
|
9295
|
+
summary: `Verification failed ${verificationFailures} time${verificationFailures === 1 ? "" : "s"} for ${task.id}; repair budget exhausted.`,
|
|
9296
|
+
toolResults,
|
|
9297
|
+
artifacts: Array.from(artifacts)
|
|
9298
|
+
};
|
|
9299
|
+
}
|
|
9300
|
+
}
|
|
9301
|
+
messages.push({
|
|
9302
|
+
role: "user",
|
|
9303
|
+
content: followUp.join("\n\n")
|
|
8659
9304
|
});
|
|
8660
|
-
executedTasks += 1;
|
|
8661
9305
|
}
|
|
8662
|
-
const
|
|
8663
|
-
|
|
8664
|
-
|
|
9306
|
+
const transcriptPath = await writeTranscriptArtifact(request.cwd, sessionId, task.id, transcript);
|
|
9307
|
+
artifacts.add(transcriptPath);
|
|
9308
|
+
return {
|
|
9309
|
+
status: "paused",
|
|
9310
|
+
summary: `Autonomous executor reached the step limit (${maxSteps}) for ${task.id}.`,
|
|
9311
|
+
toolResults,
|
|
9312
|
+
artifacts: Array.from(artifacts)
|
|
9313
|
+
};
|
|
9314
|
+
}
|
|
9315
|
+
};
|
|
9316
|
+
|
|
9317
|
+
// ../agent-runtime/dist/orchestrator.js
|
|
9318
|
+
var import_promises6 = require("node:fs/promises");
|
|
9319
|
+
var import_node_path6 = __toESM(require("node:path"), 1);
|
|
9320
|
+
|
|
9321
|
+
// ../tools/dist/types.js
|
|
9322
|
+
var ToolPermissionSchema = external_exports.enum([
|
|
9323
|
+
"safe",
|
|
9324
|
+
"approval-required",
|
|
9325
|
+
"destructive"
|
|
9326
|
+
]);
|
|
9327
|
+
var ToolDescriptorSchema = external_exports.object({
|
|
9328
|
+
id: external_exports.string().min(1),
|
|
9329
|
+
description: external_exports.string().min(1),
|
|
9330
|
+
permission: ToolPermissionSchema.default("safe"),
|
|
9331
|
+
timeoutMs: external_exports.number().int().positive(),
|
|
9332
|
+
retryable: external_exports.boolean().default(false),
|
|
9333
|
+
producesArtifacts: external_exports.boolean().default(false),
|
|
9334
|
+
allowedRoles: external_exports.array(AgentRoleSchema).default([])
|
|
9335
|
+
});
|
|
9336
|
+
|
|
9337
|
+
// ../tools/dist/registry.js
|
|
9338
|
+
var BUILTIN_TOOLS = [
|
|
9339
|
+
{
|
|
9340
|
+
id: "file.read",
|
|
9341
|
+
description: "Read a file from the workspace.",
|
|
9342
|
+
permission: "safe",
|
|
9343
|
+
timeoutMs: 5e3,
|
|
9344
|
+
retryable: false,
|
|
9345
|
+
producesArtifacts: false,
|
|
9346
|
+
allowedRoles: [
|
|
9347
|
+
"session-orchestrator",
|
|
9348
|
+
"repo-analyst",
|
|
9349
|
+
"planner",
|
|
9350
|
+
"execution-manager",
|
|
9351
|
+
"frontend-specialist",
|
|
9352
|
+
"backend-specialist",
|
|
9353
|
+
"database-specialist",
|
|
9354
|
+
"infra-specialist",
|
|
9355
|
+
"test-debugger",
|
|
9356
|
+
"reviewer",
|
|
9357
|
+
"integrator"
|
|
9358
|
+
]
|
|
9359
|
+
},
|
|
9360
|
+
{
|
|
9361
|
+
id: "file.list",
|
|
9362
|
+
description: "List workspace files, optionally filtered by a path or pattern.",
|
|
9363
|
+
permission: "safe",
|
|
9364
|
+
timeoutMs: 1e4,
|
|
9365
|
+
retryable: true,
|
|
9366
|
+
producesArtifacts: false,
|
|
9367
|
+
allowedRoles: [
|
|
9368
|
+
"session-orchestrator",
|
|
9369
|
+
"repo-analyst",
|
|
9370
|
+
"planner",
|
|
9371
|
+
"execution-manager",
|
|
9372
|
+
"frontend-specialist",
|
|
9373
|
+
"backend-specialist",
|
|
9374
|
+
"database-specialist",
|
|
9375
|
+
"infra-specialist",
|
|
9376
|
+
"test-debugger",
|
|
9377
|
+
"reviewer",
|
|
9378
|
+
"integrator"
|
|
9379
|
+
]
|
|
9380
|
+
},
|
|
9381
|
+
{
|
|
9382
|
+
id: "file.search",
|
|
9383
|
+
description: "Search workspace file contents for a text or regex pattern.",
|
|
9384
|
+
permission: "safe",
|
|
9385
|
+
timeoutMs: 15e3,
|
|
9386
|
+
retryable: true,
|
|
9387
|
+
producesArtifacts: false,
|
|
9388
|
+
allowedRoles: [
|
|
9389
|
+
"session-orchestrator",
|
|
9390
|
+
"repo-analyst",
|
|
9391
|
+
"planner",
|
|
9392
|
+
"execution-manager",
|
|
9393
|
+
"frontend-specialist",
|
|
9394
|
+
"backend-specialist",
|
|
9395
|
+
"database-specialist",
|
|
9396
|
+
"infra-specialist",
|
|
9397
|
+
"test-debugger",
|
|
9398
|
+
"reviewer",
|
|
9399
|
+
"integrator"
|
|
9400
|
+
]
|
|
9401
|
+
},
|
|
9402
|
+
{
|
|
9403
|
+
id: "file.write",
|
|
9404
|
+
description: "Write or replace a file in the workspace.",
|
|
9405
|
+
permission: "approval-required",
|
|
9406
|
+
timeoutMs: 1e4,
|
|
9407
|
+
retryable: true,
|
|
9408
|
+
producesArtifacts: true,
|
|
9409
|
+
allowedRoles: [
|
|
9410
|
+
"frontend-specialist",
|
|
9411
|
+
"backend-specialist",
|
|
9412
|
+
"database-specialist",
|
|
9413
|
+
"infra-specialist",
|
|
9414
|
+
"test-debugger",
|
|
9415
|
+
"integrator"
|
|
9416
|
+
]
|
|
9417
|
+
},
|
|
9418
|
+
{
|
|
9419
|
+
id: "file.patch",
|
|
9420
|
+
description: "Apply a structured patch to workspace files.",
|
|
9421
|
+
permission: "approval-required",
|
|
9422
|
+
timeoutMs: 1e4,
|
|
9423
|
+
retryable: true,
|
|
9424
|
+
producesArtifacts: true,
|
|
9425
|
+
allowedRoles: [
|
|
9426
|
+
"frontend-specialist",
|
|
9427
|
+
"backend-specialist",
|
|
9428
|
+
"database-specialist",
|
|
9429
|
+
"infra-specialist",
|
|
9430
|
+
"test-debugger",
|
|
9431
|
+
"integrator"
|
|
9432
|
+
]
|
|
9433
|
+
},
|
|
9434
|
+
{
|
|
9435
|
+
id: "shell.exec",
|
|
9436
|
+
description: "Run a shell command inside the workspace.",
|
|
9437
|
+
permission: "approval-required",
|
|
9438
|
+
timeoutMs: 12e4,
|
|
9439
|
+
retryable: true,
|
|
9440
|
+
producesArtifacts: true,
|
|
9441
|
+
allowedRoles: [
|
|
9442
|
+
"repo-analyst",
|
|
9443
|
+
"execution-manager",
|
|
9444
|
+
"backend-specialist",
|
|
9445
|
+
"infra-specialist",
|
|
9446
|
+
"test-debugger",
|
|
9447
|
+
"reviewer",
|
|
9448
|
+
"integrator"
|
|
9449
|
+
]
|
|
9450
|
+
},
|
|
9451
|
+
{
|
|
9452
|
+
id: "git.status",
|
|
9453
|
+
description: "Inspect the current git working tree and diff state.",
|
|
9454
|
+
permission: "safe",
|
|
9455
|
+
timeoutMs: 1e4,
|
|
9456
|
+
retryable: true,
|
|
9457
|
+
producesArtifacts: false,
|
|
9458
|
+
allowedRoles: [
|
|
9459
|
+
"session-orchestrator",
|
|
9460
|
+
"repo-analyst",
|
|
9461
|
+
"execution-manager",
|
|
9462
|
+
"reviewer",
|
|
9463
|
+
"integrator"
|
|
9464
|
+
]
|
|
9465
|
+
},
|
|
9466
|
+
{
|
|
9467
|
+
id: "git.diff",
|
|
9468
|
+
description: "Inspect the current git diff, optionally scoped to a file path.",
|
|
9469
|
+
permission: "safe",
|
|
9470
|
+
timeoutMs: 15e3,
|
|
9471
|
+
retryable: true,
|
|
9472
|
+
producesArtifacts: false,
|
|
9473
|
+
allowedRoles: [
|
|
9474
|
+
"session-orchestrator",
|
|
9475
|
+
"repo-analyst",
|
|
9476
|
+
"execution-manager",
|
|
9477
|
+
"frontend-specialist",
|
|
9478
|
+
"backend-specialist",
|
|
9479
|
+
"database-specialist",
|
|
9480
|
+
"infra-specialist",
|
|
9481
|
+
"test-debugger",
|
|
9482
|
+
"reviewer",
|
|
9483
|
+
"integrator"
|
|
9484
|
+
]
|
|
9485
|
+
},
|
|
9486
|
+
{
|
|
9487
|
+
id: "tests.run",
|
|
9488
|
+
description: "Execute verification commands such as tests, linting, and builds.",
|
|
9489
|
+
permission: "approval-required",
|
|
9490
|
+
timeoutMs: 3e5,
|
|
9491
|
+
retryable: true,
|
|
9492
|
+
producesArtifacts: true,
|
|
9493
|
+
allowedRoles: [
|
|
9494
|
+
"frontend-specialist",
|
|
9495
|
+
"backend-specialist",
|
|
9496
|
+
"database-specialist",
|
|
9497
|
+
"infra-specialist",
|
|
9498
|
+
"execution-manager",
|
|
9499
|
+
"test-debugger",
|
|
9500
|
+
"reviewer",
|
|
9501
|
+
"integrator"
|
|
9502
|
+
]
|
|
9503
|
+
}
|
|
9504
|
+
];
|
|
9505
|
+
var ToolRegistry = class {
|
|
9506
|
+
tools = /* @__PURE__ */ new Map();
|
|
9507
|
+
constructor(seed = BUILTIN_TOOLS) {
|
|
9508
|
+
for (const descriptor of seed) {
|
|
9509
|
+
const normalized = ToolDescriptorSchema.parse(descriptor);
|
|
9510
|
+
this.tools.set(normalized.id, normalized);
|
|
8665
9511
|
}
|
|
8666
|
-
return this.createSessionSnapshot(postLimitEnvelope, {
|
|
8667
|
-
startedAt: session.startedAt,
|
|
8668
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8669
|
-
notes,
|
|
8670
|
-
events
|
|
8671
|
-
});
|
|
8672
9512
|
}
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
return Array.from(roles).map((role) => AGENT_CATALOG[role]).sort((left, right) => left.role.localeCompare(right.role));
|
|
9513
|
+
list() {
|
|
9514
|
+
return Array.from(this.tools.values()).sort((left, right) => left.id.localeCompare(right.id));
|
|
8676
9515
|
}
|
|
8677
|
-
|
|
8678
|
-
return this.
|
|
9516
|
+
get(id) {
|
|
9517
|
+
return this.tools.get(id);
|
|
8679
9518
|
}
|
|
8680
|
-
|
|
8681
|
-
return
|
|
9519
|
+
byRole(role) {
|
|
9520
|
+
return this.list().filter((tool) => tool.allowedRoles.includes(role));
|
|
8682
9521
|
}
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
9522
|
+
};
|
|
9523
|
+
function createDefaultToolRegistry() {
|
|
9524
|
+
return new ToolRegistry(BUILTIN_TOOLS);
|
|
9525
|
+
}
|
|
9526
|
+
|
|
9527
|
+
// ../tools/dist/runtime.js
|
|
9528
|
+
var import_promises5 = require("node:fs/promises");
|
|
9529
|
+
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
9530
|
+
var import_node_process = __toESM(require("node:process"), 1);
|
|
9531
|
+
var import_node_child_process = require("node:child_process");
|
|
9532
|
+
var import_node_os = require("node:os");
|
|
9533
|
+
var DEFAULT_CAPTURE_LIMIT = 16e3;
|
|
9534
|
+
var DEFAULT_IGNORE_GLOBS = [
|
|
9535
|
+
"!**/node_modules/**",
|
|
9536
|
+
"!**/.git/**",
|
|
9537
|
+
"!**/.kimbho/**",
|
|
9538
|
+
"!**/dist/**",
|
|
9539
|
+
"!**/build/**",
|
|
9540
|
+
"!**/.next/**"
|
|
9541
|
+
];
|
|
9542
|
+
var DEFAULT_IGNORE_SEGMENTS = /* @__PURE__ */ new Set([
|
|
9543
|
+
"node_modules",
|
|
9544
|
+
".git",
|
|
9545
|
+
".kimbho",
|
|
9546
|
+
"dist",
|
|
9547
|
+
"build",
|
|
9548
|
+
".next"
|
|
9549
|
+
]);
|
|
9550
|
+
function parseLimitValue(value, fallback) {
|
|
9551
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0) {
|
|
9552
|
+
return value;
|
|
9553
|
+
}
|
|
9554
|
+
if (typeof value === "string") {
|
|
9555
|
+
const parsed = Number.parseInt(value, 10);
|
|
9556
|
+
if (Number.isInteger(parsed) && parsed > 0) {
|
|
9557
|
+
return parsed;
|
|
8689
9558
|
}
|
|
8690
|
-
return {
|
|
8691
|
-
status: "blocked",
|
|
8692
|
-
summary: `Task ${task.id} is ready, but no executor is available for ${task.agentRole}.`,
|
|
8693
|
-
toolResults: [],
|
|
8694
|
-
artifacts: []
|
|
8695
|
-
};
|
|
8696
9559
|
}
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
this.safeRunTool("file.read", { path: "README.md" }, context)
|
|
8703
|
-
]);
|
|
8704
|
-
const successfulResults = toolResults.filter((result) => result.success);
|
|
8705
|
-
const scripts = extractPackageScripts(successfulResults);
|
|
8706
|
-
const artifactPath = await this.writeLogArtifact(sessionId, "repo-analysis", request.cwd, [
|
|
8707
|
-
`# Repo Analysis`,
|
|
8708
|
-
``,
|
|
8709
|
-
`Goal: ${request.goal}`,
|
|
8710
|
-
`Workspace: ${request.cwd}`,
|
|
8711
|
-
`Task: ${task.id} - ${task.title}`,
|
|
8712
|
-
``,
|
|
8713
|
-
`## Observations`,
|
|
8714
|
-
`- Successful tool probes: ${successfulResults.length}/${toolResults.length}`,
|
|
8715
|
-
`- Workspace state: ${request.workspaceState}`,
|
|
8716
|
-
`- Detected scripts: ${scripts.length > 0 ? scripts.join(", ") : "none"}`,
|
|
8717
|
-
``,
|
|
8718
|
-
renderToolResultSection(toolResults)
|
|
8719
|
-
].join("\n"));
|
|
8720
|
-
return {
|
|
8721
|
-
status: "completed",
|
|
8722
|
-
summary: `Completed ${task.id}: captured repo analysis and saved ${import_node_path4.default.basename(artifactPath)}.`,
|
|
8723
|
-
toolResults,
|
|
8724
|
-
artifacts: [
|
|
8725
|
-
artifactPath
|
|
8726
|
-
]
|
|
8727
|
-
};
|
|
9560
|
+
return fallback;
|
|
9561
|
+
}
|
|
9562
|
+
function truncateOutput(value) {
|
|
9563
|
+
if (!value) {
|
|
9564
|
+
return value;
|
|
8728
9565
|
}
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
`# Architecture Brief`,
|
|
8732
|
-
``,
|
|
8733
|
-
`Goal: ${request.goal}`,
|
|
8734
|
-
`Summary: ${plan.summary}`,
|
|
8735
|
-
`Repo Strategy: ${plan.repoStrategy.mode} - ${plan.repoStrategy.reasoning}`,
|
|
8736
|
-
``,
|
|
8737
|
-
`## Assumptions`,
|
|
8738
|
-
...plan.assumptions.map((assumption) => `- ${assumption}`),
|
|
8739
|
-
``,
|
|
8740
|
-
`## Milestones`,
|
|
8741
|
-
...plan.milestones.flatMap((milestone) => [
|
|
8742
|
-
`### ${milestone.title}`,
|
|
8743
|
-
milestone.objective,
|
|
8744
|
-
...milestone.tasks.map((milestoneTask) => `- ${milestoneTask.id}: ${milestoneTask.title} [${milestoneTask.agentRole}]`),
|
|
8745
|
-
``
|
|
8746
|
-
]),
|
|
8747
|
-
`## Verification`,
|
|
8748
|
-
...plan.verificationChecklist.map((item) => `- ${item}`)
|
|
8749
|
-
].join("\n"));
|
|
8750
|
-
return {
|
|
8751
|
-
status: "completed",
|
|
8752
|
-
summary: `Completed ${task.id}: wrote architecture brief ${import_node_path4.default.basename(artifactPath)}.`,
|
|
8753
|
-
toolResults: [],
|
|
8754
|
-
artifacts: [
|
|
8755
|
-
artifactPath
|
|
8756
|
-
]
|
|
8757
|
-
};
|
|
9566
|
+
if (value.length <= DEFAULT_CAPTURE_LIMIT) {
|
|
9567
|
+
return value;
|
|
8758
9568
|
}
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
9569
|
+
const omitted = value.length - DEFAULT_CAPTURE_LIMIT;
|
|
9570
|
+
return `${value.slice(0, DEFAULT_CAPTURE_LIMIT)}
|
|
9571
|
+
... [truncated ${omitted} chars]`;
|
|
9572
|
+
}
|
|
9573
|
+
function isIgnoredWorkspacePath(value) {
|
|
9574
|
+
const normalized = value.replace(/\\/g, "/");
|
|
9575
|
+
const segments = normalized.split("/");
|
|
9576
|
+
return segments.some((segment) => DEFAULT_IGNORE_SEGMENTS.has(segment));
|
|
9577
|
+
}
|
|
9578
|
+
function extractPatchArtifacts(cwd, patch) {
|
|
9579
|
+
const paths = /* @__PURE__ */ new Set();
|
|
9580
|
+
for (const line of patch.split("\n")) {
|
|
9581
|
+
if (!line.startsWith("+++ ") && !line.startsWith("--- ")) {
|
|
9582
|
+
continue;
|
|
8771
9583
|
}
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8775
|
-
const logsDir = import_node_path4.default.join(resolveKimbhoDir(cwd), "logs");
|
|
8776
|
-
const outputPath = import_node_path4.default.join(logsDir, `${sessionId}-${label}.md`);
|
|
8777
|
-
await (0, import_promises4.writeFile)(outputPath, `${content}
|
|
8778
|
-
`, "utf8");
|
|
8779
|
-
return outputPath;
|
|
8780
|
-
}
|
|
8781
|
-
};
|
|
8782
|
-
|
|
8783
|
-
// src/commands/agents.ts
|
|
8784
|
-
function createAgentsCommand() {
|
|
8785
|
-
return new Command("agents").description("Show the current agent hierarchy and allowed tools.").option("--active", "Show only agents assigned to the latest session", false).action(async (options) => {
|
|
8786
|
-
const orchestrator = new ExecutionOrchestrator();
|
|
8787
|
-
const session = options.active ? await loadLatestSession(process.cwd()) : null;
|
|
8788
|
-
const profiles = session ? listAgentProfiles().filter((agent) => session.assignedAgents.includes(agent.role)) : listAgentProfiles();
|
|
8789
|
-
if (session) {
|
|
8790
|
-
console.log(`Session: ${session.id}`);
|
|
8791
|
-
console.log(`Ready tasks: ${session.readyTaskIds.join(", ") || "none"}`);
|
|
8792
|
-
console.log(`Blocked tasks: ${session.blockedTaskIds.join(", ") || "none"}`);
|
|
9584
|
+
const raw = line.slice(4).trim();
|
|
9585
|
+
if (!raw || raw === "/dev/null") {
|
|
9586
|
+
continue;
|
|
8793
9587
|
}
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
console.log(` tools: ${tools.join(", ")}`);
|
|
8800
|
-
console.log(` concurrency: ${agent.maxConcurrentTasks}`);
|
|
9588
|
+
const normalized = raw.startsWith("a/") || raw.startsWith("b/") ? raw.slice(2) : raw;
|
|
9589
|
+
try {
|
|
9590
|
+
paths.add(resolveWorkspacePath(cwd, normalized));
|
|
9591
|
+
} catch {
|
|
9592
|
+
continue;
|
|
8801
9593
|
}
|
|
8802
|
-
}
|
|
9594
|
+
}
|
|
9595
|
+
return Array.from(paths);
|
|
8803
9596
|
}
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
var BUILTIN_PROVIDER_TEMPLATES = [
|
|
8810
|
-
{
|
|
8811
|
-
id: "openai",
|
|
8812
|
-
label: "OpenAI",
|
|
8813
|
-
driver: "openai-responses",
|
|
8814
|
-
defaultBaseUrl: "https://api.openai.com/v1",
|
|
8815
|
-
defaultApiKeyEnv: "OPENAI_API_KEY",
|
|
8816
|
-
defaultModel: "gpt-5",
|
|
8817
|
-
notes: "Uses the OpenAI Responses API."
|
|
8818
|
-
},
|
|
8819
|
-
{
|
|
8820
|
-
id: "anthropic",
|
|
8821
|
-
label: "Anthropic",
|
|
8822
|
-
driver: "anthropic-messages",
|
|
8823
|
-
defaultBaseUrl: "https://api.anthropic.com/v1/messages",
|
|
8824
|
-
defaultApiKeyEnv: "ANTHROPIC_API_KEY",
|
|
8825
|
-
notes: "Uses the Anthropic Messages API."
|
|
8826
|
-
},
|
|
8827
|
-
{
|
|
8828
|
-
id: "openrouter",
|
|
8829
|
-
label: "OpenRouter",
|
|
8830
|
-
driver: "openai-compatible",
|
|
8831
|
-
defaultBaseUrl: "https://openrouter.ai/api/v1",
|
|
8832
|
-
defaultApiKeyEnv: "OPENROUTER_API_KEY",
|
|
8833
|
-
notes: "Routes through the OpenAI-compatible OpenRouter API."
|
|
8834
|
-
},
|
|
8835
|
-
{
|
|
8836
|
-
id: "ollama",
|
|
8837
|
-
label: "Ollama",
|
|
8838
|
-
driver: "ollama",
|
|
8839
|
-
defaultBaseUrl: "http://localhost:11434",
|
|
8840
|
-
defaultModel: "qwen2.5-coder:7b",
|
|
8841
|
-
notes: "Runs against a local Ollama server."
|
|
8842
|
-
},
|
|
8843
|
-
{
|
|
8844
|
-
id: "lmstudio",
|
|
8845
|
-
label: "LM Studio",
|
|
8846
|
-
driver: "openai-compatible",
|
|
8847
|
-
defaultBaseUrl: "http://localhost:1234/v1",
|
|
8848
|
-
notes: "Works with LM Studio's local OpenAI-compatible server."
|
|
9597
|
+
function resolveWorkspacePath(cwd, filePath) {
|
|
9598
|
+
const resolved = import_node_path5.default.resolve(cwd, filePath);
|
|
9599
|
+
const relative = import_node_path5.default.relative(cwd, resolved);
|
|
9600
|
+
if (relative.startsWith("..") || import_node_path5.default.isAbsolute(relative)) {
|
|
9601
|
+
throw new Error(`Path "${filePath}" escapes the workspace.`);
|
|
8849
9602
|
}
|
|
8850
|
-
|
|
8851
|
-
function listProviderTemplates() {
|
|
8852
|
-
return BUILTIN_PROVIDER_TEMPLATES.slice().sort((left, right) => left.id.localeCompare(right.id));
|
|
9603
|
+
return resolved;
|
|
8853
9604
|
}
|
|
8854
|
-
function
|
|
8855
|
-
return
|
|
9605
|
+
async function runSpawn(command, args, cwd, timeoutMs) {
|
|
9606
|
+
return new Promise((resolve, reject) => {
|
|
9607
|
+
const child = (0, import_node_child_process.spawn)(command, args, {
|
|
9608
|
+
cwd,
|
|
9609
|
+
env: import_node_process.default.env,
|
|
9610
|
+
stdio: [
|
|
9611
|
+
"ignore",
|
|
9612
|
+
"pipe",
|
|
9613
|
+
"pipe"
|
|
9614
|
+
]
|
|
9615
|
+
});
|
|
9616
|
+
const stdout = [];
|
|
9617
|
+
const stderr = [];
|
|
9618
|
+
let settled = false;
|
|
9619
|
+
let timedOut = false;
|
|
9620
|
+
const timer = setTimeout(() => {
|
|
9621
|
+
timedOut = true;
|
|
9622
|
+
child.kill("SIGTERM");
|
|
9623
|
+
setTimeout(() => child.kill("SIGKILL"), 1e3).unref();
|
|
9624
|
+
}, timeoutMs);
|
|
9625
|
+
child.stdout.on("data", (chunk) => {
|
|
9626
|
+
stdout.push(String(chunk));
|
|
9627
|
+
});
|
|
9628
|
+
child.stderr.on("data", (chunk) => {
|
|
9629
|
+
stderr.push(String(chunk));
|
|
9630
|
+
});
|
|
9631
|
+
child.on("error", (error) => {
|
|
9632
|
+
if (settled) {
|
|
9633
|
+
return;
|
|
9634
|
+
}
|
|
9635
|
+
settled = true;
|
|
9636
|
+
clearTimeout(timer);
|
|
9637
|
+
reject(error);
|
|
9638
|
+
});
|
|
9639
|
+
child.on("close", (code) => {
|
|
9640
|
+
if (settled) {
|
|
9641
|
+
return;
|
|
9642
|
+
}
|
|
9643
|
+
settled = true;
|
|
9644
|
+
clearTimeout(timer);
|
|
9645
|
+
resolve({
|
|
9646
|
+
code,
|
|
9647
|
+
stdout: stdout.join(""),
|
|
9648
|
+
stderr: stderr.join(""),
|
|
9649
|
+
timedOut
|
|
9650
|
+
});
|
|
9651
|
+
});
|
|
9652
|
+
});
|
|
8856
9653
|
}
|
|
8857
|
-
function
|
|
8858
|
-
|
|
9654
|
+
async function runShellCommand(toolId, command, cwd, timeoutMs) {
|
|
9655
|
+
const shell = import_node_process.default.env.SHELL ?? "/bin/sh";
|
|
9656
|
+
const result = await runSpawn(shell, [
|
|
9657
|
+
"-lc",
|
|
9658
|
+
command
|
|
9659
|
+
], cwd, timeoutMs);
|
|
9660
|
+
const success = !result.timedOut && result.code === 0;
|
|
9661
|
+
const summary = result.timedOut ? `Command timed out after ${timeoutMs}ms.` : success ? `Command completed successfully.` : `Command exited with code ${result.code ?? "unknown"}.`;
|
|
9662
|
+
return ToolResultSchema.parse({
|
|
9663
|
+
toolId,
|
|
9664
|
+
success,
|
|
9665
|
+
summary,
|
|
9666
|
+
stdout: truncateOutput(result.stdout),
|
|
9667
|
+
stderr: truncateOutput(result.stderr)
|
|
9668
|
+
});
|
|
8859
9669
|
}
|
|
8860
|
-
function
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
const normalized = trimTrailingSlash(baseUrl);
|
|
8865
|
-
if (templateId !== "lmstudio") {
|
|
8866
|
-
return normalized;
|
|
9670
|
+
async function executeFileRead(input, context) {
|
|
9671
|
+
const rawPath = typeof input.path === "string" ? input.path : null;
|
|
9672
|
+
if (!rawPath) {
|
|
9673
|
+
throw new Error("file.read requires a string path.");
|
|
8867
9674
|
}
|
|
9675
|
+
const targetPath = resolveWorkspacePath(context.cwd, rawPath);
|
|
9676
|
+
const contents = await (0, import_promises5.readFile)(targetPath, "utf8");
|
|
9677
|
+
return ToolResultSchema.parse({
|
|
9678
|
+
toolId: "file.read",
|
|
9679
|
+
success: true,
|
|
9680
|
+
summary: `Read ${import_node_path5.default.relative(context.cwd, targetPath) || import_node_path5.default.basename(targetPath)}.`,
|
|
9681
|
+
stdout: truncateOutput(contents),
|
|
9682
|
+
artifacts: [
|
|
9683
|
+
targetPath
|
|
9684
|
+
]
|
|
9685
|
+
});
|
|
9686
|
+
}
|
|
9687
|
+
async function executeFileList(input, context, timeoutMs) {
|
|
9688
|
+
const root = typeof input.path === "string" && input.path.trim().length > 0 ? input.path : ".";
|
|
9689
|
+
const pattern = typeof input.pattern === "string" && input.pattern.trim().length > 0 ? input.pattern : null;
|
|
9690
|
+
const limit = parseLimitValue(input.limit, 200);
|
|
9691
|
+
const searchRoot = resolveWorkspacePath(context.cwd, root);
|
|
9692
|
+
const relativeRoot = import_node_path5.default.relative(context.cwd, searchRoot) || ".";
|
|
9693
|
+
const rootArg = relativeRoot === "." ? "." : relativeRoot;
|
|
9694
|
+
let result;
|
|
8868
9695
|
try {
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
9696
|
+
result = await runSpawn("rg", [
|
|
9697
|
+
"--files",
|
|
9698
|
+
...DEFAULT_IGNORE_GLOBS.flatMap((glob) => [
|
|
9699
|
+
"-g",
|
|
9700
|
+
glob
|
|
9701
|
+
]),
|
|
9702
|
+
rootArg
|
|
9703
|
+
], context.cwd, timeoutMs);
|
|
9704
|
+
} catch (error) {
|
|
9705
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9706
|
+
if (!message.includes("ENOENT")) {
|
|
9707
|
+
throw error;
|
|
8873
9708
|
}
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
function buildProviderFromTemplate(templateId, options = {}) {
|
|
8880
|
-
const template = findProviderTemplate(templateId);
|
|
8881
|
-
if (!template) {
|
|
8882
|
-
throw new Error(`Unknown provider template "${templateId}".`);
|
|
9709
|
+
result = await runSpawn("find", [
|
|
9710
|
+
rootArg,
|
|
9711
|
+
"-type",
|
|
9712
|
+
"f"
|
|
9713
|
+
], context.cwd, timeoutMs);
|
|
8883
9714
|
}
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
...options.model ?? template.defaultModel ? {
|
|
8895
|
-
defaultModel: options.model ?? template.defaultModel
|
|
8896
|
-
} : {},
|
|
8897
|
-
...options.models && options.models.length > 0 ? {
|
|
8898
|
-
models: options.models
|
|
8899
|
-
} : {}
|
|
9715
|
+
const lines = result.stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !isIgnoredWorkspacePath(line));
|
|
9716
|
+
const filtered = pattern ? lines.filter((line) => line.toLowerCase().includes(pattern.toLowerCase())) : lines;
|
|
9717
|
+
const sliced = filtered.slice(0, limit);
|
|
9718
|
+
const success = !result.timedOut && result.code === 0;
|
|
9719
|
+
return ToolResultSchema.parse({
|
|
9720
|
+
toolId: "file.list",
|
|
9721
|
+
success,
|
|
9722
|
+
summary: success ? `Listed ${sliced.length} file${sliced.length === 1 ? "" : "s"} from ${rootArg}.` : result.timedOut ? `File listing timed out after ${timeoutMs}ms.` : `File listing failed with code ${result.code ?? "unknown"}.`,
|
|
9723
|
+
stdout: sliced.join("\n"),
|
|
9724
|
+
stderr: truncateOutput(result.stderr)
|
|
8900
9725
|
});
|
|
8901
9726
|
}
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
9727
|
+
async function executeFileSearch(input, context, timeoutMs) {
|
|
9728
|
+
const pattern = typeof input.pattern === "string" ? input.pattern.trim() : "";
|
|
9729
|
+
const root = typeof input.path === "string" && input.path.trim().length > 0 ? input.path : ".";
|
|
9730
|
+
const limit = parseLimitValue(input.limit, 50);
|
|
9731
|
+
if (!pattern) {
|
|
9732
|
+
throw new Error("file.search requires a non-empty pattern.");
|
|
9733
|
+
}
|
|
9734
|
+
const searchRoot = resolveWorkspacePath(context.cwd, root);
|
|
9735
|
+
const relativeRoot = import_node_path5.default.relative(context.cwd, searchRoot) || ".";
|
|
9736
|
+
const rootArg = relativeRoot === "." ? "." : relativeRoot;
|
|
9737
|
+
let result;
|
|
9738
|
+
try {
|
|
9739
|
+
result = await runSpawn("rg", [
|
|
9740
|
+
"-n",
|
|
9741
|
+
"--no-heading",
|
|
9742
|
+
"--color",
|
|
9743
|
+
"never",
|
|
9744
|
+
"--max-columns",
|
|
9745
|
+
"240",
|
|
9746
|
+
...DEFAULT_IGNORE_GLOBS.flatMap((glob) => [
|
|
9747
|
+
"-g",
|
|
9748
|
+
glob
|
|
9749
|
+
]),
|
|
9750
|
+
pattern,
|
|
9751
|
+
rootArg
|
|
9752
|
+
], context.cwd, timeoutMs);
|
|
9753
|
+
} catch (error) {
|
|
9754
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9755
|
+
if (!message.includes("ENOENT")) {
|
|
9756
|
+
throw error;
|
|
9757
|
+
}
|
|
9758
|
+
result = await runSpawn("grep", [
|
|
9759
|
+
"-R",
|
|
9760
|
+
"-n",
|
|
9761
|
+
pattern,
|
|
9762
|
+
rootArg
|
|
9763
|
+
], context.cwd, timeoutMs);
|
|
8910
9764
|
}
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
}
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
...init,
|
|
8921
|
-
signal: makeAbortSignal(timeoutMs)
|
|
9765
|
+
const isNoMatch = result.code === 1 && !result.timedOut;
|
|
9766
|
+
const lines = result.stdout.split("\n").map((line) => line.trimEnd()).filter((line) => line.length > 0 && !isIgnoredWorkspacePath(line)).slice(0, limit);
|
|
9767
|
+
const success = !result.timedOut && result.code === 0 || isNoMatch;
|
|
9768
|
+
return ToolResultSchema.parse({
|
|
9769
|
+
toolId: "file.search",
|
|
9770
|
+
success,
|
|
9771
|
+
summary: isNoMatch ? `No matches found for "${pattern}" in ${rootArg}.` : success ? `Found ${lines.length} match${lines.length === 1 ? "" : "es"} for "${pattern}" in ${rootArg}.` : result.timedOut ? `File search timed out after ${timeoutMs}ms.` : `File search failed with code ${result.code ?? "unknown"}.`,
|
|
9772
|
+
stdout: lines.join("\n"),
|
|
9773
|
+
stderr: truncateOutput(result.stderr)
|
|
8922
9774
|
});
|
|
8923
|
-
if (!response.ok) {
|
|
8924
|
-
throw new Error(`Request failed with ${response.status} ${response.statusText}`);
|
|
8925
|
-
}
|
|
8926
|
-
return response.json();
|
|
8927
|
-
}
|
|
8928
|
-
function trimTrailingSlash2(value) {
|
|
8929
|
-
return value.replace(/\/+$/, "");
|
|
8930
|
-
}
|
|
8931
|
-
function joinUrl(baseUrl, suffix) {
|
|
8932
|
-
const normalizedBase = trimTrailingSlash2(baseUrl);
|
|
8933
|
-
const normalizedSuffix = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
8934
|
-
return `${normalizedBase}${normalizedSuffix}`;
|
|
8935
9775
|
}
|
|
8936
|
-
function
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
...definition.headers
|
|
8942
|
-
};
|
|
8943
|
-
if (includeJsonContentType) {
|
|
8944
|
-
headers["content-type"] = "application/json";
|
|
9776
|
+
async function executeFileWrite(input, context) {
|
|
9777
|
+
const rawPath = typeof input.path === "string" ? input.path : null;
|
|
9778
|
+
const content = typeof input.content === "string" ? input.content : null;
|
|
9779
|
+
if (!rawPath) {
|
|
9780
|
+
throw new Error("file.write requires a string path.");
|
|
8945
9781
|
}
|
|
8946
|
-
if (
|
|
8947
|
-
|
|
9782
|
+
if (content === null) {
|
|
9783
|
+
throw new Error("file.write requires a string content field.");
|
|
8948
9784
|
}
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
headers["X-Title"] = appName;
|
|
9785
|
+
const targetPath = resolveWorkspacePath(context.cwd, rawPath);
|
|
9786
|
+
let existed = true;
|
|
9787
|
+
try {
|
|
9788
|
+
await (0, import_promises5.access)(targetPath);
|
|
9789
|
+
} catch {
|
|
9790
|
+
existed = false;
|
|
8956
9791
|
}
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
const data = Array.isArray(response.data) ? response.data : [];
|
|
8967
|
-
const models = data.flatMap((item) => {
|
|
8968
|
-
if (typeof item !== "object" || item === null) {
|
|
8969
|
-
return [];
|
|
8970
|
-
}
|
|
8971
|
-
const record = item;
|
|
8972
|
-
const pricing = typeof record.pricing === "object" && record.pricing !== null ? record.pricing : null;
|
|
8973
|
-
const architecture = typeof record.architecture === "object" && record.architecture !== null ? record.architecture : null;
|
|
8974
|
-
return [
|
|
8975
|
-
ProviderModelSchema.parse({
|
|
8976
|
-
id: String(record.id ?? ""),
|
|
8977
|
-
...typeof record.name === "string" ? {
|
|
8978
|
-
name: record.name
|
|
8979
|
-
} : {},
|
|
8980
|
-
...typeof record.description === "string" ? {
|
|
8981
|
-
description: record.description
|
|
8982
|
-
} : {},
|
|
8983
|
-
...typeof record.context_length === "number" ? {
|
|
8984
|
-
contextLength: record.context_length
|
|
8985
|
-
} : {},
|
|
8986
|
-
...pricing && typeof pricing.prompt === "string" ? {
|
|
8987
|
-
promptPrice: pricing.prompt
|
|
8988
|
-
} : {},
|
|
8989
|
-
...pricing && typeof pricing.completion === "string" ? {
|
|
8990
|
-
completionPrice: pricing.completion
|
|
8991
|
-
} : {},
|
|
8992
|
-
...architecture && typeof architecture.modality === "string" ? {
|
|
8993
|
-
modality: architecture.modality
|
|
8994
|
-
} : {},
|
|
8995
|
-
providerId
|
|
8996
|
-
})
|
|
8997
|
-
];
|
|
9792
|
+
await (0, import_promises5.mkdir)(import_node_path5.default.dirname(targetPath), { recursive: true });
|
|
9793
|
+
await (0, import_promises5.writeFile)(targetPath, content, "utf8");
|
|
9794
|
+
return ToolResultSchema.parse({
|
|
9795
|
+
toolId: "file.write",
|
|
9796
|
+
success: true,
|
|
9797
|
+
summary: `${existed ? "Updated" : "Created"} ${import_node_path5.default.relative(context.cwd, targetPath) || import_node_path5.default.basename(targetPath)}.`,
|
|
9798
|
+
artifacts: [
|
|
9799
|
+
targetPath
|
|
9800
|
+
]
|
|
8998
9801
|
});
|
|
8999
|
-
return filterModels(models, input);
|
|
9000
9802
|
}
|
|
9001
|
-
function
|
|
9002
|
-
const
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
}
|
|
9008
|
-
const record = item;
|
|
9009
|
-
return [
|
|
9010
|
-
ProviderModelSchema.parse({
|
|
9011
|
-
id: String(record.id ?? ""),
|
|
9012
|
-
...typeof record.display_name === "string" ? {
|
|
9013
|
-
name: record.display_name
|
|
9014
|
-
} : {},
|
|
9015
|
-
providerId
|
|
9016
|
-
})
|
|
9017
|
-
];
|
|
9018
|
-
});
|
|
9019
|
-
return filterModels(models, input);
|
|
9803
|
+
async function executeShell(input, context, timeoutMs) {
|
|
9804
|
+
const command = typeof input.command === "string" ? input.command : null;
|
|
9805
|
+
if (!command) {
|
|
9806
|
+
throw new Error("shell.exec requires a command string.");
|
|
9807
|
+
}
|
|
9808
|
+
return runShellCommand("shell.exec", command, context.cwd, timeoutMs);
|
|
9020
9809
|
}
|
|
9021
|
-
function
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9810
|
+
async function executeGitStatus(_input, context, timeoutMs) {
|
|
9811
|
+
return runShellCommand("git.status", "git status --short --branch", context.cwd, timeoutMs);
|
|
9812
|
+
}
|
|
9813
|
+
async function executeGitDiff(input, context, timeoutMs) {
|
|
9814
|
+
const rawPath = typeof input.path === "string" && input.path.trim().length > 0 ? input.path : null;
|
|
9815
|
+
const args = [
|
|
9816
|
+
"diff",
|
|
9817
|
+
"--no-ext-diff",
|
|
9818
|
+
"--stat",
|
|
9819
|
+
"--patch",
|
|
9820
|
+
"--unified=3"
|
|
9821
|
+
];
|
|
9822
|
+
if (rawPath) {
|
|
9823
|
+
const targetPath = resolveWorkspacePath(context.cwd, rawPath);
|
|
9824
|
+
const relativeTarget = import_node_path5.default.relative(context.cwd, targetPath) || ".";
|
|
9825
|
+
args.push("--", relativeTarget);
|
|
9826
|
+
}
|
|
9827
|
+
const repoProbe = await runSpawn("git", [
|
|
9828
|
+
"rev-parse",
|
|
9829
|
+
"--is-inside-work-tree"
|
|
9830
|
+
], context.cwd, timeoutMs);
|
|
9831
|
+
if (repoProbe.code !== 0 || repoProbe.stdout.trim() !== "true") {
|
|
9832
|
+
return ToolResultSchema.parse({
|
|
9833
|
+
toolId: "git.diff",
|
|
9834
|
+
success: false,
|
|
9835
|
+
summary: "git diff unavailable because the workspace is not a git repository.",
|
|
9836
|
+
stderr: "Not a git repository.",
|
|
9837
|
+
artifacts: []
|
|
9838
|
+
});
|
|
9839
|
+
}
|
|
9840
|
+
const result = await runSpawn("git", args, context.cwd, timeoutMs);
|
|
9841
|
+
const isClean = result.code === 0 && result.stdout.trim().length === 0;
|
|
9842
|
+
const success = !result.timedOut && (result.code === 0 || isClean);
|
|
9843
|
+
return ToolResultSchema.parse({
|
|
9844
|
+
toolId: "git.diff",
|
|
9845
|
+
success,
|
|
9846
|
+
summary: isClean ? "No git diff changes detected." : success ? `Captured git diff${rawPath ? ` for ${rawPath}` : ""}.` : result.timedOut ? `git diff timed out after ${timeoutMs}ms.` : `git diff failed with code ${result.code ?? "unknown"}.`,
|
|
9847
|
+
stdout: truncateOutput(result.stdout),
|
|
9848
|
+
stderr: truncateOutput(result.stderr)
|
|
9046
9849
|
});
|
|
9047
|
-
return filterModels(models, input);
|
|
9048
9850
|
}
|
|
9049
|
-
function
|
|
9050
|
-
const
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9851
|
+
async function executeTests(input, context, timeoutMs) {
|
|
9852
|
+
const command = typeof input.command === "string" && input.command.trim().length > 0 ? input.command : "npm test";
|
|
9853
|
+
const result = await runShellCommand("tests.run", command, context.cwd, timeoutMs);
|
|
9854
|
+
return ToolResultSchema.parse({
|
|
9855
|
+
...result,
|
|
9856
|
+
summary: result.success ? `Verification command passed: ${command}` : `Verification command failed: ${command}`
|
|
9857
|
+
});
|
|
9858
|
+
}
|
|
9859
|
+
async function executeFilePatch(input, context, timeoutMs) {
|
|
9860
|
+
const patch = typeof input.patch === "string" ? input.patch : null;
|
|
9861
|
+
if (!patch) {
|
|
9862
|
+
throw new Error("file.patch requires a unified diff in the patch field.");
|
|
9056
9863
|
}
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
|
|
9864
|
+
const tempDir = await (0, import_promises5.mkdtemp)(import_node_path5.default.join((0, import_node_os.tmpdir)(), "kimbho-patch-"));
|
|
9865
|
+
const patchPath = import_node_path5.default.join(tempDir, "change.patch");
|
|
9866
|
+
try {
|
|
9867
|
+
await (0, import_promises5.writeFile)(patchPath, patch, "utf8");
|
|
9868
|
+
const result = await runSpawn("git", [
|
|
9869
|
+
"apply",
|
|
9870
|
+
"--recount",
|
|
9871
|
+
"--whitespace=nowarn",
|
|
9872
|
+
patchPath
|
|
9873
|
+
], context.cwd, timeoutMs);
|
|
9874
|
+
const success = !result.timedOut && result.code === 0;
|
|
9875
|
+
return ToolResultSchema.parse({
|
|
9876
|
+
toolId: "file.patch",
|
|
9877
|
+
success,
|
|
9878
|
+
summary: success ? "Patch applied successfully." : result.timedOut ? `Patch timed out after ${timeoutMs}ms.` : `Patch failed with code ${result.code ?? "unknown"}.`,
|
|
9879
|
+
stdout: truncateOutput(result.stdout),
|
|
9880
|
+
stderr: truncateOutput(result.stderr),
|
|
9881
|
+
artifacts: extractPatchArtifacts(context.cwd, patch)
|
|
9063
9882
|
});
|
|
9883
|
+
} finally {
|
|
9884
|
+
await (0, import_promises5.rm)(tempDir, { recursive: true, force: true });
|
|
9064
9885
|
}
|
|
9065
|
-
return messages;
|
|
9066
|
-
}
|
|
9067
|
-
function toPromptText(input) {
|
|
9068
|
-
const messages = toChatMessages(input);
|
|
9069
|
-
return messages.map((message) => `${message.role.toUpperCase()}: ${message.content}`).join("\n\n");
|
|
9070
9886
|
}
|
|
9071
|
-
|
|
9072
|
-
|
|
9073
|
-
|
|
9074
|
-
|
|
9887
|
+
var ToolRuntime = class {
|
|
9888
|
+
registry;
|
|
9889
|
+
executors;
|
|
9890
|
+
constructor(registry = createDefaultToolRegistry()) {
|
|
9891
|
+
this.registry = registry;
|
|
9892
|
+
this.executors = /* @__PURE__ */ new Map([
|
|
9893
|
+
[
|
|
9894
|
+
"file.read",
|
|
9895
|
+
(input, context) => executeFileRead(input, context)
|
|
9896
|
+
],
|
|
9897
|
+
[
|
|
9898
|
+
"file.list",
|
|
9899
|
+
executeFileList
|
|
9900
|
+
],
|
|
9901
|
+
[
|
|
9902
|
+
"file.search",
|
|
9903
|
+
executeFileSearch
|
|
9904
|
+
],
|
|
9905
|
+
[
|
|
9906
|
+
"file.write",
|
|
9907
|
+
(input, context) => executeFileWrite(input, context)
|
|
9908
|
+
],
|
|
9909
|
+
[
|
|
9910
|
+
"file.patch",
|
|
9911
|
+
executeFilePatch
|
|
9912
|
+
],
|
|
9913
|
+
[
|
|
9914
|
+
"shell.exec",
|
|
9915
|
+
executeShell
|
|
9916
|
+
],
|
|
9917
|
+
[
|
|
9918
|
+
"git.status",
|
|
9919
|
+
executeGitStatus
|
|
9920
|
+
],
|
|
9921
|
+
[
|
|
9922
|
+
"git.diff",
|
|
9923
|
+
executeGitDiff
|
|
9924
|
+
],
|
|
9925
|
+
[
|
|
9926
|
+
"tests.run",
|
|
9927
|
+
executeTests
|
|
9928
|
+
]
|
|
9929
|
+
]);
|
|
9075
9930
|
}
|
|
9076
|
-
|
|
9931
|
+
async run(toolId, input, context) {
|
|
9932
|
+
const descriptor = this.registry.get(toolId);
|
|
9933
|
+
if (!descriptor) {
|
|
9934
|
+
throw new Error(`Unknown tool "${toolId}".`);
|
|
9935
|
+
}
|
|
9936
|
+
const executor = this.executors.get(toolId);
|
|
9937
|
+
if (!executor) {
|
|
9938
|
+
throw new Error(`No executor registered for "${toolId}".`);
|
|
9939
|
+
}
|
|
9940
|
+
return executor(input, context, descriptor.timeoutMs);
|
|
9941
|
+
}
|
|
9942
|
+
};
|
|
9943
|
+
|
|
9944
|
+
// ../agent-runtime/dist/orchestrator.js
|
|
9945
|
+
function createSessionId() {
|
|
9946
|
+
return `session-${Date.now()}`;
|
|
9077
9947
|
}
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
|
|
9948
|
+
function createEventId(type, taskId) {
|
|
9949
|
+
return `${type}-${taskId ?? "session"}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
9950
|
+
}
|
|
9951
|
+
function isTaskReady(task, completedTaskIds) {
|
|
9952
|
+
return task.status === "pending" && task.dependsOn.every((taskId) => completedTaskIds.has(taskId));
|
|
9953
|
+
}
|
|
9954
|
+
function completedTaskIdsFromPlan(plan) {
|
|
9955
|
+
return new Set(flattenPlanTasks(plan).filter((task) => task.status === "completed").map((task) => task.id));
|
|
9956
|
+
}
|
|
9957
|
+
function deriveStatus(plan, readyTasks, blockedTasks) {
|
|
9958
|
+
const tasks = flattenPlanTasks(plan);
|
|
9959
|
+
const completed = tasks.filter((task) => task.status === "completed").length;
|
|
9960
|
+
if (completed === tasks.length && tasks.length > 0) {
|
|
9961
|
+
return "completed";
|
|
9082
9962
|
}
|
|
9083
|
-
|
|
9084
|
-
|
|
9085
|
-
if (!apiKey) {
|
|
9086
|
-
return {
|
|
9087
|
-
ok: false,
|
|
9088
|
-
message: `Missing ${this.definition.apiKeyEnv ?? "API key env var"}`
|
|
9089
|
-
};
|
|
9090
|
-
}
|
|
9091
|
-
return {
|
|
9092
|
-
ok: true,
|
|
9093
|
-
message: `${this.definition.baseUrl ?? "https://api.openai.com/v1"} (${this.definition.defaultModel ?? "model not set"})`
|
|
9094
|
-
};
|
|
9963
|
+
if (readyTasks.length > 0) {
|
|
9964
|
+
return completed > 0 ? "running" : "ready";
|
|
9095
9965
|
}
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
if (!apiKey) {
|
|
9099
|
-
throw new Error(`Missing ${this.definition.apiKeyEnv ?? "API key env var"} for provider "${this.definition.id}".`);
|
|
9100
|
-
}
|
|
9101
|
-
const baseUrl = this.definition.baseUrl ?? "https://api.openai.com/v1";
|
|
9102
|
-
const payload = await requestJson(joinUrl(baseUrl, "/models"), {
|
|
9103
|
-
method: "GET",
|
|
9104
|
-
headers: buildProviderHeaders(this.definition, apiKey, false)
|
|
9105
|
-
});
|
|
9106
|
-
return mapOpenAIStyleModels(this.definition.id, payload, input);
|
|
9966
|
+
if (blockedTasks.length > 0) {
|
|
9967
|
+
return "blocked";
|
|
9107
9968
|
}
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9969
|
+
return "planned";
|
|
9970
|
+
}
|
|
9971
|
+
function updateTaskStatus(plan, taskId, status) {
|
|
9972
|
+
const milestones = plan.milestones.map((milestone) => ({
|
|
9973
|
+
...milestone,
|
|
9974
|
+
tasks: milestone.tasks.map((task) => task.id === taskId ? {
|
|
9975
|
+
...task,
|
|
9976
|
+
status
|
|
9977
|
+
} : task)
|
|
9978
|
+
}));
|
|
9979
|
+
return {
|
|
9980
|
+
...plan,
|
|
9981
|
+
milestones
|
|
9982
|
+
};
|
|
9983
|
+
}
|
|
9984
|
+
function replaceTask(plan, taskId, mapper) {
|
|
9985
|
+
const milestones = plan.milestones.map((milestone) => ({
|
|
9986
|
+
...milestone,
|
|
9987
|
+
tasks: milestone.tasks.map((task) => task.id === taskId ? mapper(task) : task)
|
|
9988
|
+
}));
|
|
9989
|
+
return {
|
|
9990
|
+
...plan,
|
|
9991
|
+
milestones
|
|
9992
|
+
};
|
|
9993
|
+
}
|
|
9994
|
+
function maybeAppendNote(notes, note) {
|
|
9995
|
+
return notes.at(-1) === note ? notes : [
|
|
9996
|
+
...notes,
|
|
9997
|
+
note
|
|
9998
|
+
];
|
|
9999
|
+
}
|
|
10000
|
+
function mergeUnique(items) {
|
|
10001
|
+
return Array.from(new Set(items.filter((item) => item.trim().length > 0)));
|
|
10002
|
+
}
|
|
10003
|
+
function renderToolResultSection(results) {
|
|
10004
|
+
return results.map((result) => {
|
|
10005
|
+
const lines = [
|
|
10006
|
+
`## ${result.toolId}`,
|
|
10007
|
+
`- success: ${result.success ? "yes" : "no"}`,
|
|
10008
|
+
`- summary: ${result.summary}`
|
|
10009
|
+
];
|
|
10010
|
+
if (result.stdout) {
|
|
10011
|
+
lines.push("");
|
|
10012
|
+
lines.push("```text");
|
|
10013
|
+
lines.push(result.stdout);
|
|
10014
|
+
lines.push("```");
|
|
9112
10015
|
}
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
headers: {
|
|
9127
|
-
...buildProviderHeaders(this.definition, apiKey)
|
|
9128
|
-
},
|
|
9129
|
-
body: JSON.stringify({
|
|
9130
|
-
model,
|
|
9131
|
-
input: messages,
|
|
9132
|
-
...typeof input.temperature === "number" ? {
|
|
9133
|
-
temperature: input.temperature
|
|
9134
|
-
} : {},
|
|
9135
|
-
...typeof input.maxTokens === "number" ? {
|
|
9136
|
-
max_output_tokens: input.maxTokens
|
|
9137
|
-
} : {}
|
|
9138
|
-
})
|
|
9139
|
-
});
|
|
9140
|
-
const text = typeof response.output_text === "string" ? response.output_text : Array.isArray(response.output) ? response.output.flatMap((item) => typeof item === "object" && item !== null && "content" in item && Array.isArray(item.content) ? item.content : []).flatMap((item) => typeof item === "object" && item !== null && "text" in item ? [
|
|
9141
|
-
String(item.text)
|
|
9142
|
-
] : []).join("\n") : "";
|
|
9143
|
-
return {
|
|
9144
|
-
text,
|
|
9145
|
-
model
|
|
9146
|
-
};
|
|
10016
|
+
if (result.stderr) {
|
|
10017
|
+
lines.push("");
|
|
10018
|
+
lines.push("```text");
|
|
10019
|
+
lines.push(result.stderr);
|
|
10020
|
+
lines.push("```");
|
|
10021
|
+
}
|
|
10022
|
+
return lines.join("\n");
|
|
10023
|
+
}).join("\n\n");
|
|
10024
|
+
}
|
|
10025
|
+
function extractPackageScripts(toolResults) {
|
|
10026
|
+
const packageResult = toolResults.find((result) => result.artifacts.some((artifact) => artifact.endsWith("package.json")) && result.stdout);
|
|
10027
|
+
if (!packageResult?.stdout) {
|
|
10028
|
+
return [];
|
|
9147
10029
|
}
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
|
|
9152
|
-
|
|
10030
|
+
try {
|
|
10031
|
+
const parsed = JSON.parse(packageResult.stdout);
|
|
10032
|
+
return Object.keys(parsed.scripts ?? {}).sort();
|
|
10033
|
+
} catch {
|
|
10034
|
+
return [];
|
|
10035
|
+
}
|
|
10036
|
+
}
|
|
10037
|
+
function createDebuggerHandoffTask(task, outcome) {
|
|
10038
|
+
const failingResults = outcome.toolResults.filter((result) => !result.success);
|
|
10039
|
+
const failureSummaries = failingResults.slice(-3).map((result) => `${result.toolId}: ${result.summary}`);
|
|
10040
|
+
const artifactList = outcome.artifacts.slice(-5);
|
|
10041
|
+
const handoffDescriptionParts = [
|
|
10042
|
+
`Debugger handoff from ${task.agentRole}.`,
|
|
10043
|
+
`Original task: ${task.title}.`,
|
|
10044
|
+
`Handoff reason: ${outcome.summary}`,
|
|
10045
|
+
`Inspect the recent failures, reproduce the issue, apply a targeted repair, and rerun verification before finishing.`,
|
|
10046
|
+
failureSummaries.length > 0 ? `Recent failures:
|
|
10047
|
+
${failureSummaries.map((item) => `- ${item}`).join("\n")}` : "",
|
|
10048
|
+
artifactList.length > 0 ? `Failure artifacts:
|
|
10049
|
+
${artifactList.map((artifact) => `- ${artifact}`).join("\n")}` : "",
|
|
10050
|
+
`Original task description: ${task.description}`
|
|
10051
|
+
].filter((part) => part.length > 0);
|
|
10052
|
+
return {
|
|
10053
|
+
...task,
|
|
10054
|
+
title: task.title.startsWith("Debug ") ? task.title : `Debug ${task.title}`,
|
|
10055
|
+
description: handoffDescriptionParts.join("\n\n"),
|
|
10056
|
+
type: "verification",
|
|
10057
|
+
status: "pending",
|
|
10058
|
+
agentRole: "test-debugger",
|
|
10059
|
+
acceptanceCriteria: mergeUnique([
|
|
10060
|
+
...task.acceptanceCriteria,
|
|
10061
|
+
"The failing verification is reproduced or explicitly explained.",
|
|
10062
|
+
"A targeted repair is applied or a concrete blocker is recorded.",
|
|
10063
|
+
"Verification passes after the repair."
|
|
10064
|
+
]),
|
|
10065
|
+
outputs: mergeUnique([
|
|
10066
|
+
...task.outputs,
|
|
10067
|
+
"Debugger handoff notes",
|
|
10068
|
+
"Verification evidence"
|
|
10069
|
+
]),
|
|
10070
|
+
filesLikelyTouched: mergeUnique([
|
|
10071
|
+
...task.filesLikelyTouched,
|
|
10072
|
+
".kimbho/logs/"
|
|
10073
|
+
]),
|
|
10074
|
+
riskLevel: "high"
|
|
10075
|
+
};
|
|
10076
|
+
}
|
|
10077
|
+
var ExecutionOrchestrator = class {
|
|
10078
|
+
toolRegistry;
|
|
10079
|
+
toolRuntime;
|
|
10080
|
+
constructor(toolRegistry = createDefaultToolRegistry(), toolRuntime = new ToolRuntime(toolRegistry)) {
|
|
10081
|
+
this.toolRegistry = toolRegistry;
|
|
10082
|
+
this.toolRuntime = toolRuntime;
|
|
9153
10083
|
}
|
|
9154
|
-
|
|
9155
|
-
const
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
};
|
|
9161
|
-
}
|
|
10084
|
+
buildEnvelope(request, plan, sessionId = createSessionId()) {
|
|
10085
|
+
const tasks = flattenPlanTasks(plan);
|
|
10086
|
+
const completedTaskIds = completedTaskIdsFromPlan(plan);
|
|
10087
|
+
const readyTasks = tasks.filter((task) => isTaskReady(task, completedTaskIds));
|
|
10088
|
+
const blockedTasks = tasks.filter((task) => task.status !== "completed" && !isTaskReady(task, completedTaskIds));
|
|
10089
|
+
const assignedAgents = this.selectAgentsForTasks(readyTasks);
|
|
9162
10090
|
return {
|
|
9163
|
-
|
|
9164
|
-
|
|
10091
|
+
sessionId,
|
|
10092
|
+
request,
|
|
10093
|
+
plan,
|
|
10094
|
+
readyTasks,
|
|
10095
|
+
blockedTasks,
|
|
10096
|
+
assignedAgents
|
|
9165
10097
|
};
|
|
9166
10098
|
}
|
|
9167
|
-
|
|
9168
|
-
const
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
10099
|
+
createSessionSnapshot(envelope, overrides = {}) {
|
|
10100
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
10101
|
+
const status = deriveStatus(envelope.plan, envelope.readyTasks, envelope.blockedTasks);
|
|
10102
|
+
return SessionSnapshotSchema.parse({
|
|
10103
|
+
id: envelope.sessionId,
|
|
10104
|
+
goal: envelope.request.goal,
|
|
10105
|
+
cwd: envelope.request.cwd,
|
|
10106
|
+
startedAt: timestamp,
|
|
10107
|
+
updatedAt: timestamp,
|
|
10108
|
+
status,
|
|
10109
|
+
request: envelope.request,
|
|
10110
|
+
plan: envelope.plan,
|
|
10111
|
+
readyTaskIds: envelope.readyTasks.map((task) => task.id),
|
|
10112
|
+
blockedTaskIds: envelope.blockedTasks.map((task) => task.id),
|
|
10113
|
+
completedTaskIds: Array.from(completedTaskIdsFromPlan(envelope.plan)),
|
|
10114
|
+
assignedAgents: envelope.assignedAgents.map((agent) => agent.role),
|
|
10115
|
+
notes: [
|
|
10116
|
+
"Initial session snapshot created from the current plan."
|
|
10117
|
+
],
|
|
10118
|
+
events: [],
|
|
10119
|
+
...overrides
|
|
9181
10120
|
});
|
|
9182
|
-
return mapAnthropicModels(this.definition.id, payload, input);
|
|
9183
10121
|
}
|
|
9184
|
-
async
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
10122
|
+
async continueSession(session, options = {}) {
|
|
10123
|
+
let workingPlan = session.plan;
|
|
10124
|
+
let notes = [
|
|
10125
|
+
...session.notes
|
|
10126
|
+
];
|
|
10127
|
+
let events = [
|
|
10128
|
+
...session.events
|
|
10129
|
+
];
|
|
10130
|
+
const maxAutoTasks = options.maxAutoTasks ?? 2;
|
|
10131
|
+
let executedTasks = 0;
|
|
10132
|
+
while (executedTasks < maxAutoTasks) {
|
|
10133
|
+
const envelope = this.buildEnvelope(session.request, workingPlan, session.id);
|
|
10134
|
+
const autoTask = envelope.readyTasks.find((task) => this.canAutoExecuteTask(task));
|
|
10135
|
+
if (!autoTask) {
|
|
10136
|
+
const nextTask = envelope.readyTasks[0];
|
|
10137
|
+
if (nextTask) {
|
|
10138
|
+
const note = `Execution paused at ${nextTask.id} (${nextTask.title}): no built-in executor is wired for ${nextTask.agentRole} yet.`;
|
|
10139
|
+
notes = maybeAppendNote(notes, note);
|
|
10140
|
+
}
|
|
10141
|
+
return this.createSessionSnapshot(this.buildEnvelope(session.request, workingPlan, session.id), {
|
|
10142
|
+
startedAt: session.startedAt,
|
|
10143
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10144
|
+
notes,
|
|
10145
|
+
events
|
|
10146
|
+
});
|
|
10147
|
+
}
|
|
10148
|
+
events.push({
|
|
10149
|
+
id: createEventId("task-started", autoTask.id),
|
|
10150
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10151
|
+
type: "task-started",
|
|
10152
|
+
taskId: autoTask.id,
|
|
10153
|
+
agentRole: autoTask.agentRole,
|
|
10154
|
+
message: `Started ${autoTask.id}: ${autoTask.title}`,
|
|
10155
|
+
toolResults: [],
|
|
10156
|
+
artifacts: []
|
|
10157
|
+
});
|
|
10158
|
+
const outcome = await this.executeTask(session.id, autoTask, session.request, workingPlan, options);
|
|
10159
|
+
workingPlan = outcome.status === "handoff" ? replaceTask(workingPlan, autoTask.id, () => createDebuggerHandoffTask(autoTask, outcome)) : updateTaskStatus(workingPlan, autoTask.id, outcome.status === "completed" ? "completed" : outcome.status === "blocked" ? "blocked" : "pending");
|
|
10160
|
+
notes = maybeAppendNote(notes, outcome.summary);
|
|
10161
|
+
events.push({
|
|
10162
|
+
id: createEventId(outcome.status === "completed" ? "task-completed" : outcome.status === "blocked" ? "task-blocked" : outcome.status === "handoff" ? "task-handed-off" : "task-paused", autoTask.id),
|
|
10163
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10164
|
+
type: outcome.status === "completed" ? "task-completed" : outcome.status === "blocked" ? "task-blocked" : outcome.status === "handoff" ? "task-handed-off" : "task-paused",
|
|
10165
|
+
taskId: autoTask.id,
|
|
10166
|
+
agentRole: autoTask.agentRole,
|
|
10167
|
+
message: outcome.summary,
|
|
10168
|
+
toolResults: outcome.toolResults,
|
|
10169
|
+
artifacts: outcome.artifacts
|
|
10170
|
+
});
|
|
10171
|
+
executedTasks += 1;
|
|
9188
10172
|
}
|
|
9189
|
-
const
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
method: "POST",
|
|
9199
|
-
headers: {
|
|
9200
|
-
"content-type": "application/json",
|
|
9201
|
-
"x-api-key": apiKey,
|
|
9202
|
-
"anthropic-version": "2023-06-01",
|
|
9203
|
-
...this.definition.headers
|
|
9204
|
-
},
|
|
9205
|
-
body: JSON.stringify({
|
|
9206
|
-
model,
|
|
9207
|
-
messages: conversation,
|
|
9208
|
-
...systemMessage ? {
|
|
9209
|
-
system: systemMessage
|
|
9210
|
-
} : {},
|
|
9211
|
-
max_tokens: input.maxTokens ?? 2048,
|
|
9212
|
-
...typeof input.temperature === "number" ? {
|
|
9213
|
-
temperature: input.temperature
|
|
9214
|
-
} : {}
|
|
9215
|
-
})
|
|
10173
|
+
const postLimitEnvelope = this.buildEnvelope(session.request, workingPlan, session.id);
|
|
10174
|
+
if (postLimitEnvelope.readyTasks.length > 0) {
|
|
10175
|
+
notes = maybeAppendNote(notes, `Auto execution limit reached after ${executedTasks} task${executedTasks === 1 ? "" : "s"}.`);
|
|
10176
|
+
}
|
|
10177
|
+
return this.createSessionSnapshot(postLimitEnvelope, {
|
|
10178
|
+
startedAt: session.startedAt,
|
|
10179
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10180
|
+
notes,
|
|
10181
|
+
events
|
|
9216
10182
|
});
|
|
9217
|
-
const content = Array.isArray(response.content) ? response.content : [];
|
|
9218
|
-
const text = content.flatMap((item) => typeof item === "object" && item !== null && "text" in item ? [
|
|
9219
|
-
String(item.text)
|
|
9220
|
-
] : []).join("\n");
|
|
9221
|
-
return {
|
|
9222
|
-
text,
|
|
9223
|
-
model
|
|
9224
|
-
};
|
|
9225
10183
|
}
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
constructor(definition) {
|
|
9230
|
-
this.definition = ProviderDefinitionSchema.parse(definition);
|
|
10184
|
+
selectAgentsForTasks(tasks) {
|
|
10185
|
+
const roles = new Set(tasks.map((task) => task.agentRole));
|
|
10186
|
+
return Array.from(roles).map((role) => AGENT_CATALOG[role]).sort((left, right) => left.role.localeCompare(right.role));
|
|
9231
10187
|
}
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
10188
|
+
toolsForAgent(role) {
|
|
10189
|
+
return this.toolRegistry.byRole(role);
|
|
10190
|
+
}
|
|
10191
|
+
canAutoExecuteTask(task) {
|
|
10192
|
+
return task.agentRole !== "session-orchestrator" && task.agentRole !== "execution-manager";
|
|
10193
|
+
}
|
|
10194
|
+
async executeTask(sessionId, task, request, plan, options = {}) {
|
|
10195
|
+
if (task.agentRole === "repo-analyst") {
|
|
10196
|
+
return this.executeRepoAnalysisTask(sessionId, task, request);
|
|
9238
10197
|
}
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
return {
|
|
9242
|
-
ok: false,
|
|
9243
|
-
message: `${this.definition.baseUrl} (${this.definition.defaultModel ?? "model not set"}), missing ${this.definition.apiKeyEnv}`
|
|
9244
|
-
};
|
|
10198
|
+
if (task.agentRole === "planner") {
|
|
10199
|
+
return this.executePlannerTask(sessionId, task, request, plan);
|
|
9245
10200
|
}
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
} catch (error) {
|
|
10201
|
+
return this.executeAutonomousSpecialistTask(sessionId, task, request, options);
|
|
10202
|
+
}
|
|
10203
|
+
async executeAutonomousSpecialistTask(sessionId, task, request, options) {
|
|
10204
|
+
const config = await loadConfig(request.cwd);
|
|
10205
|
+
if (!config) {
|
|
9252
10206
|
return {
|
|
9253
|
-
|
|
9254
|
-
|
|
10207
|
+
status: "blocked",
|
|
10208
|
+
summary: `Task ${task.id} cannot run because no .kimbho/config.json was found in ${request.cwd}.`,
|
|
10209
|
+
toolResults: [],
|
|
10210
|
+
artifacts: []
|
|
9255
10211
|
};
|
|
9256
10212
|
}
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
}
|
|
9266
|
-
const apiKey = resolveApiKey(this.definition);
|
|
9267
|
-
const payload = await requestJson(joinUrl(this.definition.baseUrl, "/models"), {
|
|
9268
|
-
method: "GET",
|
|
9269
|
-
headers: buildProviderHeaders(this.definition, apiKey, false)
|
|
10213
|
+
const executor = new AutonomousTaskExecutor(config, this.toolRegistry, this.toolRuntime);
|
|
10214
|
+
return executor.execute(sessionId, task, request, {
|
|
10215
|
+
...typeof options.maxAgentSteps === "number" ? {
|
|
10216
|
+
maxSteps: options.maxAgentSteps
|
|
10217
|
+
} : {},
|
|
10218
|
+
...typeof options.maxRepairAttempts === "number" ? {
|
|
10219
|
+
maxRepairAttempts: options.maxRepairAttempts
|
|
10220
|
+
} : {}
|
|
9270
10221
|
});
|
|
9271
|
-
return mapOpenAIStyleModels(this.definition.id, payload, input);
|
|
9272
10222
|
}
|
|
9273
|
-
async
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
}
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
10223
|
+
async executeRepoAnalysisTask(sessionId, task, request) {
|
|
10224
|
+
const context = { cwd: request.cwd };
|
|
10225
|
+
const toolResults = await Promise.all([
|
|
10226
|
+
this.safeRunTool("git.status", {}, context),
|
|
10227
|
+
this.safeRunTool("file.read", { path: "package.json" }, context),
|
|
10228
|
+
this.safeRunTool("file.read", { path: "README.md" }, context)
|
|
10229
|
+
]);
|
|
10230
|
+
const successfulResults = toolResults.filter((result) => result.success);
|
|
10231
|
+
const scripts = extractPackageScripts(successfulResults);
|
|
10232
|
+
const artifactPath = await this.writeLogArtifact(sessionId, "repo-analysis", request.cwd, [
|
|
10233
|
+
`# Repo Analysis`,
|
|
10234
|
+
``,
|
|
10235
|
+
`Goal: ${request.goal}`,
|
|
10236
|
+
`Workspace: ${request.cwd}`,
|
|
10237
|
+
`Task: ${task.id} - ${task.title}`,
|
|
10238
|
+
``,
|
|
10239
|
+
`## Observations`,
|
|
10240
|
+
`- Successful tool probes: ${successfulResults.length}/${toolResults.length}`,
|
|
10241
|
+
`- Workspace state: ${request.workspaceState}`,
|
|
10242
|
+
`- Detected scripts: ${scripts.length > 0 ? scripts.join(", ") : "none"}`,
|
|
10243
|
+
``,
|
|
10244
|
+
renderToolResultSection(toolResults)
|
|
10245
|
+
].join("\n"));
|
|
9296
10246
|
return {
|
|
9297
|
-
|
|
9298
|
-
|
|
10247
|
+
status: "completed",
|
|
10248
|
+
summary: `Completed ${task.id}: captured repo analysis and saved ${import_node_path6.default.basename(artifactPath)}.`,
|
|
10249
|
+
toolResults,
|
|
10250
|
+
artifacts: [
|
|
10251
|
+
artifactPath
|
|
10252
|
+
]
|
|
9299
10253
|
};
|
|
9300
10254
|
}
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
10255
|
+
async executePlannerTask(sessionId, task, request, plan) {
|
|
10256
|
+
const artifactPath = await this.writeLogArtifact(sessionId, "architecture-brief", request.cwd, [
|
|
10257
|
+
`# Architecture Brief`,
|
|
10258
|
+
``,
|
|
10259
|
+
`Goal: ${request.goal}`,
|
|
10260
|
+
`Summary: ${plan.summary}`,
|
|
10261
|
+
`Repo Strategy: ${plan.repoStrategy.mode} - ${plan.repoStrategy.reasoning}`,
|
|
10262
|
+
``,
|
|
10263
|
+
`## Assumptions`,
|
|
10264
|
+
...plan.assumptions.map((assumption) => `- ${assumption}`),
|
|
10265
|
+
``,
|
|
10266
|
+
`## Milestones`,
|
|
10267
|
+
...plan.milestones.flatMap((milestone) => [
|
|
10268
|
+
`### ${milestone.title}`,
|
|
10269
|
+
milestone.objective,
|
|
10270
|
+
...milestone.tasks.map((milestoneTask) => `- ${milestoneTask.id}: ${milestoneTask.title} [${milestoneTask.agentRole}]`),
|
|
10271
|
+
``
|
|
10272
|
+
]),
|
|
10273
|
+
`## Verification`,
|
|
10274
|
+
...plan.verificationChecklist.map((item) => `- ${item}`)
|
|
10275
|
+
].join("\n"));
|
|
10276
|
+
return {
|
|
10277
|
+
status: "completed",
|
|
10278
|
+
summary: `Completed ${task.id}: wrote architecture brief ${import_node_path6.default.basename(artifactPath)}.`,
|
|
10279
|
+
toolResults: [],
|
|
10280
|
+
artifacts: [
|
|
10281
|
+
artifactPath
|
|
10282
|
+
]
|
|
10283
|
+
};
|
|
9306
10284
|
}
|
|
9307
|
-
async
|
|
9308
|
-
const baseUrl = this.definition.baseUrl ?? "http://localhost:11434";
|
|
10285
|
+
async safeRunTool(toolId, input, context) {
|
|
9309
10286
|
try {
|
|
9310
|
-
await
|
|
9311
|
-
return {
|
|
9312
|
-
ok: true,
|
|
9313
|
-
message: `${baseUrl} (${this.definition.defaultModel ?? "model not set"})`
|
|
9314
|
-
};
|
|
10287
|
+
return await this.toolRuntime.run(toolId, input, context);
|
|
9315
10288
|
} catch (error) {
|
|
10289
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9316
10290
|
return {
|
|
9317
|
-
|
|
9318
|
-
|
|
10291
|
+
toolId,
|
|
10292
|
+
success: false,
|
|
10293
|
+
summary: `${toolId} failed: ${message}`,
|
|
10294
|
+
stderr: message,
|
|
10295
|
+
artifacts: []
|
|
9319
10296
|
};
|
|
9320
10297
|
}
|
|
9321
10298
|
}
|
|
9322
|
-
async
|
|
9323
|
-
|
|
9324
|
-
const
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
const model = requireModel(this.definition, input);
|
|
9330
|
-
const response = await requestJson(`${baseUrl}/api/generate`, {
|
|
9331
|
-
method: "POST",
|
|
9332
|
-
headers: {
|
|
9333
|
-
"content-type": "application/json",
|
|
9334
|
-
...this.definition.headers
|
|
9335
|
-
},
|
|
9336
|
-
body: JSON.stringify({
|
|
9337
|
-
model,
|
|
9338
|
-
prompt: input.userPrompt ?? toPromptText(input),
|
|
9339
|
-
...input.systemPrompt ? {
|
|
9340
|
-
system: input.systemPrompt
|
|
9341
|
-
} : {},
|
|
9342
|
-
stream: false,
|
|
9343
|
-
...typeof input.temperature === "number" ? {
|
|
9344
|
-
options: {
|
|
9345
|
-
temperature: input.temperature
|
|
9346
|
-
}
|
|
9347
|
-
} : {}
|
|
9348
|
-
})
|
|
9349
|
-
});
|
|
9350
|
-
return {
|
|
9351
|
-
text: typeof response.response === "string" ? response.response : "",
|
|
9352
|
-
model
|
|
9353
|
-
};
|
|
9354
|
-
}
|
|
9355
|
-
};
|
|
9356
|
-
async function createCustomModuleProvider(definition, cwd) {
|
|
9357
|
-
if (!definition.modulePath) {
|
|
9358
|
-
throw new Error(`Provider "${definition.id}" requires modulePath.`);
|
|
9359
|
-
}
|
|
9360
|
-
const modulePath = import_node_path5.default.isAbsolute(definition.modulePath) ? definition.modulePath : import_node_path5.default.join(cwd, definition.modulePath);
|
|
9361
|
-
await (0, import_promises5.access)(modulePath);
|
|
9362
|
-
const module2 = await import((0, import_node_url.pathToFileURL)(modulePath).href);
|
|
9363
|
-
const createProvider = typeof module2.createProvider === "function" ? module2.createProvider : typeof module2.default === "function" ? module2.default : null;
|
|
9364
|
-
if (!createProvider) {
|
|
9365
|
-
throw new Error(`Custom provider module "${modulePath}" must export createProvider(definition).`);
|
|
9366
|
-
}
|
|
9367
|
-
return createProvider(definition);
|
|
9368
|
-
}
|
|
9369
|
-
var BUILTIN_FACTORIES = {
|
|
9370
|
-
"openai-responses": {
|
|
9371
|
-
driver: "openai-responses",
|
|
9372
|
-
create: async (definition) => new OpenAIResponsesProvider(definition)
|
|
9373
|
-
},
|
|
9374
|
-
"anthropic-messages": {
|
|
9375
|
-
driver: "anthropic-messages",
|
|
9376
|
-
create: async (definition) => new AnthropicMessagesProvider(definition)
|
|
9377
|
-
},
|
|
9378
|
-
"openai-compatible": {
|
|
9379
|
-
driver: "openai-compatible",
|
|
9380
|
-
create: async (definition) => new OpenAICompatibleProvider(definition)
|
|
9381
|
-
},
|
|
9382
|
-
ollama: {
|
|
9383
|
-
driver: "ollama",
|
|
9384
|
-
create: async (definition) => new OllamaProvider(definition)
|
|
9385
|
-
},
|
|
9386
|
-
"custom-module": {
|
|
9387
|
-
driver: "custom-module",
|
|
9388
|
-
create: (definition, cwd) => createCustomModuleProvider(definition, cwd)
|
|
10299
|
+
async writeLogArtifact(sessionId, label, cwd, content) {
|
|
10300
|
+
await ensureKimbhoDir(cwd);
|
|
10301
|
+
const logsDir = import_node_path6.default.join(resolveKimbhoDir(cwd), "logs");
|
|
10302
|
+
const outputPath = import_node_path6.default.join(logsDir, `${sessionId}-${label}.md`);
|
|
10303
|
+
await (0, import_promises6.writeFile)(outputPath, `${content}
|
|
10304
|
+
`, "utf8");
|
|
10305
|
+
return outputPath;
|
|
9389
10306
|
}
|
|
9390
10307
|
};
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9402
|
-
const normalized = ProviderDefinitionSchema.parse(definition);
|
|
9403
|
-
const factory = this.factories.get(normalized.driver);
|
|
9404
|
-
if (!factory) {
|
|
9405
|
-
throw new Error(`No provider factory registered for driver "${normalized.driver}".`);
|
|
10308
|
+
|
|
10309
|
+
// src/commands/agents.ts
|
|
10310
|
+
function createAgentsCommand() {
|
|
10311
|
+
return new Command("agents").description("Show the current agent hierarchy and allowed tools.").option("--active", "Show only agents assigned to the latest session", false).action(async (options) => {
|
|
10312
|
+
const orchestrator = new ExecutionOrchestrator();
|
|
10313
|
+
const session = options.active ? await loadLatestSession(process.cwd()) : null;
|
|
10314
|
+
const profiles = session ? listAgentProfiles().filter((agent) => session.assignedAgents.includes(agent.role)) : listAgentProfiles();
|
|
10315
|
+
if (session) {
|
|
10316
|
+
console.log(`Session: ${session.id}`);
|
|
10317
|
+
console.log(`Ready tasks: ${session.readyTaskIds.join(", ") || "none"}`);
|
|
10318
|
+
console.log(`Blocked tasks: ${session.blockedTaskIds.join(", ") || "none"}`);
|
|
9406
10319
|
}
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
}
|
|
9413
|
-
|
|
9414
|
-
const provider = await this.createProvider(definition);
|
|
9415
|
-
if (provider.listModels) {
|
|
9416
|
-
return provider.listModels(input);
|
|
10320
|
+
for (const agent of profiles) {
|
|
10321
|
+
const tools = orchestrator.toolsForAgent(agent.role).map((tool) => tool.id);
|
|
10322
|
+
console.log(`${agent.role}`);
|
|
10323
|
+
console.log(` brain: ${agent.brainRole}`);
|
|
10324
|
+
console.log(` purpose: ${agent.purpose}`);
|
|
10325
|
+
console.log(` tools: ${tools.join(", ")}`);
|
|
10326
|
+
console.log(` concurrency: ${agent.maxConcurrentTasks}`);
|
|
9417
10327
|
}
|
|
9418
|
-
|
|
9419
|
-
id: modelId,
|
|
9420
|
-
providerId: definition.id
|
|
9421
|
-
})), input);
|
|
9422
|
-
}
|
|
9423
|
-
};
|
|
9424
|
-
function createDefaultBrainProviderRegistry(cwd = process.cwd()) {
|
|
9425
|
-
return new BrainProviderRegistry(cwd);
|
|
10328
|
+
});
|
|
9426
10329
|
}
|
|
9427
10330
|
|
|
9428
|
-
//
|
|
9429
|
-
var
|
|
9430
|
-
config;
|
|
9431
|
-
registry;
|
|
9432
|
-
constructor(config, registry = createDefaultBrainProviderRegistry()) {
|
|
9433
|
-
this.config = config;
|
|
9434
|
-
this.registry = registry;
|
|
9435
|
-
}
|
|
9436
|
-
async resolve(role) {
|
|
9437
|
-
const settings = resolveBrainSettings(this.config, role);
|
|
9438
|
-
const provider = findProviderById(this.config, settings.providerId);
|
|
9439
|
-
if (!provider) {
|
|
9440
|
-
throw new Error(`Brain role "${role}" points to unknown provider "${settings.providerId}".`);
|
|
9441
|
-
}
|
|
9442
|
-
const model = resolveBrainModel(this.config, role);
|
|
9443
|
-
if (!model) {
|
|
9444
|
-
throw new Error(`Brain role "${role}" does not resolve to a model.`);
|
|
9445
|
-
}
|
|
9446
|
-
return {
|
|
9447
|
-
role,
|
|
9448
|
-
settings,
|
|
9449
|
-
provider,
|
|
9450
|
-
model,
|
|
9451
|
-
client: await this.registry.createProvider(provider)
|
|
9452
|
-
};
|
|
9453
|
-
}
|
|
9454
|
-
};
|
|
10331
|
+
// src/commands/brains.ts
|
|
10332
|
+
var import_node_process2 = __toESM(require("node:process"), 1);
|
|
9455
10333
|
|
|
9456
10334
|
// src/ui/renderPlan.ts
|
|
9457
10335
|
function humanizeRole(role) {
|
|
@@ -9996,7 +10874,7 @@ function createModelsCommand() {
|
|
|
9996
10874
|
}
|
|
9997
10875
|
|
|
9998
10876
|
// src/commands/plan.ts
|
|
9999
|
-
var
|
|
10877
|
+
var import_promises7 = require("node:fs/promises");
|
|
10000
10878
|
var import_node_process6 = __toESM(require("node:process"), 1);
|
|
10001
10879
|
|
|
10002
10880
|
// ../planner/dist/planner.js
|
|
@@ -10301,7 +11179,7 @@ function createPlan(input) {
|
|
|
10301
11179
|
|
|
10302
11180
|
// src/commands/plan.ts
|
|
10303
11181
|
async function detectWorkspaceState(cwd) {
|
|
10304
|
-
const entries = await (0,
|
|
11182
|
+
const entries = await (0, import_promises7.readdir)(cwd);
|
|
10305
11183
|
const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
|
|
10306
11184
|
return meaningfulEntries.length === 0 ? "empty" : "existing";
|
|
10307
11185
|
}
|
|
@@ -10464,7 +11342,7 @@ function parseCount(value) {
|
|
|
10464
11342
|
return parsed;
|
|
10465
11343
|
}
|
|
10466
11344
|
function createResumeCommand() {
|
|
10467
|
-
return new Command("resume").description("Resume the latest saved Kimbho session snapshot.").option("--json", "Print the latest session as JSON", false).option("--execute", "Continue auto-executing the ready-task frontier", false).option("--max-auto-tasks <count>", "Maximum ready tasks to auto-execute", parseCount, 2).action(async (options) => {
|
|
11345
|
+
return new Command("resume").description("Resume the latest saved Kimbho session snapshot.").option("--json", "Print the latest session as JSON", false).option("--execute", "Continue auto-executing the ready-task frontier", false).option("--max-auto-tasks <count>", "Maximum ready tasks to auto-execute", parseCount, 2).option("--max-agent-steps <count>", "Maximum tool/model steps per autonomous task", parseCount, 8).option("--max-repair-attempts <count>", "Maximum failed verification cycles per autonomous task", parseCount, 2).action(async (options) => {
|
|
10468
11346
|
const cwd = import_node_process8.default.cwd();
|
|
10469
11347
|
const session = await loadLatestSession(cwd);
|
|
10470
11348
|
if (!session) {
|
|
@@ -10473,7 +11351,9 @@ function createResumeCommand() {
|
|
|
10473
11351
|
return;
|
|
10474
11352
|
}
|
|
10475
11353
|
const snapshot = options.execute ? await new ExecutionOrchestrator().continueSession(session, {
|
|
10476
|
-
maxAutoTasks: options.maxAutoTasks
|
|
11354
|
+
maxAutoTasks: options.maxAutoTasks,
|
|
11355
|
+
maxAgentSteps: options.maxAgentSteps,
|
|
11356
|
+
maxRepairAttempts: options.maxRepairAttempts
|
|
10477
11357
|
}) : session;
|
|
10478
11358
|
if (options.execute) {
|
|
10479
11359
|
await saveSession(snapshot, cwd);
|
|
@@ -10487,10 +11367,10 @@ function createResumeCommand() {
|
|
|
10487
11367
|
}
|
|
10488
11368
|
|
|
10489
11369
|
// src/commands/run.ts
|
|
10490
|
-
var
|
|
11370
|
+
var import_promises8 = require("node:fs/promises");
|
|
10491
11371
|
var import_node_process9 = __toESM(require("node:process"), 1);
|
|
10492
11372
|
async function detectWorkspaceState2(cwd) {
|
|
10493
|
-
const entries = await (0,
|
|
11373
|
+
const entries = await (0, import_promises8.readdir)(cwd);
|
|
10494
11374
|
const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
|
|
10495
11375
|
return meaningfulEntries.length === 0 ? "empty" : "existing";
|
|
10496
11376
|
}
|
|
@@ -10507,7 +11387,7 @@ function createRunCommand() {
|
|
|
10507
11387
|
"Explicit execution constraint; can be provided multiple times",
|
|
10508
11388
|
(value, previous = []) => [...previous, value],
|
|
10509
11389
|
[]
|
|
10510
|
-
).option("--json", "Print the session snapshot as JSON", false).option("--snapshot-only", "Create the session without auto-executing ready tasks", false).option("--max-auto-tasks <count>", "Maximum ready tasks to auto-execute", parseCount2, 2).action(async (goal, options) => {
|
|
11390
|
+
).option("--json", "Print the session snapshot as JSON", false).option("--snapshot-only", "Create the session without auto-executing ready tasks", false).option("--max-auto-tasks <count>", "Maximum ready tasks to auto-execute", parseCount2, 2).option("--max-agent-steps <count>", "Maximum tool/model steps per autonomous task", parseCount2, 8).option("--max-repair-attempts <count>", "Maximum failed verification cycles per autonomous task", parseCount2, 2).action(async (goal, options) => {
|
|
10511
11391
|
const cwd = import_node_process9.default.cwd();
|
|
10512
11392
|
const orchestrator = new ExecutionOrchestrator();
|
|
10513
11393
|
let request;
|
|
@@ -10542,7 +11422,9 @@ function createRunCommand() {
|
|
|
10542
11422
|
const envelope = orchestrator.buildEnvelope(request, plan);
|
|
10543
11423
|
const initialSnapshot = orchestrator.createSessionSnapshot(envelope);
|
|
10544
11424
|
const snapshot = options.snapshotOnly ? initialSnapshot : await orchestrator.continueSession(initialSnapshot, {
|
|
10545
|
-
maxAutoTasks: options.maxAutoTasks
|
|
11425
|
+
maxAutoTasks: options.maxAutoTasks,
|
|
11426
|
+
maxAgentSteps: options.maxAgentSteps,
|
|
11427
|
+
maxRepairAttempts: options.maxRepairAttempts
|
|
10546
11428
|
});
|
|
10547
11429
|
const sessionPath = await saveSession(snapshot, cwd);
|
|
10548
11430
|
if (options.json) {
|
|
@@ -10630,7 +11512,7 @@ function createProgram(onOpenShell) {
|
|
|
10630
11512
|
}
|
|
10631
11513
|
|
|
10632
11514
|
// src/shell.ts
|
|
10633
|
-
var
|
|
11515
|
+
var import_promises9 = require("node:readline/promises");
|
|
10634
11516
|
var import_node_process10 = __toESM(require("node:process"), 1);
|
|
10635
11517
|
var AMBER = "\x1B[38;5;214m";
|
|
10636
11518
|
var BOLD = "\x1B[1m";
|
|
@@ -10658,6 +11540,9 @@ var TOP_LEVEL_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
10658
11540
|
"select",
|
|
10659
11541
|
"shell",
|
|
10660
11542
|
"status",
|
|
11543
|
+
"ask",
|
|
11544
|
+
"chat",
|
|
11545
|
+
"reset-chat",
|
|
10661
11546
|
"use-model"
|
|
10662
11547
|
]);
|
|
10663
11548
|
var MODEL_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -10672,6 +11557,7 @@ var BRAIN_ROLES = [
|
|
|
10672
11557
|
"reviewer",
|
|
10673
11558
|
"fast"
|
|
10674
11559
|
];
|
|
11560
|
+
var MAX_CHAT_MESSAGES = 12;
|
|
10675
11561
|
function color(code, value) {
|
|
10676
11562
|
return `${code}${value}${RESET}`;
|
|
10677
11563
|
}
|
|
@@ -10741,6 +11627,9 @@ function renderHelp() {
|
|
|
10741
11627
|
`${color(DIM, "Commands")}`,
|
|
10742
11628
|
"/status Show the active role, provider, and workspace state.",
|
|
10743
11629
|
"/brain <role> Change shell focus to planner, coder, reviewer, or fast.",
|
|
11630
|
+
"/ask <prompt> Send a prompt to the active model.",
|
|
11631
|
+
"/chat <prompt> Alias for /ask <prompt>.",
|
|
11632
|
+
"/reset-chat Clear the active model conversation history.",
|
|
10744
11633
|
"/model Show current brain assignments.",
|
|
10745
11634
|
"/providers List configured providers.",
|
|
10746
11635
|
"/providers templates Show built-in provider templates.",
|
|
@@ -10760,7 +11649,7 @@ function renderHelp() {
|
|
|
10760
11649
|
"/quit, /exit Leave the shell.",
|
|
10761
11650
|
"",
|
|
10762
11651
|
`${color(DIM, "Tip")}`,
|
|
10763
|
-
"Type
|
|
11652
|
+
"Type natural language directly to chat with the active model. Use /run when you want a plan/session."
|
|
10764
11653
|
].join("\n");
|
|
10765
11654
|
}
|
|
10766
11655
|
function renderStartupCard(cwd, state) {
|
|
@@ -10775,7 +11664,7 @@ function renderStartupCard(cwd, state) {
|
|
|
10775
11664
|
renderCardLine("approval", state.approvalMode),
|
|
10776
11665
|
renderCardLine("sandbox", state.sandboxMode),
|
|
10777
11666
|
renderCardLine("preset", state.stackPreset),
|
|
10778
|
-
renderCardLine("shortcuts", "/
|
|
11667
|
+
renderCardLine("shortcuts", "/ask /run /brain /providers /models /quit")
|
|
10779
11668
|
];
|
|
10780
11669
|
if (!state.configured) {
|
|
10781
11670
|
cardLines.push("setup: run /init or /providers add <template> to create .kimbho/config.json");
|
|
@@ -10791,7 +11680,7 @@ function printHeader(cwd, state) {
|
|
|
10791
11680
|
console.log(color(DIM, "Terminal-native coding agent"));
|
|
10792
11681
|
console.log(renderStartupCard(cwd, state));
|
|
10793
11682
|
console.log("");
|
|
10794
|
-
console.log(color(DIM, "Tip: configure providers and models here, then
|
|
11683
|
+
console.log(color(DIM, "Tip: configure providers and models here, then chat directly. Use /run for planning and execution."));
|
|
10795
11684
|
console.log(renderHelp());
|
|
10796
11685
|
console.log("");
|
|
10797
11686
|
}
|
|
@@ -10867,6 +11756,63 @@ function normalizeInputTokens(input) {
|
|
|
10867
11756
|
head: firstToken.startsWith("/") ? firstToken.slice(1) : firstToken
|
|
10868
11757
|
};
|
|
10869
11758
|
}
|
|
11759
|
+
function trimConversation(messages) {
|
|
11760
|
+
if (messages.length <= MAX_CHAT_MESSAGES) {
|
|
11761
|
+
return messages;
|
|
11762
|
+
}
|
|
11763
|
+
return messages.slice(messages.length - MAX_CHAT_MESSAGES);
|
|
11764
|
+
}
|
|
11765
|
+
function clearConversation(runtime, role) {
|
|
11766
|
+
runtime.conversations[role] = [];
|
|
11767
|
+
}
|
|
11768
|
+
function getConversation(runtime, role) {
|
|
11769
|
+
return runtime.conversations[role] ?? [];
|
|
11770
|
+
}
|
|
11771
|
+
function clearAllConversations(runtime) {
|
|
11772
|
+
for (const role of BRAIN_ROLES) {
|
|
11773
|
+
runtime.conversations[role] = [];
|
|
11774
|
+
}
|
|
11775
|
+
}
|
|
11776
|
+
async function handleChatPrompt(cwd, prompt, runtime) {
|
|
11777
|
+
const config = await loadConfig(cwd);
|
|
11778
|
+
if (!config) {
|
|
11779
|
+
throw new Error("No config found. Run /init or /providers add <template> first.");
|
|
11780
|
+
}
|
|
11781
|
+
const registry = createDefaultBrainProviderRegistry(cwd);
|
|
11782
|
+
const resolver = new BrainResolver(config, registry);
|
|
11783
|
+
const brain = await resolver.resolve(runtime.focusRole);
|
|
11784
|
+
const history = getConversation(runtime, runtime.focusRole);
|
|
11785
|
+
const messages = trimConversation([
|
|
11786
|
+
...history,
|
|
11787
|
+
{
|
|
11788
|
+
role: "user",
|
|
11789
|
+
content: prompt
|
|
11790
|
+
}
|
|
11791
|
+
]);
|
|
11792
|
+
const result = await brain.client.generateText({
|
|
11793
|
+
model: brain.model,
|
|
11794
|
+
messages,
|
|
11795
|
+
...brain.settings.promptPreamble ? {
|
|
11796
|
+
systemPrompt: brain.settings.promptPreamble
|
|
11797
|
+
} : {},
|
|
11798
|
+
...typeof brain.settings.temperature === "number" ? {
|
|
11799
|
+
temperature: brain.settings.temperature
|
|
11800
|
+
} : {},
|
|
11801
|
+
...typeof brain.settings.maxTokens === "number" ? {
|
|
11802
|
+
maxTokens: brain.settings.maxTokens
|
|
11803
|
+
} : {}
|
|
11804
|
+
});
|
|
11805
|
+
const nextConversation = trimConversation([
|
|
11806
|
+
...messages,
|
|
11807
|
+
{
|
|
11808
|
+
role: "assistant",
|
|
11809
|
+
content: result.text
|
|
11810
|
+
}
|
|
11811
|
+
]);
|
|
11812
|
+
runtime.conversations[runtime.focusRole] = nextConversation;
|
|
11813
|
+
console.log(color(DIM, `[${brain.role}] ${brain.provider.id}/${brain.model}`));
|
|
11814
|
+
console.log(result.text);
|
|
11815
|
+
}
|
|
10870
11816
|
function defaultProviderIdForTemplate(templateId) {
|
|
10871
11817
|
switch (templateId) {
|
|
10872
11818
|
case "openai":
|
|
@@ -11145,6 +12091,7 @@ async function handleProvidersCommand(cwd, tokens, runtime) {
|
|
|
11145
12091
|
throw new Error("Usage: /providers use <provider-id>");
|
|
11146
12092
|
}
|
|
11147
12093
|
const result = await useProviderGlobally(cwd, providerId);
|
|
12094
|
+
clearAllConversations(runtime);
|
|
11148
12095
|
runtime.lastModels = null;
|
|
11149
12096
|
console.log(`Updated ${result.outputPath}`);
|
|
11150
12097
|
console.log(`All roles now use provider ${providerId}${result.model ? ` (${result.model})` : ""}`);
|
|
@@ -11189,6 +12136,7 @@ async function handleModelsCommand(cwd, tokens, runtime) {
|
|
|
11189
12136
|
throw new Error("Usage: /models use <model-id>");
|
|
11190
12137
|
}
|
|
11191
12138
|
const outputPath = await assignModelGlobally(cwd, provider.id, modelId);
|
|
12139
|
+
clearAllConversations(runtime);
|
|
11192
12140
|
console.log(`Updated ${outputPath}`);
|
|
11193
12141
|
console.log(`Selected ${provider.id}/${modelId}`);
|
|
11194
12142
|
console.log("Assigned all roles");
|
|
@@ -11230,6 +12178,7 @@ async function handleModelSelection(cwd, modelId, runtime) {
|
|
|
11230
12178
|
throw new Error(`Active role "${runtime.focusRole}" points to unknown provider "${providerId}".`);
|
|
11231
12179
|
}
|
|
11232
12180
|
const outputPath = await assignModelGlobally(cwd, provider.id, modelId);
|
|
12181
|
+
clearAllConversations(runtime);
|
|
11233
12182
|
console.log(`Updated ${outputPath}`);
|
|
11234
12183
|
console.log(`Selected ${provider.id}/${modelId}`);
|
|
11235
12184
|
console.log("Assigned all roles");
|
|
@@ -11275,10 +12224,22 @@ function toExternalCommandTokens(input, state) {
|
|
|
11275
12224
|
`scaffold ${goal}`.trim()
|
|
11276
12225
|
];
|
|
11277
12226
|
}
|
|
11278
|
-
if (
|
|
11279
|
-
|
|
12227
|
+
if (head === "run") {
|
|
12228
|
+
const goal = normalizedInput.replace(/^\/?run\b\s*/, "");
|
|
12229
|
+
return goal ? [
|
|
11280
12230
|
"run",
|
|
11281
|
-
|
|
12231
|
+
goal
|
|
12232
|
+
] : [
|
|
12233
|
+
"run"
|
|
12234
|
+
];
|
|
12235
|
+
}
|
|
12236
|
+
if (head === "plan") {
|
|
12237
|
+
const goal = normalizedInput.replace(/^\/?plan\b\s*/, "");
|
|
12238
|
+
return goal ? [
|
|
12239
|
+
"plan",
|
|
12240
|
+
goal
|
|
12241
|
+
] : [
|
|
12242
|
+
"plan"
|
|
11282
12243
|
];
|
|
11283
12244
|
}
|
|
11284
12245
|
tokens[0] = head;
|
|
@@ -11309,9 +12270,14 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
|
|
|
11309
12270
|
return;
|
|
11310
12271
|
}
|
|
11311
12272
|
const { tokens, head } = normalizeInputTokens(trimmed);
|
|
12273
|
+
const firstToken = tokens[0];
|
|
11312
12274
|
if (!head) {
|
|
11313
12275
|
return;
|
|
11314
12276
|
}
|
|
12277
|
+
if (!firstToken?.startsWith("/") && !TOP_LEVEL_COMMANDS.has(head) && !head.startsWith("-")) {
|
|
12278
|
+
await handleChatPrompt(cwd, trimmed, runtime);
|
|
12279
|
+
return;
|
|
12280
|
+
}
|
|
11315
12281
|
if (head === "provider" || head === "providers") {
|
|
11316
12282
|
const subcommand = tokens[1];
|
|
11317
12283
|
const canHandleLocally = !subcommand || subcommand === "list" || subcommand === "templates" || subcommand === "check" || subcommand === "use" || subcommand === "add" && Boolean(tokens[2]) && !tokens[2]?.startsWith("-");
|
|
@@ -11339,6 +12305,19 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
|
|
|
11339
12305
|
await printBrainAssignments(cwd);
|
|
11340
12306
|
return;
|
|
11341
12307
|
}
|
|
12308
|
+
if (head === "ask" || head === "chat") {
|
|
12309
|
+
const prompt = tokens.slice(1).join(" ").trim();
|
|
12310
|
+
if (!prompt) {
|
|
12311
|
+
throw new Error(`Usage: /${head} <prompt>`);
|
|
12312
|
+
}
|
|
12313
|
+
await handleChatPrompt(cwd, prompt, runtime);
|
|
12314
|
+
return;
|
|
12315
|
+
}
|
|
12316
|
+
if (head === "reset-chat") {
|
|
12317
|
+
clearConversation(runtime, runtime.focusRole);
|
|
12318
|
+
console.log(`Cleared ${runtime.focusRole} conversation history.`);
|
|
12319
|
+
return;
|
|
12320
|
+
}
|
|
11342
12321
|
if (head === "models") {
|
|
11343
12322
|
await handleModelsCommand(cwd, [
|
|
11344
12323
|
"models",
|
|
@@ -11368,14 +12347,20 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
|
|
|
11368
12347
|
await execute(externalTokens);
|
|
11369
12348
|
}
|
|
11370
12349
|
async function runInteractiveShell(options) {
|
|
11371
|
-
const readline = (0,
|
|
12350
|
+
const readline = (0, import_promises9.createInterface)({
|
|
11372
12351
|
input: import_node_process10.default.stdin,
|
|
11373
12352
|
output: import_node_process10.default.stdout,
|
|
11374
12353
|
terminal: Boolean(import_node_process10.default.stdin.isTTY && import_node_process10.default.stdout.isTTY)
|
|
11375
12354
|
});
|
|
11376
12355
|
const runtime = {
|
|
11377
12356
|
focusRole: "coder",
|
|
11378
|
-
lastModels: null
|
|
12357
|
+
lastModels: null,
|
|
12358
|
+
conversations: {
|
|
12359
|
+
planner: [],
|
|
12360
|
+
coder: [],
|
|
12361
|
+
reviewer: [],
|
|
12362
|
+
fast: []
|
|
12363
|
+
}
|
|
11379
12364
|
};
|
|
11380
12365
|
let closed = false;
|
|
11381
12366
|
readline.on("SIGINT", () => {
|