mocode-ai 0.4.7 → 0.4.9
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/agent/core.d.ts +112 -0
- package/dist/agent/core.js +51 -11
- package/dist/agent/index.d.ts +19 -0
- package/dist/agent/mode.d.ts +29 -0
- package/dist/agent/spawn.d.ts +36 -0
- package/dist/attachments/image.d.ts +22 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/config/file.d.ts +23 -0
- package/dist/config/index.d.ts +114 -0
- package/dist/config/index.js +48 -1
- package/dist/config/presets.d.ts +57 -0
- package/dist/context/budget.d.ts +79 -0
- package/dist/context/classifier.d.ts +14 -0
- package/dist/context/encoders/_util.d.ts +11 -0
- package/dist/context/encoders/code.d.ts +2 -0
- package/dist/context/encoders/doc.d.ts +14 -0
- package/dist/context/encoders/graph.d.ts +11 -0
- package/dist/context/encoders/index.d.ts +2 -0
- package/dist/context/encoders/log.d.ts +2 -0
- package/dist/context/encoders/memory.d.ts +2 -0
- package/dist/context/encoders/passthrough.d.ts +11 -0
- package/dist/context/encoders/search.d.ts +2 -0
- package/dist/context/encoders/summary.d.ts +12 -0
- package/dist/context/encoders/table.d.ts +2 -0
- package/dist/context/encoders/tree.d.ts +2 -0
- package/dist/context/index.d.ts +6 -0
- package/dist/context/lifecycle.d.ts +51 -0
- package/dist/context/pipeline.d.ts +9 -0
- package/dist/context/registry.d.ts +9 -0
- package/dist/context/relevance.d.ts +65 -0
- package/dist/context/types.d.ts +36 -0
- package/dist/index.d.ts +1 -0
- package/dist/llm/capabilities.d.ts +19 -0
- package/dist/llm/index.d.ts +111 -0
- package/dist/memory/discover.d.ts +15 -0
- package/dist/memory/index.d.ts +13 -0
- package/dist/memory/reflect.d.ts +34 -0
- package/dist/memory/store.d.ts +124 -0
- package/dist/permissions/index.js +138 -0
- package/dist/pet/bridge.d.ts +73 -0
- package/dist/pet/protocol.d.ts +83 -0
- package/dist/pet/state.d.ts +28 -0
- package/dist/plan/active.d.ts +23 -0
- package/dist/plan/index.d.ts +13 -0
- package/dist/plan/state.d.ts +2 -0
- package/dist/plan/store.d.ts +78 -0
- package/dist/project-skill/index.js +129 -0
- package/dist/project-skill/initializer.js +179 -0
- package/dist/project-snapshot/index.js +177 -0
- package/dist/project-snapshot/static-files.js +87 -0
- package/dist/repl/index.d.ts +22 -0
- package/dist/repl/index.js +159 -1
- package/dist/rollback/index.d.ts +79 -0
- package/dist/sandbox/command.d.ts +3 -0
- package/dist/sandbox/index.d.ts +4 -0
- package/dist/sandbox/jail.d.ts +17 -0
- package/dist/sandbox/policy.d.ts +21 -0
- package/dist/sandbox/root.d.ts +5 -0
- package/dist/session/compact.d.ts +92 -0
- package/dist/session/drop.d.ts +19 -0
- package/dist/session/index.d.ts +13 -0
- package/dist/session/persist.d.ts +30 -0
- package/dist/session/scheduler.d.ts +53 -0
- package/dist/skills/discover.d.ts +33 -0
- package/dist/skills/index.d.ts +13 -0
- package/dist/tools/builtins/ask-human.d.ts +9 -0
- package/dist/tools/builtins/codegraph.d.ts +2 -0
- package/dist/tools/builtins/codegraph.js +17 -4
- package/dist/tools/builtins/drop-context.d.ts +18 -0
- package/dist/tools/builtins/edit-file.d.ts +2 -0
- package/dist/tools/builtins/edit-file.js +1 -0
- package/dist/tools/builtins/glob.d.ts +2 -0
- package/dist/tools/builtins/grep.d.ts +2 -0
- package/dist/tools/builtins/index.d.ts +2 -0
- package/dist/tools/builtins/index.js +7 -0
- package/dist/tools/builtins/memory-forget.d.ts +2 -0
- package/dist/tools/builtins/memory-forget.js +1 -0
- package/dist/tools/builtins/memory-list.d.ts +2 -0
- package/dist/tools/builtins/memory-save.d.ts +2 -0
- package/dist/tools/builtins/memory-save.js +1 -0
- package/dist/tools/builtins/memory-search.d.ts +2 -0
- package/dist/tools/builtins/memory-update.d.ts +2 -0
- package/dist/tools/builtins/memory-update.js +1 -0
- package/dist/tools/builtins/project-skill-update.js +73 -0
- package/dist/tools/builtins/read-file.d.ts +2 -0
- package/dist/tools/builtins/read-file.js +17 -1
- package/dist/tools/builtins/run-command.d.ts +2 -0
- package/dist/tools/builtins/run-command.js +1 -0
- package/dist/tools/builtins/switch-mode.d.ts +2 -0
- package/dist/tools/builtins/task.d.ts +2 -0
- package/dist/tools/builtins/task.js +1 -0
- package/dist/tools/builtins/todolist.d.ts +2 -0
- package/dist/tools/builtins/use-skill.d.ts +2 -0
- package/dist/tools/builtins/web-fetch.d.ts +2 -0
- package/dist/tools/builtins/web-search.d.ts +2 -0
- package/dist/tools/builtins/web-search.js +12 -1
- package/dist/tools/builtins/write-file.d.ts +2 -0
- package/dist/tools/builtins/write-file.js +1 -0
- package/dist/tools/constants.d.ts +52 -0
- package/dist/tools/registry.d.ts +18 -0
- package/dist/tools/types.d.ts +42 -0
- package/dist/ui/batch.d.ts +67 -0
- package/dist/ui/clipboard.d.ts +9 -0
- package/dist/ui/content.d.ts +55 -0
- package/dist/ui/content.js +14 -3
- package/dist/ui/diff.d.ts +34 -0
- package/dist/ui/diff.js +15 -24
- package/dist/ui/intervention.d.ts +46 -0
- package/dist/ui/layout.d.ts +225 -0
- package/dist/ui/layout.js +10 -0
- package/dist/ui/markdown.d.ts +46 -0
- package/dist/ui/mouse.d.ts +29 -0
- package/dist/ui/prompt.d.ts +61 -0
- package/dist/ui/render.d.ts +60 -0
- package/dist/ui/spinner.d.ts +26 -0
- package/dist/ui/theme.d.ts +47 -0
- package/dist/updater/index.d.ts +22 -0
- package/package.json +1 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type OpenAI from 'openai';
|
|
2
|
+
import { type ChatMessage, type ChatUsage, type ToolCallRef } from '../llm/index.js';
|
|
3
|
+
/** 解析工具 arguments JSON;非法或空返 null(调用方据此降级到普通 preview)。 */
|
|
4
|
+
declare function parseArgs(raw: string): Record<string, unknown> | null;
|
|
5
|
+
/** 只读工具集:一轮多个时,连续的只读工具成组 Promise.all 并行(无副作用、互不依赖)。 */
|
|
6
|
+
declare const READ_TOOL_NAMES: Set<string>;
|
|
7
|
+
/** mutation 工具:写盘 + 在 executeTool 内记回滚 before 快照,必须串行保快照序。 */
|
|
8
|
+
declare const isMutationTool: (name: string) => boolean;
|
|
9
|
+
/** 工具调用 ● 头所需信息(交给 hooks 渲染;core 不直接写屏)。 */
|
|
10
|
+
export interface ToolCallView {
|
|
11
|
+
name: string;
|
|
12
|
+
arguments: string;
|
|
13
|
+
id: string;
|
|
14
|
+
}
|
|
15
|
+
/** mutation 执行前读旧内容供 diff:write_file 取整文件旧内容(不存在→null=新建),
|
|
16
|
+
* edit_file 取 old_string 起始行号(供 diff 显示真实文件行号)。读不到则 diff 退化为相对行号。
|
|
17
|
+
* 非 mutation 或参数非法返 { preWriteOld: null, editStartLine: 1 }。失败不阻断。 */
|
|
18
|
+
declare function readDiffContext(tc: ToolCallRef, parsed: Record<string, unknown> | null): {
|
|
19
|
+
preWriteOld: string | null;
|
|
20
|
+
editStartLine: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* agent 循环的展示副作用接缝。主 agent 注入 TUI 渲染实现;子 agent 注入静默/摘要实现。
|
|
24
|
+
* 所有方法可选——core 对 undefined hooks 安全跳过。
|
|
25
|
+
*/
|
|
26
|
+
export interface AgentHooks {
|
|
27
|
+
/** 流式正文增量(主 agent:走 markdown 渲染写内容区)。 */
|
|
28
|
+
onText?: (delta: string) => void;
|
|
29
|
+
/** 模型开始生成某 tool_call 的参数(主 agent:补换行 + 启「生成中」spinner)。 */
|
|
30
|
+
onToolCall?: (name: string) => void;
|
|
31
|
+
/** 每步开始,spinner 启「思考中」(主 agent:spinner.start)。 */
|
|
32
|
+
onStepStart?: () => void;
|
|
33
|
+
/** chat 返回后停 spinner(主 agent:spinner.stop)。 */
|
|
34
|
+
onChatDone?: () => void;
|
|
35
|
+
/** 流式正文末尾补换行(若 onToolCall 已补则 no-op);防 ● 行黏在正文行尾。 */
|
|
36
|
+
onTextEnd?: () => void;
|
|
37
|
+
/** 工具调用 ● 头渲染(主 agent:工具名 + 参数摘要)。 */
|
|
38
|
+
onToolHeader?: (tc: ToolCallRef) => void;
|
|
39
|
+
/** 启「执行 工具」spinner(主 agent:spinner.start)。 */
|
|
40
|
+
onToolStart?: (name: string) => void;
|
|
41
|
+
/** 工具执行完停 spinner(主 agent:spinner.stop)。 */
|
|
42
|
+
onToolDone?: () => void;
|
|
43
|
+
/** 工具结果渲染(主 agent:mutation 走 diff 块;其余走一行 preview)。 */
|
|
44
|
+
onToolResult?: (tc: ToolCallRef, output: string, parsed: Record<string, unknown> | null, preWriteOld: string | null, editStartLine: number) => void;
|
|
45
|
+
/** 工具步末尾补一空行(与下一轮思考/正文分隔)。 */
|
|
46
|
+
onToolBatchEnd?: () => void;
|
|
47
|
+
/** 无回复提示(模型既无文本也无工具调用)。 */
|
|
48
|
+
onNoReply?: () => void;
|
|
49
|
+
/** 达到最大步数提示。 */
|
|
50
|
+
onMaxSteps?: () => void;
|
|
51
|
+
/** 中断还原:停 spinner + 补换行 + (已中断)提示 + history 还原 + 模式还原。 */
|
|
52
|
+
onAbort?: () => void;
|
|
53
|
+
/** 跑完(正常/达上限)在回复末尾打耗时摘要行;中断不调。
|
|
54
|
+
* usage 是本轮 chat 调用累计的 token 用量(未开启 include_usage 或全失败时为 undefined)。 */
|
|
55
|
+
onDone?: (elapsedMs: number, usage?: ChatUsage) => void;
|
|
56
|
+
}
|
|
57
|
+
/** runAgentCore 的运行选项。 */
|
|
58
|
+
export interface AgentRunOptions {
|
|
59
|
+
history: ChatMessage[];
|
|
60
|
+
/** 纯文本字符串,或多模态 parts 数组(OpenAI content 数组,text + image_url)。 */
|
|
61
|
+
userInput: string | ContentPart[];
|
|
62
|
+
signal?: AbortSignal;
|
|
63
|
+
/** 每步 chat() 返回后回调:repl 据此重算并重画状态行 context 用量条。 */
|
|
64
|
+
onContextUpdate?: () => void;
|
|
65
|
+
hooks: AgentHooks;
|
|
66
|
+
/** 步数上限;缺省 = config.maxSteps。子 agent 可传更低值。 */
|
|
67
|
+
maxSteps?: number;
|
|
68
|
+
/** 工具 schema 覆盖;plan 模式传 planChatTools(只读子集)。子 agent 可传受限子集。
|
|
69
|
+
* 缺省 = 按 getAgentMode() 自动选(auto=全量 / plan=只读)。 */
|
|
70
|
+
toolsOverride?: OpenAI.Chat.Completions.ChatCompletionTool[];
|
|
71
|
+
/** 跳过回滚快照记录(子 agent 逻辑隔离用)。true = 本 agent 的 write_file/edit_file 改动
|
|
72
|
+
* 不进主回滚链,主 /rollback 不撤销。透传给 executeTool。 */
|
|
73
|
+
skipRollback?: boolean;
|
|
74
|
+
}
|
|
75
|
+
/** OpenAI content array 的子集(text + image_url);repl 构造 user 多模态消息用。 */
|
|
76
|
+
export type ContentPart = {
|
|
77
|
+
type: 'text';
|
|
78
|
+
text: string;
|
|
79
|
+
} | {
|
|
80
|
+
type: 'image_url';
|
|
81
|
+
image_url: {
|
|
82
|
+
url: string;
|
|
83
|
+
detail?: 'auto' | 'low' | 'high';
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
/** runAgentCore 的运行结果。 */
|
|
87
|
+
export interface AgentRunResult {
|
|
88
|
+
/** 正常完毕 / 达上限 true;中断 false。 */
|
|
89
|
+
completed: boolean;
|
|
90
|
+
/** 最终 assistant 文本回复(content);无回复或中断为 null。 */
|
|
91
|
+
finalText: string | null;
|
|
92
|
+
/** 本轮累计 token 用量(各 chat 步 prompt+completion 之和);后端不开 include_usage 或全失败则 undefined。 */
|
|
93
|
+
usage?: ChatUsage;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* agent 核心循环(纯逻辑):
|
|
97
|
+
* 流式调 LLM(经 hooks.onText 实时渲染)→ 有 tool_calls 就分组执行并回灌
|
|
98
|
+
* → 否则流式正文即最终回复。history 在调用间持久,由调用方持有。
|
|
99
|
+
* 步前经 session/maybeCompact 自动压缩(接近窗口上限时三层压缩);
|
|
100
|
+
* 工具结果进 history 前经 Context Optimization Pipeline(optimizeToolResult:类型化编码 + 长度裁剪)。
|
|
101
|
+
*
|
|
102
|
+
* 中断语义:signal 经 executeTool(name, args, signal) 串进工具;run_command/web_fetch 等 abort 即时杀
|
|
103
|
+
* (树杀子进程 / 取消 fetch),循环顶 if(signal.aborted) 兜底还原。不会留下未配对的 tool_call_id。
|
|
104
|
+
* abort 时 history 还原到本 turn 前(savedHistory 浅拷贝),模式还原,调 hooks.onAbort。
|
|
105
|
+
*
|
|
106
|
+
* 所有展示副作用经 hooks 注入;core 自身不直接调 layout / spinner(不依赖 ui/layout.ts)。
|
|
107
|
+
* 但 core 仍依赖 ui/render.ts 的纯函数(summarizeToolCall / truncateDisplay / fmtElapsed)——
|
|
108
|
+
* 这些是纯字符串格式化,无副作用,共享安全。
|
|
109
|
+
*/
|
|
110
|
+
export declare function runAgentCore(opts: AgentRunOptions): Promise<AgentRunResult>;
|
|
111
|
+
export { parseArgs, readDiffContext, isMutationTool, READ_TOOL_NAMES };
|
|
112
|
+
export type { ChatMessage, ToolCallRef, ChatUsage } from '../llm/index.js';
|
package/dist/agent/core.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
// spawn.ts 的 spawnAgent = runAgentCore + 静默 hooks(子 agent)。
|
|
7
7
|
import { readFileSync } from 'node:fs';
|
|
8
8
|
import { chat, planChatTools, } from '../llm/index.js';
|
|
9
|
-
import { executeTool } from '../tools/registry.js';
|
|
9
|
+
import { executeTool, tools } from '../tools/registry.js';
|
|
10
|
+
import { checkPermission } from '../permissions/index.js';
|
|
10
11
|
import { getPlanDisabledTools } from '../tools/constants.js';
|
|
11
12
|
import { getAgentMode, setAgentMode } from './mode.js';
|
|
12
13
|
import { maybeCompact, contextState, dropContextFromHistory } from '../session/index.js';
|
|
@@ -328,15 +329,38 @@ export async function runAgentCore(opts) {
|
|
|
328
329
|
while (j < calls.length && calls[j].name === 'task')
|
|
329
330
|
j++;
|
|
330
331
|
const batch = calls.slice(i, j);
|
|
331
|
-
|
|
332
|
-
//
|
|
332
|
+
// 权限预检查:逐个 task 弹确认面板(在启动子 agent 之前,体验:先问再执行)。
|
|
333
|
+
// 拒绝的 task 直接跳过,不启动子 agent;放行的收集到 allowedBatch。
|
|
334
|
+
const allowedBatch = [];
|
|
333
335
|
for (const tc of batch) {
|
|
336
|
+
const parsed = parseArgs(tc.arguments);
|
|
337
|
+
const tool = tools.find((t) => t.name === tc.name);
|
|
338
|
+
if (tool) {
|
|
339
|
+
const perm = await checkPermission(tool, parsed ?? {}, signal);
|
|
340
|
+
if (perm === 'deny') {
|
|
341
|
+
hooks.onToolHeader?.(tc);
|
|
342
|
+
const err = `错误:用户拒绝了工具 ${tc.name} 的执行。`;
|
|
343
|
+
hooks.onToolResult?.(tc, err, null, null, 1);
|
|
344
|
+
const hint = recordAndHint(tc.name, tc.arguments);
|
|
345
|
+
pushToolResult(history, tc, hint ? `${err}${hint}` : err, relprune, lifecycle, scheduler);
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
allowedBatch.push(tc);
|
|
350
|
+
}
|
|
351
|
+
if (allowedBatch.length === 0) {
|
|
352
|
+
i = j;
|
|
353
|
+
continue; // 全部被拒绝,跳过执行
|
|
354
|
+
}
|
|
355
|
+
const started = allowedBatch.map((tc) => executeTool(tc.name, tc.arguments, signal, { skipRollback, dropContext }));
|
|
356
|
+
// 先批量打印所有头 + 启 spinner(多 task 并发,spinner 只显一个,但 ● 头都打出来)
|
|
357
|
+
for (const tc of allowedBatch) {
|
|
334
358
|
hooks.onToolHeader?.(tc);
|
|
335
359
|
}
|
|
336
|
-
hooks.onToolStart?.(
|
|
360
|
+
hooks.onToolStart?.(allowedBatch[0].name); // spinner:多 task 共用一个「执行 task…」
|
|
337
361
|
// 逐个 await 出结果(按 tool_calls 原序,保 tool_call_id 配对);结果到即渲染 ↳
|
|
338
|
-
for (let k = 0; k <
|
|
339
|
-
const tc =
|
|
362
|
+
for (let k = 0; k < allowedBatch.length; k++) {
|
|
363
|
+
const tc = allowedBatch[k];
|
|
340
364
|
const output = await started[k];
|
|
341
365
|
hooks.onToolResult?.(tc, output, null, null, 1); // task 结果是摘要,无 diff
|
|
342
366
|
// Thrashing:同上
|
|
@@ -361,15 +385,31 @@ export async function runAgentCore(opts) {
|
|
|
361
385
|
i++;
|
|
362
386
|
continue;
|
|
363
387
|
}
|
|
388
|
+
// 权限预检查:在渲染 ● 头之前弹确认面板(体验:先问再执行,而非执行完再问)。
|
|
389
|
+
// 拒绝时只渲染拒绝结果,不渲染执行头;放行则继续走 header → start → executeTool 流程。
|
|
390
|
+
const parsed = parseArgs(tc.arguments);
|
|
391
|
+
const tool = tools.find((t) => t.name === tc.name);
|
|
392
|
+
if (tool) {
|
|
393
|
+
const perm = await checkPermission(tool, parsed ?? {}, signal);
|
|
394
|
+
if (perm === 'deny') {
|
|
395
|
+
hooks.onToolHeader?.(tc);
|
|
396
|
+
const err = `错误:用户拒绝了工具 ${tc.name} 的执行。`;
|
|
397
|
+
hooks.onToolResult?.(tc, err, null, null, 1);
|
|
398
|
+
const hint = recordAndHint(tc.name, tc.arguments);
|
|
399
|
+
pushToolResult(history, tc, hint ? `${err}${hint}` : err, relprune, lifecycle, scheduler);
|
|
400
|
+
i++;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
364
404
|
hooks.onToolHeader?.(tc);
|
|
365
|
-
const
|
|
366
|
-
?
|
|
405
|
+
const mutationParsed = isMutationTool(tc.name)
|
|
406
|
+
? parsed
|
|
367
407
|
: null;
|
|
368
|
-
const { preWriteOld, editStartLine } = readDiffContext(tc,
|
|
408
|
+
const { preWriteOld, editStartLine } = readDiffContext(tc, mutationParsed);
|
|
369
409
|
hooks.onToolStart?.(tc.name);
|
|
370
410
|
const output = await executeTool(tc.name, tc.arguments, signal, { skipRollback, dropContext });
|
|
371
411
|
hooks.onToolDone?.();
|
|
372
|
-
hooks.onToolResult?.(tc, output,
|
|
412
|
+
hooks.onToolResult?.(tc, output, mutationParsed, preWriteOld, editStartLine);
|
|
373
413
|
// Thrashing:同上(history 附 hint,UI 干净)
|
|
374
414
|
const hint = recordAndHint(tc.name, tc.arguments);
|
|
375
415
|
pushToolResult(history, tc, hint ? `${output}${hint}` : output, relprune, lifecycle, scheduler);
|
|
@@ -378,7 +418,7 @@ export async function runAgentCore(opts) {
|
|
|
378
418
|
// 非 mutation 工具(run_command/use_skill/memory_* 等)此处 path="" 不触发。
|
|
379
419
|
// 观察者生命周期:mutation push 后通知 lifecycle 把同 path 的旧 read 标 REFERENCED。
|
|
380
420
|
if (relprune && isMutationTool(tc.name)) {
|
|
381
|
-
const mp =
|
|
421
|
+
const mp = mutationParsed?.path;
|
|
382
422
|
if (typeof mp === 'string' && mp) {
|
|
383
423
|
relprune.observeMutation(history, mp);
|
|
384
424
|
if (lifecycle)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ChatMessage } from '../llm/index.js';
|
|
2
|
+
import { type AgentRunResult, type ContentPart } from './core.js';
|
|
3
|
+
/**
|
|
4
|
+
* agent 核心循环(主 agent,TUI 渲染版):
|
|
5
|
+
* 流式调 LLM(onText 实时写内容区)→ 有 tool_calls 就执行并回灌
|
|
6
|
+
* → 否则流式正文即最终回复。history 在调用间持久,由 REPL 持有。
|
|
7
|
+
* 步前经 session/maybeCompact 自动压缩(接近窗口上限时三层压缩);
|
|
8
|
+
* 工具结果进 history 前经 capToolResultForHistory 裁到单条上限。
|
|
9
|
+
*
|
|
10
|
+
* 所有正文写经 layout.contentWrite(保证落在内容区、跟踪续写位、底栏不被顶);
|
|
11
|
+
* spinner 经 onFrame 回调刷状态行(layout.drawStatusBar),等待时内容区静止、底栏转圈。
|
|
12
|
+
* 思考期间不写思考内容,只让 spinner 持续转「思考中…」(首个正文 / tool_call token 到达才停)。
|
|
13
|
+
*
|
|
14
|
+
* 循环逻辑委托 runAgentCore;本函数只注入 TUI 渲染 hooks(layout + spinner + diff + 回滚轮次)。
|
|
15
|
+
* 开新轮次(beginTurn)在入口调——core 不依赖 rollback,回滚轮次是主 agent 的展示副作用。
|
|
16
|
+
*/
|
|
17
|
+
export declare function runAgent(history: ChatMessage[], userInput: string | ContentPart[], signal?: AbortSignal,
|
|
18
|
+
/** 每步 chat() 返回后回调:repl 据此重算并重画状态行 context 用量条(运行中实时刷新,不冻结在轮首)。 */
|
|
19
|
+
onContextUpdate?: () => void): Promise<AgentRunResult>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent 模式的共享状态(零依赖纯叶子)。
|
|
3
|
+
*
|
|
4
|
+
* 把 `agentMode` 从 repl 的模块变量提到这里,让三方都能用、且不破坏依赖单向:
|
|
5
|
+
* - `tools/builtins/switch-mode.ts` 写它(setAgentMode)——LLM 在 agent 循环里自切 plan↔auto。
|
|
6
|
+
* - `agent/index.ts` 每步读它(getAgentMode)——决定 chat() 用全量 chatTools 还是 planChatTools 只读子集,
|
|
7
|
+
* 以及串行分支的 plan 防御 backstop。这样 LLM 中途切模式后,下一次 chat() 立即看到新工具集。
|
|
8
|
+
* - `repl/index.ts` 读写它 + 注册 onModeChange 监听器(做 applyMode 重写 history[0] + refreshStatusBase
|
|
9
|
+
* 刷状态行 modeTag)。
|
|
10
|
+
*
|
|
11
|
+
* 依赖方向无环:本模块不 import 任何业务模块。`agent/index → tools/registry → tools/builtins/switch-mode
|
|
12
|
+
* → agent/mode` 链终点是本叶子,不回到 agent/index。`tools → agent/mode` 与既有 `tools → ui`(ask-human)、
|
|
13
|
+
* `tools → rollback`(registry)同性质——都是引叶子。
|
|
14
|
+
*
|
|
15
|
+
* listener 同步触发:setAgentMode 在变更模式后同步调 listener(无 async)。switch_mode 工具在 executeTool
|
|
16
|
+
* 内调 setAgentMode → listener 同步 applyMode(重写 history[0])+ refreshStatusBase;execute 返回字符串后,
|
|
17
|
+
* agent 串行分支继续,下一步 chat() 读到新模式 + 新系统提示,一致。
|
|
18
|
+
*/
|
|
19
|
+
export type AgentMode = 'auto' | 'plan';
|
|
20
|
+
/** 当前 agent 模式。auto=全工具执行;plan=只读探查 + 产出计划(写盘/命令/记忆写入工具被 schema 剔除)。 */
|
|
21
|
+
export declare function getAgentMode(): AgentMode;
|
|
22
|
+
/**
|
|
23
|
+
* 设置 agent 模式。同模式 no-op(不触发 listener)。变更时同步触发 onModeChange 注册的监听器
|
|
24
|
+
* (repl 在 startRepl 注册:applyMode 重写 history[0] + refreshStatusBase 刷状态行)。
|
|
25
|
+
* 返回之前的模式,供调用方(如 runAgent 中断恢复)还原。
|
|
26
|
+
*/
|
|
27
|
+
export declare function setAgentMode(m: AgentMode): AgentMode;
|
|
28
|
+
/** 注册模式变更监听器(单一,后注册覆盖先注册)。repl 在 startRepl 启动时注册一次。 */
|
|
29
|
+
export declare function onModeChange(cb: (m: AgentMode) => void): void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** 子 agent 运行选项。 */
|
|
2
|
+
export interface SpawnOptions {
|
|
3
|
+
/** 子任务指令(作为子 agent 的 user 消息)。 */
|
|
4
|
+
prompt: string;
|
|
5
|
+
/** 附加系统提示(角色/约束),拼在 SUBAGENT_SUFFIX 后。 */
|
|
6
|
+
systemPromptSuffix?: string;
|
|
7
|
+
/** 允许的工具名白名单(可选)。无 = 全量工具;给则从 chatTools 过滤。 */
|
|
8
|
+
tools?: string[];
|
|
9
|
+
/** 步数上限(可选,默认 config.subAgentMaxSteps ?? 50)。 */
|
|
10
|
+
maxSteps?: number;
|
|
11
|
+
/** 主 agent 的 abort signal(可选)。透传给子 runAgentCore → chat/executeTool,
|
|
12
|
+
* 主 Ctrl+C 树杀子 agent(chat 流式 abort + run_command/web_fetch 即时取消)。 */
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}
|
|
15
|
+
/** 子 agent 运行结果。 */
|
|
16
|
+
export interface SpawnResult {
|
|
17
|
+
/** 子 agent 最终文本回复;中断或无回复为 null。 */
|
|
18
|
+
summary: string | null;
|
|
19
|
+
/** 正常完毕 true;中断 false。 */
|
|
20
|
+
completed: boolean;
|
|
21
|
+
/** 子 agent 中间过程的人类可读日志(工具调用 + 结果摘要 + 流式正文片段)。主 agent 通常不看,调试用。 */
|
|
22
|
+
transcript: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 派生一个子 agent 执行独立子任务。
|
|
26
|
+
*
|
|
27
|
+
* 行为:
|
|
28
|
+
* - 构造独立 history:[{system: 主系统提示 + SUBAGENT_SUFFIX + 自定义后缀}, {user: prompt}]
|
|
29
|
+
* - 工具子集:按 opts.tools 白名单过滤 chatTools;无白名单 = 全量。
|
|
30
|
+
* - 静默 hooks:流式正文 / 工具头 / 结果缓冲到 transcript;不写主屏(layout)。
|
|
31
|
+
* - 返回 { summary, completed, transcript }。summary 给 task 工具回灌主 history。
|
|
32
|
+
*
|
|
33
|
+
* 中断:opts.signal 透传给子 runAgentCore——主 Ctrl+C 树杀子 agent(chat abort + 工具 abort)。
|
|
34
|
+
* 子 agent 跑在主 signal 下,主 abort 即子 abort;子 agent 的 abortRestore 还原子 history + 模式。
|
|
35
|
+
*/
|
|
36
|
+
export declare function spawnAgent(opts: SpawnOptions): Promise<SpawnResult>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type ImageMime = 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp';
|
|
2
|
+
export interface ImageAttachment {
|
|
3
|
+
id: string;
|
|
4
|
+
path: string;
|
|
5
|
+
name: string;
|
|
6
|
+
bytes: number;
|
|
7
|
+
mime: ImageMime;
|
|
8
|
+
dataUrl: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const MAX_INLINE_BYTES_DEFAULT: number;
|
|
11
|
+
export declare function detectMime(p: string): ImageMime | null;
|
|
12
|
+
export declare function renderChip(att: ImageAttachment): string;
|
|
13
|
+
export type LoadImageResult = {
|
|
14
|
+
ok: true;
|
|
15
|
+
att: ImageAttachment;
|
|
16
|
+
} | {
|
|
17
|
+
ok: false;
|
|
18
|
+
reason: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function loadImageAttachment(input: string, opts: {
|
|
21
|
+
maxBytes: number;
|
|
22
|
+
}): Promise<LoadImageResult>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 首跑配置向导:交互填 LLM_BASE_URL / LLM_API_KEY / LLM_MODEL,写 ~/.mocode/config。
|
|
3
|
+
* 只交互三键、保留文件里其它键(MAX_TOKENS / CONTEXT_WINDOW_TOKENS / MOCODE_THEME 等)。
|
|
4
|
+
* prompt 全纯文本(readline 光标按字符算,不能含 ANSI)。
|
|
5
|
+
* 由 index.ts 在 `mocode config` 时动态加载,故不 import config/index.ts(避免触发 config 单例初始化 / loadEnvFiles,
|
|
6
|
+
* 直接退出);只 import config/file.ts(纯 I/O 叶子,无 env 校验 / process.exit)。
|
|
7
|
+
*/
|
|
8
|
+
export declare function runConfigWizard(): Promise<void>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ~/.mocode/config 全局 dotenv 配置的读写叶子(纯 node:fs / node:os / node:path + dotenv)。
|
|
3
|
+
*
|
|
4
|
+
* 独立于 `config/index.ts`——**不**触发 config 单例初始化 / loadEnvFiles,故 `commands/config.ts`
|
|
5
|
+
* (首跑向导,刻意不 import config/index.ts)与 `repl/index.ts`(`/theme` 持久化)都能安全共享。
|
|
6
|
+
* 主题等 UI 偏好走 `updateConfigKey` 单键写;向导多键一次性写走 `writeConfigKeys`。
|
|
7
|
+
*/
|
|
8
|
+
/** ~/.mocode/config:全局配置(与 ~/.mocode/skills 同目录,任意终端 / 任意目录生效)。 */
|
|
9
|
+
export declare const CONFIG_PATH: string;
|
|
10
|
+
/** 读已有配置(没有则空对象),用于回填默认值与保留其它键。 */
|
|
11
|
+
export declare function readConfigFile(): Record<string, string>;
|
|
12
|
+
/**
|
|
13
|
+
* 读-合并-写:把 entries 覆盖进 ~/.mocode/config,保留文件里其它键。
|
|
14
|
+
* mkdirSync 兜底(首跑无 ~/.mocode)。不吞 I/O 异常——向导路径让其抛(与历史行为一致);
|
|
15
|
+
* UI 偏好路径用 `updateConfigKey`(自带 try/catch 静默)。
|
|
16
|
+
*/
|
|
17
|
+
export declare function writeConfigKeys(entries: Record<string, string>): void;
|
|
18
|
+
/**
|
|
19
|
+
* 单键持久化(主题等 UI 偏好):写失败静默——下次启动读不到则回退默认,不阻断 REPL。
|
|
20
|
+
* 注意:shell env 设了同名键时,`config/index.ts` 的 loadEnvFiles 只 backfill undefined 的
|
|
21
|
+
* process.env,故文件写对**下次启动**可能被 shell 盖——调用方据此给 dim 警告。
|
|
22
|
+
*/
|
|
23
|
+
export declare function updateConfigKey(key: string, value: string): void;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export interface Config {
|
|
2
|
+
baseURL: string;
|
|
3
|
+
apiKey: string;
|
|
4
|
+
model: string;
|
|
5
|
+
maxTokens?: number;
|
|
6
|
+
systemPrompt: string;
|
|
7
|
+
/** 模型上下文窗口(token)。须对齐真实模型窗口(GLM-4.6≈128k,DeepSeek-V3≈64k,Qwen 视版本)。 */
|
|
8
|
+
contextWindowTokens: number;
|
|
9
|
+
/** 自动压缩触发阈值(占窗口比例)。默认 0.85,保守偏早以吸收估算误差与下一步增长。 */
|
|
10
|
+
compactThreshold: number;
|
|
11
|
+
/** 流式请求里带 stream_options.include_usage 拿真实 usage。后端不认 stream_options 时关掉。 */
|
|
12
|
+
includeUsage: boolean;
|
|
13
|
+
/** 自动压缩总开关。关掉则只靠手动 /compact。 */
|
|
14
|
+
autoCompact: boolean;
|
|
15
|
+
/** Context Optimization Pipeline 总开关(工具结果进 LLM 前的类型化编码:tree/search/log/…)。
|
|
16
|
+
* 关掉则工具结果原样进 LLM(仅长度裁剪),零行为变化。默认 true。 */
|
|
17
|
+
contextOptimize: boolean;
|
|
18
|
+
/** 相关性裁剪总开关(read_file 跨条裁剪:同 path 旧 read + 已被 mutation 覆写的旧 read
|
|
19
|
+
* 自动替换为存根)。纯静态、不调 LLM;关掉则保留所有 read 结果(只受 capToolResultForHistory
|
|
20
|
+
* 单条上限与 microcompact 旧区截短影响)。默认 true;
|
|
21
|
+
* 设 MOCODE_CONTEXT_RELPRUNE=false 全局回退。 */
|
|
22
|
+
contextRelprune: boolean;
|
|
23
|
+
/** 观察者生命周期总开关(LIVE→REFERENCED→OBSOLETE→STUB 四态机;grep/glob/codegraph
|
|
24
|
+
* producer ↔ read/edit/write consumer 引用追踪;孤立+老化非观察类工具自动 STUB;
|
|
25
|
+
* 观察类工具永远只到 REFERENCED,不自动 STUB)。与 contextRelprune 并列,纯静态、不调 LLM。
|
|
26
|
+
* 默认 true;设 MOCODE_LIFECYCLE=false 全局回退。 */
|
|
27
|
+
contextLifecycle: boolean;
|
|
28
|
+
/** Context Budget Scheduler 总开关(五区分账 + ROI 排序调度)。
|
|
29
|
+
* 关掉则 agent 步前退化为 maybeCompact(history) 老路径(只看总占用 0.85 阈值),
|
|
30
|
+
* 零行为变化。默认 true;设 MOCODE_BUDGET_SCHEDULER=false 全局回退。 */
|
|
31
|
+
contextBudget: boolean;
|
|
32
|
+
/** 后台反思 pass 总开关。关掉则只靠手动 /reflect + 机会主义 memory_update。 */
|
|
33
|
+
autoReflect: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* 记忆子系统总开关。关闭时:5 个 memory_* 工具不进工具表,buildSystemPrompt
|
|
36
|
+
* 里的 Memory Index 段 + memory_* 工具使用说明整段不出现,plan 模式提示词里的
|
|
37
|
+
* 工具名也跟着消失。运行时 /memory_switch 改;持久化 MEMORY_ENABLED。
|
|
38
|
+
* 默认 false:新用户零侵入,想用记忆功能显式打开。
|
|
39
|
+
*/
|
|
40
|
+
memoryEnabled: boolean;
|
|
41
|
+
/** 每 N 个轮次触发一次后台反思 pass(与 agent 并发,不阻塞)。默认 5。 */
|
|
42
|
+
reflectEveryN: number;
|
|
43
|
+
/** 每轮 agent 循环最大步数(防无限循环)。默认 25。 */
|
|
44
|
+
maxSteps: number;
|
|
45
|
+
/** 子 agent(task 工具派生)默认步数上限。防子任务失控耗尽配额。默认 50。 */
|
|
46
|
+
subAgentMaxSteps: number;
|
|
47
|
+
/** 会话落盘目录(cwd 下)。 */
|
|
48
|
+
sessionDir: string;
|
|
49
|
+
/** AnySearch 联网搜索 API key(可选)。不配则走匿名免费额度(按 IP 限流)。 */
|
|
50
|
+
searchApiKey?: string;
|
|
51
|
+
/** 沙箱根目录(文件操作边界,可选)。未配则 startRepl 用 process.cwd() 兜底。优先级:--sandbox-root > 本项 > cwd。 */
|
|
52
|
+
sandboxRoot?: string;
|
|
53
|
+
/** AnySearch API base,默认官方端点。 */
|
|
54
|
+
searchBaseUrl: string;
|
|
55
|
+
/** 单张图片内联字节上限(base64 前的原始字节);超此大小拒绝并提示 TODO 走 URL 上传。默认 4MB。 */
|
|
56
|
+
maxImageBytes?: number;
|
|
57
|
+
/** 主题名(对应 src/ui/theme.ts 的 THEMES 表键)。默认 default;shell env MOCODE_THEME 覆盖文件。 */
|
|
58
|
+
theme: string;
|
|
59
|
+
/** MOCODE_THEME 是否由 shell 环境变量设置(非文件回填)。若是,/theme 写文件下次启动仍被 shell 盖。 */
|
|
60
|
+
themeFromShell: boolean;
|
|
61
|
+
/** 由 shell 环境变量设置的 LLM 键名列表(非文件回填)。若含某键,/model 写该键下次启动仍被 shell 盖。 */
|
|
62
|
+
llmKeysFromShell: string[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 模型是否已配置(baseURL + apiKey 非空)。REPL 开场据此决定是否提示 /model。
|
|
66
|
+
* 未配置时 config.model 仍回退 'gpt-4o-mini',但发消息会因 baseURL/apiKey 空而失败——由 runTurn catch 友好提示。
|
|
67
|
+
*/
|
|
68
|
+
export declare function isModelConfigured(): boolean;
|
|
69
|
+
/** 兼容旧名字:repl 的 buildSystemMessage 仍引 PLAN_MODE_SUFFIX(变量)。运行时按需现拼。 */
|
|
70
|
+
export declare function buildBasePrompt(): string;
|
|
71
|
+
/**
|
|
72
|
+
* plan 模式追加到系统提示末尾的指令。
|
|
73
|
+
* 历史曾是 `export const PLAN_MODE_SUFFIX`(顶层字面量);现改为按 isMemoryEnabled()
|
|
74
|
+
* 动态拼:false 时不出现 memory_* 工具名,避免 LLM 想调不存在的工具。
|
|
75
|
+
*
|
|
76
|
+
* 注意:已改为 getter(每次访问现拼),让运行时切 /memory_switch 后立即生效。
|
|
77
|
+
* 旧 import `PLAN_MODE_SUFFIX` 路径不变;repl 推荐改用 getPlanModeSuffix()(语义更清晰)。
|
|
78
|
+
* 不能直接 `export const PLAN_MODE_SUFFIX = buildPlanModeSuffix()`:
|
|
79
|
+
* 该表达式在模块初始化时立即求值,而 buildPlanModeSuffix 内部读 config,config 还未求值 → TDZ。
|
|
80
|
+
*/
|
|
81
|
+
export declare function getPlanModeSuffix(): string;
|
|
82
|
+
export declare const config: Config;
|
|
83
|
+
/**
|
|
84
|
+
* 运行时更新模型相关配置(/model 命令调)。
|
|
85
|
+
* - 更新 config 对象字段(即时生效:chat() 读 config.model,reconfigureClient 读 config.baseURL/apiKey)。
|
|
86
|
+
* - 同步 process.env(保持内存一致:其他读 process.env 的路径也拿到新值;且使新值在下次启动的
|
|
87
|
+
* loadEnvFiles 中被视为"已设",不被文件回填覆盖——即"优先拿这里的")。
|
|
88
|
+
* 持久化(写 ~/.mocode/config)由调用方走 writeConfigKeys,此处只管内存 + env。
|
|
89
|
+
* 重建 OpenAI 客户端(baseURL/apiKey 是构造时固化的实例字段)由调用方走 reconfigureClient。
|
|
90
|
+
*/
|
|
91
|
+
export declare function updateModelConfig(opts: {
|
|
92
|
+
model?: string;
|
|
93
|
+
baseURL?: string;
|
|
94
|
+
apiKey?: string;
|
|
95
|
+
contextWindowTokens?: number;
|
|
96
|
+
}): void;
|
|
97
|
+
/**
|
|
98
|
+
* 记忆子系统总开关:单一来源。/memory_switch、/memory_status、buildSystemPrompt、
|
|
99
|
+
* tools/builtins/index.ts、tools/constants.ts 的 plan-mode 列表都从这里查。
|
|
100
|
+
* 默认 false(新用户零侵入)。
|
|
101
|
+
*/
|
|
102
|
+
export declare function isMemoryEnabled(): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* 切换记忆子系统开关(/memory_switch on|off 调)。
|
|
105
|
+
* - 更新 config 单例字段(其它模块下次调 isMemoryEnabled() 即拿新值)。
|
|
106
|
+
* - 同步 process.env.MEMORY_ENABLED(下次启动 loadEnvFiles 不被文件回填)。
|
|
107
|
+
* 持久化(写 ~/.mocode/config 的 MEMORY_ENABLED 键)由调用方走 writeConfigKeys。
|
|
108
|
+
*
|
|
109
|
+
* 注:开关切换对当前会话的 tool list / 已拼好的 systemPrompt 不会自动重算 —
|
|
110
|
+
* 工具表在 REPL 启动时构建,systemPrompt 在每轮 chat() 拼时按 isMemoryEnabled()
|
|
111
|
+
* 现查现拼(关掉时该轮拼出来的 prompt 即不带 memory_* 段)。所以切换在「下一轮
|
|
112
|
+
* agent 调用」起即时生效,本轮已发出的请求不会回滚。
|
|
113
|
+
*/
|
|
114
|
+
export declare function updateMemoryConfig(enabled: boolean): void;
|
package/dist/config/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import dotenv from 'dotenv';
|
|
5
|
+
import { loadSnapshot } from '../project-snapshot/index.js';
|
|
6
|
+
import { buildProjectSkillSection } from '../project-skill/index.js';
|
|
5
7
|
/**
|
|
6
8
|
* 按优先级加载配置文件并回填 process.env:
|
|
7
9
|
* 候选(后者覆盖前者,优先级升序):<cwd>/.env(兼容旧用法,最低)→ ~/.mocode/config(全局)→ <cwd>/.mocode/config(项目级覆盖,最高)。
|
|
@@ -77,6 +79,31 @@ const PLATFORM_NOTE = (() => {
|
|
|
77
79
|
* 默认关(新用户零侵入):这段 + 工具表里的 5 个 memory_* + 系统提示尾部的 Memory Index
|
|
78
80
|
* 都不出现;打开 /memory_switch 后下一次新建 system message 才注入。
|
|
79
81
|
*/
|
|
82
|
+
/**
|
|
83
|
+
* 项目快照段落:开 isProjectSnapshotEnabled() 且有快照时才拼。
|
|
84
|
+
* 告诉 LLM 项目已有哪些静态文件可 cache hit,减少无谓 read_file。
|
|
85
|
+
*/
|
|
86
|
+
function buildSnapshotSection() {
|
|
87
|
+
if (!config.projectSnapshotEnabled)
|
|
88
|
+
return '';
|
|
89
|
+
try {
|
|
90
|
+
const snap = loadSnapshot();
|
|
91
|
+
if (!snap)
|
|
92
|
+
return '';
|
|
93
|
+
const fileList = Object.keys(snap.files).join(', ');
|
|
94
|
+
const modules = snap.structure.modules.length ? snap.structure.modules.join(', ') : '(none)';
|
|
95
|
+
return `\n## Project Snapshot (cross-session cache)\n- A project snapshot is available with cached static files: [${fileList}]. read_file for these files will hit the snapshot cache (mtime-verified) — no disk read needed.\n- Project structure — top-level modules: [${modules}].\n- You already know the project skeleton; don't read_file these cached files just to "get an overview".\n- Use cached content directly: when a question can be answered from the cached files above (e.g. dependencies → package.json, compiler options → tsconfig.json, env vars → .env.example, project intro → README.md), answer from the snapshot without calling read_file.\n- The module list above is a navigation aid: when locating files, prefer targeted \`glob\` into the relevant module (e.g. \`src/**/*.ts\`) over broad top-level scans.\n`;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return '';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 项目快照总开关查询器(read-file 工具用)。
|
|
103
|
+
*/
|
|
104
|
+
export function isProjectSnapshotEnabled() {
|
|
105
|
+
return config.projectSnapshotEnabled;
|
|
106
|
+
}
|
|
80
107
|
const SYSTEM_PROMPT_MEMORY_SECTION = `
|
|
81
108
|
## Memory (cross-session long-term facts)
|
|
82
109
|
- A "memory index" (id/title/summary only) is injected into the system prompt. Retrieve full body via memory_search (pass id or keyword); use memory_list to see the entire index.
|
|
@@ -193,7 +220,7 @@ ${PLATFORM_NOTE}
|
|
|
193
220
|
- Confirm with the user before irreversible or outward-facing operations (delete, overwrite existing files, push, request external services), unless explicitly authorized.
|
|
194
221
|
- Operate only within authorized scope; when unsure, ask — don't guess.
|
|
195
222
|
|
|
196
|
-
${memorySection}
|
|
223
|
+
${buildSnapshotSection()}${config.projectSkillEnabled ? buildProjectSkillSection() : ''}${memorySection}
|
|
197
224
|
|
|
198
225
|
## Working notepad (todolist) — for multi-step tasks
|
|
199
226
|
- For tasks spanning **≥2 independent modules** OR when the user asks for stepwise progress ("先计划再执行" / "plan then do" / "按步骤来"), call \`todolist create\` first; update as you go. Skip for single-file edits or quick lookups.
|
|
@@ -250,6 +277,9 @@ export const config = {
|
|
|
250
277
|
theme: process.env.MOCODE_THEME || 'default',
|
|
251
278
|
themeFromShell,
|
|
252
279
|
llmKeysFromShell,
|
|
280
|
+
projectSnapshotEnabled: process.env.MOCODE_PROJECT_SNAPSHOT !== 'false',
|
|
281
|
+
permissionEnabled: process.env.MOCODE_PERMISSION !== 'false',
|
|
282
|
+
projectSkillEnabled: process.env.MOCODE_PROJECT_SKILL === 'true',
|
|
253
283
|
};
|
|
254
284
|
/**
|
|
255
285
|
* 运行时更新模型相关配置(/model 命令调)。
|
|
@@ -300,3 +330,20 @@ export function updateMemoryConfig(enabled) {
|
|
|
300
330
|
config.memoryEnabled = enabled;
|
|
301
331
|
process.env.MEMORY_ENABLED = enabled ? 'true' : 'false';
|
|
302
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* 项目专属 Skill 总开关:单一来源。/project_skill、buildBasePrompt、
|
|
335
|
+
* tools/builtins/index.ts 都从这里查。默认 false(零侵入)。
|
|
336
|
+
*/
|
|
337
|
+
export function isProjectSkillEnabled() {
|
|
338
|
+
return config.projectSkillEnabled;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* 切换项目专属 Skill 开关(/project_skill on|off 调)。
|
|
342
|
+
* - 更新 config 单例字段(其它模块下次调 isProjectSkillEnabled() 即拿新值)。
|
|
343
|
+
* - 同步 process.env.MOCODE_PROJECT_SKILL(下次启动 loadEnvFiles 不被文件回填)。
|
|
344
|
+
* 持久化(写 ~/.mocode/config 的 MOCODE_PROJECT_SKILL 键)由调用方走 writeConfigKeys。
|
|
345
|
+
*/
|
|
346
|
+
export function updateProjectSkillConfig(enabled) {
|
|
347
|
+
config.projectSkillEnabled = enabled;
|
|
348
|
+
process.env.MOCODE_PROJECT_SKILL = enabled ? 'true' : 'false';
|
|
349
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 多模型预设(`/model save <name>` 保存的命名配置)的纯 I/O 叶子。
|
|
3
|
+
*
|
|
4
|
+
* 存储:每个预设一个独立 JSON 文件 `~/.mocode/models/<name>.json`。
|
|
5
|
+
* - 选用 per-file 而非单一 index.json:写不需要读-合并-写整本,天然原子(单文件 rename),
|
|
6
|
+
* 符合 memory/store.ts 的惯例;并发写多个预设互不打架。
|
|
7
|
+
* - 路径前缀与 CONFIG_PATH 共享 `~/.mocode/`,保证权限/位置一致。
|
|
8
|
+
*
|
|
9
|
+
* 不读 process.env、不触发 config 单例初始化——repl(/model 子命令)与 commands/config.ts
|
|
10
|
+
* 之外的脚本都能安全 import。错误一律抛(写失败属异常路径,调用方决定怎么提示用户)。
|
|
11
|
+
*
|
|
12
|
+
* 命名约束:[a-zA-Z0-9_-]{1,32}。理由:
|
|
13
|
+
* - 排除路径分隔符与 .. 防止越权写文件。
|
|
14
|
+
* - 与 dotenv key 风格一致,便于未来扩展到同名 env override。
|
|
15
|
+
* - 32 位上限防极长名撑爆文件名系统。
|
|
16
|
+
*/
|
|
17
|
+
/** 预设目录:`~/.mocode/models/`(按需创建)。 */
|
|
18
|
+
export declare const MODELS_DIR: string;
|
|
19
|
+
/** 单个预设的内容:与 updateModelConfig 的可选字段对齐。 */
|
|
20
|
+
export interface ModelPreset {
|
|
21
|
+
name: string;
|
|
22
|
+
baseURL: string;
|
|
23
|
+
apiKey: string;
|
|
24
|
+
model: string;
|
|
25
|
+
contextWindow: number;
|
|
26
|
+
}
|
|
27
|
+
/** 名字是否合法(调用方复用,避免在多处重复同一正则)。 */
|
|
28
|
+
export declare function isValidPresetName(name: string): boolean;
|
|
29
|
+
/** 读单个预设;不存在抛错。 */
|
|
30
|
+
export declare function getPreset(name: string): ModelPreset;
|
|
31
|
+
/** 读单个预设;不存在返回 null(供列表/可选切换场景)。 */
|
|
32
|
+
export declare function readPreset(name: string): ModelPreset | null;
|
|
33
|
+
/** 写/覆盖一个预设(原子:写 tmp 再 rename)。 */
|
|
34
|
+
export declare function savePreset(preset: ModelPreset): void;
|
|
35
|
+
/** 删除一个预设;不存在返回 false,成功返回 true。 */
|
|
36
|
+
export declare function deletePreset(name: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* 重命名一个预设(原子:link+unlink,跨设备时退化为 copy+unlink)。
|
|
39
|
+
* 用于 /model rename <old> <new>;不存在的旧名 / 已存在的新名返回 false,具体由调用方决定提示文案。
|
|
40
|
+
*/
|
|
41
|
+
export declare function renamePreset(oldName: string, newName: string): boolean;
|
|
42
|
+
/** 列出全部预设(按 name 升序);目录不存在返回空数组。 */
|
|
43
|
+
export declare function listPresets(): ModelPreset[];
|
|
44
|
+
/**
|
|
45
|
+
* 把当前 config 的 LLM 四键(baseURL/apiKey/model/contextWindow)迁为命名预设。
|
|
46
|
+
* 用于启动时一次性兜底老用户:加 /model 之前就已经在 ~/.mocode/config 写过的配置,
|
|
47
|
+
* /model list 应该立刻能看到,而不是空。已有同名预设则不重复写。
|
|
48
|
+
*
|
|
49
|
+
* 返回新建的预设名;若未配置完整(baseURL/apiKey 缺失)或已有同名则返回 null。
|
|
50
|
+
* 设计为幂等:重启调用一次也只会生效一次。
|
|
51
|
+
*/
|
|
52
|
+
export declare function migrateCurrentToPreset(input: {
|
|
53
|
+
baseURL: string;
|
|
54
|
+
apiKey: string;
|
|
55
|
+
model: string;
|
|
56
|
+
contextWindow: number;
|
|
57
|
+
}): string | null;
|