Skip to content

fix(archive): pure-export semantics + auto-unarchive migration (#169)#170

Merged
ZhouChaunge merged 3 commits into
mainfrom
fix/archive-pure-export-169
May 26, 2026
Merged

fix(archive): pure-export semantics + auto-unarchive migration (#169)#170
ZhouChaunge merged 3 commits into
mainfrom
fix/archive-pure-export-169

Conversation

@ZhouChaunge
Copy link
Copy Markdown
Collaborator

@ZhouChaunge ZhouChaunge commented May 26, 2026

Closes #169.

改动

本 PR 含两组改动:

A. Archive 语义改为「纯导出」 (#169)

Archive 从「软隐藏 + 导出」改成「纯导出」。

  • src/chat/session-store.jsarchive(id)
    • 删除 unarchive 分支(之前不可达);
    • 删除 s.archived = true 写入;
    • 删除当前激活 session 被切走(sessionId = null + sessionLoaded 空消息广播)的副作用;
    • 删除写回 list 和 postList() 调用(不再有状态变更需要广播);
    • 仅保留:调用 exportSessionToMarkdown(s) + 错误/取消处理 + _notifyArchived(savedPath)
  • src/chat/session-store.js 构造函数新增一次性迁移 _migrateArchivedFlagIfNeeded()
    • globalState['deepseekAgent.archiveSemanticsV2Migrated'] 守护,幂等;
    • 把所有 archived: true 翻回 false
    • 迁移失败走 Logger.info('ARCHIVE_V2_MIGRATION_FAILED', ...),不阻塞激活;
    • 仅当真有 session 被翻转时才调 postList(),避免无谓刷新。

注意:session-store.js#L125.filter(s => !s.archived)provider.js#L208find(s => !s.archived) 保留,作为向后兼容。迁移完成后所有记录都 archived = false,过滤器自然变成 no-op。后续 minor 版本可移除字段。

B. Watchdog 任务不再提前结束会话

模型在执行「值守」类任务(例如长时间训练)时,如果 bg job 是在更早的轮次启动的,run._sessionStartedBgJobs 在当前 run 是空集,BG_WAIT_SKIPPED_MODEL_DONE 守卫就会在模型刚说完「我会持续监控」之后立刻结束 turn,把值守静默掐断。

  • src/chat/tool-executor.js:当模型调用 read_terminal(terminal: "deepseek-job-*") 时,把该 jobId 记入 run._monitoredBgJobs,作为「当前 run 在主动监控该任务」的信号。
  • src/chat/agent-loop.js
    • 初始化 run._monitoredBgJobs = new Set()
    • bg job 结束事件同步清理该集合;
    • BG_WAIT_SKIPPED_MODEL_DONE 守卫新增 _hasMonitoredRunningJob 条件 —— 任何被监控且仍活着的 bg job 都会阻止 turn 提前退出,循环继续走 4 分钟快照 / 等结束事件,直到任务完成或命中 4h 本轮预算。

使用场景对比

场景 旧行为 新行为
点存档 写 md + session 消失 写 md + session 保持原状
同一 session 连点 2 次 第二次取消存档(session 回来) 生成 2 个 md,session 保持原状
取消保存对话框 session 不动 session 不动(不变)
写盘失败 toast 报错,session 不动 toast 报错,session 不动(不变)
升级到带本 PR 的版本 旧版本被存档的所有 session 自动回到列表
模型值守跨 turn 的训练 模型说完「我会监控」立刻退出 turn 只要 read_terminal 过该 bg job 且任务仍在跑,turn 持续等待

测试

  • npm run build ✅。
  • npm run lint ✅ 0 errors(warnings 全是仓库现存的 indent 风格告警,未在本次新增)。
  • 手工核查 archive() 路径:导出成功 → toast;导出取消 → 静默;导出抛错 → toast;任何分支都不再改动 session state。
  • 手工核查迁移:第一次运行清零 archived 字段并写 flag;第二次进入即 short-circuit;失败路径走 Logger.info
  • 手工核查 watchdog:bg job 在前一个 turn 启动,本 turn 调用过 read_terminal(deepseek-job-X) 后,模型即便给出最终回复也不会触发 BG_WAIT_SKIPPED_MODEL_DONE,循环继续。

兼容性 / 风险

  • 不破坏数据 schema:archived 字段保留,迁移只翻值。
  • 旧的 .deep-copilot/archives/*.md 文件原封不动。
  • 仅一个可观察行为变化:升级后曾经被「藏起来」的 session 全部回到侧边栏。需要在 release notes 写一句迁移提示。
  • Watchdog 改动只放宽提前退出条件,不改变正常 turn 结束语义;模型从未 read_terminal 过的 bg job(例如旁路 dev server)依然走老的 short-circuit。

关联

Archive used to combine 'export to Markdown' with a soft-hide that removed the session from the sidebar. Users expected archive to mean 'snapshot the conversation as a Markdown file', not 'hide the conversation'. The old soft-hide also made the 'click again to un-archive' code path unreachable from the UI because hidden sessions never reappear in the list.

Changes:

- session-store.js archive(id): drop unarchive branch, drop archived=true write, drop sessionId reset. Only render to Markdown + toast.

- session-store.js constructor: one-shot idempotent migration flips every previously-hidden session back to visible, gated by archiveSemanticsV2Migrated flag in globalState. Errors are swallowed so a failed migration cannot block activation.

After upgrade, sessions hidden by the old archive action reappear in the sidebar. Repeated archive on the same session produces multiple Markdown snapshots (timestamp + _writeUnique suffix on collision).
Copilot AI review requested due to automatic review settings May 26, 2026 09:53
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review by ChatGPT

Comment thread src/chat/session-store.js
}
}

// ─── Raw storage ────────────────────────────────────────────────────────
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_migrateArchivedFlagIfNeeded 方法中,虽然错误被捕获并记录,但没有对错误进行进一步处理。建议在捕获错误后,考虑是否需要进行重试或记录更多上下文信息,以便后续排查。同时,建议确保 this._gs 的获取和使用是安全的,避免潜在的空指针异常。

Comment thread src/chat/session-store.js
if (!savedPath) return; // user cancelled

this._notifyArchived(savedPath);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

archive 方法中,虽然进行了错误处理,但在调用 exportSessionToMarkdown 时,如果发生错误,应该确保 s.archived 状态不被改变。建议在 try-catch 块中明确处理状态的变化,确保在任何情况下都能保持状态一致性。此外,建议对 this.all() 的调用进行空值检查,以防止潜在的空指针异常。

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates DeepCopilot’s “Archive” action semantics to match user expectations from #169: archiving becomes a pure export to Markdown, no longer a “soft-hide” toggle. It also adds a one-time migration to make previously hidden (archived: true) sessions visible again after upgrade.

Changes:

  • Make SessionStore.archive(id) export-only: remove any mutation of archived, sessionId, persistence, and list broadcasts.
  • Add a one-shot, idempotent migration on SessionStore construction to flip stored archived: true sessions back to false, guarded by a globalState flag.

Comment thread src/chat/session-store.js
Comment on lines +134 to +138
} catch (err) {
// Non-fatal: next launch will retry.
// eslint-disable-next-line no-console
console.warn('[deep-copilot] archive-v2 migration failed:', err && err.message || err);
}
When the model watches a bg job (e.g. long training) that was started in
an earlier turn, run._sessionStartedBgJobs is empty and the
BG_WAIT_SKIPPED_MODEL_DONE guard ends the turn right after the model says
"I will keep monitoring" -- silently dropping the watchdog.

Track jobs the model inspects via read_terminal(terminal: "deepseek-job-*")
in run._monitoredBgJobs. The guard now also refuses to end the turn while
any monitored job is still active, so the loop keeps polling/snapshotting
until completion or the 4h per-turn budget.
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review by ChatGPT

Comment thread src/chat/agent-loop.js
}
};
onBgJobEnded(_bgJobEndHandler);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

新增的 run._monitoredBgJobs 集合用于跟踪模型在当前运行中主动检查的后台作业,但未对其进行初始化检查。在使用前应确保其存在且为 Set 类型,以避免潜在的空指针异常。此外,建议在注释中详细说明该集合的用途和生命周期,以提高可维护性。

Comment thread src/chat/agent-loop.js
monitoredJobs: _monitored ? [..._monitored] : [],
});
break;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在检查 run._monitoredBgJobs 时,虽然进行了类型检查,但建议在使用前确保该集合已被正确初始化。此外,建议在条件判断中添加日志记录,以便在出现问题时能够追踪到具体的作业状态。

Comment thread src/chat/tool-executor.js
}
if (tcId) {
ctx.onStreamDelta = (delta) => {
if (!delta) return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

此段代码引入了对终端的监控机制,但在处理 args.terminal 时缺乏有效的输入验证,可能导致命令注入风险。建议在使用 args.terminal 之前,增加对其内容的严格校验,确保其符合预期格式。此外,run._monitoredBgJobs 的初始化逻辑应考虑并发访问的安全性,避免潜在的竞态条件。

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread src/chat/tool-executor.js
Comment on lines +535 to +548
// Watchdog/monitor detection: when the model inspects an existing
// deepseek-job-* terminal via read_terminal in this run, treat that
// as an active monitoring commitment. agent-loop's
// BG_WAIT_SKIPPED_MODEL_DONE guard refuses to end the turn while a
// monitored job is still alive — preventing the model from
// "promising to keep watching" and then immediately stopping when
// the underlying job was spawned in a previous turn.
if (name === 'read_terminal' && run && args && typeof args.terminal === 'string') {
const _jobId = args.terminal;
if (/^deepseek-job-/.test(_jobId)) {
if (!(run._monitoredBgJobs instanceof Set)) run._monitoredBgJobs = new Set();
run._monitoredBgJobs.add(_jobId);
}
}
Comment thread src/chat/session-store.js
Comment on lines +134 to +138
} catch (err) {
// Non-fatal: next launch will retry.
// eslint-disable-next-line no-console
console.warn('[deep-copilot] archive-v2 migration failed:', err && err.message || err);
}
Comment thread src/chat/session-store.js Outdated
Comment on lines +112 to +113
// Fire-and-forget: any postList() call after the next tick will see
// the migrated data.
…-migration comment

Addresses Copilot review on PR #170:
- Replace console.warn (and the no-console eslint suppression) in the
  archive-v2 migration error path with Logger.info('ARCHIVE_V2_MIGRATION_FAILED').
  Diagnostics now respect deepseekAgent.enableDebugLog and surface in the
  "Deep Copilot Debug" output channel.
- Reword the constructor comment: the migration is fire-and-forget and
  triggers postList() itself on completion; the sidebar refreshes when the
  migrated data lands, not necessarily on the next tick.
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review by ChatGPT

Comment thread src/chat/session-store.js
});
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在捕获异常时,建议对错误进行更详细的处理,尤其是在日志记录时。虽然使用 Logger 记录错误信息是一个好的改进,但在某些情况下,可能需要对错误进行更细致的分类或处理,以便后续的调试和维护。此外,确保 Logger 的实现是线程安全的,以避免潜在的竞态条件。

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@ZhouChaunge ZhouChaunge merged commit 8358728 into main May 26, 2026
18 checks passed
@ZhouChaunge ZhouChaunge deleted the fix/archive-pure-export-169 branch May 26, 2026 10:18
ZhouChaunge added a commit that referenced this pull request May 26, 2026
- Archive becomes a pure Markdown export; one-shot idempotent migration
  un-hides legacy soft-archived sessions (Issue #169 / PR #170).
- Watchdog turns now track read_terminal targets in run._monitoredBgJobs
  so the BG_WAIT_SKIPPED_MODEL_DONE guard no longer cuts off long-running
  monitoring turns started in an earlier turn.
- Migration failures route through Logger (replacing console.warn).
- .vscodeignore filters .tmp-*.json/.txt/.md to keep PR-review scratch
  caches out of the shipped VSIX.

Release notes: release/notes_v0416.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Archive 语义重设计:从「软隐藏 + 导出」改为「纯导出」

2 participants