Commit Graph
4 Commits
Author SHA1 Message Date
Saoud Rizwan 3ba8dd8fa7 chore: upgrade Bun to 1.4.2 (#14631)
* chore: upgrade Bun toolchain and embedded runtime to 1.4.2

Bun 1.4.1+ writes a valid ad-hoc Mach-O signature for bun build --compile
output, so the CLI is no longer SIGKILLed on macOS 27, and 1.4 ships
x64 runtimes as baseline-only, so compiled binaries run on pre-AVX2 CPUs.

Pins engines.bun, packageManager, .tool-versions, every setup-bun step,
and @types/bun. The lockfile only moves @types/bun/bun-types so the AI SDK
hoisting is left untouched.

* fix(core): swallow reader.cancel() rejection in web_fetch size guard

Bun 1.4 rejects cancel() on an errored stream, which surfaced as an
unhandled rejection right before the size-limit error was thrown.

* build(cli): boot the hub from the compiled binary as a smoke test

--version only proves the executable launches. Bun 1.4.1/1.4.2 have a
compile-time chunk ordering regression with splitting: true that only
shows up once real modules load, so start the hub daemon from the freshly
compiled host binary in an isolated data dir and require a healthy status
before the build is considered good.

* test(core): stop asserting the forced-exit path in hub shutdown e2e

Under Bun 1.3 http.Server.close() never settled while a WebSocket was
open, so the daemon always exited through the 2s deadline and the e2e
asserted the forced-exit log line. Bun 1.4 completes the graceful close
(~50ms instead of ~2s in a side-by-side probe), so that line no longer
appears. The exit code, timing, discovery cleanup, and endpoint checks
still cover the behavior; the forced path stays covered by
shutdown-coordinator.test.ts.

* build(cli): isolate the smoke test hub from CLINE_HUB_DISCOVERY_PATH

The env var overrides the data-dir-derived discovery record, so an
inherited value would have pointed hub start/stop at a real hub.
2026-09-28 21:33:32 -07:00
632fce5071 feat(llms): sampled cline-provider AI SDK tracing via the host OTLP exporter (#13974)
* fix(llms): limit Langfuse telemetry to Cline provider

Refs ENG-2505

* fix(llms): include ClinePass in Langfuse telemetry

Refs ENG-2505

* feat(llms): sampled cline-provider AI SDK tracing via the host OTLP exporter

Enables the collector-relay tracing path: when the host telemetry service
has registered an OTLP tracer provider, cline-provider streams can emit
AI SDK spans without Langfuse credentials in the process.

- CLINE_TRACE_SAMPLE_PERCENT (default 0 = off) gates emission; sampling
  is a deterministic FNV-1a hash of the task/session id, so whole tasks
  sample together and retries decide identically.
- Metadata-only by default (recordInputs/recordOutputs false): models,
  tokens, timings, errors — no prompt or completion content. Content
  requires CLINE_TRACE_RECORD_CONTENT=true explicitly.
- The direct Langfuse path (env credentials, hub/internal) is unchanged:
  full content, every request, provider-gated as before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(telemetry): client OTLP trace pipeline for cline-provider AI SDK spans

Extension side of the Langfuse collector relay:

- OpenTelemetryClientProvider gains a TracerProvider: when tracesExporter
  is configured, spans batch-export over OTLP (grpc / http/json /
  http/protobuf) to the same collector endpoint as logs/metrics.
  register() also installs the async context manager span parenting needs.
- tracesExporter plumbed through all three config sources: build constants
  (OTEL_TRACES_EXPORTER), runtime env (CLINE_OTEL_TRACES_EXPORTER), and
  remote config (openTelemetryTracesExporter) — the last is the no-release
  kill switch.
- SDK: a host that registers a traces exporter now defaults to 100% task
  sampling; CLINE_TRACE_SAMPLE_PERCENT reduces it (0 disables). Env reads
  are literal so build-time inlining works. Metadata-only stays the
  default; content still requires CLINE_TRACE_RECORD_CONTENT=true.
- CLI build inlines the new envs alongside the existing OTEL set.

No publish workflow sets OTEL_TRACES_EXPORTER yet, so shipped builds keep
tracing dark — activation is a separate one-line-per-workflow PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(llms): honor the global telemetry opt-out on the trace relay path

AI SDK spans bypass the ITelemetryService wrapper that enforces the
user's opt-out for events and metrics, so an opted-out user's prompts
would still have traced once a host registered an OTLP exporter.
resolveAiSdkTelemetry now re-checks the shared global settings file
(telemetryOptOut) per stream — covering startup state and mid-session
opt-outs across extension and CLI. The credentialed direct Langfuse
path is unchanged: it only activates on explicit operator config.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style: biome format

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(llms): one trace export path per host — direct Langfuse declines when an OTLP tracer is registered

Enablement is a per-stream boolean but export fan-out is processor-level:
attaching the direct LangfuseSpanProcessor to a host-registered tracer
provider ships every span twice (direct + collector relay). The direct
path now declines instead of cooperating when a recording provider
already owns the global slot; it only exports when it registers its own
provider. A host therefore configures exactly one of LANGFUSE_*
credentials or an OTLP traces exporter, and misconfiguring both is safe.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(hub): daemon-distinct trace identity for Langfuse hub coverage (#14034)

The detached hub daemon already has everything needed to emit AI SDK
traces: it ships in the same compiled binary as the CLI (so the publish
build's OTEL_TRACES_EXPORTER / CLINE_TRACE_RECORD_CONTENT inlining
reaches it), its telemetry handle flows into the hub server and the
scheduled-run handlers, core's OpenTelemetryProvider registers a global
tracer when the exporter is configured, and shutdown already flushes
with a hard deadline.

What was missing is identity: spans carry only the OTel resource, not
per-event metadata, so daemon-emitted traces were indistinguishable
from CLI-local ones (both service.name=cline). The daemon now declares
serviceName=cline-hub-daemon (+ serviceVersion), via new optional
serviceName/serviceVersion fields on the shared OpenTelemetryClientConfig.

Opt-out holds for the daemon unchanged: it is a same-machine, same-user
process, so the shared global settings file the per-stream gate reads is
the requesting user's own (shared/cloud topologies tracked in ENG-2525).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* fix(llms): fail closed when the telemetry opt-out cannot be verified

The relay's opt-out guard treated every settings read failure as 'no
opt-out recorded', so a corrupted or unreadable settings file (including
a torn read of the non-atomic writer) silently re-enabled tracing for a
user who had opted out. Only a genuinely absent file (ENOENT, first run)
now reads as opted in; malformed JSON and every other read failure
disable the relay — consent that cannot be verified is not consent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(llms): identify the OTLP relay explicitly instead of inferring it from any recording tracer

'Some recording tracer exists' misclassified console-only tracing as the
collector relay (enabling the relay decision) and — for mutable
providers — newly suppressed the direct Langfuse attach that worked
before the one-path guard. The hosts that build tracer providers now
stamp a relay marker only when a real OTLP span processor was created
(console-only providers stay unmarked), and the trace decisions key off
that marker:

- relay path enables only behind a marked provider;
- the one-path decline fires only for marked providers — unmarked
  mutable providers keep the original cooperative attach behavior;
- a relay decline is no longer cached, so direct Langfuse gets to retry
  once a relay is disposed.

The marker lives on the provider instance (reached through the OTel API
global), which survives bundled module duplication where a module-level
registry would not.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style: organize imports

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): own and dispose remote trace clients across config updates

* fix(llms): wire per-call Langfuse telemetry and isolate direct export

* refactor(telemetry): reject unsupported providers before initialization

* test(desktop): raise sidecar vitest timeout above the module-graph import cost

The first test in each sidecar file pays the @cline/core → @cline/llms
import cost, which lands within ~100ms of the 5s default on CI runners
(commands-account failed at 5009ms; context.test.ts passed at 4923ms).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: abeatrix <beatrix@cline.bot>
2026-09-11 15:13:54 -07:00
Saoud Rizwan d3d3bd8749 fix(cli): defer auto-update install until no CLI is attached to the hub (#13233)
* fix(core): bridge protections for updates landing under pre-3.0.55 clients

Three pieces, each proven against real released artifacts:

- postinstall shield: CLI versions <= 3.0.54 restart the hub daemon after a
  background auto-update even while it serves live sessions, and their
  fingerprint check then rejects every replacement hub, bricking the running
  TUI. That code is on users' machines and cannot be patched — but it runs
  only after the install completes, and it bails out harmlessly when no hub
  discovery record exists. The newly installed package's postinstall sets
  the record aside so the old updater never fires.
- superseded-record fallback: the set-aside record is also the only source
  of the auth token and pid the next new-build launch needs to retire the
  displaced hub (a port probe carries neither); ensure reads it back.
- bind retry: a hub retired on the fixed port can hold it ~2s after acking
  shutdown (watchdog force-exit); the replacement daemon retries EADDRINUSE
  for up to 5s instead of dying and leaving no hub at all.

* fix(cli): defer auto-update install until no CLI is attached to the hub

Installing while cline processes run swaps the npm package under them:
their respawn paths break on the new build fingerprint, and the updater
then restarted the hub daemon out from under live sessions (the 'Hub
connection closed (code=1006)' incident). Guarding the restart treats the
symptom; the fix is to never install under a running process.

The startup check now only records that an update is available. The
install runs at process exit, and only when the hub confirms no other
cli* client is attached — desktop sidecars and connectors ship their own
binaries, so only cli* clients make the swap unsafe. With nothing old
running at install time, no hub restart is needed at all: the next launch
retires the stale hub through the existing ensure path. Deletes
restartHubServerIfRunning, ensureCliHubServerAfterUpdate, and their
support code; manual 'cline update' still installs immediately and now
just notes that the update applies on next start.

* fix(cli): apply deferred update from the entrypoint exit sequence

The CLI entrypoint always terminates with an explicit process.exit(),
which never emits beforeExit — the hook the deferred installer waited on,
so it would never have run (caught by review). Invoke applyDeferredUpdate
directly from the entrypoint's exit sequence after disposeAll(), where
every normal termination passes; crash paths deliberately skip it. Also
clear the pending update once an install spawns so the apply is
idempotent.

* test(cli): isolate unit tests from the real ~/.cline

A full vitest run could leave a real hub daemon running against the
developer's actual ~/.cline discovery record (observed while validating
this PR: a daemon spawned from the globally installed cline binary,
attached to the real data dir). Point CLINE_DIR, CLINE_DATA_DIR, and
CLINE_HUB_DISCOVERY_PATH at a per-worker temp dir and disable auto-update
before any test file loads; subprocesses inherit the isolation via env.

* fix(core): discard the superseded discovery record once consumed

The set-aside record is one-shot recovery metadata, but nothing deleted
it, and it feeds a pid into retireDiscoveredHub's SIGTERM. Weeks later a
launch that finds no live record (routine after any retirement) could
read the stale file and signal whatever process the OS recycled that pid
onto (review finding by @abeatrix). Unlink it at every ensure resolution
that ends with a live, verified hub; failure paths keep it for the next
attempt.

* fix(cli): harden the exit-time update gate

Three review findings on the deferred-apply path:

- A wedged hub could stall an otherwise-finished CLI for tens of seconds
  via the hub client's default timeouts; the whole exit-time query is now
  bounded to 3s, with timeout counting as attached (never install unless
  the hub positively confirms).
- Sub-second commands exited before the startup version check resolved
  and silently dropped the update every time for one-shot-only usage;
  exit now grants the in-flight check a 250ms grace.
- client.list can lose a TUI's registration during transport churn while
  its session connection survives, so an empty client list is not proof
  of safety; cross-check sessions with participants. Participants rather
  than session status: finished sessions linger idle forever and must
  not pin updates, and participant-less scheduled runs live in the hub
  process, which the binary swap does not touch. Verified live: a
  session-holding client invisible to client.list defers the install,
  and the gate opens once it disconnects.

* docs(cli): fix stale beforeExit reference in the exit-gate comment

* style(cli): apply biome formatting to update deferral code

* fix(cli): let doctor see a hub whose record the update shield set aside

During the shielded update window the discovery record is renamed to
.superseded so pre-3.0.55 updaters cannot restart a busy hub. Doctor
read only the primary record, so in that window it reported the live
daemon - the one serving the user's still-open old session - as a stale
hub daemon and advised 'cline doctor fix', which kills it and reproduces
the exact 1006 incident the shield exists to prevent (found by QA).
Doctor now falls back to the set-aside record the same way the ensure
path does, and doctor fix clears the set-aside file along with the
primary record so a deliberate reset does not leave stale retirement
metadata pointing at a recyclable pid.

* fix(core): keep shielded sessions on one Hub authority (#13244)

* fix(core): recover shielded busy hub discovery

* chore(core): instrument shielded hub recovery

* fix(core): recover shielded hubs with attached clients

* fix(cli): recognize shielded hubs in doctor

* refactor(core): keep shield recovery minimal

* fix(core): retain shared Hub idle helper semantics

* chore(core): align busyness helper with the #13231 wording

The participants-only hasActiveHubSessions here duplicates the change on
bee/hub-lifecycle (this branch needs its semantics for the participant
gate). Matching that version byte for byte lets the two merges resolve
cleanly instead of conflicting. Also restores the module-registry reset
comment this branch dropped - it documents a real local-vs-CI gotcha.
2026-08-13 19:39:44 -07:00
Tomás BarreiroandSaoud Rizwan 220a21bdcf Move sdk/apps/ to apps/ (#11200)
* Move the apps to the root dir

* Update all references from sdk/apps/ to apps/

* Update dependencies

* Install bun types

* Fix types

* Fix types

* Fix linter

* Ingore apps from vscode

* Fix security warning

* Fix windows install

* Enable windows dev mode

* Revert "Enable windows dev mode"

This reverts commit a46c99282e.

* Revert "Ingore apps from vscode"

This reverts commit 47f7b265d2.

* Revert "Fix windows install"

This reverts commit 1dabba1556.

* update the repo root

* fix root dir

* fix path

* fix other path

* Fix unrelated changes

* fix: address apps move follow-up blockers (#11228)

* fix: update root app command paths

* fix: include moved apps in root checks

* fix: clean up moved app path references

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-03 01:49:57 +02:00