Context7 only re-indexes on its own popularity schedule, which is why the
existing /langbot-app/langbot-docs entry had already drifted before this
migration. Add the refresh call documented in Context7's GitHub Actions
integration so a docs change reaches the index on the same push.
The job is inert until a CONTEXT7_API_KEY secret exists, so it cannot fail
the build while the library registration is pending.
Co-Authored-By: Mika (Multica) <[email protected]>
Co-authored-by: multica-agent <[email protected]>
The migrated workflows required LANGBOT_WIKI_PUSH_TOKEN, which only exists in
the langbot-docs repository. The existing RELEASE_UPLOAD_GITHUB_TOKEN was
verified to authenticate as the owner with repo scope plus push/admin on
LangBot, langbot-docs, langbot-landing-page and the production fork, and it can
push the Wiki, so publication now falls back to it when the dedicated secret is
absent. Also retarget the cutover notes at the master branch.
Co-Authored-By: Mika (Multica) <[email protected]>
Co-authored-by: multica-agent <[email protected]>
LangBot's default branch is master, so the landing-submodule, wiki and blog
sync workflows would never have triggered and would have pushed to a
non-existent main. Also restore the wiki clone URL that was truncated in the
mirror copy of sync-github-wiki.yml.
Co-Authored-By: Mika (Multica) <[email protected]>
Co-authored-by: multica-agent <[email protected]>
Imported langbot-app/langbot-docs main (51dddcb3f) under docs/ with
`git subtree add`, keeping every original commit SHA and signature so the
mirror and landing-page submodule can fast-forward from a `git subtree
split` later. Retarget the docs workflows to docs/, point blog sync at the
main repository, and update the remaining internal references.
Co-Authored-By: Mika (Multica) <[email protected]>
Co-authored-by: multica-agent <[email protected]>
Move the 27 internal design docs (87 tracked files) from docs/ to dev-docs/
to make room for the public documentation site, adjust Docker/fnOS ignores,
refresh the README/AGENTS/ARCHITECTURE references, and land the docs
workflows ahead of the subtree import so they cannot diverge from mirror.
Co-Authored-By: Mika (Multica) <[email protected]>
Co-authored-by: multica-agent <[email protected]>
The declared floor was python-multipart>=0.0.27 while uv.lock resolves 0.0.32.
0.0.27 falls inside four published advisories, so any resolve that honours the
floor rather than the lock can land on a vulnerable release:
- GHSA-5rvq-cxj2-64vf / GHSA-6jv3-5f52-599m / GHSA-vffw-93wf-4j4q (quadratic-time
querystring DoS, semicolon field-separator and Content-Disposition parameter
smuggling), fixed in 0.0.30
- GHSA-v9pg-7xvm-68hf (negative Content-Length buffers the entire body), fixed in
0.0.31
The locked 0.0.32 has no known advisories. Raising the floor to match the lock
costs nothing and keeps the two from drifting apart.
Reachability, for the record: multipart parsing happens on the skills and plugin
local-upload routes, both of which require USER_TOKEN_OR_API_KEY with
RESOURCE_MANAGE, so this is an authenticated-admin DoS rather than an anonymous
one.
The resolved version is unchanged; only the requirement metadata moves.
Co-authored-by: multica-agent <[email protected]>
Classify event cleanup queries by selected columns instead of CORRELATED
plan text, preserving covering-index, primary-key, and no-sort checks.
Validated all 15 tests on ARM64 with SQLite 3.53.4 and 3.46.1.
- Switch Python base images to python:3.12-slim-trixie
- Replace libprotobuf32 with libprotobuf32t64
- Pin SQLite to 3.53.4-2 from forky for the WAL-reset fix
- Restrict forky packages to libsqlite3-0 with APT pinning
- Replace per-session mocks with lightweight shutdown counters
- Preserve the one-second timeout and 100-workspace coverage
- Assert that every session shuts down exactly once
- 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
Emit and persist a Host cancellation event before closing the runner stream.
Preserve existing terminal events and record cancelled runs consistently.
Serialize Host event sequence allocation to prevent conflicts with admin
cancellation audits, and notify observers with the persisted sequence.
Add deterministic cancellation and concurrent event allocation regressions.
The Chinese copy used 套餐 (package) for the subscription plan in the cloud
banner, the model errors and the multi-tenant docs. Name the same concept 计划
everywhere; Traditional Chinese keeps 計劃.
- Deduplicate message and history resource URLs using stable catalog references
- Persist renewed URLs with concurrent refresh protection
- Refresh OneBot images via get_msg intervals or NapCat rkey TTL
- Migrate historical payloads and cover SQLite and PostgreSQL behavior
* fix(web): keep the dedicated-environment link on a custom Cloud banner
The Cloud banner can carry per-language copy, but the custom-copy branch
rendered that text alone: everything else in the banner swaps the built-in
message for an override so the operator can say what is actually happening,
and doing so silently dropped the link to the dedicated environment -- the only
destination that turns a warning into an upgrade.
Render the dedicated link after the override when show_links is on, using new
cta_dedicated copy in every locale so the second half of the sentence reads as
a call to action rather than repeating the environment name.
* test(web): expect the new banner CTA copy in every locale
- 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.
The host inspects a package to read manifest.yaml and requirements.txt before the
SDK extracts it. Inspection normalized entry names with only
replace('\\','/').strip('/'), so it accepted names its own extractor rejects
(absolute paths, '..' components, drive letters) and it silently kept the first
member when two normalized to the same path.
There was no traversal write, because the SDK's _extract_verified_zip validates
every member before writing and the host only reads members by ZipInfo. The bug
is the divergence: a package could pass inspection and then fail to install, and
the two rule sets could drift further as either side changes.
Inspection now applies the same rule set as the SDK, via
_normalize_archive_entry_name: reject empty/NUL names, absolute paths, any '..'
component and drive letters; reject a duplicate after normalization. '.' and
empty segments are still collapsed, so './manifest.yaml' and 'components//x.py'
keep working.
Tests: 13 cases covering each rejection, the duplicate, traversal taking
precedence over the duplicate check, and two positive cases (nested/unicode
layout, cosmetic separators).
Co-authored-by: multica-agent <[email protected]>
* 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]>