@kimbho/kimbho-cli 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +406 -87
- package/dist/index.cjs.map +4 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -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.9",
|
|
3350
3350
|
description: "Kimbho CLI is a terminal-native coding agent for planning, execution, and verification.",
|
|
3351
3351
|
type: "module",
|
|
3352
3352
|
engines: {
|
|
@@ -7996,6 +7996,178 @@ var SessionSnapshotSchema = external_exports.object({
|
|
|
7996
7996
|
events: external_exports.array(SessionEventSchema).default([])
|
|
7997
7997
|
});
|
|
7998
7998
|
|
|
7999
|
+
// ../core/dist/jsonish.js
|
|
8000
|
+
function stripCodeFences(raw) {
|
|
8001
|
+
const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
8002
|
+
return (fenced?.[1] ?? raw).trim();
|
|
8003
|
+
}
|
|
8004
|
+
function normalizeSmartQuotes(value) {
|
|
8005
|
+
return value.replace(/[\u201c\u201d]/g, '"').replace(/[\u2018\u2019]/g, "'");
|
|
8006
|
+
}
|
|
8007
|
+
function maybeUnwrapQuotedJson(value) {
|
|
8008
|
+
const trimmed = value.trim();
|
|
8009
|
+
if (!trimmed.startsWith('"')) {
|
|
8010
|
+
return trimmed;
|
|
8011
|
+
}
|
|
8012
|
+
try {
|
|
8013
|
+
const parsed = JSON.parse(trimmed);
|
|
8014
|
+
return typeof parsed === "string" ? parsed.trim() : trimmed;
|
|
8015
|
+
} catch {
|
|
8016
|
+
return trimmed;
|
|
8017
|
+
}
|
|
8018
|
+
}
|
|
8019
|
+
function findBalancedObject(value) {
|
|
8020
|
+
for (let start = 0; start < value.length; start += 1) {
|
|
8021
|
+
if (value[start] !== "{") {
|
|
8022
|
+
continue;
|
|
8023
|
+
}
|
|
8024
|
+
let depth = 0;
|
|
8025
|
+
let inString = false;
|
|
8026
|
+
let escaping = false;
|
|
8027
|
+
for (let index = start; index < value.length; index += 1) {
|
|
8028
|
+
const character = value[index];
|
|
8029
|
+
if (escaping) {
|
|
8030
|
+
escaping = false;
|
|
8031
|
+
continue;
|
|
8032
|
+
}
|
|
8033
|
+
if (character === "\\") {
|
|
8034
|
+
escaping = true;
|
|
8035
|
+
continue;
|
|
8036
|
+
}
|
|
8037
|
+
if (character === '"') {
|
|
8038
|
+
inString = !inString;
|
|
8039
|
+
continue;
|
|
8040
|
+
}
|
|
8041
|
+
if (inString) {
|
|
8042
|
+
continue;
|
|
8043
|
+
}
|
|
8044
|
+
if (character === "{") {
|
|
8045
|
+
depth += 1;
|
|
8046
|
+
continue;
|
|
8047
|
+
}
|
|
8048
|
+
if (character === "}") {
|
|
8049
|
+
depth -= 1;
|
|
8050
|
+
if (depth === 0) {
|
|
8051
|
+
return value.slice(start, index + 1);
|
|
8052
|
+
}
|
|
8053
|
+
}
|
|
8054
|
+
}
|
|
8055
|
+
}
|
|
8056
|
+
return null;
|
|
8057
|
+
}
|
|
8058
|
+
function removeTrailingCommas(value) {
|
|
8059
|
+
let output = "";
|
|
8060
|
+
let inString = false;
|
|
8061
|
+
let escaping = false;
|
|
8062
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
8063
|
+
const character = value[index];
|
|
8064
|
+
if (escaping) {
|
|
8065
|
+
output += character;
|
|
8066
|
+
escaping = false;
|
|
8067
|
+
continue;
|
|
8068
|
+
}
|
|
8069
|
+
if (character === "\\") {
|
|
8070
|
+
output += character;
|
|
8071
|
+
escaping = true;
|
|
8072
|
+
continue;
|
|
8073
|
+
}
|
|
8074
|
+
if (character === '"') {
|
|
8075
|
+
output += character;
|
|
8076
|
+
inString = !inString;
|
|
8077
|
+
continue;
|
|
8078
|
+
}
|
|
8079
|
+
if (!inString && character === ",") {
|
|
8080
|
+
let lookahead = index + 1;
|
|
8081
|
+
while (lookahead < value.length && /\s/.test(value[lookahead] ?? "")) {
|
|
8082
|
+
lookahead += 1;
|
|
8083
|
+
}
|
|
8084
|
+
if (value[lookahead] === "}" || value[lookahead] === "]") {
|
|
8085
|
+
continue;
|
|
8086
|
+
}
|
|
8087
|
+
}
|
|
8088
|
+
output += character;
|
|
8089
|
+
}
|
|
8090
|
+
return output;
|
|
8091
|
+
}
|
|
8092
|
+
function escapeControlCharsInStrings(value) {
|
|
8093
|
+
let output = "";
|
|
8094
|
+
let inString = false;
|
|
8095
|
+
let escaping = false;
|
|
8096
|
+
for (const character of value) {
|
|
8097
|
+
if (escaping) {
|
|
8098
|
+
output += character;
|
|
8099
|
+
escaping = false;
|
|
8100
|
+
continue;
|
|
8101
|
+
}
|
|
8102
|
+
if (character === "\\") {
|
|
8103
|
+
output += character;
|
|
8104
|
+
escaping = true;
|
|
8105
|
+
continue;
|
|
8106
|
+
}
|
|
8107
|
+
if (character === '"') {
|
|
8108
|
+
output += character;
|
|
8109
|
+
inString = !inString;
|
|
8110
|
+
continue;
|
|
8111
|
+
}
|
|
8112
|
+
if (inString && character === "\n") {
|
|
8113
|
+
output += "\\n";
|
|
8114
|
+
continue;
|
|
8115
|
+
}
|
|
8116
|
+
if (inString && character === "\r") {
|
|
8117
|
+
output += "\\r";
|
|
8118
|
+
continue;
|
|
8119
|
+
}
|
|
8120
|
+
if (inString && character === " ") {
|
|
8121
|
+
output += "\\t";
|
|
8122
|
+
continue;
|
|
8123
|
+
}
|
|
8124
|
+
output += character;
|
|
8125
|
+
}
|
|
8126
|
+
return output;
|
|
8127
|
+
}
|
|
8128
|
+
function parseCandidate(candidate) {
|
|
8129
|
+
return JSON.parse(candidate);
|
|
8130
|
+
}
|
|
8131
|
+
function extractJsonObjectish(raw, context) {
|
|
8132
|
+
const stripped = maybeUnwrapQuotedJson(normalizeSmartQuotes(stripCodeFences(raw)));
|
|
8133
|
+
const extracted = findBalancedObject(stripped) ?? stripped;
|
|
8134
|
+
const candidates = Array.from(new Set([
|
|
8135
|
+
stripped,
|
|
8136
|
+
extracted,
|
|
8137
|
+
removeTrailingCommas(extracted),
|
|
8138
|
+
escapeControlCharsInStrings(removeTrailingCommas(extracted))
|
|
8139
|
+
].filter((candidate) => candidate.trim().length > 0)));
|
|
8140
|
+
let lastError;
|
|
8141
|
+
for (const candidate of candidates) {
|
|
8142
|
+
try {
|
|
8143
|
+
const parsed = parseCandidate(candidate);
|
|
8144
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8145
|
+
throw new Error(`Expected a JSON object in the ${context}.`);
|
|
8146
|
+
}
|
|
8147
|
+
return parsed;
|
|
8148
|
+
} catch (error) {
|
|
8149
|
+
lastError = error;
|
|
8150
|
+
}
|
|
8151
|
+
}
|
|
8152
|
+
if (lastError instanceof Error) {
|
|
8153
|
+
throw lastError;
|
|
8154
|
+
}
|
|
8155
|
+
throw new Error(`Expected a JSON object in the ${context}.`);
|
|
8156
|
+
}
|
|
8157
|
+
function summarizeStructuredOutputError(context, error) {
|
|
8158
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8159
|
+
if (/Expected a JSON object/i.test(message)) {
|
|
8160
|
+
return `${context} returned non-machine-readable output.`;
|
|
8161
|
+
}
|
|
8162
|
+
if (/Unexpected end of JSON input/i.test(message)) {
|
|
8163
|
+
return `${context} returned truncated JSON.`;
|
|
8164
|
+
}
|
|
8165
|
+
if (/JSON|property|position/i.test(message)) {
|
|
8166
|
+
return `${context} returned malformed JSON.`;
|
|
8167
|
+
}
|
|
8168
|
+
return `${context} returned invalid structured output.`;
|
|
8169
|
+
}
|
|
8170
|
+
|
|
7999
8171
|
// ../core/dist/config/config.js
|
|
8000
8172
|
var import_promises = require("node:fs/promises");
|
|
8001
8173
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
@@ -8415,6 +8587,10 @@ async function requestJson(url, init, timeoutMs = 3e4) {
|
|
|
8415
8587
|
}
|
|
8416
8588
|
return response.json();
|
|
8417
8589
|
}
|
|
8590
|
+
function isBadRequestError(error) {
|
|
8591
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8592
|
+
return /Request failed with 400\b/i.test(message);
|
|
8593
|
+
}
|
|
8418
8594
|
function trimTrailingSlash2(value) {
|
|
8419
8595
|
return value.replace(/\/+$/, "");
|
|
8420
8596
|
}
|
|
@@ -8796,20 +8972,39 @@ var OpenAICompatibleProvider = class {
|
|
|
8796
8972
|
}
|
|
8797
8973
|
const apiKey = resolveApiKey(this.definition);
|
|
8798
8974
|
const model = requireModel(this.definition, input);
|
|
8799
|
-
const
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8975
|
+
const requestBody = {
|
|
8976
|
+
model,
|
|
8977
|
+
messages: toChatMessages(input),
|
|
8978
|
+
...input.responseFormat === "json_object" ? {
|
|
8979
|
+
response_format: {
|
|
8980
|
+
type: "json_object"
|
|
8981
|
+
}
|
|
8982
|
+
} : {},
|
|
8983
|
+
...typeof input.temperature === "number" ? {
|
|
8984
|
+
temperature: input.temperature
|
|
8985
|
+
} : {},
|
|
8986
|
+
...typeof input.maxTokens === "number" ? {
|
|
8987
|
+
max_tokens: input.maxTokens
|
|
8988
|
+
} : {}
|
|
8989
|
+
};
|
|
8990
|
+
let response;
|
|
8991
|
+
try {
|
|
8992
|
+
response = await requestJson(`${this.definition.baseUrl}/chat/completions`, {
|
|
8993
|
+
method: "POST",
|
|
8994
|
+
headers: buildProviderHeaders(this.definition, apiKey),
|
|
8995
|
+
body: JSON.stringify(requestBody)
|
|
8996
|
+
}, GENERATION_TIMEOUT_MS);
|
|
8997
|
+
} catch (error) {
|
|
8998
|
+
if (!input.responseFormat || !isBadRequestError(error)) {
|
|
8999
|
+
throw error;
|
|
9000
|
+
}
|
|
9001
|
+
const { response_format: _ignored, ...fallbackBody } = requestBody;
|
|
9002
|
+
response = await requestJson(`${this.definition.baseUrl}/chat/completions`, {
|
|
9003
|
+
method: "POST",
|
|
9004
|
+
headers: buildProviderHeaders(this.definition, apiKey),
|
|
9005
|
+
body: JSON.stringify(fallbackBody)
|
|
9006
|
+
}, GENERATION_TIMEOUT_MS);
|
|
9007
|
+
}
|
|
8813
9008
|
const choices = Array.isArray(response.choices) ? response.choices : [];
|
|
8814
9009
|
const firstChoice = choices.length > 0 && typeof choices[0] === "object" && choices[0] !== null ? choices[0] : null;
|
|
8815
9010
|
const message = firstChoice && typeof firstChoice.message === "object" && firstChoice.message !== null ? firstChoice.message : null;
|
|
@@ -8852,26 +9047,46 @@ var OllamaProvider = class {
|
|
|
8852
9047
|
async generateText(input) {
|
|
8853
9048
|
const baseUrl = this.definition.baseUrl ?? "http://localhost:11434";
|
|
8854
9049
|
const model = requireModel(this.definition, input);
|
|
8855
|
-
const
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
},
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
}
|
|
8874
|
-
|
|
9050
|
+
const requestBody = {
|
|
9051
|
+
model,
|
|
9052
|
+
prompt: input.userPrompt ?? toPromptText(input),
|
|
9053
|
+
...input.systemPrompt ? {
|
|
9054
|
+
system: input.systemPrompt
|
|
9055
|
+
} : {},
|
|
9056
|
+
stream: false,
|
|
9057
|
+
...input.responseFormat === "json_object" ? {
|
|
9058
|
+
format: "json"
|
|
9059
|
+
} : {},
|
|
9060
|
+
...typeof input.temperature === "number" ? {
|
|
9061
|
+
options: {
|
|
9062
|
+
temperature: input.temperature
|
|
9063
|
+
}
|
|
9064
|
+
} : {}
|
|
9065
|
+
};
|
|
9066
|
+
let response;
|
|
9067
|
+
try {
|
|
9068
|
+
response = await requestJson(`${baseUrl}/api/generate`, {
|
|
9069
|
+
method: "POST",
|
|
9070
|
+
headers: {
|
|
9071
|
+
"content-type": "application/json",
|
|
9072
|
+
...this.definition.headers
|
|
9073
|
+
},
|
|
9074
|
+
body: JSON.stringify(requestBody)
|
|
9075
|
+
}, GENERATION_TIMEOUT_MS);
|
|
9076
|
+
} catch (error) {
|
|
9077
|
+
if (!input.responseFormat || !isBadRequestError(error)) {
|
|
9078
|
+
throw error;
|
|
9079
|
+
}
|
|
9080
|
+
const { format: _ignored, ...fallbackBody } = requestBody;
|
|
9081
|
+
response = await requestJson(`${baseUrl}/api/generate`, {
|
|
9082
|
+
method: "POST",
|
|
9083
|
+
headers: {
|
|
9084
|
+
"content-type": "application/json",
|
|
9085
|
+
...this.definition.headers
|
|
9086
|
+
},
|
|
9087
|
+
body: JSON.stringify(fallbackBody)
|
|
9088
|
+
}, GENERATION_TIMEOUT_MS);
|
|
9089
|
+
}
|
|
8875
9090
|
const usage = extractUsage(response);
|
|
8876
9091
|
return usage ? {
|
|
8877
9092
|
text: typeof response.response === "string" ? response.response : "",
|
|
@@ -11884,16 +12099,6 @@ function truncateForModel(value) {
|
|
|
11884
12099
|
return `${value.slice(0, MAX_TOOL_OUTPUT_CHARS)}
|
|
11885
12100
|
... [truncated]`;
|
|
11886
12101
|
}
|
|
11887
|
-
function extractJsonObject(raw) {
|
|
11888
|
-
const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
11889
|
-
const candidate = (fenced?.[1] ?? raw).trim();
|
|
11890
|
-
const firstBrace = candidate.indexOf("{");
|
|
11891
|
-
const lastBrace = candidate.lastIndexOf("}");
|
|
11892
|
-
if (firstBrace === -1 || lastBrace === -1 || lastBrace <= firstBrace) {
|
|
11893
|
-
throw new Error("Expected a JSON object in the model response.");
|
|
11894
|
-
}
|
|
11895
|
-
return JSON.parse(candidate.slice(firstBrace, lastBrace + 1));
|
|
11896
|
-
}
|
|
11897
12102
|
function renderToolResultForModel(result) {
|
|
11898
12103
|
const sections = [
|
|
11899
12104
|
`tool: ${result.toolId}`,
|
|
@@ -12061,6 +12266,9 @@ function summarizeSavedSessionContext(sessionId, session) {
|
|
|
12061
12266
|
...notes
|
|
12062
12267
|
];
|
|
12063
12268
|
}
|
|
12269
|
+
function summarizeModelActionError(error) {
|
|
12270
|
+
return summarizeStructuredOutputError("Model action", error);
|
|
12271
|
+
}
|
|
12064
12272
|
function buildInitialUserPrompt(task, request) {
|
|
12065
12273
|
return [
|
|
12066
12274
|
`Complete this task in the workspace.`,
|
|
@@ -12568,17 +12776,44 @@ var AutonomousTaskExecutor = class {
|
|
|
12568
12776
|
let responseText = "";
|
|
12569
12777
|
let parsedAction = null;
|
|
12570
12778
|
for (let attempt = 0; attempt <= MAX_PARSE_RETRIES; attempt += 1) {
|
|
12571
|
-
|
|
12572
|
-
|
|
12573
|
-
|
|
12574
|
-
|
|
12575
|
-
|
|
12576
|
-
|
|
12577
|
-
|
|
12578
|
-
...typeof brain.settings.maxTokens === "number" ? {
|
|
12579
|
-
maxTokens: brain.settings.maxTokens
|
|
12580
|
-
} : {}
|
|
12779
|
+
await emitProgress({
|
|
12780
|
+
type: "task-note",
|
|
12781
|
+
sessionId,
|
|
12782
|
+
taskId: task.id,
|
|
12783
|
+
agentRole: task.agentRole,
|
|
12784
|
+
step,
|
|
12785
|
+
message: attempt === 0 ? "Thinking through the next safe action." : `Re-asking the model for a stricter machine-readable action (${attempt + 1}/${MAX_PARSE_RETRIES + 1}).`
|
|
12581
12786
|
});
|
|
12787
|
+
let response;
|
|
12788
|
+
try {
|
|
12789
|
+
response = await brain.client.generateText({
|
|
12790
|
+
model: brain.model,
|
|
12791
|
+
systemPrompt: buildSystemPrompt(agent, task, request, allowedTools, options.plan),
|
|
12792
|
+
messages,
|
|
12793
|
+
responseFormat: "json_object",
|
|
12794
|
+
...typeof brain.settings.temperature === "number" ? {
|
|
12795
|
+
temperature: brain.settings.temperature
|
|
12796
|
+
} : {},
|
|
12797
|
+
...typeof brain.settings.maxTokens === "number" ? {
|
|
12798
|
+
maxTokens: brain.settings.maxTokens
|
|
12799
|
+
} : {}
|
|
12800
|
+
});
|
|
12801
|
+
} catch (error) {
|
|
12802
|
+
transcript.push({
|
|
12803
|
+
step,
|
|
12804
|
+
response: "",
|
|
12805
|
+
runtimeNote: `Model request failed: ${error instanceof Error ? error.message : String(error)}`
|
|
12806
|
+
});
|
|
12807
|
+
const transcriptPath2 = await writeTranscriptArtifact(request.cwd, sessionId, task.id, transcript);
|
|
12808
|
+
artifacts.add(transcriptPath2);
|
|
12809
|
+
return {
|
|
12810
|
+
status: "blocked",
|
|
12811
|
+
summary: `Model request failed before ${task.id} could choose a safe action: ${error instanceof Error ? error.message : String(error)}`,
|
|
12812
|
+
toolResults,
|
|
12813
|
+
artifacts: Array.from(artifacts),
|
|
12814
|
+
usage: usageTotals
|
|
12815
|
+
};
|
|
12816
|
+
}
|
|
12582
12817
|
if (options.signal?.aborted) {
|
|
12583
12818
|
const transcriptPath2 = await writeTranscriptArtifact(request.cwd, sessionId, task.id, transcript);
|
|
12584
12819
|
artifacts.add(transcriptPath2);
|
|
@@ -12607,11 +12842,33 @@ var AutonomousTaskExecutor = class {
|
|
|
12607
12842
|
} : {}
|
|
12608
12843
|
});
|
|
12609
12844
|
try {
|
|
12610
|
-
parsedAction = actionSchema.parse(
|
|
12845
|
+
parsedAction = actionSchema.parse(extractJsonObjectish(response.text, "model response"));
|
|
12611
12846
|
break;
|
|
12612
12847
|
} catch (error) {
|
|
12848
|
+
const parseSummary = summarizeModelActionError(error);
|
|
12849
|
+
transcript.push({
|
|
12850
|
+
step,
|
|
12851
|
+
response: response.text,
|
|
12852
|
+
runtimeNote: `${parseSummary} ${error instanceof Error ? error.message : String(error)}`
|
|
12853
|
+
});
|
|
12613
12854
|
if (attempt === MAX_PARSE_RETRIES) {
|
|
12614
|
-
|
|
12855
|
+
const transcriptPath2 = await writeTranscriptArtifact(request.cwd, sessionId, task.id, transcript);
|
|
12856
|
+
artifacts.add(transcriptPath2);
|
|
12857
|
+
await emitProgress({
|
|
12858
|
+
type: "task-note",
|
|
12859
|
+
sessionId,
|
|
12860
|
+
taskId: task.id,
|
|
12861
|
+
agentRole: task.agentRole,
|
|
12862
|
+
step,
|
|
12863
|
+
message: "Model stayed out of structured mode after multiple retries."
|
|
12864
|
+
});
|
|
12865
|
+
return {
|
|
12866
|
+
status: "blocked",
|
|
12867
|
+
summary: `${parseSummary} The task stopped before a safe tool action could be chosen.`,
|
|
12868
|
+
toolResults,
|
|
12869
|
+
artifacts: Array.from(artifacts),
|
|
12870
|
+
usage: usageTotals
|
|
12871
|
+
};
|
|
12615
12872
|
}
|
|
12616
12873
|
await emitProgress({
|
|
12617
12874
|
type: "task-note",
|
|
@@ -12619,7 +12876,7 @@ var AutonomousTaskExecutor = class {
|
|
|
12619
12876
|
taskId: task.id,
|
|
12620
12877
|
agentRole: task.agentRole,
|
|
12621
12878
|
step,
|
|
12622
|
-
message: `
|
|
12879
|
+
message: `Reformatting model output to match Kimbho's action schema (${attempt + 1}/${MAX_PARSE_RETRIES + 1}).`
|
|
12623
12880
|
});
|
|
12624
12881
|
messages.push({
|
|
12625
12882
|
role: "assistant",
|
|
@@ -12628,9 +12885,11 @@ var AutonomousTaskExecutor = class {
|
|
|
12628
12885
|
messages.push({
|
|
12629
12886
|
role: "user",
|
|
12630
12887
|
content: [
|
|
12631
|
-
"Your previous response
|
|
12888
|
+
"Your previous response did not parse as a valid JSON object.",
|
|
12889
|
+
parseSummary,
|
|
12632
12890
|
error instanceof Error ? error.message : String(error),
|
|
12633
|
-
"Return exactly one valid JSON object matching the required action schema."
|
|
12891
|
+
"Return exactly one valid JSON object matching the required action schema.",
|
|
12892
|
+
"Do not include prose, markdown fences, comments, or trailing explanations."
|
|
12634
12893
|
].join("\n")
|
|
12635
12894
|
});
|
|
12636
12895
|
}
|
|
@@ -13147,16 +13406,6 @@ function createPlan(input) {
|
|
|
13147
13406
|
var DEFAULT_PLANNER_TEMPERATURE = 0.1;
|
|
13148
13407
|
var DEFAULT_PLANNER_MAX_TOKENS = 2400;
|
|
13149
13408
|
var MAX_REPLAN_CONTEXT_CHARS = 8e3;
|
|
13150
|
-
function extractJsonObject2(raw) {
|
|
13151
|
-
const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
13152
|
-
const candidate = (fenced?.[1] ?? raw).trim();
|
|
13153
|
-
const firstBrace = candidate.indexOf("{");
|
|
13154
|
-
const lastBrace = candidate.lastIndexOf("}");
|
|
13155
|
-
if (firstBrace === -1 || lastBrace === -1 || lastBrace <= firstBrace) {
|
|
13156
|
-
throw new Error("Expected a JSON object in the planner response.");
|
|
13157
|
-
}
|
|
13158
|
-
return JSON.parse(candidate.slice(firstBrace, lastBrace + 1));
|
|
13159
|
-
}
|
|
13160
13409
|
function validateTaskGraph(plan) {
|
|
13161
13410
|
const taskIds = /* @__PURE__ */ new Set();
|
|
13162
13411
|
for (const task of flattenPlanTasks(plan)) {
|
|
@@ -13300,7 +13549,7 @@ async function generatePlannerPlan(request, seedPlan, invocation, userPrompt, pr
|
|
|
13300
13549
|
temperature: invocation.temperature ?? DEFAULT_PLANNER_TEMPERATURE,
|
|
13301
13550
|
maxTokens: invocation.maxTokens ?? DEFAULT_PLANNER_MAX_TOKENS
|
|
13302
13551
|
});
|
|
13303
|
-
const parsed =
|
|
13552
|
+
const parsed = extractJsonObjectish(response.text, "planner response");
|
|
13304
13553
|
const candidatePlan = KimbhoPlanSchema.parse(parsed);
|
|
13305
13554
|
const normalized = normalizeCandidatePlan(request, seedPlan, candidatePlan, true, preserveStatusesFrom);
|
|
13306
13555
|
return {
|
|
@@ -13318,7 +13567,7 @@ async function generatePlannerPlan(request, seedPlan, invocation, userPrompt, pr
|
|
|
13318
13567
|
return {
|
|
13319
13568
|
plan: preserveStatusesFrom ? preserveTaskStatuses(preserveStatusesFrom, seedPlan) : seedPlan,
|
|
13320
13569
|
source: "fallback",
|
|
13321
|
-
warning:
|
|
13570
|
+
warning: summarizeStructuredOutputError("Planner", error),
|
|
13322
13571
|
...invocation.modelLabel ? {
|
|
13323
13572
|
modelLabel: invocation.modelLabel
|
|
13324
13573
|
} : {}
|
|
@@ -13361,16 +13610,6 @@ function truncate2(value, maxChars = MAX_EXPANSION_CONTEXT_CHARS) {
|
|
|
13361
13610
|
return `${value.slice(0, maxChars)}
|
|
13362
13611
|
... [truncated]`;
|
|
13363
13612
|
}
|
|
13364
|
-
function extractJsonObject3(raw) {
|
|
13365
|
-
const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
13366
|
-
const candidate = (fenced?.[1] ?? raw).trim();
|
|
13367
|
-
const firstBrace = candidate.indexOf("{");
|
|
13368
|
-
const lastBrace = candidate.lastIndexOf("}");
|
|
13369
|
-
if (firstBrace === -1 || lastBrace === -1 || lastBrace <= firstBrace) {
|
|
13370
|
-
throw new Error("Expected a JSON object in the task expansion response.");
|
|
13371
|
-
}
|
|
13372
|
-
return JSON.parse(candidate.slice(firstBrace, lastBrace + 1));
|
|
13373
|
-
}
|
|
13374
13613
|
function slugify(value) {
|
|
13375
13614
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
|
|
13376
13615
|
}
|
|
@@ -13676,7 +13915,7 @@ async function generateExpandedTasks(request, plan, task, invocation, repoSummar
|
|
|
13676
13915
|
temperature: invocation.temperature ?? DEFAULT_EXPANDER_TEMPERATURE,
|
|
13677
13916
|
maxTokens: invocation.maxTokens ?? DEFAULT_EXPANDER_MAX_TOKENS
|
|
13678
13917
|
});
|
|
13679
|
-
const parsed =
|
|
13918
|
+
const parsed = extractJsonObjectish(response.text, "task expansion response");
|
|
13680
13919
|
const payload = TaskExpansionPayloadSchema.parse(parsed);
|
|
13681
13920
|
const normalizedTasks = normalizeExpandedTasks(plan, task, payload, "model");
|
|
13682
13921
|
const note = `Planner expanded ${task.id} into ${normalizedTasks.length} subtasks via ${invocation.modelLabel ?? "planner model"}: ${normalizedTasks.map((candidate) => candidate.id).join(", ")}.`;
|
|
@@ -13715,7 +13954,7 @@ async function generateExpandedTasks(request, plan, task, invocation, repoSummar
|
|
|
13715
13954
|
expandedTaskId: task.id,
|
|
13716
13955
|
createdTaskIds: fallbackTasks.map((candidate) => candidate.id),
|
|
13717
13956
|
note,
|
|
13718
|
-
warning:
|
|
13957
|
+
warning: summarizeStructuredOutputError("Planner task expansion", error),
|
|
13719
13958
|
...invocation.modelLabel ? {
|
|
13720
13959
|
modelLabel: invocation.modelLabel
|
|
13721
13960
|
} : {}
|
|
@@ -15570,6 +15809,7 @@ var ExecutionOrchestrator = class {
|
|
|
15570
15809
|
input.systemPrompt
|
|
15571
15810
|
].filter(Boolean).join("\n\n"),
|
|
15572
15811
|
userPrompt: input.userPrompt,
|
|
15812
|
+
responseFormat: "json_object",
|
|
15573
15813
|
...typeof input.temperature === "number" ? {
|
|
15574
15814
|
temperature: input.temperature
|
|
15575
15815
|
} : {},
|
|
@@ -15587,7 +15827,7 @@ var ExecutionOrchestrator = class {
|
|
|
15587
15827
|
if (result.warning) {
|
|
15588
15828
|
return {
|
|
15589
15829
|
plan,
|
|
15590
|
-
note:
|
|
15830
|
+
note: `${result.warning} Keeping the current task graph.`
|
|
15591
15831
|
};
|
|
15592
15832
|
}
|
|
15593
15833
|
return {
|
|
@@ -15653,6 +15893,7 @@ var ExecutionOrchestrator = class {
|
|
|
15653
15893
|
input.systemPrompt
|
|
15654
15894
|
].filter(Boolean).join("\n\n"),
|
|
15655
15895
|
userPrompt: input.userPrompt,
|
|
15896
|
+
responseFormat: "json_object",
|
|
15656
15897
|
...typeof input.temperature === "number" ? {
|
|
15657
15898
|
temperature: input.temperature
|
|
15658
15899
|
} : {},
|
|
@@ -16330,6 +16571,7 @@ async function generatePlanForRequest(request) {
|
|
|
16330
16571
|
input.systemPrompt
|
|
16331
16572
|
].filter(Boolean).join("\n\n"),
|
|
16332
16573
|
userPrompt: input.userPrompt,
|
|
16574
|
+
responseFormat: "json_object",
|
|
16333
16575
|
...typeof input.temperature === "number" ? {
|
|
16334
16576
|
temperature: input.temperature
|
|
16335
16577
|
} : {},
|
|
@@ -16359,7 +16601,7 @@ function renderPlanGenerationNotes(result) {
|
|
|
16359
16601
|
lines.push(`planner tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out`);
|
|
16360
16602
|
}
|
|
16361
16603
|
if (result.warning) {
|
|
16362
|
-
lines.push(`planner note: ${result.warning}`);
|
|
16604
|
+
lines.push(`planner note: ${result.source === "fallback" ? `${result.warning} Using the safe default plan.` : result.warning}`);
|
|
16363
16605
|
}
|
|
16364
16606
|
return lines;
|
|
16365
16607
|
}
|
|
@@ -17446,6 +17688,83 @@ function renderEventType(type) {
|
|
|
17446
17688
|
function renderInlineMarkdown(value) {
|
|
17447
17689
|
return value.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, url) => `${color(CYAN, label)} ${color(DIM, `(${url})`)}`).replace(/`([^`]+)`/g, (_match, code) => color(CYAN, code)).replace(/\*\*([^*]+)\*\*/g, (_match, boldText) => color(BOLD, boldText)).replace(/\*([^*]+)\*/g, (_match, italicText) => color(DIM, italicText));
|
|
17448
17690
|
}
|
|
17691
|
+
function hashString(value) {
|
|
17692
|
+
let hash = 0;
|
|
17693
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
17694
|
+
hash = (hash << 5) - hash + value.charCodeAt(index);
|
|
17695
|
+
hash |= 0;
|
|
17696
|
+
}
|
|
17697
|
+
return Math.abs(hash);
|
|
17698
|
+
}
|
|
17699
|
+
function pickStatusLabel(message) {
|
|
17700
|
+
const lower = message.toLowerCase();
|
|
17701
|
+
const choose = (labels) => labels[hashString(message) % labels.length];
|
|
17702
|
+
if (lower.includes("synthesizing") || lower.includes("architecture brief")) {
|
|
17703
|
+
return choose([
|
|
17704
|
+
"planning",
|
|
17705
|
+
"mapping",
|
|
17706
|
+
"sketching"
|
|
17707
|
+
]);
|
|
17708
|
+
}
|
|
17709
|
+
if (lower.includes("repo context") || lower.includes("repo analysis") || lower.includes("deterministic repo context")) {
|
|
17710
|
+
return choose([
|
|
17711
|
+
"orienting",
|
|
17712
|
+
"surveying",
|
|
17713
|
+
"mapping"
|
|
17714
|
+
]);
|
|
17715
|
+
}
|
|
17716
|
+
if (lower.includes("using ") && lower.includes(" via ")) {
|
|
17717
|
+
return choose([
|
|
17718
|
+
"warming up",
|
|
17719
|
+
"locking in",
|
|
17720
|
+
"switching gears"
|
|
17721
|
+
]);
|
|
17722
|
+
}
|
|
17723
|
+
if (lower.includes("reformatting model output") || lower.includes("structured mode")) {
|
|
17724
|
+
return choose([
|
|
17725
|
+
"reformatting",
|
|
17726
|
+
"straightening",
|
|
17727
|
+
"coaxing"
|
|
17728
|
+
]);
|
|
17729
|
+
}
|
|
17730
|
+
if (lower.includes("expanded") || lower.includes("revised the task graph") || lower.includes("keeping the current task graph")) {
|
|
17731
|
+
return choose([
|
|
17732
|
+
"replanning",
|
|
17733
|
+
"reshaping",
|
|
17734
|
+
"rerouting"
|
|
17735
|
+
]);
|
|
17736
|
+
}
|
|
17737
|
+
if (lower.includes("approval")) {
|
|
17738
|
+
return choose([
|
|
17739
|
+
"waiting",
|
|
17740
|
+
"holding",
|
|
17741
|
+
"pausing"
|
|
17742
|
+
]);
|
|
17743
|
+
}
|
|
17744
|
+
if (lower.includes("integration") || lower.includes("worktree")) {
|
|
17745
|
+
return choose([
|
|
17746
|
+
"reconciling",
|
|
17747
|
+
"merging",
|
|
17748
|
+
"untangling"
|
|
17749
|
+
]);
|
|
17750
|
+
}
|
|
17751
|
+
return choose([
|
|
17752
|
+
"thinking",
|
|
17753
|
+
"musing",
|
|
17754
|
+
"assembling",
|
|
17755
|
+
"checking"
|
|
17756
|
+
]);
|
|
17757
|
+
}
|
|
17758
|
+
function simplifyTaskNote(message) {
|
|
17759
|
+
const trimmed = message.trim();
|
|
17760
|
+
if (trimmed.startsWith("Using ") && trimmed.includes(" via ")) {
|
|
17761
|
+
return trimmed.replace(/^Using\s+/, "");
|
|
17762
|
+
}
|
|
17763
|
+
if (trimmed.startsWith("Reformatting model output")) {
|
|
17764
|
+
return trimmed;
|
|
17765
|
+
}
|
|
17766
|
+
return trimmed;
|
|
17767
|
+
}
|
|
17449
17768
|
function renderTerminalMarkdown(markdown) {
|
|
17450
17769
|
const lines = markdown.replace(/\r\n/g, "\n").split("\n");
|
|
17451
17770
|
const output = [];
|
|
@@ -17658,7 +17977,7 @@ function renderLiveExecutionEvent(event) {
|
|
|
17658
17977
|
];
|
|
17659
17978
|
case "task-note":
|
|
17660
17979
|
return [
|
|
17661
|
-
`${color(DIM,
|
|
17980
|
+
`${color(DIM, `${pickStatusLabel(event.message)}...`)} ${simplifyTaskNote(event.message)}`
|
|
17662
17981
|
];
|
|
17663
17982
|
case "approval-requested":
|
|
17664
17983
|
return [
|