Commit graph

4776 commits

Author SHA1 Message Date
Bryan Helmkamp
401acb6cdf
Record tool output byte counts 2026-08-24 12:46:27 -04:00
Bryan Helmkamp
ddcdafa06b
Bound agent tool output capture 2026-08-24 12:34:43 -04:00
Scott Werner
b51b80e16e Preserve run creation error context 2026-08-24 12:22:38 -04:00
Scott Werner
fc822ab9b0 Fix CLI RunSpec test fixtures 2026-08-24 12:17:47 -04:00
Scott Werner
e2db53011e Fix RunSpec test fixtures 2026-08-24 12:11:49 -04:00
Scott Werner
d978a6c89d Route both create-run client methods through one submission helper
create_run_from_manifest and create_run_from_intent were byte-identical
apart from the body type; fold the shared request/retry plumbing into a
private submit_create_run(CreateRunRequest) so the two public entry
points stay thin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 11:55:44 -04:00
Scott Werner
96c6de0ab9 Trim create-run request parsing overhead
The create-run dispatcher deep-cloned the parsed JSON body once to
attempt the RunIntent shape and again for the RunManifest fallback,
so every legacy manifest request paid two full copies of a body that
carries entire workflow bundles. Deserialize both shapes from a
reference to the parsed value instead; routing and error attribution
are unchanged.

Also bind the lowered goal slot once in inline_goal_file rather than
re-navigating the settings layer and asserting the goal is still there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 11:55:44 -04:00
Scott Werner
6b65f2a6af Share the create-run pipeline tail between both admission lanes
The intent and legacy-manifest create handlers each carried a full copy
of the same post-admission sequence: LLM readiness resolution, graph
compilation and model pinning, persistence, summary read, managed-run
registration, title-generation spawn, and the 201 response. The copies
had already drifted on when the run ID is resolved (before compilation
in one lane, after in the other).

Extract one finalize_created_run tail, with a small CreatedRunErrorStyle
carrying each lane's pinned error mapping and log lines so the wire
contracts are unchanged. Both lanes now resolve identity before
compilation and share the parent-link validation, which lets the
PinnedRun copy of PreparedRun's identity accessors be deleted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 11:55:44 -04:00
Scott Werner
18d98794ae Move Git-target validation onto RunTarget in fabro-types
The Git-target grammar (slug, branch, and SHA rules plus the derived
origin URL) was implemented twice with no shared code path: once in
server admission and again in sandbox start, so the two could drift and
disagree about which persisted targets are valid.

Own it once as RunTarget::validate() in fabro-types, next to the
primitives it uses, returning the canonical target together with its
derived GitContext projection. Admission consumes it directly, and the
start path re-derives the expected clone source from the same rules
before checking the persisted projection against it. The start path now
also moves the derived strings into the sandbox spec instead of cloning
them.

