Commit Graph
696 Commits
Author SHA1 Message Date
Hyu a2b7f6b7bb fix: stabilize native streaming and improve bot controls 2026-10-10 18:02:42 +08:00
Hyu e8da99be63 fix(wecomcs): handle service events and prevent duplicate replies 2026-10-10 16:25:36 +08:00
Hyu 846d1f14e6 fix(bots): restore sessions across processor types 2026-10-09 23:49:35 +08:00
Hyu 96280b3bcc fix(events): restrict pipeline routes and load agent prompts 2026-10-09 21:22:34 +08:00
Hyu cdc4086dea fix(lark): identify bots automatically and support standard events 2026-10-09 21:00:27 +08:00
Hyu 396b09ecce fix(acceptance): repair official account replies and processor navigation 2026-10-09 20:13:51 +08:00
Hyu fea35e8b3f fix(acceptance): improve execution monitoring and processor navigation 2026-10-09 18:54:32 +08:00
Hyu c5c3ec0b2c feat(models): add workspace starred model and processor defaults 2026-10-09 18:34:29 +08:00
Hyu 4c70977a1d fix(acceptance): refine processor setup and dashboard monitoring 2026-10-09 18:19:08 +08:00
Hyu e22bea7f1e fix(acceptance): improve bot setup and execution log presentation 2026-10-09 17:03:15 +08:00
Hyu 4b370f0a92 fix(telemetry): skip unrouted events and preserve matched deliveries 2026-10-08 13:33:57 +08:00
RockChinQ f96f663a80 feat(assistant): add streaming background sessions and refresh workspace UI 2026-10-08 13:24:46 +08:00
Hyu 40a3a941a3 fix(monitoring): unify execution records and stabilize streaming cleanup 2026-10-08 03:02:37 +08:00
Hyu 9f76037e64 feat(monitoring): unify execution views and trace tool calls 2026-10-07 22:36:10 +08:00
Hyu de886edc1b style(telemetry): apply ruff format to the telemetry manager
The single-line assignment fits the configured line length; ruff format
rejects the wrapped form.
2026-10-07 10:20:08 +08:00
Hyu 4482e8d876 feat(telemetry)!: upload one record per execution node and one closure per chain
Core no longer assembles a whole trace locally. Every workflow node is uploaded
the moment it completes as its own `execution_node` record, and each execution
closes with exactly one `execution_chain` record; all records of one execution
carry the same `event_id`, so Space groups them back into a single chain.

- every execution starts at an inbound event: event-less Pipeline lanes and
  WebUI/API debug runs synthesize their virtual inbound event at the boundary
  and are marked debug=true, so a Runner is never a trace origin
- sampling is decided once per event from a stable hash of the event id (the
  previous `int(id[:8], 16)` raised on every `platform:`-prefixed id and so
  dropped every successful inbound-event chain) and is reported on each record
  as sample{mode,denominator}; the default mode is now `all`
- the local trace buffer, the 120s TTL, the per-process trace cap and the shared
  open-node mirror are gone, which also removes the truncated-chain upload and
  the re-buffered-closed-trace leak they caused
- a broken chain is always uploaded, and its reason falls back to the first
  failed node so the chain explains itself
- flush keeps records until the sender accepts them and counts what it drops;
  telemetry health replaces the unused send-task queue
- heartbeat reports bound Agent / plugin-processor / event-binding counts
- bound Pipeline and Agent resources (plugins, MCP servers, knowledge bases,
  tools with their Host source, skills, models and the Runner schema selectors
  that picked them) are reported on the chain record
- failure text is bounded and redacted instead of passing exception text through
- the dead per-query feature counters and their call sites are removed

Wire contract: schema 2 (`execution_node` / `execution_chain`).
2026-10-07 09:41:41 +08:00
RockChinQ dcc7e17d77 fix(cloud): retain all execution records for activity counts 2026-10-03 05:03:02 +00:00
RockChinQ b205c4000f fix(runtime): isolate bounded DNS resolution from blocking SDK work 2026-10-02 16:13:25 +00:00
RockChinQ 4f1d897347 fix(cloud): retry workspace snapshots every five minutes instead of daily 2026-10-02 15:52:22 +00:00
RockChinQ 17cda1612f fix(telemetry): avoid global plugin enumeration in workspace snapshots and support model adapters 2026-10-02 15:46:28 +00:00
RockChinQ 2e5bbc8bdd feat(web): integrate ecosystem social preview images 2026-10-02 02:16:38 +00:00
Hyu 34b5fe0202 fix(plugin): share the runtime file-transfer root and 404 uninstalled icon requests
The SDK binds one transfer root inode (mode 0700) and picks it per profile: a shared worker uses /tmp/lbp-rpc while a host without the profile env fell back to data/temp/lbp, so plugin icons and assets died with 'Invalid file transfer capability'. The host now pins the shared root, and a plugin absent from the resolved Workspace answers 404 instead of 500.
2026-10-02 01:34:47 +08:00
Hyu 38d5750785 fix(plugin): report when an installation failure happened, not when it was sampled
The connector stored only uuid/code/message, so the ops reporter stamped every entry with its own sampling time and a stale failure looked freshly broken on each report. Failures now carry failed_at and the payload publishes it.
2026-10-02 01:34:46 +08:00
Hyu 73797dfa04 style(plugin): format the runtime ops reporter with ruff 2026-10-01 19:04:43 +08:00
Hyu 4c71de5eca feat(plugin): push plugin runtime ops samples to the Space control plane
The shared Cloud deployment runs one Plugin Runtime for every Workspace, but
nothing reported its health: the instance only exposed two aggregate numbers on
its own health probe, and the per-installation failure registry, the desired
state registry and the reconcile outcome lived in memory with no reader.

