feat(kap-server): add session-less POST /workspace/fs:search route - #2437
Conversation
…sions - fs:search accepts a workspace id or absolute root in the session_id slot so the @ file mention works before the session exists - kimi-web searchFiles falls back to the active workspace id in draft state
🦋 Changeset detectedLatest commit: 8ee2a8f The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7cfa0692a8
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| async function searchFiles(query: string): Promise<Array<{ path: string; name: string }>> { | ||
| const sid = rawState.activeSessionId; | ||
| if (!sid) return []; | ||
| const id = rawState.activeSessionId ?? rawState.activeWorkspaceId; |
There was a problem hiding this comment.
Use resolved active workspace for draft file search
In the no-session draft path, the UI uses client.activeWorkspaceId.value, whose computed fallback returns the first visible workspace when rawState.activeWorkspaceId is unset or stale. This line reads only the raw field, so after loading a registered workspace with no persisted selection/session (or after a stale persisted id), the composer can submit to a workspace but @ autocomplete still returns [] or queries the wrong id. Please pass the same resolved active workspace into this composable or fall back to workspacesView.value[0]?.id.
Useful? React with 👍 / 👎.
| const workspaceFs = | ||
| session === undefined && fsAction === 'search' | ||
| ? await resolveWorkspaceFs(core, session_id) |
There was a problem hiding this comment.
Map workspace-resolution failures for draft search
For fs:search with a registered workspace id whose root was removed or became unreadable, handlerFor can throw while materializing the workspace through createOrTouch. Because this fallback resolution runs before the switch's try/catch, those errors bypass sendMappedError and hit the global handler as 50001 instead of the declared filesystem/session envelope, turning a stale workspace entry into an internal-error response. Move this resolution inside the mapped-error try or catch it here.
Useful? React with 👍 / 👎.
- sessionLegacyService.status returns thinking_level '' when the main agent has no bound model (mirroring model: undefined), so clients fall back to the catalog default instead of folding in the wire model's 'off' zero value - add regression test for a never-bound main agent status - add web changesets: draft @ file mention, new-session thinking level
- TextIndex.build() yields to the event loop during tokenization and batches postings writes (~1 MiB), so large rebuilds no longer hard-block the host process - writes landing mid-build are queued and replayed onto the new base at swap time, keeping the rebuilt index exact - PostingsFile.rebuildSync renamed to async rebuild with a synchronous commit section (beforeRename hook + atomic rename) - onCompacted hook is now awaited (sync or async); open-time compaction runs in the background so open() returns without blocking on the snapshot rewrite and postings rebuild - compaction skips the postings rebuild when the index's write buffer is clean (needsRebuild) - createTextIndex registers before building so concurrent writes feed the build queue; dropTextIndex throws while a build is in flight
- rename IWorkspaceHandlerService to ISessionLifecycleService and move src/workspace/workspaceHandler/ to src/workspace/sessionLifecycle/; update all consumers (gateway, sessionExport, sessionLegacy, sessionLookup, kap-server, klient, node-sdk, kimi-inspect, kimi-code) - rename IStateService to IAppStateService and add the Workspace-scope IWorkspaceStateService, so the state domain spans all four scope tiers - add cascading StateRegistry.inspect(): each tier injects the parent tier's registry and folds App to current scope into one StateInspection tree; check-domain-layers gains a Rule 2b exemption for state-on-state imports
Carry the workspace reference (registered id or absolute root) in the
request body and resolve it to the same Workspace-scope fs service the
session route uses, so clients no longer borrow the session route's
{session_id} slot. kimi-web's @ file mention now calls this route with
the workspace ref instead of a session id; the session-route fallback
stays for wire compatibility.
…IWorkspaceStateService - move workspaceDirs / workspaceInstructions / workspaceSkillCatalog / workspaceTrust runtime state from bare instance fields into the workspace state container - extend gen-state-manifest.mts to scan app/workspace scopes, emitting AppStateSnapshot / WorkspaceStateSnapshot alongside Session/Agent - regenerate docs/state-manifest.d.ts and update AGENTS.md + agent-core-dev skill - update affected tests to register the state services and assert the new state keys
Related Issue
No linked issue — the problems are explained per theme below.
Problem
kimi-web's
@file mention must search files before a session exists (the new-session draft), but the only search endpoint wasPOST /sessions/{id}/fs:search, forcing the client to abuse the{session_id}slot to carry a workspace reference, and to keep a session id around for ordinary searches. This branch makes session-less workspace search first-class and carries the stacked engine work it builds on:POST /api/v1/workspace/fs:search; draft@mentions had to borrow the session route's{session_id}slot, and kimi-web could not search files without a session id.workspaceHandlerwas misnamed: the Workspace-scope service owns session create/resume/fork/close, sosessionLifecyclematches its role (reviving the name of the deleted App-scope domain).off) instead of an empty level, so new sessions briefly showed a stale thinking level (e.g. Max).What changed
1. Session-less workspace file search (
7cfa0692a,848713317)POST /api/v1/workspace/fs:search: the request body is the engine's fs-search request plus aworkspacefield (registered workspace id or absolute root, registered on the spot), resolved to the same Workspace-scope fs service a session would resolve to. Unknown refs map to40410 workspace.not_found; the response shape matches the session route'sfs:search.@mention now always calls this route with the workspace ref — the active session's workspace, or the draft's active workspace — so no session id is involved anywhere.{session_id}slot, introduced in7cfa0692a) stays for wire compatibility.packages/kap-server/test/fs.test.ts(registered id, absolute root, empty query listing, unknown ref, missing field), the API-surface snapshot gains the route, and the file's temp-dir cleanup follows the repo's retry pattern for the async query-store shard writer.2. refactor(agent-core-v2): rename workspaceHandler → sessionLifecycle (
7c0a93b0c)IWorkspaceHandlerService→ISessionLifecycleService) across the engine, kap-server, klient, kimi-inspect, kimi-code, and the agent-dev docs/skills. No behavior change.3. perf(minidb): async, non-blocking text index rebuilds (
91e133b88)TextIndex.build()yields to the event loop during tokenization and batches postings writes; writes landing mid-build are queued and replayed onto the new base at swap time; open-time compaction runs in the background; the postings rebuild is skipped when the write buffer is clean.ENOTEMPTYtemp-dir cleanup errors (pre-existing on this branch, unrelated to theme 1).4. fix(agent-core-v2): empty thinking level for unbound main agent (
ff57f5fa0)sessionLegacyService.statusreturns an emptythinking_levelwhen the main agent has no bound model, so clients fall back to the catalog default; ships web changesets for the draft@mention and the new-session thinking level.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.