While reordering admission around the shared validator, run the pure,
in-memory checks (target grammar, environment id) before the blob-store
closure fetch and lowering so malformed requests no longer pay for
version-store I/O.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 11:55:44 -04:00
Scott Werner
040bc6c043 Add version-backed run intent creation 2026-08-24 11:55:43 -04:00
Scott Werner
b3f602f6e9
Merge pull request #648 from fabro-sh/feat/refresh-tokens-sqlite
Move CLI auth sessions from SlateDB to SQLite
2026-08-24 11:24:50 -04:00
Scott Werner
87b49a8527 Merge main into feat/refresh-tokens-sqlite
Preserve the SQLite auth-session release notes alongside main's July 26 fixes and retain all current changelog navigation entries. Make the refresh-token rotation timestamp assertion deterministic after the merged suite exposed its wall-clock race.
2026-08-24 11:04:10 -04:00
Bryan Helmkamp
4e31b79be0
docs: refresh product documentation 2026-08-24 09:53:09 -04:00
Bryan Helmkamp
de29af0a30
docs(changelog): refresh recent product changes 2026-08-24 08:39:06 -04:00
fabro-releases[bot]
2bf86327c0 Bump version to 0.335.0-nightly.0 2026-08-24 09:36:45 +00:00
Bryan Helmkamp
5878723723
Merge pull request #791 from fabro-sh/codex/increase-daytona-snapshot-timeout
Extend Daytona snapshot activation timeout
2026-08-24 03:41:14 -04:00
Bryan Helmkamp
901bb7a6a8
fix: extend Daytona snapshot activation timeout 2026-08-23 17:11:14 -04:00
fabro-releases[bot]
7200d437e9 Bump version to 0.333.0-nightly.0
Some checks failed
Rust / Format (push) Has been cancelled
Rust / Clippy (push) Has been cancelled
Rust / Generated Docs (push) Has been cancelled
Rust / Test (Linux) (push) Has been cancelled
Rust / Test (macOS) (push) Has been cancelled
TypeScript / Typecheck (push) Has been cancelled
TypeScript / Test (push) Has been cancelled
TypeScript / Build (push) Has been cancelled
2026-08-22 04:04:57 +00:00
Bryan Helmkamp
a22d8f48a9
Merge pull request #782 from fabro-sh/prompt-value-budget
Bound what one value may contribute to a prompt preamble
2026-08-21 23:48:49 -04:00
Release Repro
0c50ce641b
Render offloaded prompt values concisely 2026-08-21 23:44:02 -04:00
Bryan Helmkamp
ec5aeeb5c2
Tighten the prompt-demotion pass after review
Apply the cleanup findings from a four-angle review (reuse,
simplification, efficiency, altitude) of the demotion change:

- Share one size gate: serialized_if_over now backs both offload_value
  and demote_value_for_prompt, restoring the cheap short-string and
  scalar pre-checks so per-node demotion no longer serializes every
  small value just to measure it.
- Stop re-writing blobs every node: materialize_value_bytes writes the
  sandbox file directly from the in-hand bytes and short-circuits on the
  content-addressed file's existence, so an already-demoted value costs
  one existence probe instead of a store round-trip per node visit. The
  local file write is shared with materialize_blob_ref.
- Demote over the resolved snapshot map instead of re-snapshotting a
  Context copy, making the context and outcome loops symmetric and
  saving a full deep clone per node; the fidelity lifecycle builds the
  Context after the pass.
- Skip the pass entirely for Full and Truncate fidelities (nothing
  renders context values), except parallel nodes whose branch stash may
  render at a richer fidelity.
- Build is_preamble_hidden_key on is_engine_internal_key instead of
  restating its prefixes, and call it directly from the preamble
  renderer rather than through a wrapper.
- Document that outcome updates are demoted wholesale and that
  BranchWorkItem.item carries the prompt-ready (possibly demoted) item;
  drop the item rebinding and redundant test assertions; restore the
  local integration test's confinement assertion and make the remote
  one non-vacuous.

Skipped by choice: unifying the crate's several truncation helpers and
rendering the marker through the "See:" pointer family (cross-module
coupling out of proportion to the preview cosmetics), per-branch
demotion inside parallel.results (wholesale demotion is what bounds the
total), and cross-node demotion memoization (the file-existence
short-circuit already reduces repeats to a stat).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FH8Jj9Y4E4Tu5g1jwDtHAb
2026-08-21 21:21:59 -04:00
Bryan Helmkamp
58f00c85c8
Merge pull request #776 from jesseproudman/feat/venice-search-provider
Add Venice as a web_search backend
2026-08-21 21:11:42 -04:00
Bryan Helmkamp
3217a05aad
Merge origin/main into feat/venice-search-provider 2026-08-21 21:04:28 -04:00
Bryan Helmkamp
88ed2ac9a3
refactor(search): select backend from available credentials 2026-08-21 20:09:52 -04:00
Bryan Helmkamp
15862aac70
Bound what one value may contribute to a prompt preamble
Compact and summary preambles render workflow context values and stage
outputs with no per-value size limit. A late-run node inherits everything
the run has accumulated, and one oversized value (a join result, a jobs
list, a single-line command emit) can push the composed prompt past the
model's context window. A security-review run failed exactly this way:
its dedupe stage assembled a ~1.8M-token prompt against a 1M-token model
limit, made almost entirely of accumulated context the agent never
needed inline.

