mocode-ai 1.0.13 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -19
- package/README.zh-CN.md +1 -1
- package/bin/mocode-agent-host.js +3 -0
- package/dist/agent/core.js +10 -5
- package/dist/agent/mode.js +9 -10
- package/dist/agent/spawn.js +2 -2
- package/dist/config/index.js +43 -65
- package/dist/context/artifacts.js +2 -2
- package/dist/context/classifier.js +3 -6
- package/dist/context/encoders/_util.js +2 -1
- package/dist/context/encoders/index.js +3 -4
- package/dist/context/lifecycle.js +3 -23
- package/dist/context/relevance.js +3 -22
- package/dist/host/protocol.js +40 -0
- package/dist/host/stdio.js +203 -0
- package/dist/i18n/index.js +0 -30
- package/dist/memory/index.js +6 -0
- package/dist/repl/index.js +32 -284
- package/dist/sandbox/policy.js +6 -4
- package/dist/skills/builtin-skills.js +56 -0
- package/dist/skills/discover.js +7 -0
- package/dist/skills/index.js +23 -4
- package/dist/tools/builtins/edit-file.js +68 -14
- package/dist/tools/builtins/glob.js +1 -1
- package/dist/tools/builtins/grep.js +1 -1
- package/dist/tools/builtins/index.js +0 -14
- package/dist/tools/builtins/read-file.js +1 -1
- package/dist/tools/constants.js +2 -1
- package/package.json +3 -2
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import readline from 'node:readline';
|
|
3
|
+
import { runAgentCore } from '../agent/core.js';
|
|
4
|
+
import { setAgentMode } from '../agent/mode.js';
|
|
5
|
+
import { buildBasePrompt, config } from '../config/index.js';
|
|
6
|
+
import { refreshChatTools } from '../llm/index.js';
|
|
7
|
+
import { initializeAllMcp, getMcpTools, getMcpWarnings, closeAllMcp } from '../mcp/index.js';
|
|
8
|
+
import { setSandboxRoot } from '../sandbox/index.js';
|
|
9
|
+
import { createContextState, loadSession, newSessionId, saveSession } from '../session/index.js';
|
|
10
|
+
import { setCurrentSessionId } from '../session/state.js';
|
|
11
|
+
import { effectiveSystemPrompt } from '../skills/index.js';
|
|
12
|
+
import { registerToolsExtension } from '../tools/registry.js';
|
|
13
|
+
import { parseCommand } from './protocol.js';
|
|
14
|
+
let initialized = null;
|
|
15
|
+
let activeRun = null;
|
|
16
|
+
let sessionId = '';
|
|
17
|
+
let history = [];
|
|
18
|
+
let queryHistory = [];
|
|
19
|
+
let contextState = createContextState();
|
|
20
|
+
const approvals = new Map();
|
|
21
|
+
function write(envelope) { process.stdout.write(`${JSON.stringify(envelope)}\n`); }
|
|
22
|
+
function emit(event, payload = {}, requestId) {
|
|
23
|
+
write({ type: 'event', event, payload, requestId });
|
|
24
|
+
}
|
|
25
|
+
function error(message, requestId) { write({ type: 'error', error: message, requestId }); }
|
|
26
|
+
async function initializeRuntime() {
|
|
27
|
+
if (initialized)
|
|
28
|
+
return initialized;
|
|
29
|
+
initialized = (async () => {
|
|
30
|
+
setSandboxRoot(process.cwd());
|
|
31
|
+
setAgentMode('auto');
|
|
32
|
+
await initializeAllMcp();
|
|
33
|
+
registerToolsExtension('mcp', getMcpTools());
|
|
34
|
+
refreshChatTools();
|
|
35
|
+
emit('runtime_ready', { projectRoot: process.cwd(), warnings: getMcpWarnings() });
|
|
36
|
+
})();
|
|
37
|
+
return initialized;
|
|
38
|
+
}
|
|
39
|
+
function systemMessage() { return effectiveSystemPrompt(buildBasePrompt(sessionId)); }
|
|
40
|
+
function createSession() {
|
|
41
|
+
sessionId = newSessionId();
|
|
42
|
+
setCurrentSessionId(sessionId, process.cwd());
|
|
43
|
+
setAgentMode('auto');
|
|
44
|
+
history = [{ role: 'system', content: systemMessage() }];
|
|
45
|
+
queryHistory = [];
|
|
46
|
+
contextState = createContextState();
|
|
47
|
+
}
|
|
48
|
+
function restoreSession(id) {
|
|
49
|
+
const loaded = loadSession(id);
|
|
50
|
+
if (!loaded?.history.length)
|
|
51
|
+
return false;
|
|
52
|
+
sessionId = loaded.id;
|
|
53
|
+
setCurrentSessionId(sessionId, process.cwd());
|
|
54
|
+
setAgentMode('auto');
|
|
55
|
+
history = [...loaded.history];
|
|
56
|
+
if (history[0]?.role === 'system')
|
|
57
|
+
history[0] = { role: 'system', content: systemMessage() };
|
|
58
|
+
else
|
|
59
|
+
history.unshift({ role: 'system', content: systemMessage() });
|
|
60
|
+
queryHistory = [...(loaded.queryHistory ?? [])];
|
|
61
|
+
contextState = createContextState();
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
function prepareSession(requestedSessionId) {
|
|
65
|
+
if (!requestedSessionId) {
|
|
66
|
+
if (!sessionId)
|
|
67
|
+
createSession();
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
if (requestedSessionId === sessionId)
|
|
71
|
+
return true;
|
|
72
|
+
return restoreSession(requestedSessionId) ? true : null;
|
|
73
|
+
}
|
|
74
|
+
function waitForApproval(runId, request) {
|
|
75
|
+
const approvalId = randomUUID();
|
|
76
|
+
emit('approval_requested', {
|
|
77
|
+
approvalId,
|
|
78
|
+
title: request.title,
|
|
79
|
+
detail: request.detail ?? '',
|
|
80
|
+
options: (request.options ?? []).map((option) => typeof option === 'string' ? option : option.label),
|
|
81
|
+
}, runId);
|
|
82
|
+
return new Promise((resolve) => approvals.set(approvalId, { resolve, runId }));
|
|
83
|
+
}
|
|
84
|
+
function hooksFor(runId) {
|
|
85
|
+
return {
|
|
86
|
+
onStepStart: () => emit('status', { value: 'thinking' }, runId),
|
|
87
|
+
onText: (text) => emit('text_delta', { text }, runId),
|
|
88
|
+
onToolCall: (name) => emit('status', { value: 'preparing_tool', tool: name }, runId),
|
|
89
|
+
onToolHeader: (tool) => emit('tool_started', { id: tool.id, name: tool.name, arguments: tool.arguments }, runId),
|
|
90
|
+
onToolStart: (name) => emit('status', { value: 'running_tool', tool: name }, runId),
|
|
91
|
+
onToolResult: (tool, output) => emit('tool_completed', { id: tool.id, name: tool.name, output }, runId),
|
|
92
|
+
onValidationStart: (command) => emit('validation_started', { command }, runId),
|
|
93
|
+
onValidationResult: (result) => emit('validation_completed', { result }, runId),
|
|
94
|
+
onAbort: () => emit('run_aborted', {}, runId),
|
|
95
|
+
onDone: (elapsedMs, usage) => emit('run_finished', { elapsedMs, usage }, runId),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async function run(command) {
|
|
99
|
+
if (activeRun)
|
|
100
|
+
return error('An agent run is already active for this project.', command.id);
|
|
101
|
+
if (!command.prompt.trim())
|
|
102
|
+
return;
|
|
103
|
+
try {
|
|
104
|
+
await initializeRuntime();
|
|
105
|
+
const resumed = prepareSession(command.sessionId);
|
|
106
|
+
if (resumed === null)
|
|
107
|
+
return error(`Session ${command.sessionId} could not be restored.`, command.id);
|
|
108
|
+
const controller = new AbortController();
|
|
109
|
+
activeRun = { id: command.id, controller };
|
|
110
|
+
queryHistory.push(command.prompt);
|
|
111
|
+
const userInput = command.attachments?.length
|
|
112
|
+
? [{ type: 'text', text: command.prompt }, ...command.attachments.map((attachment) => ({
|
|
113
|
+
type: 'image_url',
|
|
114
|
+
image_url: { url: attachment.dataUrl },
|
|
115
|
+
}))]
|
|
116
|
+
: command.prompt;
|
|
117
|
+
emit('run_started', {
|
|
118
|
+
sessionId,
|
|
119
|
+
projectRoot: process.cwd(),
|
|
120
|
+
resumed,
|
|
121
|
+
attachments: command.attachments?.map((attachment) => attachment.name) ?? [],
|
|
122
|
+
}, command.id);
|
|
123
|
+
const result = await runAgentCore({
|
|
124
|
+
history,
|
|
125
|
+
userInput,
|
|
126
|
+
signal: controller.signal,
|
|
127
|
+
hooks: hooksFor(command.id),
|
|
128
|
+
contextState,
|
|
129
|
+
autoValidate: config.autoValidate,
|
|
130
|
+
permissionPrompt: (request) => waitForApproval(command.id, request),
|
|
131
|
+
});
|
|
132
|
+
saveSession(history, sessionId, queryHistory);
|
|
133
|
+
emit('run_completed', {
|
|
134
|
+
sessionId,
|
|
135
|
+
completed: result.completed,
|
|
136
|
+
terminationReason: result.terminationReason,
|
|
137
|
+
changedFiles: result.changedFiles ?? [],
|
|
138
|
+
validation: result.validation,
|
|
139
|
+
usage: result.usage,
|
|
140
|
+
}, command.id);
|
|
141
|
+
}
|
|
142
|
+
catch (cause) {
|
|
143
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
144
|
+
try {
|
|
145
|
+
if (sessionId)
|
|
146
|
+
saveSession(history, sessionId, queryHistory);
|
|
147
|
+
}
|
|
148
|
+
catch { /* Preserve the runtime error. */ }
|
|
149
|
+
emit('run_failed', { message }, command.id);
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
for (const [approvalId, waiter] of approvals) {
|
|
153
|
+
if (waiter.runId === command.id) {
|
|
154
|
+
waiter.resolve({ action: 'cancelled' });
|
|
155
|
+
approvals.delete(approvalId);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
activeRun = null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function cancel(command) {
|
|
162
|
+
if (!activeRun)
|
|
163
|
+
return emit('run_idle', {}, command.id);
|
|
164
|
+
activeRun.controller.abort();
|
|
165
|
+
for (const [approvalId, waiter] of approvals) {
|
|
166
|
+
if (waiter.runId === activeRun.id) {
|
|
167
|
+
waiter.resolve({ action: 'cancelled' });
|
|
168
|
+
approvals.delete(approvalId);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
emit('cancelling', {}, command.id);
|
|
172
|
+
}
|
|
173
|
+
function resolveApproval(command) {
|
|
174
|
+
const waiter = approvals.get(command.approvalId);
|
|
175
|
+
if (!waiter)
|
|
176
|
+
return error('Approval request has expired.', command.id);
|
|
177
|
+
approvals.delete(command.approvalId);
|
|
178
|
+
waiter.resolve({ action: command.action, value: command.value });
|
|
179
|
+
}
|
|
180
|
+
async function handle(command) {
|
|
181
|
+
if (command.type === 'run')
|
|
182
|
+
return run(command);
|
|
183
|
+
if (command.type === 'cancel')
|
|
184
|
+
return cancel(command);
|
|
185
|
+
resolveApproval(command);
|
|
186
|
+
}
|
|
187
|
+
const input = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
188
|
+
void initializeRuntime().catch((cause) => error(cause instanceof Error ? cause.message : String(cause)));
|
|
189
|
+
for await (const line of input) {
|
|
190
|
+
if (!line.trim())
|
|
191
|
+
continue;
|
|
192
|
+
try {
|
|
193
|
+
const command = parseCommand(JSON.parse(line));
|
|
194
|
+
if (!command)
|
|
195
|
+
error('Invalid Mocode Work host command.');
|
|
196
|
+
else
|
|
197
|
+
void handle(command);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
error('Invalid JSON command.');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
await closeAllMcp().catch(() => undefined);
|
package/dist/i18n/index.js
CHANGED
|
@@ -23,18 +23,6 @@ const zhCN = {
|
|
|
23
23
|
'commands.subagentOn': '开启子 Agent',
|
|
24
24
|
'commands.subagentOff': '关闭子 Agent',
|
|
25
25
|
'commands.subagentStatus': '查看子 Agent 状态',
|
|
26
|
-
'commands.skill': '项目专属 Skill 管理',
|
|
27
|
-
'commands.toggle': '切换开关',
|
|
28
|
-
'commands.skillOn': '开启项目 Skill',
|
|
29
|
-
'commands.skillOff': '关闭项目 Skill',
|
|
30
|
-
'commands.skillView': '查看当前内容',
|
|
31
|
-
'commands.skillInit': '扫描项目生成或优化 Skill',
|
|
32
|
-
'commands.snapshot': '项目快照管理',
|
|
33
|
-
'commands.snapshotToggle': '切换快照开关',
|
|
34
|
-
'commands.snapshotOn': '开启项目快照',
|
|
35
|
-
'commands.snapshotOff': '关闭项目快照',
|
|
36
|
-
'commands.snapshotStatus': '查看当前状态',
|
|
37
|
-
'commands.snapshotRefresh': '重新扫描并生成快照摘要',
|
|
38
26
|
'commands.theme': '切换颜色主题(↑↓·Enter)',
|
|
39
27
|
'commands.model': '模型配置与预设管理',
|
|
40
28
|
'commands.modelConfigure': '配置新模型(向导)',
|
|
@@ -78,8 +66,6 @@ const zhCN = {
|
|
|
78
66
|
'running.chooseTurn': '选择轮次…',
|
|
79
67
|
'running.init': '初始化',
|
|
80
68
|
'running.generateMemory': '生成 MOCODE.md…',
|
|
81
|
-
'running.snapshot': '刷新快照',
|
|
82
|
-
'running.scanning': '扫描项目文件中…',
|
|
83
69
|
'running.plan': '切 plan',
|
|
84
70
|
'running.auto': '切 auto',
|
|
85
71
|
'running.clear': '清空',
|
|
@@ -93,7 +79,6 @@ const zhCN = {
|
|
|
93
79
|
'running.switching': '切换中…',
|
|
94
80
|
'running.memoryStatus': '查记忆状态',
|
|
95
81
|
'running.subagent': '子 Agent',
|
|
96
|
-
'running.skill': '项目 Skill',
|
|
97
82
|
'running.language': '切语言',
|
|
98
83
|
'running.chooseLanguage': '选择语言…',
|
|
99
84
|
'running.upgrade': '升级',
|
|
@@ -280,18 +265,6 @@ const en = {
|
|
|
280
265
|
'commands.subagentOn': 'Enable sub-agents',
|
|
281
266
|
'commands.subagentOff': 'Disable sub-agents',
|
|
282
267
|
'commands.subagentStatus': 'Show sub-agent status',
|
|
283
|
-
'commands.skill': 'Manage the project-specific Skill',
|
|
284
|
-
'commands.toggle': 'Toggle the feature',
|
|
285
|
-
'commands.skillOn': 'Enable the project Skill',
|
|
286
|
-
'commands.skillOff': 'Disable the project Skill',
|
|
287
|
-
'commands.skillView': 'View current content',
|
|
288
|
-
'commands.skillInit': 'Generate or improve the project Skill',
|
|
289
|
-
'commands.snapshot': 'Manage project snapshots',
|
|
290
|
-
'commands.snapshotToggle': 'Toggle project snapshots',
|
|
291
|
-
'commands.snapshotOn': 'Enable project snapshots',
|
|
292
|
-
'commands.snapshotOff': 'Disable project snapshots',
|
|
293
|
-
'commands.snapshotStatus': 'Show current status',
|
|
294
|
-
'commands.snapshotRefresh': 'Rescan and regenerate the snapshot summary',
|
|
295
268
|
'commands.theme': 'Switch color theme (↑↓·Enter)',
|
|
296
269
|
'commands.model': 'Model configuration and presets',
|
|
297
270
|
'commands.modelConfigure': 'Configure a new model (wizard)',
|
|
@@ -335,8 +308,6 @@ const en = {
|
|
|
335
308
|
'running.chooseTurn': 'Choose a turn…',
|
|
336
309
|
'running.init': 'Initialize',
|
|
337
310
|
'running.generateMemory': 'Generating MOCODE.md…',
|
|
338
|
-
'running.snapshot': 'Refresh snapshot',
|
|
339
|
-
'running.scanning': 'Scanning project files…',
|
|
340
311
|
'running.plan': 'Plan mode',
|
|
341
312
|
'running.auto': 'Auto mode',
|
|
342
313
|
'running.clear': 'Clear',
|
|
@@ -350,7 +321,6 @@ const en = {
|
|
|
350
321
|
'running.switching': 'Switching…',
|
|
351
322
|
'running.memoryStatus': 'Memory status',
|
|
352
323
|
'running.subagent': 'Sub-agent',
|
|
353
|
-
'running.skill': 'Project Skill',
|
|
354
324
|
'running.language': 'Language',
|
|
355
325
|
'running.chooseLanguage': 'Choose a language…',
|
|
356
326
|
'running.upgrade': 'Upgrade',
|
package/dist/memory/index.js
CHANGED
|
@@ -8,6 +8,11 @@ export { kickoffReflection, drainMemoryBackground, getLastReflectResult, clearLa
|
|
|
8
8
|
/** system 消息中 memory 段的字符上限(防过大占窗口——system 在 history[0],compactHistory 不压缩)。 */
|
|
9
9
|
const MAX_MEMORY_CHARS = 20000;
|
|
10
10
|
let cache = null;
|
|
11
|
+
/** 失效 memory 缓存:下次 loadMemory()/buildMemorySection() 重新扫描 MOCODE.md。
|
|
12
|
+
* 用于 /init 等"刚写完 MOCODE.md,下次轮想让新内容立刻可见"的场景。 */
|
|
13
|
+
export function invalidateMemoryCache() {
|
|
14
|
+
cache = null;
|
|
15
|
+
}
|
|
11
16
|
/**
|
|
12
17
|
* 合并全局 + 项目各级 MOCODE.md(远→近拼接,各段空行分隔),超 MAX_MEMORY_CHARS 截断 + 提示。
|
|
13
18
|
* 懒加载(首次调用触发扫描;启动期 repl 调一次)。无 MOCODE.md 返空串。
|
|
@@ -47,6 +52,7 @@ export function buildMemorySection() {
|
|
|
47
52
|
'',
|
|
48
53
|
'## Project Memory (MOCODE.md)',
|
|
49
54
|
'The following is project memory (architecture / conventions / commands and other cross-session long-term facts). Act accordingly:',
|
|
55
|
+
'If any fact here conflicts with the current code, treat the code as the source of truth and surface a brief reminder to update MOCODE.md (do not silently rewrite the file yourself).',
|
|
50
56
|
mem,
|
|
51
57
|
].join('\n');
|
|
52
58
|
}
|