mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(cli): keep search index ephemeral
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-27-web-session-search.md
|
||||
2026-07-27-web-session-search.md: 421922d25bc61c1813a515e8439af0c7956b2efb
|
||||
2026-07-27-web-session-search.zh.md: 04035e74fae718dbf65b294b4293d368ecc89cf7
|
||||
2026-07-27-web-session-search.md: 8791d02249cc310e768712b3967dcfead3a950e0
|
||||
2026-07-27-web-session-search.zh.md: 737f91fbe2c00ac5aa75fb6c30b8b22f80b0854f
|
||||
|
||||
@@ -10,7 +10,7 @@ The Web sidebar exposes session titles and Workspace membership but cannot retri
|
||||
|
||||
## Decision
|
||||
|
||||
The shared Web/headless composition mounts [`@deepseek-ai/dsh-session-query-sqlite`](../../../../packages/session-query/session-query-sqlite/README.md) at a process-owned `.sessions/session-query-<pid>.db` path. Process scoping preserves the SQLite backend's single-owner contract when multiple CLI or Web processes run from the same directory. Opening the database does not scan logs; the first content query lazily reconciles changed live and persisted sessions. The database is a disposable derived index, separate from canonical JSONL persistence.
|
||||
The shared Web/headless composition mounts [`@deepseek-ai/dsh-session-query-sqlite`](../../../../packages/session-query/session-query-sqlite/README.md) with an in-memory database. Each service instance owns one connection-private index, preserving the SQLite backend's single-owner contract across parallel CLI or Web invocations without leaving process-scoped derived files behind. The database starts empty; the first content query of each invocation lazily reconciles live and persisted sessions. It remains a disposable derived index, separate from canonical JSONL persistence.
|
||||
|
||||
The host gateway exposes `session.search` through the existing typed RPC stack. It derives the authorization set from the same visible summaries as `session.list`, passes those ids to `ctx.sessionQuery.searchSessions`, and restricts indexed matches to current-surface `user/message`, `assistant/message`, and `steering/message` events. The response is one page of at most 20 session ids and snippets; `hasMore` tells the UI to ask for a narrower query rather than exposing pagination. The carrier signal cancels superseded work, including the persistence listing and bounded batches of cold-session metadata stats that build the visibility set. A missing query service or an indexing/query failure remains a business error and does not mutate the canonical session store.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Web 侧边栏会展示会话标题及其 Workspace 归属,但无法根据只
|
||||
|
||||
## 决策
|
||||
|
||||
Web 与 headless 共用的组合会在由单一进程拥有的 `.sessions/session-query-<pid>.db` 路径挂载 [`@deepseek-ai/dsh-session-query-sqlite`](../../../../packages/session-query/session-query-sqlite/README.md)。当多个 CLI 或 Web 进程从同一目录运行时,进程级隔离可维持 SQLite 后端的单一所有者契约。打开数据库时不会扫描日志;首次内容查询会惰性对齐发生变更的实时会话与持久化会话。该数据库是可丢弃的派生索引,与规范 JSONL 持久化相互独立。
|
||||
Web 与 headless 共用的组合会使用内存数据库挂载 [`@deepseek-ai/dsh-session-query-sqlite`](../../../../packages/session-query/session-query-sqlite/README.md)。每个服务实例都独占一个连接私有索引,因此并行 CLI 或 Web 调用可维持 SQLite 后端的单一所有者契约,又不会留下进程级派生文件。数据库从空状态启动;每次调用的首次内容查询会惰性对齐实时会话与持久化会话。它仍是与规范 JSONL 持久化相互独立的可丢弃派生索引。
|
||||
|
||||
宿主网关通过现有的类型化 RPC 栈公开 `session.search`。它根据 `session.list` 使用的同一组可见摘要推导授权集合,将这些 id 传给 `ctx.sessionQuery.searchSessions`,并将索引匹配限制为当前 surface 中的 `user/message`、`assistant/message` 和 `steering/message` 事件。响应只包含一页,最多 20 个会话 id 及其摘要片段;`hasMore` 会指示 UI 提示用户缩小查询范围,而不是公开分页能力。载体信号会取消已被取代的工作,包括持久化列表枚举和构建可见集合时分批受限执行的冷会话元数据 stat。查询服务缺失或索引、查询失败仍作为业务错误处理,不会修改规范会话存储。
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write apps/cli/README.md
|
||||
README.md: c1d76e42a6788f48c4dd01bbf71281e1081a411c
|
||||
README.zh.md: 69bd88ca8fc7086c94017ac7d25ebd55e8585427
|
||||
README.md: 0c4ff8d36a89e234512f42d130774fe3717968b9
|
||||
README.zh.md: 7e116ecb32061060816f27279a5d3b555a584f6b
|
||||
|
||||
@@ -14,7 +14,7 @@ The TUI surface:
|
||||
- tells the agent where its own source lives: after boot it adds a prompt section naming this harness checkout, resolved from the launcher's real path so it holds under a PATH symlink and an arbitrary cwd, so the self-referential `cordis` toolset can read and modify it;
|
||||
- applies the personal overlay from `~/.dsh` (see [app-boot's Personal config](../../packages/ui/app-boot/README.md#personal-config)): `.env` fills environment gaps (ambient > project `.env` > personal `.env`), `config.yaml` patches the booted tree.
|
||||
|
||||
The Web and headless surfaces boot one shared composition (`cordis.yml`): both treat the invoking directory as the default project and Workspace root, create named Workspaces beneath that root unless `--workspace-root <path>` overrides it, load applicable `AGENTS.md`/`CLAUDE.md` instructions into each agent-loop request prefix with a 65,536-byte render budget, opt into first-message model titles, and mount a disposable, process-owned SQLite content index at `.sessions/session-query-<pid>.db`. The process-specific path preserves the backend's single-owner contract across parallel invocations. The index is opened without scanning at boot and lazily reconciles changed live and persisted logs on the first session search. Headless differs only in listening on an OS-assigned port (parallel `dsh -p` runs never collide; the stderr-printed URL opens the live session in a browser). Both need the frontend dist and client bundles built (`pnpm run build && pnpm run build:web`).
|
||||
The Web and headless surfaces boot one shared composition (`cordis.yml`): both treat the invoking directory as the default project and Workspace root, create named Workspaces beneath that root unless `--workspace-root <path>` overrides it, load applicable `AGENTS.md`/`CLAUDE.md` instructions into each agent-loop request prefix with a 65,536-byte render budget, opt into first-message model titles, and mount a disposable in-memory SQLite content index. Each service instance owns its database, so parallel invocations neither share unsupported SQLite state nor leave derived index files behind. The index starts empty and lazily reconciles live and persisted logs on the first session search of each invocation. Headless differs only in listening on an OS-assigned port (parallel `dsh -p` runs never collide; the stderr-printed URL opens the live session in a browser). Both need the frontend dist and client bundles built (`pnpm run build && pnpm run build:web`).
|
||||
|
||||
`DSH_TOOLS_MODE` selects the tool presentation mode for the whole Web/headless process: `native` (the schema default when unset), `code` (the `run_code`-only Code Mode wire), or `both`; any other value fails loud at boot through the `dsh-tools` config schema. It is a TEMPORARY seam — process-wide because Loader composition is static — and is removed once the web UI owns per-session tool-mode selection; the TUI surface ignores it (its config tree pins its own mode).
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ TUI 界面:
|
||||
- 告知 agent 自身源码所在位置:启动后添加一个命名此 harness checkout 的提示词段。该路径从启动器的真实路径解析,因此在 PATH 符号链接和任意 cwd 下仍然有效,使自指的 `cordis` 工具集可以读取并修改它;
|
||||
- 应用 `~/.dsh` 中的个人覆盖(参见 [app-boot 的个人配置](../../packages/ui/app-boot/README.md#personal-config)):`.env` 填补环境缺口(环境中已有的值 > 项目 `.env` > 个人 `.env`),`config.yaml` 则修补已启动的树。
|
||||
|
||||
Web 和无头界面启动同一个共享组合(`cordis.yml`):两者都将调用目录视为默认项目和 Workspace 根目录,除非通过 `--workspace-root <path>` 覆盖,否则会在该根目录下创建具名 Workspace;它们会把适用的 `AGENTS.md`/`CLAUDE.md` 指令加载到每个 agent-loop 请求前缀中,渲染预算为 65,536 字节,并选用首条消息模型标题,且在 `.sessions/session-query-<pid>.db` 挂载一个可丢弃、由单一进程拥有的 SQLite 内容索引。该进程专属路径可在并行调用时维持后端的单一所有者契约。该索引在启动时不经扫描即打开,并在首次会话搜索时惰性对账已更改的实时日志和持久化日志。无头界面唯一的差异是监听操作系统分配的端口(并行 `dsh -p` 运行绝不冲突;stderr 打印的 URL 会在浏览器中打开实时会话)。两者都需要先构建前端 dist 和客户端 bundle(`pnpm run build && pnpm run build:web`)。
|
||||
Web 和无头界面启动同一个共享组合(`cordis.yml`):两者都将调用目录视为默认项目和 Workspace 根目录,除非通过 `--workspace-root <path>` 覆盖,否则会在该根目录下创建具名 Workspace;它们会把适用的 `AGENTS.md`/`CLAUDE.md` 指令加载到每个 agent-loop 请求前缀中,渲染预算为 65,536 字节,并选用首条消息模型标题,且挂载一个可丢弃的内存 SQLite 内容索引。每个服务实例独占自己的数据库,因此并行调用既不会共享不受支持的 SQLite 状态,也不会留下派生索引文件。该索引从空状态启动,并在每次调用的首次会话搜索时惰性对账实时日志与持久化日志。无头界面唯一的差异是监听操作系统分配的端口(并行 `dsh -p` 运行绝不冲突;stderr 打印的 URL 会在浏览器中打开实时会话)。两者都需要先构建前端 dist 和客户端 bundle(`pnpm run build && pnpm run build:web`)。
|
||||
|
||||
`DSH_TOOLS_MODE` 为整个 Web/无头进程选择工具呈现模式:可选值为 `native`(未设置时的 schema 默认值)、`code`(仅含 `run_code` 的 Code Mode 协议接口)或 `both`;任何其他值都会经由 `dsh-tools` 配置 schema 在启动时明确报错。它是一个临时 seam:Loader 组合是静态的,因此该设置作用于整个进程;待 Web UI 负责逐会话工具模式选择后便会移除。TUI 界面会忽略该变量(其配置树固定了自身模式)。
|
||||
|
||||
|
||||
@@ -89,14 +89,13 @@
|
||||
config:
|
||||
root: './.sessions'
|
||||
|
||||
# Lazy, process-owned content index for session.search. Opening the database
|
||||
# at boot does not scan logs; the first search reconciles changed
|
||||
# live/persisted sessions. The pid prevents concurrent dsh processes in the
|
||||
# same cwd from sharing one unsupported SQLite owner path.
|
||||
# Lazy, service-owned content index for session.search. The in-memory database
|
||||
# cannot be shared across processes or leak derived files across invocations;
|
||||
# the first search reconciles changed live/persisted sessions for this boot.
|
||||
- id: session-query-sqlite
|
||||
name: '@deepseek-ai/dsh-session-query-sqlite'
|
||||
config:
|
||||
path: !!js "'./.sessions/session-query-' + process.pid + '.db'"
|
||||
path: ':memory:'
|
||||
|
||||
- id: storage
|
||||
name: '@deepseek-ai/dsh-storage'
|
||||
|
||||
@@ -163,7 +163,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
|
||||
// stops matching a row fails the boot sweep loudly instead of drifting).
|
||||
const patches: PatchOptions[] = [
|
||||
{ id: 'session-persistence-jsonl', config: { root: persistenceRoot } },
|
||||
{ id: 'session-query-sqlite', config: { path: join(persistenceRoot, 'session-query.db') } },
|
||||
{ id: 'session-query-sqlite', config: { path: ':memory:' } },
|
||||
// storage-json's './.storages' yml default is cwd-relative and resolves
|
||||
// per write; the scaffold restores the original cwd after boot, so the
|
||||
// row gets an absolute temp root (removed with the workspace at close).
|
||||
|
||||
@@ -1052,9 +1052,9 @@ Requires: `sessions`
|
||||
/** Combined session-query configuration backed by SQLite full-text search. */
|
||||
export interface Config extends SessionQueryConfig {
|
||||
/**
|
||||
* Dedicated derived-index path; `:memory:` is supported for tests. Missing
|
||||
* directories and database files are created owner-only on POSIX filesystems;
|
||||
* existing modes are preserved.
|
||||
* Dedicated derived-index path; `:memory:` is supported for ephemeral
|
||||
* indexes. Missing directories and database files are created owner-only on
|
||||
* POSIX filesystems; existing modes are preserved.
|
||||
*/
|
||||
path: string
|
||||
/** SQLite journal mode. Defaults to `wal`. */
|
||||
|
||||
@@ -75,9 +75,9 @@ const STABLE_OBSERVATION_ATTEMPTS = 2
|
||||
/** Combined session-query configuration backed by SQLite full-text search. */
|
||||
export interface Config extends SessionQueryConfig {
|
||||
/**
|
||||
* Dedicated derived-index path; `:memory:` is supported for tests. Missing
|
||||
* directories and database files are created owner-only on POSIX filesystems;
|
||||
* existing modes are preserved.
|
||||
* Dedicated derived-index path; `:memory:` is supported for ephemeral
|
||||
* indexes. Missing directories and database files are created owner-only on
|
||||
* POSIX filesystems; existing modes are preserved.
|
||||
*/
|
||||
path: string
|
||||
/** SQLite journal mode. Defaults to `wal`. */
|
||||
|
||||
Reference in New Issue
Block a user