Skip to content

build(deps-dev): Bump react and @types/react - #652

Closed
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/bun/multi-9b1536b8cd
Closed

dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/bun/multi-9b1536b8cd

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Aug 4, 2026

Copy link
Copy Markdown
Contributor

Bumps react and @types/react. These dependencies needed to be updated together.
Updates react from 19.2.7 to 19.2.8

Release notes

Sourced from react's releases.

19.2.8 (July 21st, 2026)

React Server Components

Commits

Updates @types/react from 19.2.17 to 19.2.18

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [react](https://github.com/react/react/tree/HEAD/packages/react) and [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react). These dependencies needed to be updated together.

Updates `react` from 19.2.7 to 19.2.8
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.2.8/packages/react)

Updates `@types/react` from 19.2.17 to 19.2.18
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: react
  dependency-version: 19.2.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
- dependency-name: "@types/react"
  dependency-version: 19.2.18
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Aug 4, 2026
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​types/​react@​19.2.17 ⏵ 19.2.18100 +110079 +194100
Updatedreact@​19.2.7 ⏵ 19.2.81001008497100

View full report

NiveditJain added a commit that referenced this pull request Aug 5, 2026
Lands #652 on this branch. React refuses to render when `react` and
`react-dom` are not the exact same version, so the matching react-dom
bump from #607 rides along: #652 on its own failed 15 component test
files with `Incompatible React versions`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014GcYCCtSc5VGD6YCoxsrsY
@NiveditJain

Copy link
Copy Markdown
Member

