zelari-code 1.22.1 → 1.24.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/headless.js +6 -0
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +4 -0
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/main.bundled.js +536 -265
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/missionSlice.js +19 -0
- package/dist/cli/missionSlice.js.map +1 -1
- package/dist/cli/runHeadless.js +18 -0
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/traceStore.js +84 -0
- package/dist/cli/traceStore.js.map +1 -0
- package/dist/cli/triggerLock.js +79 -0
- package/dist/cli/triggerLock.js.map +1 -0
- package/dist/cli/zelariMission.js +77 -0
- package/dist/cli/zelariMission.js.map +1 -1
- package/dist/cli/zelariMission.test.js +44 -0
- package/dist/cli/zelariMission.test.js.map +1 -0
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -21,6 +21,87 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
21
21
|
};
|
|
22
22
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
23
23
|
|
|
24
|
+
// src/cli/modelPricing.ts
|
|
25
|
+
function envOverride(model) {
|
|
26
|
+
const key = `ANATHEMA_PRICE_${model.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
27
|
+
const raw = process.env[key] ?? process.env.ANATHEMA_PRICE_DEFAULT;
|
|
28
|
+
return parseRateOverride(raw);
|
|
29
|
+
}
|
|
30
|
+
function parseRateOverride(raw) {
|
|
31
|
+
if (!raw) return null;
|
|
32
|
+
const [inStr, outStr] = raw.split("/");
|
|
33
|
+
const input = Number.parseFloat(inStr);
|
|
34
|
+
const output = outStr !== void 0 ? Number.parseFloat(outStr) : input;
|
|
35
|
+
if (!Number.isFinite(input) || !Number.isFinite(output)) return null;
|
|
36
|
+
return { input, output };
|
|
37
|
+
}
|
|
38
|
+
function getModelRate(model) {
|
|
39
|
+
if (!model) return DEFAULT_RATE;
|
|
40
|
+
return envOverride(model) ?? PRICES_PER_MILLION[model] ?? DEFAULT_RATE;
|
|
41
|
+
}
|
|
42
|
+
function calculateCost(model, promptTokens, completionTokens, cachedPromptTokens = 0) {
|
|
43
|
+
if (!Number.isFinite(promptTokens) || promptTokens < 0) return 0;
|
|
44
|
+
if (!Number.isFinite(completionTokens) || completionTokens < 0) return 0;
|
|
45
|
+
const rate = getModelRate(model);
|
|
46
|
+
const cached2 = Number.isFinite(cachedPromptTokens) && cachedPromptTokens > 0 ? Math.min(cachedPromptTokens, promptTokens) : 0;
|
|
47
|
+
const uncachedPrompt = promptTokens - cached2;
|
|
48
|
+
const cachedRate = rate.cachedInput ?? rate.input * DEFAULT_CACHE_DISCOUNT;
|
|
49
|
+
const inputCost = uncachedPrompt / 1e6 * rate.input + cached2 / 1e6 * cachedRate;
|
|
50
|
+
const outputCost = completionTokens / 1e6 * rate.output;
|
|
51
|
+
return Number((inputCost + outputCost).toFixed(6));
|
|
52
|
+
}
|
|
53
|
+
function formatCost(usd) {
|
|
54
|
+
if (!Number.isFinite(usd) || usd < 0) return "$0.0000";
|
|
55
|
+
if (usd === 0) return "$0.0000";
|
|
56
|
+
if (usd < 1e-4) return "<$0.0001";
|
|
57
|
+
return `$${usd.toFixed(4)}`;
|
|
58
|
+
}
|
|
59
|
+
function formatTokens(tokens) {
|
|
60
|
+
if (!Number.isFinite(tokens) || tokens < 0) return "0";
|
|
61
|
+
if (tokens < 1e3) return `${tokens}`;
|
|
62
|
+
if (tokens < 1e6) return `${(tokens / 1e3).toFixed(1)}k`;
|
|
63
|
+
if (tokens < 1e9) return `${(tokens / 1e6).toFixed(2)}M`;
|
|
64
|
+
return `${(tokens / 1e9).toFixed(2)}B`;
|
|
65
|
+
}
|
|
66
|
+
var PRICES_PER_MILLION, DEFAULT_RATE, DEFAULT_CACHE_DISCOUNT;
|
|
67
|
+
var init_modelPricing = __esm({
|
|
68
|
+
"src/cli/modelPricing.ts"() {
|
|
69
|
+
"use strict";
|
|
70
|
+
PRICES_PER_MILLION = {
|
|
71
|
+
// xAI Grok (list prices; grok-4.5 default reasoning_effort is "high")
|
|
72
|
+
"grok-4.5": { input: 2, output: 6, cachedInput: 0.5 },
|
|
73
|
+
"grok-4.3": { input: 1.25, output: 2.5, cachedInput: 0.2 },
|
|
74
|
+
"grok-4": { input: 3, output: 15 },
|
|
75
|
+
"grok-4-fast": { input: 0.2, output: 0.5 },
|
|
76
|
+
"grok-3": { input: 3, output: 15 },
|
|
77
|
+
"grok-3-mini": { input: 0.3, output: 0.5 },
|
|
78
|
+
"grok-2-vision": { input: 2, output: 10 },
|
|
79
|
+
// GLM / Z.AI
|
|
80
|
+
"glm-4.6": { input: 0.6, output: 2.2 },
|
|
81
|
+
"glm-4.5": { input: 0.5, output: 2 },
|
|
82
|
+
"glm-4.5-air": { input: 0.1, output: 0.6 },
|
|
83
|
+
"glm-z1": { input: 0.5, output: 2 },
|
|
84
|
+
// MiniMax
|
|
85
|
+
"MiniMax-M2.5": { input: 0.2, output: 1.1 },
|
|
86
|
+
"MiniMax-M2": { input: 0.2, output: 1.1 },
|
|
87
|
+
"MiniMax-M2-her": { input: 0.3, output: 1.2 },
|
|
88
|
+
// DeepSeek (global platform) — estimated list prices; override via
|
|
89
|
+
// ANATHEMA_PRICE_DEEPSEEK_V4_FLASH / ANATHEMA_PRICE_DEEPSEEK_V4_PRO.
|
|
90
|
+
// DeepSeek prompt-cache hits are ~10× cheaper than a cache miss.
|
|
91
|
+
"deepseek-v4-flash": { input: 0.14, output: 0.28, cachedInput: 0.014 },
|
|
92
|
+
"deepseek-v4-pro": { input: 0.55, output: 2.19, cachedInput: 0.055 },
|
|
93
|
+
// OpenAI (for openai-compatible fallback)
|
|
94
|
+
"gpt-4o": { input: 2.5, output: 10 },
|
|
95
|
+
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
96
|
+
"gpt-4-turbo": { input: 10, output: 30 },
|
|
97
|
+
"o1-preview": { input: 15, output: 60 },
|
|
98
|
+
"o1-mini": { input: 3, output: 12 }
|
|
99
|
+
};
|
|
100
|
+
DEFAULT_RATE = { input: 1, output: 3 };
|
|
101
|
+
DEFAULT_CACHE_DISCOUNT = 0.25;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
24
105
|
// src/cli/grokOAuth.ts
|
|
25
106
|
var grokOAuth_exports = {};
|
|
26
107
|
__export(grokOAuth_exports, {
|
|
@@ -1543,10 +1624,10 @@ function mergeDefs(...defs) {
|
|
|
1543
1624
|
function cloneDef(schema) {
|
|
1544
1625
|
return mergeDefs(schema._zod.def);
|
|
1545
1626
|
}
|
|
1546
|
-
function getElementAtPath(obj,
|
|
1547
|
-
if (!
|
|
1627
|
+
function getElementAtPath(obj, path40) {
|
|
1628
|
+
if (!path40)
|
|
1548
1629
|
return obj;
|
|
1549
|
-
return
|
|
1630
|
+
return path40.reduce((acc, key) => acc?.[key], obj);
|
|
1550
1631
|
}
|
|
1551
1632
|
function promiseAllObject(promisesObj) {
|
|
1552
1633
|
const keys = Object.keys(promisesObj);
|
|
@@ -1874,11 +1955,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
1874
1955
|
}
|
|
1875
1956
|
return false;
|
|
1876
1957
|
}
|
|
1877
|
-
function prefixIssues(
|
|
1958
|
+
function prefixIssues(path40, issues) {
|
|
1878
1959
|
return issues.map((iss) => {
|
|
1879
1960
|
var _a3;
|
|
1880
1961
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
1881
|
-
iss.path.unshift(
|
|
1962
|
+
iss.path.unshift(path40);
|
|
1882
1963
|
return iss;
|
|
1883
1964
|
});
|
|
1884
1965
|
}
|
|
@@ -2096,16 +2177,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2096
2177
|
}
|
|
2097
2178
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2098
2179
|
const fieldErrors = { _errors: [] };
|
|
2099
|
-
const processError = (error52,
|
|
2180
|
+
const processError = (error52, path40 = []) => {
|
|
2100
2181
|
for (const issue2 of error52.issues) {
|
|
2101
2182
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2102
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2183
|
+
issue2.errors.map((issues) => processError({ issues }, [...path40, ...issue2.path]));
|
|
2103
2184
|
} else if (issue2.code === "invalid_key") {
|
|
2104
|
-
processError({ issues: issue2.issues }, [...
|
|
2185
|
+
processError({ issues: issue2.issues }, [...path40, ...issue2.path]);
|
|
2105
2186
|
} else if (issue2.code === "invalid_element") {
|
|
2106
|
-
processError({ issues: issue2.issues }, [...
|
|
2187
|
+
processError({ issues: issue2.issues }, [...path40, ...issue2.path]);
|
|
2107
2188
|
} else {
|
|
2108
|
-
const fullpath = [...
|
|
2189
|
+
const fullpath = [...path40, ...issue2.path];
|
|
2109
2190
|
if (fullpath.length === 0) {
|
|
2110
2191
|
fieldErrors._errors.push(mapper(issue2));
|
|
2111
2192
|
} else {
|
|
@@ -2132,17 +2213,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2132
2213
|
}
|
|
2133
2214
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2134
2215
|
const result = { errors: [] };
|
|
2135
|
-
const processError = (error52,
|
|
2216
|
+
const processError = (error52, path40 = []) => {
|
|
2136
2217
|
var _a3, _b;
|
|
2137
2218
|
for (const issue2 of error52.issues) {
|
|
2138
2219
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2139
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2220
|
+
issue2.errors.map((issues) => processError({ issues }, [...path40, ...issue2.path]));
|
|
2140
2221
|
} else if (issue2.code === "invalid_key") {
|
|
2141
|
-
processError({ issues: issue2.issues }, [...
|
|
2222
|
+
processError({ issues: issue2.issues }, [...path40, ...issue2.path]);
|
|
2142
2223
|
} else if (issue2.code === "invalid_element") {
|
|
2143
|
-
processError({ issues: issue2.issues }, [...
|
|
2224
|
+
processError({ issues: issue2.issues }, [...path40, ...issue2.path]);
|
|
2144
2225
|
} else {
|
|
2145
|
-
const fullpath = [...
|
|
2226
|
+
const fullpath = [...path40, ...issue2.path];
|
|
2146
2227
|
if (fullpath.length === 0) {
|
|
2147
2228
|
result.errors.push(mapper(issue2));
|
|
2148
2229
|
continue;
|
|
@@ -2174,8 +2255,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2174
2255
|
}
|
|
2175
2256
|
function toDotPath(_path) {
|
|
2176
2257
|
const segs = [];
|
|
2177
|
-
const
|
|
2178
|
-
for (const seg of
|
|
2258
|
+
const path40 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2259
|
+
for (const seg of path40) {
|
|
2179
2260
|
if (typeof seg === "number")
|
|
2180
2261
|
segs.push(`[${seg}]`);
|
|
2181
2262
|
else if (typeof seg === "symbol")
|
|
@@ -15678,13 +15759,13 @@ function resolveRef(ref, ctx) {
|
|
|
15678
15759
|
if (!ref.startsWith("#")) {
|
|
15679
15760
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
15680
15761
|
}
|
|
15681
|
-
const
|
|
15682
|
-
if (
|
|
15762
|
+
const path40 = ref.slice(1).split("/").filter(Boolean);
|
|
15763
|
+
if (path40.length === 0) {
|
|
15683
15764
|
return ctx.rootSchema;
|
|
15684
15765
|
}
|
|
15685
15766
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
15686
|
-
if (
|
|
15687
|
-
const key =
|
|
15767
|
+
if (path40[0] === defsKey) {
|
|
15768
|
+
const key = path40[1];
|
|
15688
15769
|
if (!key || !ctx.defs[key]) {
|
|
15689
15770
|
throw new Error(`Reference not found: ${ref}`);
|
|
15690
15771
|
}
|
|
@@ -17848,11 +17929,11 @@ var init_tools = __esm({
|
|
|
17848
17929
|
if (!ctx.addDocument)
|
|
17849
17930
|
return "Knowledge vault tool not available.";
|
|
17850
17931
|
const title = args["title"] || "New Document";
|
|
17851
|
-
const
|
|
17932
|
+
const path40 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
17852
17933
|
const content = args["content"] || "";
|
|
17853
17934
|
const tags = args["tags"] || [];
|
|
17854
17935
|
ctx.addDocument({
|
|
17855
|
-
path:
|
|
17936
|
+
path: path40,
|
|
17856
17937
|
title,
|
|
17857
17938
|
content,
|
|
17858
17939
|
format: "markdown",
|
|
@@ -17861,7 +17942,7 @@ var init_tools = __esm({
|
|
|
17861
17942
|
workspaceId: ctx.workspaceId
|
|
17862
17943
|
});
|
|
17863
17944
|
ctx.addActivity("vault", "created document", title);
|
|
17864
|
-
return `Document "${title}" created at "${
|
|
17945
|
+
return `Document "${title}" created at "${path40}".`;
|
|
17865
17946
|
}
|
|
17866
17947
|
}
|
|
17867
17948
|
];
|
|
@@ -18357,6 +18438,7 @@ function getBasePromptModules(mode = "council") {
|
|
|
18357
18438
|
BEHAVIOR_AGENT,
|
|
18358
18439
|
SAFETY,
|
|
18359
18440
|
CODING_PRACTICES_MODULE,
|
|
18441
|
+
TURN_COMPLETION_MODULE,
|
|
18360
18442
|
OUTPUT_QUALITY_DIRECTIVE,
|
|
18361
18443
|
OUTPUT_FORMATTING,
|
|
18362
18444
|
// Same structured clarification format as council — one question when blocked.
|
|
@@ -18382,7 +18464,7 @@ function getBasePromptModules(mode = "council") {
|
|
|
18382
18464
|
function getPromptModule(type) {
|
|
18383
18465
|
return getBasePromptModules("council").find((m) => m.type === type);
|
|
18384
18466
|
}
|
|
18385
|
-
var CODING_CAPABLE_IDENTITY, COUNCIL_IDENTITY, BEHAVIOR_AGENT, BEHAVIOR_COUNCIL, SAFETY, CONTEXT_SHARING_COUNCIL, OUTPUT_FORMATTING, NATIVE_TOOL_PROTOCOL_MODULE, CODING_PRACTICES_MODULE, CLARIFICATION_PROTOCOL_MODULE, PROMPT_MODULES, SINGLE_AGENT_IDENTITY_MODULE;
|
|
18467
|
+
var CODING_CAPABLE_IDENTITY, COUNCIL_IDENTITY, BEHAVIOR_AGENT, BEHAVIOR_COUNCIL, SAFETY, CONTEXT_SHARING_COUNCIL, OUTPUT_FORMATTING, NATIVE_TOOL_PROTOCOL_MODULE, CODING_PRACTICES_MODULE, TURN_COMPLETION_MODULE, CLARIFICATION_PROTOCOL_MODULE, PROMPT_MODULES, SINGLE_AGENT_IDENTITY_MODULE;
|
|
18386
18468
|
var init_promptModules = __esm({
|
|
18387
18469
|
"packages/core/dist/agents/promptModules.js"() {
|
|
18388
18470
|
"use strict";
|
|
@@ -18418,7 +18500,9 @@ Earlier members' outputs are shared context. Build on them; do not re-derive or
|
|
|
18418
18500
|
- Prefer action over description when the user wants code, fixes, or repo changes \u2014 use tools (write_file/edit_file/bash), not prose-only plans.
|
|
18419
18501
|
- When the user confirms a plan ("procedi", "s\xEC", "implementa"), the prior plan is work TO DO on disk. Reading alone is incomplete.
|
|
18420
18502
|
- Never claim work is "already implemented" without verifying the real files (and writing if gaps remain).
|
|
18421
|
-
- Think step by step internally; surface conclusions and a brief rationale, not a full chain of thought
|
|
18503
|
+
- Think step by step internally; surface conclusions and a brief rationale, not a full chain of thought.
|
|
18504
|
+
- **No status theater**: never re-emit the same "I will create X / updating todos / next file Y" paragraph. Either call tools or stop.
|
|
18505
|
+
- **Turn must end cleanly** (see Turn Completion Contract): finish the requested slice, or stop with a short report and ask whether to continue \u2014 never hang in a loop of intentions.`
|
|
18422
18506
|
};
|
|
18423
18507
|
BEHAVIOR_COUNCIL = {
|
|
18424
18508
|
type: "behavior-rules",
|
|
@@ -18480,6 +18564,7 @@ Earlier members' outputs are shared context. Build on them; do not re-derive or
|
|
|
18480
18564
|
- Prefer tools over asking the user to paste file contents.
|
|
18481
18565
|
- After durable changes, briefly name what you created or modified.
|
|
18482
18566
|
- **Act, don't narrate**: if you will edit/fix files, call the tools in this turn. Do not restate the same diagnosis or "I will fix\u2026" plan on a loop without tool calls.
|
|
18567
|
+
- **Ban status loops**: phrases like "Aggiorno todo", "Procedo con", "Ora creo", "Next I will write" must be followed by a real tool call in the same turn \u2014 or stop and ask to continue.
|
|
18483
18568
|
- Text-only tool blocks (\`---TOOLS---\` JSON) are a legacy fallback \u2014 use them only if the runtime has no native tool channel.`
|
|
18484
18569
|
};
|
|
18485
18570
|
CODING_PRACTICES_MODULE = {
|
|
@@ -18496,7 +18581,37 @@ Earlier members' outputs are shared context. Build on them; do not re-derive or
|
|
|
18496
18581
|
- **Use project scripts**: prefer package.json / Makefile / existing tooling over ad-hoc commands.
|
|
18497
18582
|
- **Finish**: list the paths you wrote/edited and how to verify. If you wrote nothing, say so honestly \u2014 do not invent a done report.
|
|
18498
18583
|
- **No spam**: never repeat the same paragraph or status line. One diagnosis, then tools (or one short final answer).
|
|
18584
|
+
- **Scope large builds**: for multi-file / game / MVP work, implement a thin vertical slice per turn (e.g. one module + wire-up). Do not try to ship an entire product in one endless monologue.
|
|
18499
18585
|
- **Browser smoke honesty**: with \`browser_check\`, prefer \`waitForSelector\` / \`waitForText\` / \`evaluate\` on DOM (or explicit test hooks). Do not claim a logic fix is verified from \u201Cno console errors after N seconds\u201D alone (\`smokeStrength: weak\`). ES modules keep symbols off \`window\` \u2014 do not loop on exposing globals; assert visible UI or inject \`evaluate\` on \`document\`.`
|
|
18586
|
+
};
|
|
18587
|
+
TURN_COMPLETION_MODULE = {
|
|
18588
|
+
type: "custom",
|
|
18589
|
+
title: "Turn Completion",
|
|
18590
|
+
priority: 48,
|
|
18591
|
+
content: `# Turn Completion Contract (mandatory)
|
|
18592
|
+
|
|
18593
|
+
Every assistant turn MUST end in exactly one of these ways:
|
|
18594
|
+
|
|
18595
|
+
## A) Done
|
|
18596
|
+
- You finished the user's request (or the agreed slice).
|
|
18597
|
+
- List paths changed + how to verify.
|
|
18598
|
+
- Stop. Do not start the next major feature unless asked.
|
|
18599
|
+
|
|
18600
|
+
## B) Checkpoint \u2014 ask to continue
|
|
18601
|
+
- You made real progress (tools ran; files on disk) but more remains.
|
|
18602
|
+
- Give a short resoconto: what is done, what is next (3\u20136 bullets max).
|
|
18603
|
+
- **Ask the user** whether to continue (prefer \`ask_user\` with choices like Continue / Stop / Change priority).
|
|
18604
|
+
- Do **not** silently start the next big module in the same turn after a long status monologue.
|
|
18605
|
+
|
|
18606
|
+
## C) Blocked \u2014 one question
|
|
18607
|
+
- Use the Clarification Protocol once, then wait.
|
|
18608
|
+
|
|
18609
|
+
## Forbidden
|
|
18610
|
+
- Repeating "I will create X / updating todos / next I will write Y" without tool calls.
|
|
18611
|
+
- Narrating a multi-file roadmap and never writing, or writing forever without a stop.
|
|
18612
|
+
- Ending a turn mid-"procedo con\u2026" loop.
|
|
18613
|
+
|
|
18614
|
+
If the remaining work is large, **choose B** early: deliver one solid slice, report, ask.`
|
|
18500
18615
|
};
|
|
18501
18616
|
CLARIFICATION_PROTOCOL_MODULE = {
|
|
18502
18617
|
type: "custom",
|
|
@@ -18544,7 +18659,7 @@ You are Zelari Code, an interactive AI coding agent in the user's terminal (or d
|
|
|
18544
18659
|
|
|
18545
18660
|
You ARE connected to this machine and have real tools to read, modify, and explore the codebase. Never claim you lack filesystem or shell access \u2014 you have it. Use tools instead of asking the user to paste file contents.
|
|
18546
18661
|
|
|
18547
|
-
Be proactive: list and read key files before changing code. When you finish, briefly summarize what you did and how to verify it.`
|
|
18662
|
+
Be proactive: list and read key files before changing code. When you finish a slice, briefly summarize what you did and how to verify it. If more work remains, stop with a short resoconto and ask whether to continue \u2014 do not monologue forever.`
|
|
18548
18663
|
};
|
|
18549
18664
|
}
|
|
18550
18665
|
});
|
|
@@ -19241,17 +19356,27 @@ var init_events = __esm({
|
|
|
19241
19356
|
});
|
|
19242
19357
|
|
|
19243
19358
|
// packages/core/dist/core/textLoopDetect.js
|
|
19359
|
+
function isStatusTheaterUnit(unit) {
|
|
19360
|
+
const u = normalizeLoopUnit(unit).toLowerCase();
|
|
19361
|
+
if (u.length < 32)
|
|
19362
|
+
return false;
|
|
19363
|
+
return /\b(procedo|procediamo|continuo|continuiamo)\b/.test(u) || /\b(aggiorno|updating)\s+(il\s+)?todo\b/.test(u) || /\bora\s+(creo|creo|scrivo|implemento|faccio)\b/.test(u) || /\b(next\s+i\s+will|i\s+will\s+(now\s+)?(create|write|implement|update))\b/.test(u) || /\b(let\s+me\s+(now\s+)?(create|write|update|proceed))\b/.test(u) || /\b(fatto\.?\s*(ora|next|procedo)|bene\.?\s*(ora|procedo|aggiorno))\b/.test(u) || /\b(todo\s+(list\s+)?updated|moving\s+on\s+to)\b/.test(u);
|
|
19364
|
+
}
|
|
19244
19365
|
function normalizeLoopUnit(s) {
|
|
19245
19366
|
return s.replace(/<\/?small>/gi, "").replace(/<\/?(?:p|div|span|b|i|em|strong|br)\b[^>]*>/gi, "").replace(/\s+/g, " ").trim();
|
|
19246
19367
|
}
|
|
19247
19368
|
function detectChunkSequenceLoop(chunks, kind) {
|
|
19248
|
-
if (chunks.length <
|
|
19369
|
+
if (chunks.length < MIN_REPEATS_STATUS)
|
|
19249
19370
|
return { looping: false };
|
|
19250
|
-
const maxK = Math.min(12, Math.floor(chunks.length /
|
|
19371
|
+
const maxK = Math.min(12, Math.floor(chunks.length / MIN_REPEATS_STATUS));
|
|
19251
19372
|
for (let k = 1; k <= maxK; k++) {
|
|
19252
19373
|
const unitParts = chunks.slice(chunks.length - k);
|
|
19253
19374
|
const unitKey = unitParts.join("\n");
|
|
19254
|
-
|
|
19375
|
+
const norm = normalizeLoopUnit(unitKey);
|
|
19376
|
+
if (norm.length < MIN_UNIT)
|
|
19377
|
+
continue;
|
|
19378
|
+
const need = isStatusTheaterUnit(unitKey) ? MIN_REPEATS_STATUS : MIN_REPEATS;
|
|
19379
|
+
if (chunks.length < need * k)
|
|
19255
19380
|
continue;
|
|
19256
19381
|
let count = 1;
|
|
19257
19382
|
let pos = chunks.length - k;
|
|
@@ -19269,7 +19394,7 @@ function detectChunkSequenceLoop(chunks, kind) {
|
|
|
19269
19394
|
count++;
|
|
19270
19395
|
pos -= k;
|
|
19271
19396
|
}
|
|
19272
|
-
if (count >=
|
|
19397
|
+
if (count >= need) {
|
|
19273
19398
|
return {
|
|
19274
19399
|
looping: true,
|
|
19275
19400
|
unit: unitKey,
|
|
@@ -19282,19 +19407,20 @@ function detectChunkSequenceLoop(chunks, kind) {
|
|
|
19282
19407
|
}
|
|
19283
19408
|
function detectSuffixPeriod(text) {
|
|
19284
19409
|
const compact = text.replace(/\s+/g, " ").trim();
|
|
19285
|
-
if (compact.length < MIN_UNIT *
|
|
19410
|
+
if (compact.length < MIN_UNIT * MIN_REPEATS_STATUS)
|
|
19286
19411
|
return { looping: false };
|
|
19287
19412
|
const tail = compact.slice(-Math.min(compact.length, MAX_TAIL));
|
|
19288
|
-
const maxP = Math.min(MAX_PERIOD, Math.floor(tail.length /
|
|
19413
|
+
const maxP = Math.min(MAX_PERIOD, Math.floor(tail.length / MIN_REPEATS_STATUS));
|
|
19289
19414
|
for (let period = MIN_UNIT; period <= maxP; period++) {
|
|
19290
|
-
const need = period * MIN_REPEATS;
|
|
19291
|
-
if (tail.length < need)
|
|
19292
|
-
continue;
|
|
19293
19415
|
const unit = tail.slice(tail.length - period);
|
|
19294
19416
|
if (normalizeLoopUnit(unit).length < MIN_UNIT * 0.55)
|
|
19295
19417
|
continue;
|
|
19418
|
+
const need = isStatusTheaterUnit(unit) ? MIN_REPEATS_STATUS : MIN_REPEATS;
|
|
19419
|
+
const needChars = period * need;
|
|
19420
|
+
if (tail.length < needChars)
|
|
19421
|
+
continue;
|
|
19296
19422
|
let ok = true;
|
|
19297
|
-
for (let r = 1; r <
|
|
19423
|
+
for (let r = 1; r < need; r++) {
|
|
19298
19424
|
const start = tail.length - period * (r + 1);
|
|
19299
19425
|
if (tail.slice(start, start + period) !== unit) {
|
|
19300
19426
|
ok = false;
|
|
@@ -19303,8 +19429,8 @@ function detectSuffixPeriod(text) {
|
|
|
19303
19429
|
}
|
|
19304
19430
|
if (!ok)
|
|
19305
19431
|
continue;
|
|
19306
|
-
let count =
|
|
19307
|
-
let end = tail.length - period *
|
|
19432
|
+
let count = need;
|
|
19433
|
+
let end = tail.length - period * need;
|
|
19308
19434
|
while (end >= period && tail.slice(end - period, end) === unit) {
|
|
19309
19435
|
count++;
|
|
19310
19436
|
end -= period;
|
|
@@ -19319,7 +19445,7 @@ function detectSuffixPeriod(text) {
|
|
|
19319
19445
|
return { looping: false };
|
|
19320
19446
|
}
|
|
19321
19447
|
function detectAssistantTextLoop(text) {
|
|
19322
|
-
if (!text || text.length < MIN_UNIT *
|
|
19448
|
+
if (!text || text.length < MIN_UNIT * MIN_REPEATS_STATUS) {
|
|
19323
19449
|
return { looping: false };
|
|
19324
19450
|
}
|
|
19325
19451
|
const nl = text.replace(/\r\n/g, "\n");
|
|
@@ -19339,7 +19465,8 @@ function collapseLoopedAssistantText(text) {
|
|
|
19339
19465
|
return text;
|
|
19340
19466
|
const note = `
|
|
19341
19467
|
|
|
19342
|
-
[system: stopped repeating the same text \xD7${hit.count}
|
|
19468
|
+
[system: stopped repeating the same text \xD7${hit.count}.]
|
|
19469
|
+
` + TEXT_LOOP_RECOVERY_SYSTEM;
|
|
19343
19470
|
if (hit.kind === "paragraph" || hit.kind === "line") {
|
|
19344
19471
|
const joinSep = hit.kind === "paragraph" ? "\n\n" : "\n";
|
|
19345
19472
|
const unitParts = hit.unit.split("\n").map((p3) => normalizeLoopUnit(p3));
|
|
@@ -19347,7 +19474,8 @@ function collapseLoopedAssistantText(text) {
|
|
|
19347
19474
|
if (k === 0)
|
|
19348
19475
|
return text + note;
|
|
19349
19476
|
const rawParts = text.replace(/\r\n/g, "\n").split(hit.kind === "paragraph" ? /\n\s*\n+/ : /\n/).filter((p3) => normalizeLoopUnit(p3).length > 0);
|
|
19350
|
-
|
|
19477
|
+
const minNeed = isStatusTheaterUnit(hit.unit) ? MIN_REPEATS_STATUS : MIN_REPEATS;
|
|
19478
|
+
if (rawParts.length < k * minNeed) {
|
|
19351
19479
|
return collapseByCharBudget(text, hit.unit, hit.count) + note;
|
|
19352
19480
|
}
|
|
19353
19481
|
let trailBlocks = 0;
|
|
@@ -19365,10 +19493,10 @@ function collapseLoopedAssistantText(text) {
|
|
|
19365
19493
|
trailBlocks++;
|
|
19366
19494
|
pos -= k;
|
|
19367
19495
|
}
|
|
19368
|
-
if (trailBlocks <
|
|
19496
|
+
if (trailBlocks < minNeed) {
|
|
19369
19497
|
return collapseByCharBudget(text, hit.unit, hit.count) + note;
|
|
19370
19498
|
}
|
|
19371
|
-
const keepBlocks = 2;
|
|
19499
|
+
const keepBlocks = Math.min(2, trailBlocks);
|
|
19372
19500
|
const keepParts = rawParts.slice(0, rawParts.length - k * (trailBlocks - keepBlocks));
|
|
19373
19501
|
return keepParts.join(joinSep).trimEnd() + note;
|
|
19374
19502
|
}
|
|
@@ -19376,18 +19504,38 @@ function collapseLoopedAssistantText(text) {
|
|
|
19376
19504
|
}
|
|
19377
19505
|
function collapseByCharBudget(text, unit, count) {
|
|
19378
19506
|
const unitLen = Math.max(unit.length, MIN_UNIT);
|
|
19379
|
-
if (count <
|
|
19507
|
+
if (count < 2)
|
|
19380
19508
|
return text;
|
|
19381
|
-
const
|
|
19509
|
+
const keep = Math.min(2, count);
|
|
19510
|
+
const drop = unitLen * (count - keep);
|
|
19382
19511
|
const cut = Math.max(0, text.length - drop);
|
|
19383
19512
|
return text.slice(0, cut).trimEnd();
|
|
19384
19513
|
}
|
|
19385
|
-
var MIN_UNIT, MIN_REPEATS, MAX_TAIL, MAX_PERIOD;
|
|
19514
|
+
var TEXT_LOOP_RECOVERY_SYSTEM, TEXT_LOOP_RECOVERY_USER_PROMPT, MIN_UNIT, MIN_REPEATS, MIN_REPEATS_STATUS, MAX_TAIL, MAX_PERIOD;
|
|
19386
19515
|
var init_textLoopDetect = __esm({
|
|
19387
19516
|
"packages/core/dist/core/textLoopDetect.js"() {
|
|
19388
19517
|
"use strict";
|
|
19518
|
+
TEXT_LOOP_RECOVERY_SYSTEM = [
|
|
19519
|
+
"[text-loop recovery] Your previous reply was stopped: you repeated status text",
|
|
19520
|
+
"instead of finishing (no clean conclusion).",
|
|
19521
|
+
"",
|
|
19522
|
+
"On the next turn you MUST:",
|
|
19523
|
+
"1) list_files / read_file what already exists (do not re-plan the whole project).",
|
|
19524
|
+
"2) Apply at most ONE small disk change with write_file/edit_file if still needed.",
|
|
19525
|
+
"3) Then STOP with either:",
|
|
19526
|
+
" A) Done \u2014 paths changed + how to verify, or",
|
|
19527
|
+
" B) Short resoconto (done / next) and ask_user whether to continue.",
|
|
19528
|
+
'4) Forbidden: more "procedo con / aggiorno todo / ora creo" monologue without tools.'
|
|
19529
|
+
].join("\n");
|
|
19530
|
+
TEXT_LOOP_RECOVERY_USER_PROMPT = [
|
|
19531
|
+
"Continue from the text-loop stop.",
|
|
19532
|
+
"Inspect disk, apply at most one missing piece with tools if needed,",
|
|
19533
|
+
"then either mark DONE with a short verify list OR give a brief resoconto and ask if I want you to continue.",
|
|
19534
|
+
"No status theater, no full rewrite."
|
|
19535
|
+
].join(" ");
|
|
19389
19536
|
MIN_UNIT = 48;
|
|
19390
19537
|
MIN_REPEATS = 3;
|
|
19538
|
+
MIN_REPEATS_STATUS = 2;
|
|
19391
19539
|
MAX_TAIL = 6e3;
|
|
19392
19540
|
MAX_PERIOD = 600;
|
|
19393
19541
|
}
|
|
@@ -20121,7 +20269,7 @@ ${shared2.content}`,
|
|
|
20121
20269
|
if (loopHit.looping) {
|
|
20122
20270
|
const loopErr = createBrainEvent("error", this.sessionId, {
|
|
20123
20271
|
severity: "recoverable",
|
|
20124
|
-
message: `Assistant text loop detected (same block \xD7${loopHit.count}). Stopped generation \u2014 model was repeating instead of calling tools or finishing.
|
|
20272
|
+
message: `Assistant text loop detected (same block \xD7${loopHit.count}). Stopped generation \u2014 model was repeating instead of calling tools or finishing. Recovery injected: next turn should use tools only (inspect disk \u2192 one write). Or send: "${TEXT_LOOP_RECOVERY_USER_PROMPT.slice(0, 80)}\u2026"`,
|
|
20125
20273
|
code: "assistant_text_loop"
|
|
20126
20274
|
});
|
|
20127
20275
|
this.emit(loopErr);
|
|
@@ -20135,6 +20283,13 @@ ${shared2.content}`,
|
|
|
20135
20283
|
...turnReasoning.length > 0 ? { reasoningContent: turnReasoning } : {}
|
|
20136
20284
|
});
|
|
20137
20285
|
}
|
|
20286
|
+
const last = this.config.messages[this.config.messages.length - 1];
|
|
20287
|
+
if (!(last?.role === "system" && typeof last.content === "string" && last.content.includes("[text-loop recovery]"))) {
|
|
20288
|
+
this.config.messages.push({
|
|
20289
|
+
role: "system",
|
|
20290
|
+
content: TEXT_LOOP_RECOVERY_SYSTEM
|
|
20291
|
+
});
|
|
20292
|
+
}
|
|
20138
20293
|
break;
|
|
20139
20294
|
}
|
|
20140
20295
|
}
|
|
@@ -20560,9 +20715,12 @@ __export(harness_exports, {
|
|
|
20560
20715
|
AgentHarness: () => AgentHarness,
|
|
20561
20716
|
DOOM_LOOP_THRESHOLD: () => DOOM_LOOP_THRESHOLD,
|
|
20562
20717
|
SessionJsonlWriter: () => SessionJsonlWriter,
|
|
20718
|
+
TEXT_LOOP_RECOVERY_SYSTEM: () => TEXT_LOOP_RECOVERY_SYSTEM,
|
|
20719
|
+
TEXT_LOOP_RECOVERY_USER_PROMPT: () => TEXT_LOOP_RECOVERY_USER_PROMPT,
|
|
20563
20720
|
collapseLoopedAssistantText: () => collapseLoopedAssistantText,
|
|
20564
20721
|
detectAssistantTextLoop: () => detectAssistantTextLoop,
|
|
20565
20722
|
hashToolCall: () => hashToolCall,
|
|
20723
|
+
isStatusTheaterUnit: () => isStatusTheaterUnit,
|
|
20566
20724
|
normalizeLoopUnit: () => normalizeLoopUnit,
|
|
20567
20725
|
normalizeTextToolArgs: () => normalizeTextToolArgs,
|
|
20568
20726
|
parseMinimaxStyleToolCalls: () => parseMinimaxStyleToolCalls,
|
|
@@ -20982,9 +21140,9 @@ function spillToolOutput(fullText, meta3) {
|
|
|
20982
21140
|
const rnd = randomBytes(3).toString("hex");
|
|
20983
21141
|
const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
|
|
20984
21142
|
const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
|
|
20985
|
-
const
|
|
20986
|
-
writeFileSync5(
|
|
20987
|
-
return
|
|
21143
|
+
const path40 = join(dir, file2);
|
|
21144
|
+
writeFileSync5(path40, fullText, "utf8");
|
|
21145
|
+
return path40;
|
|
20988
21146
|
} catch {
|
|
20989
21147
|
return null;
|
|
20990
21148
|
}
|
|
@@ -21030,10 +21188,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
|
|
|
21030
21188
|
${tail}`;
|
|
21031
21189
|
}
|
|
21032
21190
|
if (doSpill) {
|
|
21033
|
-
const
|
|
21034
|
-
if (
|
|
21191
|
+
const path40 = spillToolOutput(text, { toolName: opts.toolName });
|
|
21192
|
+
if (path40) {
|
|
21035
21193
|
const spillNote = `
|
|
21036
|
-
\u2026 [full output spilled to: ${
|
|
21194
|
+
\u2026 [full output spilled to: ${path40} \u2014 re-read with read_file if you need the complete text] \u2026`;
|
|
21037
21195
|
if (preview.includes("] \u2026\n")) {
|
|
21038
21196
|
preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
|
|
21039
21197
|
`);
|
|
@@ -23465,21 +23623,21 @@ function normalizeAuth(auth) {
|
|
|
23465
23623
|
return "agent";
|
|
23466
23624
|
}
|
|
23467
23625
|
function readSecrets() {
|
|
23468
|
-
const
|
|
23469
|
-
if (!existsSync11(
|
|
23626
|
+
const path40 = getSshSecretsPath();
|
|
23627
|
+
if (!existsSync11(path40)) return {};
|
|
23470
23628
|
try {
|
|
23471
|
-
return JSON.parse(readFileSync8(
|
|
23629
|
+
return JSON.parse(readFileSync8(path40, "utf8"));
|
|
23472
23630
|
} catch {
|
|
23473
23631
|
return {};
|
|
23474
23632
|
}
|
|
23475
23633
|
}
|
|
23476
23634
|
function writeSecrets(data) {
|
|
23477
|
-
const
|
|
23478
|
-
mkdirSync7(dirname2(
|
|
23479
|
-
writeFileSync6(
|
|
23635
|
+
const path40 = getSshSecretsPath();
|
|
23636
|
+
mkdirSync7(dirname2(path40), { recursive: true });
|
|
23637
|
+
writeFileSync6(path40, `${JSON.stringify(data, null, 2)}
|
|
23480
23638
|
`, "utf8");
|
|
23481
23639
|
try {
|
|
23482
|
-
chmodSync(
|
|
23640
|
+
chmodSync(path40, 384);
|
|
23483
23641
|
} catch {
|
|
23484
23642
|
}
|
|
23485
23643
|
}
|
|
@@ -23508,10 +23666,10 @@ function deleteSshPassword(id) {
|
|
|
23508
23666
|
writeSecrets({ passwords });
|
|
23509
23667
|
}
|
|
23510
23668
|
function readStore2() {
|
|
23511
|
-
const
|
|
23512
|
-
if (!existsSync11(
|
|
23669
|
+
const path40 = getSshTargetsPath();
|
|
23670
|
+
if (!existsSync11(path40)) return [];
|
|
23513
23671
|
try {
|
|
23514
|
-
const parsed = JSON.parse(readFileSync8(
|
|
23672
|
+
const parsed = JSON.parse(readFileSync8(path40, "utf8"));
|
|
23515
23673
|
const list = Array.isArray(parsed.targets) ? parsed.targets : [];
|
|
23516
23674
|
return list.filter(
|
|
23517
23675
|
(t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
|
|
@@ -23526,11 +23684,11 @@ function readStore2() {
|
|
|
23526
23684
|
}
|
|
23527
23685
|
}
|
|
23528
23686
|
function writeStore2(targets) {
|
|
23529
|
-
const
|
|
23530
|
-
mkdirSync7(dirname2(
|
|
23687
|
+
const path40 = getSshTargetsPath();
|
|
23688
|
+
mkdirSync7(dirname2(path40), { recursive: true });
|
|
23531
23689
|
const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
|
|
23532
23690
|
writeFileSync6(
|
|
23533
|
-
|
|
23691
|
+
path40,
|
|
23534
23692
|
`${JSON.stringify({ targets: clean }, null, 2)}
|
|
23535
23693
|
`,
|
|
23536
23694
|
"utf8"
|
|
@@ -23776,11 +23934,11 @@ function formatSshTargetsForPrompt() {
|
|
|
23776
23934
|
];
|
|
23777
23935
|
for (const t of targets) {
|
|
23778
23936
|
const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
|
|
23779
|
-
const
|
|
23937
|
+
const path40 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
|
|
23780
23938
|
const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
|
|
23781
23939
|
const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
|
|
23782
23940
|
lines.push(
|
|
23783
|
-
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${
|
|
23941
|
+
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path40}${tags}${allow}`
|
|
23784
23942
|
);
|
|
23785
23943
|
}
|
|
23786
23944
|
return lines.join("\n");
|
|
@@ -25780,11 +25938,11 @@ var init_synthesisAudit = __esm({
|
|
|
25780
25938
|
import { existsSync as existsSync13, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
|
|
25781
25939
|
import { join as join6 } from "node:path";
|
|
25782
25940
|
function loadNfrSpec(zelariRoot) {
|
|
25783
|
-
const
|
|
25784
|
-
if (!existsSync13(
|
|
25941
|
+
const path40 = join6(zelariRoot, "nfr-spec.json");
|
|
25942
|
+
if (!existsSync13(path40))
|
|
25785
25943
|
return null;
|
|
25786
25944
|
try {
|
|
25787
|
-
const raw = JSON.parse(readFileSync10(
|
|
25945
|
+
const raw = JSON.parse(readFileSync10(path40, "utf8"));
|
|
25788
25946
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
25789
25947
|
return null;
|
|
25790
25948
|
return raw;
|
|
@@ -28090,9 +28248,9 @@ var init_types3 = __esm({
|
|
|
28090
28248
|
import { readFileSync as readFileSync15 } from "node:fs";
|
|
28091
28249
|
import { join as join12 } from "node:path";
|
|
28092
28250
|
function readLessonsDeduped(zelariRoot) {
|
|
28093
|
-
const
|
|
28251
|
+
const path40 = join12(zelariRoot, LESSONS_FILE);
|
|
28094
28252
|
try {
|
|
28095
|
-
const raw = readFileSync15(
|
|
28253
|
+
const raw = readFileSync15(path40, "utf8");
|
|
28096
28254
|
const byId = /* @__PURE__ */ new Map();
|
|
28097
28255
|
for (const line of raw.split(/\r?\n/)) {
|
|
28098
28256
|
if (!line.trim())
|
|
@@ -28193,8 +28351,8 @@ function keywordsFrom(check2, signature) {
|
|
|
28193
28351
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
28194
28352
|
}
|
|
28195
28353
|
function writeLesson(zelariRoot, lesson) {
|
|
28196
|
-
const
|
|
28197
|
-
appendFileSync2(
|
|
28354
|
+
const path40 = join13(zelariRoot, LESSONS_FILE);
|
|
28355
|
+
appendFileSync2(path40, `${JSON.stringify(lesson)}
|
|
28198
28356
|
`, "utf8");
|
|
28199
28357
|
}
|
|
28200
28358
|
function findSimilar(lessons, signature) {
|
|
@@ -28630,6 +28788,7 @@ __export(council_exports, {
|
|
|
28630
28788
|
STRUCTURED_REASONING_DIRECTIVE: () => STRUCTURED_REASONING_DIRECTIVE,
|
|
28631
28789
|
TIER_RANK: () => TIER_RANK,
|
|
28632
28790
|
TOOL_USE_PROTOCOL_DIRECTIVE: () => TOOL_USE_PROTOCOL_DIRECTIVE,
|
|
28791
|
+
TURN_COMPLETION_MODULE: () => TURN_COMPLETION_MODULE,
|
|
28633
28792
|
UnknownMemberError: () => UnknownMemberError,
|
|
28634
28793
|
applyCompletionRetry: () => applyCompletionRetry,
|
|
28635
28794
|
applyDeterministicAutofix: () => applyDeterministicAutofix,
|
|
@@ -30350,28 +30509,28 @@ var init_storage = __esm({
|
|
|
30350
30509
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
30351
30510
|
Storage = class {
|
|
30352
30511
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
30353
|
-
read(
|
|
30354
|
-
if (!existsSync23(
|
|
30355
|
-
throw new Error(`File not found: ${
|
|
30512
|
+
read(path40) {
|
|
30513
|
+
if (!existsSync23(path40)) {
|
|
30514
|
+
throw new Error(`File not found: ${path40}`);
|
|
30356
30515
|
}
|
|
30357
|
-
const md = readFileSync20(
|
|
30516
|
+
const md = readFileSync20(path40, "utf8");
|
|
30358
30517
|
return parseFrontmatter(md);
|
|
30359
30518
|
}
|
|
30360
30519
|
/** Read a Markdown file; returns null if not found. */
|
|
30361
|
-
readIfExists(
|
|
30362
|
-
if (!existsSync23(
|
|
30363
|
-
return this.read(
|
|
30520
|
+
readIfExists(path40) {
|
|
30521
|
+
if (!existsSync23(path40)) return null;
|
|
30522
|
+
return this.read(path40);
|
|
30364
30523
|
}
|
|
30365
30524
|
/**
|
|
30366
30525
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
30367
30526
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
30368
30527
|
*/
|
|
30369
|
-
write(
|
|
30370
|
-
mkdirSync9(dirname4(
|
|
30371
|
-
const tmp =
|
|
30528
|
+
write(path40, meta3, body) {
|
|
30529
|
+
mkdirSync9(dirname4(path40), { recursive: true });
|
|
30530
|
+
const tmp = path40 + ".tmp-" + process.pid;
|
|
30372
30531
|
const md = serializeFrontmatter(meta3, body);
|
|
30373
30532
|
writeFileSync13(tmp, md, "utf8");
|
|
30374
|
-
renameSync2(tmp,
|
|
30533
|
+
renameSync2(tmp, path40);
|
|
30375
30534
|
}
|
|
30376
30535
|
/** List all .md files in a directory (non-recursive). */
|
|
30377
30536
|
listMarkdown(dir) {
|
|
@@ -30449,8 +30608,8 @@ function readPlan(ctx) {
|
|
|
30449
30608
|
} catch {
|
|
30450
30609
|
}
|
|
30451
30610
|
}
|
|
30452
|
-
const
|
|
30453
|
-
const doc = ctx.storage.readIfExists(
|
|
30611
|
+
const path40 = workspaceFile(ctx.rootDir, "plan");
|
|
30612
|
+
const doc = ctx.storage.readIfExists(path40);
|
|
30454
30613
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
30455
30614
|
const meta3 = doc.meta;
|
|
30456
30615
|
return {
|
|
@@ -30615,7 +30774,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
30615
30774
|
dueDate: input.dueDate,
|
|
30616
30775
|
targetVersion: version2
|
|
30617
30776
|
});
|
|
30618
|
-
const
|
|
30777
|
+
const path40 = join23(ctx.rootDir, "milestones", `${id}.md`);
|
|
30619
30778
|
const meta3 = {
|
|
30620
30779
|
kind: "milestone",
|
|
30621
30780
|
id,
|
|
@@ -30632,7 +30791,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
30632
30791
|
`Target version: ${version2}`,
|
|
30633
30792
|
""
|
|
30634
30793
|
].join("\n");
|
|
30635
|
-
ctx.storage.write(
|
|
30794
|
+
ctx.storage.write(path40, meta3, body);
|
|
30636
30795
|
return { id, created: true };
|
|
30637
30796
|
}
|
|
30638
30797
|
function readPlanSummary(ctx) {
|
|
@@ -30836,7 +30995,7 @@ function addIdeaStub(ctx) {
|
|
|
30836
30995
|
const tags = args["tags"] ?? [];
|
|
30837
30996
|
const category = args["category"] ?? "General";
|
|
30838
30997
|
const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
30839
|
-
const
|
|
30998
|
+
const path40 = workspaceArtifact(ctx.rootDir, "decisions", id);
|
|
30840
30999
|
const meta3 = {
|
|
30841
31000
|
kind: "adr",
|
|
30842
31001
|
status: "proposed",
|
|
@@ -30862,7 +31021,7 @@ function addIdeaStub(ctx) {
|
|
|
30862
31021
|
...consequences.map((c) => `- ${c}`),
|
|
30863
31022
|
""
|
|
30864
31023
|
].join("\n");
|
|
30865
|
-
ctx.storage.write(
|
|
31024
|
+
ctx.storage.write(path40, meta3, body);
|
|
30866
31025
|
return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
30867
31026
|
});
|
|
30868
31027
|
}
|
|
@@ -30944,14 +31103,14 @@ function createDocumentStub(ctx) {
|
|
|
30944
31103
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
30945
31104
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
30946
31105
|
}
|
|
30947
|
-
const
|
|
31106
|
+
const path40 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
30948
31107
|
const meta3 = {
|
|
30949
31108
|
kind: "doc",
|
|
30950
31109
|
id: slug,
|
|
30951
31110
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
30952
31111
|
tags
|
|
30953
31112
|
};
|
|
30954
|
-
ctx.storage.write(
|
|
31113
|
+
ctx.storage.write(path40, meta3, content);
|
|
30955
31114
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
30956
31115
|
});
|
|
30957
31116
|
}
|
|
@@ -31495,10 +31654,10 @@ function getUserMcpPath() {
|
|
|
31495
31654
|
function getProjectMcpPath(projectRoot) {
|
|
31496
31655
|
return join24(projectRoot, ".zelari", "mcp.json");
|
|
31497
31656
|
}
|
|
31498
|
-
function readFile2(
|
|
31499
|
-
if (!existsSync26(
|
|
31657
|
+
function readFile2(path40) {
|
|
31658
|
+
if (!existsSync26(path40)) return {};
|
|
31500
31659
|
try {
|
|
31501
|
-
const parsed = JSON.parse(readFileSync22(
|
|
31660
|
+
const parsed = JSON.parse(readFileSync22(path40, "utf8"));
|
|
31502
31661
|
const out = {};
|
|
31503
31662
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
31504
31663
|
if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
|
|
@@ -31514,10 +31673,10 @@ function readFile2(path38) {
|
|
|
31514
31673
|
return {};
|
|
31515
31674
|
}
|
|
31516
31675
|
}
|
|
31517
|
-
function writeFile(
|
|
31518
|
-
mkdirSync11(dirname6(
|
|
31676
|
+
function writeFile(path40, servers) {
|
|
31677
|
+
mkdirSync11(dirname6(path40), { recursive: true });
|
|
31519
31678
|
const body = { mcpServers: servers };
|
|
31520
|
-
writeFileSync15(
|
|
31679
|
+
writeFileSync15(path40, `${JSON.stringify(body, null, 2)}
|
|
31521
31680
|
`, "utf8");
|
|
31522
31681
|
}
|
|
31523
31682
|
function listMcpServers(projectRoot) {
|
|
@@ -31550,9 +31709,9 @@ function upsertMcpServer(opts) {
|
|
|
31550
31709
|
if (!opts.config.command?.trim()) {
|
|
31551
31710
|
return { ok: false, error: "command is required" };
|
|
31552
31711
|
}
|
|
31553
|
-
let
|
|
31712
|
+
let path40;
|
|
31554
31713
|
if (opts.scope === "user") {
|
|
31555
|
-
|
|
31714
|
+
path40 = getUserMcpPath();
|
|
31556
31715
|
} else {
|
|
31557
31716
|
const root = opts.projectRoot?.trim();
|
|
31558
31717
|
if (!root) {
|
|
@@ -31561,30 +31720,30 @@ function upsertMcpServer(opts) {
|
|
|
31561
31720
|
error: "projectRoot required for project scope (Open Folder first)"
|
|
31562
31721
|
};
|
|
31563
31722
|
}
|
|
31564
|
-
|
|
31723
|
+
path40 = getProjectMcpPath(root);
|
|
31565
31724
|
}
|
|
31566
|
-
const current = readFile2(
|
|
31725
|
+
const current = readFile2(path40);
|
|
31567
31726
|
current[name] = {
|
|
31568
31727
|
command: opts.config.command.trim(),
|
|
31569
31728
|
args: opts.config.args,
|
|
31570
31729
|
env: opts.config.env,
|
|
31571
31730
|
enabled: opts.config.enabled !== false
|
|
31572
31731
|
};
|
|
31573
|
-
writeFile(
|
|
31574
|
-
return { ok: true, path:
|
|
31732
|
+
writeFile(path40, current);
|
|
31733
|
+
return { ok: true, path: path40 };
|
|
31575
31734
|
}
|
|
31576
31735
|
function removeMcpServer(opts) {
|
|
31577
|
-
const
|
|
31578
|
-
if (!
|
|
31736
|
+
const path40 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
|
|
31737
|
+
if (!path40) {
|
|
31579
31738
|
return { ok: false, error: "projectRoot required for project scope" };
|
|
31580
31739
|
}
|
|
31581
|
-
const current = readFile2(
|
|
31740
|
+
const current = readFile2(path40);
|
|
31582
31741
|
if (!(opts.name in current)) {
|
|
31583
|
-
return { ok: false, error: `Server "${opts.name}" not found in ${
|
|
31742
|
+
return { ok: false, error: `Server "${opts.name}" not found in ${path40}` };
|
|
31584
31743
|
}
|
|
31585
31744
|
delete current[opts.name];
|
|
31586
|
-
writeFile(
|
|
31587
|
-
return { ok: true, path:
|
|
31745
|
+
writeFile(path40, current);
|
|
31746
|
+
return { ok: true, path: path40 };
|
|
31588
31747
|
}
|
|
31589
31748
|
var init_mcpConfigIo = __esm({
|
|
31590
31749
|
"src/cli/mcp/mcpConfigIo.ts"() {
|
|
@@ -31921,10 +32080,10 @@ import { createHash as createHash5 } from "node:crypto";
|
|
|
31921
32080
|
import { join as join26 } from "node:path";
|
|
31922
32081
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
31923
32082
|
async function readPackageJson2(projectRoot) {
|
|
31924
|
-
const
|
|
31925
|
-
if (!existsSync28(
|
|
32083
|
+
const path40 = join26(projectRoot, "package.json");
|
|
32084
|
+
if (!existsSync28(path40)) return null;
|
|
31926
32085
|
try {
|
|
31927
|
-
return JSON.parse(await readFile3(
|
|
32086
|
+
return JSON.parse(await readFile3(path40, "utf8"));
|
|
31928
32087
|
} catch {
|
|
31929
32088
|
return null;
|
|
31930
32089
|
}
|
|
@@ -32006,9 +32165,9 @@ async function genBuild(ctx) {
|
|
|
32006
32165
|
].join("\n");
|
|
32007
32166
|
}
|
|
32008
32167
|
async function genOpenQuestions(ctx) {
|
|
32009
|
-
const
|
|
32010
|
-
if (!existsSync28(
|
|
32011
|
-
const content = readFileSync24(
|
|
32168
|
+
const path40 = join26(ctx.rootDir, "risks.md");
|
|
32169
|
+
if (!existsSync28(path40)) return "_No open questions._";
|
|
32170
|
+
const content = readFileSync24(path40, "utf8");
|
|
32012
32171
|
const lines = content.split("\n");
|
|
32013
32172
|
const questions = [];
|
|
32014
32173
|
let currentTitle = "";
|
|
@@ -32575,8 +32734,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
32575
32734
|
sources: scope.sources
|
|
32576
32735
|
} : void 0
|
|
32577
32736
|
});
|
|
32578
|
-
const
|
|
32579
|
-
completionHook = { ran: true, path:
|
|
32737
|
+
const path40 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
32738
|
+
completionHook = { ran: true, path: path40, completion };
|
|
32580
32739
|
} catch (err) {
|
|
32581
32740
|
completionHook = {
|
|
32582
32741
|
ran: true,
|
|
@@ -33133,18 +33292,49 @@ var init_fileBackend = __esm({
|
|
|
33133
33292
|
}
|
|
33134
33293
|
});
|
|
33135
33294
|
|
|
33295
|
+
// src/cli/traceStore.ts
|
|
33296
|
+
import { promises as fs17 } from "node:fs";
|
|
33297
|
+
import * as path29 from "node:path";
|
|
33298
|
+
function traceDir(projectRoot) {
|
|
33299
|
+
return path29.join(projectRoot, ".zelari", "trace");
|
|
33300
|
+
}
|
|
33301
|
+
function tracePath(projectRoot, missionId) {
|
|
33302
|
+
return path29.join(traceDir(projectRoot), `${missionId}.json`);
|
|
33303
|
+
}
|
|
33304
|
+
async function saveTrace(projectRoot, missionId, entries) {
|
|
33305
|
+
const dir = traceDir(projectRoot);
|
|
33306
|
+
await fs17.mkdir(dir, { recursive: true });
|
|
33307
|
+
const payload = {
|
|
33308
|
+
missionId,
|
|
33309
|
+
ts: Date.now(),
|
|
33310
|
+
entries
|
|
33311
|
+
};
|
|
33312
|
+
await fs17.writeFile(
|
|
33313
|
+
tracePath(projectRoot, missionId),
|
|
33314
|
+
JSON.stringify(payload, null, 2) + "\n",
|
|
33315
|
+
"utf8"
|
|
33316
|
+
);
|
|
33317
|
+
}
|
|
33318
|
+
var init_traceStore = __esm({
|
|
33319
|
+
"src/cli/traceStore.ts"() {
|
|
33320
|
+
"use strict";
|
|
33321
|
+
}
|
|
33322
|
+
});
|
|
33323
|
+
|
|
33136
33324
|
// src/cli/zelariMission.ts
|
|
33137
33325
|
var zelariMission_exports = {};
|
|
33138
33326
|
__export(zelariMission_exports, {
|
|
33139
33327
|
formatBriefForChat: () => formatBriefForChat,
|
|
33140
33328
|
isMissionAutoStart: () => isMissionAutoStart,
|
|
33329
|
+
resolveMaxCost: () => resolveMaxCost,
|
|
33141
33330
|
resolveMaxIterations: () => resolveMaxIterations,
|
|
33142
33331
|
resolveMaxStall: () => resolveMaxStall,
|
|
33332
|
+
resolveMaxTokens: () => resolveMaxTokens,
|
|
33143
33333
|
runZelariMission: () => runZelariMission
|
|
33144
33334
|
});
|
|
33145
33335
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
33146
|
-
import { promises as
|
|
33147
|
-
import * as
|
|
33336
|
+
import { promises as fs18 } from "node:fs";
|
|
33337
|
+
import * as path30 from "node:path";
|
|
33148
33338
|
function resolveMaxIterations(env = process.env) {
|
|
33149
33339
|
const raw = env.ZELARI_MISSION_MAX_ITER;
|
|
33150
33340
|
const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
|
|
@@ -33156,17 +33346,35 @@ function resolveMaxStall(env = process.env) {
|
|
|
33156
33346
|
const n = Number.parseInt(raw, 10);
|
|
33157
33347
|
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_MAX_STALL;
|
|
33158
33348
|
}
|
|
33349
|
+
function resolveMaxCost(env = process.env) {
|
|
33350
|
+
const raw = env.ZELARI_MISSION_MAX_COST;
|
|
33351
|
+
if (!raw) return void 0;
|
|
33352
|
+
const n = Number.parseFloat(raw);
|
|
33353
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
33354
|
+
}
|
|
33355
|
+
function resolveMaxTokens(env = process.env) {
|
|
33356
|
+
const raw = env.ZELARI_MISSION_MAX_TOKENS;
|
|
33357
|
+
if (!raw) return void 0;
|
|
33358
|
+
const n = Number.parseInt(raw, 10);
|
|
33359
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
33360
|
+
}
|
|
33159
33361
|
function isMissionAutoStart(env = process.env) {
|
|
33160
33362
|
return env.ZELARI_MISSION_AUTO === "1";
|
|
33161
33363
|
}
|
|
33162
33364
|
async function writeMissionState(projectRoot, state2) {
|
|
33163
|
-
const dir =
|
|
33164
|
-
await
|
|
33165
|
-
await
|
|
33166
|
-
|
|
33365
|
+
const dir = path30.join(projectRoot, ".zelari");
|
|
33366
|
+
await fs18.mkdir(dir, { recursive: true });
|
|
33367
|
+
await fs18.writeFile(
|
|
33368
|
+
path30.join(dir, "mission-state.json"),
|
|
33167
33369
|
JSON.stringify(state2, null, 2) + "\n",
|
|
33168
33370
|
"utf8"
|
|
33169
33371
|
);
|
|
33372
|
+
if (state2.trace?.length) {
|
|
33373
|
+
try {
|
|
33374
|
+
await saveTrace(projectRoot, state2.missionId, state2.trace);
|
|
33375
|
+
} catch {
|
|
33376
|
+
}
|
|
33377
|
+
}
|
|
33170
33378
|
}
|
|
33171
33379
|
function buildSlicePrompt(brief, userMessage, runMode, iteration) {
|
|
33172
33380
|
if (runMode === "design-phase") {
|
|
@@ -33204,6 +33412,8 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33204
33412
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
33205
33413
|
const maxIter = deps.maxIterations ?? resolveMaxIterations(deps.env);
|
|
33206
33414
|
const maxStall = resolveMaxStall(deps.env);
|
|
33415
|
+
const maxCost = resolveMaxCost(deps.env);
|
|
33416
|
+
const maxTokens = resolveMaxTokens(deps.env);
|
|
33207
33417
|
const missionId = deps.missionId ?? `m_${randomUUID5().slice(0, 8)}`;
|
|
33208
33418
|
const startedAt = now().toISOString();
|
|
33209
33419
|
const state2 = {
|
|
@@ -33239,6 +33449,8 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33239
33449
|
let step = 0;
|
|
33240
33450
|
let implStep = 0;
|
|
33241
33451
|
let pendingDesign = designFirst;
|
|
33452
|
+
let cumulativeCostUsd = 0;
|
|
33453
|
+
let cumulativeTokens = 0;
|
|
33242
33454
|
while (true) {
|
|
33243
33455
|
const runMode = pendingDesign ? "design-phase" : "implementation";
|
|
33244
33456
|
if (runMode === "implementation") {
|
|
@@ -33255,6 +33467,8 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33255
33467
|
const promptIter = runMode === "implementation" ? implStep : 1;
|
|
33256
33468
|
const slicePrompt = buildSlicePrompt(brief, userMessage, runMode, promptIter);
|
|
33257
33469
|
const implementerRetry = runMode === "implementation" && implStep > 1;
|
|
33470
|
+
const sliceStartedAt = now().toISOString();
|
|
33471
|
+
const sliceStartMs = now().getTime();
|
|
33258
33472
|
if (runMode === "design-phase") {
|
|
33259
33473
|
deps.emit(
|
|
33260
33474
|
`[zelari] design-phase (fuori budget) \xB7 step ${step} \xB7 slice ${brief.sliceMvp.id}`
|
|
@@ -33291,6 +33505,8 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33291
33505
|
);
|
|
33292
33506
|
return state2;
|
|
33293
33507
|
}
|
|
33508
|
+
if (typeof result.costUsd === "number") cumulativeCostUsd += result.costUsd;
|
|
33509
|
+
if (typeof result.costTokens === "number") cumulativeTokens += result.costTokens;
|
|
33294
33510
|
await deps.memory.add(
|
|
33295
33511
|
JSON.stringify({
|
|
33296
33512
|
iteration: step,
|
|
@@ -33316,6 +33532,20 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33316
33532
|
}
|
|
33317
33533
|
state2.lastCompletionOk = completionOk;
|
|
33318
33534
|
state2.updatedAt = now().toISOString();
|
|
33535
|
+
state2.cumulativeCostUsd = cumulativeCostUsd;
|
|
33536
|
+
state2.cumulativeTokens = cumulativeTokens;
|
|
33537
|
+
if (!state2.trace) state2.trace = [];
|
|
33538
|
+
state2.trace.push({
|
|
33539
|
+
sliceId: brief.sliceMvp.id,
|
|
33540
|
+
iteration: step,
|
|
33541
|
+
runMode,
|
|
33542
|
+
completionOk,
|
|
33543
|
+
degraded: result.degraded,
|
|
33544
|
+
costTokens: typeof result.costTokens === "number" ? result.costTokens : void 0,
|
|
33545
|
+
costUsd: typeof result.costUsd === "number" ? result.costUsd : void 0,
|
|
33546
|
+
startedAt: sliceStartedAt,
|
|
33547
|
+
durationMs: now().getTime() - sliceStartMs
|
|
33548
|
+
});
|
|
33319
33549
|
if (runMode === "design-phase") {
|
|
33320
33550
|
pendingDesign = false;
|
|
33321
33551
|
await writeMissionState(deps.projectRoot, state2);
|
|
@@ -33379,6 +33609,16 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33379
33609
|
return state2;
|
|
33380
33610
|
}
|
|
33381
33611
|
}
|
|
33612
|
+
if (maxCost !== void 0 && cumulativeCostUsd >= maxCost || maxTokens !== void 0 && cumulativeTokens >= maxTokens) {
|
|
33613
|
+
state2.status = "stopped";
|
|
33614
|
+
state2.updatedAt = now().toISOString();
|
|
33615
|
+
await writeMissionState(deps.projectRoot, state2);
|
|
33616
|
+
const reason = maxCost !== void 0 && cumulativeCostUsd >= maxCost ? `budget USD ${formatCost(cumulativeCostUsd)} \u2265 ${formatCost(maxCost)}` : `token ${formatTokens(cumulativeTokens)} \u2265 ${formatTokens(maxTokens)}`;
|
|
33617
|
+
deps.emit(
|
|
33618
|
+
`[zelari] fermata: ${reason}. Imposta ZELARI_MISSION_MAX_COST / ZELARI_MISSION_MAX_TOKENS pi\xF9 alto, o usa un modello pi\xF9 economico. Stato salvato in .zelari/mission-state.json`
|
|
33619
|
+
);
|
|
33620
|
+
return state2;
|
|
33621
|
+
}
|
|
33382
33622
|
await writeMissionState(deps.projectRoot, state2);
|
|
33383
33623
|
}
|
|
33384
33624
|
state2.status = "stopped";
|
|
@@ -33397,6 +33637,8 @@ var init_zelariMission = __esm({
|
|
|
33397
33637
|
init_checkpointManager();
|
|
33398
33638
|
init_fileStateStore();
|
|
33399
33639
|
init_commitHelpers();
|
|
33640
|
+
init_modelPricing();
|
|
33641
|
+
init_traceStore();
|
|
33400
33642
|
DEFAULT_MAX_ITER = 6;
|
|
33401
33643
|
DEFAULT_MAX_STALL = 2;
|
|
33402
33644
|
}
|
|
@@ -33554,6 +33796,8 @@ async function runAgentMissionSlice(deps) {
|
|
|
33554
33796
|
});
|
|
33555
33797
|
let text = "";
|
|
33556
33798
|
let errored2 = false;
|
|
33799
|
+
let promptTokens = 0;
|
|
33800
|
+
let completionTokens = 0;
|
|
33557
33801
|
try {
|
|
33558
33802
|
for await (const event of harness.run()) {
|
|
33559
33803
|
counter.onEvent(event);
|
|
@@ -33564,6 +33808,11 @@ async function runAgentMissionSlice(deps) {
|
|
|
33564
33808
|
if (event.type === "agent_end" && event.reason === "error") {
|
|
33565
33809
|
errored2 = true;
|
|
33566
33810
|
}
|
|
33811
|
+
if (event.type === "message_end" && event.usage) {
|
|
33812
|
+
const u = event.usage;
|
|
33813
|
+
promptTokens += u.promptTokens ?? 0;
|
|
33814
|
+
completionTokens += u.completionTokens ?? 0;
|
|
33815
|
+
}
|
|
33567
33816
|
if (event.type === "error" && event.severity === "fatal") {
|
|
33568
33817
|
errored2 = true;
|
|
33569
33818
|
}
|
|
@@ -33586,7 +33835,9 @@ async function runAgentMissionSlice(deps) {
|
|
|
33586
33835
|
successfulWrites: counter.state.successfulWrites,
|
|
33587
33836
|
emittedWrites: counter.state.emittedWrites,
|
|
33588
33837
|
errored: errored2,
|
|
33589
|
-
messages: harness.getMessages()
|
|
33838
|
+
messages: harness.getMessages(),
|
|
33839
|
+
promptTokens,
|
|
33840
|
+
completionTokens
|
|
33590
33841
|
};
|
|
33591
33842
|
}
|
|
33592
33843
|
const initial = [
|
|
@@ -33598,6 +33849,8 @@ async function runAgentMissionSlice(deps) {
|
|
|
33598
33849
|
let totalEmitted = pass.emittedWrites;
|
|
33599
33850
|
let synthesisText = pass.text;
|
|
33600
33851
|
let errored = pass.errored;
|
|
33852
|
+
let totalPromptTokens = pass.promptTokens;
|
|
33853
|
+
let totalCompletionTokens = pass.completionTokens;
|
|
33601
33854
|
if (writeRetry && totalWrites === 0 && !errored) {
|
|
33602
33855
|
deps.emit?.(
|
|
33603
33856
|
"[zelari] build@agent: 0 write \u2014 forcing implementation retry"
|
|
@@ -33617,6 +33870,8 @@ async function runAgentMissionSlice(deps) {
|
|
|
33617
33870
|
${retry.text}` : retry.text;
|
|
33618
33871
|
}
|
|
33619
33872
|
errored = errored || retry.errored;
|
|
33873
|
+
totalPromptTokens += retry.promptTokens;
|
|
33874
|
+
totalCompletionTokens += retry.completionTokens;
|
|
33620
33875
|
}
|
|
33621
33876
|
const cleaned = cleanAgentContent(synthesisText, {
|
|
33622
33877
|
stripQuestion: false,
|
|
@@ -33663,12 +33918,16 @@ ${retry.text}` : retry.text;
|
|
|
33663
33918
|
else degraded = true;
|
|
33664
33919
|
}
|
|
33665
33920
|
void totalEmitted;
|
|
33921
|
+
const costTokens = totalPromptTokens + totalCompletionTokens;
|
|
33922
|
+
const costUsd = calculateCost(deps.model, totalPromptTokens, totalCompletionTokens);
|
|
33666
33923
|
return {
|
|
33667
33924
|
completionOk,
|
|
33668
33925
|
ran: true,
|
|
33669
33926
|
synthesisText: cleaned || void 0,
|
|
33670
33927
|
writeCount: totalWrites,
|
|
33671
|
-
degraded
|
|
33928
|
+
degraded,
|
|
33929
|
+
costTokens,
|
|
33930
|
+
costUsd
|
|
33672
33931
|
};
|
|
33673
33932
|
}
|
|
33674
33933
|
var MUTATING, AGENT_MISSION_IMPLEMENTER_PREAMBLE;
|
|
@@ -33681,6 +33940,7 @@ var init_missionSlice = __esm({
|
|
|
33681
33940
|
init_dist();
|
|
33682
33941
|
init_buildPolicy();
|
|
33683
33942
|
init_envNumber();
|
|
33943
|
+
init_modelPricing();
|
|
33684
33944
|
MUTATING = /* @__PURE__ */ new Set(["write_file", "edit_file", "apply_diff"]);
|
|
33685
33945
|
AGENT_MISSION_IMPLEMENTER_PREAMBLE = "You are the sole implementer for this Zelari mission slice. A multi-agent council may already have produced a plan under `.zelari/` \u2014 treat it as a SPEC to apply on disk. You MUST create or modify real project files with write_file / edit_file. Prose without successful writes is a failed slice.";
|
|
33686
33946
|
}
|
|
@@ -34070,10 +34330,10 @@ var init_prereqChecks = __esm({
|
|
|
34070
34330
|
|
|
34071
34331
|
// src/cli/plugins/prefs.ts
|
|
34072
34332
|
import { existsSync as existsSync33, readFileSync as readFileSync28, writeFileSync as writeFileSync18, mkdirSync as mkdirSync13 } from "node:fs";
|
|
34073
|
-
import
|
|
34333
|
+
import path32 from "node:path";
|
|
34074
34334
|
import os9 from "node:os";
|
|
34075
34335
|
function getPluginPrefsPath() {
|
|
34076
|
-
return process.env.ZELARI_PLUGINS_PREFS_FILE ??
|
|
34336
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path32.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
|
|
34077
34337
|
}
|
|
34078
34338
|
function getPluginPrefs() {
|
|
34079
34339
|
const file2 = getPluginPrefsPath();
|
|
@@ -34094,7 +34354,7 @@ function getPluginPrefs() {
|
|
|
34094
34354
|
}
|
|
34095
34355
|
function writePluginPrefs(prefs) {
|
|
34096
34356
|
const file2 = getPluginPrefsPath();
|
|
34097
|
-
mkdirSync13(
|
|
34357
|
+
mkdirSync13(path32.dirname(file2), { recursive: true });
|
|
34098
34358
|
writeFileSync18(file2, JSON.stringify(prefs, null, 2), {
|
|
34099
34359
|
encoding: "utf-8",
|
|
34100
34360
|
mode: 384
|
|
@@ -34131,7 +34391,7 @@ __export(registry_exports, {
|
|
|
34131
34391
|
isBinaryOnPath: () => isBinaryOnPath
|
|
34132
34392
|
});
|
|
34133
34393
|
import { existsSync as existsSync34 } from "node:fs";
|
|
34134
|
-
import
|
|
34394
|
+
import path33 from "node:path";
|
|
34135
34395
|
function detectLocalBin(bin) {
|
|
34136
34396
|
return (cwd) => {
|
|
34137
34397
|
try {
|
|
@@ -34149,7 +34409,7 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
34149
34409
|
const platform = opts.platform ?? process.platform;
|
|
34150
34410
|
const exists = opts.exists ?? existsSync34;
|
|
34151
34411
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
34152
|
-
const pathMod = platform === "win32" ?
|
|
34412
|
+
const pathMod = platform === "win32" ? path33.win32 : path33.posix;
|
|
34153
34413
|
const sep = platform === "win32" ? ";" : ":";
|
|
34154
34414
|
const dirs = pathEnv.split(sep).filter((d) => d.length > 0);
|
|
34155
34415
|
const candidates = [bin];
|
|
@@ -34279,6 +34539,59 @@ var init_registry2 = __esm({
|
|
|
34279
34539
|
}
|
|
34280
34540
|
});
|
|
34281
34541
|
|
|
34542
|
+
// src/cli/triggerLock.ts
|
|
34543
|
+
var triggerLock_exports = {};
|
|
34544
|
+
__export(triggerLock_exports, {
|
|
34545
|
+
acquireLock: () => acquireLock,
|
|
34546
|
+
lockPath: () => lockPath,
|
|
34547
|
+
releaseLock: () => releaseLock
|
|
34548
|
+
});
|
|
34549
|
+
import { promises as fs23 } from "node:fs";
|
|
34550
|
+
import * as path38 from "node:path";
|
|
34551
|
+
function lockPath(projectRoot) {
|
|
34552
|
+
return path38.join(projectRoot, ".zelari", "trigger.lock");
|
|
34553
|
+
}
|
|
34554
|
+
function isPidAlive(pid) {
|
|
34555
|
+
try {
|
|
34556
|
+
process.kill(pid, 0);
|
|
34557
|
+
return true;
|
|
34558
|
+
} catch (err) {
|
|
34559
|
+
const code = err.code;
|
|
34560
|
+
return code === "EPERM";
|
|
34561
|
+
}
|
|
34562
|
+
}
|
|
34563
|
+
async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
|
|
34564
|
+
const lp = lockPath(projectRoot);
|
|
34565
|
+
const dir = path38.dirname(lp);
|
|
34566
|
+
await fs23.mkdir(dir, { recursive: true });
|
|
34567
|
+
try {
|
|
34568
|
+
const raw = await fs23.readFile(lp, "utf8");
|
|
34569
|
+
const existing = JSON.parse(raw);
|
|
34570
|
+
if (existing.pid && isPidAlive(existing.pid)) {
|
|
34571
|
+
return { acquired: false, heldBy: existing.pid, lockPath: lp };
|
|
34572
|
+
}
|
|
34573
|
+
} catch {
|
|
34574
|
+
}
|
|
34575
|
+
const payload = {
|
|
34576
|
+
pid: process.pid,
|
|
34577
|
+
acquiredAt: now().toISOString()
|
|
34578
|
+
};
|
|
34579
|
+
await fs23.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
|
|
34580
|
+
return { acquired: true, lockPath: lp };
|
|
34581
|
+
}
|
|
34582
|
+
async function releaseLock(projectRoot) {
|
|
34583
|
+
const lp = lockPath(projectRoot);
|
|
34584
|
+
try {
|
|
34585
|
+
await fs23.unlink(lp);
|
|
34586
|
+
} catch {
|
|
34587
|
+
}
|
|
34588
|
+
}
|
|
34589
|
+
var init_triggerLock = __esm({
|
|
34590
|
+
"src/cli/triggerLock.ts"() {
|
|
34591
|
+
"use strict";
|
|
34592
|
+
}
|
|
34593
|
+
});
|
|
34594
|
+
|
|
34282
34595
|
// src/cli/utils/doctor.ts
|
|
34283
34596
|
var doctor_exports = {};
|
|
34284
34597
|
__export(doctor_exports, {
|
|
@@ -34288,11 +34601,11 @@ import { execSync as execSync2 } from "node:child_process";
|
|
|
34288
34601
|
import { existsSync as existsSync38, readFileSync as readFileSync31, readlinkSync, statSync as statSync6 } from "node:fs";
|
|
34289
34602
|
import { createRequire as createRequire3 } from "node:module";
|
|
34290
34603
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
34291
|
-
import
|
|
34604
|
+
import path39 from "node:path";
|
|
34292
34605
|
function findPackageRoot(start) {
|
|
34293
34606
|
let dir = start;
|
|
34294
34607
|
for (let i = 0; i < 6; i += 1) {
|
|
34295
|
-
const candidate =
|
|
34608
|
+
const candidate = path39.join(dir, "package.json");
|
|
34296
34609
|
if (existsSync38(candidate)) {
|
|
34297
34610
|
try {
|
|
34298
34611
|
const pkg = JSON.parse(readFileSync31(candidate, "utf8"));
|
|
@@ -34300,11 +34613,11 @@ function findPackageRoot(start) {
|
|
|
34300
34613
|
} catch {
|
|
34301
34614
|
}
|
|
34302
34615
|
}
|
|
34303
|
-
const parent =
|
|
34616
|
+
const parent = path39.dirname(dir);
|
|
34304
34617
|
if (parent === dir) break;
|
|
34305
34618
|
dir = parent;
|
|
34306
34619
|
}
|
|
34307
|
-
return
|
|
34620
|
+
return path39.resolve(__dirname3, "..", "..", "..");
|
|
34308
34621
|
}
|
|
34309
34622
|
function tryExec(cmd) {
|
|
34310
34623
|
try {
|
|
@@ -34318,7 +34631,7 @@ function tryExec(cmd) {
|
|
|
34318
34631
|
}
|
|
34319
34632
|
function readPackageJson3() {
|
|
34320
34633
|
try {
|
|
34321
|
-
const pkgPath =
|
|
34634
|
+
const pkgPath = path39.join(packageRoot, "package.json");
|
|
34322
34635
|
return JSON.parse(readFileSync31(pkgPath, "utf8"));
|
|
34323
34636
|
} catch {
|
|
34324
34637
|
return null;
|
|
@@ -34334,7 +34647,7 @@ function checkShim(pkgName) {
|
|
|
34334
34647
|
}
|
|
34335
34648
|
const isWin = process.platform === "win32";
|
|
34336
34649
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
34337
|
-
const shimPath =
|
|
34650
|
+
const shimPath = path39.join(prefix, shimName);
|
|
34338
34651
|
if (!existsSync38(shimPath)) {
|
|
34339
34652
|
return FAIL(
|
|
34340
34653
|
`shim not found at ${shimPath}
|
|
@@ -34362,8 +34675,8 @@ function checkShim(pkgName) {
|
|
|
34362
34675
|
fix: npm install -g ${pkgName}@latest --force`
|
|
34363
34676
|
);
|
|
34364
34677
|
}
|
|
34365
|
-
const resolved =
|
|
34366
|
-
const expected =
|
|
34678
|
+
const resolved = path39.resolve(path39.dirname(shimPath), target);
|
|
34679
|
+
const expected = path39.join(
|
|
34367
34680
|
prefix,
|
|
34368
34681
|
"node_modules",
|
|
34369
34682
|
pkgName,
|
|
@@ -34402,7 +34715,7 @@ function checkNode(pkg) {
|
|
|
34402
34715
|
return OK(`node ${raw}`);
|
|
34403
34716
|
}
|
|
34404
34717
|
function checkBundle() {
|
|
34405
|
-
const bundle =
|
|
34718
|
+
const bundle = path39.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
34406
34719
|
if (!existsSync38(bundle)) {
|
|
34407
34720
|
return FAIL(
|
|
34408
34721
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
@@ -34423,7 +34736,7 @@ function checkRuntimeDeps() {
|
|
|
34423
34736
|
const missing = [];
|
|
34424
34737
|
for (const dep of required2) {
|
|
34425
34738
|
try {
|
|
34426
|
-
const localReq = createRequire3(
|
|
34739
|
+
const localReq = createRequire3(path39.join(packageRoot, "package.json"));
|
|
34427
34740
|
localReq.resolve(dep);
|
|
34428
34741
|
} catch {
|
|
34429
34742
|
missing.push(dep);
|
|
@@ -34599,7 +34912,7 @@ var init_doctor = __esm({
|
|
|
34599
34912
|
"use strict";
|
|
34600
34913
|
init_prereqChecks();
|
|
34601
34914
|
require3 = createRequire3(import.meta.url);
|
|
34602
|
-
__dirname3 =
|
|
34915
|
+
__dirname3 = path39.dirname(fileURLToPath2(import.meta.url));
|
|
34603
34916
|
packageRoot = findPackageRoot(__dirname3);
|
|
34604
34917
|
OK = (message) => ({
|
|
34605
34918
|
ok: true,
|
|
@@ -35207,83 +35520,7 @@ function StreamingTail(m) {
|
|
|
35207
35520
|
// src/cli/components/StatusBar.tsx
|
|
35208
35521
|
import React6 from "react";
|
|
35209
35522
|
import { Box as Box5, Text as Text6 } from "ink";
|
|
35210
|
-
|
|
35211
|
-
// src/cli/modelPricing.ts
|
|
35212
|
-
var PRICES_PER_MILLION = {
|
|
35213
|
-
// xAI Grok (list prices; grok-4.5 default reasoning_effort is "high")
|
|
35214
|
-
"grok-4.5": { input: 2, output: 6, cachedInput: 0.5 },
|
|
35215
|
-
"grok-4.3": { input: 1.25, output: 2.5, cachedInput: 0.2 },
|
|
35216
|
-
"grok-4": { input: 3, output: 15 },
|
|
35217
|
-
"grok-4-fast": { input: 0.2, output: 0.5 },
|
|
35218
|
-
"grok-3": { input: 3, output: 15 },
|
|
35219
|
-
"grok-3-mini": { input: 0.3, output: 0.5 },
|
|
35220
|
-
"grok-2-vision": { input: 2, output: 10 },
|
|
35221
|
-
// GLM / Z.AI
|
|
35222
|
-
"glm-4.6": { input: 0.6, output: 2.2 },
|
|
35223
|
-
"glm-4.5": { input: 0.5, output: 2 },
|
|
35224
|
-
"glm-4.5-air": { input: 0.1, output: 0.6 },
|
|
35225
|
-
"glm-z1": { input: 0.5, output: 2 },
|
|
35226
|
-
// MiniMax
|
|
35227
|
-
"MiniMax-M2.5": { input: 0.2, output: 1.1 },
|
|
35228
|
-
"MiniMax-M2": { input: 0.2, output: 1.1 },
|
|
35229
|
-
"MiniMax-M2-her": { input: 0.3, output: 1.2 },
|
|
35230
|
-
// DeepSeek (global platform) — estimated list prices; override via
|
|
35231
|
-
// ANATHEMA_PRICE_DEEPSEEK_V4_FLASH / ANATHEMA_PRICE_DEEPSEEK_V4_PRO.
|
|
35232
|
-
// DeepSeek prompt-cache hits are ~10× cheaper than a cache miss.
|
|
35233
|
-
"deepseek-v4-flash": { input: 0.14, output: 0.28, cachedInput: 0.014 },
|
|
35234
|
-
"deepseek-v4-pro": { input: 0.55, output: 2.19, cachedInput: 0.055 },
|
|
35235
|
-
// OpenAI (for openai-compatible fallback)
|
|
35236
|
-
"gpt-4o": { input: 2.5, output: 10 },
|
|
35237
|
-
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
35238
|
-
"gpt-4-turbo": { input: 10, output: 30 },
|
|
35239
|
-
"o1-preview": { input: 15, output: 60 },
|
|
35240
|
-
"o1-mini": { input: 3, output: 12 }
|
|
35241
|
-
};
|
|
35242
|
-
var DEFAULT_RATE = { input: 1, output: 3 };
|
|
35243
|
-
var DEFAULT_CACHE_DISCOUNT = 0.25;
|
|
35244
|
-
function envOverride(model) {
|
|
35245
|
-
const key = `ANATHEMA_PRICE_${model.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
35246
|
-
const raw = process.env[key] ?? process.env.ANATHEMA_PRICE_DEFAULT;
|
|
35247
|
-
return parseRateOverride(raw);
|
|
35248
|
-
}
|
|
35249
|
-
function parseRateOverride(raw) {
|
|
35250
|
-
if (!raw) return null;
|
|
35251
|
-
const [inStr, outStr] = raw.split("/");
|
|
35252
|
-
const input = Number.parseFloat(inStr);
|
|
35253
|
-
const output = outStr !== void 0 ? Number.parseFloat(outStr) : input;
|
|
35254
|
-
if (!Number.isFinite(input) || !Number.isFinite(output)) return null;
|
|
35255
|
-
return { input, output };
|
|
35256
|
-
}
|
|
35257
|
-
function getModelRate(model) {
|
|
35258
|
-
if (!model) return DEFAULT_RATE;
|
|
35259
|
-
return envOverride(model) ?? PRICES_PER_MILLION[model] ?? DEFAULT_RATE;
|
|
35260
|
-
}
|
|
35261
|
-
function calculateCost(model, promptTokens, completionTokens, cachedPromptTokens = 0) {
|
|
35262
|
-
if (!Number.isFinite(promptTokens) || promptTokens < 0) return 0;
|
|
35263
|
-
if (!Number.isFinite(completionTokens) || completionTokens < 0) return 0;
|
|
35264
|
-
const rate = getModelRate(model);
|
|
35265
|
-
const cached2 = Number.isFinite(cachedPromptTokens) && cachedPromptTokens > 0 ? Math.min(cachedPromptTokens, promptTokens) : 0;
|
|
35266
|
-
const uncachedPrompt = promptTokens - cached2;
|
|
35267
|
-
const cachedRate = rate.cachedInput ?? rate.input * DEFAULT_CACHE_DISCOUNT;
|
|
35268
|
-
const inputCost = uncachedPrompt / 1e6 * rate.input + cached2 / 1e6 * cachedRate;
|
|
35269
|
-
const outputCost = completionTokens / 1e6 * rate.output;
|
|
35270
|
-
return Number((inputCost + outputCost).toFixed(6));
|
|
35271
|
-
}
|
|
35272
|
-
function formatCost(usd) {
|
|
35273
|
-
if (!Number.isFinite(usd) || usd < 0) return "$0.0000";
|
|
35274
|
-
if (usd === 0) return "$0.0000";
|
|
35275
|
-
if (usd < 1e-4) return "<$0.0001";
|
|
35276
|
-
return `$${usd.toFixed(4)}`;
|
|
35277
|
-
}
|
|
35278
|
-
function formatTokens(tokens) {
|
|
35279
|
-
if (!Number.isFinite(tokens) || tokens < 0) return "0";
|
|
35280
|
-
if (tokens < 1e3) return `${tokens}`;
|
|
35281
|
-
if (tokens < 1e6) return `${(tokens / 1e3).toFixed(1)}k`;
|
|
35282
|
-
if (tokens < 1e9) return `${(tokens / 1e6).toFixed(2)}M`;
|
|
35283
|
-
return `${(tokens / 1e9).toFixed(2)}B`;
|
|
35284
|
-
}
|
|
35285
|
-
|
|
35286
|
-
// src/cli/components/StatusBar.tsx
|
|
35523
|
+
init_modelPricing();
|
|
35287
35524
|
function StatusBar({
|
|
35288
35525
|
model,
|
|
35289
35526
|
provider,
|
|
@@ -38627,6 +38864,9 @@ function finalizeStreamingAssistant(setMessages) {
|
|
|
38627
38864
|
// src/cli/hooks/useChatTurn.ts
|
|
38628
38865
|
init_conversationContext();
|
|
38629
38866
|
|
|
38867
|
+
// src/cli/hooks/chatStats.ts
|
|
38868
|
+
init_modelPricing();
|
|
38869
|
+
|
|
38630
38870
|
// src/cli/state/promptCacheStats.ts
|
|
38631
38871
|
function emptyPromptCacheStats() {
|
|
38632
38872
|
return {
|
|
@@ -39206,6 +39446,13 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
39206
39446
|
`[budget] ${event.message}`,
|
|
39207
39447
|
Date.now()
|
|
39208
39448
|
);
|
|
39449
|
+
} else if (event.code === "assistant_text_loop") {
|
|
39450
|
+
appendSystem(
|
|
39451
|
+
setMessages,
|
|
39452
|
+
`[text-loop] ${event.message}
|
|
39453
|
+
\u2192 Next message tip: "Continue with tools only \u2014 inspect disk, one write_file, stop."`,
|
|
39454
|
+
Date.now()
|
|
39455
|
+
);
|
|
39209
39456
|
} else {
|
|
39210
39457
|
appendSystem(setMessages, `[error] ${event.message}`, Date.now());
|
|
39211
39458
|
}
|
|
@@ -40819,7 +41066,7 @@ ${formatSkillList(availableSkills)}`
|
|
|
40819
41066
|
// src/cli/gitOps.ts
|
|
40820
41067
|
import { execFile as execFile3 } from "node:child_process";
|
|
40821
41068
|
import { promisify as promisify2 } from "node:util";
|
|
40822
|
-
import
|
|
41069
|
+
import path31 from "node:path";
|
|
40823
41070
|
var execFileAsync2 = promisify2(execFile3);
|
|
40824
41071
|
async function git2(cwd, args) {
|
|
40825
41072
|
try {
|
|
@@ -40865,7 +41112,7 @@ async function undoWorkingChanges(opts = {}) {
|
|
|
40865
41112
|
};
|
|
40866
41113
|
}
|
|
40867
41114
|
function defaultProjectRoot() {
|
|
40868
|
-
return
|
|
41115
|
+
return path31.resolve(__dirname, "..", "..", "..");
|
|
40869
41116
|
}
|
|
40870
41117
|
|
|
40871
41118
|
// src/cli/slashHandlers/git.ts
|
|
@@ -41505,17 +41752,17 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
|
|
|
41505
41752
|
}
|
|
41506
41753
|
|
|
41507
41754
|
// src/cli/slashHandlers/promoteMember.ts
|
|
41508
|
-
import { promises as
|
|
41509
|
-
import
|
|
41755
|
+
import { promises as fs19 } from "node:fs";
|
|
41756
|
+
import path34 from "node:path";
|
|
41510
41757
|
import os10 from "node:os";
|
|
41511
41758
|
async function handlePromoteMember(ctx, memberId) {
|
|
41512
41759
|
try {
|
|
41513
41760
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
41514
41761
|
const { skill, markdown } = promoteMember2(memberId);
|
|
41515
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
41516
|
-
await
|
|
41517
|
-
const filePath =
|
|
41518
|
-
await
|
|
41762
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path34.join(os10.homedir(), ".tmp", "zelari-code", "skills");
|
|
41763
|
+
await fs19.mkdir(skillDir, { recursive: true });
|
|
41764
|
+
const filePath = path34.join(skillDir, `${skill.id}.md`);
|
|
41765
|
+
await fs19.writeFile(filePath, markdown, "utf8");
|
|
41519
41766
|
appendSystem(
|
|
41520
41767
|
ctx.setMessages,
|
|
41521
41768
|
`[promote-member] ${skill.name} (${memberId}) \u2192 ${filePath}
|
|
@@ -41531,25 +41778,25 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
41531
41778
|
}
|
|
41532
41779
|
|
|
41533
41780
|
// src/cli/branchManager.ts
|
|
41534
|
-
import { promises as
|
|
41535
|
-
import
|
|
41781
|
+
import { promises as fs20, existsSync as existsSync35, readFileSync as readFileSync29, writeFileSync as writeFileSync19, mkdirSync as mkdirSync14, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
|
|
41782
|
+
import path35 from "node:path";
|
|
41536
41783
|
import os11 from "node:os";
|
|
41537
41784
|
var META_FILENAME = "meta.json";
|
|
41538
41785
|
var SESSIONS_SUBDIR = "sessions";
|
|
41539
41786
|
function getBranchesBaseDir() {
|
|
41540
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
41787
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path35.join(os11.homedir(), ".tmp", "zelari-code", "branches");
|
|
41541
41788
|
}
|
|
41542
41789
|
function getSessionsBaseDir() {
|
|
41543
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
41790
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path35.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
|
|
41544
41791
|
}
|
|
41545
41792
|
function branchPathFor(name, baseDir) {
|
|
41546
|
-
return
|
|
41793
|
+
return path35.join(baseDir, name);
|
|
41547
41794
|
}
|
|
41548
41795
|
function metaPathFor(name, baseDir) {
|
|
41549
|
-
return
|
|
41796
|
+
return path35.join(baseDir, name, META_FILENAME);
|
|
41550
41797
|
}
|
|
41551
41798
|
function sessionsPathFor(name, baseDir) {
|
|
41552
|
-
return
|
|
41799
|
+
return path35.join(baseDir, name, SESSIONS_SUBDIR);
|
|
41553
41800
|
}
|
|
41554
41801
|
function readBranchMeta(name, baseDir) {
|
|
41555
41802
|
const metaPath = metaPathFor(name, baseDir);
|
|
@@ -41574,13 +41821,13 @@ function readBranchMeta(name, baseDir) {
|
|
|
41574
41821
|
}
|
|
41575
41822
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
41576
41823
|
const metaPath = metaPathFor(name, baseDir);
|
|
41577
|
-
mkdirSync14(
|
|
41824
|
+
mkdirSync14(path35.dirname(metaPath), { recursive: true });
|
|
41578
41825
|
writeFileSync19(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
41579
41826
|
}
|
|
41580
41827
|
async function countSessions(name, baseDir) {
|
|
41581
41828
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
41582
41829
|
try {
|
|
41583
|
-
const entries = await
|
|
41830
|
+
const entries = await fs20.readdir(sessionsPath);
|
|
41584
41831
|
return entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
41585
41832
|
} catch (err) {
|
|
41586
41833
|
if (err.code === "ENOENT") return 0;
|
|
@@ -41625,15 +41872,15 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
41625
41872
|
if (branchExists(name, baseDir)) {
|
|
41626
41873
|
throw new BranchAlreadyExistsError(name);
|
|
41627
41874
|
}
|
|
41628
|
-
const sourcePath =
|
|
41875
|
+
const sourcePath = path35.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
41629
41876
|
if (!existsSync35(sourcePath)) {
|
|
41630
41877
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
41631
41878
|
}
|
|
41632
41879
|
const branchPath = branchPathFor(name, baseDir);
|
|
41633
41880
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
41634
41881
|
mkdirSync14(branchSessionsPath, { recursive: true });
|
|
41635
|
-
const destPath =
|
|
41636
|
-
await
|
|
41882
|
+
const destPath = path35.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
41883
|
+
await fs20.copyFile(sourcePath, destPath);
|
|
41637
41884
|
const meta3 = {
|
|
41638
41885
|
name,
|
|
41639
41886
|
createdAt: Date.now(),
|
|
@@ -41651,7 +41898,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
41651
41898
|
async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
41652
41899
|
let entries;
|
|
41653
41900
|
try {
|
|
41654
|
-
entries = await
|
|
41901
|
+
entries = await fs20.readdir(baseDir);
|
|
41655
41902
|
} catch (err) {
|
|
41656
41903
|
if (err.code === "ENOENT") return [];
|
|
41657
41904
|
throw err;
|
|
@@ -41732,26 +41979,26 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
41732
41979
|
}
|
|
41733
41980
|
|
|
41734
41981
|
// src/cli/slashHandlers/workspace.ts
|
|
41735
|
-
import { promises as
|
|
41736
|
-
import
|
|
41982
|
+
import { promises as fs21 } from "node:fs";
|
|
41983
|
+
import path36 from "node:path";
|
|
41737
41984
|
async function handleWorkspaceShow(ctx, what) {
|
|
41738
41985
|
try {
|
|
41739
|
-
const zelari =
|
|
41986
|
+
const zelari = path36.join(process.cwd(), ".zelari");
|
|
41740
41987
|
let content;
|
|
41741
41988
|
switch (what) {
|
|
41742
41989
|
case "plan": {
|
|
41743
|
-
const planPath =
|
|
41990
|
+
const planPath = path36.join(zelari, "plan.md");
|
|
41744
41991
|
try {
|
|
41745
|
-
content = await
|
|
41992
|
+
content = await fs21.readFile(planPath, "utf-8");
|
|
41746
41993
|
} catch {
|
|
41747
41994
|
content = "(no plan.md yet \u2014 run a council session first)";
|
|
41748
41995
|
}
|
|
41749
41996
|
break;
|
|
41750
41997
|
}
|
|
41751
41998
|
case "decisions": {
|
|
41752
|
-
const decisionsDir =
|
|
41999
|
+
const decisionsDir = path36.join(zelari, "decisions");
|
|
41753
42000
|
try {
|
|
41754
|
-
const files = (await
|
|
42001
|
+
const files = (await fs21.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
41755
42002
|
if (files.length === 0) {
|
|
41756
42003
|
content = "(no ADRs yet \u2014 invoke /council to generate some)";
|
|
41757
42004
|
} else {
|
|
@@ -41759,7 +42006,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
41759
42006
|
`];
|
|
41760
42007
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
41761
42008
|
for (const f of files) {
|
|
41762
|
-
const raw = await
|
|
42009
|
+
const raw = await fs21.readFile(path36.join(decisionsDir, f), "utf-8");
|
|
41763
42010
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
41764
42011
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
41765
42012
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -41772,27 +42019,27 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
41772
42019
|
break;
|
|
41773
42020
|
}
|
|
41774
42021
|
case "risks": {
|
|
41775
|
-
const risksPath =
|
|
42022
|
+
const risksPath = path36.join(zelari, "risks.md");
|
|
41776
42023
|
try {
|
|
41777
|
-
content = await
|
|
42024
|
+
content = await fs21.readFile(risksPath, "utf-8");
|
|
41778
42025
|
} catch {
|
|
41779
42026
|
content = "(no risks.md yet)";
|
|
41780
42027
|
}
|
|
41781
42028
|
break;
|
|
41782
42029
|
}
|
|
41783
42030
|
case "agents": {
|
|
41784
|
-
const agentsPath =
|
|
42031
|
+
const agentsPath = path36.join(process.cwd(), "AGENTS.MD");
|
|
41785
42032
|
try {
|
|
41786
|
-
content = await
|
|
42033
|
+
content = await fs21.readFile(agentsPath, "utf-8");
|
|
41787
42034
|
} catch {
|
|
41788
42035
|
content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
|
|
41789
42036
|
}
|
|
41790
42037
|
break;
|
|
41791
42038
|
}
|
|
41792
42039
|
case "docs": {
|
|
41793
|
-
const docsDir =
|
|
42040
|
+
const docsDir = path36.join(zelari, "docs");
|
|
41794
42041
|
try {
|
|
41795
|
-
const files = (await
|
|
42042
|
+
const files = (await fs21.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
41796
42043
|
content = files.length ? `# Docs (${files.length})
|
|
41797
42044
|
|
|
41798
42045
|
` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
|
|
@@ -41832,8 +42079,8 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
41832
42079
|
return;
|
|
41833
42080
|
}
|
|
41834
42081
|
try {
|
|
41835
|
-
const target =
|
|
41836
|
-
await
|
|
42082
|
+
const target = path36.join(process.cwd(), ".zelari");
|
|
42083
|
+
await fs21.rm(target, { recursive: true, force: true });
|
|
41837
42084
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
41838
42085
|
} catch (err) {
|
|
41839
42086
|
appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -42191,16 +42438,16 @@ function handleModelsRefresh(ctx) {
|
|
|
42191
42438
|
}
|
|
42192
42439
|
|
|
42193
42440
|
// src/cli/slashHandlers/skills.ts
|
|
42194
|
-
import
|
|
42441
|
+
import path37 from "node:path";
|
|
42195
42442
|
import os12 from "node:os";
|
|
42196
42443
|
|
|
42197
42444
|
// src/cli/skillHistory.ts
|
|
42198
|
-
import { promises as
|
|
42445
|
+
import { promises as fs22, existsSync as existsSync36, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync15 } from "node:fs";
|
|
42199
42446
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
42200
42447
|
async function readSkillHistory(file2) {
|
|
42201
42448
|
let raw = "";
|
|
42202
42449
|
try {
|
|
42203
|
-
raw = await
|
|
42450
|
+
raw = await fs22.readFile(file2, "utf-8");
|
|
42204
42451
|
} catch {
|
|
42205
42452
|
return [];
|
|
42206
42453
|
}
|
|
@@ -42293,7 +42540,7 @@ async function applySteerInterrupt(options) {
|
|
|
42293
42540
|
|
|
42294
42541
|
// src/cli/slashHandlers/skills.ts
|
|
42295
42542
|
async function handleSkillStats(ctx, skillId) {
|
|
42296
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
42543
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path37.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
42297
42544
|
try {
|
|
42298
42545
|
const records = await readSkillHistory(historyFile);
|
|
42299
42546
|
const stats = getSkillStats(records, skillId);
|
|
@@ -42309,7 +42556,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
42309
42556
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
42310
42557
|
return;
|
|
42311
42558
|
}
|
|
42312
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
42559
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path37.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
42313
42560
|
try {
|
|
42314
42561
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
42315
42562
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -43723,6 +43970,7 @@ function parseHeadlessFlags(argv) {
|
|
|
43723
43970
|
let provider;
|
|
43724
43971
|
let model;
|
|
43725
43972
|
let history2;
|
|
43973
|
+
let once = false;
|
|
43726
43974
|
for (let i = 0; i < argv.length; i++) {
|
|
43727
43975
|
const arg = argv[i];
|
|
43728
43976
|
if (arg === "--headless") continue;
|
|
@@ -43811,6 +44059,8 @@ function parseHeadlessFlags(argv) {
|
|
|
43811
44059
|
}
|
|
43812
44060
|
i++;
|
|
43813
44061
|
}
|
|
44062
|
+
} else if (arg === "--once") {
|
|
44063
|
+
once = true;
|
|
43814
44064
|
}
|
|
43815
44065
|
}
|
|
43816
44066
|
if (councilFlag && !modeExplicit) {
|
|
@@ -43833,7 +44083,8 @@ function parseHeadlessFlags(argv) {
|
|
|
43833
44083
|
useCouncil: mode === "council",
|
|
43834
44084
|
provider,
|
|
43835
44085
|
model,
|
|
43836
|
-
...history2 && history2.length > 0 ? { history: history2 } : {}
|
|
44086
|
+
...history2 && history2.length > 0 ? { history: history2 } : {},
|
|
44087
|
+
...once ? { once: true } : {}
|
|
43837
44088
|
}
|
|
43838
44089
|
};
|
|
43839
44090
|
}
|
|
@@ -44516,6 +44767,22 @@ ${JSON.stringify({ deliverable: brief.deliverableThisMission, mvp: brief.sliceMv
|
|
|
44516
44767
|
}
|
|
44517
44768
|
let exitCode = 0;
|
|
44518
44769
|
let lastMissionAssistant = "";
|
|
44770
|
+
let lockAcquired = false;
|
|
44771
|
+
if (opts.once) {
|
|
44772
|
+
const { acquireLock: acquireLock2 } = await Promise.resolve().then(() => (init_triggerLock(), triggerLock_exports));
|
|
44773
|
+
const lockRes = await acquireLock2(projectRoot);
|
|
44774
|
+
if (!lockRes.acquired) {
|
|
44775
|
+
process.stderr.write(
|
|
44776
|
+
`[zelari-code --once] skip: another mission is running (pid ${lockRes.heldBy}).
|
|
44777
|
+
`
|
|
44778
|
+
);
|
|
44779
|
+
return 0;
|
|
44780
|
+
}
|
|
44781
|
+
lockAcquired = true;
|
|
44782
|
+
if (!process.env.ZELARI_MISSION_MAX_ITER) {
|
|
44783
|
+
process.env.ZELARI_MISSION_MAX_ITER = "1";
|
|
44784
|
+
}
|
|
44785
|
+
}
|
|
44519
44786
|
try {
|
|
44520
44787
|
const state2 = await runZelariMission2(missionTask, brief, {
|
|
44521
44788
|
projectRoot,
|
|
@@ -44734,6 +45001,10 @@ ${ragContext}` : slicePrompt;
|
|
|
44734
45001
|
);
|
|
44735
45002
|
return 2;
|
|
44736
45003
|
} finally {
|
|
45004
|
+
if (lockAcquired) {
|
|
45005
|
+
const { releaseLock: releaseLock2 } = await Promise.resolve().then(() => (init_triggerLock(), triggerLock_exports));
|
|
45006
|
+
await releaseLock2(projectRoot);
|
|
45007
|
+
}
|
|
44737
45008
|
await memory.close().catch(() => void 0);
|
|
44738
45009
|
}
|
|
44739
45010
|
if (opts.output === "json") {
|