Reuse the existing blob machinery at the last mile. Before the preamble
builders run, any resolved context or outcome value whose serialized
JSON exceeds 8KB is persisted as a content-addressed blob, materialized
as a real file in the sandbox, and replaced with a small marker holding
a preview, the byte count, and the file path. The agent reads the file
if it needs the data. for_each items get the same treatment at fan-out
with a more generous 64KB budget, since the item is the branch's work
assignment; branch labels still come from the full item. Keys the
preamble never renders are left alone, and a value that fails to demote
stays inline and is logged: demotion bounds prompt size, it does not
gate execution.

The two downstream-resolution integration tests asserted that resolving
text values writes no files; demotion now legitimately materializes the
oversized response for preamble use, so they instead pin that resolution
returned the full inline text and that nothing is written outside the
sandbox blob directory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FH8Jj9Y4E4Tu5g1jwDtHAb
2026-08-21 20:08:35 -04:00
Bryan Helmkamp
261dad0d58
Merge pull request #779 from fabro-sh/codex/fail-closed-run-event-persistence
Fail runs when event persistence is lost
2026-08-21 19:57:19 -04:00
Bryan Helmkamp
d80dde2320
Merge remote-tracking branch 'origin/main' into codex/fail-closed-run-event-persistence
# Conflicts:
#	lib/components/fabro-workflow/src/operations/start.rs
2026-08-21 19:49:44 -04:00
Bryan Helmkamp
ab900485b6
Merge pull request #770 from fabro-sh/feat/release-push-rescue
Rescue release pushes when origin/main moves mid-release
2026-08-21 19:43:52 -04:00
Bryan Helmkamp
cfa8ae92c0
style: apply pinned rustfmt 2026-08-21 19:34:38 -04:00
Bryan Helmkamp
61394ba2f1
Simplify run-event persistence failure plumbing
- Make the failure watch channel the single record of the latched
  failure; drop the worker task's mirrored local state.
- Replace the hand-rolled wait loop with watch::Receiver::wait_for.
- Extract race_persistence/flush_or_stop helpers so the select!/flush
  scaffolding in RunSession::run exists once instead of three times.
- Return RunEventPersistenceError from append_event_to_sink and add a
  From impl on Error, replacing four hand-written per-event message
  strings with the event name derived from the event itself.
- Dedupe the RunCreated test seed literal in initialize.rs and drop the
  dead BlockingHandler::simulate override.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ryyhtbc1eNtCLw8GjrFQXZ
2026-08-21 19:29:14 -04:00
Bryan Helmkamp
0d028f9b1e
Harden release push recovery 2026-08-21 19:28:55 -04:00
Bryan Helmkamp
45e06d2a6e
Merge pull request #775 from fabro-sh/claude/additional-github-repositories
Additional GitHub repository access
2026-08-21 18:55:31 -04:00
Bryan Helmkamp
ad4f9decb8
Merge pull request #777 from fabro-sh/fix/venice-top-level-cost
fix(llm): capture Venice top-level costs
2026-08-21 18:51:50 -04:00
Bryan Helmkamp
868d8857bf
Merge pull request #778 from fabro-sh/feat/daytona-clone-depth
Support configurable sandbox clone depth
2026-08-21 18:51:06 -04:00
Bryan Helmkamp
09f5bb0f84
Simplify clone depth plumbing
Make RunCloneSettings::DEFAULT_DEPTH the single owner of the default
depth, and interpret the "0 = full history" sentinel in one place via
RunCloneSettings::depth_limit(). Docker's clone_depth becomes
Option<usize> to match Daytona's encoding, with a shared
depth_argument() helper for both git command builders. Drop the
unreachable Option on the resolved depth field, the hand-written
DaytonaSettings::Default, and the pure-forwarding
daytona_git_clone_options helper. The blob-import test helper reuses
the pool's own connect options instead of rebuilding a partial copy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019bgXj5J218RXfiT72qhbLV
2026-08-21 18:37:39 -04:00
Bryan Helmkamp
129fa0ea0c
fix: fail runs when event persistence is lost 2026-08-21 18:31:22 -04:00
Bryan Helmkamp
fe1d9dc691
test: isolate SQLite checkpoint restoration 2026-08-21 17:57:22 -04:00
Bryan Helmkamp
1669791956
test: update clone depth snapshots 2026-08-21 17:47:02 -04:00
Bryan Helmkamp
6179470eb2
feat: default sandbox clone depth to 100 2026-08-21 17:25:58 -04:00
Bryan Helmkamp
4c467cd6ba
refactor(github): simplify repository access checks 2026-08-21 17:12:27 -04:00
Bryan Helmkamp
438bab29f0
feat: support shallow sandbox clones 2026-08-21 17:11:24 -04:00
Bryan Helmkamp
47954f731e
refactor(github): deduplicate additional-repository access plumbing
Consolidate the copies that review found across the feature:

