zelari-code 0.1.3 → 0.2.0
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 +26 -1
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/main.bundled.js +255 -57
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/provider/openai-compatible.js +51 -3
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/toolRegistry.js +4 -0
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/main/core/AgentHarness.js +80 -1
- package/dist/main/core/AgentHarness.js.map +1 -1
- package/dist/main/core/tools/builtin/listFiles.js +86 -0
- package/dist/main/core/tools/builtin/listFiles.js.map +1 -0
- package/package.json +1 -1
package/dist/cli/main.bundled.js
CHANGED
|
@@ -523,11 +523,11 @@ __export(updater_exports, {
|
|
|
523
523
|
});
|
|
524
524
|
import { createRequire } from "node:module";
|
|
525
525
|
import { spawn as spawn2 } from "node:child_process";
|
|
526
|
-
import
|
|
526
|
+
import path16 from "node:path";
|
|
527
527
|
import { fileURLToPath } from "node:url";
|
|
528
528
|
function getCurrentVersion() {
|
|
529
529
|
try {
|
|
530
|
-
const pkgPath =
|
|
530
|
+
const pkgPath = path16.resolve(__dirname2, "..", "..", "package.json");
|
|
531
531
|
const pkg = require2(pkgPath);
|
|
532
532
|
return pkg.version;
|
|
533
533
|
} catch {
|
|
@@ -626,7 +626,7 @@ var init_updater = __esm({
|
|
|
626
626
|
"src/cli/updater.ts"() {
|
|
627
627
|
"use strict";
|
|
628
628
|
require2 = createRequire(import.meta.url);
|
|
629
|
-
__dirname2 =
|
|
629
|
+
__dirname2 = path16.dirname(fileURLToPath(import.meta.url));
|
|
630
630
|
REGISTRY_URL = "https://registry.npmjs.org/zelari-code/latest";
|
|
631
631
|
}
|
|
632
632
|
});
|
|
@@ -1849,6 +1849,41 @@ var AgentHarness = class {
|
|
|
1849
1849
|
});
|
|
1850
1850
|
this.emit(initialMsgEnd);
|
|
1851
1851
|
yield initialMsgEnd;
|
|
1852
|
+
const MAX_TOOL_LOOP_ITERATIONS = 12;
|
|
1853
|
+
let toolLoopTurns = 0;
|
|
1854
|
+
while (!this.cancelled && !hadError && toolLoopTurns < MAX_TOOL_LOOP_ITERATIONS && initialFinishRef.value === "tool_calls") {
|
|
1855
|
+
toolLoopTurns++;
|
|
1856
|
+
const turnMessageId = crypto.randomUUID();
|
|
1857
|
+
const msgStart = createBrainEvent(
|
|
1858
|
+
"message_start",
|
|
1859
|
+
this.sessionId,
|
|
1860
|
+
{ messageId: turnMessageId, role: "assistant" }
|
|
1861
|
+
);
|
|
1862
|
+
this.emit(msgStart);
|
|
1863
|
+
yield msgStart;
|
|
1864
|
+
let turnLength = 0;
|
|
1865
|
+
const turnFinishRef = { value: "stop" };
|
|
1866
|
+
const turnUsageRef = { value: null };
|
|
1867
|
+
for await (const ev of this.runSingleTurn(turnMessageId, turnFinishRef, turnUsageRef)) {
|
|
1868
|
+
if (ev.type === "message_delta") {
|
|
1869
|
+
turnLength += ev.delta.length;
|
|
1870
|
+
} else if (ev.type === "error") {
|
|
1871
|
+
if (ev.severity !== "cancelled") hadError = true;
|
|
1872
|
+
}
|
|
1873
|
+
yield ev;
|
|
1874
|
+
}
|
|
1875
|
+
totalLength += turnLength;
|
|
1876
|
+
const msgEnd = createBrainEvent("message_end", this.sessionId, {
|
|
1877
|
+
messageId: turnMessageId,
|
|
1878
|
+
totalLength: turnLength,
|
|
1879
|
+
finishReason: turnFinishRef.value,
|
|
1880
|
+
...turnUsageRef.value ? { usage: turnUsageRef.value } : {}
|
|
1881
|
+
});
|
|
1882
|
+
this.emit(msgEnd);
|
|
1883
|
+
yield msgEnd;
|
|
1884
|
+
initialFinishRef.value = turnFinishRef.value;
|
|
1885
|
+
if (hadError || this.cancelled) break;
|
|
1886
|
+
}
|
|
1852
1887
|
let turns = 0;
|
|
1853
1888
|
while (turns < this.maxQueuedIterations) {
|
|
1854
1889
|
if (this.cancelled) break;
|
|
@@ -1919,6 +1954,8 @@ var AgentHarness = class {
|
|
|
1919
1954
|
});
|
|
1920
1955
|
let toolCallsThisTurn = 0;
|
|
1921
1956
|
const maxToolCalls = this.config.maxToolCallsPerTurn;
|
|
1957
|
+
let turnText = "";
|
|
1958
|
+
const turnToolCalls = [];
|
|
1922
1959
|
for await (const delta of stream) {
|
|
1923
1960
|
if (this.cancelled) {
|
|
1924
1961
|
const cancelEvent = createBrainEvent("error", this.sessionId, {
|
|
@@ -1931,6 +1968,7 @@ var AgentHarness = class {
|
|
|
1931
1968
|
break;
|
|
1932
1969
|
}
|
|
1933
1970
|
if (delta.kind === "text") {
|
|
1971
|
+
turnText += delta.delta;
|
|
1934
1972
|
const deltaEvent = createBrainEvent(
|
|
1935
1973
|
"message_delta",
|
|
1936
1974
|
this.sessionId,
|
|
@@ -1947,6 +1985,7 @@ var AgentHarness = class {
|
|
|
1947
1985
|
yield thinkEvent;
|
|
1948
1986
|
} else if (delta.kind === "tool_call") {
|
|
1949
1987
|
toolCallsThisTurn++;
|
|
1988
|
+
turnToolCalls.push({ id: delta.toolCallId, name: delta.toolName, args: delta.args });
|
|
1950
1989
|
const toolStartEvent = createBrainEvent("tool_execution_start", this.sessionId, {
|
|
1951
1990
|
toolCallId: delta.toolCallId,
|
|
1952
1991
|
toolName: delta.toolName,
|
|
@@ -1966,18 +2005,24 @@ var AgentHarness = class {
|
|
|
1966
2005
|
signal: this.activeController?.signal
|
|
1967
2006
|
}
|
|
1968
2007
|
);
|
|
2008
|
+
const resultStr = result.ok ? String(result.value) : result.error;
|
|
1969
2009
|
const endEvent = createBrainEvent(
|
|
1970
2010
|
"tool_execution_end",
|
|
1971
2011
|
this.sessionId,
|
|
1972
2012
|
{
|
|
1973
2013
|
toolCallId: delta.toolCallId,
|
|
1974
|
-
result:
|
|
2014
|
+
result: resultStr,
|
|
1975
2015
|
isError: !result.ok,
|
|
1976
2016
|
durationMs: Date.now() - startMs
|
|
1977
2017
|
}
|
|
1978
2018
|
);
|
|
1979
2019
|
this.emit(endEvent);
|
|
1980
2020
|
yield endEvent;
|
|
2021
|
+
this.config.messages.push({
|
|
2022
|
+
role: "tool",
|
|
2023
|
+
toolCallId: delta.toolCallId,
|
|
2024
|
+
content: resultStr
|
|
2025
|
+
});
|
|
1981
2026
|
} else if (skipped) {
|
|
1982
2027
|
const endEvent = createBrainEvent(
|
|
1983
2028
|
"tool_execution_end",
|
|
@@ -1991,9 +2036,21 @@ var AgentHarness = class {
|
|
|
1991
2036
|
);
|
|
1992
2037
|
this.emit(endEvent);
|
|
1993
2038
|
yield endEvent;
|
|
2039
|
+
this.config.messages.push({
|
|
2040
|
+
role: "tool",
|
|
2041
|
+
toolCallId: delta.toolCallId,
|
|
2042
|
+
content: `[skipped] maxToolCallsPerTurn reached (limit=${maxToolCalls})`
|
|
2043
|
+
});
|
|
1994
2044
|
}
|
|
1995
2045
|
} else if (delta.kind === "finish") {
|
|
1996
2046
|
finishRef.value = delta.reason;
|
|
2047
|
+
if (turnToolCalls.length > 0 || turnText.length > 0) {
|
|
2048
|
+
this.config.messages.push({
|
|
2049
|
+
role: "assistant",
|
|
2050
|
+
content: turnText,
|
|
2051
|
+
...turnToolCalls.length > 0 ? { toolCalls: turnToolCalls } : {}
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
1997
2054
|
break;
|
|
1998
2055
|
} else if (delta.kind === "error") {
|
|
1999
2056
|
const errEvent = createBrainEvent("error", this.sessionId, {
|
|
@@ -2245,9 +2302,33 @@ function resolveBaseUrl(providerId) {
|
|
|
2245
2302
|
}
|
|
2246
2303
|
function openaiCompatibleProvider(config2) {
|
|
2247
2304
|
return async function* (params) {
|
|
2305
|
+
const messages = params.messages.map((m) => {
|
|
2306
|
+
if (m.role === "tool") {
|
|
2307
|
+
return {
|
|
2308
|
+
role: "tool",
|
|
2309
|
+
tool_call_id: m.toolCallId,
|
|
2310
|
+
content: m.content
|
|
2311
|
+
};
|
|
2312
|
+
}
|
|
2313
|
+
if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0) {
|
|
2314
|
+
return {
|
|
2315
|
+
role: "assistant",
|
|
2316
|
+
content: m.content ?? "",
|
|
2317
|
+
tool_calls: m.toolCalls.map((tc) => ({
|
|
2318
|
+
id: tc.id,
|
|
2319
|
+
type: "function",
|
|
2320
|
+
function: {
|
|
2321
|
+
name: tc.name,
|
|
2322
|
+
arguments: JSON.stringify(tc.args ?? {})
|
|
2323
|
+
}
|
|
2324
|
+
}))
|
|
2325
|
+
};
|
|
2326
|
+
}
|
|
2327
|
+
return { role: m.role, content: m.content };
|
|
2328
|
+
});
|
|
2248
2329
|
const body = {
|
|
2249
2330
|
model: config2.model,
|
|
2250
|
-
messages
|
|
2331
|
+
messages,
|
|
2251
2332
|
stream: true,
|
|
2252
2333
|
temperature: 0.7,
|
|
2253
2334
|
// Task G.4.2 — request the provider to send real token usage in
|
|
@@ -2257,6 +2338,17 @@ function openaiCompatibleProvider(config2) {
|
|
|
2257
2338
|
// the harness will fall back to the ~4-char/token approximation.
|
|
2258
2339
|
stream_options: { include_usage: true }
|
|
2259
2340
|
};
|
|
2341
|
+
if (params.tools && params.tools.length > 0) {
|
|
2342
|
+
body.tools = params.tools.map((t) => ({
|
|
2343
|
+
type: "function",
|
|
2344
|
+
function: {
|
|
2345
|
+
name: t.name,
|
|
2346
|
+
description: t.description,
|
|
2347
|
+
parameters: t.parameters
|
|
2348
|
+
}
|
|
2349
|
+
}));
|
|
2350
|
+
body.tool_choice = "auto";
|
|
2351
|
+
}
|
|
2260
2352
|
let response;
|
|
2261
2353
|
try {
|
|
2262
2354
|
response = await fetch(`${config2.baseUrl}/chat/completions`, {
|
|
@@ -2339,6 +2431,9 @@ function openaiCompatibleProvider(config2) {
|
|
|
2339
2431
|
}
|
|
2340
2432
|
}
|
|
2341
2433
|
}
|
|
2434
|
+
if (choice?.finish_reason) {
|
|
2435
|
+
yield { kind: "finish", reason: choice.finish_reason };
|
|
2436
|
+
}
|
|
2342
2437
|
} catch {
|
|
2343
2438
|
}
|
|
2344
2439
|
}
|
|
@@ -2840,11 +2935,11 @@ var TOOL_DEFINITIONS = [
|
|
|
2840
2935
|
execute: (args, ctx) => {
|
|
2841
2936
|
if (!ctx.addDocument) return "Knowledge vault tool not available.";
|
|
2842
2937
|
const title = args["title"] || "New Document";
|
|
2843
|
-
const
|
|
2938
|
+
const path18 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
2844
2939
|
const content = args["content"] || "";
|
|
2845
2940
|
const tags = args["tags"] || [];
|
|
2846
2941
|
ctx.addDocument({
|
|
2847
|
-
path:
|
|
2942
|
+
path: path18,
|
|
2848
2943
|
title,
|
|
2849
2944
|
content,
|
|
2850
2945
|
format: "markdown",
|
|
@@ -2853,7 +2948,7 @@ var TOOL_DEFINITIONS = [
|
|
|
2853
2948
|
workspaceId: ctx.workspaceId
|
|
2854
2949
|
});
|
|
2855
2950
|
ctx.addActivity("vault", "created document", title);
|
|
2856
|
-
return `Document "${title}" created at "${
|
|
2951
|
+
return `Document "${title}" created at "${path18}".`;
|
|
2857
2952
|
}
|
|
2858
2953
|
}
|
|
2859
2954
|
];
|
|
@@ -5120,10 +5215,10 @@ function mergeDefs(...defs) {
|
|
|
5120
5215
|
function cloneDef(schema) {
|
|
5121
5216
|
return mergeDefs(schema._zod.def);
|
|
5122
5217
|
}
|
|
5123
|
-
function getElementAtPath(obj,
|
|
5124
|
-
if (!
|
|
5218
|
+
function getElementAtPath(obj, path18) {
|
|
5219
|
+
if (!path18)
|
|
5125
5220
|
return obj;
|
|
5126
|
-
return
|
|
5221
|
+
return path18.reduce((acc, key) => acc?.[key], obj);
|
|
5127
5222
|
}
|
|
5128
5223
|
function promiseAllObject(promisesObj) {
|
|
5129
5224
|
const keys = Object.keys(promisesObj);
|
|
@@ -5532,11 +5627,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
5532
5627
|
}
|
|
5533
5628
|
return false;
|
|
5534
5629
|
}
|
|
5535
|
-
function prefixIssues(
|
|
5630
|
+
function prefixIssues(path18, issues) {
|
|
5536
5631
|
return issues.map((iss) => {
|
|
5537
5632
|
var _a3;
|
|
5538
5633
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
5539
|
-
iss.path.unshift(
|
|
5634
|
+
iss.path.unshift(path18);
|
|
5540
5635
|
return iss;
|
|
5541
5636
|
});
|
|
5542
5637
|
}
|
|
@@ -5683,16 +5778,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
5683
5778
|
}
|
|
5684
5779
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
5685
5780
|
const fieldErrors = { _errors: [] };
|
|
5686
|
-
const processError = (error52,
|
|
5781
|
+
const processError = (error52, path18 = []) => {
|
|
5687
5782
|
for (const issue2 of error52.issues) {
|
|
5688
5783
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5689
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5784
|
+
issue2.errors.map((issues) => processError({ issues }, [...path18, ...issue2.path]));
|
|
5690
5785
|
} else if (issue2.code === "invalid_key") {
|
|
5691
|
-
processError({ issues: issue2.issues }, [...
|
|
5786
|
+
processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
|
|
5692
5787
|
} else if (issue2.code === "invalid_element") {
|
|
5693
|
-
processError({ issues: issue2.issues }, [...
|
|
5788
|
+
processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
|
|
5694
5789
|
} else {
|
|
5695
|
-
const fullpath = [...
|
|
5790
|
+
const fullpath = [...path18, ...issue2.path];
|
|
5696
5791
|
if (fullpath.length === 0) {
|
|
5697
5792
|
fieldErrors._errors.push(mapper(issue2));
|
|
5698
5793
|
} else {
|
|
@@ -5719,17 +5814,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
5719
5814
|
}
|
|
5720
5815
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
5721
5816
|
const result = { errors: [] };
|
|
5722
|
-
const processError = (error52,
|
|
5817
|
+
const processError = (error52, path18 = []) => {
|
|
5723
5818
|
var _a3, _b;
|
|
5724
5819
|
for (const issue2 of error52.issues) {
|
|
5725
5820
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
5726
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
5821
|
+
issue2.errors.map((issues) => processError({ issues }, [...path18, ...issue2.path]));
|
|
5727
5822
|
} else if (issue2.code === "invalid_key") {
|
|
5728
|
-
processError({ issues: issue2.issues }, [...
|
|
5823
|
+
processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
|
|
5729
5824
|
} else if (issue2.code === "invalid_element") {
|
|
5730
|
-
processError({ issues: issue2.issues }, [...
|
|
5825
|
+
processError({ issues: issue2.issues }, [...path18, ...issue2.path]);
|
|
5731
5826
|
} else {
|
|
5732
|
-
const fullpath = [...
|
|
5827
|
+
const fullpath = [...path18, ...issue2.path];
|
|
5733
5828
|
if (fullpath.length === 0) {
|
|
5734
5829
|
result.errors.push(mapper(issue2));
|
|
5735
5830
|
continue;
|
|
@@ -5761,8 +5856,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
5761
5856
|
}
|
|
5762
5857
|
function toDotPath(_path) {
|
|
5763
5858
|
const segs = [];
|
|
5764
|
-
const
|
|
5765
|
-
for (const seg of
|
|
5859
|
+
const path18 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
5860
|
+
for (const seg of path18) {
|
|
5766
5861
|
if (typeof seg === "number")
|
|
5767
5862
|
segs.push(`[${seg}]`);
|
|
5768
5863
|
else if (typeof seg === "symbol")
|
|
@@ -18454,13 +18549,13 @@ function resolveRef(ref, ctx) {
|
|
|
18454
18549
|
if (!ref.startsWith("#")) {
|
|
18455
18550
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
18456
18551
|
}
|
|
18457
|
-
const
|
|
18458
|
-
if (
|
|
18552
|
+
const path18 = ref.slice(1).split("/").filter(Boolean);
|
|
18553
|
+
if (path18.length === 0) {
|
|
18459
18554
|
return ctx.rootSchema;
|
|
18460
18555
|
}
|
|
18461
18556
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
18462
|
-
if (
|
|
18463
|
-
const key =
|
|
18557
|
+
if (path18[0] === defsKey) {
|
|
18558
|
+
const key = path18[1];
|
|
18464
18559
|
if (!key || !ctx.defs[key]) {
|
|
18465
18560
|
throw new Error(`Reference not found: ${ref}`);
|
|
18466
18561
|
}
|
|
@@ -19073,8 +19168,83 @@ var grepContentTool = {
|
|
|
19073
19168
|
}
|
|
19074
19169
|
};
|
|
19075
19170
|
|
|
19076
|
-
// src/
|
|
19171
|
+
// src/main/core/tools/builtin/listFiles.ts
|
|
19172
|
+
import { promises as fs11 } from "node:fs";
|
|
19077
19173
|
import path10 from "node:path";
|
|
19174
|
+
var ListFilesArgsSchema = external_exports.object({
|
|
19175
|
+
/** Directory to list (relative to cwd or absolute). Defaults to cwd. */
|
|
19176
|
+
path: external_exports.string().optional(),
|
|
19177
|
+
/** Max traversal depth. 1 = immediate children only (default). */
|
|
19178
|
+
maxDepth: external_exports.number().int().positive().max(10).default(1),
|
|
19179
|
+
/** Glob-style patterns to exclude (matched against each entry name). */
|
|
19180
|
+
exclude: external_exports.array(external_exports.string()).default([
|
|
19181
|
+
"node_modules",
|
|
19182
|
+
".git",
|
|
19183
|
+
"dist",
|
|
19184
|
+
"build",
|
|
19185
|
+
".next",
|
|
19186
|
+
"__pycache__",
|
|
19187
|
+
".cache"
|
|
19188
|
+
])
|
|
19189
|
+
});
|
|
19190
|
+
function matchesAny(name, patterns) {
|
|
19191
|
+
return patterns.some((p3) => {
|
|
19192
|
+
if (p3.includes("*")) {
|
|
19193
|
+
const regex = new RegExp("^" + p3.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$");
|
|
19194
|
+
return regex.test(name);
|
|
19195
|
+
}
|
|
19196
|
+
return name === p3;
|
|
19197
|
+
});
|
|
19198
|
+
}
|
|
19199
|
+
async function walk(dir, baseRel, depth, maxDepth, exclude, entries, signal) {
|
|
19200
|
+
if (depth > maxDepth) return;
|
|
19201
|
+
let dirents;
|
|
19202
|
+
try {
|
|
19203
|
+
dirents = await fs11.readdir(dir, { withFileTypes: true });
|
|
19204
|
+
} catch {
|
|
19205
|
+
return;
|
|
19206
|
+
}
|
|
19207
|
+
if (signal?.aborted) return;
|
|
19208
|
+
for (const dirent of dirents) {
|
|
19209
|
+
if (matchesAny(dirent.name, exclude)) continue;
|
|
19210
|
+
const rel = baseRel ? `${baseRel}/${dirent.name}` : dirent.name;
|
|
19211
|
+
const isDir = dirent.isDirectory();
|
|
19212
|
+
entries.push({
|
|
19213
|
+
name: rel,
|
|
19214
|
+
type: isDir ? "directory" : dirent.isFile() ? "file" : "other"
|
|
19215
|
+
});
|
|
19216
|
+
if (isDir && depth < maxDepth) {
|
|
19217
|
+
await walk(path10.join(dir, dirent.name), rel, depth + 1, maxDepth, exclude, entries, signal);
|
|
19218
|
+
}
|
|
19219
|
+
}
|
|
19220
|
+
}
|
|
19221
|
+
var listFilesTool = {
|
|
19222
|
+
name: "list_files",
|
|
19223
|
+
description: "List files and directories in the given path (defaults to the working directory). Returns names with types (file/directory). Use this to discover the project structure before reading specific files. Supports a maxDepth for recursive listing and excludes common dependency/build directories by default.",
|
|
19224
|
+
permissions: ["read"],
|
|
19225
|
+
timeoutMs: 15e3,
|
|
19226
|
+
inputSchema: ListFilesArgsSchema,
|
|
19227
|
+
execute: async (args, ctx) => {
|
|
19228
|
+
try {
|
|
19229
|
+
const target = args.path ? path10.isAbsolute(args.path) ? args.path : path10.join(ctx.cwd, args.path) : ctx.cwd;
|
|
19230
|
+
const entries = [];
|
|
19231
|
+
await walk(target, "", 1, args.maxDepth, args.exclude, entries, ctx.signal);
|
|
19232
|
+
entries.sort((a, b) => {
|
|
19233
|
+
if (a.type === "directory" && b.type !== "directory") return -1;
|
|
19234
|
+
if (a.type !== "directory" && b.type === "directory") return 1;
|
|
19235
|
+
return a.name.localeCompare(b.name);
|
|
19236
|
+
});
|
|
19237
|
+
const MAX_ENTRIES = 500;
|
|
19238
|
+
const truncated = entries.length > MAX_ENTRIES;
|
|
19239
|
+
return typedOk({ dir: target, entries: truncated ? entries.slice(0, MAX_ENTRIES) : entries, truncated });
|
|
19240
|
+
} catch (err) {
|
|
19241
|
+
return typedErr(err instanceof Error ? err.message : String(err));
|
|
19242
|
+
}
|
|
19243
|
+
}
|
|
19244
|
+
};
|
|
19245
|
+
|
|
19246
|
+
// src/cli/safety/sandboxPath.ts
|
|
19247
|
+
import path11 from "node:path";
|
|
19078
19248
|
var SandboxViolationError = class extends Error {
|
|
19079
19249
|
constructor(message, attemptedPath, resolvedPath) {
|
|
19080
19250
|
super(message);
|
|
@@ -19087,9 +19257,9 @@ function resolveSandboxedPath(userPath, options = {}) {
|
|
|
19087
19257
|
if (typeof userPath !== "string" || userPath.length === 0) {
|
|
19088
19258
|
throw new SandboxViolationError("Empty path", userPath, "");
|
|
19089
19259
|
}
|
|
19090
|
-
const root =
|
|
19091
|
-
const resolved =
|
|
19092
|
-
const rootWithSep = root.endsWith(
|
|
19260
|
+
const root = path11.resolve(options.root ?? process.cwd());
|
|
19261
|
+
const resolved = path11.isAbsolute(userPath) ? path11.resolve(userPath) : path11.resolve(root, userPath);
|
|
19262
|
+
const rootWithSep = root.endsWith(path11.sep) ? root : root + path11.sep;
|
|
19093
19263
|
if (resolved !== root && !resolved.startsWith(rootWithSep)) {
|
|
19094
19264
|
throw new SandboxViolationError(
|
|
19095
19265
|
`Path escapes sandbox root: ${userPath} \u2192 ${resolved} (root: ${root})`,
|
|
@@ -19152,8 +19322,8 @@ function assertShellAllowed(command) {
|
|
|
19152
19322
|
}
|
|
19153
19323
|
|
|
19154
19324
|
// src/cli/safety/auditLogger.ts
|
|
19155
|
-
import { promises as
|
|
19156
|
-
import
|
|
19325
|
+
import { promises as fs12 } from "node:fs";
|
|
19326
|
+
import path12 from "node:path";
|
|
19157
19327
|
import os7 from "node:os";
|
|
19158
19328
|
var AuditLogger = class {
|
|
19159
19329
|
logPath;
|
|
@@ -19172,8 +19342,8 @@ var AuditLogger = class {
|
|
|
19172
19342
|
async append(entry) {
|
|
19173
19343
|
const line = JSON.stringify(entry) + "\n";
|
|
19174
19344
|
this.writeQueue = this.writeQueue.then(async () => {
|
|
19175
|
-
await
|
|
19176
|
-
await
|
|
19345
|
+
await fs12.mkdir(path12.dirname(this.logPath), { recursive: true });
|
|
19346
|
+
await fs12.appendFile(this.logPath, line, "utf-8");
|
|
19177
19347
|
});
|
|
19178
19348
|
return this.writeQueue;
|
|
19179
19349
|
}
|
|
@@ -19216,7 +19386,7 @@ var AuditLogger = class {
|
|
|
19216
19386
|
function defaultAuditPath() {
|
|
19217
19387
|
const override = process.env.ANATHEMA_AUDIT_LOG;
|
|
19218
19388
|
if (override && override.trim().length > 0) return override;
|
|
19219
|
-
return
|
|
19389
|
+
return path12.join(os7.tmpdir(), "zelari-code", "audit.jsonl");
|
|
19220
19390
|
}
|
|
19221
19391
|
function redactArgs(args) {
|
|
19222
19392
|
const redacted = {};
|
|
@@ -19248,6 +19418,7 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
19248
19418
|
const safeWriteFile = wrapWithSandbox(writeFileTool, ["path"], root, audit, sessionId);
|
|
19249
19419
|
const safeEditFile = wrapWithSandbox(editFileTool, ["path"], root, audit, sessionId);
|
|
19250
19420
|
const safeGrepContent = wrapWithSandbox(grepContentTool, ["path"], root, audit, sessionId);
|
|
19421
|
+
const safeListFiles = wrapWithSandbox(listFilesTool, ["path"], root, audit, sessionId);
|
|
19251
19422
|
const safeBash = wrapWithShellSafety(bashTool, audit, sessionId);
|
|
19252
19423
|
const registry3 = new ToolRegistry();
|
|
19253
19424
|
registry3.register(safeReadFile);
|
|
@@ -19255,12 +19426,14 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
19255
19426
|
registry3.register(safeEditFile);
|
|
19256
19427
|
registry3.register(safeBash);
|
|
19257
19428
|
registry3.register(safeGrepContent);
|
|
19429
|
+
registry3.register(safeListFiles);
|
|
19258
19430
|
const tools = [
|
|
19259
19431
|
safeReadFile,
|
|
19260
19432
|
safeWriteFile,
|
|
19261
19433
|
safeEditFile,
|
|
19262
19434
|
safeBash,
|
|
19263
|
-
safeGrepContent
|
|
19435
|
+
safeGrepContent,
|
|
19436
|
+
safeListFiles
|
|
19264
19437
|
].map((t) => ({
|
|
19265
19438
|
name: t.name,
|
|
19266
19439
|
description: t.description,
|
|
@@ -19377,7 +19550,7 @@ function redactForAudit(args) {
|
|
|
19377
19550
|
// src/cli/gitOps.ts
|
|
19378
19551
|
import { execFile } from "node:child_process";
|
|
19379
19552
|
import { promisify } from "node:util";
|
|
19380
|
-
import
|
|
19553
|
+
import path13 from "node:path";
|
|
19381
19554
|
var execFileAsync = promisify(execFile);
|
|
19382
19555
|
async function git(cwd, args) {
|
|
19383
19556
|
try {
|
|
@@ -19423,7 +19596,7 @@ async function undoWorkingChanges(opts = {}) {
|
|
|
19423
19596
|
};
|
|
19424
19597
|
}
|
|
19425
19598
|
function defaultProjectRoot() {
|
|
19426
|
-
return
|
|
19599
|
+
return path13.resolve(__dirname, "..", "..", "..");
|
|
19427
19600
|
}
|
|
19428
19601
|
|
|
19429
19602
|
// src/cli/compaction.ts
|
|
@@ -19464,16 +19637,16 @@ function formatCompactionSummary(result) {
|
|
|
19464
19637
|
}
|
|
19465
19638
|
|
|
19466
19639
|
// src/cli/metrics.ts
|
|
19467
|
-
import { promises as
|
|
19468
|
-
import
|
|
19640
|
+
import { promises as fs13, existsSync as existsSync7, statSync as statSync3, renameSync, appendFileSync, mkdirSync as mkdirSync7 } from "node:fs";
|
|
19641
|
+
import path14 from "node:path";
|
|
19469
19642
|
import os8 from "node:os";
|
|
19470
19643
|
var METRICS_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
19471
19644
|
var MetricsLogger = class {
|
|
19472
19645
|
file;
|
|
19473
19646
|
writeQueue = Promise.resolve();
|
|
19474
19647
|
constructor(file2) {
|
|
19475
|
-
this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ??
|
|
19476
|
-
mkdirSync7(
|
|
19648
|
+
this.file = file2 ?? process.env.ANATHEMA_METRICS_FILE ?? path14.join(os8.homedir(), ".tmp", "zelari-code", "metrics.jsonl");
|
|
19649
|
+
mkdirSync7(path14.dirname(this.file), { recursive: true });
|
|
19477
19650
|
}
|
|
19478
19651
|
/** Fire-and-forget record append. */
|
|
19479
19652
|
record(rec) {
|
|
@@ -19524,8 +19697,8 @@ function getMetricsLogger() {
|
|
|
19524
19697
|
}
|
|
19525
19698
|
|
|
19526
19699
|
// src/cli/skillHistory.ts
|
|
19527
|
-
import { promises as
|
|
19528
|
-
import
|
|
19700
|
+
import { promises as fs14, existsSync as existsSync8, statSync as statSync4, renameSync as renameSync2, appendFileSync as appendFileSync2, mkdirSync as mkdirSync8 } from "node:fs";
|
|
19701
|
+
import path15 from "node:path";
|
|
19529
19702
|
import os9 from "node:os";
|
|
19530
19703
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
19531
19704
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
@@ -19534,8 +19707,8 @@ var SkillHistoryLogger = class {
|
|
|
19534
19707
|
writeQueue = Promise.resolve();
|
|
19535
19708
|
inflight = /* @__PURE__ */ new Map();
|
|
19536
19709
|
constructor(file2) {
|
|
19537
|
-
this.file = file2 ?? process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
19538
|
-
mkdirSync8(
|
|
19710
|
+
this.file = file2 ?? process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path15.join(os9.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
19711
|
+
mkdirSync8(path15.dirname(this.file), { recursive: true });
|
|
19539
19712
|
}
|
|
19540
19713
|
/**
|
|
19541
19714
|
* Mark the start of a skill invocation. Returns a unique invocationId
|
|
@@ -19613,7 +19786,7 @@ var SkillHistoryLogger = class {
|
|
|
19613
19786
|
async function readSkillHistory(file2) {
|
|
19614
19787
|
let raw = "";
|
|
19615
19788
|
try {
|
|
19616
|
-
raw = await
|
|
19789
|
+
raw = await fs14.readFile(file2, "utf-8");
|
|
19617
19790
|
} catch {
|
|
19618
19791
|
return [];
|
|
19619
19792
|
}
|
|
@@ -19649,7 +19822,7 @@ function getSkillStats(records, skillId, sinceTs) {
|
|
|
19649
19822
|
}
|
|
19650
19823
|
|
|
19651
19824
|
// src/cli/app.tsx
|
|
19652
|
-
import
|
|
19825
|
+
import path17 from "node:path";
|
|
19653
19826
|
import os10 from "node:os";
|
|
19654
19827
|
var MODEL = process.env.OPENAI_MODEL ?? "grok-4";
|
|
19655
19828
|
var PROVIDER = "openai-compatible";
|
|
@@ -19887,13 +20060,38 @@ function App() {
|
|
|
19887
20060
|
primary: baseProviderStream,
|
|
19888
20061
|
fallback: failoverResolution.fallback
|
|
19889
20062
|
});
|
|
20063
|
+
const cwd = process.cwd();
|
|
20064
|
+
const toolList = toolRegistry.toOpenAITools().map((t) => `- ${t.name}: ${t.description}`).join("\n");
|
|
20065
|
+
const systemPrompt = [
|
|
20066
|
+
"You are Zelari Code, an interactive AI coding agent operating directly in the user's terminal.",
|
|
20067
|
+
"",
|
|
20068
|
+
"You ARE connected to this machine and have real tools to read, modify, and explore the codebase.",
|
|
20069
|
+
"Never claim you lack filesystem or shell access \u2014 you have it. Use your tools instead of asking the user to paste file contents.",
|
|
20070
|
+
"",
|
|
20071
|
+
`# Working Directory`,
|
|
20072
|
+
`You are running in: ${cwd}`,
|
|
20073
|
+
"All relative file paths are resolved against this directory. Always work with real files here.",
|
|
20074
|
+
"",
|
|
20075
|
+
"# Available Tools",
|
|
20076
|
+
"You can call these tools. Use them to take action and gather information autonomously:",
|
|
20077
|
+
toolList,
|
|
20078
|
+
"",
|
|
20079
|
+
"# Guidelines",
|
|
20080
|
+
"- To understand a project, list files (bash: ls) and read key files (read_file), don't ask the user to paste them.",
|
|
20081
|
+
"- Be proactive: explore, read, and act. Prefer doing over asking.",
|
|
20082
|
+
"- When you finish a task, briefly summarize what you did."
|
|
20083
|
+
].join("\n");
|
|
19890
20084
|
const harness = new AgentHarness({
|
|
19891
20085
|
model: envConfig.model,
|
|
19892
20086
|
provider: PROVIDER,
|
|
19893
|
-
messages: [
|
|
20087
|
+
messages: [
|
|
20088
|
+
{ role: "system", content: systemPrompt },
|
|
20089
|
+
{ role: "user", content: userText }
|
|
20090
|
+
],
|
|
19894
20091
|
tools: toolRegistry.toOpenAITools(),
|
|
19895
20092
|
toolRegistry,
|
|
19896
|
-
providerStream
|
|
20093
|
+
providerStream,
|
|
20094
|
+
cwd
|
|
19897
20095
|
});
|
|
19898
20096
|
harnessRef.current = harness;
|
|
19899
20097
|
setQueueCount(harness.queueLength);
|
|
@@ -20418,9 +20616,9 @@ ${lines.join("\n")}`;
|
|
|
20418
20616
|
if (result.kind === "promote_member" && result.promoteMemberId) {
|
|
20419
20617
|
try {
|
|
20420
20618
|
const { skill, markdown } = promoteMember(result.promoteMemberId);
|
|
20421
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
20619
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path17.join(os10.homedir(), ".tmp", "zelari-code", "skills");
|
|
20422
20620
|
await fs.promises.mkdir(skillDir, { recursive: true });
|
|
20423
|
-
const filePath =
|
|
20621
|
+
const filePath = path17.join(skillDir, `${skill.id}.md`);
|
|
20424
20622
|
await fs.promises.writeFile(filePath, markdown, "utf8");
|
|
20425
20623
|
setMessages((prev) => [
|
|
20426
20624
|
...prev,
|
|
@@ -21150,7 +21348,7 @@ ${lines.join("\n")}`,
|
|
|
21150
21348
|
return;
|
|
21151
21349
|
}
|
|
21152
21350
|
if (result.kind === "skill_stats") {
|
|
21153
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
21351
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path17.join(os10.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
21154
21352
|
try {
|
|
21155
21353
|
const records = await readSkillHistory(historyFile);
|
|
21156
21354
|
const stats = getSkillStats(records, result.skillStatsSkillId);
|
|
@@ -21179,7 +21377,7 @@ ${lines.join("\n")}`,
|
|
|
21179
21377
|
setInput("");
|
|
21180
21378
|
return;
|
|
21181
21379
|
}
|
|
21182
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
21380
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path17.join(os10.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
21183
21381
|
try {
|
|
21184
21382
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
21185
21383
|
setMessages((prev) => [
|