Conversation
Print/headless sessions no longer start fire-and-forget qmd update/embed. A qmd launcher whose grandchild inherits stdio could keep the host event loop alive long after the model had already answered; awaited queries (memory_search) are unaffected. Background qmd bookkeeping (enabled, lifecycle, in-flight, pending, timer, child handles) is now per extension registration, so one Pi instance in the same process can no longer cancel another instance's work. Shutdown clears the debounce timer and pending state, kills the tracked direct launcher and waits at most ~1s for it, and is safe to call repeatedly. The deadline is no longer execFile's `timeout` option: that timer is ref'd, so a 600s deadline kept the host waiting 600s even with the child and both pipes unref'd. A self-owned unref'd watchdog replaces it and only the direct child is killed; the grandchild tree is explicitly not promised. Verified: `bun test` -> 193 pass / 0 fail (baseline before change: 182 pass); `npx tsc -p tsconfig.json --noEmit`; `npx biome check index.ts test/unit.test.ts`; `git diff --check`. Real print-mode smoke with qmd enabled exits rc=0 in ~3s, and a Node fixture with a stdio-inheriting grandchild exits in ~0.74s instead of waiting for the 600s watchdog. Signed-off-by: Howie Liang <hao.c.code@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3bcca69d40
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const mode = params.mode ?? "keyword"; | ||
| const limit = clampSearchLimit(params.limit); | ||
|
|
||
| if (!qmdState.enabled && qmdState.indexDirty) { |
There was a problem hiding this comment.
Respect disabled qmd updates during headless search
When PI_MEMORY_QMD_UPDATE is manual or off, every mutation still marks indexDirty, and this branch unconditionally runs qmd update before the next headless search. This overrides the documented opt-out in README.md:190-205 and can add an unwanted process invocation of up to 30 seconds; gate this refresh on background mode or avoid marking the index dirty when automatic updates are disabled.
Useful? React with 👍 / 👎.
| if (!qmdState.enabled && qmdState.indexDirty) { | ||
| try { | ||
| await new Promise<void>((resolve, reject) => { | ||
| execFileFn("qmd", ["update"], { timeout: DEFAULT_QMD_UPDATE_TIMEOUT_MS }, (err) => |
There was a problem hiding this comment.
Refresh vectors before semantic headless searches
After a headless write, this only runs qmd update, which refreshes the text index but not the embeddings required by semantic and deep modes (README.md:180-181). The subsequent vsearch/query can therefore return stale or missing results, and its existing self-heal cannot start an embed because qmdState.enabled is false in headless sessions; synchronously refresh embeddings for those modes before searching.
Useful? React with 👍 / 👎.
Follow-up to #39. Keeps background qmd work disabled for headless/print sessions, tracks mutations as index-dirty, and synchronously runs qmd update before an explicit headless memory_search so queries cannot read a stale index.