zelari-code 1.8.0 → 1.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/app.js +44 -24
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/components/LiveRegion.js +9 -1
- package/dist/cli/components/LiveRegion.js.map +1 -1
- package/dist/cli/components/Sidebar.js +38 -7
- package/dist/cli/components/Sidebar.js.map +1 -1
- package/dist/cli/components/brandArt.js +62 -0
- package/dist/cli/components/brandArt.js.map +1 -0
- package/dist/cli/hooks/chatStats.js +7 -0
- package/dist/cli/hooks/chatStats.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +58 -28
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/hooks/useTerminalSize.js +17 -10
- package/dist/cli/hooks/useTerminalSize.js.map +1 -1
- package/dist/cli/main.bundled.js +496 -130
- package/dist/cli/main.bundled.js.map +4 -4
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -16880,6 +16880,25 @@ var init_walk = __esm({
|
|
|
16880
16880
|
// packages/core/dist/core/tools/builtin/search.js
|
|
16881
16881
|
import { promises as fs6 } from "node:fs";
|
|
16882
16882
|
import path7 from "node:path";
|
|
16883
|
+
function coerceStringList(value, fallback) {
|
|
16884
|
+
if (value === void 0 || value === null)
|
|
16885
|
+
return fallback;
|
|
16886
|
+
if (Array.isArray(value)) {
|
|
16887
|
+
const cleaned = value.filter((x) => typeof x === "string" && x.trim().length > 0);
|
|
16888
|
+
return cleaned.length > 0 ? cleaned : fallback;
|
|
16889
|
+
}
|
|
16890
|
+
if (typeof value === "string") {
|
|
16891
|
+
const s = value.trim();
|
|
16892
|
+
if (!s)
|
|
16893
|
+
return fallback;
|
|
16894
|
+
if (s.includes(",") && !s.includes("{")) {
|
|
16895
|
+
const parts = s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
16896
|
+
return parts.length > 0 ? parts : fallback;
|
|
16897
|
+
}
|
|
16898
|
+
return [s];
|
|
16899
|
+
}
|
|
16900
|
+
return fallback;
|
|
16901
|
+
}
|
|
16883
16902
|
async function searchFile(absPath, relPath, regex, contextLines, remainingSlots) {
|
|
16884
16903
|
let buf;
|
|
16885
16904
|
try {
|
|
@@ -16923,13 +16942,14 @@ async function isDirectory(p3) {
|
|
|
16923
16942
|
return false;
|
|
16924
16943
|
}
|
|
16925
16944
|
}
|
|
16926
|
-
var GrepContentArgsSchema, grepContentTool;
|
|
16945
|
+
var stringOrStringArray, GrepContentArgsSchema, grepContentTool;
|
|
16927
16946
|
var init_search = __esm({
|
|
16928
16947
|
"packages/core/dist/core/tools/builtin/search.js"() {
|
|
16929
16948
|
"use strict";
|
|
16930
16949
|
init_zod();
|
|
16931
16950
|
init_toolTypes();
|
|
16932
16951
|
init_walk();
|
|
16952
|
+
stringOrStringArray = external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]);
|
|
16933
16953
|
GrepContentArgsSchema = external_exports.object({
|
|
16934
16954
|
/** File OR directory to search (relative to cwd or absolute). */
|
|
16935
16955
|
path: external_exports.string().min(1),
|
|
@@ -16940,21 +16960,22 @@ var init_search = __esm({
|
|
|
16940
16960
|
/** Max matches returned (total matches still counted). */
|
|
16941
16961
|
maxMatches: external_exports.number().int().positive().max(1e3).default(50),
|
|
16942
16962
|
/**
|
|
16943
|
-
* Glob
|
|
16963
|
+
* Glob pattern(s) to INCLUDE when path is a directory.
|
|
16964
|
+
* Accepts a string OR string[] (models often emit a bare string).
|
|
16944
16965
|
* Default ['*'] = all files. Ignored when path is a file.
|
|
16945
16966
|
*/
|
|
16946
|
-
include:
|
|
16967
|
+
include: stringOrStringArray.optional().default(["*"]),
|
|
16947
16968
|
/**
|
|
16948
|
-
* Glob
|
|
16949
|
-
*
|
|
16969
|
+
* Glob pattern(s) to EXCLUDE when path is a directory.
|
|
16970
|
+
* Accepts a string OR string[]. Defaults to common noise dirs.
|
|
16950
16971
|
*/
|
|
16951
|
-
exclude:
|
|
16972
|
+
exclude: stringOrStringArray.optional().default(DEFAULT_EXCLUDES),
|
|
16952
16973
|
/** Max recursion depth when path is a directory (default 8). */
|
|
16953
16974
|
maxDepth: external_exports.number().int().positive().max(15).default(8)
|
|
16954
16975
|
});
|
|
16955
16976
|
grepContentTool = {
|
|
16956
16977
|
name: "grep_content",
|
|
16957
|
-
description:
|
|
16978
|
+
description: 'Regex search for content in a file OR recursively in a directory. When path is a directory, include/exclude globs filter which files are searched (default: all files, excluding node_modules/dist/.git/etc.). include/exclude accept a single glob string (e.g. "*.ts") OR an array of globs. Returns matches with line numbers and surrounding context.',
|
|
16958
16979
|
permissions: ["read"],
|
|
16959
16980
|
timeoutMs: 3e4,
|
|
16960
16981
|
inputSchema: GrepContentArgsSchema,
|
|
@@ -16962,6 +16983,8 @@ var init_search = __esm({
|
|
|
16962
16983
|
try {
|
|
16963
16984
|
const absRoot = path7.isAbsolute(args.path) ? args.path : path7.join(ctx.cwd, args.path);
|
|
16964
16985
|
const regex = new RegExp(args.pattern, "gm");
|
|
16986
|
+
const include = coerceStringList(args.include, ["*"]);
|
|
16987
|
+
const exclude = coerceStringList(args.exclude, DEFAULT_EXCLUDES);
|
|
16965
16988
|
if (!await isDirectory(absRoot)) {
|
|
16966
16989
|
const single = await searchFile(absRoot, args.path, regex, args.contextLines, args.maxMatches);
|
|
16967
16990
|
return typedOk({
|
|
@@ -16973,8 +16996,8 @@ var init_search = __esm({
|
|
|
16973
16996
|
});
|
|
16974
16997
|
}
|
|
16975
16998
|
const allEntries = [];
|
|
16976
|
-
await walk(absRoot, "", 0, args.maxDepth,
|
|
16977
|
-
const matchedFiles = filterByInclude(allEntries,
|
|
16999
|
+
await walk(absRoot, "", 0, args.maxDepth, exclude, allEntries, ctx.signal);
|
|
17000
|
+
const matchedFiles = filterByInclude(allEntries, include);
|
|
16978
17001
|
const allMatches = [];
|
|
16979
17002
|
let totalMatches = 0;
|
|
16980
17003
|
let truncated = false;
|
|
@@ -18271,20 +18294,24 @@ The council shares a context window across turns. Follow these rules:
|
|
|
18271
18294
|
type: "tool-usage-guidelines",
|
|
18272
18295
|
title: "Tool Usage",
|
|
18273
18296
|
priority: 60,
|
|
18274
|
-
content:
|
|
18275
|
-
|
|
18276
|
-
|
|
18277
|
-
-
|
|
18278
|
-
-
|
|
18279
|
-
-
|
|
18280
|
-
-
|
|
18281
|
-
|
|
18282
|
-
|
|
18283
|
-
|
|
18284
|
-
|
|
18285
|
-
|
|
18286
|
-
|
|
18287
|
-
|
|
18297
|
+
content: [
|
|
18298
|
+
"# Tool Usage Guidelines",
|
|
18299
|
+
"",
|
|
18300
|
+
"- Use tools to create or modify durable state (tasks, ideas, documents, mind maps, milestones).",
|
|
18301
|
+
"- Tool calls go in a dedicated block at the end of your response using the exact format documented below.",
|
|
18302
|
+
"- Only use tools listed in the AVAILABLE TOOLS section. Never invent tool names.",
|
|
18303
|
+
"- Pass arguments as JSON. Required parameters must be present.",
|
|
18304
|
+
"- One tool call per entry. Multiple entries are allowed in a single block.",
|
|
18305
|
+
"",
|
|
18306
|
+
"Format (ONE JSON array only - never multiple arrays stacked):",
|
|
18307
|
+
"```",
|
|
18308
|
+
"---TOOLS---",
|
|
18309
|
+
'[{"name":"<toolName>","args":{"key":"value"}},{"name":"<toolName2>","args":{"key":"value"}}]',
|
|
18310
|
+
"---END---",
|
|
18311
|
+
"```",
|
|
18312
|
+
"Rules: valid JSON; escape newlines inside strings as \\n; do NOT stack separate",
|
|
18313
|
+
"JSON arrays (one tool per array) - put every call in the SAME outer array."
|
|
18314
|
+
].join("\n")
|
|
18288
18315
|
},
|
|
18289
18316
|
{
|
|
18290
18317
|
type: "custom",
|
|
@@ -18944,18 +18971,131 @@ function normalizeTextToolArgs(name, args) {
|
|
|
18944
18971
|
}
|
|
18945
18972
|
function parseTextToolCalls(text) {
|
|
18946
18973
|
const m = /---TOOLS---\s*([\s\S]*?)---END---/.exec(text);
|
|
18947
|
-
if (
|
|
18948
|
-
|
|
18974
|
+
if (m?.[1]) {
|
|
18975
|
+
let body = m[1].trim();
|
|
18976
|
+
body = body.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim();
|
|
18977
|
+
const candidates = [body];
|
|
18978
|
+
if (/\]\s*\[/.test(body)) {
|
|
18979
|
+
candidates.push(body.replace(/\]\s*\[/g, ","));
|
|
18980
|
+
}
|
|
18981
|
+
if (body.includes('\\"')) {
|
|
18982
|
+
candidates.push(body.replace(/\\"/g, '"'));
|
|
18983
|
+
if (/\]\s*\[/.test(body)) {
|
|
18984
|
+
candidates.push(body.replace(/\\"/g, '"').replace(/\]\s*\[/g, ","));
|
|
18985
|
+
}
|
|
18986
|
+
}
|
|
18987
|
+
for (const cand of candidates) {
|
|
18988
|
+
const items = tryParseToolArray(cand);
|
|
18989
|
+
if (items.length > 0)
|
|
18990
|
+
return items;
|
|
18991
|
+
}
|
|
18992
|
+
const arrays = extractJsonArrays(body);
|
|
18993
|
+
if (arrays.length > 1) {
|
|
18994
|
+
const merged = [];
|
|
18995
|
+
for (const a of arrays) {
|
|
18996
|
+
merged.push(...tryParseToolArray(a));
|
|
18997
|
+
}
|
|
18998
|
+
if (merged.length > 0)
|
|
18999
|
+
return merged;
|
|
19000
|
+
}
|
|
19001
|
+
const objs = extractToolObjects(body);
|
|
19002
|
+
if (objs.length > 0)
|
|
19003
|
+
return objs;
|
|
19004
|
+
}
|
|
19005
|
+
const mini = parseMinimaxStyleToolCalls(text);
|
|
19006
|
+
if (mini.length > 0)
|
|
19007
|
+
return mini;
|
|
19008
|
+
return [];
|
|
19009
|
+
}
|
|
19010
|
+
function parseMinimaxStyleToolCalls(text) {
|
|
19011
|
+
const out = [];
|
|
19012
|
+
const normalized = text.replace(/\]\s*<\s*\]\s*minimax\s*\[\s*>\s*\[</gi, "<minimax:").replace(/\]\s*<\s*\]\s*minimax\s*\[\s*>/gi, "<minimax:").replace(/minimax\s*\[\s*>\s*\[</gi, "minimax:").replace(/\]\s*</g, "<");
|
|
19013
|
+
const blockRe = /<(?:minimax:)?tool_call[^>]*>([\s\S]*?)<\/(?:minimax:)?tool_call>|<invoke\s+name=["']([^"']+)["'][^>]*>([\s\S]*?)(?:<\/invoke>|(?=<invoke\s+name=)|$)/gi;
|
|
19014
|
+
let match;
|
|
19015
|
+
while ((match = blockRe.exec(normalized)) !== null) {
|
|
19016
|
+
if (match[2]) {
|
|
19017
|
+
out.push({ name: match[2], args: parseLooseArgs(match[3] ?? "") });
|
|
19018
|
+
} else if (match[1]) {
|
|
19019
|
+
const inner = match[1];
|
|
19020
|
+
const inv = /invoke\s+name=["']([^"']+)["']/i.exec(inner);
|
|
19021
|
+
if (inv) {
|
|
19022
|
+
out.push({ name: inv[1], args: parseLooseArgs(inner) });
|
|
19023
|
+
} else {
|
|
19024
|
+
const jsonish = tryParseToolArray(inner.trim());
|
|
19025
|
+
if (jsonish.length)
|
|
19026
|
+
out.push(...jsonish);
|
|
19027
|
+
else {
|
|
19028
|
+
const objs = extractToolObjects(inner);
|
|
19029
|
+
if (objs.length)
|
|
19030
|
+
out.push(...objs);
|
|
19031
|
+
}
|
|
19032
|
+
}
|
|
19033
|
+
}
|
|
19034
|
+
}
|
|
19035
|
+
if (out.length === 0) {
|
|
19036
|
+
const loose = /invoke\s+name=["']([^"']+)["']([\s\S]*?)(?=invoke\s+name=|$)/gi;
|
|
19037
|
+
let lm;
|
|
19038
|
+
while ((lm = loose.exec(normalized)) !== null) {
|
|
19039
|
+
const name = lm[1];
|
|
19040
|
+
const args = parseLooseArgs(lm[2] ?? "");
|
|
19041
|
+
if (name)
|
|
19042
|
+
out.push({ name, args });
|
|
19043
|
+
}
|
|
19044
|
+
}
|
|
19045
|
+
return out;
|
|
19046
|
+
}
|
|
19047
|
+
function parseLooseArgs(body) {
|
|
19048
|
+
const args = {};
|
|
19049
|
+
const paramTag = /(?:parameter\s+name=|<\s*)["']?([a-zA-Z_][\w]*)["']?\s*(?:>|=\s*["']?)([^<\]\n]+)/gi;
|
|
19050
|
+
let m;
|
|
19051
|
+
while ((m = paramTag.exec(body)) !== null) {
|
|
19052
|
+
const key = m[1];
|
|
19053
|
+
if (key === "invoke" || key === "minimax" || key === "tool_call")
|
|
19054
|
+
continue;
|
|
19055
|
+
args[key] = m[2].replace(/^["']|["']$/g, "").trim();
|
|
19056
|
+
}
|
|
19057
|
+
const lineRe = /(?:^|[\s\[>])([a-zA-Z_][\w]*)\s*[>:=]\s*([^\n<\]]+)/g;
|
|
19058
|
+
while ((m = lineRe.exec(body)) !== null) {
|
|
19059
|
+
const key = m[1];
|
|
19060
|
+
if (args[key] !== void 0)
|
|
19061
|
+
continue;
|
|
19062
|
+
if (["invoke", "name", "minimax", "tool_call", "parameter"].includes(key))
|
|
19063
|
+
continue;
|
|
19064
|
+
args[key] = m[2].replace(/^["']|["']$/g, "").trim();
|
|
19065
|
+
}
|
|
19066
|
+
if (Object.keys(args).length === 0) {
|
|
19067
|
+
const brace = body.indexOf("{");
|
|
19068
|
+
if (brace >= 0) {
|
|
19069
|
+
try {
|
|
19070
|
+
const parsed = JSON.parse(body.slice(brace));
|
|
19071
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19072
|
+
return parsed;
|
|
19073
|
+
}
|
|
19074
|
+
} catch {
|
|
19075
|
+
}
|
|
19076
|
+
}
|
|
19077
|
+
}
|
|
19078
|
+
return args;
|
|
19079
|
+
}
|
|
19080
|
+
function tryParseToolArray(raw) {
|
|
18949
19081
|
let parsed;
|
|
18950
19082
|
try {
|
|
18951
|
-
parsed = JSON.parse(
|
|
19083
|
+
parsed = JSON.parse(raw.trim());
|
|
18952
19084
|
} catch {
|
|
18953
19085
|
return [];
|
|
18954
19086
|
}
|
|
18955
|
-
if (!Array.isArray(parsed))
|
|
18956
|
-
|
|
19087
|
+
if (!Array.isArray(parsed)) {
|
|
19088
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19089
|
+
parsed = [parsed];
|
|
19090
|
+
} else {
|
|
19091
|
+
return [];
|
|
19092
|
+
}
|
|
19093
|
+
}
|
|
19094
|
+
return normalizeToolItems(parsed);
|
|
19095
|
+
}
|
|
19096
|
+
function normalizeToolItems(items) {
|
|
18957
19097
|
const out = [];
|
|
18958
|
-
for (const item of
|
|
19098
|
+
for (const item of items) {
|
|
18959
19099
|
if (item && typeof item === "object" && typeof item.name === "string") {
|
|
18960
19100
|
const rawArgs = item.args;
|
|
18961
19101
|
out.push({
|
|
@@ -18966,6 +19106,70 @@ function parseTextToolCalls(text) {
|
|
|
18966
19106
|
}
|
|
18967
19107
|
return out;
|
|
18968
19108
|
}
|
|
19109
|
+
function extractJsonArrays(text) {
|
|
19110
|
+
const out = [];
|
|
19111
|
+
let i = 0;
|
|
19112
|
+
while (i < text.length) {
|
|
19113
|
+
if (text[i] !== "[") {
|
|
19114
|
+
i++;
|
|
19115
|
+
continue;
|
|
19116
|
+
}
|
|
19117
|
+
let depth = 0;
|
|
19118
|
+
let inStr = false;
|
|
19119
|
+
let esc2 = false;
|
|
19120
|
+
let end = -1;
|
|
19121
|
+
for (let j = i; j < text.length; j++) {
|
|
19122
|
+
const c = text[j];
|
|
19123
|
+
if (inStr) {
|
|
19124
|
+
if (esc2)
|
|
19125
|
+
esc2 = false;
|
|
19126
|
+
else if (c === "\\")
|
|
19127
|
+
esc2 = true;
|
|
19128
|
+
else if (c === '"')
|
|
19129
|
+
inStr = false;
|
|
19130
|
+
continue;
|
|
19131
|
+
}
|
|
19132
|
+
if (c === '"') {
|
|
19133
|
+
inStr = true;
|
|
19134
|
+
continue;
|
|
19135
|
+
}
|
|
19136
|
+
if (c === "[")
|
|
19137
|
+
depth++;
|
|
19138
|
+
else if (c === "]") {
|
|
19139
|
+
depth--;
|
|
19140
|
+
if (depth === 0) {
|
|
19141
|
+
end = j;
|
|
19142
|
+
break;
|
|
19143
|
+
}
|
|
19144
|
+
}
|
|
19145
|
+
}
|
|
19146
|
+
if (end > i) {
|
|
19147
|
+
out.push(text.slice(i, end + 1));
|
|
19148
|
+
i = end + 1;
|
|
19149
|
+
} else {
|
|
19150
|
+
i++;
|
|
19151
|
+
}
|
|
19152
|
+
}
|
|
19153
|
+
return out;
|
|
19154
|
+
}
|
|
19155
|
+
function extractToolObjects(text) {
|
|
19156
|
+
const out = [];
|
|
19157
|
+
const re = /\{\s*"name"\s*:\s*"([^"]+)"\s*,\s*"args"\s*:\s*(\{[\s\S]*?\})\s*\}/g;
|
|
19158
|
+
let match;
|
|
19159
|
+
while ((match = re.exec(text)) !== null) {
|
|
19160
|
+
const name = match[1];
|
|
19161
|
+
let args = {};
|
|
19162
|
+
try {
|
|
19163
|
+
const parsed = JSON.parse(match[2]);
|
|
19164
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19165
|
+
args = parsed;
|
|
19166
|
+
}
|
|
19167
|
+
} catch {
|
|
19168
|
+
}
|
|
19169
|
+
out.push({ name, args });
|
|
19170
|
+
}
|
|
19171
|
+
return out;
|
|
19172
|
+
}
|
|
18969
19173
|
var AgentHarness;
|
|
18970
19174
|
var init_AgentHarness = __esm({
|
|
18971
19175
|
"packages/core/dist/core/AgentHarness.js"() {
|
|
@@ -18977,6 +19181,7 @@ var init_AgentHarness = __esm({
|
|
|
18977
19181
|
sessionId;
|
|
18978
19182
|
maxQueuedIterations;
|
|
18979
19183
|
maxToolLoopIterations;
|
|
19184
|
+
maxToolLoopHardCap;
|
|
18980
19185
|
cancelled = false;
|
|
18981
19186
|
activeController = null;
|
|
18982
19187
|
queue = [];
|
|
@@ -19002,6 +19207,9 @@ var init_AgentHarness = __esm({
|
|
|
19002
19207
|
this.sessionId = config2.sessionId ?? crypto.randomUUID();
|
|
19003
19208
|
this.maxQueuedIterations = config2.maxQueuedIterations ?? 3;
|
|
19004
19209
|
this.maxToolLoopIterations = config2.maxToolLoopIterations ?? 30;
|
|
19210
|
+
const soft = this.maxToolLoopIterations;
|
|
19211
|
+
const hardCfg = config2.maxToolLoopHardCap;
|
|
19212
|
+
this.maxToolLoopHardCap = typeof hardCfg === "number" && hardCfg > 0 ? Math.max(soft, hardCfg) : Math.max(soft * 3, soft + 60);
|
|
19005
19213
|
}
|
|
19006
19214
|
/**
|
|
19007
19215
|
* Snapshot of the live transcript (`this.config.messages`) as mutated
|
|
@@ -19278,7 +19486,11 @@ ${shared2.content}`,
|
|
|
19278
19486
|
this.emit(initialMsgEnd);
|
|
19279
19487
|
yield initialMsgEnd;
|
|
19280
19488
|
let toolLoopTurns = 0;
|
|
19281
|
-
|
|
19489
|
+
let softCap = this.maxToolLoopIterations;
|
|
19490
|
+
const hardCap = this.maxToolLoopHardCap;
|
|
19491
|
+
let extensions = 0;
|
|
19492
|
+
const maxExtensions = 8;
|
|
19493
|
+
while (!this.cancelled && !hadError && toolLoopTurns < softCap && initialFinishRef.value === "tool_calls") {
|
|
19282
19494
|
toolLoopTurns++;
|
|
19283
19495
|
const turnMessageId = crypto.randomUUID();
|
|
19284
19496
|
const msgStart = createBrainEvent("message_start", this.sessionId, { messageId: turnMessageId, role: "assistant", ...this.memberFields() });
|
|
@@ -19309,9 +19521,25 @@ ${shared2.content}`,
|
|
|
19309
19521
|
initialFinishRef.value = turnFinishRef.value;
|
|
19310
19522
|
if (hadError || this.cancelled)
|
|
19311
19523
|
break;
|
|
19524
|
+
if (initialFinishRef.value === "tool_calls" && toolLoopTurns >= softCap && toolLoopTurns < hardCap && extensions < maxExtensions) {
|
|
19525
|
+
extensions++;
|
|
19526
|
+
const chunk = this.maxToolLoopIterations;
|
|
19527
|
+
softCap = Math.min(hardCap, softCap + chunk);
|
|
19528
|
+
this.config.messages.push({
|
|
19529
|
+
role: "user",
|
|
19530
|
+
content: `[system] Soft tool budget reached (${toolLoopTurns}/${hardCap} hard). Work may be incomplete \u2014 CONTINUE with tools as needed to finish remaining steps. Prefer concrete progress over summarizing. Do not apologize for budgets.`
|
|
19531
|
+
});
|
|
19532
|
+
const note = createBrainEvent("error", this.sessionId, {
|
|
19533
|
+
severity: "recoverable",
|
|
19534
|
+
message: `Tool budget extended (${toolLoopTurns}\u2192${softCap}, hard ${hardCap}) to allow completion.`,
|
|
19535
|
+
code: "tool_budget_extended"
|
|
19536
|
+
});
|
|
19537
|
+
this.emit(note);
|
|
19538
|
+
yield note;
|
|
19539
|
+
}
|
|
19312
19540
|
}
|
|
19313
|
-
const
|
|
19314
|
-
if (!this.cancelled && !hadError && initialFinishRef.value === "tool_calls" &&
|
|
19541
|
+
const hitHardCap = toolLoopTurns >= hardCap || toolLoopTurns >= softCap && softCap >= hardCap;
|
|
19542
|
+
if (!this.cancelled && !hadError && initialFinishRef.value === "tool_calls" && hitHardCap) {
|
|
19315
19543
|
yield* this.runFinalAnswerTurn();
|
|
19316
19544
|
}
|
|
19317
19545
|
let turns = 0;
|
|
@@ -19475,13 +19703,14 @@ ${cached2}`
|
|
|
19475
19703
|
pendingNativeTools.length = 0;
|
|
19476
19704
|
}
|
|
19477
19705
|
const textTools = parseTextToolCalls(turnText);
|
|
19478
|
-
const
|
|
19479
|
-
|
|
19480
|
-
|
|
19481
|
-
|
|
19706
|
+
const toolsToRun = textTools.filter((tt) => {
|
|
19707
|
+
const key = hashToolCall(tt.name, tt.args);
|
|
19708
|
+
return !turnToolCalls.some((n) => hashToolCall(n.name, n.args) === key);
|
|
19709
|
+
});
|
|
19710
|
+
if ((/---TOOLS---/.test(turnText) || /minimax|invoke\s+name=/i.test(turnText)) && textTools.length === 0) {
|
|
19482
19711
|
const parseErr = createBrainEvent("error", this.sessionId, {
|
|
19483
19712
|
severity: "recoverable",
|
|
19484
|
-
message: "Found
|
|
19713
|
+
message: "Found text-format tool block but parse failed; tool calls were not executed. Prefer native tool_call, or ---TOOLS--- with ONE valid JSON array.",
|
|
19485
19714
|
code: "text_tools_parse_failed"
|
|
19486
19715
|
});
|
|
19487
19716
|
this.emit(parseErr);
|
|
@@ -19613,7 +19842,7 @@ ${cached2}`
|
|
|
19613
19842
|
yield msgStart;
|
|
19614
19843
|
this.config.messages.push({
|
|
19615
19844
|
role: "user",
|
|
19616
|
-
content: "[system]
|
|
19845
|
+
content: "[system] Hard tool-iteration ceiling reached. Stop calling tools and give a clear status: what is DONE, what remains, and the exact next steps. Use what you already gathered. Do not apologize for the tools."
|
|
19617
19846
|
});
|
|
19618
19847
|
let totalLength = 0;
|
|
19619
19848
|
const finishRef = { value: "stop" };
|
|
@@ -19812,6 +20041,7 @@ __export(harness_exports, {
|
|
|
19812
20041
|
SessionJsonlWriter: () => SessionJsonlWriter,
|
|
19813
20042
|
hashToolCall: () => hashToolCall,
|
|
19814
20043
|
normalizeTextToolArgs: () => normalizeTextToolArgs,
|
|
20044
|
+
parseMinimaxStyleToolCalls: () => parseMinimaxStyleToolCalls,
|
|
19815
20045
|
parseTextToolCalls: () => parseTextToolCalls,
|
|
19816
20046
|
readSession: () => readSession,
|
|
19817
20047
|
wrapLegacyStream: () => wrapLegacyStream
|
|
@@ -23934,11 +24164,19 @@ function parseClarificationRequest(text) {
|
|
|
23934
24164
|
}
|
|
23935
24165
|
}
|
|
23936
24166
|
function parseThinking(text) {
|
|
23937
|
-
const
|
|
23938
|
-
|
|
24167
|
+
const complete = text.match(/<think(?:ing)?>([\s\S]*?)<\/think(?:ing)?>/i);
|
|
24168
|
+
if (complete)
|
|
24169
|
+
return complete[1].trim();
|
|
24170
|
+
const open = text.match(/<think(?:ing)?>([\s\S]*)$/i);
|
|
24171
|
+
return open ? open[1].trim() : "";
|
|
23939
24172
|
}
|
|
23940
|
-
function cleanAgentContent(text) {
|
|
23941
|
-
|
|
24173
|
+
function cleanAgentContent(text, opts = {}) {
|
|
24174
|
+
const stripQuestion = opts.stripQuestion !== false;
|
|
24175
|
+
let out = text.replace(/<think(?:ing)?>[\s\S]*?<\/think(?:ing)?>/gi, "").replace(/<think(?:ing)?>[\s\S]*$/gi, "").replace(/<\/think(?:ing)?>/gi, "").replace(/<minimax:tool_call>[\s\S]*?<\/minimax:tool_call>/g, "");
|
|
24176
|
+
if (stripQuestion) {
|
|
24177
|
+
out = out.replace(/---QUESTION---[\s\S]*?---END---/g, "");
|
|
24178
|
+
}
|
|
24179
|
+
return out.replace(/\n{3,}/g, "\n\n").trim();
|
|
23942
24180
|
}
|
|
23943
24181
|
function restrictImplementationWrites(toolNames, opts) {
|
|
23944
24182
|
if (opts.runMode !== "implementation" || opts.isImplementer)
|
|
@@ -30520,7 +30758,7 @@ import React14 from "react";
|
|
|
30520
30758
|
import { render } from "ink";
|
|
30521
30759
|
|
|
30522
30760
|
// src/cli/app.tsx
|
|
30523
|
-
import React9, { useState as useState8, useMemo, useCallback as useCallback5, useEffect as useEffect7 } from "react";
|
|
30761
|
+
import React9, { useState as useState8, useMemo, useCallback as useCallback5, useEffect as useEffect7, useRef as useRef6 } from "react";
|
|
30524
30762
|
import { Box as Box8, Static, useInput as useInput2, useStdin as useStdin2 } from "ink";
|
|
30525
30763
|
|
|
30526
30764
|
// src/cli/components/InputBar.tsx
|
|
@@ -30918,9 +31156,12 @@ function WorkingIndicator({
|
|
|
30918
31156
|
|
|
30919
31157
|
// src/cli/components/LiveRegion.tsx
|
|
30920
31158
|
var LIVE_STREAM_TAIL_LINES = 10;
|
|
31159
|
+
var MAX_LIVE_TOOLS = 4;
|
|
30921
31160
|
function LiveRegion({ live, busy, elapsedMs = null }) {
|
|
30922
31161
|
const { streaming, runningTools } = live;
|
|
30923
31162
|
if (!streaming && runningTools.length === 0 && !busy) return null;
|
|
31163
|
+
const visibleTools = runningTools.slice(0, MAX_LIVE_TOOLS);
|
|
31164
|
+
const hiddenTools = runningTools.length - visibleTools.length;
|
|
30924
31165
|
return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column", paddingX: 1 }, streaming && streaming.role === "assistant" && /* @__PURE__ */ React5.createElement(
|
|
30925
31166
|
StreamingTail,
|
|
30926
31167
|
{
|
|
@@ -30930,7 +31171,7 @@ function LiveRegion({ live, busy, elapsedMs = null }) {
|
|
|
30930
31171
|
memberName: streaming.memberName,
|
|
30931
31172
|
memberId: streaming.memberId
|
|
30932
31173
|
}
|
|
30933
|
-
),
|
|
31174
|
+
), visibleTools.map((t) => /* @__PURE__ */ React5.createElement(Box4, { key: t.id, flexDirection: "column" }, renderMessage(t, true))), hiddenTools > 0 ? /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " \u2026 +", hiddenTools, " more tools") : null, busy && runningTools.length === 0 && !streaming && /* @__PURE__ */ React5.createElement(WorkingIndicator, { elapsedMs }));
|
|
30934
31175
|
}
|
|
30935
31176
|
function StreamingTail(m) {
|
|
30936
31177
|
const lines = m.content.split("\n");
|
|
@@ -31093,11 +31334,25 @@ import React8 from "react";
|
|
|
31093
31334
|
import { Box as Box7, Text as Text8 } from "ink";
|
|
31094
31335
|
var SIDEBAR_WIDTH = 28;
|
|
31095
31336
|
var SIDEBAR_MIN_COLUMNS = 96;
|
|
31096
|
-
var
|
|
31097
|
-
var
|
|
31098
|
-
var
|
|
31337
|
+
var SIDEBAR_HIDE_COLUMNS = 88;
|
|
31338
|
+
var SIDEBAR_MIN_ROWS = 16;
|
|
31339
|
+
var SIDEBAR_HIDE_ROWS = 14;
|
|
31340
|
+
var MAX_FILES_SHORT = 4;
|
|
31341
|
+
var MAX_FILES_TALL = 8;
|
|
31342
|
+
var SIDEBAR_CHROME_LINES = 4;
|
|
31099
31343
|
function shouldShowSidebar(columns, rows) {
|
|
31100
|
-
return columns >= SIDEBAR_MIN_COLUMNS && rows >=
|
|
31344
|
+
return columns >= SIDEBAR_MIN_COLUMNS && rows >= SIDEBAR_MIN_ROWS;
|
|
31345
|
+
}
|
|
31346
|
+
function sidebarVisibility(columns, rows, currentlyVisible) {
|
|
31347
|
+
if (currentlyVisible) {
|
|
31348
|
+
return columns >= SIDEBAR_HIDE_COLUMNS && rows >= SIDEBAR_HIDE_ROWS;
|
|
31349
|
+
}
|
|
31350
|
+
return shouldShowSidebar(columns, rows);
|
|
31351
|
+
}
|
|
31352
|
+
function maxSidebarFiles(rows) {
|
|
31353
|
+
const budget = Math.max(2, rows - 14 - SIDEBAR_CHROME_LINES);
|
|
31354
|
+
const cap = rows >= 28 ? MAX_FILES_TALL : MAX_FILES_SHORT;
|
|
31355
|
+
return Math.min(cap, budget);
|
|
31101
31356
|
}
|
|
31102
31357
|
function truncatePath(p3, max) {
|
|
31103
31358
|
if (p3.length <= max) return p3;
|
|
@@ -31105,7 +31360,7 @@ function truncatePath(p3, max) {
|
|
|
31105
31360
|
}
|
|
31106
31361
|
function Sidebar({ version: version2, changes, rows }) {
|
|
31107
31362
|
void version2;
|
|
31108
|
-
const maxFiles = rows
|
|
31363
|
+
const maxFiles = maxSidebarFiles(rows);
|
|
31109
31364
|
const visible = changes.files.slice(0, maxFiles);
|
|
31110
31365
|
const hidden = changes.files.length - visible.length;
|
|
31111
31366
|
const innerWidth = SIDEBAR_WIDTH - 4;
|
|
@@ -31117,7 +31372,9 @@ function Sidebar({ version: version2, changes, rows }) {
|
|
|
31117
31372
|
borderStyle: "single",
|
|
31118
31373
|
borderColor: "gray",
|
|
31119
31374
|
paddingX: 1,
|
|
31120
|
-
flexShrink: 0
|
|
31375
|
+
flexShrink: 0,
|
|
31376
|
+
height: Math.min(rows - 8, SIDEBAR_CHROME_LINES + maxFiles + 2),
|
|
31377
|
+
overflow: "hidden"
|
|
31121
31378
|
},
|
|
31122
31379
|
/* @__PURE__ */ React8.createElement(Box7, { justifyContent: "center" }, /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, changes.branch ? truncatePath(changes.branch, 18) : "git")),
|
|
31123
31380
|
/* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "\u2500".repeat(innerWidth)),
|
|
@@ -31129,6 +31386,53 @@ function FileRow({ file: file2, pathWidth }) {
|
|
|
31129
31386
|
return /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text8, { wrap: "truncate" }, /* @__PURE__ */ React8.createElement(Text8, { color: file2.untracked ? "yellow" : "white" }, name), file2.untracked ? /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, " new") : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(Text8, { color: "green" }, " +", file2.added ?? "\xB7"), /* @__PURE__ */ React8.createElement(Text8, { color: "red" }, " -", file2.removed ?? "\xB7"))));
|
|
31130
31387
|
}
|
|
31131
31388
|
|
|
31389
|
+
// src/cli/components/brandArt.ts
|
|
31390
|
+
var BRAND_LOGO_ASCII = [
|
|
31391
|
+
" -#%=",
|
|
31392
|
+
" +%%%%#.",
|
|
31393
|
+
" :#%%%%%%%-",
|
|
31394
|
+
":%%@@@@@@@@=",
|
|
31395
|
+
"-%@@@@@@@@@@@+",
|
|
31396
|
+
":%@@@@@@%@@@@@@=",
|
|
31397
|
+
"-%%@@@@@.+%@@%@+",
|
|
31398
|
+
" -@@@@@@:%++%@=",
|
|
31399
|
+
"=*%@@@@@@:@@%=*@#+.",
|
|
31400
|
+
"%%%%%@@@@:+++-*%@@*",
|
|
31401
|
+
".%%%%%@@@@@+%+=:%@@@@:",
|
|
31402
|
+
"*@@@@@@@@@@@@@%@@@@@@*"
|
|
31403
|
+
].join("\n");
|
|
31404
|
+
var BRAND_LOGO_COMPACT = [
|
|
31405
|
+
" -#%=",
|
|
31406
|
+
" +%%%%#.",
|
|
31407
|
+
":%%@@@@=",
|
|
31408
|
+
"*@@@@@@*"
|
|
31409
|
+
].join("\n");
|
|
31410
|
+
function formatBannerWithLogoRight(opts) {
|
|
31411
|
+
const logoSrc = opts.compact ? BRAND_LOGO_COMPACT : BRAND_LOGO_ASCII;
|
|
31412
|
+
const logoLines = logoSrc.split("\n");
|
|
31413
|
+
const logoWidth = Math.max(...logoLines.map((l) => l.length));
|
|
31414
|
+
const cols = Math.max(logoWidth + 20, Math.min(opts.columns, 120));
|
|
31415
|
+
const left = [...opts.leftLines];
|
|
31416
|
+
const wordmark = `ZELARI CODE v${opts.version}`;
|
|
31417
|
+
const rightExtra = [wordmark];
|
|
31418
|
+
const rightBlock = [...logoLines, ...rightExtra];
|
|
31419
|
+
const rows = Math.max(left.length, rightBlock.length);
|
|
31420
|
+
const out = [];
|
|
31421
|
+
for (let i = 0; i < rows; i++) {
|
|
31422
|
+
const L = left[i] ?? "";
|
|
31423
|
+
const R = rightBlock[i] ?? "";
|
|
31424
|
+
if (!R) {
|
|
31425
|
+
out.push(L);
|
|
31426
|
+
continue;
|
|
31427
|
+
}
|
|
31428
|
+
const maxLeft = Math.max(0, cols - R.length - 2);
|
|
31429
|
+
const leftClipped = L.length > maxLeft ? L.slice(0, Math.max(0, maxLeft - 1)) + "\u2026" : L;
|
|
31430
|
+
const pad = Math.max(2, cols - leftClipped.length - R.length);
|
|
31431
|
+
out.push(leftClipped + " ".repeat(pad) + R);
|
|
31432
|
+
}
|
|
31433
|
+
return out.join("\n");
|
|
31434
|
+
}
|
|
31435
|
+
|
|
31132
31436
|
// src/cli/modelDiscovery.ts
|
|
31133
31437
|
import { promises as fs3, existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
31134
31438
|
import { homedir } from "node:os";
|
|
@@ -34113,10 +34417,12 @@ function computeSessionStatsDelta(realUsage, userText, assistantContent, model,
|
|
|
34113
34417
|
const completionTokens = realUsage ? realUsage.completionTokens : Math.ceil(assistantContent.length / 4);
|
|
34114
34418
|
const cachedPromptTokens = realUsage?.cachedPromptTokens ?? 0;
|
|
34115
34419
|
const turnCost = calculateCost(model, promptTokens, completionTokens, cachedPromptTokens);
|
|
34420
|
+
const contextTokens = realUsage ? realUsage.totalTokens || promptTokens + completionTokens : promptTokens + completionTokens;
|
|
34116
34421
|
return {
|
|
34117
34422
|
totalTokens: prev2.totalTokens + promptTokens + completionTokens,
|
|
34118
34423
|
totalCostUsd: prev2.totalCostUsd + turnCost,
|
|
34119
|
-
cachedTokens: (prev2.cachedTokens ?? 0) + cachedPromptTokens
|
|
34424
|
+
cachedTokens: (prev2.cachedTokens ?? 0) + cachedPromptTokens,
|
|
34425
|
+
contextTokens
|
|
34120
34426
|
};
|
|
34121
34427
|
}
|
|
34122
34428
|
|
|
@@ -34402,6 +34708,11 @@ function useChatTurn(params) {
|
|
|
34402
34708
|
min: 1
|
|
34403
34709
|
});
|
|
34404
34710
|
const maxToolLoopIterations = budget.maxToolLoopIterations;
|
|
34711
|
+
const maxToolLoopHardCap = envNumber(process.env.ZELARI_MAX_TOOL_LOOP_HARD, {
|
|
34712
|
+
default: 0,
|
|
34713
|
+
// 0 → harness default (soft×3, min soft+60)
|
|
34714
|
+
min: 0
|
|
34715
|
+
});
|
|
34405
34716
|
const harness2 = new AgentHarness({
|
|
34406
34717
|
model: envConfig.model,
|
|
34407
34718
|
provider: "openai-compatible",
|
|
@@ -34422,7 +34733,8 @@ function useChatTurn(params) {
|
|
|
34422
34733
|
providerStream,
|
|
34423
34734
|
cwd,
|
|
34424
34735
|
maxToolCallsPerTurn,
|
|
34425
|
-
maxToolLoopIterations
|
|
34736
|
+
maxToolLoopIterations,
|
|
34737
|
+
...maxToolLoopHardCap > 0 ? { maxToolLoopHardCap } : {}
|
|
34426
34738
|
});
|
|
34427
34739
|
harnessRef.current = harness2;
|
|
34428
34740
|
setQueueCount(harness2.queueLength);
|
|
@@ -34478,15 +34790,16 @@ function useChatTurn(params) {
|
|
|
34478
34790
|
if (event.type === "message_delta") {
|
|
34479
34791
|
assistantContent += event.delta;
|
|
34480
34792
|
streamContent += event.delta;
|
|
34793
|
+
const displayContent = cleanAgentContent(streamContent);
|
|
34481
34794
|
if (useLiveModel) {
|
|
34482
|
-
setStreaming(commitStreaming,
|
|
34795
|
+
setStreaming(commitStreaming, displayContent, Date.now(), {
|
|
34483
34796
|
...event.memberId ? { memberId: event.memberId } : {},
|
|
34484
34797
|
...event.memberName ? { memberName: event.memberName } : {}
|
|
34485
34798
|
});
|
|
34486
34799
|
} else {
|
|
34487
34800
|
appendOrExtendStreamingAssistant(
|
|
34488
34801
|
commitStreaming,
|
|
34489
|
-
|
|
34802
|
+
displayContent,
|
|
34490
34803
|
Date.now(),
|
|
34491
34804
|
{
|
|
34492
34805
|
...event.memberId ? { memberId: event.memberId } : {},
|
|
@@ -34496,7 +34809,15 @@ function useChatTurn(params) {
|
|
|
34496
34809
|
}
|
|
34497
34810
|
} else if (event.type === "error") {
|
|
34498
34811
|
flushStreaming();
|
|
34499
|
-
|
|
34812
|
+
if (event.code === "tool_budget_extended") {
|
|
34813
|
+
appendSystem(
|
|
34814
|
+
setMessages,
|
|
34815
|
+
`[budget] ${event.message}`,
|
|
34816
|
+
Date.now()
|
|
34817
|
+
);
|
|
34818
|
+
} else {
|
|
34819
|
+
appendSystem(setMessages, `[error] ${event.message}`, Date.now());
|
|
34820
|
+
}
|
|
34500
34821
|
} else if (event.type === "tool_execution_start") {
|
|
34501
34822
|
toolNameById.set(event.toolCallId, event.toolName);
|
|
34502
34823
|
flushStreaming();
|
|
@@ -34552,32 +34873,41 @@ function useChatTurn(params) {
|
|
|
34552
34873
|
const all = h.getMessages();
|
|
34553
34874
|
const seedLen = 1 + historySeedLen + 1;
|
|
34554
34875
|
if (all.length > seedLen) {
|
|
34555
|
-
appendMessages(
|
|
34876
|
+
appendMessages(
|
|
34877
|
+
all.slice(seedLen).map(
|
|
34878
|
+
(m) => m.role === "assistant" && m.content ? {
|
|
34879
|
+
...m,
|
|
34880
|
+
content: cleanAgentContent(m.content, {
|
|
34881
|
+
stripQuestion: false
|
|
34882
|
+
})
|
|
34883
|
+
} : m
|
|
34884
|
+
)
|
|
34885
|
+
);
|
|
34556
34886
|
}
|
|
34557
34887
|
}
|
|
34558
34888
|
} catch {
|
|
34559
34889
|
}
|
|
34560
34890
|
if (turnSucceeded && assistantContent) {
|
|
34561
34891
|
try {
|
|
34892
|
+
const needsScrub = assistantContent.includes("<think") || assistantContent.includes("<thinking") || assistantContent.includes("---QUESTION---");
|
|
34893
|
+
if (needsScrub) {
|
|
34894
|
+
setMessages(
|
|
34895
|
+
(prev2) => prev2.map((m) => {
|
|
34896
|
+
if (m.role !== "assistant") return m;
|
|
34897
|
+
if (!m.content.includes("<think") && !m.content.includes("<thinking") && !m.content.includes("---QUESTION---")) {
|
|
34898
|
+
return m;
|
|
34899
|
+
}
|
|
34900
|
+
const cleaned = cleanAgentContent(m.content);
|
|
34901
|
+
return cleaned === m.content ? m : { ...m, content: cleaned };
|
|
34902
|
+
})
|
|
34903
|
+
);
|
|
34904
|
+
}
|
|
34562
34905
|
const clar = parseClarificationRequest(assistantContent);
|
|
34563
34906
|
if (clar && clar.choices && clar.choices.length >= 2) {
|
|
34564
34907
|
setLastClarification({
|
|
34565
34908
|
question: clar.question,
|
|
34566
34909
|
choices: clar.choices
|
|
34567
34910
|
});
|
|
34568
|
-
const cleaned = cleanAgentContent(assistantContent);
|
|
34569
|
-
if (cleaned !== assistantContent) {
|
|
34570
|
-
setMessages((prev2) => {
|
|
34571
|
-
const next = [...prev2];
|
|
34572
|
-
for (let i = next.length - 1; i >= 0; i--) {
|
|
34573
|
-
if (next[i].role === "assistant") {
|
|
34574
|
-
next[i] = { ...next[i], content: cleaned };
|
|
34575
|
-
break;
|
|
34576
|
-
}
|
|
34577
|
-
}
|
|
34578
|
-
return next;
|
|
34579
|
-
});
|
|
34580
|
-
}
|
|
34581
34911
|
if (setPicker) {
|
|
34582
34912
|
setPicker({
|
|
34583
34913
|
kind: "clarification",
|
|
@@ -34866,17 +35196,23 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34866
35196
|
streamMemberId = memberId;
|
|
34867
35197
|
}
|
|
34868
35198
|
streamContent += event.delta;
|
|
34869
|
-
setStreaming(
|
|
34870
|
-
|
|
34871
|
-
|
|
34872
|
-
|
|
35199
|
+
setStreaming(
|
|
35200
|
+
commitStreaming,
|
|
35201
|
+
cleanAgentContent(streamContent),
|
|
35202
|
+
event.ts,
|
|
35203
|
+
{
|
|
35204
|
+
...event.memberId ? { memberId: event.memberId } : {},
|
|
35205
|
+
...event.memberName ? { memberName: event.memberName } : {}
|
|
35206
|
+
}
|
|
35207
|
+
);
|
|
34873
35208
|
} else {
|
|
34874
35209
|
commitStreaming((prev2) => {
|
|
34875
35210
|
const last = prev2[prev2.length - 1];
|
|
34876
35211
|
if (last && last.role === "assistant" && last.id.startsWith("streaming-") && (last.memberId ?? null) === (event.memberId ?? null)) {
|
|
35212
|
+
const nextContent = cleanAgentContent(last.content + event.delta);
|
|
34877
35213
|
return [
|
|
34878
35214
|
...prev2.slice(0, -1),
|
|
34879
|
-
{ ...last, content:
|
|
35215
|
+
{ ...last, content: nextContent }
|
|
34880
35216
|
];
|
|
34881
35217
|
}
|
|
34882
35218
|
return [
|
|
@@ -34884,7 +35220,7 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
34884
35220
|
{
|
|
34885
35221
|
id: `streaming-${crypto.randomUUID()}`,
|
|
34886
35222
|
role: "assistant",
|
|
34887
|
-
content: event.delta,
|
|
35223
|
+
content: cleanAgentContent(event.delta),
|
|
34888
35224
|
ts: event.ts,
|
|
34889
35225
|
...event.memberId ? { memberId: event.memberId } : {},
|
|
34890
35226
|
...event.memberName ? { memberName: event.memberName } : {}
|
|
@@ -37569,8 +37905,9 @@ function useBatchedMessages(state2, setState, cadenceMs = 16) {
|
|
|
37569
37905
|
// src/cli/hooks/useTerminalSize.ts
|
|
37570
37906
|
import { useEffect as useEffect6, useState as useState7 } from "react";
|
|
37571
37907
|
import { useStdout as useStdout2 } from "ink";
|
|
37908
|
+
var DEFAULT_COALESCE_MS = 120;
|
|
37572
37909
|
function useTerminalSize(options = {}) {
|
|
37573
|
-
const { defaults = { columns: 80, rows: 24 }, coalesceMs =
|
|
37910
|
+
const { defaults = { columns: 80, rows: 24 }, coalesceMs = DEFAULT_COALESCE_MS } = options;
|
|
37574
37911
|
const { stdout } = useStdout2();
|
|
37575
37912
|
const [size, setSize] = useState7({
|
|
37576
37913
|
columns: stdout?.columns ?? defaults.columns,
|
|
@@ -37578,25 +37915,25 @@ function useTerminalSize(options = {}) {
|
|
|
37578
37915
|
});
|
|
37579
37916
|
useEffect6(() => {
|
|
37580
37917
|
if (!stdout) return;
|
|
37581
|
-
|
|
37918
|
+
const read = () => ({
|
|
37582
37919
|
columns: stdout.columns ?? defaults.columns,
|
|
37583
37920
|
rows: stdout.rows ?? defaults.rows
|
|
37584
37921
|
});
|
|
37922
|
+
const commit = (next) => {
|
|
37923
|
+
setSize(
|
|
37924
|
+
(prev2) => prev2.columns === next.columns && prev2.rows === next.rows ? prev2 : next
|
|
37925
|
+
);
|
|
37926
|
+
};
|
|
37927
|
+
commit(read());
|
|
37585
37928
|
let rafId = null;
|
|
37586
37929
|
const handleResize = () => {
|
|
37587
37930
|
if (coalesceMs <= 0) {
|
|
37588
|
-
|
|
37589
|
-
columns: stdout.columns ?? defaults.columns,
|
|
37590
|
-
rows: stdout.rows ?? defaults.rows
|
|
37591
|
-
});
|
|
37931
|
+
commit(read());
|
|
37592
37932
|
return;
|
|
37593
37933
|
}
|
|
37594
37934
|
if (rafId !== null) clearTimeout(rafId);
|
|
37595
37935
|
rafId = setTimeout(() => {
|
|
37596
|
-
|
|
37597
|
-
columns: stdout.columns ?? defaults.columns,
|
|
37598
|
-
rows: stdout.rows ?? defaults.rows
|
|
37599
|
-
});
|
|
37936
|
+
commit(read());
|
|
37600
37937
|
rafId = null;
|
|
37601
37938
|
}, coalesceMs);
|
|
37602
37939
|
};
|
|
@@ -37622,7 +37959,12 @@ function App() {
|
|
|
37622
37959
|
const [input, setInput] = useState8("");
|
|
37623
37960
|
const [busy, setBusy] = useState8(false);
|
|
37624
37961
|
const [providerConfig, setProviderConfig] = useState8(() => getProviderConfig());
|
|
37625
|
-
const [sessionStats, setSessionStats] = useState8({
|
|
37962
|
+
const [sessionStats, setSessionStats] = useState8({
|
|
37963
|
+
totalTokens: 0,
|
|
37964
|
+
totalCostUsd: 0,
|
|
37965
|
+
cachedTokens: 0,
|
|
37966
|
+
contextTokens: 0
|
|
37967
|
+
});
|
|
37626
37968
|
const [clearEpoch, setClearEpoch] = useState8(0);
|
|
37627
37969
|
const [mode, setMode] = useState8("agent");
|
|
37628
37970
|
const [phase2, setPhaseUi] = useState8("build");
|
|
@@ -37723,60 +38065,84 @@ function App() {
|
|
|
37723
38065
|
}
|
|
37724
38066
|
setPicker(null);
|
|
37725
38067
|
}, [picker]);
|
|
38068
|
+
const bannerColsRef = useRef6(null);
|
|
38069
|
+
if (bannerColsRef.current === null) {
|
|
38070
|
+
bannerColsRef.current = size.columns > 0 ? size.columns : 80;
|
|
38071
|
+
}
|
|
37726
38072
|
const banner = useMemo(() => {
|
|
37727
|
-
const
|
|
37728
|
-
const
|
|
37729
|
-
|
|
38073
|
+
const cols = bannerColsRef.current ?? 80;
|
|
38074
|
+
const content = formatBannerWithLogoRight({
|
|
38075
|
+
leftLines: [
|
|
38076
|
+
`zelari-code \xB7 ${activeProviderSpec.id}/${activeModel}`,
|
|
38077
|
+
`cwd: ${cwd}`,
|
|
38078
|
+
`/help \xB7 /plan \xB7 /build \xB7 /view-plan \xB7 shift+tab mode`
|
|
38079
|
+
],
|
|
38080
|
+
version: VERSION,
|
|
38081
|
+
columns: cols,
|
|
38082
|
+
compact: cols < 72
|
|
38083
|
+
});
|
|
37730
38084
|
return {
|
|
37731
38085
|
id: "banner-once",
|
|
37732
38086
|
role: "system",
|
|
37733
38087
|
ts: 0,
|
|
37734
|
-
content
|
|
37735
|
-
cwd: ${cwd}
|
|
37736
|
-
/help \xB7 /plan \xB7 /build \xB7 /view-plan \xB7 shift+tab mode (agent/council/zelari)`
|
|
38088
|
+
content
|
|
37737
38089
|
};
|
|
37738
38090
|
}, [activeProviderSpec.id, activeModel, cwd]);
|
|
38091
|
+
const [sidebarOpen, setSidebarOpen] = useState8(false);
|
|
38092
|
+
useEffect7(() => {
|
|
38093
|
+
setSidebarOpen((prev2) => sidebarVisibility(size.columns, size.rows, prev2));
|
|
38094
|
+
}, [size.columns, size.rows]);
|
|
37739
38095
|
const staticKey = `${session.sessionId || "pre-bootstrap"}-${clearEpoch}`;
|
|
37740
38096
|
const staticItems = session.sessionId ? [banner, ...session.messages] : [];
|
|
37741
|
-
const
|
|
37742
|
-
return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Static, { key: staticKey, items: staticItems }, (item) => renderMessage(item)), /* @__PURE__ */ React9.createElement(
|
|
37743
|
-
|
|
38097
|
+
const dynamicMaxRows = Math.max(8, Math.min(18, size.rows - 2));
|
|
38098
|
+
return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Static, { key: staticKey, items: staticItems }, (item) => renderMessage(item)), /* @__PURE__ */ React9.createElement(
|
|
38099
|
+
Box8,
|
|
37744
38100
|
{
|
|
37745
|
-
|
|
37746
|
-
|
|
37747
|
-
|
|
37748
|
-
|
|
37749
|
-
|
|
37750
|
-
}
|
|
37751
|
-
|
|
37752
|
-
|
|
37753
|
-
|
|
37754
|
-
|
|
37755
|
-
|
|
37756
|
-
|
|
37757
|
-
|
|
37758
|
-
|
|
37759
|
-
|
|
37760
|
-
|
|
37761
|
-
|
|
37762
|
-
|
|
37763
|
-
|
|
37764
|
-
|
|
37765
|
-
|
|
37766
|
-
|
|
37767
|
-
|
|
37768
|
-
|
|
37769
|
-
|
|
37770
|
-
|
|
37771
|
-
|
|
37772
|
-
|
|
37773
|
-
|
|
37774
|
-
|
|
37775
|
-
|
|
37776
|
-
|
|
37777
|
-
|
|
37778
|
-
|
|
37779
|
-
|
|
38101
|
+
flexDirection: "row",
|
|
38102
|
+
width: size.columns > 0 ? size.columns : void 0,
|
|
38103
|
+
height: dynamicMaxRows,
|
|
38104
|
+
overflow: "hidden"
|
|
38105
|
+
},
|
|
38106
|
+
/* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", flexGrow: 1, paddingX: 1, overflow: "hidden" }, /* @__PURE__ */ React9.createElement(LiveRegion, { live: session.live, busy, elapsedMs: timer.elapsedMs }), picker ? /* @__PURE__ */ React9.createElement(
|
|
38107
|
+
SelectList,
|
|
38108
|
+
{
|
|
38109
|
+
title: picker.title,
|
|
38110
|
+
items: picker.items,
|
|
38111
|
+
onSelect: onPickerSelect,
|
|
38112
|
+
onCancel: onPickerCancel,
|
|
38113
|
+
maxVisible: Math.max(3, Math.min(8, size.rows - 12))
|
|
38114
|
+
}
|
|
38115
|
+
) : /* @__PURE__ */ React9.createElement(
|
|
38116
|
+
InputBar,
|
|
38117
|
+
{
|
|
38118
|
+
value: input,
|
|
38119
|
+
onChange: setInput,
|
|
38120
|
+
onSubmit: handleSubmit,
|
|
38121
|
+
disabled: busy
|
|
38122
|
+
}
|
|
38123
|
+
), /* @__PURE__ */ React9.createElement(
|
|
38124
|
+
StatusBar,
|
|
38125
|
+
{
|
|
38126
|
+
model: activeModel,
|
|
38127
|
+
provider: activeProviderSpec.id,
|
|
38128
|
+
sessionId: session.sessionId ? session.sessionId.slice(0, 8) : "...",
|
|
38129
|
+
sessionActive: session.sessionActive,
|
|
38130
|
+
queueCount: chatTurn.queueCount,
|
|
38131
|
+
busy,
|
|
38132
|
+
mode,
|
|
38133
|
+
phase: phase2,
|
|
38134
|
+
cwd,
|
|
38135
|
+
elapsedMs: timer.elapsedMs,
|
|
38136
|
+
lastMs: timer.lastMs,
|
|
38137
|
+
costUsd: sessionStats.totalCostUsd,
|
|
38138
|
+
cachedTokens: sessionStats.cachedTokens,
|
|
38139
|
+
contextUsed: sessionStats.contextTokens || 0,
|
|
38140
|
+
contextLimit: Number(process.env.ZELARI_CONTEXT_LIMIT) || 2e5,
|
|
38141
|
+
brandVersion: VERSION
|
|
38142
|
+
}
|
|
38143
|
+
)),
|
|
38144
|
+
sidebarOpen && /* @__PURE__ */ React9.createElement(Sidebar, { version: VERSION, changes: gitChanges, rows: size.rows })
|
|
38145
|
+
));
|
|
37780
38146
|
}
|
|
37781
38147
|
|
|
37782
38148
|
// src/cli/components/SplashScreen.tsx
|