Commit Graph
4619 Commits
Author SHA1 Message Date
BiFangKNT 3456668497 feat(media): centralize resource URLs and persist refreshed image URLs
- 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
2026-10-23 12:49:56 +08:00
BiFangKNT 1be47ae925 fix(monitoring): refresh image URLs in execution details
- 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
2026-10-23 10:21:54 +08:00
BiFangKNT e3c7e49214 test(provider): initialize Codex requester fixture 2026-10-23 09:02:23 +08:00
BiFangKNT baddaa2b74 feat(onebot): refresh expired image URLs in history and model requests
- 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
2026-10-23 08:41:50 +08:00
RockChinQ c538e696b2 chore(release): bump version to 4.11.2 (#2758) v4.11.2 2026-10-22 23:45:23 +08:00
RockChinQ 7fa9c0e426 fix: 修复独立环境登录 404 与「添加第一个模型」500(4.11 回归) (#2755)
* 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.
2026-10-22 23:26:30 +08:00
RockChinQandmultica-agent da508f9603 fix(plugin): reject archive entry names the SDK would refuse to extract (#2757)
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]>
2026-10-22 22:04:36 +08:00
RockChinQandmultica-agent 0b341086fb LANG-145: enforce credential-session TTL on a timer, not on traffic (#2756)
* 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]>
2026-10-22 21:51:01 +08:00
RockChinQandmultica-agent a8dd85063f LANG-145: hide passkey UI unless the core reports it is supported (#2754)
* fix(adapters): stop SSRF and arbitrary local file reads in media handling

The platform adapters fetch URLs and open paths that come from inbound
messages, so both are attacker-influenced.

New guard module pkg/utils/outbound_url.py:

- assert_safe_outbound_url: checks the scheme, rejects literal private /
  loopback / link-local / reserved / multicast addresses, and resolves the
  hostname so a public-looking name pointing at an internal address is refused
  too. Optional allowed_domains confines a fetch to a known prefix.
- assert_safe_local_media_path: resolves a caller-supplied path and requires it
  to stay inside the media roots (data dir + temp dir), so symlinks and .. cannot
  escape. Directories are refused.

Wired in:

- DingTalk send_message: session_webhook is taken from the inbound event and was
  POSTed verbatim, so a forged event could redirect reply content (including LLM
  output) to any host. Now restricted to dingtalk.com.
- DingTalk send_attachment: component.url was fetched with no destination check;
  now goes through the SSRF guard, which also covers cloud metadata.
- DingTalk upload_image_media: caller-supplied file_path was opened directly; now
  confined to the media roots.
- Slack attachment_bytes: component.path was opened directly and uploaded to the
  channel (arbitrary file exfiltration); now confined to the media roots.
- Slack download_media: redirects were followed with no destination check. Every
  hop is now validated, the hop count is bounded, and a redirect off the Slack
  domain drops the bearer token.

Tests: 25 new cases for the guard (internal address forms, scheme rejection,
suffix impostor, traversal, symlink escape, directory). The Slack credential test
now stubs DNS because the guard resolves before fetching, and asserts
allow_redirects is False for every case since redirects are handled manually.

Co-authored-by: multica-agent <[email protected]>

* fix(web): hide passkey UI unless the core reports it is supported

The core refuses every passkey route on a cloud deployment (403
edition_unsupported) and reports passkey_login_enabled/passkey_supported as
false. The WebUI still rendered the controls:

- Login page read 'passkey_login_enabled !== false || passkey_supported', so an
  absent flag counted as enabled. It now requires an explicit true.
- The account settings panel always rendered the passkey section and called
  GET /passkeys on open, which 403s on cloud. It now reads the capabilities
  first, skips the list call when unsupported, and does not render the section.

Also drop the client-supplied origin from both passkey options requests. The
server resolves the WebAuthn RP ID and expected origin from api.webui_url /
api.webhook_prefix, so sending window.location.origin was dead weight that would
invite a future handler to trust it again.

Verified: tsc --noEmit clean, eslint clean (only two pre-existing hook-dep
warnings), pnpm build succeeds.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-agent <[email protected]>
2026-10-22 21:26:33 +08:00
RockChinQandmultica-agent c759da50b1 fix(adapters): stop SSRF and arbitrary local file reads in media handling (#2753)
The platform adapters fetch URLs and open paths that come from inbound
messages, so both are attacker-influenced.

New guard module pkg/utils/outbound_url.py:

- assert_safe_outbound_url: checks the scheme, rejects literal private /
  loopback / link-local / reserved / multicast addresses, and resolves the
  hostname so a public-looking name pointing at an internal address is refused
  too. Optional allowed_domains confines a fetch to a known prefix.
- assert_safe_local_media_path: resolves a caller-supplied path and requires it
  to stay inside the media roots (data dir + temp dir), so symlinks and .. cannot
  escape. Directories are refused.

Wired in:

- DingTalk send_message: session_webhook is taken from the inbound event and was
  POSTed verbatim, so a forged event could redirect reply content (including LLM
  output) to any host. Now restricted to dingtalk.com.
- DingTalk send_attachment: component.url was fetched with no destination check;
  now goes through the SSRF guard, which also covers cloud metadata.
- DingTalk upload_image_media: caller-supplied file_path was opened directly; now
  confined to the media roots.
- Slack attachment_bytes: component.path was opened directly and uploaded to the
  channel (arbitrary file exfiltration); now confined to the media roots.
- Slack download_media: redirects were followed with no destination check. Every
  hop is now validated, the hop count is bounded, and a redirect off the Slack
  domain drops the bearer token.

Tests: 25 new cases for the guard (internal address forms, scheme rejection,
suffix impostor, traversal, symlink escape, directory). The Slack credential test
now stubs DNS because the guard resolves before fetching, and asserts
allow_redirects is False for every case since redirects are handled manually.

Co-authored-by: multica-agent <[email protected]>
2026-10-22 20:46:27 +08:00
RockChinQandmultica-agent 01789e498a LANG-145: bind WebAuthn to server config and make passkeys OSS-only (#2744)
* 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]>
2026-10-22 20:39:21 +08:00
huanghuoguoguo cc19395f5c perf(core): reduce execution projection and dispatch overhead (#2746)
* perf(core): join execution projection and reuse authorized tool schemas

* perf(pipeline): avoid duplicate checks before stage output validation

* perf(plugin): reuse reconciled settings within event dispatch

* fix(workspace): keep execution projection reads free of unrelated flushes
2026-10-22 20:00:42 +08:00
RockChinQ 2c59bc2c55 Fix LangBot Cloud links in README
Updated links to LangBot Cloud in README.md.
2026-10-22 19:42:37 +08:00
huanghuoguoguo 7a410bcb31 test(core): strengthen handler contracts and report source quality (#2632) 2026-10-22 17:33:09 +08:00
RockChinQ 49651edd45 Merge pull request #2742 from langbot-app/fix/cloud-projection-startup-window
fix(cloud): keep the directory projection fresh during startup
2026-10-22 01:36:41 +08:00
RockChinQandmultica-agent 5c0236ac3e fix(cloud): keep the directory projection fresh during startup
Boot continues for minutes after the first directory snapshot is committed:
platform ports reconnect, knowledge bases and pipelines reload, and a cold
shared Runtime restore may still be running. The refresh loop only started at
the end of that stage, so any boot slower than the projection staleness window
failed closed while restoring knowledge bases, and Core then exited during
startup - every retry hit the same window.

Start the single refresh task as soon as the first snapshot is committed and
keep the later start as a guarded fallback so the loop is never created twice.

Observed on the production Cloud instance: a boot that took ~120s to reach
knowledge base restore raised 'Cloud directory projection is stale' and the
process exited, while the same image had booted earlier with a faster start.

Co-authored-by: multica-agent <[email protected]>
2026-10-21 17:20:36 +00:00
RockChinQ af013eb077 Merge pull request #2741 from langbot-app/agent/mika/99145c30e373
feat(cloud): add configurable Cloud-only WebUI notice banner
2026-10-22 00:43:46 +08:00
RockChinQandmultica-agent d45b0a3957 feat(cloud): add configurable Cloud-only WebUI notice banner
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]>
2026-10-21 16:26:43 +00:00
0d365c98d8 feat(web): change upgrade button to outline variant to match workspace switcher style (#2740)
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-21 22:18:44 +08:00
RockChinQ 49caa69dbc Merge pull request #2738 from langbot-app/fix/plan-credits-copy-and-upgrade-button
fix(web): outline the plan upgrade control and call the grant 模型积分
2026-10-21 18:41:27 +08:00
Chin Rockandmultica-agent 6bea0f15e1 fix(web): outline the plan upgrade control and call the grant 模型积分
Co-authored-by: multica-agent <[email protected]>
2026-10-21 10:32:21 +00:00
RockChinQ 3eecf0496f Merge pull request #2737 from langbot-app/agent/sg-claude/07b0571b414f
feat(web): rename credits label, shorten workspace switcher, add upgrade button
2026-10-21 16:23:45 +08:00
Chin Rockandmultica-agent 61c59687c2 fix(web): satisfy prettier and align the switcher layout spec after the upgrade control
Co-authored-by: multica-agent <[email protected]>
2026-10-21 08:06:42 +00:00
fa2913a986 feat(web): rename credits label to model credits, shorten workspace switcher, add upgrade button
- creditsLabel: 每月赠送积分 → 模型积分 (zh), Monthly credits → Model credits (en), 毎月のクレジット → モデルクレジット (ja)
- creditsHelp: append note that own model providers remain usable after credits run out, all locales
- WorkspaceSwitcher trigger: cap workspace name to 6rem, remove justify-start
- HomeSidebar: add ArrowUp upgrade icon button next to workspace switcher (cloud_projection only), opens CloudPlansDialog lazy-loaded

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-21 07:45:56 +00:00
彼方 09b373c333 Merge pull request #2735 from langbot-app/fix/image-referrer-policy
fix(web): omit referrer when loading message images
2026-10-21 13:11:35 +08:00
BiFangKNT 726ad5530f fix(web): omit referrer when loading message images
Set no-referrer on message thumbnails and previews to prevent
QQ image requests from returning 400 and triggering ORB.

Add browser coverage for image loading and request headers.
2026-10-21 12:03:00 +08:00
RockChinQ fc62f2ee13 Merge pull request #2734 from langbot-app/agent/mika/be8d90f5d320
chore(release): prepare 4.11.1 with SDK 0.7.16
v4.11.1
2026-10-20 23:52:47 +08:00
RockChinQandmultica-agent 816d7fee3b chore(release): prepare 4.11.1 with SDK 0.7.16
Co-authored-by: multica-agent <[email protected]>
2026-10-20 15:44:50 +00:00
RockChinQandmultica-agent d13ae56ea5 fix: bot routing setup and busy-session response checks (#2728)
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]>
2026-10-20 21:22:53 +08:00
RockChinQandmultica-agent f8e32ef14f LANG-134: Finish original inbound monitoring row on pipeline error (#2729)
* fix(inbound-error-state): Finish original inbound monitoring row on pipeline error (LANG-134)

* fix(pipeline): finalize monitoring on early exits and cancellation

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-agent <[email protected]>
2026-10-20 20:39:10 +08:00
RockChinQ 819f65bd8a Merge pull request #2731 from langbot-app/task/LANG-136-explicit-response-files
LANG-136: Deliver explicit attachments without implicit Box scan
2026-10-20 19:46:48 +08:00
RockChinQ 71feb33244 Merge pull request #2730 from langbot-app/task/LANG-135-safe-history-indexes
LANG-135: Guard 0039 indexes against missing historical columns
2026-10-20 18:11:30 +08:00
RockChinQ 8cc8243162 Merge pull request #2727 from langbot-app/task/LANG-132-install-diagnostics
LANG-132: Sanitize plugin installation network diagnostics
2026-10-20 17:10:04 +08:00
Chin Rockandmultica-agent c28ae761ac fix(web): format migration diagnostics and tolerate wrapped translations
Co-authored-by: multica-agent <[email protected]>
2026-10-20 09:09:41 +00:00
彼方 4a95a16801 Merge pull request #2732 from langbot-app/chore/update-sdk-0.7.15
chore(deps): update langbot-plugin to 0.7.15
2026-10-20 16:19:02 +08:00
BiFangKNT 6bdd5e8c94 chore(deps): pin released langbot-plugin 0.7.15 2026-10-20 15:59:00 +08:00
Chin Rock 0990cc429d fix(explicit-response-files): Deliver explicit attachments without implicit Box scan (LANG-136) 2026-10-20 07:54:05 +00:00
Chin Rock 4d91903596 fix(safe-history-indexes): Guard 0039 indexes against missing historical columns (LANG-135) 2026-10-20 07:54:02 +00:00
Chin Rock 40d40204cf fix(install-diagnostics): Sanitize plugin installation network diagnostics (LANG-132) 2026-10-20 07:53:56 +00:00
彼方 1baba1b9f9 Merge pull request #2725 from langbot-app/fix/session-summary-persistence
fix(monitoring): persist session summaries to avoid history scans
2026-10-20 11:53:06 +08:00
BiFangKNT 3b1ea56f4f fix(monitoring): avoid historical payload reads in execution filters
- Add covering indexes through migration 0040
- Preserve PostgreSQL FORCE RLS state when marking historical summaries
- Add query-plan and migration regression tests to CI
2026-10-20 10:45:44 +08:00
BiFangKNT 7129e20cf6 fix(monitoring): paginate conversation IDs before loading message payloads
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.
2026-10-20 09:34:05 +08:00
BiFangKNT 17aa5a75ac test(persistence): align migration fixtures with startup bootstrap 2026-10-20 06:50:07 +08:00
BiFangKNT 810d053df9 fix(monitoring): persist session summaries to avoid history scans
- 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.
2026-10-20 06:12:00 +08:00
Chin Rockandmultica-agent b7992e4ea6 fix(ci): align HTTP attachment test with explicit file export
Co-authored-by: multica-agent <[email protected]>
2026-10-19 19:02:21 +00:00
41b55952ff fix(runner): avoid duplicate inline image payloads (#2631)
* fix(runner): avoid duplicate inline image payloads

* fix(runner): preserve attachments and complete HTTP agent delivery

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Qingsong Li <[email protected]>
Co-authored-by: Chin Rock <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-20 02:14:36 +08:00
RockChinQandmultica-agent a10a6eb780 fix(ci): provision real-stack browser dependencies and disambiguate runtime assertion (#2724)
Co-authored-by: multica-agent <[email protected]>
2026-10-20 00:08:14 +08:00
RockChinQandmultica-agent d9e4e7d9df fix(pipeline): decouple Runner from extension permissions (LANG-54) (#2723)
* fix(pipeline): decouple runner from extension allowlist

Co-authored-by: multica-agent <[email protected]>

* fix(pipeline): complete runner diagnostics and real runtime coverage

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: multica-agent <[email protected]>
2026-10-19 23:07:11 +08:00
RockChinQ 5a6ddbff6f Merge pull request #2656 from fluo-rite/fix/telegram-mention-entities
fix(telegram): respect entity boundaries when parsing bot mentions
2026-10-19 17:51:31 +08:00
RockChinQandmultica-agent 52e8042f51 fix(telegram): match mention usernames case-insensitively
Co-authored-by: multica-agent <[email protected]>
2026-10-19 17:33:51 +08:00