Skip to content

Releases: moda-labs/bobi-agent

v0.58.0

Choose a tag to compare

@underminedsk underminedsk released this 22 Aug 01:38
Immutable release. Only release title and notes can be modified.
da4fa63

Minor release: fleet usage answers over MCP, a durable WorkflowRun ledger with period-deduped admission, operator alerts when a brain cannot authenticate or is out of credits, one executor and one turn primitive behind every run, the mcp 2.0 breakage closed, and the review-remediation initiative finished.

Added

  • Rolling fleet usage summary over MCP: per-fleet job counts, tokens, and cost over a 1-365 day window, with per-instance detail and partial results (#1061)
  • A WorkflowRun ledger entry for every run; period: hourly|daily|weekly|monthly dedupes admission per bucket across every dispatch path (#1052, #1049)
  • Alerts on brain authentication and credit failures; auth failures are terminal and visible, triggering messages stay replayable (#1042, #1041)
  • A framework communication baseline prepended to every team's instructions; opt out with BOBI_COMMUNICATION_STYLE=off (#1044)

Fixed

  • Framework protection via launch-time file-integrity checks instead of runtime chmod, unblocking uv tool upgrade/pipx/pip (#1060)
  • mcp pinned >=1.23.0,<2 plus shims for the 2.0 renames that broke fresh installs (#1046, #1054)
  • connect() is never a turn: dispatch text no longer executes before step 0 (#1017)
  • Sleep cycle no longer wedges on over-budget memory (#1069)
  • Liveness verdicts deferred under legitimate heavy load (#1022)
  • doctor detects processes running code an in-place upgrade replaced (#1035)
  • Failed monitor publishes parked and retried; duplicate suppression on by default; dated runtime logs; attributed preflight failures (#1019, #1036, #1033, #1034)
  • Login destination no longer caller-controlled; team archives fail closed; issue-assignment dispatch scoped to self (#1009, #1031, #1032)
  • Distribution builds accept Node 20+ (#1010)

Changed

  • Ad-hoc --wait runs through the workflow executor; spawn_adhoc's executor role retired (#1058)
  • Review-remediation initiative closed: consolidation refactors, deferred web-UI items, test-suite cleanup (#1065, #1059)

Full detail in CHANGELOG.md 0.58.0.

v0.57.0

Choose a tag to compare

@underminedsk underminedsk released this 11 Aug 19:59
Immutable release. Only release title and notes can be modified.
7025981

Minor release: agents can author their own OTLP telemetry, subscription login
gets five fixes along one path (gateways, provider config dirs, stale bubbles,
and two Slack failure modes), and the review-remediation sweep closes Lane A
and Phase 5 and lands all five Phase 6 consolidation batches.

Added

  • bobi agent <name> otel — agent-authored OTLP telemetry (#980, #978).
    Three commands (metric, gauge/counter/histogram kinds; log;
    check [--send]) that let an agent record telemetry of its own choosing
    to any OTLP endpoint: the agent supplies the observation, bobi supplies the
    wire format and the fleet identity labels the agent cannot resolve for
    itself. Opt-in per team via tool_library: [otel] — deliberately not in the
    base prompt. Operator setup and the resource-attribute table are in
    docs/OTEL.md.
  • Browser-level e2e coverage for both web UIs (#990, #991). Setup
    back-navigation, the shared top-bar contract across bobi setup and
    bobi app (which had already regressed once, silently), a type-rendering
    guard, and 36 tests over the web app itself. Both surfaces now boot through
    one _serve() fixture.

Fixed

Subscription login, end to end:

  • A Claude-gateway team could not use subscription login (#1002). The
    login guard reused the gateway-declaration predicate as an auth-mode
    predicate and raised before selecting the PTY flow; the Docker entrypoint
    rejected such teams before bootstrap could run; and bootstrap discarded the
    configured base URL. A declared base_url with no ANTHROPIC_AUTH_TOKEN
    now runs the ordinary subscription bootstrap; explicitly token-authenticated
    Claude gateways and all OpenAI-compatible gateways still fail closed.
  • A successful login could be reported as failed under CLAUDE_CONFIG_DIR
    (#1003).
    Credentials were resolved relative to $HOME regardless of the
    provider's own config-dir override, so bobi missed what the CLI had just
    written and repeated the login ceremony on every start. Claude credentials
    now follow CLAUDE_CONFIG_DIR and Codex CODEX_HOME, with the HOME
    fallback preserved.
  • Stale bubble credentials wedged login bootstrap after an event-server
    restart (#996).
    Failed channel registration/JOIN is now probed to
    distinguish a stale bubble, credentials re-mint through the compare-and-swap
    guard, and the JOIN retries once. Stored subscription credentials are also
    structurally validated — missing, malformed, blank, or expired refresh
    tokens trigger bootstrap instead of being assumed healthy.
  • Slack channel login told users to paste the code where the adapter could
    not hear it (#1001).
    A top-level channel message is not a routable inbound
    shape, so the pasted code was dropped and bootstrap waited to timeout. The
    prompt now says reply-in-thread or @mention the bot, matching the routes
    that actually exist.
  • A Slack bot token missing users:read looked healthy while every inbound
    event was silently dropped (#998).
    bots.info failing left an empty app
    id, and subscription detection fell back to a workspace-only topic that no
    publisher uses — exact topic matching then delivered nothing. Identity
    resolution now fails closed, naming the missing scope.

Review-remediation Lane A closes (#970, #972 — the final 20 bugs, each with a
test proven to fail first):

  • Codex sessions wiped each other's MCP servers (D009). Codex re-reads
    config.toml on every codex exec, and any session built without
    mcp_servers in its options — every monitor check, gate, and workflow step
    — read the absent key as "the team has none" and erased the bobi-managed
    block out from under the manager and every other live session.
  • --project never matched a repo with a hyphen in its name (D069). The
    lossy cwd decode (-Users-z-dev-bobi-agent/Users/z/dev/bobi/agent) is
    replaced by the transcript's real cwd, with the decode kept only as a
    fallback.
  • Plus 18 more across the history indexer, both search paths, the doctor
    probes, packaging, and the long-running/blocking surfaces.

Test infrastructure:

  • The container-image integration tests were unrunnable locally (#975).
    The fixture's fallback docker build used a source mode that cannot succeed
    anywhere (.dockerignore drops both .git and the prebuilt event server,
    and the image is deliberately Node-free), reading as "docker unavailable."
    It now builds the wheel first, the way CI always has.
  • A Worker MCP test measured the runner, not the code (#973). The 3s
    stopwatch flaked on a loaded 2-core runner; the wait contract is now
    asserted by poll count.

Changed / Removed

  • Dead-code purge — review-remediation Phase 5 complete, 42 items (#979,
    #982, #983, #984).
    Fourteen zero-consumer names, the expired
    curator→sleep-cycle and policy→long_term_memory rename compat layers, dead
    CLI helpers duplicating the live start/stop paths, and the callerless
    terminal run_setup entry point (the web UI is the entry point).
  • Consolidation — review-remediation Phase 6, all five batches (#993, #995,
    #997, #999, #1000).
    Behavior-preserving single-sourcing across events/,
    cli.py, setup/, monitors/ + sdk.py, and compose/build: one
    definition each for Slack identity resolution, event-server port
    resolution, subscribe: parsing, launch-admission defaults, _parse_iso,
    and credential precedence (two setup screens had it in the opposite order
    from the runtime). Two deliberate deltas: monitor notify_channel no
    longer re-parses agent.yaml on every notification, and the hidden ask
    command loses its unused --source option.
  • The dogfood-content-review agent pack is deleted (#974) — no
    auto-dispatch, no CI reference, no substantive change since 2026-07-01.
  • Docs (#985): the single-agent page is documented in README.md; the
    never-built "Edit design" affordance is cut from the single-agent-view
    plan's scope rather than left claimed.

v0.56.0

Choose a tag to compare

@underminedsk underminedsk released this 05 Aug 16:45
Immutable release. Only release title and notes can be modified.
f78477e

Minor release: the single-agent page that 0.55.0 gave the local dashboard now
works on a hosted fleet too — the runtime that connects the published
pieces moves into the framework, and the six read/write commands the hosted
page was missing are added to the admin protocol. Plus the review-remediation
sweep's Phase 4 security and user-surface batches.

⚠ Breaking for out-of-tree TeamRuntime implementations

Seven TeamRuntime methods — runs, overview, run_details, resume_run,
remind_run, close_run and the widened transcript — are now
@abstractmethod, and their base-class fallbacks are deleted. A subclass that
does not implement all seven cannot be instantiated. Both in-tree
implementations (LocalRuntime, EventBusRuntime) satisfy the ABC; anyone
carrying a private subclass must implement them or delegate to the shared
builders, which are importable for exactly that reason.

Added

  • EventBusRuntime is part of the framework (#967, #963). This repo
    shipped the sidecar (bobi/supervisor/), the Cloudflare event bus
    (event-server/worker/) and the console UI (bobi/webapp/) while
    withholding the ~470 lines that connect them, so every published piece could
    be self-hosted and a hosted console still could not be assembled. The class
    now lives at bobi/webapp/event_bus.py as a pure move — the only diff
    against its former home is five docstring phrases that became false once the
    file changed repos, and two import lines in its tests.
  • The hosted single-agent page gains history and composition (#968, #963).
    /overview and /runs answered 409 on a hosted agent while /health,
    /status, /spend and the lifecycle verbs answered 200 — an agent could be
    watched and recovered, but what it had done and what it was made of were
    missing. Six new admin commands (runs, overview, run_details,
    resume_run, remind_run, close_run), each a thin delegate to the same
    pure builder LocalRuntime already calls, so there is one read
    implementation and the two runtimes cannot drift. SUPERVISOR_VERSION moves
    0.1.0 → 0.2.0 (additive, per the compatibility promise).
  • transcript takes an optional detail: true. The chat view (messages)
    has already discarded every tool call, so a hosted debugging transcript built
    by reshaping it would silently omit most of what an agent did between
    speaking. The reply gains entries + usage; a supervisor too old to know
    the argument replies without entries, and that is reported as unavailable
    rather than rendered as a debugging view with the tool calls missing.

Fixed

  • A prune: entry could delete host files (#961). Prune names an item on a
    surface, never a path, but nothing validated it: an absolute name collapsed
    the staging join to that absolute path, and a .. segment walked out of the
    staging directory, so bobi agents install rmtree'd host directories.
    Compose now raises ComposeError.
  • /api/credential/value served ambient secrets (#961). The setup wizard's
    endpoint fell back to os.environ for any requested name, so anything
    holding the page's per-launch nonce could read a secret merely exported in
    the launching shell — never saved through setup and outside the endpoint's
    own justification. run/.env is now the whole surface.
  • The setup picker confined paths to the wrong tree (#961). The boundary
    was BOBI_HOME rather than the user's home directory that the comment,
    DESIGN.md and the 400 message all claimed, so /api/mcp/detect rejected
    every real project folder with a message the path already satisfied.
  • An unescaped Slack app name broke the manifest (#961). Bobi: Staging
    rendered an unparseable manifest and Bobi #1 silently created an app named
    Bobi; names are now emitted through the YAML emitter, byte-identically for
    ordinary ones.
  • A commented-out line in agent.yaml took down every start/status/dispatch
    path (#965).
    A key present with an empty value is YAML null, so
    raw.get(key, default) returned None and the default never applied —
    services:, requires:, event_server: and spend_cap: all crashed with a
    traceback naming neither the key nor the file.
  • The setup wizard silently discarded an MCP edit made during a probe
    (#965).
    The handler captured the entry, awaited a probe of up to 60s, then
    wrote that stale snapshot back — and a result for the old command would
    have marked the new one connected, rendering a never-tested config green.
  • event-server stop wedged on a truncated pid file (#965). It raised on
    garbage and on PermissionError, leaving the stale files behind so every
    later stop failed identically.
  • agents update exited 0 when every pack failed (#965), while the
    named-pack form exited 1 for the identical failure.
  • agents browse died on an unquoted version: 1.0 (#965), taking down
    the whole listing over one row; the same coercion fixes a str-vs-float
    comparison that made an installed pack read as an upgrade to itself.
  • One malformed session crashed a team's cost rollup (#965). The fold's
    comment promised it must not 500 on one bad session and the token fields were
    guarded; the cost fields were not, and a string is truthy.
  • Remove-then-re-add crashed bobi agents install (#965) — the natural
    idiom for wholesale-replacing an inherited keyed entry deep-merged into the
    tombstone and raised a raw TypeError.
  • An open-mode pack could install from unvalidated source (#965). The
    validation-freshness gate sat behind if state.mode == "create": although
    DESIGN.md already called a fresh validation to install a hard floor.
  • Switching a team off Slack left it running the Slack adapter (#965).
    chat is a setup-managed overlay key but the overlay only ever wrote it.
  • One binary file 500'd the setup review viewer (#965)/api/file had no
    decode handling while /api/files lists binaries with no suffix filter.

Changed

  • --resume is removed from bobi setup (#965). The webapp resumes an
    unfinished session unconditionally, so the flag named the default. All four
    documentation sites are corrected, including the disconnect overlay that
    printed the now-erroring command to users.
  • The public/private line is documented where it is enforced (#967).
    AGENTS.md and docs/ADMIN_PROTOCOL.md record which side each piece lives
    on and point at the reference client.

v0.55.0

Choose a tag to compare

@underminedsk underminedsk released this 05 Aug 01:11
Immutable release. Only release title and notes can be modified.
284ad4d

Minor release: the dashboard gains a real per-agent page, and the review-remediation sweep lands its first three Lane A phases — session lifecycle honesty, persistence atomicity, and agent-pack routes that had been failing silently.

Added

The single-agent view (#948, MOD-261). The machine-scoped dashboard becomes a real per-agent page. Behind it is a unified runs read model: a monitor firing, a chat, and a workflow run are one list of runs rather than three unrelated event sprays — which is also why a monitor now records one run per firing instead of a spray of events. Agent state becomes a tri-state (running / stalled / stopped) rather than a bare pid check, so "the process exists" stops being mistaken for "the agent is working", and the page can surface a stalled workflow run and offer to resume it.

New per-agent nouns under /api/agents/{name}: GET runs, overview, health, sessions, details, spend, and POST workflows/runs/{run_id}/resume. The overview also reports what the script cache did not spend. Integrates #906, #912, #913, #914, #915, #916, #919 and the #941 restyle.

Fixed

  • A session could not be stopped while its startup turn was still in flight (#949)stop() silently no-opped and the thread plus its brain subprocess kept running, leaking a live, token-burning agent for every timed-out phase while reporting "session failed to start". Setting an event cannot interrupt a turn parked in await client.receive_response(), so _run is now a cancellable task.
  • start() waited out the full timeout after the session thread had already crashed (#949) — a launch that failed in milliseconds measured 30.006s, and launch paths pass timeouts up to 3600s.
  • A supervised agent's terminal failure was never persisted (#949) — the except asyncio.TimeoutError handler was unreachable, so TERMINAL_FAILED never landed and the reconciler had nothing to re-emit.
  • Every durable writer now shares one atomic-write helper (#951) — the pattern had been re-implemented in six places while a comparable set of writers used a bare write_text. Because every loader treats unparseable state as empty, the cost is never "lose one field": a torn monitor state file re-fires every monitor, a torn spend file zeroes the runaway-loop backstop's window, a torn setup.json discards the whole wizard session, and a torn config.toml loses the operator's foreign codex keys.
  • Agent-pack routes that silently matched nothing (#957)eng-team's github.issues.assigned matches a type no adapter emits, so issue-lifecycle never fired on assignment (this is why issue pickup has needed a Slack directive); the dogfood workflow's issues_count > 0 used an unsupported operator and fell through to a truthy check, closing a 3-issue audit as having passed review. Validation now catches this class via a per-source event-type table that fails open on unknown adapters. eng-team 1.5.2 → 1.5.3, dogfood-content-review 1.2.1 → 1.2.2.
  • A raise from _make_session escaped the workflow orchestrator's terminal-honesty handler (#957) — the registry entry stuck at running with no session.failed or workflow.failed.
  • The Worker deploy smoke gated on a version carrying the previous run's credentials (#954)wrangler deploy and the secret bulk after it publish two Worker versions reporting the same release sha, so a sha-only gate could not tell them apart. Readiness now also requires that an operator-authenticated route answers 200 to this run's freshly minted token and that version_id holds still.

Changed

  • AGENTS.md states the dated plan-filename convention the repo already follows (#950).
  • Lane C of the review-remediation plan is 2 PRs, not 3 (#947) — its six remaining web-UI items are deferred, since the single-agent work above rewrites those surfaces.

Full detail in CHANGELOG.md.

v0.54.0

Choose a tag to compare

@underminedsk underminedsk released this 04 Aug 01:53
Immutable release. Only release title and notes can be modified.
808d8eb

Minor release: the MCP fleet-control surface gains the tools that act, and a security + reuse sweep lands across the event server.

Added

MCP fleet control gains its write half (#944). POST /mcp on the event-server Worker adds bobi_read_transcript, bobi_send_message, and bobi_lifecycle alongside 0.53.0's three read tools, so an agent can act on a fleet rather than only observe it. No new authority and no new admin vocabulary — the same FLEET_OPERATOR_TOKEN gate runs before any tool body, and the tools drive the same nine commands the sidecar already answers.

Commands resolve through a bounded server-side wait (5s, MCP_COMMAND_WAIT_MS, 0 disables): fast commands return in one tool call instead of three, slow ones return status: "pending" plus a command_id. pending means not-yet-answered, never failed.

Three behaviors are stated in the tool descriptions because an agent would otherwise believe the opposite: bobi_send_message does not return the agent's reply (the supervisor resolves chat only when the whole turn ends, minutes later — read it back with bobi_read_transcript); transcript output is untrusted third-party content, returned in its own block between explicit warnings; and bobi_lifecycle requires a reason, recorded as an audit control, with self-targeting allowed and annotated rather than refused.

Fixed

  • Attribute injection in the agent-reply markdown renderer (#942) — agent output could close an href attribute and land a live event handler, and agent replies are prompt-injectable from anything the agent reads.
  • Unvalidated register payload on the unauthenticated mint path (#942) — a bare string registered one subscription per character; a non-string element threw after an orphan bubble had been persisted.
  • Unbounded request-body buffering (#942) — the 413 arrived after the memory was spent, and on a non-loopback bind the peak was unauthenticated. Now capped at 8 MiB (BOBI_ES_MAX_BODY_BYTES).
  • Slack file uploads in DMs and thread replies never arrived (#945) — every file_share-subtyped message was skipped before classification.
  • The circuit breaker's pause buffer was unbounded and never released (#945) — a conversation that tripped then went quiet held its buffer for the life of the process.
  • /channels/send accepted required text and delivered it nowhere (#945) — on WhatsApp the user got the file and never the sentence explaining it.
  • Concurrent Slack workspace registrations raced away a bot's signing secret (#945) — a fleet roll restarting both agents could 401 one app's inbound events.

Changed

  • The release workflow publishes the pins the image dispatch needs (#943).
  • Event-server reuse sweep (#945) — eight duplicated or dead surfaces converged.

Full detail in CHANGELOG.md.

v0.53.0

Choose a tag to compare

@underminedsk underminedsk released this 03 Aug 22:48
Immutable release. Only release title and notes can be modified.
ea915d0

Minor release: the fleet control plane grows an agent-shaped surface, and Claude token telemetry stops reading zero.

Added

The fleet control plane is served as MCP from the event-server Worker (#923). POST /mcp on the Worker exposes the same control plane the hosted console already drives, so an agent binds to named tools with declared schemas instead of re-deriving the /fleet/* URL shapes, the 202-then-poll contract, and the per-command argument shapes. The read half only — bobi_fleet_status, bobi_instance_detail, bobi_command_result — each calling in-process the same builder its HTTP route calls, so there is no second implementation of the read model and no HTTP hop. Stateless: one McpServer per request, no Durable Object, no session id. Auth is the existing FLEET_OPERATOR_TOKEN via requireOperator, checked in the route before any tool body runs — the MCP route widens the interface, not the authority. Nothing here can restart, stop, or message an instance. A team consumes the endpoint with no framework change, via mcp_servers type: http plus headers.

Self-hosters redeploying their own Worker: wrangler.jsonc now sets compatibility_flags: ["nodejs_compat"], and it is not optional. createMcpHandler carries its per-request context in an AsyncLocalStorage, so the bundle imports node:async_hooks; without the flag workerd refuses to start and every route on the script goes down with it, bus included. The compatibility date is past 2024-09-23, so the flag is additive rather than replacing workerd's own APIs.

bobi agent <name> costs backfill recovers lost token telemetry (#935, #936). Reads Claude's retained JSONL transcripts and fills the token counters the defect below left at zero. Dry run by default, --write to apply, idempotent across runs. It only ever fills: recorded tokens and provider dollars are never overwritten, last_activity is never bumped, and a session whose transcript is gone stays honestly unknown rather than estimated into place. Against a real 151-session registry it repaired 112 with provider dollars byte-identical before and after. costs became a command group; the bare costs invocation is unchanged.

Fixed

Claude token counts were recorded as zero while the dollars arrived (#935, #936). claude_agent_sdk 0.2.128 passes the CLI's modelUsage through verbatim, so its keys are camelCase, but _one_model_usage_to_cost read only the legacy snake_case spellings — every token field came back 0 while total_cost_usd arrived on a separate attribute, producing exactly the reported shape of rows with dollars and no tokens. Each field is now read across both spellings, first match wins. AssistantMessage.usage is deliberately untouched: it is the raw Anthropic API shape, which is genuinely snake_case.

Changed

Documentation re-verified against the tree it describes (#937, #938, #939). The review-remediation plan re-checked all 229 items against main and its Lane D swept 24 of them: repo-root and pack docs, stale specs, and the engine, monitor, event-server, and setup-skill references that had drifted from the code.


Full changelog: https://github.com/moda-labs/bobi-agent/blob/v0.53.0/CHANGELOG.md

v0.52.0

Choose a tag to compare

@underminedsk underminedsk released this 03 Aug 14:27
Immutable release. Only release title and notes can be modified.
af76437

Bobi looks like Bobi, and public CI proves the product rather than trusting it.

This is also the first release published entirely from bobi-agent — the
reference container image now builds and pushes from this repo rather than the
archived private deploy repo, completing the public half of the 2026-07 reorg.

Added

  • Public CI proves both brains, against the real image (#909, #911). The
    container-image job runs the built image through one real Claude ask and one
    real Codex ask, against an ephemeral event server started from the real Worker
    sources. Off by default: nightly, on dispatch, or on the ci:live label —
    never on a fork PR.
  • A real wrangler deploy is exercised (#909, #911). wrangler dev proves
    the Worker's code but never a deployment — the KV binding, the v1
    new_sqlite_classes Durable Object migration, and account-side provisioning
    were covered nowhere. worker-deploy-smoke.yml deploys a dedicated
    bobi-events-ci-smoke Worker in a separate Cloudflare account and runs a
    health check plus a publish→subscribe round-trip against the deployed URL.
  • The live lanes must prove they RAN (#909, #911). Every test in both lanes
    carries a skipif, so a renamed secret would have skipped to green.
    scripts/assert_junit_ran.py now rejects any skip, any wrong count, and any
    missing named test; tests/test_ci_live_wiring.py fails if a live step is
    deleted.
  • The reference image is published from this repo (#898). Dockerfile,
    docker/, release-image.yml, container.yml and the container contract
    tests moved here, with a new docs/REFERENCE_IMAGE.md. A self-hoster runs
    Bobi from a public pull instead of a repo grant.

Changed

  • bobi setup and bobi app reskinned onto the Bobi design system (#883).
    Vendored as docs/design-system/, now the source of truth for anything visual
    on any Bobi surface. Violet means state — live, enforced, gated, focused —
    never decoration. Geist / Geist Mono / Inter vendored as woff2, so the offline
    constraint holds: no CDN, no network at runtime, no build step.
  • The supervisor arrives via the wheel, not a COPY (#898). The
    bobi-supervisor shim is gone; the entrypoint execs
    bobi agent "${AGENT_NAME}" supervise -- --foreground.
  • The public surface no longer points at an archived repo (#905, #907).
    Including the setup wizard's Cloud card, which now points at
    ghcr.io/moda-labs/bobi and docs/REFERENCE_IMAGE.md.
  • docs/RELEASE_RUNBOOK.md describes the two-repo train (#900).

Fixed

  • Subagent launch chains are bounded before a process is spawned (#849,
    #888).
    Launches recorded no ancestry, so a workflow step launching its own
    workflow was unbounded: one scheduled trigger became 50 runs in 44 minutes,
    stopped only by the spend governor. Launches now carry BOBI_LAUNCH_LINEAGE
    and are refused up front when self-recursive or deeper than max_launch_depth
    (default 8).
  • Two supervisor test files now test the shipped code, not a private copy
    (#904).
  • Three integration tests failed on ambient BOBI_HOME, not on the code
    (#908).

Full changelog: https://github.com/moda-labs/bobi-agent/blob/v0.52.0/CHANGELOG.md

v0.51.1

Choose a tag to compare

@underminedsk underminedsk released this 31 Jul 21:06
Immutable release. Only release title and notes can be modified.

Patch release: an agent no longer needs a build-time secret in order to run.

Fixed

  • Build-time-only ${VAR} refs stop gating a runtime (#886). A variable
    referenced only by a build: step was classified a required runtime secret,
    so bobi agents install --non-interactive refused to install an agent whose
    dependency was already baked into its image. It took a team down during the
    0.51.0 fleet roll: the deploy side deliberately withholds build secrets from
    the runtime env-file (and enforces them host-side before a build instead),
    while this side refused to proceed without one — and because the deploy pauses
    the old runtime before pushing, the box was left frozen rather than merely
    un-updated. The dependency the secret would have installed was already
    present, and its success check passed.

    The fix is structural rather than a name list: a ref found only under
    top-level build: — the apt/npm/run_root/run image layer — is marked
    build_only and excluded from the install and startup gates. A name used both
    under build: and anywhere else stays required, because the runtime use is
    real. An unparseable agent.yaml yields no build-only names at all, so a
    classification bug over-requires a secret rather than quietly ceasing to
    require one. docs/TOOL_LIBRARY.md now states the rule where dependency
    authors will meet it.

v0.51.0

Choose a tag to compare

@underminedsk underminedsk released this 31 Jul 19:45
Immutable release. Only release title and notes can be modified.

Minor release: the product surface goes public. The Cloudflare event-server
Worker and the admin sidecar were previously reachable only through a grant on
the private deploy repo; both now ship in this repo, and the admin wire format
they speak is a documented, versioned contract rather than an internal
agreement between two halves of one team's infrastructure. Self-hosting the
durable event tier no longer requires anything private. This is Lane 1 of the
repo reorg (plans/2026-07-29-repo-reorg.md, Phases 1-5); the reference
container image and the ops-repo consolidation follow in later releases.

Added

  • The Cloudflare Worker event server is public (#880). event-server/worker/
    is a third npm workspace package alongside src/ (local) and core/. It is
    the durable variant of the same protocol the Node servers speak - same webhook
    verification, same bubble model, same topics - with per-deployment sessions in
    Durable Objects and replay in KV, so registrations and cursors survive a
    restart. It lands as its own workspace rather than more files under
    event-server/src/ because the two compile units are mutually exclusive: the
    local unit is Node-only (lib es2024 + @types/node), the Worker needs
    Cloudflare's globals and worker-configuration.d.ts, and their vitest configs
    are node-environment vs. defineWorkersConfig. wrangler.jsonc ships with a
    placeholder KV namespace id that fails loudly at wrangler deploy - a fresh
    clone cannot silently deploy against someone else's namespace. Deployment
    walkthrough in docs/SELF_HOSTED_EVENT_SERVER.md ("Deploying the Worker").
    Requires a Workers Paid plan, because the Durable Objects are SQLite-backed.
  • bobi agent <name> supervise (#880). The admin sidecar, restored as a
    first-class public CLI surface. It spawns and probes the manager, publishes
    heartbeat and lifecycle telemetry, and listens on the admin topic so a wedged
    manager can still be restarted from outside the box. Everything after --
    forwards to the manager's start command
    (bobi agent <name> supervise -- --foreground). This is what a container
    entrypoint runs as PID 1, not an interactive command. The agent name is a CLI
    positional; the private bobi-supervisor shim that read it from BOBI_ROOT
    is gone.
  • docs/ADMIN_PROTOCOL.md (#880). The admin channel as a documented,
    versioned contract - topics, envelopes, command and telemetry schemas, and the
    auth model. This, not the code move, is what an external consumer binds to.
    bobi/supervisor/admin.py binds the two sides byte-for-byte, so the server
    half of a documented contract cannot stay private.

Changed

  • Positioning: the Worker is a self-host option, not a paid tier (#880).
    README.md and docs/SELF_HOSTED_EVENT_SERVER.md now present three
    self-hosted shapes - tunnel, standalone Node, Worker - and tell you to pick on
    durability rather than on price. The in-memory restart caveat carries an
    explicit pointer to the durable answer.
  • Packaging: neither the wheel nor the sdist carries the Worker sources, and
    now that is stated rather than incidental.
    The wheel's event-server inputs
    are declared as src/**/*.ts + core/src/**/*.ts (bobi/events/artifact.py),
    which cannot match worker/. The sdist takes event-server as a whole
    directory, so the Worker workspace was in scope and fell out only by
    accident: Hatch's safe_walk follows the npm workspace symlink
    node_modules/bobi-events-worker -> ../worker and then prunes the real
    directory as an already-visited inode, which made the sdist's contents depend
    on whether npm ci had run (core escaped only by sorting ahead of
    node_modules). event-server/worker is now an explicit sdist exclude,
    pinned by tests/test_sdist_contents.py. No published bytes change - this is
    what the release workflow already emitted - but the result is now
    deterministic. Nothing in the Python distribution builds or runs the Worker;
    its deploy path is a git clone plus wrangler deploy.
  • The event-server npm tree audit is default-deny (#880). Only extraneous:
    is treated as non-fatal in bobi/events/server.py; everything else blocks.
    npm 10.8.2 (bundled with Node 20) reports the Worker's optional dev deps as
    extraneous from a lockfile npm 11 calls clean, and the audit must not be
    loosened wholesale to absorb that.

Removed

  • The repo-split bridges (#880). The npm publish path for
    @moda-labs/bobi-events-core (core/scripts/{pack,smoke}.mjs) and the
    worker-integration.yml cross-repo CI existed only to carry code across the
    private/public boundary. With the Worker home, they dissolve.

Fixed

  • validate_team no longer invalidates its own hash (#873). Finalizing a
    team could land in a state where install was permanently unreachable -
    "the team source changed since validate_team last passed" - and re-running
    validate_team re-armed the same trap. When the setup's run/ directory sits
    inside the source tree, saving run/state/setup.json rewrites a file the
    freshness hash just covered, so the frozen digest is stale the instant it is
    recorded. source_tree_hash gains an exclude set and both call sites pass
    the setup state file. Two guards close the way in: create mode now applies the
    same empty-target bar the registry branch enforced, and the run/ containment
    check rejects a location that encloses run/ (the agents root) and not only
    one nested inside it.
  • Plan-artifact CI reads heading-style phase markers (#885). The check
    recognized list-style markers only, so a plan whose phases are headings passed
    vacuously.
  • The unit-test matrix reports under a stable required check (#877). A
    skipped matrix job never expands its matrix, so required Unit tests (3.12)
    contexts never reported and the PR hung forever. A non-matrix gate job now
    checks the matrix result explicitly.
  • A plan's review surface freezes at approval, not before (#878).

v0.50.0

Choose a tag to compare

@underminedsk underminedsk released this 30 Jul 18:34
Immutable release. Only release title and notes can be modified.

Long agent jobs stop dying silently.

Honest turn errors (#845, PR #847). turn failed was a literal fallback — the
brain had already diagnosed max_turns_reached (max=…, turns=…), but the workflow
drain read only result_text (empty on exactly that path) and threw the diagnosis
away. One shared composition now backs the drain, Session.last_error() and
_run_agent_supervised, and the session log's stop record carries every terminal
fact the brain reported.

A configurable, resumable turn budget (#845, PR #847). max_turns resolves
step override → roles.<role>.max_turnsbrain.max_turnsDEFAULT_MAX_TURNS
(1000, up from a hardcoded 200), at every site that carried the literal. A step
override now actually wins on steps after the first — it previously no-opped
silently while documented as working. Hitting the cap restarts the step on the
saved session id with a fresh budget instead of discarding the run mid-edit.

The checklist-execution worker protocol (#852, PR #865). skills/checklist-execution.md
— how an agent works a long job from a committed markdown checklist: read once,
one item at a time, verify, commit per item. Persist-per-item is the durability
primitive; there is no engine and no framework module behind it, pinned by
tests/test_no_checklist_engine.py. A verify: is a proposed proof, never
something the framework executes.

--fresh on dispatch (#852, PR #865). spawn_adhoc names sessions from
sha256(task)[:8], so re-dispatching an identical task string reused the previous
session — the checklist shape exactly. The name stays stable (it is the git branch)
and the resume became optional.

A plan-artifact CI check (#852, PR #865) in its own workflow, asserting the
review-surface freeze, an append-only appendix, [f] state tags, and gate-line
classification — and never executing a verify:.

Full detail in CHANGELOG.md.