Qwen Code Daemon 无会话工作区记忆 API:Sessionless Managed Memory 任务队列的设计与实现
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
导读
在 Qwen Code(qwen-code)的 daemon 架构中,托管记忆(managed memory)的自动抽取、dream 压缩等能力此前必须依托一个活跃的聊天会话才能写入。本文基于 daemon-workspace-remember 设计文档,结合仓库中的 workspace-remember.ts、remember.ts 等源码实现,完整讲解这套无会话(sessionless)工作区记忆任务 API:它如何在「不创建、不污染任何可见会话」的前提下完成 remember / forget / dream 三类记忆操作,如何通过串行任务车道(task lane)保证并发安全,以及如何通过 202 + 轮询的异步契约让设置面板、SDK 调用方获得可靠的状态反馈。读完本文,你将掌握这套 API 的完整端点契约、任务生命周期、容量与超时策略、错误码体系,以及从 HTTP 路由到 ACP 子进程、再到核心记忆写入逻辑的完整调用链。
1. 问题背景:为什么记忆写入必须摆脱会话依赖
在引入本设计之前,daemon 的托管记忆系统(自动抽取、dream agent)要求必须存在一个活跃的聊天会话才能写入记忆,这带来了两个现实问题:
- 设置面板无法写入记忆——web-shell 的设置界面需要保存用户提供的事实(例如「所有编辑器一律使用 TypeScript strict mode」),但为此创建或污染一个可见的聊天会话显然不合理。
- 会话列表被污染——为了执行一次
/remember命令而临时创建一个「用完即弃」的会话,会在会话列表里留下大量用户从未主动打开过的「幽灵会话」,造成困惑。
解决思路是一个无会话的工作区级记忆任务 API:它负责排队 remember、forget、dream 三类任务,在不创建可见会话的前提下执行它们,并通过轮询暴露执行状态。
需要说明的是:本文描述的设计方案在仓库中标记为Proposed(PR #5884,分支
codex/sessionless-daemon-remember),但对应实现文件已经存在于当前仓库源码中(见下文各小节引用的具体路径),本文以源码现状为准展开讲解。
2. 设计概览:一条从客户端到 ACP 子进程的完整链路
整体架构如下(摘自设计文档):
┌──────────────┐ POST /workspace/memory/{task} ┌─────────────────────────┐ │ SDK / UI │ ─────────────────────────────────► │ workspace-remember.ts │ │ client │ │ (WorkspaceRemember- │ │ │ GET /workspace/memory/{task}/:id │ TaskLane) │ │ │ ─────────────────────────────────► │ │ └──────────────┘ └────────────┬────────────┘ │ bridge.runWorkspaceMemory* ┌────────────▼────────────┐ │ HttpAcpBridge │ │ extMethod( │ │ 'qwen/control/ │ │ workspace/memory/ │ │ {task}') │ └────────────┬────────────┘ │ ACP stdio (JSON-RPC) ┌────────────▼────────────┐ │ qwen --acp child │ │ (QwenAgent.extMethod) │ │ → remember / forget / │ │ dream core logic │ └─────────────────────────┘这套设计具备四个关键性质:
- 无需会话(No session required)——bridge 只确保 ACP 子进程被拉起,但绝不创建 / 加载 / 恢复任何 ACP 会话。这一点在 bridgeTypes.ts 的接口注释中明确声明:「必须确保 ACP child 存在,但不得创建/加载/恢复 ACP 会话,也不得触碰按会话的 prompt 队列」。
- 串行执行(Serial execution)——任务通过一条 promise-chain 车道逐条执行,防止对托管记忆文件系统产生并发写入。
- 隐藏(Hidden)——remember/dream 通过隐藏 agent 运行,forget 使用隐藏记忆配置,所有操作都不会产生可见会话。
- 能力广播(Capability-advertised)——daemon 的
/capabilities响应中会声明workspace_memory_remember、workspace_memory_forget、workspace_memory_dream三项能力;remember 还会附带modes: ['workspace', 'clean']。从 SDK 源码看,还衍生出workspace_memory_remember_project_scope、workspace_memory_remember_user_scope、workspace_memory_forget_scope等更细粒度的能力标签,用于旧 daemon 兼容性预检(见 DaemonClient.ts)。
3. HTTP API 端点详解
所有端点都挂在/workspace/memory/{task}路径下,采用「POST 入队 + GET 轮询」的异步任务模式。POST 统一返回202 Accepted与任务快照,客户端随后轮询GET /workspace/memory/{task}/:taskId直到任务进入终态。
3.1POST /workspace/memory/remember—— 入队一个 remember 任务
请求体:
{ "content": "The user prefers dark mode in all editors", "contextMode": "workspace" }| Field | Type | Required | Description |
|---|---|---|---|
content | string | yes | 要记住的事实。最大64 KiB(UTF-8 字节长度)。 |
contextMode | string | no | "workspace"(默认)——agent 可看到工作区记忆上下文;"clean"——agent 看不到任何先前的用户记忆。 |
scope | string | no | 目标范围:"project"或"user",省略时由 agent 自动选择(源码中新增的可选字段,见 workspace-remember.ts)。 |
Headers:
Authorization: Bearer <token>(必填)X-Qwen-Client-Id: <clientId>(可选——用于限定任务的可见范围)
响应202 Accepted:
{ "taskId": "remember-a1b2c3d4-...", "status": "queued", "contextMode": "workspace", "createdAt": "2026-06-01T12:00:00.000Z", "updatedAt": "2026-06-01T12:00:00.000Z" }错误响应:
| Status | Code | Condition |
|---|---|---|
| 400 | invalid_content | content 缺失、为空或超出 64 KiB |
| 400 | invalid_context_mode | contextMode 不是"workspace"或"clean" |
| 400 | invalid_scope | scope 不是"project"或"user"(源码新增) |
| 400 | invalid_client_id | X-Qwen-Client-Id 未在 bridge 注册 |
| 403 | untrusted_workspace | 工作区未被信任(源码新增防护) |
| 409 | managed_memory_unavailable | 该工作区未配置托管记忆 |
| 429 | remember_queue_full | 已有 16 个待处理任务排队 |
| 500 | remember_failed | 可用性检查意外抛错 |
实现细节:路由层在入队前会做多道校验——
content经trim()后必须非空,且Buffer.byteLength(trimmedContent, 'utf8')不得超过常量MAX_REMEMBER_CONTENT_BYTES = 64 * 1024(定义于 workspace-memory-remember-constants.ts);X-Qwen-Client-Id必须存在于bridge.knownClientIds()集合中(workspace-remember.ts);随后调用isWorkspaceMemoryRememberAvailable()做可用性预检,以便在入队前同步返回409(workspace-remember.ts)。
3.2GET /workspace/memory/remember/:taskId—— 轮询 remember 任务
Headers:与 POST 相同;X-Qwen-Client-Id必须与发起方一致才能看到任务。
响应200 OK(queued / running):
{ "taskId": "remember-a1b2c3d4-...", "status": "queued", "contextMode": "workspace", "createdAt": "2026-06-01T12:00:00.000Z", "updatedAt": "2026-06-01T12:00:00.000Z", "result": null, "error": null }status为"queued"或"running",取决于任务是否已开始执行;result仅在status === "completed"时非空;error仅在status === "failed"时非空。
响应200 OK(completed):
{ "taskId": "remember-a1b2c3d4-...", "status": "completed", "contextMode": "workspace", "createdAt": "2026-06-01T12:00:00.000Z", "updatedAt": "2026-06-01T12:00:05.000Z", "result": { "summary": "Saved dark-mode preference to user memory.", "filesTouched": ["~/.qwen/memories/user/user.md"], "touchedScopes": ["user"] } }响应200 OK(failed):
{ "taskId": "remember-a1b2c3d4-...", "status": "failed", "contextMode": "workspace", "createdAt": "2026-06-01T12:00:00.000Z", "updatedAt": "2026-06-01T12:00:03.000Z", "error": { "code": "remember_path_escape", "message": "Remember agent touched a path outside managed memory." } }错误响应:
| Status | Code | Condition |
|---|---|---|
| 400 | invalid_client_id | X-Qwen-Client-Id 未注册 |
| 404 | remember_task_not_found | 任务不存在或属于其他 client |
可见性语义(源码印证):任务快照通过
lane.get(taskId, requesterClientId, kind)获取(workspace-remember.ts)——若任务携带originatorClientId,则请求方必须与之完全一致;若任务未携带而请求方带了 clientId,同样返回不可见。这保证了任务的多客户端隔离。
3.3POST /workspace/memory/forget—— 入队一个 forget 任务
forget 任务由 daemon 选出匹配的托管自动记忆条目并删除,全程不创建会话。
请求体:
{ "query": "old preference" }| Field | Type | Required | Description |
|---|---|---|---|
query | string | yes | 用自然语言描述要遗忘的内容。最大64 KiB(UTF-8 字节长度)。 |
scope | string | no | 限定遗忘范围"project"/"user",省略则两边都查(源码新增)。 |
初始响应为202 Accepted,返回forget-...前缀的 taskId,随后轮询GET /workspace/memory/forget/:taskId直到终态。
completed 结果:
{ "summary": "Forgot 1 memory entry.", "removedEntries": [ { "topic": "project", "summary": "old preference", "filePath": "/path/to/memory.md" } ], "touchedTopics": ["project"], "touchedScopes": ["project"] }3.4GET /workspace/memory/forget/:taskId
轮询 forget 任务。结构上与 remember 轮询一致,差异在于:没有contextMode字段;未知或未授权的 taskId 返回forget_task_not_found。
3.5POST /workspace/memory/dream—— 入队一个 dream 任务
dream 任务执行托管自动记忆的 dream 压缩流程(跨条目去重、汇总),同样不创建会话。
请求体:空 JSON 对象或省略 body。
初始响应为202 Accepted,返回dream-...前缀的 taskId,轮询GET /workspace/memory/dream/:taskId直到终态。
completed 结果:
{ "summary": "Managed auto-memory dream completed.", "touchedTopics": ["project"], "dedupedEntries": 1 }3.6GET /workspace/memory/dream/:taskId
轮询 dream 任务,结构与 remember 轮询一致,无contextMode字段,未知或未授权的 taskId 返回dream_task_not_found。
4. 任务生命周期与容量控制
4.1 状态机
enqueue() │ ▼ ┌─────────────────────┐ │ queued │ (awaiting serial lane slot) └──────────┬──────────┘ │ lane picks up ▼ ┌─────────────────────┐ │ running │ (bridge.runWorkspaceMemoryRemember in progress) └──────────┬──────────┘ │ ┌───────┴────────┐ ▼ ▼ ┌──────────┐ ┌──────────┐ │ completed│ │ failed │ └──────────┘ └──────────┘- queued——任务已创建,正在串行车道中等待。
- running——bridge 调用在途,forked agent 正在执行。
- completed——agent 成功结束,
result已填充。 - failed——agent 抛错或超时,
error已填充。
4.2 容量与保留策略
车道级常量定义在 workspace-remember.ts 的WorkspaceRememberTaskLane中:
| 常量 | 值 | 含义 |
|---|---|---|
MAX_TASKS | 1000 | 车道最多保存1000 条任务记录,达到上限后按 FIFO 逐出终态任务(evictTerminalTasks) |
TERMINAL_TASK_TTL_MS | 5 分钟 | 终态任务(completed/failed)超过该 TTL 后也会被清理 |
MAX_PENDING | 16 | 同一时刻最多16 条待处理任务(queued + running) |
MAX_NON_REMEMBER_PENDING | 8 | forget/dream 两类任务合计最多8 条待处理 |
两级容量设计(assertCapacity,workspace-remember.ts)的意图很明确:forget 和 dream 这类「突发性手工维护」共享较小的子配额,避免突发维护耗尽 remember 自动写入所需的全部车道槽位——例如自动记忆抽取是常态工作负载,不能被一次批量 dream 挤掉。
另外,beginDrain()/dispose()提供了工作区下线语义:dispose()会把所有 queued 任务标记为failed(workspace_removed错误),正在 running 的任务在结束后也会被failRunningTaskAfterRemoval修正为失败(workspace-remember.ts)。
5. 源码级实现剖析
5.1 串行任务车道(WorkspaceRememberTaskLane)
车道位于 workspace-remember.ts,核心是一个Map<taskId, TaskRecord>加一条单一 promise 链(this.tail)。每次enqueue()/enqueueForget()/enqueueDream()都会向链尾追加一个run函数,其执行流程为:
- 将任务状态置为
running; - 调用对应的 bridge 方法:
runWorkspaceMemoryRemember/runWorkspaceMemoryForget/runWorkspaceMemoryDream; - 成功:置为
completed,填充result;若任务确实改写了托管记忆,则发布memory_changed事件; - 失败:置为
failed,用稳定的公开错误码填充error。
三条enqueue*路径还各有额外校验:
- remember 在
result.filesTouched.length === 0时抛remember_no_update(「agent 没有更新任何记忆」); - 发布事件前会通过
publishManagedMemoryChanged检查touchedScopes非空(workspace-remember.ts)。
车道的本质是严格串行化——任意时刻只有一个工作区记忆任务在执行,从根本上杜绝了对托管记忆文件系统的并发写入(详见第 8 节「为什么串行」)。
5.2 Bridge 层(HttpAcpBridge)
工作区记忆方法在BridgeInterface(bridgeTypes.ts)中新增了四个方法:
isWorkspaceMemoryRememberAvailable()——调用子进程的qwen/control/workspace/memory/remember/availabilityext-method,返回boolean。用于在入队前快速失败(409)。runWorkspaceMemoryRemember(request)——调用qwen/control/workspace/memory/rememberext-method,超时300 s(WORKSPACE_MEMORY_REMEMBER_TIMEOUT_MS)。不创建也不加载会话。runWorkspaceMemoryForget(request)——调用qwen/control/workspace/memory/forgetext-method,使用同一 bridge 超时。不创建也不加载会话。runWorkspaceMemoryDream()——调用qwen/control/workspace/memory/dreamext-method,使用同一 bridge 超时。不创建也不加载会话。
这些方法都会调用ensureChannel()(必要时拉起 ACP 子进程),并在没有活跃会话时重启空闲计时器。
5.3 ACP 子进程执行(QwenAgent.extMethod)
在 acpAgent.ts 中,workspaceMemoryRemember/workspaceMemoryForget/workspaceMemoryDream的 handler 执行以下步骤:
- 校验任务级入参(remember 的
content/contextMode/scope,forget 的query); - 检查
config.isManagedMemoryAvailable(); - 调用对应的核心操作,并携带一个295 s的 abort 信号(
WORKSPACE_MEMORY_REMEMBER_CHILD_TIMEOUT_MS = 295_000,见 acpAgent.ts)。forget 场景下该信号会贯穿MemoryManager.forget的条目选择、模型侧查询和落盘文件系统变更全过程。
值得一提的细节:子进程侧使用AbortSignal.timeout()构造超时,超时后错误被映射为remember_timeout等带稳定 code 的错误(acpAgent.ts),而不是传输层超时——这正是第 6 节超时链设计的落地。
5.4 核心 remember 逻辑(packages/core/src/memory/remember.ts)
runManagedRememberByAgent()(remember.ts)是 remember 的最终执行者,流程如下:
- 通过
buildCleanMemorySystemPrompt()从项目的托管记忆索引构建一份干净的记忆系统提示词; - 若
contextMode === 'clean',通过getUserMemory: () => ''剥离先前的用户记忆,实现「白板」写入;同时createHiddenRememberConfig()会禁用聊天记录、转录路径,clean 模式下还会禁用全部 hooks(remember.ts); - 创建
memoryScopedAgentConfig(见 5.5),把文件 I/O 严格限制在记忆目录内; - 用
runForkedAgent运行一个forked 无头 agent:- 名称:
managed-auto-memory-remember - 工具:
read_file、grep、ls、write_file、edit - 最大轮数:6(
getMemoryAgentMaxTurns() ?? 6) - 最大时长:5 分钟(
getMemoryAgentTimeoutMinutes() ?? 5)
- 名称:
- 用
classifyTouchedScopes()校验所有被写入的文件都位于允许的记忆路径内;若 agent 写到了记忆目录之外,抛出remember_path_escape(remember.ts); - 对任何被触碰的 scope 重建记忆索引(
rebuildManagedAutoMemoryIndex/rebuildUserAutoMemoryIndex)——这是关键一致性保障:MEMORY.md会逐字加载进每个未来的会话,因此失败、取消的 run 也必须在报错前重建索引,绝不允许把手工写入的索引文件留在磁盘上(见 remember.ts 中覆盖 failed/cancelled/escape/no-update 各路径的重建逻辑); - 返回
{ summary, filesTouched, touchedScopes }。
此外还有两道额外防线:remember_no_update(agent 完成但未写入任何条目)和remember_scope_mismatch(指定了scope但 agent 写到了范围之外)。
5.5 记忆作用域 agent 配置(memory-scoped-agent-config.ts)
createMemoryScopedAgentConfig()(memory-scoped-agent-config.ts)创建一份权限受限的Config包装器,把原PermissionManager替换为MemoryScopedPermissionManager:
- 写工具(
write_file、edit):仅允许写入项目自动记忆根目录或用户记忆根目录(~/.qwen/memories); - 读工具(
read_file、grep、ls):当restrictReadsToMemoryPaths为 true 时,仅允许读取记忆目录内; - Shell:默认禁用;若
allowShell开启,仅允许只读命令(通过isShellCommandReadOnlyASTInDirectory做 AST 级只读判定,memory-scoped-agent-config.ts); - Symlink 解析:所有路径判定前先
realpath解析,防止通过符号链接逃逸出记忆目录(realpathExistingOrNew/realpathNewPath,memory-scoped-agent-config.ts); - Pinned 记忆保护:
protectPinnedMemory开启时,pinned/目录被声明为只读,任何对其的写入直接deny(memory-scoped-agent-config.ts); - 决策合并:作用域决策与基础决策按优先级
deny > ask > allow > default合并,bypassBaseAskForScopedPaths开启时允许用allow覆盖基础配置的ask(memory-scoped-agent-config.ts)。
该模块配有独立的单元测试 memory-scoped-agent-config.test.ts,覆盖路径逃逸、pinned 保护、只读 shell 等边界场景。
6. 事件通知:memory_changed(scope:managed)
当某个工作区记忆任务成功完成且确实触碰了托管记忆时,daemon 会在 SSE 事件流(GET /session/:id/events)上发布一个memory_changed事件,scope为managed。订阅了按会话事件流的客户端都会收到该通知。
Payload:
{ "type": "memory_changed", "data": { "scope": "managed", "source": "workspace_memory_remember", "taskId": "remember-a1b2c3d4-...", "touchedScopes": ["user", "project"] } }| Field | Type | Description |
|---|---|---|
scope | "managed" | 与基于文件的memory_changed事件(scope: 'workspace' | 'global')区分开 |
source | string | "workspace_memory_remember"、"workspace_memory_forget"或"workspace_memory_dream" |
taskId | string | 与 POST 返回的任务 ID 关联 |
touchedScopes | string[] | 变更了哪些托管记忆 scope:"user"、"project" |
POST 时若提供了originatorClientId,它会被附加到事件信封上,使事件总线能将通知路由回发起方客户端(workspace-remember.ts)。事件只在touchedScopes非空时发布。
7. 错误处理与超时链
7.1 错误码全景
| Code | Origin | Meaning |
|---|---|---|
invalid_content | HTTP route | content 缺失、为空或超过 64 KiB |
invalid_context_mode | HTTP route | contextMode 不是"workspace"或"clean" |
invalid_query | HTTP route | forget query 缺失、为空或超过 64 KiB |
invalid_scope | HTTP route | scope 不是"project"或"user"(源码新增) |
invalid_client_id | HTTP route | Client-Id header 不在 bridge 的已知集合中 |
managed_memory_unavailable | Bridge / ACP child | 工作区未配置托管记忆 |
remember_queue_full | Task lane | 达到 16 条待处理任务上限 |
remember_path_escape | Core remember logic | Agent 写到了托管记忆目录之外的路径 |
remember_scope_mismatch | Core remember logic | Agent 写到了请求 scope 之外(源码新增) |
remember_no_update | Core remember logic | Agent 未更新任何记忆(源码新增) |
remember_failed | Catch-all | 未分类的 agent 失败、超时或内部错误 |
remember_task_not_found | HTTP route | GET 未知或未授权的任务 ID |
forget_task_not_found | HTTP route | GET 未知或未授权的 forget 任务 ID |
dream_task_not_found | HTTP route | GET 未知或未授权的 dream 任务 ID |
workspace_removed | Task lane | 工作区运行时被移除(源码新增,dispose路径) |
workspace_draining | Task lane | 工作区运行时正在被移除(源码新增,入队时拒绝) |
remember_timeout/forget_timeout/dream_timeout | ACP child | 子进程侧超时(源码新增) |
这些错误码的公开消息与 HTTP 状态映射集中在 workspace-remember.ts(publicErrorMessage/publicErrorStatus)以及 workspace-remember-errors.ts(workspaceMemoryFailureCode/workspaceMemoryFailureDiagnostics)中,并配有单元测试 workspace-remember-errors.test.ts。注意部分错误(如remember_path_escape)通过shouldSuppressRememberErrorDetails抑制内部细节外泄,避免把敏感路径暴露给客户端。
7.2 超时链
Agent forked runner: 5 min maxTimeMinutes Child abort signal: 295 s (WORKSPACE_MEMORY_REMEMBER_CHILD_TIMEOUT_MS) Bridge timeout: 300 s (WORKSPACE_MEMORY_REMEMBER_TIMEOUT_MS)子进程在 bridge 超时之前先 abort,从而保证错误以干净的语义错误(remember_timeout等)向上传播,而不是传输层超时。这条「子进程先于 bridge 兜底」的链式设计,配合任务车道层 5 分钟终态 TTL,构成了完整的超时与资源回收机制。
8. SDK 集成
8.1 TypeScript SDK(@qwen-code/sdk-typescript)
DaemonClient上暴露了对应方法(DaemonClient.ts):
// Queue a remember task const task = await client.rememberWorkspaceMemory( 'The project uses pnpm workspaces', { contextMode: 'workspace' }, ); // task.taskId, task.status === 'queued' // Poll until terminal const result = await client.getWorkspaceMemoryRememberTask(task.taskId); // result.status === 'completed' | 'failed' const forget = await client.forgetWorkspaceMemory('old preference'); const forgetResult = await client.getWorkspaceMemoryForgetTask(forget.taskId); const dream = await client.dreamWorkspaceMemory(); const dreamResult = await client.getWorkspaceMemoryDreamTask(dream.taskId);SDK 侧还做了能力预检:当调用方传入scope时,SDK 会先通过requireCapability()检查对应能力标签(workspace_memory_remember_project_scope/workspace_memory_remember_user_scope/workspace_memory_forget_scope),因为旧版 daemon 会静默忽略scope参数——预检把「静默降级」变成「响亮的错误」,避免无 scope 的 forget 误删两个存储中的条目(DaemonClient.ts)。这些方法在 DaemonClient.test.ts 中有完整的单测覆盖。
8.2 UI 事件规范化
SDK 的 normalizer 把原始 SSE 的memory_changed(scope: 'managed')事件映射为DaemonUiWorkspaceMemoryChangedEvent(normalizer.ts、types.ts):
{ type: 'workspace.memory.changed', scope: 'managed', source: 'workspace_memory_remember', taskId: 'remember-...', touchedScopes: ['user', 'project'] }这是对既有workspace.memory.changed事件类型的扩展——该事件此前只承载scope: 'workspace' | 'global'(基于文件的 QWEN.md 写入),如今新增'managed'分支。事件在 UI 层的terminal.ts、transcript.ts中按新分支分发,并有 daemonUi.test.ts 验证。
9. 设计权衡:为什么这样设计
为什么无会话(Why sessionless)?
CLI 中的/remember斜杠命令本身已经能在会话内工作,但设置面板和程序化 SDK 调用方不应该为了持久化一条事实而创建一个会话。会话意味着对话历史、回合跟踪和在会话列表中的可见性——这些对一个「即发即忘」的记忆写入来说全部是多余的负担。无会话 API 让记忆写入成为一等公民操作,与聊天会话完全解耦。
为什么串行执行(Why serial execution)?
托管记忆系统把事实存储在带索引的 markdown 文件中。多个 remember 任务并发写入可能损坏索引或产生合并冲突——单线程车道是最简单且正确的解法(结合 5.1 节可知,这是由一条 promise 链this.tail保证的硬性串行约束)。
为什么用任务队列而非同步执行(Why a task queue)?
记忆写入涉及 LLM agent 决定「在哪里、如何存」这条事实(在 user 与 project scope 之间选择、挑选正确的文件、格式化),通常耗时2–30 秒。同步 HTTP 请求要么超时、要么阻塞客户端。异步「队列 + 轮询」模式保持了 HTTP 契约的简洁,也让客户端能展示进度 UI。
为什么需要contextMode?
"workspace"(默认)——remember agent 能看到既有记忆作为上下文,从而对已有条目去重或更新;"clean"——agent 看不到任何先前的用户记忆,适合调用方希望强制全新写入、跳过去重逻辑的场景(例如批量导入)。
为什么限制读取路径(Why restrict reads to memory paths)?
remember agent 只应读写托管记忆目录内的内容。这能阻止一类提示注入攻击:精心构造的content诱导 agent 去读取敏感的项目文件并把内容泄漏进记忆条目。restrictReadsToMemoryPaths配合 symlink 解析(5.5 节)从权限层面对此做了硬性封堵。
10. 延伸阅读
- 设计文档原文:daemon-workspace-remember.md
- 车道与路由实现:workspace-remember.ts 及其测试 workspace-remember.test.ts
- 错误码与诊断:workspace-remember-errors.ts
- 核心 remember 逻辑:remember.ts 及其测试 remember.test.ts
- 记忆作用域权限包装器:memory-scoped-agent-config.ts
- Bridge 接口定义:bridgeTypes.ts
- ACP 子进程 ext-method 处理:acpAgent.ts
- SDK 客户端方法与 UI 事件规范化:DaemonClient.ts、normalizer.ts
- 相关设计背景(托管记忆体系):managed-memory-microcompaction、auto-memory 设计目录
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考