* chore(web-studio): remove dead code (unreachable files and unused exports)
Static sweep of web-studio/src for code no module reaches from the
main.tsx / routeTree.gen.ts entry graph, plus exported symbols nothing
references anywhere (including their own file and tests).
Deleted files:
- src/router.tsx — superseded by the inline createRouter in main.tsx
- src/lib/sessions/generate-title.ts — never called
- src/lib/sessions/types/session.ts — re-export barrel nobody imports
- src/components/ui/{breadcrumb,combobox,context-menu,input-group,progress}.tsx
— shadcn primitives never added to any screen; re-addable via `shadcn add`
Removed unused exports (and the imports/constants they were the last
consumer of):
- lib/admin-options.ts: sortedAccounts
- lib/sessions/types/chat.ts: ChatState
- lib/sessions/types/message.ts: getTextContent, getToolParts, getContextParts
- routes/home/-lib/format.ts: formatDateKey, formatTimestamp
- routes/playground/-lib/types.ts: VikingEntryHandler
- routes/playground/-lib/utils.ts: buildBreadcrumbs
- routes/resources/-hooks/viking-fm.ts: usePrefetchVikingFsList, useVikingFind
- routes/resources/-lib/normalize.ts: sameUri, normalizeUriForDisplay
- routes/resources/-lib/upload.ts: getExtensionFromName
No behaviour change. `vite build` succeeds, `tsc --noEmit` output is
byte-identical to main, and `vitest run` shows the same pre-existing
19 failed files / 56 failed tests as main.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(web-studio): drop the unused pnpm lockfile
Nothing in the repo installs web-studio with pnpm: `make build-studio`
runs `npm ci && npm run build`, and both setup-node steps in
`.github/workflows/_build.yml` cache on `web-studio/package-lock.json`.
No workflow reads `pnpm-lock.yaml`.
The file had also drifted out of sync with package.json — 24 specifiers
missing — so `pnpm install --frozen-lockfile` failed outright, which only
ever hurt someone reaching for pnpm locally.
Also drops the `pnpm.onlyBuiltDependencies` field: pnpm 11 no longer
reads it and warns when it is present.
package-lock.json stays as the single source of truth.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
- Add rehype-raw and rehype-sanitize so raw HTML in Markdown renders while stripping scripts and other dangerous tags
- Give table cells full borders, center content both axes, and pass through colSpan / rowSpan
- Add file-preview-html tests covering raw HTML rendering, table attribute passthrough, and script stripping
Route internal viking:// Markdown targets through the existing resource navigator instead of the attachment download endpoint. Preserve download fallback behavior for previews without a navigation callback and add a click regression test.
Test: NODE_OPTIONS='--localstorage-file=/tmp/openviking-vitest-localstorage' pnpm test\nTest: pnpm build
* feat: support oidc and ldap auth
* feat: support oidc and ldap auth
* fix: remove heima partner, clean up auth docs, add web-studio unsupported auth banner
- Remove heima from partner list in README (en/zh/ja)
- Remove unsupported env var references (OPENVIKING_AUTH_MODE, OPENVIKING_USERNAME,
OPENVIKING_PASSWORD) from LDAP auth docs
- Remove temporary switch bash snippets from auth docs
- Fix ldap_password description
- Add web-studio unsupported-auth-mode banner for oidc/ldap servers
* fix: address OIDC/LDAP review comments on auth plugin design
Key changes driven by PR review:
- **Role mapping**: OIDC and LDAP external identities always resolve to
USER role. Removed map_role() calls and group_membership-based role
mapping. Admin access is gated by the root API key mechanism only.
- **LDAP credential extraction**: Removed query-parameter-based username/
password extraction (security concern — passwords in URLs can leak via
shell history, proxy logs, and monitoring). Clients must use Basic Auth
header or form data.
- **OIDC identifier sanitization**: Auth0 and other providers may include
characters like "|" in the `sub` claim. These are now replaced with "_"
to produce valid OpenViking user identifiers.
- **Dead code removal**: Removed _extract_groups, memberof_attribute,
require_root_api_key_for_admin, _initialize_api_key_manager, and
get_request_context_checks from both plugins since they are no longer
needed.
- **Docs**: Removed query-parameter curl example, memberof_attribute and
require_root_api_key_for_admin config references.
Co-authored-by: TRAE CLI <noreply@bytedance.com>
* feat: support oidc and ldap auth
* feat: support oidc and ldap auth
* fix(auth): bind lazy OIDC imports at module scope
---------
Co-authored-by: TRAE CLI <noreply@bytedance.com>
Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
* fix(feishu): surface permission errors clearly and keep users on page
Map Feishu/Lark API failures to typed OpenViking errors with actionable hints, and keep Web Studio from treating HTTP 403 permission denials as session logout.
* fix(feishu): simplify API error mapping
* refactor(feishu): inline API error mapping
---------
Co-authored-by: wugj <wugj@g-bits.com>
Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
In trusted mode, /health identity resolution requires X-OpenViking-Account
and X-OpenViking-User headers alongside X-API-Key. Without them, the
trusted auth plugin raises InvalidArgumentError, the exception is swallowed
by the health handler, and the response omits role/account_id/user_id —
so the studio falls back to connectionRole='unknown' and gates the admin
UI behind 'Usage/Audit 未初始化'.
Sending the headers is always safe: in api_key mode the server strips
them from the request scope; in trusted mode they are required.
Closes#2977 for the web-studio side.
* fix(web-studio): lazy-create playground sessions to prevent orphans
The AgentPanel used to call POST /sessions on every page load (via a
useEffect gated on botHealth.isSuccess), creating empty UUID sessions
even when the user never sent a message. These orphan sessions cluttered
the session list.
Fix: generate a client-side UUID as the initial sessionId instead of an
empty string. The session is lazily created on the backend by the first
addMessage call (POST /sessions/{id}/messages uses auto_create=True).
- Remove startSession() and its auto-create useEffect
- Always have a sessionId (createRandomUuid fallback)
- Simplify useChat params (persistMessages always true, no preview fallback)
- Remove dead isCreating UI branch
- Call onSessionChange on mount so the URL stays in sync
- Error retry button now uses handleNewSession()
Closes: orphan empty sessions created on playground page load
* feat(sessions): sort session list by filesystem modTime
- Backend: add mod_time to session list API response (from viking_fs.ls stat)
- Frontend: useSessionListByRecency sorts by mod_time instead of N detail fetches
- Frontend: sidebar and playground history use recency-sorted list
- Frontend: sortTreeEntries sorts directories by modTime descending
Reduces session list load from 1+N requests to 1 request.
When the Root API Key field holds an account-admin key (rather than a
root key), the studio kept using the previously selected / default
assumed account. An account-admin key is scoped to its own account, so
admin and data calls against any other account were rejected by the
server with "ADMIN can only manage account: <x>".
/health already resolves the presented key and echoes back its identity
(role + account_id + user_id). Read account_id alongside role and, when
the key resolves as an admin key, pin the assumed account to the admin
key's own account so admin/data calls target the right tenant. Root keys
are not account-scoped, so their account selection is left untouched.
Also relabel the field "Root API Key" -> "Root or Admin API Key" (en + zh)
to reflect that an account-admin key is accepted there too.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the connection setup with a clean three-field model plus a
dedicated User Management panel.
- Connection card is now exactly Server URL, Root API Key, User API Key.
The Root key is the sole control-plane credential (unlocks User
Management); the User key powers Playground / tenant data APIs and is
set via "Use as User API Key". Removes the duplicated account/user
dropdowns and the dual-purpose API-key field.
- Admin access is gated on the Root key only, never the User key.
- Stop normalizing the base URL on every keystroke (it collapsed
"http://" back to "http:"); normalize at request time instead.
- Disable autocapitalize/autocorrect/spellcheck on URL/key/id inputs
(the browser was capitalizing "http" -> "Http").
- Debounce field edits and key live behaviour off the committed
connection, so typing no longer re-probes / refetches per keystroke.
- Guard the account-filter effect against a render loop and add
keepPreviousData to the probe so adopting/rotating a key no longer
blanks the panel.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(web-studio): move playground actions into mobile drawer
* fix(web-studio): make mobile playground actions fullscreen
* docs: add web studio mobile screenshots
The push-OTP feature (mint an OTP in Studio to hand to an MCP client) was never
wired to a consumer: consume_otp had zero production callers and no endpoint or
grant ever redeemed an OTP. The 'full happy path' test actually exercised the
display_code flow, not OTP. So the sidebar footer's 'OAuth setup' entry minted a
code with nowhere to use it — dead, confusing UX.
Remove it end-to-end and repurpose the footer slot into an entry for the
cross-device verify page (enter the 6-char display_code), which previously had no
discoverable entry point in Studio.
Frontend:
- delete oauth-setup-dialog.tsx + /oauth/setup route (+ routeTree, i18n)
- extract CrossDeviceVerifyForm from verify.tsx; add CrossDeviceVerifyDialog
- footer 'OAuth verify' entry opens the verify dialog (desktop) / page (mobile)
Backend:
- drop issue_otp route + OTPRequest/OTPResponse, storage insert_otp/consume_otp,
oauth_config.otp_ttl_seconds, and the OTP-specific tests
- keep otp.py generate_otp (cross-device display_code) + hash_secret, the shared
_atomic_consume_code, and the oauth_codes.kind column
- convert the race/expiry/revoke/GC storage tests to auth-code rows
Docs: update 11-oauth, 06-mcp-integration, and the design doc to reflect removal.
Iterate the OAuth consent/verify pages to match the Studio Connection &
Identity UX: when the caller is root/admin in api_key mode, let them pick a
concrete account + user from dropdowns instead of pasting a raw API key. The
selected user's api_key becomes the Bearer that binds the OAuth grant.
This is required for root, not just nicer UX: the backend oauth-verify
rejects identities without a per-user key fingerprint, so root cannot
authorize as itself and must select a concrete user.
- IdentityPicker: add 'select' mode + optional directory prop (presentational)
- useIdentityDirectory: gate on api_key + root/admin, list accounts/users,
detect root vs account-admin via GET /accounts 403, resolve user keys
- consent.tsx / verify.tsx: default-mode effect (root -> select), keep select
payload in sync, soften the sign-in prompt
- Extract shared admin data layer (#/lib/admin) + AccountSelect/UserSelect
(#/components/identity-select) so settings and consent share one source
- i18n: add oauth.identityPicker.select* keys (en + zh-CN)
No backend changes.
* feat: save images in Markdown to vikingfs
* feat: save images in doc to vikingfs
* fix: change the image file reading operation to asynchronous.
* feat: support generating L0 and L1 from images in Markdown and importing them into vector indexes
* fix: add updating image links when modifying existing files; restrict accessible paths for image loading; revise the unit tests raised for images
* fix: 限制保存的图片所在目录,避免将其他目录下的内容加入 vikingfs;对markdown代码中的图片链接保持不变
---------
Co-authored-by: zhanghaoyu.la <zhanghaoyu.la@bytedance.com>
Co-authored-by: Qin Haojie <qinhaojie.exe@bytedance.com>
* fix: stabilize studio identity and streaming chat
* fix: hide unsupported studio terminal commands
* fix: remove unsupported terminal command copy
* fix: run selected terminal suggestion on enter
* fix: group supported terminal commands
* fix: add terminal quick start and history
* fix: scope session visibility by user
* fix: harden bot user scoping
* fix: forward request scoped bot identity
* fix: add terminal quick start translations
* fix: add terminal command group translations
* fix: simplify studio identity scoping
* fix: support api key copy on dev urls
* fix: stop passing agent id to ov http client
* fix: search follow-up memory questions
* Fix Studio admin and data API key handling
* Refine Studio connection identity UI
* Polish Studio header alignment
* Remove duplicate Playground directory header
* Show processing tasks in Playground context tree
* Fix Playground folder focus toggling
* Remove Context Management route
* feat(web-studio): pass create_parent param in resource upload
The backend supports auto-creating parent directories via create_parent,
but the frontend upload form never sent it. Add the parameter (default true)
with a checkbox in advanced options.
* fix(web-studio): improve file save performance and tree collapse behavior
Set wait=false for content write to skip blocking on vector indexing.
Reset expandedKeys on navigation to collapse tree to current directory,
and clear descendant expanded state on folder expand for cleaner UX.
* style(web-studio): enhance language switcher styling and active state indication
* refactor(web-studio): unify resource browser palette state
Refactor the resource browser palette so directory browsing, keyboard handling, and palette mode parsing have a single owner.
Changes:
- make DirBrowser a controlled view owned by FindPalette
- remove DirBrowser internal focusUri/startUri syncing
- remove internalNavRef and document-level keydown listener
- move palette keyboard handling to FindPalette container onKeyDown
- add explicit PaletteMode parsing for idle/search/dirBrowse states
- centralize directory browse query parsing and URI-to-query writing
- lift directory list loading and active cursor state into FindPalette
- add shared useListNavigation hook for active index movement
- debounce directory peek requests by 150ms
- debounce file stat preview requests by 150ms
- extract reusable ItemColumn list component
- export existing useDebouncedValue hook
- add scope reset/back navigation UI copy
This keeps the current browse directory, keyboard dispatch, and palette mode state in one place, avoiding the previous two-way sync loop between FindPalette and DirBrowser.
* refactor(web-studio): flatten DirBrowser detail pane into a switch
Replace the 6-way nested ternary in DirBrowser's right pane with an
explicit DetailView discriminated union computed in one useMemo, rendered
by a flat DetailPane switch (preview/loading/error/subdir/empty/idle).
Merge ItemColumn's onSelect/onSelectFile into a single onSelect(entry,
index); callers branch on entry.isDir. Behavior is unchanged except the
transient peek flash during the 150ms debounce window is gone, since all
directory states are now gated behind cursorItem.isDir.
* fix(web-studio): handle palette pane navigation focus
Use the clicked right-pane entry URI when navigating from the DirBrowser detail pane, instead of reusing the left-pane cursor item.
Clamp list navigation when visible entries change, and restore palette input focus after window focus or visibility changes so keyboard cursor handling remains active after switching apps.
* feat(web-studio): add onOpenFile prop to DirBrowser and FindPalette components
* fix(web-studio): prevent invalid dir scope confirmation and expandedKeys conflict
- Guard Enter in dirBrowse mode with dirListQuery.isSuccess so only
existing directories can be confirmed as search scope
- Remove handleToggle() from handleSelect to eliminate double-write
conflict on expandedKeys when clicking directory rows
- Add dirListQuery.isSuccess to handleKeyDown deps to fix stale closure
preventing scope confirmation in empty directories on cold cache