zidane 5.4.2 → 5.5.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/README.md +45 -1
- package/dist/{agent-DxBoKDba.d.ts → agent-CvImMxMQ.d.ts} +256 -5
- package/dist/agent-CvImMxMQ.d.ts.map +1 -0
- package/dist/chat.d.ts +137 -16
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +3 -2
- package/dist/contexts/docker.d.ts +1 -1
- package/dist/contexts-DhmMlT2W.js +472 -0
- package/dist/contexts-DhmMlT2W.js.map +1 -0
- package/dist/contexts.d.ts +3 -3
- package/dist/contexts.js +1 -1
- package/dist/{errors-Byb0F8B9.js → errors-CDwtPIMX.js} +4 -2
- package/dist/{errors-Byb0F8B9.js.map → errors-CDwtPIMX.js.map} +1 -1
- package/dist/{index-BOtXdQkW.d.ts → index-B0uc2C5x.d.ts} +9 -3
- package/dist/index-B0uc2C5x.d.ts.map +1 -0
- package/dist/{index-BiO_5Hm4.d.ts → index-CbS75MD3.d.ts} +2 -2
- package/dist/index-CbS75MD3.d.ts.map +1 -0
- package/dist/{index-B2VOOijU.d.ts → index-CtXksgqb.d.ts} +73 -4
- package/dist/index-CtXksgqb.d.ts.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +11 -11
- package/dist/{interpolate-ERgZUxgg.js → interpolate-BaaKaKzN.js} +156 -19
- package/dist/interpolate-BaaKaKzN.js.map +1 -0
- package/dist/{login-CJbeAadS.js → login-iTy-0wYz.js} +3 -3
- package/dist/{login-CJbeAadS.js.map → login-iTy-0wYz.js.map} +1 -1
- package/dist/{mcp-DhmmJfxK.js → mcp-CNUbvbsy.js} +2 -2
- package/dist/{mcp-DhmmJfxK.js.map → mcp-CNUbvbsy.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-D0xT979U.js → messages-fTR19Ga6.js} +2 -2
- package/dist/{messages-D0xT979U.js.map → messages-fTR19Ga6.js.map} +1 -1
- package/dist/{presets-MCcvxiNT.js → presets-h6UWhghO.js} +3 -2
- package/dist/presets-h6UWhghO.js.map +1 -0
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-x3LZByR5.js → providers-G0VBZK9j.js} +4 -4
- package/dist/{providers-x3LZByR5.js.map → providers-G0VBZK9j.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +2 -2
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +2 -1
- package/dist/session/sqlite.js.map +1 -1
- package/dist/{session-BHZwxmfr.js → session-CbkiJDlH.js} +3 -2
- package/dist/session-CbkiJDlH.js.map +1 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +1 -1
- package/dist/{tools-BNfyY14s.js → tools-D_icxa-V.js} +813 -284
- package/dist/tools-D_icxa-V.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +2 -2
- package/dist/{transcript-anchors-DonKvoh4.d.ts → transcript-anchors-3FFw2xuk.d.ts} +98 -15
- package/dist/transcript-anchors-3FFw2xuk.d.ts.map +1 -0
- package/dist/tui.d.ts +29 -5
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +879 -70
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-TKvy0q29.js → turn-operations-CtgBlBHn.js} +412 -125
- package/dist/turn-operations-CtgBlBHn.js.map +1 -0
- package/dist/types-IcokUOyC.js.map +1 -1
- package/dist/types-KukEp-mi.d.ts +253 -0
- package/dist/types-KukEp-mi.d.ts.map +1 -0
- package/dist/types.d.ts +4 -4
- package/dist/types.js +1 -1
- package/docs/ARCHITECTURE.md +37 -3
- package/docs/CHAT.md +4 -2
- package/docs/RUN_IN_BACKGROUND.md +612 -0
- package/docs/SKILL.md +83 -14
- package/docs/TUI.md +40 -2
- package/package.json +4 -4
- package/dist/agent-DxBoKDba.d.ts.map +0 -1
- package/dist/contexts-BwiHIr2w.js +0 -129
- package/dist/contexts-BwiHIr2w.js.map +0 -1
- package/dist/index-B2VOOijU.d.ts.map +0 -1
- package/dist/index-BOtXdQkW.d.ts.map +0 -1
- package/dist/index-BiO_5Hm4.d.ts.map +0 -1
- package/dist/interpolate-ERgZUxgg.js.map +0 -1
- package/dist/presets-MCcvxiNT.js.map +0 -1
- package/dist/session-BHZwxmfr.js.map +0 -1
- package/dist/tools-BNfyY14s.js.map +0 -1
- package/dist/transcript-anchors-DonKvoh4.d.ts.map +0 -1
- package/dist/turn-operations-TKvy0q29.js.map +0 -1
- package/dist/types-Ce78ds4h.d.ts +0 -88
- package/dist/types-Ce78ds4h.d.ts.map +0 -1
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { a as multiEdit, c as grep, d as resolveOldString, f as styleReplacementForVia, i as readFile$1, l as glob, n as createSpawnTool, o as listFiles,
|
|
2
|
-
import { s as errorMessage } from "./errors-
|
|
1
|
+
import { H as previewLine, R as fmtTokens, U as shortId, a as multiEdit, c as grep, d as resolveOldString, f as styleReplacementForVia, i as readFile$1, l as glob, n as createSpawnTool, o as listFiles, t as writeFile$1, u as edit, y as shell } from "./tools-D_icxa-V.js";
|
|
2
|
+
import { s as errorMessage } from "./errors-CDwtPIMX.js";
|
|
3
3
|
import { n as toolResultToText } from "./types-IcokUOyC.js";
|
|
4
|
-
import { r as normalizeMcpServers } from "./mcp-
|
|
5
|
-
import { a as discoverSkills } from "./interpolate-
|
|
4
|
+
import { r as normalizeMcpServers } from "./mcp-CNUbvbsy.js";
|
|
5
|
+
import { a as discoverSkills } from "./interpolate-BaaKaKzN.js";
|
|
6
6
|
import { n as formatTokenUsage } from "./stats-DgOvY7wd.js";
|
|
7
|
-
import { n as definePreset, t as composePresets } from "./presets-
|
|
8
|
-
import { a as writeFileAtomic, i as anthropic, n as openai, r as cerebras, t as openrouter } from "./providers-
|
|
7
|
+
import { n as definePreset, t as composePresets } from "./presets-h6UWhghO.js";
|
|
8
|
+
import { a as writeFileAtomic, i as anthropic, n as openai, r as cerebras, t as openrouter } from "./providers-G0VBZK9j.js";
|
|
9
|
+
import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
10
|
+
import { homedir } from "node:os";
|
|
9
11
|
import { spawn } from "node:child_process";
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
10
13
|
import { readdir, stat, writeFile } from "node:fs/promises";
|
|
11
|
-
import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
12
14
|
import { getModel, getModels } from "@mariozechner/pi-ai";
|
|
13
|
-
import { existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
14
|
-
import { homedir } from "node:os";
|
|
15
15
|
import { anthropicOAuthProvider, openaiCodexOAuthProvider } from "@mariozechner/pi-ai/oauth";
|
|
16
16
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
17
17
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -1889,7 +1889,7 @@ function scoreFiles(catalog, query, limit, formatPath) {
|
|
|
1889
1889
|
return scored.slice(0, limit).map(({ entry, display }) => ({
|
|
1890
1890
|
id: display,
|
|
1891
1891
|
label: entry.name,
|
|
1892
|
-
description: parentDir(
|
|
1892
|
+
description: parentDir(entry.path),
|
|
1893
1893
|
insertText: `@${display} `,
|
|
1894
1894
|
data: entry
|
|
1895
1895
|
}));
|
|
@@ -2144,6 +2144,12 @@ const KEYBINDING_DEFS = [
|
|
|
2144
2144
|
label: "messages",
|
|
2145
2145
|
description: "enter select-turn mode to navigate previous messages"
|
|
2146
2146
|
},
|
|
2147
|
+
{
|
|
2148
|
+
action: "cancelToolCall",
|
|
2149
|
+
default: "ctrl+k",
|
|
2150
|
+
label: "cancel tool",
|
|
2151
|
+
description: "open the in-flight tool picker to cancel a single tool call without aborting the run (esc still aborts the whole run)"
|
|
2152
|
+
},
|
|
2147
2153
|
{
|
|
2148
2154
|
action: "enterQueueSelection",
|
|
2149
2155
|
default: "",
|
|
@@ -2180,6 +2186,12 @@ const KEYBINDING_DEFS = [
|
|
|
2180
2186
|
label: "copy",
|
|
2181
2187
|
description: "copy the selected turn content to the clipboard (OSC 52)"
|
|
2182
2188
|
},
|
|
2189
|
+
{
|
|
2190
|
+
action: "turnEdit",
|
|
2191
|
+
default: "e",
|
|
2192
|
+
label: "edit",
|
|
2193
|
+
description: "edit the text content of the selected turn"
|
|
2194
|
+
},
|
|
2183
2195
|
{
|
|
2184
2196
|
action: "sessionDelete",
|
|
2185
2197
|
default: "d",
|
|
@@ -2754,7 +2766,8 @@ function extractEditPayload(name, input, priorContent) {
|
|
|
2754
2766
|
oldString,
|
|
2755
2767
|
newString,
|
|
2756
2768
|
...input.replace_all === true ? { replaceAll: true } : {}
|
|
2757
|
-
}]
|
|
2769
|
+
}],
|
|
2770
|
+
...priorContent !== void 0 ? { priorContent } : {}
|
|
2758
2771
|
};
|
|
2759
2772
|
}
|
|
2760
2773
|
if (name === "multi_edit") {
|
|
@@ -2772,7 +2785,8 @@ function extractEditPayload(name, input, priorContent) {
|
|
|
2772
2785
|
return {
|
|
2773
2786
|
tool: "multi_edit",
|
|
2774
2787
|
path,
|
|
2775
|
-
hunks
|
|
2788
|
+
hunks,
|
|
2789
|
+
...priorContent !== void 0 ? { priorContent } : {}
|
|
2776
2790
|
};
|
|
2777
2791
|
}
|
|
2778
2792
|
if (name === "write_file") {
|
|
@@ -2784,7 +2798,8 @@ function extractEditPayload(name, input, priorContent) {
|
|
|
2784
2798
|
hunks: [{
|
|
2785
2799
|
oldString: priorContent ?? "",
|
|
2786
2800
|
newString: content
|
|
2787
|
-
}]
|
|
2801
|
+
}],
|
|
2802
|
+
...priorContent !== void 0 ? { priorContent } : {}
|
|
2788
2803
|
};
|
|
2789
2804
|
}
|
|
2790
2805
|
}
|
|
@@ -2985,6 +3000,104 @@ function buildContextualDiff(payload, priorContent, contextLines = 3) {
|
|
|
2985
3000
|
}
|
|
2986
3001
|
return `${parts.join("\n")}\n`;
|
|
2987
3002
|
}
|
|
3003
|
+
/**
|
|
3004
|
+
* Build a per-hunk digest used by the compact diff view.
|
|
3005
|
+
*
|
|
3006
|
+
* Strategy:
|
|
3007
|
+
* - When `priorContent` is present and the payload describes a real
|
|
3008
|
+
* file transformation, compute the contextual diff once, then walk
|
|
3009
|
+
* the LCS ops splitting at runs of `add` / `remove` to anchor each
|
|
3010
|
+
* summary entry to the **real** file line. This guarantees the
|
|
3011
|
+
* summary's `L<n>` matches what the user sees in their editor.
|
|
3012
|
+
* - Otherwise, fall back to per-hunk LCS over the (oldString,
|
|
3013
|
+
* newString) snippet pair. Line numbers are absent because the
|
|
3014
|
+
* snippet has no file position.
|
|
3015
|
+
*/
|
|
3016
|
+
function summarizeEditPayload(payload) {
|
|
3017
|
+
const prior = payload.priorContent;
|
|
3018
|
+
if (prior !== void 0) return summarizeOpsByHunk(computeLineDiff(prior, applyEditPayload(payload, prior)));
|
|
3019
|
+
const hunks = [];
|
|
3020
|
+
let totalAdded = 0;
|
|
3021
|
+
let totalRemoved = 0;
|
|
3022
|
+
for (const hunk of payload.hunks) {
|
|
3023
|
+
const ops = computeLineDiff(hunk.oldString, hunk.newString);
|
|
3024
|
+
let added = 0;
|
|
3025
|
+
let removed = 0;
|
|
3026
|
+
let firstOld;
|
|
3027
|
+
let firstNew;
|
|
3028
|
+
for (const op of ops) if (op.op === "add") {
|
|
3029
|
+
added++;
|
|
3030
|
+
if (firstNew === void 0) firstNew = op.text;
|
|
3031
|
+
} else if (op.op === "remove") {
|
|
3032
|
+
removed++;
|
|
3033
|
+
if (firstOld === void 0) firstOld = op.text;
|
|
3034
|
+
}
|
|
3035
|
+
totalAdded += added;
|
|
3036
|
+
totalRemoved += removed;
|
|
3037
|
+
hunks.push({
|
|
3038
|
+
added,
|
|
3039
|
+
removed,
|
|
3040
|
+
...firstOld !== void 0 ? { firstOld } : {},
|
|
3041
|
+
...firstNew !== void 0 ? { firstNew } : {}
|
|
3042
|
+
});
|
|
3043
|
+
}
|
|
3044
|
+
return {
|
|
3045
|
+
totalAdded,
|
|
3046
|
+
totalRemoved,
|
|
3047
|
+
hunks
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
/**
|
|
3051
|
+
* Walk an LCS op stream and emit one summary entry per *run* of
|
|
3052
|
+
* non-context ops, with the new-file line number where each run
|
|
3053
|
+
* starts. Adjacent add/remove ops collapse into the same entry —
|
|
3054
|
+
* matches git's hunk grouping at zero context.
|
|
3055
|
+
*/
|
|
3056
|
+
function summarizeOpsByHunk(ops) {
|
|
3057
|
+
const hunks = [];
|
|
3058
|
+
let totalAdded = 0;
|
|
3059
|
+
let totalRemoved = 0;
|
|
3060
|
+
let nl = 1;
|
|
3061
|
+
let i = 0;
|
|
3062
|
+
while (i < ops.length) {
|
|
3063
|
+
if (ops[i].op === "context") {
|
|
3064
|
+
nl++;
|
|
3065
|
+
i++;
|
|
3066
|
+
continue;
|
|
3067
|
+
}
|
|
3068
|
+
const runStartLine = nl;
|
|
3069
|
+
let added = 0;
|
|
3070
|
+
let removed = 0;
|
|
3071
|
+
let firstOld;
|
|
3072
|
+
let firstNew;
|
|
3073
|
+
while (i < ops.length && ops[i].op !== "context") {
|
|
3074
|
+
const cur = ops[i];
|
|
3075
|
+
if (cur.op === "add") {
|
|
3076
|
+
added++;
|
|
3077
|
+
if (firstNew === void 0) firstNew = cur.text;
|
|
3078
|
+
nl++;
|
|
3079
|
+
} else {
|
|
3080
|
+
removed++;
|
|
3081
|
+
if (firstOld === void 0) firstOld = cur.text;
|
|
3082
|
+
}
|
|
3083
|
+
i++;
|
|
3084
|
+
}
|
|
3085
|
+
totalAdded += added;
|
|
3086
|
+
totalRemoved += removed;
|
|
3087
|
+
hunks.push({
|
|
3088
|
+
line: runStartLine,
|
|
3089
|
+
added,
|
|
3090
|
+
removed,
|
|
3091
|
+
...firstOld !== void 0 ? { firstOld } : {},
|
|
3092
|
+
...firstNew !== void 0 ? { firstNew } : {}
|
|
3093
|
+
});
|
|
3094
|
+
}
|
|
3095
|
+
return {
|
|
3096
|
+
totalAdded,
|
|
3097
|
+
totalRemoved,
|
|
3098
|
+
hunks
|
|
3099
|
+
};
|
|
3100
|
+
}
|
|
2988
3101
|
function previewEditPayload(payload, priorContent, contextLines = 3) {
|
|
2989
3102
|
const resolution = [];
|
|
2990
3103
|
const resolvedHunks = [];
|
|
@@ -3208,9 +3321,45 @@ function deriveSessionTitle(turns, metadata) {
|
|
|
3208
3321
|
*/
|
|
3209
3322
|
function eventsFromTurns(turns, runs = []) {
|
|
3210
3323
|
const runById = /* @__PURE__ */ new Map();
|
|
3211
|
-
for (const run of runs) runById.set(run.id, run);
|
|
3324
|
+
for (const run of runs) if (!runById.has(run.id)) runById.set(run.id, run);
|
|
3325
|
+
const runStack = [];
|
|
3326
|
+
const pendingToolCallsPerRun = /* @__PURE__ */ new Map();
|
|
3327
|
+
for (const turn of turns) {
|
|
3328
|
+
const rid = turn.runId;
|
|
3329
|
+
if (!rid) continue;
|
|
3330
|
+
const idxInStack = runStack.indexOf(rid);
|
|
3331
|
+
if (idxInStack >= 0) runStack.length = idxInStack + 1;
|
|
3332
|
+
else {
|
|
3333
|
+
const top = runStack[runStack.length - 1];
|
|
3334
|
+
if (!(top !== void 0 && (pendingToolCallsPerRun.get(top)?.size ?? 0) > 0)) runStack.length = 0;
|
|
3335
|
+
runStack.push(rid);
|
|
3336
|
+
if (!runById.has(rid)) {
|
|
3337
|
+
const depth = runStack.length - 1;
|
|
3338
|
+
const parentRunId = depth > 0 ? runStack[depth - 1] : void 0;
|
|
3339
|
+
let inferredPrompt = "";
|
|
3340
|
+
if (turn.role === "user") {
|
|
3341
|
+
for (const block of turn.content) if (block.type === "text" && block.text.trim()) {
|
|
3342
|
+
inferredPrompt = block.text;
|
|
3343
|
+
break;
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
runById.set(rid, {
|
|
3347
|
+
id: rid,
|
|
3348
|
+
startedAt: turn.createdAt,
|
|
3349
|
+
prompt: inferredPrompt,
|
|
3350
|
+
status: "completed",
|
|
3351
|
+
depth,
|
|
3352
|
+
...parentRunId ? { parentRunId } : {}
|
|
3353
|
+
});
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
const pending = pendingToolCallsPerRun.get(rid) ?? /* @__PURE__ */ new Set();
|
|
3357
|
+
for (const block of turn.content) if (block.type === "tool_call") pending.add(block.id);
|
|
3358
|
+
else if (block.type === "tool_result") pending.delete(block.callId);
|
|
3359
|
+
pendingToolCallsPerRun.set(rid, pending);
|
|
3360
|
+
}
|
|
3212
3361
|
const childLabelByRunId = /* @__PURE__ */ new Map();
|
|
3213
|
-
|
|
3362
|
+
[...runById.values()].filter((r) => (r.depth ?? 0) > 0).sort((a, b) => a.startedAt - b.startedAt).forEach((r, i) => childLabelByRunId.set(r.id, `child-${i + 1}`));
|
|
3214
3363
|
const labelFor = (runId) => childLabelByRunId.get(runId) ?? runId;
|
|
3215
3364
|
const toolByCallId = /* @__PURE__ */ new Map();
|
|
3216
3365
|
for (const turn of turns) {
|
|
@@ -3256,7 +3405,7 @@ function eventsFromTurns(turns, runs = []) {
|
|
|
3256
3405
|
});
|
|
3257
3406
|
};
|
|
3258
3407
|
const pushSpawnStart = (run) => {
|
|
3259
|
-
const taskPreview = run.prompt
|
|
3408
|
+
const taskPreview = previewLine(run.prompt, 80);
|
|
3260
3409
|
events.push({
|
|
3261
3410
|
kind: "spawn-start",
|
|
3262
3411
|
text: taskPreview,
|
|
@@ -3286,6 +3435,11 @@ function eventsFromTurns(turns, runs = []) {
|
|
|
3286
3435
|
};
|
|
3287
3436
|
if (turn.role === "user") {
|
|
3288
3437
|
for (const block of turn.content) if (block.type === "text" && block.text.trim()) {
|
|
3438
|
+
const taskEvent = parseTaskNotificationBlock(block.text, tag);
|
|
3439
|
+
if (taskEvent) {
|
|
3440
|
+
events.push(taskEvent);
|
|
3441
|
+
continue;
|
|
3442
|
+
}
|
|
3289
3443
|
if (depth === 0) {
|
|
3290
3444
|
if (lastDepthAtEmission === 0) events.push({
|
|
3291
3445
|
kind: "separator",
|
|
@@ -3361,6 +3515,84 @@ function eventsFromTurns(turns, runs = []) {
|
|
|
3361
3515
|
return events;
|
|
3362
3516
|
}
|
|
3363
3517
|
/** Shared formatter for the `↳ name(args)` line shown on tool calls. */
|
|
3518
|
+
/**
|
|
3519
|
+
* Pattern matching the leading `<task-notification>` shape that the
|
|
3520
|
+
* agent's notification-injection path produces (see
|
|
3521
|
+
* `renderTaskNotificationXml` in `src/agent.ts`). Anchored so a stray
|
|
3522
|
+
* notification-shaped phrase mid-prompt doesn't trigger a false-positive
|
|
3523
|
+
* detection — the model's wire format always starts the user-turn
|
|
3524
|
+
* content block with this exact opening tag.
|
|
3525
|
+
*/
|
|
3526
|
+
const TASK_NOTIFICATION_RE = /^\s*<task-notification>([\s\S]*?)<\/task-notification>\s*$/;
|
|
3527
|
+
/**
|
|
3528
|
+
* Per-field extractors. Compiled once at module load (parsing happens
|
|
3529
|
+
* on every session replay, and a fresh `new RegExp` per pick burns
|
|
3530
|
+
* cycles for no reason).
|
|
3531
|
+
*/
|
|
3532
|
+
const TASK_NOTIFICATION_FIELD_RES = {
|
|
3533
|
+
taskId: /<task-id>([\s\S]*?)<\/task-id>/,
|
|
3534
|
+
status: /<status>([\s\S]*?)<\/status>/,
|
|
3535
|
+
exitCode: /<exit-code>([\s\S]*?)<\/exit-code>/,
|
|
3536
|
+
command: /<command>([\s\S]*?)<\/command>/,
|
|
3537
|
+
outputFile: /<output-file>([\s\S]*?)<\/output-file>/,
|
|
3538
|
+
durationMs: /<duration-ms>([\s\S]*?)<\/duration-ms>/,
|
|
3539
|
+
summary: /<summary>([\s\S]*?)<\/summary>/
|
|
3540
|
+
};
|
|
3541
|
+
/**
|
|
3542
|
+
* Detect a `<task-notification>` text block on replay and convert it
|
|
3543
|
+
* into a structured `task-notification` StreamEvent so the TUI's banner
|
|
3544
|
+
* renderer can paint it cleanly. Returns `null` when the block isn't a
|
|
3545
|
+
* notification (the caller falls back to the normal user-prompt path).
|
|
3546
|
+
*
|
|
3547
|
+
* Reads every field DIRECTLY from its tag — does NOT parse `<summary>`
|
|
3548
|
+
* for the underlying data. The summary is a derived display string,
|
|
3549
|
+
* not a source of truth: changing its format (localization, signal in
|
|
3550
|
+
* the label, whatever) must NEVER break replay. Each field falls back
|
|
3551
|
+
* to a safe default so older transcripts pre-dating a tag addition
|
|
3552
|
+
* still render.
|
|
3553
|
+
*/
|
|
3554
|
+
function parseTaskNotificationBlock(text, tag) {
|
|
3555
|
+
const m = text.match(TASK_NOTIFICATION_RE);
|
|
3556
|
+
if (!m) return null;
|
|
3557
|
+
const body = m[1];
|
|
3558
|
+
const pick = (re) => {
|
|
3559
|
+
const inner = body.match(re);
|
|
3560
|
+
return inner ? unescapeXml(inner[1].trim()) : void 0;
|
|
3561
|
+
};
|
|
3562
|
+
const taskId = pick(TASK_NOTIFICATION_FIELD_RES.taskId) ?? "?";
|
|
3563
|
+
const statusRaw = pick(TASK_NOTIFICATION_FIELD_RES.status) ?? "exited";
|
|
3564
|
+
const status = statusRaw === "killed" ? "killed" : "exited";
|
|
3565
|
+
const exitCode = Number.parseInt(pick(TASK_NOTIFICATION_FIELD_RES.exitCode) ?? "0", 10) || 0;
|
|
3566
|
+
const command = pick(TASK_NOTIFICATION_FIELD_RES.command) ?? "";
|
|
3567
|
+
const outputPath = pick(TASK_NOTIFICATION_FIELD_RES.outputFile) ?? "";
|
|
3568
|
+
const durationMs = Number.parseInt(pick(TASK_NOTIFICATION_FIELD_RES.durationMs) ?? "0", 10) || 0;
|
|
3569
|
+
return {
|
|
3570
|
+
kind: "task-notification",
|
|
3571
|
+
text: pick(TASK_NOTIFICATION_FIELD_RES.summary) ?? `${taskId} ${statusRaw}${exitCode ? ` ${exitCode}` : ""}`,
|
|
3572
|
+
task: {
|
|
3573
|
+
taskId,
|
|
3574
|
+
status,
|
|
3575
|
+
exitCode,
|
|
3576
|
+
outputPath,
|
|
3577
|
+
command,
|
|
3578
|
+
durationMs
|
|
3579
|
+
},
|
|
3580
|
+
turnId: tag.turnId,
|
|
3581
|
+
...tag.childId !== void 0 ? { childId: tag.childId } : {},
|
|
3582
|
+
...tag.depth !== void 0 ? { depth: tag.depth } : {}
|
|
3583
|
+
};
|
|
3584
|
+
}
|
|
3585
|
+
/**
|
|
3586
|
+
* Reverse of `escapeXml` for the small set of entities the writer
|
|
3587
|
+
* emits. Not a full HTML entity decoder — just enough to round-trip
|
|
3588
|
+
* `<` / `>` / `&` / quotes through persistence.
|
|
3589
|
+
*
|
|
3590
|
+
* `&` MUST be replaced last, otherwise `&lt;` becomes `<`
|
|
3591
|
+
* then `<` (data corruption — silent double-decode).
|
|
3592
|
+
*/
|
|
3593
|
+
function unescapeXml(s) {
|
|
3594
|
+
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, "\"").replace(/'/g, "'").replace(/&/g, "&");
|
|
3595
|
+
}
|
|
3364
3596
|
function toolCallPreview(name, input) {
|
|
3365
3597
|
const args = JSON.stringify(input);
|
|
3366
3598
|
return args && args !== "{}" ? `${name}(${args})` : name;
|
|
@@ -3493,7 +3725,8 @@ const MARGIN_TOP = {
|
|
|
3493
3725
|
"markdown": 1,
|
|
3494
3726
|
"spawn-start": 1,
|
|
3495
3727
|
"spawn-end": 0,
|
|
3496
|
-
"compact-summary": 1
|
|
3728
|
+
"compact-summary": 1,
|
|
3729
|
+
"task-notification": 1
|
|
3497
3730
|
};
|
|
3498
3731
|
const TOOL_KINDS = new Set(["tool", "tool-result"]);
|
|
3499
3732
|
/**
|
|
@@ -3503,20 +3736,28 @@ const TOOL_KINDS = new Set(["tool", "tool-result"]);
|
|
|
3503
3736
|
* - A `tool` / `tool-result` event right after another
|
|
3504
3737
|
* `tool` / `tool-result` collapses to a tight list — call→result
|
|
3505
3738
|
* pairs and back-to-back calls read as one logical block.
|
|
3506
|
-
* -
|
|
3507
|
-
*
|
|
3508
|
-
*
|
|
3509
|
-
*
|
|
3510
|
-
*
|
|
3739
|
+
* - Consecutive `task-notification` banners stack tight — a burst of
|
|
3740
|
+
* background-task completions reads as one log column rather than
|
|
3741
|
+
* scattered banners.
|
|
3742
|
+
*
|
|
3743
|
+
* NB: parent-level events (`depth === 0`) following a subagent block
|
|
3744
|
+
* (`depth > 0`) get their normal default margin. An earlier revision
|
|
3745
|
+
* collapsed this transition to 0 on the assumption that the subagent's
|
|
3746
|
+
* `🌳` end-marker provided enough visual separation — it didn't. A
|
|
3747
|
+
* 2-cell emoji on the same row as the close marker doesn't create a
|
|
3748
|
+
* vertical break, and in hide-subagent-output mode the SubagentBlock
|
|
3749
|
+
* box isn't rendered either, so the parent's follow-up markdown was
|
|
3750
|
+
* glued directly against the close line. Subagents should READ like
|
|
3751
|
+
* tool calls: open with breathing room, close tight inside, and open
|
|
3752
|
+
* a clean gap before the parent's next thought — that's the contract
|
|
3753
|
+
* the tool-call → markdown transition uses, mirrored here.
|
|
3511
3754
|
*
|
|
3512
3755
|
* Renderer-agnostic — TUI uses it as Yoga `marginTop`; a CSS host can
|
|
3513
3756
|
* use the same number as the row's top margin in `em` / `rem`.
|
|
3514
3757
|
*/
|
|
3515
3758
|
function marginTopFor(event, previous) {
|
|
3516
3759
|
if (TOOL_KINDS.has(event.kind) && previous && TOOL_KINDS.has(previous.kind)) return 0;
|
|
3517
|
-
|
|
3518
|
-
const previousDepth = previous?.depth ?? 0;
|
|
3519
|
-
if (eventDepth === 0 && previousDepth > 0) return 0;
|
|
3760
|
+
if (event.kind === "task-notification" && previous?.kind === "task-notification") return 0;
|
|
3520
3761
|
return MARGIN_TOP[event.kind] ?? 0;
|
|
3521
3762
|
}
|
|
3522
3763
|
/**
|
|
@@ -4845,6 +5086,7 @@ const DEFAULT_SETTINGS = {
|
|
|
4845
5086
|
autoCompact: true,
|
|
4846
5087
|
autoCompactThreshold: .8,
|
|
4847
5088
|
showEditDiffs: true,
|
|
5089
|
+
editDiffDisplay: "full",
|
|
4848
5090
|
targetFps: 60,
|
|
4849
5091
|
allowInteraction: true,
|
|
4850
5092
|
smoothStreaming: true,
|
|
@@ -4996,6 +5238,18 @@ const SETTINGS_CHOICES = [
|
|
|
4996
5238
|
}
|
|
4997
5239
|
]
|
|
4998
5240
|
},
|
|
5241
|
+
{
|
|
5242
|
+
key: "editDiffDisplay",
|
|
5243
|
+
label: "Edit diff density",
|
|
5244
|
+
description: "full unified diff with line numbers · or compact per-hunk summary list",
|
|
5245
|
+
options: [{
|
|
5246
|
+
value: "full",
|
|
5247
|
+
label: "Full"
|
|
5248
|
+
}, {
|
|
5249
|
+
value: "compact",
|
|
5250
|
+
label: "Compact"
|
|
5251
|
+
}]
|
|
5252
|
+
},
|
|
4999
5253
|
{
|
|
5000
5254
|
key: "autoCompactThreshold",
|
|
5001
5255
|
label: "Auto-compact threshold",
|
|
@@ -5295,51 +5549,6 @@ function toEntries(paths, source, maxFiles) {
|
|
|
5295
5549
|
return out;
|
|
5296
5550
|
}
|
|
5297
5551
|
//#endregion
|
|
5298
|
-
//#region src/chat/format.ts
|
|
5299
|
-
/** Compact token formatter — 12_415 → "12.4k", 1_234_567 → "1.23M". */
|
|
5300
|
-
function fmtTokens(n) {
|
|
5301
|
-
if (n < 1e3) return String(n);
|
|
5302
|
-
if (n < 1e6) return `${(n / 1e3).toFixed(n < 1e4 ? 2 : 1)}k`;
|
|
5303
|
-
return `${(n / 1e6).toFixed(2)}M`;
|
|
5304
|
-
}
|
|
5305
|
-
/** Compact relative-time formatter — "just now / 5m / 3h / 2d". */
|
|
5306
|
-
function ageString(ts, now = Date.now()) {
|
|
5307
|
-
const m = Math.floor((now - ts) / 6e4);
|
|
5308
|
-
if (m < 1) return "just now";
|
|
5309
|
-
if (m < 60) return `${m}m ago`;
|
|
5310
|
-
const h = Math.floor(m / 60);
|
|
5311
|
-
if (h < 24) return `${h}h ago`;
|
|
5312
|
-
return `${Math.floor(h / 24)}d ago`;
|
|
5313
|
-
}
|
|
5314
|
-
/** Six-char short form of a session id for headers and lists. */
|
|
5315
|
-
function shortId(id) {
|
|
5316
|
-
return id.replace(/-/g, "").slice(0, 6);
|
|
5317
|
-
}
|
|
5318
|
-
/**
|
|
5319
|
-
* Compact an absolute path for display: replace the user's `$HOME`
|
|
5320
|
-
* prefix with `~` (so `/Users/yael/Code/zidane` → `~/Code/zidane`),
|
|
5321
|
-
* and optionally left-truncate with an ellipsis when the result
|
|
5322
|
-
* still exceeds `maxWidth` (so the path's *tail* — the part the user
|
|
5323
|
-
* recognizes — stays visible: `…/zidane` rather than `/Users/yaeluil…`).
|
|
5324
|
-
*
|
|
5325
|
-
* `maxWidth` is the maximum *display width* in cells. Omit to skip
|
|
5326
|
-
* truncation. Paths outside `$HOME` are returned verbatim modulo
|
|
5327
|
-
* truncation. The ellipsis (`…`) counts as one cell.
|
|
5328
|
-
*
|
|
5329
|
-
* `home` overrides `os.homedir()` for tests; production callers leave
|
|
5330
|
-
* it undefined and pay the cheap one-syscall lookup per call.
|
|
5331
|
-
*/
|
|
5332
|
-
function compactPath(path, maxWidth, home) {
|
|
5333
|
-
const h = home ?? homedir();
|
|
5334
|
-
let display = path;
|
|
5335
|
-
if (h) {
|
|
5336
|
-
if (path === h) display = "~";
|
|
5337
|
-
else if (path.startsWith(`${h}/`)) display = `~${path.slice(h.length)}`;
|
|
5338
|
-
}
|
|
5339
|
-
if (maxWidth !== void 0 && maxWidth > 1 && display.length > maxWidth) return `…${display.slice(display.length - maxWidth + 1)}`;
|
|
5340
|
-
return display;
|
|
5341
|
-
}
|
|
5342
|
-
//#endregion
|
|
5343
5552
|
//#region src/chat/generate-title.ts
|
|
5344
5553
|
/** Hard cap on the result length. Anything longer is truncated client-side. */
|
|
5345
5554
|
const TITLE_MAX_CHARS = 60;
|
|
@@ -6552,13 +6761,17 @@ async function runOAuthLogin(descriptor, options) {
|
|
|
6552
6761
|
*
|
|
6553
6762
|
* Rule:
|
|
6554
6763
|
*
|
|
6555
|
-
* -
|
|
6556
|
-
*
|
|
6557
|
-
*
|
|
6558
|
-
*
|
|
6559
|
-
*
|
|
6560
|
-
*
|
|
6561
|
-
*
|
|
6764
|
+
* - File at `cwd` itself → `.`.
|
|
6765
|
+
* - File under `cwd` (subtree) → forward-slashed CWD-relative,
|
|
6766
|
+
* no `..`.
|
|
6767
|
+
* - File above `cwd` but still inside the project → CWD-relative
|
|
6768
|
+
* with `..` traversal (e.g. `../EDIT_THIS.md`). Shorter and more
|
|
6769
|
+
* scannable than absolute, and the agent's tools resolve both.
|
|
6770
|
+
* - File outside the project (input was passed absolute) → returned
|
|
6771
|
+
* verbatim. Inputs that arrive as project-root-relative strings
|
|
6772
|
+
* are always inside the project by construction, so this branch
|
|
6773
|
+
* only kicks in when a direct caller hands the function an
|
|
6774
|
+
* absolute target.
|
|
6562
6775
|
*
|
|
6563
6776
|
* Pure, no I/O, deterministic.
|
|
6564
6777
|
*/
|
|
@@ -6569,13 +6782,18 @@ async function runOAuthLogin(descriptor, options) {
|
|
|
6569
6782
|
*
|
|
6570
6783
|
* Inputs:
|
|
6571
6784
|
* - `projectRelativePath` — forward-slashed, no leading slash. Returned
|
|
6572
|
-
* verbatim when it's empty or already absolute (defensive
|
|
6785
|
+
* verbatim when it's empty or already absolute (defensive — the
|
|
6786
|
+
* absolute-input branch is the de-facto "outside the project"
|
|
6787
|
+
* escape; the catalog itself never emits absolute paths).
|
|
6573
6788
|
* - `projectRoot` — absolute path of the discovery anchor.
|
|
6574
6789
|
* - `cwd` — absolute path of `process.cwd()` at the moment of
|
|
6575
6790
|
* insertion.
|
|
6576
6791
|
*
|
|
6577
|
-
* Output is always either an absolute path
|
|
6578
|
-
*
|
|
6792
|
+
* Output is always either an absolute path (only when the input arrived
|
|
6793
|
+
* absolute) OR a CWD-relative path. The relative form may include
|
|
6794
|
+
* `../` segments when the target sits above `cwd` within the project,
|
|
6795
|
+
* which keeps cross-tree references short and scannable
|
|
6796
|
+
* (e.g. `../EDIT_THIS.md` instead of `/Users/.../zidane/EDIT_THIS.md`).
|
|
6579
6797
|
*/
|
|
6580
6798
|
function formatPathForCwd(projectRelativePath, projectRoot, cwd) {
|
|
6581
6799
|
if (projectRelativePath.length === 0) return projectRelativePath;
|
|
@@ -6585,7 +6803,6 @@ function formatPathForCwd(projectRelativePath, projectRoot, cwd) {
|
|
|
6585
6803
|
if (target === here) return ".";
|
|
6586
6804
|
const rel = relative(here, target);
|
|
6587
6805
|
if (rel.length === 0) return ".";
|
|
6588
|
-
if (rel === ".." || rel.startsWith(`..${sep}`) || rel.startsWith("../")) return target;
|
|
6589
6806
|
return sep === "/" ? rel : rel.split(sep).join(posix.sep);
|
|
6590
6807
|
}
|
|
6591
6808
|
//#endregion
|
|
@@ -6703,7 +6920,7 @@ function getSafelist(dataDir, projectDir) {
|
|
|
6703
6920
|
return readProjects(dataDir)[projectDir]?.safelist ?? [];
|
|
6704
6921
|
}
|
|
6705
6922
|
/**
|
|
6706
|
-
* Tools that always pass without prompting.
|
|
6923
|
+
* Tools that always pass without prompting. Four categories:
|
|
6707
6924
|
*
|
|
6708
6925
|
* - **Pure reads** (`read_file`, `list_files`, `glob`, `grep`) — no
|
|
6709
6926
|
* side effects on disk or the model's own state.
|
|
@@ -6717,6 +6934,18 @@ function getSafelist(dataDir, projectDir) {
|
|
|
6717
6934
|
* a per-call approval prompt would drown the conversation in
|
|
6718
6935
|
* interruptions that buy no safety since the tool's surface is
|
|
6719
6936
|
* read/write on a metadata bag we already trust the agent with.
|
|
6937
|
+
* - **Skill activation** (`skills_use`) — mutates a small per-agent
|
|
6938
|
+
* Map of active skills and returns the activated skill's bundled
|
|
6939
|
+
* instructions (or releases an entry on `mode: "deactivate"`).
|
|
6940
|
+
* Pure state on the agent — no shell, no disk writes, no network.
|
|
6941
|
+
* Gating it would also defeat the "model recovers from
|
|
6942
|
+
* `AgentToolNotAllowedError` by calling
|
|
6943
|
+
* `skills_use({ mode: "deactivate", name })`" path: a stuck model
|
|
6944
|
+
* would have to ask the user permission to *unstick* itself on
|
|
6945
|
+
* every retry. `skills_read` and `skills_run_script` are
|
|
6946
|
+
* intentionally NOT on this list — the former touches disk and
|
|
6947
|
+
* the latter executes arbitrary scripts, so both keep going
|
|
6948
|
+
* through the regular gate.
|
|
6720
6949
|
*
|
|
6721
6950
|
* Users who want to gate any of these must disable safe-mode entirely
|
|
6722
6951
|
* (or fork this list in their own embedding).
|
|
@@ -6729,7 +6958,8 @@ const IMPLICITLY_SAFE_TOOLS = [
|
|
|
6729
6958
|
"ask_user",
|
|
6730
6959
|
"present_plan",
|
|
6731
6960
|
"todowrite",
|
|
6732
|
-
"todoread"
|
|
6961
|
+
"todoread",
|
|
6962
|
+
"skills_use"
|
|
6733
6963
|
];
|
|
6734
6964
|
/** Common input keys carrying the "primary argument" we scope safelists on. */
|
|
6735
6965
|
const PRIMARY_ARG_KEYS = [
|
|
@@ -7245,33 +7475,40 @@ const TICK_INTERVAL_MS = 16;
|
|
|
7245
7475
|
/**
|
|
7246
7476
|
* Smooth-streaming display rate, in characters per second.
|
|
7247
7477
|
*
|
|
7248
|
-
* -
|
|
7249
|
-
*
|
|
7250
|
-
*
|
|
7251
|
-
* - {@link
|
|
7252
|
-
*
|
|
7253
|
-
*
|
|
7254
|
-
*
|
|
7255
|
-
*
|
|
7256
|
-
*
|
|
7257
|
-
*
|
|
7258
|
-
*
|
|
7259
|
-
*
|
|
7260
|
-
*
|
|
7261
|
-
*
|
|
7262
|
-
*
|
|
7478
|
+
* Three-segment continuous ramp (no step functions, no cliff) so the
|
|
7479
|
+
* typewriter cadence never visibly "jumps" mid-stream:
|
|
7480
|
+
*
|
|
7481
|
+
* - {@link SMOOTH_BASE_CPS} — calm-state floor. ~200 CPS is a fast
|
|
7482
|
+
* typist's pace; comfortably readable without feeling like a
|
|
7483
|
+
* deliberate performance.
|
|
7484
|
+
* - {@link SMOOTH_BURST_CPS} at {@link SMOOTH_BURST_BACKLOG_CHARS} —
|
|
7485
|
+
* moderate catch-up speed once the buffer is one paragraph behind
|
|
7486
|
+
* the provider.
|
|
7487
|
+
* - {@link SMOOTH_MAX_CPS} at {@link SMOOTH_MAX_BACKLOG_CHARS} —
|
|
7488
|
+
* bounded ceiling. Past this we plateau at max-cps rather than
|
|
7489
|
+
* instant-draining; max-cps is already well above any human reading
|
|
7490
|
+
* rate so even a 10K-char dump completes in ~2.5s of fast typewriter
|
|
7491
|
+
* instead of a single-frame teleport.
|
|
7492
|
+
*
|
|
7493
|
+
* The previous failsafe (instant drain at 2K chars) was a literal
|
|
7494
|
+
* step from ~10 chars/frame to "everything in one frame" — the worst
|
|
7495
|
+
* jump in the smooth pipeline. The continued ramp eliminates it
|
|
7496
|
+
* without losing bounded latency, since CPS keeps growing with
|
|
7497
|
+
* backlog up to the cap.
|
|
7263
7498
|
*/
|
|
7264
7499
|
const SMOOTH_BASE_CPS = 200;
|
|
7265
7500
|
const SMOOTH_BURST_CPS = 600;
|
|
7266
7501
|
const SMOOTH_BURST_BACKLOG_CHARS = 400;
|
|
7267
|
-
const
|
|
7502
|
+
const SMOOTH_MAX_CPS = 4e3;
|
|
7503
|
+
const SMOOTH_MAX_BACKLOG_CHARS = 4e3;
|
|
7268
7504
|
const PARENT_OWNER = "parent";
|
|
7269
7505
|
function emptyBucket(owner, depth) {
|
|
7270
7506
|
return {
|
|
7271
7507
|
markdown: "",
|
|
7272
7508
|
thinking: "",
|
|
7273
7509
|
owner,
|
|
7274
|
-
depth
|
|
7510
|
+
depth,
|
|
7511
|
+
smoothCarry: 0
|
|
7275
7512
|
};
|
|
7276
7513
|
}
|
|
7277
7514
|
function applyBucket(prev, bucket) {
|
|
@@ -7383,23 +7620,52 @@ function turnContextSize(usage) {
|
|
|
7383
7620
|
return (usage.input ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheCreation ?? 0);
|
|
7384
7621
|
}
|
|
7385
7622
|
/**
|
|
7386
|
-
*
|
|
7387
|
-
* current backlog
|
|
7388
|
-
*
|
|
7389
|
-
*
|
|
7623
|
+
* Target chars-per-second for the next smooth-mode tick given the
|
|
7624
|
+
* current bucket backlog. Two linear segments — base→burst over the
|
|
7625
|
+
* first {@link SMOOTH_BURST_BACKLOG_CHARS}, burst→max over the rest of
|
|
7626
|
+
* the ramp — so the function is continuous everywhere and plateaus
|
|
7627
|
+
* (rather than cliffs) once backlog passes {@link SMOOTH_MAX_BACKLOG_CHARS}.
|
|
7390
7628
|
*
|
|
7391
|
-
*
|
|
7392
|
-
*
|
|
7393
|
-
*
|
|
7394
|
-
* legacy step at the burst threshold) so the chars-per-frame count never
|
|
7395
|
-
* jumps mid-stream — visible jumps in the typewriter cadence read as the
|
|
7396
|
-
* text itself jumping.
|
|
7629
|
+
* Exported for tests; the runtime path goes through
|
|
7630
|
+
* {@link smoothCharsForTick} which folds in the per-bucket fractional
|
|
7631
|
+
* remainder.
|
|
7397
7632
|
*/
|
|
7398
|
-
function
|
|
7633
|
+
function smoothCpsForBacklog(backlog) {
|
|
7399
7634
|
if (backlog <= 0) return 0;
|
|
7400
|
-
if (backlog >=
|
|
7401
|
-
|
|
7402
|
-
return
|
|
7635
|
+
if (backlog >= SMOOTH_MAX_BACKLOG_CHARS) return SMOOTH_MAX_CPS;
|
|
7636
|
+
if (backlog <= SMOOTH_BURST_BACKLOG_CHARS) return SMOOTH_BASE_CPS + backlog / SMOOTH_BURST_BACKLOG_CHARS * (SMOOTH_BURST_CPS - SMOOTH_BASE_CPS);
|
|
7637
|
+
return SMOOTH_BURST_CPS + (backlog - SMOOTH_BURST_BACKLOG_CHARS) / (SMOOTH_MAX_BACKLOG_CHARS - SMOOTH_BURST_BACKLOG_CHARS) * (SMOOTH_MAX_CPS - SMOOTH_BURST_CPS);
|
|
7638
|
+
}
|
|
7639
|
+
/**
|
|
7640
|
+
* Number of characters to drain on the next smooth-mode tick + the
|
|
7641
|
+
* fractional remainder to carry into the next tick.
|
|
7642
|
+
*
|
|
7643
|
+
* Combining the CPS curve with a fractional accumulator means the
|
|
7644
|
+
* effective draining rate matches the target CPS exactly. The legacy
|
|
7645
|
+
* `Math.ceil(cps * dt)` rounded UP on every tick — at 200 CPS / 16ms,
|
|
7646
|
+
* that's 4 chars/tick (= 250 CPS effective), a 25 % speed-up. Worse,
|
|
7647
|
+
* `ceil` produced visible step transitions (4 → 5 → 7 → 8 → 10 as
|
|
7648
|
+
* backlog grew) that read as the typewriter cadence jumping.
|
|
7649
|
+
*/
|
|
7650
|
+
function smoothCharsForTick(backlog, carry, dtMs) {
|
|
7651
|
+
if (backlog <= 0) return {
|
|
7652
|
+
take: 0,
|
|
7653
|
+
carry: 0
|
|
7654
|
+
};
|
|
7655
|
+
const want = smoothCpsForBacklog(backlog) * dtMs / 1e3 + carry;
|
|
7656
|
+
const take = Math.floor(want);
|
|
7657
|
+
if (take <= 0) return {
|
|
7658
|
+
take: 0,
|
|
7659
|
+
carry: want
|
|
7660
|
+
};
|
|
7661
|
+
if (take >= backlog) return {
|
|
7662
|
+
take: backlog,
|
|
7663
|
+
carry: 0
|
|
7664
|
+
};
|
|
7665
|
+
return {
|
|
7666
|
+
take,
|
|
7667
|
+
carry: want - take
|
|
7668
|
+
};
|
|
7403
7669
|
}
|
|
7404
7670
|
/**
|
|
7405
7671
|
* Slice `n` chars off the front of `buf` without splitting a UTF-16
|
|
@@ -7423,6 +7689,7 @@ function takeChars(buf, n) {
|
|
|
7423
7689
|
function useStreamBuffer(setEvents, options) {
|
|
7424
7690
|
const bucketsRef = useRef(/* @__PURE__ */ new Map());
|
|
7425
7691
|
const tickerRef = useRef(null);
|
|
7692
|
+
const lastTickMsRef = useRef(0);
|
|
7426
7693
|
const getSmoothRef = useRef(options?.getSmooth);
|
|
7427
7694
|
getSmoothRef.current = options?.getSmooth;
|
|
7428
7695
|
/**
|
|
@@ -7443,6 +7710,7 @@ function useStreamBuffer(setEvents, options) {
|
|
|
7443
7710
|
clearInterval(tickerRef.current);
|
|
7444
7711
|
tickerRef.current = null;
|
|
7445
7712
|
}
|
|
7713
|
+
lastTickMsRef.current = 0;
|
|
7446
7714
|
}, []);
|
|
7447
7715
|
/**
|
|
7448
7716
|
* Has at least one bucket got unflushed *markdown* content?
|
|
@@ -7494,25 +7762,34 @@ function useStreamBuffer(setEvents, options) {
|
|
|
7494
7762
|
const buckets = bucketsRef.current;
|
|
7495
7763
|
const portions = [];
|
|
7496
7764
|
let stillHasContent = false;
|
|
7765
|
+
const now = Date.now();
|
|
7766
|
+
const dtMs = lastTickMsRef.current === 0 ? TICK_INTERVAL_MS : Math.min(TICK_INTERVAL_MS * 4, Math.max(1, now - lastTickMsRef.current));
|
|
7767
|
+
lastTickMsRef.current = now;
|
|
7497
7768
|
for (const bucket of buckets.values()) {
|
|
7498
7769
|
const thinking = bucket.thinking;
|
|
7499
7770
|
bucket.thinking = "";
|
|
7500
7771
|
let taken = "";
|
|
7501
7772
|
if (bucket.markdown.length > 0) if (smooth) {
|
|
7502
|
-
const
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7773
|
+
const { take, carry } = smoothCharsForTick(bucket.markdown.length, bucket.smoothCarry, dtMs);
|
|
7774
|
+
bucket.smoothCarry = carry;
|
|
7775
|
+
if (take > 0) {
|
|
7776
|
+
const result = takeChars(bucket.markdown, take);
|
|
7777
|
+
taken = result.taken;
|
|
7778
|
+
bucket.markdown = result.rest;
|
|
7779
|
+
if (bucket.markdown.length === 0) bucket.smoothCarry = 0;
|
|
7780
|
+
}
|
|
7506
7781
|
} else {
|
|
7507
7782
|
taken = bucket.markdown;
|
|
7508
7783
|
bucket.markdown = "";
|
|
7784
|
+
bucket.smoothCarry = 0;
|
|
7509
7785
|
}
|
|
7510
7786
|
if (taken || thinking) portions.push({
|
|
7511
7787
|
markdown: taken,
|
|
7512
7788
|
thinking,
|
|
7513
7789
|
owner: bucket.owner,
|
|
7514
7790
|
depth: bucket.depth,
|
|
7515
|
-
turnId: bucket.turnId
|
|
7791
|
+
turnId: bucket.turnId,
|
|
7792
|
+
smoothCarry: 0
|
|
7516
7793
|
});
|
|
7517
7794
|
if (bucket.markdown.length > 0) stillHasContent = true;
|
|
7518
7795
|
}
|
|
@@ -7686,13 +7963,21 @@ const TOOL_DISPLAY = {
|
|
|
7686
7963
|
}
|
|
7687
7964
|
},
|
|
7688
7965
|
shell: {
|
|
7689
|
-
displayName: "Shell",
|
|
7966
|
+
displayName: (input) => input?.run_in_background === true ? "Shell (background)" : "Shell",
|
|
7690
7967
|
format: (input) => {
|
|
7691
7968
|
const command = stringField(input, "command");
|
|
7692
7969
|
if (!command) return null;
|
|
7693
7970
|
return { target: truncate(command.trim(), 200) };
|
|
7694
7971
|
}
|
|
7695
7972
|
},
|
|
7973
|
+
shell_kill: {
|
|
7974
|
+
displayName: "Kill task",
|
|
7975
|
+
format: (input) => {
|
|
7976
|
+
const taskId = stringField(input, "task_id");
|
|
7977
|
+
if (!taskId) return null;
|
|
7978
|
+
return { target: taskId };
|
|
7979
|
+
}
|
|
7980
|
+
},
|
|
7696
7981
|
edit: {
|
|
7697
7982
|
displayName: "Edit",
|
|
7698
7983
|
format: (input) => {
|
|
@@ -7752,7 +8037,9 @@ const TOOL_DISPLAY = {
|
|
|
7752
8037
|
}
|
|
7753
8038
|
},
|
|
7754
8039
|
skills_use: {
|
|
7755
|
-
displayName:
|
|
8040
|
+
displayName: (input) => {
|
|
8041
|
+
return (input ? stringField(input, "mode") : void 0) === "deactivate" ? "Disable skill" : "Enable skill";
|
|
8042
|
+
},
|
|
7756
8043
|
format: (input) => {
|
|
7757
8044
|
const name = stringField(input, "name");
|
|
7758
8045
|
if (!name) return null;
|
|
@@ -7846,9 +8133,9 @@ const TOOL_DISPLAY = {
|
|
|
7846
8133
|
* affordance ("everything starting with `Github` came from the github
|
|
7847
8134
|
* MCP server").
|
|
7848
8135
|
*/
|
|
7849
|
-
function displayNameFor(name) {
|
|
8136
|
+
function displayNameFor(name, input) {
|
|
7850
8137
|
const entry = TOOL_DISPLAY[name];
|
|
7851
|
-
if (entry) return entry.displayName;
|
|
8138
|
+
if (entry) return typeof entry.displayName === "function" ? entry.displayName(input) : entry.displayName;
|
|
7852
8139
|
return sentenceCase(name.startsWith("mcp_") ? name.slice(4) : name);
|
|
7853
8140
|
}
|
|
7854
8141
|
/**
|
|
@@ -8058,6 +8345,6 @@ function countNeighbors(turnIds, turnId) {
|
|
|
8058
8345
|
};
|
|
8059
8346
|
}
|
|
8060
8347
|
//#endregion
|
|
8061
|
-
export { useMcpAuthDispatch as $,
|
|
8348
|
+
export { useMcpAuthDispatch as $, tryOpenBrowser as $n, PLAN_MODE_DOCTRINE_NO_PROMPTS as $r, loadState as $t, getSafelist as A, DEFAULT_KEYBINDINGS as An, resolveAgentId as Ar, resolveChipColor as At, supportsOAuth as B, SKILLS_TRIGGER as Bn, isTodoTool as Br, useDiscoveryOptional as Bt, resolveSessionExportTarget as C, mergeApprovalAndBodyOutcomes as Cn, piIdOf as Cr, SETTINGS_CHOICES as Ct, useSafeModeQueue as D, stripEditOutcomesAnnotation as Dn, DEFAULT_PERSIST_EXCLUDE_TOOLS as Dr, useSettings as Dt, useSafeModeActions as E, rewriteMultiEditHeader as En, DEFAULT_AGENT_ID as Er, clampFps as Et, suggestSafelistEntry as F, matchesBinding as Fn, TODO_STATUS_GLYPHS as Fr, CATPPUCCIN_MACCHIATO as Ft, defaultMcpsConfigPaths as G, uniqueFilesFromReferences as Gn, useActiveTodos as Gr, createStateStore as Gt, filterModelCatalog as H, uniqueSkillNamesFromReferences as Hn, pruneTodosByRun as Hr, useConfig as Ht, writeProjects as I, mergeKeybindings as In, TODO_WRITE_COUNTS_METADATA_KEY as Ir, CATPPUCCIN_MOCHA as It, projectUserPaths as J, findActiveTrigger as Jn, DOING_TASKS_DOCTRINE as Jr, isEditErrorResult as Jt, discoverProjectMcps as K, applyInsert as Kn, ACTIONS_WITH_CARE_DOCTRINE as Kr, deriveSessionTitle as Kt, splitPromptSegments as L, parseBindingSpec as Ln, createTodoTools as Lr, createDiscoverySlot as Lt, matchesSafelistEntry as M, KEYBINDING_DEF_BY_ACTION as Mn, TODOREAD_TOOL as Mr, VAPORWAVE_THEME as Mt, projectsFilePath as N, ensureKeybindingsFile as Nn, TODOS_METADATA_KEY as Nr, CATPPUCCIN_FRAPPE as Nt, IMPLICITLY_SAFE_TOOLS as O, summarizeOutcomes as On, PLAN_AGENT as Or, BUILTIN_THEMES as Ot, readProjects as P, keybindingsPath as Pn, TODOWRITE_TOOL as Pr, CATPPUCCIN_LATTE as Pt, McpAuthProvider as Q, buildLinearRamp as Qn, PLAN_MODE_DOCTRINE as Qr, listSessionMeta as Qt, formatPathForCwd as R, readKeybindings as Rn, getArchivedTodosForRun as Rr, DiscoveryProvider as Rt, renderSession as S, maskToOutcomeKinds as Sn, openrouterDescriptor as Sr, DEFAULT_SETTINGS as St, SafeModeProvider as T, resolveApprovalForPayload as Tn, BUILTIN_AGENTS as Tr, SettingsProvider as Tt, indexOfEntry as U, FILES_TRIGGER as Un, selectActiveTodos as Ur, resolveConfig as Ut, buildModelCatalog as V, createSkillsCompletionProvider as Vn, pickActiveRunId as Vr, ConfigProvider as Vt, buildMcpServers as W, createFilesCompletionProvider as Wn, setTodosForRun as Wr, EDIT_TOOL_NAMES as Wt, mcpCredentialsPath as X, useCompletion as Xn, INTERACTION_GUIDANCE as Xr, isVisible as Xt, createFileMcpCredentialStore as Y, mergeReferences as Yn, IDENTITY_PREFIX as Yr, isTurnHighlighted as Yt, patchMcpCredential as Z, blendHsl as Zn, INTERACTION_GUIDANCE_NO_PROMPTS as Zr, lastContextSizeFromTurns as Zt, turnContextSize as _, previewEditPayload as _n, getContextWindow as _r, truncateTrailing as _t, computeTurnAnchors as a, titleFromTurns as an, credentialsPath as ar, InteractionsProvider as at, defaultSkillScanPaths as b, tokenize as bn, modelsForDescriptor as br, listProjectFiles as bt, formatToolCall as c, turnSelectionOwnership as cn, removeProviderCredential as cr, createInteractionTools as ct, useSelectStyle as d, buildContextualDiff as dn, BUILTIN_PROVIDERS as dr, pendingInteractionsFromTurns as dt, SUBAGENT_GUIDANCE as ei, marginTopFor as en, bootProfileEnabled as er, useMcpAuthState as et, useSurfaces as f, buildUnifiedDiff as fn, OUTPUT_RESERVE_TOKENS as fr, serializeInteractionResponse as ft, finalizeStreamingMarkdownForOwner as g, filetypeFromPath as gn, effectiveContextWindow as gr, hintsLength as gt, finalizeStreamingMarkdown as h, extractEditPayload as hn, credKeyOf as hr, clipHintsToWidth as ht, turnAsText as i, envSection as ii, sumRunCosts as in, applyApiKeyEnv as ir, ASK_USER_TOOL as it, isOnSafelist as j, KEYBINDING_DEFS as jn, singleAgentRegistry as jr, resolveTheme as jt, addToSafelist as k, findGitRoot$1 as kn, accentColor as kr, DEFAULT_THEME as kt, ThemeProvider as l, updateToolEventOutcomes as ln, setProviderCredential as lr, isInteractionTool as lt, useTheme as m, computeLineDiff as mn, cerebrasDescriptor as mr, useInteractionsQueue as mt, deleteTurnSafely as n, buildBuildSystem as ni, selectableTurnIds as nn, shouldAutoCompact as nr, reduceMcpAuth as nt, TOOL_DISPLAY as o, toolCallPreview as on, readCredentials as or, PRESENT_PLAN_TOOL as ot, useSyntaxStyles as p, computeInlineDiff as pn, anthropicDescriptor as pr, useInteractionsActions as pt, parseMcpsFile as q, collectReferences as qn, COMMUNICATION_DOCTRINE as qr, eventsFromTurns as qt, truncateTurnsAt as r, buildPlanSystem as ri, stripSpawnTokensLine as rn, detectAuth as rr, splitMarkdownCodeBlocks as rt, displayNameFor as s, toolResultText as sn, readProviderCredential as sr, buildResumedToolResultsTurn as st, countNeighbors as t, TOKEN_DISCIPLINE_DOCTRINE as ti, saveState as tn, bootTick as tr, getMcpAuthStatus as tt, useColors as u, applyEditPayload as un, writeCredentials as ur, makeRequestInteraction as ut, useStreamBuffer as v, splitLines as vn, getModelInfo as vr, cleanTitle as vt, writeSessionExport as w, parseEditOutcomesFromResult as wn, BUILD_AGENT as wr, SETTINGS_TOGGLES as wt, discoverProjectSkills as x, buildEditOutcomesAnnotation as xn, openaiDescriptor as xr, useEnabledToggleSet as xt, buildSkillsConfig as y, summarizeEditPayload as yn, modelSupportsReasoning as yr, generateSessionTitle as yt, runOAuthLogin as z, stripJsonComments as zn, getTodosForRun as zr, useDiscovery as zt };
|
|
8062
8349
|
|
|
8063
|
-
//# sourceMappingURL=turn-operations-
|
|
8350
|
+
//# sourceMappingURL=turn-operations-CtgBlBHn.js.map
|