mocode-ai 1.2.5 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@
3
3
  // scheduler is the only automatic rewrite coordinator at real pressure.
4
4
  export { optimizeToolResult } from './pipeline.js';
5
5
  export { classify, knownToolKinds } from './classifier.js';
6
- export { recordArtifact, invalidateArtifacts, rehydrateArtifacts, refreshArtifactFreshness, pruneStaleArtifacts, collectArtifactRefs, formatArtifactTokenSources, } from './artifacts.js';
6
+ export { recordArtifact, invalidateArtifacts, rehydrateArtifacts, refreshArtifactFreshness, pruneStaleArtifacts, collectArtifactRefs, formatArtifactTokenSources, knownEditTargets, } from './artifacts.js';
7
7
  export { registerEncoder, registerAll, getEncoder, registeredKinds, } from './registry.js';
8
8
  // ── Context Budget Scheduler ───────────────────────────────────────────────
9
9
  export { evaluateBudget, scheduleActions, formatReport, quickEstimate, userTurnBoundary, BUDGET_LAYERS, DEFAULT_BUDGET_POLICY, BUDGET_RATIO, HOT_TURN_WINDOW, } from './budget.js';
@@ -33,7 +33,12 @@ async function initializeRuntime() {
33
33
  await initializeAllMcp();
34
34
  registerToolsExtension('mcp', getMcpTools());
35
35
  refreshChatTools();
36
- emit('runtime_ready', { projectRoot: process.cwd(), warnings: getMcpWarnings() });
36
+ emit('runtime_ready', {
37
+ projectRoot: process.cwd(),
38
+ provider: config.provider,
39
+ promptCache: config.provider === 'anthropic' && config.anthropicPromptCache,
40
+ warnings: getMcpWarnings(),
41
+ });
37
42
  })();
38
43
  return initialized;
39
44
  }
@@ -117,6 +122,8 @@ async function run(command) {
117
122
  sessionId,
118
123
  projectRoot: process.cwd(),
119
124
  resumed,
125
+ provider: config.provider,
126
+ promptCache: config.provider === 'anthropic' && config.anthropicPromptCache,
120
127
  attachments: command.attachments?.map((attachment) => attachment.name) ?? [],
121
128
  }, command.id);
