fix(ingest): detect active question tools awaiting input#45
Conversation
Users typing ~/projects/foo in the add-project form got a 'directory does not exist' error because Node's fs.existsSync doesn't expand tilde. The new expandTilde() helper (reusing the same pattern from XDG path handling) resolves ~ to os.homedir() before validation and storage.
- Add isActiveQuestionTool() for known question tools in pending|running - Use it in main-session, inclusion, and background-task derivation - Keep isPendingQuestionTool() pending-only for stale suppression - Add regressions for file-based, SQLite, and sound-diff paths
There was a problem hiding this comment.
Pull request overview
Improves ingest/UI status derivation so canonical “question” tools are detected as question not only when status: "pending", but also when status: "running" (as observed in OpenCode’s SQLite DB), restoring question styling and question sounds.
Changes:
- Add
isActiveQuestionTool(toolName, status)to treat known question tools as active forpending | running, and use it across main-session, inclusion, and background-task derivation paths. - Update file-based and SQLite background-task derivation to surface
questionstatus when canonical question tools arerunning. - Add regression tests covering file-based, SQLite bridge/background, and sound-diff behavior; also add
~expansion for project source registration.
[RISK:medium]
[SCORES]
{"security":4,"safety":4,"performance":4,"featureQuality":3,"confidence":4}
[/SCORES]
[SUMMARY]
The question-tool detection changes look well-covered by targeted regressions and align with the PR’s stated problem. However, the SQLite session inclusion path now appears unable to derive error status while still treating error as an attention/severity state, which can cause errored sessions to be filtered out after the idle window; this should be addressed before approval.
[/SUMMARY]
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ui/components/AddProjectForm.tsx | Updates project-root placeholder to a ~-based example path. |
| src/server/api.ts | Expands ~ in projectRoot before validating and persisting a new source. |
| src/ingest/tool-names.ts | Adds isActiveQuestionTool() (pending or running) while keeping isPendingQuestionTool() for stale suppression. |
| src/ingest/sqlite-derive.ts | Uses active-question detection for SQLite-derived session/background task statuses. |
| src/ingest/session-status.ts | Classifies active running canonical question tools as question for main session status. |
| src/ingest/session-inclusion.ts | Updates inclusion prefilter to treat active running question tools as question; refactors batch-query maps. |
| src/ingest/paths.ts | Adds expandTilde() helper for ~ / ~/... expansion. |
| src/ingest/background-tasks.ts | Uses active-question detection for file-based background task status derivation. |
| src/tests/session-status-derivation.test.ts | Adds regression for file-based “running question tool => question”. |
| src/tests/session-inclusion.test.ts | Adds regression asserting question sessions sort ahead of busy sessions. |
| src/tests/session-diff.test.ts | New regression ensuring question sound plays on transition into question. |
| src/tests/question-bridge-sqlite.test.ts | Adds regression for SQLite background question tool with status: "running". |
| src/tests/background-tasks-derive.test.ts | Adds regression for SQLite background task marked question when child tool is running. |
Comments suppressed due to low confidence (1)
src/ingest/session-inclusion.ts:12
- [SEVERITY:warning] [DIM:featureQuality]
findIncludedSessionsSqlite no longer queries terminal part statuses (e.g. error), but the inclusion/ordering logic still treats error as an attention status (STATUS_SEVERITY.error = 0, isAttentionStatus includes it). As written, deriveSessionStatusFromMaps cannot return "error", so sessions with tool errors may be filtered out once they age past idleWindowMs, and the error severity branch becomes unreachable.
Consider restoring a lightweight terminal-part query for state.status = 'error' (or otherwise deriving error from available data) and returning "error" from deriveSessionStatusFromMaps when applicable; alternatively, if error-attention is intentionally removed, delete the error severity/attention handling to avoid misleading logic.
// Severity levels for attention-first ordering
const STATUS_SEVERITY: Record<string, number> = {
error: 0,
question: 1,
running_tool: 2,
| if (!projectRoot || typeof projectRoot !== "string" || projectRoot.trim() === "") { | ||
| return c.json({ ok: false, error: "projectRoot is required and must be a non-empty string" }, 400) | ||
| } | ||
|
|
||
| if (!fs.existsSync(projectRoot)) { | ||
| const resolvedRoot = expandTilde(projectRoot.trim()) | ||
|
|
||
| if (!fs.existsSync(resolvedRoot)) { | ||
| return c.json({ ok: false, error: "projectRoot directory does not exist" }, 400) | ||
| } | ||
|
|
||
| const sourceId = addOrUpdateSource(opts.storageRoot, projectRoot, label) | ||
| const sourceId = addOrUpdateSource(opts.storageRoot, resolvedRoot, label) |
| export function expandTilde(p: string, hd?: string): string { | ||
| const home = hd ?? os.homedir() | ||
| if (p === "~") return home | ||
| if (p.startsWith("~/") || p.startsWith("~\\")) { | ||
| return path.join(home, p.slice(2).replace(/[\\/]+/g, path.sep)) | ||
| } | ||
| return p | ||
| } |
Summary
Fixes incorrect question-state detection when canonical
questiontools havestatus: "running"in OpenCode's SQLite database.Problem
running_toolinstead ofquestionbecause detection only acceptedstatus: "pending".Solution
isActiveQuestionTool(toolName, status)for known question tools inpending | running.isPendingQuestionTool()pending-only to preserve stale-running suppression behavior.Test Results