@kimbho/kimbho-cli 0.1.3 → 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 +24 -4
- package/dist/index.cjs +2311 -1227
- 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({
|
|
@@ -8072,6 +8107,34 @@ function assignBrain(config, role, settings) {
|
|
|
8072
8107
|
}
|
|
8073
8108
|
});
|
|
8074
8109
|
}
|
|
8110
|
+
function buildRetainedBrainSettings(current, providerId, model) {
|
|
8111
|
+
return {
|
|
8112
|
+
providerId,
|
|
8113
|
+
...model ? {
|
|
8114
|
+
model
|
|
8115
|
+
} : {},
|
|
8116
|
+
...typeof current.temperature === "number" ? {
|
|
8117
|
+
temperature: current.temperature
|
|
8118
|
+
} : {},
|
|
8119
|
+
...typeof current.maxTokens === "number" ? {
|
|
8120
|
+
maxTokens: current.maxTokens
|
|
8121
|
+
} : {},
|
|
8122
|
+
...current.promptPreamble ? {
|
|
8123
|
+
promptPreamble: current.promptPreamble
|
|
8124
|
+
} : {}
|
|
8125
|
+
};
|
|
8126
|
+
}
|
|
8127
|
+
function assignProviderToAllBrains(config, providerId, model) {
|
|
8128
|
+
return KimbhoConfigSchema.parse({
|
|
8129
|
+
...config,
|
|
8130
|
+
brains: {
|
|
8131
|
+
planner: buildRetainedBrainSettings(config.brains.planner, providerId, model),
|
|
8132
|
+
coder: buildRetainedBrainSettings(config.brains.coder, providerId, model),
|
|
8133
|
+
reviewer: buildRetainedBrainSettings(config.brains.reviewer, providerId, model),
|
|
8134
|
+
fast: buildRetainedBrainSettings(config.brains.fast, providerId, model)
|
|
8135
|
+
}
|
|
8136
|
+
});
|
|
8137
|
+
}
|
|
8075
8138
|
function resolveBrainSettings(config, role) {
|
|
8076
8139
|
return config.brains[role];
|
|
8077
8140
|
}
|
|
@@ -8136,1255 +8199,2137 @@ async function loadLatestSession(cwd = process.cwd()) {
|
|
|
8136
8199
|
return loadSession(import_node_path2.default.join(sessionsDir, latest));
|
|
8137
8200
|
}
|
|
8138
8201
|
|
|
8139
|
-
// ../
|
|
8140
|
-
var
|
|
8141
|
-
"safe",
|
|
8142
|
-
"approval-required",
|
|
8143
|
-
"destructive"
|
|
8144
|
-
]);
|
|
8145
|
-
var ToolDescriptorSchema = external_exports.object({
|
|
8146
|
-
id: external_exports.string().min(1),
|
|
8147
|
-
description: external_exports.string().min(1),
|
|
8148
|
-
permission: ToolPermissionSchema.default("safe"),
|
|
8149
|
-
timeoutMs: external_exports.number().int().positive(),
|
|
8150
|
-
retryable: external_exports.boolean().default(false),
|
|
8151
|
-
producesArtifacts: external_exports.boolean().default(false),
|
|
8152
|
-
allowedRoles: external_exports.array(AgentRoleSchema).default([])
|
|
8153
|
-
});
|
|
8154
|
-
|
|
8155
|
-
// ../tools/dist/registry.js
|
|
8156
|
-
var BUILTIN_TOOLS = [
|
|
8202
|
+
// ../brains/dist/templates.js
|
|
8203
|
+
var BUILTIN_PROVIDER_TEMPLATES = [
|
|
8157
8204
|
{
|
|
8158
|
-
id: "
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
"session-orchestrator",
|
|
8166
|
-
"repo-analyst",
|
|
8167
|
-
"planner",
|
|
8168
|
-
"execution-manager",
|
|
8169
|
-
"frontend-specialist",
|
|
8170
|
-
"backend-specialist",
|
|
8171
|
-
"database-specialist",
|
|
8172
|
-
"infra-specialist",
|
|
8173
|
-
"test-debugger",
|
|
8174
|
-
"reviewer",
|
|
8175
|
-
"integrator"
|
|
8176
|
-
]
|
|
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."
|
|
8177
8212
|
},
|
|
8178
8213
|
{
|
|
8179
|
-
id: "
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
allowedRoles: [
|
|
8186
|
-
"frontend-specialist",
|
|
8187
|
-
"backend-specialist",
|
|
8188
|
-
"database-specialist",
|
|
8189
|
-
"infra-specialist",
|
|
8190
|
-
"test-debugger",
|
|
8191
|
-
"integrator"
|
|
8192
|
-
]
|
|
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."
|
|
8193
8220
|
},
|
|
8194
8221
|
{
|
|
8195
|
-
id: "
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
allowedRoles: [
|
|
8202
|
-
"repo-analyst",
|
|
8203
|
-
"execution-manager",
|
|
8204
|
-
"backend-specialist",
|
|
8205
|
-
"infra-specialist",
|
|
8206
|
-
"test-debugger",
|
|
8207
|
-
"reviewer",
|
|
8208
|
-
"integrator"
|
|
8209
|
-
]
|
|
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."
|
|
8210
8228
|
},
|
|
8211
8229
|
{
|
|
8212
|
-
id: "
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
allowedRoles: [
|
|
8219
|
-
"session-orchestrator",
|
|
8220
|
-
"repo-analyst",
|
|
8221
|
-
"execution-manager",
|
|
8222
|
-
"reviewer",
|
|
8223
|
-
"integrator"
|
|
8224
|
-
]
|
|
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."
|
|
8225
8236
|
},
|
|
8226
8237
|
{
|
|
8227
|
-
id: "
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
producesArtifacts: true,
|
|
8233
|
-
allowedRoles: [
|
|
8234
|
-
"execution-manager",
|
|
8235
|
-
"test-debugger",
|
|
8236
|
-
"reviewer",
|
|
8237
|
-
"integrator"
|
|
8238
|
-
]
|
|
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."
|
|
8239
8243
|
}
|
|
8240
8244
|
];
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
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;
|
|
8248
8257
|
}
|
|
8249
|
-
|
|
8250
|
-
|
|
8258
|
+
const normalized = trimTrailingSlash(baseUrl);
|
|
8259
|
+
if (templateId !== "lmstudio") {
|
|
8260
|
+
return normalized;
|
|
8251
8261
|
}
|
|
8252
|
-
|
|
8253
|
-
|
|
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;
|
|
8254
8270
|
}
|
|
8255
|
-
|
|
8256
|
-
|
|
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}".`);
|
|
8257
8277
|
}
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
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
|
+
});
|
|
8261
8295
|
}
|
|
8262
8296
|
|
|
8263
|
-
// ../
|
|
8297
|
+
// ../brains/dist/registry.js
|
|
8264
8298
|
var import_promises3 = require("node:fs/promises");
|
|
8265
8299
|
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
8266
|
-
var
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
function truncateOutput(value) {
|
|
8271
|
-
if (!value) {
|
|
8272
|
-
return value;
|
|
8273
|
-
}
|
|
8274
|
-
if (value.length <= DEFAULT_CAPTURE_LIMIT) {
|
|
8275
|
-
return value;
|
|
8300
|
+
var import_node_url = require("node:url");
|
|
8301
|
+
function resolveApiKey(definition) {
|
|
8302
|
+
if (!definition.apiKeyEnv) {
|
|
8303
|
+
return null;
|
|
8276
8304
|
}
|
|
8277
|
-
|
|
8278
|
-
return `${value.slice(0, DEFAULT_CAPTURE_LIMIT)}
|
|
8279
|
-
... [truncated ${omitted} chars]`;
|
|
8305
|
+
return process.env[definition.apiKeyEnv] ?? null;
|
|
8280
8306
|
}
|
|
8281
|
-
function
|
|
8282
|
-
const
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
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}`);
|
|
8286
8320
|
}
|
|
8287
|
-
return
|
|
8321
|
+
return response.json();
|
|
8288
8322
|
}
|
|
8289
|
-
|
|
8290
|
-
return
|
|
8291
|
-
const child = (0, import_node_child_process.spawn)(command, args, {
|
|
8292
|
-
cwd,
|
|
8293
|
-
env: import_node_process.default.env,
|
|
8294
|
-
stdio: [
|
|
8295
|
-
"ignore",
|
|
8296
|
-
"pipe",
|
|
8297
|
-
"pipe"
|
|
8298
|
-
]
|
|
8299
|
-
});
|
|
8300
|
-
const stdout = [];
|
|
8301
|
-
const stderr = [];
|
|
8302
|
-
let settled = false;
|
|
8303
|
-
let timedOut = false;
|
|
8304
|
-
const timer = setTimeout(() => {
|
|
8305
|
-
timedOut = true;
|
|
8306
|
-
child.kill("SIGTERM");
|
|
8307
|
-
setTimeout(() => child.kill("SIGKILL"), 1e3).unref();
|
|
8308
|
-
}, timeoutMs);
|
|
8309
|
-
child.stdout.on("data", (chunk) => {
|
|
8310
|
-
stdout.push(String(chunk));
|
|
8311
|
-
});
|
|
8312
|
-
child.stderr.on("data", (chunk) => {
|
|
8313
|
-
stderr.push(String(chunk));
|
|
8314
|
-
});
|
|
8315
|
-
child.on("error", (error) => {
|
|
8316
|
-
if (settled) {
|
|
8317
|
-
return;
|
|
8318
|
-
}
|
|
8319
|
-
settled = true;
|
|
8320
|
-
clearTimeout(timer);
|
|
8321
|
-
reject(error);
|
|
8322
|
-
});
|
|
8323
|
-
child.on("close", (code) => {
|
|
8324
|
-
if (settled) {
|
|
8325
|
-
return;
|
|
8326
|
-
}
|
|
8327
|
-
settled = true;
|
|
8328
|
-
clearTimeout(timer);
|
|
8329
|
-
resolve({
|
|
8330
|
-
code,
|
|
8331
|
-
stdout: stdout.join(""),
|
|
8332
|
-
stderr: stderr.join(""),
|
|
8333
|
-
timedOut
|
|
8334
|
-
});
|
|
8335
|
-
});
|
|
8336
|
-
});
|
|
8323
|
+
function trimTrailingSlash2(value) {
|
|
8324
|
+
return value.replace(/\/+$/, "");
|
|
8337
8325
|
}
|
|
8338
|
-
|
|
8339
|
-
const
|
|
8340
|
-
const
|
|
8341
|
-
|
|
8342
|
-
command
|
|
8343
|
-
], cwd, timeoutMs);
|
|
8344
|
-
const success = !result.timedOut && result.code === 0;
|
|
8345
|
-
const summary = result.timedOut ? `Command timed out after ${timeoutMs}ms.` : success ? `Command completed successfully.` : `Command exited with code ${result.code ?? "unknown"}.`;
|
|
8346
|
-
return ToolResultSchema.parse({
|
|
8347
|
-
toolId,
|
|
8348
|
-
success,
|
|
8349
|
-
summary,
|
|
8350
|
-
stdout: truncateOutput(result.stdout),
|
|
8351
|
-
stderr: truncateOutput(result.stderr)
|
|
8352
|
-
});
|
|
8326
|
+
function joinUrl(baseUrl, suffix) {
|
|
8327
|
+
const normalizedBase = trimTrailingSlash2(baseUrl);
|
|
8328
|
+
const normalizedSuffix = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
8329
|
+
return `${normalizedBase}${normalizedSuffix}`;
|
|
8353
8330
|
}
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
if (!rawPath) {
|
|
8357
|
-
throw new Error("file.read requires a string path.");
|
|
8358
|
-
}
|
|
8359
|
-
const targetPath = resolveWorkspacePath(context.cwd, rawPath);
|
|
8360
|
-
const contents = await (0, import_promises3.readFile)(targetPath, "utf8");
|
|
8361
|
-
return ToolResultSchema.parse({
|
|
8362
|
-
toolId: "file.read",
|
|
8363
|
-
success: true,
|
|
8364
|
-
summary: `Read ${import_node_path3.default.relative(context.cwd, targetPath) || import_node_path3.default.basename(targetPath)}.`,
|
|
8365
|
-
stdout: truncateOutput(contents),
|
|
8366
|
-
artifacts: [
|
|
8367
|
-
targetPath
|
|
8368
|
-
]
|
|
8369
|
-
});
|
|
8331
|
+
function isOpenRouter(definition) {
|
|
8332
|
+
return definition.baseUrl?.includes("openrouter.ai") ?? false;
|
|
8370
8333
|
}
|
|
8371
|
-
|
|
8372
|
-
const
|
|
8373
|
-
|
|
8374
|
-
|
|
8334
|
+
function buildProviderHeaders(definition, apiKey, includeJsonContentType = true) {
|
|
8335
|
+
const headers = {
|
|
8336
|
+
...definition.headers
|
|
8337
|
+
};
|
|
8338
|
+
if (includeJsonContentType) {
|
|
8339
|
+
headers["content-type"] = "application/json";
|
|
8375
8340
|
}
|
|
8376
|
-
|
|
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;
|
|
8377
8353
|
}
|
|
8378
|
-
|
|
8379
|
-
|
|
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;
|
|
8380
8358
|
}
|
|
8381
|
-
|
|
8382
|
-
const
|
|
8383
|
-
const
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
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
|
+
];
|
|
8387
8393
|
});
|
|
8394
|
+
return filterModels(models, input);
|
|
8388
8395
|
}
|
|
8389
|
-
|
|
8390
|
-
const
|
|
8391
|
-
|
|
8392
|
-
|
|
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
|
+
});
|
|
8393
8451
|
}
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
"--recount",
|
|
8401
|
-
"--whitespace=nowarn",
|
|
8402
|
-
patchPath
|
|
8403
|
-
], context.cwd, timeoutMs);
|
|
8404
|
-
const success = !result.timedOut && result.code === 0;
|
|
8405
|
-
return ToolResultSchema.parse({
|
|
8406
|
-
toolId: "file.patch",
|
|
8407
|
-
success,
|
|
8408
|
-
summary: success ? "Patch applied successfully." : result.timedOut ? `Patch timed out after ${timeoutMs}ms.` : `Patch failed with code ${result.code ?? "unknown"}.`,
|
|
8409
|
-
stdout: truncateOutput(result.stdout),
|
|
8410
|
-
stderr: truncateOutput(result.stderr),
|
|
8411
|
-
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
|
|
8412
8458
|
});
|
|
8413
|
-
} finally {
|
|
8414
|
-
await (0, import_promises3.rm)(tempDir, { recursive: true, force: true });
|
|
8415
8459
|
}
|
|
8460
|
+
return messages;
|
|
8416
8461
|
}
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
(input, context) => executeFileRead(input, context)
|
|
8426
|
-
],
|
|
8427
|
-
[
|
|
8428
|
-
"file.patch",
|
|
8429
|
-
executeFilePatch
|
|
8430
|
-
],
|
|
8431
|
-
[
|
|
8432
|
-
"shell.exec",
|
|
8433
|
-
executeShell
|
|
8434
|
-
],
|
|
8435
|
-
[
|
|
8436
|
-
"git.status",
|
|
8437
|
-
executeGitStatus
|
|
8438
|
-
],
|
|
8439
|
-
[
|
|
8440
|
-
"tests.run",
|
|
8441
|
-
executeTests
|
|
8442
|
-
]
|
|
8443
|
-
]);
|
|
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.`);
|
|
8444
8470
|
}
|
|
8445
|
-
|
|
8446
|
-
const descriptor = this.registry.get(toolId);
|
|
8447
|
-
if (!descriptor) {
|
|
8448
|
-
throw new Error(`Unknown tool "${toolId}".`);
|
|
8449
|
-
}
|
|
8450
|
-
const executor = this.executors.get(toolId);
|
|
8451
|
-
if (!executor) {
|
|
8452
|
-
throw new Error(`No executor registered for "${toolId}".`);
|
|
8453
|
-
}
|
|
8454
|
-
return executor(input, context, descriptor.timeoutMs);
|
|
8455
|
-
}
|
|
8456
|
-
};
|
|
8457
|
-
|
|
8458
|
-
// ../agent-runtime/dist/orchestrator.js
|
|
8459
|
-
function createSessionId() {
|
|
8460
|
-
return `session-${Date.now()}`;
|
|
8461
|
-
}
|
|
8462
|
-
function createEventId(type, taskId) {
|
|
8463
|
-
return `${type}-${taskId ?? "session"}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
8464
|
-
}
|
|
8465
|
-
function isTaskReady(task, completedTaskIds) {
|
|
8466
|
-
return task.status === "pending" && task.dependsOn.every((taskId) => completedTaskIds.has(taskId));
|
|
8467
|
-
}
|
|
8468
|
-
function completedTaskIdsFromPlan(plan) {
|
|
8469
|
-
return new Set(flattenPlanTasks(plan).filter((task) => task.status === "completed").map((task) => task.id));
|
|
8471
|
+
return model;
|
|
8470
8472
|
}
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
return "completed";
|
|
8476
|
-
}
|
|
8477
|
-
if (readyTasks.length > 0) {
|
|
8478
|
-
return completed > 0 ? "running" : "ready";
|
|
8479
|
-
}
|
|
8480
|
-
if (blockedTasks.length > 0) {
|
|
8481
|
-
return "blocked";
|
|
8473
|
+
var OpenAIResponsesProvider = class {
|
|
8474
|
+
definition;
|
|
8475
|
+
constructor(definition) {
|
|
8476
|
+
this.definition = ProviderDefinitionSchema.parse(definition);
|
|
8482
8477
|
}
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
status
|
|
8491
|
-
} : task)
|
|
8492
|
-
}));
|
|
8493
|
-
return {
|
|
8494
|
-
...plan,
|
|
8495
|
-
milestones
|
|
8496
|
-
};
|
|
8497
|
-
}
|
|
8498
|
-
function maybeAppendNote(notes, note) {
|
|
8499
|
-
return notes.at(-1) === note ? notes : [
|
|
8500
|
-
...notes,
|
|
8501
|
-
note
|
|
8502
|
-
];
|
|
8503
|
-
}
|
|
8504
|
-
function renderToolResultSection(results) {
|
|
8505
|
-
return results.map((result) => {
|
|
8506
|
-
const lines = [
|
|
8507
|
-
`## ${result.toolId}`,
|
|
8508
|
-
`- success: ${result.success ? "yes" : "no"}`,
|
|
8509
|
-
`- summary: ${result.summary}`
|
|
8510
|
-
];
|
|
8511
|
-
if (result.stdout) {
|
|
8512
|
-
lines.push("");
|
|
8513
|
-
lines.push("```text");
|
|
8514
|
-
lines.push(result.stdout);
|
|
8515
|
-
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
|
+
};
|
|
8516
8485
|
}
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
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}".`);
|
|
8522
8495
|
}
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
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);
|
|
8530
8502
|
}
|
|
8531
|
-
|
|
8532
|
-
const
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
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
|
+
};
|
|
8536
8542
|
}
|
|
8537
|
-
}
|
|
8538
|
-
var
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
this.toolRegistry = toolRegistry;
|
|
8543
|
-
this.toolRuntime = toolRuntime;
|
|
8543
|
+
};
|
|
8544
|
+
var AnthropicMessagesProvider = class {
|
|
8545
|
+
definition;
|
|
8546
|
+
constructor(definition) {
|
|
8547
|
+
this.definition = ProviderDefinitionSchema.parse(definition);
|
|
8544
8548
|
}
|
|
8545
|
-
|
|
8546
|
-
const
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
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
|
+
}
|
|
8551
8557
|
return {
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
plan,
|
|
8555
|
-
readyTasks,
|
|
8556
|
-
blockedTasks,
|
|
8557
|
-
assignedAgents
|
|
8558
|
+
ok: true,
|
|
8559
|
+
message: `${this.definition.baseUrl ?? "https://api.anthropic.com/v1/messages"} (${this.definition.defaultModel ?? "model not set"})`
|
|
8558
8560
|
};
|
|
8559
8561
|
}
|
|
8560
|
-
|
|
8561
|
-
const
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
completedTaskIds: Array.from(completedTaskIdsFromPlan(envelope.plan)),
|
|
8575
|
-
assignedAgents: envelope.assignedAgents.map((agent) => agent.role),
|
|
8576
|
-
notes: [
|
|
8577
|
-
"Initial session snapshot created from the current plan."
|
|
8578
|
-
],
|
|
8579
|
-
events: [],
|
|
8580
|
-
...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
|
+
}
|
|
8581
8576
|
});
|
|
8577
|
+
return mapAnthropicModels(this.definition.id, payload, input);
|
|
8582
8578
|
}
|
|
8583
|
-
async
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
const
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
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")
|
|
8631
9304
|
});
|
|
8632
|
-
executedTasks += 1;
|
|
8633
9305
|
}
|
|
8634
|
-
const
|
|
8635
|
-
|
|
8636
|
-
|
|
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);
|
|
8637
9511
|
}
|
|
8638
|
-
return this.createSessionSnapshot(postLimitEnvelope, {
|
|
8639
|
-
startedAt: session.startedAt,
|
|
8640
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8641
|
-
notes,
|
|
8642
|
-
events
|
|
8643
|
-
});
|
|
8644
9512
|
}
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
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));
|
|
8648
9515
|
}
|
|
8649
|
-
|
|
8650
|
-
return this.
|
|
9516
|
+
get(id) {
|
|
9517
|
+
return this.tools.get(id);
|
|
8651
9518
|
}
|
|
8652
|
-
|
|
8653
|
-
return
|
|
9519
|
+
byRole(role) {
|
|
9520
|
+
return this.list().filter((tool) => tool.allowedRoles.includes(role));
|
|
8654
9521
|
}
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
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;
|
|
8661
9558
|
}
|
|
8662
|
-
return {
|
|
8663
|
-
status: "blocked",
|
|
8664
|
-
summary: `Task ${task.id} is ready, but no executor is available for ${task.agentRole}.`,
|
|
8665
|
-
toolResults: [],
|
|
8666
|
-
artifacts: []
|
|
8667
|
-
};
|
|
8668
9559
|
}
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
this.safeRunTool("file.read", { path: "README.md" }, context)
|
|
8675
|
-
]);
|
|
8676
|
-
const successfulResults = toolResults.filter((result) => result.success);
|
|
8677
|
-
const scripts = extractPackageScripts(successfulResults);
|
|
8678
|
-
const artifactPath = await this.writeLogArtifact(sessionId, "repo-analysis", request.cwd, [
|
|
8679
|
-
`# Repo Analysis`,
|
|
8680
|
-
``,
|
|
8681
|
-
`Goal: ${request.goal}`,
|
|
8682
|
-
`Workspace: ${request.cwd}`,
|
|
8683
|
-
`Task: ${task.id} - ${task.title}`,
|
|
8684
|
-
``,
|
|
8685
|
-
`## Observations`,
|
|
8686
|
-
`- Successful tool probes: ${successfulResults.length}/${toolResults.length}`,
|
|
8687
|
-
`- Workspace state: ${request.workspaceState}`,
|
|
8688
|
-
`- Detected scripts: ${scripts.length > 0 ? scripts.join(", ") : "none"}`,
|
|
8689
|
-
``,
|
|
8690
|
-
renderToolResultSection(toolResults)
|
|
8691
|
-
].join("\n"));
|
|
8692
|
-
return {
|
|
8693
|
-
status: "completed",
|
|
8694
|
-
summary: `Completed ${task.id}: captured repo analysis and saved ${import_node_path4.default.basename(artifactPath)}.`,
|
|
8695
|
-
toolResults,
|
|
8696
|
-
artifacts: [
|
|
8697
|
-
artifactPath
|
|
8698
|
-
]
|
|
8699
|
-
};
|
|
9560
|
+
return fallback;
|
|
9561
|
+
}
|
|
9562
|
+
function truncateOutput(value) {
|
|
9563
|
+
if (!value) {
|
|
9564
|
+
return value;
|
|
8700
9565
|
}
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
`# Architecture Brief`,
|
|
8704
|
-
``,
|
|
8705
|
-
`Goal: ${request.goal}`,
|
|
8706
|
-
`Summary: ${plan.summary}`,
|
|
8707
|
-
`Repo Strategy: ${plan.repoStrategy.mode} - ${plan.repoStrategy.reasoning}`,
|
|
8708
|
-
``,
|
|
8709
|
-
`## Assumptions`,
|
|
8710
|
-
...plan.assumptions.map((assumption) => `- ${assumption}`),
|
|
8711
|
-
``,
|
|
8712
|
-
`## Milestones`,
|
|
8713
|
-
...plan.milestones.flatMap((milestone) => [
|
|
8714
|
-
`### ${milestone.title}`,
|
|
8715
|
-
milestone.objective,
|
|
8716
|
-
...milestone.tasks.map((milestoneTask) => `- ${milestoneTask.id}: ${milestoneTask.title} [${milestoneTask.agentRole}]`),
|
|
8717
|
-
``
|
|
8718
|
-
]),
|
|
8719
|
-
`## Verification`,
|
|
8720
|
-
...plan.verificationChecklist.map((item) => `- ${item}`)
|
|
8721
|
-
].join("\n"));
|
|
8722
|
-
return {
|
|
8723
|
-
status: "completed",
|
|
8724
|
-
summary: `Completed ${task.id}: wrote architecture brief ${import_node_path4.default.basename(artifactPath)}.`,
|
|
8725
|
-
toolResults: [],
|
|
8726
|
-
artifacts: [
|
|
8727
|
-
artifactPath
|
|
8728
|
-
]
|
|
8729
|
-
};
|
|
9566
|
+
if (value.length <= DEFAULT_CAPTURE_LIMIT) {
|
|
9567
|
+
return value;
|
|
8730
9568
|
}
|
|
8731
|
-
|
|
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;
|
|
9583
|
+
}
|
|
9584
|
+
const raw = line.slice(4).trim();
|
|
9585
|
+
if (!raw || raw === "/dev/null") {
|
|
9586
|
+
continue;
|
|
9587
|
+
}
|
|
9588
|
+
const normalized = raw.startsWith("a/") || raw.startsWith("b/") ? raw.slice(2) : raw;
|
|
8732
9589
|
try {
|
|
8733
|
-
|
|
8734
|
-
} catch
|
|
8735
|
-
|
|
8736
|
-
return {
|
|
8737
|
-
toolId,
|
|
8738
|
-
success: false,
|
|
8739
|
-
summary: `${toolId} failed: ${message}`,
|
|
8740
|
-
stderr: message,
|
|
8741
|
-
artifacts: []
|
|
8742
|
-
};
|
|
9590
|
+
paths.add(resolveWorkspacePath(cwd, normalized));
|
|
9591
|
+
} catch {
|
|
9592
|
+
continue;
|
|
8743
9593
|
}
|
|
8744
9594
|
}
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
9595
|
+
return Array.from(paths);
|
|
9596
|
+
}
|
|
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.`);
|
|
8752
9602
|
}
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
}
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
9603
|
+
return resolved;
|
|
9604
|
+
}
|
|
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
|
+
});
|
|
9653
|
+
}
|
|
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
|
+
});
|
|
9669
|
+
}
|
|
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.");
|
|
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;
|
|
9695
|
+
try {
|
|
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;
|
|
8773
9708
|
}
|
|
9709
|
+
result = await runSpawn("find", [
|
|
9710
|
+
rootArg,
|
|
9711
|
+
"-type",
|
|
9712
|
+
"f"
|
|
9713
|
+
], context.cwd, timeoutMs);
|
|
9714
|
+
}
|
|
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)
|
|
8774
9725
|
});
|
|
8775
9726
|
}
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
notes: "Runs against a local Ollama server."
|
|
8814
|
-
},
|
|
8815
|
-
{
|
|
8816
|
-
id: "lmstudio",
|
|
8817
|
-
label: "LM Studio",
|
|
8818
|
-
driver: "openai-compatible",
|
|
8819
|
-
defaultBaseUrl: "http://localhost:1234/v1",
|
|
8820
|
-
notes: "Works with LM Studio's local OpenAI-compatible server."
|
|
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);
|
|
8821
9764
|
}
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
|
|
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)
|
|
9774
|
+
});
|
|
8828
9775
|
}
|
|
8829
|
-
function
|
|
8830
|
-
const
|
|
8831
|
-
|
|
8832
|
-
|
|
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.");
|
|
8833
9781
|
}
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
9782
|
+
if (content === null) {
|
|
9783
|
+
throw new Error("file.write requires a string content field.");
|
|
9784
|
+
}
|
|
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;
|
|
9791
|
+
}
|
|
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
|
+
]
|
|
8850
9801
|
});
|
|
8851
9802
|
}
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
var import_node_url = require("node:url");
|
|
8857
|
-
function resolveApiKey(definition) {
|
|
8858
|
-
if (!definition.apiKeyEnv) {
|
|
8859
|
-
return null;
|
|
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.");
|
|
8860
9807
|
}
|
|
8861
|
-
return
|
|
9808
|
+
return runShellCommand("shell.exec", command, context.cwd, timeoutMs);
|
|
8862
9809
|
}
|
|
8863
|
-
function
|
|
8864
|
-
|
|
8865
|
-
setTimeout(() => controller.abort(), timeoutMs).unref?.();
|
|
8866
|
-
return controller.signal;
|
|
9810
|
+
async function executeGitStatus(_input, context, timeoutMs) {
|
|
9811
|
+
return runShellCommand("git.status", "git status --short --branch", context.cwd, timeoutMs);
|
|
8867
9812
|
}
|
|
8868
|
-
async function
|
|
8869
|
-
const
|
|
8870
|
-
|
|
8871
|
-
|
|
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)
|
|
8872
9849
|
});
|
|
8873
|
-
|
|
8874
|
-
|
|
9850
|
+
}
|
|
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.");
|
|
9863
|
+
}
|
|
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)
|
|
9882
|
+
});
|
|
9883
|
+
} finally {
|
|
9884
|
+
await (0, import_promises5.rm)(tempDir, { recursive: true, force: true });
|
|
8875
9885
|
}
|
|
8876
|
-
return response.json();
|
|
8877
9886
|
}
|
|
8878
|
-
|
|
8879
|
-
|
|
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
|
+
]);
|
|
9930
|
+
}
|
|
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()}`;
|
|
8880
9947
|
}
|
|
8881
|
-
function
|
|
8882
|
-
|
|
8883
|
-
const normalizedSuffix = suffix.startsWith("/") ? suffix : `/${suffix}`;
|
|
8884
|
-
return `${normalizedBase}${normalizedSuffix}`;
|
|
9948
|
+
function createEventId(type, taskId) {
|
|
9949
|
+
return `${type}-${taskId ?? "session"}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
8885
9950
|
}
|
|
8886
|
-
function
|
|
8887
|
-
return
|
|
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";
|
|
9962
|
+
}
|
|
9963
|
+
if (readyTasks.length > 0) {
|
|
9964
|
+
return completed > 0 ? "running" : "ready";
|
|
9965
|
+
}
|
|
9966
|
+
if (blockedTasks.length > 0) {
|
|
9967
|
+
return "blocked";
|
|
9968
|
+
}
|
|
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
|
+
};
|
|
8888
9983
|
}
|
|
8889
|
-
function
|
|
8890
|
-
const
|
|
8891
|
-
...
|
|
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
|
|
8892
9992
|
};
|
|
8893
|
-
if (includeJsonContentType) {
|
|
8894
|
-
headers["content-type"] = "application/json";
|
|
8895
|
-
}
|
|
8896
|
-
if (apiKey) {
|
|
8897
|
-
headers.authorization = `Bearer ${apiKey}`;
|
|
8898
|
-
}
|
|
8899
|
-
if (isOpenRouter(definition)) {
|
|
8900
|
-
const siteUrl = process.env.OPENROUTER_SITE_URL;
|
|
8901
|
-
const appName = process.env.OPENROUTER_APP_NAME ?? "Kimbho CLI";
|
|
8902
|
-
if (siteUrl) {
|
|
8903
|
-
headers["HTTP-Referer"] = siteUrl;
|
|
8904
|
-
}
|
|
8905
|
-
headers["X-Title"] = appName;
|
|
8906
|
-
}
|
|
8907
|
-
return headers;
|
|
8908
9993
|
}
|
|
8909
|
-
function
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
9994
|
+
function maybeAppendNote(notes, note) {
|
|
9995
|
+
return notes.at(-1) === note ? notes : [
|
|
9996
|
+
...notes,
|
|
9997
|
+
note
|
|
9998
|
+
];
|
|
8913
9999
|
}
|
|
8914
|
-
function
|
|
8915
|
-
|
|
8916
|
-
const data = Array.isArray(response.data) ? response.data : [];
|
|
8917
|
-
const models = data.flatMap((item) => {
|
|
8918
|
-
if (typeof item !== "object" || item === null) {
|
|
8919
|
-
return [];
|
|
8920
|
-
}
|
|
8921
|
-
const record = item;
|
|
8922
|
-
const pricing = typeof record.pricing === "object" && record.pricing !== null ? record.pricing : null;
|
|
8923
|
-
const architecture = typeof record.architecture === "object" && record.architecture !== null ? record.architecture : null;
|
|
8924
|
-
return [
|
|
8925
|
-
ProviderModelSchema.parse({
|
|
8926
|
-
id: String(record.id ?? ""),
|
|
8927
|
-
...typeof record.name === "string" ? {
|
|
8928
|
-
name: record.name
|
|
8929
|
-
} : {},
|
|
8930
|
-
...typeof record.description === "string" ? {
|
|
8931
|
-
description: record.description
|
|
8932
|
-
} : {},
|
|
8933
|
-
...typeof record.context_length === "number" ? {
|
|
8934
|
-
contextLength: record.context_length
|
|
8935
|
-
} : {},
|
|
8936
|
-
...pricing && typeof pricing.prompt === "string" ? {
|
|
8937
|
-
promptPrice: pricing.prompt
|
|
8938
|
-
} : {},
|
|
8939
|
-
...pricing && typeof pricing.completion === "string" ? {
|
|
8940
|
-
completionPrice: pricing.completion
|
|
8941
|
-
} : {},
|
|
8942
|
-
...architecture && typeof architecture.modality === "string" ? {
|
|
8943
|
-
modality: architecture.modality
|
|
8944
|
-
} : {},
|
|
8945
|
-
providerId
|
|
8946
|
-
})
|
|
8947
|
-
];
|
|
8948
|
-
});
|
|
8949
|
-
return filterModels(models, input);
|
|
10000
|
+
function mergeUnique(items) {
|
|
10001
|
+
return Array.from(new Set(items.filter((item) => item.trim().length > 0)));
|
|
8950
10002
|
}
|
|
8951
|
-
function
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
}
|
|
8958
|
-
const record = item;
|
|
8959
|
-
return [
|
|
8960
|
-
ProviderModelSchema.parse({
|
|
8961
|
-
id: String(record.id ?? ""),
|
|
8962
|
-
...typeof record.display_name === "string" ? {
|
|
8963
|
-
name: record.display_name
|
|
8964
|
-
} : {},
|
|
8965
|
-
providerId
|
|
8966
|
-
})
|
|
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}`
|
|
8967
10009
|
];
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
const data = Array.isArray(response.models) ? response.models : [];
|
|
8974
|
-
const models = data.flatMap((item) => {
|
|
8975
|
-
if (typeof item !== "object" || item === null) {
|
|
8976
|
-
return [];
|
|
10010
|
+
if (result.stdout) {
|
|
10011
|
+
lines.push("");
|
|
10012
|
+
lines.push("```text");
|
|
10013
|
+
lines.push(result.stdout);
|
|
10014
|
+
lines.push("```");
|
|
8977
10015
|
}
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
} : {},
|
|
8987
|
-
...details && typeof details.family === "string" ? {
|
|
8988
|
-
modality: details.family
|
|
8989
|
-
} : {},
|
|
8990
|
-
...details && typeof details.parameter_size === "string" ? {
|
|
8991
|
-
description: details.parameter_size
|
|
8992
|
-
} : {},
|
|
8993
|
-
providerId
|
|
8994
|
-
})
|
|
8995
|
-
];
|
|
8996
|
-
});
|
|
8997
|
-
return filterModels(models, input);
|
|
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");
|
|
8998
10024
|
}
|
|
8999
|
-
function
|
|
9000
|
-
const
|
|
9001
|
-
if (
|
|
9002
|
-
|
|
9003
|
-
role: "system",
|
|
9004
|
-
content: input.systemPrompt
|
|
9005
|
-
});
|
|
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 [];
|
|
9006
10029
|
}
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
content: input.userPrompt
|
|
9013
|
-
});
|
|
10030
|
+
try {
|
|
10031
|
+
const parsed = JSON.parse(packageResult.stdout);
|
|
10032
|
+
return Object.keys(parsed.scripts ?? {}).sort();
|
|
10033
|
+
} catch {
|
|
10034
|
+
return [];
|
|
9014
10035
|
}
|
|
9015
|
-
return messages;
|
|
9016
10036
|
}
|
|
9017
|
-
function
|
|
9018
|
-
const
|
|
9019
|
-
|
|
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
|
+
};
|
|
9020
10076
|
}
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
10077
|
+
var ExecutionOrchestrator = class {
|
|
10078
|
+
toolRegistry;
|
|
10079
|
+
toolRuntime;
|
|
10080
|
+
constructor(toolRegistry = createDefaultToolRegistry(), toolRuntime = new ToolRuntime(toolRegistry)) {
|
|
10081
|
+
this.toolRegistry = toolRegistry;
|
|
10082
|
+
this.toolRuntime = toolRuntime;
|
|
9025
10083
|
}
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
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);
|
|
10090
|
+
return {
|
|
10091
|
+
sessionId,
|
|
10092
|
+
request,
|
|
10093
|
+
plan,
|
|
10094
|
+
readyTasks,
|
|
10095
|
+
blockedTasks,
|
|
10096
|
+
assignedAgents
|
|
10097
|
+
};
|
|
9032
10098
|
}
|
|
9033
|
-
|
|
9034
|
-
const
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
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
|
|
10120
|
+
});
|
|
10121
|
+
}
|
|
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;
|
|
9040
10172
|
}
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
};
|
|
9045
|
-
}
|
|
9046
|
-
async listModels(input) {
|
|
9047
|
-
const apiKey = resolveApiKey(this.definition);
|
|
9048
|
-
if (!apiKey) {
|
|
9049
|
-
throw new Error(`Missing ${this.definition.apiKeyEnv ?? "API key env var"} for provider "${this.definition.id}".`);
|
|
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"}.`);
|
|
9050
10176
|
}
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
10177
|
+
return this.createSessionSnapshot(postLimitEnvelope, {
|
|
10178
|
+
startedAt: session.startedAt,
|
|
10179
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10180
|
+
notes,
|
|
10181
|
+
events
|
|
9055
10182
|
});
|
|
9056
|
-
return mapOpenAIStyleModels(this.definition.id, payload, input);
|
|
9057
10183
|
}
|
|
9058
|
-
|
|
9059
|
-
const
|
|
9060
|
-
|
|
9061
|
-
throw new Error(`Missing ${this.definition.apiKeyEnv ?? "API key env var"} for provider "${this.definition.id}".`);
|
|
9062
|
-
}
|
|
9063
|
-
const baseUrl = this.definition.baseUrl ?? "https://api.openai.com/v1";
|
|
9064
|
-
const model = requireModel(this.definition, input);
|
|
9065
|
-
const messages = toChatMessages(input).map((message) => ({
|
|
9066
|
-
role: message.role,
|
|
9067
|
-
content: [
|
|
9068
|
-
{
|
|
9069
|
-
type: "input_text",
|
|
9070
|
-
text: message.content
|
|
9071
|
-
}
|
|
9072
|
-
]
|
|
9073
|
-
}));
|
|
9074
|
-
const response = await requestJson(`${baseUrl}/responses`, {
|
|
9075
|
-
method: "POST",
|
|
9076
|
-
headers: {
|
|
9077
|
-
...buildProviderHeaders(this.definition, apiKey)
|
|
9078
|
-
},
|
|
9079
|
-
body: JSON.stringify({
|
|
9080
|
-
model,
|
|
9081
|
-
input: messages,
|
|
9082
|
-
...typeof input.temperature === "number" ? {
|
|
9083
|
-
temperature: input.temperature
|
|
9084
|
-
} : {},
|
|
9085
|
-
...typeof input.maxTokens === "number" ? {
|
|
9086
|
-
max_output_tokens: input.maxTokens
|
|
9087
|
-
} : {}
|
|
9088
|
-
})
|
|
9089
|
-
});
|
|
9090
|
-
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 ? [
|
|
9091
|
-
String(item.text)
|
|
9092
|
-
] : []).join("\n") : "";
|
|
9093
|
-
return {
|
|
9094
|
-
text,
|
|
9095
|
-
model
|
|
9096
|
-
};
|
|
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));
|
|
9097
10187
|
}
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
definition;
|
|
9101
|
-
constructor(definition) {
|
|
9102
|
-
this.definition = ProviderDefinitionSchema.parse(definition);
|
|
10188
|
+
toolsForAgent(role) {
|
|
10189
|
+
return this.toolRegistry.byRole(role);
|
|
9103
10190
|
}
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
if (!apiKey) {
|
|
9107
|
-
return {
|
|
9108
|
-
ok: false,
|
|
9109
|
-
message: `Missing ${this.definition.apiKeyEnv ?? "ANTHROPIC_API_KEY"}`
|
|
9110
|
-
};
|
|
9111
|
-
}
|
|
9112
|
-
return {
|
|
9113
|
-
ok: true,
|
|
9114
|
-
message: `${this.definition.baseUrl ?? "https://api.anthropic.com/v1/messages"} (${this.definition.defaultModel ?? "model not set"})`
|
|
9115
|
-
};
|
|
10191
|
+
canAutoExecuteTask(task) {
|
|
10192
|
+
return task.agentRole !== "session-orchestrator" && task.agentRole !== "execution-manager";
|
|
9116
10193
|
}
|
|
9117
|
-
async
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
throw new Error(`Missing ${this.definition.apiKeyEnv ?? "ANTHROPIC_API_KEY"} for provider "${this.definition.id}".`);
|
|
10194
|
+
async executeTask(sessionId, task, request, plan, options = {}) {
|
|
10195
|
+
if (task.agentRole === "repo-analyst") {
|
|
10196
|
+
return this.executeRepoAnalysisTask(sessionId, task, request);
|
|
9121
10197
|
}
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
const payload = await requestJson(modelsEndpoint, {
|
|
9125
|
-
method: "GET",
|
|
9126
|
-
headers: {
|
|
9127
|
-
"anthropic-version": "2023-06-01",
|
|
9128
|
-
"x-api-key": apiKey,
|
|
9129
|
-
...this.definition.headers
|
|
9130
|
-
}
|
|
9131
|
-
});
|
|
9132
|
-
return mapAnthropicModels(this.definition.id, payload, input);
|
|
9133
|
-
}
|
|
9134
|
-
async generateText(input) {
|
|
9135
|
-
const apiKey = resolveApiKey(this.definition);
|
|
9136
|
-
if (!apiKey) {
|
|
9137
|
-
throw new Error(`Missing ${this.definition.apiKeyEnv ?? "ANTHROPIC_API_KEY"} for provider "${this.definition.id}".`);
|
|
10198
|
+
if (task.agentRole === "planner") {
|
|
10199
|
+
return this.executePlannerTask(sessionId, task, request, plan);
|
|
9138
10200
|
}
|
|
9139
|
-
|
|
9140
|
-
const model = requireModel(this.definition, input);
|
|
9141
|
-
const messages = toChatMessages(input);
|
|
9142
|
-
const systemMessage = messages.find((message) => message.role === "system")?.content;
|
|
9143
|
-
const conversation = messages.filter((message) => message.role !== "system").map((message) => ({
|
|
9144
|
-
role: message.role === "assistant" ? "assistant" : "user",
|
|
9145
|
-
content: message.content
|
|
9146
|
-
}));
|
|
9147
|
-
const response = await requestJson(endpoint, {
|
|
9148
|
-
method: "POST",
|
|
9149
|
-
headers: {
|
|
9150
|
-
"content-type": "application/json",
|
|
9151
|
-
"x-api-key": apiKey,
|
|
9152
|
-
"anthropic-version": "2023-06-01",
|
|
9153
|
-
...this.definition.headers
|
|
9154
|
-
},
|
|
9155
|
-
body: JSON.stringify({
|
|
9156
|
-
model,
|
|
9157
|
-
messages: conversation,
|
|
9158
|
-
...systemMessage ? {
|
|
9159
|
-
system: systemMessage
|
|
9160
|
-
} : {},
|
|
9161
|
-
max_tokens: input.maxTokens ?? 2048,
|
|
9162
|
-
...typeof input.temperature === "number" ? {
|
|
9163
|
-
temperature: input.temperature
|
|
9164
|
-
} : {}
|
|
9165
|
-
})
|
|
9166
|
-
});
|
|
9167
|
-
const content = Array.isArray(response.content) ? response.content : [];
|
|
9168
|
-
const text = content.flatMap((item) => typeof item === "object" && item !== null && "text" in item ? [
|
|
9169
|
-
String(item.text)
|
|
9170
|
-
] : []).join("\n");
|
|
9171
|
-
return {
|
|
9172
|
-
text,
|
|
9173
|
-
model
|
|
9174
|
-
};
|
|
9175
|
-
}
|
|
9176
|
-
};
|
|
9177
|
-
var OpenAICompatibleProvider = class {
|
|
9178
|
-
definition;
|
|
9179
|
-
constructor(definition) {
|
|
9180
|
-
this.definition = ProviderDefinitionSchema.parse(definition);
|
|
10201
|
+
return this.executeAutonomousSpecialistTask(sessionId, task, request, options);
|
|
9181
10202
|
}
|
|
9182
|
-
async
|
|
9183
|
-
|
|
10203
|
+
async executeAutonomousSpecialistTask(sessionId, task, request, options) {
|
|
10204
|
+
const config = await loadConfig(request.cwd);
|
|
10205
|
+
if (!config) {
|
|
9184
10206
|
return {
|
|
9185
|
-
|
|
9186
|
-
|
|
10207
|
+
status: "blocked",
|
|
10208
|
+
summary: `Task ${task.id} cannot run because no .kimbho/config.json was found in ${request.cwd}.`,
|
|
10209
|
+
toolResults: [],
|
|
10210
|
+
artifacts: []
|
|
9187
10211
|
};
|
|
9188
10212
|
}
|
|
9189
|
-
const
|
|
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
|
+
} : {}
|
|
10221
|
+
});
|
|
10222
|
+
}
|
|
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"));
|
|
9190
10246
|
return {
|
|
9191
|
-
|
|
9192
|
-
|
|
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
|
+
]
|
|
9193
10253
|
};
|
|
9194
10254
|
}
|
|
9195
|
-
async
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
model,
|
|
9217
|
-
messages: toChatMessages(input),
|
|
9218
|
-
...typeof input.temperature === "number" ? {
|
|
9219
|
-
temperature: input.temperature
|
|
9220
|
-
} : {},
|
|
9221
|
-
...typeof input.maxTokens === "number" ? {
|
|
9222
|
-
max_tokens: input.maxTokens
|
|
9223
|
-
} : {}
|
|
9224
|
-
})
|
|
9225
|
-
});
|
|
9226
|
-
const choices = Array.isArray(response.choices) ? response.choices : [];
|
|
9227
|
-
const firstChoice = choices.length > 0 && typeof choices[0] === "object" && choices[0] !== null ? choices[0] : null;
|
|
9228
|
-
const message = firstChoice && typeof firstChoice.message === "object" && firstChoice.message !== null ? firstChoice.message : null;
|
|
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"));
|
|
9229
10276
|
return {
|
|
9230
|
-
|
|
9231
|
-
|
|
10277
|
+
status: "completed",
|
|
10278
|
+
summary: `Completed ${task.id}: wrote architecture brief ${import_node_path6.default.basename(artifactPath)}.`,
|
|
10279
|
+
toolResults: [],
|
|
10280
|
+
artifacts: [
|
|
10281
|
+
artifactPath
|
|
10282
|
+
]
|
|
9232
10283
|
};
|
|
9233
10284
|
}
|
|
9234
|
-
|
|
9235
|
-
var OllamaProvider = class {
|
|
9236
|
-
definition;
|
|
9237
|
-
constructor(definition) {
|
|
9238
|
-
this.definition = ProviderDefinitionSchema.parse(definition);
|
|
9239
|
-
}
|
|
9240
|
-
async healthCheck() {
|
|
9241
|
-
const baseUrl = this.definition.baseUrl ?? "http://localhost:11434";
|
|
10285
|
+
async safeRunTool(toolId, input, context) {
|
|
9242
10286
|
try {
|
|
9243
|
-
await
|
|
9244
|
-
return {
|
|
9245
|
-
ok: true,
|
|
9246
|
-
message: `${baseUrl} (${this.definition.defaultModel ?? "model not set"})`
|
|
9247
|
-
};
|
|
10287
|
+
return await this.toolRuntime.run(toolId, input, context);
|
|
9248
10288
|
} catch (error) {
|
|
10289
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9249
10290
|
return {
|
|
9250
|
-
|
|
9251
|
-
|
|
10291
|
+
toolId,
|
|
10292
|
+
success: false,
|
|
10293
|
+
summary: `${toolId} failed: ${message}`,
|
|
10294
|
+
stderr: message,
|
|
10295
|
+
artifacts: []
|
|
9252
10296
|
};
|
|
9253
10297
|
}
|
|
9254
10298
|
}
|
|
9255
|
-
async
|
|
9256
|
-
|
|
9257
|
-
const
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
const model = requireModel(this.definition, input);
|
|
9263
|
-
const response = await requestJson(`${baseUrl}/api/generate`, {
|
|
9264
|
-
method: "POST",
|
|
9265
|
-
headers: {
|
|
9266
|
-
"content-type": "application/json",
|
|
9267
|
-
...this.definition.headers
|
|
9268
|
-
},
|
|
9269
|
-
body: JSON.stringify({
|
|
9270
|
-
model,
|
|
9271
|
-
prompt: input.userPrompt ?? toPromptText(input),
|
|
9272
|
-
...input.systemPrompt ? {
|
|
9273
|
-
system: input.systemPrompt
|
|
9274
|
-
} : {},
|
|
9275
|
-
stream: false,
|
|
9276
|
-
...typeof input.temperature === "number" ? {
|
|
9277
|
-
options: {
|
|
9278
|
-
temperature: input.temperature
|
|
9279
|
-
}
|
|
9280
|
-
} : {}
|
|
9281
|
-
})
|
|
9282
|
-
});
|
|
9283
|
-
return {
|
|
9284
|
-
text: typeof response.response === "string" ? response.response : "",
|
|
9285
|
-
model
|
|
9286
|
-
};
|
|
9287
|
-
}
|
|
9288
|
-
};
|
|
9289
|
-
async function createCustomModuleProvider(definition, cwd) {
|
|
9290
|
-
if (!definition.modulePath) {
|
|
9291
|
-
throw new Error(`Provider "${definition.id}" requires modulePath.`);
|
|
9292
|
-
}
|
|
9293
|
-
const modulePath = import_node_path5.default.isAbsolute(definition.modulePath) ? definition.modulePath : import_node_path5.default.join(cwd, definition.modulePath);
|
|
9294
|
-
await (0, import_promises5.access)(modulePath);
|
|
9295
|
-
const module2 = await import((0, import_node_url.pathToFileURL)(modulePath).href);
|
|
9296
|
-
const createProvider = typeof module2.createProvider === "function" ? module2.createProvider : typeof module2.default === "function" ? module2.default : null;
|
|
9297
|
-
if (!createProvider) {
|
|
9298
|
-
throw new Error(`Custom provider module "${modulePath}" must export createProvider(definition).`);
|
|
9299
|
-
}
|
|
9300
|
-
return createProvider(definition);
|
|
9301
|
-
}
|
|
9302
|
-
var BUILTIN_FACTORIES = {
|
|
9303
|
-
"openai-responses": {
|
|
9304
|
-
driver: "openai-responses",
|
|
9305
|
-
create: async (definition) => new OpenAIResponsesProvider(definition)
|
|
9306
|
-
},
|
|
9307
|
-
"anthropic-messages": {
|
|
9308
|
-
driver: "anthropic-messages",
|
|
9309
|
-
create: async (definition) => new AnthropicMessagesProvider(definition)
|
|
9310
|
-
},
|
|
9311
|
-
"openai-compatible": {
|
|
9312
|
-
driver: "openai-compatible",
|
|
9313
|
-
create: async (definition) => new OpenAICompatibleProvider(definition)
|
|
9314
|
-
},
|
|
9315
|
-
ollama: {
|
|
9316
|
-
driver: "ollama",
|
|
9317
|
-
create: async (definition) => new OllamaProvider(definition)
|
|
9318
|
-
},
|
|
9319
|
-
"custom-module": {
|
|
9320
|
-
driver: "custom-module",
|
|
9321
|
-
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;
|
|
9322
10306
|
}
|
|
9323
10307
|
};
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
const normalized = ProviderDefinitionSchema.parse(definition);
|
|
9336
|
-
const factory = this.factories.get(normalized.driver);
|
|
9337
|
-
if (!factory) {
|
|
9338
|
-
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"}`);
|
|
9339
10319
|
}
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
}
|
|
9346
|
-
|
|
9347
|
-
const provider = await this.createProvider(definition);
|
|
9348
|
-
if (provider.listModels) {
|
|
9349
|
-
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}`);
|
|
9350
10327
|
}
|
|
9351
|
-
|
|
9352
|
-
id: modelId,
|
|
9353
|
-
providerId: definition.id
|
|
9354
|
-
})), input);
|
|
9355
|
-
}
|
|
9356
|
-
};
|
|
9357
|
-
function createDefaultBrainProviderRegistry(cwd = process.cwd()) {
|
|
9358
|
-
return new BrainProviderRegistry(cwd);
|
|
10328
|
+
});
|
|
9359
10329
|
}
|
|
9360
10330
|
|
|
9361
|
-
//
|
|
9362
|
-
var
|
|
9363
|
-
config;
|
|
9364
|
-
registry;
|
|
9365
|
-
constructor(config, registry = createDefaultBrainProviderRegistry()) {
|
|
9366
|
-
this.config = config;
|
|
9367
|
-
this.registry = registry;
|
|
9368
|
-
}
|
|
9369
|
-
async resolve(role) {
|
|
9370
|
-
const settings = resolveBrainSettings(this.config, role);
|
|
9371
|
-
const provider = findProviderById(this.config, settings.providerId);
|
|
9372
|
-
if (!provider) {
|
|
9373
|
-
throw new Error(`Brain role "${role}" points to unknown provider "${settings.providerId}".`);
|
|
9374
|
-
}
|
|
9375
|
-
const model = resolveBrainModel(this.config, role);
|
|
9376
|
-
if (!model) {
|
|
9377
|
-
throw new Error(`Brain role "${role}" does not resolve to a model.`);
|
|
9378
|
-
}
|
|
9379
|
-
return {
|
|
9380
|
-
role,
|
|
9381
|
-
settings,
|
|
9382
|
-
provider,
|
|
9383
|
-
model,
|
|
9384
|
-
client: await this.registry.createProvider(provider)
|
|
9385
|
-
};
|
|
9386
|
-
}
|
|
9387
|
-
};
|
|
10331
|
+
// src/commands/brains.ts
|
|
10332
|
+
var import_node_process2 = __toESM(require("node:process"), 1);
|
|
9388
10333
|
|
|
9389
10334
|
// src/ui/renderPlan.ts
|
|
9390
10335
|
function humanizeRole(role) {
|
|
@@ -9860,7 +10805,7 @@ function createModelsCommand() {
|
|
|
9860
10805
|
console.log(`Updated ${outputPath}`);
|
|
9861
10806
|
console.log(`Synced ${models.length} models for ${provider.id}`);
|
|
9862
10807
|
});
|
|
9863
|
-
command.command("use").description("Select a model for a provider and
|
|
10808
|
+
command.command("use").description("Select a model for a provider and assign it globally unless a specific role is requested.").argument("<model>", "Model id to select").requiredOption("--provider <provider>", "Provider id to use").option("--role <role>", "Brain role to assign: planner, coder, reviewer, or fast").option("--set-default", "Also set this model as the provider default", false).option("--force", "Skip remote model validation", false).option("--temperature <value>", "Temperature override for the role", parseNumber2).option("--max-tokens <value>", "Max token override for the role", parseInteger).action(async (model, options) => {
|
|
9864
10809
|
let config = await loadConfig(import_node_process5.default.cwd());
|
|
9865
10810
|
if (!config) {
|
|
9866
10811
|
requireConfigMessage2();
|
|
@@ -9910,12 +10855,16 @@ function createModelsCommand() {
|
|
|
9910
10855
|
maxTokens: options.maxTokens
|
|
9911
10856
|
} : {}
|
|
9912
10857
|
});
|
|
10858
|
+
} else {
|
|
10859
|
+
config = assignProviderToAllBrains(config, provider.id, model);
|
|
9913
10860
|
}
|
|
9914
10861
|
const outputPath = await saveConfig(config, import_node_process5.default.cwd());
|
|
9915
10862
|
console.log(`Updated ${outputPath}`);
|
|
9916
10863
|
console.log(`Selected ${provider.id}/${model}`);
|
|
9917
10864
|
if (options.role) {
|
|
9918
10865
|
console.log(`Assigned role ${options.role}`);
|
|
10866
|
+
} else {
|
|
10867
|
+
console.log("Assigned all roles");
|
|
9919
10868
|
}
|
|
9920
10869
|
if (shouldSetDefault) {
|
|
9921
10870
|
console.log(`Set provider default model to ${model}`);
|
|
@@ -9925,7 +10874,7 @@ function createModelsCommand() {
|
|
|
9925
10874
|
}
|
|
9926
10875
|
|
|
9927
10876
|
// src/commands/plan.ts
|
|
9928
|
-
var
|
|
10877
|
+
var import_promises7 = require("node:fs/promises");
|
|
9929
10878
|
var import_node_process6 = __toESM(require("node:process"), 1);
|
|
9930
10879
|
|
|
9931
10880
|
// ../planner/dist/planner.js
|
|
@@ -10230,7 +11179,7 @@ function createPlan(input) {
|
|
|
10230
11179
|
|
|
10231
11180
|
// src/commands/plan.ts
|
|
10232
11181
|
async function detectWorkspaceState(cwd) {
|
|
10233
|
-
const entries = await (0,
|
|
11182
|
+
const entries = await (0, import_promises7.readdir)(cwd);
|
|
10234
11183
|
const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
|
|
10235
11184
|
return meaningfulEntries.length === 0 ? "empty" : "existing";
|
|
10236
11185
|
}
|
|
@@ -10393,7 +11342,7 @@ function parseCount(value) {
|
|
|
10393
11342
|
return parsed;
|
|
10394
11343
|
}
|
|
10395
11344
|
function createResumeCommand() {
|
|
10396
|
-
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) => {
|
|
10397
11346
|
const cwd = import_node_process8.default.cwd();
|
|
10398
11347
|
const session = await loadLatestSession(cwd);
|
|
10399
11348
|
if (!session) {
|
|
@@ -10402,7 +11351,9 @@ function createResumeCommand() {
|
|
|
10402
11351
|
return;
|
|
10403
11352
|
}
|
|
10404
11353
|
const snapshot = options.execute ? await new ExecutionOrchestrator().continueSession(session, {
|
|
10405
|
-
maxAutoTasks: options.maxAutoTasks
|
|
11354
|
+
maxAutoTasks: options.maxAutoTasks,
|
|
11355
|
+
maxAgentSteps: options.maxAgentSteps,
|
|
11356
|
+
maxRepairAttempts: options.maxRepairAttempts
|
|
10406
11357
|
}) : session;
|
|
10407
11358
|
if (options.execute) {
|
|
10408
11359
|
await saveSession(snapshot, cwd);
|
|
@@ -10416,10 +11367,10 @@ function createResumeCommand() {
|
|
|
10416
11367
|
}
|
|
10417
11368
|
|
|
10418
11369
|
// src/commands/run.ts
|
|
10419
|
-
var
|
|
11370
|
+
var import_promises8 = require("node:fs/promises");
|
|
10420
11371
|
var import_node_process9 = __toESM(require("node:process"), 1);
|
|
10421
11372
|
async function detectWorkspaceState2(cwd) {
|
|
10422
|
-
const entries = await (0,
|
|
11373
|
+
const entries = await (0, import_promises8.readdir)(cwd);
|
|
10423
11374
|
const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
|
|
10424
11375
|
return meaningfulEntries.length === 0 ? "empty" : "existing";
|
|
10425
11376
|
}
|
|
@@ -10436,7 +11387,7 @@ function createRunCommand() {
|
|
|
10436
11387
|
"Explicit execution constraint; can be provided multiple times",
|
|
10437
11388
|
(value, previous = []) => [...previous, value],
|
|
10438
11389
|
[]
|
|
10439
|
-
).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) => {
|
|
10440
11391
|
const cwd = import_node_process9.default.cwd();
|
|
10441
11392
|
const orchestrator = new ExecutionOrchestrator();
|
|
10442
11393
|
let request;
|
|
@@ -10471,7 +11422,9 @@ function createRunCommand() {
|
|
|
10471
11422
|
const envelope = orchestrator.buildEnvelope(request, plan);
|
|
10472
11423
|
const initialSnapshot = orchestrator.createSessionSnapshot(envelope);
|
|
10473
11424
|
const snapshot = options.snapshotOnly ? initialSnapshot : await orchestrator.continueSession(initialSnapshot, {
|
|
10474
|
-
maxAutoTasks: options.maxAutoTasks
|
|
11425
|
+
maxAutoTasks: options.maxAutoTasks,
|
|
11426
|
+
maxAgentSteps: options.maxAgentSteps,
|
|
11427
|
+
maxRepairAttempts: options.maxRepairAttempts
|
|
10475
11428
|
});
|
|
10476
11429
|
const sessionPath = await saveSession(snapshot, cwd);
|
|
10477
11430
|
if (options.json) {
|
|
@@ -10559,7 +11512,7 @@ function createProgram(onOpenShell) {
|
|
|
10559
11512
|
}
|
|
10560
11513
|
|
|
10561
11514
|
// src/shell.ts
|
|
10562
|
-
var
|
|
11515
|
+
var import_promises9 = require("node:readline/promises");
|
|
10563
11516
|
var import_node_process10 = __toESM(require("node:process"), 1);
|
|
10564
11517
|
var AMBER = "\x1B[38;5;214m";
|
|
10565
11518
|
var BOLD = "\x1B[1m";
|
|
@@ -10587,6 +11540,9 @@ var TOP_LEVEL_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
10587
11540
|
"select",
|
|
10588
11541
|
"shell",
|
|
10589
11542
|
"status",
|
|
11543
|
+
"ask",
|
|
11544
|
+
"chat",
|
|
11545
|
+
"reset-chat",
|
|
10590
11546
|
"use-model"
|
|
10591
11547
|
]);
|
|
10592
11548
|
var MODEL_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -10601,6 +11557,7 @@ var BRAIN_ROLES = [
|
|
|
10601
11557
|
"reviewer",
|
|
10602
11558
|
"fast"
|
|
10603
11559
|
];
|
|
11560
|
+
var MAX_CHAT_MESSAGES = 12;
|
|
10604
11561
|
function color(code, value) {
|
|
10605
11562
|
return `${code}${value}${RESET}`;
|
|
10606
11563
|
}
|
|
@@ -10670,15 +11627,19 @@ function renderHelp() {
|
|
|
10670
11627
|
`${color(DIM, "Commands")}`,
|
|
10671
11628
|
"/status Show the active role, provider, and workspace state.",
|
|
10672
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.",
|
|
10673
11633
|
"/model Show current brain assignments.",
|
|
10674
11634
|
"/providers List configured providers.",
|
|
10675
11635
|
"/providers templates Show built-in provider templates.",
|
|
10676
|
-
"/providers add <tpl> [id]
|
|
10677
|
-
"
|
|
11636
|
+
"/providers add <tpl> [id] [--base-url <url>] [--model <id>] [--api-key-env <env>]",
|
|
11637
|
+
" Add a provider from a built-in template.",
|
|
11638
|
+
"/providers use <id> Use a provider for all agent roles.",
|
|
10678
11639
|
"/providers check Run provider health checks.",
|
|
10679
11640
|
"/models [search] Discover models for the active role's provider.",
|
|
10680
11641
|
"/select <n> Select a model from the last numbered /models list.",
|
|
10681
|
-
"/use-model <id> Assign a model to
|
|
11642
|
+
"/use-model <id> Assign a model to all agent roles.",
|
|
10682
11643
|
"/plan <goal> Create a structured implementation plan.",
|
|
10683
11644
|
"/run <goal> Start a Kimbho execution session for a goal.",
|
|
10684
11645
|
"/resume Show the latest saved session.",
|
|
@@ -10688,7 +11649,7 @@ function renderHelp() {
|
|
|
10688
11649
|
"/quit, /exit Leave the shell.",
|
|
10689
11650
|
"",
|
|
10690
11651
|
`${color(DIM, "Tip")}`,
|
|
10691
|
-
"Type
|
|
11652
|
+
"Type natural language directly to chat with the active model. Use /run when you want a plan/session."
|
|
10692
11653
|
].join("\n");
|
|
10693
11654
|
}
|
|
10694
11655
|
function renderStartupCard(cwd, state) {
|
|
@@ -10703,7 +11664,7 @@ function renderStartupCard(cwd, state) {
|
|
|
10703
11664
|
renderCardLine("approval", state.approvalMode),
|
|
10704
11665
|
renderCardLine("sandbox", state.sandboxMode),
|
|
10705
11666
|
renderCardLine("preset", state.stackPreset),
|
|
10706
|
-
renderCardLine("shortcuts", "/
|
|
11667
|
+
renderCardLine("shortcuts", "/ask /run /brain /providers /models /quit")
|
|
10707
11668
|
];
|
|
10708
11669
|
if (!state.configured) {
|
|
10709
11670
|
cardLines.push("setup: run /init or /providers add <template> to create .kimbho/config.json");
|
|
@@ -10719,7 +11680,7 @@ function printHeader(cwd, state) {
|
|
|
10719
11680
|
console.log(color(DIM, "Terminal-native coding agent"));
|
|
10720
11681
|
console.log(renderStartupCard(cwd, state));
|
|
10721
11682
|
console.log("");
|
|
10722
|
-
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."));
|
|
10723
11684
|
console.log(renderHelp());
|
|
10724
11685
|
console.log("");
|
|
10725
11686
|
}
|
|
@@ -10795,6 +11756,63 @@ function normalizeInputTokens(input) {
|
|
|
10795
11756
|
head: firstToken.startsWith("/") ? firstToken.slice(1) : firstToken
|
|
10796
11757
|
};
|
|
10797
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
|
+
}
|
|
10798
11816
|
function defaultProviderIdForTemplate(templateId) {
|
|
10799
11817
|
switch (templateId) {
|
|
10800
11818
|
case "openai":
|
|
@@ -10811,6 +11829,52 @@ function defaultProviderIdForTemplate(templateId) {
|
|
|
10811
11829
|
return `${templateId}-main`;
|
|
10812
11830
|
}
|
|
10813
11831
|
}
|
|
11832
|
+
function parseProviderAddOptions(tokens) {
|
|
11833
|
+
const templateId = tokens[2];
|
|
11834
|
+
if (!templateId) {
|
|
11835
|
+
throw new Error("Usage: /providers add <template> [provider-id] [--base-url <url>] [--model <id>] [--api-key-env <env>]");
|
|
11836
|
+
}
|
|
11837
|
+
let index = 3;
|
|
11838
|
+
let providerId;
|
|
11839
|
+
if (tokens[index] && !tokens[index]?.startsWith("-")) {
|
|
11840
|
+
providerId = tokens[index];
|
|
11841
|
+
index += 1;
|
|
11842
|
+
}
|
|
11843
|
+
const options = {
|
|
11844
|
+
templateId,
|
|
11845
|
+
...providerId ? {
|
|
11846
|
+
providerId
|
|
11847
|
+
} : {}
|
|
11848
|
+
};
|
|
11849
|
+
while (index < tokens.length) {
|
|
11850
|
+
const flag = tokens[index];
|
|
11851
|
+
if (!flag?.startsWith("--")) {
|
|
11852
|
+
throw new Error(`Unexpected token "${flag}".`);
|
|
11853
|
+
}
|
|
11854
|
+
const value = tokens[index + 1];
|
|
11855
|
+
if (!value || value.startsWith("--")) {
|
|
11856
|
+
throw new Error(`Missing value for ${flag}.`);
|
|
11857
|
+
}
|
|
11858
|
+
switch (flag) {
|
|
11859
|
+
case "--label":
|
|
11860
|
+
options.label = value;
|
|
11861
|
+
break;
|
|
11862
|
+
case "--base-url":
|
|
11863
|
+
options.baseUrl = normalizeTemplateBaseUrl(templateId, value) ?? value;
|
|
11864
|
+
break;
|
|
11865
|
+
case "--api-key-env":
|
|
11866
|
+
options.apiKeyEnv = value;
|
|
11867
|
+
break;
|
|
11868
|
+
case "--model":
|
|
11869
|
+
options.model = value;
|
|
11870
|
+
break;
|
|
11871
|
+
default:
|
|
11872
|
+
throw new Error(`Unknown option "${flag}". Supported: --label, --base-url, --api-key-env, --model.`);
|
|
11873
|
+
}
|
|
11874
|
+
index += 2;
|
|
11875
|
+
}
|
|
11876
|
+
return options;
|
|
11877
|
+
}
|
|
10814
11878
|
function buildCachedModels(provider, search, limit = 25) {
|
|
10815
11879
|
const normalized = search?.trim().toLowerCase();
|
|
10816
11880
|
const filtered = normalized ? provider.models.filter((modelId) => modelId.toLowerCase().includes(normalized)) : provider.models;
|
|
@@ -10857,7 +11921,7 @@ async function fetchModelsForProvider(cwd, config, provider, search, limit = 25)
|
|
|
10857
11921
|
throw error;
|
|
10858
11922
|
}
|
|
10859
11923
|
}
|
|
10860
|
-
async function
|
|
11924
|
+
async function assignModelGlobally(cwd, providerId, model) {
|
|
10861
11925
|
const config = await loadConfig(cwd);
|
|
10862
11926
|
if (!config) {
|
|
10863
11927
|
throw new Error("No config found. Run /init or /providers add first.");
|
|
@@ -10866,8 +11930,7 @@ async function assignModelToRole(cwd, role, providerId, model) {
|
|
|
10866
11930
|
if (!provider) {
|
|
10867
11931
|
throw new Error(`Unknown provider "${providerId}".`);
|
|
10868
11932
|
}
|
|
10869
|
-
const
|
|
10870
|
-
const nextConfig = assignBrain(
|
|
11933
|
+
const nextConfig = assignProviderToAllBrains(
|
|
10871
11934
|
upsertProvider(config, {
|
|
10872
11935
|
...provider,
|
|
10873
11936
|
defaultModel: model,
|
|
@@ -10876,25 +11939,13 @@ async function assignModelToRole(cwd, role, providerId, model) {
|
|
|
10876
11939
|
model
|
|
10877
11940
|
]))
|
|
10878
11941
|
}),
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
providerId,
|
|
10882
|
-
model,
|
|
10883
|
-
...typeof currentSettings.temperature === "number" ? {
|
|
10884
|
-
temperature: currentSettings.temperature
|
|
10885
|
-
} : {},
|
|
10886
|
-
...typeof currentSettings.maxTokens === "number" ? {
|
|
10887
|
-
maxTokens: currentSettings.maxTokens
|
|
10888
|
-
} : {},
|
|
10889
|
-
...currentSettings.promptPreamble ? {
|
|
10890
|
-
promptPreamble: currentSettings.promptPreamble
|
|
10891
|
-
} : {}
|
|
10892
|
-
}
|
|
11942
|
+
providerId,
|
|
11943
|
+
model
|
|
10893
11944
|
);
|
|
10894
11945
|
const outputPath = await saveConfig(nextConfig, cwd);
|
|
10895
11946
|
return outputPath;
|
|
10896
11947
|
}
|
|
10897
|
-
async function
|
|
11948
|
+
async function useProviderGlobally(cwd, providerId) {
|
|
10898
11949
|
const config = await loadConfig(cwd);
|
|
10899
11950
|
if (!config) {
|
|
10900
11951
|
throw new Error("No config found. Run /init or /providers add first.");
|
|
@@ -10903,23 +11954,8 @@ async function useProviderForRole(cwd, role, providerId) {
|
|
|
10903
11954
|
if (!provider) {
|
|
10904
11955
|
throw new Error(`Unknown provider "${providerId}".`);
|
|
10905
11956
|
}
|
|
10906
|
-
const currentSettings = resolveBrainSettings(config, role);
|
|
10907
11957
|
const resolvedModel = provider.defaultModel ?? provider.models[0] ?? null;
|
|
10908
|
-
const nextConfig =
|
|
10909
|
-
providerId: provider.id,
|
|
10910
|
-
...resolvedModel ? {
|
|
10911
|
-
model: resolvedModel
|
|
10912
|
-
} : {},
|
|
10913
|
-
...typeof currentSettings.temperature === "number" ? {
|
|
10914
|
-
temperature: currentSettings.temperature
|
|
10915
|
-
} : {},
|
|
10916
|
-
...typeof currentSettings.maxTokens === "number" ? {
|
|
10917
|
-
maxTokens: currentSettings.maxTokens
|
|
10918
|
-
} : {},
|
|
10919
|
-
...currentSettings.promptPreamble ? {
|
|
10920
|
-
promptPreamble: currentSettings.promptPreamble
|
|
10921
|
-
} : {}
|
|
10922
|
-
});
|
|
11958
|
+
const nextConfig = assignProviderToAllBrains(config, provider.id, resolvedModel);
|
|
10923
11959
|
const outputPath = await saveConfig(nextConfig, cwd);
|
|
10924
11960
|
return {
|
|
10925
11961
|
outputPath,
|
|
@@ -10950,6 +11986,7 @@ async function printProviderList(cwd, focusRole) {
|
|
|
10950
11986
|
console.log(` label: ${provider.label ?? "-"}`);
|
|
10951
11987
|
console.log(` driver: ${provider.driver}`);
|
|
10952
11988
|
console.log(` model: ${provider.defaultModel ?? "-"}`);
|
|
11989
|
+
console.log(` baseUrl: ${provider.baseUrl ?? "-"}`);
|
|
10953
11990
|
console.log(` auth: ${envState}`);
|
|
10954
11991
|
console.log(` cachedModels: ${provider.models.length}`);
|
|
10955
11992
|
}
|
|
@@ -10964,9 +12001,21 @@ function printProviderTemplates() {
|
|
|
10964
12001
|
console.log(` notes: ${template.notes}`);
|
|
10965
12002
|
}
|
|
10966
12003
|
}
|
|
10967
|
-
async function addProviderFromTemplate(cwd,
|
|
10968
|
-
const provider = buildProviderFromTemplate(templateId, {
|
|
10969
|
-
providerId: providerId ?? defaultProviderIdForTemplate(templateId)
|
|
12004
|
+
async function addProviderFromTemplate(cwd, options) {
|
|
12005
|
+
const provider = buildProviderFromTemplate(options.templateId, {
|
|
12006
|
+
providerId: options.providerId ?? defaultProviderIdForTemplate(options.templateId),
|
|
12007
|
+
...options.label ? {
|
|
12008
|
+
label: options.label
|
|
12009
|
+
} : {},
|
|
12010
|
+
...options.baseUrl ? {
|
|
12011
|
+
baseUrl: options.baseUrl
|
|
12012
|
+
} : {},
|
|
12013
|
+
...options.apiKeyEnv ? {
|
|
12014
|
+
apiKeyEnv: options.apiKeyEnv
|
|
12015
|
+
} : {},
|
|
12016
|
+
...options.model ? {
|
|
12017
|
+
model: options.model
|
|
12018
|
+
} : {}
|
|
10970
12019
|
});
|
|
10971
12020
|
const config = await loadConfig(cwd);
|
|
10972
12021
|
if (!config) {
|
|
@@ -11025,30 +12074,27 @@ async function handleProvidersCommand(cwd, tokens, runtime) {
|
|
|
11025
12074
|
return;
|
|
11026
12075
|
}
|
|
11027
12076
|
if (subcommand === "add") {
|
|
11028
|
-
const
|
|
11029
|
-
const
|
|
11030
|
-
|
|
11031
|
-
throw new Error("Usage: /providers add <template> [provider-id]");
|
|
11032
|
-
}
|
|
11033
|
-
const outputPath = await addProviderFromTemplate(cwd, templateId, providerId);
|
|
11034
|
-
const resolvedProviderId = providerId ?? defaultProviderIdForTemplate(templateId);
|
|
12077
|
+
const options = parseProviderAddOptions(tokens);
|
|
12078
|
+
const outputPath = await addProviderFromTemplate(cwd, options);
|
|
12079
|
+
const resolvedProviderId = options.providerId ?? defaultProviderIdForTemplate(options.templateId);
|
|
11035
12080
|
console.log(`Updated ${outputPath}`);
|
|
11036
|
-
console.log(`Added provider ${resolvedProviderId} from template ${templateId}`);
|
|
12081
|
+
console.log(`Added provider ${resolvedProviderId} from template ${options.templateId}`);
|
|
12082
|
+
if (options.baseUrl) {
|
|
12083
|
+
console.log(`Base URL ${options.baseUrl}`);
|
|
12084
|
+
}
|
|
11037
12085
|
console.log(`Next: /providers use ${resolvedProviderId}`);
|
|
11038
12086
|
return;
|
|
11039
12087
|
}
|
|
11040
12088
|
if (subcommand === "use") {
|
|
11041
12089
|
const providerId = tokens[2];
|
|
11042
|
-
const role = tokens[3];
|
|
11043
12090
|
if (!providerId) {
|
|
11044
|
-
throw new Error("Usage: /providers use <provider-id>
|
|
12091
|
+
throw new Error("Usage: /providers use <provider-id>");
|
|
11045
12092
|
}
|
|
11046
|
-
const
|
|
11047
|
-
|
|
11048
|
-
runtime.focusRole = targetRole;
|
|
12093
|
+
const result = await useProviderGlobally(cwd, providerId);
|
|
12094
|
+
clearAllConversations(runtime);
|
|
11049
12095
|
runtime.lastModels = null;
|
|
11050
12096
|
console.log(`Updated ${result.outputPath}`);
|
|
11051
|
-
console.log(`
|
|
12097
|
+
console.log(`All roles now use provider ${providerId}${result.model ? ` (${result.model})` : ""}`);
|
|
11052
12098
|
return;
|
|
11053
12099
|
}
|
|
11054
12100
|
throw new Error("Usage: /providers [list|templates|add|use|check]");
|
|
@@ -11089,10 +12135,11 @@ async function handleModelsCommand(cwd, tokens, runtime) {
|
|
|
11089
12135
|
if (!modelId) {
|
|
11090
12136
|
throw new Error("Usage: /models use <model-id>");
|
|
11091
12137
|
}
|
|
11092
|
-
const outputPath = await
|
|
12138
|
+
const outputPath = await assignModelGlobally(cwd, provider.id, modelId);
|
|
12139
|
+
clearAllConversations(runtime);
|
|
11093
12140
|
console.log(`Updated ${outputPath}`);
|
|
11094
12141
|
console.log(`Selected ${provider.id}/${modelId}`);
|
|
11095
|
-
console.log(
|
|
12142
|
+
console.log("Assigned all roles");
|
|
11096
12143
|
return;
|
|
11097
12144
|
}
|
|
11098
12145
|
const search = !subcommand || MODEL_SUBCOMMANDS.has(subcommand) ? void 0 : tokens.slice(1).join(" ");
|
|
@@ -11118,7 +12165,7 @@ async function handleModelsCommand(cwd, tokens, runtime) {
|
|
|
11118
12165
|
console.log(` ${index + 1}. ${renderModelLine2(model)}`);
|
|
11119
12166
|
}
|
|
11120
12167
|
console.log(``);
|
|
11121
|
-
console.log(
|
|
12168
|
+
console.log("Use /select <number> or /use-model <model-id> to assign one to all roles.");
|
|
11122
12169
|
}
|
|
11123
12170
|
async function handleModelSelection(cwd, modelId, runtime) {
|
|
11124
12171
|
const config = await loadConfig(cwd);
|
|
@@ -11130,10 +12177,11 @@ async function handleModelSelection(cwd, modelId, runtime) {
|
|
|
11130
12177
|
if (!provider) {
|
|
11131
12178
|
throw new Error(`Active role "${runtime.focusRole}" points to unknown provider "${providerId}".`);
|
|
11132
12179
|
}
|
|
11133
|
-
const outputPath = await
|
|
12180
|
+
const outputPath = await assignModelGlobally(cwd, provider.id, modelId);
|
|
12181
|
+
clearAllConversations(runtime);
|
|
11134
12182
|
console.log(`Updated ${outputPath}`);
|
|
11135
12183
|
console.log(`Selected ${provider.id}/${modelId}`);
|
|
11136
|
-
console.log(
|
|
12184
|
+
console.log("Assigned all roles");
|
|
11137
12185
|
}
|
|
11138
12186
|
async function handleSelectCommand(cwd, tokens, runtime) {
|
|
11139
12187
|
const rawIndex = tokens[1];
|
|
@@ -11176,10 +12224,22 @@ function toExternalCommandTokens(input, state) {
|
|
|
11176
12224
|
`scaffold ${goal}`.trim()
|
|
11177
12225
|
];
|
|
11178
12226
|
}
|
|
11179
|
-
if (
|
|
11180
|
-
|
|
12227
|
+
if (head === "run") {
|
|
12228
|
+
const goal = normalizedInput.replace(/^\/?run\b\s*/, "");
|
|
12229
|
+
return goal ? [
|
|
11181
12230
|
"run",
|
|
11182
|
-
|
|
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"
|
|
11183
12243
|
];
|
|
11184
12244
|
}
|
|
11185
12245
|
tokens[0] = head;
|
|
@@ -11210,9 +12270,14 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
|
|
|
11210
12270
|
return;
|
|
11211
12271
|
}
|
|
11212
12272
|
const { tokens, head } = normalizeInputTokens(trimmed);
|
|
12273
|
+
const firstToken = tokens[0];
|
|
11213
12274
|
if (!head) {
|
|
11214
12275
|
return;
|
|
11215
12276
|
}
|
|
12277
|
+
if (!firstToken?.startsWith("/") && !TOP_LEVEL_COMMANDS.has(head) && !head.startsWith("-")) {
|
|
12278
|
+
await handleChatPrompt(cwd, trimmed, runtime);
|
|
12279
|
+
return;
|
|
12280
|
+
}
|
|
11216
12281
|
if (head === "provider" || head === "providers") {
|
|
11217
12282
|
const subcommand = tokens[1];
|
|
11218
12283
|
const canHandleLocally = !subcommand || subcommand === "list" || subcommand === "templates" || subcommand === "check" || subcommand === "use" || subcommand === "add" && Boolean(tokens[2]) && !tokens[2]?.startsWith("-");
|
|
@@ -11240,6 +12305,19 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
|
|
|
11240
12305
|
await printBrainAssignments(cwd);
|
|
11241
12306
|
return;
|
|
11242
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
|
+
}
|
|
11243
12321
|
if (head === "models") {
|
|
11244
12322
|
await handleModelsCommand(cwd, [
|
|
11245
12323
|
"models",
|
|
@@ -11269,14 +12347,20 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
|
|
|
11269
12347
|
await execute(externalTokens);
|
|
11270
12348
|
}
|
|
11271
12349
|
async function runInteractiveShell(options) {
|
|
11272
|
-
const readline = (0,
|
|
12350
|
+
const readline = (0, import_promises9.createInterface)({
|
|
11273
12351
|
input: import_node_process10.default.stdin,
|
|
11274
12352
|
output: import_node_process10.default.stdout,
|
|
11275
12353
|
terminal: Boolean(import_node_process10.default.stdin.isTTY && import_node_process10.default.stdout.isTTY)
|
|
11276
12354
|
});
|
|
11277
12355
|
const runtime = {
|
|
11278
12356
|
focusRole: "coder",
|
|
11279
|
-
lastModels: null
|
|
12357
|
+
lastModels: null,
|
|
12358
|
+
conversations: {
|
|
12359
|
+
planner: [],
|
|
12360
|
+
coder: [],
|
|
12361
|
+
reviewer: [],
|
|
12362
|
+
fast: []
|
|
12363
|
+
}
|
|
11280
12364
|
};
|
|
11281
12365
|
let closed = false;
|
|
11282
12366
|
readline.on("SIGINT", () => {
|