Add RuntimeOpsReporter, which every 30 seconds reads the Runtime ``/healthz``
probe over HTTP (derived from ``plugin.runtime_ws_url``), merges it with the
connector's desired-state, failure and reconcile summaries, and posts one
sample to the Space control plane on the existing authenticated direction.
Delivery is best-effort and bounded: a single in-flight request, a ten second
timeout, dropped samples counted in ``runtime_ops`` on the instance health
probe, and a failed probe recorded as a drop so the dashboard shows staleness
instead of grading a stale sample as healthy.

Cloud-only: a non-cloud deployment never enables the reporter, and the channel
is independent of product telemetry so ``space.disable_telemetry`` cannot blind
the ops dashboard.
2026-10-01 19:01:16 +08:00
Hyu 923ebe12af feat(telemetry): attach called platform APIs to their step node on execution traces
Every host step now opens a stage scope, so observations recorded inside it (including cross-task plugin/RPC calls resolved through the execution registry) carry the owning step as parent; the pipeline lane records its own pipeline/run node so acks and replies issued outside the runner still hang off a node.
2026-10-01 18:10:42 +08:00
Hyu df64f83e12 fix(telemetry): schedule the trace payload send instead of dropping it
close_trace() called the coroutine TelemetryManager.start_send_task without
awaiting or scheduling it, so every payload closed on that path was silently
dropped and the runtime logged "coroutine ... was never awaited". The only
other caller awaits it correctly, so both call styles stay supported: call the
manager, schedule the returned coroutine on the running loop, and close it with
a debug log when no loop is available.

Regression case added in tests/unit_tests/telemetry/test_trace.py: it fails
before this change (payload never delivered) and passes after.
2026-10-01 02:32:28 +08:00
Hyu 0efc1fb1cc feat(telemetry): trace one inbound event end to end
Execution telemetry already reported window counters. This adds one bounded,
content-free chain per inbound platform event, so a single event can be
followed from its source platform through routing and processing to the
platform API calls it caused.

- telemetry/trace.py: ContextVar trace identity with route and run scopes, and
  a 32-stage bound per chain.
- telemetry/execution.py: keeps at most 64 chains for 120s, decides sampling
  from the observed outcome, and sends one payload per chain keyed by
  query_id = trace_id, so Space fetches a chain by primary identity. Window
  counters are unchanged and remain the source of coverage statistics.
- botmgr / pipelinemgr / orchestrator: bind the trace at the ingress boundary,
  scope routing identity per dispatch, and attach the run identity, so nested
  lanes (event -> route -> pipeline/runner -> platform API) reuse one chain. A
  run reached without an ingress (WebUI debug, service API) owns its own chain.
- space.execution_trace selects off | failures | sampled | all (default
  sampled: failures, WebUI debug runs and every N-th success).

