247 Commits

Author SHA1 Message Date
Vasudev Awatramani c713fb7009 tests: expand coverage for WorkflowClient and TickPersistenceDecorator (#663) 2026-07-12 17:59:38 -04:00
Adrian Lyjak 26050d5803 Key state storage by run id and namespace (#724) 2026-07-07 18:48:04 -04:00
Adrian Lyjak 126d40a66a Pin snapshot and journal serialization formats with golden fixtures (#723) 2026-07-07 12:25:16 -04:00
llama-org-ci-bot[bot] 02f96757e1 chore: version packages (#722)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and publish to npm
yourself or [setup this action to publish
automatically](https://github.com/changesets/action#with-publishing). If
you're not ready to do a release yet, that's fine, whenever you add more
changesets to main, this PR will be updated.


# Releases
## llama-agents-server@0.6.4

### Patch Changes

- c42b064: Retry transient Agent Data read failures and use a longer
default timeout.

Co-authored-by: llama-org-ci-bot[bot] <231146559+llama-org-ci-bot[bot]@users.noreply.github.com>
2026-07-06 12:40:45 -04:00
Diego Kiedanski c42b064851 AgentDataClient retries transient read failures (#719)
AgentDataClient used the default httpx timeout on its shared client, so
a slow Agent Data search could fail after 5 seconds as a `ReadTimeout`
with little useful message. Cloud persistence reads through this path,
and search is the only idempotent Agent Data operation in this client.

This branch gives the shared client a 30 second read timeout with a 5
second connect timeout, then retries transient search failures with
bounded exponential backoff. The retry path covers transport failures
and 500/502/503/504 responses. Create, update, and delete paths still
make one attempt because Agent Data writes do not have idempotency keys;
replaying an ambiguous write timeout could duplicate or overwrite data.

The retry code keeps timeout and attempt config type-stable, validates
attempt counts, and has server coverage for retryable reads, final
failure, 4xx handling, and all write methods as non-retried. It also
adds a patch changeset for `llama-agents-server`.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Adrian Lyjak <adrianlyjak@gmail.com>
2026-07-06 12:21:05 -04:00
llama-org-ci-bot[bot] db8cd1b69f chore: version packages (#718) 2026-07-02 16:24:41 -04:00
Adrian Lyjak b7936a6310 Time out kube-apiserver requests and add real health probes (#717) 2026-07-01 18:14:23 -04:00
Adrian Lyjak b8ecfa2ac9 Stop patch releases from bumping dependent packages (#715) 2026-06-30 17:19:17 -04:00
llama-org-ci-bot[bot] 9a5036184b chore: version packages (#713) 2026-06-30 16:55:48 -04:00
Adrian Lyjak 0dff2ccebb Don't crash ctx.store edits on non-deepcopyable state values (#714)
Every `ctx.store` write copies the whole live state before the edit, so
a concurrent reader keeps seeing the committed state until the
`edit_state` block finishes. The copy is a deep copy, and that is the
problem. State often holds live objects that cannot be deep-copied. A
`Memory` wraps sqlalchemy, aiosqlite, and tiktoken modules. An LLM
client wraps a `threading.Lock`. Deep-copying either one throws:

- `wf.run(memory=Memory.from_defaults(...))` raises `TypeError: cannot
pickle 'module' object` (#709)
- `await ctx.store.set("client", some_client)` raises `TypeError: cannot
pickle '_thread.lock' object` (#710)

The deep copy only exists to isolate data a reader could otherwise catch
mid-edit. A live handle like memory or an LLM client is shared by
reference across the whole run anyway, so copying it gains no isolation.
It just crashes. So copy each entry on its own, and when one will not
deep-copy, keep the reference instead.

Plain data like dicts, lists, and scalars still deep-copies, so edit
isolation is unchanged for anything a reader could actually observe
torn. Typed state that is not a `DictState` still copies whole-model,
falling back to a shallow copy if some field refuses to deep-copy
instead of raising.

Fixes #709, fixes #710.
2026-06-30 16:48:56 -04:00
Adrian Lyjak 8be81d5306 Reload workflow failures without crashing on un-reconstructable exceptions (#711)
Rehydrating a persisted `WorkflowFailedEvent` could take down the whole
event/context reload. `_deserialize_exception` rebuilt the original
exception with `cls(message)`, but only caught `(ImportError,
AttributeError, ValueError)`. Any exception whose constructor doesn't
take a single positional message raised `TypeError`, which escaped
`model_validate` and crashed `JsonSerializer.deserialize`.
`json.JSONDecodeError(msg, doc, pos)` and `LLMError(error_type,
message)` are the common cases. One awkward exception type on a failed
run made that run unloadable.

Reconstruction is now total. It returns an `Exception` for any input and
never raises. When the real type can't be rebuilt, it degrades to a new
`UnreconstructedException` carrying the original qualified type name and
message. A type can't be rebuilt when it is unimportable, disallowed by
the allowlist, or has a constructor that rejects `cls(message)`.

```python
exc = json.JSONDecodeError("Expecting value", "doc", 0)
reloaded = roundtrip(WorkflowFailedEvent(exception=exc, ...))
# before: TypeError escapes, the whole reload dies
# after:  UnreconstructedException, .original_type == "json.decoder.JSONDecodeError",
#         str(reloaded.exception) == str(exc)
```

`str(UnreconstructedException(msg))` is the original message with no
type prefix, so a re-serialize cycle doesn't double-wrap it. Importable
single-argument exceptions still round-trip by type identity.

## Allowlist gating

The same code path had an asymmetry worth closing. Every other
deserialized type runs through `JsonSerializer`'s `allowed_types` gate,
but the exception path called `import_module_from_qualified_name`
directly. A checkpoint blob could name any importable `module.Class` and
the loader would import and call it. Exception types now honor the same
allowlist. `builtins.*` is exempt, an unset allowlist stays permissive
to match how `allowed_types` already works, and anything else has to be
a member or it degrades without importing.

Reconstruction also only constructs genuine `Exception` subclasses. The
qualified name comes from the blob and could resolve to any callable.
Without that check the `builtins` exemption would run
`builtins.eval(message)` on an attacker-controlled message even under a
strict allowlist. A blob naming a non-exception builtin now degrades
instead of being called.

The allowlist reaches the validator through a `ContextVar` set around
`model_validate`, not pydantic's `model_validate(context=...)`. The
event models (`DictLikeModel`) define a custom `__init__`, and pydantic
drops the validation context before the nested field validators run, so
the context never arrives at the exception field.

Gating is best-effort by design. It covers the top-level failure
carriers (`WorkflowFailedEvent`, `StepFailedEvent`, `StepWorkerFailed`).
Serialization still stores only type and message, so `original_type` is
a breadcrumb that is dropped on re-serialization.
2026-06-30 15:39:11 -04:00
llama-org-ci-bot[bot] 53198bdccd chore: version packages (#706) 2026-06-22 15:48:58 -04:00
冯基魁 36fffec9d9 fix(workflows): keep implicit waiters distinct across fan-out (#704) 2026-06-22 15:38:02 -04:00
Adrian Lyjak d16172ecef Fix DBOS idle release never releasing workflows (#703) 2026-06-22 14:43:00 -04:00
Adrian Lyjak 52fe4ad5af Run durable workflows in process (#699) 2026-06-18 18:50:42 -04:00
llama-org-ci-bot[bot] e164a5c75a chore: version packages (#690)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and publish to npm
yourself or [setup this action to publish
automatically](https://github.com/changesets/action#with-publishing). If
you're not ready to do a release yet, that's fine, whenever you add more
changesets to main, this PR will be updated.


# Releases
## llama-index-utils-workflow@0.11.0

### Minor Changes

-   aee5fda: Add typed runtime step identities.

### Patch Changes

-   44887eb: Sanitize slashes in Mermaid execution diagram step IDs.

## llama-index-workflows@2.22.0

### Minor Changes

-   cb89120: Add `list[E]` fan-out returns and `list[E]` fan-in joins.
- fd223e8: Steps can declare multiple single-event parameters to fire
once when one of each has arrived.
-   aee5fda: Add typed runtime step identities.

### Patch Changes

- 34e166c: Fix idle checks racing buffered events and stale
`ctx.collect_events()` firings.
- 5724404: Preserve retry state for in-progress workflow steps across
serialized context resume.
- 58e0174: Fix ctx.store.get() returning bound dict methods instead of
stored values for state keys named items, keys, values, or get

## llama-agents-client@0.3.9

### Patch Changes

-   Updated dependencies [34e166c]
-   Updated dependencies [cb89120]
-   Updated dependencies [fd223e8]
-   Updated dependencies [5724404]
-   Updated dependencies [58e0174]
-   Updated dependencies [aee5fda]
    -   llama-index-workflows@2.22.0

## llama-agents-server@0.6.1

### Patch Changes

- 71a1aac: Fix startup resume incorrectly failing newly-created handlers
before their first persisted tick
-   Updated dependencies [34e166c]
-   Updated dependencies [cb89120]
-   Updated dependencies [fd223e8]
-   Updated dependencies [5724404]
-   Updated dependencies [58e0174]
-   Updated dependencies [aee5fda]
    -   llama-index-workflows@2.22.0
    -   llama-agents-client@0.3.9

Co-authored-by: llama-org-ci-bot[bot] <231146559+llama-org-ci-bot[bot]@users.noreply.github.com>
2026-06-17 17:46:33 -04:00
Adrian Lyjak 8dc16b6987 Split the control loop into runner/reduce/streams and decompose the reducer god-functions (#694)
`control_loop.py` was a single 2128-line module fusing two unrelated
concerns — the stateful async runtime and the pure reducer — with the
reducer dominated by two functions that read as walls:
`_process_step_result_tick` (~400 lines) and `_process_add_event_tick`
(~230 lines). You couldn't follow either in one pass.

This splits the module into a package along the reducer pattern's
natural seam and breaks the two god-functions into named phases. No
behavior change: the reducer is pure, so the existing transformation and
collection-stream suites pin command ordering and stream accounting.

## Package split

`control_loop.py` becomes `control_loop/`:

- `runner.py` — the async runtime: worker tasks, the scheduled-wakeup
heap, the adapter, turning reducer commands into side effects.
- `reduce.py` — the pure reducer: `_reduce_tick` and every per-tick
processor, plus retry/collect/replay helpers. `State + Tick -> (State,
Commands)`.
- `streams.py` — collection-stream accounting: fan-out stream lifecycle,
open-work-item counting, collect-batch release.

The dependency graph is a DAG: `runner -> reduce -> streams`. The one
back-edge (`streams`' release path needs `reduce`'s
`_add_or_enqueue_event`) is a single inline import at that chokepoint,
commented.

The package facade exports only the cross-package API — `control_loop`,
`rebuild_state_from_ticks`, `rebuild_state_from_ticks_stream`,
`replay_ticks_stream` — the symbols sibling packages (server, dbos,
`external_context`, `step_function`) actually import. White-box tests
import the internals they exercise straight from the owning submodule
(`.reduce` / `.runner` / `.streams`). `import time` stays in the facade
because tests patch `control_loop.time.time`, and the submodule loggers
keep the `workflows.runtime.control_loop` name so caplog filters still
match.

## Decomposing the reducer functions

Both now read top-to-bottom as a sequence of named steps.
`_process_step_result_tick`:

```python
this_execution = _find_in_progress(worker_state, tick.worker_id)
rerun = _rerun_for_stale_collect_buffer(tick, worker_state, this_execution)
if rerun is not None:
    return state, rerun
scope = _fan_out_scope(state, step_name, this_execution, fanned_out=fanned_out)
for result in tick.result:
    _apply_step_result(result, ..., acc=acc)
acc.commands.extend(_resolve_work_item_in_stream(tick, state, scope, acc, now_seconds))
# finalize: NOT_RUNNING transition, drop from in_progress, drain queue
```

The per-result flags that were locals threaded through 400 lines now
ride on a small `_StepResultAcc` carrier, and the ~120-line
retry/`catch_error` branch is extracted to
`_schedule_retry_or_route_failure`.

`_process_add_event_tick` splits into its three real phases:
`_redeliver_collection_payload` (payload-carrying ticks route straight
to their collect target and stop), `_resolve_waiters`, then
`_route_to_accepting_steps` — with the gnarly collect-member routing
pulled into `_route_member_to_collect_step`.

One rename: `_apply_stream_work_delta` -> `_adjust_open_work_items`,
matching the `open_work_items` field it mutates.
2026-06-16 19:43:33 -04:00
Adrian Lyjak cb89120d29 Join fan-out streams with list[E] parameters (#674) 2026-06-16 15:22:10 -04:00
Adrian Lyjak 34e166cd41 Fix idle checks and stale collect buffers (#687) 2026-06-16 12:24:44 -04:00
Adrian Lyjak fd223e86ce Add multi-parameter fan-in steps (#673) 2026-06-15 20:45:20 -04:00
Adrian Lyjak aee5fdaf1e Add typed runtime step ids (#685) 2026-06-15 20:22:39 -04:00
Adrian Lyjak 44887eb438 Sanitize slashes in Mermaid execution step IDs (#684)
Mermaid execution diagrams only normalized `:` and `#` in tick-derived
node IDs, so a slash in a step name was emitted into the Mermaid ID even
though the visible label rendered fine.

The renderer now runs execution IDs through the shared Mermaid ID
sanitizer, and that sanitizer also normalizes slashes for graph
rendering.
2026-06-15 19:48:42 -04:00
Adrian Lyjak d8ab2a6964 Add list return fan-out syntax (#683) 2026-06-15 19:39:05 -04:00
Adrian Lyjak 5724404343 Preserve in-progress retry state in serialized contexts (#680) 2026-06-15 18:17:20 -04:00
Adrian Lyjak 71a1aac20d Ignore fresh handlers during startup resume (#681) 2026-06-15 18:10:22 -04:00
Adrian Lyjak 58e0174747 Resolve dynamic state keys before mapping methods in store path lookups (#669) 2026-06-15 17:05:34 -04:00
llama-org-ci-bot[bot] 335f52e0ea chore: version packages (#643) 2026-06-15 16:33:14 -04:00
Adrian Lyjak 070fc70f91 Centralize workflow state decoding, seeding, and locking (#659) 2026-06-15 16:19:50 -04:00
Adrian Lyjak 41e354a312 Keep delayed retries in serialized state so snapshots during the delay window resume (#664) 2026-06-15 13:22:54 -04:00
Adrian Lyjak c66b3f0e3d Upgrade basedpyright hook to 1.39.7 so pre-commit works on Python 3.14 (#676)
basedpyright 1.31.1 (the current pre-commit rev) can't parse Python
3.14's stdlib `string/templatelib.py` (t-strings). With a 3.14 project
venv, every file-scoped run — i.e. what pre-commit does on `git commit`
— reports `Cannot access attribute "interpolations" for class "str"`
against templatelib for *any* changed file, blocking commits unless you
`--no-verify`. Full `pre-commit run -a` and CI don't hit it (CI resolves
ubuntu's system 3.12), so it only bites local dev on newer interpreters.

This bumps the mirror rev to 1.39.7 and fixes the 12 errors the newer
version legitimately flags, all `reportPrivateImportUsage` — symbols
imported through modules that only re-export them transitively:

- `WorkflowTick` now imported from `workflows.runtime.types.ticks`
instead of `.plugin`
- `Auth`/`Environment` from `llama_agents.cli.config.schema` instead of
`._config`
- `httpx` imported directly instead of through `manage_client`
- `DeploymentNotFoundError` from `llama_agents.core.server.manage_api`
(its public export) instead of through the control-plane service module
- tests patch `dulwich.repo.Repo` and stdlib `webbrowser` directly
instead of reaching through the module under test (same objects, so
identical behavior)

1.39.7 also warns on the wildcard imports in the `llama_deploy`
back-compat shims; those are intentional re-exports and warnings don't
fail the hook, so they're left alone.

Verified the hook passes both file-scoped and full runs under 3.12,
3.13, and 3.14 venvs.

Open question for reviewers: `lint.yml` doesn't pin a Python for
`setup-uv`, so CI lint type-checks against whatever ubuntu ships
(currently 3.12). Worth pinning for determinism, but not done here.
2026-06-11 18:28:32 -04:00
Adrian Lyjak 4dfd394451 Add regression tests for ctx.to_dict() after breaking out of stream_events() (#671) 2026-06-11 16:36:08 -04:00
vanraj Solanki db1258b243 support subclass-aware event routing (#656) 2026-06-11 14:43:30 -04:00
llama-org-ci-bot[bot] 5fd64dc91b Update debugger assets (#662) 2026-06-05 13:34:46 -04:00
HaoJun 9a4dd16183 feat(workflows): add opt-in type allowlist for JsonSerializer deserialization (#640) 2026-05-22 09:57:05 -04:00
llama-org-ci-bot[bot] 7ff5325302 chore: version packages (#635)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and publish to npm
yourself or [setup this action to publish
automatically](https://github.com/changesets/action#with-publishing). If
you're not ready to do a release yet, that's fine, whenever you add more
changesets to main, this PR will be updated.


# Releases
## llamactl@0.10.2

### Patch Changes

- 06ff626: Fix `projects use <id>` and `projects get <id>` failing with
"not found" for projects outside the default org
- abc176e: Only auto-push push-mode updates from repos with the
deployment remote configured.

Co-authored-by: llama-org-ci-bot[bot] <231146559+llama-org-ci-bot[bot]@users.noreply.github.com>
2026-05-14 19:08:08 -04:00
Adrian Lyjak abc176e6ce Safeguard push-mode auto-push (#639)
Push-mode updates were too eager: `edit`, `apply`, and `update`
configured the deployment remote before pushing, so any current git repo
could become the source repo by accident.

This keeps create smooth, but makes updates require an existing
`llamaagents-<deployment>` remote before auto-pushing. `--push` is the
explicit escape hatch for linking and pushing the current repo, and
`--no-push` still skips the push entirely.

Docs now describe the safer default for push-mode deployments.
2026-05-14 17:57:50 -04:00
Adrian Lyjak 06ff626efe fix: projects use/get cross-org lookup (#638)
Tab completion lists projects from all orgs the user can access, but
`projects use <id>` validated against only the default org. A project
from a non-default org would tab-complete fine then fail with "Project
not found".

The issue is `_discover_organization` running before the direct-ID path,
scoping the validation query to whichever org has `is_default=True`.
Moved org discovery to the interactive listing branch only. When the
user passes a project ID directly, validation now queries all accessible
orgs (unless they explicitly passed `--org`). Same fix applied to
`projects get <id>`.
2026-05-14 16:17:08 -04:00
llama-org-ci-bot[bot] 3263e9b0f1 chore: version packages (#633)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and publish to npm
yourself or [setup this action to publish
automatically](https://github.com/changesets/action#with-publishing). If
you're not ready to do a release yet, that's fine, whenever you add more
changesets to main, this PR will be updated.


# Releases
## llamactl@0.10.1

### Patch Changes

-   532a6fa: Fix inline auth recovery while saving deployments.

Co-authored-by: llama-org-ci-bot[bot] <231146559+llama-org-ci-bot[bot]@users.noreply.github.com>
2026-05-11 11:07:24 -04:00
Adrian Lyjak 532a6fa240 Fix deployment save auth recovery (#632)
The github auth flow was lost/overlooked during the refactor to use
$EDITOR. This restores the functionality, waiting for auth and opening a
browser.
2026-05-11 08:47:49 -04:00
llama-org-ci-bot[bot] 7a0b62e6d4 chore: version packages (#612) 2026-05-06 22:43:26 +00:00
Adrian Lyjak 2497225051 Update llamactl auth command docs (#626) 2026-05-06 18:41:16 -04:00
Adrian Lyjak 1452c184cb Restructure llamactl auth commands (#624) 2026-05-06 18:36:30 -04:00
Adrian Lyjak 4df3a02896 docs: refresh llamactl docs (#619) 2026-05-06 14:17:53 -04:00
Adrian Lyjak 4f6c7fd589 Preserve secret placeholders in deployments get (#623) 2026-05-06 13:26:33 -04:00
Adrian Lyjak 865baba041 Polish llamactl command output (#622) 2026-05-05 19:52:34 -04:00
Adrian Lyjak 0b673e8686 Remove dead llamactl Textual UI (#621) 2026-05-05 16:04:29 -04:00
Adrian Lyjak 474b9ee398 Drop questionary, make llamactl non-interactive-safe (#618) 2026-05-05 15:45:24 -04:00
Adrian Lyjak 0b9d6c204f llamactl: use editor loop for deployment create/edit (#617) 2026-05-04 15:09:37 -04:00
Adrian Lyjak 87ef930f8a Add env var based auth to llamactl (#615) 2026-05-03 14:10:08 -04:00
Adrian Lyjak c3fac21383 llamactl: annotate apply failures in YAML (#613) 2026-05-02 11:42:55 -04:00