@tiens.nguyen/gu-cli 1.0.783 → 1.0.785
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/completed-flush.mjs +47 -0
- package/gonext_agent_chat.py +1 -1
- package/gu-cli.mjs +9 -3
- package/gu-repl.mjs +473 -35
- package/gu_hooks.py +102 -4
- package/gu_skills.py +64 -4
- package/hooks-command.mjs +14 -9
- package/hooks-config.mjs +130 -33
- package/mcp-command.mjs +11 -5
- package/mcp-config.mjs +99 -11
- package/model-prices.mjs +484 -0
- package/package.json +4 -2
- package/skill-source.mjs +60 -0
- package/skills.mjs +24 -12
- package/step-tokens.mjs +45 -0
- package/terminal-layout.mjs +14 -0
- package/terminal-viewport.mjs +65 -0
- package/turn-stop.mjs +17 -0
package/completed-flush.mjs
CHANGED
|
@@ -103,3 +103,50 @@ export function completedFlush({
|
|
|
103
103
|
const steps = tail.slice(0, close);
|
|
104
104
|
return steps ? { kind: "steps", text: steps } : { kind: "none", text: "" };
|
|
105
105
|
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* WHILE THE JOB IS STILL RUNNING: which of the newly arrived text is the turn's WORK (step lines
|
|
109
|
+
* and thoughts, inside <think>…</think>) and which is the ANSWER (outside it). Task #326.
|
|
110
|
+
*
|
|
111
|
+
* An agent turn's stream is `<think>steps…</think>answer`. The answer is the caller's to print —
|
|
112
|
+
* once, wrapped, from the completed resultText. completedFlush has always stopped at </think> for
|
|
113
|
+
* that reason, but the RUNNING poll did not: it fed everything to the step printer. So whenever
|
|
114
|
+
* the answer's chunk was polled before the job flipped to "completed" (any answer long enough to
|
|
115
|
+
* be posted on its own), every paragraph of it was printed as a "●" step bullet — and then the
|
|
116
|
+
* whole answer again under "recap:". Reported as the answer appearing twice; short answers escaped
|
|
117
|
+
* only because their chunk and the completion arrived in the same poll.
|
|
118
|
+
*
|
|
119
|
+
* `state` is carried from poll to poll: { inThink, held }. `held` is the start of a tag cut in two
|
|
120
|
+
* by a poll boundary ("</thi" + "nk>"), kept back until the rest arrives.
|
|
121
|
+
* → { parts: [{ inside, text }], state } — the tags themselves are dropped.
|
|
122
|
+
*/
|
|
123
|
+
export function splitLiveStream(fresh, state = {}) {
|
|
124
|
+
let text = String(state.held ?? "") + String(fresh ?? "");
|
|
125
|
+
let inThink = Boolean(state.inThink);
|
|
126
|
+
const parts = [];
|
|
127
|
+
const push = (inside, t) => {
|
|
128
|
+
if (!t) return;
|
|
129
|
+
const last = parts[parts.length - 1];
|
|
130
|
+
if (last && last.inside === inside) last.text += t;
|
|
131
|
+
else parts.push({ inside, text: t });
|
|
132
|
+
};
|
|
133
|
+
// Hold back a tail that could be the beginning of a tag.
|
|
134
|
+
let held = "";
|
|
135
|
+
const lt = text.lastIndexOf("<");
|
|
136
|
+
if (lt >= 0 && lt > text.length - 8) {
|
|
137
|
+
const tail = text.slice(lt).toLowerCase();
|
|
138
|
+
if ("<think>".startsWith(tail) || "</think>".startsWith(tail)) {
|
|
139
|
+
held = text.slice(lt);
|
|
140
|
+
text = text.slice(0, lt);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const re = /<(\/?)think>/gi;
|
|
144
|
+
let at = 0;
|
|
145
|
+
for (let m; (m = re.exec(text)); ) {
|
|
146
|
+
push(inThink, text.slice(at, m.index));
|
|
147
|
+
inThink = m[1] !== "/";
|
|
148
|
+
at = m.index + m[0].length;
|
|
149
|
+
}
|
|
150
|
+
push(inThink, text.slice(at));
|
|
151
|
+
return { parts, state: { inThink, held } };
|
|
152
|
+
}
|
package/gonext_agent_chat.py
CHANGED
|
@@ -7782,7 +7782,7 @@ def run_agent_chat(cfg):
|
|
|
7782
7782
|
_skill_call = _skills_mod.invocation(task_text, _skills)
|
|
7783
7783
|
if _skill_call:
|
|
7784
7784
|
_sk, _sk_body, _sk_req = _skill_call
|
|
7785
|
-
task_text = (_skills_mod.render(_sk, _sk_body) + "\n\nThe user's request: "
|
|
7785
|
+
task_text = (_skills_mod.render(_sk, _sk_body, _sk_req) + "\n\nThe user's request: "
|
|
7786
7786
|
+ (_sk_req or "(nothing more — do what the skill describes)"))
|
|
7787
7787
|
_log(f"skill invoked: {_sk['name']}")
|
|
7788
7788
|
|
package/gu-cli.mjs
CHANGED
|
@@ -20,7 +20,7 @@ import { execFile as execFileCallback, spawn } from "node:child_process";
|
|
|
20
20
|
import { createHash, randomUUID } from "node:crypto";
|
|
21
21
|
import { writeFileSync, unlinkSync } from "node:fs";
|
|
22
22
|
import { createTaskPool, createFolderQueue } from "./job-pools.mjs";
|
|
23
|
-
import { NO_STEP, foldStepTokens, withStepTokens } from "./step-tokens.mjs";
|
|
23
|
+
import { NO_STEP, foldStepTokens, withStepTokens, NO_CACHE, foldTurnCache, withTurnCache } from "./step-tokens.mjs";
|
|
24
24
|
import { agentStoppedLine, jobEnded, jobStatusFrom409, stopGracefully } from "./agent-stop.mjs";
|
|
25
25
|
import { ragJobConfig } from "./rag-job-config.mjs";
|
|
26
26
|
import { codingKeyFallbackLine, isOllamaCloudUrl } from "./coding-key-prompt.mjs";
|
|
@@ -4138,6 +4138,9 @@ async function runAgentChatJob(job) {
|
|
|
4138
4138
|
// Task #298: the CURRENT model call's own ↑ in / ↓ out, for the thinking line (the bar
|
|
4139
4139
|
// already shows the totals). Rides on the breakdown — see step-tokens.mjs.
|
|
4140
4140
|
let stepTokens = NO_STEP;
|
|
4141
|
+
// #323: the turn's cached prompt tokens (when the provider reports them) and its first step's
|
|
4142
|
+
// prompt — what the terminal needs to say what the turn COST. Rides on the breakdown too.
|
|
4143
|
+
let turnCache = NO_CACHE;
|
|
4141
4144
|
// Raw model-stream tokens (event.type === "stream") are Python code + `Thought:`
|
|
4142
4145
|
// prose + <code>/<|"|> fragments. The web Thought panel renders reasoning as
|
|
4143
4146
|
// markdown, which turns `#`-prefixed code comments into <h1> headings and
|
|
@@ -4305,17 +4308,20 @@ async function runAgentChatJob(job) {
|
|
|
4305
4308
|
if (breakdown) latestPromptBreakdown = breakdown;
|
|
4306
4309
|
const prevStep = stepTokens;
|
|
4307
4310
|
stepTokens = foldStepTokens(stepTokens, event);
|
|
4311
|
+
const prevCache = turnCache;
|
|
4312
|
+
turnCache = foldTurnCache(turnCache, event);
|
|
4313
|
+
const cacheChanged = turnCache.cached !== prevCache.cached || turnCache.firstIn !== prevCache.firstIn || turnCache.cacheReported !== prevCache.cacheReported;
|
|
4308
4314
|
const stepChanged =
|
|
4309
4315
|
stepTokens.step !== prevStep.step ||
|
|
4310
4316
|
stepTokens.stepIn !== prevStep.stepIn ||
|
|
4311
4317
|
stepTokens.stepOut !== prevStep.stepOut;
|
|
4312
|
-
if (latestPromptBreakdown) latestPromptBreakdown = withStepTokens(latestPromptBreakdown, stepTokens);
|
|
4318
|
+
if (latestPromptBreakdown) latestPromptBreakdown = withTurnCache(withStepTokens(latestPromptBreakdown, stepTokens), turnCache);
|
|
4313
4319
|
if (
|
|
4314
4320
|
nextTotal !== latestCodeTokens ||
|
|
4315
4321
|
nextMax !== latestMaxCodeTokens ||
|
|
4316
4322
|
nextOut !== latestCodeOutputTokens ||
|
|
4317
4323
|
breakdown ||
|
|
4318
|
-
(stepChanged && latestPromptBreakdown)
|
|
4324
|
+
((stepChanged || cacheChanged) && latestPromptBreakdown)
|
|
4319
4325
|
) {
|
|
4320
4326
|
latestCodeTokens = nextTotal;
|
|
4321
4327
|
latestMaxCodeTokens = nextMax;
|