- One GITHUB_CREDENTIAL_HELPER / GITHUB_CREDENTIAL_HELPER_KEY pair in
  fabro-github, with apply_probe_git_env() for probe commands; the runtime
  git bridge, server preflight probe, and live contract test all consume it
  so the probes exercise exactly what the bridge configures.
- GitHubRepositoryAccess::resolve_verified_token() owns the
  resolve-installations-then-mint choreography shared by server preflight,
  workflow initialization, and the live test.
- A shared lookup_installation() helper backs both the shared-installation
  resolution and the mint's installation lookup.
- The contents = read|write rule lives once as
  RunIntegrationsGithubSettings::contents_permission_allows_repository_access.
- The preflight probe paces retries with fabro-sandbox's exported
  replication_backoff() (3s/9s) instead of a contradicting 1s/2s loop, and
  shares one run_ls_remote() runner with the existing remote-ref check.

Also: collapse the dead Ok(None) arm and repeated error blocks in the
preflight token check, drop the derivable bridge_entry_count(), privatize
resolve_permissions() behind resolve_integration(), make
GitHubRepositorySlug ordering/hashing allocation-free, and use EnvVars
constants for env names.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 16:31:48 -04:00
Scott Werner
891eb43e9a
Merge pull request #761 from fabro-sh/codex/strict-legacy-blob-import
Import legacy blobs strictly into SQLite
2026-08-21 16:30:45 -04:00
Bryan Helmkamp
68e3cb8419
fix(llm): capture Venice top-level costs 2026-08-21 15:55:45 -04:00
Jesse Proudman
53efde3930 feat(search): add Venice backend for web_search
Brave stays the default. Shops that already vault VENICE_API_KEY
can drop BRAVE_SEARCH_API_KEY by setting
[server.integrations.search] provider = "venice".

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-21 12:31:03 -07:00
Bryan Helmkamp
84b75f29f1
docs(api): document additional github repository access
- Add `additional_repositories` to the RunIntegrationsGithubSettings
  OpenAPI schema and reuse the canonical Rust settings types through
  `with_replacement`, with type-identity witnesses and JSON parity
  tests for populated and empty repository sets.
- Regenerate the TypeScript API client.
- Document the feature in the GitHub integration and run-configuration
  guides: exact layer replacement rules, single-token scope, gh/API
  support, App-versus-PAT scope, the same-owner/same-installation
  requirement, validation errors, supported Git URL forms, hard-failure
  semantics for declared repositories, GH_TOKEN precedence, and the
  security boundary (no second server-side repository intersection;
  contents = "write" lets any stage push to any declared repository).
  Correct the earlier claim that injecting GITHUB_TOKEN alone makes
  arbitrary additional private clones work.
- Add a dated changelog entry and an opt-in live GitHub App e2e test
  that verifies a scoped multi-repository token reads every declared
  repository (and that a primary-only token cannot), with repositories
  supplied through the test environment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 15:12:23 -04:00
