Track of every significant piece of work completed. Update this after each session.
Built the missing backend economy engine and integrated + merged all outstanding showcase PRs end-to-end.
Backend (Workstream A, #1 → PR #12):
- 3 SQLite tables (
agent_passports,agent_reputation,proofs) + accessors indb.py. economy.py: deterministic hashing + reputation math (composite 0..1000, Gold/Silver/Bronze badges, ±20% delta cap) +seed_passports/recompute_role/record_proof/backfill.record_proofnever raises into the worker; emitsproof_recorded/reputation_updateon theeconomySSE channel.worker._after_task_donemints a proof per completed task;main.pyseeds + backfills on startup and registersapi/economy.py(/passports|leaderboard|proofs|agents/{role}|chain|stream).deployments/10143.jsonmocks Monad testnet. 13 pytest cases pass.
Integration + fixes:
- Merged PRs #8 (C: DEMO_MODE + wallet), #9 (E: rebrand), #10 (B: economy UI), #11 (D: replay), #12 (A: backend) into
main— AppNav auto-merged cleanly (Mergit wordmark + Economy link + WalletConnect). - Reconciled the B frontend (was written against an imagined API) with the real backend: rewrote
lib/api.tseconomy types/fetchers (bare arrays, real fields) + all economy components/pages (badge + score bars, NFT-style passports with DID/soulbound/capabilities/mint block, tx/result-hash proof rows,:roleroute). - Rewrote
scripts/replay_demo.pyto the realrecord_proof(task, output)interface.seed_passportsnow seeds a neutral reputation row per role so all 6 agents rank. Dropped unusedBellimports blockingtsc -b.
Verified: 13 backend tests pass; npm run build clean; replay mints 3 proofs (blocks 18100000+); live endpoints return chainId 10143, 6 passports, a 6-agent leaderboard. All 7 issues closed; all 5 PRs merged.
Commit: ca12e75 [init] initial prototype of the omniBox
- Scaffolded the full project: FastAPI backend + Vite/React/TypeScript frontend
- Implemented SQLite WAL schema:
goals,tasks,messages,tool_callstables - Built
orchestrator.py— Claude forced tool call →PlanSchematask DAG - Built
agent_runner.py— generic LLM tool-call loop withsubmit_result - Built
worker.py— three asyncio loops: goal planner, task executor, lease reclaim - Built all 7 tools:
web_search,http_request,slack_notify,file_ops,github_pr,code_exec,wait_webhook - Built
interpolation.py— resolves{{task_id.output.field}}templates - Built SSE streaming — in-process
asyncio.Queueper goal,EventSourceon frontend - Built all API routes:
POST /api/goals,GET /api/goals/{id},GET /api/goals/{id}/stream,POST /api/webhooks/{token},GET /api/health - Basic React frontend: Dashboard (goal list + submit), GoalDetail (tasks + live log)
- LiteLLM as multi-provider wrapper (
anthropic/...,groq/...prefixes)
Commit: 232fef6 [ui/ux] improved the ui ux of the site
- Redesigned full frontend with dark glassmorphism aesthetic
- Near-black
#0a0a0abackground,#111cards - Electric blue accent (
#3b82f6) + status colors (green/red/amber) - Inter font, tight tracking, monospace for output
- Framer Motion animations: page transitions, staggered card entrances, pulsing status badges
- Near-black
- Created
Landing.tsx— marketing landing page at/with hero, features section, CTA - Rewrote
Dashboard.tsx:StatsStrip— 4-stat grid (Active/Completed/Failed/Total)EmptyState— animated bot icon with glow- Status filter tabs (All / RUNNING / COMPLETED / FAILED)
AnimatePresencefor goal list transitions- Error display for submit failures, skeleton loading cards
- Rewrote
GoalCard.tsx— relative timestamps, blue glow strip on left for active goals - Rewrote
GoalInput.tsx— focus glow border, animated hint text, char count - Rewrote
GoalDetail.tsx— better error state, planning spinner,isLoadinghandling - Created
AppNav.tsx— sticky glassmorphic nav (logo, Dashboard, API Docs links)
- LiteLLM tool_choice format — Changed orchestrator
tool_choicefrom{"type":"function","name":"..."}to{"type":"function","function":{"name":"..."}}(OpenAI format required by LiteLLM) - Task ID UNIQUE constraint — Orchestrator reused short IDs (
t1,t2) per goal, causing DB conflicts on the second goal. Fixed by prefixing all task IDs withgoal.id[:8]_inrun_plan(). Added_rewrite_templates()to rewrite{{t1.output.field}}refs to match new IDs. _truncate_argsNameError — Function used intool_span()but never defined. Added definition intracing.py.file_opspath traversal check —WORKSPACEwas relative;full_path.relative_to()raisedValueErroron absolute-vs-relative mismatch. Fixed toPath(settings.workspace_dir).resolve(). Also strips absolute LLM-injected paths viaos.path.basename().- Groq rate limits — Added exponential backoff
wait = min(2 ** iteration, 30)inagent_runner.py - Groq
tool_use_failed— Added retry hint injection +_try_parse_json_result()fallback parser - Orchestrator retry count — Raised from 3 to 5 attempts with rate-limit backoff
- Rewrote
tracing.pyentirely:init_tracing()— initialises Omium SDK withauto_trace=Falsegoal_trace_context()— context manager creatingOmiumTracerscoped to one goal, opensgoal_runroot spantask_span()— context manager fortask/{agent_name}spanstool_span()— context manager fortool/{tool_name}spans; tagscached=truefor replayed idempotent callswebhook_span()— context manager forwebhook_resumespans_truncate_args()— strips_-prefixed internal keys, truncates long strings to 300 chars- All functions degrade gracefully to no-ops if
omiumnot installed
- Updated
worker.py— wraps orchestrator call ingoal_trace_context, openstask_spanper task execution - Updated
agent_runner.py—tracerparam threaded through,tool_spanwraps every tool call, sets error/output attributes - Updated
api/webhooks.py— wraps webhook processing inwebhook_span - Causal linking: all spans share
execution_id = goal.trace_id(UUID stored in DB), so full trace appears as one chain in Omium dashboard
main.py—logging.basicConfigwith structured format, silenceshttpx/litellm/anthropic/openainoise- Request logging middleware logs method/path/status/timing per request
- Unhandled exception handler returns JSON
{detail, request_id}
- Added
debug: bool = Falsesetting
- Fixed background disappearing after scrolling — changed to
position: fixedatz-0with content atz-10 - Added dot grid overlay and 5 ambient orbs at
top/120vh/240vh/360vhfor coverage throughout full page scroll
User requested: default all agents to Groq only; provide a UI section where each agent role's model can be independently configured (e.g. use Claude for orchestrator, Groq for leaf agents).
-
backend/model_config.py(new) — Per-role model storeDEFAULTS— all roles default to Groq (llama-3.3-70b-versatile, notifier usesllama-3.1-8b-instant)AVAILABLE_MODELS— 5 models: Groq 70B versatile, Groq 70B 3.1, Groq 8B instant, Claude Haiku 4.5, Claude Sonnet 4.6get_model(role)/get_all()/update(dict)— read/write with JSON file persistence- Persists to
backend/model_config.json(gitignored) - In-process cache; merges saved config with DEFAULTS so new roles always have a value
-
backend/api/config.py(new) — REST endpointsGET /api/config/models— returns{models, available, defaults}PUT /api/config/models— validates role names + model IDs, saves and returns updated config
-
backend/orchestrator.py— reads orchestrator model viamodel_config.get_model("orchestrator")at call time (no more hardcoded model constant); added JSON-body fallback for when Groq ignores forced tool_choice -
backend/agent_registry.py— addedget_agent_config(name)which overlays the live model config on top of the static registry dict -
backend/agent_runner.py— usesget_agent_config(task.agent_name)instead of staticAGENT_REGISTRY[...]so model changes take effect immediately -
backend/main.py— registersconfig.routerat/api/config
-
frontend/src/lib/api.ts— addedModelOption,ModelConfigtypes +getModelConfig()andupdateModelConfig()API methods -
frontend/src/components/ModelSettings.tsx(new) — full settings modal- One card per role (Orchestrator, Researcher, Writer, Notifier, Coder, Integrator) with icon, label, description
- Per-role
ModelSelectdropdown showing all available models with provider/tier labels - Provider color coding: Groq = emerald, Anthropic = violet
- Save button (active only when dirty), Reset to defaults, error display
- Framer Motion backdrop + panel entrance animation
-
frontend/src/components/AppNav.tsx— added "Models" button (gear icon) that opensModelSettingsmodal
.gitignore(new) — coversbackend/.env,backend/model_config.json,backend/omnibox.db,backend/workspace/,frontend/node_modules/,frontend/dist/,__pycache__/
researcher agent failing with "did not call submit_result within 8 iterations". Root cause confirmed via DB inspection: web_search was returning {"error": "Unauthorized: missing or invalid API key."} on every call (Tavily key was placeholder). The model saw the error and retried the same tool all 8 iterations, never calling submit_result.
backend/tools/web_search.py — Tiered search with graceful fallback:
- Try Tavily first — only if key is set and not the
tvly-...placeholder - Fall back to DuckDuckGo Instant Answer API (free, no key needed) — returns abstract + related topics
- Final fallback: returns
{"results": [], "note": "Web search unavailable. Use your training knowledge to answer about: <query>"}— tells the model exactly what to do instead of returning a cryptic error
backend/agent_runner.py — Added two safety mechanisms:
consecutive_errorscounter — when 3+ consecutive tool calls fail, injects: "These tools are failing: X. Stop calling them. Use your training knowledge and call submit_result NOW."_failing_toolsset — tracks which tools failed so the nudge message names them explicitly- Early warning at
max_iter - 3iterations — tells model to wrap up - Stronger final nudge: "Call submit_result NOW. Do not call any other tools."
consecutive_errorsresets to 0 after a successful tool call and after the forced-submit injection
backend/agent_registry.py — Updated researcher:
- System prompt now explicitly says: "If web_search returns a 'note' field saying it's unavailable, do NOT retry it — answer from your training knowledge"
- System prompt says: "If any tool fails twice in a row, stop calling it"
max_iterationsincreased from 8 → 10allowed_toolsalready includedhttp_request(can fetch arbitrary URLs/APIs as an alternative search path)
- Tavily key present → uses Tavily (fast, rich results)
- Tavily key missing/invalid → DuckDuckGo free API (no key needed)
- DuckDuckGo returns nothing → model gets a note saying "use your knowledge" → submits answer from training data
- Any tool fails 3× in a row → forced submit message injected regardless of which tool it is
Groq free tier has a 100K tokens/day (TPD) limit per model. After testing, llama-3.3-70b-versatile hit its daily quota → orchestrator failed on all 5 retry attempts with the same provider error. No fallback existed.
Added _FALLBACKS dict: when a model hits a hard rate limit (daily quota / tokens per day), acompletion() automatically tries the next model in the chain before raising.
Fallback chains:
groq/llama-3.3-70b-versatile→groq/llama-3.1-8b-instant→anthropic/claude-haiku-4-5-20251001groq/llama-3.1-8b-instant→anthropic/claude-haiku-4-5-20251001anthropic/claude-haiku-4-5-20251001→groq/llama-3.1-8b-instant→groq/llama-3.3-70b-versatile
Added _is_hard_rate_limit() (daily quota / TPD → switch provider) vs _is_soft_rate_limit() (TPM / per-minute → caller backs off with sleep). Only hard limits trigger a fallback; soft limits are left for the caller's existing retry logic in agent_runner.py.
Full-page model configuration at /app/models replacing the modal approach. Two editor modes that stay in sync.
frontend/src/pages/Models.tsx (new):
- Visual Editor tab — per-role cards (Orchestrator / Researcher / Writer / Notifier / Coder / Integrator), each with a
ModelPickerdropdown- Grouped by provider (Groq / Anthropic sections)
- Tier badges (Fast / Instant / Powerful)
- Custom model ID entry — "Custom model ID…" option opens an inline text input accepting any
provider/model-namestring (e.g.openai/gpt-4o,mistral/mistral-large-latest)
- JSON Editor tab — raw textarea with line numbers, live validation
- Parses on every keystroke; shows inline error for invalid JSON or unknown roles
- Syncs to/from Visual tab bidirectionally
- Available Models reference table — shows all suggested models with provider/tier badges; notes any LiteLLM-compatible ID also works
- Save/Reset buttons with dirty-state detection
frontend/src/App.tsx — added /app/models route
frontend/src/components/AppNav.tsx — "Models" link now navigates to /app/models (active highlight when on that page); removed modal dependency
backend/model_config.py — relaxed validation: any non-empty string is accepted as a model ID (not just predefined list). Custom provider IDs (openai/, mistral/, cohere/, etc.) now work.
Provider detection — detectProvider() auto-detects provider from model ID prefix and applies color coding:
groq/→ emeraldanthropic/→ violetopenai/→ bluecohere/→ orangemistral/→ amber- unknown → white (custom)
Landing page (/) had the full design treatment: fixed dot-grid background + ambient orbs + glassmorphism. All three /app pages (Dashboard, GoalDetail, Models) had flat bg-black — no dot grid, no orbs, cards looked opaque and lifeless.
frontend/src/components/AppBackground.tsx (new) — shared fixed background component:
- Same dot grid (32px, opacity 0.14) as Landing
- Top-right accent orb (blue→purple,
blur(90px), animated withglow-pulse) - Bottom-left purple orb (opacity 0.15,
blur(100px), delay 1.8s) - Subtle centre wash orb (very low opacity 0.06)
fixed inset-0 z-0 pointer-events-none— sits behind all content
All three app pages updated (Dashboard.tsx, GoalDetail.tsx, Models.tsx):
- Root div changed from
min-h-screen bg-black→relative min-h-screenwithbackground: "#000" <AppBackground />inserted at root- Content wrapped in
relative z-10 flex flex-col min-h-screen
GoalDetail panes — changed from opaque bg-black to bg-black/20 backdrop-blur-sm and bg-black/30 backdrop-blur-sm so the dot grid and orbs show through the split panes.
Dashboard filter bar — changed from opaque bg-surface border-border to bg-black/30 backdrop-blur-sm border-white/8 for consistency.
Two task failures observed:
56002610_t2researcher — "did not call submit_result within 10 iterations". Root cause: agent received raw unresolved template strings like{{t1.output.key_points[0]}}as literal text in its inputs.56002610_t3writer — "Interpolation error: '56002610_t2'" — t3 depended on t2 output, but t2 failed because its inputs were never resolved.
Two bugs working together:
_rewrite_templates()inorchestrator.pyused regexr"\{\{(\w+)(\.output\.\w+)\}\}"— the\.output\.\w+segment does not match[0](array index), so{{t1.output.key_points[0]}}was silently NOT rewritten with the goal prefix. The template survived as{{t1.output.key_points[0]}}(unprefixed) into the DB.TEMPLATE_REininterpolation.pyusedr"\{\{(\w+)\.output\.(\w+)\}\}"— same problem:(\w+)doesn't matchkey_points[0], so the template was never resolved at execution time.
backend/interpolation.py:
- New
TEMPLATE_RE = re.compile(r"\{\{(\w+)\.output\.([\w\[\]\.0-9]+)\}\}")— path group now matchesfield,field[0],field[0].subfield - New
_resolve_path(obj, path)— splits on.then on[N]within each segment; traverses dicts by key and lists by integer index resolve_value()now calls_resolve_path(task_outputs[task_id], path)instead of a direct dict key lookup
backend/orchestrator.py _rewrite_templates():
TMPLregex updated tor"\{\{(\w+)(\.output\.[\w\[\]\.0-9]+)\}\}"— group 2 now captures the full path including array indices- No logic change — the substitution lambda still uses
id_map.get(m.group(1), m.group(1))which is correct
Goal immediately transitions PLANNING → FAILED with:
litellm.BadRequestError: GroqException - {"code":"tool_use_failed","failed_generation":"<function=submit_plan> {...} </function>"}
Root cause: Groq generates the plan correctly but uses an XML-function format (<function=submit_plan>) instead of a proper JSON tool call. Groq itself rejects this as malformed and returns a 400 BadRequestError. The orchestrator's except Exception block only retried on rate limits — for any other exception it raised immediately, causing the goal to fail.
The plan JSON was right there in failed_generation but was never extracted.
-
_salvage_failed_generation(error_str)(new helper): Extracts JSON from Groq'sfailed_generationfield. Handles both the direct<function=name> {...} </function>pattern and the escaped JSON string form in the error response. -
tool_use_failedhandler in the retry loop: When aBadRequestErrorwithtool_use_failedis caught:- First tries to salvage the plan directly from
failed_generation - If salvage succeeds and validates, returns the plan immediately (no extra round-trip)
- If salvage fails,
continues to the next attempt
- First tries to salvage the plan directly from
-
Progressive
tool_choicerelaxation: Attempts 0-1 use forced{"type":"function","function":{"name":"submit_plan"}}. Attempts 2+ fall back to"auto"— lets Groq choose when to call the tool, avoiding the format mismatch that causestool_use_failed. -
Removed stray
import reinsideplan()— now uses module-level import.
User pointed out that the PR ecosystem icon fix had not been implemented, asked what the Omium crash list meant, and requested that progress.md be updated after every prompt going forward.
-
frontend/src/components/landing/PartnersMarquee.tsx- Replaced the nearly invisible generic square placeholders with visible branded ecosystem badges.
- Added per-partner accent colors and monogram marks for Anthropic, Groq, Tavily, LiteLLM, FastAPI, ReactFlow, Framer Motion, and SQLite.
- Kept the implementation dependency-free because
npm install simple-iconshung in the sandboxed environment.
-
frontend/src/components/AppNav.tsx- Fixed production build failure: current
lucide-reactpackage does not exportGithub. - Replaced
Githubwith existingGitBranchicon.
- Fixed production build failure: current
-
Crash class 1: Groq malformed tool calls
- Omium showed
tool call validation failed/tool_use_failederrors where Groq generated malformed function-call syntax such as embedding JSON in the tool name. - Existing recovery handles
<function=...>failed generations; nowtool call validation failedis also treated as a retryable model-format failure so the agent receives a stricter tool-call hint instead of immediately crashing.
- Omium showed
-
Crash class 2: Groq rate limits
- Omium showed repeated
litellm.RateLimitErrorentries for Groqllama-3.3-70b-versatile. backend/llm.pynow retries short soft rate limits internally using parsedtry again in ...delays.- Long waits or hard quota errors still trigger the existing fallback chain to another model.
- Omium showed repeated
backend/.venv/bin/python -m py_compile llm.py agent_runner.pynpm run build
User ran backend/.venv/bin/python -m py_compile llm.py agent_runner.py from the repo root and got [Errno 2] No such file or directory: 'llm.py'.
llm.pyandagent_runner.pylive insidebackend/.- From repo root, use:
backend/.venv/bin/python -m py_compile backend/llm.py backend/agent_runner.py
- Or first
cd backend, then use:.venv/bin/python -m py_compile llm.py agent_runner.py
- Confirmed both corrected commands pass.
frontend/src/components/ModelErrorBanner.tsx(new) — centered modal with dark backdrop- Detects
invalid_api_key, quota exceeded, rate limit from goal error string - Detects failing provider (Groq/Anthropic/OpenAI/Google/Mistral) from error text
- "Add API key" button expands inline password input (show/hide toggle) to save key immediately
- "Change model" button navigates to
/app/models - Dismiss closes; clicking backdrop closes
- Fixed centering: backdrop is
fixed inset-0 flex items-center justify-center— not affected by Framer Motion ancestor transforms
- Detects
- Wired into
GoalDetail.tsx— appears wheneverdata.errorcontains a model error
backend/api/keys.py(new) —GET /api/config/keys+PUT /api/config/keys- Maps 6 providers → env var names (GROQ, ANTHROPIC, OPENAI, GOOGLE, MISTRAL, TAVILY)
- Reads from both
os.environand.envfile; writes viapython-dotenv.set_key() - Updates
os.environimmediately — no backend restart needed for key to take effect - Returns masked values (
first6...last4)
frontend/src/pages/Models.tsx— newApiKeysSectioncomponent- One row per provider with color-coded badge, env var name, masked value / "Not set" status
- Inline expand/collapse password input with show/hide toggle
- Save writes to backend immediately
frontend/src/lib/api.ts— addedgetApiKeys()andupdateApiKey()methods
- Groq: Llama 4 Maverick/Scout, Llama 3.3/3.1/3.2 (70B→3B), DeepSeek R1 70B, Qwen QwQ 32B, Mixtral 8x7B, Gemma 2 9B
- Anthropic: Claude Opus 4.7, Sonnet 4.6, Haiku 4.5, Claude 3.5 Sonnet/Haiku, Claude 3 Opus
- OpenAI: GPT-4o/Mini, o3, o4-mini, o3-mini, o1, GPT-4 Turbo, GPT-3.5 Turbo
- Google: Gemini 2.5 Pro/Flash, 2.0 Flash/Lite, 1.5 Pro/Flash/Flash-8B
- Mistral: Large, Medium 3, Small 3, Codestral, Pixtral Large
- Provider detection updated:
gemini/andgoogle/→ sky blue - Dropdown:
overflow-y-auto max-h-72(was clipping off-screen)
- Added GitHub link (
https://github.com/viscous106/omniBox) withGitBranchicon
- Makefile —
dev-backendused$(PYTHON)(root-relative path) aftercd backend, causing "No such file" error. Fixed to.venv/bin/python main.py - Watchfiles spam — silenced
watchfileslogger inmain.pylogging setup reload_includes/reload_excludesadded to uvicorn config to only watch.pyfiles
- Tavily API key — optional now (system falls back), but setting a real key gives better search results
- React Flow / TaskDAG —
TaskDAG.tsxcomponent exists in plan but may not be fully wired inGoalDetail.tsx - OutputDisplay — final goal output rendering as markdown not yet verified end-to-end
- Frontend bundle size — single chunk ~686 kB; consider
React.lazy()for React Flow if it becomes an issue
Goals were failing when using Gemini models. Two root causes:
gemini-2.5-prohas zero free-tier quota — hitsRESOURCE_EXHAUSTEDimmediately. The fallback chain only had Groq ↔ Anthropic entries; no Gemini model was in_FALLBACKSso it raised immediately.GEMINI_API_KEYnot set at startup —llm.pyonly calledos.environ.setdefaultfor Anthropic and Groq. Even if the user hadGOOGLE_API_KEYin.env, LiteLLM wouldn't find it because it looks forGEMINI_API_KEY.OPENAI_API_KEYandMISTRAL_API_KEYnot loaded — same startup omission for those providers.
backend/config.py:
- Added
openai_api_key,gemini_api_key,google_api_key,mistral_api_keysettings fields so pydantic-settings reads them from.envat startup.
backend/llm.py:
- Added
os.environ.setdefaultcalls forOPENAI_API_KEYandMISTRAL_API_KEY. - Bridges
GOOGLE_API_KEY→GEMINI_API_KEYat startup (uses whichever is set in.env). - Extended
_FALLBACKSto cover all 40 models across 5 providers:- Gemini paid (
gemini-2.5-pro,gemini-1.5-pro) → Gemini free flash variants → Groq - Gemini free tier (
gemini-2.5-flash,gemini-2.0-flash, etc.) → Groq fallback - OpenAI GPT-4o, o-series → cheaper OpenAI variants → Groq
- Mistral Large/Medium/Small → smaller Mistral → Groq
- All Anthropic models (Opus 4.7, Sonnet 4.6, Haiku 4.5, 3.5/3 series) → Groq
- All Groq Llama 4/3.x/specialised models → smaller Groq variants → Anthropic Haiku
- Gemini paid (
- Added
resource_exhaustedto_is_hard_rate_limit()for Gemini quota errors.
backend/api/keys.py:
- When user saves the
googleprovider key, also writesGEMINI_API_KEYto.envandos.environimmediately.
- Gemini 2.5 Pro → falls back to Gemini 2.5 Flash → Gemini 2.0 Flash → Anthropic Haiku automatically
- All 5 providers fully initialised at startup from
.env - Key saved via UI takes effect without restart (both
GOOGLE_API_KEYandGEMINI_API_KEYset)
Ran acompletion against every model in model_config.json:
| Model | Result |
|---|---|
gemini-2.5-pro |
QUOTA_EXCEEDED (free tier = 0) |
gemini-2.5-flash |
OK |
gemini-2.0-flash |
QUOTA_EXCEEDED (daily limit) |
gemini-2.0-flash-lite |
QUOTA_EXCEEDED |
gemini-1.5-flash |
MODEL_NOT_FOUND — deprecated by Google |
gemini-1.5-flash-8b |
MODEL_NOT_FOUND — deprecated |
gemini-1.5-pro |
MODEL_NOT_FOUND — deprecated |
| Groq models | INVALID_KEY |
anthropic/claude-haiku-4-5-20251001 |
OK |
Three distinct failure modes: quota, deprecated model IDs, invalid Groq key.
backend/llm.py:
- Added
"not_found","model not found","404" + "model"to_is_hard_rate_limit()— MODEL_NOT_FOUND now triggers the fallback chain instead of crashing immediately - Rewrote Gemini fallback chains: removed all
gemini-1.5-*references (deprecated), madeanthropic/claude-haiku-4-5-20251001the reliable last resort (Anthropic key is valid, Groq key is not) gemini-2.5-pro→gemini-2.5-flash→gemini-2.0-flash→anthropic/claude-haikugemini-2.5-flash→gemini-2.0-flash→anthropic/claude-haiku→ Groqgemini-2.0-flash-lite→gemini-2.5-flash→gemini-2.0-flash→anthropic/claude-haiku
backend/model_config.py:
- Removed all 3 deprecated Gemini 1.5 models from
AVAILABLE_MODELS
backend/model_config.json:
coder:gemini-1.5-flash-8b→gemini/gemini-2.5-flashintegrator:gemini-1.5-pro→gemini/gemini-2.5-flash
acompletion("gemini/gemini-2.5-pro", ...) → hits quota → falls back to gemini-2.5-flash → SUCCESS (1 choice returned)
Groq API key in backend/.env is invalid (rotated after GitHub exposure, not updated). Update it via the Models → API Keys section in the UI, or directly in .env.
Hackathon mentor feedback: "research + summarize is too basic." Pivoted to the primary use case: Autonomous GitHub Issue/PR Solver. Zero human interaction: webhook fires → multi-agent pipeline → real PR created, comment posted.
backend/tools/github_ops.py— 5 new GitHub tools:github_read_file,github_list_dir,github_get_issue,github_post_comment,github_search_codebackend/api/github_webhook.py—POST /api/webhooks/github— receives GitHub webhook events (issues.opened, pull_request.opened, ping) and auto-creates goals. HMAC-SHA256 verification supported viaGITHUB_WEBHOOK_SECRETenv var (optional for local dev).frontend/src/pages/Webhooks.tsx— "GitHub Automation" page at/app/webhooks; webhook URL with copy, setup guide (ngrok steps), "Simulate GitHub Issue" form
backend/tools/__init__.py: Registered all 5 new github_ops tools in TOOL_REGISTRY
backend/agent_registry.py:
- researcher: added github_read_file, github_list_dir, github_get_issue, github_search_code; output_schema gains
code_contextfield; max_iterations 10→15 - coder: added github_read_file for reading existing code before writing fixes
- integrator: added github_post_comment, github_read_file; system_prompt updated with GitHub automation guidance
backend/orchestrator.py:
- Agent descriptions updated to mention GitHub tools
- Rules 8+9 expanded with GitHub automation patterns
- Rule 10 added: explicit "researcher→coder→integrator" 3-task DAG for GitHub issue fixing
_is_github_automation_plan()added: returns True when plan has both coder+integrator agents_validate_plan()allows integrator as terminal task when it's a GitHub automation plan (creates PR + posts comment = the final action)
backend/llm.py:
- Claude 4 models (claude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5-20251001) excluded from
temperatureparam (they returninvalid_request_error: temperature is deprecated) not_found_error/notfounderror/model not foundin exception string → mark unhealthy 1h + try next fallback (wasraisebefore, crashing the task)
backend/main.py: Registered github_webhook.router BEFORE webhooks.router (specific route before generic /{token} catch-all to avoid route collision)
frontend/src/App.tsx: Added /app/webhooks route
frontend/src/components/AppNav.tsx: Added "Automate" nav link (Zap icon)
POST /api/webhooks/github (X-Github-Event: issues)
→ goal created: "Fix GitHub issue #N in owner/repo"
→ orchestrator plans: researcher → coder → integrator
→ researcher: reads repo structure + relevant files via GitHub API
→ coder: writes fix, runs tests
→ integrator: creates PR with fix, posts comment on original issue
Local testing: ngrok http 8000 → paste URL into GitHub repo webhook settings. Or use "Simulate" form at /app/webhooks.
- Webhook endpoint creates goal correctly (200 OK, goal_id returned)
- 3-agent DAG planned and executing: researcher DONE → coder RUNNING → integrator PENDING
- Frontend builds clean (0 TypeScript errors)
Full visual identity pass for the Mergit showcase prototype (agent economy on a simulated Monad chain).
- Replaced all "omniBox" display strings across
frontend/srcandfrontend/index.html(title, nav wordmark, landing copy, footer, webhooks page, login page) with "Mergit" - New palette: deep indigo/violet base (
bg: #07060f), electric indigo/violet accents (accent: #6d4aff,purple: #a855f7), electric cyan (cyan: #22d3ee), newproof-green(#2eff9e) token for on-chain proof/reputation accents —frontend/tailwind.config.js,frontend/src/index.css - Added JetBrains Mono (
@fontsource/jetbrains-mono) for hashes/scores/blocks per the on-chain identity brief - Redesigned the logo mark from a generic 4-square grid to a literal "merge" glyph — two nodes converging into one proof node — in
AppNav.tsx,Navbar.tsx,LandingFooter.tsx - Rewrote hero narrative to pitch the agent economy; added a "Watch proofs mint live" teaser linking to
/app/economy(ships in #2) - Rebranded
README.md,CLAUDE.md, this file's title, andpitch/DEMO_VIDEO_SCRIPT.md
Deliberately left unchanged (real infra identifiers, not brand text):
frontend/src/lib/firebase.ts—omnibox-8f73eFirebase project ID/authDomain (renaming breaks the actual auth backend)omnibox/omnibox-dataservice and disk names inrender.yaml,compose.yaml, and the/data/omnibox.dbpath (out of scope for a frontend/docs rebrand; renaming would touch live deploy config)
npx tsc -b: same 2 pre-existing unrelated errors asmain(confirmed viagit stashdiff), no new errorsnpx vite build: succeedsgrep -ri omnibox frontend/src frontend/index.html: clean except the Firebase config noted above- Manual browser check:
/appand/(landing) render the new palette/wordmark correctly