Landing blocker for #61 and #63: the deployment checkout is 12 commits behind and `merge --ff-only` refuses while tracked paths are dirty. Discard is not available here (workspace policy: no `checkout`/`stash` on shared state), so the dirt is committed. - `agent/APPEND_SYSTEM.md`: the point-of-use / one-work-log / PR-link doctrine, already byte-identical to `origin/main` (`cmp` against the trunk blob: no difference). No new prose — the commit exists so the trunk merge can proceed. - `agent/config.yml` and `agent/managed-skills/**`: the machine-live tier the heartbeat owns (AGENTS.md § Live-sync tiers). `ALLOW_SECRETS=1` was used, deliberately and after auditing the staged diff by hand: the only credential-shaped strings in it are two copies of `AKIA0000000000000EXAMPLE` — the inert all-zero AWS example id — and they appear inside the two skill pages that teach that exact placeholder shape. This checkout is still running the pre-#61 hook; the merged gate (value-scoped placeholder filter, `71601f7`) exempts this value, so the refusal dies with this landing. No `--no-verify` was used: the gate ran and its verdict is recorded here. Refs #61
4.1 KiB
name, description
| name | description |
|---|---|
| omp-search-provider-fallback-debug | Diagnose why the omp harness web_search silently uses a fallback provider (e.g. Kagi) instead of the configured primary (SearXNG): the fallbackChains masking pattern, the searxng.endpoint base-URL scheme/guard-chain trap, and the cli.js + content-type probes that prove it. |
omp web_search silently using the wrong provider
Symptom: agent searches return results from a fallback provider (e.g. Kagi) even though
modelRoles.web is set to the intended primary (e.g. web/searxng). The primary errors
every time and the failure is invisible because a fallback answers successfully.
The masking mechanism (always check this first)
~/.omp/agent/config.yml:
modelRoles.web:is the primary (e.g.web/searxng).retry.fallbackChains.web:is an ordered list tried when the primary throws. The FIRST entry silently serves whenever the primary errors. In this fleet it isweb/kagi, so any SearXNG error == Kagi answers and you never see an error.
So "searches go through Kagi" almost always means "SearXNG is failing", not a routing mistake.
The SearXNG endpoint trap (the usual root cause here)
searxng.endpoint in config.yml must be a full base URL pointing at the direct JSON port,
used verbatim by omp (no scheme auto-added, no normalize — confirmed in cli.js). Two ways
it breaks:
- Bare host (e.g.
search.franksdank.cloud, no scheme): omp doesnew URL(\${endpoint}/search`)` → throws immediately (URL needs a scheme). SearXNG never contacted. - The guarded vhost (
https://search.franksdank.cloud): returns Anubis' "Making sure you're not a bot!" challenge page at HTTP 200text/html. omp callso.json()unconditionally regardless of content-type → throws on the leading<. There is deliberately NO API path through the guard chain (JS challenge + UA denylist).
Correct value (direct published port, http scheme preserved — reachable from the LAN only):
searxng:
endpoint: http://192.168.0.143:8081 # Dankclaw; NOT the vhost, NOT schemeless
The published port source (192.168.x/loopback/LAN) is in SearXNG's pass_ip, so it bypasses
the JSON rate limiter (hardcoded API_MAX=4/IP/hr, not settable except via pass_ip).
Prove it (don't guess)
- Content-type probe, both paths — the decisive check:
vhost =
curl -sk -o /tmp/vh -w "vhost ct=%{content_type}\n" "https://search.franksdank.cloud/search?q=test&format=json" # expect text/html (Anubis) curl -s -o /tmp/dp -w "direct ct=%{content_type}\n" "http://192.168.0.143:8081/search?q=test&format=json" # expect application/jsontext/html+ direct =application/jsonconfirms the trap. - Code path, in the installed omp dist (NOT a worktree):
/home/blank/.bun/install/global/node_modules/@oh-my-pi/pi-coding-agent/dist/cli.js(find install:dirname $(readlink -f $(which omp))). Grep the SearXNG provider:searxng.endpointconfig def → description "Base URL of a self-hosted SearXNG instance".- the request builder
...=e.replace(/\/+$/,""),new URL(\${r}/search`)...` → endpoint is verbatim, scheme NOT added. X2c/J2c→if(!o.ok)throwthenreturn await o.json()→ HTML 200 passes.okand blows up in.json().
Apply + verify
- Fix is a harness config edit (
cfg://searxng/endpointprompts the user, or edit config.yml). Confirm with the user first — it is their live harness config. - Verification after applying: fire a real
web_searchand confirm it returnsprovider:"searxng"(not"searxng"-then-fallback / not"kagi").
Related gotcha (same masking, different cause)
If SearXNG answers JSON but returns empty results with unresponsive_engines (VPN-exit-IP
CAPTCHA/429 on google/brave), omp's vcs throws a 503 → Kagi again. That's an engine-health
problem, fix in [[SearXNG]]/[[SearXNG Providers]], not the endpoint.
Hardening option
Drop or reorder web/kagi / web/perplexity in retry.fallbackChains.web so primary failures
surface instead of silently spending a paid Kagi credit. Flag to user before editing.