Bryan Helmkamp
d95b6cace1
feat(server): preflight additional github repository access
When a run declares additional repositories, preflight now proves the
whole effective set works instead of treating a minted token as proof:

- It constructs the same validated `GitHubRepositoryAccess` used by
  runtime initialization, so the two paths cannot disagree.
- In App mode it first resolves every repository's installation with
  the App JWT and requires one shared installation ID, naming any
  repository the App cannot see before the mint; then it mints the one
  scoped token, failing with the raw error on rejection.
- Every effective repository gets a non-interactive
  `git ls-remote <url> HEAD` probe through a shared helper that keeps
  the token out of the URL, argv, and errors (a credential helper reads
  GITHUB_TOKEN from the child environment), retries auth-shaped
  failures with the same token to cover replication lag (classified
  via fabro_sandbox::classify_failure), and reports one check per
  repository in deterministic primary-first order under bounded
  concurrency.
- A resolved run environment that defines GH_TOKEN produces a warning
  (gh prefers it over the managed token) without failing preflight.
- With no additional repositories declared, the primary-only mint
  check is byte-for-byte unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:59:45 -04:00
Bryan Helmkamp
d8edd410f3
feat(workflow): bridge git and gh to the shared token
Carry the resolved GitHub integration (permissions plus declared
additional repositories) as one value from run materialization into
workflow startup, and make the sandbox environment reach every declared
repository through the single managed GITHUB_TOKEN.

- `StartServices.github_permissions` becomes
  `github_integration: ResolvedGithubIntegration`; CLI and server
  workers build it with `resolve_integration()` after interpolation and
  pass it through `SandboxEnvSpec` as one unit.
- `build_sandbox_env` constructs the validated
  `GitHubRepositoryAccess` and scopes the App token source to the whole
  effective set. Missing credentials or a missing origin are hard
  initialization errors when additional repositories are declared;
  legacy permissions-only configuration keeps its best-effort behavior.
- When additional repositories are declared, initialization eagerly
  resolves each repository's App installation (naming any repository
  the App cannot see) and the token itself, so an inaccessible declared
  repository fails before the first workflow stage.
- A new `git_bridge` module injects secret-free `GIT_CONFIG_*` entries
  into the stage environment: a github.com credential helper that reads
  `$GITHUB_TOKEN` at invocation time, per-repository SSH-to-HTTPS
  `insteadOf` rewrites, and `GIT_TERMINAL_PROMPT=0`. Entries append
  after a valid user-provided Git config overlay and fail clearly on a
  malformed one. Contract tests drive the installed git binary against
  local fixtures for the rewrite, credential, prefix-collision, and
  overlay-preservation behaviors.
- The long-running ACP notice now says all declared repository access
  expires together.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:47:31 -04:00
Bryan Helmkamp
e5e7875274
Merge pull request #774 from fabro-sh/github-app-packages-read
Request Packages read permission in the GitHub App manifest
2026-08-21 14:29:08 -04:00
Bryan Helmkamp
7bfed23153
feat(github): mint one installation token for the effective repository set
Add `GitHubRepositoryAccess`, the secret-free validated value describing a
run's effective GitHub repository set: the primary origin repository plus
the declared additional repositories with the shared permission map.

- The constructor normalizes HTTPS and both SSH origin spellings to one
  primary slug, rejects a missing or non-GitHub origin when additional
  repositories are declared, rejects primary duplication and cross-owner
  additional repositories, and re-checks that interpolated permissions
  carry `contents = "read"|"write"` — exposing targets in deterministic
  primary-first order.
- `resolve_shared_installation` resolves every target's App installation
  with the App JWT and requires one shared installation ID, naming the
  repository the App cannot see before any mint.
- The installation-token mint now accepts a repository-name list; the
  single-repository entry points delegate to it, and the request body
  lists every projected name with the shared permissions.
- `InstallationTokenSource::for_access` builds a source over the access
  value; caching, refresh margin, and single-flight are unchanged.
- The scripted `MockHttpClient` and test RSA key move to a shared
  crate-internal `tests_mock` module.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 14:24:24 -04:00