Bug Description
Summary
In the Web Studio playground, the bot/agent has no way to know which directory the
user has selected in the left-side resource tree. The tree selection
(currentUri, mirrored into the URL as ?uri=viking://...) is dropped at the
AgentPanel boundary — it is never sent to the bot chat endpoint, so the agent
always answers from the knowledge-base root (or the per-session sandbox cwd)
instead of the directory the user is currently looking at.
Defects (verified against main)
The full request path carries no directory field end-to-end:
-
Frontend builds the request without the directory.
web-studio/src/routes/playground/route.tsx holds currentUri in state and
passes it to TerminalPanel (L842), but not to AgentPanel (L851):
{activePanel === 'terminal' ? (
<TerminalPanel currentUri={currentUri} ... />
) : (
<AgentPanel
initialSessionId={sessionId}
onOpenResource={onOpenResource}
onSessionChange={onSessionChange}
/>
)}
AgentPanel.send (agent-panel.tsx:169) only forwards the message text:
const send = useCallback((message: string) => {
void chat.send(message)
}, ...)
-
The chat request body has no directory field.
web-studio/types/ov-server/bot/v1/chat.ts:
export type BotChatRequest = {
channel_id?: string
message: string
need_reply?: boolean
session_id?: string
stream?: boolean
user_id?: string
}
No selectedDir / currentUri / targetUri / cwd field exists.
-
Server proxy injects identity but not the directory.
openviking/server/routers/bot.py, _attach_openviking_connection attaches
account_id / user_id / agent_id / role / api_key / server_url, but nothing
about the user-selected directory.
-
Backend ChatRequest / OpenVikingConnection have no directory field.
bot/vikingbot/channels/openapi_models.py — OpenVikingConnection only
carries identity fields; ChatRequest likewise has none.
-
Agent context injects only the sandbox cwd, not the selected directory.
bot/vikingbot/agent/loop.py builds ContextBuilder from
sandbox_manager.get_workspace_path(session_key); build_system_prompt
(bot/vikingbot/agent/context.py) injects ## Sandbox Environment with
sandbox_cwd — a per-session FUSE mount path derived from session_key, which
has no relation to the tree node the user clicked.
Impact
- The agent and the resource tree behave as two disconnected modules. Asking
"list the current directory" / "summarize this directory" after selecting a
node does not ground the answer in that directory; the LLM often picks the
wrong tool (list_dir over the sandbox) or lists viking:// root.
- The empty-state copy in the Agent panel promises "Agent actions sync with the
tree on the left" (and the reverse direction is tracked separately in
bug-report-agent-directory-sync.md), but neither direction is implemented.
This issue covers the tree → agent direction.
Repro
1. Open the Studio playground (e.g. http://<host>:1933/studio/playground).
2. In the left resource tree, click a directory, e.g.
viking://resources/xxxxxxxxxx/
(the URL becomes /studio/playground?...&uri=viking://resources/...%2F).
3. Switch to the Agent panel and send: "list the current directory".
4. Observe the agent does NOT call openviking_list on the selected directory.
Steps to Reproduce
- Start the server with
--with-bot and open the Studio playground.
- In the left resource tree, select a directory node. The browser URL updates to
/studio/playground?...&uri=viking://resources/<dir>/.
- Switch to the Agent panel and send a directory-scoped question, e.g.
list the current directory or summarize this directory.
- Observe the agent's tool calls: it does not pass the selected URI to
openviking_list. It either calls list_dir (sandbox filesystem) or
openviking_list with the default viking:// root.
Expected Behavior
When the user has selected a directory in the tree, the agent should be aware of
it and ground directory-scoped requests on that URI:
- Selected:
viking://resources/5th-gen-amd-epyc-processor-architecture-w_ae3f95e9/
- User sends:
list the current directory
- Expected: agent calls
openviking_list({"uri": "viking://resources/5th-gen-amd-epyc-processor-architecture-w_ae3f95e9/"})
- Actual: agent calls
list_dir (sandbox) or openviking_list({"uri": "viking://"}) (root)
When the request is unrelated to the selected directory, the agent should ignore
it and answer normally.
Actual Behavior
The agent never receives the selected directory. The /bot/v1/chat/stream
request body is {message, session_id, user_id, stream} — no directory field —
so the LLM has no signal about which tree node the user is viewing and defaults
to the sandbox cwd or the knowledge-base root.
Root causes (see Defects above):
route.tsx does not pass currentUri to AgentPanel.
BotChatRequest has no directory field, so sendChatStream cannot send one.
_attach_openviking_connection injects only identity.
OpenVikingConnection / ChatRequest have no directory field.
build_system_prompt injects only sandbox_cwd, not the selected directory.
Minimal Reproducible Example
# The bot chat request body that the Studio playground actually sends
# (captured from /bot/v1/chat/stream). There is no field for the selected dir.
import json
body = {
"message": "list the current directory",
"session_id": "<sid>",
"user_id": "<uid>",
"stream": True,
}
print(json.dumps(body, indent=2))
# {"message": "...", "session_id": "...", "user_id": "...", "stream": true}
# ^ no selectedDir / currentUri / targetUri anywhere
# Backend: the connection forwarded to vikingbot also lacks any directory field.
# openviking/server/routers/bot.py :: _build_openviking_connection
connection = {
"account_id": ctx.user.account_id,
"user_id": ctx.user.user_id,
"agent_id": "web-playground",
"role": ...,
"api_key_type": ...,
"server_url": ...,
"namespace_policy": ...,
}
# ^ no current_dir / selected_dir
Error Logs
No exception is raised. The agent simply does not ground on the selected
directory — a silent functional gap, not a crash.
Evidence (after a user clicked viking://resources/<dir>/ and sent a message):
- /bot/v1/chat/stream request body: {message, session_id, user_id, stream}
(no directory field)
- vikingbot.log shows:
[TOOL_CALL]: openviking_list({"uri": "viking://"}) # root, not the selected dir
OpenViking Version
0.4.5 (also reproduced on main @ 2846bb6, 2026-06-26)
Python Version
3.10
Operating System
Linux
Model Backend
Other
Additional Context
Suggested fix (minimal, no frontend rebuild needed)
The Studio playground already mirrors the selected directory into the URL query
(?uri=viking://..., via syncSearch in route.tsx). For same-origin chat
requests the browser sends that full URL in the Referer header, so the server
proxy can recover the selected directory without any frontend change:
openviking/server/routers/bot.py: in _attach_openviking_connection, parse
the uri= query from request.headers["referer"] and attach it to the
forwarded openviking_connection (e.g. as current_dir).
bot/vikingbot/channels/openapi_models.py: add current_dir: Optional[str]
to OpenVikingConnection.
bot/vikingbot/agent/loop.py: read current_dir from the connection and pass
it to ContextBuilder.
bot/vikingbot/agent/context.py: in build_system_prompt, inject a
## User-Selected Directory section that tells the LLM to prefer that URI as
the openviking_list / openviking_search argument when the request
concerns it, and to ignore it otherwise.
This is the reverse direction of the empty-state promise tracked in
bug-report-agent-directory-sync.md (that one is agent → tree; this one is
tree → agent). A proper frontend fix (passing currentUri to AgentPanel and
adding a field to BotChatRequest) is more correct long-term, but the
Referer-based approach above works today with backend-only changes.
Reference implementation
A working, verified implementation of the fix above is attached for the
maintainers' reference. It is additive only — 4 files, +71 lines, 0 deletions,
no existing logic touched — so it is low-risk to review and can serve as the
base for a PR.
Changed files (additive, against v0.4.5):
| File |
+lines |
Change |
openviking/server/routers/bot.py |
+30 |
_extract_current_dir + _stamp_current_dir, inject into openviking_connection |
vikingbot/channels/openapi_models.py |
+6 |
current_dir: Optional[str] field on OpenVikingConnection |
vikingbot/agent/loop.py |
+16 |
read current_dir from connection, pass to ContextBuilder |
vikingbot/agent/context.py |
+19 |
ContextBuilder accepts current_dir; build_system_prompt injects ## User-Selected Directory |
Unified diff (source_diff.patch, git a/ b/ style, applies cleanly on
v0.4.5 with patch -p1):
--- a/vikingbot/agent/context.py
+++ b/vikingbot/agent/context.py
@@ -38,6 +38,7 @@
is_group_chat: bool = False,
eval: bool = False,
openviking_connection: dict[str, Any] | None = None,
+ current_dir: str | None = None,
):
self.workspace = workspace
self._templates_ensured = False
@@ -49,6 +50,7 @@
self._is_group_chat = is_group_chat
self._eval = eval
self._openviking_connection = openviking_connection
+ self._current_dir = (current_dir or "").strip() or None
self.latest_relevant_memories: str | None = None
@@ -125,6 +127,24 @@
f"## Sandbox Environment\n\n...`{sandbox_cwd}`..."
)
+ # User-selected directory (forwarded by the Studio proxy from the tree
+ # node the user is currently viewing). Strong guidance: when the user's
+ # intent involves that directory, prefer it as the target URI for
+ # openviking_list / openviking_search so answers are grounded in what
+ # the user is looking at.
+ if self._current_dir:
+ parts.append(
+ f"## User-Selected Directory\n\n"
+ f"The user is currently viewing this directory in the Studio tree:\n"
+ f"`{self._current_dir}`\n\n"
+ f"When the user's request concerns this directory (e.g. listing, "
+ f"summarizing, or finding documents under it), prefer passing this "
+ f"URI as the `uri` argument to `openviking_list`, or as the "
+ f"`target_uri` for `openviking_search`, instead of defaulting to the "
+ f"knowledge-base root. If the request is unrelated to this directory, "
+ f"ignore it and answer normally."
+ )
+
# Bootstrap files
--- a/vikingbot/agent/loop.py
+++ b/vikingbot/agent/loop.py
@@ -931,6 +947,8 @@
if not isinstance(openviking_connection, dict):
openviking_connection = None
msg.openviking_connection = openviking_connection
+ # User-selected directory forwarded by the Studio proxy (if any).
+ current_dir = openviking_connection.get("current_dir") if openviking_connection else None
profile_user_list = []
@@ -1082,6 +1100,7 @@
is_group_chat=is_group_chat,
eval=self._eval,
openviking_connection=openviking_connection,
+ current_dir=current_dir,
)
--- a/openviking/server/routers/bot.py
+++ b/openviking/server/routers/bot.py
@@ -93,6 +93,30 @@
+def _extract_current_dir(request: Request) -> str:
+ """Extract the user-selected directory URI from the browser request.
+
+ The Studio playground mirrors its selected tree node into the URL query
+ (``?uri=viking://resources/.../``). The browser sends that full URL in the
+ ``Referer`` header for same-origin chat requests, so we can recover the
+ directory the user is currently looking at — without any frontend change —
+ and forward it to vikingbot so the agent can act on it.
+
+ Returns the URI string when the selected entry is a directory, else ``""``.
+ """
+ from urllib.parse import urlparse, parse_qs
+
+ for src in (request.headers.get("referer"), str(request.url)):
+ if not src or "uri=" not in src:
+ continue
+ uri = parse_qs(urlparse(src).query).get("uri", [""])[0]
+ # Only forward directory selections (trailing slash). File URIs are
+ # ignored so the agent is not biased toward a single file.
+ if uri and uri.endswith("/"):
+ return uri
+ return ""
+
+
def _attach_openviking_connection(
...
+def _stamp_current_dir(enriched: dict, current_dir: str) -> None:
+ """Attach the user-selected directory to the forwarded connection dict."""
+ if not current_dir:
+ return
+ conn = enriched.get("openviking_connection")
+ if isinstance(conn, dict):
+ conn["current_dir"] = current_dir
@@ -131,9 +158,19 @@
enriched = dict(body)
+ current_dir = _extract_current_dir(request)
api_key = _extract_forward_api_key(request)
...
# _stamp_current_dir(enriched, current_dir) called before each return path
+ enriched["openviking_connection"] = _build_openviking_connection(...)
+ _stamp_current_dir(enriched, current_dir)
+ return enriched
--- a/vikingbot/channels/openapi_models.py
+++ b/vikingbot/channels/openapi_models.py
@@ -62,6 +62,12 @@
server_url: Optional[str] = Field(default=None, description="OpenViking server URL")
+ current_dir: Optional[str] = Field(
+ default=None,
+ description="User-selected directory URI (viking://...) forwarded by the "
+ "Studio proxy so the agent can ground its answers on the tree node the "
+ "user is currently viewing.",
+ )
The full source_diff.patch and the four patched source files (preserving the
repo's directory structure under patched_sources/) are included in the
attached archive bot_dir_sync_patch.zip for convenience.
Verified working
The approach above was implemented and verified locally on v0.4.5: after
selecting a directory and sending "list the current directory", the agent
correctly calls openviking_list with the selected URI; unrelated questions
("what memories do you have") ignore the directory and answer normally.
Related
- Reverse direction (agent → tree,
viking:// link rendering): separate issue.
bot_dir_sync_patch.zip
Bug Description
Summary
In the Web Studio playground, the bot/agent has no way to know which directory the
user has selected in the left-side resource tree. The tree selection
(
currentUri, mirrored into the URL as?uri=viking://...) is dropped at theAgentPanelboundary — it is never sent to the bot chat endpoint, so the agentalways answers from the knowledge-base root (or the per-session sandbox cwd)
instead of the directory the user is currently looking at.
Defects (verified against
main)The full request path carries no directory field end-to-end:
Frontend builds the request without the directory.
web-studio/src/routes/playground/route.tsxholdscurrentUriin state andpasses it to
TerminalPanel(L842), but not toAgentPanel(L851):AgentPanel.send(agent-panel.tsx:169) only forwards the message text:The chat request body has no directory field.
web-studio/types/ov-server/bot/v1/chat.ts:No
selectedDir/currentUri/targetUri/cwdfield exists.Server proxy injects identity but not the directory.
openviking/server/routers/bot.py,_attach_openviking_connectionattachesaccount_id / user_id / agent_id / role / api_key / server_url, but nothingabout the user-selected directory.
Backend
ChatRequest/OpenVikingConnectionhave no directory field.bot/vikingbot/channels/openapi_models.py—OpenVikingConnectiononlycarries identity fields;
ChatRequestlikewise has none.Agent context injects only the sandbox cwd, not the selected directory.
bot/vikingbot/agent/loop.pybuildsContextBuilderfromsandbox_manager.get_workspace_path(session_key);build_system_prompt(
bot/vikingbot/agent/context.py) injects## Sandbox Environmentwithsandbox_cwd— a per-session FUSE mount path derived fromsession_key, whichhas no relation to the tree node the user clicked.
Impact
"list the current directory" / "summarize this directory" after selecting a
node does not ground the answer in that directory; the LLM often picks the
wrong tool (
list_dirover the sandbox) or listsviking://root.tree on the left" (and the reverse direction is tracked separately in
bug-report-agent-directory-sync.md), but neither direction is implemented.This issue covers the tree → agent direction.
Repro
Steps to Reproduce
--with-botand open the Studio playground./studio/playground?...&uri=viking://resources/<dir>/.list the current directoryorsummarize this directory.openviking_list. It either callslist_dir(sandbox filesystem) oropenviking_listwith the defaultviking://root.Expected Behavior
When the user has selected a directory in the tree, the agent should be aware of
it and ground directory-scoped requests on that URI:
viking://resources/5th-gen-amd-epyc-processor-architecture-w_ae3f95e9/list the current directoryopenviking_list({"uri": "viking://resources/5th-gen-amd-epyc-processor-architecture-w_ae3f95e9/"})list_dir(sandbox) oropenviking_list({"uri": "viking://"})(root)When the request is unrelated to the selected directory, the agent should ignore
it and answer normally.
Actual Behavior
The agent never receives the selected directory. The
/bot/v1/chat/streamrequest body is
{message, session_id, user_id, stream}— no directory field —so the LLM has no signal about which tree node the user is viewing and defaults
to the sandbox cwd or the knowledge-base root.
Root causes (see Defects above):
route.tsxdoes not passcurrentUritoAgentPanel.BotChatRequesthas no directory field, sosendChatStreamcannot send one._attach_openviking_connectioninjects only identity.OpenVikingConnection/ChatRequesthave no directory field.build_system_promptinjects onlysandbox_cwd, not the selected directory.Minimal Reproducible Example
Error Logs
OpenViking Version
0.4.5 (also reproduced on
main@ 2846bb6, 2026-06-26)Python Version
3.10
Operating System
Linux
Model Backend
Other
Additional Context
Suggested fix (minimal, no frontend rebuild needed)
The Studio playground already mirrors the selected directory into the URL query
(
?uri=viking://..., viasyncSearchinroute.tsx). For same-origin chatrequests the browser sends that full URL in the
Refererheader, so the serverproxy can recover the selected directory without any frontend change:
openviking/server/routers/bot.py: in_attach_openviking_connection, parsethe
uri=query fromrequest.headers["referer"]and attach it to theforwarded
openviking_connection(e.g. ascurrent_dir).bot/vikingbot/channels/openapi_models.py: addcurrent_dir: Optional[str]to
OpenVikingConnection.bot/vikingbot/agent/loop.py: readcurrent_dirfrom the connection and passit to
ContextBuilder.bot/vikingbot/agent/context.py: inbuild_system_prompt, inject a## User-Selected Directorysection that tells the LLM to prefer that URI asthe
openviking_list/openviking_searchargument when the requestconcerns it, and to ignore it otherwise.
This is the reverse direction of the empty-state promise tracked in
bug-report-agent-directory-sync.md(that one is agent → tree; this one istree → agent). A proper frontend fix (passing
currentUritoAgentPanelandadding a field to
BotChatRequest) is more correct long-term, but theReferer-based approach above works today with backend-only changes.
Reference implementation
A working, verified implementation of the fix above is attached for the
maintainers' reference. It is additive only — 4 files, +71 lines, 0 deletions,
no existing logic touched — so it is low-risk to review and can serve as the
base for a PR.
Changed files (additive, against v0.4.5):
openviking/server/routers/bot.py_extract_current_dir+_stamp_current_dir, inject intoopenviking_connectionvikingbot/channels/openapi_models.pycurrent_dir: Optional[str]field onOpenVikingConnectionvikingbot/agent/loop.pycurrent_dirfrom connection, pass toContextBuildervikingbot/agent/context.pyContextBuilderacceptscurrent_dir;build_system_promptinjects## User-Selected DirectoryUnified diff (
source_diff.patch, gita/b/style, applies cleanly onv0.4.5 with
patch -p1):Verified working
The approach above was implemented and verified locally on v0.4.5: after
selecting a directory and sending "list the current directory", the agent
correctly calls
openviking_listwith the selected URI; unrelated questions("what memories do you have") ignore the directory and answer normally.
Related
viking://link rendering): separate issue.bot_dir_sync_patch.zip