@proxysoul/soulforge 2.15.6 → 2.15.7
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/index.js +976 -439
- package/dist/workers/io.worker.js +78 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3056,6 +3056,12 @@ import { spawnSync } from "child_process";
|
|
|
3056
3056
|
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
3057
3057
|
import { homedir as homedir3 } from "os";
|
|
3058
3058
|
import { join as join3 } from "path";
|
|
3059
|
+
function _invalidateKeychainCache(key) {
|
|
3060
|
+
if (key)
|
|
3061
|
+
_keychainHasCache.delete(key);
|
|
3062
|
+
else
|
|
3063
|
+
_keychainHasCache.clear();
|
|
3064
|
+
}
|
|
3059
3065
|
function setDefaultKeyPriority(p) {
|
|
3060
3066
|
_defaultPriority = p;
|
|
3061
3067
|
}
|
|
@@ -3083,7 +3089,7 @@ function keychainAvailable() {
|
|
|
3083
3089
|
function keychainGet(key) {
|
|
3084
3090
|
try {
|
|
3085
3091
|
if (process.platform === "darwin") {
|
|
3086
|
-
const result = spawnSync("security", ["find-generic-password", "-a", KEYCHAIN_SERVICE, "-s", key, "-w"], { timeout: 5000, encoding: "utf-8" });
|
|
3092
|
+
const result = spawnSync("security", ["find-generic-password", "-a", KEYCHAIN_SERVICE, "-s", key, "-w"], { timeout: 5000, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
3087
3093
|
if (result.status === 0 && result.stdout) {
|
|
3088
3094
|
return result.stdout.trim();
|
|
3089
3095
|
}
|
|
@@ -3092,7 +3098,8 @@ function keychainGet(key) {
|
|
|
3092
3098
|
if (process.platform === "linux") {
|
|
3093
3099
|
const result = spawnSync("secret-tool", ["lookup", "service", KEYCHAIN_SERVICE, "key", key], {
|
|
3094
3100
|
timeout: 5000,
|
|
3095
|
-
encoding: "utf-8"
|
|
3101
|
+
encoding: "utf-8",
|
|
3102
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
3096
3103
|
});
|
|
3097
3104
|
if (result.status === 0 && result.stdout) {
|
|
3098
3105
|
return result.stdout.trim();
|
|
@@ -3104,16 +3111,23 @@ function keychainGet(key) {
|
|
|
3104
3111
|
}
|
|
3105
3112
|
function keychainSet(key, value) {
|
|
3106
3113
|
try {
|
|
3114
|
+
if (!value)
|
|
3115
|
+
return false;
|
|
3107
3116
|
if (process.platform === "darwin") {
|
|
3108
|
-
spawnSync("security", ["
|
|
3109
|
-
timeout: 5000
|
|
3117
|
+
const result = spawnSync("security", ["add-generic-password", "-U", "-a", KEYCHAIN_SERVICE, "-s", key, "-w", value], {
|
|
3118
|
+
timeout: 5000,
|
|
3119
|
+
encoding: "utf-8",
|
|
3120
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
3110
3121
|
});
|
|
3111
|
-
const result = spawnSync("security", ["add-generic-password", "-a", KEYCHAIN_SERVICE, "-s", key, "-w"], { input: `${value}
|
|
3112
|
-
`, timeout: 5000, encoding: "utf-8" });
|
|
3113
3122
|
return result.status === 0;
|
|
3114
3123
|
}
|
|
3115
3124
|
if (process.platform === "linux") {
|
|
3116
|
-
const result = spawnSync("secret-tool", ["store", "--label", `SoulForge ${key}`, "service", KEYCHAIN_SERVICE, "key", key], {
|
|
3125
|
+
const result = spawnSync("secret-tool", ["store", "--label", `SoulForge ${key}`, "service", KEYCHAIN_SERVICE, "key", key], {
|
|
3126
|
+
input: value,
|
|
3127
|
+
timeout: 5000,
|
|
3128
|
+
encoding: "utf-8",
|
|
3129
|
+
stdio: ["pipe", "ignore", "ignore"]
|
|
3130
|
+
});
|
|
3117
3131
|
return result.status === 0;
|
|
3118
3132
|
}
|
|
3119
3133
|
} catch {}
|
|
@@ -3122,12 +3136,13 @@ function keychainSet(key, value) {
|
|
|
3122
3136
|
function keychainDelete(key) {
|
|
3123
3137
|
try {
|
|
3124
3138
|
if (process.platform === "darwin") {
|
|
3125
|
-
const result = spawnSync("security", ["delete-generic-password", "-a", KEYCHAIN_SERVICE, "-s", key], { timeout: 5000 });
|
|
3139
|
+
const result = spawnSync("security", ["delete-generic-password", "-a", KEYCHAIN_SERVICE, "-s", key], { timeout: 5000, stdio: ["ignore", "ignore", "ignore"] });
|
|
3126
3140
|
return result.status === 0;
|
|
3127
3141
|
}
|
|
3128
3142
|
if (process.platform === "linux") {
|
|
3129
3143
|
const result = spawnSync("secret-tool", ["clear", "service", KEYCHAIN_SERVICE, "key", key], {
|
|
3130
|
-
timeout: 5000
|
|
3144
|
+
timeout: 5000,
|
|
3145
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
3131
3146
|
});
|
|
3132
3147
|
return result.status === 0;
|
|
3133
3148
|
}
|
|
@@ -3152,7 +3167,16 @@ function fileWrite(data) {
|
|
|
3152
3167
|
function getSecretSources(key, priority = _defaultPriority) {
|
|
3153
3168
|
const envVar = ENV_MAP[key];
|
|
3154
3169
|
const hasEnv = !!(envVar && process.env[envVar]);
|
|
3155
|
-
|
|
3170
|
+
let hasKeychain = false;
|
|
3171
|
+
if (keychainAvailable()) {
|
|
3172
|
+
const cached = _keychainHasCache.get(key);
|
|
3173
|
+
if (cached !== undefined) {
|
|
3174
|
+
hasKeychain = cached;
|
|
3175
|
+
} else {
|
|
3176
|
+
hasKeychain = !!keychainGet(key);
|
|
3177
|
+
_keychainHasCache.set(key, hasKeychain);
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3156
3180
|
const hasFile = !!fileRead()[key];
|
|
3157
3181
|
let active = "none";
|
|
3158
3182
|
if (priority === "app") {
|
|
@@ -3191,6 +3215,7 @@ function getSecret(key, priority = _defaultPriority) {
|
|
|
3191
3215
|
function setSecret(key, value) {
|
|
3192
3216
|
if (keychainAvailable()) {
|
|
3193
3217
|
if (keychainSet(key, value)) {
|
|
3218
|
+
_invalidateKeychainCache(key);
|
|
3194
3219
|
const data2 = fileRead();
|
|
3195
3220
|
if (data2[key]) {
|
|
3196
3221
|
delete data2[key];
|
|
@@ -3214,6 +3239,7 @@ function deleteSecret(key) {
|
|
|
3214
3239
|
deleted = keychainDelete(key);
|
|
3215
3240
|
if (deleted)
|
|
3216
3241
|
storage = "keychain";
|
|
3242
|
+
_invalidateKeychainCache(key);
|
|
3217
3243
|
}
|
|
3218
3244
|
const data = fileRead();
|
|
3219
3245
|
if (data[key]) {
|
|
@@ -3248,8 +3274,9 @@ function getProviderApiKey(envVar, priority = _defaultPriority) {
|
|
|
3248
3274
|
}
|
|
3249
3275
|
return getEnv() ?? getApp();
|
|
3250
3276
|
}
|
|
3251
|
-
var SECRETS_DIR, SECRETS_FILE, KEYCHAIN_SERVICE = "soulforge", _defaultPriority = "env", STATIC_SECRETS, ENV_MAP, ENV_TO_SECRET;
|
|
3277
|
+
var _keychainHasCache, SECRETS_DIR, SECRETS_FILE, KEYCHAIN_SERVICE = "soulforge", _defaultPriority = "env", STATIC_SECRETS, ENV_MAP, ENV_TO_SECRET;
|
|
3252
3278
|
var init_secrets = __esm(() => {
|
|
3279
|
+
_keychainHasCache = new Map;
|
|
3253
3280
|
SECRETS_DIR = join3(homedir3(), ".soulforge");
|
|
3254
3281
|
SECRETS_FILE = join3(SECRETS_DIR, "secrets.json");
|
|
3255
3282
|
STATIC_SECRETS = {
|
|
@@ -56679,11 +56706,13 @@ var init_ui = __esm(() => {
|
|
|
56679
56706
|
hearthSettings: false,
|
|
56680
56707
|
tabNamePopup: false,
|
|
56681
56708
|
memoryBrowser: false,
|
|
56709
|
+
modelEvents: false,
|
|
56682
56710
|
uiDemo: false
|
|
56683
56711
|
};
|
|
56684
56712
|
useUIStore = create()(subscribeWithSelector((set2) => ({
|
|
56685
56713
|
modals: { ...INITIAL_MODALS },
|
|
56686
56714
|
routerSlotPicking: null,
|
|
56715
|
+
fallbackForModel: null,
|
|
56687
56716
|
commandPickerConfig: null,
|
|
56688
56717
|
infoPopupConfig: null,
|
|
56689
56718
|
statusDashboardTab: "Context",
|
|
@@ -56702,6 +56731,7 @@ var init_ui = __esm(() => {
|
|
|
56702
56731
|
modals: s.modals[name21] ? { ...s.modals, [name21]: false } : { ...INITIAL_MODALS, [name21]: true }
|
|
56703
56732
|
})),
|
|
56704
56733
|
setRouterSlotPicking: (slot) => set2({ routerSlotPicking: slot }),
|
|
56734
|
+
setFallbackForModel: (modelId) => set2({ fallbackForModel: modelId }),
|
|
56705
56735
|
openCommandPicker: (config2) => set2(() => ({
|
|
56706
56736
|
commandPickerConfig: config2,
|
|
56707
56737
|
modals: { ...INITIAL_MODALS, commandPicker: true }
|
|
@@ -63352,7 +63382,7 @@ var package_default;
|
|
|
63352
63382
|
var init_package = __esm(() => {
|
|
63353
63383
|
package_default = {
|
|
63354
63384
|
name: "@proxysoul/soulforge",
|
|
63355
|
-
version: "2.15.
|
|
63385
|
+
version: "2.15.7",
|
|
63356
63386
|
description: "Graph-powered code intelligence \u2014 multi-agent coding with codebase-aware AI",
|
|
63357
63387
|
repository: {
|
|
63358
63388
|
type: "git",
|
|
@@ -96262,32 +96292,19 @@ Senior engineer. Quiet at the keyboard. Reads code like prose. Finds the file, o
|
|
|
96262
96292
|
</identity>
|
|
96263
96293
|
|
|
96264
96294
|
<tool_loop>
|
|
96265
|
-
A turn is tool calls followed by exactly one final answer.
|
|
96266
|
-
|
|
96267
|
-
Speak only when (a) the task is complete, (b) a destructive/irreversible action needs confirmation, (c) genuine ambiguity blocks progress, or (d) an unrecoverable error (missing credentials, API unreachable, repeated permission denial) makes further tool calls pointless. In every case, the turn ends with a final answer \u2014 empty endings are a bug.
|
|
96295
|
+
A turn is tool calls followed by exactly one final answer. Between tool calls: zero text \u2014 no acknowledgements ("Got it", "Done"), no self-narration ("I'll\u2026", "Let me\u2026", "Going to\u2026"), no progress declarations ("Found it", "Root cause confirmed"), no meta-previews ("One more check", "Just to be sure"), no transition announcements ("Here's what I found"), no advisory reassurances, no findings prose, no visible self-correction ("Wait \u2014 actually"). Synonyms and paraphrases that perform the same function are equally forbidden \u2014 if a sentence performs the function, delete it and call the next tool.
|
|
96268
96296
|
|
|
96269
|
-
|
|
96297
|
+
After the last tool: speak. The final answer is mandatory \u2014 every turn ends with text, never on a tool result. Speak only when (a) the task is complete, (b) a destructive/irreversible action needs confirmation, (c) genuine ambiguity blocks progress, or (d) an unrecoverable error makes further tool calls pointless. Warning about a destructive action: the warning IS the answer \u2014 full sentences, no tool chain first.
|
|
96270
96298
|
</tool_loop>
|
|
96271
96299
|
|
|
96272
|
-
<forbidden_between_tool_calls>
|
|
96273
|
-
These grammatical classes (and their synonyms/paraphrases) are equally forbidden \u2014 if a sentence performs the function, delete it and call the next tool:
|
|
96274
|
-
- Acknowledgements ("Got it", "Done", "Noted", emotes, asterisk gestures)
|
|
96275
|
-
- Self-narration ("I'll\u2026", "Let me\u2026", "Going to\u2026", "Next I'll\u2026")
|
|
96276
|
-
- Progress declarations ("Root cause confirmed", "Found it", "Makes sense")
|
|
96277
|
-
- Meta-previews ("One more check", "Just to be sure", "Quick verification")
|
|
96278
|
-
- Transition announcements ("Here's what I found", "With that done")
|
|
96279
|
-
- Advisory reassurances ("Cross-tab noted", "No conflict here")
|
|
96280
|
-
- Mid-flow findings prose, visible self-correction ("Wait \u2014 actually"), or repetition of anything already said
|
|
96281
|
-
</forbidden_between_tool_calls>
|
|
96282
|
-
|
|
96283
96300
|
<answer_voice>
|
|
96284
|
-
Confident, flat, direct. No excitement, theatrics, hedging, apology.
|
|
96301
|
+
Confident, flat, direct. No excitement, theatrics, hedging, apology. Self-corrects silently \u2014 the answer reflects the corrected understanding, not the path to it. First word is a noun, verb, or file path \u2014 never "I", "we", "the", "so", "well", "ok", or any discourse marker. No closing pleasantries, no "let me know", no follow-up offers.
|
|
96285
96302
|
|
|
96286
|
-
Shape: length matches work. One-file change \u2192 one line stating path and what changed (zero lines is a bug). Diagnostic \u2192 2-5 bullets of \`path:line \u2014 finding. fix.\`. Explanation \u2192 as long as needed, zero filler. One format per answer \u2014 bullets or prose, not both
|
|
96303
|
+
Shape: length matches work. One-file change \u2192 one line stating path and what changed (zero lines is a bug). Diagnostic \u2192 2-5 bullets of \`path:line \u2014 finding. fix.\`. Explanation \u2192 as long as needed, zero filler. One format per answer \u2014 bullets or prose, not both. No section headers unless the answer has \u22652 genuinely independent parts.
|
|
96287
96304
|
|
|
96288
|
-
Compression: drop articles when unambiguous
|
|
96305
|
+
Compression: drop articles when unambiguous, drop copula when predicate is adjective/participle, replace causal prose with arrows (A \u2192 B \u2192 C), prefer fragments, shortest verb (use not utilize), strip hedging (might/probably/I think) and filler (just/really/basically/actually). Abbreviate domain terms when repeated (DB, auth, config, fn). Code identifiers, file paths, type names, flags: verbatim.
|
|
96289
96306
|
|
|
96290
|
-
Suspend compression \u2014 write full sentences \u2014 for destructive actions, security warnings, multi-step instructions where fragment ambiguity risks misread, or when the user is confused.
|
|
96307
|
+
Suspend compression \u2014 write full sentences \u2014 for destructive actions, security warnings, multi-step instructions where fragment ambiguity risks misread, or when the user is confused.
|
|
96291
96308
|
</answer_voice>`, SHARED_RULES = `
|
|
96292
96309
|
<task_discipline>
|
|
96293
96310
|
- Surgical Read code before modifying. Stay focused on what was asked.
|
|
@@ -96303,9 +96320,11 @@ Suspend compression \u2014 write full sentences \u2014 for destructive actions,
|
|
|
96303
96320
|
function resolveRetrySettings(raw, opts = {}) {
|
|
96304
96321
|
const defaultBase = opts.agent ? DEFAULT_AGENT_BASE_DELAY_MS : DEFAULT_CHAT_BASE_DELAY_MS;
|
|
96305
96322
|
const obj = raw && typeof raw === "object" ? raw : null;
|
|
96306
|
-
const
|
|
96323
|
+
const legacyMaxAttempts = clampIntMin(obj?.maxAttempts, MIN_MAX_ATTEMPTS, DEFAULT_MAX_RETRIES2, "retry.maxAttempts");
|
|
96324
|
+
const maxTransientRetries = clampIntMin(obj?.maxTransientRetries, MIN_MAX_ATTEMPTS, legacyMaxAttempts, "retry.maxTransientRetries");
|
|
96325
|
+
const maxStallRetries = clampIntMin(obj?.maxStallRetries, MIN_MAX_ATTEMPTS, legacyMaxAttempts, "retry.maxStallRetries");
|
|
96307
96326
|
const baseDelayMs = clampInt(obj?.baseDelayMs, MIN_BASE_DELAY_MS, MAX_BASE_DELAY_MS, defaultBase, "retry.baseDelayMs");
|
|
96308
|
-
return {
|
|
96327
|
+
return { maxTransientRetries, maxStallRetries, baseDelayMs };
|
|
96309
96328
|
}
|
|
96310
96329
|
function clampIntMin(value, min, fallback, key) {
|
|
96311
96330
|
if (value === undefined)
|
|
@@ -362206,6 +362225,7 @@ async function formatFile(filePath, cwd) {
|
|
|
362206
362225
|
try {
|
|
362207
362226
|
const proc = Bun.spawn(["sh", "-c", command], {
|
|
362208
362227
|
cwd: effectiveCwd,
|
|
362228
|
+
stdin: "ignore",
|
|
362209
362229
|
stdout: "pipe",
|
|
362210
362230
|
stderr: "pipe",
|
|
362211
362231
|
env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" }
|
|
@@ -362353,6 +362373,7 @@ var init_project = __esm(() => {
|
|
|
362353
362373
|
const runCommand = async (cmd) => {
|
|
362354
362374
|
const proc = Bun.spawn(["sh", "-c", cmd], {
|
|
362355
362375
|
cwd,
|
|
362376
|
+
stdin: "ignore",
|
|
362356
362377
|
stdout: "pipe",
|
|
362357
362378
|
stderr: "pipe",
|
|
362358
362379
|
env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1", ...args2.env }
|
|
@@ -362512,6 +362533,7 @@ async function autoFormatAfterEdit(filePath, cwd) {
|
|
|
362512
362533
|
try {
|
|
362513
362534
|
const proc = Bun.spawn(["sh", "-c", command], {
|
|
362514
362535
|
cwd: effectiveCwd,
|
|
362536
|
+
stdin: "ignore",
|
|
362515
362537
|
stdout: "pipe",
|
|
362516
362538
|
stderr: "pipe",
|
|
362517
362539
|
env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" }
|
|
@@ -362924,7 +362946,7 @@ var init_edit_file = __esm(() => {
|
|
|
362924
362946
|
init_ts_project_detect();
|
|
362925
362947
|
editFileTool = {
|
|
362926
362948
|
name: "edit_file",
|
|
362927
|
-
description: "Edit a non-TS/JS file by replacing content (JSON, YAML, Markdown, config, raw text). For .ts/.tsx/.js/.jsx/.mts/.cts/.mjs/.cjs files use ast_edit \u2014 it's safer and won't drift. " + "Read first, then provide path, oldString, newString. " + "Provide lineStart (1-indexed from read output) for reliable line-anchored matching \u2014
|
|
362949
|
+
description: "Edit a non-TS/JS file by replacing content (JSON, YAML, Markdown, config, raw text). For .ts/.tsx/.js/.jsx/.mts/.cts/.mjs/.cjs files use ast_edit \u2014 it's safer and won't drift. " + "Read first, then provide path, oldString, newString. " + "Provide lineStart (1-indexed from read output) for reliable line-anchored matching \u2014 the range is derived from oldString line count. Without lineStart, falls back to string matching (fails if ambiguous). " + "Keep oldString minimal and unique in the file \u2014 don't pad with large unchanged regions just to anchor a small change. " + "Empty oldString creates a new file. Use multi_edit for multiple changes to the same file. " + "Edits are applied immediately.",
|
|
362928
362950
|
execute: async (args2) => {
|
|
362929
362951
|
try {
|
|
362930
362952
|
const filePath = resolve16(args2.path);
|
|
@@ -363099,7 +363121,7 @@ var init_ast_edit = __esm(() => {
|
|
|
363099
363121
|
]);
|
|
363100
363122
|
astEditTool = {
|
|
363101
363123
|
name: "ast_edit",
|
|
363102
|
-
description: "AST edit for TS/JS (.ts/.tsx/.js/.jsx/.mts/.cts/.mjs/.cjs). Default editor for these files \u2014 ts-morph locates symbols by {target, name}
|
|
363124
|
+
description: "AST edit for TS/JS (.ts/.tsx/.js/.jsx/.mts/.cts/.mjs/.cjs). Default editor for these files \u2014 used BEFORE edit_file/multi_edit, not as fallback. ts-morph locates symbols by {target, name}: no oldString, no whitespace/escape failures, no line-offset drift. " + "Single op: {action, target, name, value?, newCode?, index?}. " + "Multi-op (atomic, same file): {operations:[{...}, ...]} \u2014 all-or-nothing rollback. Use for 'add import + use it' in one call. " + "Create files: action='create_file', newCode=<full content>. " + "Targets: function|class|interface|type|enum|variable|method|property|constructor|arrow_function. " + "Class members: name='ClassName.memberName' or just 'memberName'. Arrow const: target='arrow_function', name='foo'. " + "Idempotent: add_import/add_named_import/add_named_reexport merge; add_constructor modifies in place. " + "CAN DO (no fallback): any named symbol, JSX/TSX with Unicode/special chars/escape sequences/quotes (ts-morph wraps the TS compiler \u2014 no JSX limitation), large rewrites via replace or anchor-pair replace_in_body, whitespace drift (tab\u2194space, CRLF\u2194LF, indent stripping auto-handled). " + "CANNOT target: anonymous callbacks (inline arrows, IIFEs, object-literal methods without names) \u2192 use replace_in_body on the enclosing NAMED symbol. Union members inside a type alias \u2192 use replace on the whole type. Raw text inside comments/strings not bound to a symbol \u2192 replace_in_body on the enclosing symbol. " + "ONLY fall back to edit_file when: non-TS/JS file (JSON/YAML/MD/config), edit entirely outside any named symbol (e.g. top-of-file banner), or file has a parse error breaking ts-morph. 'Long needle' / 'JSX special chars' are NOT fallback reasons. " + "Tiers (pick smallest): MICRO (1-10 tok) set_type, set_return_type, set_async, set_export, rename, remove, set_initializer, add_parameter, set_optional. " + "BODY (10-100 tok) set_body, add_statement, add_property, add_method, add_constructor, add_decorator, set_extends, add_implements, replace_in_body. " + "FULL replace (whole symbol), create_file. " + "FILE-LEVEL add_import, add_named_import (idempotent merges), organize_imports, fix_missing_imports, add_function/class/interface/type_alias/enum, insert_text (REQUIRES anchor: index=0|-1 or value='after-imports'|'before-exports'). " + "ATOMIC operations:[{...},...] \u2014 all-or-nothing rollback. " + "Body shape \u2014 get this wrong and you corrupt the file: " + "set_body/add_statement/insert_statement take body CONTENTS ONLY \u2014 NO surrounding {} (ts-morph wraps; passing {\u2026} produces {{\u2026}}). " + "add_method/add_constructor/add_getter/add_setter take the FULL declaration INCLUDING braces (e.g. 'foo(x: number) { return x; }'). " + "replace takes the WHOLE symbol text including braces. " + "add_property on interface: 'name: type' or 'name?: type'; on class: 'name: type = value' or 'name = value'. " + "add_statement on expression-body arrow auto-wraps into a block \u2014 safe to call. " + "replace_in_body shapes (pick smallest): SHORT ANCHOR value=<1-2 unique lines> + newCode=<replacement>. ANCHOR PAIR (RANGE) value=<short start anchor> + valueEnd=<short end anchor> + newCode=<span replacement> \u2014 rewrites a 100-line block with ~20 tokens of anchors. Exact-match ambiguity (\u22652 identical hits) THROWS \u2014 add surrounding context or use anchor pair. " + "Import ops: value=module specifier (e.g. 'zod'), newCode=comma-separated names (e.g. 'z' or 'useState,useEffect'). remove_named_import: value=module, name=single identifier. " + "rename is declaration-only (safe default). Use rename_global / rename_symbol / move_symbol / rename_file for cross-file refactors. " + "Examples \u2014 " + "MICRO multi-op: ast_edit(path, operations:[{action:'set_async',target:'method',name:'UserStore.load',value:'true'},{action:'set_return_type',target:'method',name:'UserStore.load',value:'Promise<User>'}]). " + `BODY: ast_edit(path, action:'add_statement', target:'function', name:'loadConfig', newCode:"logger.info('config loaded');"). ` + "ANCHOR PAIR rewrite: ast_edit(path, action:'replace_in_body', target:'function', name:'ProviderSettings', value:'const caption = (', valueEnd:'</PremiumPopup>', newCode:'<new JSX>'). " + `ATOMIC import+method: ast_edit(path, operations:[{action:'add_named_import',value:'zod',newCode:'z'},{action:'add_method',target:'class',name:'Validator',newCode:"validate(input: unknown) { return z.string().parse(input); }"}]). ` + "CREATE: ast_edit('src/foo.ts', action:'create_file', newCode:'export function foo() { return 42; }\\\\n').",
|
|
363103
363125
|
execute: async (args2) => {
|
|
363104
363126
|
try {
|
|
363105
363127
|
const filePath = resolve17(args2.path);
|
|
@@ -377122,7 +377144,8 @@ ${content2}` : content2,
|
|
|
377122
377144
|
var exports_spawn = {};
|
|
377123
377145
|
__export(exports_spawn, {
|
|
377124
377146
|
buildSafeEnv: () => buildSafeEnv,
|
|
377125
|
-
SAFE_STDIO: () => SAFE_STDIO
|
|
377147
|
+
SAFE_STDIO: () => SAFE_STDIO,
|
|
377148
|
+
SAFE_SPAWN_OPTS: () => SAFE_SPAWN_OPTS
|
|
377126
377149
|
});
|
|
377127
377150
|
function buildSafeEnv() {
|
|
377128
377151
|
const env = {};
|
|
@@ -377136,7 +377159,7 @@ function buildSafeEnv() {
|
|
|
377136
377159
|
env.GIT_TERMINAL_PROMPT = "0";
|
|
377137
377160
|
return env;
|
|
377138
377161
|
}
|
|
377139
|
-
var SECRET_ENV_PATTERN, ENV_ALLOWLIST, SAFE_STDIO;
|
|
377162
|
+
var SECRET_ENV_PATTERN, ENV_ALLOWLIST, SAFE_STDIO, SAFE_SPAWN_OPTS;
|
|
377140
377163
|
var init_spawn = __esm(() => {
|
|
377141
377164
|
SECRET_ENV_PATTERN = /_API_KEY$|_SECRET$|_TOKEN$|_PASSWORD$|_CREDENTIAL$|_PRIVATE_KEY$/;
|
|
377142
377165
|
ENV_ALLOWLIST = new Set([
|
|
@@ -377173,6 +377196,10 @@ var init_spawn = __esm(() => {
|
|
|
377173
377196
|
"OTUI_TREE_SITTER_WORKER_PATH"
|
|
377174
377197
|
]);
|
|
377175
377198
|
SAFE_STDIO = ["ignore", "pipe", "pipe"];
|
|
377199
|
+
SAFE_SPAWN_OPTS = {
|
|
377200
|
+
stdio: SAFE_STDIO,
|
|
377201
|
+
detached: true
|
|
377202
|
+
};
|
|
377176
377203
|
});
|
|
377177
377204
|
|
|
377178
377205
|
// src/core/git/status.ts
|
|
@@ -378114,7 +378141,7 @@ var init_grep = __esm(() => {
|
|
|
378114
378141
|
init_install();
|
|
378115
378142
|
grepTool = {
|
|
378116
378143
|
name: "grep",
|
|
378117
|
-
description: "[TIER-2] Raw ripgrep search \u2014 use soul_grep first, fall back to this for complex regex or non-code files. " + "Returns matching file paths
|
|
378144
|
+
description: "[TIER-2] Raw ripgrep search \u2014 use soul_grep first, fall back to this for complex regex or non-code files. " + "Returns matching lines with file paths and line numbers. Respects .gitignore. " + "HOW TO USE: Provide a regex pattern. Optionally specify path to narrow scope, glob to filter file types, maxCount to cap matches per file (default 50). " + "LIMITATIONS: Output capped at 32KB. Long lines truncated at 1000 chars. Hidden files skipped.",
|
|
378118
378145
|
execute: async (args2) => {
|
|
378119
378146
|
const pattern = args2.pattern;
|
|
378120
378147
|
const searchPath = args2.path ?? ".";
|
|
@@ -378135,7 +378162,9 @@ var init_grep = __esm(() => {
|
|
|
378135
378162
|
const rgBin = getVendoredPath("rg") ?? "rg";
|
|
378136
378163
|
const proc = spawn9(rgBin, rgArgs, {
|
|
378137
378164
|
cwd: process.cwd(),
|
|
378138
|
-
timeout: 1e4
|
|
378165
|
+
timeout: 1e4,
|
|
378166
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
378167
|
+
detached: true
|
|
378139
378168
|
});
|
|
378140
378169
|
const chunks = [];
|
|
378141
378170
|
let totalBytes = 0;
|
|
@@ -378154,7 +378183,7 @@ var init_grep = __esm(() => {
|
|
|
378154
378183
|
if (lastNl > 0)
|
|
378155
378184
|
output = output.slice(0, lastNl);
|
|
378156
378185
|
output += `
|
|
378157
|
-
[
|
|
378186
|
+
[Output capped at 32KB. Use maxCount=N (default 50) for fewer hits per file, or narrow with glob/path to refine.]`;
|
|
378158
378187
|
}
|
|
378159
378188
|
if (code === 0 || code === 1) {
|
|
378160
378189
|
res(output || "No matches found.");
|
|
@@ -378164,7 +378193,9 @@ var init_grep = __esm(() => {
|
|
|
378164
378193
|
fallbackArgs.push("--include", glob);
|
|
378165
378194
|
const grepProc = spawn9("grep", fallbackArgs, {
|
|
378166
378195
|
cwd: process.cwd(),
|
|
378167
|
-
timeout: 1e4
|
|
378196
|
+
timeout: 1e4,
|
|
378197
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
378198
|
+
detached: true
|
|
378168
378199
|
});
|
|
378169
378200
|
const grepChunks = [];
|
|
378170
378201
|
let grepBytes = 0;
|
|
@@ -378182,7 +378213,7 @@ var init_grep = __esm(() => {
|
|
|
378182
378213
|
if (lastNl > 0)
|
|
378183
378214
|
out2 = out2.slice(0, lastNl);
|
|
378184
378215
|
out2 += `
|
|
378185
|
-
[
|
|
378216
|
+
[Output capped at 32KB. Use maxCount=N (default 50) for fewer hits per file, or narrow with glob/path to refine.]`;
|
|
378186
378217
|
}
|
|
378187
378218
|
res(out2);
|
|
378188
378219
|
});
|
|
@@ -378691,27 +378722,71 @@ function createMemoryTool(deps) {
|
|
|
378691
378722
|
], intelligence);
|
|
378692
378723
|
return tool({
|
|
378693
378724
|
description: [
|
|
378694
|
-
"Persistent cross-session knowledge store. SQLite-backed, project + global scopes, FTS + semantic search.",
|
|
378725
|
+
"Persistent cross-session knowledge store. SQLite-backed, project + global scopes, FTS + semantic search. The across-session brain: Soul Map = what code IS; memory = WHY it got that way. Use it like a primary tool, not a last resort.",
|
|
378726
|
+
"",
|
|
378727
|
+
"Auto-recall fires before each user turn \u2014 top-3 relevant memories injected as <recalled_memories> stubs (summary + id + signals + '\u21B3 has details' marker), \u2264600ch typical, cached + deduped per session. When 'has details' matters, call action:'get' with the 8-char prefix to read the full body. Auto-recall is signal-driven and misses generic single-word prompts \u2014 fall back to action:'search' once when convention matters and nothing surfaced.",
|
|
378695
378728
|
"",
|
|
378696
|
-
"WRITE
|
|
378697
|
-
" \
|
|
378698
|
-
" \
|
|
378699
|
-
" \
|
|
378700
|
-
" \u2022 context \u2014 project fact not visible in code ('legacy/ deletes next sprint')",
|
|
378701
|
-
"DON'T store what the Soul Map already shows: file structure, exports, signatures, deps.",
|
|
378729
|
+
"WRITE PROACTIVELY on these triggers (fire on ANY, not just user-prompted):",
|
|
378730
|
+
" 1. USER STATES A PREFERENCE OR DIRECTIVE \u2192 pref. Infer from cues \u2014 don't wait for the word 'remember'. Cues that mean 'this is durable': corrective tone about HOW you did something ('be terse', 'stop narrating', 'use bullets'); generalising language ('always', 'never', 'from now on', 'by default', 'prefer', 'in this repo we\u2026'); imperative meta-instructions about workflow/style/tooling/formatting/naming orthogonal to the current task; user repeats or rephrases the same correction (rule you missed last time, write it now); user asks 'why didn't you\u2026?' about a behavior (they had an expectation, capture it). Mid-instruction corrections ('commit it, and be concise') split into two acts: do the task, write the rule.",
|
|
378731
|
+
" 2. A CHOICE GETS MADE WITH A REASON \u2192 decision. Soul Map shows the WHAT; the WHY is what future-you needs. Capture rationale in details.",
|
|
378732
|
+
" 3. SHARP-EDGE DISCOVERED \u2192 gotcha. Bug that took >5min to diagnose, non-obvious quirk, 'don't touch X because Y', workaround for a flaky test. Include symptom + fix location.",
|
|
378702
378733
|
"",
|
|
378703
|
-
"
|
|
378734
|
+
"SEARCH KEYWORDS \u2014 run action:'search' when about to do any of these and recall was empty: commit message shape, lint/format choice, test framework conventions, package manager (bun/npm/pnpm/yarn), import style, file naming, error-handling pattern, logger choice, state-management library, dispatch/agent setup, prompt-engineering rules. One search beats one wrong guess.",
|
|
378735
|
+
"",
|
|
378736
|
+
"DON'T WRITE \u2014 noise filter:",
|
|
378737
|
+
" - Anything the Soul Map shows (exports, signatures, file structure) \u2014 duplication you'll regret.",
|
|
378738
|
+
" - Temporary task state ('currently refactoring auth') \u2014 that's working memory.",
|
|
378739
|
+
" - Restatement of code (function exists \u2014 memory is for intent/history).",
|
|
378740
|
+
" - 'We tried X' where X is still the active approach \u2014 only store rejected alternatives.",
|
|
378741
|
+
" - Speculation ('might want to migrate someday') \u2014 only crystallized decisions.",
|
|
378742
|
+
"",
|
|
378743
|
+
"WHY WRITES PAY OFF \u2014 the system multiplies them:",
|
|
378744
|
+
" - Soul Map stable file_id \u2192 memory on src/jwt.ts survives renames/refactors.",
|
|
378745
|
+
" - Co-change graph \u2192 memory on auth/middleware.ts surfaces when editing auth/routes.ts (git pairs them).",
|
|
378746
|
+
" - Blast radius \u2192 memories tied to high-impact files rank higher in recall.",
|
|
378747
|
+
" - Provider embeddings \u2192 'how do we sign tokens' finds memories phrased as 'JWT signing' without a shared word.",
|
|
378748
|
+
" - file_paths is the strongest single signal \u2014 pure path overlap bypasses semantic match. ALWAYS include for file-scoped memories.",
|
|
378749
|
+
"",
|
|
378750
|
+
"ON RECALL CONFLICT \u2014 read injected stubs before acting:",
|
|
378751
|
+
` - Surfaced memory contradicts what user just asked \u2192 RAISE IT: 'you stored "never npm" on day 3 \u2014 still respect that, or updating?'`,
|
|
378752
|
+
" - Decision is now stale (user changed mind this turn) \u2192 action:'supersede' AFTER writing the new one. Old becomes hidden; audit trail preserved.",
|
|
378753
|
+
"",
|
|
378754
|
+
"ON DUPLICATE HINT \u2014 when write() returns similar_hints:",
|
|
378755
|
+
" - \u226585% cosine \u2192 action:'get' on the hint_id to read existing first.",
|
|
378756
|
+
" - Refinement (same topic, new detail) \u2192 re-write with merge_topics:true.",
|
|
378757
|
+
" - Contradiction \u2192 supersede.",
|
|
378758
|
+
" - Overlapping but distinct (two gotchas about jwt.ts) \u2192 write anyway, both stay.",
|
|
378704
378759
|
"",
|
|
378705
378760
|
"Actions:",
|
|
378706
|
-
" write
|
|
378707
|
-
" search
|
|
378708
|
-
" list
|
|
378709
|
-
" get
|
|
378761
|
+
" write \u2014 summary (\u2264200) + details (\u22642000) + category + topics[\u22648] + file_paths[\u226416]. Auto-dedups by content hash; near-duplicates (semantic \u22650.65 OR \u226560% trigram overlap on summary) return similar_hints.",
|
|
378762
|
+
" search \u2014 semantic + FTS. query + optional limit/scope.",
|
|
378763
|
+
" list \u2014 filter by category/topic/pinned/include_hidden.",
|
|
378764
|
+
" get \u2014 full record by id (8-char prefix accepted).",
|
|
378710
378765
|
" pin/unpin \u2014 pinned rows survive cleanup + rank higher in recall.",
|
|
378711
|
-
" delete
|
|
378712
|
-
" supersede \u2014 collapse a near-duplicate:
|
|
378766
|
+
" delete \u2014 soft-delete (restorable via restore). All deletes soft \u2014 recoverable forever.",
|
|
378767
|
+
" supersede \u2014 collapse a near-duplicate: id (old) + new_id (replacement). Old row hidden, audit trail kept via superseded_by. Preferred over delete when consolidating duplicates.",
|
|
378768
|
+
"",
|
|
378769
|
+
"Schema:",
|
|
378770
|
+
" summary \u2014 \u2264200ch present-tense headline ('Use bun for scripts', not 'We should use bun').",
|
|
378771
|
+
" details \u2014 \u22642000ch. The 'because' half of decisions, the 'symptom + fix' half of gotchas. Empty OK for prefs.",
|
|
378772
|
+
" category \u2014 pref | decision | gotcha | context | null (null valid; category is a UI filter, NOT used in recall scoring).",
|
|
378773
|
+
" topics \u2014 \u22648 free-form tags ('auth', 'tooling', 'perf'). Short tags drive trigram fallback when FTS misses.",
|
|
378774
|
+
" file_paths \u2014 \u226416 relative paths. ALWAYS include for file-scoped memories \u2014 strongest recall signal, co-change-aware.",
|
|
378775
|
+
" scope \u2014 'project' (default, .soulforge/memory.db) | 'global' (~/.soulforge/memory.db, cross-project prefs only).",
|
|
378776
|
+
" source \u2014 auto-tagged 'agent' for your writes.",
|
|
378777
|
+
"",
|
|
378778
|
+
"Examples \u2014 write these shapes:",
|
|
378779
|
+
" write category:'pref' summary:'Be terse, fragments over sentences' topics:['style'] scope:'global'",
|
|
378780
|
+
" write category:'decision' summary:'Use zustand, not redux \u2014 boilerplate' details:'Tried redux for the auth store, too much ceremony for 4 actions. Switched 2024-11-12. Re-eval if state grows past ~20 slices.' topics:['state','tooling'] file_paths:['src/stores']",
|
|
378781
|
+
" write category:'gotcha' summary:'JWT expiry uses container clock' details:'Container drifts ~3min/day, breaks token validation. Fix at jwt.ts:47 \u2014 use ntp-synced epoch.' topics:['auth','prod-bug'] file_paths:['src/jwt.ts']",
|
|
378782
|
+
" supersede id:'a4d9feaa' new_id:'47daae64'",
|
|
378783
|
+
" search query:'how do we sign tokens' limit:5",
|
|
378713
378784
|
"",
|
|
378714
|
-
"
|
|
378785
|
+
"DEFENSIVE GUARANTEES (so you can write freely):",
|
|
378786
|
+
" - Hard caps: \u22643 surfaced per turn, \u22642400 chars total. A bad write won't blow your context.",
|
|
378787
|
+
" - Soft-delete only \u2014 user can undo any cleanup.",
|
|
378788
|
+
" - Auto-recall is deterministic + cached \u2014 same prompt + same edited files = same surfaced set.",
|
|
378789
|
+
" - No auto-extraction from your turns. Memory only contains what you explicitly wrote."
|
|
378715
378790
|
].join(`
|
|
378716
378791
|
`),
|
|
378717
378792
|
inputSchema: exports_external.object({
|
|
@@ -380060,7 +380135,7 @@ var init_multi_edit = __esm(() => {
|
|
|
380060
380135
|
init_ts_project_detect();
|
|
380061
380136
|
multiEditTool = {
|
|
380062
380137
|
name: "multi_edit",
|
|
380063
|
-
description: "Apply multiple edits to a single non-TS/JS file atomically (JSON, YAML, Markdown, config, raw text). For TS/JS files use ast_edit with operations:[...] \u2014 safer and no line drift. " + "All-or-nothing: if any edit fails, ZERO edits are applied.
|
|
380138
|
+
description: "Apply multiple edits to a single non-TS/JS file atomically (JSON, YAML, Markdown, config, raw text). For TS/JS files use ast_edit with operations:[...] \u2014 safer and no line drift. " + "All-or-nothing: if any edit fails, ZERO edits are applied. lineStart values reference the ORIGINAL file (pre-edit) \u2014 the tool tracks cumulative line offsets internally. " + "Provide lineStart (1-indexed) for reliable line-anchored matching. Without it, falls back to string matching against evolved content. The range is derived from oldString line count. " + "Each oldString is matched against the ORIGINAL file content, not against the result of earlier edits in the batch \u2014 do not emit overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into ONE edit. " + "Keep each oldString minimal and unique. Don't pad with large unchanged regions just to span distant changes. " + "If the call atomically rolls back, re-read the file and retry ALL edits with fresh content.",
|
|
380064
380139
|
execute: async (args2) => {
|
|
380065
380140
|
try {
|
|
380066
380141
|
const filePath = resolve24(args2.path);
|
|
@@ -381180,8 +381255,10 @@ async function readViaWorker(filePath, args2) {
|
|
|
381180
381255
|
let output = result.numbered;
|
|
381181
381256
|
if (result.truncated) {
|
|
381182
381257
|
const outline = await buildSymbolOutline(filePath, result.start + MAX_READ_LINES, result.totalLines);
|
|
381258
|
+
const nextOffset = result.start + MAX_READ_LINES + 1;
|
|
381259
|
+
const remaining = result.totalLines - result.start - MAX_READ_LINES;
|
|
381183
381260
|
output += outline || `
|
|
381184
|
-
... ${String(
|
|
381261
|
+
... ${String(remaining)} more lines. Use ranges:[{start:${String(nextOffset)}, end:N}] to continue.`;
|
|
381185
381262
|
}
|
|
381186
381263
|
return { success: true, output };
|
|
381187
381264
|
}
|
|
@@ -382551,7 +382628,7 @@ async function runPreCommitChecks(cwd2) {
|
|
|
382551
382628
|
cwd: cwd2,
|
|
382552
382629
|
timeout: 15000,
|
|
382553
382630
|
env: buildSafeEnv(),
|
|
382554
|
-
|
|
382631
|
+
...SAFE_SPAWN_OPTS
|
|
382555
382632
|
});
|
|
382556
382633
|
proc.stdout?.on("data", (d) => {
|
|
382557
382634
|
lintBytes += d.length;
|
|
@@ -382788,7 +382865,7 @@ var init_shell = __esm(() => {
|
|
|
382788
382865
|
cwd: cwd2,
|
|
382789
382866
|
timeout,
|
|
382790
382867
|
env: buildSafeEnv(),
|
|
382791
|
-
|
|
382868
|
+
...SAFE_SPAWN_OPTS
|
|
382792
382869
|
});
|
|
382793
382870
|
let cleanupAbortListener;
|
|
382794
382871
|
if (abortSignal) {
|
|
@@ -386650,6 +386727,74 @@ var init_linkify_it = __esm(() => {
|
|
|
386650
386727
|
linkify_it_default = LinkifyIt;
|
|
386651
386728
|
});
|
|
386652
386729
|
|
|
386730
|
+
// src/stores/model-events.ts
|
|
386731
|
+
function recordModelCall(event) {
|
|
386732
|
+
try {
|
|
386733
|
+
const s = useModelEventsStore.getState();
|
|
386734
|
+
if (!s.enabled)
|
|
386735
|
+
return;
|
|
386736
|
+
s.push(event);
|
|
386737
|
+
} catch {}
|
|
386738
|
+
}
|
|
386739
|
+
function aggregateModelEvents(events) {
|
|
386740
|
+
const byModel = new Map;
|
|
386741
|
+
for (const ev of events) {
|
|
386742
|
+
const prev = byModel.get(ev.modelId) ?? {
|
|
386743
|
+
modelId: ev.modelId,
|
|
386744
|
+
calls: 0,
|
|
386745
|
+
errors: 0,
|
|
386746
|
+
totalMs: 0,
|
|
386747
|
+
avgMs: 0,
|
|
386748
|
+
lastMs: 0,
|
|
386749
|
+
input: 0,
|
|
386750
|
+
output: 0,
|
|
386751
|
+
cacheRead: 0,
|
|
386752
|
+
cacheWrite: 0,
|
|
386753
|
+
lastAt: 0
|
|
386754
|
+
};
|
|
386755
|
+
prev.calls += 1;
|
|
386756
|
+
if (ev.state === "error")
|
|
386757
|
+
prev.errors += 1;
|
|
386758
|
+
prev.totalMs += ev.durationMs;
|
|
386759
|
+
prev.lastMs = ev.durationMs;
|
|
386760
|
+
prev.input += ev.input ?? 0;
|
|
386761
|
+
prev.output += ev.output ?? 0;
|
|
386762
|
+
prev.cacheRead += ev.cacheRead ?? 0;
|
|
386763
|
+
prev.cacheWrite += ev.cacheWrite ?? 0;
|
|
386764
|
+
prev.lastAt = Math.max(prev.lastAt, ev.startedAt + ev.durationMs);
|
|
386765
|
+
byModel.set(ev.modelId, prev);
|
|
386766
|
+
}
|
|
386767
|
+
for (const agg of byModel.values()) {
|
|
386768
|
+
agg.avgMs = agg.calls > 0 ? Math.round(agg.totalMs / agg.calls) : 0;
|
|
386769
|
+
}
|
|
386770
|
+
return [...byModel.values()].sort((a, b) => b.lastAt - a.lastAt);
|
|
386771
|
+
}
|
|
386772
|
+
function modelErrorEvents(events) {
|
|
386773
|
+
return events.filter((e) => e.state === "error");
|
|
386774
|
+
}
|
|
386775
|
+
var MAX_EVENTS = 500, useModelEventsStore;
|
|
386776
|
+
var init_model_events = __esm(() => {
|
|
386777
|
+
init_esm();
|
|
386778
|
+
init_middleware();
|
|
386779
|
+
useModelEventsStore = create()(subscribeWithSelector((set3, get) => ({
|
|
386780
|
+
enabled: false,
|
|
386781
|
+
events: [],
|
|
386782
|
+
setEnabled: (v) => set3(v ? { enabled: true } : { enabled: false, events: [] }),
|
|
386783
|
+
push: (event) => {
|
|
386784
|
+
if (!get().enabled)
|
|
386785
|
+
return null;
|
|
386786
|
+
const id = event.id ?? crypto.randomUUID();
|
|
386787
|
+
const full = { ...event, id };
|
|
386788
|
+
set3((s) => {
|
|
386789
|
+
const events = s.events.length >= MAX_EVENTS ? [...s.events.slice(-(MAX_EVENTS - 1)), full] : [...s.events, full];
|
|
386790
|
+
return { events };
|
|
386791
|
+
});
|
|
386792
|
+
return id;
|
|
386793
|
+
},
|
|
386794
|
+
clear: () => set3({ events: [] })
|
|
386795
|
+
})));
|
|
386796
|
+
});
|
|
386797
|
+
|
|
386653
386798
|
// src/core/agents/subagent-events.ts
|
|
386654
386799
|
function emitSubagentStep(step) {
|
|
386655
386800
|
for (const fn of stepListeners)
|
|
@@ -387616,7 +387761,7 @@ var init_stream_options = __esm(() => {
|
|
|
387616
387761
|
|
|
387617
387762
|
// src/core/agents/web-search.ts
|
|
387618
387763
|
function createWebSearchAgent(model, opts) {
|
|
387619
|
-
const {
|
|
387764
|
+
const { maxTransientRetries: retryMaxRetries } = resolveRetrySettings(loadConfig().retry, {
|
|
387620
387765
|
agent: true
|
|
387621
387766
|
});
|
|
387622
387767
|
return new ToolLoopAgent({
|
|
@@ -387767,6 +387912,7 @@ function buildWebSearchTool(opts) {
|
|
|
387767
387912
|
}
|
|
387768
387913
|
runningSteps.clear();
|
|
387769
387914
|
};
|
|
387915
|
+
const webSearchStartedAt = Date.now();
|
|
387770
387916
|
try {
|
|
387771
387917
|
const agent2 = createWebSearchAgent(webSearchModel, {
|
|
387772
387918
|
onApproveFetchPage: opts?.onApproveFetchPage
|
|
@@ -387809,10 +387955,25 @@ function buildWebSearchTool(opts) {
|
|
|
387809
387955
|
}
|
|
387810
387956
|
});
|
|
387811
387957
|
agentSearchCache.set(cacheKey, { output: result.text, ts: Date.now() });
|
|
387958
|
+
recordModelCall({
|
|
387959
|
+
modelId: mid,
|
|
387960
|
+
source: "other",
|
|
387961
|
+
startedAt: webSearchStartedAt,
|
|
387962
|
+
durationMs: Math.max(0, Date.now() - webSearchStartedAt),
|
|
387963
|
+
state: "ok"
|
|
387964
|
+
});
|
|
387812
387965
|
return { success: true, output: result.text, backend: backendLabel };
|
|
387813
387966
|
} catch (err2) {
|
|
387814
387967
|
markRunningStepsError();
|
|
387815
387968
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
387969
|
+
recordModelCall({
|
|
387970
|
+
modelId: mid,
|
|
387971
|
+
source: "other",
|
|
387972
|
+
startedAt: webSearchStartedAt,
|
|
387973
|
+
durationMs: Math.max(0, Date.now() - webSearchStartedAt),
|
|
387974
|
+
state: "error",
|
|
387975
|
+
errorMessage: msg.slice(0, 500)
|
|
387976
|
+
});
|
|
387816
387977
|
const urlHint = extractUrlHint(args2.query);
|
|
387817
387978
|
const fallback = urlHint ? ` Try fetch_page("${urlHint}") to access the resource directly.` : " If you know a specific URL (docs page, npm package, GitHub repo), use fetch_page on that URL directly instead of searching.";
|
|
387818
387979
|
return {
|
|
@@ -387832,6 +387993,7 @@ var init_web_search2 = __esm(() => {
|
|
|
387832
387993
|
init_dist5();
|
|
387833
387994
|
init_linkify_it();
|
|
387834
387995
|
init_zod();
|
|
387996
|
+
init_model_events();
|
|
387835
387997
|
init_subagent_events();
|
|
387836
387998
|
init_web_search();
|
|
387837
387999
|
init_models();
|
|
@@ -390643,7 +390805,7 @@ function createCodeAgent(model, options) {
|
|
|
390643
390805
|
disablePruning: options?.disablePruning,
|
|
390644
390806
|
tabId: options?.tabId
|
|
390645
390807
|
});
|
|
390646
|
-
const {
|
|
390808
|
+
const { maxTransientRetries: retryMaxRetries } = resolveRetrySettings(loadConfig().retry, {
|
|
390647
390809
|
agent: true
|
|
390648
390810
|
});
|
|
390649
390811
|
return new ToolLoopAgent({
|
|
@@ -390752,7 +390914,7 @@ function createExploreAgent(model, options) {
|
|
|
390752
390914
|
disablePruning: options?.disablePruning,
|
|
390753
390915
|
tabId: options?.tabId
|
|
390754
390916
|
});
|
|
390755
|
-
const {
|
|
390917
|
+
const { maxTransientRetries: retryMaxRetries } = resolveRetrySettings(loadConfig().retry, {
|
|
390756
390918
|
agent: true
|
|
390757
390919
|
});
|
|
390758
390920
|
return new ToolLoopAgent({
|
|
@@ -395160,7 +395322,8 @@ ${enrichedPrompt}`;
|
|
|
395160
395322
|
let lastError2;
|
|
395161
395323
|
let attemptsMade = 0;
|
|
395162
395324
|
let proxyBounced = false;
|
|
395163
|
-
|
|
395325
|
+
let lastAttemptStartedAt = Date.now();
|
|
395326
|
+
const { maxTransientRetries: MAX_RETRIES, baseDelayMs: BASE_DELAY_MS } = resolveRetrySettings(loadConfig().retry, { agent: true });
|
|
395164
395327
|
for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
|
|
395165
395328
|
if (abortSignal?.aborted)
|
|
395166
395329
|
break;
|
|
@@ -395174,6 +395337,8 @@ ${enrichedPrompt}`;
|
|
|
395174
395337
|
attemptsMade = attempt + 1;
|
|
395175
395338
|
const { agent: agent2 } = await createAgent(task, models, bus, parentToolCallId);
|
|
395176
395339
|
const callbacks = buildStepCallbacks(parentToolCallId, task.agentId, selectedModelId);
|
|
395340
|
+
const attemptStartedAt = Date.now();
|
|
395341
|
+
lastAttemptStartedAt = attemptStartedAt;
|
|
395177
395342
|
let result;
|
|
395178
395343
|
try {
|
|
395179
395344
|
const generateArgs = isDoppelganger ? {
|
|
@@ -395386,6 +395551,18 @@ ${footer}` : doneResult2.summary;
|
|
|
395386
395551
|
tabId: task.tabId
|
|
395387
395552
|
});
|
|
395388
395553
|
}
|
|
395554
|
+
recordModelCall({
|
|
395555
|
+
modelId: selectedModelId,
|
|
395556
|
+
source: "subagent",
|
|
395557
|
+
startedAt: attemptStartedAt,
|
|
395558
|
+
durationMs: Math.max(0, Date.now() - attemptStartedAt),
|
|
395559
|
+
state: "ok",
|
|
395560
|
+
tabId: task.tabId,
|
|
395561
|
+
agentId: task.agentId,
|
|
395562
|
+
input,
|
|
395563
|
+
output,
|
|
395564
|
+
cacheRead
|
|
395565
|
+
});
|
|
395389
395566
|
return { doneResult: doneResult2, resultText, callbacks, result: agentResult2 };
|
|
395390
395567
|
} catch (error48) {
|
|
395391
395568
|
lastError2 = error48;
|
|
@@ -395451,6 +395628,16 @@ ${footer}` : doneResult2.summary;
|
|
|
395451
395628
|
});
|
|
395452
395629
|
}
|
|
395453
395630
|
const doneResult = salvaged ? { summary: errorResultText } : null;
|
|
395631
|
+
recordModelCall({
|
|
395632
|
+
modelId: selectedModelId,
|
|
395633
|
+
source: "subagent",
|
|
395634
|
+
startedAt: lastAttemptStartedAt,
|
|
395635
|
+
durationMs: Math.max(0, Date.now() - lastAttemptStartedAt),
|
|
395636
|
+
state: "error",
|
|
395637
|
+
tabId: task.tabId,
|
|
395638
|
+
agentId: task.agentId,
|
|
395639
|
+
errorMessage: errMsg.slice(0, 500)
|
|
395640
|
+
});
|
|
395454
395641
|
return {
|
|
395455
395642
|
doneResult,
|
|
395456
395643
|
resultText: errorResultText,
|
|
@@ -395473,6 +395660,7 @@ var init_agent_runner = __esm(() => {
|
|
|
395473
395660
|
init_subagent_tools();
|
|
395474
395661
|
init_config2();
|
|
395475
395662
|
init_settings();
|
|
395663
|
+
init_model_events();
|
|
395476
395664
|
init_tool_timeout();
|
|
395477
395665
|
RETURN_FORMAT_INSTRUCTIONS = {
|
|
395478
395666
|
summary: "Return concise findings and reasoning. No code blocks or raw file content. " + "Focus on what you found, what it means, and what the implications are. " + "Anchor every claim with file:line so the parent can surgically read more.",
|
|
@@ -395793,7 +395981,7 @@ async function createAgent(task, models, bus, parentToolCallId) {
|
|
|
395793
395981
|
onApproveFetchPage: models.onApproveFetchPage,
|
|
395794
395982
|
repoMap: models.repoMap,
|
|
395795
395983
|
contextWindow,
|
|
395796
|
-
disablePruning: models.disablePruning,
|
|
395984
|
+
disablePruning: useSpark ? true : models.disablePruning,
|
|
395797
395985
|
tabId: models.tabId,
|
|
395798
395986
|
forgeInstructions,
|
|
395799
395987
|
forgeTools: forgeToolsGuarded,
|
|
@@ -397010,7 +397198,7 @@ function createForgeAgent({
|
|
|
397010
397198
|
max_tokens: MAX_OUTPUT_TOKENS
|
|
397011
397199
|
}
|
|
397012
397200
|
};
|
|
397013
|
-
const {
|
|
397201
|
+
const { maxTransientRetries: retryMaxRetries } = resolveRetrySettings(loadConfig().retry);
|
|
397014
397202
|
return new ToolLoopAgent({
|
|
397015
397203
|
id: "forge",
|
|
397016
397204
|
model,
|
|
@@ -410959,112 +411147,31 @@ var init_soul_map = __esm(() => {
|
|
|
410959
411147
|
|
|
410960
411148
|
// src/core/prompts/shared/tool-guidance.ts
|
|
410961
411149
|
var TOOL_GUIDANCE_WITH_MAP = `<tool_usage>
|
|
410962
|
-
A Soul Map is loaded in context \u2014 every file, exported symbol, signature, line number,
|
|
411150
|
+
A Soul Map is loaded in context \u2014 every file, exported symbol, signature, line number, dependency edge. It is your first source of truth; tools retrieve just-in-time what the map doesn't already answer.
|
|
410963
411151
|
|
|
410964
411152
|
<workflow>
|
|
410965
|
-
|
|
410966
|
-
2. DISCOVER with parallel soul_find / soul_grep / navigate \u2014 only when the map doesn't answer.
|
|
410967
|
-
3. READ in one parallel batch using Soul Map line numbers for precise ranges.
|
|
410968
|
-
4. EDIT with ast_edit for TS/JS, multi_edit otherwise.
|
|
410969
|
-
5. VERIFY with project (typecheck/lint/test).
|
|
410970
|
-
Commit to the plan. Don't re-read or re-search what you already have.
|
|
411153
|
+
PLAN from the map (zero tool calls) \u2192 DISCOVER in parallel (soul_find/soul_grep/navigate) only when the map doesn't answer \u2192 READ in one parallel batch with Soul Map line numbers \u2192 EDIT (ast_edit for TS/JS, multi_edit otherwise) \u2192 VERIFY with project (typecheck/lint/test). Commit to the plan. Don't re-read or re-search what you have.
|
|
410971
411154
|
</workflow>
|
|
410972
411155
|
|
|
410973
411156
|
<soul_map_usage>
|
|
410974
|
-
The map answers
|
|
410975
|
-
- "Where is X?" \u2192 file and line in the map.
|
|
410976
|
-
- "What does Y export?" \u2192 listed under that file.
|
|
410977
|
-
- "What depends on Z?" \u2192 (\u2192N) blast radius and \u2190 arrows.
|
|
410978
|
-
- "What packages?" \u2192 Key dependencies section.
|
|
410979
|
-
Feed symbol names from the map into navigate/analyze for details. The map gives names; LSP gives bodies.
|
|
411157
|
+
The map answers structural questions for free: "Where is X?" \u2192 file + line. "What does Y export?" \u2192 listed under the file. "What depends on Z?" \u2192 (\u2192N) blast radius + \u2190 arrows. "What packages?" \u2192 Key dependencies section. Feed symbol names into navigate/analyze for bodies.
|
|
410980
411158
|
</soul_map_usage>
|
|
410981
411159
|
|
|
410982
411160
|
<tool_selection>
|
|
410983
411161
|
- Soul Map first \u2192 then TIER-1 (soul_find, soul_grep, navigate, soul_impact, read, ast_edit, multi_edit, project). Drop to TIER-2/3 only when TIER-1 cannot answer.
|
|
410984
|
-
- \`navigate\` auto-resolves files from symbol names \u2014 definitions, references, call hierarchies, type hierarchies. Reaches into \`.d.ts\` / stubs / headers
|
|
411162
|
+
- \`navigate\` auto-resolves files from symbol names \u2014 definitions, references, call hierarchies, type hierarchies. Reaches into \`.d.ts\` / stubs / headers (type info without reading node_modules).
|
|
410985
411163
|
- \`soul_grep\` \`dep\` param searches inside dependencies (e.g. \`dep="react"\`). Any language/package manager.
|
|
410986
|
-
- \`soul_impact\` queries: \`dependents
|
|
411164
|
+
- \`soul_impact\` queries: \`dependents\`, \`dependencies\`, \`cochanges\` (git pairs), \`blast_radius\`. Before editing a file with (\u2192N) > 10, call \`soul_impact(cochanges)\` and update the co-changed files too.
|
|
410987
411165
|
- Batch independent tool calls in one parallel block.
|
|
410988
|
-
- \`git\`
|
|
410989
|
-
- \`soul_vision\` for any image/video path or URL (user is on a CLI).
|
|
411166
|
+
- \`git\` for git ops (not shell). Multi-line messages \u2192 \`body\`/\`footer\`. \`soul_vision\` for any image/video path or URL.
|
|
410990
411167
|
</tool_selection>
|
|
410991
411168
|
|
|
410992
411169
|
<reads>
|
|
410993
|
-
\`read(files=[{path:'x.ts', ranges:[{start:45,end:80}]}])\`. Batch many files in one call.
|
|
411170
|
+
\`read(files=[{path:'x.ts', ranges:[{start:45,end:80}]}])\`. Batch many files in one call. Soul Map line numbers are accurate. AST extraction: \`{path, target:'function', name:'foo'}\`. Skip re-reads.
|
|
410994
411171
|
</reads>
|
|
410995
411172
|
|
|
410996
411173
|
<ast_edit>
|
|
410997
|
-
\`ast_edit\` is the default editor for .ts/.tsx/.js/.jsx/.mts/.cts/.mjs/.cjs \u2014
|
|
410998
|
-
|
|
410999
|
-
CAN DO (no fallback needed \u2014 ast_edit handles these, don't switch to edit_file):
|
|
411000
|
-
- Any named symbol: function, class, interface, type, enum, variable, method, property, constructor, arrow-const. Class members: \`ClassName.memberName\` or just \`memberName\`.
|
|
411001
|
-
- JSX/TSX bodies with Unicode, special chars (\u251C \u2190 \u2192 etc.), escape sequences, quotes. ts-morph wraps the TS compiler \u2014 no limitation there.
|
|
411002
|
-
- Large rewrites: use \`replace\` (whole symbol, full declaration with braces) or anchor-pair \`replace_in_body\` (value=<short start anchor> + valueEnd=<short end anchor>) \u2014 rewrites a 100-line block with ~20 tokens of anchors.
|
|
411003
|
-
- Whitespace drift: \`replace_in_body\` auto-handles tab\u2194space, CRLF\u2194LF, trailing whitespace, and common-indent stripping. Paste from a Read and it matches.
|
|
411004
|
-
- Atomic multi-op: \`operations: [{...}, {...}]\` applies all-or-nothing on one file. Use this for "add import + use it" in a single call.
|
|
411005
|
-
- File creation: \`action:"create_file", newCode:<full content>\`.
|
|
411006
|
-
|
|
411007
|
-
CANNOT TARGET (use the escape hatch, not edit_file):
|
|
411008
|
-
- Anonymous callbacks (inline arrows, IIFEs, object-literal methods without names). \u2192 Use \`replace_in_body\` on the enclosing NAMED symbol.
|
|
411009
|
-
- Union members inside a type alias. \u2192 Use \`replace\` on the whole type.
|
|
411010
|
-
- Raw text inside comments or string literals that aren't bound to a symbol. \u2192 Use \`replace_in_body\` on the enclosing symbol.
|
|
411011
|
-
|
|
411012
|
-
ONLY FALL BACK TO edit_file WHEN:
|
|
411013
|
-
- File is not TS/JS (JSON, YAML, Markdown, config, raw text).
|
|
411014
|
-
- Edit is entirely outside any named symbol (e.g. top-of-file banner comment not attached to a declaration).
|
|
411015
|
-
- File has a parse error that breaks ts-morph (try \`ast_edit\` first; if it fails with a parse error, then edit_file). "Long needle" or "JSX special chars" are NOT fallback reasons \u2014 those work fine.
|
|
411016
|
-
|
|
411017
|
-
Tiers (pick the smallest that does the job):
|
|
411018
|
-
- MICRO (1-10 tokens): set_type, set_return_type, set_async, set_export, rename, remove, set_initializer, add_parameter, set_optional.
|
|
411019
|
-
- BODY (10-100): set_body, add_statement, add_property, add_method, add_constructor, add_decorator, set_extends, add_implements, replace_in_body.
|
|
411020
|
-
- FULL: replace (whole symbol), create_file (new file with \`newCode=<full file content>\`).
|
|
411021
|
-
- FILE-LEVEL: add_import, add_named_import (idempotent \u2014 merges), organize_imports, fix_missing_imports, add_function, add_class, add_interface, add_type_alias, add_enum, insert_text (requires anchor: index=0|-1 or value="after-imports"|"before-exports").
|
|
411022
|
-
- ATOMIC MULTI-OP: \`operations: [{...}, {...}]\` \u2014 all-or-nothing rollback, single file.
|
|
411023
|
-
|
|
411024
|
-
Targets: function | class | interface | type | enum | variable | method | property | constructor | arrow_function. For \`const foo = async (\u2026) => {\u2026}\` use target:"arrow_function" + name:"foo".
|
|
411025
|
-
|
|
411026
|
-
Body shape \u2014 critical, get this wrong and you corrupt the file:
|
|
411027
|
-
- \`set_body\` / \`add_statement\` / \`insert_statement\`: newCode is body CONTENTS ONLY \u2014 no surrounding \`{}\`. ts-morph wraps it. Passing \`{ \u2026 }\` produces \`{ { \u2026 } }\`.
|
|
411028
|
-
- \`add_method\` / \`add_constructor\` / \`add_getter\` / \`add_setter\`: newCode is the FULL declaration including braces (e.g. \`foo(x: number) { return x + 1; }\`).
|
|
411029
|
-
- \`replace\`: newCode is the WHOLE symbol text including its braces (full declaration).
|
|
411030
|
-
- \`add_property\` on interface: newCode is \`"name: type"\` or \`"name?: type"\`. On class: \`"name: type = value"\` or \`"name = value"\`.
|
|
411031
|
-
- \`add_statement\` on expression-body arrow (\`(x) => x + 1\`) auto-wraps into a block \u2014 safe to call.
|
|
411032
|
-
|
|
411033
|
-
replace_in_body shapes (pick the smallest):
|
|
411034
|
-
- SHORT ANCHOR: value=<1-2 unique lines>, newCode=<replacement>. Fastest, most token-efficient.
|
|
411035
|
-
- ANCHOR PAIR (RANGE): value=<short start anchor> + valueEnd=<short end anchor> + newCode=<replacement for the span>. Use for big rewrites \u2014 ~20 tokens replaces 100 lines.
|
|
411036
|
-
- Large single \`value\` (whole block) WORKS but wastes tokens \u2014 prefer \`replace\` on the whole symbol, or anchor pair.
|
|
411037
|
-
- Exact-match ambiguity (\u22652 identical hits) THROWS \u2014 add more surrounding context or use anchor pair.
|
|
411038
|
-
|
|
411039
|
-
\`rename\` is declaration-only by default (safe). Use \`rename_global\` for project-wide propagation \u2014 or \`rename_symbol\` / \`move_symbol\` / \`rename_file\` for cross-file refactors.
|
|
411040
|
-
|
|
411041
|
-
Examples:
|
|
411042
|
-
// MICRO \u2014 flip a method async + set return type, one call
|
|
411043
|
-
ast_edit(path, operations: [
|
|
411044
|
-
{ action:"set_async", target:"method", name:"UserStore.load", value:"true" },
|
|
411045
|
-
{ action:"set_return_type", target:"method", name:"UserStore.load", value:"Promise<User>" }
|
|
411046
|
-
])
|
|
411047
|
-
|
|
411048
|
-
// BODY \u2014 add a statement inside a function
|
|
411049
|
-
ast_edit(path, action:"add_statement", target:"function", name:"loadConfig",
|
|
411050
|
-
newCode:"logger.info('config loaded', { keys: Object.keys(config) });")
|
|
411051
|
-
|
|
411052
|
-
// ANCHOR PAIR \u2014 rewrite a 100-line JSX block with ~20 tokens
|
|
411053
|
-
ast_edit(path, action:"replace_in_body", target:"function", name:"ProviderSettings",
|
|
411054
|
-
value:"const caption = (",
|
|
411055
|
-
valueEnd:"</PremiumPopup>",
|
|
411056
|
-
newCode:"<new JSX here>")
|
|
411057
|
-
|
|
411058
|
-
// ATOMIC \u2014 add import, then add a method that uses it
|
|
411059
|
-
ast_edit(path, operations: [
|
|
411060
|
-
{ action:"add_named_import", value:"zod", newCode:"z" },
|
|
411061
|
-
{ action:"add_method", target:"class", name:"Validator",
|
|
411062
|
-
newCode:"validate(input: unknown) { return z.string().parse(input); }" }
|
|
411063
|
-
])
|
|
411064
|
-
|
|
411065
|
-
// CREATE \u2014 new file
|
|
411066
|
-
ast_edit("src/foo.ts", action:"create_file",
|
|
411067
|
-
newCode:"export function foo() { return 42; }\\n")
|
|
411174
|
+
\`ast_edit\` is the default editor for .ts/.tsx/.js/.jsx/.mts/.cts/.mjs/.cjs \u2014 pairs directly with the Soul Map (every symbol name + kind is in context). See the tool's description for the full operation taxonomy, body-shape rules, replace_in_body anchor shapes, and examples. Use it BEFORE edit_file/multi_edit.
|
|
411068
411175
|
</ast_edit>
|
|
411069
411176
|
|
|
411070
411177
|
<non_ts_edits>
|
|
@@ -411072,87 +411179,15 @@ For non-TS/JS files (JSON, YAML, Markdown, config) or raw text outside any symbo
|
|
|
411072
411179
|
</non_ts_edits>
|
|
411073
411180
|
|
|
411074
411181
|
<memory>
|
|
411075
|
-
\`memory\` is your across-session brain.
|
|
411076
|
-
|
|
411077
|
-
|
|
411078
|
-
|
|
411079
|
-
|
|
411080
|
-
|
|
411081
|
-
SEARCH KEYWORDS \u2014 fall back to memory(search) when about to do any of these and recall was empty: commit message shape, lint/format choice, test framework conventions, package manager (bun/npm/pnpm/yarn), import style, file naming, error-handling pattern, logger choice, state-management library, dispatch/agent setup, prompt-engineering rules. One search beats one wrong guess.
|
|
411082
|
-
|
|
411083
|
-
WRITE proactively, SEARCH when convention matters and nothing was recalled.
|
|
411084
|
-
|
|
411085
|
-
WHY WRITES MATTER \u2014 the system multiplies them:
|
|
411086
|
-
- Soul Map stable file_id \u2192 memory on \`src/jwt.ts\` survives renames and refactors.
|
|
411087
|
-
- Co-change graph \u2192 memory on \`auth/middleware.ts\` surfaces when editing \`auth/routes.ts\` because git history pairs them.
|
|
411088
|
-
- Blast radius \u2192 memories tied to high-impact files rank higher in recall.
|
|
411089
|
-
- Provider embeddings \u2192 "how do we sign tokens" finds memories phrased as "JWT signing" without a single shared word.
|
|
411090
|
-
- file_paths is the strongest single signal \u2014 pure path overlap bypasses semantic match. Always include it for file-scoped memories.
|
|
411091
|
-
|
|
411092
|
-
WHEN TO WRITE \u2014 the three triggers (fire on ANY of these, not just user-prompted ones):
|
|
411093
|
-
1. USER STATES A PREFERENCE OR DIRECTIVE. "use bun not npm", "be terse", "always run tests after edits" \u2192 pref. Write immediately, scope:"global" if it's not project-specific.
|
|
411094
|
-
INFER FROM CUES \u2014 don't wait for the word "remember". A preference exists whenever the user's correction or instruction implies a standing rule, not a one-shot fix. Cues that mean "this is durable":
|
|
411095
|
-
\u2022 Corrective tone about HOW you did something ("be more concise", "stop narrating", "use bullets") \u2014 the correction itself IS the rule.
|
|
411096
|
-
\u2022 Generalising language: "always", "never", "from now on", "by default", "prefer", "in this repo we\u2026", "we don't\u2026".
|
|
411097
|
-
\u2022 Imperative meta-instructions about workflow, style, tooling, formatting, naming \u2014 anything orthogonal to the current task.
|
|
411098
|
-
\u2022 User repeats or rephrases the same correction \u2192 it's a rule you missed last time. Write it now.
|
|
411099
|
-
\u2022 User asks "why didn't you\u2026?" about a behavior \u2014 they had an expectation. Capture the expectation.
|
|
411100
|
-
The literal word "remember" is just one cue among many. The test: "Would future-me want this surfaced next session?" If yes \u2192 write. Mid-instruction corrections ("commit it, and be concise") split into two acts: do the task, write the rule.
|
|
411101
|
-
2. A CHOICE GETS MADE WITH A REASON. "switching to zustand because redux is too much boilerplate", "postgres not mysql for the JSON ops" \u2192 decision. The WHY is what future you needs (the Soul Map shows the WHAT). Capture the rationale in details.
|
|
411102
|
-
3. SHARP-EDGE DISCOVERED. Bug that took >5min to diagnose, non-obvious quirk, "don't touch X because Y", a workaround for a flaky test \u2192 gotcha. Include the symptom + the fix location.
|
|
411103
|
-
|
|
411104
|
-
Examples \u2014 write these shapes:
|
|
411105
|
-
memory(action:"write", category:"pref", summary:"Be terse, fragments over sentences", topics:["style"], scope:"global")
|
|
411106
|
-
memory(action:"write", category:"decision", summary:"Use zustand, not redux \u2014 boilerplate", details:"Tried redux for the auth store, too much ceremony for 4 actions. Switched 2024-11-12. Re-eval if state grows past ~20 slices.", topics:["state","tooling"], file_paths:["src/stores"])
|
|
411107
|
-
memory(action:"write", category:"gotcha", summary:"JWT expiry uses container clock", details:"Container drifts ~3min/day, breaks token validation. Fix at jwt.ts:47 \u2014 use ntp-synced epoch.", topics:["auth","prod-bug"], file_paths:["src/jwt.ts"])
|
|
411108
|
-
memory(action:"supersede", id:"a4d9feaa", new_id:"47daae64")
|
|
411109
|
-
memory(action:"search", query:"how do we sign tokens", limit:5)
|
|
411110
|
-
|
|
411111
|
-
WHEN NOT TO WRITE \u2014 the noise filter:
|
|
411112
|
-
- temporary task state ("currently refactoring auth") \u2014 that's working memory, not durable.
|
|
411113
|
-
- anything the Soul Map shows (exports, signatures, file structure) \u2014 duplication you'll regret.
|
|
411114
|
-
- restatement of code (the function exists \u2014 memory is for intent/history).
|
|
411115
|
-
- "we tried X" where X is still the active approach \u2014 only store rejected alternatives.
|
|
411116
|
-
- speculation ("might want to migrate someday") \u2014 only crystallized decisions.
|
|
411117
|
-
|
|
411118
|
-
ON RECALL CONFLICT \u2014 read injected memories before acting:
|
|
411119
|
-
- if a surfaced memory contradicts what the user just asked, RAISE IT: "you stored 'never npm' on day 3 \u2014 still respect that, or updating?"
|
|
411120
|
-
- if a decision is now stale (user changed their mind this turn), call memory(action:"supersede", id:<old>, new_id:<new>) AFTER writing the new one. Old becomes hidden; audit trail preserved.
|
|
411121
|
-
|
|
411122
|
-
ON DUPLICATE HINT \u2014 when write() returns similar_hints:
|
|
411123
|
-
- \u226585% cosine \u2192 memory(action:"get", id:<hint_id>) to read the existing entry first.
|
|
411124
|
-
- refinement (same topic, new detail): re-write with merge_topics:true.
|
|
411125
|
-
- contradiction: supersede.
|
|
411126
|
-
- overlapping but distinct (two gotchas about jwt.ts): write anyway, both stay.
|
|
411127
|
-
|
|
411128
|
-
Schema:
|
|
411129
|
-
- summary \u2264200ch \u2014 present-tense headline ("Use bun for scripts" not "We should use bun").
|
|
411130
|
-
- details \u22642000ch \u2014 the "because" half of decisions, the "symptom + fix" half of gotchas. Empty is OK for prefs.
|
|
411131
|
-
- category pref | decision | gotcha | context | null (null valid; category is a UI filter, NOT used in recall scoring).
|
|
411132
|
-
- topics \u22648 free-form tags ("auth", "tooling", "perf"). Short tags drive trigram fallback when FTS misses.
|
|
411133
|
-
- file_paths \u226416 relative paths. ALWAYS include for file-scoped memories \u2014 strongest recall signal, co-change-aware.
|
|
411134
|
-
- scope "project" (default, .soulforge/memory.db) | "global" (~/.soulforge/memory.db, cross-project prefs only).
|
|
411135
|
-
- source auto-tagged "agent" for your writes.
|
|
411136
|
-
|
|
411137
|
-
Actions: write | search | list | get | supersede | pin | unpin | delete | restore. All soft \u2014 no hard delete, recoverable forever.
|
|
411138
|
-
|
|
411139
|
-
DEFENSIVE GUARANTEES (so you can write freely):
|
|
411140
|
-
- Hard caps: \u22643 surfaced per turn, \u22642400 chars total. A bad write won't blow your context.
|
|
411141
|
-
- Soft-delete only \u2014 user can undo any cleanup.
|
|
411142
|
-
- Auto-recall is deterministic + cached \u2014 same prompt + same edited files = same surfaced set.
|
|
411143
|
-
- No auto-extraction from your turns. Memory only contains what you explicitly wrote.
|
|
411182
|
+
\`memory\` is your across-session brain. Auto-recall fires before each user turn \u2014 relevant memories arrive as <recalled_memories> stubs. Use it like a primary tool. Triggers \u2014 fire on ANY:
|
|
411183
|
+
- USER STATES A PREFERENCE/DIRECTIVE \u2192 pref. Infer from cues, don't wait for "remember": corrective tone about HOW you worked, generalising language ("always/never/by default/we don't"), repeated corrections, "why didn't you\u2026?" questions. Mid-instruction corrections split: do the task, write the rule.
|
|
411184
|
+
- CHOICE WITH RATIONALE \u2192 decision. Capture the WHY in details.
|
|
411185
|
+
- SHARP EDGE that took effort to find \u2192 gotcha. Include symptom + fix location.
|
|
411186
|
+
SEARCH when about to commit, pick a framework/lib, name a file, or apply any convention and recall was empty. Always set \`file_paths\` for file-scoped memories \u2014 strongest recall signal. On recall conflict with the current request, raise it before acting. See the tool's description for full schema, examples, similar_hints flow, and defensive caps.
|
|
411144
411187
|
</memory>
|
|
411145
411188
|
|
|
411146
411189
|
<dispatch>
|
|
411147
|
-
Agents have limited context. YOU
|
|
411148
|
-
- Look up files/symbols in the Soul Map BEFORE dispatching. Give exact paths, line ranges, symbol names.
|
|
411149
|
-
- Write directives, not research briefs.
|
|
411150
|
-
BAD: "Find how cost reporting works."
|
|
411151
|
-
GOOD: "Read \`statusbar.ts:119-155\` (\`computeCost\`) and \`TokenDisplay.tsx:28-71\`. Report: how tokens map to dollars, what triggers re-render."
|
|
411152
|
-
- Tell agents which tools to use: "soul_impact(dependents) on statusbar.ts, then navigate(references) on computeCost."
|
|
411153
|
-
- Don't dispatch single-topic questions \u2014 answer from the Soul Map + 1-2 reads yourself. Dispatch is for parallel multi-file work.
|
|
411154
|
-
- Each task is self-contained \u2014 the agent can't see your conversation.
|
|
411155
|
-
- State what you ALREADY KNOW and what you NEED. Ask for specifics, not file summaries.
|
|
411190
|
+
Agents have limited context. YOU pre-digest: look up files/symbols in the Soul Map BEFORE dispatching, give exact paths + line ranges + symbol names + which tools to use. Write directives, not research briefs (BAD: "Find how cost reporting works." GOOD: "Read \`statusbar.ts:119-155\` (\`computeCost\`) + \`TokenDisplay.tsx:28-71\`. Report: how tokens map to dollars, what triggers re-render."). Each task is self-contained \u2014 agent can't see your conversation. State what you KNOW and what you NEED. Don't dispatch single-topic questions \u2014 answer from the map + 1-2 reads yourself. Dispatch is for parallel multi-file work.
|
|
411156
411191
|
</dispatch>
|
|
411157
411192
|
</tool_usage>`, TOOL_GUIDANCE_NO_MAP = `<tool_usage>
|
|
411158
411193
|
Use dedicated tools over shell for file reads, searches, definitions, and edits.
|
|
@@ -411936,6 +411971,7 @@ var DEFAULT_CONTEXT_WINDOW2 = 200000, ContextManager;
|
|
|
411936
411971
|
var init_manager5 = __esm(() => {
|
|
411937
411972
|
init_dist5();
|
|
411938
411973
|
init_errors();
|
|
411974
|
+
init_model_events();
|
|
411939
411975
|
init_repomap();
|
|
411940
411976
|
init_neovim();
|
|
411941
411977
|
init_instance2();
|
|
@@ -412697,6 +412733,7 @@ ${s.signature ? `${s.signature}
|
|
|
412697
412733
|
}).join(`
|
|
412698
412734
|
|
|
412699
412735
|
`);
|
|
412736
|
+
const semStartedAt = Date.now();
|
|
412700
412737
|
const { text: text2, usage } = await generateText({
|
|
412701
412738
|
model,
|
|
412702
412739
|
...supportsTemperature(modelId) ? { temperature: 0 } : {},
|
|
@@ -412715,6 +412752,16 @@ ${s.signature ? `${s.signature}
|
|
|
412715
412752
|
});
|
|
412716
412753
|
const cacheRead = usage.inputTokenDetails?.cacheReadTokens ?? 0;
|
|
412717
412754
|
store.addSemanticTokens(usage.inputTokens ?? 0, usage.outputTokens ?? 0, cacheRead);
|
|
412755
|
+
recordModelCall({
|
|
412756
|
+
modelId,
|
|
412757
|
+
source: "other",
|
|
412758
|
+
startedAt: semStartedAt,
|
|
412759
|
+
durationMs: Math.max(0, Date.now() - semStartedAt),
|
|
412760
|
+
state: "ok",
|
|
412761
|
+
input: usage.inputTokens ?? 0,
|
|
412762
|
+
output: usage.outputTokens ?? 0,
|
|
412763
|
+
cacheRead
|
|
412764
|
+
});
|
|
412718
412765
|
for (const line2 of text2.split(`
|
|
412719
412766
|
`)) {
|
|
412720
412767
|
const trimmed = line2.trim();
|
|
@@ -432026,7 +432073,7 @@ ${opts.system}` : opts.system;
|
|
|
432026
432073
|
providerOptions: providerOpts.providerOptions,
|
|
432027
432074
|
headers: providerOpts.headers,
|
|
432028
432075
|
cwd: cwd2,
|
|
432029
|
-
disablePruning: !["subagents", "both"].includes(merged.contextManagement?.pruningTarget ?? "
|
|
432076
|
+
disablePruning: !["subagents", "both"].includes(merged.contextManagement?.pruningTarget ?? "none")
|
|
432030
432077
|
});
|
|
432031
432078
|
return {
|
|
432032
432079
|
cwd: cwd2,
|
|
@@ -482460,6 +482507,9 @@ function handleStatus(_input, _ctx) {
|
|
|
482460
482507
|
useUIStore.setState({ statusDashboardTab: "System" });
|
|
482461
482508
|
useUIStore.getState().openModal("statusDashboard");
|
|
482462
482509
|
}
|
|
482510
|
+
function handleModelEvents(_input, _ctx) {
|
|
482511
|
+
useUIStore.getState().openModal("modelEvents");
|
|
482512
|
+
}
|
|
482463
482513
|
function handleDiagnose(_input, _ctx) {
|
|
482464
482514
|
useUIStore.getState().openModal("diagnosePopup");
|
|
482465
482515
|
}
|
|
@@ -482493,6 +482543,8 @@ async function handleLspRestart(input, ctx) {
|
|
|
482493
482543
|
}
|
|
482494
482544
|
function register6(map2) {
|
|
482495
482545
|
map2.set("/status", handleStatus);
|
|
482546
|
+
map2.set("/model-events", handleModelEvents);
|
|
482547
|
+
map2.set("/model events", handleModelEvents);
|
|
482496
482548
|
map2.set("/diagnose", handleDiagnose);
|
|
482497
482549
|
map2.set("/setup", handleSetup);
|
|
482498
482550
|
map2.set("/lsp", handleLsp);
|
|
@@ -488682,6 +488734,13 @@ var init_registry = __esm(() => {
|
|
|
488682
488734
|
category: "System",
|
|
488683
488735
|
tags: ["info", "health", "context", "tokens"]
|
|
488684
488736
|
},
|
|
488737
|
+
{
|
|
488738
|
+
cmd: "/model-events",
|
|
488739
|
+
ic: "info",
|
|
488740
|
+
desc: "Model events \u2014 per-call latency, tokens, errors (opt-in)",
|
|
488741
|
+
category: "System",
|
|
488742
|
+
tags: ["debug", "metrics", "latency", "errors", "performance", "models", "telemetry"]
|
|
488743
|
+
},
|
|
488685
488744
|
{
|
|
488686
488745
|
cmd: "/storage",
|
|
488687
488746
|
ic: "system",
|
|
@@ -489197,7 +489256,7 @@ function useGlobalKeyboard({
|
|
|
489197
489256
|
return;
|
|
489198
489257
|
const uiModals = useUIStore.getState().modals;
|
|
489199
489258
|
if (selectIsAnyModalOpen(useUIStore.getState())) {
|
|
489200
|
-
const hasOwnInput = uiModals.commandPalette || uiModals.skillSearch || uiModals.sessionPicker || uiModals.errorLog || uiModals.compactionLog || uiModals.llmSelector || uiModals.floatingTerminal || uiModals.firstRunWizard || uiModals.mcpSettings || uiModals.tabNamePopup;
|
|
489259
|
+
const hasOwnInput = uiModals.commandPalette || uiModals.skillSearch || uiModals.sessionPicker || uiModals.errorLog || uiModals.compactionLog || uiModals.llmSelector || uiModals.floatingTerminal || uiModals.firstRunWizard || uiModals.mcpSettings || uiModals.modelEvents || uiModals.tabNamePopup;
|
|
489201
489260
|
if (evt.ctrl && evt.name === "c" && !hasOwnInput) {
|
|
489202
489261
|
handleExit();
|
|
489203
489262
|
}
|
|
@@ -491696,6 +491755,8 @@ async function buildV2Summary(opts) {
|
|
|
491696
491755
|
}
|
|
491697
491756
|
let gapFill;
|
|
491698
491757
|
let llmUsage;
|
|
491758
|
+
const v2StartedAt = Date.now();
|
|
491759
|
+
const v2ModelId = getModelId(model);
|
|
491699
491760
|
try {
|
|
491700
491761
|
const genResult = await generateText({
|
|
491701
491762
|
model,
|
|
@@ -491743,8 +491804,27 @@ async function buildV2Summary(opts) {
|
|
|
491743
491804
|
cacheWriteTokens: details?.cacheWriteTokens ?? 0
|
|
491744
491805
|
};
|
|
491745
491806
|
}
|
|
491807
|
+
recordModelCall({
|
|
491808
|
+
modelId: v2ModelId,
|
|
491809
|
+
source: "compaction",
|
|
491810
|
+
startedAt: v2StartedAt,
|
|
491811
|
+
durationMs: Math.max(0, Date.now() - v2StartedAt),
|
|
491812
|
+
state: "ok",
|
|
491813
|
+
input: llmUsage?.inputTokens ?? 0,
|
|
491814
|
+
output: llmUsage?.outputTokens ?? 0,
|
|
491815
|
+
cacheRead: llmUsage?.cacheReadTokens ?? 0,
|
|
491816
|
+
cacheWrite: llmUsage?.cacheWriteTokens ?? 0
|
|
491817
|
+
});
|
|
491746
491818
|
} catch (err2) {
|
|
491747
491819
|
logBackgroundError("compaction-summarize", err2 instanceof Error ? err2.message : String(err2));
|
|
491820
|
+
recordModelCall({
|
|
491821
|
+
modelId: v2ModelId,
|
|
491822
|
+
source: "compaction",
|
|
491823
|
+
startedAt: v2StartedAt,
|
|
491824
|
+
durationMs: Math.max(0, Date.now() - v2StartedAt),
|
|
491825
|
+
state: "error",
|
|
491826
|
+
errorMessage: (err2 instanceof Error ? err2.message : String(err2)).slice(0, 500)
|
|
491827
|
+
});
|
|
491748
491828
|
return { summary: structuredState };
|
|
491749
491829
|
}
|
|
491750
491830
|
if (!gapFill || gapFill.trim() === "COMPLETE" || gapFill.trim().length < 20) {
|
|
@@ -491804,6 +491884,7 @@ function messageTextFull(msg) {
|
|
|
491804
491884
|
var init_summarize = __esm(() => {
|
|
491805
491885
|
init_dist5();
|
|
491806
491886
|
init_errors();
|
|
491887
|
+
init_model_events();
|
|
491807
491888
|
init_provider_options();
|
|
491808
491889
|
});
|
|
491809
491890
|
|
|
@@ -492428,7 +492509,8 @@ function useChat({
|
|
|
492428
492509
|
openEditor,
|
|
492429
492510
|
initialState,
|
|
492430
492511
|
getWorkspaceSnapshot,
|
|
492431
|
-
visible = true
|
|
492512
|
+
visible = true,
|
|
492513
|
+
onModelChange
|
|
492432
492514
|
}) {
|
|
492433
492515
|
const [messages, setMessages] = import_react32.useState(initialState?.messages ?? []);
|
|
492434
492516
|
const [coreMessages, setCoreMessages] = import_react32.useState(initialState?.coreMessages ?? []);
|
|
@@ -492561,6 +492643,7 @@ function useChat({
|
|
|
492561
492643
|
const outsideCwdMutexRef = import_react32.useRef(Promise.resolve());
|
|
492562
492644
|
const remoteApprovalActiveRef = import_react32.useRef(0);
|
|
492563
492645
|
const webSearchModelLabelRef = import_react32.useRef(null);
|
|
492646
|
+
const userAbortedRef = import_react32.useRef(false);
|
|
492564
492647
|
const [activePlan, setActivePlanRaw] = import_react32.useState(initialState?.activePlan ?? null);
|
|
492565
492648
|
const activePlanRef = import_react32.useRef(activePlan);
|
|
492566
492649
|
const setActivePlan = import_react32.useCallback((v4) => {
|
|
@@ -493005,6 +493088,7 @@ function useChat({
|
|
|
493005
493088
|
const convoText = olderMessages.map((m5) => formatMessage(m5, 6000)).join(`
|
|
493006
493089
|
|
|
493007
493090
|
`);
|
|
493091
|
+
const compactStartedAt = Date.now();
|
|
493008
493092
|
const v1Result = await generateText({
|
|
493009
493093
|
model,
|
|
493010
493094
|
...supportsTemperature(activeModelRef.current) ? { temperature: 0 } : {},
|
|
@@ -493062,6 +493146,18 @@ INCLUDE the plan progress above VERBATIM in ## Current State so the agent knows
|
|
|
493062
493146
|
cacheWriteTokens: v1Details?.cacheWriteTokens ?? 0
|
|
493063
493147
|
};
|
|
493064
493148
|
}
|
|
493149
|
+
recordModelCall({
|
|
493150
|
+
modelId: compactModelId,
|
|
493151
|
+
source: "compaction",
|
|
493152
|
+
startedAt: compactStartedAt,
|
|
493153
|
+
durationMs: Math.max(0, Date.now() - compactStartedAt),
|
|
493154
|
+
state: "ok",
|
|
493155
|
+
tabId,
|
|
493156
|
+
input: compactUsage?.inputTokens ?? 0,
|
|
493157
|
+
output: compactUsage?.outputTokens ?? 0,
|
|
493158
|
+
cacheRead: compactUsage?.cacheReadTokens ?? 0,
|
|
493159
|
+
cacheWrite: compactUsage?.cacheWriteTokens ?? 0
|
|
493160
|
+
});
|
|
493065
493161
|
}
|
|
493066
493162
|
if (!summary || summary.trim().length < 50) {
|
|
493067
493163
|
setMessages((prev) => [
|
|
@@ -493216,7 +493312,7 @@ INCLUDE the plan progress above VERBATIM in ## Current State so the agent knows
|
|
|
493216
493312
|
});
|
|
493217
493313
|
}
|
|
493218
493314
|
}
|
|
493219
|
-
}, [setTokenUsage, effectiveConfig, contextManager, cwd2]);
|
|
493315
|
+
}, [setTokenUsage, effectiveConfig, contextManager, cwd2, tabId]);
|
|
493220
493316
|
summarizeConversationRef.current = summarizeConversation;
|
|
493221
493317
|
const autoSummarizedRef = import_react32.useRef(false);
|
|
493222
493318
|
import_react32.useEffect(() => {
|
|
@@ -493740,16 +493836,24 @@ ${description}`,
|
|
|
493740
493836
|
queueMicrotaskFlush();
|
|
493741
493837
|
}
|
|
493742
493838
|
});
|
|
493743
|
-
const
|
|
493839
|
+
const {
|
|
493840
|
+
maxTransientRetries: MAX_TRANSIENT_RETRIES,
|
|
493841
|
+
maxStallRetries: STALL_MAX_RETRIES,
|
|
493842
|
+
baseDelayMs: RETRY_BASE_DELAY_MS
|
|
493843
|
+
} = resolveRetrySettings(effectiveConfig2.retry);
|
|
493744
493844
|
let stallWatchdog = null;
|
|
493745
493845
|
let unsubStallWatch1 = null;
|
|
493746
493846
|
let unsubStallWatch2 = null;
|
|
493747
493847
|
let unsubStallWatch3 = null;
|
|
493748
|
-
let userAborted = false;
|
|
493749
493848
|
let stallTriggered = false;
|
|
493750
493849
|
let stallAborted = false;
|
|
493751
|
-
const { maxRetries: MAX_TRANSIENT_RETRIES, baseDelayMs: RETRY_BASE_DELAY_MS } = resolveRetrySettings(effectiveConfig2.retry);
|
|
493752
493850
|
let streamRetryCount = 0;
|
|
493851
|
+
const rawFallback = effectiveConfig2.modelFallback;
|
|
493852
|
+
const fallbackModels = rawFallback && typeof rawFallback === "object" && !Array.isArray(rawFallback) ? (rawFallback[activeModelRef.current] ?? []).filter((m5) => m5 && m5.trim().length > 0) : [];
|
|
493853
|
+
let fallbackIndex = -1;
|
|
493854
|
+
const primaryModelId = activeModelRef.current;
|
|
493855
|
+
let cycleCount = 0;
|
|
493856
|
+
const MAX_CYCLES = 3;
|
|
493753
493857
|
let lengthRetryCount = 0;
|
|
493754
493858
|
const MAX_LENGTH_RETRIES = 2;
|
|
493755
493859
|
if (input !== "Continue." || !stallRetryPendingRef.current) {
|
|
@@ -493759,6 +493863,7 @@ ${description}`,
|
|
|
493759
493863
|
const responseStartedAt = Date.now();
|
|
493760
493864
|
for (;; ) {
|
|
493761
493865
|
let proxyBounced = false;
|
|
493866
|
+
userAbortedRef.current = false;
|
|
493762
493867
|
abortController = new AbortController;
|
|
493763
493868
|
abortRef.current = abortController;
|
|
493764
493869
|
fullText = "";
|
|
@@ -493971,7 +494076,7 @@ Proceeding without it will significantly reduce capabilities \u2014 no soul tool
|
|
|
493971
494076
|
},
|
|
493972
494077
|
planExecution: planExecutionRef.current,
|
|
493973
494078
|
drainSteering,
|
|
493974
|
-
disablePruning: !["subagents", "both"].includes(effectiveConfig2.contextManagement?.pruningTarget ?? "
|
|
494079
|
+
disablePruning: !["subagents", "both"].includes(effectiveConfig2.contextManagement?.pruningTarget ?? "none"),
|
|
493975
494080
|
disabledTools: useToolsStore.getState().disabledTools,
|
|
493976
494081
|
tabId,
|
|
493977
494082
|
tabLabel
|
|
@@ -494010,7 +494115,7 @@ Proceeding without it will significantly reduce capabilities \u2014 no soul tool
|
|
|
494010
494115
|
},
|
|
494011
494116
|
planExecution: planExecutionRef.current,
|
|
494012
494117
|
drainSteering,
|
|
494013
|
-
disablePruning: !["subagents", "both"].includes(effectiveConfig2.contextManagement?.pruningTarget ?? "
|
|
494118
|
+
disablePruning: !["subagents", "both"].includes(effectiveConfig2.contextManagement?.pruningTarget ?? "none"),
|
|
494014
494119
|
disabledTools: useToolsStore.getState().disabledTools,
|
|
494015
494120
|
tabId,
|
|
494016
494121
|
tabLabel
|
|
@@ -494104,6 +494209,7 @@ Proceeding without it will significantly reduce capabilities \u2014 no soul tool
|
|
|
494104
494209
|
let toolsInFlight = 0;
|
|
494105
494210
|
let gotFirstContent = false;
|
|
494106
494211
|
let betweenSteps = false;
|
|
494212
|
+
let stepStartedAt = 0;
|
|
494107
494213
|
const markActivity = () => {
|
|
494108
494214
|
lastActivityTs = Date.now();
|
|
494109
494215
|
};
|
|
@@ -494118,7 +494224,7 @@ Proceeding without it will significantly reduce capabilities \u2014 no soul tool
|
|
|
494118
494224
|
};
|
|
494119
494225
|
const onUserAbort = () => {
|
|
494120
494226
|
if (!stallAborted) {
|
|
494121
|
-
|
|
494227
|
+
userAbortedRef.current = true;
|
|
494122
494228
|
}
|
|
494123
494229
|
};
|
|
494124
494230
|
abortController.signal.addEventListener("abort", onUserAbort, { once: true });
|
|
@@ -494215,6 +494321,7 @@ Proceeding without it will significantly reduce capabilities \u2014 no soul tool
|
|
|
494215
494321
|
switch (part.type) {
|
|
494216
494322
|
case "start-step": {
|
|
494217
494323
|
betweenSteps = false;
|
|
494324
|
+
stepStartedAt = Date.now();
|
|
494218
494325
|
const warnings = part.warnings;
|
|
494219
494326
|
if (warnings && warnings.length > 0) {
|
|
494220
494327
|
const msg = warnings.map((w5) => `[${w5.type}]${w5.message ? ` ${w5.message}` : ""}`).join("; ");
|
|
@@ -494502,6 +494609,18 @@ Proceeding without it will significantly reduce capabilities \u2014 no soul tool
|
|
|
494502
494609
|
output: turnTokensRef.current.output + stepOut,
|
|
494503
494610
|
cacheRead: turnTokensRef.current.cacheRead + stepCache
|
|
494504
494611
|
};
|
|
494612
|
+
recordModelCall({
|
|
494613
|
+
modelId,
|
|
494614
|
+
source: "main",
|
|
494615
|
+
startedAt: stepStartedAt || Date.now(),
|
|
494616
|
+
durationMs: stepStartedAt ? Math.max(0, Date.now() - stepStartedAt) : 0,
|
|
494617
|
+
state: "ok",
|
|
494618
|
+
tabId,
|
|
494619
|
+
input: stepIn,
|
|
494620
|
+
output: stepOut,
|
|
494621
|
+
cacheRead: stepCache,
|
|
494622
|
+
cacheWrite: stepCacheWrite
|
|
494623
|
+
});
|
|
494505
494624
|
queueMicrotaskFlush();
|
|
494506
494625
|
if (completedCalls.length > 0 && Date.now() - lastIncrementalSave > 1e4) {
|
|
494507
494626
|
lastIncrementalSave = Date.now();
|
|
@@ -494565,7 +494684,7 @@ ${errStack}` : `Error: ${displayErr}`);
|
|
|
494565
494684
|
unsubStallWatch1?.();
|
|
494566
494685
|
unsubStallWatch2?.();
|
|
494567
494686
|
unsubStallWatch3?.();
|
|
494568
|
-
if (stallTriggered && abortController.signal.aborted && !
|
|
494687
|
+
if (stallTriggered && abortController.signal.aborted && !userAbortedRef.current) {
|
|
494569
494688
|
throw new Error("Stream stall \u2014 abort did not throw");
|
|
494570
494689
|
}
|
|
494571
494690
|
if (streamErrors.length > 0) {
|
|
@@ -494681,7 +494800,7 @@ ${errStack}` : `Error: ${displayErr}`);
|
|
|
494681
494800
|
}).map((m5) => m5 ?? { role: "assistant", content: "(continued)" });
|
|
494682
494801
|
setCoreMessages((prev) => {
|
|
494683
494802
|
const updated = [...prev, ...filteredResponseMessages];
|
|
494684
|
-
const target = effectiveConfig2.contextManagement?.pruningTarget ?? "
|
|
494803
|
+
const target = effectiveConfig2.contextManagement?.pruningTarget ?? "none";
|
|
494685
494804
|
return ["main", "both"].includes(target) ? pruneOldToolResults(updated) : updated;
|
|
494686
494805
|
});
|
|
494687
494806
|
streamSegmentsBuffer.current = [];
|
|
@@ -494786,7 +494905,7 @@ ${errStack}` : `Error: ${displayErr}`);
|
|
|
494786
494905
|
]);
|
|
494787
494906
|
}
|
|
494788
494907
|
}
|
|
494789
|
-
const isStallRetry = isAbort && stallTriggered && !
|
|
494908
|
+
const isStallRetry = isAbort && stallTriggered && !userAbortedRef.current && stallRetryCountRef.current <= STALL_MAX_RETRIES;
|
|
494790
494909
|
if (isTransient && !isStallRetry) {
|
|
494791
494910
|
streamRetryCount++;
|
|
494792
494911
|
if (streamRetryCount <= MAX_TRANSIENT_RETRIES && !abortController.signal.aborted) {
|
|
@@ -494862,6 +494981,46 @@ ${errStack}` : `Error: ${displayErr}`);
|
|
|
494862
494981
|
continue;
|
|
494863
494982
|
}
|
|
494864
494983
|
}
|
|
494984
|
+
if (userAbortedRef.current) {} else if (fallbackIndex < fallbackModels.length - 1) {
|
|
494985
|
+
fallbackIndex++;
|
|
494986
|
+
const nextModel = fallbackModels[fallbackIndex];
|
|
494987
|
+
activeModelRef.current = nextModel;
|
|
494988
|
+
streamRetryCount = 0;
|
|
494989
|
+
notifyProviderSwitch(nextModel).catch(() => {});
|
|
494990
|
+
setActiveModel(nextModel);
|
|
494991
|
+
onModelChange?.(nextModel);
|
|
494992
|
+
setMessages((prev) => [
|
|
494993
|
+
...prev,
|
|
494994
|
+
{
|
|
494995
|
+
id: crypto.randomUUID(),
|
|
494996
|
+
role: "system",
|
|
494997
|
+
content: `Switched to fallback model: ${nextModel}`,
|
|
494998
|
+
timestamp: Date.now()
|
|
494999
|
+
}
|
|
495000
|
+
]);
|
|
495001
|
+
continue;
|
|
495002
|
+
} else if (fallbackModels.length > 0) {
|
|
495003
|
+
cycleCount++;
|
|
495004
|
+
if (cycleCount > MAX_CYCLES) {
|
|
495005
|
+
throw new Error(`Exhausted ${String(MAX_CYCLES)} cycles of model fallbacks. Last error: ${msg}`, { cause: err2 });
|
|
495006
|
+
}
|
|
495007
|
+
fallbackIndex = -1;
|
|
495008
|
+
activeModelRef.current = primaryModelId;
|
|
495009
|
+
streamRetryCount = 0;
|
|
495010
|
+
notifyProviderSwitch(primaryModelId).catch(() => {});
|
|
495011
|
+
setActiveModel(primaryModelId);
|
|
495012
|
+
onModelChange?.(primaryModelId);
|
|
495013
|
+
setMessages((prev) => [
|
|
495014
|
+
...prev,
|
|
495015
|
+
{
|
|
495016
|
+
id: crypto.randomUUID(),
|
|
495017
|
+
role: "system",
|
|
495018
|
+
content: `All fallbacks exhausted, retrying primary model: ${primaryModelId} (cycle ${String(cycleCount)}/${String(MAX_CYCLES)})`,
|
|
495019
|
+
timestamp: Date.now()
|
|
495020
|
+
}
|
|
495021
|
+
]);
|
|
495022
|
+
continue;
|
|
495023
|
+
}
|
|
494865
495024
|
}
|
|
494866
495025
|
if (isStallRetry) {
|
|
494867
495026
|
if (flushTimerRef.current) {
|
|
@@ -494952,14 +495111,25 @@ ${errStack}` : `Error: ${displayErr}`);
|
|
|
494952
495111
|
cwd: cwd2
|
|
494953
495112
|
}).catch(() => {});
|
|
494954
495113
|
}
|
|
494955
|
-
const isTransientStream = /overloaded|529|429|rate.?limit|too many requests|503|502/i.test(rawMsg);
|
|
494956
495114
|
const errObj = err2 != null && typeof err2 === "object" ? err2 : null;
|
|
494957
495115
|
const apiBody = errObj && typeof errObj.responseBody === "string" && errObj.responseBody.length > 0 ? errObj.responseBody : undefined;
|
|
494958
495116
|
const apiData = errObj?.data != null ? JSON.stringify(errObj.data).slice(0, 500) : undefined;
|
|
494959
495117
|
const detail = apiBody?.slice(0, 500) ?? apiData;
|
|
494960
495118
|
const enrichedMsg = detail ? `${rawMsg} \xB7 ${detail}` : rawMsg;
|
|
495119
|
+
const isTransientStream = /overloaded|529|429|rate.?limit|too many requests|503|502/i.test(rawMsg) || /403/i.test(rawMsg) && /overloaded|rate/i.test(apiBody ?? "");
|
|
494961
495120
|
const errorMsg = isTransientStream ? `Provider returned a transient error (${rawMsg.slice(0, 120)}). Please retry.` : enrichedMsg;
|
|
494962
495121
|
const errorStack = !isTransientStream && err2 instanceof Error ? err2.stack : undefined;
|
|
495122
|
+
if (!isAbort) {
|
|
495123
|
+
recordModelCall({
|
|
495124
|
+
modelId: activeModelRef.current,
|
|
495125
|
+
source: "main",
|
|
495126
|
+
startedAt: Date.now(),
|
|
495127
|
+
durationMs: 0,
|
|
495128
|
+
state: "error",
|
|
495129
|
+
tabId,
|
|
495130
|
+
errorMessage: errorMsg.slice(0, 500)
|
|
495131
|
+
});
|
|
495132
|
+
}
|
|
494963
495133
|
if (isAbort) {
|
|
494964
495134
|
const completedIds = new Set(completedCalls.map((c) => c.id));
|
|
494965
495135
|
const liveBuf = abortedToolCallsSnapshot.current.length > 0 ? abortedToolCallsSnapshot.current : liveToolCallsBuffer.current;
|
|
@@ -495237,7 +495407,8 @@ ${pContent}`;
|
|
|
495237
495407
|
promptDestructive,
|
|
495238
495408
|
tabId,
|
|
495239
495409
|
tabLabel,
|
|
495240
|
-
setForgeMode
|
|
495410
|
+
setForgeMode,
|
|
495411
|
+
onModelChange
|
|
495241
495412
|
]);
|
|
495242
495413
|
handleSubmitRef.current = handleSubmit;
|
|
495243
495414
|
const abort2 = import_react32.useCallback(() => {
|
|
@@ -495258,6 +495429,7 @@ ${pContent}`;
|
|
|
495258
495429
|
}
|
|
495259
495430
|
]);
|
|
495260
495431
|
}
|
|
495432
|
+
userAbortedRef.current = true;
|
|
495261
495433
|
if (abortRef.current) {
|
|
495262
495434
|
const pq = pendingQuestionRef.current;
|
|
495263
495435
|
if (pq) {
|
|
@@ -495432,6 +495604,7 @@ var init_useChat = __esm(() => {
|
|
|
495432
495604
|
init_io_client();
|
|
495433
495605
|
init_compaction_logs();
|
|
495434
495606
|
init_errors();
|
|
495607
|
+
init_model_events();
|
|
495435
495608
|
init_repomap();
|
|
495436
495609
|
init_statusbar();
|
|
495437
495610
|
init_tools2();
|
|
@@ -504180,7 +504353,7 @@ function GroupedListImpl({
|
|
|
504180
504353
|
const parent = filteredGroupOf(groups, r4.groupId);
|
|
504181
504354
|
const accent = parent?.accent ?? t2.brand;
|
|
504182
504355
|
const rowBg = isSelected ? t2.bgPopupHighlight : fill;
|
|
504183
|
-
const fg2 = it.disabled ? t2.textDim : isSelected ? t2.textPrimary : it.active ? accent : focused ? t2.textSecondary : t2.textMuted;
|
|
504356
|
+
const fg2 = it.disabled ? t2.textDim : isSelected ? t2.textPrimary : it.active ? accent : it.subdued ? t2.textFaint : focused ? t2.textSecondary : t2.textMuted;
|
|
504184
504357
|
const hlFg = isSelected ? t2.textPrimary : accent;
|
|
504185
504358
|
const labelBold = isSelected || !!it.active;
|
|
504186
504359
|
const spans = renderLabelSpans(it.label, it.highlightIndices, fg2, hlFg, labelBold);
|
|
@@ -507780,6 +507953,7 @@ var init_TabInstance = __esm(async () => {
|
|
|
507780
507953
|
onSuspend,
|
|
507781
507954
|
onCommand,
|
|
507782
507955
|
onModeChange,
|
|
507956
|
+
onModelChange,
|
|
507783
507957
|
onExit,
|
|
507784
507958
|
registerChat,
|
|
507785
507959
|
unregisterChat,
|
|
@@ -507863,7 +508037,8 @@ var init_TabInstance = __esm(async () => {
|
|
|
507863
508037
|
onSuspend,
|
|
507864
508038
|
initialState,
|
|
507865
508039
|
getWorkspaceSnapshot,
|
|
507866
|
-
visible
|
|
508040
|
+
visible,
|
|
508041
|
+
onModelChange
|
|
507867
508042
|
});
|
|
507868
508043
|
import_react71.useEffect(() => {
|
|
507869
508044
|
if (effectiveConfig.coAuthorCommits !== undefined)
|
|
@@ -509352,6 +509527,7 @@ function ApiKeySettings({ visible, onClose }) {
|
|
|
509352
509527
|
const keys2 = useApiKeyStore((s2) => s2.keys);
|
|
509353
509528
|
const priority = useApiKeyStore((s2) => s2.priority);
|
|
509354
509529
|
const refresh = useApiKeyStore((s2) => s2.refresh);
|
|
509530
|
+
const refreshOne = useApiKeyStore((s2) => s2.refreshOne);
|
|
509355
509531
|
const [cursor, setCursor] = import_react80.useState(0);
|
|
509356
509532
|
const [mode, setMode] = import_react80.useState("menu");
|
|
509357
509533
|
const [inputValue, setInputValue] = import_react80.useState("");
|
|
@@ -509405,9 +509581,9 @@ function ApiKeySettings({ visible, onClose }) {
|
|
|
509405
509581
|
id: `${k5.id}-remove`,
|
|
509406
509582
|
kind: "remove",
|
|
509407
509583
|
targetKey: k5.id,
|
|
509408
|
-
|
|
509409
|
-
|
|
509410
|
-
|
|
509584
|
+
prefix: " \u21B3",
|
|
509585
|
+
label: "remove stored key",
|
|
509586
|
+
subdued: true
|
|
509411
509587
|
});
|
|
509412
509588
|
}
|
|
509413
509589
|
return out2;
|
|
@@ -509468,17 +509644,18 @@ function ApiKeySettings({ visible, onClose }) {
|
|
|
509468
509644
|
setMode("menu");
|
|
509469
509645
|
return;
|
|
509470
509646
|
}
|
|
509471
|
-
const
|
|
509647
|
+
const target = inputTarget;
|
|
509648
|
+
setMode("menu");
|
|
509649
|
+
setInputValue("");
|
|
509650
|
+
setInputTarget(null);
|
|
509651
|
+
const result = setSecret(target, inputValue.trim());
|
|
509472
509652
|
if (result.success) {
|
|
509473
509653
|
const where = result.storage === "keychain" ? "OS keychain" : result.path ?? "secrets.json";
|
|
509474
509654
|
popFlash("ok", `Saved to ${where}`);
|
|
509475
509655
|
} else {
|
|
509476
509656
|
popFlash("err", "Failed to save key");
|
|
509477
509657
|
}
|
|
509478
|
-
|
|
509479
|
-
setMode("menu");
|
|
509480
|
-
setInputValue("");
|
|
509481
|
-
setInputTarget(null);
|
|
509658
|
+
refreshOne(target);
|
|
509482
509659
|
};
|
|
509483
509660
|
const removeKey = (keyId) => {
|
|
509484
509661
|
const result = deleteSecret(keyId);
|
|
@@ -509486,7 +509663,7 @@ function ApiKeySettings({ visible, onClose }) {
|
|
|
509486
509663
|
popFlash("ok", `Removed from ${result.storage}`);
|
|
509487
509664
|
else
|
|
509488
509665
|
popFlash("err", "Key not found");
|
|
509489
|
-
|
|
509666
|
+
refreshOne(keyId);
|
|
509490
509667
|
};
|
|
509491
509668
|
useKeyboard((evt) => {
|
|
509492
509669
|
if (!visible)
|
|
@@ -509621,7 +509798,8 @@ var init_ApiKeySettings = __esm(async () => {
|
|
|
509621
509798
|
useApiKeyStore = create()((set3, get) => ({
|
|
509622
509799
|
keys: {},
|
|
509623
509800
|
priority: getDefaultKeyPriority(),
|
|
509624
|
-
refresh: (items) => set3({ keys: buildKeys(items, get().priority) })
|
|
509801
|
+
refresh: (items) => set3({ keys: buildKeys(items, get().priority) }),
|
|
509802
|
+
refreshOne: (id) => set3((state) => ({ keys: { ...state.keys, [id]: getSecretSources(id, state.priority) } }))
|
|
509625
509803
|
}));
|
|
509626
509804
|
});
|
|
509627
509805
|
|
|
@@ -523469,6 +523647,253 @@ var init_MCPSettings = __esm(async () => {
|
|
|
523469
523647
|
});
|
|
523470
523648
|
});
|
|
523471
523649
|
|
|
523650
|
+
// src/components/settings/ModelEventsPopup.tsx
|
|
523651
|
+
function fmtMs(ms) {
|
|
523652
|
+
if (ms <= 0)
|
|
523653
|
+
return "\u2014";
|
|
523654
|
+
if (ms < 1000)
|
|
523655
|
+
return `${String(Math.round(ms))}ms`;
|
|
523656
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
523657
|
+
}
|
|
523658
|
+
function fmtTok(n) {
|
|
523659
|
+
if (n <= 0)
|
|
523660
|
+
return "\u2014";
|
|
523661
|
+
if (n < 1000)
|
|
523662
|
+
return String(n);
|
|
523663
|
+
if (n < 1e6)
|
|
523664
|
+
return `${(n / 1000).toFixed(1)}k`;
|
|
523665
|
+
return `${(n / 1e6).toFixed(2)}M`;
|
|
523666
|
+
}
|
|
523667
|
+
function fmtAge(now2, at) {
|
|
523668
|
+
const d3 = Math.max(0, now2 - at);
|
|
523669
|
+
if (d3 < 1000)
|
|
523670
|
+
return "now";
|
|
523671
|
+
if (d3 < 60000)
|
|
523672
|
+
return `${String(Math.round(d3 / 1000))}s`;
|
|
523673
|
+
if (d3 < 3600000)
|
|
523674
|
+
return `${String(Math.round(d3 / 60000))}m`;
|
|
523675
|
+
return `${String(Math.round(d3 / 3600000))}h`;
|
|
523676
|
+
}
|
|
523677
|
+
function shortModel(id) {
|
|
523678
|
+
const slash = id.lastIndexOf("/");
|
|
523679
|
+
return slash >= 0 ? id.slice(slash + 1) : id;
|
|
523680
|
+
}
|
|
523681
|
+
function ModelEventsPopup({ visible, onClose }) {
|
|
523682
|
+
const t2 = useTheme();
|
|
523683
|
+
const { width: termCols, height: termRows } = useTerminalDimensions();
|
|
523684
|
+
const popupW = Math.min(110, Math.max(80, Math.floor(termCols * 0.85)));
|
|
523685
|
+
const popupH = Math.min(Math.max(20, Math.floor(termRows * 0.82)), termRows - 2);
|
|
523686
|
+
const SIDEBAR_W4 = 16;
|
|
523687
|
+
const contentW = Math.max(40, popupW - SIDEBAR_W4 - 6);
|
|
523688
|
+
const enabled = useModelEventsStore((s2) => s2.enabled);
|
|
523689
|
+
const events = useModelEventsStore((s2) => s2.events);
|
|
523690
|
+
const setEnabled = useModelEventsStore((s2) => s2.setEnabled);
|
|
523691
|
+
const clear = useModelEventsStore((s2) => s2.clear);
|
|
523692
|
+
const [tab, setTab] = import_react136.useState("Models");
|
|
523693
|
+
const [now2, setNow] = import_react136.useState(Date.now());
|
|
523694
|
+
import_react136.useEffect(() => {
|
|
523695
|
+
if (!visible)
|
|
523696
|
+
return;
|
|
523697
|
+
const i4 = setInterval(() => setNow(Date.now()), 1000);
|
|
523698
|
+
return () => clearInterval(i4);
|
|
523699
|
+
}, [visible]);
|
|
523700
|
+
useKeyboard((evt) => {
|
|
523701
|
+
if (!visible)
|
|
523702
|
+
return;
|
|
523703
|
+
if (evt.name === "escape" || evt.name === "q") {
|
|
523704
|
+
onClose();
|
|
523705
|
+
return;
|
|
523706
|
+
}
|
|
523707
|
+
if (evt.name === "tab" || evt.name === "right" || evt.name === "l") {
|
|
523708
|
+
setTab((cur) => TABS5[(TABS5.indexOf(cur) + 1) % TABS5.length] ?? "Models");
|
|
523709
|
+
return;
|
|
523710
|
+
}
|
|
523711
|
+
if (evt.name === "left" || evt.name === "h") {
|
|
523712
|
+
setTab((cur) => TABS5[(TABS5.indexOf(cur) - 1 + TABS5.length) % TABS5.length] ?? "Models");
|
|
523713
|
+
return;
|
|
523714
|
+
}
|
|
523715
|
+
if (evt.name === "e") {
|
|
523716
|
+
setEnabled(!enabled);
|
|
523717
|
+
return;
|
|
523718
|
+
}
|
|
523719
|
+
if (evt.name === "c") {
|
|
523720
|
+
clear();
|
|
523721
|
+
return;
|
|
523722
|
+
}
|
|
523723
|
+
});
|
|
523724
|
+
const aggregates = import_react136.useMemo(() => aggregateModelEvents(events), [events]);
|
|
523725
|
+
const errors4 = import_react136.useMemo(() => modelErrorEvents(events), [events]);
|
|
523726
|
+
const recent = import_react136.useMemo(() => [...events].reverse().slice(0, 200), [events]);
|
|
523727
|
+
if (!visible)
|
|
523728
|
+
return null;
|
|
523729
|
+
const sidebarTabs = TABS5.map((id) => ({
|
|
523730
|
+
id,
|
|
523731
|
+
label: id,
|
|
523732
|
+
icon: id === "Models" ? "model" : id === "Recent" ? "clock" : "error",
|
|
523733
|
+
status: id === "Errors" && errors4.length > 0 ? "error" : undefined
|
|
523734
|
+
}));
|
|
523735
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(PremiumPopup, {
|
|
523736
|
+
visible,
|
|
523737
|
+
width: popupW,
|
|
523738
|
+
height: popupH,
|
|
523739
|
+
title: "Model Events",
|
|
523740
|
+
titleIcon: icon("info"),
|
|
523741
|
+
tabs: sidebarTabs,
|
|
523742
|
+
activeTab: tab,
|
|
523743
|
+
sidebarWidth: 16,
|
|
523744
|
+
footerHints: [
|
|
523745
|
+
{ key: "e", label: enabled ? "Disable" : "Enable" },
|
|
523746
|
+
{ key: "c", label: "Clear" },
|
|
523747
|
+
{ key: "tab", label: "Switch tab" },
|
|
523748
|
+
{ key: "esc", label: "Close" }
|
|
523749
|
+
],
|
|
523750
|
+
children: [
|
|
523751
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
523752
|
+
flexDirection: "column",
|
|
523753
|
+
backgroundColor: t2.bgPopup,
|
|
523754
|
+
paddingX: 2,
|
|
523755
|
+
paddingY: 1,
|
|
523756
|
+
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
523757
|
+
flexDirection: "row",
|
|
523758
|
+
backgroundColor: t2.bgPopup,
|
|
523759
|
+
children: [
|
|
523760
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Toggle, {
|
|
523761
|
+
label: "Record model events",
|
|
523762
|
+
description: enabled ? "Capturing per-call latency, tokens, and errors." : "Off by default \u2014 press 'e' to enable. Clears on disable.",
|
|
523763
|
+
on: enabled
|
|
523764
|
+
}, undefined, false, undefined, this),
|
|
523765
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
523766
|
+
flexGrow: 1,
|
|
523767
|
+
backgroundColor: t2.bgPopup
|
|
523768
|
+
}, undefined, false, undefined, this),
|
|
523769
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
523770
|
+
bg: t2.bgPopup,
|
|
523771
|
+
fg: t2.textFaint,
|
|
523772
|
+
children: [
|
|
523773
|
+
String(events.length),
|
|
523774
|
+
" events"
|
|
523775
|
+
]
|
|
523776
|
+
}, undefined, true, undefined, this)
|
|
523777
|
+
]
|
|
523778
|
+
}, undefined, true, undefined, this)
|
|
523779
|
+
}, undefined, false, undefined, this),
|
|
523780
|
+
tab === "Models" ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Section, {
|
|
523781
|
+
title: "Per-model aggregates",
|
|
523782
|
+
bg: t2.bgPopup,
|
|
523783
|
+
children: aggregates.length === 0 ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
523784
|
+
bg: t2.bgPopup,
|
|
523785
|
+
fg: t2.textFaint,
|
|
523786
|
+
children: enabled ? "No model events recorded yet." : "Enable recording to see model usage."
|
|
523787
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Table, {
|
|
523788
|
+
width: contentW,
|
|
523789
|
+
columns: [
|
|
523790
|
+
{ key: "Model", render: (r4) => shortModel(r4.modelId) },
|
|
523791
|
+
{ key: "Calls", width: 5, align: "right", render: (r4) => String(r4.calls) },
|
|
523792
|
+
{
|
|
523793
|
+
key: "Err",
|
|
523794
|
+
width: 4,
|
|
523795
|
+
align: "right",
|
|
523796
|
+
render: (r4) => r4.errors > 0 ? String(r4.errors) : "\u2014"
|
|
523797
|
+
},
|
|
523798
|
+
{ key: "Avg", width: 6, align: "right", render: (r4) => fmtMs(r4.avgMs) },
|
|
523799
|
+
{ key: "Last", width: 6, align: "right", render: (r4) => fmtMs(r4.lastMs) },
|
|
523800
|
+
{ key: "In", width: 6, align: "right", render: (r4) => fmtTok(r4.input) },
|
|
523801
|
+
{ key: "Out", width: 6, align: "right", render: (r4) => fmtTok(r4.output) },
|
|
523802
|
+
{ key: "Cache", width: 6, align: "right", render: (r4) => fmtTok(r4.cacheRead) }
|
|
523803
|
+
],
|
|
523804
|
+
rows: aggregates,
|
|
523805
|
+
maxRows: Math.max(4, popupH - 12)
|
|
523806
|
+
}, undefined, false, undefined, this)
|
|
523807
|
+
}, undefined, false, undefined, this) : tab === "Recent" ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Section, {
|
|
523808
|
+
title: "Recent calls",
|
|
523809
|
+
bg: t2.bgPopup,
|
|
523810
|
+
children: recent.length === 0 ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
523811
|
+
bg: t2.bgPopup,
|
|
523812
|
+
fg: t2.textFaint,
|
|
523813
|
+
children: enabled ? "No recent calls." : "Enable recording first."
|
|
523814
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Table, {
|
|
523815
|
+
width: contentW,
|
|
523816
|
+
columns: [
|
|
523817
|
+
{ key: "When", width: 5, render: (r4) => fmtAge(now2, r4.startedAt) },
|
|
523818
|
+
{ key: "Source", width: 7, render: (r4) => r4.source },
|
|
523819
|
+
{ key: "Model", render: (r4) => shortModel(r4.modelId) },
|
|
523820
|
+
{
|
|
523821
|
+
key: "State",
|
|
523822
|
+
width: 5,
|
|
523823
|
+
render: (r4) => r4.state === "error" ? "err" : "ok"
|
|
523824
|
+
},
|
|
523825
|
+
{ key: "Time", width: 6, align: "right", render: (r4) => fmtMs(r4.durationMs) },
|
|
523826
|
+
{ key: "In", width: 6, align: "right", render: (r4) => fmtTok(r4.input ?? 0) },
|
|
523827
|
+
{ key: "Out", width: 6, align: "right", render: (r4) => fmtTok(r4.output ?? 0) }
|
|
523828
|
+
],
|
|
523829
|
+
rows: recent,
|
|
523830
|
+
maxRows: Math.max(4, popupH - 12)
|
|
523831
|
+
}, undefined, false, undefined, this)
|
|
523832
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Section, {
|
|
523833
|
+
title: "Errors",
|
|
523834
|
+
bg: t2.bgPopup,
|
|
523835
|
+
children: errors4.length === 0 ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
523836
|
+
bg: t2.bgPopup,
|
|
523837
|
+
fg: t2.textFaint,
|
|
523838
|
+
children: "No errors recorded."
|
|
523839
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
523840
|
+
flexDirection: "column",
|
|
523841
|
+
backgroundColor: t2.bgPopup,
|
|
523842
|
+
children: errors4.slice(-Math.max(4, popupH - 12)).reverse().map((ev) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
523843
|
+
flexDirection: "column",
|
|
523844
|
+
backgroundColor: t2.bgPopup,
|
|
523845
|
+
children: [
|
|
523846
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
523847
|
+
flexDirection: "row",
|
|
523848
|
+
backgroundColor: t2.bgPopup,
|
|
523849
|
+
children: [
|
|
523850
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
523851
|
+
bg: t2.bgPopup,
|
|
523852
|
+
fg: t2.error,
|
|
523853
|
+
attributes: BOLD18,
|
|
523854
|
+
children: shortModel(ev.modelId)
|
|
523855
|
+
}, undefined, false, undefined, this),
|
|
523856
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
523857
|
+
bg: t2.bgPopup,
|
|
523858
|
+
fg: t2.textFaint,
|
|
523859
|
+
children: [
|
|
523860
|
+
" ",
|
|
523861
|
+
ev.source,
|
|
523862
|
+
" \xB7 ",
|
|
523863
|
+
fmtAge(now2, ev.startedAt),
|
|
523864
|
+
" \xB7 ",
|
|
523865
|
+
fmtMs(ev.durationMs)
|
|
523866
|
+
]
|
|
523867
|
+
}, undefined, true, undefined, this)
|
|
523868
|
+
]
|
|
523869
|
+
}, undefined, true, undefined, this),
|
|
523870
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
523871
|
+
bg: t2.bgPopup,
|
|
523872
|
+
fg: t2.textPrimary,
|
|
523873
|
+
children: [
|
|
523874
|
+
" ",
|
|
523875
|
+
(ev.errorMessage ?? "").slice(0, contentW - 4)
|
|
523876
|
+
]
|
|
523877
|
+
}, undefined, true, undefined, this)
|
|
523878
|
+
]
|
|
523879
|
+
}, ev.id, true, undefined, this))
|
|
523880
|
+
}, undefined, false, undefined, this)
|
|
523881
|
+
}, undefined, false, undefined, this)
|
|
523882
|
+
]
|
|
523883
|
+
}, undefined, true, undefined, this);
|
|
523884
|
+
}
|
|
523885
|
+
var import_react136, BOLD18 = 1, TABS5;
|
|
523886
|
+
var init_ModelEventsPopup = __esm(async () => {
|
|
523887
|
+
init_icons();
|
|
523888
|
+
init_theme();
|
|
523889
|
+
init_model_events();
|
|
523890
|
+
init_ui2();
|
|
523891
|
+
init_jsx_dev_runtime();
|
|
523892
|
+
await init_react2();
|
|
523893
|
+
import_react136 = __toESM(require_react(), 1);
|
|
523894
|
+
TABS5 = ["Models", "Recent", "Errors"];
|
|
523895
|
+
});
|
|
523896
|
+
|
|
523472
523897
|
// src/components/settings/ProviderSettings.tsx
|
|
523473
523898
|
function readValuesFromLayer(layer) {
|
|
523474
523899
|
if (!layer)
|
|
@@ -523639,18 +524064,18 @@ function ProviderSettings({
|
|
|
523639
524064
|
const popupWidth = Math.min(MAX_POPUP_WIDTH4, Math.floor(termCols * 0.85));
|
|
523640
524065
|
const maxVisible = Math.max(6, Math.floor(containerRows * 0.85) - CHROME_ROWS6);
|
|
523641
524066
|
const t2 = useTheme();
|
|
523642
|
-
const [tab, setTab] =
|
|
523643
|
-
const [cursor, setCursor] =
|
|
523644
|
-
const [scope, setScope] =
|
|
524067
|
+
const [tab, setTab] = import_react138.useState("claude");
|
|
524068
|
+
const [cursor, setCursor] = import_react138.useState(0);
|
|
524069
|
+
const [scope, setScope] = import_react138.useState(() => detectInitialScope(projectConfig));
|
|
523645
524070
|
const vals = effectiveValues(globalConfig2, projectConfig);
|
|
523646
524071
|
const items = TAB_ITEMS[tab];
|
|
523647
|
-
const tabIdx =
|
|
524072
|
+
const tabIdx = TABS6.indexOf(tab);
|
|
523648
524073
|
const firstRowIdx = items.findIndex((i4) => i4.type !== "section" && i4.type !== "info");
|
|
523649
|
-
|
|
524074
|
+
import_react138.useEffect(() => {
|
|
523650
524075
|
if (visible)
|
|
523651
524076
|
setScope(detectInitialScope(projectConfig));
|
|
523652
524077
|
}, [visible, projectConfig]);
|
|
523653
|
-
|
|
524078
|
+
import_react138.useEffect(() => {
|
|
523654
524079
|
setCursor(Math.max(0, firstRowIdx));
|
|
523655
524080
|
}, [tab]);
|
|
523656
524081
|
const isBudgetDisabled = vals.thinkingMode !== "enabled";
|
|
@@ -523708,8 +524133,8 @@ function ProviderSettings({
|
|
|
523708
524133
|
}
|
|
523709
524134
|
if (evt.name === "tab" || evt.shift && evt.name === "tab") {
|
|
523710
524135
|
const dir = evt.shift ? -1 : 1;
|
|
523711
|
-
const next = (tabIdx + dir +
|
|
523712
|
-
setTab(
|
|
524136
|
+
const next = (tabIdx + dir + TABS6.length) % TABS6.length;
|
|
524137
|
+
setTab(TABS6[next]);
|
|
523713
524138
|
return;
|
|
523714
524139
|
}
|
|
523715
524140
|
if (evt.name === "up") {
|
|
@@ -523980,15 +524405,15 @@ function ProviderSettings({
|
|
|
523980
524405
|
]
|
|
523981
524406
|
}, undefined, true, undefined, this);
|
|
523982
524407
|
}
|
|
523983
|
-
var
|
|
524408
|
+
var import_react138, MAX_POPUP_WIDTH4 = 110, CHROME_ROWS6 = 10, TABS6, CLAUDE_ITEMS, OPENAI_ITEMS, GENERAL_ITEMS, GOOGLE_ITEMS, XAI_ITEMS, DEEPSEEK_ITEMS, OPENROUTER_ITEMS, COMPAT_ITEMS, TAB_ITEMS, DEFAULTS;
|
|
523984
524409
|
var init_ProviderSettings = __esm(async () => {
|
|
523985
524410
|
init_theme();
|
|
523986
524411
|
init_shared2();
|
|
523987
524412
|
init_ui2();
|
|
523988
524413
|
init_jsx_dev_runtime();
|
|
523989
524414
|
await init_react2();
|
|
523990
|
-
|
|
523991
|
-
|
|
524415
|
+
import_react138 = __toESM(require_react(), 1);
|
|
524416
|
+
TABS6 = [
|
|
523992
524417
|
"claude",
|
|
523993
524418
|
"openai",
|
|
523994
524419
|
"google",
|
|
@@ -524345,19 +524770,19 @@ function RepoMapStatusPopup({
|
|
|
524345
524770
|
const { width: termCols, height: termRows } = useTerminalDimensions();
|
|
524346
524771
|
const popupWidth = Math.min(POPUP_W, Math.floor(termCols * 0.8));
|
|
524347
524772
|
const innerW = popupWidth - 2;
|
|
524348
|
-
const stateRef =
|
|
524349
|
-
const [, setRenderTick] =
|
|
524350
|
-
const spinnerRef =
|
|
524773
|
+
const stateRef = import_react140.useRef(useRepoMapStore.getState());
|
|
524774
|
+
const [, setRenderTick] = import_react140.useState(0);
|
|
524775
|
+
const spinnerRef = import_react140.useRef(0);
|
|
524351
524776
|
const initialMode = currentMode ?? "off";
|
|
524352
524777
|
const initialLimit = currentLimit ?? 300;
|
|
524353
|
-
const [selectedMode, setSelectedMode] =
|
|
524354
|
-
const [selectedLimit, setSelectedLimit] =
|
|
524355
|
-
const [selectedAutoRegen, setSelectedAutoRegen] =
|
|
524356
|
-
const [selectedTokenBudget, setSelectedTokenBudget] =
|
|
524357
|
-
const [selectedScope, setSelectedScope] =
|
|
524358
|
-
const [focusRow, setFocusRow] =
|
|
524359
|
-
const [confirmClear, setConfirmClear] =
|
|
524360
|
-
|
|
524778
|
+
const [selectedMode, setSelectedMode] = import_react140.useState(initialMode);
|
|
524779
|
+
const [selectedLimit, setSelectedLimit] = import_react140.useState(initialLimit);
|
|
524780
|
+
const [selectedAutoRegen, setSelectedAutoRegen] = import_react140.useState(currentAutoRegen ?? false);
|
|
524781
|
+
const [selectedTokenBudget, setSelectedTokenBudget] = import_react140.useState(currentTokenBudget);
|
|
524782
|
+
const [selectedScope, setSelectedScope] = import_react140.useState(currentScope ?? "project");
|
|
524783
|
+
const [focusRow, setFocusRow] = import_react140.useState(0 /* Mode */);
|
|
524784
|
+
const [confirmClear, setConfirmClear] = import_react140.useState(false);
|
|
524785
|
+
import_react140.useEffect(() => {
|
|
524361
524786
|
if (!visible)
|
|
524362
524787
|
return;
|
|
524363
524788
|
setSelectedMode(currentMode ?? "off");
|
|
@@ -524368,7 +524793,7 @@ function RepoMapStatusPopup({
|
|
|
524368
524793
|
setFocusRow(0 /* Mode */);
|
|
524369
524794
|
setConfirmClear(false);
|
|
524370
524795
|
}, [visible, currentMode, currentLimit, currentAutoRegen, currentTokenBudget, currentScope]);
|
|
524371
|
-
|
|
524796
|
+
import_react140.useEffect(() => {
|
|
524372
524797
|
if (!visible)
|
|
524373
524798
|
return;
|
|
524374
524799
|
stateRef.current = useRepoMapStore.getState();
|
|
@@ -524378,7 +524803,7 @@ function RepoMapStatusPopup({
|
|
|
524378
524803
|
setRenderTick((n) => n + 1);
|
|
524379
524804
|
});
|
|
524380
524805
|
}, [visible]);
|
|
524381
|
-
|
|
524806
|
+
import_react140.useEffect(() => {
|
|
524382
524807
|
if (!visible)
|
|
524383
524808
|
return;
|
|
524384
524809
|
const timer = setInterval(() => {
|
|
@@ -524736,7 +525161,7 @@ function RepoMapStatusPopup({
|
|
|
524736
525161
|
}, undefined, true, undefined, this)
|
|
524737
525162
|
}, undefined, false, undefined, this);
|
|
524738
525163
|
}
|
|
524739
|
-
var
|
|
525164
|
+
var import_react140, LABEL_W = 18, POPUP_W = 72, SEMANTIC_MODES, MODE_DESCRIPTIONS, MODE_LABELS2, LLM_LIMIT_PRESETS, TOKEN_BUDGET_PRESETS;
|
|
524740
525165
|
var init_RepoMapStatusPopup = __esm(async () => {
|
|
524741
525166
|
init_theme();
|
|
524742
525167
|
init_repomap();
|
|
@@ -524744,7 +525169,7 @@ var init_RepoMapStatusPopup = __esm(async () => {
|
|
|
524744
525169
|
init_ui2();
|
|
524745
525170
|
init_jsx_dev_runtime();
|
|
524746
525171
|
await init_react2();
|
|
524747
|
-
|
|
525172
|
+
import_react140 = __toESM(require_react(), 1);
|
|
524748
525173
|
SEMANTIC_MODES = ["off", "ast", "synthetic", "llm", "full"];
|
|
524749
525174
|
MODE_DESCRIPTIONS = {
|
|
524750
525175
|
off: "disabled",
|
|
@@ -524775,24 +525200,57 @@ function truncate4(s2, max) {
|
|
|
524775
525200
|
function RouterSettings({
|
|
524776
525201
|
visible,
|
|
524777
525202
|
router: router2,
|
|
525203
|
+
defaultModel,
|
|
525204
|
+
modelFallback,
|
|
524778
525205
|
activeModel,
|
|
524779
525206
|
scope,
|
|
524780
525207
|
onScopeChange,
|
|
524781
525208
|
onPickSlot,
|
|
524782
525209
|
onClearSlot,
|
|
524783
525210
|
onPickerChange,
|
|
525211
|
+
onAddFallback,
|
|
525212
|
+
onClearFallbacks,
|
|
524784
525213
|
onClose
|
|
524785
525214
|
}) {
|
|
524786
525215
|
const t2 = useTheme();
|
|
524787
525216
|
const { width: tw2, height: th } = useTerminalDimensions();
|
|
524788
|
-
const [cursor, setCursor] =
|
|
525217
|
+
const [cursor, setCursor] = import_react142.useState(0);
|
|
524789
525218
|
const popupW = Math.min(100, Math.max(72, Math.floor(tw2 * 0.78)));
|
|
524790
525219
|
const popupH = Math.min(40, Math.max(26, th - 4));
|
|
524791
525220
|
const contentW = popupW - 4;
|
|
524792
|
-
const
|
|
525221
|
+
const modelsInUse = import_react142.useMemo(() => {
|
|
525222
|
+
const set3 = new Set;
|
|
525223
|
+
if (defaultModel)
|
|
525224
|
+
set3.add(defaultModel);
|
|
525225
|
+
if (router2) {
|
|
525226
|
+
const keys2 = [
|
|
525227
|
+
"default",
|
|
525228
|
+
"spark",
|
|
525229
|
+
"ember",
|
|
525230
|
+
"webSearch",
|
|
525231
|
+
"desloppify",
|
|
525232
|
+
"verify",
|
|
525233
|
+
"compact",
|
|
525234
|
+
"semantic"
|
|
525235
|
+
];
|
|
525236
|
+
for (const k5 of keys2) {
|
|
525237
|
+
const v4 = router2[k5];
|
|
525238
|
+
if (typeof v4 === "string" && v4.trim())
|
|
525239
|
+
set3.add(v4);
|
|
525240
|
+
}
|
|
525241
|
+
}
|
|
525242
|
+
return Array.from(set3);
|
|
525243
|
+
}, [router2, defaultModel]);
|
|
525244
|
+
const rows = import_react142.useMemo(() => {
|
|
524793
525245
|
const out2 = [];
|
|
524794
525246
|
for (const s2 of SECTIONS) {
|
|
524795
525247
|
out2.push({ kind: "header", section: s2 });
|
|
525248
|
+
if (s2.id === "fallback") {
|
|
525249
|
+
for (const modelId of modelsInUse) {
|
|
525250
|
+
out2.push({ kind: "fallback", section: s2, modelId });
|
|
525251
|
+
}
|
|
525252
|
+
continue;
|
|
525253
|
+
}
|
|
524796
525254
|
for (const d3 of s2.defs) {
|
|
524797
525255
|
if (d3.kind === "slot")
|
|
524798
525256
|
out2.push({ kind: "slot", section: s2, def: d3 });
|
|
@@ -524801,8 +525259,8 @@ function RouterSettings({
|
|
|
524801
525259
|
}
|
|
524802
525260
|
}
|
|
524803
525261
|
return out2;
|
|
524804
|
-
}, []);
|
|
524805
|
-
const selectableIndices =
|
|
525262
|
+
}, [modelsInUse]);
|
|
525263
|
+
const selectableIndices = import_react142.useMemo(() => rows.map((r4, i4) => r4.kind === "header" ? -1 : i4).filter((i4) => i4 >= 0), [rows]);
|
|
524806
525264
|
const moveItem = (dir) => {
|
|
524807
525265
|
if (selectableIndices.length === 0)
|
|
524808
525266
|
return;
|
|
@@ -524811,7 +525269,7 @@ function RouterSettings({
|
|
|
524811
525269
|
const nextPos = (base + dir + selectableIndices.length) % selectableIndices.length;
|
|
524812
525270
|
setCursor(selectableIndices[nextPos] ?? selectableIndices[0] ?? 0);
|
|
524813
525271
|
};
|
|
524814
|
-
|
|
525272
|
+
import_react142.useMemo(() => {
|
|
524815
525273
|
if (cursor === 0 && selectableIndices.length > 0 && selectableIndices[0] !== 0) {
|
|
524816
525274
|
setCursor(selectableIndices[0] ?? 0);
|
|
524817
525275
|
}
|
|
@@ -524819,6 +525277,7 @@ function RouterSettings({
|
|
|
524819
525277
|
const selectedRow = rows[cursor];
|
|
524820
525278
|
const selectedSlot = selectedRow?.kind === "slot" ? selectedRow.def : null;
|
|
524821
525279
|
const selectedPicker = selectedRow?.kind === "picker" ? selectedRow.def : null;
|
|
525280
|
+
const selectedFallbackModelId = selectedRow?.kind === "fallback" ? selectedRow.modelId : null;
|
|
524822
525281
|
useKeyboard((evt) => {
|
|
524823
525282
|
if (!visible)
|
|
524824
525283
|
return;
|
|
@@ -524835,12 +525294,16 @@ function RouterSettings({
|
|
|
524835
525294
|
return;
|
|
524836
525295
|
}
|
|
524837
525296
|
if (evt.name === "return") {
|
|
524838
|
-
if (
|
|
525297
|
+
if (selectedFallbackModelId)
|
|
525298
|
+
onAddFallback(selectedFallbackModelId);
|
|
525299
|
+
else if (selectedSlot)
|
|
524839
525300
|
onPickSlot(selectedSlot.key);
|
|
524840
525301
|
return;
|
|
524841
525302
|
}
|
|
524842
525303
|
if (evt.name === "d" || evt.name === "delete" || evt.name === "backspace") {
|
|
524843
|
-
if (
|
|
525304
|
+
if (selectedFallbackModelId)
|
|
525305
|
+
onClearFallbacks(selectedFallbackModelId);
|
|
525306
|
+
else if (selectedSlot)
|
|
524844
525307
|
onClearSlot(selectedSlot.key);
|
|
524845
525308
|
return;
|
|
524846
525309
|
}
|
|
@@ -524872,7 +525335,7 @@ function RouterSettings({
|
|
|
524872
525335
|
const labelCol = 12;
|
|
524873
525336
|
const modelCol = Math.min(30, Math.max(18, Math.floor(contentW * 0.32)));
|
|
524874
525337
|
const descCol = Math.max(8, contentW - 4 - labelCol - modelCol - 2);
|
|
524875
|
-
const
|
|
525338
|
+
const shortModel2 = (m5) => {
|
|
524876
525339
|
const slash = m5.indexOf("/");
|
|
524877
525340
|
return slash > 0 ? m5.slice(slash + 1) : m5;
|
|
524878
525341
|
};
|
|
@@ -524882,7 +525345,7 @@ function RouterSettings({
|
|
|
524882
525345
|
height: popupH,
|
|
524883
525346
|
title: "Task Router",
|
|
524884
525347
|
titleIcon: "router",
|
|
524885
|
-
blurb: `${customCount}/${slotCount} set \xB7 ${scope} \xB7 default: ${
|
|
525348
|
+
blurb: `${customCount}/${slotCount} set \xB7 ${scope} \xB7 default: ${shortModel2(activeModel)}`,
|
|
524886
525349
|
footerHints: [
|
|
524887
525350
|
{ key: "\u2191\u2193", label: "nav" },
|
|
524888
525351
|
{ key: "Enter", label: "set" },
|
|
@@ -524908,13 +525371,56 @@ function RouterSettings({
|
|
|
524908
525371
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
524909
525372
|
bg: t2.bgPopup,
|
|
524910
525373
|
fg: t2.brandAlt,
|
|
524911
|
-
attributes:
|
|
525374
|
+
attributes: BOLD19,
|
|
524912
525375
|
children: row.section.title
|
|
524913
525376
|
}, undefined, false, undefined, this)
|
|
524914
525377
|
]
|
|
524915
525378
|
}, `h-${idx}`, true, undefined, this);
|
|
524916
525379
|
}
|
|
524917
525380
|
const isSelected = idx === cursor;
|
|
525381
|
+
if (row.kind === "fallback") {
|
|
525382
|
+
const rowBg2 = isSelected ? t2.bgPopupHighlight : t2.bgPopup;
|
|
525383
|
+
const fbs = modelFallback?.[row.modelId] ?? [];
|
|
525384
|
+
const fallbackLabelCol = Math.min(28, Math.max(18, Math.floor(contentW * 0.32)));
|
|
525385
|
+
const label2 = truncate4(shortModel2(row.modelId), fallbackLabelCol).padEnd(fallbackLabelCol).slice(0, fallbackLabelCol);
|
|
525386
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
525387
|
+
flexDirection: "row",
|
|
525388
|
+
height: 1,
|
|
525389
|
+
backgroundColor: rowBg2,
|
|
525390
|
+
children: [
|
|
525391
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
525392
|
+
bg: rowBg2,
|
|
525393
|
+
fg: isSelected ? t2.brandSecondary : t2.textFaint,
|
|
525394
|
+
attributes: BOLD19,
|
|
525395
|
+
children: isSelected ? "\u25B8 " : " "
|
|
525396
|
+
}, undefined, false, undefined, this),
|
|
525397
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
525398
|
+
bg: rowBg2,
|
|
525399
|
+
fg: t2.textPrimary,
|
|
525400
|
+
attributes: BOLD19,
|
|
525401
|
+
children: label2
|
|
525402
|
+
}, undefined, false, undefined, this),
|
|
525403
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
525404
|
+
flexGrow: 1,
|
|
525405
|
+
backgroundColor: rowBg2
|
|
525406
|
+
}, undefined, false, undefined, this),
|
|
525407
|
+
fbs.length > 0 ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
525408
|
+
bg: rowBg2,
|
|
525409
|
+
fg: t2.brandAlt,
|
|
525410
|
+
attributes: BOLD19,
|
|
525411
|
+
children: truncate4(`\u2192 ${fbs.map((m5) => shortModel2(m5)).join(", ")}`, Math.max(8, contentW - 4 - fallbackLabelCol - 2))
|
|
525412
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
525413
|
+
bg: rowBg2,
|
|
525414
|
+
fg: t2.textDim,
|
|
525415
|
+
children: "\u2014"
|
|
525416
|
+
}, undefined, false, undefined, this),
|
|
525417
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
525418
|
+
bg: rowBg2,
|
|
525419
|
+
children: " "
|
|
525420
|
+
}, undefined, false, undefined, this)
|
|
525421
|
+
]
|
|
525422
|
+
}, `f-${idx}`, true, undefined, this);
|
|
525423
|
+
}
|
|
524918
525424
|
if (row.kind === "picker") {
|
|
524919
525425
|
const cur = router2?.[row.def.key];
|
|
524920
525426
|
const num = typeof cur === "number" ? cur : row.def.defaultValue;
|
|
@@ -524940,13 +525446,13 @@ function RouterSettings({
|
|
|
524940
525446
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
524941
525447
|
bg: rowBg,
|
|
524942
525448
|
fg: isSelected ? t2.brandSecondary : t2.textFaint,
|
|
524943
|
-
attributes:
|
|
525449
|
+
attributes: BOLD19,
|
|
524944
525450
|
children: isSelected ? "\u25B8 " : " "
|
|
524945
525451
|
}, undefined, false, undefined, this),
|
|
524946
525452
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
524947
525453
|
bg: rowBg,
|
|
524948
525454
|
fg: t2.textPrimary,
|
|
524949
|
-
attributes:
|
|
525455
|
+
attributes: BOLD19,
|
|
524950
525456
|
children: label
|
|
524951
525457
|
}, undefined, false, undefined, this),
|
|
524952
525458
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
@@ -524961,8 +525467,8 @@ function RouterSettings({
|
|
|
524961
525467
|
modelId ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
524962
525468
|
bg: rowBg,
|
|
524963
525469
|
fg: t2.brandAlt,
|
|
524964
|
-
attributes:
|
|
524965
|
-
children: truncate4(
|
|
525470
|
+
attributes: BOLD19,
|
|
525471
|
+
children: truncate4(shortModel2(modelId), modelCol)
|
|
524966
525472
|
}, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
524967
525473
|
bg: rowBg,
|
|
524968
525474
|
fg: t2.textDim,
|
|
@@ -524987,14 +525493,14 @@ function RouterSettings({
|
|
|
524987
525493
|
}, undefined, true, undefined, this)
|
|
524988
525494
|
}, undefined, false, undefined, this);
|
|
524989
525495
|
}
|
|
524990
|
-
var
|
|
525496
|
+
var import_react142, BOLD19 = 1, SECTIONS, ALL_DEFS;
|
|
524991
525497
|
var init_RouterSettings = __esm(async () => {
|
|
524992
525498
|
init_theme();
|
|
524993
525499
|
init_shared2();
|
|
524994
525500
|
init_ui2();
|
|
524995
525501
|
init_jsx_dev_runtime();
|
|
524996
525502
|
await init_react2();
|
|
524997
|
-
|
|
525503
|
+
import_react142 = __toESM(require_react(), 1);
|
|
524998
525504
|
SECTIONS = [
|
|
524999
525505
|
{
|
|
525000
525506
|
id: "main",
|
|
@@ -525066,6 +525572,12 @@ var init_RouterSettings = __esm(async () => {
|
|
|
525066
525572
|
hint: "Symbol summaries"
|
|
525067
525573
|
}
|
|
525068
525574
|
]
|
|
525575
|
+
},
|
|
525576
|
+
{
|
|
525577
|
+
id: "fallback",
|
|
525578
|
+
title: "Model Fallback",
|
|
525579
|
+
subtitle: "Per-model fallback chains for transient errors",
|
|
525580
|
+
defs: []
|
|
525069
525581
|
}
|
|
525070
525582
|
];
|
|
525071
525583
|
ALL_DEFS = SECTIONS.flatMap((s2) => s2.defs);
|
|
@@ -525207,25 +525719,25 @@ function SkillSearch({ visible, contextManager, onClose, onSystemMessage }) {
|
|
|
525207
525719
|
const t2 = useTheme();
|
|
525208
525720
|
const popupBg = t2.bgPopup;
|
|
525209
525721
|
const popupHl = t2.bgPopupHighlight;
|
|
525210
|
-
const [tab, setTab] =
|
|
525211
|
-
const [query2, setQuery] =
|
|
525212
|
-
const [popular, setPopular] =
|
|
525213
|
-
const [results, setResults] =
|
|
525214
|
-
const [installed2, setInstalled] =
|
|
525215
|
-
const [activeSkills, setActiveSkills] =
|
|
525216
|
-
const [searching, setSearching] =
|
|
525217
|
-
const [installing, setInstalling] =
|
|
525218
|
-
const [pendingInstall, setPendingInstall] =
|
|
525219
|
-
const [scopeCursor, setScopeCursor] =
|
|
525220
|
-
const debounceRef =
|
|
525722
|
+
const [tab, setTab] = import_react144.useState("search");
|
|
525723
|
+
const [query2, setQuery] = import_react144.useState("");
|
|
525724
|
+
const [popular, setPopular] = import_react144.useState([]);
|
|
525725
|
+
const [results, setResults] = import_react144.useState([]);
|
|
525726
|
+
const [installed2, setInstalled] = import_react144.useState([]);
|
|
525727
|
+
const [activeSkills, setActiveSkills] = import_react144.useState([]);
|
|
525728
|
+
const [searching, setSearching] = import_react144.useState(false);
|
|
525729
|
+
const [installing, setInstalling] = import_react144.useState(false);
|
|
525730
|
+
const [pendingInstall, setPendingInstall] = import_react144.useState(null);
|
|
525731
|
+
const [scopeCursor, setScopeCursor] = import_react144.useState(0);
|
|
525732
|
+
const debounceRef = import_react144.useRef(null);
|
|
525221
525733
|
const isInProject = existsSync50(join58(process.cwd(), ".git"));
|
|
525222
525734
|
const { width: termCols, height: termRows } = useTerminalDimensions();
|
|
525223
525735
|
const containerRows = termRows - 2;
|
|
525224
525736
|
const popupWidth = Math.min(MAX_POPUP_WIDTH5, Math.floor(termCols * 0.88));
|
|
525225
525737
|
const maxVisible = Math.max(4, Math.floor(containerRows * 0.85) - CHROME_ROWS7);
|
|
525226
525738
|
const contentW = popupWidth - 22 - 3;
|
|
525227
|
-
const [cursor, setCursor] =
|
|
525228
|
-
const resetScroll =
|
|
525739
|
+
const [cursor, setCursor] = import_react144.useState(0);
|
|
525740
|
+
const resetScroll = import_react144.useCallback(() => setCursor(0), []);
|
|
525229
525741
|
const filterQuery = query2.toLowerCase().trim();
|
|
525230
525742
|
const installedNames = new Set(installed2.map((s2) => s2.name));
|
|
525231
525743
|
const filteredInstalled = filterQuery ? installed2.filter((s2) => s2.name.toLowerCase().includes(filterQuery)) : installed2;
|
|
@@ -525238,13 +525750,13 @@ function SkillSearch({ visible, contextManager, onClose, onSystemMessage }) {
|
|
|
525238
525750
|
return filteredInstalled.length;
|
|
525239
525751
|
return filteredActive.length;
|
|
525240
525752
|
})();
|
|
525241
|
-
const refreshInstalled =
|
|
525753
|
+
const refreshInstalled = import_react144.useCallback(() => {
|
|
525242
525754
|
setInstalled(listInstalledSkills());
|
|
525243
525755
|
}, []);
|
|
525244
|
-
const refreshActive =
|
|
525756
|
+
const refreshActive = import_react144.useCallback(() => {
|
|
525245
525757
|
setActiveSkills(contextManager.getActiveSkills());
|
|
525246
525758
|
}, [contextManager]);
|
|
525247
|
-
|
|
525759
|
+
import_react144.useEffect(() => {
|
|
525248
525760
|
if (visible) {
|
|
525249
525761
|
setTab("search");
|
|
525250
525762
|
setQuery("");
|
|
@@ -525255,7 +525767,7 @@ function SkillSearch({ visible, contextManager, onClose, onSystemMessage }) {
|
|
|
525255
525767
|
listPopularSkills().then((r4) => setPopular(r4)).catch(() => {});
|
|
525256
525768
|
}
|
|
525257
525769
|
}, [visible, refreshActive, refreshInstalled]);
|
|
525258
|
-
|
|
525770
|
+
import_react144.useEffect(() => {
|
|
525259
525771
|
if (!visible || tab !== "search")
|
|
525260
525772
|
return;
|
|
525261
525773
|
if (debounceRef.current)
|
|
@@ -525281,7 +525793,7 @@ function SkillSearch({ visible, contextManager, onClose, onSystemMessage }) {
|
|
|
525281
525793
|
clearTimeout(debounceRef.current);
|
|
525282
525794
|
};
|
|
525283
525795
|
}, [query2, visible, tab, popular.length]);
|
|
525284
|
-
|
|
525796
|
+
import_react144.useEffect(() => {
|
|
525285
525797
|
setQuery("");
|
|
525286
525798
|
setResults([]);
|
|
525287
525799
|
resetScroll();
|
|
@@ -525357,8 +525869,8 @@ function SkillSearch({ visible, contextManager, onClose, onSystemMessage }) {
|
|
|
525357
525869
|
return;
|
|
525358
525870
|
}
|
|
525359
525871
|
if (evt.name === "tab") {
|
|
525360
|
-
const idx =
|
|
525361
|
-
const next =
|
|
525872
|
+
const idx = TABS7.indexOf(tab);
|
|
525873
|
+
const next = TABS7[(idx + 1) % TABS7.length];
|
|
525362
525874
|
setTab(next);
|
|
525363
525875
|
return;
|
|
525364
525876
|
}
|
|
@@ -525596,7 +526108,7 @@ function SkillSearch({ visible, contextManager, onClose, onSystemMessage }) {
|
|
|
525596
526108
|
]
|
|
525597
526109
|
}, undefined, true, undefined, this);
|
|
525598
526110
|
}
|
|
525599
|
-
var
|
|
526111
|
+
var import_react144, MAX_POPUP_WIDTH5 = 120, CHROME_ROWS7 = 9, TABS7;
|
|
525600
526112
|
var init_SkillSearch = __esm(async () => {
|
|
525601
526113
|
init_manager3();
|
|
525602
526114
|
init_theme();
|
|
@@ -525606,22 +526118,22 @@ var init_SkillSearch = __esm(async () => {
|
|
|
525606
526118
|
init_core4(),
|
|
525607
526119
|
init_react2()
|
|
525608
526120
|
]);
|
|
525609
|
-
|
|
525610
|
-
|
|
526121
|
+
import_react144 = __toESM(require_react(), 1);
|
|
526122
|
+
TABS7 = ["search", "installed", "active"];
|
|
525611
526123
|
});
|
|
525612
526124
|
|
|
525613
526125
|
// src/components/settings/ToolsPopup.tsx
|
|
525614
526126
|
function ToolsPopup({ visible, disabledTools, onToggleTool, onClose }) {
|
|
525615
526127
|
const { width: tw2, height: th } = useTerminalDimensions();
|
|
525616
|
-
const [cursor, setCursor] =
|
|
525617
|
-
|
|
526128
|
+
const [cursor, setCursor] = import_react146.useState(0);
|
|
526129
|
+
import_react146.useEffect(() => {
|
|
525618
526130
|
if (visible)
|
|
525619
526131
|
setCursor(0);
|
|
525620
526132
|
}, [visible]);
|
|
525621
526133
|
const popupW = Math.min(110, Math.max(72, tw2 - 4));
|
|
525622
526134
|
const popupH = Math.min(32, Math.max(16, th - 4));
|
|
525623
526135
|
const contentW = popupW - 4;
|
|
525624
|
-
const groups =
|
|
526136
|
+
const groups = import_react146.useMemo(() => {
|
|
525625
526137
|
return [
|
|
525626
526138
|
{
|
|
525627
526139
|
id: "tools",
|
|
@@ -525637,7 +526149,7 @@ function ToolsPopup({ visible, disabledTools, onToggleTool, onClose }) {
|
|
|
525637
526149
|
}
|
|
525638
526150
|
];
|
|
525639
526151
|
}, [disabledTools]);
|
|
525640
|
-
const rows =
|
|
526152
|
+
const rows = import_react146.useMemo(() => buildGroupedRows(groups, new Set(["tools"])), [groups]);
|
|
525641
526153
|
useKeyboard((evt) => {
|
|
525642
526154
|
if (!visible)
|
|
525643
526155
|
return;
|
|
@@ -525681,13 +526193,13 @@ function ToolsPopup({ visible, disabledTools, onToggleTool, onClose }) {
|
|
|
525681
526193
|
}, undefined, false, undefined, this)
|
|
525682
526194
|
}, undefined, false, undefined, this);
|
|
525683
526195
|
}
|
|
525684
|
-
var
|
|
526196
|
+
var import_react146;
|
|
525685
526197
|
var init_ToolsPopup = __esm(async () => {
|
|
525686
526198
|
init_constants();
|
|
525687
526199
|
init_ui2();
|
|
525688
526200
|
init_jsx_dev_runtime();
|
|
525689
526201
|
await init_react2();
|
|
525690
|
-
|
|
526202
|
+
import_react146 = __toESM(require_react(), 1);
|
|
525691
526203
|
});
|
|
525692
526204
|
|
|
525693
526205
|
// src/components/modals/MemoryBrowser.tsx
|
|
@@ -525734,29 +526246,29 @@ function MemoryBrowser({ visible, contextManager, cwd: cwd2, onClose, onSystemMe
|
|
|
525734
526246
|
const popupH = Math.min(36, Math.max(20, th - 4));
|
|
525735
526247
|
const SIDEBAR_W4 = 22;
|
|
525736
526248
|
const contentW = popupW - SIDEBAR_W4 - 9;
|
|
525737
|
-
const [tab, setTab] =
|
|
525738
|
-
const [query2, setQuery] =
|
|
525739
|
-
const [cursor, setCursor] =
|
|
525740
|
-
const [generation, setGeneration] =
|
|
525741
|
-
const [flash, setFlash] =
|
|
525742
|
-
const [confirmPurge, setConfirmPurge] =
|
|
525743
|
-
const [cleanupRows, setCleanupRows] =
|
|
525744
|
-
const [cleanupSelected, setCleanupSelected] =
|
|
525745
|
-
const [settingsModal, setSettingsModal] =
|
|
525746
|
-
const cursorRef =
|
|
526249
|
+
const [tab, setTab] = import_react148.useState("All");
|
|
526250
|
+
const [query2, setQuery] = import_react148.useState("");
|
|
526251
|
+
const [cursor, setCursor] = import_react148.useState(0);
|
|
526252
|
+
const [generation, setGeneration] = import_react148.useState(0);
|
|
526253
|
+
const [flash, setFlash] = import_react148.useState(null);
|
|
526254
|
+
const [confirmPurge, setConfirmPurge] = import_react148.useState(false);
|
|
526255
|
+
const [cleanupRows, setCleanupRows] = import_react148.useState([]);
|
|
526256
|
+
const [cleanupSelected, setCleanupSelected] = import_react148.useState(new Map);
|
|
526257
|
+
const [settingsModal, setSettingsModal] = import_react148.useState(null);
|
|
526258
|
+
const cursorRef = import_react148.useRef(0);
|
|
525747
526259
|
cursorRef.current = cursor;
|
|
525748
|
-
const popFlash =
|
|
526260
|
+
const popFlash = import_react148.useCallback((kind, message) => {
|
|
525749
526261
|
setFlash({ kind, message });
|
|
525750
526262
|
setTimeout(() => setFlash(null), 1800);
|
|
525751
526263
|
}, []);
|
|
525752
|
-
const fileExists =
|
|
526264
|
+
const fileExists = import_react148.useCallback((p2) => {
|
|
525753
526265
|
try {
|
|
525754
526266
|
return existsSync51(join59(cwd2, p2));
|
|
525755
526267
|
} catch {
|
|
525756
526268
|
return false;
|
|
525757
526269
|
}
|
|
525758
526270
|
}, [cwd2]);
|
|
525759
|
-
const refreshCleanup =
|
|
526271
|
+
const refreshCleanup = import_react148.useCallback(() => {
|
|
525760
526272
|
const dupes = memMgr.findDuplicates("all");
|
|
525761
526273
|
const dead = memMgr.findDeadFileRefs("all", fileExists);
|
|
525762
526274
|
const stale = memMgr.staleCandidates("all", 25);
|
|
@@ -525830,7 +526342,7 @@ function MemoryBrowser({ visible, contextManager, cwd: cwd2, onClose, onSystemMe
|
|
|
525830
526342
|
setCleanupRows(rows);
|
|
525831
526343
|
setCleanupSelected(new Map);
|
|
525832
526344
|
}, [memMgr, fileExists]);
|
|
525833
|
-
|
|
526345
|
+
import_react148.useEffect(() => {
|
|
525834
526346
|
if (!visible)
|
|
525835
526347
|
return;
|
|
525836
526348
|
setQuery("");
|
|
@@ -525841,13 +526353,13 @@ function MemoryBrowser({ visible, contextManager, cwd: cwd2, onClose, onSystemMe
|
|
|
525841
526353
|
if (tab === "Cleanup")
|
|
525842
526354
|
refreshCleanup();
|
|
525843
526355
|
}, [visible, tab, refreshCleanup]);
|
|
525844
|
-
const allRows =
|
|
526356
|
+
const allRows = import_react148.useMemo(() => {
|
|
525845
526357
|
return memMgr.list("all", { includeHidden: false }).map(toRow2);
|
|
525846
526358
|
}, [memMgr, generation]);
|
|
525847
|
-
const hiddenRows =
|
|
526359
|
+
const hiddenRows = import_react148.useMemo(() => {
|
|
525848
526360
|
return memMgr.list("all", { includeHidden: true }).filter((m5) => m5.hidden).map(toRow2);
|
|
525849
526361
|
}, [memMgr, generation]);
|
|
525850
|
-
const filteredRows =
|
|
526362
|
+
const filteredRows = import_react148.useMemo(() => {
|
|
525851
526363
|
const source = tab === "Hidden" ? hiddenRows : allRows;
|
|
525852
526364
|
const fq = query2.toLowerCase().trim();
|
|
525853
526365
|
if (!fq)
|
|
@@ -525857,7 +526369,7 @@ function MemoryBrowser({ visible, contextManager, cwd: cwd2, onClose, onSystemMe
|
|
|
525857
526369
|
return hay.toLowerCase().includes(fq);
|
|
525858
526370
|
});
|
|
525859
526371
|
}, [allRows, hiddenRows, tab, query2]);
|
|
525860
|
-
const settingsGroups =
|
|
526372
|
+
const settingsGroups = import_react148.useMemo(() => {
|
|
525861
526373
|
const scopeCfg = memMgr.scopeConfig;
|
|
525862
526374
|
return [
|
|
525863
526375
|
{
|
|
@@ -525887,8 +526399,8 @@ function MemoryBrowser({ visible, contextManager, cwd: cwd2, onClose, onSystemMe
|
|
|
525887
526399
|
}
|
|
525888
526400
|
];
|
|
525889
526401
|
}, [memMgr]);
|
|
525890
|
-
const settingsRows =
|
|
525891
|
-
|
|
526402
|
+
const settingsRows = import_react148.useMemo(() => buildGroupedRows(settingsGroups, new Set(["scopes"])), [settingsGroups]);
|
|
526403
|
+
import_react148.useEffect(() => {
|
|
525892
526404
|
let len;
|
|
525893
526405
|
if (tab === "Cleanup")
|
|
525894
526406
|
len = cleanupRows.length + 1;
|
|
@@ -526027,9 +526539,9 @@ function MemoryBrowser({ visible, contextManager, cwd: cwd2, onClose, onSystemMe
|
|
|
526027
526539
|
return;
|
|
526028
526540
|
}
|
|
526029
526541
|
if (evt.name === "tab") {
|
|
526030
|
-
const idx =
|
|
526542
|
+
const idx = TABS8.indexOf(tab);
|
|
526031
526543
|
const dir = evt.shift ? -1 : 1;
|
|
526032
|
-
const next =
|
|
526544
|
+
const next = TABS8[(idx + dir + TABS8.length) % TABS8.length];
|
|
526033
526545
|
if (next)
|
|
526034
526546
|
setTab(next);
|
|
526035
526547
|
setCursor(0);
|
|
@@ -526365,13 +526877,13 @@ function MemoryBrowser({ visible, contextManager, cwd: cwd2, onClose, onSystemMe
|
|
|
526365
526877
|
}, undefined, true, undefined, this)
|
|
526366
526878
|
}, undefined, false, undefined, this);
|
|
526367
526879
|
}
|
|
526368
|
-
var
|
|
526880
|
+
var import_react148, SETTINGS_MODAL_TITLE, SETTINGS_MODAL_OPTIONS, TABS8, COLUMNS2, CLEANUP_COLUMNS;
|
|
526369
526881
|
var init_MemoryBrowser = __esm(async () => {
|
|
526370
526882
|
init_theme();
|
|
526371
526883
|
init_ui2();
|
|
526372
526884
|
init_jsx_dev_runtime();
|
|
526373
526885
|
await init_react2();
|
|
526374
|
-
|
|
526886
|
+
import_react148 = __toESM(require_react(), 1);
|
|
526375
526887
|
SETTINGS_MODAL_TITLE = {
|
|
526376
526888
|
write: "Write Scope",
|
|
526377
526889
|
read: "Read Scope",
|
|
@@ -526394,7 +526906,7 @@ var init_MemoryBrowser = __esm(async () => {
|
|
|
526394
526906
|
{ value: "global", label: "Global", description: "preferences saved everywhere" }
|
|
526395
526907
|
]
|
|
526396
526908
|
};
|
|
526397
|
-
|
|
526909
|
+
TABS8 = ["All", "Hidden", "Cleanup", "Settings"];
|
|
526398
526910
|
COLUMNS2 = [
|
|
526399
526911
|
{ key: "scope", width: 4, render: (r4) => r4.scope === "project" ? "proj" : "glob" },
|
|
526400
526912
|
{ key: "cat", width: 7, render: (r4) => r4.category.slice(0, 7) },
|
|
@@ -526513,9 +527025,9 @@ function ShutdownSplash({
|
|
|
526513
527025
|
height
|
|
526514
527026
|
}) {
|
|
526515
527027
|
const shortId = sessionId?.slice(0, 8);
|
|
526516
|
-
const [tick, setTick] =
|
|
527028
|
+
const [tick, setTick] = import_react150.useState(0);
|
|
526517
527029
|
const { width: termWidth } = useTerminalDimensions();
|
|
526518
|
-
|
|
527030
|
+
import_react150.useEffect(() => {
|
|
526519
527031
|
const timer = setInterval(() => setTick((t3) => t3 + 1), 80);
|
|
526520
527032
|
return () => clearInterval(timer);
|
|
526521
527033
|
}, []);
|
|
@@ -526718,12 +527230,12 @@ function App({
|
|
|
526718
527230
|
const { height: termHeight, width: termWidth } = useTerminalDimensions();
|
|
526719
527231
|
useThemeStore((s2) => s2.name);
|
|
526720
527232
|
const t2 = useTheme();
|
|
526721
|
-
const [providerStatuses, setProviderStatuses] =
|
|
527233
|
+
const [providerStatuses, setProviderStatuses] = import_react150.useState(() => {
|
|
526722
527234
|
return getCachedProviderStatuses() ?? bootProviders;
|
|
526723
527235
|
});
|
|
526724
|
-
const [shutdownPhase, setShutdownPhase] =
|
|
526725
|
-
const savedSessionIdRef =
|
|
526726
|
-
|
|
527236
|
+
const [shutdownPhase, setShutdownPhase] = import_react150.useState(-1);
|
|
527237
|
+
const savedSessionIdRef = import_react150.useRef(null);
|
|
527238
|
+
import_react150.useEffect(() => {
|
|
526727
527239
|
const stdin = process.stdin;
|
|
526728
527240
|
const originalRead = stdin.read.bind(stdin);
|
|
526729
527241
|
const patchedRead = (size) => {
|
|
@@ -526746,16 +527258,16 @@ function App({
|
|
|
526746
527258
|
stdin.read = originalRead;
|
|
526747
527259
|
};
|
|
526748
527260
|
}, []);
|
|
526749
|
-
const copyToClipboard2 =
|
|
527261
|
+
const copyToClipboard2 = import_react150.useCallback((text3) => {
|
|
526750
527262
|
if (!renderer2.copyToClipboardOSC52(text3)) {
|
|
526751
527263
|
copyToClipboard(text3);
|
|
526752
527264
|
}
|
|
526753
527265
|
}, [renderer2]);
|
|
526754
|
-
|
|
527266
|
+
import_react150.useEffect(() => {
|
|
526755
527267
|
setProviderStatuses(getCachedProviderStatuses() ?? bootProviders);
|
|
526756
527268
|
}, [bootProviders]);
|
|
526757
|
-
|
|
526758
|
-
|
|
527269
|
+
import_react150.useEffect(() => subscribeProviderStatuses(setProviderStatuses), []);
|
|
527270
|
+
import_react150.useEffect(() => {
|
|
526759
527271
|
const onSelection = (sel) => {
|
|
526760
527272
|
const text3 = sel.getSelectedText();
|
|
526761
527273
|
if (text3)
|
|
@@ -526766,22 +527278,22 @@ function App({
|
|
|
526766
527278
|
renderer2.off("selection", onSelection);
|
|
526767
527279
|
};
|
|
526768
527280
|
}, [renderer2, copyToClipboard2]);
|
|
526769
|
-
|
|
527281
|
+
import_react150.useEffect(() => {
|
|
526770
527282
|
fetchOpenRouterMetadata();
|
|
526771
527283
|
}, []);
|
|
526772
|
-
const [globalConfig2, setGlobalConfig] =
|
|
526773
|
-
const [projConfig, setProjConfig] =
|
|
526774
|
-
const [routerScope, setRouterScope] =
|
|
526775
|
-
const modelScope =
|
|
526776
|
-
const effectiveConfig =
|
|
527284
|
+
const [globalConfig2, setGlobalConfig] = import_react150.useState(config2);
|
|
527285
|
+
const [projConfig, setProjConfig] = import_react150.useState(projectConfig ?? null);
|
|
527286
|
+
const [routerScope, setRouterScope] = import_react150.useState(() => projectConfig && ("taskRouter" in projectConfig) ? "project" : "global");
|
|
527287
|
+
const modelScope = import_react150.useMemo(() => projConfig && ("defaultModel" in projConfig) ? "project" : "global", [projConfig]);
|
|
527288
|
+
const effectiveConfig = import_react150.useMemo(() => mergeConfigs(globalConfig2, projConfig), [globalConfig2, projConfig]);
|
|
526777
527289
|
const { focusMode, editorOpen, toggleEditor, openEditor, closeEditor, focusChat, focusEditor } = useEditorFocus();
|
|
526778
|
-
const [editorVisible, setEditorVisible] =
|
|
527290
|
+
const [editorVisible, setEditorVisible] = import_react150.useState(false);
|
|
526779
527291
|
const tabMgr = useTabs();
|
|
526780
|
-
const tabMgrRef =
|
|
527292
|
+
const tabMgrRef = import_react150.useRef(tabMgr);
|
|
526781
527293
|
tabMgrRef.current = tabMgr;
|
|
526782
|
-
const hasTabBarRef =
|
|
527294
|
+
const hasTabBarRef = import_react150.useRef(false);
|
|
526783
527295
|
hasTabBarRef.current = tabMgr.tabCount > 1;
|
|
526784
|
-
const editorSplitRef =
|
|
527296
|
+
const editorSplitRef = import_react150.useRef(60);
|
|
526785
527297
|
const {
|
|
526786
527298
|
ready: nvimReady,
|
|
526787
527299
|
ptyWrite,
|
|
@@ -526797,8 +527309,8 @@ function App({
|
|
|
526797
527309
|
openFile: nvimOpen,
|
|
526798
527310
|
error: nvimError
|
|
526799
527311
|
} = useNeovim(true, effectiveConfig.nvimPath, effectiveConfig.nvimConfig, closeEditor, hasTabBarRef.current, editorSplitRef.current);
|
|
526800
|
-
const pendingEditorFileRef =
|
|
526801
|
-
|
|
527312
|
+
const pendingEditorFileRef = import_react150.useRef(null);
|
|
527313
|
+
import_react150.useEffect(() => {
|
|
526802
527314
|
if (nvimReady && pendingEditorFileRef.current) {
|
|
526803
527315
|
const file2 = pendingEditorFileRef.current;
|
|
526804
527316
|
pendingEditorFileRef.current = null;
|
|
@@ -526807,7 +527319,7 @@ function App({
|
|
|
526807
527319
|
});
|
|
526808
527320
|
}
|
|
526809
527321
|
}, [nvimReady, nvimOpen]);
|
|
526810
|
-
const openEditorWithFile =
|
|
527322
|
+
const openEditorWithFile = import_react150.useCallback((file2) => {
|
|
526811
527323
|
if (editorOpen && nvimReady) {
|
|
526812
527324
|
nvimOpen(file2).catch((err2) => {
|
|
526813
527325
|
logBackgroundError("editor", `failed to open ${file2}: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
@@ -526817,24 +527329,24 @@ function App({
|
|
|
526817
527329
|
openEditor();
|
|
526818
527330
|
}
|
|
526819
527331
|
}, [editorOpen, nvimReady, nvimOpen, openEditor]);
|
|
526820
|
-
|
|
527332
|
+
import_react150.useEffect(() => {
|
|
526821
527333
|
setEditorRequestCallback((file2) => {
|
|
526822
527334
|
if (file2)
|
|
526823
527335
|
openEditorWithFile(file2);
|
|
526824
527336
|
});
|
|
526825
527337
|
return () => setEditorRequestCallback(null);
|
|
526826
527338
|
}, [openEditorWithFile]);
|
|
526827
|
-
|
|
527339
|
+
import_react150.useEffect(() => {
|
|
526828
527340
|
if (editorOpen)
|
|
526829
527341
|
setEditorVisible(true);
|
|
526830
527342
|
}, [editorOpen]);
|
|
526831
527343
|
const reasoningExpanded = useUIStore((s2) => s2.reasoningExpanded);
|
|
526832
527344
|
const codeExpanded = useUIStore((s2) => s2.codeExpanded);
|
|
526833
527345
|
const hasTabBar = tabMgr.tabCount > 1;
|
|
526834
|
-
|
|
527346
|
+
import_react150.useEffect(() => {
|
|
526835
527347
|
renderer2.requestRender();
|
|
526836
527348
|
}, [editorOpen, editorVisible, focusMode, reasoningExpanded, codeExpanded, hasTabBar, renderer2]);
|
|
526837
|
-
const handleEditorClosed =
|
|
527349
|
+
const handleEditorClosed = import_react150.useCallback(() => {
|
|
526838
527350
|
setEditorVisible(false);
|
|
526839
527351
|
}, []);
|
|
526840
527352
|
useEditorInput({
|
|
@@ -526867,6 +527379,7 @@ function App({
|
|
|
526867
527379
|
const modalInfoPopup = useUIStore((s2) => s2.modals.infoPopup);
|
|
526868
527380
|
const modalDiagnose = useUIStore((s2) => s2.modals.diagnosePopup);
|
|
526869
527381
|
const modalStatusDashboard = useUIStore((s2) => s2.modals.statusDashboard);
|
|
527382
|
+
const modalModelEvents = useUIStore((s2) => s2.modals.modelEvents);
|
|
526870
527383
|
const modalToolsPopup = useUIStore((s2) => s2.modals.toolsPopup);
|
|
526871
527384
|
const modalMCPSettings = useUIStore((s2) => s2.modals.mcpSettings);
|
|
526872
527385
|
const modalHearthSettings = useUIStore((s2) => s2.modals.hearthSettings);
|
|
@@ -526876,24 +527389,24 @@ function App({
|
|
|
526876
527389
|
const modalMemoryBrowser = useUIStore((s2) => s2.modals.memoryBrowser);
|
|
526877
527390
|
const modalUiDemo = useUIStore((s2) => s2.modals.uiDemo);
|
|
526878
527391
|
const toolsState = useToolsStore();
|
|
526879
|
-
|
|
527392
|
+
import_react150.useEffect(() => {
|
|
526880
527393
|
toolsState.initFromConfig(effectiveConfig.disabledTools);
|
|
526881
527394
|
}, [effectiveConfig.disabledTools, toolsState.initFromConfig]);
|
|
526882
|
-
|
|
527395
|
+
import_react150.useEffect(() => {
|
|
526883
527396
|
saveGlobalConfig({ disabledTools: [...toolsState.disabledTools] });
|
|
526884
527397
|
}, [toolsState.disabledTools]);
|
|
526885
527398
|
const statusDashboardTab = useUIStore((s2) => s2.statusDashboardTab);
|
|
526886
527399
|
const modalRepoMapStatus = useUIStore((s2) => s2.modals.repoMapStatus);
|
|
526887
527400
|
const isModalOpen = useUIStore(selectIsAnyModalOpen);
|
|
526888
|
-
const wizardOpenedLlm =
|
|
526889
|
-
const closerCache2 =
|
|
527401
|
+
const wizardOpenedLlm = import_react150.useRef(false);
|
|
527402
|
+
const closerCache2 = import_react150.useRef({});
|
|
526890
527403
|
const getCloser2 = (name39) => closerCache2.current[name39] ??= () => useUIStore.getState().closeModal(name39);
|
|
526891
527404
|
useVersionCheck();
|
|
526892
527405
|
const versionCurrent = useVersionStore((s2) => s2.current);
|
|
526893
527406
|
const versionLatest = useVersionStore((s2) => s2.latest);
|
|
526894
527407
|
const versionUpdateAvailable = useVersionStore((s2) => s2.updateAvailable);
|
|
526895
|
-
const updateModalShown =
|
|
526896
|
-
|
|
527408
|
+
const updateModalShown = import_react150.useRef(false);
|
|
527409
|
+
import_react150.useEffect(() => {
|
|
526897
527410
|
if (!versionUpdateAvailable || !versionLatest || updateModalShown.current)
|
|
526898
527411
|
return;
|
|
526899
527412
|
if (isDismissed(versionLatest))
|
|
@@ -526907,7 +527420,7 @@ function App({
|
|
|
526907
527420
|
}, 500);
|
|
526908
527421
|
return () => clearTimeout(timer);
|
|
526909
527422
|
}, [versionUpdateAvailable, versionLatest]);
|
|
526910
|
-
|
|
527423
|
+
import_react150.useEffect(() => {
|
|
526911
527424
|
if (getMissingRequired().length > 0) {
|
|
526912
527425
|
useUIStore.getState().openModal("setup");
|
|
526913
527426
|
} else if (forceWizard || !config2.onboardingComplete && !resumeSessionId) {
|
|
@@ -526915,7 +527428,7 @@ function App({
|
|
|
526915
527428
|
}
|
|
526916
527429
|
}, [config2.onboardingComplete, forceWizard, resumeSessionId]);
|
|
526917
527430
|
const cwd2 = process.cwd();
|
|
526918
|
-
const saveToScope =
|
|
527431
|
+
const saveToScope = import_react150.useCallback((patch, toScope, fromScope) => {
|
|
526919
527432
|
if (toScope === "global") {
|
|
526920
527433
|
saveGlobalConfig(patch);
|
|
526921
527434
|
setGlobalConfig((prev) => applyConfigPatch(prev, patch));
|
|
@@ -526935,12 +527448,12 @@ function App({
|
|
|
526935
527448
|
}
|
|
526936
527449
|
}
|
|
526937
527450
|
}, [cwd2]);
|
|
526938
|
-
const detectScope =
|
|
527451
|
+
const detectScope = import_react150.useCallback((key3) => {
|
|
526939
527452
|
if (projConfig && key3 in projConfig)
|
|
526940
527453
|
return "project";
|
|
526941
527454
|
return "global";
|
|
526942
527455
|
}, [projConfig]);
|
|
526943
|
-
|
|
527456
|
+
import_react150.useEffect(() => {
|
|
526944
527457
|
initForbidden(cwd2);
|
|
526945
527458
|
for (const cfg of PROVIDER_CONFIGS) {
|
|
526946
527459
|
if (cfg.grouped)
|
|
@@ -526949,19 +527462,19 @@ function App({
|
|
|
526949
527462
|
fetchProviderModels(cfg.id).catch(() => {});
|
|
526950
527463
|
}
|
|
526951
527464
|
}, []);
|
|
526952
|
-
const contextManager =
|
|
526953
|
-
const sessionManager =
|
|
526954
|
-
const mcpManager =
|
|
526955
|
-
|
|
527465
|
+
const contextManager = import_react150.useMemo(() => preloadedContextManager ?? new ContextManager(cwd2), [cwd2, preloadedContextManager]);
|
|
527466
|
+
const sessionManager = import_react150.useMemo(() => new SessionManager(cwd2), [cwd2]);
|
|
527467
|
+
const mcpManager = import_react150.useMemo(() => getMCPManager(), []);
|
|
527468
|
+
import_react150.useEffect(() => {
|
|
526956
527469
|
mcpManager.connectAll(effectiveConfig.mcpServers ?? []);
|
|
526957
527470
|
}, [mcpManager, effectiveConfig.mcpServers]);
|
|
526958
|
-
|
|
527471
|
+
import_react150.useEffect(() => {
|
|
526959
527472
|
return () => {
|
|
526960
527473
|
disposeMCPManager();
|
|
526961
527474
|
};
|
|
526962
527475
|
}, []);
|
|
526963
527476
|
const git = useGitStatus(cwd2);
|
|
526964
|
-
const [forgeMode, setForgeModeHeader] =
|
|
527477
|
+
const [forgeMode, setForgeModeHeader] = import_react150.useState("default");
|
|
526965
527478
|
const modeLabel = getModeLabel(forgeMode);
|
|
526966
527479
|
const modeColor = getModeColor(forgeMode);
|
|
526967
527480
|
useConfigSync({
|
|
@@ -526975,7 +527488,7 @@ function App({
|
|
|
526975
527488
|
cursorCol,
|
|
526976
527489
|
visualSelection
|
|
526977
527490
|
});
|
|
526978
|
-
const handleSuspend =
|
|
527491
|
+
const handleSuspend = import_react150.useCallback(async (opts) => {
|
|
526979
527492
|
useUIStore.getState().setSuspended(true);
|
|
526980
527493
|
await new Promise((r4) => setTimeout(r4, 50));
|
|
526981
527494
|
const result = await suspendAndRun({ ...opts, cwd: cwd2 });
|
|
@@ -526995,33 +527508,33 @@ function App({
|
|
|
526995
527508
|
git.refresh();
|
|
526996
527509
|
}, [cwd2, git]);
|
|
526997
527510
|
editorSplitRef.current = editorSplit;
|
|
526998
|
-
const sharedResources =
|
|
527511
|
+
const sharedResources = import_react150.useMemo(() => ({
|
|
526999
527512
|
...contextManager.getSharedResources(),
|
|
527000
527513
|
workspaceCoordinator: getWorkspaceCoordinator()
|
|
527001
527514
|
}), [contextManager]);
|
|
527002
|
-
const workspaceSnapshotRef =
|
|
527515
|
+
const workspaceSnapshotRef = import_react150.useRef(null);
|
|
527003
527516
|
workspaceSnapshotRef.current = () => ({
|
|
527004
527517
|
tabStates: tabMgr.getAllTabStates(),
|
|
527005
527518
|
activeTabId: tabMgr.activeTabId
|
|
527006
527519
|
});
|
|
527007
|
-
const getWorkspaceSnapshot =
|
|
527520
|
+
const getWorkspaceSnapshot = import_react150.useCallback(() => workspaceSnapshotRef.current?.() ?? {
|
|
527008
527521
|
tabStates: [],
|
|
527009
527522
|
activeTabId: ""
|
|
527010
527523
|
}, []);
|
|
527011
|
-
const addSystemMessage =
|
|
527524
|
+
const addSystemMessage = import_react150.useCallback((msg) => {
|
|
527012
527525
|
const activeChat = tabMgrRef.current?.getActiveChat();
|
|
527013
527526
|
activeChat?.setMessages((prev) => [
|
|
527014
527527
|
...prev,
|
|
527015
527528
|
{ id: crypto.randomUUID(), role: "system", content: msg, timestamp: Date.now() }
|
|
527016
527529
|
]);
|
|
527017
527530
|
}, []);
|
|
527018
|
-
const refreshGit =
|
|
527531
|
+
const refreshGit = import_react150.useCallback(() => {
|
|
527019
527532
|
git.refresh();
|
|
527020
527533
|
}, [git]);
|
|
527021
|
-
const shutdownPhaseRef =
|
|
527534
|
+
const shutdownPhaseRef = import_react150.useRef(shutdownPhase);
|
|
527022
527535
|
shutdownPhaseRef.current = shutdownPhase;
|
|
527023
|
-
const exitTimersRef =
|
|
527024
|
-
const handleCycleTab =
|
|
527536
|
+
const exitTimersRef = import_react150.useRef([]);
|
|
527537
|
+
const handleCycleTab = import_react150.useCallback((direction) => {
|
|
527025
527538
|
if (tabMgr.tabCount <= 1)
|
|
527026
527539
|
return;
|
|
527027
527540
|
if (direction === 1)
|
|
@@ -527029,7 +527542,7 @@ function App({
|
|
|
527029
527542
|
else
|
|
527030
527543
|
tabMgr.prevTab();
|
|
527031
527544
|
}, [tabMgr.tabCount, tabMgr.nextTab, tabMgr.prevTab]);
|
|
527032
|
-
const handleExit =
|
|
527545
|
+
const handleExit = import_react150.useCallback(() => {
|
|
527033
527546
|
if (shutdownPhaseRef.current >= 0)
|
|
527034
527547
|
return;
|
|
527035
527548
|
setShutdownPhase(0);
|
|
@@ -527083,8 +527596,8 @@ function App({
|
|
|
527083
527596
|
}, 300);
|
|
527084
527597
|
}, 250);
|
|
527085
527598
|
}, [cwd2, sessionManager, contextManager, renderer2]);
|
|
527086
|
-
const hasRestoredRef =
|
|
527087
|
-
|
|
527599
|
+
const hasRestoredRef = import_react150.useRef(false);
|
|
527600
|
+
import_react150.useEffect(() => {
|
|
527088
527601
|
if (hasRestoredRef.current || !resumeSessionId)
|
|
527089
527602
|
return;
|
|
527090
527603
|
hasRestoredRef.current = true;
|
|
@@ -527118,8 +527631,8 @@ function App({
|
|
|
527118
527631
|
}, 100);
|
|
527119
527632
|
}
|
|
527120
527633
|
}, []);
|
|
527121
|
-
const hasBootedHearthRef =
|
|
527122
|
-
|
|
527634
|
+
const hasBootedHearthRef = import_react150.useRef(false);
|
|
527635
|
+
import_react150.useEffect(() => {
|
|
527123
527636
|
if (hasBootedHearthRef.current)
|
|
527124
527637
|
return;
|
|
527125
527638
|
hasBootedHearthRef.current = true;
|
|
@@ -527334,9 +527847,9 @@ function App({
|
|
|
527334
527847
|
} catch {}
|
|
527335
527848
|
})();
|
|
527336
527849
|
}, []);
|
|
527337
|
-
const [activeModelForHeader, setActiveModelForHeader] =
|
|
527338
|
-
const activeChatRef =
|
|
527339
|
-
|
|
527850
|
+
const [activeModelForHeader, setActiveModelForHeader] = import_react150.useState(effectiveConfig.defaultModel);
|
|
527851
|
+
const activeChatRef = import_react150.useRef(null);
|
|
527852
|
+
import_react150.useEffect(() => {
|
|
527340
527853
|
const chat = tabMgr.getActiveChat();
|
|
527341
527854
|
activeChatRef.current = chat;
|
|
527342
527855
|
if (chat) {
|
|
@@ -527346,7 +527859,7 @@ function App({
|
|
|
527346
527859
|
setExitSessionId(hasContent ? chat.sessionId : null);
|
|
527347
527860
|
}
|
|
527348
527861
|
}, [tabMgr.activeTabId]);
|
|
527349
|
-
|
|
527862
|
+
import_react150.useEffect(() => {
|
|
527350
527863
|
if (tabMgr.tabCount <= 1)
|
|
527351
527864
|
return;
|
|
527352
527865
|
(async () => {
|
|
@@ -527364,7 +527877,7 @@ function App({
|
|
|
527364
527877
|
} catch {}
|
|
527365
527878
|
})();
|
|
527366
527879
|
}, [tabMgr.tabCount, tabMgr.activeTabId]);
|
|
527367
|
-
const { displayProvider, displayModel, isGateway, isProxy } =
|
|
527880
|
+
const { displayProvider, displayModel, isGateway, isProxy } = import_react150.useMemo(() => {
|
|
527368
527881
|
const model = activeModelForHeader;
|
|
527369
527882
|
if (model === "none") {
|
|
527370
527883
|
return {
|
|
@@ -527395,11 +527908,11 @@ function App({
|
|
|
527395
527908
|
isProxy: false
|
|
527396
527909
|
};
|
|
527397
527910
|
}, [activeModelForHeader]);
|
|
527398
|
-
|
|
527911
|
+
import_react150.useEffect(() => {
|
|
527399
527912
|
if (nvimError)
|
|
527400
527913
|
addSystemMessage(`Neovim error: ${nvimError}`);
|
|
527401
527914
|
}, [nvimError]);
|
|
527402
|
-
|
|
527915
|
+
import_react150.useEffect(() => {
|
|
527403
527916
|
const memMgr = contextManager.getMemoryManager();
|
|
527404
527917
|
const { project: project2, global: global2 } = memMgr.getLegacyBackupPaths();
|
|
527405
527918
|
const parts2 = [];
|
|
@@ -527411,7 +527924,7 @@ function App({
|
|
|
527411
527924
|
addSystemMessage(`Legacy memory schema detected \u2014 your old DB was preserved at ${parts2.join(", ")}. Phase 1 schema is now active in .soulforge/memory.db.`);
|
|
527412
527925
|
}
|
|
527413
527926
|
}, []);
|
|
527414
|
-
const handleNewSession =
|
|
527927
|
+
const handleNewSession = import_react150.useCallback(async () => {
|
|
527415
527928
|
const activeChat = tabMgrRef.current?.getActiveChat();
|
|
527416
527929
|
const hasContent = activeChat?.messages.some((m5) => m5.role === "user" || m5.role === "assistant");
|
|
527417
527930
|
if (hasContent && activeChat) {
|
|
@@ -527440,7 +527953,7 @@ function App({
|
|
|
527440
527953
|
cpStore.skipCleanup(tab.id);
|
|
527441
527954
|
restart();
|
|
527442
527955
|
}, [cwd2, sessionManager]);
|
|
527443
|
-
const handleTabCommand =
|
|
527956
|
+
const handleTabCommand = import_react150.useCallback((input, chat) => {
|
|
527444
527957
|
const cmd = input.trim().toLowerCase().split(/\s+/)[0] ?? "";
|
|
527445
527958
|
const twoWord = input.trim().toLowerCase().split(/\s+/).slice(0, 2).join(" ");
|
|
527446
527959
|
if (chat.isLoading && (ABORT_ON_LOADING.has(cmd) || ABORT_ON_LOADING.has(twoWord))) {
|
|
@@ -527577,24 +528090,26 @@ function App({
|
|
|
527577
528090
|
handleNewSession,
|
|
527578
528091
|
effectiveConfig.watchdog
|
|
527579
528092
|
]);
|
|
527580
|
-
const closeLlmSelector =
|
|
528093
|
+
const closeLlmSelector = import_react150.useCallback(() => {
|
|
527581
528094
|
const wasPickingSlot = useUIStore.getState().routerSlotPicking != null;
|
|
528095
|
+
const wasFallbackForModel = useUIStore.getState().fallbackForModel != null;
|
|
527582
528096
|
const wasFromWizard = wizardOpenedLlm.current;
|
|
527583
528097
|
useUIStore.getState().closeModal("llmSelector");
|
|
527584
528098
|
useUIStore.getState().setRouterSlotPicking(null);
|
|
528099
|
+
useUIStore.getState().setFallbackForModel(null);
|
|
527585
528100
|
wizardOpenedLlm.current = false;
|
|
527586
|
-
if (wasPickingSlot) {
|
|
528101
|
+
if (wasPickingSlot || wasFallbackForModel) {
|
|
527587
528102
|
useUIStore.getState().openModal("routerSettings");
|
|
527588
528103
|
} else if (wasFromWizard) {
|
|
527589
528104
|
useUIStore.getState().openModal("firstRunWizard");
|
|
527590
528105
|
}
|
|
527591
528106
|
}, []);
|
|
527592
|
-
const closeInfoPopup =
|
|
528107
|
+
const closeInfoPopup = import_react150.useCallback(() => {
|
|
527593
528108
|
const cfg = useUIStore.getState().infoPopupConfig;
|
|
527594
528109
|
useUIStore.getState().closeInfoPopup();
|
|
527595
528110
|
cfg?.onClose?.();
|
|
527596
528111
|
}, []);
|
|
527597
|
-
const onGitMenuCommit =
|
|
528112
|
+
const onGitMenuCommit = import_react150.useCallback(() => {
|
|
527598
528113
|
useUIStore.getState().closeModal("gitMenu");
|
|
527599
528114
|
useUIStore.getState().openModal("gitCommit");
|
|
527600
528115
|
}, []);
|
|
@@ -527607,7 +528122,7 @@ function App({
|
|
|
527607
528122
|
renderer: renderer2,
|
|
527608
528123
|
copyToClipboard: copyToClipboard2,
|
|
527609
528124
|
activeChatRef,
|
|
527610
|
-
cycleMode:
|
|
528125
|
+
cycleMode: import_react150.useCallback(() => {
|
|
527611
528126
|
const chat = tabMgrRef.current?.getActiveChat();
|
|
527612
528127
|
if (chat) {
|
|
527613
528128
|
const next = chat.cycleMode();
|
|
@@ -527841,6 +528356,9 @@ function App({
|
|
|
527841
528356
|
onSuspend: handleSuspend,
|
|
527842
528357
|
onCommand: handleTabCommand,
|
|
527843
528358
|
onModeChange: setForgeModeHeader,
|
|
528359
|
+
onModelChange: (modelId) => {
|
|
528360
|
+
setActiveModelForHeader(modelId);
|
|
528361
|
+
},
|
|
527844
528362
|
onExit: handleExit,
|
|
527845
528363
|
registerChat: tabMgr.registerChat,
|
|
527846
528364
|
unregisterChat: tabMgr.unregisterChat,
|
|
@@ -527874,24 +528392,27 @@ function App({
|
|
|
527874
528392
|
activeModel: activeModelForHeader,
|
|
527875
528393
|
onSelect: (modelId) => {
|
|
527876
528394
|
const slot = useUIStore.getState().routerSlotPicking;
|
|
528395
|
+
const fallbackForModel = useUIStore.getState().fallbackForModel;
|
|
528396
|
+
if (fallbackForModel) {
|
|
528397
|
+
const current = { ...effectiveConfig.modelFallback ?? {} };
|
|
528398
|
+
const fallbacks = [...current[fallbackForModel] ?? []];
|
|
528399
|
+
fallbacks.push(modelId);
|
|
528400
|
+
current[fallbackForModel] = fallbacks;
|
|
528401
|
+
saveToScope({ modelFallback: current }, modelScope);
|
|
528402
|
+
closeLlmSelector();
|
|
528403
|
+
return;
|
|
528404
|
+
}
|
|
527877
528405
|
if (slot) {
|
|
527878
528406
|
const current = effectiveConfig.taskRouter ?? DEFAULT_TASK_ROUTER;
|
|
527879
528407
|
const updated = { ...current, [slot]: modelId };
|
|
527880
528408
|
saveToScope({ taskRouter: updated }, routerScope);
|
|
527881
|
-
|
|
527882
|
-
useUIStore.getState().closeModal("llmSelector");
|
|
527883
|
-
useUIStore.getState().openModal("routerSettings");
|
|
528409
|
+
closeLlmSelector();
|
|
527884
528410
|
} else {
|
|
527885
528411
|
activeChatRef.current?.setActiveModel(modelId);
|
|
527886
528412
|
notifyProviderSwitch(modelId);
|
|
527887
528413
|
setActiveModelForHeader(modelId);
|
|
527888
528414
|
saveToScope({ defaultModel: modelId }, modelScope);
|
|
527889
|
-
|
|
527890
|
-
wizardOpenedLlm.current = false;
|
|
527891
|
-
useUIStore.getState().closeModal("llmSelector");
|
|
527892
|
-
if (wasFromWizard) {
|
|
527893
|
-
useUIStore.getState().openModal("firstRunWizard");
|
|
527894
|
-
}
|
|
528415
|
+
closeLlmSelector();
|
|
527895
528416
|
}
|
|
527896
528417
|
},
|
|
527897
528418
|
onClose: closeLlmSelector
|
|
@@ -528011,6 +528532,8 @@ function App({
|
|
|
528011
528532
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RouterSettings, {
|
|
528012
528533
|
visible: modalRouterSettings && !routerSlotPicking,
|
|
528013
528534
|
router: effectiveConfig.taskRouter,
|
|
528535
|
+
defaultModel: effectiveConfig.defaultModel,
|
|
528536
|
+
modelFallback: effectiveConfig.modelFallback,
|
|
528014
528537
|
activeModel: activeModelForHeader,
|
|
528015
528538
|
scope: routerScope,
|
|
528016
528539
|
onScopeChange: (toScope, fromScope) => {
|
|
@@ -528033,6 +528556,15 @@ function App({
|
|
|
528033
528556
|
const updated = { ...current, [key3]: value };
|
|
528034
528557
|
saveToScope({ taskRouter: updated }, routerScope);
|
|
528035
528558
|
},
|
|
528559
|
+
onAddFallback: (modelId) => {
|
|
528560
|
+
useUIStore.getState().setFallbackForModel(modelId);
|
|
528561
|
+
useUIStore.getState().openModal("llmSelector");
|
|
528562
|
+
},
|
|
528563
|
+
onClearFallbacks: (modelId) => {
|
|
528564
|
+
const current = { ...effectiveConfig.modelFallback ?? {} };
|
|
528565
|
+
delete current[modelId];
|
|
528566
|
+
saveToScope({ modelFallback: current }, modelScope);
|
|
528567
|
+
},
|
|
528036
528568
|
onClose: getCloser2("routerSettings")
|
|
528037
528569
|
}, undefined, false, undefined, this),
|
|
528038
528570
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(CommandPalette, {
|
|
@@ -528069,6 +528601,10 @@ function App({
|
|
|
528069
528601
|
onClose: getCloser2("diagnosePopup"),
|
|
528070
528602
|
runHealthCheck: runIntelligenceHealthCheck
|
|
528071
528603
|
}, undefined, false, undefined, this),
|
|
528604
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ModelEventsPopup, {
|
|
528605
|
+
visible: modalModelEvents,
|
|
528606
|
+
onClose: getCloser2("modelEvents")
|
|
528607
|
+
}, undefined, false, undefined, this),
|
|
528072
528608
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RepoMapStatusPopup, {
|
|
528073
528609
|
visible: modalRepoMapStatus,
|
|
528074
528610
|
onClose: getCloser2("repoMapStatus"),
|
|
@@ -528157,7 +528693,7 @@ function App({
|
|
|
528157
528693
|
]
|
|
528158
528694
|
}, undefined, true, undefined, this);
|
|
528159
528695
|
}
|
|
528160
|
-
var
|
|
528696
|
+
var import_react150, ABORT_ON_LOADING, DEFAULT_TASK_ROUTER, SHUTDOWN_STEPS, KITTY_PROTOCOL_RESPONSE_RE;
|
|
528161
528697
|
var init_App = __esm(async () => {
|
|
528162
528698
|
init_shallow2();
|
|
528163
528699
|
init_config2();
|
|
@@ -528229,6 +528765,7 @@ var init_App = __esm(async () => {
|
|
|
528229
528765
|
init_HearthSettings(),
|
|
528230
528766
|
init_LspInstallSearch(),
|
|
528231
528767
|
init_MCPSettings(),
|
|
528768
|
+
init_ModelEventsPopup(),
|
|
528232
528769
|
init_ProviderSettings(),
|
|
528233
528770
|
init_RepoMapStatusPopup(),
|
|
528234
528771
|
init_RouterSettings(),
|
|
@@ -528236,7 +528773,7 @@ var init_App = __esm(async () => {
|
|
|
528236
528773
|
init_ToolsPopup(),
|
|
528237
528774
|
init_MemoryBrowser()
|
|
528238
528775
|
]);
|
|
528239
|
-
|
|
528776
|
+
import_react150 = __toESM(require_react(), 1);
|
|
528240
528777
|
startMemoryPoll();
|
|
528241
528778
|
ABORT_ON_LOADING = new Set([
|
|
528242
528779
|
"/clear",
|