Chains carry only code-defined identifiers (adapter and runner types, route
identity as type:uuid, run id); never message content, tool arguments or
platform user identifiers.
2026-10-01 02:12:44 +08:00
RockChinQ d3e4bd55cd fix: document telemetry opt-out and silence delivery failures (#2618)
* fix: document telemetry opt-out and silence delivery failures

* style: format telemetry debug logging
2026-09-30 22:27:05 +08:00
RockChinQ 461b483977 feat: bounded execution telemetry for all processing modes (#2617)
* feat: report bounded execution observations via existing telemetry

* fix: tolerate optional telemetry context in routing
2026-09-30 22:00:24 +08:00
RockChinQ 30585c8657 refactor: remove beta diagnostics collection and reporting (#2616) 2026-09-30 20:24:35 +08:00
Hyu 5a31ac75fc style(tools): format the sandbox interpreter and skill tool changes with ruff 2026-09-30 18:45:00 +08:00
Hyu 4fc700b7e0 fix(tools): hide register_skill when Cloud sandboxes cannot scan host directories
scan_skill_directory is disabled for Cloud-managed sandboxes, but the
register_skill tool was still advertised to the model whenever a backend was
available, so every call failed closed without a usable explanation. Advertise
register_skill only when the runtime may scan sandbox directories, and keep
activate available in both modes.
2026-09-30 18:44:16 +08:00
Hyu ff3f321120 fix(tools): resolve the sandbox interpreter for workspace file scripts
The workspace file tools (read/write/edit/glob/grep) ran a script through
`python`, which a sandbox built from a host rootfs may not provide: those hosts
ship `python3` only, and a read-only /usr prevents creating a `python` shim
inside the jail. Every file tool then failed with `/bin/sh: 1: python: not
found`. Resolve the interpreter with `command -v python3 || command -v python`,
matching the interpreter the Box service already uses for its own scripts.
2026-09-30 18:44:16 +08:00
Hyu 0c3917ee0f style(api): format the streamed Agent debug helper with ruff 2026-09-30 15:50:48 +08:00
Hyu 5758f20768 fix(vector): do not bind a query dimension for text-only search
A full-text search sends no query vector by design, but search() validated
len([]) == 0 as the expected embedding dimension, so the request failed with
'Embedding dimension 0 is not enabled for this deployment' and hid the
backend capability error. An empty query vector now means no expected
dimension to verify.
2026-09-30 15:47:59 +08:00
Hyu e3e45d7b34 fix(api): keep the Workspace tenant scope for streamed Agent debug runs
The NDJSON debug stream emits its frames after the request handler returned,
so the request-scoped tenant scope is already closed by then. The deferred
execution therefore failed with TenantScopeRequiredError on its first
persistence access and the client only saw a generic runner_error frame with
no log entry.

Re-enter the Workspace tenant scope around the deferred execution and log
unexpected failures with their traceback.
2026-09-30 15:47:59 +08:00
RockChinQ 1fde3b4283 fix(certification): align certified sharing and unsigned dedicated admission (#2610)
Integrate certification policy, SDK/Core semantics, and documentation on canonical branch. Resolve duplicated documentation and keep invalid Cloud signatures rejected.
2026-09-29 00:56:18 +08:00
Hyu 6515ba3b94 fix(api): serve the passkey and password routes for the authenticated Account
Passkeys and passwords belong to the Account, but those routes required a Workspace -- which the WebUI never sends for them -- and then resolved the caller by looking the login name up as an email address. Every Account whose login name is not its email answered 404, so the passkey panel could neither add nor list a key.

The routes now authenticate with the account token and address the Account the caller authenticated as, matching the self-service second-factor routes in the same file. /space/bind-authorize-url carried the same Workspace requirement.

The login page also surfaced a TypeError instead of the passkey login failure message when an assertion was rejected.
2026-09-28 23:53:14 +08:00
RockChinQ 0468c2be15 Merge pull request #2609 from langbot-app/fix/cloud-unsigned-dedicated-20260928
fix(plugin): allow unsigned Cloud plugins as dedicated
2026-09-28 22:40:02 +08:00
Hyu 6ee9c0a9a3 fix(operation-trace): bind the workspace scope on every off-request write
Cloud runtime refuses unscoped database access and workspace_operation_logs is row-level-secured on the bound Workspace. The background writer, the retention pass and the metadata reads all run outside a request lifetime, so each of them failed and the failure was swallowed by the best-effort handler: enabling tracing recorded nothing at all.

The route wrapper also traces after the handler released its scope, so level resolution has to carry the Workspace itself. Without it the level read back as 0 and the request was dropped before it was even queued.

The startup gate now enumerates the Workspaces through this instance's execution bindings, because a cross-Workspace metadata scan cannot see them under RLS, and it runs after the Workspace service exists. The writer starts with a clean context so it cannot inherit the scope of the request that first enqueued a trace, and a dropped write is reported instead of being logged only at debug.
2026-09-28 22:38:35 +08:00
Hyu e14e277424 fix(api): scope TOTP oversight to the caller's workspace
The second-factor listing was instance-wide and the three mutating endpoints never checked that the target Account belongs to the caller's Workspace, so an owner or admin could see and reset another tenant's account. The listing now resolves the Workspace members and the mutations answer 404 for a foreign account, which does not disclose that it exists.

Also fixes the TOTP write path on Postgres: six write points passed timezone-aware datetimes into timestamp-without-time-zone columns, which asyncpg rejects, so every enroll, confirm, revoke and recovery-code consumption raised a 500 there. SQLite tolerated the same values.

The panel derives its oversight view from the API authorization instead of the length of the account list, which would have hidden it in a single-member Workspace.
2026-09-28 22:38:30 +08:00
RockChinQ ce118b4f41 fix(plugin): allow unsigned Cloud plugins on dedicated workers 2026-09-28 14:26:10 +00:00
RockChinQ b51a448b1e feat: stateless certified shared worker
Implements stateless singleton component model for certified shared Workers, integrating with SDK 0.7.0.
2026-09-28 13:25:40 +08:00
RockChinQ e778445982 feat: add beta environment banner
Add beta environment warning banner for Cloud beta deployments with i18n support (zh/en/ja)
2026-09-28 13:17:03 +08:00
TyperBody 280be0d7a4 fix(operation-trace): audit every route for misclassified mutations
Systematically cross-checked all 263 route/method pairs against the classifier
and fixed every case where a real change could be mislabelled:

- POST /skills and PUT /skills/<name> were classified as skill_view (read
  bucket), so creating or editing a skill was silently unlogged at the mutation
  level. They now record skill_update.
- The public webhook ingress (/bots/<uuid>) and the embedded chat widget
  (/embed/*) are unauthenticated visitor traffic, not Workspace changes; they
  are dropped instead of logged as create/resource.
- Uploading files and indexing documents (data flowing into a resource) are
  offered as observations so they never fill the log; deleting a knowledge-base
  file is destructive and now records file_delete.
- A plugin's own config-file edit/delete is a plugin change, not an opaque
  resource delete; ordered before the /config rule that also matched it.
- The bucket now comes from the action table (plus 'a read verb is always a
  read'), so a write action can no longer be forced into the read bucket.
- Added agents and files resource families; localized every new action/resource
  type in all nine locales.
2026-09-28 02:17:50 +08:00
TyperBody 67ff4d3054 fix(operation-trace): record uninstalls and deletes, drop assistant chatter
- Split the route rules on the HTTP method so a DELETE is classified as a
  destructive action instead of falling into the read/write bucket. Uninstalling
  a plugin or skill and deleting a knowledge base or MCP server were recorded as
  a view/update and left no readable trace.
- Add plugin_uninstall, skill_uninstall, knowledge_base_delete and mcp_delete.
- Skip the assistant's own conversation traffic: a chat session produced opaque
  create/resource rows that answered nothing.
- Localize the new actions and the assistant_conversation resource type in all locales.
- Sync the assistant tool action list and apply ruff formatting.
2026-09-28 02:06:57 +08:00
TyperBody b0fab2386e Merge remote-tracking branch 'origin/master' into feat/workspace-operation-traceability 2026-09-28 01:56:03 +08:00
TyperBody 7eeb0393a6 feat(operation-trace): collapsible retention policy and assistant trace tool
- Collapse the retention policy section by default, showing a one-line summary when collapsed.
- Add the owner/admin-only list_operation_logs assistant tool backed by audit.view.
- Enforce per-tool permissions on both tool listing and invocation.
- Add a substring search filter (search) to query_logs and the tool so callers can
  ask narrow questions (plugin name, account, verb) instead of dumping the full history.
- Project records to a compact shape so a page stays under the assistant result budget.
- Localize the new assistant tool label and retention summary across all locales.
2026-09-28 01:56:00 +08:00
TyperBody 9ecb469d13 feat(operation-trace): lead with the change, fix pipeline extension tracing
The panel answered "who, when, what" poorly: the actual before → after diff
was hidden behind a click, the actor was a muted line, and a whole row went to
the route while duration and hash sat in a permanent right column. The data
already arrives with the page, so the panel now reads top-down in the order an
operator actually looks:

* who and when first, then the action and resource, then the diff spelled out
  inline (long values collapse to their size so a pipeline config does not
  render as two near-identical blobs), with route / method / status / duration
  / evidence hash moved into the expanded diagnostics line;
* the whole card is the expand toggle with a rotating chevron, which removes
  the ambiguity of a small text link whose expanded state looked identical;
* records are bucketed into today / yesterday / dated groups so a long history
  reads as a timeline;
* a "changes only" toggle (reusing the level filter) answers "who changed
  something" in one click -- in the live log 489 of 499 rows are pure views,
  so this is the difference between a haystack and the ten rows that matter.

Tracing fixes found while reading the live log:

* PUT /pipelines/<uuid>/extensions was classified as a plugin config change
  because the generic ('/extensions') rule ran first; it now maps to a
  pipeline-specific action;
* that handler recorded no diff at all, so "modified extension config" could
  never say what changed. It now captures the previous bindings and reports the
  before → after.

Cache: the integrity result is now keyed by (Workspace, listing filters) so
each view reuses its own verification, and a delete invalidates all views for
the Workspace. Fourteen tests cover the projection, the verifier, the cache
lifecycle and the failure modes.
2026-09-28 01:03:02 +08:00