diff --git a/MEMORY.md b/MEMORY.md index 54d3a51..0282628 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -43,11 +43,9 @@ - Required sequence before any gateway intervention: (1) run `openclaw gateway status`; (2) report findings to Tao; (3) prefer non-disruptive diagnosis first; (4) only touch gateway lifecycle if Tao explicitly approves the exact command. ## 7) Model policy (current preference) -- Historical temporary preference once was: all subagents **primary** = `openai-codex/gpt-5.2`, **fallback** = `github-copilot/claude-opus-4.6`. -- Current important exception / newer rule: **execution agents** (esp. `codex_worker`, and by default other workers unless Tao says otherwise) must target **primary** = `openai-codex/gpt-5.4`. - - **No silent fallback:** if the system falls back to any other model (or if `openai-codex/gpt-5.4` becomes unknown/unavailable), main must **immediately notify Tao**. - - **No auto-fallback for execution agents:** if 5.4 is unavailable, workers should not continue on another model; main should report and wait for Tao’s model decision. - - If an execution agent is not responding, main must consider **model unavailability/fallback** as a first-class suspected cause and **tell Tao**. +- Current config (2026-03-21): all agents **primary** = `claude-local/claude-sonnet-4-6`, **fallback** = `openai-codex/gpt-5.4` (only one fallback; Minimax removed). +- **铁律:Fallback 发生时必须通知 Tao。** 适用于**所有 agent**(main、tech_geek、travel_assistant、finance_assistant、所有 execution agents)。无论哪条链路触发了 fallback,都必须立即通知 Tao,说明:哪个 agent、从什么 model fallback 到了什么 model、原因(timeout / error / unavailable)。不允许静默 fallback。 + - If an agent is not responding, main must consider **model unavailability/fallback** as a first-class suspected cause and **tell Tao**. - Additional long-lived execution agents initialized locally for repeat use: **docs_worker**, **qa_worker**, **ops_worker**. - Execution-agent system needs a standardized dispatch/handoff layer: task input template, result format, blocker/escalation rules, and a **main-to-Tao forwarding rule**. - **Hard reporting/forwarding protocol (must-follow):** @@ -72,3 +70,9 @@ - Local memory dir: `/Users/taodeng/.openclaw/workspace/main/memory/`. - Added `memory/INDEX.md` to classify daily notes vs topic notes vs automation state JSON (do not rename/move state JSON without updating jobs). - Plan: create a separate GitHub KB repo (option A) for shareable/curated knowledge; keep private/sensitive items local and only publish redacted content. + +## 11) Oracle Cloud +- See [memory/oracle_cloud.md](memory/oracle_cloud.md) for SSH connection details and deployment notes. + +## 12) 开发原则 (Dev Principles) +- See [memory/dev_principles.md](memory/dev_principles.md) for Tao's principles on new project development (when to reuse vs build, code quality, testing, versioning, etc.). Read before starting any new project. diff --git a/memory/CURRENT_STATE.md b/memory/CURRENT_STATE.md index 00aa6b5..17f4c47 100644 --- a/memory/CURRENT_STATE.md +++ b/memory/CURRENT_STATE.md @@ -1,33 +1,31 @@ # Current State -> Last updated: 2026-03-13T07:02:00+10:00 +> Last updated: 2026-03-21T02:00:58.027Z ## Objective -Validate the memory-continuity lifecycle plugin path safely after upgrade. +但是文件数目不可以超过500条 ## Current Step -Startup-injection testing is confirmed on multiple resident subagents after the upgrade; Discord main/channel/thread paths have now been tested and are not currently supported for reliable continuity recovery; compaction-path testing is now being prepared on `travel_assistant` as the disposable session. +Conversation ended after 181 messages ## Key Decisions -- Keep `SKILL.md + standard lifecycle plugin` as the primary architecture. -- Treat ContextEngine as a future option, not the v1 default. -- Do not force compaction on the main live session just to test Experiment C. -- Use disposable resident subagent sessions for future compaction-path testing. -- `travel_assistant` is now a valid sample again after removing the old wrong workspace and fixing its `AGENTS.md` startup rule to read `memory/CURRENT_STATE.md`. -- Alpha support boundary is now explicit: resident subagent startup continuity is supported; Discord main/channel/thread continuity is not yet supported in `v0.3.0-probe`. +- Auto-extracted from conversation ## Next Action -Two-track next step: -1. If continuing active testing, use `travel_assistant` as the sacrificial session, reload the updated probe logging, then force/accelerate a real compaction event and compare hook behavior against the failure chain seen on main. -2. If pausing for upstream uncertainty, keep the fallback-continuity GitHub issue open and wait for OpenClaw feedback before spending more effort on the main-session fallback path. +Continue from where we left off ## Blockers -- Experiment C still lacks a real compaction event, so compaction-hook behavior remains unproven. -- New upstream-suspected blocker: fallback continuity failure may be caused by OpenClaw providing incomplete plugin runtime (`workspaceDir=`) on fallback/new-session recovery path. +None ## Unsurfaced Results -- Experiment A is now confirmed on multiple resident subagents. -- `tech_geek` recovered plugin-injected startup continuity state without using `read`. -- `travel_assistant` also recovered the correct upgraded smoke-test state after fixing its old workspace pollution and startup rule. -- Fresh Discord main/channel/thread tests failed to preserve short facts or concrete working-state recovery across new sessions; treat Discord main continuity as unsupported for this alpha. -- New execution-model finding: main can successfully dispatch and consume results from temporary subagents (single, follow-up, and parallel two-worker tests all succeeded). -- New ACP finding: Codex ACP worker is validated as a usable execution worker for main; Claude ACP today remains an invalid sample because Tao confirmed Claude was already in timeout/overuse state, so do not classify that failure as product/runtime evidence. +🦞 大内总管: 已加硬上限。 + +**改动:** +- `MAX_MEMORY_FILES = 500` +- 每次 archive 后检查 `memory/` 总文件数 +- 超过 500 → 自动删最旧的可删文件,降到 450(留 50 个余量) +- **不会删的**:`CURRENT_STATE.md`、`MEMORY.md`、`INDEX.md`、主题文件、JSON 状态文件 + +**三层防护:** +1. 单类 archive 上限 20 个 +2. 总文件数硬上限 500 +3. 清理时按修改时间排序,从最旧的开始删 diff --git a/skills/memory-continuity/SKILL.md b/skills/memory-continuity/SKILL.md index 57d0825..c12f273 100644 --- a/skills/memory-continuity/SKILL.md +++ b/skills/memory-continuity/SKILL.md @@ -130,6 +130,22 @@ Update the file by **overwriting** it, not appending, at these moments: | Before handoff / subagent exit | Preserves outputs and unsurfaced results | | After a substantive state change | Keeps checkpoint aligned with actual work | +### Override rule + +**CURRENT_STATE.md must always be overwritten when:** +- A new task or objective starts — regardless of what is currently in the file +- The previous objective is complete or abandoned +- The user gives a new task that supersedes the previous one + +Having content in CURRENT_STATE.md does NOT mean it should be preserved. +Content only matters if Objective is still active and work is genuinely in progress. + +Checking before overwrite: +- Read the file +- If Objective matches the current task → update in place (overwrite) +- If Objective does NOT match → overwrite the entire file with the new state +- Never append. Never skip the update because "there's already something there". + ### 4. Keep the checkpoint small `CURRENT_STATE.md` should usually stay under about 40 lines and be readable in diff --git a/skills/memory-continuity/plugin/lifecycle-prototype.ts b/skills/memory-continuity/plugin/lifecycle-prototype.ts index c4a3796..7f909ac 100644 --- a/skills/memory-continuity/plugin/lifecycle-prototype.ts +++ b/skills/memory-continuity/plugin/lifecycle-prototype.ts @@ -1,6 +1,18 @@ import fs from "node:fs"; import path from "node:path"; +const MAX_ARCHIVE_COUNT = 20; +const MAX_MEMORY_FILES = 500; + +// Protected files that should never be auto-deleted +const PROTECTED_FILES = new Set([ + "CURRENT_STATE.md", + "MEMORY.md", + "INDEX.md", + "dev_principles.md", + "oracle_cloud.md", +]); + const PLACEHOLDER_VALUES = new Set([ "", "none", @@ -82,11 +94,70 @@ function formatArchiveStamp(date = new Date()) { ].join("-") + `_${pad(date.getHours())}-${pad(date.getMinutes())}`; } +function pruneArchives(dir: string, pattern: RegExp, max: number) { + try { + const files = fs.readdirSync(dir) + .filter((f: string) => pattern.test(f)) + .sort(); + const excess = files.length - max; + if (excess > 0) { + for (let i = 0; i < excess; i++) { + fs.unlinkSync(path.join(dir, files[i])); + } + console.log(`[memory-continuity] pruned ${excess} old archive(s) in ${dir}`); + } + } catch {} +} + +function enforceFileLimit(memoryDir: string) { + try { + const allFiles = fs.readdirSync(memoryDir, { recursive: true }) as string[]; + // Only count files, not directories + const fileList = allFiles + .map((f: string) => ({ name: String(f), full: path.join(memoryDir, String(f)) })) + .filter((f) => { + try { return fs.statSync(f.full).isFile(); } catch { return false; } + }); + + if (fileList.length <= MAX_MEMORY_FILES) return; + + // Sort deletable files by mtime (oldest first), skip protected + const deletable = fileList + .filter((f) => { + const base = path.basename(f.name); + if (PROTECTED_FILES.has(base)) return false; + if (base.endsWith(".json")) return false; // automation state + return true; + }) + .map((f) => ({ ...f, mtime: fs.statSync(f.full).mtimeMs })) + .sort((a, b) => a.mtime - b.mtime); + + const toDelete = fileList.length - 450; // bring down to 450 for headroom + const deleted = Math.min(toDelete, deletable.length); + for (let i = 0; i < deleted; i++) { + fs.unlinkSync(deletable[i].full); + } + if (deleted > 0) { + console.log(`[memory-continuity] enforced file limit: deleted ${deleted} oldest files (${fileList.length} → ${fileList.length - deleted})`); + } + } catch {} +} + function appendArchive(workspaceDir: string, markdown: string) { const stamp = formatArchiveStamp(); const archiveDir = path.join(workspaceDir, "memory", "session_archive"); fs.mkdirSync(archiveDir, { recursive: true }); fs.writeFileSync(path.join(archiveDir, `${stamp}.md`), markdown, "utf8"); + + // Prune session_archive/ beyond MAX_ARCHIVE_COUNT + pruneArchives(archiveDir, /\.md$/, MAX_ARCHIVE_COUNT); + + // Also prune STATE_ARCHIVE_*.md in memory/ root (generated by OpenClaw native) + const memoryDir = path.join(workspaceDir, "memory"); + pruneArchives(memoryDir, /^STATE_ARCHIVE_.*\.md$/, MAX_ARCHIVE_COUNT); + + // Hard cap: total files in memory/ must not exceed MAX_MEMORY_FILES + enforceFileLimit(memoryDir); } export default function register(api: any) {