jinzd-ai-cli 0.4.267 → 0.4.269
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/{batch-GY5MAN5F.js → batch-HUOYPVJQ.js} +2 -2
- package/dist/{chunk-THHPKZJ3.js → chunk-3TOMHXDW.js} +1 -1
- package/dist/{chunk-7HECI3U5.js → chunk-4HTJMLFY.js} +3 -1
- package/dist/{chunk-EWRZMIQE.js → chunk-5IUFUCIT.js} +1 -1
- package/dist/{chunk-WPN7LDLM.js → chunk-5PRWBJCI.js} +1 -1
- package/dist/{chunk-QN7WD2SM.js → chunk-6PROC2JO.js} +3 -3
- package/dist/{chunk-6C6FLDGH.js → chunk-AAERHPBN.js} +1 -1
- package/dist/{chunk-5AEOIETL.js → chunk-DJNGUPM3.js} +4 -4
- package/dist/{chunk-FI6IWUXW.js → chunk-KIHNZBEA.js} +1 -1
- package/dist/{chunk-ARYCWJN6.js → chunk-NVN3FHXI.js} +1 -1
- package/dist/{chunk-RVNL7MQG.js → chunk-QPPUYCRF.js} +1 -1
- package/dist/{chunk-JH343BDI.js → chunk-S3F5LOV3.js} +313 -2
- package/dist/{chunk-XLLUQEGO.js → chunk-V6KQ67VF.js} +1 -1
- package/dist/{chunk-5CU72V6P.js → chunk-XJAGGJVU.js} +3 -3
- package/dist/{ci-MUEQSNKU.js → ci-4QS2J7BG.js} +4 -4
- package/dist/{ci-format-2ZGGSOFK.js → ci-format-6L6M67WC.js} +2 -2
- package/dist/{constants-SZTJESDF.js → constants-IO5VJLEE.js} +1 -1
- package/dist/{doctor-cli-Z272364P.js → doctor-cli-ZU4E2DDL.js} +4 -4
- package/dist/electron-server.js +455 -58
- package/dist/{hub-MRLGKMRI.js → hub-AMDWXSKZ.js} +1 -1
- package/dist/index.js +117 -245
- package/dist/{persistent-memory-3FTOC7QT.js → persistent-memory-52SE5K44.js} +2 -2
- package/dist/{persistent-memory-HZASMDJQ.js → persistent-memory-H4DNDIOQ.js} +2 -2
- package/dist/{pr-VLLIALRK.js → pr-Z4O5H6I3.js} +4 -4
- package/dist/{run-tests-NTDZ6DDU.js → run-tests-FZSYHWYX.js} +2 -2
- package/dist/{run-tests-6MWE6D5S.js → run-tests-PYKKYMPA.js} +2 -2
- package/dist/{server-IALCWKSU.js → server-WTJSM2MT.js} +147 -41
- package/dist/{server-SP3AEHRQ.js → server-ZSTVYSYC.js} +5 -5
- package/dist/{task-orchestrator-Q6WFFI4J.js → task-orchestrator-PNLI3V36.js} +5 -5
- package/dist/{usage-DJDLHICZ.js → usage-GVFUVYUG.js} +2 -2
- package/package.json +1 -1
package/dist/electron-server.js
CHANGED
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
getGitContext,
|
|
27
27
|
getGitRoot,
|
|
28
28
|
isMemoryExpired,
|
|
29
|
+
isMemoryStale,
|
|
29
30
|
listMemoryEntries,
|
|
31
|
+
listOrphanedProjectMemories,
|
|
30
32
|
loadMemoryEntries,
|
|
31
33
|
memoryStorePath,
|
|
32
34
|
parseWritableScope,
|
|
@@ -34,14 +36,14 @@ import {
|
|
|
34
36
|
touchMemoryReferences,
|
|
35
37
|
updateMemoryApproval,
|
|
36
38
|
updateMemoryEntry
|
|
37
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-KIHNZBEA.js";
|
|
38
40
|
import {
|
|
39
41
|
redactJson,
|
|
40
42
|
scanString
|
|
41
43
|
} from "./chunk-YVTASHS5.js";
|
|
42
44
|
import {
|
|
43
45
|
runTestsTool
|
|
44
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-NVN3FHXI.js";
|
|
45
47
|
import {
|
|
46
48
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
47
49
|
APP_NAME,
|
|
@@ -51,6 +53,7 @@ import {
|
|
|
51
53
|
CONFIG_FILE_NAME,
|
|
52
54
|
CONTEXT_FILE_CANDIDATES,
|
|
53
55
|
CONTEXT_FILE_MAX_BYTES,
|
|
56
|
+
CONTEXT_PRESSURE_THRESHOLD,
|
|
54
57
|
CUSTOM_COMMANDS_DIR_NAME,
|
|
55
58
|
DEFAULT_MAX_TOKENS,
|
|
56
59
|
DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
|
|
@@ -73,7 +76,7 @@ import {
|
|
|
73
76
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
74
77
|
VERSION,
|
|
75
78
|
buildUserIdentityPrompt
|
|
76
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-4HTJMLFY.js";
|
|
77
80
|
import {
|
|
78
81
|
hasSemanticIndex,
|
|
79
82
|
semanticSearch
|
|
@@ -5257,9 +5260,9 @@ var SessionManager = class {
|
|
|
5257
5260
|
try {
|
|
5258
5261
|
const data = JSON.parse(readFileSync4(filePath, "utf-8"));
|
|
5259
5262
|
const messages = data.messages ?? [];
|
|
5260
|
-
const
|
|
5263
|
+
const matches2 = [];
|
|
5261
5264
|
for (const msg of messages) {
|
|
5262
|
-
if (
|
|
5265
|
+
if (matches2.length >= 3) break;
|
|
5263
5266
|
let text = "";
|
|
5264
5267
|
if (typeof msg.content === "string") {
|
|
5265
5268
|
text = msg.content;
|
|
@@ -5272,10 +5275,10 @@ var SessionManager = class {
|
|
|
5272
5275
|
const start = Math.max(0, idx - 30);
|
|
5273
5276
|
const end = Math.min(text.length, idx + query.length + 60);
|
|
5274
5277
|
const snippet = (start > 0 ? "\u2026" : "") + text.slice(start, end).replace(/\n/g, " ") + (end < text.length ? "\u2026" : "");
|
|
5275
|
-
|
|
5278
|
+
matches2.push({ role: msg.role, snippet });
|
|
5276
5279
|
}
|
|
5277
5280
|
}
|
|
5278
|
-
if (
|
|
5281
|
+
if (matches2.length > 0) {
|
|
5279
5282
|
results.push({
|
|
5280
5283
|
sessionMeta: {
|
|
5281
5284
|
id: data.id,
|
|
@@ -5286,7 +5289,7 @@ var SessionManager = class {
|
|
|
5286
5289
|
updated: safeDate(data.updated),
|
|
5287
5290
|
title: data.title
|
|
5288
5291
|
},
|
|
5289
|
-
matches
|
|
5292
|
+
matches: matches2
|
|
5290
5293
|
});
|
|
5291
5294
|
}
|
|
5292
5295
|
} catch (err) {
|
|
@@ -9394,20 +9397,20 @@ Results sorted by most recent modification time. Automatically skips node_module
|
|
|
9394
9397
|
if (!pattern) throw new ToolError("glob_files", "pattern is required");
|
|
9395
9398
|
if (!existsSync16(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
|
|
9396
9399
|
const regex = globToRegex(pattern);
|
|
9397
|
-
const
|
|
9398
|
-
collectMatchingFiles(rootPath, rootPath, regex,
|
|
9399
|
-
if (
|
|
9400
|
+
const matches2 = [];
|
|
9401
|
+
collectMatchingFiles(rootPath, rootPath, regex, matches2, maxResults);
|
|
9402
|
+
if (matches2.length === 0) {
|
|
9400
9403
|
return `No files matched pattern: ${pattern}
|
|
9401
9404
|
Searched in: ${rootPath}`;
|
|
9402
9405
|
}
|
|
9403
|
-
|
|
9406
|
+
matches2.sort((a, b) => b.mtime - a.mtime);
|
|
9404
9407
|
const lines = [
|
|
9405
|
-
`Found ${
|
|
9408
|
+
`Found ${matches2.length} file(s) matching: ${pattern}`,
|
|
9406
9409
|
`Searched in: ${rootPath}`,
|
|
9407
9410
|
"",
|
|
9408
|
-
...
|
|
9411
|
+
...matches2.map((m) => m.relPath)
|
|
9409
9412
|
];
|
|
9410
|
-
if (
|
|
9413
|
+
if (matches2.length >= maxResults) {
|
|
9411
9414
|
lines.push("");
|
|
9412
9415
|
lines.push(`(Results truncated at ${maxResults}. Use max_results or narrow your pattern.)`);
|
|
9413
9416
|
}
|
|
@@ -10926,10 +10929,10 @@ function trustPlugin(configDir, name) {
|
|
|
10926
10929
|
}
|
|
10927
10930
|
function getInstalledPlugin(configDir, name) {
|
|
10928
10931
|
const plugins = listInstalledPlugins(configDir);
|
|
10929
|
-
const
|
|
10930
|
-
if (
|
|
10931
|
-
if (
|
|
10932
|
-
return
|
|
10932
|
+
const matches2 = plugins.filter((p) => p.name === name || p.name.startsWith(name));
|
|
10933
|
+
if (matches2.length === 0) throw new Error(`plugin not found: ${name}`);
|
|
10934
|
+
if (matches2.length > 1) throw new Error(`plugin name is ambiguous: ${name}`);
|
|
10935
|
+
return matches2[0];
|
|
10933
10936
|
}
|
|
10934
10937
|
function listInstalledPlugins(configDir) {
|
|
10935
10938
|
const root = pluginRoot(configDir);
|
|
@@ -11450,8 +11453,8 @@ function summarizeContent(content) {
|
|
|
11450
11453
|
function extractEvidence(content, toolNames) {
|
|
11451
11454
|
const evidence = /* @__PURE__ */ new Set();
|
|
11452
11455
|
if (toolNames.length > 0) evidence.add(`Tools: ${toolNames.join(", ")}`);
|
|
11453
|
-
const
|
|
11454
|
-
for (const match of
|
|
11456
|
+
const matches2 = content.match(/(?:[\w.-]+[\\/])+[\w.-]+\.(?:ts|tsx|js|jsx|json|md|css|html|py|go|rs|yml|yaml)/g) ?? [];
|
|
11457
|
+
for (const match of matches2.slice(0, 8)) evidence.add(match);
|
|
11455
11458
|
return [...evidence].slice(0, 10);
|
|
11456
11459
|
}
|
|
11457
11460
|
function formatAgentResult(input) {
|
|
@@ -14161,6 +14164,112 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
14161
14164
|
return committedSize < originalSize;
|
|
14162
14165
|
}
|
|
14163
14166
|
|
|
14167
|
+
// src/core/context-pressure.ts
|
|
14168
|
+
function estimateConversationTokens(input) {
|
|
14169
|
+
let total = 0;
|
|
14170
|
+
if (input.systemPrompt) total += estimateTokens(input.systemPrompt);
|
|
14171
|
+
for (const msg of input.messages) {
|
|
14172
|
+
if (typeof msg.content === "string") {
|
|
14173
|
+
total += estimateTokens(msg.content);
|
|
14174
|
+
} else if (Array.isArray(msg.content)) {
|
|
14175
|
+
for (const part of msg.content) {
|
|
14176
|
+
if (part.type === "text" && part.text) total += estimateTokens(part.text);
|
|
14177
|
+
}
|
|
14178
|
+
}
|
|
14179
|
+
if (msg.toolCalls) {
|
|
14180
|
+
for (const tc of msg.toolCalls) {
|
|
14181
|
+
total += estimateTokens(JSON.stringify(tc.arguments));
|
|
14182
|
+
}
|
|
14183
|
+
}
|
|
14184
|
+
}
|
|
14185
|
+
return total;
|
|
14186
|
+
}
|
|
14187
|
+
function evaluateContextPressure(input) {
|
|
14188
|
+
if (input.contextWindow <= 0) return { shouldCompact: false, ratio: null, percent: null };
|
|
14189
|
+
const ratio = input.estimatedTokens / input.contextWindow;
|
|
14190
|
+
const threshold = input.threshold ?? CONTEXT_PRESSURE_THRESHOLD;
|
|
14191
|
+
return {
|
|
14192
|
+
shouldCompact: input.autoCompactEnabled && ratio >= threshold,
|
|
14193
|
+
ratio,
|
|
14194
|
+
percent: Math.round(ratio * 100)
|
|
14195
|
+
};
|
|
14196
|
+
}
|
|
14197
|
+
var AUTO_COMPACT_INSTRUCTION = "Preserve key context, compress old content, ensure seamless continuation";
|
|
14198
|
+
|
|
14199
|
+
// src/core/model-router.ts
|
|
14200
|
+
var TAG_REGEX = /(?:^|\s)#([a-zA-Z][\w-]{0,31})\b/g;
|
|
14201
|
+
function extractTags(message) {
|
|
14202
|
+
const tags = /* @__PURE__ */ new Set();
|
|
14203
|
+
let m;
|
|
14204
|
+
TAG_REGEX.lastIndex = 0;
|
|
14205
|
+
while ((m = TAG_REGEX.exec(message)) !== null) {
|
|
14206
|
+
tags.add(m[1].toLowerCase());
|
|
14207
|
+
}
|
|
14208
|
+
return tags;
|
|
14209
|
+
}
|
|
14210
|
+
function matches(message, matcher) {
|
|
14211
|
+
const trimmed = message.trim();
|
|
14212
|
+
const lower = trimmed.toLowerCase();
|
|
14213
|
+
if (matcher.tag) {
|
|
14214
|
+
const tags = extractTags(trimmed);
|
|
14215
|
+
if (!tags.has(matcher.tag.toLowerCase())) return false;
|
|
14216
|
+
}
|
|
14217
|
+
if (matcher.contains && matcher.contains.length > 0) {
|
|
14218
|
+
const hit = matcher.contains.some((kw) => lower.includes(kw.toLowerCase()));
|
|
14219
|
+
if (!hit) return false;
|
|
14220
|
+
}
|
|
14221
|
+
if (typeof matcher.maxLength === "number") {
|
|
14222
|
+
if (trimmed.length > matcher.maxLength) return false;
|
|
14223
|
+
}
|
|
14224
|
+
if (typeof matcher.minLength === "number") {
|
|
14225
|
+
if (trimmed.length < matcher.minLength) return false;
|
|
14226
|
+
}
|
|
14227
|
+
const hasAnyCondition = !!matcher.tag || matcher.contains && matcher.contains.length > 0 || typeof matcher.maxLength === "number" || typeof matcher.minLength === "number";
|
|
14228
|
+
return !!hasAnyCondition;
|
|
14229
|
+
}
|
|
14230
|
+
function pickModel(message, currentModel, config, availableModels = []) {
|
|
14231
|
+
if (!config.enabled || config.rules.length === 0) {
|
|
14232
|
+
return { model: currentModel, reason: "routing disabled", overridden: false };
|
|
14233
|
+
}
|
|
14234
|
+
const isAvailable = (m) => availableModels.length === 0 || availableModels.includes(m);
|
|
14235
|
+
for (let i = 0; i < config.rules.length; i++) {
|
|
14236
|
+
const rule = config.rules[i];
|
|
14237
|
+
if (!matches(message, rule.match)) continue;
|
|
14238
|
+
if (!isAvailable(rule.model)) continue;
|
|
14239
|
+
if (rule.model === currentModel) {
|
|
14240
|
+
return {
|
|
14241
|
+
model: currentModel,
|
|
14242
|
+
reason: `rule "${rule.name ?? `#${i}`}" matched (same as current)`,
|
|
14243
|
+
overridden: false,
|
|
14244
|
+
ruleIdx: i
|
|
14245
|
+
};
|
|
14246
|
+
}
|
|
14247
|
+
return {
|
|
14248
|
+
model: rule.model,
|
|
14249
|
+
reason: `rule "${rule.name ?? `#${i}`}" matched`,
|
|
14250
|
+
overridden: true,
|
|
14251
|
+
ruleIdx: i
|
|
14252
|
+
};
|
|
14253
|
+
}
|
|
14254
|
+
if (config.fallback && config.fallback !== currentModel && isAvailable(config.fallback)) {
|
|
14255
|
+
return {
|
|
14256
|
+
model: config.fallback,
|
|
14257
|
+
reason: "fallback",
|
|
14258
|
+
overridden: true
|
|
14259
|
+
};
|
|
14260
|
+
}
|
|
14261
|
+
return { model: currentModel, reason: "no rule matched", overridden: false };
|
|
14262
|
+
}
|
|
14263
|
+
function stripRoutingTags(message) {
|
|
14264
|
+
return message.replace(/(?:^|\s)#(fast|deep|default)\b/gi, " ").replace(/\s{2,}/g, " ").trim();
|
|
14265
|
+
}
|
|
14266
|
+
function decideModel(input) {
|
|
14267
|
+
if (!input.routingConfig || !input.routingConfig.enabled) {
|
|
14268
|
+
return { model: input.currentModel, reason: "routing disabled", overridden: false };
|
|
14269
|
+
}
|
|
14270
|
+
return pickModel(input.userInput, input.currentModel, input.routingConfig, input.availableModels);
|
|
14271
|
+
}
|
|
14272
|
+
|
|
14164
14273
|
// src/core/context-files.ts
|
|
14165
14274
|
import { existsSync as existsSync25, readFileSync as readFileSync20 } from "fs";
|
|
14166
14275
|
import { join as join19, relative as relative4, resolve as resolve8 } from "path";
|
|
@@ -14511,6 +14620,84 @@ Output the Markdown content directly, do not wrap the entire file in a code bloc
|
|
|
14511
14620
|
return parts.join("\n");
|
|
14512
14621
|
}
|
|
14513
14622
|
|
|
14623
|
+
// src/core/memory-filters.ts
|
|
14624
|
+
function parseFlags(items) {
|
|
14625
|
+
const flags = {};
|
|
14626
|
+
const text = [];
|
|
14627
|
+
for (let i = 0; i < items.length; i++) {
|
|
14628
|
+
const item = items[i];
|
|
14629
|
+
if (item.startsWith("--")) {
|
|
14630
|
+
const key = item.slice(2);
|
|
14631
|
+
const next = items[i + 1];
|
|
14632
|
+
if (next && !next.startsWith("--")) {
|
|
14633
|
+
flags[key] = next;
|
|
14634
|
+
i++;
|
|
14635
|
+
} else {
|
|
14636
|
+
flags[key] = true;
|
|
14637
|
+
}
|
|
14638
|
+
} else {
|
|
14639
|
+
text.push(item);
|
|
14640
|
+
}
|
|
14641
|
+
}
|
|
14642
|
+
return { flags, text: text.join(" ").trim() };
|
|
14643
|
+
}
|
|
14644
|
+
var SCOPES = /* @__PURE__ */ new Set(["personal", "project", "session", "team"]);
|
|
14645
|
+
var BOOLEAN_FLAGS = ["pending", "orphaned"];
|
|
14646
|
+
function parseMemoryShowFilters(args) {
|
|
14647
|
+
const { flags, text } = parseFlags(args);
|
|
14648
|
+
const strayTerms = [];
|
|
14649
|
+
for (const key of BOOLEAN_FLAGS) {
|
|
14650
|
+
const v = flags[key];
|
|
14651
|
+
if (typeof v === "string") {
|
|
14652
|
+
strayTerms.push(v);
|
|
14653
|
+
flags[key] = true;
|
|
14654
|
+
}
|
|
14655
|
+
}
|
|
14656
|
+
const scopeRaw = flags["scope"];
|
|
14657
|
+
const freeText = [text, ...strayTerms].filter(Boolean).join(" ").trim();
|
|
14658
|
+
const searchRaw = typeof flags["search"] === "string" ? flags["search"] : freeText || void 0;
|
|
14659
|
+
const staleRaw = flags["stale"];
|
|
14660
|
+
let staleDays;
|
|
14661
|
+
let invalidStale = false;
|
|
14662
|
+
if (staleRaw !== void 0) {
|
|
14663
|
+
const n = typeof staleRaw === "string" ? Number(staleRaw) : NaN;
|
|
14664
|
+
if (!Number.isFinite(n) || n <= 0) invalidStale = true;
|
|
14665
|
+
else staleDays = n;
|
|
14666
|
+
}
|
|
14667
|
+
return {
|
|
14668
|
+
pending: flags["pending"] === true,
|
|
14669
|
+
orphaned: flags["orphaned"] === true,
|
|
14670
|
+
scope: typeof scopeRaw === "string" && SCOPES.has(scopeRaw) ? scopeRaw : void 0,
|
|
14671
|
+
search: searchRaw?.toLowerCase(),
|
|
14672
|
+
staleDays,
|
|
14673
|
+
invalidStale
|
|
14674
|
+
};
|
|
14675
|
+
}
|
|
14676
|
+
function filterMemoryEntries(entries, filters) {
|
|
14677
|
+
let out = entries;
|
|
14678
|
+
const applied = [];
|
|
14679
|
+
if (filters.orphaned) applied.push("orphaned");
|
|
14680
|
+
if (filters.pending) {
|
|
14681
|
+
out = out.filter((m) => !m.approved && !isMemoryExpired(m));
|
|
14682
|
+
applied.push("pending");
|
|
14683
|
+
}
|
|
14684
|
+
if (filters.scope) {
|
|
14685
|
+
out = out.filter((m) => m.scope === filters.scope);
|
|
14686
|
+
applied.push(`scope:${filters.scope}`);
|
|
14687
|
+
}
|
|
14688
|
+
if (filters.search) {
|
|
14689
|
+
const q = filters.search;
|
|
14690
|
+
out = out.filter((m) => m.content.toLowerCase().includes(q) || m.id.startsWith(q));
|
|
14691
|
+
applied.push(`search:"${q}"`);
|
|
14692
|
+
}
|
|
14693
|
+
if (filters.staleDays !== void 0) {
|
|
14694
|
+
const days = filters.staleDays;
|
|
14695
|
+
out = out.filter((m) => isMemoryStale(m, days));
|
|
14696
|
+
applied.push(`stale>${days}d`);
|
|
14697
|
+
}
|
|
14698
|
+
return { entries: out, applied };
|
|
14699
|
+
}
|
|
14700
|
+
|
|
14514
14701
|
// src/hub/convergence.ts
|
|
14515
14702
|
function convergenceThreshold(total) {
|
|
14516
14703
|
if (total <= 0) return Infinity;
|
|
@@ -15287,6 +15474,12 @@ async function handlePlan(args, ctx) {
|
|
|
15287
15474
|
} else if (sub === "execute") {
|
|
15288
15475
|
ctx.planMode = false;
|
|
15289
15476
|
ctx.send({ type: "info", message: "Plan mode OFF. Executing with all tools enabled." });
|
|
15477
|
+
} else if (sub === "status" || sub === "show") {
|
|
15478
|
+
ctx.send({ type: "info", message: `Plan mode: ${ctx.planMode ? "ON (read-only tools only)" : "OFF"}` });
|
|
15479
|
+
return;
|
|
15480
|
+
} else if (sub !== void 0) {
|
|
15481
|
+
ctx.send({ type: "error", message: `Unknown /plan option "${sub}" \u2014 expected execute | exit | status` });
|
|
15482
|
+
return;
|
|
15290
15483
|
} else {
|
|
15291
15484
|
ctx.planMode = !ctx.planMode;
|
|
15292
15485
|
ctx.send({ type: "info", message: `Plan mode: ${ctx.planMode ? "ON (read-only tools only)" : "OFF"}` });
|
|
@@ -15824,7 +16017,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
|
|
|
15824
16017
|
Cost: ${formatCost(cost)}` : "";
|
|
15825
16018
|
let memoryLine = "";
|
|
15826
16019
|
try {
|
|
15827
|
-
const { countPendingMemories } = await import("./persistent-memory-
|
|
16020
|
+
const { countPendingMemories } = await import("./persistent-memory-52SE5K44.js");
|
|
15828
16021
|
const pending = countPendingMemories(ctx.config.getConfigDir());
|
|
15829
16022
|
if (pending > 0) memoryLine = `
|
|
15830
16023
|
Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
|
|
@@ -15928,14 +16121,14 @@ async function handleSession(args, ctx) {
|
|
|
15928
16121
|
return;
|
|
15929
16122
|
}
|
|
15930
16123
|
const list2 = ctx.sessions.listSessions();
|
|
15931
|
-
const
|
|
15932
|
-
if (
|
|
16124
|
+
const matches2 = list2.filter((s) => s.messageCount === filterMessages);
|
|
16125
|
+
if (matches2.length === 0) {
|
|
15933
16126
|
ctx.send({ type: "info", message: `No sessions with ${filterMessages} message(s) found.` });
|
|
15934
16127
|
return;
|
|
15935
16128
|
}
|
|
15936
16129
|
let deleted = 0;
|
|
15937
16130
|
let failed = 0;
|
|
15938
|
-
for (const s of
|
|
16131
|
+
for (const s of matches2) {
|
|
15939
16132
|
if (ctx.sessions.deleteSession(s.id)) {
|
|
15940
16133
|
deleted++;
|
|
15941
16134
|
} else {
|
|
@@ -16637,7 +16830,7 @@ async function handleTest(args, ctx) {
|
|
|
16637
16830
|
const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
|
|
16638
16831
|
testArgs = isCommand ? { command: argStr } : { filter: argStr };
|
|
16639
16832
|
}
|
|
16640
|
-
const runTests = ctx.runTests ?? (await import("./run-tests-
|
|
16833
|
+
const runTests = ctx.runTests ?? (await import("./run-tests-PYKKYMPA.js")).executeTests;
|
|
16641
16834
|
const report = await runTests(testArgs);
|
|
16642
16835
|
ctx.send({ type: "info", message: report });
|
|
16643
16836
|
} catch (err) {
|
|
@@ -16895,7 +17088,7 @@ async function handleMemory(args, ctx) {
|
|
|
16895
17088
|
ctx.handleMemoryManage(sub, args[1], sub === "expire" ? args[2] : void 0);
|
|
16896
17089
|
} else if (sub === "export") {
|
|
16897
17090
|
if (args[1] === "md") {
|
|
16898
|
-
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-
|
|
17091
|
+
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-52SE5K44.js");
|
|
16899
17092
|
ctx.send({
|
|
16900
17093
|
type: "export_data",
|
|
16901
17094
|
format: "md",
|
|
@@ -16915,7 +17108,8 @@ async function handleMemory(args, ctx) {
|
|
|
16915
17108
|
});
|
|
16916
17109
|
}
|
|
16917
17110
|
} else {
|
|
16918
|
-
|
|
17111
|
+
const isShow = sub === "show" || sub === "view" || sub === "list";
|
|
17112
|
+
ctx.memoryShow(isShow ? args.slice(1) : args);
|
|
16919
17113
|
}
|
|
16920
17114
|
}
|
|
16921
17115
|
async function handleAddDir(args, ctx) {
|
|
@@ -16946,6 +17140,119 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
|
|
|
16946
17140
|
It will be included in AI context for subsequent messages.` });
|
|
16947
17141
|
}
|
|
16948
17142
|
|
|
17143
|
+
// src/core/profile-command.ts
|
|
17144
|
+
var PROFILE_FIELDS = ["name", "nickname", "role", "bio", "interests", "locale", "extra"];
|
|
17145
|
+
function stripSurroundingQuotes(value) {
|
|
17146
|
+
const pairs = [['"', '"'], ["'", "'"], ["\u201C", "\u201D"], ["\u2018", "\u2019"], ["\u300C", "\u300D"]];
|
|
17147
|
+
for (const [open, close] of pairs) {
|
|
17148
|
+
if (value.length >= 2 && value.startsWith(open) && value.endsWith(close)) {
|
|
17149
|
+
return value.slice(open.length, value.length - close.length).trim();
|
|
17150
|
+
}
|
|
17151
|
+
}
|
|
17152
|
+
return value;
|
|
17153
|
+
}
|
|
17154
|
+
function showRows(profile) {
|
|
17155
|
+
const rows = [];
|
|
17156
|
+
if (profile.name) rows.push(["Name", profile.name]);
|
|
17157
|
+
if (profile.nickname) rows.push(["Nickname", profile.nickname]);
|
|
17158
|
+
if (profile.role) rows.push(["Role", profile.role]);
|
|
17159
|
+
if (profile.bio) rows.push(["Bio", profile.bio]);
|
|
17160
|
+
if (profile.interests?.length) rows.push(["Interests", profile.interests.join(", ")]);
|
|
17161
|
+
if (profile.locale) rows.push(["Locale", profile.locale]);
|
|
17162
|
+
if (profile.extra) {
|
|
17163
|
+
rows.push(["Extra", profile.extra.length > 80 ? `${profile.extra.slice(0, 80)}\u2026` : profile.extra]);
|
|
17164
|
+
}
|
|
17165
|
+
return rows;
|
|
17166
|
+
}
|
|
17167
|
+
function applyProfileCommand(args, profile) {
|
|
17168
|
+
const sub = (args[0] ?? "show").toLowerCase();
|
|
17169
|
+
if (sub === "show" || sub === "view") {
|
|
17170
|
+
const rows = showRows(profile);
|
|
17171
|
+
if (rows.length === 0) {
|
|
17172
|
+
return {
|
|
17173
|
+
summary: "No profile configured yet.",
|
|
17174
|
+
rows: [],
|
|
17175
|
+
details: ["Set one with /profile set <field> <value> \u2014 it is injected into every conversation."],
|
|
17176
|
+
changed: false,
|
|
17177
|
+
isError: false
|
|
17178
|
+
};
|
|
17179
|
+
}
|
|
17180
|
+
return {
|
|
17181
|
+
summary: "\u{1F464} Your Profile",
|
|
17182
|
+
rows,
|
|
17183
|
+
details: ["This identity is injected into every AI conversation."],
|
|
17184
|
+
changed: false,
|
|
17185
|
+
isError: false
|
|
17186
|
+
};
|
|
17187
|
+
}
|
|
17188
|
+
if (sub === "clear") {
|
|
17189
|
+
return {
|
|
17190
|
+
profile: {},
|
|
17191
|
+
summary: "Profile cleared. The AI will no longer know who you are.",
|
|
17192
|
+
rows: [],
|
|
17193
|
+
details: [],
|
|
17194
|
+
changed: true,
|
|
17195
|
+
isError: false
|
|
17196
|
+
};
|
|
17197
|
+
}
|
|
17198
|
+
if (sub === "set") {
|
|
17199
|
+
const field = args[1];
|
|
17200
|
+
const value = args.slice(2).join(" ").trim();
|
|
17201
|
+
if (!field || !PROFILE_FIELDS.includes(field)) {
|
|
17202
|
+
return {
|
|
17203
|
+
summary: "Usage: /profile set <field> <value>",
|
|
17204
|
+
rows: [],
|
|
17205
|
+
details: [`Fields: ${PROFILE_FIELDS.join(", ")}`],
|
|
17206
|
+
changed: false,
|
|
17207
|
+
isError: true
|
|
17208
|
+
};
|
|
17209
|
+
}
|
|
17210
|
+
if (!value) {
|
|
17211
|
+
return {
|
|
17212
|
+
summary: `Usage: /profile set ${field} <value>`,
|
|
17213
|
+
rows: [],
|
|
17214
|
+
details: [],
|
|
17215
|
+
changed: false,
|
|
17216
|
+
isError: true
|
|
17217
|
+
};
|
|
17218
|
+
}
|
|
17219
|
+
const updated = { ...profile };
|
|
17220
|
+
if (field === "interests") {
|
|
17221
|
+
updated.interests = value.split(",").map((s) => stripSurroundingQuotes(s.trim())).filter(Boolean);
|
|
17222
|
+
} else {
|
|
17223
|
+
updated[field] = stripSurroundingQuotes(value);
|
|
17224
|
+
}
|
|
17225
|
+
const shown = field === "interests" ? (updated.interests ?? []).join(", ") : stripSurroundingQuotes(value);
|
|
17226
|
+
return {
|
|
17227
|
+
profile: updated,
|
|
17228
|
+
summary: `Profile updated: ${field} = ${shown}`,
|
|
17229
|
+
rows: [],
|
|
17230
|
+
details: [],
|
|
17231
|
+
changed: true,
|
|
17232
|
+
isError: false
|
|
17233
|
+
};
|
|
17234
|
+
}
|
|
17235
|
+
if (sub === "edit") {
|
|
17236
|
+
return {
|
|
17237
|
+
summary: "Interactive editing lives in the setup wizard.",
|
|
17238
|
+
rows: [],
|
|
17239
|
+
details: [
|
|
17240
|
+
"Run `aicli config` in a terminal, or use /profile set <field> <value> here.",
|
|
17241
|
+
`Fields: ${PROFILE_FIELDS.join(", ")}`
|
|
17242
|
+
],
|
|
17243
|
+
changed: false,
|
|
17244
|
+
isError: false
|
|
17245
|
+
};
|
|
17246
|
+
}
|
|
17247
|
+
return {
|
|
17248
|
+
summary: `Unknown /profile option "${args[0]}" \u2014 expected show | set | clear | edit`,
|
|
17249
|
+
rows: [],
|
|
17250
|
+
details: [],
|
|
17251
|
+
changed: false,
|
|
17252
|
+
isError: true
|
|
17253
|
+
};
|
|
17254
|
+
}
|
|
17255
|
+
|
|
16949
17256
|
// src/web/commands/security-commands.ts
|
|
16950
17257
|
async function handleConfig(args, ctx) {
|
|
16951
17258
|
const sub = args[0];
|
|
@@ -17007,6 +17314,18 @@ async function handleBug(_args, ctx) {
|
|
|
17007
17314
|
];
|
|
17008
17315
|
ctx.send({ type: "info", message: lines.join("\n") });
|
|
17009
17316
|
}
|
|
17317
|
+
async function handleProfile(args, ctx) {
|
|
17318
|
+
const outcome = applyProfileCommand(args, ctx.config.get("userProfile") ?? {});
|
|
17319
|
+
if (outcome.profile) {
|
|
17320
|
+
ctx.config.set("userProfile", outcome.profile);
|
|
17321
|
+
ctx.config.save();
|
|
17322
|
+
}
|
|
17323
|
+
const lines = [outcome.summary];
|
|
17324
|
+
for (const [label, value] of outcome.rows) lines.push(` ${label}: ${value}`);
|
|
17325
|
+
for (const line of outcome.details) lines.push(line);
|
|
17326
|
+
ctx.send({ type: outcome.isError ? "error" : "info", message: lines.join("\n") });
|
|
17327
|
+
if (outcome.changed) ctx.sendStatus();
|
|
17328
|
+
}
|
|
17010
17329
|
|
|
17011
17330
|
// src/web/commands/agent-commands.ts
|
|
17012
17331
|
async function handleAuto(args, ctx) {
|
|
@@ -17050,6 +17369,17 @@ async function handlePermissions(args, ctx) {
|
|
|
17050
17369
|
}
|
|
17051
17370
|
async function handleYolo(args, ctx) {
|
|
17052
17371
|
const sub = args[0]?.toLowerCase();
|
|
17372
|
+
if (sub === "status") {
|
|
17373
|
+
ctx.send({
|
|
17374
|
+
type: "info",
|
|
17375
|
+
message: `YOLO mode: ${ctx.toolExecutor.sessionAutoApprove ? "ON (write tools auto-approved)" : "OFF"}`
|
|
17376
|
+
});
|
|
17377
|
+
return;
|
|
17378
|
+
}
|
|
17379
|
+
if (sub !== void 0 && sub !== "on" && sub !== "off") {
|
|
17380
|
+
ctx.send({ type: "error", message: `Unknown /yolo option "${sub}" \u2014 expected on | off | status` });
|
|
17381
|
+
return;
|
|
17382
|
+
}
|
|
17053
17383
|
if (sub === "off") {
|
|
17054
17384
|
ctx.toolExecutor.sessionAutoApprove = false;
|
|
17055
17385
|
ctx.send({ type: "info", message: "\u{1F512} Auto-approve disabled \u2014 confirmations restored for this session." });
|
|
@@ -17207,11 +17537,11 @@ async function handleSearch(args, ctx) {
|
|
|
17207
17537
|
}
|
|
17208
17538
|
const lines = [`\u{1F50D} Found ${results.length} session(s) containing "${query}"`, ""];
|
|
17209
17539
|
for (const r of results) {
|
|
17210
|
-
const { sessionMeta, matches } = r;
|
|
17540
|
+
const { sessionMeta, matches: matches2 } = r;
|
|
17211
17541
|
const dateStr = sessionMeta.updated instanceof Date ? sessionMeta.updated.toLocaleDateString() : String(sessionMeta.updated);
|
|
17212
17542
|
lines.push(`**${sessionMeta.id.slice(0, 8)}** [${dateStr}] ${sessionMeta.provider} / ${sessionMeta.model}`);
|
|
17213
17543
|
if (sessionMeta.title) lines.push(` ${sessionMeta.title}`);
|
|
17214
|
-
for (const m of
|
|
17544
|
+
for (const m of matches2) {
|
|
17215
17545
|
const icon = m.role === "user" ? "\u{1F464}" : "\u{1F916}";
|
|
17216
17546
|
lines.push(` ${icon} ${m.snippet}`);
|
|
17217
17547
|
}
|
|
@@ -17261,14 +17591,17 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
17261
17591
|
}
|
|
17262
17592
|
|
|
17263
17593
|
// src/web/session-handler.ts
|
|
17264
|
-
var CLI_ONLY_WEB_COMMANDS = /* @__PURE__ */ new
|
|
17265
|
-
"copy",
|
|
17266
|
-
"paste",
|
|
17267
|
-
"exit",
|
|
17268
|
-
"quit",
|
|
17269
|
-
"snapshot",
|
|
17270
|
-
"hooks",
|
|
17271
|
-
"hook"
|
|
17594
|
+
var CLI_ONLY_WEB_COMMANDS = /* @__PURE__ */ new Map([
|
|
17595
|
+
["copy", "It copies to the terminal clipboard; use your browser/OS copy instead."],
|
|
17596
|
+
["paste", "It reads the terminal clipboard; paste directly into the composer instead."],
|
|
17597
|
+
["exit", "Close the browser tab to end this Web session."],
|
|
17598
|
+
["quit", "Close the browser tab to end this Web session."],
|
|
17599
|
+
["snapshot", "Dev-state snapshots are written when the terminal REPL exits."],
|
|
17600
|
+
["hooks", "Hook trust is granted per machine from the terminal."],
|
|
17601
|
+
["hook", "Hook trust is granted per machine from the terminal."],
|
|
17602
|
+
// 以下两条并非「终端才有意义」,而是 Web 侧尚未实现——如实说明,别假装是设计。
|
|
17603
|
+
["route", "Smart model routing is not applied to Web sessions yet (config.routing is REPL-only)."],
|
|
17604
|
+
["security", "Redaction status and toggles are terminal-only for now; /status shows the current profile."]
|
|
17272
17605
|
]);
|
|
17273
17606
|
var webCommandHandlers = {
|
|
17274
17607
|
provider: handleProvider,
|
|
@@ -17304,6 +17637,7 @@ var webCommandHandlers = {
|
|
|
17304
17637
|
config: handleConfig,
|
|
17305
17638
|
doctor: handleDoctor,
|
|
17306
17639
|
bug: handleBug,
|
|
17640
|
+
profile: handleProfile,
|
|
17307
17641
|
auto: handleAuto,
|
|
17308
17642
|
permissions: handlePermissions,
|
|
17309
17643
|
yolo: handleYolo,
|
|
@@ -17338,17 +17672,18 @@ var SessionHandler = class {
|
|
|
17338
17672
|
sessionAccruedCostUsd = 0;
|
|
17339
17673
|
sessionCostIncomplete = false;
|
|
17340
17674
|
/** Accumulate a TokenUsage (with optional cache fields) into sessionTokenUsage. */
|
|
17341
|
-
addWebSessionUsage(u) {
|
|
17675
|
+
addWebSessionUsage(u, modelOverride) {
|
|
17676
|
+
const usageModel = modelOverride ?? this.currentModel;
|
|
17342
17677
|
this.sessionTokenUsage.inputTokens += u.inputTokens;
|
|
17343
17678
|
this.sessionTokenUsage.outputTokens += u.outputTokens;
|
|
17344
17679
|
this.sessionTokenUsage.cacheCreationTokens += u.cacheCreationTokens ?? 0;
|
|
17345
17680
|
this.sessionTokenUsage.cacheReadTokens += u.cacheReadTokens ?? 0;
|
|
17346
|
-
const priced = this.costTracker.addCost(this.currentProvider,
|
|
17681
|
+
const priced = this.costTracker.addCost(this.currentProvider, usageModel, u);
|
|
17347
17682
|
if (priced === null) this.sessionCostIncomplete = true;
|
|
17348
17683
|
else this.sessionAccruedCostUsd += priced;
|
|
17349
17684
|
const totalTokens = u.inputTokens + u.outputTokens + (u.cacheCreationTokens ?? 0) + (u.cacheReadTokens ?? 0);
|
|
17350
|
-
if (totalTokens > 0 && !getPricing(this.currentProvider,
|
|
17351
|
-
const key = `${this.currentProvider}/${
|
|
17685
|
+
if (totalTokens > 0 && !getPricing(this.currentProvider, usageModel)) {
|
|
17686
|
+
const key = `${this.currentProvider}/${usageModel}`;
|
|
17352
17687
|
if (!this.unknownPricingWarnings.has(key)) {
|
|
17353
17688
|
this.unknownPricingWarnings.add(key);
|
|
17354
17689
|
this.send({ type: "info", message: `Cost notice: pricing is unknown for ${key}. Tokens are tracked, but cost is excluded from totals. Run /cost or aicli usage to review.` });
|
|
@@ -17796,6 +18131,16 @@ var SessionHandler = class {
|
|
|
17796
18131
|
} else {
|
|
17797
18132
|
msgContent = content;
|
|
17798
18133
|
}
|
|
18134
|
+
const routingDecision = this.computeRoutingDecision(typeof msgContent === "string" ? msgContent : content);
|
|
18135
|
+
if (typeof msgContent === "string") {
|
|
18136
|
+
msgContent = stripRoutingTags(msgContent);
|
|
18137
|
+
} else if (msgContent.length > 0 && msgContent[msgContent.length - 1]?.type === "text") {
|
|
18138
|
+
const last = msgContent[msgContent.length - 1];
|
|
18139
|
+
msgContent = [...msgContent.slice(0, -1), { type: "text", text: stripRoutingTags(last.text) }];
|
|
18140
|
+
}
|
|
18141
|
+
if (routingDecision.overridden) {
|
|
18142
|
+
this.send({ type: "info", message: `\u2192 Routed to ${routingDecision.model} (${routingDecision.reason})` });
|
|
18143
|
+
}
|
|
17799
18144
|
const userMessage = { role: "user", content: msgContent, timestamp: /* @__PURE__ */ new Date() };
|
|
17800
18145
|
session.addMessage(userMessage);
|
|
17801
18146
|
const provider = this.providers.get(this.currentProvider);
|
|
@@ -17807,10 +18152,13 @@ var SessionHandler = class {
|
|
|
17807
18152
|
const hasToolSupport = typeof provider.chatWithTools === "function";
|
|
17808
18153
|
const { toolDefs, mcpBudgetNote } = hasToolSupport ? this.getFilteredToolDefs() : { toolDefs: [], mcpBudgetNote: null };
|
|
17809
18154
|
if (hasToolSupport && toolDefs.length > 0) {
|
|
17810
|
-
await this.handleChatWithTools(provider, session.messages, toolDefs, mcpBudgetNote);
|
|
18155
|
+
await this.handleChatWithTools(provider, session.messages, toolDefs, mcpBudgetNote, routingDecision.model);
|
|
17811
18156
|
} else {
|
|
17812
|
-
await this.handleChatSimple(provider, session.messages);
|
|
18157
|
+
await this.handleChatSimple(provider, session.messages, routingDecision.model);
|
|
17813
18158
|
}
|
|
18159
|
+
await this.checkContextPressure();
|
|
18160
|
+
const budgetWarning = this.costTracker.checkBudget(this.config.get("monthlyBudget"));
|
|
18161
|
+
if (budgetWarning) this.send({ type: "info", message: `\u26A0 ${budgetWarning}` });
|
|
17814
18162
|
} catch (err) {
|
|
17815
18163
|
const parts = [];
|
|
17816
18164
|
let current = err;
|
|
@@ -17829,8 +18177,9 @@ var SessionHandler = class {
|
|
|
17829
18177
|
this.sendSessionList();
|
|
17830
18178
|
}
|
|
17831
18179
|
}
|
|
17832
|
-
async handleChatSimple(provider, messages) {
|
|
18180
|
+
async handleChatSimple(provider, messages, modelOverride) {
|
|
17833
18181
|
const session = this.sessions.current;
|
|
18182
|
+
const effectiveModel = modelOverride ?? this.currentModel;
|
|
17834
18183
|
const modelParams = this.getModelParams();
|
|
17835
18184
|
const ac = new AbortController();
|
|
17836
18185
|
this.abortController = ac;
|
|
@@ -17838,7 +18187,7 @@ var SessionHandler = class {
|
|
|
17838
18187
|
const { stable: simplStable, volatile: simplVolatile } = this.buildSystemPrompt();
|
|
17839
18188
|
const stream = provider.chatStream({
|
|
17840
18189
|
messages,
|
|
17841
|
-
model:
|
|
18190
|
+
model: effectiveModel,
|
|
17842
18191
|
systemPrompt: simplStable,
|
|
17843
18192
|
systemPromptVolatile: simplVolatile,
|
|
17844
18193
|
stream: true,
|
|
@@ -17857,7 +18206,7 @@ var SessionHandler = class {
|
|
|
17857
18206
|
if (chunk.done) {
|
|
17858
18207
|
this.send({ type: "response_done", content: fullContent, usage: chunk.usage });
|
|
17859
18208
|
if (chunk.usage) {
|
|
17860
|
-
this.addWebSessionUsage(chunk.usage);
|
|
18209
|
+
this.addWebSessionUsage(chunk.usage, effectiveModel);
|
|
17861
18210
|
session.addTokenUsage(chunk.usage);
|
|
17862
18211
|
}
|
|
17863
18212
|
if (chunk.reasoningContent) simpleReasoning = chunk.reasoningContent;
|
|
@@ -17882,8 +18231,9 @@ var SessionHandler = class {
|
|
|
17882
18231
|
this.abortController = null;
|
|
17883
18232
|
}
|
|
17884
18233
|
}
|
|
17885
|
-
async handleChatWithTools(provider, messages, toolDefs, mcpBudgetNote) {
|
|
18234
|
+
async handleChatWithTools(provider, messages, toolDefs, mcpBudgetNote, modelOverride) {
|
|
17886
18235
|
const session = this.sessions.current;
|
|
18236
|
+
const effectiveModel = modelOverride ?? this.currentModel;
|
|
17887
18237
|
const apiMessages = [...messages];
|
|
17888
18238
|
const maxToolRounds = this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
|
|
17889
18239
|
const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
|
|
@@ -17913,7 +18263,7 @@ ${mcpBudgetNote}` : "");
|
|
|
17913
18263
|
callModel: async (_round, extraMessages) => {
|
|
17914
18264
|
const chatRequest = {
|
|
17915
18265
|
messages: apiMessages,
|
|
17916
|
-
model:
|
|
18266
|
+
model: effectiveModel,
|
|
17917
18267
|
systemPrompt,
|
|
17918
18268
|
systemPromptVolatile,
|
|
17919
18269
|
stream: false,
|
|
@@ -17958,7 +18308,7 @@ Details: ${errMsg.split("\n")[0]}
|
|
|
17958
18308
|
const summaryResult = await provider.chatWithTools(
|
|
17959
18309
|
{
|
|
17960
18310
|
messages: apiMessages,
|
|
17961
|
-
model:
|
|
18311
|
+
model: effectiveModel,
|
|
17962
18312
|
systemPrompt,
|
|
17963
18313
|
systemPromptVolatile,
|
|
17964
18314
|
stream: false,
|
|
@@ -17974,7 +18324,7 @@ Details: ${errMsg.split("\n")[0]}
|
|
|
17974
18324
|
executeTools: async (toolCalls) => {
|
|
17975
18325
|
googleSearchContext.configManager = this.config;
|
|
17976
18326
|
spawnAgentContext.provider = provider;
|
|
17977
|
-
spawnAgentContext.model =
|
|
18327
|
+
spawnAgentContext.model = effectiveModel;
|
|
17978
18328
|
spawnAgentContext.systemPrompt = systemPromptVolatile ? `${systemPrompt}
|
|
17979
18329
|
|
|
17980
18330
|
---
|
|
@@ -18329,10 +18679,11 @@ ${summaryContent}`,
|
|
|
18329
18679
|
}
|
|
18330
18680
|
// ── Commands ─────────────────────────────────────────────────────
|
|
18331
18681
|
async handleCommand(name, args) {
|
|
18332
|
-
|
|
18682
|
+
const cliOnlyReason = CLI_ONLY_WEB_COMMANDS.get(name);
|
|
18683
|
+
if (cliOnlyReason) {
|
|
18333
18684
|
this.send({
|
|
18334
18685
|
type: "error",
|
|
18335
|
-
message: `/${name} is
|
|
18686
|
+
message: `/${name} is not available in the Web UI. ${cliOnlyReason}`
|
|
18336
18687
|
});
|
|
18337
18688
|
return;
|
|
18338
18689
|
}
|
|
@@ -18343,6 +18694,43 @@ ${summaryContent}`,
|
|
|
18343
18694
|
this.send({ type: "error", message: `Unknown command: /${name}. Type /help for available commands.` });
|
|
18344
18695
|
}
|
|
18345
18696
|
}
|
|
18697
|
+
/**
|
|
18698
|
+
* 到达上下文压力阈值时自动压缩(与 REPL 同一判据,见 core/context-pressure.ts)。
|
|
18699
|
+
* 上下文窗口未知时不动——不基于猜测改用户的对话历史。
|
|
18700
|
+
*/
|
|
18701
|
+
/**
|
|
18702
|
+
* 智能模型路由决策(审计附录 D-5)。候选限定在当前 provider 注册的模型内——
|
|
18703
|
+
* 跨 provider 换模型会破坏 API key 与对话连续性。
|
|
18704
|
+
*/
|
|
18705
|
+
computeRoutingDecision(userInput) {
|
|
18706
|
+
let availableModels = [];
|
|
18707
|
+
try {
|
|
18708
|
+
availableModels = this.providers.get(this.currentProvider).info.models.map((m) => m.id);
|
|
18709
|
+
} catch {
|
|
18710
|
+
availableModels = [];
|
|
18711
|
+
}
|
|
18712
|
+
return decideModel({
|
|
18713
|
+
userInput,
|
|
18714
|
+
currentModel: this.currentModel,
|
|
18715
|
+
routingConfig: this.config.get("routing"),
|
|
18716
|
+
availableModels
|
|
18717
|
+
});
|
|
18718
|
+
}
|
|
18719
|
+
async checkContextPressure() {
|
|
18720
|
+
const contextWindow = this.getContextWindowSize();
|
|
18721
|
+
const session = this.sessions.current;
|
|
18722
|
+
const pressure = evaluateContextPressure({
|
|
18723
|
+
autoCompactEnabled: this.config.get("autoCompact") === true,
|
|
18724
|
+
estimatedTokens: contextWindow > 0 && session ? estimateConversationTokens({ systemPrompt: this.activeSystemPrompt, messages: session.messages }) : 0,
|
|
18725
|
+
contextWindow
|
|
18726
|
+
});
|
|
18727
|
+
if (!pressure.shouldCompact) return;
|
|
18728
|
+
this.send({
|
|
18729
|
+
type: "info",
|
|
18730
|
+
message: `\u26A0 Context pressure: ~${pressure.percent}% of the ${contextWindow.toLocaleString()}-token window used. Auto-compacting\u2026`
|
|
18731
|
+
});
|
|
18732
|
+
await this.compactSession(AUTO_COMPACT_INSTRUCTION);
|
|
18733
|
+
}
|
|
18346
18734
|
async compactSession(instruction) {
|
|
18347
18735
|
const session = this.sessions.current;
|
|
18348
18736
|
if (!session || session.messages.length < 6) {
|
|
@@ -18490,18 +18878,27 @@ ${summaryContent}`,
|
|
|
18490
18878
|
});
|
|
18491
18879
|
}
|
|
18492
18880
|
}
|
|
18493
|
-
memoryShow() {
|
|
18881
|
+
memoryShow(args = []) {
|
|
18494
18882
|
const configDir = this.config.getConfigDir();
|
|
18495
|
-
const
|
|
18496
|
-
|
|
18883
|
+
const filters = parseMemoryShowFilters(args);
|
|
18884
|
+
if (filters.invalidStale) {
|
|
18885
|
+
this.send({ type: "error", message: "Usage: /memory show --stale <days> (positive number)" });
|
|
18886
|
+
return;
|
|
18887
|
+
}
|
|
18888
|
+
const source = filters.orphaned ? listOrphanedProjectMemories(configDir) : listMemoryEntries(configDir, { includeExpired: true, includeRejected: true });
|
|
18889
|
+
const { entries, applied } = filterMemoryEntries(source, filters);
|
|
18890
|
+
const filterNote = applied.length > 0 ? ` (filtered: ${applied.join(", ")})` : "";
|
|
18891
|
+
const content = entries.length === 0 ? applied.length > 0 ? `(no entries match [${applied.join(", ")}])` : "(empty \u2014 no persistent memory entries yet)" : `Governed persistent memory${filterNote}
|
|
18892
|
+
|
|
18893
|
+
` + entries.map((entry) => {
|
|
18497
18894
|
const status = isMemoryExpired(entry) ? "expired" : entry.approved ? "approved" : "pending";
|
|
18498
|
-
const
|
|
18895
|
+
const source2 = [
|
|
18499
18896
|
entry.source ?? "unknown",
|
|
18500
18897
|
entry.sourceSession ? `session ${entry.sourceSession.slice(0, 8)}` : "",
|
|
18501
18898
|
entry.sourceProject ? `project ${entry.sourceProject}` : ""
|
|
18502
18899
|
].filter(Boolean).join(" \xB7 ");
|
|
18503
18900
|
return `## ${entry.id.slice(0, 8)} \xB7 ${status} \xB7 scope:${entry.scope} \xB7 sensitivity:${entry.sensitivity}
|
|
18504
|
-
Source: ${
|
|
18901
|
+
Source: ${source2}
|
|
18505
18902
|
${entry.content}`;
|
|
18506
18903
|
}).join("\n\n");
|
|
18507
18904
|
this.send({
|