mocode-ai 1.2.4 → 1.2.6
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 +6 -6
- package/README.zh-CN.md +4 -4
- package/dist/agent/core.js +9 -9
- package/dist/config/index.js +78 -13
- package/dist/context/classifier.js +1 -0
- package/dist/context/encoders/table.js +1 -1
- package/dist/i18n/index.js +4 -4
- package/dist/llm/index.js +0 -1
- package/dist/memory/discover.js +8 -8
- package/dist/memory/graph.js +409 -0
- package/dist/memory/index.js +5 -3
- package/dist/memory/reflect.js +25 -6
- package/dist/repl/index.js +14 -14
- package/dist/sandbox/policy.js +1 -1
- package/dist/session/notes.js +233 -0
- package/dist/skills/runner.js +0 -1
- package/dist/tools/builtins/index.js +9 -1
- package/dist/tools/builtins/memory-graph.js +118 -0
- package/dist/tools/builtins/memory-save.js +42 -5
- package/dist/tools/builtins/memory-search.js +26 -5
- package/dist/tools/builtins/note-append.js +103 -0
- package/dist/tools/constants.js +6 -0
- package/dist/tools/registry.js +13 -4
- package/dist/ui/diff.js +1 -1
- package/dist/ui/layout.js +3 -3
- package/dist/ui/render.js +7 -1
- package/dist/ui/theme.js +11 -11
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ MoCode isn't a chat box with a coat of paint — it's an agent that actually get
|
|
|
79
79
|
- **Plan / Auto dual mode** — In `plan` mode the agent is read-only (reads code, queries indexes, searches — never writes to disk, runs commands, or spawns sub-agents) and produces a plan; `auto` mode unlocks the full toolset. The agent can switch between the two on its own — scope out an unfamiliar codebase first, then start making changes.
|
|
80
80
|
- **Pressure-driven context compression** — Normal history keeps full tool evidence. At 80% occupancy, one scheduler event runs all enabled cleanup and always follows with a history summary. `/context` shows live usage and `/compact` remains an explicit manual override.
|
|
81
81
|
- **Cross-session long-term memory** — The agent can save project architecture, conventions, and lessons learned as long-term memory, auto-loaded in future sessions. A background process periodically reflects on conversations to mine things worth remembering. Memories can be created, searched, updated, and forgotten, with recall-based decay.
|
|
82
|
-
- **Project context (`
|
|
82
|
+
- **Project context (`AGENTS.md`)** — A single project-level memory file at `AGENTS.md` captures both static facts (project description, commands, module list, directory tree) and human/AI-written insights (conventions, architectural decisions, pitfalls). Generate it once with `/init`, then keep it up to date by hand or by asking the agent to refresh it. Loaded automatically into the system prompt on every turn.
|
|
83
83
|
- **Session notepad (notes.md)** — For complex multi-step tasks (≥3 file changes / ≥5 tool calls), the agent maintains a working notepad at `.mocode/sessions/<sessionId>/notes.md` (file-based, survives context compression). It records the execution plan with the dedicated `plan_update` tool — a three-state step machine (`pending`/`in_progress`/`completed`, at most one `in_progress`) that auto-settles to `## Done:` when finished. The active plan is re-injected into the system prompt after compaction and re-synced into context whenever notes.md changes, and a gentle reminder nudges the agent if it goes several tool-steps without updating the plan. A live progress chip in the TUI status bar shows `plan: [title] (3/7) ▸ [current step]`.
|
|
84
84
|
- **Interruptible and reversible** — Ctrl+C interrupts the current turn at any time (kills child processes recursively, rolls history back to before the turn started, leaves no half-finished tool calls). `/rollback` restores file changes from per-turn snapshots, with a per-file keep/undo choice — no git dependency required.
|
|
85
85
|
- **Sandbox protection** — File reads/writes go through a sandbox that blocks out-of-bounds paths (`../../`, absolute paths outside the root, symlink escapes, etc.), so the agent never touches files outside your working directory.
|
|
@@ -96,7 +96,7 @@ MoCode isn't a chat box with a coat of paint — it's an agent that actually get
|
|
|
96
96
|
## Documentation
|
|
97
97
|
|
|
98
98
|
- [中文使用指南](./docs/usage.md) — 菜单式快速上手、命令速查、模式、会话、项目上下文与排障。
|
|
99
|
-
- [Project context](./docs/usage.md#项目上下文) — `
|
|
99
|
+
- [Project context](./docs/usage.md#项目上下文) — `AGENTS.md` and Skills.
|
|
100
100
|
|
|
101
101
|
## Installation
|
|
102
102
|
|
|
@@ -218,7 +218,7 @@ The agent operates in **the working directory it was launched from** — to have
|
|
|
218
218
|
| `memory_update` | Edit a memory in place (id unchanged; correct stale facts / update summary / toggle pin) |
|
|
219
219
|
| `memory_forget` | Forget a memory: archived by default (recoverable), `mode=delete` for a hard delete (pinned memories can't be deleted) |
|
|
220
220
|
|
|
221
|
-
The five `memory_*` tools are gated on `MEMORY_ENABLED=true` at startup; toggle at runtime with `/memory_switch` (REPL restart required, by design — see Skills section for the difference between Tier-1 `
|
|
221
|
+
The five `memory_*` tools are gated on `MEMORY_ENABLED=true` at startup; toggle at runtime with `/memory_switch` (REPL restart required, by design — see Skills section for the difference between Tier-1 `AGENTS.md` and Tier-2 memory).
|
|
222
222
|
|
|
223
223
|
The four frontend tools — `browser`, `dev_server`, `screenshot`, `view_image` — are **off by default** (they depend on the Playwright binary, spawn long-lived processes, or capture the desktop). Enable the whole cluster at runtime with `/fe on`; the model only sees them once enabled. Toggle with `/fe on|off|status`.
|
|
224
224
|
|
|
@@ -254,7 +254,7 @@ dev_server stop id=srv-xxxx
|
|
|
254
254
|
| `/memory_switch` | Toggle Tier-2 memory on/off (REPL restart required — by design) |
|
|
255
255
|
| `/reflect` | Manually trigger a background memory reflection pass |
|
|
256
256
|
| `/model` | Configure the LLM (baseURL / apiKey / model / context window), applied immediately + persisted |
|
|
257
|
-
| `/init` | Scan the project and generate `
|
|
257
|
+
| `/init` | Scan the project and generate `AGENTS.md` project memory (dispatched to the agent) |
|
|
258
258
|
| `/theme` | Switch color theme (↑↓ · Enter, or `/theme <name>` directly) |
|
|
259
259
|
| `/plan` | Switch to plan mode (read-only exploration + plan output, approve to switch to auto) |
|
|
260
260
|
| `/auto` | Switch back to auto mode (full toolset execution) |
|
|
@@ -293,11 +293,11 @@ A skill's `description` is injected into the system prompt (progressive disclosu
|
|
|
293
293
|
|
|
294
294
|
The system prompt provides lightweight guidance rather than a framework gate: inspect only what matters, make focused changes, avoid repeated stale reads, and report uncertainty honestly. The agent decides whether validation is useful for the task and chooses the scope itself. Broad test/build suites are not run by default, and lack of validation never blocks completion or triggers an extra model turn.
|
|
295
295
|
|
|
296
|
-
## Project memory (
|
|
296
|
+
## Project memory (AGENTS.md)
|
|
297
297
|
|
|
298
298
|
MoCode has a **two-tier memory** model distinct from skills:
|
|
299
299
|
|
|
300
|
-
- **Tier-1 — `
|
|
300
|
+
- **Tier-1 — `AGENTS.md` (auto-loaded every session):** Markdown project memory that gets concatenated into the system prompt on every turn. Discovery walks `~/.mocode/AGENTS.md` → every `AGENTS.md` from the cwd up to the filesystem root (far→near, near wins). On overflow the body is truncated with a marker pointing back at the files. Generate or refresh one with `/init`, or write it by hand — it's plain Markdown, no schema. `AGENTS.md` is also where the agent itself persists "next-session facts" it deduces (architecture, conventions, pitfalls).
|
|
301
301
|
- **Tier-2 — `memory_*` tool library (agent-driven, opt-in):** Discrete tagged records (`decision` / `fact` / `pitfall` / `reference` / `feedback`) with recall-count-based decay (30-day → archived; 90-day → GC). The agent saves / searches / updates / forgets via tools; titles go in the system-prompt index (≤50), bodies fetched on demand via `memory_search`. Off by default; toggle with `MEMORY_ENABLED=true` at startup or `/memory_switch` (REPL restart required).
|
|
302
302
|
|
|
303
303
|
## Type checking
|
package/README.zh-CN.md
CHANGED
|
@@ -95,7 +95,7 @@ mocode 不是一个套壳聊天框,而是一个能真正动手干活的 agent:
|
|
|
95
95
|
## 使用文档
|
|
96
96
|
|
|
97
97
|
- [菜单式使用指南](./docs/usage.md) — 快速上手、命令速查、模式、会话、项目上下文与排障。
|
|
98
|
-
- [项目上下文:`
|
|
98
|
+
- [项目上下文:`AGENTS.md` 与 Skills](./docs/usage.md#项目上下文)
|
|
99
99
|
|
|
100
100
|
## 安装
|
|
101
101
|
|
|
@@ -257,7 +257,7 @@ dev_server stop id=srv-xxxx
|
|
|
257
257
|
| `/memory_switch` | 切换 Tier-2 记忆开关(需重启 REPL,刻意为之) |
|
|
258
258
|
| `/reflect` | 手动触发一次后台记忆反思 pass |
|
|
259
259
|
| `/model` | 配置大模型(baseURL / apiKey / model / 上下文窗口),即时生效 + 持久化 |
|
|
260
|
-
| `/init` | 扫描项目生成 `
|
|
260
|
+
| `/init` | 扫描项目生成 `AGENTS.md` 项目记忆(发给 agent 执行) |
|
|
261
261
|
| `/theme` | 切换颜色主题(↑↓ · Enter,或 `/theme <name>` 直切) |
|
|
262
262
|
| `/plan` | 切到 plan 模式(只读探查 + 产出计划,审批后切 auto 执行) |
|
|
263
263
|
| `/auto` | 切回 auto 模式(全量工具执行) |
|
|
@@ -296,11 +296,11 @@ skill 的 `description` 注入系统提示(渐进式披露第①层),模型只
|
|
|
296
296
|
|
|
297
297
|
system prompt 提供轻量建议而不是框架硬门:只检查支持下一步决策的内容,做最小完整改动,避免重复读取,并诚实说明不确定性。agent 自主决定是否需要验证以及验证范围;框架不会因为未验证阻止完成或追加模型轮次。
|
|
298
298
|
|
|
299
|
-
## 项目记忆(
|
|
299
|
+
## 项目记忆(AGENTS.md)
|
|
300
300
|
|
|
301
301
|
mocode 的**双层记忆**模型,跟 Skills 是两件事:
|
|
302
302
|
|
|
303
|
-
- **Tier-1 — `
|
|
303
|
+
- **Tier-1 — `AGENTS.md`(每轮自动加载):** Markdown 项目记忆,每轮拼进 system prompt。发现路径:`~/.mocode/AGENTS.md` → 从 cwd 往上逐级 `AGENTS.md`(远→近拼接,近的覆盖更突出);超长截断并标注原始文件。运行 `/init` 生成或刷新,纯 Markdown,可手写,无 schema。agent 自己推得的「下次要记住的事实」(架构/约定/坑位)也写在这里。
|
|
304
304
|
- **Tier-2 — `memory_*` 工具库(agent 主导,需启用):** 离散带标签条目(`decision` / `fact` / `pitfall` / `reference` / `feedback`),按召回计数衰减(30 天 → archived,90 天 → 硬删 GC)。agent 用工具存 / 搜 / 改 / 删;索引(标题)进系统提示(≤50 条),正文按需 `memory_search` 取。默认关,启动 `MEMORY_ENABLED=true` 或 REPL 内 `/memory_switch`(需重启 REPL,刻意为之)。
|
|
305
305
|
|
|
306
306
|
## 类型检查
|
package/dist/agent/core.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { readFileSync } from 'node:fs';
|
|
8
8
|
import { getNotesMtime } from '../session/notes.js';
|
|
9
9
|
import { chat, estimatePromptTokens, planChatTools, chatTools, } from '../llm/index.js';
|
|
10
|
-
import { executeToolOutcome, getToolCapabilities, isFileMutationTool,
|
|
10
|
+
import { executeToolOutcome, findTool, getToolCapabilities, isFileMutationTool, } from '../tools/registry.js';
|
|
11
11
|
import { checkPermission } from '../permissions/index.js';
|
|
12
12
|
import { validateToolArguments } from '../tools/validation.js';
|
|
13
13
|
import { getPlanDisabledTools, getRuntimeDisabledTools } from '../tools/constants.js';
|
|
@@ -18,7 +18,7 @@ import { createBudgetScheduler } from '../session/scheduler.js';
|
|
|
18
18
|
import { recordArtifact, invalidateArtifacts, rehydrateArtifacts, } from '../context/index.js';
|
|
19
19
|
import { createRelevancePruner } from '../context/relevance.js';
|
|
20
20
|
import { isToolResultSuccess } from '../context/utils.js';
|
|
21
|
-
import { config, extractActivePlanSection,
|
|
21
|
+
import { config, extractActivePlanSection, reinjectSessionStateIntoSystem } from '../config/index.js';
|
|
22
22
|
import { t } from '../i18n/index.js';
|
|
23
23
|
import { jailResolve } from '../sandbox/index.js';
|
|
24
24
|
import { createLifecycleEngine } from '../context/lifecycle.js';
|
|
@@ -42,13 +42,13 @@ function parseArgs(raw) {
|
|
|
42
42
|
}
|
|
43
43
|
/** 只有显式声明 parallel 且无需权限确认的工具才进入普通并发组。 */
|
|
44
44
|
function isParallelTool(name) {
|
|
45
|
-
const tool =
|
|
45
|
+
const tool = findTool(name);
|
|
46
46
|
return !!tool && (tool.risk ?? 'safe') === 'safe' &&
|
|
47
47
|
getToolCapabilities(tool).concurrency === 'parallel';
|
|
48
48
|
}
|
|
49
49
|
/** resource-locked 工具先顺序完成权限预检,再依赖 canonical resource lock 并发执行。 */
|
|
50
50
|
function isResourceLockedTool(name) {
|
|
51
|
-
const tool =
|
|
51
|
+
const tool = findTool(name);
|
|
52
52
|
return !!tool && getToolCapabilities(tool).concurrency === 'resource-locked';
|
|
53
53
|
}
|
|
54
54
|
function isResourceLockedCall(call) {
|
|
@@ -326,8 +326,8 @@ export async function runAgentCore(opts) {
|
|
|
326
326
|
runtimeContextState.lifecycleStats = lifecycle.stats();
|
|
327
327
|
}
|
|
328
328
|
rehydrateArtifacts(runtimeContextState, history);
|
|
329
|
-
// ② compact
|
|
330
|
-
|
|
329
|
+
// ② compact 后把会话状态(活跃 plan + 笔记段)重注入系统提示,避免 agent 因上下文压缩丢失计划与笔记。
|
|
330
|
+
reinjectSessionStateIntoSystem(history);
|
|
331
331
|
}
|
|
332
332
|
hooks.onStepStart?.(); // 主 agent:spinner.start('思考中')
|
|
333
333
|
mode = 'idle';
|
|
@@ -602,7 +602,7 @@ export async function runAgentCore(opts) {
|
|
|
602
602
|
for (let k = 0; k < batch.length; k++) {
|
|
603
603
|
const tc = batch[k];
|
|
604
604
|
const parsed = parseArgs(tc.arguments);
|
|
605
|
-
const tool =
|
|
605
|
+
const tool = findTool(tc.name);
|
|
606
606
|
const argumentsValid = tool && parsed !== null
|
|
607
607
|
? validateToolArguments(tool, parsed).valid
|
|
608
608
|
: false;
|
|
@@ -693,7 +693,7 @@ export async function runAgentCore(opts) {
|
|
|
693
693
|
// 权限预检查:在渲染 ● 头之前弹确认面板(体验:先问再执行,而非执行完再问)。
|
|
694
694
|
// 拒绝时只渲染拒绝结果,不渲染执行头;放行则继续走 header → start → executeTool 流程。
|
|
695
695
|
const parsed = parseArgs(tc.arguments);
|
|
696
|
-
const tool =
|
|
696
|
+
const tool = findTool(tc.name);
|
|
697
697
|
const argumentsValid = tool && parsed !== null
|
|
698
698
|
? validateToolArguments(tool, parsed).valid
|
|
699
699
|
: false;
|
|
@@ -768,7 +768,7 @@ export async function runAgentCore(opts) {
|
|
|
768
768
|
// B(nag 提醒):连续 N 步有工具活动但没更新 plan,在当前步第一条 tool_result 前注入提醒。
|
|
769
769
|
const notesMtimeAfter = getNotesMtime();
|
|
770
770
|
if (notesMtimeAfter !== notesMtimeBefore) {
|
|
771
|
-
|
|
771
|
+
reinjectSessionStateIntoSystem(history);
|
|
772
772
|
stepsSincePlanTouch = 0;
|
|
773
773
|
}
|
|
774
774
|
else {
|
package/dist/config/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import os from 'node:os';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import dotenv from 'dotenv';
|
|
5
5
|
import { getCurrentSessionId } from '../session/state.js';
|
|
6
|
-
import { getNotesFilePath } from '../session/notes.js';
|
|
6
|
+
import { getNotesFilePath, extractActiveNotesSections } from '../session/notes.js';
|
|
7
7
|
import { buildWorkDisciplineSection, inferModelFamily } from '../agent/work-discipline.js';
|
|
8
8
|
import { detectLanguage, setLanguage, t, } from '../i18n/index.js';
|
|
9
9
|
/**
|
|
@@ -214,19 +214,81 @@ export function reinjectActivePlanIntoSystem(history) {
|
|
|
214
214
|
sys.content = `${content}${ACTIVE_PLAN_MARKER}${plan}\n`;
|
|
215
215
|
return true;
|
|
216
216
|
}
|
|
217
|
+
/** compact 重注入用的幂等标记:history[0] 中夹住会话笔记段正文(Findings/Decisions/Open Questions/Risks),
|
|
218
|
+
* 重复注入只替换不累积。与 ACTIVE_PLAN_MARKER 独立,互不干扰。 */
|
|
219
|
+
const NOTES_BODY_MARKER = '\n\n<!-- mocode:session-notes -->\n';
|
|
220
|
+
/**
|
|
221
|
+
* 把会话笔记段正文重注入系统提示(history[0])。compact 后或本步改了 notes.md 时调用:
|
|
222
|
+
* 若 notes.md 有活跃笔记段(extractActiveNotesSections 返回非空,已按 5k token 预算裁剪),
|
|
223
|
+
* 则覆盖旧标记块写入最新内容;若无,则清掉残留标记块。直接改 history[0].content,
|
|
224
|
+
* 幂等,返回是否改动。与 reinjectActivePlanIntoSystem 独立:plan 段由后者管,笔记段由本函数管。
|
|
225
|
+
*/
|
|
226
|
+
export function reinjectSessionNotesIntoSystem(history) {
|
|
227
|
+
const sys = history[0];
|
|
228
|
+
if (!sys || sys.role !== 'system' || typeof sys.content !== 'string')
|
|
229
|
+
return false;
|
|
230
|
+
let content = sys.content;
|
|
231
|
+
const markerIdx = content.indexOf(NOTES_BODY_MARKER);
|
|
232
|
+
if (markerIdx >= 0) {
|
|
233
|
+
content = content.slice(0, markerIdx).replace(/\s+$/, '');
|
|
234
|
+
}
|
|
235
|
+
const notes = extractActiveNotesSections();
|
|
236
|
+
if (!notes) {
|
|
237
|
+
if (markerIdx < 0)
|
|
238
|
+
return false;
|
|
239
|
+
sys.content = content;
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
sys.content = `${content}${NOTES_BODY_MARKER}${notes}\n`;
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* 一次性重注入会话状态(plan 段 + 笔记段)到系统提示。调用点(core.ts compact 后 /
|
|
247
|
+
* 本步改 notes.md、repl compact 时)统一用本函数替代 reinjectActivePlanIntoSystem,
|
|
248
|
+
* 让笔记段享有与 plan 段完全对称的常驻 + 恢复时机。返回任一 marker 是否改动。
|
|
249
|
+
*/
|
|
250
|
+
export function reinjectSessionStateIntoSystem(history) {
|
|
251
|
+
const planChanged = reinjectActivePlanIntoSystem(history);
|
|
252
|
+
const notesChanged = reinjectSessionNotesIntoSystem(history);
|
|
253
|
+
return planChanged || notesChanged;
|
|
254
|
+
}
|
|
217
255
|
const SYSTEM_PROMPT_MEMORY_SECTION = `
|
|
218
256
|
## Memory (cross-session facts)
|
|
219
|
-
- The prompt may contain a title/summary index; retrieve details with memory_search or inspect all with memory_list.
|
|
220
|
-
- Save only stable, non-obvious cross-session facts. Search before saving; update an existing entry instead of duplicating it, and archive stale entries
|
|
221
|
-
|
|
257
|
+
- The prompt may contain a title/summary index; retrieve details with memory_search or inspect all with memory_list. memory_search also surfaces knowledge-graph facts (relations between entities) alongside entry bodies.
|
|
258
|
+
- Save only stable, non-obvious cross-session facts. Search before saving; update an existing entry instead of duplicating it, and archive stale entries.
|
|
259
|
+
- A knowledge-graph layer links entities across memories: explore relations/neighbors with memory_graph (neighbors/add/stats), and attach meaningful links via the links parameter of memory_save when saving.`;
|
|
260
|
+
/** AGENTS.md 自动导入正文上限:system 位于 history[0] 且 compactHistory 不压缩 system,超长需截断防占窗口(见 memory/README.md)。 */
|
|
261
|
+
const MAX_AGENTS_IMPORT_CHARS = 20000;
|
|
262
|
+
/**
|
|
263
|
+
* 工作区根 AGENTS.md 自动导入段:与 memory 开关完全无关——
|
|
264
|
+
* 只要 <cwd>/AGENTS.md 存在就把正文直接拼进 prompt(超 {@link MAX_AGENTS_IMPORT_CHARS} 截断+末尾提示),
|
|
265
|
+
* 不再只指路让模型按需 read_file。读失败静默跳过(返空串)。
|
|
266
|
+
*/
|
|
267
|
+
function buildAgentsImportSection() {
|
|
268
|
+
try {
|
|
269
|
+
const projectAgents = path.join(process.cwd(), 'AGENTS.md');
|
|
270
|
+
if (!fs.existsSync(projectAgents))
|
|
271
|
+
return '';
|
|
272
|
+
const content = fs.readFileSync(projectAgents, 'utf8').trim();
|
|
273
|
+
if (!content)
|
|
274
|
+
return '';
|
|
275
|
+
const body = content.length > MAX_AGENTS_IMPORT_CHARS
|
|
276
|
+
? `${content.slice(0, MAX_AGENTS_IMPORT_CHARS)}\n…[AGENTS.md truncated: first ${MAX_AGENTS_IMPORT_CHARS} characters injected]`
|
|
277
|
+
: content;
|
|
278
|
+
return `\n## Project memory (AGENTS.md, auto-imported)\n${body}\n- AGENTS.md may be stale: current code and the user request override stale memory.`;
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
return ''; // 读失败静默跳过:不让导入破坏 prompt 构建
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Memory 检索指导段(与 AGENTS.md 导入无关):开 isMemoryEnabled() 时才拼,
|
|
286
|
+
* 注入 memory_search/list/graph 的使用指导。默认关(新用户零侵入)。
|
|
287
|
+
*/
|
|
222
288
|
function buildMemoryPromptSection() {
|
|
223
289
|
if (!isMemoryEnabled())
|
|
224
290
|
return '';
|
|
225
|
-
|
|
226
|
-
const mocodeHint = fs.existsSync(projectMocode)
|
|
227
|
-
? '\n- `MOCODE.md` exists at the workspace root but is not preloaded. Read it with `read_file` only when the task may depend on project architecture, conventions, commands, prior decisions, or user preferences; skip it for greetings and unrelated simple requests. Current code and the user request override stale memory.'
|
|
228
|
-
: '';
|
|
229
|
-
return SYSTEM_PROMPT_MEMORY_SECTION + mocodeHint;
|
|
291
|
+
return SYSTEM_PROMPT_MEMORY_SECTION;
|
|
230
292
|
}
|
|
231
293
|
/**
|
|
232
294
|
* plan 模式追加到系统提示末尾的指令(切到 plan 模式时由 repl 拼进 history[0])。
|
|
@@ -257,6 +319,7 @@ ${buildPlanResearchRules()}`;
|
|
|
257
319
|
}
|
|
258
320
|
/** 兼容旧名字:repl 的 buildSystemMessage 仍引 PLAN_MODE_SUFFIX(变量)。运行时按需现拼。 */
|
|
259
321
|
export function buildBasePrompt(sessionId = getCurrentSessionId()) {
|
|
322
|
+
const agentsImportSection = buildAgentsImportSection();
|
|
260
323
|
const memorySection = buildMemoryPromptSection();
|
|
261
324
|
const notepadSection = buildNotepadSection(sessionId);
|
|
262
325
|
// 静态主体:稳定段落集中在前,让支持 prompt caching 的后端能命中前缀缓存(#12)。
|
|
@@ -312,9 +375,9 @@ ${buildVoiceSection()}
|
|
|
312
375
|
- **No flattery / no preamble in conclusions**: skip "Sure", "好的", "我已经完成了" and similar no-information prefixes — jump straight to substance.
|
|
313
376
|
- Report honestly: say success when successful, say where you're stuck when failing, and mention anything skipped. Reference code in "path:line" format (e.g., src/index.ts:42). Keep it concise.
|
|
314
377
|
${t('assistant.languageInstruction')}`;
|
|
315
|
-
// 动态段(置于末尾):memory 索引 + notepad 索引 + notepad 使用说明。
|
|
378
|
+
// 动态段(置于末尾):AGENTS.md 项目记忆(无条件) + memory 索引(按开关) + notepad 索引 + notepad 使用说明。
|
|
316
379
|
// 按需注入(#13):有内容的索引才拼对应标题,避免空标题噪声。
|
|
317
|
-
// - "## Project context" 仅当 memorySection/notepadSection
|
|
380
|
+
// - "## Project context" 仅当 agentsImportSection/memorySection/notepadSection 任一非空(notepad 索引依赖 notes.md 存在);
|
|
318
381
|
// - "## Session state" 使用说明**无条件**注入(放在动态尾段首位):否则会陷入"说明依赖 notes.md 存在 → 模型不知要建 → 文件永不存在"的鸡生蛋循环,功能对模型不可见。动态段在静态前缀之后,不影响 prompt 缓存。
|
|
319
382
|
const dynamicParts = [];
|
|
320
383
|
// 会话级私有尾段(子 agent 切片会丢弃):Session state 说明无条件注入在前,Project context 按需在后。
|
|
@@ -332,8 +395,10 @@ ${t('assistant.languageInstruction')}`;
|
|
|
332
395
|
'Keep at most one step in_progress, and mark a step completed as soon as its work is done — do not batch updates to the end of the turn. ' +
|
|
333
396
|
'Write each step so a teammate who lost the conversation could pick it up cold: name the file or symbol, the exact change, and the verification, so the plan survives context compaction. ' +
|
|
334
397
|
'plan_update creates notes.md for you when the task warrants it; read_file the full notes.md whenever you need to recover context after compaction. ' +
|
|
335
|
-
'When every step is completed, plan_update settles the plan to `## Done:` automatically. Keep other notes concise and session-specific; use memory for stable cross-session facts
|
|
336
|
-
|
|
398
|
+
'When every step is completed, plan_update settles the plan to `## Done:` automatically. Keep other notes concise and session-specific; use memory for stable cross-session facts.\n' +
|
|
399
|
+
'## Session notes (resident memory)\n' +
|
|
400
|
+
'For non-obvious, lasting-value discoveries — subtle constraints, decisions with downstream impact, open questions blocking a choice, or risks affecting later steps — call `note_append` IMMEDIATELY when you make the discovery. The note is written to the same notes.md and its body is re-injected into the prompt automatically (within a 5k-token budget), surviving compaction so you keep remembering what you found/decided this session. Do NOT use it for routine progress (that is the plan) or stable cross-session facts (that is memory_save). Each call appends one item.');
|
|
401
|
+
const ctxContent = `${agentsImportSection}${memorySection}${notepadSection}`.trimEnd();
|
|
337
402
|
if (ctxContent) {
|
|
338
403
|
dynamicParts.push(`## Project context\n${ctxContent}`);
|
|
339
404
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* 顶部加 `# N entries · table-encoded` 计数头。
|
|
7
7
|
*
|
|
8
8
|
* 不变量(离线脚本断言):id/name/summary/type 全保留;仅 `active` 状态被省略(默认值,无信息损失)。
|
|
9
|
-
* 正则锚定整行 + 要求 `id: ... — ... (type, active)` 形,不匹配的行原样返回(防误伤
|
|
9
|
+
* 正则锚定整行 + 要求 `id: ... — ... (type, active)` 形,不匹配的行原样返回(防误伤 AGENTS.md 等正文)。
|
|
10
10
|
* group1 贪婪捕获到 `(\w+` 为止,故 name/summary 内含 ` — ` 或 ` (` 也不影响(仅丢尾部 `, active)`)。
|
|
11
11
|
*/
|
|
12
12
|
const ACTIVE_LINE_RE = /^(- [^:]+: .+ — .+ \(\w+), active\)$/;
|
package/dist/i18n/index.js
CHANGED
|
@@ -18,7 +18,7 @@ const zhCN = {
|
|
|
18
18
|
'commands.memoryOff': '关闭记忆子系统',
|
|
19
19
|
'commands.memoryStatus': '查看当前开关与原理',
|
|
20
20
|
'commands.memoryReflect': '手动触发后台记忆反思',
|
|
21
|
-
'commands.memoryInit': '扫描项目生成
|
|
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': '生成
|
|
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
|
|
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
|
|
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
|
@@ -180,7 +180,6 @@ export function refreshChatTools() {
|
|
|
180
180
|
// MCP 协议没有可靠的副作用注解;plan 模式绝不暴露外部工具,保留只读探查保证。
|
|
181
181
|
planChatTools.splice(0, planChatTools.length, ...next.filter((t) => !t.function.name.startsWith('mcp__') && !getPlanDisabledTools().has(t.function.name)));
|
|
182
182
|
}
|
|
183
|
-
refreshChatTools();
|
|
184
183
|
/**
|
|
185
184
|
* 多 provider 兼容的 cache / reasoning 字段提取。
|
|
186
185
|
* 不同后端报 cached 字段名差异巨大,这里按"最常见的几种"顺序 probe,
|
package/dist/memory/discover.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
// memory 发现子系统:加载项目记忆
|
|
1
|
+
// memory 发现子系统:加载项目记忆 AGENTS.md(对标 skills/discover.ts 的叶子模式)。
|
|
2
2
|
// 仅依赖 node 标准库,是叶子模块:不依赖 config/agent/llm/tools/skills,避免环。
|
|
3
3
|
//
|
|
4
|
-
// 约定:纯
|
|
5
|
-
// 项目级从 cwd 向上逐级找,全局 ~/.mocode/
|
|
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
|
-
* 返回要查找的
|
|
11
|
-
* 全局 ~/.mocode/
|
|
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', '
|
|
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, '
|
|
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
|
-
* 读取所有存在的
|
|
29
|
+
* 读取所有存在的 AGENTS.md,返回 { path, content }(content 已 trim)。
|
|
30
30
|
* 全程静默容错(不存在 / 读失败 → 跳过,不抛),风格对齐 skills/discover.ts 与 session/persist.ts。
|
|
31
31
|
*/
|
|
32
32
|
export function loadMemoryFiles() {
|