* feat(plugin): upload debug plugin to LangBot Space with optional GitHub sync
- SDK: reusable in-memory .lbpkg packaging + git sync helper (local git by default, optional repo/token override)
- routing: space-upload config/upload/git-sync endpoints; debug plugins resolved via OSS bridge without touching get_plugin_info
- frontend: debug-card entry + upload dialog with multi-language metadata, icon upload and changelog
- i18n across 8 locales; operation-trace + action allowlist entries
* fix(plugin-space-upload): stable dialog width, single-line locale tabs, broader error surfacing
- fixed dialog width so toggling the GitHub section no longer resizes it
- language tabs render on one line (horizontal scroll when needed)
- surface the real upload/git error message (msg field) instead of [object Object]
- hint that a missing GitHub repo is auto-created when a token is provided
* fix(plugin-space-upload): narrow dialog and reset sm:max-w clamp
The base DialogContent applies sm:max-w-lg at the sm breakpoint, which overrode the unprefixed max-w-none and clamped the upload card to 32rem on desktop. Set an explicit sm:max-width and size the card to ~36rem. Locale tabs now wrap instead of scrolling horizontally.
* fix
* fix(plugin-space-upload): address CI and UX feedback
- narrow the upload dialog to ~36rem and reset the sm:max-w clamp
- guide the publisher to the Space review page after a successful upload
- drop the redundant null comparison flagged by code quality
- update the operation-action expectation and Space/LangBot Account wording
- ruff format the touched API modules
* feat(plugin-space-upload): public repo, editable author/license, card red dot
* fix(market): replace yellow download count with update available badge
* style(market): unify installed and update indicators into badges
* feat(plugin): redesign debug card banner, style debug icon, and prioritize debug plugins
* feat(plugin-space-upload): simplify upload dialog layout, add creator verification and market redirection
* fix(i18n): use LangBot Account terminology in Space upload dialog
Co-authored-by: multica-agent <[email protected]>
* style(i18n): reflow reworded locale strings for prettier
Co-authored-by: multica-agent <[email protected]>
* test(utils): stop timing out the pattern-count cap test on loaded runners
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Hyu <[email protected]>
Co-authored-by: RockChinQ <[email protected]>
Co-authored-by: multica-agent <[email protected]>
- Add migration 0042 to enable incremental vacuum with verified backup and restore
- Enable incremental vacuum before creating tables in new SQLite databases
- Adapt reclamation batches to elapsed time and lock contention
- Cover migration recovery, WAL mode, and concurrent writes with regression tests
- Refresh images in inputs, outputs, deliveries, and conversation history
- Reuse OneBot refresh logic without rewriting persisted records
- Cover existing image URLs across agent and pipeline execution views
- Cache NapCat rkeys by type and refresh only when their TTL expires
- Renew image URLs through get_msg for other OneBot v11 implementations
- Refresh session views, Runner history, and each model request attempt
- Isolate caches by connection without downloading images or rewriting history
* fix(space): keep dedicated-environment Space login working
A dedicated environment runs the OSS runtime with system.edition=cloud. The
process-level 'LangBot Models' compatibility provider is part of the OSS
single-Workspace contract, but write_space_model_providers() skipped creating it
for every non-community edition. The Space OAuth callback then unconditionally
rewarded the provider row by api key, so provider_service raised
WorkspaceNotFoundError('Provider not found') and the whole callback returned 404
-- after the Account and its Workspace had already been created. Every retry
failed the same way, making dedicated-environment login impossible on 4.11.x.
Gate the compatibility provider on the deployment shape instead of the edition
(genuine multi-tenant Cloud runs in CLOUD_RUNTIME persistence and never reaches
this path), and treat a missing provider row as a model-routing warning rather
than an authentication failure so a data anomaly can never block login.
* fix(agent): bind the first model to the 4.11 runner config
create_llm_model() still wrote the auto-selected model into the pre-4.11
ai['local-agent'] container. On a 4.11 Workspace that key does not exist, so the
assignment raised KeyError and POST /provider/models/llm returned 500 the first
time a user added a model -- and the default pipeline kept primary='', so every
Agent run failed with 'No authorized model for local-agent'.
Delegate to RunnerDefaultConfigService, which resolves the selected runner and
writes the schema-defined model selector into ai.runner_config[<runner id>].
Also default the NOT NULL description column on pipeline and bot creation:
omitting an optional description returned a 500 from the constraint instead of
creating the resource.
* fix(agent): keep seeding the model for pipelines that never migrated
set_pipeline_llm_model_if_empty() returns False when a pipeline has no 4.11
runner selected -- that is exactly the un-migrated shape, which still carries
ai.local-agent. Seed that container, but only when it exists, so the legacy path
keeps its old behaviour without the KeyError.
* fix(platform): enforce credential-session TTL on a timer, not on traffic
The four adapter registration flows (Lark one-click app, DingTalk device flow,
WeCom bot, QQ Official bind) each park a short-lived secret in an in-memory
session dict until the caller persists it: app_secret, client_secret, or the bot
secret.
Each store had a TTL and a cleanup function, but the cleanup only ran at the top
of a route handler. An abandoned flow therefore kept its secret in memory until
some *other* request happened to arrive, which for a quiet instance can be far
past the validity window (15 min for Lark, 10 for DingTalk, 5 for WeCom/QQ).
Changes:
- Move the sweep to module level as _sweep_credential_sessions(stores) so it is
directly testable, and have it scrub app_secret/client_secret/secret as it
evicts, so an evicted session dict no longer retains a readable credential.
- Add a background sweeper task started from the router group's initialize(),
running every 60s, so the TTL holds regardless of traffic.
- Keep the per-route cleanups: they make eviction immediate under load.
Tests: 9 new cases (expiry, fresh sessions survive, secret scrubbing, per-store
TTL, cancelling a pending handshake task, not cancelling a finished one,
malformed session without created_at, multiple evictions, empty stores).
Co-authored-by: multica-agent <[email protected]>
* fix(platform): tolerate a task manager without create_task when starting the sweeper
The sweeper is started from the adapter router group's initialize(). Some
embeddings of the app provide a task manager that only exposes create_user_task,
so an unconditional create_task broke those harnesses. Fall back to the per-route
cleanups when create_task is unavailable.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>
* LANG-145: bind WebAuthn to server config and make passkeys OSS-only
Two problems in the passkey implementation:
1. The RP ID and expected origin came from the request: a JSON 'origin' field,
then the Origin/Referer headers, then the request URL. WebAuthn's
anti-phishing guarantee depends on values the server chose; trusting the
request lets a sibling host under the same registrable domain obtain a valid
assertion for a victim's credential. Resolution now follows the same rule the
OAuth and invitation links already use: api.webui_url, then
api.webhook_prefix, then the configured port on loopback.
2. Passkeys are now an OSS-only feature. Cloud deployments authenticate through
their own account service, so every passkey route (register options/verify,
auth options/verify, list, rename, delete) refuses with
edition_unsupported, and /account-info advertises neither passkey_login_enabled
nor passkey_supported. Both cloud flavours are covered: system.edition ==
'cloud' and deployment.mode == 'cloud'.
Tests:
- New unit suite for the edition matrix and for origin resolution (including
that client-supplied origin/Origin/Referer are ignored).
- The three 'reverse proxy scenario' tests asserted the old, unsafe behaviour
(server trusting the request). They now assert the server-config contract.
- test_smoke cloud capabilities expectation updated to passkey_* = false.
Co-authored-by: multica-agent <[email protected]>
* style(api): drop the typing import left unused by the WebAuthn origin change
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>
Cloud deployments need a way to tell readers what is happening to the
instance they are using (maintenance windows, service notices) without
shipping a release per notice.
Add a cloud.banner config block (switch, tone, dismissal, link targets and
per-language copy) and expose the resolved banner on the unauthenticated
/api/v1/system/info payload so the WebUI can render it before login. The
banner resolves to null on OSS deployments, so a self-hosted copy of
config.yaml never shows managed-service copy. Copy is plain text; only
absolute http(s) link targets are rendered.
The WebUI renders it once from the root layout, offsets the fixed app shell
and the wizard through --cloud-banner-height, and keeps a dismissal bound to
the notice it dismissed so new copy reappears.
Co-authored-by: multica-agent <[email protected]>
Apply group response rules before busy-session steering. Initialize usable private-message and group-mention routes for new bots, including Wizard-created pipelines, and preserve member-configured rules across saves and retries.
Fix empty pipeline configuration, refresh the processor sidebar after automatic creation, and show creation feedback. Align routing regressions and use Node 24 for the browser CI job.
Correct the task's erroneous agent attribution while preserving every code tree and the original human contribution.
Co-authored-by: multica-agent <[email protected]>
Count and paginate lightweight records, then load payloads only for the
selected page to avoid materializing large historical messages.
Add regression coverage for oversized history, ingress deduplication,
stable pagination, and PostgreSQL tenant isolation.
- Maintain counts and display snapshots in source transactions with replay-safe deduplication.
- Read session lists exclusively from summaries and remove execution-only filters.
- Automatically initialize historical summaries in bounded, resumable batches during OSS startup.
- Add behavioral contracts covering accounting, retention, query cost and PostgreSQL concurrency.
Verified with the required session_summary suite and real container upgrade startup.
- Reject structured inline images on ORM and Core writes
- Route plugin run results through the media-aware journal
- Redact legacy inline images before merging run metadata
- Cover storage contracts and regressions on SQLite and PostgreSQL
- Apply media retention to inline images while preserving text and URLs
- Persist per-workspace cursors atomically with payload updates
- Bound candidate scans and verify event ownership before loading payloads
- Add cleanup indexes and regression tests, and fix Windows path assertions
- Externalize inline images in messages and run events before persistence
- Keep snapshots, transcripts, and metadata free of inline payloads
- Serialize messages after externalization to reduce peak memory usage
- Preserve live messages, URL-only images, and SDK image retrieval behavior
- Add regression tests for deduplication, redaction, storage failures, and serialization order
- Load overview first and fetch secondary sections when expanded
- Add a covering index and select conversation IDs before loading page payloads
- Preserve full-detail API compatibility and add pagination and lazy-loading tests
Exclude message_content and variables from the message source used by
conversation session summaries. These fields were carried through window
sorting even though the resulting summaries did not use them.
Preserve complete payloads for message detail queries. Add regression
coverage for summary counts, session metadata, and full message retrieval.
Require a valid owner and unexpired lease for pending Pipeline messages
to appear as active tasks. Exclude historical ingress records without
rewriting their stored status.
Project only the fields needed by snapshots, skip linkage queries for empty
results, and resolve linked runs from selected message IDs instead of a
cross-table OR join that scans historical messages.
Cover lease filtering, terminal transitions, Workspace isolation, payload
exclusion, and indexed lookup plans with regression tests.
- Expose the workspace's purchased plan revision from the entitlement
snapshot.
- Stack the purchased revision card in front of the latest revision in
the plan comparison dialog; both cards share the same layout, and the
purchased card's copy and prices come from the Space revision endpoint
while benefits stay on the entitlement snapshot.
- Add a cancel-first tooltip for subscribing to the newer revision.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Coerce/default/drop invalid or unknown legacy fields with warnings instead of blocking
- Never block on LocalAgent model; prune dangling KB/tool/MCP references
- Mark custom (non-builtin) 4.10 runners as unsupported_runner instead of failing
- Pin runner plugins to current marketplace versions (0.2.x); fall back to latest when a pin is withdrawn
- Raise plugin install/upgrade timeout to 600s for slow dependency installs
- Assistant configure_pipeline writes the 4.11 runner shape
Co-Authored-By: Claude Opus 5.5 <[email protected]>