122
129
  const result = await runAgentCore({
@@ -134,6 +141,8 @@ async function run(command) {
134
141
  terminationReason: result.terminationReason,
135
142
  changedFiles: result.changedFiles ?? [],
136
143
  usage: result.usage,
144
+ provider: config.provider,
145
+ promptCache: config.provider === 'anthropic' && config.anthropicPromptCache,
137
146
  usagePercent: Math.round(contextUsagePercent() * 100),
138
147
  contextWindow: config.contextWindowTokens,
139
148
  }, command.id);
@@ -18,7 +18,7 @@ const zhCN = {
18
18
  'commands.memoryOff': '关闭记忆子系统',
19
19
  'commands.memoryStatus': '查看当前开关与原理',
20
20
  'commands.memoryReflect': '手动触发后台记忆反思',
21
- 'commands.memoryInit': '扫描项目生成 MOCODE.md 项目记忆',
21
+ 'commands.memoryInit': '扫描项目生成 AGENTS.md 项目记忆',
22
22
  'commands.subagent': '子 Agent 开关(默认关闭)',
23
23
  'commands.subagentOn': '开启子 Agent',
24
24
  'commands.subagentOff': '关闭子 Agent',
@@ -69,7 +69,7 @@ const zhCN = {
69
69
  'running.rollback': '回滚',
70
70
  'running.chooseTurn': '选择轮次…',
71
71
  'running.init': '初始化',
72
- 'running.generateMemory': '生成 MOCODE.md…',
72
+ 'running.generateMemory': '生成 AGENTS.md…',
73
73
  'running.plan': '切 plan',
74
74
  'running.auto': '切 auto',
75
75
  'running.clear': '清空',
@@ -277,7 +277,7 @@ const en = {
277
277
  'commands.memoryOff': 'Disable the memory subsystem',
278
278
  'commands.memoryStatus': 'Show current state and behavior',
279
279
  'commands.memoryReflect': 'Run background memory reflection',
280
- 'commands.memoryInit': 'Scan project and generate MOCODE.md',
280
+ 'commands.memoryInit': 'Scan project and generate AGENTS.md',
281
281
  'commands.subagent': 'Sub-agent controls (disabled by default)',
282
282
  'commands.subagentOn': 'Enable sub-agents',
283
283
  'commands.subagentOff': 'Disable sub-agents',
@@ -328,7 +328,7 @@ const en = {
328
328
  'running.rollback': 'Rollback',
329
329
  'running.chooseTurn': 'Choose a turn…',
330
330
  'running.init': 'Initialize',
331
- 'running.generateMemory': 'Generating MOCODE.md…',
331
+ 'running.generateMemory': 'Generating AGENTS.md…',
332
332
  'running.plan': 'Plan mode',
333
333
  'running.auto': 'Auto mode',
334
334
  'running.clear': 'Clear',
package/dist/llm/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import OpenAI from 'openai';
2
- import { config, isSubAgentEnabled, isFrontendToolsEnabled } from '../config/index.js';
2
+ import { config, getActiveModel, isSubAgentEnabled, isFrontendToolsEnabled } from '../config/index.js';
3
3
  import { tools } from '../tools/registry.js';
4
4
  import { getPlanDisabledTools, FRONTEND_TOOLS } from '../tools/constants.js';
5
5
  import { ThinkTagFilter } from './think-filter.js';
6
+ import { anthropicChatOnce } from './providers/anthropic.js';
6
7
  // 强制关闭第三方调试日志泄漏:openai SDK 在 process.env.DEBUG === 'true' 时用裸
7
8
  // console.log 把请求/响应直写 stdout,会污染 TUI 输入框(并泄露 headers/URL)。
8
9
  // 仅拦截 'true' 这一开关值——保留 namespace 形式的 DEBUG(如 DEBUG=express:*) 调试能力。
@@ -180,7 +181,6 @@ export function refreshChatTools() {
180
181
  // MCP 协议没有可靠的副作用注解;plan 模式绝不暴露外部工具,保留只读探查保证。
181
182
  planChatTools.splice(0, planChatTools.length, ...next.filter((t) => !t.function.name.startsWith('mcp__') && !getPlanDisabledTools().has(t.function.name)));
182
183
  }
183
- refreshChatTools();
184
184
  /**
185
185
  * 多 provider 兼容的 cache / reasoning 字段提取。
186
186
  * 不同后端报 cached 字段名差异巨大,这里按"最常见的几种"顺序 probe,
@@ -268,6 +268,9 @@ toolsOverride) {
268
268
  throw new DOMException('This operation was aborted', 'AbortError');
269
269
  }
270
270
  try {
271
+ if (config.provider === 'anthropic') {
272
+ return await anthropicChatOnce(messages, handlers, signal, toolsOverride ?? chatTools);
273
+ }
271
274
  return await chatOnce(messages, handlers, signal, toolsOverride);
272
275
  }
273
276
  catch (err) {
@@ -328,6 +331,22 @@ export function normalizeImageDetail(messages) {
328
331
  }
329
332
  /** 单次流式 LLM 请求(无重试);chat() 的内部实现,可被 __setChatCreateImpl 注入桩以做单测。 */
330
333
  async function chatOnce(messages, handlers, signal, toolsOverride) {
334
+ // 防御:messages 必须至少含一条非空 user 消息,否则 OpenAI/Anthropic 都会 400。
335
+ // compact force 分支曾把所有 user 丢进摘要 → 重建 history 无 user → 下一轮 400。
336
+ // 在 transport 边界拦住所有类似回归(compact / 外部注入 / resume 损坏)。
337
+ const hasNonEmptyUser = messages.some((m) => {
338
+ if (m.role !== 'user')
339
+ return false;
340
+ const c = m.content;
341
+ if (typeof c === 'string')
342
+ return c.length > 0;
343
+ if (Array.isArray(c))
344
+ return c.some((p) => p.type === 'text' && (p.text?.length ?? 0) > 0);
345
+ return false;
346
+ });
347
+ if (!hasNonEmptyUser) {
348
+ throw new Error('messages must contain at least one non-empty user message');
349
+ }
331
350
  // signal 透传给 SDK 第二参(RequestOptions);abort 后 for await 抛错,chat 不 catch,透传 runAgent 处理。
332
351
  // createImplOverride 的 body 类型故意宽成 Record(测试桩用),生产走 client 分支时由 OpenAI 自己的类型守门。
333
352
  const create = createImplOverride
@@ -335,7 +354,7 @@ async function chatOnce(messages, handlers, signal, toolsOverride) {
335
354
  : (body, opts) => client.chat.completions.create(body, opts);
336
355
  const activeTools = toolsOverride ?? chatTools;
337
356
  const stream = await create({
338
- model: config.model,
357
+ model: getActiveModel(),
339
358
  messages: normalizeImageDetail(messages),
340
359
  tools: activeTools,
341
360
  stream: true,
@@ -0,0 +1,370 @@
1
+ import { config, getActiveModel } from '../../config/index.js';
2
+ let fetchImplOverride = null;
3
+ /** 仅供单测注入;生产路径使用 Node 18+ 全局 fetch。 */
4
+ export function __setAnthropicFetchImpl(impl) {
5
+ fetchImplOverride = impl;
6
+ }
7
+ function parseJsonObject(value) {
8
+ if (!value.trim())
9
+ return {};
10
+ try {
11
+ const parsed = JSON.parse(value);
12
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
13
+ ? parsed
14
+ : {};
15
+ }
16
+ catch {
17
+ return {};
18
+ }
19
+ }
20
+ function textBlocks(content) {
21
+ if (content == null)
22
+ return [];
23
+ if (typeof content === 'string')
24
+ return content ? [{ type: 'text', text: content }] : [];
25
+ if (!Array.isArray(content))
26
+ return [{ type: 'text', text: String(content) }];
27
+ const blocks = [];
28
+ for (const raw of content) {
29
+ if (!raw || typeof raw !== 'object')
30
+ continue;
31
+ const part = raw;
32
+ if (part.type === 'text' && typeof part.text === 'string') {
33
+ blocks.push({ type: 'text', text: part.text });
34
+ continue;
35
+ }
36
+ if (part.type !== 'image_url')
37
+ continue;
38
+ const image = part.image_url;
39
+ if (typeof image?.url !== 'string')
40
+ continue;
41
+ const match = /^data:(image\/(?:jpeg|png|gif|webp));base64,(.+)$/s.exec(image.url);
42
+ if (!match)
43
+ continue;
44
+ blocks.push({
45
+ type: 'image',
46
+ source: { type: 'base64', media_type: match[1], data: match[2] },
47
+ });
48
+ }
49
+ return blocks;
50
+ }
51
+ function appendMessage(messages, role, blocks, cacheCandidates, cacheable = true) {
52
+ if (blocks.length === 0)
53
+ return;
54
+ const previous = messages[messages.length - 1];
55
+ if (previous?.role === role)
56
+ previous.content.push(...blocks);
57
+ else
58
+ messages.push({ role, content: [...blocks] });
59
+ if (cacheable)
60
+ cacheCandidates.push(...blocks);
61
+ }
62
+ /**
63
+ * 把会话落盘使用的 OpenAI 兼容消息转换为 Anthropic Messages 内容块。
64
+ * history 保持旧格式以兼容已有 session;provider 边界负责 tool_use/tool_result 编码。
65
+ */
66
+ export function encodeAnthropicMessages(input, promptCache = config.anthropicPromptCache) {
67
+ const system = [];
68
+ const messages = [];
69
+ const cacheCandidates = [];
70
+ let dialogStarted = false;
71
+ for (const raw of input) {
72
+ const message = raw;
73
+ if (message.role === 'system' && !dialogStarted) {
74
+ system.push(...textBlocks(message.content));
75
+ continue;
76
+ }
77
+ dialogStarted = true;
78
+ if (message.role === 'system') {
79
+ const blocks = textBlocks(message.content).map((block) => ({
80
+ ...block,
81
+ text: `[System reminder]\n${String(block.text ?? '')}`,
82
+ }));
83
+ // 动态尾部 reminder 每步可能变化,不把 cache breakpoint 放在它之后。
84
+ appendMessage(messages, 'user', blocks, cacheCandidates, false);
85
+ continue;
86
+ }
87
+ if (message.role === 'user') {
88
+ appendMessage(messages, 'user', textBlocks(message.content), cacheCandidates);
89
+ continue;
90
+ }
91
+ if (message.role === 'assistant') {
92
+ const blocks = textBlocks(message.content);
93
+ for (const call of message.tool_calls ?? []) {
94
+ blocks.push({
95
+ type: 'tool_use',
96
+ id: call.id ?? '',
97
+ name: call.function?.name ?? '',
98
+ input: parseJsonObject(call.function?.arguments ?? ''),
99
+ });
100
+ }
101
+ appendMessage(messages, 'assistant', blocks, cacheCandidates);
102
+ continue;
103
+ }
104
+ if (message.role === 'tool') {
105
+ appendMessage(messages, 'user', [{
106
+ type: 'tool_result',
107
+ tool_use_id: message.tool_call_id ?? '',
108
+ content: typeof message.content === 'string'
109
+ ? message.content
110
+ : JSON.stringify(message.content ?? ''),
111
+ }], cacheCandidates);
112
+ }
113
+ }
114
+ if (promptCache) {
115
+ // 最后一个稳定消息块形成会话前缀断点;动态 session reminder 明确不参与。
116
+ const last = cacheCandidates[cacheCandidates.length - 1];
117
+ if (last)
118
+ last.cache_control = { type: 'ephemeral' };
119
+ // 没有对话时仍缓存稳定 system prompt。
120
+ else if (system.length > 0)
121
+ system[system.length - 1].cache_control = { type: 'ephemeral' };
122
+ }
123
+ return { system, messages };
124
+ }
125
+ export function encodeAnthropicTools(tools, promptCache = config.anthropicPromptCache) {
126
+ const encoded = tools.map((tool) => ({
127
+ name: tool.function.name,
128
+ description: tool.function.description,
129
+ input_schema: tool.function.parameters ?? { type: 'object', properties: {} },
130
+ }));
131
+ if (promptCache && encoded.length > 0) {
132
+ encoded[encoded.length - 1] = {
133
+ ...encoded[encoded.length - 1],
134
+ cache_control: { type: 'ephemeral' },
135
+ };
136
+ }
137
+ return encoded;
138
+ }
139
+ function endpoint(baseURL) {
140
+ const base = baseURL.replace(/\/+$/, '');
141
+ if (/\/v1$/i.test(base))
142
+ return `${base}/messages`;
143
+ if (/\/v1\/messages$/i.test(base))
144
+ return base;
145
+ return `${base}/v1/messages`;
146
+ }
147
+ export function buildAnthropicRequest(messages, tools) {
148
+ const encoded = encodeAnthropicMessages(messages);
149
+ const anthropicTools = encodeAnthropicTools(tools);
150
+ return {
151
+ model: getActiveModel(),
152
+ max_tokens: config.maxTokens ?? 8192,
153
+ stream: true,
154
+ system: encoded.system,
155
+ messages: encoded.messages,
156
+ ...(anthropicTools.length > 0 ? { tools: anthropicTools } : {}),
157
+ };
158
+ }
159
+ class AnthropicHttpError extends Error {
160
+ status;
161
+ code;
162
+ headers;
163
+ constructor(status, message, headers, code) {
164
+ super(message);
165
+ this.name = 'AnthropicAPIError';
166
+ this.status = status;
167
+ this.code = code;
168
+ this.headers = headers;
169
+ }
170
+ }
171
+ async function throwResponseError(response) {
172
+ let message = `Anthropic API HTTP ${response.status}`;
173
+ let code;
174
+ try {
175
+ const body = JSON.parse(await response.text());
176
+ if (typeof body.error?.message === 'string')
177
+ message = body.error.message;
178
+ if (typeof body.error?.type === 'string')
179
+ code = body.error.type;
180
+ }
181
+ catch {
182
+ // 非 JSON 错误页只保留状态码,避免把代理 HTML 大段写进 TUI。
183
+ }
184
+ throw new AnthropicHttpError(response.status, message, response.headers, code);
185
+ }
186
+ function decodeSseRecord(record) {
187
+ let event = '';
188
+ const data = [];
189
+ for (const line of record.split(/\r?\n/)) {
190
+ if (line.startsWith('event:'))
191
+ event = line.slice(6).trim();
192
+ else if (line.startsWith('data:'))
193
+ data.push(line.slice(5).trimStart());
194
+ }
195
+ if (data.length === 0)
196
+ return null;
197
+ return { event, data: data.join('\n') };
198
+ }
199
+ async function* readSse(response) {
200
+ if (!response.body)
201
+ throw new Error('Anthropic 流式响应缺少 body');
202
+ const reader = response.body.getReader();
203
+ const decoder = new TextDecoder();
204
+ let buffer = '';
205
+ while (true) {
206
+ const { value, done } = await reader.read();
207
+ buffer += decoder.decode(value, { stream: !done });
208
+ let match;
209
+ while ((match = /\r?\n\r?\n/.exec(buffer)) !== null) {
210
+ const record = buffer.slice(0, match.index);
211
+ buffer = buffer.slice(match.index + match[0].length);
212
+ const parsed = decodeSseRecord(record);
213
+ if (parsed)
214
+ yield parsed;
215
+ }
216
+ if (done)
217
+ break;
218
+ }
219
+ const parsed = decodeSseRecord(buffer);
220
+ if (parsed)
221
+ yield parsed;
222
+ }
223
+ function finiteToken(value) {
224
+ return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : 0;
225
+ }
226
+ function usageFromAnthropic(raw) {
227
+ const direct = finiteToken(raw.input_tokens);
228
+ const cacheRead = finiteToken(raw.cache_read_input_tokens);
229
+ const cacheCreation = finiteToken(raw.cache_creation_input_tokens);
230
+ const output = finiteToken(raw.output_tokens);
231
+ const prompt = direct + cacheRead + cacheCreation;
232
+ return {
233
+ promptTokens: prompt,
234
+ completionTokens: output,
235
+ totalTokens: prompt + output,
236
+ cachedTokens: cacheRead,
237
+ cacheCreationTokens: cacheCreation,
238
+ reasoningTokens: 0,
239
+ };
240
+ }
241
+ function addLiveCount(text, state) {
242
+ for (const ch of text) {
243
+ const cp = ch.codePointAt(0) ?? 0;
244
+ if ((cp >= 0x4e00 && cp <= 0x9fff)
245
+ || (cp >= 0x3400 && cp <= 0x4dbf)
246
+ || (cp >= 0x3040 && cp <= 0x30ff)
247
+ || (cp >= 0xac00 && cp <= 0xd7a3))
248
+ state.cjk++;
249
+ else
250
+ state.other++;
251
+ }
252
+ }
253
+ /** Anthropic Messages API 单次请求;外层 chat() 继续统一负责重试。 */
254
+ export async function anthropicChatOnce(messages, handlers, signal, tools) {
255
+ const fetchImpl = fetchImplOverride ?? fetch;
256
+ const response = await fetchImpl(endpoint(config.baseURL), {
257
+ method: 'POST',
258
+ headers: {
259
+ 'content-type': 'application/json',
260
+ accept: 'text/event-stream',
261
+ 'x-api-key': config.apiKey,
262
+ 'anthropic-version': process.env.ANTHROPIC_VERSION || '2023-06-01',
263
+ },
264
+ body: JSON.stringify(buildAnthropicRequest(messages, tools)),
265
+ signal,
266
+ });
267
+ if (!response.ok)
268
+ await throwResponseError(response);
269
+ let content = '';
270
+ let usage;
271
+ const toolAcc = new Map();
272
+ const live = { cjk: 0, other: 0 };
273
+ const reportLive = () => {
274
+ handlers.onProgress?.({ completionTokens: Math.ceil(live.cjk + live.other / 4) });
275
+ };
276
+ for await (const record of readSse(response)) {
277
+ if (record.data === '[DONE]')
278
+ continue;
279
+ let event;
280
+ try {
281
+ event = JSON.parse(record.data);
282
+ }
283
+ catch {
284
+ continue;
285
+ }
286
+ const type = typeof event.type === 'string' ? event.type : record.event;
287
+ if (type === 'error') {
288
+ const error = event.error;
289
+ const thrown = new Error(typeof error?.message === 'string' ? error.message : 'Anthropic stream error');
290
+ thrown.name = typeof error?.type === 'string' ? error.type : 'AnthropicStreamError';
291
+ throw thrown;
292
+ }
293
+ if (type === 'message_start') {
294
+ const message = event.message;
295
+ if (message?.usage)
296
+ usage = usageFromAnthropic(message.usage);
297
+ continue;
298
+ }
299
+ if (type === 'content_block_start') {
300
+ const index = finiteToken(event.index);
301
+ const block = event.content_block;
302
+ if (block?.type === 'text' && typeof block.text === 'string' && block.text) {
303
+ content += block.text;
304
+ addLiveCount(block.text, live);
305
+ handlers.onText?.(block.text);
306
+ reportLive();
307
+ }
308
+ else if (block?.type === 'tool_use') {
309
+ const initialInput = block.input && typeof block.input === 'object'
310
+ ? block.input
311
+ : undefined;
312
+ const call = {
313
+ id: typeof block.id === 'string' ? block.id : '',
314
+ name: typeof block.name === 'string' ? block.name : '',
315
+ // 流式 tool_use 通常以 input:{} 开始,真正 JSON 紧随 input_json_delta;
316
+ // 只有非空 input 才直接采用,避免累加成 `{}{...}`。
317
+ arguments: initialInput && Object.keys(initialInput).length > 0
318
+ ? JSON.stringify(initialInput)
319
+ : '',
320
+ };
321
+ toolAcc.set(index, call);
322
+ if (call.name)
323
+ handlers.onToolCall?.(call.name);
324
+ }
325
+ continue;
326
+ }
327
+ if (type === 'content_block_delta') {
328
+ const index = finiteToken(event.index);
329
+ const delta = event.delta;
330
+ if (delta?.type === 'text_delta' && typeof delta.text === 'string') {
331
+ content += delta.text;
332
+ addLiveCount(delta.text, live);
333
+ handlers.onText?.(delta.text);
334
+ reportLive();
335
+ }
336
+ else if (delta?.type === 'input_json_delta' && typeof delta.partial_json === 'string') {
337
+ const call = toolAcc.get(index) ?? { id: '', name: '', arguments: '' };
338
+ call.arguments += delta.partial_json;
339
+ toolAcc.set(index, call);
340
+ addLiveCount(delta.partial_json, live);
341
+ reportLive();
342
+ }
343
+ continue;
344
+ }
345
+ if (type === 'message_delta') {
346
+ const raw = event.usage;
347
+ if (raw) {
348
+ const previous = usage ?? usageFromAnthropic({});
349
+ const output = finiteToken(raw.output_tokens);
350
+ usage = {
351
+ ...previous,
352
+ completionTokens: output,
353
+ totalTokens: previous.promptTokens + output,
354
+ };
355
+ }
356
+ }
357
+ }
358
+ if (usage) {
359
+ handlers.onProgress?.({
360
+ completionTokens: usage.completionTokens,
361
+ promptTokens: usage.promptTokens,
362
+ cachedTokens: usage.cachedTokens,
363
+ cacheCreationTokens: usage.cacheCreationTokens,
364
+ });
365
+ }
366
+ const toolCalls = [...toolAcc.entries()]
367
+ .sort((a, b) => a[0] - b[0])
368
+ .map(([, call]) => ({ ...call, arguments: call.arguments || '{}' }));
369
+ return { content: content || null, toolCalls, usage };
370
+ }
@@ -1,23 +1,23 @@
1
- // memory 发现子系统:加载项目记忆 MOCODE.md(对标 skills/discover.ts 的叶子模式)。
1
+ // memory 发现子系统:加载项目记忆 AGENTS.md(对标 skills/discover.ts 的叶子模式)。
2
2
  // 仅依赖 node 标准库,是叶子模块:不依赖 config/agent/llm/tools/skills,避免环。
3
3
  //
4
- // 约定:纯 MOCODE.md(mocode 是独立工具,有自己的工具集与约定)。
5
- // 项目级从 cwd 向上逐级找,全局 ~/.mocode/MOCODE.md。全量注入 systemPrompt(超长截断);
4
+ // 约定:纯 AGENTS.md(mocode 是独立工具,有自己的工具集与约定)。
5
+ // 项目级从 cwd 向上逐级找,全局 ~/.mocode/AGENTS.md。全量注入 systemPrompt(超长截断);
6
6
  import { existsSync, readFileSync } from 'node:fs';
7
7
  import os from 'node:os';
8
8
  import path from 'node:path';
9
9
  /**
10
- * 返回要查找的 MOCODE.md 路径列表,按「远→近」顺序(合并时近的在后,更突出):
11
- * 全局 ~/.mocode/MOCODE.md → 项目级从根到 cwd 逐级 MOCODE.md。
10
+ * 返回要查找的 AGENTS.md 路径列表,按「远→近」顺序(合并时近的在后,更突出):
11
+ * 全局 ~/.mocode/AGENTS.md → 项目级从根到 cwd 逐级 AGENTS.md。
12
12
  * 向上遍历 cwd 到根收集 [cwd..root],反转为 [root..cwd](远→近),前拼全局。
13
13
  */
14
14
  export function resolveMemoryFiles() {
15
- const globalPath = path.join(os.homedir(), '.mocode', 'MOCODE.md');
15
+ const globalPath = path.join(os.homedir(), '.mocode', 'AGENTS.md');
16
16
  const projectFiles = [];
17
17
  let dir = process.cwd();
18
18
  const root = path.parse(dir).root; // win32 'C:\\', POSIX '/'
19
19
  for (;;) {
20
- projectFiles.push(path.join(dir, 'MOCODE.md'));
20
+ projectFiles.push(path.join(dir, 'AGENTS.md'));
21
21
  if (dir === root || dir === path.dirname(dir))
22
22
  break; // 到根:dirname 自身
23
23
  dir = path.dirname(dir);
@@ -26,7 +26,7 @@ export function resolveMemoryFiles() {
26
26
  return [globalPath, ...projectFiles];
27
27
  }
28
28
  /**
29
- * 读取所有存在的 MOCODE.md,返回 { path, content }(content 已 trim)。
29
+ * 读取所有存在的 AGENTS.md,返回 { path, content }(content 已 trim)。
30
30
  * 全程静默容错(不存在 / 读失败 → 跳过,不抛),风格对齐 skills/discover.ts 与 session/persist.ts。
31
31
  */
32
32
  export function loadMemoryFiles() {
@@ -1,6 +1,7 @@
1
1
  // Memory barrel: Tier-2 JSONL store + knowledge-graph layer + background reflection.
2
- // MOCODE.md is intentionally not loaded here: the system prompt only tells the agent
3
- // to read the workspace file on demand, keeping its full body out of every request.
2
+ // AGENTS.md is intentionally not loaded here either: config/index.ts
3
+ // (buildAgentsImportSection) auto-imports the workspace-root AGENTS.md body into the
4
+ // system prompt — independent of the memory switch — truncating it when it exceeds the cap.
4
5
  export { buildMemoryIndexSection, loadAll, gcMemories, } from './store.js';
5
6
  export { addTriple, upsertEntity, findEntity, searchGraph, neighborsOf, pathBetween, graphStats, } from './graph.js';
6
7
  export { kickoffReflection, drainMemoryBackground, getLastReflectResult, clearLastReflectResult, snapshotTranscript, formatReflectResult, runReflection, } from './reflect.js';