Landed on the failproofaid branch instead (PR #632), commit 4cd9ad0 — react 19.2.8, @types/react 19.2.18.

Note for whoever picks this up next: this bump is not green on its own. React refuses to render when react and react-dom are not the exact same version, so react 19.2.8 against react-dom 19.2.7 fails 15 component test files with Incompatible React versions. The matching react-dom bump from #607 is included in the same commit.

@NiveditJain NiveditJain closed this Aug 5, 2026
@dependabot @github

dependabot Bot commented on behalf of github Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

OK, I won't notify you again about this release, but will get in touch when a new version is available. You can also ignore all major, minor, or patch releases for a dependency by adding an ignore condition with the desired update_types to your config file.

If you change your mind, just re-open this PR and I'll resolve any conflicts on it.

@dependabot
dependabot Bot deleted the dependabot/bun/multi-9b1536b8cd branch August 5, 2026 07:54
SiddarthAA added a commit that referenced this pull request Sep 4, 2026
Applied while red-teaming the pull-based Evaluator v2 worker against a live
stack (agenteye #652 / failproofai #758). All five reproduced live and are
covered by tests/test_evaluator_review_fixes.py; the full evaluator suite
(153 tests) still passes.

- authoring._bounded: reject C0 control characters and DEL in bounded result
  text (reasoning/summary/description/display_value/unit/label), matching the
  server's check_bounded. The SDK previously validated only byte length, so a
  result quoting transcript text with a NUL or ANSI escape was accepted locally
  then rejected by the server with a NON-RETRYABLE 422 — a successful eval was
  silently lost and its assignment dead-lettered. TAB/LF/CR are kept.

- authoring.EvalResult.result_items: carry the eval's `reasoning` on the PRIMARY
  result (result_key == eval_key) for metric- and assertion-kind evals, not only
  on a score item. A non-score eval previously dropped its reasoning entirely.

- runtime._execute_run_in_slot: always bound an evaluation by wall clock. A LOCAL
  (customer-authored) eval declaring no timeout_seconds ran with NO timeout — a
  hung await or unbounded judge call wedged the worker slot and held the lease
  forever. Falls back to DEFAULT_EVAL_TIMEOUT_SECONDS (300s, the contract's
  per-eval default); managed evals keep their tighter in-sandbox cap.

- runtime._execute_run_in_slot: log the full traceback LOCALLY when a customer
  eval raises. The wire error stays scrubbed to the exception type (it can quote
  transcript data), but the pod now records the real exception so an author can
  debug their own eval instead of seeing only "evaluation raised HTTPError".

- source._OBJECT_REPR: scope the output-boundary guard to the " at 0x..." repr
  tail and drop the bare "0x"+hex alternative, which false-rejected legitimate
  result text (a hex colour like 0xFFFFFF, a digest, an address the agent
  logged) as an embedded object repr. The tail still catches a '<'-stripped repr.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SiddarthAA added a commit that referenced this pull request Sep 4, 2026
The server returns error code `insufficient_permissions` (HTTP 403) when a
worker key lacks `evaluations:run` (server worker_auth.rs), but that code was
absent from the frozen ERROR_SPECS on both sides and from both contract.json
fixtures. Add it (403, non-retryable) to the SDK's ERROR_SPECS and the SDK
fixture; the mirroring server-side entry lands in agenteye #652. Keeps the two
contract.json fixtures byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SiddarthAA added a commit that referenced this pull request Sep 4, 2026
…oo-large transcripts

Follow-up review fixes (agenteye #652 / failproofai #758), all covered by
tests/test_evaluator_review_fixes.py; evaluator suite (145 tests) green.

- _heartbeat now beats IMMEDIATELY before its first sleep. The pre-plan
  condition phase can consume most of the claim-time lease, and the server only
  renews a planned assignment's lease on heartbeat — so sleeping a full interval
  before the first beat let the lease expire after the runs had started and
  cancelled every one. (Server-side: the plan handler also extends the lease.)

- process_assignment tolerates an idempotent replay whose run set diverges from
  this attempt's plan. On reclaim the server re-serves the FIRST attempt's runs,
  which are authoritative — even one this attempt's condition would have skipped.
  The status-equality check is skipped on replay, and a replayed run absent from
  this attempt's selection is reconstructed from the assignment's descriptors
  instead of raising and dead-lettering an assignment that could never converge.

- process_assignment catches transcript_too_large at the transcript fetch and
  returns cleanly (logging + a metric) instead of propagating. No runs are
  planned yet, the error is non-retryable, and re-raising wedged the poll loop
  and burned the whole retry budget — reproduced live, where a 28.6 MiB session
  froze the pod. The server terminalizes the assignment as too_large.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
NiveditJain pushed a commit that referenced this pull request Sep 7, 2026
…ed pods, fork+exec sandbox, review hardening (#758)

* docs(sdk): clarify evaluator v2 boundary

* feat(sdk): add evaluator v2 worker runtime

* fix(evaluator): resume only unfinished replayed runs

* feat(sdk): run hosted evaluator definitions

* fix(evaluator): harden managed source sandbox and contain poison definitions

An adversarial review of the server-authored `execution_mode='python'`
evaluations (which run in the shared managed pod) found the AST sandbox
escapable several ways: `str.format`/`format_map` C-level field traversal,
generator/frame introspection (`gi_frame.f_globals`) that reached the eval
globals and could poison a process-shared namespace across evaluations, and
`type.mro()` type-object reach — none of which start with `_`, so the dunder
guard never saw them.

Replace the attribute denylist with a **default-deny allowlist** (the transcript
data surface plus pure string/collection methods), give each eval **fresh
per-call globals** so nothing persists between evaluations, and reject any result
whose text embeds a runtime object repr (`<... at 0x...>`, the heap-pointer/ASLR
disclosure that falls out of any bound method's repr) at the output boundary.
Drop `enumerate` and bare generator expressions — both were gratuitous
pointer-repr sources.

Also compile managed source lazily inside the per-run executor, so a definition
the sandbox rejects dead-letters as one bounded `failed`/`eval_error` run instead
of crashing the assignment and being reclaimed until its attempt budget is spent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* feat(evaluator): normal short polling instead of long-poll in the worker

The v2 worker long-polled the claim endpoint (wait_seconds, server held the
request open up to 25s), which ties up a server request handler per idle worker
and does not match the normal-polling cadence of our other cloud surfaces. The
worker now polls normally: claim returns immediately, and on an empty claim the
worker sleeps the server-advertised poll_interval_seconds (from the register
response, default 10s) before polling again.

Wire changes (mirrored with the server): ClaimRequest drops wait_seconds and
MAX_CLAIM_WAIT_SECONDS is removed; RegisterResponse gains poll_interval_seconds,
which the worker adopts like heartbeat_interval_seconds and rejects if
non-positive. WorkerConfig drops claim_wait_seconds and the
request_timeout_seconds > claim_wait_seconds constraint. Contract fixture updated
in lockstep with the agenteye copy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* fix(evaluator): sandbox managed source in a killable process (hermes SEC-001)

Managed (server-authored) evaluations ran in an in-process ThreadPoolExecutor
with an asyncio.wait_for timeout that only cancelled the awaiter — Python cannot
kill the running thread, so `sum(range(10**20))` kept burning CPU well past the
timeout and tied up the sole worker slot; conditions ran with no timeout at all.

Run managed conditions/evaluators in a forked child with hard RLIMIT_CPU +
RLIMIT_AS + a parent-side wall-clock SIGKILL, killed on timeout before capacity
is released. The kernel enforces the limits on a separate process the parent can
terminate outright — the one thing a thread cannot do. Only the result crosses
back, as a small pickle, with the child's exception semantics preserved.
`resource` is imported at module level (never in the child) and the child does
only eval->pickle->write->_exit, so the fork holds no lock another thread owns.
Defense in depth at compile: reject `**` with a large/non-constant exponent and
cap AST size. A managed condition the sandbox rejects now dead-letters as
condition_error instead of raising out of the plan loop.

Also require execution_mode on the wire (F2): a falsy/missing value was silently
coerced to `local`, running a `python` definition down the customer path.

Only server-authored source is isolated; customer evaluators run their own
trusted code in-process. New tests cover the compute/condition bombs, the AST
bounds, the fork result round-trip, and the execution_mode rejection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* fix(evaluator): fail closed when the fork sandbox is unavailable (hermes SEC-001)

The non-POSIX fallback in _run_killable ran managed source directly (`return
fn(session)`), so on a platform without os.fork an allowed-but-expensive
expression like `sum(range(10**20))` got NO CPU/memory/wall-clock enforcement and
could hold a worker slot indefinitely. Refuse instead: raise
EvaluationSandboxUnavailable rather than execute server-authored source without a
killable boundary. The managed worker only ships on Linux (fork present), so this
never trips in production; it closes the "no fork => no sandbox => run it anyway"
gap. New test simulates a fork-less platform and asserts managed eval + condition
both fail closed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* fix(evaluator): fork+exec the sandbox + clamp the timeout (hermes SEC-001)

Two SEC-001 problems in the fork-based sandbox:

1. os.fork() DEADLOCKS the worker. The managed worker is multi-threaded (asyncio
   loop, executor pool, writer daemon, health server); forking it and running
   Python in the child hangs on a lock another thread held at fork. Reproduced in
   the container: the worker registered, forked on its first managed eval, and
   hung (health down, no progress). Unit tests missed it because they fork from a
   single-threaded context.

   Replace fork-and-run-Python with fork+EXEC: a fresh
   `python -m failproofai_sdk.evaluator._sandbox_runner` process reads the
   (kind, source, transcript-wire, limits) tuple, installs RLIMIT_CPU + RLIMIT_AS
   on itself, evaluates, and returns the pickled result; the parent bounds
   wall-clock with subprocess timeout + kill. exec clears the inherited lock
   state, so it is safe from a multi-threaded process. Verified under real
   background-thread churn: normal eval works, compute + condition bombs are
   killed at budget, no hang.

2. The server-provided per-definition timeout had no upper bound, so a large
   timeout_seconds removed the execution bound. Clamp the effective CPU/wall
   budget to MAX_SANDBOX_TIMEOUT_SECONDS (60s).

Fails closed (EvaluationSandboxUnavailable) if the sandbox cannot be spawned or
the transcript is not serializable. Tests now use a real SessionTranscript (it
must cross the process boundary via to_wire).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* fix(evaluator): bound the sandbox result on both sides (hermes SEC-001)

The parent read the sandbox child's entire stdout and pickle.loads'd it, so a
permitted expression building a huge result (metrics={str(x):1 for x in
range(100000)}) could OOM the worker despite the child RLIMIT. Bound it on both
sides: the child now validates the result (result_items / the 25-result limit)
and refuses to serialize anything over SANDBOX_MAX_RESULT_BYTES (1 MiB) before it
crosses; the parent reads via a temp-file-in / Popen with a capped, timed stdout
read and kills the child on overflow or timeout. Input moves to a temp file (the
transcript can be large; feeding a big stdin while bounding stdout invites a pipe
deadlock). eval_key is threaded to the sandbox so the child can enforce the
25-item limit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* fix(evaluator): bound aggregate sandbox memory (hermes SEC-001)

A 2 GiB per-sandbox limit did not bound the host: max_concurrency up to 32 could
run 32 sandboxes at once (~64 GiB), and a permitted expression can allocate memory
before returning a valid result (`([0]*200000000, EvalResult(...))[1]`). Lower the
per-sandbox address space to 512 MiB (generous for a <=25 MiB transcript, rejects
the ~1.6 GiB allocation) AND cap concurrent sandbox processes with a semaphore, so
the aggregate (~2 GiB) is bounded independent of the worker's claim concurrency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* Harden evaluator v2 sandbox; fix condition selection and error mirror

- COR-001: managed (python) applicability now follows the server's
  condition_source, never a colliding local condition with the same
  (eval_key, eval_version). Condition selection branches on execution_mode,
  mirroring the evaluator branch below it.
- API-001: recognize the server's `incomplete_plan` terminal error — added to
  the ERROR_SPECS mirror and the shared contract.json fixture (byte-identical
  with the server).
- Adversarial-audit (SEC): close a heap-address disclosure bypass. The
  output-boundary `<obj at 0xADDR>` guard was anchored on `<`, so an allow-listed
  str(x).replace("<","") / f-string / % kept the live address while stripping the
  match. The defense moves to compile time: a bound method (the only reachable
  value with a pointer repr — transcript and result types are frozen, pointer-free
  dataclasses) may only be CALLED, never referenced as a bare value, so no
  reachable value carries a pointer repr through str()/f-string/%. The
  output-boundary scan is kept and broadened (no leading `<`) as defense in depth.

Regression tests cover the colliding-key condition, the three bypass payloads,
and that legitimate called-method/data-attribute stringification still works.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* Count sandbox-slot wait against the execution timeout (hermes SEC-001)

`_run_sandboxed` acquired the MAX_CONCURRENT_SANDBOXES slot with an unbounded
wait and only started its wall-clock deadline afterward. The runtime runs this in
a thread and `asyncio.wait_for` cancels only the awaiter, so a run queued behind
busy slots could — after its caller was already reported timed out — still
acquire a slot and launch a sandbox; 28 threads could pile up behind 4 long
sandboxes and starve the worker (conditions have no runtime-level wait at all).

One wall-clock deadline now covers BOTH the slot wait and execution: the slot is
acquired with the remaining budget, and on timeout (or a slot acquired exactly at
the deadline) the run raises EvaluationTimeout without spawning a child.
Regression test: more concurrent compute bombs than slots all resolve within ~one
budget, not N serialized budgets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* fix(evaluator): report why source was rejected; scrub the sandbox child's env

Found reviewing #758 alongside agenteye#652 as one system, with a
real server, a managed worker and two customer-hosted workers running.

Surface the reason a server-authored definition was rejected
  Every failure collapsed to "evaluation raised <TypeName>", so a hosted
  definition that can never run reported only "evaluation raised
  UnsafeEvaluatorSource" — on every session, forever, with nothing telling the
  author what was wrong. It matters because the server accepts any source that
  passes its size and key checks: it does not (and in Rust cannot cheaply)
  validate the sandbox's single-expression grammar, so a definition that is
  structurally unrunnable is published with 201 Created and then fails silently
  per-session. Observed exactly that end to end: a perfectly ordinary
  multi-statement evaluator was accepted by the API and failed every session
  with no diagnosis. UnsafeEvaluatorSource now reports its detail
  ("evaluator_source must be one expression"), bounded to
  MAX_ERROR_MESSAGE_BYTES.

  Deliberately narrower than the generic handler, which still reports the type
  name only: UnsafeEvaluatorSource is raised by our own validator BEFORE any
  customer source executes and its message describes the source's shape, so it
  carries no transcript content — whereas an arbitrary eval exception can quote
  the transcript it was reading into a field that is persisted and displayed.
  Ordered before `except Exception` so it is reachable (it subclasses
  ValueError).

Scrub the sandbox child's environment
  subprocess.Popen inherited os.environ, so the sandbox executing untrusted
  server-authored source ran with FAILPROOFAI_EVALUATOR_TOKEN in its
  environment — on the FailproofAI-managed pod, the cross-tenant credential the
  whole fleet authenticates with. The AST allowlist and empty __builtins__ stop
  a managed expression from reaching os.environ today, so this is defence in
  depth rather than a live escape: it means a future gap in those restrictions
  cannot be escalated into credential theft. Only what the interpreter needs is
  forwarded, PYTHONPATH included — without it the child cannot import the
  sandbox runner at all.

Verified: 148 evaluator tests pass. The sandbox itself held under direct
attack — open()/eval()/globals() die as NameError on empty builtins, dunder and
introspection attributes are refused at compile time, and a 10**9-element
allocation bomb was contained as a per-eval MemoryError in 497ms with no host
memory movement.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(evaluator): give the condition compute bomb a margin that survives a fast interpreter

`test_condition_compute_bomb_is_also_bounded` failed on CI under Python 3.14
after the previous commit, while passing locally — the signature of a
machine-speed coin flip rather than a real regression.

The cause is the bomb's size, not the sandbox. The test asserts that a CPU-bound
condition cannot finish inside its budget, but used `sum(range(10**8))` against a
1-second budget: ~1.35 CPU-seconds measured on 3.14, a 1.35x margin. On a fast
enough runner the sum simply completes and nothing times out. 3.14 is the version
that fails first because it is the fastest — 1.35s against 3.13's 1.44s here.

Its evaluator twin one function above already uses `10**9` (~13x margin) for the
same 1-second budget, so the condition variant was carrying a bomb ten times
smaller for no stated reason. Matching it restores the margin and costs no
wall-clock: the sandbox kills the child at its budget either way, so a bigger
bomb only widens the gap between "killed" and "could have finished". The test
still completes in ~1.06s.

This changes a test rather than the code because the code is correct — the
property under test (a CPU bomb in a condition is stopped by the sandbox budget)
is unchanged and now actually verified rather than raced. The threshold was the
defect.

Also records this PR's two SDK fixes in the changelog section they belong to.

Verified with CI's own command on the version that failed:
`uv sync --locked --extra dev --python 3.14 && uv run pytest tests/ -q`
— 961 passed, 9 skipped, 0 failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fp-cloud-cli: mirror the new evaluations:run permission

Adds `evaluations:run` to ALL_PERMISSIONS (in the server's declared order, after
evaluations:trigger). It is a normal key-assignable grant — the credential a
customer evaluator pod authenticates with — so `fp keys create --permission
evaluations:run` and `fp users` accept it, it lands in the admin preset, and it
stays out of read-only/standard. Mirrors server auth.rs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* sdk(evaluator): fail closed when kernel resource limits are unavailable

SEC-001 (hermes review): on a platform without the stdlib `resource` module
(e.g. Windows), the sandbox child's `_install_limits` no-ops, so a permitted
expression could allocate unbounded memory in each managed-worker child before
the parent's wall-clock kill lands — the advertised RLIMIT_AS cap is never
imposed. `_run_sandboxed` now refuses with `EvaluationSandboxUnavailable` BEFORE
spawning any child when `_resource` is unavailable, rather than run
server-authored source unbounded. Regression test verifies no child is started.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* sdk(evaluator): bound the condition phase by the lease; make sync-eval thread leaks observable

Two hermes advisories on the v2 worker runtime.

Pre-plan condition phase vs the lease. Conditions were evaluated serially with
no lease awareness before the plan was created, and a managed condition may run
its full sandbox budget — so a few near-budget conditions could burn the whole
assignment lease before the plan request, and the server then fences the plan as
lease_lost and reclaims the assignment in a loop instead of recording a result.
The lease cannot be renewed before the plan (the server only extends a lease for
a planned assignment with running runs), so the worker now bounds the phase to
the lease: each condition is capped to the time remaining before a plan-submission
margin (using assignment.lease_expires_at when it is in the future, else the
negotiated lease duration measured from now, which keeps the bound from firing
spuriously on a replayed/stale deadline in tests or under clock skew), and once
that budget is gone the remaining conditions are skipped as `lease_exhausted`
rather than run. Local conditions, which previously ran with no timeout at all,
are bounded the same way. The complete fix — renewing the lease during the
condition phase — needs a server-side pre-plan heartbeat and is tracked separately.

Timed-out synchronous evaluators. A synchronous evaluator that overruns its
timeout runs in the executor thread and cannot be cancelled (CPython cannot
interrupt a running thread), so its thread is orphaned; with the pool sized to
the concurrency limit, one orphan on a single-slot worker silently stopped all
further local evaluation. The eval executor now carries headroom over the
semaphore so an orphaned thread does not immediately starve live capacity — the
semaphore stays the true concurrency bound — and each orphan increments
`sync_evaluations_orphaned` and logs a warning naming the evaluator so a hung one
is findable. This is a finite cushion, not a cure for a permanently-blocked
evaluator; the CHANGELOG points authors at async or managed evaluators for long
or untrusted work.

Tests: sync-timeout orphan counting + executor headroom; lease-exhausted
conditions are skipped without running; lease-phase deadline honors a future
lease and falls back to the duration for a stale one; condition budget caps by
both remaining lease and per-definition timeout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* sdk(evaluator): let a managed comprehension read session on Python 3.10 (hermes COR-001)

The sandbox eval passed `session` in the eval locals mapping
(eval(code, fresh_globals, {"session": session})). A list/set/dict comprehension
runs in its own scope and resolves a free name like `session` from GLOBALS, so on
CPython 3.10/3.11 — supported versions — an allowed source such as
`all([session.event_count > 0 for i in range(1)])` raised NameError. `session`
now goes in the fresh per-call globals mapping and both the condition and
evaluator eval paths use empty locals, so a comprehension resolves it while
per-call isolation is preserved. Verified on 3.10 (reproduced the NameError
without the fix) and passing 3.10–3.14. Regression test:
test_comprehension_body_can_read_session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018v4UWTsTpQaxMGdt7sJVtW

* chore(security): time-box the nltk advisory that has no fix

OSV turned red on GHSA-8mgp-746c-j5xp (nltk 3.10.3, CVSS 8.3), surfaced
2026-09-03 against a branch that neither introduced it nor can resolve it —
the surface-late case `osv-scanner.toml` documents.

nltk is transitive via llama-index-core and appears only in
`sdk/python/uv.lock`, the dev/test lockfile that pins every extra so CI can
exercise the adapters. The SDK's runtime dependency list is empty and the
`llamaindex` extra is imported lazily, so nltk reaches no shipped code path.
OSV reports "0 vulnerabilities can be fixed" with an empty FIXED VERSION;
there is nothing to bump to, and the only alternative is dropping the extra
and its adapter tests.

`[[IgnoredVulns]]`, not `PackageOverrides`, so any OTHER advisory against nltk
still blocks — including a fixable one, which is the signal worth keeping.
Dated to match the chromadb entries so the whole list is re-reviewed in one
pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WXCEX4M9pjMUaM3vM237L

* sdk(evaluator): fix five worker-runtime defects found in review

Applied while red-teaming the pull-based Evaluator v2 worker against a live
stack (agenteye #652 / failproofai #758). All five reproduced live and are
covered by tests/test_evaluator_review_fixes.py; the full evaluator suite
(153 tests) still passes.

- authoring._bounded: reject C0 control characters and DEL in bounded result
  text (reasoning/summary/description/display_value/unit/label), matching the
  server's check_bounded. The SDK previously validated only byte length, so a
  result quoting transcript text with a NUL or ANSI escape was accepted locally
  then rejected by the server with a NON-RETRYABLE 422 — a successful eval was
  silently lost and its assignment dead-lettered. TAB/LF/CR are kept.

- authoring.EvalResult.result_items: carry the eval's `reasoning` on the PRIMARY
  result (result_key == eval_key) for metric- and assertion-kind evals, not only
  on a score item. A non-score eval previously dropped its reasoning entirely.

- runtime._execute_run_in_slot: always bound an evaluation by wall clock. A LOCAL
  (customer-authored) eval declaring no timeout_seconds ran with NO timeout — a
  hung await or unbounded judge call wedged the worker slot and held the lease
  forever. Falls back to DEFAULT_EVAL_TIMEOUT_SECONDS (300s, the contract's
  per-eval default); managed evals keep their tighter in-sandbox cap.

- runtime._execute_run_in_slot: log the full traceback LOCALLY when a customer
  eval raises. The wire error stays scrubbed to the exception type (it can quote
  transcript data), but the pod now records the real exception so an author can
  debug their own eval instead of seeing only "evaluation raised HTTPError".

- source._OBJECT_REPR: scope the output-boundary guard to the " at 0x..." repr
  tail and drop the bare "0x"+hex alternative, which false-rejected legitimate
  result text (a hex colour like 0xFFFFFF, a digest, an address the agent
  logged) as an embedded object repr. The tail still catches a '<'-stripped repr.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* sdk(evaluator): register insufficient_permissions in the error contract

The server returns error code `insufficient_permissions` (HTTP 403) when a
worker key lacks `evaluations:run` (server worker_auth.rs), but that code was
absent from the frozen ERROR_SPECS on both sides and from both contract.json
fixtures. Add it (403, non-retryable) to the SDK's ERROR_SPECS and the SDK
fixture; the mirroring server-side entry lands in agenteye #652. Keeps the two
contract.json fixtures byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* sdk(evaluator): harden the run loop against lease loss, replay, and too-large transcripts

Follow-up review fixes (agenteye #652 / failproofai #758), all covered by
tests/test_evaluator_review_fixes.py; evaluator suite (145 tests) green.

- _heartbeat now beats IMMEDIATELY before its first sleep. The pre-plan
  condition phase can consume most of the claim-time lease, and the server only
  renews a planned assignment's lease on heartbeat — so sleeping a full interval
  before the first beat let the lease expire after the runs had started and
  cancelled every one. (Server-side: the plan handler also extends the lease.)

- process_assignment tolerates an idempotent replay whose run set diverges from
  this attempt's plan. On reclaim the server re-serves the FIRST attempt's runs,
  which are authoritative — even one this attempt's condition would have skipped.
  The status-equality check is skipped on replay, and a replayed run absent from
  this attempt's selection is reconstructed from the assignment's descriptors
  instead of raising and dead-lettering an assignment that could never converge.

- process_assignment catches transcript_too_large at the transcript fetch and
  returns cleanly (logging + a metric) instead of propagating. No runs are
  planned yet, the error is non-retryable, and re-raising wedged the poll loop
  and burned the whole retry budget — reproduced live, where a 28.6 MiB session
  froze the pod. The server terminalizes the assignment as too_large.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: chhhee10 <chetanraghuvanshi85@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants