chore(ci): extend Discord watch rotation schedule - #14
chore(ci): extend Discord watch rotation schedule#14github-actions[bot] wants to merge 52 commits into
Conversation
…nches Picking a specific ACP agent (e.g. acp:droid) via a per-session harness override silently launched whichever agent was listed first in the acp: config block. The server canonicalized acp:<slug> to bare acp before persisting the override, dropping the slug; the runner's _build_acp_spawn_env then read the slug only from the bundle agent's own spec (which carries no slug), so it fell back to acp_agents()[0]. Preserve the concrete acp:<slug> through _validated_harness_override, and thread the raw override into _build_acp_spawn_env via a new override_harness param (forwarded through _build_spawn_env_from_spec at both override call sites) so the override wins over the bundle spec's harness. Non-acp overrides and the no-override spec path are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generic-ACP agents (acp:<slug>) fell back to the generic bot icon in the agent picker because iconForAgent had no branch for them. Add DroidIcon and GrokIcon glyph components and map them in iconForAgent off the acp:<slug> harness id, so a pinned droid/grok agent renders with its own mark like the native harnesses do. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ACP agents (droid, grok) advertise a SessionModelState in their session/new response (availableModels + currentModelId) and support session/set_model, but run_turn ignored the per-request config.model the adapter threads from the /model override — so switching a model on an ACP session forced a subprocess respawn instead of a live switch, and droid ignores a model in session/new anyway. Capture the models from session/new, expose available_models() / current_model_id() for the picker, and apply the /model override live via session/set_model before session/prompt when the agent offers it and the model actually changed. A /model on a droid session now switches without a respawn; agents that advertise no models are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ACP agents (droid, grok) advertise a live model list (session/new
SessionModelState) and support session/set_model, but omnigent surfaced no
picker — the model-picker UI is gated on the native-agent wrapper label, which
ACP sessions don't have.
Surface the list end to end:
- Executor ABC gains available_models() / current_model_id() (default empty);
AcpExecutor already captures them from session/new.
- The harness scaffold exposes GET /v1/sessions/{id}/model-options; the
ExecutorAdapter answers it from the inner executor.
- The runner's model-options route gains an `acp` branch that queries the
running harness (via a new non-spawning process_manager.existing_client).
- ACP sessions get an `acp-native-ui` wrapper label at create, mapped to the
model-options fetch; the web adds an `acp` picker kind that reads the live
list (like codex/opencode) and rides a pick as model_override — applied live
by the executor's session/set_model.
Empty until the first turn creates the agent session, matching the codex path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ACP model-options branch keyed on _session_harness_name, which returns the cached bundle spec's harness — for a session started via a harness override (e.g. Polly overridden to acp:droid) that's the bundle agent's harness (claude-sdk), not the picked acp one. So the model picker stayed empty for the common override path. Also key off the running subprocess's harness (new process_manager.running_harness), which is the actually-picked one, so an acp override is recognized. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The web model picker keys on id/displayName, but ACP agents return modelId/name — so the options rendered with empty ids. Map them in the runner's ACP model-options branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Grok Build (`grok`) had no first-class harness — only usable as a custom `acp:` agent or as `xai/grok-*` behind openai-agents (issue omnigent-ai#2881). Add `harness: grok` (alias `grok-build`) driving `grok agent stdio` via the generic AcpExecutor, the same reuse pattern the issue suggests (like qwen/goose). - inner/grok_harness.py: thin create_app wrapping AcpExecutor with a fixed `grok agent stdio` command; auth is Grok's own (grok login / XAI_API_KEY). - Registry: valid_harnesses / harness_modules / alias grok-build / capabilities (ACP profile) / label "Grok Build" / HARNESS_GROK_MODEL. - Install spec (curl x.ai/cli/install.sh, grok login --device-auth) + readiness gating on the grok binary. - Model picker: the runner model-options branch and the acp-native-ui wrapper label recognize `grok`, so a native grok session gets the live model picker built for ACP agents. Follow-up: an `omnigent grok` terminal wrapper (native TUI launch). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The REPL /model readout ran describe_active_credential for the session's harness. ACP agents (acp:<slug>, grok) own their own auth and model, so there is no Omnigent provider — it fell back to the fleet default and reported e.g. gpt-5.5 for a droid session running claude-sonnet-5, which is misleading. Report the session's own model for ACP-based harnesses instead, noting the agent owns its model and that /model <name> switches it via the agent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Gives Grok a persistent picker entry running the already-shipped ACP grok harness (full ACP UX: streaming, policy+consent-gated tools, live /model). Lands in the "Agents" group, not the native-TUI row — that gate is isNativeCodingAgent, which an ACP harness is deliberately not in, and a native-TUI grok would require a bespoke TUI screen-scraper for zero capability gain. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The auto-created terminal REPL resumes a session with a one-shot client.sessions.get(); a transient Cloudflare 502 (origin briefly draining after a deploy) killed the whole attach and showed a permanent "Failed to resume" until the tab was reopened, while the SSE-based chat reconnected fine. Bounded retry (4 attempts, 0.5s backoff) on 5xx; a 4xx (bad session id) still fails loud on the first try. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…llama test_build_startup_header_creds_line_hints_first_available asserts the openai surface with no default falls back to a configured Databricks workspace. On a dev machine running a local Ollama, ambient detection (a hardcoded localhost:11434 TCP probe) injects an openai-serving provider that outranks Databricks, so the creds line read "Codex → Ollama" and the test failed — while CI (no Ollama) passed. Pin detect_providers to none so the test exercises config-order fallback deterministically. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The grok harness added `grok` + `grok-build` to the configured-harness map; test_configured_harness_map_covers_all_spellings pinned an expected_keys set that omitted them, so it failed with both as extra items. Add them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Polly's orchestration roster was six coding workers (claude_code, codex, opencode, cursor, hermes, pi). Cursor is unused here, so drop it; add four more cross-vendor coders so Polly can delegate to and cross-review across a wider vendor spread: - grok — Grok Build (xAI), `grok` ACP harness (pi-style headless, ask window) - droid — Droid (Factory), `acp:droid` ACP harness (pi-style headless) - kimi — Kimi Code (Moonshot), `kimi-native` TUI worker - antigravity — Antigravity (Google/Gemini), `antigravity-native` TUI worker Updates the roster prompt (worker list, native-vs-headless split), the preflight `command -v` set (adds grok/droid/kimi/agy, drops cursor-agent), and bumps max_dispatches_per_turn 6 -> 9 so a fan-out can reach the full roster. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mpts antigravity-native only auto-clears its interactive gate on a headless (no-TTY) launch. A polly sub-agent is web-attended (watchable in the Subagents panel), so it isn't headless and sat waiting on selections the orchestrator can't answer. Set the explicit override in its executor config so the worker runs unattended; the omnigent blast_radius guardrail still gates the catastrophic set (force-push, rm -rf /, hard-reset to a remote ref). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n't stall _derive_terminal_launch_args_from_spec produces a dispatched sub-agent's terminal_launch_args and had branches for claude/codex/cursor-native but none for antigravity-native, so a polly antigravity worker got None and agy launched at its default request-review mode — parking on "Requesting permission for: ..." a TUI prompt an orchestrator can't answer. The dispatch then never completed (agy never went idle, so the reader never emitted the completion edge and the orchestrator never woke). Add an antigravity-native branch mirroring claude-native's opt-in-verbatim contract: an explicit permission_mode: bypassPermissions translates to agy's sole pre-emptive control flag; any other/absent mode leaves agy prompting. Runner and relay are unchanged — the flag rides the server-persisted terminal_launch_args and build_agy_launch appends it verbatim. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The agy RPC reader is stream-primary: it opens StreamAgentStateUpdates, which only carries frames minted AFTER the stream opens. When the reader binds to a cascade whose steps are already committed — exactly what the adopt-in-place rebind onto a TUI-minted cascade does after a /clear — it mirrors nothing. The assistant's reply never reaches the web chat and the turn stays open forever, while the TUI shows the answer fine. The poll loop is the only caller of GetCascadeTrajectorySteps, and it runs only in the `except` arm of the stream call. A silent (not failed) stream never raises, so the fallback never fires: observed live as 1446 summary polls, 0 step fetches, 0 events posted, and no error logged. Read the committed snapshot once at bind and run it through the same _process_committed_step path before entering the stream. The shared state seen-set keeps it idempotent against the stream's own delivery, and a backfill failure is swallowed so the stream/poll paths still run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Goose is now installed + authenticated on every fleet host, so add it as a dispatchable worker. Uses the headless `goose` ACP harness (drives `goose acp`) rather than `goose-native`: a dispatched worker needs structured turn events, which the ACP path emits directly, while the native TUI mirrors its transcript out-of-band — the fragile path that stalled antigravity dispatches. Same goose binary either way, so the preflight gate is unchanged in spirit. Updates the roster prompt (NINE -> TEN, native-vs-headless split), the preflight `command -v` set, and max_dispatches_per_turn 9 -> 10. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
agy's connect-RPC port is discovered by running lsof against the agy pid. The
argv used a bare "lsof", resolved through the caller's PATH — but the omnigent
host daemon runs under launchd/systemd with a minimal PATH. macOS ships lsof in
/usr/sbin, which that PATH omits, so every probe raised
FileNotFoundError: [Errno 2] No such file or directory: 'lsof'
and discovery reported "no agy connect-RPC port bound within 20s". The reader
then never bound, never polled, and mirrored nothing: an antigravity session on
a macOS host answered in its TUI while the web chat stayed empty forever.
Resolve the binary via shutil.which with absolute fallbacks (/usr/sbin, /usr/bin,
/bin) and log distinctly when it is genuinely absent. Unavailable lsof still
degrades to "no ports" rather than raising.
Note this is a DIFFERENT defect from the reader backfill fix: that one applied
where discovery succeeded, this one is why discovery never succeeded on macOS.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
While agy is parked it emits neither a frame nor a connect trailer, so the stream body's `async for` blocked indefinitely. That wedge made the loop's `stop` checkpoint unreachable (rotation could only be actuated by cancelling the task) and meant the poll fallback — gated on an exception — could never fire for an idle stream. Bound each entry with a per-frame deadline. On expiry the generator is closed and the entry ends, so the outer loop re-checks `stop` and re-opens; a real frame is unaffected. Closes the structural hazard the backfill fix worked around. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The reader resolved agy's connect-RPC port once at bind and held it for the life of the run. When agy restarted its language server the port moved, so every poll raised ConnectError against the dead port and the loop retried it forever — the session mirrored nothing and no output ever reached the web UI. Make state.port the single source of truth: the poll loop reads it each tick and re-resolves the cascade's current port on a refused connect, and the rotation detector follows the same value so it cannot go blind after a rebind. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: apeltekci <andrew@peltekci.com>
The kimi forwarder mirrored wire content but never posted an external_session_status edge — the only native forwarder that didn't (claude/codex/opencode/cursor all do). A kimi sub-agent therefore finished, delivered its answer to the transcript, and left the parent waiting on it forever: _mark_subagent_terminal_and_wake was never reached, so no result ever landed in the parent's inbox. kimi's wire has no turn.end row; its agent loop steps while step.end carries finishReason 'tool_use' and stops on 'end_turn' (1:1 with turn.prompt across every recorded session). Map that edge to external_session_status: idle, carrying the turn's final assistant text — the runner delivers an empty result when an idle edge forwards none. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: apeltekci <andrew@peltekci.com>
The kimi permission-request hook minted a fresh random _omnigent_elicitation_id on every invocation, despite the comment claiming a stable re-attach id. kimi fires PermissionRequest fire-and-forget and re-fires after a severed long-poll (reconnect / transient 5xx), so a re-fire created a NEW approval card and orphaned the one the human answered: the live hook's long-poll never saw a verdict, _request_web_approval returned None, and the keystroke was never injected — leaving kimi parked at its own TUI approval menu forever while the server marked the answered card resolved and told the parent 'continuing'. Key the elicitation id on kimi's per-tool-call id (session + tool_call_id) so a re-fired hook re-parks the same server elicitation and picks up a verdict that landed while it was disconnected. Distinct call ids stay distinct; fall back to a random id only when kimi omits the call id. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: apeltekci <andrew@peltekci.com>
A headless polly kimi worker launched at kimi's default permission mode and parked on its in-TUI approval menu — an orchestrator can't answer it, so the dispatch stalled (the same wall that hung env-python-toolchain). Mirror the antigravity/cursor headless-bypass contract: translate an explicit permission_mode: bypassPermissions on a kimi-native sub-spec into kimi's -y/--yolo auto-approve flag, and set that mode on the packaged kimi worker so polly fan-outs run kimi with real tools unattended. Opt-in verbatim: any other mode leaves kimi prompting. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: apeltekci <andrew@peltekci.com>
# Conflicts: # omnigent/server/routes/sessions.py # web/src/pages/ChatPage.tsx
A scheduled task fires with no human present, but a native-terminal harness
(claude-native, codex-native, cursor-native, antigravity-native, kimi-native)
stops to ask for tool permission — Claude Code's approval menu, agy's
request-review, kimi's in-TUI menu. With nobody to answer, the terminal never
renders its input prompt and the run fails on readiness ("terminal did not
become ready within 30s"); observed on a claude-native news-digest task whose
web-search tool triggered Claude Code's approval prompt.
The fire path created the conversation without terminal_launch_args, so the
harness always launched at its prompting default. Derive the harness's
don't-prompt flag for the run by synthesizing permission_mode: bypassPermissions
into the spec and reusing _derive_terminal_launch_args_from_spec — the same
single source of truth the headless polly sub-agent path uses. omnigent's own
PreToolUse policy hook still gates every tool call; only the interactive prompt
is suppressed. Non-native (SDK) harnesses derive no args. Best-effort: any
resolution failure leaves the run at the harness default rather than blocking.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: apeltekci <andrew@peltekci.com>
# Conflicts: # tests/server/scheduled/test_fire.py
Adds a fork-only workflow that builds deploy/docker/Dockerfile and pushes
ghcr.io/apple-techie/omnigent-server:{sha-<short>,deploy-latest} on every push
to deploy/acp-slug-branding, authenticating with the built-in GITHUB_TOKEN
(permissions: packages: write) so a merge/sync no longer needs a manual
docker push + personal token. Additive and namespaced to the fork, so upstream
merges never touch it. amd64-only to match the deploy host.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: apeltekci <andrew@peltekci.com>
GitHub excludes forks from a package's Actions-access list and the source-repo inherit path doesn't grant it either, so GITHUB_TOKEN can't push to this account-owned package. Authenticate the push with the GHCR_PAT repo secret (a user PAT authenticates as the account and pushes regardless). Derive the owner from github.repository_owner for both the registry login and the image name rather than hardcoding it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: apeltekci <andrew@peltekci.com>
…ss merge) Add upstream-drift-sync.yml: daily, trial-merges upstream/main into a standing auto/upstream-sync branch. Clean merge -> push branch + open/update a PR (deploy <- sync) for human diff review; merging it fires deploy-image.yml. Conflicts -> open/refresh a 'manual merge needed' issue listing the conflicted files and touch nothing (conflict resolution needs a human in the merge, not an approval after). Never merges to deploy on its own; runs on GITHUB_TOKEN (same-repo branch/PR/issue ops). Also: skip the deploy-image rebuild on .github-only pushes (paths-ignore) so a workflow tweak doesn't republish a byte-identical image. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: apeltekci <andrew@peltekci.com>
Add optional workflow_dispatch inputs so a throwaway branch can exercise the merge->PR path without touching the real deploy branch; empty on schedule keeps the deploy/acp-slug-branding + auto/upstream-sync defaults. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: apeltekci <andrew@peltekci.com>
…upstream Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: apeltekci <andrew@peltekci.com>
Creating a project against a container-deployed server failed with 405. create_app mounts the projects router only when a project store is wired, and the Docker entrypoint built every other store but never this one — so POST /v1/projects was not a route at all and fell through to the SPA catch-all (GET-only), which answers 405. The CLI server path already wires it, so the same build worked under `omnigent server start` and failed in the container. Construct SqlAlchemyProjectStore from the resolved database URL and pass it to create_app, mirroring the other stores. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A host whose tunnel recycled (seen live as "no close frame received or sent. Reconnecting") reconnected and upserted itself online, then the OLD connection's teardown ran and unconditionally called set_offline — leaving a live, heartbeating host stuck at status=offline. The ping loop's heartbeat only bumps updated_at and never restores status, so the row stayed offline (and hidden in the UI) until a later reconnect happened to win the race. Give HostRegistry.deregister the connection generation guard the runner registry already uses: it removes the entry only when it is that exact connection, and returns what it removed. The tunnel's three teardown paths now call set_offline only when they actually removed their own connection, so a teardown that lost the race leaves the newer tunnel alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ce one The fire path synthesized permission_mode: bypassPermissions onto a copy of every agent's spec before deriving terminal launch args. That did three wrong things: it overrode a bundle that deliberately declared plan/acceptEdits, it silenced the user's consent gate (PermissionRequest) which the runtime keeps deliberately independent of the deployment's policy gate, and on a root host it produced a flag Claude Code refuses outright — the CLI exits 1 before the terminal is ready and the run fails as required_terminal_exited. Because the synthesized value crosses the shared per-harness mapping it also fanned out to five different vendor flags, so the breakage was never specific to one harness. Derive the launch args from the spec as loaded. A bundle that wants a headless mode declares it (the recipe the agent-authoring docs already give); a bundle that declares nothing launches at the harness default, as it did before this path forced anything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: apeltekci <andrew@peltekci.com>
Fork hygiene: every remaining fork-only product change must map to an open upstream PR. These had none and no viable path to one, so they come out rather than living on as permanent divergence. - scheduled/fire.py back to upstream parity (drops the forced permission bypass and its follow-up). Upstream PR closed in favour of the maintainer's per-task permission selector; routing the spec through _derive_terminal_launch_args_from_spec would still grant codex-native and cursor-native full bypass via that helper's defaults. - repl conversation-resume 5xx retry. Wrong layer (the SDK client has no transport retry, so one call site is patched and the rest stay bare), reinvents post_session_event_with_retry, misses 429, and leaves _list_all_conversation_items free to swallow the error and render an empty resumed conversation. - polly roster + worker bypass config and the built-in Grok Build agent. These are deployment content, not product: the roster names harnesses upstream doesn't ship, so a packaged bundle would be broken for every upstream user. Both now ship via OMNIGENT_BUILTIN_AGENT_DIRS, which supersedes the packaged bundles by name at startup. Signed-off-by: apeltekci <andrew@peltekci.com>
Replaces the fork's version with what was actually submitted upstream: declines ACP harnesses at describe_active_credential rather than in the readout (the startup header calls the resolver independently and lied the same way), derives the predicate from the declared capability record instead of a hardcoded list, drops the fork-only grok id, covers goose and qwen, and no longer claims an Omnigent-side /model override reaches the agent. Signed-off-by: apeltekci <andrew@peltekci.com>
Follow-on to the fork-only revert: these asserted the dropped roster and the forced permission bypass. Signed-off-by: apeltekci <andrew@peltekci.com>
Closes the 66-commit drift. Three conflicts resolved: - web/package-lock.json: accepted upstream's deletion. Upstream migrated web and electron to a root pnpm workspace, so the lockfile is superseded by pnpm-lock.yaml and the Dockerfile now runs `pnpm install --frozen-lockfile` instead of copying the npm lockfile. Deleting it without the rest of that migration would have broken the image build. - harness_readiness.py: took upstream's `_installer_only_availability(GOOSE_KEY)` and moved the fork's grok branch onto the same helper, matching the convention upstream converted the other CLI-gated harnesses to. - test_acp_spawn_env.py: additive on both sides, kept both tests. Also fixes a silent auto-merge break: the fork's `acp:<slug>` override branch skipped the `cfg` assignment that upstream's new `has_embedded` check reads, so an override raised UnboundLocalError. Hoisted the assignment. Signed-off-by: apeltekci <andrew@peltekci.com>
Merge upstream/main into deploy (66-commit drift, 3 conflicts resolved)
The previous sync landed upstream's *content* but not its *lineage*: a `git stash` run mid-merge cleared MERGE_HEAD, so the commit came out with a single parent and git still reported deploy as 66 behind with a merge base predating the drift. Every future sync would have re-conflicted on the same files. This is the real merge. Three conflicts, all the same shape — fork adds a hunk, upstream side empty — so HEAD's hunk is kept and upstream's other edits to those files stay auto-merged: - harness_install.py / harness_readiness.py: the grok harness entries - test_acp_spawn_env.py: the ACP model-options test Signed-off-by: apeltekci <andrew@peltekci.com>
Fork-only with no upstream path — the branded-icon PR was deliberately scoped to grok, so this had nowhere to land. Droid agents fall back to the generic BotIcon like every other unmapped ACP agent. Signed-off-by: apeltekci <andrew@peltekci.com>
Reverts the picker-visibility half of upstream omnigent-ai#2143 for this deployment. openai-agents was never removed as a harness — valid_harnesses, harness_modules and capabilities were untouched there — only its two label entries, so restoring them is enough to make it selectable again. Both sources must carry it: the web merges server rows on top of its static BRAIN_HARNESS_LABELS fallback and only ever adds, so a row missing from either one stays missing from the picker. Signed-off-by: apeltekci <andrew@peltekci.com>
Four conflicts. Three are our copy vs upstream's now-merged copy of the same change (omnigent-ai#3431, merged 2026-07-29) — took upstream wholesale, since the maintainer's version is stricter: the helper is public (harness_owns_its_credential) and gains an early `if canonical in _HARNESS_FAMILY: return False`. The fourth, runner/app.py, needed a real port rather than a side: ours: _build_acp_spawn_env(spec, ..., override_harness=acp_override) upstream: _build_acp_spawn_env(effective_spec, cwd=cwd, workdir=workdir) Upstream introduced `effective_spec`, which bakes in the per-session model override. Taking our side verbatim would have silently dropped that override for every ACP session, so this keeps upstream's spec AND our acp:<slug> threading (omnigent-ai#3057). The openai-agents picker label survives the merge untouched in both sources despite upstream refactoring harness_plugins.py three times (omnigent-ai#3239, omnigent-ai#3495, omnigent-ai#3500) — different regions, no conflict. Signed-off-by: apeltekci <andrew@peltekci.com>
…er image The server validates a bundle's harness against valid_harnesses() at spec parse (_omnigent_compat.py), so a bundle declaring `harness: droid` is rejected before it ever reaches a host — droid was usable only via the CLI on machines with the plugin installed by hand. Installs omnigent-droid in the server-builder stage, whose /opt/venv the runtime stage copies. Pinned to a commit so a rebuild can't silently pick up a different plugin revision, and --no-deps because the plugin depends on an unpinned `omnigent` that would otherwise pull core from PyPI over this image's own editable install. Deployment plumbing, not product divergence: the plugin lives in its own repo on the sanctioned community-harness entry point, so nothing here changes upstream product code. Signed-off-by: apeltekci <andrew@peltekci.com>
…ttps The server-builder stage is python-slim, which ships no git binary, so uv failed the build with "Git executable not found". GitHub's source archive needs no git and stays pinned to the same commit, so use that instead of adding git (and a layer) to the image purely to fetch one small package. Signed-off-by: apeltekci <andrew@peltekci.com>
…names
Ports the upstream PR onto deploy and bumps the droid plugin pin so the
picker works for droid.
The picker route and the session marker both matched harness names ("acp",
plus a fork-only "grok" arm), so a community ACP plugin could never be
covered. Both now read the declared capability record
(IntegrationMode.ACP_SUBPROCESS) — grok stays covered because it declares
ACP_SUBPROCESS, so the hardcoded arm was redundant.
The marker also only ran for sessions created with an explicit
harness_override, so the droid builtin agent — which declares
`harness: droid` rather than being picked as an override — was never
labelled and rendered without a picker. It now falls back to the agent's
declared harness, best-effort so a spec that cannot be loaded leaves the
session unlabelled rather than failing the create.
Plugin side (omnigent-droid @ 30854927): session/new already returned a
models block that was parsed and discarded; it is now kept and exposed via
available_models() / current_model_id().
Signed-off-by: apeltekci <andrew@peltekci.com>
Plugin registers a spawn-env builder so the per-session /model override lands in HARNESS_DROID_MODEL, and applies session/set_model per turn so a mid-session pick is not inert. Signed-off-by: apeltekci <andrew@peltekci.com>
An ACP agent owns its own model, so a session with no override has no Omnigent-visible model to show. But `acp` fell through to the branch that falls back to `selectedModel` — the global sticky — so the composer confidently labelled a fresh droid session with a pick left over from an unrelated session while droid was answering on its own configured model. Same hazard the neighbouring comment already names for SDK/bundle agents; `pickerSelectedModel` already excluded the sticky for acp, this path didn't. Signed-off-by: apeltekci <andrew@peltekci.com>
🤖 Polly ReviewReview could not be completed: |
…ent-ai#4080) Closes omnigent-ai#866 The `omnigent` Homebrew formula has not built since 0.7.0, and the tap reported green anyway, so 0.7.0, 0.8.0 and 0.8.1 all merged with no bottle — every user compiled from source, and CEL policies silently did not work. - **Root cause was the CEL migration.** omnigent-ai#2970 swapped `cel-expr-python` for `cel-python` on the premise that it is pure Python. It is not: `cel-python` hard-depends on `google-re2`, whose sdist runs `bazel build` whenever `GITHUB_ACTIONS` is set. The bazel dependency moved rather than disappeared. - **Pin compiled extensions to upstream wheels.** `generate_formula.py` gains `WHEEL_REQUIRED` / `PREFER_WHEEL` / `PURE_WHEEL` with abi3 and universal2 handling, so grpcio (by far the most expensive build), protobuf, regex, uvloop, httptools, argon2-cffi-bindings, markupsafe, pyyaml, zstandard and google-re2 stop being compiled. Native wheels rank above pure-Python ones, so protobuf keeps its upb build instead of the slow fallback. - **jiter, tiktoken and watchfiles keep building from source.** Their maturin wheels carry no Mach-O install-name padding, so Homebrew relocation fails with "Failed changing dylib ID" (omnigent-ai#866). `pendulum` can go neither way — its wheel cannot be relocated and its sdist does not link on 3.14 (pyo3 leaves `_Py_NoneStruct` undefined) — so it takes the pure-Python wheel, which ships no extension module at all. - **A dropped dependency is now an error, not a warning.** A missing sdist used to be skipped silently, yielding a formula whose venv lacked an import; `--allow-no-sdist` is the explicit waiver. The formula test also asserts `import re2, celpy`, since omnigent imports celpy behind `try/except ImportError` and would otherwise disable policies silently. - **Delete `update-homebrew.yml`.** It raced `homebrew-tap-pr.yml` on the same `release: published` event and asserted on hand-maintained stanzas the template no longer emits, so it failed on every run. Its one worthwhile part moves into `homebrew-tap-pr.yml`: an admin/maintain gate on manual dispatch (it writes to another repo with an App token), plus `persist-credentials: false`. Its nightly `schedule` is deliberately NOT carried over -- that cron only existed because `brew update-python-resources` resolves through pip's `--uploaded-prior-to=P1D` window and so could never see a same-day release. The generator runs `uv pip compile --no-config` straight against PyPI, so the blindness it worked around no longer exists, and a nightly regeneration would just burn a runner to print "nothing to do". Verified by building the generated formula in the tap, not by inspection. - `omnigent-ai/homebrew-tap#18` contains **verbatim output of this `generate_formula.py`** and bottled successfully on macos-15 and macos-26 (run 30944428771, `bottles_macos-15` / `bottles_macos-26` ≈ 37 MB each). This is the check that matters: it proves the generator — not a hand-edit — produces a buildable formula, so the next release regenerates something that works. - `omnigent-ai/homebrew-tap#17` carries the same fix for the shipped 0.8.1 formula and is green on all three runners, with `brew test` running `import re2, celpy`. Inspected the bottle: `celpy/__init__.py`, `re2/_re2.cpython-314-darwin.so`, and a relocated `jiter/jiter.cpython-314-darwin.so`. - Audited every pinned wheel by replaying Homebrew's own operation, `install_name_tool -id <Cellar path>` against each extracted `.so`, so the wheel/source split is evidence-based rather than guessed. - `python3.12 -m py_compile`, `ruff check`, `ruff format --check`, `brew style` (no offenses), `ruby -c`, plus stubbed-PyPI unit checks of the new failure paths (missing sdist is fatal, `--allow-no-sdist` waives it, abi3 accepted, free-threaded `cp314t` rejected). - Confirmed generator output matches the green formula: same 100 resources, identical sdist/wheel split, no non-comment differences. N/A — release tooling, no user-visible UI. - [x] Bug fix - [ ] Feature - [ ] UI / frontend change - [ ] Refactor / chore - [ ] Docs - [ ] Test / CI - [ ] Breaking change - [ ] Unit tests added / updated - [ ] Integration tests added / updated - [ ] E2E tests added / updated - [x] Manual verification completed - [ ] Existing tests cover this change - [ ] Not applicable The generator has no test suite in this repo, and its real contract — "the emitted formula builds under Homebrew on macOS" — cannot be asserted here. It is covered instead by building the generated formula on the tap's `brew test-bot` matrix (homebrew-tap#18, bottles produced on macos-15 and macos-26). The two new generator failure paths were exercised locally against stubbed PyPI metadata, and every wheel pin was verified relocatable with `install_name_tool`. `brew install omnigent` works again, and installs prebuilt wheels instead of compiling grpcio and friends from source. Signed-off-by: Zeyi (Rice) Fan <zeyi.f@databricks.com>
|
Closing — artifact of the fork retirement, nothing to salvage. This is upstream's own The other 38 files (~1400 lines) are the old fork's divergence leaking into the diff — It is also a Discord on-call rotation for upstream's maintainers, which is not this repo's concern. Upstream has since paused the cron ( |
Automated monthly housekeeping: pruned elapsed dates and extended
rotation_schedule.json~3 months out. Generated by the discord-watch-rotation-maintain workflow.