A hardening + finalization pass over the agent-first CLI: correctness fixes,
richer machine-readable signals, flag/naming consistency, and a symmetric
config surface. Pre-1.0, so it includes breaking renames.
Correctness:
- agent update: resolve/validate --model/--rerank-model (was storing a bogus
name verbatim, corrupting config.model_id).
- doctor: honor WEKNORA_HOST / WEKNORA_API_KEY (headless path no longer reports
"no host configured").
- session ask / MCP session_ask: text answer was empty on non-TTY — the agent
stream sets Done=true on an intermediate agent_query frame before the answer,
and AgentAccumulator treated the first Done as terminal. Terminate on the
`complete` event (new sdk.AgentResponseTypeComplete), not a per-frame Done.
- batch exit codes: any per-item failure collapses to operation.failed (exit 1),
including `doc upload --recursive` partial failures — a permanent per-file
failure (e.g. a duplicate) no longer surfaces as a retryable exit 7 an agent
would loop on; per-item typed errors stay in the envelope.
Agent-first signals & discovery:
- error.exit_code in the envelope (type + exit_code disambiguate the
input.invalid_argument exit-2-vs-5 split in one JSON read).
- meta.hint on empty content search and on draft doc create; doc wait fails fast
on a never-parsing draft instead of hanging to --timeout.
- retrieval-readiness is visible in the natural flow: kb status / kb check emit
retrieval_ready, and kb create hints the fix when no embedding model is bound
— an unconfigured KB no longer looks silently healthy.
- schema contract completeness: every leaf declares output + >=1 example
(drift-guarded); output strings match the meta actually emitted; chunk list
and search docs now emit meta.total_count (both previously dropped it).
- schema tolerates a quoted multi-word command label; zero-state auth — and
`link` with no profile — point at profile setup / the headless WEKNORA_KB_ID
path instead of looping on `auth login`.
- id-addressed reads tolerate a redundant --kb (doc view/wait, chunk list/view
accept and ignore it, declared in schema) so a carried-over --kb doesn't
exit 2; streaming commands warn that --jq does not apply to an NDJSON stream.
- keep JSON-always as the default; --jq hints spell out the .data path.
Consistency & gating:
- doc create: drop the deprecated --name alias (--title only; pre-1.0 break).
- chunk list --limit aligned to 1..10000; model list --limit/-L with
has_more/total_count; api write-gates -X PUT/PATCH (exit 10); skills install
expands a leading ~.
- docs corrected: search docs / doc list --keyword help is case-insensitive
(server does LOWER LIKE); AGENTS.md risk-action list (no phantom kb.init; add
model.update / kb.config.set) and batch example (failed item carries `error`);
session resume --message id comes from `message list`, not the stream.
- auth/profile ergonomics: env credentials are now first-class — `auth token`
prints the active WEKNORA_API_KEY / WEKNORA_TOKEN, and auth login/logout/refresh
give an env-aware message instead of looping on "run auth login". `auth logout`
clears credentials but keeps the profile registered (host preserved for
re-login); deleting a profile is `profile remove`'s job (clean logout/remove
separation, matching gh / lark).
Config surface (symmetric read/write, in-place model edits):
- kb config now returns a secret-free KBModelConfigView (was {}); `kb config`
reads, new `kb config set` writes; `kb init` removed (misnomer).
- kb create --chat-model: retrieval-ready in one step.
- model update: edit a model in place (id preserved, references survive) —
rotate --api-key-stdin, change base-url / display-name / etc.
- session continue-stream renamed to session resume.
Docs: AGENTS.md is the single wire-contract source; CHANGELOG slimmed; stale
kb-init / continue-stream references removed; skill wire-vocab guard extended.
AGENTS.md / weknora-shared SKILL document retrieval_ready (a KB needs an
embedding model to be searchable), that --jq does not apply to NDJSON
streams, and the env-credential-first auth path; the KB quickstart example
now creates a retrieval-ready KB.
Make document keyword search case-insensitive (LOWER(...) LIKE LOWER(?)) in the
list filter and SearchKnowledge / SearchKnowledgeInScopes paths so a query and
its differently-cased variant return the same results.
Classify not-found consistently as HTTP 404 (not 500) so clients read a
permanent resource.not_found instead of a retryable 5xx: map the document /
chunk not-found sentinels in the KB-access middleware (typed ErrChunkNotFound
via errors.Is), and return 404 for a missing message on the continue-stream and
message-delete endpoints.
Adds the client-library surface the agent-first CLI builds on:
- named server error-code constants + typed *APIError from non-2xx responses,
- MessageSearchMode / AgentMode / KBSelectionMode and model type/source
enumerators so the CLI validates flags against the server vocabulary,
- SetKBModelConfig for binding embedding/chat models to a knowledge base.
Restore onVisibleChange in card menu close handler so moveMenuMode resets correctly, and align list view popup chrome with card view via shared card-more styles.
- Add DocumentActionMenu.vue: shared component for document context menu
items (edit, view-trace, rebuild, cancel-parse, move, batch-manage,
delete) with consistent visibility conditions
- Add DocumentCardView.vue: standalone card/grid view component extracted
from KnowledgeBase.vue inline template, including hover popover, tag
chips, move sub-flow, and status display
- Update DocumentListView.vue: use shared DocumentActionMenu, add
canMutateKnowledge gating for move/batch-manage, add view-trace and
batch-manage menu items, add move sub-flow support (targets/confirm)
- Update KnowledgeBase.vue: use DocumentCardView component, add
handleCardAction bridge, remove unused helpers and imports
Both card and list views now render identical operation menus from a
single source of truth.
Validate parsed WeKnora_settings is a plain object, deep-clone defaults
on reset, and reuse safeSetItem for reconcile write-back. Extracts load
logic into settingsStorage with unit tests for corrupt and non-object JSON.
- Wrap JSON.parse in loadAndReconcileSettings() with try/catch, fall
back to defaultSettings and remove corrupted WeKnora_settings on
parse failure instead of crashing the app
- Add try/catch around localStorage.setItem write-back for quota
exceeded protection
- Register app.config.errorHandler to catch unhandled Vue errors and
prevent silent component tree crashes
Follow up on #1740/#1741 by typing terminal SSE errors in the SDK,
classifying them as server.error in the CLI/MCP, marking Projector done
on error frames, attaching session_id to MCP stream failures, and
migrating the e2e chat step to the bounded JSON envelope.
Reject refresh attempts when the DB token type is not refresh_token,
and add service-layer tests for bearer refresh rejection, logout
revocation, and expired-token logout handling.
Record the active transport at run_stdio/run_sse/run_http startup so
create_knowledge_from_file path checks apply even when MCP_TRANSPORT
is not set in the environment.
Reject refresh JWTs in ValidateToken, revoke all user sessions on logout,
and restrict create_knowledge_from_file to allowed directories on network MCP transports.
Strip model base URLs from viewer-accessible initialization config
responses and merge redacted parser secrets before engine check overrides
so admin test flows keep working after masked GET responses.
Apply the same tenant/integration secret policy to auth login, OIDC
callback, switch-tenant, and invite-register payloads, and return masked
config objects from Admin-only tenant KV PUT handlers.
Prevent low-privileged tenant members from reading raw API keys and
infrastructure credentials via tenant profile, KV config, model/MCP/web
search listings, and KB initialization config responses.
Use SSRF-safe HTTP clients in chat pipeline web_fetch and Feishu/Yuque/Notion
connectors, validating tenant-supplied base_url values before outbound requests.
Use the SSRF-safe HTTP client for file_url downloads and add redirect-aware
guards to the docreader Playwright path, closing GHSA-44fp, GHSA-rv6v, and
GHSA-c7m6.
Gate wiki GET routes and initialization config reads with KBAccessRead,
and bind async task progress endpoints to the caller tenant via task ID
parsing. Closes cross-tenant read paths reported in GHSA-ph8f-4qwx-89rr
and related initialization/progress leaks.
Harden validateNode with default-deny semantics and PG17 SQL/JSON node
recursion so dangerous functions cannot hide inside JSON_SCALAR/JSON_VALUE.
Also recurse into FROM subqueries for DuckDB data_analysis and block
read_text/read_blob and related file-access functions.
IM channels cannot complete in-conversation OAuth prompts, so when an
MCP service needs authorization the reply now includes clickable
authorization URLs (when APP_EXTERNAL_URL and OAuth manager are
available) instead of only directing users to the admin console.
Restore logger config via t.Cleanup after t.Setenv so Yuque token tests
do not leak global state, add unit tests for MCP OAuth non-interactive
context helpers and IM auth notice formatting, and fix EmbedChatCore
indentation.
Some VLM models (e.g. certain vision models) only accept temperature=1.
Previously temperature was hardcoded to 0.1. Now reads from model's
ExtraConfig["temperature"] with fallback to default 0.1.
Usage: set ExtraConfig.temperature=1 in the VLM model configuration.
Remove redundant hero icons from the Chrome extension and Claw Skill
landing headers, and keep Claw CTA hover states on the lobster accent
instead of the default brand green.
Gate the integrations API tab and sidebar preview behind owner role,
matching Settings and the /api-principal-config backend guard, so users
without permission no longer see a 403 error when opening the tab.
Implement migration 000064 to introduce principal columns in MCP OAuth tokens, widen user_id in sessions, and add tenant API principal configuration. Remove previous migrations related to API principal config and user_id widening.
Wire embed visitor ID through chat and MCP OAuth flows, add embed i18n,
redesign API integration settings (signed-token docs, layout), and rename
the integrations tab to API Integration with a server icon.