mocode-ai 1.7.1 → 1.7.2
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/session/notes.js +58 -8
- package/package.json +1 -1
package/dist/session/notes.js
CHANGED
|
@@ -307,6 +307,16 @@ function trimSectionToBudget(body, budgetTokens) {
|
|
|
307
307
|
return null;
|
|
308
308
|
return `${header}\n${kept.join('\n')}`;
|
|
309
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* 快照段正文的已知子标题(extractProgressSnapshot 从摘要原样透传 `## Objective` /
|
|
312
|
+
* `## In Progress` / `## Next Steps` 级标题)。段扫描器把任何 `##` 行当段边界,
|
|
313
|
+
* 若不豁免,快照正文会被切碎成游离段(优先级 0)漏进注入、绕过快照的过滤逻辑。
|
|
314
|
+
*/
|
|
315
|
+
const SNAPSHOT_SUBHEADERS = /^##\s+(Objective|In Progress|Next Steps)\s*(?:[—–-].*)?$/;
|
|
316
|
+
/** 快照段的边界:下一个非快照子标题的 `##` 行。 */
|
|
317
|
+
function isSnapshotBoundary(line) {
|
|
318
|
+
return /^##\s/.test(line) && !SNAPSHOT_SUBHEADERS.test(line);
|
|
319
|
+
}
|
|
310
320
|
/**
|
|
311
321
|
* 读 notes.md,提取所有活跃笔记段正文(排除 `## Plan:` 与 `## Done:`),
|
|
312
322
|
* 按 NOTES_INJECT_BUDGET_TOKENS 裁剪后返回——供 reinject 注入 system prompt。
|
|
@@ -327,6 +337,9 @@ export function extractActiveNotesSections(budget = NOTES_INJECT_BUDGET_TOKENS,
|
|
|
327
337
|
return '';
|
|
328
338
|
}
|
|
329
339
|
const lines = content.split('\n');
|
|
340
|
+
// 活跃 plan 存在时快照即过期(writeCompactionSnapshot 在 plan 在场时不写,
|
|
341
|
+
// 但快照先于 plan 写入的时序残留仍会被本函数读到):跳过注入,与写入侧对称。
|
|
342
|
+
const hasActivePlan = lines.some((l) => /^## Plan:\s*.+$/.test(l));
|
|
330
343
|
const sections = [];
|
|
331
344
|
let i = 0;
|
|
332
345
|
while (i < lines.length) {
|
|
@@ -336,16 +349,23 @@ export function extractActiveNotesSections(budget = NOTES_INJECT_BUDGET_TOKENS,
|
|
|
336
349
|
continue;
|
|
337
350
|
}
|
|
338
351
|
const title = m[1];
|
|
339
|
-
|
|
340
|
-
|
|
352
|
+
const isSnapshot = matchSectionKey(title) === 'compaction_snapshot';
|
|
353
|
+
const skip =
|
|
354
|
+
// Plan/Done 段(Plan 有专属 ACTIVE_PLAN_MARKER 重注入;Done 是归档不常驻)
|
|
355
|
+
/^Plan:/.test(title) ||
|
|
356
|
+
/^Done:/.test(title) ||
|
|
357
|
+
// 活跃 plan 在场时快照过期:不注入(authoritative 标签 + 过期数据 = 模型反复自证忽略)
|
|
358
|
+
(hasActivePlan && isSnapshot);
|
|
359
|
+
const nextSection = (line) => (isSnapshot ? isSnapshotBoundary(line) : /^##\s/.test(line));
|
|
360
|
+
if (skip) {
|
|
341
361
|
i++;
|
|
342
|
-
while (i < lines.length &&
|
|
362
|
+
while (i < lines.length && !nextSection(lines[i]))
|
|
343
363
|
i++;
|
|
344
364
|
continue;
|
|
345
365
|
}
|
|
346
366
|
const start = i;
|
|
347
367
|
i++;
|
|
348
|
-
while (i < lines.length &&
|
|
368
|
+
while (i < lines.length && !nextSection(lines[i]))
|
|
349
369
|
i++;
|
|
350
370
|
const body = lines.slice(start, i).join('\n').trim();
|
|
351
371
|
if (body)
|
|
@@ -384,18 +404,48 @@ export function extractActiveNotesSections(budget = NOTES_INJECT_BUDGET_TOKENS,
|
|
|
384
404
|
// 与 plan/notes 不同:这是机器产出、整段替换(不逐条累积),因此不会跨压缩膨胀。
|
|
385
405
|
/** 快照段标题。extractActiveNotesSections 经 matchSectionKey 识别并注入。 */
|
|
386
406
|
export const COMPACTION_SNAPSHOT_TITLE = NOTE_SECTION_TITLES.compaction_snapshot;
|
|
407
|
+
/** 从 notes.md 正文移除整个 `## Compaction Snapshot` 段(含其 `## Objective` 等子标题);无该段返原文。 */
|
|
408
|
+
function stripSnapshotSection(existing) {
|
|
409
|
+
const header = `## ${COMPACTION_SNAPSHOT_TITLE}`;
|
|
410
|
+
const lines = existing.split('\n');
|
|
411
|
+
const start = lines.findIndex((l) => l.trim() === header);
|
|
412
|
+
if (start < 0)
|
|
413
|
+
return existing;
|
|
414
|
+
let end = lines.length;
|
|
415
|
+
for (let k = start + 1; k < lines.length; k++) {
|
|
416
|
+
if (isSnapshotBoundary(lines[k])) {
|
|
417
|
+
end = k;
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const before = lines.slice(0, start).join('\n').replace(/\s+$/, '');
|
|
422
|
+
const after = lines.slice(end).join('\n').replace(/^\s+/, '');
|
|
423
|
+
return [before, after].filter((s) => s.length > 0).join('\n\n') + '\n';
|
|
424
|
+
}
|
|
387
425
|
/**
|
|
388
426
|
* 把压缩摘要的关键段固结到 notes.md 的 `## Compaction Snapshot` 段。
|
|
389
|
-
* 整段替换旧快照(不累积);仅在当前无活跃 plan
|
|
390
|
-
*
|
|
427
|
+
* 整段替换旧快照(不累积);仅在当前无活跃 plan 时写入。body 为空时不动。
|
|
428
|
+
* 有活跃 plan 时移除残留的旧快照段:写入侧「不写」若配上注入侧「照旧注入」,
|
|
429
|
+
* 模型每步都会看到一份 authoritative 标签的过期数据,被迫反复自证「快照是旧的,
|
|
430
|
+
* 我忽略它」——与 {@link extractActiveNotesSections} 的对称过滤一起根治。
|
|
431
|
+
* 永不抛错:压缩主流程不能因快照失败而失败。
|
|
391
432
|
*/
|
|
392
433
|
export function writeCompactionSnapshot(body, sessionId = getCurrentSessionId()) {
|
|
393
434
|
try {
|
|
394
435
|
const trimmed = (body ?? '').trim();
|
|
395
436
|
if (!trimmed)
|
|
396
437
|
return;
|
|
397
|
-
if (readActivePlanTitle(sessionId))
|
|
398
|
-
|
|
438
|
+
if (readActivePlanTitle(sessionId)) {
|
|
439
|
+
// 已有权威计划,快照是冗余:连同旧快照段一并移除(注入侧不再有过期数据可读)
|
|
440
|
+
const p = getNotesFilePath(sessionId);
|
|
441
|
+
if (!p)
|
|
442
|
+
return;
|
|
443
|
+
const existing = fs.readFileSync(p, 'utf8').replace(/\r\n?/g, '\n');
|
|
444
|
+
const next = stripSnapshotSection(existing);
|
|
445
|
+
if (next !== existing)
|
|
446
|
+
fs.writeFileSync(p, next, 'utf8');
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
399
449
|
const p = getNotesFilePath(sessionId);
|
|
400
450
|
if (!p)
|
|
401
451
|
return;
|