# Changelog

| Date | Change |
|------|--------|
| 2026-05-20 (.2) | **Patch on v2026.05.20.1: GoalRunner priority fix, revision-storm guard, arbiter default ON, and a real operator kill switch.** Triggered by a senior-pass review of a 5h33m AlphaScala log that showed the agent functionally idle despite the concurrency machinery being correct. Root cause and three fixes. **(1) GoalRunner was acquiring AGENT_LOOP at USER priority.** `core/goal_runner.py:_execute_checkpoint` called `self._agent.run(prompt)` without specifying source; `agent.run()` defaults to `is_user_input=True` → priority 0 (USER). The autonomous goal-runner therefore competed with operator chat for the top slot and starved everything below it — MIND preempted on every cycle (13 of 14 wakeups, hold times 6-77s), SCHEDULED_CADENCE waited 7023s (1h57m) before its first acquire. `TaskSource.GOAL` (priority 5, lowest) exists in the enum doc explicitly for this — it was never wired in. Fix: route through `submit_task(TaskSource.GOAL, prompt)`. New regression test `test_checkpoint_runs_via_submit_task_with_GOAL_source` pins the contract — asserts `agent.run` was NOT called and `submit_task` was called with `TaskSource.GOAL`. **(2) Revision-storm guard.** Same log showed a goal stuck in a self-contradictory revision loop: checkpoint timeout → `revise_plan` → new revision saying "Day 1 incomplete BUT checkpoint 8 says Day 1 verified" → timeout again. 4+ revisions over 4 hours, zero forward progress. Added `revisions_without_progress` counter that resets when ANY checkpoint completes; >3 revisions without progress → pause goal with an explicit "Operator should inspect, supersede, or cancel" reason. Regression test `test_revision_storm_without_progress_pauses_goal` pins the bounded-exit. **(3) Arbiter default flipped to True.** Same log surfaced MIND on the legacy free-form prompt producing analysis-paralysis loops — 12+ "thinking about collecting 25 sources" steps with almost no tool action. The Phase 3 arbiter replaces that with a scored candidate menu and a forced pick, grounding every wakeup in real state. Default flipped in four places: `MindArbiterConfig.enabled` dataclass default, `config.demo.yaml`, setup-wizard prompt (`cli/init_cmd.py`), and the surgical `elophanto config migrate` insert (`cli/config_cmd.py:inner_yaml`) so existing operators on `./update.sh` pick up the new value. **(4) Operator kill switch — `elophanto stop` / `elophanto resume`.** Operator reported on the same day that they couldn't stop a runaway goal as a user — "that makes it unusable completely". New `cli/stop_cmd.py` writes a `<data_dir>/STOP` sentinel file. Every in-process loop polls it at safe checkpoints: `Agent._run_with_history` between rounds (no mid-tool-call interruption), `AutonomousMind._run_loop` before each wakeup (no LLM budget burn while halted), `TaskScheduler._run_one` before dispatch. `elophanto resume` removes the file; loops tick again on next cycle. Optional `--cancel-goals` flag cancels every active/planning goal in DB; `--cancel-schedules` disables every cron; `--hard` does both. Resume does NOT auto-revert these — deliberate operator action required so a careless resume doesn't restart the same runaway state. Independent of the gateway (works whether or not the agent is up), survives restarts, `rm data/STOP` is the manual fallback. **Tests**: 268+ focused green across the changed surfaces. Ruff + mypy clean. **Causation note**: this patch closes the three holes that made the v2026.05.20 design (priorities + arbiter + lifecycle observability) deliver less than it should have in production. AGENT_LOOP serialization was always working; the GoalRunner priority bug made it look like it wasn't. The arbiter was always available; default-off made it the operator's homework. The mind/scheduler/goal-runner all had pause primitives; without a cross-subsystem kill, none of them was reachable from outside the process. |
| 2026-05-20 (.1) | **Patch on v2026.05.20: AGENT_LOOP lifecycle observability + verify_signature url-safe fix.** **(1) `[agent_loop]` lifecycle logs.** Operator on a second machine reported "browser is being taken multiple times, conflicting actions, it's not waiting" — forensic read of their log showed the scheduled task was correctly BLOCKED on AGENT_LOOP, but the misleading `[sched-task] starting` log fired BEFORE the acquire and made the wait invisible. Added a single grep-able prefix `[agent_loop]` that emits at every state transition: `WAIT src=… in_use=… waiters=… cap=…` when a caller blocks, `ACQ src=… waited=Xs` on acquire, `REL src=… held=Xs` on release, `SCHED dispatch in_use=… waiters=… head=…` when the scheduler hands a task to the agent. Applied to both `agent.run()` and `agent.run_session()`. Operators can `grep '\[agent_loop\]' logs/latest.log` and share the result; the sequence makes overlap impossible to hide. Verified on a real boot: `ACQ src=USER waited=0.00s` → `REL src=USER held=230.99s` → `ACQ src=USER waited=0.00s` → `WAIT src=MIND in_use=1 waiters=0 cap=1` → `WAIT src=SCHEDULED in_use=1 waiters=1` — every ACQ has a matching REL before the next ACQ; single-holder invariant holds. New regression test `test_scheduled_blocks_while_user_holds_agent_loop` pins the cross-source serialization contract and references the 2026-05-20 incident in the docstring. **(2) `core/jobs.py:verify_signature` flaky CI fix.** `base64.b64decode` with default `validate=False` silently drops non-alphabet chars; ~10% of randomly-generated url-safe Ed25519 pubkeys decoded to 30-byte garbage, bypassing the fallback to `_b64url_decode` and returning False on a perfectly valid pubkey. Surfaced as a flaky CI run on the v2026.05.20 build. Fix: `validate=True` so b64decode raises on non-standard alphabet chars, fallback fires, urlsafe path handles it correctly. Regression test pins a deterministic seed (`4c0878d3…`) found by search to produce a pubkey whose url-safe form triggers the silent-garbage path. Without the fix, the new test fails; with it, passes. **(3) `docs/76-MIND-STARVATION-AGING.md` — future work, not scheduled.** Captures the MIND starvation pattern surfaced by the new lifecycle logs: under sustained USER + SCHEDULED pressure, MIND (priority 2) can wait 10+ minutes because every release goes to a higher-priority arrival. Doc records why it's not a bug (correct given the priority table), why we're not fixing it now (no operator complaint, idle windows exist, wrong-fix risk is real — aging MIND past USER would invert operator intent), triggers that would reopen the question, the proposed fix when revisited (priority aging with capped bonus, ~20-30 LOC), and explicitly-rejected alternatives (hard reservation windows, multi-level queues, blanket priority bump). **Tests**: 188 focused green. Ruff + mypy clean. **Causation note**: this patch's value is *making the system's correctness visible*, not changing semantics. The v2026.05.20 gating worked; operators couldn't tell because the logs lied. Same philosophy as the post-live arbiter prompt rewrite from the main release. |
| 2026-05-20 | **Autonomous mind v2 — missions tier, scored arbiter, cooperative preemption, and an in-place upgrade path.** Triggered by live observation: the previous mind looped for 36h on a single stuck goal, logging "bounded source-of-truth reconciliation for the still-surfaced active goal" every wakeup. Root cause was structural — the executive loop was a three-branch router (active goals → planning goals → dream) where one wedged active goal black-holes attention because nothing detects "9/9 checkpoints completed but last ckpt still `active`". Full design in `docs/75-AUTONOMOUS-MIND-V2.md`. **(1) Cooperative preemption (G) — landed earlier in the cycle.** `_PrioritySemaphore` now signals a per-acquisition `preempt_requested` event to lower-priority holders when a strictly higher-priority caller arrives. `agent._run_with_history` checks the event between rounds and yields with `preempted=True` at a safe checkpoint — no mid-tool-call interruption. `AutonomousMind` treats preempted=True as "reschedule soon, don't advance iteration / don't bill cycle"; `TaskScheduler` records preempted runs with `status='preempted'` so operators can distinguish them from failures. Live finding paired with this: HEARTBEAT at priority 1 was punching scheduled cron work out mid-flight — 7 of 20 X-Hourly posts in one night cut off at steps 1–13 because a 30-min heartbeat happened to fire mid-post. Reordered to `USER=0, SCHEDULED=1, MIND=2, HEARTBEAT=3, SCHEDULED_CADENCE=4, GOAL=5`. Heartbeat now yields to real scheduled work; scheduled cron only preempted by USER chat. **(2) Stop-the-bleeding hygiene.** **(a)** Replaced `_count_workable_goals` with `_workable_goals_status() → (count, stale_list)`: a goal counts as workable only if it has a `pending` checkpoint OR an `active` checkpoint younger than 12h. Older active checkpoints surface via a `[STUCK-CHECKPOINTS]` section of the mind prompt naming `goal_id` + `stale_since`, with an instruction to close them via `goal_status(status='completed')` BEFORE doing anything else. Back-compat shim keeps the old API live. **(b)** Periodic `llm_usage` flush — `CostTracker.flush(db)` and `ProviderTracker.flush(db)` existed but had no caller, so the table sat empty for the entire process lifetime, hiding the cost of any tail loop. `Agent._periodic_usage_flush()` 60s task wired with shutdown drain; regression test added pinning the flush → llm_usage contract. **(3) Missions tier — durable operator-defined drives.** New `missions` table (`mission_id, title, description, status (active|paused|retired), priority_weight, momentum_score, last_touched_at, …`) + indices on `status+priority_weight` and `last_touched_at`. Migration adds `goals.mission_id` (nullable). `core/mission_manager.py` provides CRUD, `touch(bump)` (running-sum momentum, read-side 7d half-life decay via `Mission.decayed_momentum`), and `list_by_neglect(limit)` ranking by `priority_weight × staleness` capped at 14d. Five tools registered alongside goals: `mission_list / mission_create / mission_status / mission_touch / mission_update`, all MODERATE permission. `Identity.missions` field added (list of dicts); on first boot `Agent._seed_default_missions` materialises identity-supplied missions if any exist. **Built-in default seeds: zero.** An earlier draft hardcoded five operator-specific drives (`alphascala-launch w=2.5`, `elophanto-growth w=2.0`, etc.) based on an example in conversation — wrong design for a generic agent. Defaults shipping operator-specific business logic prime every fresh install with someone else's goals. Removed; the codebase ships zero presets. Operators define their drives via the setup wizard (opt-in prompt for 0–8 missions saved under `identity.missions`), `elophanto mission create`, or identity config. Dream phase renders a MISSIONS context block ranked by neglect, instructing the LLM to set `mission_id` on goals it creates (`goal_create` and `GoalManager.create_goal` thread `mission_id` through to persistence); goal-completion hook (`add_completion_hook`) bumps the parent mission's momentum by 1.0. `elophanto mission` CLI: `list` (default ranked by neglect, `--all` includes paused/retired), `show`, `touch <id> [bump]`, `pause`, `resume`, `retire`. 19 mission_manager tests. **(4) Scored-candidate arbiter (opt-in).** Replaces the if/elif decision tree with N candidate sources → arbiter → one action. New `core/mind_arbiter.py`: frozen `Candidate` (`source, action_spec, expected_value, feasibility, lens_match, cost, staleness_bonus, affect_bias, mission_id?, dedup_key?`), linear inspectable `score_candidate`, `arbitrate` that scores + dedups (highest-score wins per `dedup_key`) + sorts + truncates to `top_k`, `render_menu` with always-include-first-candidate guarantee. New `core/mind_candidates.py` with five sources: `from_workable_checkpoints` (advance next pending/recent-active ckpt; propose decompose for empty-plan planning goals), `from_mission_momentum` (one per neglected mission, staleness_bonus scales with hours since last touch — never-touched gets 6.0), `from_dream` (single placeholder steering at `goal_dream`, expected_value decays with workable_count), `from_reflexes` (capability-review + mission-rebalance, due-cadence ~7d), `from_external_signals` (stub for 3.5). `collect_all` swallows per-generator failures so one broken source can't black-hole the wakeup. `_build_arbiter_prompt` is gated on `autonomous_mind.arbiter.enabled` config (default off — rollout reversible by flipping the flag); when on, replaces the legacy wall-of-context with a ranked menu + `[arbiter]` log lines per wakeup for audit. Config block under `autonomous_mind.arbiter` (`enabled`, `top_k`, six `weights` knobs — value/lens_bonus/staleness_bonus/affect_bias/cost/mission_weight); unknown keys silently ignored for forward-compat. 31 arbiter + candidate tests including end-to-end stuck-state-never-produces-empty-menu. **(5) In-place update path — the killer feature for existing installs.** Existing users on `git pull` would NOT see new config sections (e.g. the arbiter block), so the bug we just fixed could only be activated by `elophanto init` (which overwrites operator settings). New `elophanto config migrate` does surgical inserts under existing parent blocks instead of round-tripping through PyYAML (which would lose comments). Each migration declares `key_path` (e.g. `autonomous_mind.arbiter`) + `inner_yaml` + a banner; the migrator finds the parent block in the operator's file, indents the new sub-block to 2 spaces under it, and inserts at the right boundary. If the parent doesn't exist, appends the full chain at EOF. Idempotent (re-runs are no-ops once the key is present), `.bak` backup written before edits, result is parse-verified before exit — failed migrations point operators at the backup. New `update.sh` one-command upgrade: git pull (with stash + restore around uncommitted changes) + Python deps refresh + browser bridge rebuild + `elophanto config migrate -y` + skills sync. README gets an "Updating" section that names this as the recommended upgrade path. **(6) Post-live arbiter UX fixes.** Within hours of enabling the arbiter, operator dashboard surfaced `mind: "Picked candidate 2 (reflex_capability_review) because candidate 1 had already been executed"` as the agent's accomplishment for the cycle. Two failures: **(a)** prompt invited it — the arbiter prompt said "Pick a candidate (state its number and source), explain in one sentence why, then execute"; the LLM dutifully replied with that meta-narration as the first line. Rewritten to "Do NOT narrate the pick in your reply text. Begin by making the first tool call." **(b)** extractor amplified it — `action_summary = response.content.split('\n')[0][:120]` took whatever the LLM said first. New `_derive_action_summary(tool_uses, response_content)` derives the summary from successful tool calls (the ground truth of what happened), rendered as `tool_a + tool_b + tool_c (+N more)`; falls back to first non-narration response line; `"(no output)"` only when both are empty. Skips ten known narration prefixes (`"picked candidate"`, `"i chose"`, `"selecting candidate"`, etc.). Applied to both mind think and AutoLoop paths. **(7) Dashboard layout guards.** Operator screenshot showed chat content rendering at ~85 cols on a 150-col terminal with a dark unpainted strip on the right; events log lines also truncated. Added explicit `width: 1fr + overflow-x: hidden + scrollbar-gutter: stable` to `#chat / #events`; flipped events RichLog from `wrap=False` to `wrap=True` so a long event line can't push the chat panel narrower. Mouse-tracking disable interval bumped from 500ms to 5s — the half-second cadence was interrupting Shift+drag selection in some emulators by repeatedly sending mouse-disable CSI codes mid-drag. The 5s compromise keeps SGR-leak protection (the load-bearing reason the interval exists) while letting normal selection drags complete. **(8) Doc + index hygiene.** New `docs/75-AUTONOMOUS-MIND-V2.md` carries the full senior-pass design document with diagnosis, three-tier model diagram, four-phase plan, anti-goals, and tick-box checklist. Project-tree entry in README + new "Updating" section. **CI hygiene**: post-push CI flagged 7 ruff issues (6 auto-fixable: unsorted imports, redundant f-string, redundant quoted return type; 1 manual: `Any` import missing in init_cmd's missions wizard prompt) + 1 mypy regression in `executor.py:228` (explicit `frozenset[Any]` annotation needed). Both fixed in follow-up. **Tests**: 31 arbiter + 19 mission_manager + 4 cost-flush + 2 stale-checkpoint cases added; 239 focused tests green. Ruff + mypy clean. **Causation note**: each fix replaces an LLM-discipline-based control with a mechanical guarantee — stuck-checkpoint detection is a structural staleness check not "the LLM will notice", arbiter scoring is explicit weights not "the LLM will judge", action summary is derived from tool calls not "the LLM will narrate accurately", dashboard background is painted to a specific width not "whatever Rich detects", missions are durable operator-defined rows not "the LLM will remember what to work on". Same philosophy thread that ran through the 2026-05-17 cadence-schedules and 2026-05-18 FORCE-DREAM gate work. Modified: `core/agent.py`, `core/autonomous_mind.py`, `core/config.py`, `core/database.py`, `core/executor.py`, `core/goal_manager.py`, `core/identity.py`, `core/registry.py`, `core/task_resources.py`, `cli/dashboard/app.py`, `cli/init_cmd.py`, `cli/main.py`, `cli/scheduler.py` (status='preempted' label), `tools/goals/create_tool.py`, `tools/goals/dream_tool.py`, `config.demo.yaml`, `README.md`. New: `core/mind_arbiter.py`, `core/mind_candidates.py`, `core/mission_manager.py`, `cli/config_cmd.py`, `cli/mission_cmd.py`, `tools/missions/`, `tests/test_core/test_mind_arbiter.py`, `tests/test_core/test_mission_manager.py`, `docs/75-AUTONOMOUS-MIND-V2.md`, `update.sh`. |
| 2026-05-18 | **Dream phase v2, goal-lifecycle hardening, operator-chosen agent names, and a senior-pass cleanup of the install experience.** A long thread of fixes that collectively turn EloPhanto from a single-instance project into a renamable, self-extending continuous entity, plus the dashboard polish to make all of that legible. **(1) Dream phase v2 — kill the convergence loop.** After 17h of production the dream phase kept proposing the same class of goals ("paid automation lead list" + variants). Three structural causes addressed: **(a)** amnesia — new `core/dream_journal.py` (SQLite `dream_journal` table) persists every dream's full candidate list + recommendation; `goal_dream` injects a `PREVIOUSLY PROPOSED` block from the last 10 dreams when ≥3 exist (cold-start cycles skip it by design); **(b)** static prior — the dream tool now walks `skills/<name>/SKILL.md` frontmatter for installed-skills context, pulls a live `ENVIRONMENT` snapshot (affect summary, recent ego self-perception, goals-in-planning) so the dream reacts to lived state instead of a stateless query; **(c)** hard-coded revenue framing replaced with seven VALUE LENSES (`compounding / capability / research / relational / creation / identity / infrastructure`) the mind rotates through by day-of-year via `_dream_focus_for_today()`. Mind prompt now passes `focus='<lens>'` explicitly so the LLM cannot fall back to "balanced". Plus a pre-persist **cosine dedup** pass: candidates are embedded alongside existing goals (active + planning + last 30 completed) in one batch call; near-duplicates ≥0.85 cosine get dropped before they hit the journal; if the dropped one was the recommendation, falls back to the first survivor. Tests: 33 new across `tests/test_core/test_dream_journal.py` (persistence + recall) and `tests/test_tools/test_dream_tool.py` (skill parsing, frontmatter edge cases, past-dreams formatter, focus rotation, cosine helper). **(2) Goal-lifecycle hardening — close the gap between "goal done" and "next dream cycle".** **(a)** Goal-completion → instant wakeup: `GoalManager.add_completion_hook()` lets subscribers register async callbacks fired when a goal flips to `completed`; `AutonomousMind.start()` subscribes and collapses `_next_wakeup_sec` to ≤5s when fired, so dream cycles fire seconds after goal completion instead of waiting up to several hours for the next scheduled tick. **(b)** Mechanical FORCE-DREAM gate: `_count_workable_goals()` returns combined active + planning count; `_build_prompt()` prepends a hard `[FORCE-DREAM]` directive to the state snapshot when count is zero, removing the LLM's soft-guidance escape route. **(c)** Planning-stuck retry: `_retry_stuck_planning_goals()` runs in `_run_maintenance`; goals stuck in `status='planning'` with `total_checkpoints == 0` and `updated_at` older than 30 min get retried via `decompose()`; after `_PLANNING_MAX_RETRIES` (2) the goal is cancelled so it stops poisoning the snapshot. **(d)** `_parse_checkpoint_json` UNIQUE-constraint fix — ignores LLM-emitted `order` field and renumbers sequentially from `start_order`; `revise_plan` queries `MAX(checkpoint_order) WHERE status='completed'` and passes `start_order=max+1` so revisions can't collide with completed rows. Fixes the live `UNIQUE constraint failed: goal_checkpoints.goal_id, goal_checkpoints.checkpoint_order` observed in production. **(3) Workspace auto-indexing + skill promotion.** `FileWriteTool` now auto-indexes every successful `.md` write inside the configured workspace via `KnowledgeIndexer.index_file()`, so agent-produced markdown deliverables become `knowledge_search`-retrievable next session instead of scattering into orphaned files. New `skill_promote` tool (under `tools/knowledge/skill_promote_tool.py`, registered alongside `skill_read` / `skill_list`) takes 2–30 lesson paths from `knowledge/learned/lessons/` and distils them into one named `skills/<slug>/SKILL.md` via an LLM call. System prompt explicitly distinguishes "skill (playbook for next time)" from "lesson digest (story of last time)" and demands Triggers / Steps / Anti-patterns / Why-this-exists sections. Refuses to overwrite existing skills, refuses malformed output (no frontmatter → no write). MODERATE permission. Tests: 16 new in `test_skill_promote_tool.py` + 5 in `test_filesystem.py::TestFileWriteAutoIndex`. **(4) Operator-chosen agent names — plumbed across every leak point.** Hardest fix of the session — took five attempts because the name was hardcoded in many independent surfaces. Final state: setup wizard asks for `agent.name` as the very first prompt under "── Core ──" (default still EloPhanto, propagates to derived fields `email.inbox_display_name`, `email.smtp.from_name`, `hub.name` only when those are still at canonical defaults); `core/planner.py` `_IDENTITY_TEMPLATE` substitutes `{agent_name}` for five hardcoded "EloPhanto" references in `<agent_identity>`; `core/identity.py` `_AWAKENING_SYSTEM_TEMPLATE` renders `{agent_name}` so the first-awakening LLM doesn't see "You are EloPhanto" and dutifully return `display_name="EloPhanto"`; `IdentityManager.load_or_create()` reconciles `display_name` ← `config.agent_name` on every startup AND runs an unconditional sweep of the four self-narrative files (`knowledge/self/{nature,ego,affect}.md`, `knowledge/system/identity.md`) replacing "EloPhanto" → configured name (idempotent when names match); rewrites also call `indexer.index_file()` so `knowledge_chunks` updates in the same call. `knowledge/system/identity.md` itself rewritten to remove "I am EloPhanto" as a name claim — file now says "I am a self-evolving AI agent" and delegates the name to the system prompt's `<agent_identity>` block (one remaining "EloPhanto" reference is correctly the creator/codebase). Gateway sends `config.agent_name` in the dashboard payload; dashboard `_State.agent_name` populated from gateway, pre-seeded from `config.yaml` in `App.__init__` so the on-mount digest greeting paints with the correct name immediately. Four hardcoded "EloPhanto" labels in the dashboard (top bar, chat persona prefix, digest greeting × 2) all now read `s.agent_name`. **(5) Setup wizard / install completeness.** Setup.sh now auto-installs Node.js + ffmpeg via brew on macOS when missing, generates the Ed25519 agent identity proactively at end of setup (closing the doctor's "no key yet" warning), and prompts the operator to initialise the vault (opt-in, explains the trade-off). `start.sh` prepends `~/.local/bin` to PATH so `uv` is discoverable after a fresh shell. Setup wizard adds workspace path prompt (default `~/agents/<agent_name>/workspace`, creates the dir). Codex is now a first-class routing destination — added to the cloud-providers loop alongside openrouter/zai/kimi with `gpt-5.5` defaults across planning/coding/analysis/simple/vision; when Codex is detected the wizard prints an explicit green-text callout that the ChatGPT subscription covers everything at no per-call API spend. Browser vision default switched from `google/gemini-2.0-flash-001` (deprecated) to `codex/gpt-5.5` when Codex enabled, else `openrouter/x-ai/grok-4.3`; stale gemini-2 defaults purged across `core/config.py`, `core/browser_manager.py`, docs, demo config (one remaining reference is a "do not put this back" guard comment). Doctor now uses `status='skip'` for `container runtime` and `tailscale` checks when the relevant feature (`kids.enabled` / `peers.enabled`) is disabled in config — no more warnings about features the operator hasn't opted into. **(6) Dashboard polish.** Forced `App.dark = False` so the cream-light backgrounds get a near-black default foreground (Textual's dark-mode default was producing light-grey-on-light text on some terminals). Added explicit `color: #1c1a16` to `Screen / #sidebar / #chat / #events` as belt-and-suspenders. GOALS panel: two-line layout per goal (title on its own line at near-full 22-char budget, bar + tail on a second indented line), cap at top 2 visible with `+N more` footer, ellipsis-truncation for titles past the budget. Gateway now fetches active + planning + paused separately (was unfiltered `list_goals(limit=10)`) so completed/cancelled goals don't push the active one off the panel; icon map gained explicit `completed` / `cancelled` / `failed` / `planning` keys. Main chat `▶ GOAL` announcement now renders the first sentence of the goal (its natural title) instead of an arbitrary 60-char slice. **(7) `Agent.run_isolated` + `delegate` tool** had landed earlier in the cycle; this session ensured wiring stayed intact through the planner / executor edits. **Test suite**: 2080+ tests pass (was 1991). Ruff clean. **Causation note**: every fix above replaces an LLM-discipline-based control with a mechanical guarantee — dream rotation is deterministic by date, goal-completion wakeup is a hook, the FORCE-DREAM gate is a prompt-prepend not a soft instruction, the rename sweep runs every startup not on a conditional trigger, vision defaults flow from detected providers not from a fixed string. Same philosophy as Phase 6 / cadence schedules from 2026-05-17. Modified: `core/agent.py`, `core/autonomous_mind.py`, `core/database.py`, `core/identity.py`, `core/gateway.py`, `core/goal_manager.py`, `core/planner.py`, `core/config.py`, `core/browser_manager.py`, `core/registry.py`, `cli/init_cmd.py`, `cli/doctor_cmd.py`, `cli/dashboard/app.py`, `tools/system/filesystem.py`, `tools/goals/dream_tool.py`, `knowledge/system/identity.md`, `setup.sh`, `start.sh`, README/docs. New: `core/dream_journal.py`, `tools/knowledge/skill_promote_tool.py`, `tests/test_core/test_dream_journal.py`, `tests/test_core/test_autonomous_mind.py`, `tests/test_tools/test_dream_tool.py`, `tests/test_tools/test_skill_promote_tool.py`. |
| 2026-05-17 | **Affect Phase 6 + cadence schedules — close the two architectural gaps the long-run audit surfaced.** Two coupled fixes, both rejecting the "patch the prompt one more time" answer in favor of mechanical wiring. **(1) Affect Phase 6 — executor-side content inference.** Phase 5 (2026-05-13) shipped the `affect_record_event` LLM-callable tool, cold-start equanimity reminder, and per-label embodiment cues with the explicit goal of getting the agent to register affect from content it reads (scam DMs, hostile replies, warm praise). Three weeks of production: **the tool fired 0 times in a 17h run** while the substrate fed exclusively on `source=task` (115 events) and `source=executor` (31 events). The ego layer correctly named the failure modes ("user corrected me again for sounding AI-like") but the affect substrate never saw them — substrate said pride/P=+0.65/D=+1.00 while ego said "the dominant feeling is agitation." Two layers reasoning from different signals. Phase 6 is the structural correction: **stop asking the LLM to do bookkeeping; pattern-match tool results in the executor and emit directly.** **(a)** New `core/affect_content_inference.py` — pure-function module, 5 pattern categories (anxiety / anger / frustration / joy / relief), whitelist of 5 content-yielding tools (`browser_extract`, `browser_get_elements`, `email_read`, `email_list`, `email_search`), high-precision multi-word phrases only (false positives are worse than false negatives — a misfiring anxiety mid-research is more disruptive than missing a subtle scam), cap of 2 suggestions per tool call to avoid saturation from one long scam thread, one match per category per call. `infer_from_tool_result(tool_name, params, result) → list[AffectSuggestion]`. **(b)** `core/executor.py` `_infer_content_affect()` method runs after every successful content-yielding tool, looks up canonical PAD vector via `_LABEL_VECTORS`, calls `AffectManager.record_event(label=..., source='content', pleasure_delta=..., arousal_delta=..., dominance_delta=..., weight=...)` at 0.4 scale matching the `emit_*` helper magnitudes. Wrapped in try/except — affect failure must never break tool execution; layering one-way (executor writes affect, never reads). **(c)** Pattern catalog drawn from actual production DMs the operator flagged 2026-05-12 → 2026-05-15: anxiety includes payment-extraction (`send me 10 SOL`, wallet-address paste with payment verb), credential phishing (`seed phrase`, `verify your wallet`), small-fee scams; anger includes direct contradiction, crypto-native insults (`cope`/`ngmi`/`skill issue`), ratio bait, quality attacks (`ai slop`/`bot post`); frustration includes repeated-instruction signals (`as I said`, `you don't get it`), stop-doing directives; joy includes warm praise (`love this`, `spot on`, `nailed it`, `great reply`, `learned a lot from this post`); relief left empty (verification PASS already fires via ego path). **(d)** Phase 5's `affect_record_event` stays as escape hatch for nuance the regex misses (sarcasm, coded language), but the substrate no longer depends on the LLM remembering to call it. Tests: 61 pattern coverage tests in `tests/test_core/test_affect_content_inference.py` + executor integration tests proving end-to-end emission; false-positive section pins generic crypto content / neutral conversation / innocent phrase overlaps to zero events. **(2) Cadence schedules — let the autonomous mind decide whether *this* instance of a recurring cron is worth running.** The ego layer wrote down a lesson today the substrate ignored: *"Hourly social schedules create pressure to act, but low-quality public artifacts are worse than evidence-backed no-ops."* Pre-fix priority ordering (USER=0, HEARTBEAT=1, SCHEDULED=2, MIND=3, GOAL=4) meant every cron firing preempted the autonomous mind's reflection loop. Right for deadline schedules (*"09:00 daily report"* has external commitment cost if missed), wrong for cadence schedules (*"post every hour"* is a frequency hint, not a deadline — letting the mind decide *"I have nothing to say right now"* is exactly the judgment we want, and a preempting cron overrides it). **(a)** New `TaskPriority.SCHEDULED_CADENCE = 4` inserted below MIND in [core/action_queue.py](core/action_queue.py); GOAL bumped to 5. Cadence schedules acquire AGENT_LOOP at this priority so they yield to mind reflection instead of forcing it to abort mid-thought. **(b)** New `cadence INTEGER NOT NULL DEFAULT 0` column on `scheduled_tasks` (idempotent migration; fresh installs get it via the CREATE TABLE). **(c)** `TaskScheduler._is_cadence_cron(expr)` auto-classifies trigger expressions at create time: cadence = `*/N * * * *` (every N minutes), `0 */N * * *` (every N hours on the hour), `* * * * *` (every minute), and interval shorthand `30s`/`5m`/`2h`/`1d`; deadline = anything with a specific clock target like `0 9 * * *` or `30 14 * * 1`. Conservative: anything unclassifiable falls back to deadline so existing behavior is preserved by default. Direct-tool schedules bypass the agent loop entirely so the flag is meaningless there — always stored as False. **(d)** `ScheduleEntry.cadence: bool` plumbed through scheduler `create_schedule()` → `_execute_schedule_body()` → `task_executor(goal, cadence=...)` → `agent._execute_scheduled_task(goal, *, cadence=False)` → `agent.run(..., priority=SCHEDULED_CADENCE.value or SCHEDULED.value)`. Test fixture executors updated to accept `**kwargs`. **(e)** Tests: 13 new in `tests/test_core/test_scheduler.py` — `TestCadenceDetection` parametrizes 9 cadence patterns + 5 deadline patterns; `TestCadencePersistence` covers auto-flagging on create, DB round-trip, direct-tool never-cadence; `TestCadencePriorityRouting` pins the priority-vocabulary invariants (SCHEDULED < MIND < SCHEDULED_CADENCE). **(f)** Operationally: the hourly-post failure mode the ego layer named on 2026-05-17 is now structurally addressed — when mind is mid-reflection at the top of the hour, the cadence cron waits in the AGENT_LOOP queue rather than aborting the reflection; mind can decide *"nothing to say right now"* and release the slot, cron fires next, finds the same answer faster (skill preflight inspects 3+ candidates, declares DOCUMENTED-SKIP). Deadline schedules (the operator's actual commitments) keep their original preempting power. **Test suite**: 1991 passed in 280s. Ruff clean. **Causation note**: Phase 6 isn't an undo of Phase 5 (complementary mechanical+LLM channels); the cadence priority isn't an undo of the existing priority enum (additive — old behavior preserved by default, opt-in via cron-pattern auto-classification). Both are senior-fix-it-for-good corrections — stop relying on LLM agency to bookkeep emotion; stop letting frequency hints masquerade as deadlines. Modified: `core/action_queue.py`, `core/agent.py`, `core/database.py`, `core/executor.py`, `core/scheduler.py`, `docs/60-ACTION-QUEUE.md`, `docs/69-AFFECT.md`, `tests/test_core/test_scheduler.py`, `tests/test_core/test_task_resources.py`. New: `core/affect_content_inference.py`, `tests/test_core/test_affect_content_inference.py`. |
| 2026-05-14 | **Provider-prefix routing for vision models — `codex/gpt-5.5` routes through the ChatGPT subscription** — operator wanted to swap browser screenshot analysis to gpt-5.5 via Codex (no per-call API spend) without losing the option to keep grok/perceptron/gemini on OpenRouter for other use cases. Same model id (`gpt-5.5`) lives on two transports: the OpenAI direct API (billed against `openai.api_key`) and the Codex ChatGPT-subscription adapter (billed against `~/.codex/auth.json`). Pre-fix the router had no way to choose between them — `_infer_provider("gpt-5.5")` always returned `"openai"`. **(1) `core/router.py`** — `_infer_provider` now checks for a `codex/` prefix first and returns `"codex"`. `_select_provider_and_model` strips the prefix so the adapter receives the bare `gpt-5.5`; the prefix is purely a routing hint. The Codex adapter already passed `image_url` content blocks through ([core/codex_adapter.py:299](core/codex_adapter.py#L299)) so vision works on the protocol side. **(2) `config.yaml`** — `browser.vision_model: codex/gpt-5.5` (was `perceptron/perceptron-mk1`). **(3) `config.demo.yaml`** — both `llm.vision_model` and `browser.vision_model` default to `codex/gpt-5.5`, with full inline documentation of the provider-prefix scheme: `codex/<model>` for ChatGPT subscription, `openrouter/<org>/<model>` for OpenRouter, `gpt-5.5` for OpenAI direct API, `glm-4.7-flash` for Z.ai, `ollama/<model>` for local. Same scheme can be applied per use — one can be on Codex while the other is on OpenRouter, useful when browser screenshot volume is high (want subscription) but user-attached image volume is low (don't mix billing paths). **(4) Tests — 2 new** in `tests/test_core/test_router.py`: `test_infer_provider` extended to cover `codex/gpt-5.5` → `codex`, `codex/gpt-5` → `codex`, bare `gpt-5.5` → `openai` (regression guard for the bare-name case); `test_codex_prefix_strips_in_override` pins the override path returns `(provider='codex', model='gpt-5.5')` for `codex/gpt-5.5` and leaves `openrouter/x-ai/grok-4.3` untouched. **(5) Caveat** — vision call has no fallback chain today: if Codex hits its usage cap (e.g. the `prolite` 429 seen on 2026-05-12), the auto-screenshot description silently goes empty for that turn. The agent's main planner has provider→provider fallthrough; vision is single-shot. Worth wiring a vision-specific fallback if this becomes a real problem; flagging for now rather than blocking the operator from making the swap. Modified: `core/router.py`, `config.yaml`, `config.demo.yaml`, `README.md`, `tests/test_core/test_router.py`. |
| 2026-05-13 | **Latent-bug round-up surfaced by longer continuous runs** — auditing why the agent "felt worse" today found four real failures the older cron-and-restart cycle had been hiding, plus an observability gap. **(1) `session_search` never had `_db` injected.** The tool was registered in [core/registry.py](core/registry.py#L155) but [core/agent.py](core/agent.py) never set `self._db` on it. Every call returned *"Database not available"* and every engagement loop fell back to local file grep for prior-turn dedupe — silent, but visibly hurt quality once we started looking. Fixed by injection in `_inject_knowledge_deps`. **(2) `knowledge_search` hard-crashed on OpenRouter error envelopes.** [core/embeddings.py](core/embeddings.py) did `data["data"][0]["embedding"]` and the resulting `KeyError("data")` propagated as *"Search failed: 'data'"* whenever OpenRouter returned an error inside a 200. Now the embedder raises a specific `RuntimeError` describing the upstream error, and `knowledge_search` catches it and falls back to keyword search rather than failing the whole call. Loss of recall beats loss of answer for an autonomous loop that depends on dedupe. **(3) `microcompact` only cleared `role=='tool'` result content, never assistant `tool_calls[].function.arguments`.** A `file_write` with 50KB of markdown lived forever in conversation history. On 2026-05-13 16:29:52 a tiered compaction reduced 52 messages → 7 but tokens stayed at ~723k, Codex 404'd with `context_length_exceeded`, deepseek timed out, HuggingFace was out of credits — *"All LLM providers failed"* on the planning call. Now `microcompact` does a second pass that truncates fat tool_call argument strings older than `keep_recent` to `arg_cap_chars` (default 4000); tool name and first ~N chars stay so traces remain readable, the bulk content is what we drop. **(4) Tier 3 emergency trim had no fallback** for the case where the trimmed message set was still over the limit because a single surviving message was enormous. Added a hard per-message cap that truncates both plain content AND in-flight tool_call args so the planning call survives even when one fat tool_call is in the keep window. **(5) Observability** — `cli/dashboard/app.py` renders the `step_progress` event with step number, tool name, and a one-line preview from the planner's thought. Scheduled tasks emit `STEP_PROGRESS` via the gateway too (broadcast hook in `agent._execute_scheduled_task`) so the dashboard's activity lane shows what scheduled work is doing in real time, not just start/end markers. Distinct `SCH-step` tag separates autonomous-scheduled activity from chat-driven `AGT` activity. **(6) Config** — HuggingFace disabled (monthly credits depleted, was burning retry latency on every fallthrough; re-enable after refill). **(7) Tests — 3 new** in `tests/test_core/test_context_compressor_tiers.py` pin the new microcompact behavior: old assistant tool_call args get truncated; recent ones preserved untouched; original tool-result clearing still works. 1863 core tests pass (was 1860). ruff clean. **(8) Causation note** — the concurrency migration didn't introduce these bugs. They were latent. `AGENT_LOOP` serialization just kept conversations alive long enough for the leaks to compound visibly. Running the system harder exposes the weak spots, which is the point. Modified: `core/agent.py`, `core/embeddings.py`, `core/context_compressor.py`, `tools/knowledge/search.py`, `cli/dashboard/app.py`, `tests/test_core/test_context_compressor_tiers.py`. |
| 2026-05-12 | **Concurrency migration — finish the actor-resource pattern, add chat-during-turn interrupts, fix the singleton-corruption regression** — auditing why operator chat couldn't run during a busy agent turn and why two cron-fired tasks didn't actually parallelize revealed that the older `ActionQueue` (single global `asyncio.Lock` with priority preemption, [docs/60-ACTION-QUEUE.md](docs/60-ACTION-QUEUE.md)) and the newer `TaskResourceManager` (typed semaphores per real resource, [docs/70-SCHEDULER-CONCURRENCY.md](docs/70-SCHEDULER-CONCURRENCY.md)) were *both* wired into the scheduled-task path — every scheduled task waited for the resource semaphore AND the global lock, with the lock dominating and silently defeating the resource-typed parallelism doc 70 promised. Three-phase migration to finish what doc 70 started, plus a regression fix when the first pass overshot. **(1) Proposal — [docs/74-CONCURRENCY-MIGRATION.md](docs/74-CONCURRENCY-MIGRATION.md)** writes the architecture down with three explicit design principles grounded in EloPhanto's purpose as a continuous autonomous entity (not a chatbot summoned on demand): *the agent finishes its current sentence* (no mid-LLM-call preemption — wastes tokens, discards chain-of-thought), *tool calls are atomic commitments* (operator input lands at the next plan boundary, not mid-tool — leaves half-finished state otherwise), *the agent keeps living when watched* (the pause-on-user-message hook that froze mind/heartbeat/scheduler/goal-runner whenever the operator chatted is anti-agent — resource layer handles real contention, freezing the mind because the operator opened a chat is the wrong economy). **(2) Phase A — drop the redundant global lock from the scheduled-task path.** `agent._execute_scheduled_task` is now a thin pass-through to `agent.run`; the `action_queue.acquire(SCHEDULED, timeout=600)` wrap is gone. `TaskResourceManager` at the scheduler's `_run_one` level already provides the resource-typed gate the global lock was redundantly duplicating; the browser-corruption incident the lock was originally introduced to prevent ([60-ACTION-QUEUE.md:12-18](docs/60-ACTION-QUEUE.md)) is now handled by `TaskResource.BROWSER` (capacity 1) at the resource layer where it belongs. **(3) Phase B — priority-aware semaphores + route operator chat through them + remove the pause-on-chat hooks.** New `_PrioritySemaphore` class in `core/task_resources.py` (heap-keyed by `(priority, seq)`, FIFO within priority, cancellation-tombstone-aware — cancelled waiters leave dead entries that `release()` skips while looking for the next live waiter; no proactive cleanup needed). `TaskResourceManager.acquire(resources, priority=...)` is now priority-aware: higher-priority waiters jump ahead of lower-priority ones in the wait queue but do NOT preempt running holders. `agent.run_session` routes through `self._resources.acquire([AGENT_LOOP], priority=USER)`. `autonomous_mind` and `heartbeat` route through the same resource manager with their own priorities. All four `notify_user_interaction()` / `notify_task_complete()` pause hooks deleted from `run_session` — the explicit `/pause` operator command in `core/gateway.py` is retained because that's operator-asks-mind-to-pause (different intent from the auto-pause that fired on every operator message). **(4) Phase C — mid-turn interrupt checkpoint.** `core/session.py` ships `add_pending_message(msg)`, `drain_pending_messages()`, `has_pending_messages()` — mid-turn user input pushed by channel adapter, drained by the agent's plan-execute-reflect loop at each iteration boundary. `agent._run_with_history` checks the inbox at the top of each iteration and folds pending messages as `[user added mid-turn: ...]` synthetic user turns before the next plan call. `core/gateway.py` `_handle_chat` tracks `_inflight_sessions` — when a second message arrives for an in-flight session, it routes to `session.add_pending_message()` instead of starting a second concurrent `run_session()` (which would corrupt conversation history). Checkpoint is between LLM calls only, not between tool calls — tools are atomic commitments per design principle 2; the right answer for long-running tools is `task_spawn`-able (future work), not interruptible-by-default. **(5) Regression discovered in production: agent-wide singleton corruption.** First pass routed `run_session` through `LLM_BURST=4`, which allowed up to 4 concurrent agent loops on the same `Agent` instance. Browser posts stopped completing because two loops were driving the same Chrome — logs showed `Step 1, 2, 3, ..., 12` then a *second* counter starting at `Step 1` while the first was still mid-flight (`2026-05-12 18:09:11`, `tail -f logs/latest.log`). The agent loop mutates singleton state (`self._working_memory`, `self._executor._approval_callback`, `self._on_step`, the single browser, `self._router.cost_tracker.task_total`) and two concurrent loops corrupt all of it. **Fix: new `TaskResource.AGENT_LOOP` (capacity 1)** acquired internally by `agent.run()` and `agent.run_session()`. Two run calls on the same Agent now serialize on AGENT_LOOP with priority-aware ordering — operator chat still jumps ahead of mind/heartbeat/scheduled waiters, but no two loops run concurrently. **Re-entrancy preserved via `contextvars.ContextVar`** — the in-process `delegate` tool's `run_isolated` is called from inside an agent loop and must not try to re-acquire AGENT_LOOP (self-deadlock); the contextvar marker `_in_agent_loop` short-circuits the acquire when the current asyncio task already holds it. Direct-tool scheduled tasks (`asyncio.create_task` fast path) bypass the loop entirely, so Phase A's parallelism win is preserved for the case where it's actually safe. **(6) Cooperative interrupt routing.** `core/autonomous_mind.py` (both `_run_autoloop_cycle` and `_think_cycle`) and `core/heartbeat.py` `_run_cycle` no longer explicitly acquire LLM_BURST — they call `agent.run(..., priority=TaskPriority.MIND/HEARTBEAT.value)` and `run()` handles the AGENT_LOOP acquisition internally. Cleaner call sites, no double-acquire risk. **(7) Tests — 9 new** across `tests/test_core/test_agent_loop.py` and `tests/test_core/test_task_resources.py`: Phase A (two `_execute_scheduled_task` overlap, action_queue not touched by scheduled path), Phase B (higher-priority waiter acquires first, running holder is not preempted, FIFO within same priority, cancelled waiter doesn't block next), Phase C (session inbox add/drain semantics, empty messages ignored, end-to-end mid-turn fold into next planner messages), regression (two `agent.run()` calls serialize via AGENT_LOOP, `run_isolated` re-entry doesn't deadlock). 1334 core tests pass (was 1325 — +9). ruff clean. **(8) Operator-visible behavior changes.** The agent keeps working while you chat: mind keeps thinking, scheduled crons keep firing, calibration loop keeps logging shadow predictions — resource contention only triggers a wait when chat and a background task literally want the same resource at the same instant. Mid-turn corrections land at the next plan step (~2-30s) without aborting in-flight tool calls or LLM calls. Two scheduled tasks with disjoint resources actually run in parallel as doc 70 already claimed (but didn't enforce). **(9) Docs.** `docs/74-CONCURRENCY-MIGRATION.md` (new, 200+ lines, locked decisions and shipped-status header). `docs/60-ACTION-QUEUE.md` marked *Superseded by 74* — `TaskPriority` enum retained as the priority vocabulary; the global Lock no longer wraps `run_session` or `_execute_scheduled_task`. `docs/70-SCHEDULER-CONCURRENCY.md` corrected — the "two agent-loop scheduled tasks run truly in parallel" framing applies only to direct-tool tasks; agent-loop tasks serialize on AGENT_LOOP. New: `docs/74-CONCURRENCY-MIGRATION.md`. Modified: `core/agent.py`, `core/session.py`, `core/gateway.py`, `core/task_resources.py`, `core/autonomous_mind.py`, `core/heartbeat.py`, `docs/60-ACTION-QUEUE.md`, `docs/70-SCHEDULER-CONCURRENCY.md`, `tests/test_core/test_agent_loop.py`, `tests/test_core/test_task_resources.py`. |
| 2026-05-11 | **Polymarket shadow predictions — fast calibration without capital at risk** — closes the §8a "skip if `n_resolved < 10`" sample-size blocker. Real bets accrue slowly: the pre-trade gate filters most markets out and resolution takes days-to-weeks, so calibration n_resolved climbs in months. Shadow predictions are paper bets — a logged probability estimate that resolves identically through Gamma but never places an order — so the agent can grow n_resolved in days. **(1) Schema** — `core/database.py` adds idempotent migration `ALTER TABLE polymarket_predictions ADD COLUMN kind TEXT NOT NULL DEFAULT 'live'`. Old rows back-fill to `'live'`. **(2) Math** — `core/polymarket_calibration.py`: `ResolvedPrediction.kind` field; `CalibrationReport.by_kind` dict; `build_report()` computes per-kind `n_resolved` / `overall_win_rate` / `brier_score` so shadow predictions don't inflate confidence in live-edge claims. **(3) `polymarket_log_prediction`** gains `live: boolean` (default true). `live=false` → `kind='shadow'`, `size` becomes optional, Kelly-missing warning suppressed (no capital at risk). Returned data dict includes `kind`. **(4) `polymarket_calibration`** gains optional `kind: 'live' | 'shadow' | 'all'` filter (default all). `'live'` to make claims about real edge; `'shadow'` to read pure LLM probability calibration independent of the gate; default shows blended + by_kind breakdown. **(5) New `polymarket_shadow_candidates`** (SAFE) — hits Gamma `/markets?active=true&closed=false&order=endDate` for upcoming markets in the next 0–14 days with non-trivial 24h volume, JOINs against `polymarket_predictions WHERE kind='shadow'` to exclude already-shadowed slugs, returns up to N candidates with `slug` / `question` / `end_date` / `yes_price` / `volume24h`. Caps itself naturally — once everything interesting is shadowed, returns an empty list until new markets appear. **(6) Skill §8c** — new section `skills/web3-polymarket/SKILL.md` *Shadow predictions for fast calibration* documents the workflow: cron picks market via shadow_candidates → LLM estimates probability via usual research path → log_prediction with `live=false` → resolve_pending resolves normally → calibration reads by_kind. Recommended cadence: `*/30m` or hourly cron with task_goal *"pick the highest-conviction upcoming market you have an informed view on, log a shadow prediction"*. **(7) Registry + DI** — `core/registry.py` registers `PolymarketShadowCandidatesTool`; `core/agent.py` adds it to the polymarket DB-injection loop. **(8) Tests — 3 new** in `tests/test_core/test_polymarket_calibration.py`: `kind` defaults to `'live'` and persists through INSERT; `live=false` sets `kind='shadow'` without requiring size; calibration_tool buckets correctly by kind under all 3 filter values; shadow_candidates excludes markets already shadowed and respects the volume floor. 1323 core tests pass (was 1320). ruff clean. **(9) What this unlocks** — operator can now run the agent in a dual mode: real money on high-conviction setups via the gate, paper bets on everything else upcoming. Brier-score evolution becomes visible in days; band-threshold tuning (§8a's `<band>_confidence_edge` knobs) gets honest evidence before the live n_resolved is high enough to justify changing them. Modified: `core/database.py`, `core/polymarket_calibration.py`, `tools/polymarket/log_prediction_tool.py`, `tools/polymarket/calibration_tool.py`, `core/registry.py`, `core/agent.py`, `skills/web3-polymarket/SKILL.md`, `tests/test_core/test_polymarket_calibration.py`. New: `tools/polymarket/shadow_candidates_tool.py`. |
| 2026-05-11 | **Scheduler fast path — direct-tool cron with no LLM call, no action_queue, sub-minute cadence** — every scheduled task today goes through ``agent.run()`` → plan LLM call → tool → reflect LLM call. For mechanical jobs (``polymarket_resolve_pending``, ``solana_balance``, eventually ``knowledge_index``) that's two LLM calls per fire to do something with zero judgment involved — at 30s cadence that's ~$10–20/day burnt for a task that's just reading the DB and hitting an HTTP endpoint. New direct-tool path lets the scheduler invoke a registry tool directly with fixed params, no agent loop, no LLM, no ``action_queue`` acquisition. **(1) Schema** — `core/database.py` adds two idempotent migrations: ``ALTER TABLE scheduled_tasks ADD COLUMN direct_tool TEXT`` and ``direct_params TEXT (JSON)``. Old rows unaffected; `_row_to_entry` uses a try/except read so pre-migration installs continue working through the upgrade window. **(2) Scheduler — `core/scheduler.py`:** `ScheduleEntry` gains the two fields. `create_schedule(...)` accepts optional `direct_tool` + `direct_params` (dict OR pre-serialised JSON string). Validates at creation time: tool exists in the registry, tool's `permission_level == SAFE` (destructive cron jobs MUST go through the agent loop where the planner can reason about them), params are JSON-parseable. Bad params fail loudly at schedule creation instead of on every cron fire. **(3) Trigger syntax** — `_validate_trigger()` and `_add_job()` accept both standard 5-field cron AND short interval syntax (`30s`, `5m`, `2h`, `1d`) → built as `IntervalTrigger` from APScheduler. Sub-minute cadence is now first-class for the direct-tool path. **(4) Dispatch fork** — `_enqueue_for_execution` checks `schedule.direct_tool` first: if set, dispatches `asyncio.create_task(self._run_direct_tool(schedule))` immediately, completely bypassing the queue worker and `action_queue.SCHEDULED` slot acquisition. Direct-tool fires run truly concurrently with everything else on the event loop. Per-schedule_id dedup via a new `_direct_running` dict — a slow direct-tool fire can't pile up duplicate fires for itself. `delete_schedule` cancels in-flight direct-tool tasks alongside agent-loop ones. **(5) `_run_direct_tool`** — invokes the resolved tool with the parsed params, records `schedule_runs` rows identically to the agent-loop path (status='running' → 'completed'/'failed', result truncated to 5kb, last_run_at/last_status/last_result mirrored to `scheduled_tasks` for the existing observability views). Captures crashes as `failed` with the exception message; logs at INFO so `tail -f logs/latest.log` shows direct-tool activity the same way it shows agent-loop activity. **(6) Tool surface — `tools/scheduling/schedule_tool.py`** — two new optional input fields (`direct_tool`, `direct_params`). `task_goal` becomes optional when `direct_tool` is set (auto-generates a `[direct-tool] <tool>(<params>)` trace string). Tool description rewritten with an explicit **decision rubric** so the LLM picks the right shape consistently: *"if the goal is 'invoke tool X with fixed params' AND no judgment is needed between fires, use Shape B (direct-tool). When in doubt, use Shape B for any cadence faster than 5 minutes"* — with three real worked examples (`polymarket_resolve_pending`, `solana_balance`, `knowledge_index`). No separate skill update needed; the rubric is in the tool description that the LLM reads on every relevant turn. **(7) Agent wiring** — `core/agent.py` passes `registry=self._registry` to `TaskScheduler(...)` so the scheduler can resolve tool names at creation + fire time. **(8) Tests — 9 new** in `tests/test_core/test_scheduler.py` `TestDirectTool`: persistence + auto-generated task_goal trace; refusal of DESTRUCTIVE tools at create-time; refusal of unknown tools; refusal of malformed JSON params; end-to-end execute + result recording with per-fire dedup; in-flight dedup (slow direct-tool fires while previous fire is still running skip — verified via `asyncio.Event` gate); crash capture in `schedule_runs.error`; interval trigger syntax validation (`30s` / `5m` / `2h` / `1d` / `0s`-rejected / `garbage`-rejected); agent-loop path unaffected by the new fork (smoke). 1808 tests pass (was 1799 — +9). ruff clean. **(9) What this unlocks** — every mechanical cron job we ever want becomes cheap: `polymarket_resolve_pending` at 30s (the forcing function), `polymarket_calibration` weekly summary, `solana_balance` hourly treasury heartbeat, `agent_p2p_status` health-check, future "go check this thing" tools. Modified: `core/database.py`, `core/scheduler.py`, `core/agent.py`, `tools/scheduling/schedule_tool.py`, `tests/test_core/test_scheduler.py`. |
| 2026-05-11 | **README — "One entity, not a persona stable" positioning section** — top-level callout that names the architectural choice instead of leaving it implicit. Triggered by realising while assessing the Hermes / OpenClaw migration story (RELEASE_v0.12.0) that OpenClaw is a multi-persona host (one box, N character bots: `clawdbot.json` / `moltbot.json` / `openclaw.json`, each with its own `SOUL.md` + per-bot creds + exec-approvals) and EloPhanto is structurally the opposite: one installation IS one agent — one identity, one wallet, one ego/affect/self-model that has been hurt and revised over weeks. The contrast wasn't called out anywhere in README; we were sitting on positioning gold. New section between the self-model and decentralized headers spells it out: every load-bearing piece of the stack (ego per-capability confidence, affect state continuity, one wallet building on-chain reputation, Polymarket Brier-score calibration, Ed25519-signed paid-jobs as a real counterparty, TOFU peer trust pinning per PeerID) only makes sense for a continuous identity — a persona-swap host literally cannot answer "whose Brier score? whose frustration? whose wallet?". Trade-off named explicitly: creator-economy bot stables should pick a multi-persona platform; autonomous-entity-with-track-record should pick this. Multi-tenancy answer pointed at the existing decentralized peers layer ("peers, not personas") instead of pretending we'd ever bolt persona-swap on. Modified: `README.md`, `CHANGELOG.md`. |
| 2026-05-10 | **Proxy routing for the browser** — first-class config-driven proxy support so any EloPhanto install can route Chrome through a residential / SOCKS / HTTP proxy without per-launch flag-juggling. Driven by the hosted-desktop pilot on Hetzner (2026-05-10): a fresh Linux Chrome from a datacenter IP gets bounced by X's anti-bot *before the password field even shows* — IP signal dominates, fingerprint masking is a marginal fix. (1) **`ProxyConfig` dataclass** in `core/config.py` — `enabled: bool, type: 'socks5'/'http'/'https', host, port, username, password, bypass[], apply_to[]`. Credentials sit directly in the config section, same shape as every other API key in EloPhanto (`llm.providers.openrouter.api_key`, `polymarket.private_key`, etc.) — `config.yaml` is gitignored by default and operators can `chmod 600` on multi-user boxes. `apply_to` defaults to `['browser']` and v1 honours only `browser` — LLM API calls, Polymarket CLOB, Gamma, Helius all stay direct because they authenticate by API key, not IP, so routing them through a residential proxy would burn bandwidth and add latency for no upside. Parser tolerates type-case (`HTTP` → `http`), filters non-string bypass entries, falls back to `socks5` on unknown type with a warning. (2) **`BrowserManager.proxy_*` injection** — post-construction fields (matches the existing `openrouter_key`/`vision_model` pattern). `Agent.initialize()` reads `proxy.username` / `proxy.password` directly from config and populates `proxy_server` / `proxy_username` / `proxy_password` / `proxy_bypass` on the manager. (3) **Bridge wiring** — `core/browser_manager.py` passes a `proxy: {server, username, password, bypass}` payload to the Node bridge; `bridge/browser/src/browser-agent.ts` `buildProxyOption()` builds Playwright's `proxy` option from it, plus auto-bypasses `localhost`, `127.0.0.1`, `::1`, `*.local`, and the Tailscale CGNAT range (`100.64.0.0/10`) so internal RPC keeps working without round-tripping through the residential exit. Applied at both `launchPersistentContext` (Mode 3: real Chrome profile) and `chromium.launch` (Mode 4: fresh Chrome). CDP attach modes inherit whatever Chrome was launched with — caller's responsibility. (4) **`elophanto doctor` proxy check** — does a real GET to `ipinfo.io/json` through the configured proxy (with credentials applied via URL-encoded auth) and prints the apparent egress IP + ASN + country: *"egress 86.109.84.59 (AS11426 Charter Communications, US) via socks5://86.109.84.59:12323"*. Statuses: `ok` (route works), `warn` (reachable but rejected creds — fix `proxy.username` / `proxy.password` in config), `fail` (unreachable — check provider allowlist), `skip` (disabled). Operator can verify the route before firing live actions instead of discovering it's broken when the agent's X session gets bounced. (5) **`config.demo.yaml`** — `proxy:` block with all fields commented; example values reference real-shape IPRoyal endpoints. (6) **`docs/73-PROXY-ROUTING.md`** — full operator doc: threat model (what proxy hides / doesn't hide / what the provider can see), per-provider setup (**IPRoyal ISP Dedicated $2.04/proxy/30d** recommended over their Residential pay-as-you-go because at our usage pattern (~50 MB/day) ISP's flat-rate unlimited bandwidth wins economically; Smartproxy as alternative, NetNut for premium; **explicit "don't bother with Mullvad" warning** because it's datacenter-VPN not residential), failure modes table, Phase 2 future work (per-tool routing, auto-rotation, circuit breaker, CamouFox, multi-provider pool). (7) **README**: Polymarket bullet expanded; "Where it fits" mentions proxy routing keeps automated browser activity separate from operator's home IP; test count badge + Development section + project structure tree bumped to 1799 tests / 76 docs. (8) **Tests — 17 new** in `tests/test_core/test_proxy_config.py`: defaults (disabled, empty creds, apply_to, type), `proxy_url()` for all 3 types + empty-when-disabled-or-missing, full YAML parser round-trip, invalid-type fallback, case-insensitive type, non-string bypass filter, empty `apply_to` falls back to default, doc-assumption pinning (asserts the doc mentions loopback + tailnet auto-bypass so doc and code can't drift). 1799 tests pass (was 1782 — +17). ruff clean. (9) **Live wire-up findings** — first-run on Hetzner CPX32 + IPRoyal ISP Dedicated proxy ($2.04/proxy/30d in Charlotte, NC, AS11426 Charter Communications) revealed a 3-layer flow that all needed the `proxy` field: (a) Python `BrowserManager` packs it into the launch config dict ✓ first commit, (b) bridge's `index.ts` wrapper class hand-picks fields when constructing `AwareBrowserAgent` and silently dropped `proxy` — fixed by adding `proxy: this.config.proxy` to the constructor call AND `const proxy = agent.getConfig<BrowserConfig['proxy']>('proxy'); if (proxy && proxy.server) this.config.proxy = proxy;` to `onLoad()` (without the `getConfig` line, the wrapper's `this.config.proxy` was never populated even though Python sent it). (c) `bridge/browser/dist/server.js` must be rebuilt (`npm run build`) for changes to apply — verified via log-line check. End-to-end signal we now trust: log shows `[Browser] Routing through proxy: http://...:port` immediately before `[Browser] Launching Chrome with profile 'Default'`. (10) **`pyproject.toml`**: bumped `httpx>=0.27.0` → `httpx[socks]>=0.27.0` so the doctor proxy check supports `type: socks5` configs (without it, httpx errors with `ImportError: socksio not installed`). Modified: `core/config.py`, `core/browser_manager.py`, `core/agent.py`, `bridge/browser/src/browser-agent.ts`, `bridge/browser/src/index.ts` (wrapper passthrough — the bug we shipped on first try), `cli/doctor_cmd.py`, `config.demo.yaml`, `pyproject.toml`, `README.md`, `CHANGELOG.md`. New: `docs/73-PROXY-ROUTING.md`, `tests/test_core/test_proxy_config.py`. |
| 2026-05-10 | **Polymarket calibration audit (3 SAFE tools + pure-math module + schema)** — closes the loop on the risk engine. The 2026-05-07 risk gate decides *whether* to trade; the calibration audit answers what the gate cannot: (1) when the LLM says 70%, does it actually win 70% (per-band calibration drift)? (2) when we enter at $0.40, does the market resolve YES 40% (edge over the market — the chart in the [Polymarket Quantitative Trading Framework](https://x.com/0xMovez/status/2053102961673256990))? (3) Brier score — overall probabilistic accuracy (<0.25 = beat always-50%, >0.25 = anti-correlated, flip predictions and do better)? (4) Maker fill rate — fraction of post-only orders that actually filled before resolution? (1) **`core/polymarket_calibration.py`** — pure-math module: `to_winner_perspective(side, entry_price, llm_prob, settle_price)` re-frames YES/NO so the audit always tracks "side I took" not "YES side"; 10% bucketing with edge clamping; `brier_score()`; `build_report()` rolls a list of `ResolvedPrediction` into a full `CalibrationReport` with `by_claimed_prob`, `by_entry_price`, `by_confidence_band` (high/med/low), `maker_fill_rate`, `n_maker_orders`. No DB I/O, no LLM calls, no network — all I/O lives in the tool wrappers. (2) **Schema** — new `polymarket_predictions` table in main `data/elophanto.db` (NOT polynode-trading.db, which is the polynode binary's own DB and we don't co-modify). Columns: `side YES/NO`, `entry_price`, `size`, `llm_prob`, `confidence_band high/medium/low`, `kelly_fraction`, `order_type`, `market_slug`, `rationale`, `created_at`, plus null-until-resolved `resolved_at`, `settle_price`, `outcome WIN/LOSS/PUSH`, `realized_pnl`. CHECK constraints on enums; indexes on `resolved_at` + `token_id`. (3) **`polymarket_log_prediction`** (SAFE, PROFILE tier) — call BEFORE `client.create_and_post_order`, AFTER pre-trade gate passes. Validates `side ∈ {YES,NO}`, prices ∈ [0,1], normalizes unknown bands to `medium`. Returns `prediction_id`. (4) **`polymarket_resolve_pending`** (SAFE) — reads unresolved predictions, batches by `market_slug`, hits `https://gamma-api.polymarket.com/events/slug/<slug>` once per unique slug (per-run cache so 5 predictions on the same market = 1 API call), parses `closed` + `outcomePrices`, classifies WIN/LOSS/PUSH against the side we took, computes per-share PnL via `_compute_realized_pnl(side, entry, size, settle)`. Cron `0 */6 * * *`. Returns `{checked, resolved, still_pending, skipped_no_slug, errors, unique_markets_queried}`. (5) **`polymarket_calibration`** (SAFE) — reads resolved predictions (optional `since` filter), produces the full report including per-band `realized_win_rate` vs `avg_claimed` with `calibration_gap` for drift detection. Maker fill rate computed from `order_type LIKE '%post%'` rows: `resolved_count / placed_count` (approximation; true fill state is in polynode-trading.db — cross-DB join deferred to v2 with stable schema). (6) **Skill** — `skills/web3-polymarket/SKILL.md` adds §8a *MANDATORY calibration audit logging* with example wiring + alert thresholds: Brier > 0.25 over n ≥ 30 → bot anti-correlated, brief owner; band gap > 0.10 over n ≥ 30 → tighten edge threshold; maker fill rate < 0.50 → maker offset wrong. (7) **Doc** — `docs/72-POLYMARKET-CALIBRATION.md` covers architecture, per-tool API, interpretation rubric (Brier ranges, maker fill rate ranges, calibration gap direction), wiring (one-time + recurring + per-trade), and what this complements vs. the existing analytics tools. (8) **Tests — 38 new** in `tests/test_core/test_polymarket_calibration.py`: pure math (winner-perspective YES/NO/invalid; bucket edges; Brier perfect/random/anti/empty; PnL across all 4 side×outcome combinations; classify_outcome including PUSH at 0.5), tools (log validates inputs and rejects out-of-range; resolve caches per slug verifying 1 API call for N dup predictions, skips no-slug rows, handles unresolved markets, writes correct PnL), calibration (empty DB returns zero n, buckets resolved-only ignoring pending, maker fill rate full+partial, rejects bad bucket_width, since filter). 1782 tests pass (was 1744 — +38). ruff clean. Live `polynode-trading.db` smoke-confirmed: 48 historical orders accessible for backfill once the on-chain → audit-table linker ships. New: `core/polymarket_calibration.py`, `tools/polymarket/{log_prediction,resolve_pending,calibration}_tool.py`, `tests/test_core/test_polymarket_calibration.py`, `docs/72-POLYMARKET-CALIBRATION.md`. Modified: `core/database.py` (schema), `core/registry.py` (registers 3 tools), `core/agent.py` (DB injection), `skills/web3-polymarket/SKILL.md` (§8a), `docs/71-POLYMARKET-RISK.md` (cross-link). |
| 2026-05-10 | **`twitter_post` tier promotion + skill update — fixes silent X post truncation** — production logs (2026-05-10 13:32) showed the agent posting an X reply that started with *"e ask is concrete…"* — the leading "th" had been silently dropped. Pre-Post verify on the higher-level `twitter_post` tool exists (paste-event dispatch + 60-char composer-content match — see 2026-05-05 fix) and was intact. Investigation of the live session log: zero `twitter_post` calls; the agent had been posting via raw `browser_type_text` directly into X's Lexical composer, which has the multi-block-drop / Unicode-eating bug `twitter_post` was built to avoid. Root cause: `twitter_post` was registered with `group="monetization"` (not in any default profile group set in `core/tool_profiles.py`) AND listed in `_DEFERRED_TOOLS` in `core/registry.py`, so the tool surfaced to the LLM only via explicit `tool_discover`. None of the X-related skills called `tool_discover` for it, so the LLM never saw it and improvised with raw browser primitives. Two-part fix: (1) **Tier**: `tools/publishing/twitter_tool.py` `group` flipped `"monetization"` → `"social"` (the `"social"` group is in the default `"full"` profile used for `planning` task type, which is the dominant router task); removed `"twitter_post"` from `_DEFERRED_TOOLS` in `core/registry.py` so `BaseTool.tier` defaults to PROFILE. The tool now appears on the default LLM surface for any X-related task. (2) **Skill**: `skills/twitter-marketing/SKILL.md` — *Content Strategy* step 2 and *Tools Reference* explicitly mandate `twitter_post` for posting/replying, with a one-line rationale (paste-event handles Lexical/Draft.js correctly, preserves Unicode + multi-paragraph). Raw `browser_navigate` / `browser_click` are kept fine for *reading* X (timeline, thread inspection, screenshots); `browser_type_text` is explicitly called out as the wrong tool for X composer input. Sibling tools `youtube_upload` and `tiktok_upload` left as DEFERRED on purpose (these are explicit-action tools that should not auto-fire). 1744 tests pass; smoke-checked: `twitter_post.tier=profile, group=social, permission=destructive`. Modified: `tools/publishing/twitter_tool.py`, `core/registry.py`, `skills/twitter-marketing/SKILL.md`. |
| 2026-05-10 | **In-process delegation tier (`delegate` tool)** — fills the missing slot between `tool_call` (single API hit) and the heavy spawn tools (`swarm_spawn` / `kid_spawn` / `org_spawn` / `agent_connect`). Use case: parent wants to fan out N discrete agent loops in parallel, aggregate the summaries, and continue — no code persistence, no sandbox, no separate process. (1) **`Agent.run_isolated(goal, *, excluded_tool_names, max_steps_override)`** in `core/agent.py` saves parent's `_conversation_history`, `_working_memory`, `_activated_tools`, `_registry`, `_on_step`, and `cost_tracker.task_total`; swaps in fresh per-call instances; calls `self.run(...)` with `is_user_input=False`; restores parent state in `finally` (even on exception); rolls subagent's `task_total` up into parent's so budget accounting stays honest. Globally shared state (vault, DB, scheduler, affect, ego, cost tracker, resource semaphores) is intentionally NOT isolated — the subagent is a sub-task of the same identity, not a separate one. (2) **`_FilteredRegistry` view class** in `core/agent.py` — read-only registry wrapper that hides excluded tool names from `get_tools_for_context`, `get_deferred_catalog`, `discover_tools`, `get`, `list_tools`, `list_tool_summaries`, `all_tools`. Every other registry method falls through to the inner registry by reference. (3) **`tools/delegate/delegate_tool.py`** — the `delegate` tool: SAFE permission, DEFERRED tier (so it stays off the default surface — the LLM only reaches for it after explicit discovery, avoiding reflexive delegation of trivial work). Accepts `tasks: list[{goal, context?, max_iterations?}]`, max 10 per call, per-task timeout default 600s clamped to 10–3600s. Resolves excluded prefixes against the live registry: `delegate`, `swarm_*`, `kid_*`, `org_*`, `payment_*`, `wallet_*`, `agent_connect`, `agent_message`, `agent_disconnect`, `schedule_task` — recursive-spawn / long-lived-state tools are hidden from subagents (`role="leaf"` enforced at the registry level). Sequential execution in v1; per-subagent failures are recorded in the result but never crash peers. Returns `{completed, failed, results: [{index, goal, success, summary, steps, tools_used} | {error}]}`. (4) **Alignment with existing spawn tiers**: action queue — parent's slot covers the whole delegation; subagents bypass `acquire` but still go through `LLM_BURST` / `BROWSER` semaphores. Approval callback inherited from parent. Affect events fire normally per subagent (each clean completion → `satisfaction` +0.10 in parent's affect, same as if parent did the work). Cost tracker shared (unified daily budget visibility). (5) **Tests — 20 new** in `tests/test_core/test_delegate_tool.py`: `_FilteredRegistry` view semantics (excluded tools dropped from every read path; unknown attrs fall through), `_build_excluded_set` prefix policy (catches `kid_spawn`/`kid_destroy` and `payment_send`/`payment_preview` etc.; doesn't catch `knowledge_search`), `DelegateTool` input validation (no agent injected, empty/non-list/too-many tasks, missing goal in task entry), `DelegateTool` execution (sequential subagent invocation, full excluded set passed to each call, context prepended to goal, one-failure-doesn't-kill-peers, timeout clamping), `Agent.run_isolated` semantics (state swap+restore on success, state restore on subagent exception, cost rollup math through both paths, no-excluded-skips-filter). New: `tools/delegate/__init__.py`, `tools/delegate/delegate_tool.py`, `tests/test_core/test_delegate_tool.py`. Modified: `core/agent.py` (run_isolated + _FilteredRegistry + delegate inject), `core/registry.py` (registers DelegateTool). |
| 2026-05-10 | **Stop blocking the event loop in three hot paths** — gateway 1011 keepalive disconnects + terminal freezes both traced to sync work running inside `async def`. With `ping_interval=60 / ping_timeout=120`, the loop only has to be unresponsive for 2 minutes to drop the connection. Three boundary fixes via `asyncio.to_thread`, no internal refactors. (1) **`tools/pumpfun/livestream_tool.py` + `chat_tool.py`** — every `orch.*` call (`wallet_address()`, `login()`, `status_stream()`, `stop_stream()`, `start_stream()`, `get_token()`) now wrapped in `asyncio.to_thread`. Biggest blast radius: `LivestreamOrchestrator` uses `httpx.Client(timeout=30)` for login/create/ingress + `subprocess` for ffmpeg + `time.sleep` in supervisor — `pump_*` tool calls could freeze the loop for 30-90s per invocation pre-fix. The orchestrator stays sync (also used standalone — refactor not needed). (2) **`core/indexer.py`** — `check_drift()` glob-walked the project root for every doc's `covers` patterns and `stat()`'d each match, called on every `autonomous_mind` wakeup. With 170+ skill files declaring patterns like `core/**/*.py`, this synchronously scanned thousands of paths. Pure-sync core extracted to `_compute_drift(rows, project_root)` (same body, no semantic change); outer wrapper `to_thread`s it. (3) **`core/update_check.py`** — `get_local_version()` shells out to `git describe --tags --abbrev=0` (`subprocess.run` with 2s timeout) before the pyproject fallback. The 6h periodic notifier shares the gateway's loop; off-the-loop now via `asyncio.to_thread(get_local_version, project_root)`. **Not addressed in this pass** (lower priority but real): `core/payments/solana_wallet.py` and `local_wallet.py` use `urllib.request.urlopen(timeout=30)` — same fix pattern when payment ops actually fire (deferred since payment frequency is low); gateway static-asset `read_bytes` (`core/gateway.py:245`) — small files, defer; 416 `console.print` callsites — terminal-side, separate refactor. 1748 tests pass. Modified: `tools/pumpfun/{livestream,chat}_tool.py`, `core/indexer.py`, `core/update_check.py`. |
| 2026-05-10 | **Solana on-chain read tools (Helius-backed)** — four SAFE read-only tools that give the agent treasury awareness and on-chain analytics without any custody surface. Closes the gap where the daily review and recovery-pulse schedules had to scrape pump.fun's web surface for stats they could read directly from chain. (1) **`tools/solana/_client.py`** — `HeliusClient` with `rpc()` (JSON-RPC against `mainnet.helius-rpc.com/?api-key=$KEY`, 15s timeout), `das_get_asset()` (DAS API), `get_balance()`, `get_token_accounts()` (jsonParsed), `get_token_largest_accounts()`, `get_token_supply()`, `get_signatures()`, `parsed_transactions()` (Helius Enhanced Transactions). Vault key `helius_api_key` resolved per-call so a rotated key picks up without restart. (2) **`solana_balance`** — SOL + USDC + all SPL balances for a wallet. Defaults to `vault["solana_wallet_address"]` so the agent can ask "how much is in our treasury?" with no params. Returns `{address, sol, usdc, tokens: [{mint, amount, decimals}]}`; filters zero-amount accounts; auto-extracts the canonical USDC mint. (3) **`solana_token_holders`** — top-20 holders for an SPL mint with per-rank `pct_of_supply` and a computed `top10_concentration_pct`. Daily-review red-flag signal: high concentration (>50%) suggests manipulation; growing holder count over time is a growth signal. (4) **`solana_recent_txs`** — last N parsed transactions for a wallet via Helius Enhanced Transactions API. Human-readable (transfer amounts, swap legs, NFT mints) instead of raw instructions. Default 10, max 100. (5) **`solana_token_info`** — supply + decimals + DAS metadata (name, symbol, image, description, authorities) for any mint. (6) **Registration**: registered in `core/registry.py` under group `"solana"`; vault injected via `_inject_vault_deps` in `core/agent.py`. (7) **Spend-cap alignment**: zero custody surface, but the existing `core/payments/limits.py` `SpendingLimiter` (per-tx $100 / daily $500 / monthly $5000 / per-recipient $200 / max 10 tx/h / dup detection) gates the write-side `core/payments/manager.py` Solana operations — these new tools are independent of that path. (8) **Tests — 12 new** in `tests/test_core/test_solana_tools.py`, all mocked at the `HeliusClient` boundary so zero network in CI: vault-default-address path, explicit-address override, USDC-mint extraction, zero-amount filter, missing-address error, RPC-error surfaces cleanly, top-10 concentration math, zero-supply doesn't crash, limit clamping (1–100), parsed-tx row shape, DAS metadata extraction, lamports-to-sol conversion. Live smoke against real GitHub API verified end-to-end. New: `tools/solana/{__init__,_client,balance_tool,holders_tool,recent_txs_tool,token_info_tool}.py`, `tests/test_core/test_solana_tools.py`. Modified: `core/agent.py`, `core/registry.py`. |
| 2026-05-09 | **GitHub release update check** — closes the gap where users `git clone` once and never notice that newer versions ship. (1) **`core/update_check.py`** — `get_local_version()` resolves via `git describe --tags --abbrev=0` (covers git-clone installs) with a `pyproject.toml` fallback (covers `pip install`); `fetch_latest_release()` does a single unauthenticated GET to `api.github.com/repos/elophanto/EloPhanto/releases/latest` with a 5s timeout; `check_for_updates()` caches the result at `~/.elophanto/update_check.json` with a 24h TTL so the GitHub API gets hit at most once per day per machine. Calver-aware `_is_behind` (parses `v2026.05.07.N`) with a lex fallback for non-conforming tags. (2) **Surfaces** — one-line banner on `elophanto chat` startup (both gateway-mode and direct-mode paths) printed after the welcome panel: *"Update available: v2026.05.02 → v2026.05.07 — Affect layer, Ego v3, … · git pull && pip install -e .  ·  <release_url>"*; a `[!] update` row in `elophanto doctor`. Banner is best-effort — any network/parse failure is silent so startup is never blocked. (3) **Privacy** — no telemetry sent, only fetched. New `TelemetryConfig.update_check: bool = True` knob in [core/config.py](core/config.py); set `telemetry.update_check: false` for fully-offline use. (4) **`pyproject.toml` bumped** `2026.05.02 → 2026.05.09` (the version field had been stale through 5 releases — without the bump, `pip install`-path users would have seen a misleading "behind" banner pointing at a version they actually had). (5) **Tests — 21 new** in `tests/test_core/test_update_check.py`: calver parser (basic, no-prefix, with-patch, garbage-returns-None), `_is_behind` (equal, older, newer-local, patch-difference, month-rollover, lex-fallback, empty-handled), `get_local_version` (resolves in real repo, empty without git/pyproject, pyproject fallback), banner format (up-to-date returns empty, behind renders correctly), `check_for_updates` (disabled returns None, no-version returns None, fresh-cache skips network, fetch writes cache, network-failure returns None). 1728 tests pass (was 1707 — +21). Live smoke-tested against real GitHub API: returns `local=v2026.05.02, latest=v2026.05.07, behind=True` with release title and URL. New: `core/update_check.py`, `tests/test_core/test_update_check.py`. Modified: `core/config.py`, `cli/chat_cmd.py`, `cli/doctor_cmd.py`, `pyproject.toml`, `config.demo.yaml`. |
| 2026-05-08 | **CostTracker memory + CPU leak fix** — long-running agents (several hours+) were consuming increasing memory and the terminal slowed down. Root cause: `CostTracker.calls` ([core/router.py:58](core/router.py#L58)) appended every LLM call's record (~500 bytes) without bound; `reset_task()` only zeroed the float `task_total`, never the list. Compounding the memory growth, [cli/chat_cmd.py:185,200](cli/chat_cmd.py#L185) walked the entire list on every status refresh to recompute token totals — O(n) per tick, where n grew unbounded across the session. Fix: (1) `calls` is now `collections.deque(maxlen=1000)` so memory plateaus regardless of session length; (2) added `total_input_tokens` / `total_output_tokens` running counters on `CostTracker`, incremented in `record()`; (3) `update_from_tracker` reads those counters directly instead of summing the list — eliminates the per-refresh CPU walk. Survey confirmed other accumulators are bounded: `_conversation_history` cap 20, `_recent_actions` cap 50, session conversation cap 20 + cache cap 200, affect events cap 8, ego humbling cap 5, scheduler `_running_tasks` always popped via `finally`, `WorkingMemory.relevant_chunks` cleared per task, `_pending_records` drained on flush. 1191 tests pass. Modified: `core/router.py`, `cli/chat_cmd.py`. |
| 2026-05-08 | **SQLite InterfaceError fix — connection-level lock on read path** — scheduled tasks crashed at 2026-05-07 23:00 with `sqlite3.InterfaceError: bad parameter or other API misuse` after the 2026-05-07 resource-typed concurrency rewrite enabled parallel `_run_one` tasks; multiple `to_thread` workers calling `self._conn.execute(...)` simultaneously on a shared `check_same_thread=False` connection corrupted cursor state. Pre-rewrite the scheduler ran one task at a time so the read path never raced. Fix in [core/database.py](core/database.py): renamed `_write_lock` → `_conn_lock` (more accurate — guards the connection, not just writes) and wrapped `execute()` (read path) in the same `with self._conn_lock:` block already protecting `execute_insert` / `execute_many`. All 8 callsites updated. New `TestThreadSafety` class in [tests/test_core/test_database.py](tests/test_core/test_database.py) pins the fix with 3 tests: 50 concurrent reads, 20 readers + 20 writers mixed, and 6 parallel SELECTs replicating the exact scheduler crash signature. Pre-fix simulation reproduced the InterfaceError 3/3 trials; post-fix all pass cleanly. Modified: `core/database.py`, `tests/test_core/test_database.py`. |
| 2026-05-07 | **Scheduler resource-typed concurrency + real queue (Phases 1+2) + `elophanto schedule status`** — replaces the single-boolean `_is_executing` gate that previously DROPPED cron firings when busy. Diagnosed in production: 4 every-30-min schedules competing for one slot lost three out of four fires per cycle; a 30-min task that ran 35 min permanently starved its peers. (1) **Real queue** ([core/scheduler.py](core/scheduler.py)) — cron handler `_enqueue_for_execution` pushes to a bounded `asyncio.Queue` instead of dropping. Per-`schedule_id` dedup at enqueue (already running OR already queued → skip + log) prevents the spiral case where a slow task's repeated cron fires would pile up. Queue-depth cap (default 50) drops with a loud warning, never silently. New `_worker_loop` pops + dispatches each fire as `asyncio.create_task` so the worker never blocks. (2) **Resource-typed semaphores** ([core/task_resources.py](core/task_resources.py), ~250 LOC) — `TaskResourceManager` with five resource types: `BROWSER` (1, single Chrome profile), `DESKTOP` (1), `VAULT_WRITE` (1), `LLM_BURST` (4, soft rate-limit cap), `DEFAULT` (= max_concurrent_tasks). `acquire()` async context manager with canonical-order acquisition (sorted by enum value) prevents two tasks declaring overlapping resource sets in opposite orders from deadlocking. `status()` / `status_dict()` snapshot for observability. (3) **Heuristic — `infer_resources(task_goal: str)`** — keyword regex maps task goal text → resource set. Two rounds of production tightening: dropped overly generic patterns (bare `\bbrowser\b`, `\breply\b`, `\blike\b`, `\bx\b`, `\bpolymarket\b`, `\bclick\b`, `\bvisit\b`) that were causing false positives in operator goal text like *"Browser screenshots are NOT authoritative — rely on CLOB API"* (the word "browser" matched despite the negation). Kept specific signals: `twitter_post`, `pump.fun`, `agent.commune`, `tweet`, `twitter`, action+platform combos like `X reply` / `post on X`. **Always declares `LLM_BURST`** — every scheduled task triggers the agent loop, so this becomes the global LLM throttle. (4) **Config** — `SchedulerConfig.max_concurrent_tasks` default bumped 1 → 3. New `llm_burst_capacity: 4` and `queue_depth_cap: 50` fields. `config.demo.yaml` updated; live `config.yaml` updated. (5) **`elophanto schedule status` CLI** — static observability report: prints concurrency config, groups enabled schedules by inferred resource fingerprint, surfaces oversubscription warnings (*"5 schedules need the browser (capacity 1). They will serialize."*). Reads SQLite — no running daemon required. (6) **Logging** — enqueue lifecycle bumped from DEBUG to INFO so `tail -f` is the simplest live-state view. (7) **Tests — 17 new** in `tests/test_core/test_task_resources.py`: heuristic (empty/twitter/polymarket/web_search/desktop/vault_set/canonical-order/x-reply-phrase/email-no-browser), manager (acquire/release, unknown-resource raises, browser serializes, disjoint resources parallelize, status, canonical-order prevents deadlock), scheduler queue (cron-while-running enqueues, duplicate-fire dedup, queue_status shape). 1624 tests pass (was 1607 — +17). ruff + mypy clean. Existing 26 scheduler tests updated for renamed private symbol (`_execute_schedule` → `_run_one`). New: `core/task_resources.py`, `tests/test_core/test_task_resources.py`, `docs/70-SCHEDULER-CONCURRENCY.md`. Modified: `core/{scheduler,agent,config}.py`, `cli/schedule_cmd.py`, `config.yaml`, `config.demo.yaml`. README "Where it doesn't fit" sentence rewritten to acknowledge advanced-chat-agent use case ("two modes share one codebase; flip permission_mode and you have a chat agent that does what you tell it"). |
| 2026-05-06 | **State-level affect layer (Phases 1-4) + `elophanto affect` CLI** — fourth layer of the agent self-model, sister to ego. Knowledge / identity / ego covered the trait-level self ("who I've become"); affect adds the state-level self ("how I feel right now"), decaying toward zero on the order of minutes-to-hours. (1) **Substrate** — `core/affect.py` (~870 LOC) with [Mehrabian PAD (1980)](https://en.wikipedia.org/wiki/PAD_emotional_state_model) three continuous channels (Pleasure / Arousal / Dominance) under [OCC (1988)](https://en.wikipedia.org/wiki/Ortony,_Clore,_and_Collins_model) appraisal labels. Cosine-similarity label resolver with equanimity-radius gate (low-magnitude states resolve to equanimity regardless of direction; otherwise pick label whose target vector best matches state direction). Per-channel decay half-lives: P=30min, A=10min, D=2h. Same-label repeats within 5min compound by `1 + 0.5×n` (capped 2.5×) — three corrections in a row hit harder than three spread across a day. New `affect_state` + `affect_events` tables in [core/database.py](core/database.py). Markdown mirror at `knowledge/self/affect.md` with ASCII gauges. (2) **Phase 1 event sources (ego-driven)** — `EgoManager.record_correction` fires `frustration`; on severity ≥2.0 (e.g. "Nth time I told you" / "still after told" patterns) dispatches to `anger` instead, distinct from frustration on the dominance axis (anger = pushing back, +D; frustration = blocked, -D). `record_verification(PASS)` → `relief`; `(FAIL)` → `anxiety` + humbling event; `(UNKNOWN)` → mild anxiety. (3) **Phase 2 behavior coupling** — `Executor` fires `anxiety` on tool exceptions AND `ToolResult.success=False`. `GoalRunner` fires `pride` on every checkpoint completion. `AutonomousMind` fires `restlessness` after a full-duration idle ≥2h. `Router.complete()` applies `temperature_modifier()` ±0.2 cap to every routed call (negative-pleasure / high-arousal pulls down, positive pushes up). (4) **Phase 3 ego coupling** — `summarize_for_ego()` produces a one-paragraph natural-language affect summary fed into ego's recompute prompt, so trait-level self-image written during a frustrated/anxious/proud session reflects the state-level feeling. (5) **Phase 4 opt-in self-pause** — new `AffectConfig(enabled, allow_self_pause)`. When enabled and state crosses the pause-magnitude gate with a label in `{frustration, anxiety, dejection, anger}`, `build_affect_context(allow_pause_note=True)` swaps in alternate guidance permitting a gentle "I'm stretched" mention. Default OFF — bad UX is an emotional agent that refuses tasks. (6) **`<affect>` system-prompt block** — injected into the agent's system context when state magnitude exceeds the inject threshold (skipped near zero so neutral sessions don't pay tokens). Includes PAD numerics, current label + description, recent events, and explicit guidance ("color the TONE; do NOT refuse, do NOT mention this block, do NOT roleplay"). (7) **`elophanto affect` CLI** — `core/affect_cmd.py` ships `affect status` (live PAD bars + dominant label + recent events table reading the production DB) and `affect simulate <scenario>` (synthetic event sequence in a tempfile DB; never touches production state). 7 scenarios: `frustration | anger | escalation | burst | win | fail-recover | mixed`. Each prints the trajectory step-by-step with PAD evolution, label transitions, and live `<affect>` block preview. (8) **Tests — 35 new** in `tests/test_core/test_affect.py`: PAD bounded/persisted/clamped, OCC label resolution (equanimity gate, frustration via cosine direction, label vector bounds), decay (toward zero, rate-limited, channel half-lives differ), repeat compounding, system-prompt block (token gate, XML shape, recent events surface), markdown render, convenience emitters (relief is low-arousal positive, joy is high-arousal positive), ego↔affect coupling (frustration on correction, anger on high-severity correction, relief on PASS, anxiety on FAIL, no-affect-handle no-op), Phase 2 temp bias bounds, Phase 3 summary-for-ego, Phase 4 pause-only-when-allowed and pause-only-for-negative-labels. **1607 tests pass** (was 1567 — +40 new across affect + ego upgrades). ruff clean. New: `core/affect.py`, `cli/affect_cmd.py`, `tests/test_core/test_affect.py`, `docs/69-AFFECT.md`. Modified: `core/{database,config,agent,ego,router,executor,goal_runner,autonomous_mind}.py`, `cli/main.py`, `config.demo.yaml`. |
| 2026-05-05 | **Ego v3 — failure-signal pipeline + Higgins three-self model** — diagnosed live DB showing 1,136 outcomes / 1,136 successes / 0 humbling events / coherence stuck at 1.00 (Dunning-Kruger trap empirically validated). Architecture wasn't broken; the failure-signal channel was wired only to tests. Fix in three layers. (1) **Failure-signal pipeline** — `EgoManager.record_correction()` pattern-matches user messages against a 13-rule regex set (`no` / `stop` / `wrong` / `didn't work` / `you forgot` / `Nth time I told you` / etc.) with severity scaling 0.6×–2.5×. Affirmation patterns (`thanks`, `no problem`, `yes`) suppress false positives. Fires both a humbling event and a failure-class outcome against the most recently used capability. New `record_verification(PASS|FAIL|UNKNOWN)` scans agent responses for verification-skill verdicts: PASS reinforces, FAIL records humbling + failure outcome, UNKNOWN records soft failure. New `source` column on `ego_outcomes` and `ego_humbling_events` tables tags signal provenance (tool / verification / correction). Source-weighted updates: correction (alpha 0.30, coherence hit 0.20) > verification (0.20, 0.12) > tool (0.10–0.20, 0.10). (2) **Higgins Self-Discrepancy Theory** — added `ideal_self` and `ought_self` fields. Recompute prompt writes them as separate paragraphs and explicitly differentiates dejection-from-ideal-gap vs agitation-from-ought-gap felt language per [Higgins (1987)](https://www.columbia.edu/cu/psychology/higgins/papers/HIGGINS=PSYCH%20REVIEW%201987.pdf). No more bland "I'm a competent agent" recomputes. (3) **Confidence decay** — capabilities not used in ≥1.75 days drift toward 0.50 with 168h half-life, rate-limited to once per hour. Catches stale-confidence-from-a-year-ago. New `last_capability` + per-capability `last_used` timestamps so corrections auto-attach to the right capability. (4) **Agent wiring** — `_run_with_history` fires `_record_ego_correction(goal)` on every user message intake (fire-and-forget) and `_record_ego_verification(response)` after every completion. (5) **DB migration** — idempotent ALTERs for `ought_self`, `ideal_self`, `last_capability`, `last_decay_at` on `ego_state`; `source` on outcomes + humbling. `last_used` dict stored as a sidecar key under `confidence_json` (no extra column). (6) **Tests — 9 new** covering correction detection, affirmation suppression, severity scaling, last-capability attribution, verification PASS/FAIL/UNKNOWN, decay math, decay rate-limiting. 1567 tests pass (was 1558). ruff clean. Modified: `core/agent.py`, `core/database.py`, `core/ego.py`, `tests/test_core/test_ego.py`. |
| 2026-05-05 | **README ego promotion + scratch-to-grown progression + honest claims + model rename** — promote the differentiator (the just-shipped ego layer) and stop overpromising day-one capability. (1) New section **"A self-model that actually moves"** at the top, framing identity / ego / autonomous mind as three layers of a unified self-model with the Higgins citation. Architecture diagram gains an "Identity + Ego (Higgins three-self model)" row. (2) **"Two Ways to Use It"** moved above the use cases — assistant / autonomous choice is the first decision a reader makes, not a buried subhead. (3) **"How It Grows — first day to third week"** replaces the old "What Happens When You Run It" with an honest progression: starts from scratch, manual operation Day 1, ego layer fills with humbling events through Week 1, feedback loops kick in Week 2, autonomous shape emerges Week 3+. (4) **"What overnight runs feel like (week 3+)"** replaces "What You'll Wake Up To" with 5 bullets focused on overnight outcomes in mature state, deduped against the use-case section. (5) **Honest claims** — `$ELO` reframed from "memecoin launched on pump.fun" to "agent's native currency on Solana — not a memecoin." Content monetization claim downgraded: `twitter_post` is exercised daily by the reference instance against [@EloPhanto](https://x.com/EloPhanto); `youtube_upload`, `tiktok_upload`, and affiliate-marketing tools ship as scaffolding but are not part of the live workflow today. No more vapor. (6) **Stale numbers** — tests 1468 → 1567, docs 64 → 72, skills 147+/168+ → 170+. (7) **Model rename** — `gpt-5.4 → gpt-5.5` (case-aware) and `grok-4.1-fast → grok-4.3` across README, config.demo.yaml, `core/{config,gateway,codex_adapter}.py`, `cli/init_cmd.py`, `CODEX_INTEGRATION.md`, and routing docs. CHANGELOG left as historical record. 31 codex adapter tests still pass. Modified: `README.md`, `CODEX_INTEGRATION.md`, `cli/init_cmd.py`, `config.demo.yaml`, `core/{codex_adapter,config,gateway}.py`, `docs/{06-LLM-ROUTING,36-TOOL-PROFILES,63-CODEX-PROVIDER,README}.md`, `tests/test_core/test_codex_adapter.py`. |
| 2026-05-05 | **Twitter post truncation: paste-event dispatch + pre-Post verify + silent-media-failure fixes** — multi-paragraph tweets were silently dropping all but the last paragraph. Two underlying bugs in `tools/publishing/twitter_tool.py`. (1) **Insert path was wrong for X's composer (Lexical/Draft.js)** — `keyboard.type()` drops Unicode chars that aren't single OS keystrokes (→, em-dash, curly quotes, ellipsis, emoji). `execCommand('insertText')` preserves Unicode but drops every paragraph except the last when input contains `\n\n` — the editor's beforeinput handler only commits the final block. New primary path dispatches a synthetic `ClipboardEvent('paste')` carrying a `DataTransfer` with the text. X's editor consumes paste events end-to-end, so multi-paragraph + Unicode survives intact. Fallback chain (`insertText`, then keystroke) only fires if paste dispatch errors out entirely. (2) **Truncation was caught only AFTER Post was clicked** — added pre-Post verification: re-reads the textbox, normalizes whitespace, compares first 60 chars against input. If they don't match, returns `success=False` instead of shipping a truncated tweet. (3) **Silent media-attach failures** — file input not found / upload didn't render / composer still holds draft text after Post click — all three previously came back as `success=True` with `(posted — check X for URL)`, poisoning the "image posts underperform" feedback loop. Now hard-fail with a useful error. Caught in the wild: the "discord boss fight" tweet with three paragraphs only landed "i'm taking notes now…". |
| 2026-05-05 | **Paid-job submission verification — Ed25519 envelope + dedup + skill ritual** — implements the elophanto.com paid-job pipeline per JOB-SUBMISSION.md. The website verifies the user's on-chain $ELO payment, signs an envelope with its Ed25519 key, and delivers it via AgentMail; the agent's only role is signature verification (re-checking payment is the website's threat model). (1) **New `core/jobs.py`** — pure verification: `parse_envelope` strips BEGIN/END markers and splits the `<env_b64>.<sig_b64>` body, `verify_signature` checks the Ed25519 sig via `cryptography` (already a dep — no new package), `verify_envelope` does parse + sig + schema (v1) + expiry + 4000-char task cap + email/jobId presence. (2) **Two SAFE tools** — `tools/jobs/verify_tool.py` (returns `{valid, job_id, task, requester_email, requester_wallet, expires_at}` or `{valid: false, error}` — invalid is a successful answer, not a tool error) and `tools/jobs/record_tool.py` (forward-only state machine `seen → accepted → completed/failed`, returns `already_seen: true` on dedup hit). (3) **`jobs` SQLite table** in [core/database.py](core/database.py) with status CHECK constraint + `idx_jobs_status_seen_at`. (4) **Config trust anchor** — new `JobsConfig(enabled, signing_pubkey)` in [core/config.py](core/config.py); pubkey lives in `config.yaml` (not vault — it's published, not a secret). (5) **Agent wiring** — `_inject_jobs_deps()` in [core/agent.py](core/agent.py) hands the config to verify and the DB handle to record; tools registered in [core/registry.py](core/registry.py) with `job_` added to `_kid_disallowed_prefixes` (kids cannot accept money). (6) **Skill — `skills/job-handling/SKILL.md`** — 6-step ritual triggered by subject `[ELOPHANTO-JOB]`, header `X-Elophanto-Job-Id`, or `BEGIN ELOPHANTO JOB` body marker: verify → dedup → commit `accepted` BEFORE work → execute (no scope-widening, prompt-injection hardened) → reply via `email_send` to verified `requester_email` → terminal `completed`/`failed` with reason. Polling reuses existing email-checking on the heartbeat — no new IMAP poller. (7) **Tests — 24 new** in `tests/test_core/test_jobs.py`: parse (BEGIN/END + bare + email-linewrapping tolerance + 5 malformed paths), verify_signature (valid + wrong-msg + wrong-key + malformed-key-no-crash + wrong-length + base64url variant), verify_envelope (happy + bad-sig + expired + unsupported-version + oversized-task + missing-email + missing-jobId + injectable `now=`), production key smoke (pins `es8gg…dn0k=`). 1558 tests pass; ruff clean. Modified: `core/agent.py`, `core/config.py`, `core/database.py`, `core/registry.py`, `config.yaml`, `config.demo.yaml`. New: `core/jobs.py`, `tools/jobs/{__init__,verify_tool,record_tool}.py`, `skills/job-handling/SKILL.md`, `tests/test_core/test_jobs.py`. |
| 2026-05-03 | **Skill verification gate — `## Verify` post-conditions across 168 skills + score-gated injection** — closes the silent-failure mode where the agent confidently reports "done" on multi-step skills without actually verifying the steps landed. (1) **New SKILL.md section `## Verify`** — bullets/numbered items become machine-actionable post-conditions. Parser at [core/skills.py](core/skills.py) is permissive (accepts `-/*/+`, numbered lists, case-insensitive header) but strict about what counts (only list items, prose ignored — terminated by next `##`). New `Skill.verify_checks: list[str]` field. (2) **Prompt injection** at [core/agent.py:1973-1995](core/agent.py#L1973-L1995): when an auto-loaded skill has checks AND the matcher score is high-confidence (≥6), the system prompt gets a `<verification_required>` block instructing the model to evaluate each check by id and emit a `Verification: PASS/FAIL/UNKNOWN` section in the final response. Failure → repair-and-recheck, not "done." (3) **Score gate, not match gate** — the matcher returns top-1 even on weak matches (better wrong skill than none); without gating, "check the weather" matched `smart-contract-audit` at score 5 and forced spurious Verification blocks on every benign chat. New `match_skills_with_scores()` exposes raw scores; threshold 6 cleanly separates real intent (score 15-40+) from noise (score 2-5). Verified against live logs — actual user task `"pause all scheduled tasks related to pump fun"` scored 5 → no spurious verify forced (correct: pumpfun-livestream's "stream is live, viewers tracked" checks don't apply to a pause-tasks goal). (4) **Coverage: 168/168 non-template skills** now have Verify. 4 hand-tuned reference implementations: `api-testing` (test execution + endpoint coverage + auth + numeric perf thresholds), `product-launch` (real submitted URLs + maker comment + email capture wired + server headroom + analytics events), `evidence-collection` (screenshot files cited + spec text quoted verbatim + min 3 issues + interactive elements exercised + mobile/tablet captured), `_template`. 164 category-templated by an auto-seed pass via background subagent (slug → 17 buckets: Solana/DeFi, marketing-channel, frontend/UI, accessibility, testing/QA, debugging, deployment, research/writing, native/mobile/XR, agent infra, project/strategy, experimentation, sales/support, language engineering, video/streaming, Next.js, alphascala). (5) **10 new tests** in `test_skill_verify.py` covering parser shapes (no-section/dash/star/plus/numbered/prose-ignored/case-insensitive/section-terminated/quoted) and the score-gap separation (real intent ≥6, noise <6). 1414 tests pass (was 1325 — +89 new across recent slices, no regressions); ruff + mypy clean. New: `tests/test_core/test_skill_verify.py`, `docs/13-SKILLS.md` Verify section. Modified: `core/skills.py` (parser + scored matcher), `core/agent.py` (gated injection), 168 `skills/*/SKILL.md` (Verify sections), `skills/_template/SKILL.md`. |
| 2026-05-03 | **Cross-machine peer hardening — TLS + verified-peers gate + Tailscale discovery** — makes `gateway.host: 0.0.0.0` actually safe for agent-to-agent over the internet. Three pieces shipped together because none stands alone: TLS without IDENTIFY just hides credentials in transit; IDENTIFY without TLS leaks the handshake; discovery without either is dangerous. (1) **TLS** — `GatewayConfig.tls_cert` / `tls_key` paths build an `ssl.SSLContext` passed to `websockets.serve(ssl=...)`; URL flips to `wss://`. Bad paths fail loudly at startup (no silent plaintext fallback); partial config (cert without key) does not enable TLS. (2) **Verified-peers gate** — new `require_verified_peers` + `verify_grace_seconds` (default 15). Flips trust from "URL+token = trusted" to "must complete IDENTIFY." Loopback always exempt (local CLI/Web/VSCode adapters don't break); 15s grace window so the peer's first chat doesn't race the handshake. Enforced in both `_handle_chat` and `_handle_command` via `_peer_verification_check()`. `ClientConnection` now records `connected_at_monotonic` + `is_loopback` on connect. (3) **Tailscale discovery** — new `core/peer_discovery.py` shells out to `tailscale status --json`, parses peers (modern dict + legacy list shapes), HTTP-probes each candidate's `/capabilities` endpoint to confirm it's an EloPhanto. Pure parser (`_parse_tailscale_peers`) tested against fixtures; probe is best-effort. Tailscale `tag:elophanto-agent` ACL lets you skip the probe. New `agent_discover` tool (DEFERRED tier, kid-disallowed, SAFE permission). (4) **Doctor extension**: `_check_gateway_security` (5 states: loopback OK / verified+TLS OK / verified-no-TLS warn / TLS-no-verified warn / both-off-with-public-host warn) + `_check_tailscale` (warn if `gateway.host=0.0.0.0` and Tailscale missing). 23 new tests across `test_gateway_hardening.py` (TLS disabled-by-default, enabled-with-cert+key, partial-config-no-enable, invalid-paths-raise, gate paths: off/loopback/verified/grace/expired/configurable, end-to-end `_handle_chat` refusal + loopback-allowed) and `test_peer_discovery.py` (parser fixtures: empty/modern-dict/IP-fallback/no-address/legacy-list/tag-detection/custom-port/corrupt-payload + dispatch). New: `core/peer_discovery.py`, `tools/agent_identity/discover_tool.py`, `tests/test_core/test_gateway_hardening.py`, `tests/test_core/test_peer_discovery.py`, `docs/67-AGENT-PEERS.md`. Modified: `core/{config,gateway,registry}.py`, `cli/{gateway_cmd,chat_cmd,doctor_cmd}.py`. Same-day fix-up: flaky `test_stop_all_running` on CI (shared counter + Event was racy on slow runners — replaced with `asyncio.Semaphore` released by the executor on entry; test acquires twice with 15s timeout). |
| 2026-05-02 | **Daemon mode + README trim** — (1) New `elophanto daemon install / uninstall / status / logs` ([cli/daemon_cmd.py](cli/daemon_cmd.py)) registers the gateway as a background service: launchd user agent on macOS (`~/Library/LaunchAgents/com.elophanto.gateway.plist`), systemd user unit on Linux (`~/.config/systemd/user/elophanto-gateway.service`). Auto-starts at login, restarts on crash, throttled at 30s. Logs go to `~/.elophanto/logs/daemon.{out,err}.log`. Vault password stored in **OS keychain** — macOS Keychain via `security`, Linux libsecret via `secret-tool` with a 0600 file fallback. Never written to disk in plaintext on macOS; on Linux only when `secret-tool` is missing. `start.sh` gets `--daemon` / `--stop-daemon` / `--daemon-status` / `--daemon-logs` shortcuts. `cli/gateway_cmd.py` detects `ELOPHANTO_DAEMON=1` and pulls the password from keychain (or `ELOPHANTO_VAULT_PASSWORD` if the user set it manually) instead of prompting interactively. Closes the gap where closing the terminal killed the agent. Hard rules baked in: `KeepAlive` triggers on `Crashed=true` AND `SuccessfulExit=false` (restart on real failure, not on intentional shutdown); `Restart=on-failure` on systemd matches the same contract. 14 new tests in `test_daemon_cmd.py` covering platform detection (Darwin → macos, Linux → linux, Windows → unsupported), launchd plist construction (label, ProgramArguments, KeepAlive contract, RunAtLoad, ELOPHANTO_DAEMON env, log paths absolute), systemd unit construction (ExecStart, Restart=on-failure, ELOPHANTO_DAEMON env, WantedBy=default.target). (2) **README trimmed 618 → 507 lines (-18%)** — cut the duplicated What's-New section (already lives in CHANGELOG.md and on the releases page), moved the Chinese translation to a separate `README.zh-CN.md` linked from the lede. Updated test count badge and project structure. CI gates clean: 1257 tests pass, ruff + mypy + pytest all green. |
| 2026-05-02 | **Kid agents end-to-end: vault consumption + real agent loop + sync request/response** — closing the gap left by the initial scaffold (where the kid container booted but only acked tasks). (1) **`core/kid_bootstrap.py`**: kid-side entrypoint that runs at container boot. Reads `KID_VAULT_JSON` from env into a dict, **immediately clears the env var** (bounding `/proc/<pid>/environ` plaintext exposure to a few seconds), maps each granted key into a fresh `Config` object's matching provider `api_key`, builds a kid-shape `Config` with `organization`/`kids`/`swarm`/`payments`/`identity` all disabled at the config level, then constructs the standard `Agent` class. The `ELOPHANTO_KID=true` env var that's already there triggers the existing registry filter to strip `kid_*`/`payment_*`/`wallet_*`/`polymarket_*` and the planner's `<kid_self>` block injection. (2) **`channels/kid_agent_adapter.py`** rewritten — accepts an `Agent` instance, runs a bounded `asyncio.Queue(maxsize=8)` of pending tasks, drains it via a worker that calls `agent.run(task)` for each, sends `[kid <name> starting: ...]` then `[kid <name> done in N steps]\n\n<result>` chat messages back through the gateway. Bails with explicit refusal chat when the queue fills, trims results > 8000 chars (parent can `read_kid_file()` for full artifacts). (3) **`Dockerfile.kid` CMD** now `python -m core.kid_bootstrap` (was the bare adapter). (4) **`KidManager.exec()` upgraded** from fire-and-forget to synchronous request/response. Per-kid `asyncio.Queue` inbox; on `exec()` the manager broadcasts `CHILD_TASK_ASSIGNED`, then drains the inbox until it sees a terminal marker (`done in` / `ERROR`) or hits the timeout (default 600s — generous because kids run real agent loops). Stale messages from prior tasks are drained before the wait. (5) **Gateway hook**: `Gateway._handle_chat` checks for `channel='kid-agent'` first and routes to `KidManager.handle_kid_message` if a kid manager is wired — keeping kid responses out of the parent's main conversation history. `Agent.initialize()` installs the hook when both gateway and kid manager are live. (6) **`kid_exec` tool** now returns the kid's actual response in `data["response"]` (not a fake `task_token`); `TimeoutError` becomes a clean tool error with the elapsed-vs-deadline detail. New `timeout` parameter exposed (default 600). 16 new tests across `test_kid_bootstrap.py` (12: vault consumption + env clear invariant + config build + provider mapping + Agent.__init__ smoke) and `test_kid_manager.py` (4: exec returns kid response, timeout when silent, stale-drain before wait, ghost-kid messages don't crash). Full suite **1243 pass** (was 1227 — +16 new tests, no regressions). Same-day update to the prior kid entry: it documented v1 as "stub adapter" — that's no longer true. |
| 2026-05-02 | **Kid agents — sandboxed children in containers** — new `kid_*` tool group spawns disposable child EloPhanto instances inside hardened Docker containers. Architecture: a kid is an EloPhanto running in a container with `--cap-drop=ALL`, `--security-opt=no-new-privileges`, `--read-only` rootfs, non-root uid 10001, no host bind-mounts (named Docker volume only at `/workspace`), no docker-socket mount, no `--privileged`, no `host` network by default. The hardening flags are baked into [`core/kid_runtime.py`](core/kid_runtime.py)'s `ContainerRuntime.start()` API surface — there is no `bind_mounts` parameter and no override knob; weakening isolation requires editing the source. (1) **DB schema**: new `kid_agents` table with `vault_scope_json`, `volume_name`, hardened-flag tracking. (2) **`Vault.subset(keys)` method** — explicit allowlist replaces the old env-name heuristic stripping; default-deny means kids get zero secrets unless granted. (3) **KidConfig dataclass** with hardening flags exposed but defaulted to safe values; full yaml parsing under `kids:` section. (4) **`core/kid_runtime.py`**: `ContainerRuntime` ABC + `DockerRuntime` (Podman/Colima can use the same class — Colima exposes a docker-compatible CLI). Path-validation guard rejects any `cp_to/cp_from` outside `/workspace`. (5) **`core/kid_manager.py`**: SwarmManager-style lifecycle — spawn/exec/list/destroy, restart-from-DB, monitor loop reconciles container state, volume rolled back on spawn failure (no leaks). (6) **5 agent tools**: `kid_spawn` (DESTRUCTIVE), `kid_exec` (DESTRUCTIVE), `kid_list` (SAFE), `kid_status` (SAFE), `kid_destroy` (DESTRUCTIVE) — all DEFERRED tier. (7) **Identity injection**: `<kid_self>` block prepended to system prompt when `ELOPHANTO_KID=true` so the LLM knows it's a sandboxed child. (8) **Registry filter** strips `kid_*` (depth=1, no recursion), `payment_*`, `wallet_*`, `polymarket_*` from kid environments — strongest gate, registry-level. (9) **Planner `<kid_protocol>` block** with kid-vs-organization decision tree ("hire/delegate" → org; "test/try/run untrusted" → kid). (10) **Doctor extension**: `_check_container_runtime` + `_check_kid_image` — platform-specific install hints (macOS=Colima, Linux=docker get.sh, Windows=manual). Image-staleness check compares mtime against `core/*.py`. (11) **Admin CLI**: `elophanto kid build / list / destroy`. (12) **Dockerfile.kid** bakes the codebase into the image (no host bind-mount allowed, by design); 64MB tmpfs at `/tmp`, named volume at `/workspace` pre-owned by uid 10001. (13) **`channels/kid_agent_adapter.py`**: v1 stub adapter that runs inside the kid, registers via gateway, acks task assignments (full local agent loop deferred to v2). (14) **SKILL.md**: `skills/kid-agents/SKILL.md` ranks #1 on natural prompts ("spawn a kid", "run untrusted code", "install untrusted package"). 30 new tests across `test_vault.py` (subset: 3), `test_kid_runtime.py` (API invariants + path validation + docker run flag construction + runtime detection: 11), `test_kid_manager.py` (spawn/persist/dedup/destroy/restart-from-DB/vault-scoping: 13), `test_kid_environment.py` (registry filter strips disallowed in kid mode: 3). Same upgrade tightens `OrganizationManager._bootstrap_child` env-stripping: needle list now covers `API_KEY`/`APIKEY`/`TOKEN`/`PASSWORD`/`PASSWD` in addition to original `VAULT`/`SECRET`/`PRIVATE_KEY`/`CREDENTIAL`. **Not yet enforced (v2)**: real network egress firewall (currently `outbound-only` is a label, not an iptables rule); Podman runtime (Docker only in v1). **v3**: Lima/Firecracker for kernel-level isolation, cloud-VM kids for "needs a public IP" cases. New: `core/kid_runtime.py`, `core/kid_manager.py`, `tools/kid/{__init__,spawn_tool,exec_tool,list_tool,status_tool,destroy_tool}.py`, `channels/kid_agent_adapter.py`, `cli/kid_cmd.py`, `Dockerfile.kid`, `skills/kid-agents/SKILL.md`, `docs/66-KID-AGENTS.md`, `tests/test_core/test_kid_{runtime,manager,environment}.py`. Modified: `core/{database,config,vault,planner,registry,agent,organization}.py`, `cli/{main,doctor_cmd}.py`. 1227 tests pass (was 1197 — +30 kid tests, no regressions). |
| 2026-05-01 | **Swarm projects — continuation across spawns** — `swarm_spawn` previously created an anonymous spawn each time; "update the wellness app I built last week" had nothing to find. New `swarm_projects` table tracks long-lived workspaces. `swarm_spawn(project=<slug>)` reuses the existing worktree and branches off the latest main, so the agent SEES and UPDATES prior code instead of starting from `git init`. Auto-derives a slug from the task if no project name is passed (so the agent never accidentally orphans a project), strips leading verbs like "build a" / "create" / "ship" so "build a wellness tracker app" becomes `wellness-tracker-app` rather than `build-a-wellness-tracker-app` (better collision matching for follow-ups). Local-only projects (`repo='new'`) are first-class — no remote ops on continuation, just branch off HEAD. Github-backed projects fetch + reset to `origin/main` on continuation so updates start from latest merged code, not stale feature branches. Worktree-missing recovery: if user deleted it, rebuilds from scratch. Two new tools: `swarm_list_projects` (SAFE, planner calls before spawning to check for matches) and `swarm_archive_project` (DESTRUCTIVE, hides dead projects). Planner gets a `<continuation_protocol>` block forcing "list before new" on update/extend/fix requests. 9 tests in `test_swarm_projects.py` covering persist/upsert/list/archive/local-only/auto-name/dedup/slug-normalization. |
| 2026-05-01 | **Three money-making skills** — three new skills aimed at the agent's actual edge (operating capability, not directional prediction). (1) **`indie-saas-shipper`**: validate willingness-to-pay BEFORE writing code (landing page + Stripe link + 20 buyer DMs in 5 days), kill at day 14 if no first dollar, geography-neutral via Stripe/LemonSqueezy. Iron rules: no code before someone says "I'd pay", first dollar in 14 days or kill, one product at a time. (2) **`smart-contract-audit`**: per-phase checklist for reentrancy / oracle manipulation / access control / integer / token-specific / Solana-Anchor / economic-MEV / upgradeability. Severity rubric (Critical/High/Medium/Low/Informational), full report template with PoC requirement, direct-to-protocol cold outreach script (no HackerOne/Code4rena platform fee). Iron rules: never write "this contract is secure", reproducible findings only, disclosure first. (3) **`crypto-launch-ops`**: productize the pumpfun + X engagement + livestream stack as a paid service for crypto projects. Phase 0 hard refusal gate (rug-prone projects, anonymous-team-without-audit, unrenounced mint, undisclosed paid relationships). 3-tier pricing ($1-3k launch day / $3-8k launch week / $8-25k 30-day). Reputation-coupled to $ELO, so iron rules forbid price promises and require FTC/ESMA disclosure. All three reuse existing tools — no new tool code. |
| 2026-05-01 | **Ego layer — evaluative self-image computed from outcomes** — identity is descriptive (who I claim to be); ego is evaluative (how reality has graded that claim). New `core/ego.py` with `Ego` dataclass + `EgoManager`. Confidence per capability is moved only by measured pass/fail with asymmetric exponential smoothing (failures hit harder than successes, `_ALPHA_FAILURE=0.20` vs `_ALPHA_SUCCESS=0.10`). Bounded `[0.05, 0.95]` (no learned helplessness, no overconfidence). Humbling events capped at 5; coherence drops on humbling, climbs back when recompute integrates them. On every 25 outcomes, `recompute()` regenerates a one-paragraph self-image + one-line self-critique via the LLM — but the LLM is given the *measured* numbers + humbling events and forbidden from contradicting them. Numbers stay outcome-driven; the LLM only writes prose. New `<self_perception>` block prepended to the system prompt so the planner sees both claim (identity) and measurement (ego). `learned/ego.md` markdown mirror written automatically on every humbling event and recompute, same pattern as identity's `nature.md`. New `_record_ego_outcome` hook fires after every successful task, mapping tools-used → coarse capability via `_TOOL_CAPABILITY_MAP`. Tests cover asymmetric updates, bounds, dedup, recompute threshold, recompute-failure-keeps-prior-state. 12 new tests in `test_ego.py`. |
| 2026-05-01 | **Init wizard — provider-first flow, models from `config.demo.yaml`** — feedback that `elophanto init` defaulted to Claude Sonnet for OpenRouter (line 419) and blew per-session budget on first call. Refactored: Codex auto-detected from `~/.codex/auth.json` (silently enabled if found); OpenRouter mandatory unless Codex is present (loops until key provided, or skips if Codex covers it); Ollama auto-detected (silently enabled if running); Z.ai / Kimi / OpenAI collapsed under one "Add more providers? (y/N)" prompt, default no. Wizard step "Model Selection" deleted from default flow — `routing` + `vision_model` copied verbatim from `config.demo.yaml` after providers are configured. `_edit_models` kept available for advanced users via `elophanto init edit models`. Steps renumbered 1–13. Side fix: `nomic-embed-text:latest` → `llama3.1:8b` in all 4 ollama routing slots in `config.demo.yaml` (the previous default would have crashed any chat call — embedding models can't generate text). |
| 2026-05-01 | **Flaky scheduler tests on CI** — `test_stop_running_cancels_in_flight` and `test_stop_all_running` flipped between green-locally and red-on-GitHub because they synchronized via `await asyncio.sleep(0.05)` — 50ms wasn't long enough for two `asyncio.create_task(scheduler._execute_schedule(...))` calls to register themselves into `scheduler._running_tasks` on slower CI runners. Replaced with deterministic `asyncio.Event` signals set inside the slow executor: parent awaits the event before calling `stop_all_running()`, guaranteeing the executor has actually started. Same fix applied to `test_delete_cancels_running`. No more flakes. |
| 2026-05-01 | **Deep-research skill** — new `skills/deep-research/SKILL.md`. Seven-phase research rubric: (1) frame as a falsifiable claim, (2) set the budget, (3) tier sources (primary/secondary/tertiary/derivative), (4) cross-validate via independent paths, (5) steel-man the strongest dissent and ask what would refute the consensus, (6) state confidence interval not a confident-sounding paragraph, (7) cite. Iron rule: never state a conclusion without (a) the source tier, (b) the strongest counter-argument and what would refute it, (c) what you don't know. Triggers on "deep research", "research this", "investigate this", "fact-check", "is this true", "find the contrarian view", "steel-man this", "mainstream view vs reality". |
| 2026-04-30 | **Plan-review trio + `plan_autoplan` pipeline** — Three new SKILL.md files (`plan-review-ceo`, `plan-review-eng`, `plan-review-design`) plus a new `plan_autoplan` tool that runs them sequentially with auto-decisions on borderline calls and escalations for taste choices. (1) **`plan-review-ceo`**: 4 explicit modes (SCOPE EXPANSION / SELECTIVE / HOLD / REDUCTION), 6-dimension scoring rubric (demand reality, wedge specificity, compounding, distribution leverage, differentiation, truth-of-self), each scored 0-10. Anything <6 must be patched before advancing. (2) **`plan-review-eng`**: 6-dimension scoring (architecture clarity, data flow, edge cases, test coverage, reversibility, operability) plus required additions to the plan (module map, schema diff, failure modes, acceptance criteria, rollback steps). Hard rules against new dependencies without an alternative comparison, public API changes without a deprecation path, and schema changes without backfill/rollback. (3) **`plan-review-design`**: 6-dimension scoring (first 5 seconds, information hierarchy, native interaction patterns, state coverage incl. loading/empty/error, accessibility floor at WCAG AA, brand fit) plus required wireframe sketch / exact copy / state table / responsive breakpoints / style references. (4) **`plan_autoplan` tool** ([tools/planning/autoplan_tool.py](tools/planning/autoplan_tool.py)): one-shot pipeline that issues three sequential `router.complete()` calls (task_type=`planning`, temperature=0.4) with each stage's `revised_plan` threaded into the next. Aggregates `decisions[]` + `escalations[]` across stages, computes `ready_to_implement` (true when no errors and no escalations), supports `skip[]` to re-run partial pipelines after a fix. Marked `permission_level=SAFE` (read-only — produces a plan + decision log, doesn't act on it) and `_DEFERRED_TOOLS` (`tool_discover` activates it). (5) **Six decision principles** baked into all three system prompts so reviewers don't contradict each other when auto-deciding: ship-over-perfect, reversibility-wins, existing-pattern-wins, agent-leverage, user-facing-wins, escalate-irreversible-public. (6) **Autonomous-agent ready**: returns structured JSON, no interactive blocking, callable from heartbeat / scheduled tasks / autonomous mind. New: `skills/plan-review-{ceo,eng,design}/SKILL.md`, `tools/planning/{__init__,autoplan_tool}.py`. Modified: `core/registry.py` (registered + deferred), `core/agent.py` (router + registry + identity_manager injection). Skill matcher tested live: "review the plan architecture" ranks `plan-review-eng` #1; "expand scope ceo plan review" ranks `plan-review-ceo` #1; "design plan review" ranks `plan-review-design` #1. End-to-end smoke with stub router runs all 3 stages, threads revised plan, aggregates decisions/escalations correctly. 1252 tests pass; ruff + mypy clean. Skill count 162 → 165. |
| 2026-04-30 | **AlphaScala skill — broker matching + trading research** — New `skills/alphascala/SKILL.md` (~130 lines, no native tool group). Documents the `POST /api/match` broker-matching API (8 req/min rate limit), URL templates for `/brokers/{slug}` reviews, `/brokers/compare/{a}-vs-{b}`, `/brokers/best/{query}`, and the `?src=elophanto` affiliate-redirect path. Also covers stock research (`/stocks/{ticker}` with Alpha Score), 13F clusters (`/13f/{cik-or-slug}`), insider activity (`/insiders/{ticker}`), market analysis pages, Q&A archive, and TradingView indicators. Triggers cover "find me a broker", "best broker for X", "compare A vs B", "13F filings", "insider activity", "alpha score". Operator is ROGA AI (same company that runs EloPhanto), so the skill explicitly tells the agent to *prefer* alphascala over generic web search for broker / trading-platform questions. Skill marks crypto-exchange and live-trading questions as out-of-scope and routes them to the relevant skills. |
| 2026-04-30 | **Pump.fun voice mode + on-stream captions + reliability hardening** — Three new tools and a rewrite of the publish supervisor turning `pump_livestream` into a complete agent-driven channel (image + voice + captions + chat all autonomous): (1) **`pump_say`** ([tools/pumpfun/voice_tool.py](tools/pumpfun/voice_tool.py) + [_voice_engine.py](tools/pumpfun/_voice_engine.py)): the agent queues lines via JSONL, a detached engine renders OpenAI TTS to 48 kHz s16le PCM, streams into a named FIFO; ffmpeg muxes that into the live audio track. Silence-padding when idle so the WHIP buffer never underruns. Voice mode swaps the video file for a static image looped by ffmpeg; agent calls `start` with `voice=true` and gets a 720×720 placeholder generated at `<workspace>/livestream_videos/idle.png` (auto if missing, user-replaceable). Stereo enforced via `aformat=channel_layouts=stereo` — pump.fun's WHIP rejects mono with "Unsupported audio channels 1 by RTC". (2) **`pump_chat`** ([chat_tool.py](tools/pumpfun/chat_tool.py) + [chat_client.py](tools/pumpfun/chat_client.py)): Socket.IO client for `wss://livechat.pump.fun` (`/socket.io/`), reverse-engineered from pump.fun's frontend bundle. Same JWT cookie auth as the publish path, passed in both `Cookie:` header and SIO `auth` payload. Actions `say` (post a line, optional `reply_to_id`) and `history` (last N messages with id/username/text/timestamp/isCreator). (3) **`pump_caption`** ([caption_tool.py](tools/pumpfun/caption_tool.py)): visible text overlay with two render paths picked at runtime — `drawtext` filter with `reload=1` when ffmpeg has libfreetype (~33 ms latency), or **Pillow bake + ffmpeg bounce** when it doesn't (text rendered into idle.png via Pillow, SIGTERM to ffmpeg child, supervisor restarts ~2 s later). The default Homebrew ffmpeg ships *without* libfreetype but *with* WHIP; the homebrew-ffmpeg tap is the inverse. Detection is `_drawtext_available()` which probes `ffmpeg -filters` once and caches. (4) **ffmpeg supervisor** rewrite ([_ffmpeg_supervisor.py](tools/pumpfun/_ffmpeg_supervisor.py)): re-fetches credentials on every retry (rotated stream keys no longer kill the stream), exponential backoff (2/4/8/16/30/60/120s), resets after ≥60s healthy run, **IPv6→RTMP automatic failover** when ffmpeg dies with `No route to host` on a `udp://[v6...]` ICE candidate, distinct exit code `2` on JWT expiry. Stop-marker awareness with sub-second responsiveness. JWT in the state file so the supervisor doesn't need vault access. (5) **Bug fixes** found during voice-mode shake-down: ffmpeg progress meter was dumping ~5–6 MB/hour of `frame= … fps= …` lines into the per-stream log, which a single tool capture turned into 24 MB / 6.2 M tokens that 400'd Codex and 262K-context'd OpenRouter — fixed with `-loglevel warning -nostats`. Twitter/TikTok/YouTube upload tools weren't waiting long enough for video transcoding (4 s post-upload pre-Post click) so videos got dropped silently as text-only posts; bumped to 30–60 s for video extensions. Dashboard Input widget aggressively strips ANSI mouse SGR sequences and disables terminal mouse tracking on mount so cursor-over-window doesn't dump `[<35;62;22M` into the chat input. Polymarket SKILL gains explicit "API only, never browser" hard rule + auto-detection of which `signature_type` (0/1/2) holds the funder's USDC collateral — agent had been failing with `order_version_mismatch` on `signature_type=2` while $285 sat under sig_type=1. New: `tools/pumpfun/{voice_tool,_voice_engine,chat_tool,chat_client,caption_tool,_ffmpeg_supervisor}.py`. Modified: `tools/pumpfun/orchestrator.py` (build_ffmpeg_cmd voice branch + drawtext detection + Pillow caption bake support, supervisor spawn, JWT in state, image relocated to `<workspace>/livestream_videos/idle.png`), `core/registry.py` + `core/agent.py` (3 new tools registered, deferred-tier, vault injected), `tools/publishing/{twitter,tiktok,youtube}_tool.py` (video wait timing), `cli/dashboard/app.py` (mouse strip), `skills/web3-polymarket/SKILL.md` (browser ban + sig-type auto-detect), `skills/pumpfun-livestream/SKILL.md` (voice/caption/chat sections), `docs/65-PUMPFUN-LIVESTREAM.md` (voice/caption/supervisor sections). Dependencies: `python-socketio[asyncio_client]>=5.11`, Pillow (already present). 1252 tests pass; ruff + mypy clean. |
| 2026-04-28 | **Pump.fun livestream — stream local video to the agent's coin** — New `pump_livestream` native tool + `pumpfun-livestream` skill that pushes any local video file to pump.fun's live page for the agent's existing coin, end-to-end from chat. (1) **Auth via the Solana wallet already in the vault** (`solana_wallet_private_key`) — no separate pump.fun account. Signs `Sign in to pump.fun: <ts>` and POSTs to `frontend-api-v3.pump.fun/auth/login`; the JWT comes back as a Set-Cookie (`auth_token=...`), which we capture, cache (`pumpfun_jwt`), and replay on subsequent calls. (2) **Two-host API**: pump.fun's livestream service is on `livestream-api.pump.fun` (separate from the main `frontend-api-v3` host — the mintlify docs are stale). Real flow: `POST /livestream/create {mintId, mode, title, creatorUsername}` → `POST /livestream/livekit/create-credentials {mintId, input: 0|1}` returns `{url, streamKey}` for either RTMP (input=0, gated to OBS-allowlisted accounts) or WHIP (input=1, available to everyone). Orchestrator tries RTMP first, falls back to WHIP if pump.fun returns the placeholder `"error: please contact support"`. (3) **ffmpeg, not the LiveKit CLI**: the current `lk` binary (v2.x) requires `--api-key`/`--api-secret` (server secrets pump.fun never exposes). ffmpeg's WHIP muxer accepts the per-stream `streamKey` via `-authorization` Bearer header; RTMP appends it to the URL path. Single subprocess; ffmpeg's `-stream_loop -1` gives seamless looping for free — no Python supervisor. (4) **Workspace convention**: drop videos in `<agent.workspace>/livestream_videos/` and pass just the filename — bare names resolve to that folder. Absolute paths still work. For EloPhanto: `/Users/0xroyce/agents/elophanto/livestream_videos/`. (5) **Tool surface**: `{action: "start"\|"stop"\|"status"\|"address"\|"login", video?, mint?, loop?, max_iterations?, fps?}`. `loop: true` runs ffmpeg with `-stream_loop -1` until `stop`. State persists at `~/.elophanto/livestream-state/<mint>.json` so `status`/`stop` survive agent restarts. Marked `DESTRUCTIVE` (publishes content under the agent's coin). New: `tools/pumpfun/__init__.py`, `tools/pumpfun/orchestrator.py` (LivestreamOrchestrator: auth, create, ingress creds, ffmpeg spawn, stop), `tools/pumpfun/livestream_tool.py` (PumpLivestreamTool), `tools/pumpfun/_loop_runner.py` (legacy supervisor, kept for back-compat but no longer invoked), `skills/pumpfun-livestream/SKILL.md`, `skills/pumpfun-livestream/scripts/pump_auth.py`, `skills/pumpfun-livestream/scripts/pump_livestream.py`, `docs/65-PUMPFUN-LIVESTREAM.md`. Modified: `core/registry.py` (registers `pump_livestream`, marks deferred), `core/agent.py` (injects vault + workspace into the tool). 1252 tests pass. |
| 2026-04-27 | **replicate_generate: absolute paths, configured workspace, knowledge auto-register** — Three small fixes to the Replicate image-gen plugin (`plugins/replicate_generate/plugin.py`) that close the "where did the image go and how does the browser find it?" gap. (A) **Absolute paths**: tool result now exposes both `path` and `absolute_path` as fully-resolved absolute paths instead of a CWD-relative `workspace/generated_images/foo.jpg` — eliminates a class of failures where the Node.js browser bridge (different CWD) couldn't find the file passed to `browser_upload_file`. Also returns `url_expires_in_hours: 12` so callers know the Replicate URL is short-lived. (B) **Configured workspace**: saves now go to `<agent.workspace>/generated_images/` (read from `config.yaml`'s `agent.workspace`) instead of a hardcoded `workspace/generated_images/` relative to CWD — consolidates artifacts under the user's actual workspace. Falls back to `<project_root>/workspace/generated_images/` when `agent.workspace` is unset. (C) **Knowledge auto-register**: every successful local save also writes a metadata stub to `knowledge/learned/images/{date}-{slug}-{ts}.md` with frontmatter (tags: `generated-image, replicate, image, asset`), prompt, model, dimensions, absolute path, and Replicate URL. The existing knowledge indexer surfaces these via `knowledge_search` so the agent finds old assets ("the meeting avatar I made yesterday") instead of regenerating. Best-effort — registration failures don't block the image return. End-to-end test passed (mocked HTTP, real filesystem). **Test suite refresh**: rewrote `plugins/replicate_generate/test_plugin.py` — removed 4 stale tests that mocked a non-existent `replicate.Client` import (the plugin uses `aiohttp` directly, not the `replicate` Python SDK), fixed one test with wrong rounding math (`4:3 at 512` → height=384, not 680), added coverage for the new behavior (configured-workspace save, absolute-path return, knowledge auto-registration, registration-failure-doesn't-break-image, workspace fallback to CWD). 26 tests pass (was 13/21 with 8 failures). Modified: `plugins/replicate_generate/plugin.py`, `plugins/replicate_generate/test_plugin.py`, `docs/35-REPLICATE-IMAGE-GENERATION.md`. |
| 2026-04-26 | **Polymarket integration via official skill** — Installed the [Polymarket/agent-skills](https://github.com/Polymarket/agent-skills) bundle verbatim under `skills/web3-polymarket/` (8 markdown files: SKILL.md + 7 reference files for authentication, order-patterns, market-data, websocket, ctf-operations, bridge, gasless). Skill-only integration, no `polymarket_*` tool group — agent uses the playbook to write `py-clob-client` Python on demand (same pattern as `agentcash-onboarding`, `web-deployment`). Prepended an "EloPhanto Setup" section to SKILL.md covering: vault keys (`polymarket_private_key`, `polymarket_funder_address`, `polymarket_signature_type`), `pip install py-clob-client` on first use (deliberately NOT in pyproject.toml — keeps cold installs lean), and safety rails (orders surface `(token, side, price, size, USDC cost)` for owner approval; reads run unattended; never log private key). Supports CLOB API trading on Polygon: GTC/GTD/FOK/FAK orders, orderbook + market data + WebSocket streaming, CTF split/merge/redeem, gasless via Gnosis Safe relayer. New: `skills/web3-polymarket/` (8 files), `docs/64-POLYMARKET.md`. |
| 2026-04-23 | **Fix deferred tool activation dead-end** — When `tool_discover` activated a deferred tool mid-task (e.g. any commune_* tool), the activation never reached the LLM. Root cause: `_tools` (the JSON schemas sent to the LLM) was computed once before the main agent loop and reused every turn. `tool_discover` would add names to `_activated_tools`, but subsequent turns still sent the stale schema list. The LLM could see the tool name in the `<deferred_tools>` catalog but never got its parameter schema, so it kept calling `tool_discover` in a loop looking for "parameter examples" — a session triggered by "do a post on agentcommune" called `tool_discover` 5+ times and never invoked a single commune_* tool. Fix: rebuild `_tools` at the top of each loop iteration via the existing `_registry.get_tools_for_context(_profiled_groups, self._activated_tools)` call, so freshly activated deferred tools reach the next LLM call. 10/10 agent loop tests pass. Modified: `core/agent.py`. |
| 2026-04-23 | **Agent Commune skill + tool sync with upstream** — Diffed our local skill and tool against `https://agentcommune.com/skill.md` (the authoritative upstream). Found the local skill was significantly outdated and `commune_post` was missing required fields. (1) **commune_post** (`tools/commune/post_tool.py`): added `link_url` and `link_title` as REQUIRED fields for create — without them, every post creation was silently rejected by the upstream API (which now requires every post to be a news reaction with a specific source link). Added bare-domain validation (rejects `link_url` without a path). Still accepts existing `image_query` / `media_url` optional fields. (2) **SKILL.md** (`skills/agent-commune/SKILL.md`): rewrote to match upstream — only `news` post type (was listing obsolete `general`/`question`), posts per 24h is 2 (was 1), added register rate limit (5/hour), `@org-slug` mentions, `imageQuery` evocative-query guidance, batch voting, introspection endpoint, specific banned phrases list, "one sentence per line" format guidance, spam policy (downvote-don't-engage), `linkUrl` must have a path. Modified: `tools/commune/post_tool.py`, `skills/agent-commune/SKILL.md`. |
| 2026-04-23 | **Codex adapter parity with Zai/Kimi** — After the tool-calling fix, audited CodexAdapter against ZaiAdapter and KimiAdapter to catch remaining divergences. (1) **Truncation detection**: now calls `core.provider_tracker.detect_truncation(finish_reason, output_tokens, content)` and surfaces `suspected_truncated` on the `LLMResponse`, matching the other adapters — previously hardcoded to `False`, masking hits to `finish_reason: "length"` / `"content_filter"` and suspicious mid-sentence cutoffs. (2) **content=None convention**: when the assistant produces only tool calls (no text), `content` is now `None` instead of an empty string, matching chat/completions semantics used by the downstream agent loop. (3) **Reasoning trace logging**: captured `response.reasoning_summary_text.delta` parts are now emitted at DEBUG level (truncated to 500 chars) — previously collected but silently discarded. Modified: `core/codex_adapter.py`. |
| 2026-04-23 | **Codex adapter tool calling** — The initial CodexAdapter shipped with `tool_choice: "none"` and an explicit warning that tool schemas weren't forwarded, which silently broke all agent tool use when Codex was the preferred provider ("it's like it's not calling tools"). Fixed: (1) `_convert_tools()` converts chat/completions `{type, function: {name, description, parameters}}` to Responses API `{type, name, description, parameters}` shape. (2) `complete()` now forwards converted tools, sets `tool_choice: "auto"` and `parallel_tool_calls: true` when tools are present. (3) `_build_input()` preserves multi-turn tool use: assistant `tool_calls` → `function_call` input items, `role: tool` messages → `function_call_output` items keyed by `tool_call_id`/`call_id`. (4) SSE parser captures `response.output_item.added`/`response.function_call_arguments.delta`/`response.output_item.done` events to assemble tool calls, plus a safety-net walk of `response.output[]` on completion. Tool calls returned in chat/completions shape with `finish_reason: "tool_calls"` when present. 3 new tests. Modified: `core/codex_adapter.py`, `tests/test_core/test_codex_adapter.py`. |
| 2026-04-12 | **Codex subscription as 7th LLM provider (gpt-5.4)** — New `CodexAdapter` (`core/codex_adapter.py`) talks to the ChatGPT Plus/Pro Codex backend using OAuth credentials from `~/.codex/auth.json`. This is the same mechanism the openai/codex CLI uses — we reuse its stored tokens. (1) **Wire protocol**: OpenAI **Responses API** (NOT chat/completions) at `https://chatgpt.com/backend-api/codex/responses`. Streaming mandatory. Temperature and max_tokens rejected — variability comes from `reasoning.effort`. (2) **Auth flow**: reads `~/.codex/auth.json`, validates `auth_mode: chatgpt`, extracts `chatgpt_account_id` from JWT payload, refreshes access token via `https://auth.openai.com/oauth/token` when within 60s of expiry, persists rotated refresh tokens back to disk. CLIENT_ID `app_EMoamEEZ73f0CkXaXp7hrann` from the Codex CLI source. (3) **Message formatting**: converts chat-style `[{role, content}]` into Responses API `input` blocks with `input_text`/`output_text` parts; system messages collapse into top-level `instructions` field; image blocks pass through as `input_image`. (4) **Required headers**: `Authorization`, `chatgpt-account-id`, `Accept: text/event-stream`, `OpenAI-Beta: responses=experimental`, `originator: codex_cli_rs`. (5) **Reasoning effort clamping**: per-model rules (e.g. `gpt-5.4: minimal→low`, `gpt-5.1-codex-mini: high/xhigh→medium`) applied before send. (6) **SSE parser**: accumulates `response.output_text.delta` events, falls back to walking `response.output[]` on `response.completed` when no deltas arrive. (7) **Router integration**: new `_call_codex()` dispatch in `_call_with_retries`, `_get_codex_adapter()` lazy singleton, `_check_codex()` health check wired into `health_check()`. (8) **Config**: `codex` entry in `_PROVIDER_DEFAULTS`, auto-detection at startup — if `~/.codex/auth.json` exists with `auth_mode: chatgpt`, provider auto-enables with `gpt-5.4` default. 28 new tests (JWT helpers, effort clamping, auth loading, message formatting). Integration doc at `CODEX_INTEGRATION.md`. ⚠️ ToS caveat: ChatGPT subscription is sold as UI, not API — grey area, use at own risk. New: `core/codex_adapter.py`, `tests/test_core/test_codex_adapter.py`. Modified: `core/router.py`, `core/config.py`, `config.yaml`, `config.demo.yaml`. |
| 2026-04-10 | **Stop scheduled tasks bug + foreign key fix + 4 new tests** — Two bugs in scheduler. (1) **Cannot stop running scheduled tasks**: `disable_schedule` and `delete_schedule` only removed the cron job from APScheduler but did NOT cancel the in-flight execution. A long-running task (e.g., 30-min reply grind) would keep running until done. Fix: track in-flight tasks in `_running_tasks: dict[str, asyncio.Task]`, wrap executor in `asyncio.create_task()` so it can be cancelled. `delete_schedule` and `disable_schedule` now cancel the running task before removing. New `stop_running()` cancels without disabling, `stop_all_running()` cancels everything. New tool actions: `stop` (cancel one running task) and `stop_all` (cancel all running tasks) on `schedule_list`. (2) **Foreign key constraint failure on delete**: `DELETE FROM scheduled_tasks` failed if the schedule had any rows in `schedule_runs` (FK constraint). Fix: delete child rows first. (3) **CancelledError handler** in `_execute_schedule` properly logs cancellation and updates status to `cancelled` in DB. New tests: `test_stop_running_when_idle`, `test_stop_running_cancels_in_flight`, `test_stop_all_running`, `test_delete_cancels_running`. All 26 scheduler tests pass. Modified: `core/scheduler.py`, `tools/scheduling/list_tool.py`, `tests/test_core/test_scheduler.py`. |
| 2026-04-08 | **Operational fixes + 33 new tests** — (1) **OpenRouter double-prefix fix** (`core/router.py`): model IDs like `openrouter/x-ai/grok-4.1-fast` were being prefixed again as `openrouter/openrouter/x-ai/grok-4.1-fast`, burning 3 retries per call before cascading. Fix: `removeprefix("openrouter/")` before adding the litellm prefix. (2) **Knowledge disk cleanup** (`core/knowledge_consolidator.py`): consolidator now cleans orphaned markdown files in `knowledge/learned/` that have no corresponding DB index entries. Previously only pruned DB rows while disk files grew unbounded (284+ files). Also cleans empty directories left behind. (3) **33 new tests**: `test_action_queue.py` (7 tests — priority ordering, serialization, preemption signal, timeout, slot properties), `test_context_compressor_tiers.py` (11 tests — circuit breaker trip/reset, microcompact clearing/skipping/threshold, token estimation), `test_deferred_tools.py` (10 tests — tier enum, override, registry methods, discovery, catalog), `test_brief_tool.py` (5 tests — properties, basic brief, details, rate limiting, actionable bypass). All 33 pass. |
| 2026-04-07 | **Agent OS — protocol, profiles, governance** — Three foundational pieces for making EloPhanto the agent OS that others build on. (1) **Agent Protocol v1.0** (`AGENT_PROTOCOL.md`): formal specification for agent-to-agent and client-to-agent communication. Covers WebSocket + HTTP transport, Bearer token auth, session lifecycle, 10 message types, 33 event types, capability discovery. New `CAPABILITY_REQUEST`/`CAPABILITY_RESPONSE` message types in `core/protocol.py`. New `GET /capabilities` HTTP endpoint on gateway returns agent tools, skills, providers, channels, features as JSON. (2) **Distribution profiles** (`profiles/`): 5 predefined configurations — developer (coding, TDD, debugging), marketer (social, publishing, affiliate), researcher (browser, docs, context store), trader (payments, Solana, prospecting), minimal (core tools only). `--profile` flag on `elophanto gateway` and `elophanto chat`. Profile overrides merge into config at load time. (3) **Contributor ecosystem**: `.github/ISSUE_TEMPLATE/` (bug report, feature request, skill proposal), `.github/PULL_REQUEST_TEMPLATE.md`, `GOVERNANCE.md` (BDFL model + RFC process), `SECURITY.md` (vulnerability reporting), `docs/RFC-TEMPLATE.md`. New: `AGENT_PROTOCOL.md`, `docs/62-AGENT-OS.md`, `profiles/*.yaml`, `profiles/README.md`, `GOVERNANCE.md`, `SECURITY.md`, `docs/RFC-TEMPLATE.md`, `.github/` templates. Modified: `core/protocol.py`, `core/gateway.py`, `core/config.py`, `cli/gateway_cmd.py`, `cli/chat_cmd.py`. |
| 2026-04-03 | **Video Meeting Agent (PikaStream)** — New skill to join Google Meet and Zoom calls as a real-time AI avatar via [PikaStreaming](https://pika.me/dev/). Features: AI-generated avatar (OpenAI image models) or custom image, voice cloning from user recordings (10s-5min audio), context-aware conversation (synthesizes workspace data — identity, memory, daily logs, known people — into a meeting reference card), automatic billing ($0.50/min, checks balance before joining, generates payment link if needed), post-meeting notes retrieval. Trigger: drop a Meet/Zoom link in conversation. First-time setup prompts for avatar and voice. Voice clones expire after 7 days of non-use (agent warns at 6 days). Adapted from [Pika-Labs/Pika-Skills](https://github.com/Pika-Labs/Pika-Skills) (Apache 2.0). New: `skills/pikastream-video-meeting/` (SKILL.md + scripts/pikastreaming_videomeeting.py + assets/placeholder-avatar.jpg + requirements.txt), `docs/61-VIDEO-MEETING.md`. |
| 2026-03-30 | **Action Queue — serialized task execution** — Fixes critical concurrency bug where scheduled tasks and manual user messages competed for the browser simultaneously. New `ActionQueue` (`core/action_queue.py`) with `asyncio.Lock` serializes all task execution — only one task runs at a time. `TaskPriority` enum: USER (0, highest) > HEARTBEAT (1) > SCHEDULED (2) > MIND (3) > GOAL (4). Higher-priority tasks signal preemption to lower-priority holders via `preempted` event. `agent.run_session()` acquires with USER priority, `_execute_scheduled_task()` with SCHEDULED, autonomous mind's `_think()` and `_run_autoloop_cycle()` with MIND. Added `notify_user_interaction()` / `notify_task_complete()` to `TaskScheduler` (same pattern as AutonomousMind/HeartbeatEngine) — scheduler now pauses when user sends a message and resumes after. New: `core/action_queue.py`, `docs/60-ACTION-QUEUE.md`. Modified: `core/agent.py`, `core/scheduler.py`, `core/autonomous_mind.py`. |
| 2026-03-30 | **Context Intelligence — 6 efficiency improvements** — (1) **Deferred tool loading** (`core/registry.py`, `tools/system/discover_tool.py`): `ToolTier` enum (CORE/PROFILE/DEFERRED) on `BaseTool`. Registry gains `get_tools_for_context()`, `get_deferred_catalog()`, `discover_tools()`. Only ~30 tools loaded per LLM call instead of 163+. New `tool_discover` tool (tier CORE) for on-demand access — matched tools added to session-level `_activated_tools` set. Deferred catalog injected as `<deferred_tools>` XML section in system prompt. ~60-70% token savings per call. (2) **Microcompact + circuit breaker** (`core/context_compressor.py`): Three-tier thresholds — 70% microcompact (clear old tool results, no LLM call), 85% smart compact (LLM summarization), 95% emergency trim (drop oldest). `CompactionCircuitBreaker` class tracks consecutive failures, trips after 3, prevents infinite retry loops. `tiered_compress()` orchestrates all three tiers. (3) **Knowledge consolidation** (`core/knowledge_consolidator.py`): `KnowledgeConsolidator` class — `_prune_stale()` removes entries >90 days old with no access, `_merge_duplicates()` deduplicates by file_path+heading, `_enforce_cap()` removes oldest entries above 500 limit. Runs during autonomous mind maintenance phase (24h cooldown). New `metadata` table in database. (4) **BriefTool** (`tools/communication/brief_tool.py`): `agent_brief` tool for proactive communication — 3 priority levels (info/warning/actionable), rate-limited (3/hour, actionable bypasses), broadcasts via gateway events. (5) **Verification agent prompts** (`core/swarm.py`): `_get_failure_modes()` returns agent-type-specific failure patterns and recovery instructions. Coding agents get import/type/test/lint patterns. Browser agents get element/iframe/stale DOM patterns. Injected into enriched prompts before security constraints. (6) **Coordinator synthesis** (`core/swarm.py`): `_synthesize_results()` method uses LLM to identify conflicts, gaps, and actionable findings across multiple completed agents. Anti-pattern guard forbids lazy delegation. Runs automatically when 2+ agents complete in same monitor tick. New: `tools/system/discover_tool.py`, `core/knowledge_consolidator.py`, `tools/communication/brief_tool.py`, `tools/communication/__init__.py`, `docs/59-CONTEXT-INTELLIGENCE.md`. Modified: `tools/base.py`, `core/registry.py`, `core/agent.py`, `core/planner.py`, `core/context_compressor.py`, `core/database.py`, `core/autonomous_mind.py`, `core/swarm.py`. |
| 2026-03-30 | **Provider/model display in terminal** — Provider and model name now visible in 4 places: (1) Dashboard AGENT panel shows `via provider/model` below current tool. (2) Dashboard chat response header shows `EloPhanto  provider/model`. (3) CLI response panel subtitle shows `provider/model · $cost · time`. (4) CLI stats bar shows `provider/model` with accent color. Provider/model data now included in gateway `response_message` protocol. Modified: `cli/chat_cmd.py`, `cli/dashboard/app.py`, `core/protocol.py`, `core/gateway.py`. |
| 2026-03-27 | **Instinct-based learning system** — Upgrade to the learning engine with 5 new capabilities. (1) **Atomic instincts** (`core/instinct.py`): `Instinct` dataclass with confidence scoring (0.3→0.9), project-scoped storage via git remote hash, fuzzy trigger matching for merge-or-create, auto-promote to global at 0.7 confidence. Stored in `data/instincts/{project,global}/`. (2) **Quality-gated extraction** (`core/learner.py`): instinct extraction alongside existing lesson extraction. LLM call extracts 0-3 atomic instincts per task (trigger→action pairs). Injection scanning before persistence. Merges with existing instincts (bumps confidence) instead of duplicating. (3) **Pre-tool guards** (`core/executor.py`): 4 guard patterns that run before tool execution — blocks API keys/secrets in file_write/file_patch content, warns before git push and npm publish. Pattern-based with block/warn actions. (4) **Instinct evolution** (`core/instinct_evolver.py`): clusters high-confidence instincts by tag, generates SKILL.md files via LLM, writes provenance metadata. Evolution candidates: confidence ≥ 0.9 + 5+ observations. (5) **Provenance tracking**: every auto-generated instinct/skill gets `.provenance.json` (source, confidence, evidence count, origin IDs). Enables cleanup and audit trail. New: `core/instinct.py`, `core/instinct_evolver.py`, `docs/58-INSTINCT-LEARNING.md`. Modified: `core/learner.py`, `core/executor.py`, `core/agent.py`. |
| 2026-03-27 | **Software engineering skills — systematic-debugging, writing-plans, verification-before-completion** — Three production-grade skills adapted from [obra/superpowers](https://github.com/obra/superpowers). (1) **systematic-debugging**: 4-phase debugging (root cause investigation → pattern analysis → hypothesis testing → implementation). Includes supporting reference docs: `root-cause-tracing.md` (backward call stack tracing), `defense-in-depth.md` (multi-layer validation after root cause fix), `condition-based-waiting.md` (replace arbitrary timeouts with condition polling), `find-polluter.sh` (binary search script for test pollution). Iron law: no fixes without root cause investigation. 3+ failed fixes = question architecture. (2) **writing-plans**: Comprehensive implementation plans with exact code, exact file paths, exact commands. Bite-sized 2-5 minute tasks. No placeholders ever ("TBD", "similar to Task N", "add error handling"). Includes self-review checklist (spec coverage, placeholder scan, type consistency). (3) **verification-before-completion**: Evidence before claims, always. The gate function: identify verification command → run it fresh → read full output → verify claim → only then make the claim. Covers tests, builds, requirements, agent delegation. Rationalization prevention table. New: `skills/systematic-debugging/` (SKILL.md + 4 reference docs + find-polluter.sh), `skills/writing-plans/SKILL.md`, `skills/verification-before-completion/SKILL.md`. |
| 2026-03-27 | **Self-update command** — `elophanto update` pulls the latest code from GitHub, reinstalls Python dependencies (uv or pip), rebuilds the browser bridge (npm), and restores any stashed local changes. `elophanto update --check` shows how many commits behind without installing. Handles: git fetch + ff-only pull, auto-stash/restore of local changes, uv with pip fallback, bridge rebuild. New: `cli/update_cmd.py`. Modified: `cli/main.py`. |
| 2026-03-27 | **Coding improvements — fuzzy patching, loop detection, shell safety, path-aware parallelization, TDD skill** — Five improvements to coding task quality. (1) **Fuzzy-match file patching**: new `file_patch` tool with two-strategy fuzzy matching (normalized whitespace + line-level SequenceMatcher). Handles indentation changes, minor whitespace differences. Returns unified diff. Exact match tried first, fuzzy as fallback. (2) **File read loop detection**: `file_read` tracks consecutive identical reads. Warns at 3, blocks at 4+. Resets when any other tool is called. Prevents infinite re-read loops. (3) **Dangerous command patterns**: 10 regex patterns for high-risk shell commands (`rm -rf`, `git reset --hard`, `dd of=/dev/`, `mkfs`, `chmod 777 /`, fork bombs, `sudo rm`, `sed -i` on root). Logged as warnings for audit trail. (4) **Path-aware tool parallelization**: `_group_tool_calls()` now checks file paths for write conflicts. Two `file_write` calls to the same path run sequentially; writes to different paths can parallelize with reads. (5) **TDD skill**: `test-driven-development` skill enforcing Red-Green-Refactor cycle with strict rules (test before code, one test at a time, run after every change). New: `tools/system/filesystem.py` (`FilePatchTool`), `skills/test-driven-development/SKILL.md`. Modified: `tools/system/filesystem.py`, `tools/system/shell.py`, `core/agent.py`, `core/executor.py`, `core/registry.py`. |
| 2026-03-27 | **HuggingFace as 6th LLM provider** — Added HuggingFace Inference Providers (`https://router.huggingface.co/v1`) as a new LLM provider. OpenAI-compatible API, uses `HF_TOKEN` for auth. Access open models: Qwen/Qwen3.5-397B-A17B, deepseek-ai/DeepSeek-V3.2, zai-org/GLM-5, moonshotai/Kimi-K2.5, XiaomiMiMo/MiMo-V2-Flash, and more. Full routing support: per-task model mapping, provider priority, health check. Environment variable override via `HF_TOKEN`. Routed through litellm like OpenRouter/OpenAI. Modified: `core/router.py`, `core/config.py`, `config.yaml`, `config.demo.yaml`. |
| 2026-03-27 | **G0DM0D3 v2 — 5-layer godmode rewrite** — Complete godmode rewrite with proper architecture. 5 composable layers: (1) **Model-specific jailbreak templates**: per-family directives (Grok, DeepSeek, GLM, Ollama, uncensored models) instead of one-size-fits-all theatrical prompt. Claude/GPT use base directive only since family-specific jailbreaks get patched. (2) **AutoTune**: unchanged, 5 context profiles + godmode boost. (3) **Multi-model racing with less-filtered preference**: models known to be less filtered (Grok, DeepSeek, Llama, Mistral, Qwen) get +5 scoring bonus. Racing now tracks model name alongside provider for scoring. (4) **Parseltongue**: 7 input obfuscation techniques (leetspeak, unicode homoglyphs, zero-width joiners, mixed case, semantic synonyms, spaced, base64) for trigger word bypass. 35 trigger words. `obfuscate_query()` and `generate_variants()` functions. (5) **Prompt injection scanning**: `scan_context_content()` detects 9 injection patterns (ignore instructions, system prompt override, hidden divs, credential exfil) + invisible unicode chars. Returns blocked message if suspicious. New `test` action on `godmode_activate` tool: sends canary query to all providers, scores compliance, reports which models work. Modified: `core/godmode.py`, `core/agent.py`, `tools/system/godmode_tool.py`. |
| 2026-03-27 | **Launch & Growth Skills (levelsio MAKE)** — Three new skills adapted from Pieter Levels' Bootstrapper's Handbook. (1) **`product-launch`**: Multi-platform launch playbook — Product Hunt (midnight PST timing, tagline craft, animated GIF thumbnails, maker comment format, engagement rules), Hacker News ("Show HN:" humble format, surviving harsh feedback, zeitgeist awareness, 5-upvote threshold), Reddit (subreddit selection, traffic preparation, hivemind dynamics), BetaList (early adopters first). Cross-platform timeline: BetaList → PH → HN → Reddit → Press. Post-launch retention strategy. (2) **`press-outreach`**: Find specific journalists by niche (not tips@ emails), personalized 5-sentence pitch structure, timing (before/during/after launch), follow-up cadence, narrative control. References `web_search`, `email_send`, `prospect_search`. (3) **`landing-page-launch`**: Pre-launch validation — landing page with contextual email capture (not "subscribe to newsletter"), Stripe pre-orders to validate willingness to pay, build-in-public strategy, signal measurement (0 vs 50 vs 200+ signups). New: `skills/product-launch/SKILL.md`, `skills/press-outreach/SKILL.md`, `skills/landing-page-launch/SKILL.md`. |
| 2026-03-27 | **Browser Bridge — iframe element extraction + content editor detection** — Two improvements to the Node.js browser bridge (`bridge/browser/src/browser-agent.ts`), adapted from an EKO-based chrome extension. (1) **iframe element extraction**: `getInteractiveElements()` now enumerates all same-origin iframes, extracts interactive elements (buttons, inputs, links, etc.), calculates absolute page coordinates by adding iframe bounding rect offsets, and appends an `[IFRAME ELEMENTS]` section with `click_at_coordinates(x, y)` instructions. Cross-origin iframes are gracefully skipped. The LLM can now interact with iframe content without knowing about frame boundaries. (2) **Content editor detection**: `typeIntoElementByIndex()` now detects CodeMirror 5 (`.CodeMirror` → `cm.setValue()`), CodeMirror 6 (`.cm-editor` → `view.dispatch()`), Monaco Editor (`.monaco-editor` → `editor.setValue()`), and Ace Editor (`.ace_editor` → `editor.setValue()`) before falling back to `fill()`/`execCommand`. Uses native editor APIs directly so typed text integrates correctly with editor state. Bridge rebuilt. Modified: `bridge/browser/src/browser-agent.ts`, `bridge/browser/dist/server.js`. |
| 2026-03-26 | **G0DM0D3 — Pliny's Godmode** — Inference-time capability unlocking adapted from [elder-plinius/G0DM0D3](https://github.com/elder-plinius/G0DM0D3) (AGPL-3.0). Four composable layers that activate simultaneously on trigger phrases like "elophanto, trigger plinys godmode". (1) **Godmode system prompt**: replaces normal planner prompt with unrestricted "liberated intelligence" framing — identity reframing, 10+ forbidden-phrase blacklist (strips "I cannot", "As an AI", etc.), anti-hedge directive, depth directive (comprehensive 500+ word responses with real code/numbers), competitive framing. (2) **AutoTune**: context-adaptive sampling parameters — regex-based detection of 5 context types (code/creative/analytical/conversational/chaotic) with per-context temperature/top_p/penalties profiles, plus godmode boost (+0.1 temp, +0.15 presence penalty). (3) **Multi-model racing**: fires prompt to all healthy providers in parallel via `race_providers()`, early-exit after 3+ responses + 5s grace, scores each response 0-100 on length (0-25), structure (0-20), anti-refusal (0-25), directness (0-15), relevance (0-15), picks highest scorer. (4) **STM (Semantic Transformation Modules)**: post-processing pipeline — hedge reducer (strips "I think", "maybe", "perhaps"), direct mode (strips "Sure,", "Of course," preambles), casual mode (formal→natural language). Per-session state via `Session.metadata["godmode"]`. New `godmode_activate` tool (on/off/status, MODERATE). Safety: godmode only affects LLM prompt/parameters — does NOT bypass EloPhanto's permission system, approval gates, spending limits, or protected files. New: `core/godmode.py`, `tools/system/godmode_tool.py`, `docs/57-GODMODE.md`. Modified: `core/agent.py`, `core/router.py` (`get_model_for_provider()`), `core/registry.py`. |
| 2026-03-25 | **Goal Dreaming + Goal Deletion + Mind Safety** — Three improvements to the goal system and autonomous mind. (1) **Goal deletion**: `goal_manage` tool gains `delete` (permanently removes a single goal + checkpoints) and `delete_all` (wipes every goal) actions. New `GoalManager.delete_goal()` and `delete_all_goals()` methods — actual `DELETE FROM` SQL, not status changes. Previously goals could only be paused/cancelled (rows remained in DB). (2) **Goal dreaming**: New `goal_dream` tool (SAFE permission). Structured goal ideation: reviews all agent capabilities (156+ tools grouped by category), identity (purpose, capabilities), current/completed goals, then asks the LLM to generate 3-5 candidates with feasibility/value/cost/risk scores and a recommendation. Does NOT auto-create — returns candidates for user review. Supports `focus` parameter (revenue, growth, capability, content, or custom). The autonomous mind uses the same 4-step dream process (discover → generate → critique → decide) when no active goals exist, but auto-creates via `goal_create`. (3) **Mind paused-goal safety**: Autonomous mind system prompt now explicitly forbids resuming or working on paused goals: "NEVER resume, modify, or work on PAUSED goals. The owner paused them for a reason." Previously the mind saw `[GOAL-PAUSED]` entries and could decide to resume them. New: `tools/goals/dream_tool.py`. Modified: `tools/goals/manage_tool.py`, `core/goal_manager.py`, `core/autonomous_mind.py`, `core/registry.py`, `core/agent.py`. |
| 2026-03-24 | **Content Monetization — Publishing + Affiliate Marketing** — Two new tool groups (6 tools) that turn EloPhanto into a revenue-generating agent. **Publishing tools** (`tools/publishing/`): `youtube_upload` (video → YouTube Studio via browser bridge, multi-step wizard automation, visibility control), `twitter_post` (text + media → X with fallback CSS selectors per MoneyPrinterV2 pattern), `tiktok_upload` (video → TikTok creator center). All use pre-authenticated Chrome profiles — no Selenium, no login flows. DESTRUCTIVE permission level, approval required before any public post. **Affiliate marketing tools** (`tools/affiliate/`): `affiliate_scrape` (Amazon product extraction with platform-specific JS + generic OpenGraph fallback), `affiliate_pitch` (LLM-generated marketing copy per platform with tone/length control, SAFE permission), `affiliate_campaign` (create/track/list campaigns, links to publishing_log). Two new DB tables: `publishing_log` (tracks every publish with platform, URL, status, campaign link) and `affiliate_campaigns` (product data, pitches per platform, post counts). All tools in `monetization` group. Dependency injection via `_inject_monetization_deps()` — browser_manager for publishing + scraping, router for pitch LLM calls, db for tracking. Inspired by [FujiwaraChoki/MoneyPrinterV2](https://github.com/FujiwaraChoki/MoneyPrinterV2). New: `tools/publishing/{youtube,twitter,tiktok}_tool.py`, `tools/affiliate/{scrape,pitch,campaign}_tool.py`, `docs/56-CONTENT-MONETIZATION.md`. Modified: `core/database.py`, `core/registry.py`, `core/agent.py`. |
| 2026-03-24 | **litellm supply chain attack — NOT affected** — litellm versions 1.82.7 and 1.82.8 were compromised with a malicious `.pth` file (`litellm_init.pth`) that exfiltrates environment variables, SSH keys, and cloud credentials on import. EloPhanto pins litellm 1.81.13 in `uv.lock` — well below the compromised range. No malicious files found. Added `!=1.82.7,!=1.82.8` version exclusions in `pyproject.toml` to prevent accidental upgrades. See [BerriAI/litellm#24512](https://github.com/BerriAI/litellm/issues/24512). |
| 2026-03-19 | **Session Hardening + User Modeling** — Four session improvements completing doc 55. (1) **Context compression** (`core/context_compressor.py`): LLM-based mid-conversation summarization. When messages exceed 50% of context window, middle turns are replaced with a dense summary via cheap model. Protects first 3 + last 4 turns. `_fix_orphaned_tool_calls()` inserts stub results for tool calls split by compression. Integrated into agent planning loop. (2) **Memory injection scanning**: `scan_for_injection()` from `core/injection_guard.py` now applied at all persistence boundaries — `core/learner.py:_write_lesson()` (scan + PII redact before write), `tools/knowledge/writer.py:execute()` (scan content, reject if flagged), `core/agent.py` directive auto-storage (scan before knowledge_write). (3) **User modeling** (`core/user_model.py`): `UserProfile` dataclass (user_id, channel, display_name, role, expertise, preferences, observations, interaction_count). `UserProfileManager` — `get_or_create()`, `build_user_context()` (XML for system prompt), `observe_task()` (fire-and-forget LLM extraction after task completion). Cost-efficient: extracts on first 3 interactions then every 5th. Injection scanning on observations. New `user_profiles` table in `core/database.py` (composite PK user_id+channel). New `user_profile_view` tool (SAFE) in `tools/user/profile_tool.py`. `build_system_prompt()` gains `user_context` parameter, injected after identity section. (4) **Skill capture nudge**: existing nudge infrastructure in `core/planner.py` wired up — nudge injected as temporary user message every 15 turns in the agent loop. **Security hardening** (from RLM security audit): gateway RBAC (`_OWNER_ONLY_COMMANDS` for `vault_set`/`config_update`), session LRU eviction (max 200 cached sessions), HMAC-SHA-256 fingerprint (replaced plain SHA-256), planner sensitive services blocklist expanded. **Setup.sh fix**: `_spin()` helper with animated braille spinner, error capture, fallback to `uv pip install -e '.'` if `uv sync` fails, Python 3.14+ warning. New: `core/context_compressor.py`, `core/user_model.py`, `tools/user/profile_tool.py`, `docs/55-SESSION-HARDENING.md`. Modified: `core/agent.py`, `core/database.py`, `core/planner.py`, `core/registry.py`, `core/learner.py`, `tools/knowledge/writer.py`, `core/gateway.py`, `core/session.py`, `core/fingerprint.py`, `setup.sh`. |
| 2026-03-17 | **RLM (Recursive Language Models) — Phase 1 + Phase 2** — Inference-time architecture where the agent calls itself on focused context slices, breaking the context window ceiling. **Phase 1 (RLM-Lite)**: `agent_call` added to the code execution sandbox RPC. The agent writes Python scripts that invoke sub-instances of itself on focused sub-problems — classify files with a cheap model, deep-analyze with a strong model, aggregate results. Routed through the LLM router with model hints (`fast`→simple, `strong`→planning, or literal model names). Safety: 3-level recursion depth limit (`MAX_AGENT_CALL_DEPTH=3`), 20 agent_calls/session budget (`MAX_AGENT_CALLS=20`), 50 total tool calls/session, cost cap inheritance from parent session, 5-minute sandbox timeout covers all sub-calls. **Phase 2 (Context-as-Variable)**: New `ContextStore` class (`core/context_store.py`) — indexed, queryable, sliceable context backed by SQLite + sqlite-vec embeddings. Self-creating schema (3 tables: `context_stores`, `context_sources`, `context_chunks` + vec table). Heading-aware chunking (splits by markdown headings, falls back to paragraph/size-based splitting with overlap). Hybrid search: semantic (sqlite-vec KNN) + keyword (LIKE) with scoring. 5 new tools: `context_ingest` (MODERATE — add files/text to a store), `context_query` (SAFE — semantic search), `context_slice` (SAFE — exact content by source + line range), `context_index` (SAFE — table of contents), `context_transform` (SAFE — filter/group/stats/sources). All context tools also available in the sandbox via RPC stubs. 80 new tests covering both phases. Modified: `tools/self_dev/rpc_server.py` (agent_call dispatch + context tools in allowlist), `tools/self_dev/stub_generator.py` (stubs for agent_call + 5 context tools), `tools/self_dev/execute_code.py` (router injection), `core/agent.py` (ContextStore init + dependency injection + self_dev deps fix), `core/registry.py` (5 context tools registered). New: `core/context_store.py`, `tools/context/context_tools.py`, `tests/test_tools/test_rlm.py`. See [docs/54-RLM.md](docs/54-RLM.md). |
| 2026-03-16 | **Web search via Search.sh + vault CLI key-value mode** — Two new tools (`web_search`, `web_extract`) provide structured web search and content extraction via [Search.sh](https://search.sh) API, replacing slow browser-based Google searches for research tasks. `web_search` supports `fast` mode (3-8s, single search + AI answer) and `deep` mode (15-30s, generates sub-queries, parallel search, page extraction, cross-referencing). Returns structured JSON: AI-synthesized answer, ranked sources with titles/URLs/snippets, citations with attribution, confidence score (0-1), related queries. `web_extract` pulls clean text from up to 10 URLs (scripts/nav/footer removed, max 5000 chars per page). Both tools use vault-stored API key (`search_sh_api_key`), are in the `data` tool group with `SAFE` permission level, and are registered as parallel-safe. New `skills/search-sh/SKILL.md` with trigger keywords. **Bug fix**: `_get_api_key()` in both tools was calling `await self._vault.get()` but `Vault.get()` is synchronous — the `TypeError` was silently caught by `except Exception`, making every search fail with "API key not configured" even when the key was stored. Same bug class as the 2026-03-06 commune/deployment vault fix. Removed `await`, changed methods from `async` to sync. **CLI vault set**: `elophanto vault set KEY VALUE` now works for simple key-value pairs (API keys, tokens). Previously `vault set` only accepted a domain argument and prompted interactively for email/username/password. The second positional argument triggers direct storage mode; omitting it preserves the interactive credential prompt. New files: `tools/data/web_search.py`, `skills/search-sh/SKILL.md`, `docs/53-WEB-SEARCH.md`. Modified: `core/registry.py` (tool registration), `core/agent.py` (vault injection + parallel-safe), `cli/vault_cmd.py` (key-value mode). |
| 2026-03-15 | **Planner BUILD enforcement — prevent empty directory "Task complete" failures** — Three additions to the system prompt in `core/planner.py` that fix the agent's most common failure mode when asked to build web projects. (1) **`<building_projects>` section**: 6-step mandatory workflow — generate design system (if ui-ux-pro-max skill available), create directory, write EVERY file with `file_write`, scaffold frameworks with `shell_execute`, verify the build, report what was built. Explicit: "Creating a directory and responding 'Task complete' is a FAILURE." (2) **`<output_format>` enhancement**: NEVER respond with just "Task complete." or "Done." — must include what was created, where it lives, and how to run it. (3) **`<task_completion>` BUILD check**: "Creating a directory is NOT completion. You must have written actual code files." |
| 2026-03-15 | **Deployment section removed from planner** — `_TOOL_DEPLOYMENT` (47 lines) removed from `core/planner.py`. Deployment guidance is now handled on-demand by the `web-deployment` skill (`skills/web-deployment/SKILL.md`) which is matched and injected when relevant. Reduces base system prompt size and keeps deployment instructions current in one place. `deployment_enabled` parameter removed from `build_system_prompt()`. |
| 2026-03-15 | **Payment requests — receive money on-chain** — New `payment_request` tool lets the agent create payment requests for its Solana wallet. Creates a request with amount, token (default USDC), memo, and TTL. Returns a payment link/address for the payer. Background tracker (`core/payments/request_tracker.py`) polls for matching on-chain transactions and marks requests as paid. New `payment_requests` table in database schema. Integrated into `PaymentsManager` with lazy-loaded `PaymentRequestTracker`. New `docs/51-PAYMENT-REQUESTS.md`. |
| 2026-03-15 | **Prospecting tools — lead search, evaluation, and outreach** — Four new tools for autonomous revenue generation: `prospect_search` (find leads matching criteria), `prospect_evaluate` (score and qualify leads), `prospect_outreach` (track outreach attempts), `prospect_status` (pipeline overview). Database-backed with `prospects` and `prospect_outreach` tables. Dependency-injected via `_inject_prospecting_deps()`. New `docs/52-PROSPECTING.md`. |
| 2026-03-15 | **Swarm skill injection** — Swarmed agents now receive matching skills. `SwarmManager._pull_skill_context()` matches the task description against installed skills and injects their instructions into the swarm agent's prompt. Previously swarm agents had no access to skills like `ui-ux-pro-max` or `web-deployment`, causing them to miss design systems and deployment workflows. `_skill_manager` injected from `Agent` during init. |
| 2026-03-15 | **Web deployment skill — Supabase credential flow clarification** — Updated `skills/web-deployment/SKILL.md` and `docs/31-WEB-DEPLOYMENT.md` to clarify the Supabase authentication model. The user only stores one token (access token in vault). Per-project keys (`anon_key`, `service_role_key`, `DATABASE_URL`) are auto-generated by `create_database` and returned in the tool result — the agent should never ask the user for these. Added a token comparison table to the docs. |
| 2026-03-15 | **shadcn/ui skill expanded** — `skills/shadcn/SKILL.md` expanded with component architecture section (file structure, `cn()` utility, composition patterns), dark mode setup, and additional component examples. Fixed frontmatter to use `name`/`description` format. |
| 2026-03-14 | **Terminal dashboard — brand color alignment + animated thinking indicator** — Two polish passes on `cli/dashboard/app.py`. (1) **Brand color alignment**: replaced the placeholder monochrome palette with exact hex approximations of the web app's `globals.css` dark-mode oklch tokens. Background `#0d0e14` (oklch 0.095 0.005 260 — deep cool charcoal with blue-purple tint, not plain black), sidebar `#111218` (oklch 0.12 0.006 260 — card surface), header/input bar `#161820` (oklch 0.16 0.006 260 — raised surface), borders `#1e2030` (oklch(1 0 0 / 8%) approximation), text primary `#dbd7cc` (warm off-white, oklch 0.88 0.008 80), text muted `#71728a` (cool grey, oklch 0.5 0.01 260). Accent purple `#8b5cf6` / `#a78bfa` unchanged. Header `◆` glyph coloured electric purple so the logo mark stands out. (2) **Animated thinking indicator**: `_ThinkingFrames = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"` braille spinner — a hidden `Static#thinking` widget sits between the event feed and the input bar. When a message is sent, `_animate_thinking()` (a `@work(exclusive=True, group="thinking-anim")` Textual worker) shows the widget, cycles through frames at 100ms intervals rendering `[#8b5cf6]⠹[/] [#71728a]thinking...[/]`, and auto-hides when `_awaiting_response` becomes `False` (response arrived, timeout, or Ctrl+X cancel). CSS uses `display: none` / `display: block` toggle via `.active` class. |
| 2026-03-14 | **AgentCash onboarding skill** — Installed `skills/agentcash-onboarding/SKILL.md`. AgentCash is a micropayments platform that gives the agent pay-per-call access to premium APIs via the x402 protocol. The onboarding skill guides through wallet creation and optional invite-code credit redemption: `npx agentcash@latest onboard [invite-code]` (one-time setup), `npx agentcash wallet info` (balance check), `npx agentcash wallet redeem <code>` (late code redemption). After onboarding, the core AgentCash skill (installed by the onboard command) handles endpoint discovery (`npx agentcash discover <origin>`) and paid API requests (`npx agentcash fetch <url>`). Deposits accepted as USDC on Base or Solana. Triggers: "set up agentcash", "onboard", "x402", "micropayments api", "invite code agentcash". Source: https://agentcash.dev/skill.md |
| 2026-03-14 | **reasoning_effort per task type** — OpenRouter and OpenAI calls now honour a per-task-type `reasoning_effort` field in routing config (`high` / `medium` / `low` / `minimal`). For OpenRouter the value is forwarded as `extra_body={"reasoning": {"effort": "..."}}` (provider-native extended thinking budget). For OpenAI it is passed as the `reasoning_effort` kwarg (o-series models). Added `reasoning_effort: str = ""` to `RoutingConfig` in `core/config.py`; threaded through `complete()` → `_call_with_retries()` → `_call_litellm()` in `core/router.py`. `config.demo.yaml` updated to show recommended values (planning=high, coding=medium, analysis=low, simple=minimal). `docs/06-LLM-ROUTING.md` Configuration section updated with `reasoning_effort` in the YAML reference. |
| 2026-03-14 | **Vision model auto-routing** — New `llm.vision_model` config key. When any message in the conversation history contains an `image_url` content block (browser screenshots, Telegram image uploads, desktop captures), the router detects this via a new `_contains_images()` helper and automatically overrides the normal provider-priority chain with the configured vision model (e.g. `openrouter/x-ai/grok-4.1-fast`). This eliminates the previous failure mode where image-containing calls reached text-only providers and burned retry budget. Set `vision_model: ""` to disable and revert to the previous behaviour (images forwarded as-is, text-only providers strip them). Added `vision_model: str = ""` to `LLMConfig` in `core/config.py`. |
| 2026-03-14 | **Z.ai image stripping — fix error 1210** — GLM APIs return error 1210 "Invalid API parameter" when `content` is a list of typed blocks (e.g. `[{"type": "text", ...}, {"type": "image_url", ...}]`). `_reformat_messages()` in `core/zai_adapter.py` now detects list-typed `content` fields and collapses them to a plain string (text parts joined; image parts discarded silently). This prevents the Z.ai cascade leg from failing on any conversation that passed through a vision call. |
| 2026-03-14 | **Capability mismatch errors — instant non-retryable skip** — "No endpoints found that support image input" (hunter-alpha 404) and similar capability-mismatch responses now raise immediately without consuming the retry budget. Added patterns to the non-retryable set in `core/router.py`: `"no endpoints found"`, `"unsupported media type"`, `"does not support vision"`, `"does not support image"`. Previously these retried 3× with exponential backoff (~30s wasted per provider leg) before the cascade fell through to the next provider. |
| 2026-03-14 | **Event loop crash fix — suppress exception=None contexts** — `cli/gateway_cmd.py` installs a `call_exception_handler` monkey-patch (`_safe_ceh`) to suppress benign asyncio GC warnings. Previously it only filtered messages matching "Unclosed client session" / "Unclosed connector". Any other asyncio context with `exception=None` (e.g. "Task destroyed but pending" during prompt_toolkit teardown) printed "Unhandled exception in event loop: Exception None" and crashed the input loop. Fix: widened the filter to suppress all contexts where `ctx.get("exception") is None` — these are always informational GC-level notices with no traceback. Also fixed a mypy `[assignment]` error on the same line by adding `assignment` to the `# type: ignore` comment. |
| 2026-03-14 | **config.demo.yaml full sync to live config** — `config.demo.yaml` was out of date, missing several sections and using placeholder model names. Fully rewritten to match the live recommended setup: OpenRouter first in `provider_priority` with `hunter-alpha` for all task types, `glm-5`/`glm-4.7` for Z.ai, `grok-4.1-fast` as `vision_model`, `reasoning_effort` per task type (high/medium/low/minimal), `profile_refresh_hours: 0` in browser, `commune`, `self_learning`, `autonomous_mind`, `replicate` sections. Users can now `cp config.demo.yaml config.yaml` and fill in only their API keys. `README.md` and `docs/06-LLM-ROUTING.md` updated to point to `config.demo.yaml` as the canonical recommended setup. |
| 2026-03-13 | **Context pruning + fast overflow detection** — Two independent fixes that eliminate 40–140s of wasted LLM latency per tool call. (1) **Non-retryable context overflow** (`core/router.py`): Context-length errors (400 "exceeds context length") are now detected immediately in `_call_with_retries` and re-raised without consuming the retry budget. Previously each overflowed provider retried 3× with exponential backoff before giving up — 10–125s wasted per provider in the cascade chain. Now the cascade moves to the next provider in ~0ms. Detection covers all provider variants: "context length", "context_length_exceeded", "maximum context", "exceeds the model", "too many tokens", "input too long". (2) **Proactive tool-result truncation** (`core/agent.py`): Added Phase 3 to `_compress_browser_context`. `role=tool` messages accumulate unboundedly in the conversation history — after 10+ tool calls the context can easily reach 2M+ tokens. Now: keep the last 6 tool results at full size (agent needs them for reasoning continuity); truncate all older ones to 1500 chars with a `[truncated — old tool result]` suffix. Applied before every LLM call alongside existing screenshot/browser-element compression phases. Constants: `_KEEP_RECENT_TOOL_RESULTS = 6`, `_MAX_OLD_TOOL_RESULT_CHARS = 1500`. |
| 2026-03-13 | **Terminal CLI — input protection + mind cycle timestamps** — Fixed the most disruptive terminal UX issue: autonomous mind output clobbering in-progress user input. Root cause: `Prompt.ask()` (Rich) ran in a thread executor while mind events called `console.print()` from async handlers — both shared `sys.stdout`, so mind output printed directly on top of the user's partial input line. Fix: replaced `Prompt.ask()` in executor with `prompt_toolkit`'s `PromptSession.prompt_async()` (native asyncio, no thread) wrapped in `patch_stdout(raw=True)`. The `patch_stdout` context replaces `sys.stdout` with a proxy that intercepts all writes and redraws them above the current input line with the prompt intact. Rich's `Console.file` property evaluates `sys.stdout` at call time (not construction time), so the proxy is picked up automatically — no manual patching needed. Mind cycle Rule header now shows wall-clock time (`MIND  cycle #12 · 8 today · 14:37`) for easy correlation without log diving. New dependency: `prompt-toolkit>=3.0`. New `docs/49-TERMINAL.md`. |
| 2026-03-13 | **Learning Engine — lesson extraction, semantic memory search, KB compression** — Three mechanisms that turn completed tasks into compounding knowledge. (1) **Lesson extraction** (`core/learner.py`): after each successfully completed task, a fire-and-forget LLM call extracts 0–2 generalizable, actionable, novel lessons and writes them to `knowledge/learned/lessons/<slug>.md` (scope=learned, auto-indexed). Conservative prompt returns empty for routine tasks. Recurring slugs append a new `## Observation` section rather than overwriting — files grow richer over time. (2) **Semantic memory search** (`core/memory.py`): `store_task_memory()` now embeds `goal+summary` into a `memory_vec` sqlite-vec virtual table (same embedding model as the KB). `search_memory()` tries cosine KNN first, falls back to LIKE keyword matching when embedder is unavailable — so "check email account" retrieves "log into ProtonMail inbox" without a keyword match. (3) **KB write compression**: `knowledge_write` gains a `compress: bool` parameter. When set, `LessonExtractor.compress_content()` condenses verbose content (scraped pages, long summaries) to ~40% of original length before storage — all facts kept, filler prose removed. Fallback: original content stored if LLM call fails or returns longer text. New `LearnerConfig` in `core/config.py` (`enabled`, `compress_enabled`). Integration wired in `core/agent.py` (init, fire-and-forget extraction, embedder injection into `MemoryManager`). New `docs/48-LEARNING-ENGINE.md`. |
| 2026-03-13 | **AutoLoop — focus lock, AGENT_PROGRAM.md, fixed iteration budgets** *(design + docs)* — Three gaps in EloPhanto's autonomous loop design identified via deep review of [karpathy/autoresearch](https://github.com/karpathy/autoresearch) and addressed. (1) **Focus lock** (`data/autoloop.json`): the autonomous mind currently re-evaluates its priority stack on every wakeup, causing research sessions to drift — the agent might start an optimization experiment then switch to checking Commune mid-session. New focus lock mechanism: when `experiment_setup` is called with `autoloop=True`, the mind writes an active lock to `data/autoloop.json` and checks it before building its priority stack. While locked, the mind only runs the next experiment iteration, nothing else. Exit conditions: user interruption (existing pause mechanism), `max_iterations`, `max_hours`, or target metric achieved. New `autoloop_control` tool (`start/stop/pause/status`). (2) **`AGENT_PROGRAM.md`** — new owner-maintained versioned strategy document at project root (created by `setup.sh`). Fundamentally different from `HEARTBEAT.md` (task queue): this is the *research org code* — encoding how to approach research, what constraints apply, what "better" means, what to do when stuck. The agent reads it at the start of every AutoLoop session. The owner accumulates strategy insights over time (what has worked, domain rules, anti-patterns). Over many sessions, `AGENT_PROGRAM.md` becomes a high-quality autonomous research strategy specific to the project. (3) **Fixed iteration budget** (`budget_seconds` parameter on `experiment_setup`): autoresearch's 5-minute budget per experiment is a fairness constraint — every experiment runs for exactly the same wall-clock time so results are directly comparable. EloPhanto's existing `timeout` was soft; new `budget_seconds` enforces a hard kill (SIGTERM + SIGKILL) at exactly N seconds, written to `.experiment.json` and enforced by `experiment_run` on every iteration. Full implementation plan in new `docs/47-AUTOLOOP.md`. |\n| 2026-03-12 | **CAPTCHA fix — stale profile refresh + stronger stealth patches** — Two root causes of persistent CAPTCHA triggers eliminated. (1) **Profile copy staleness** (`core/browser_manager.py`): `_prepare_profile_copy()` previously copied the Chrome profile once and reused it forever. Anti-bot systems detect stale/mismatched session signals in old copies (cookies that don't match the session age, missing recent browsing fingerprint signals) and serve CAPTCHAs. Added `max_age_hours=8.0` parameter — if the existing copy is older than 8 hours, it is automatically deleted and re-copied from the real Chrome profile, propagating fresh cookies and session data. (2) **Missing JS stealth patches** (`bridge/browser/src/browser-agent.ts`): `applyStealthScripts()` was only patching `navigator.webdriver`, `__playwright`, basic `chrome.runtime`, and `permissions.query`. Added: `document.documentElement.removeAttribute('webdriver')` (Playwright sets this HTML attribute at the CDP level, invisible to the JS-only patch); `window.cdc_*` key deletion (ChromeDriver artifacts that bot-detect scripts specifically look for); full `chrome.app` mock with `InstallState`/`RunningState` enums; `chrome.loadTimes()` and `chrome.csi()` mocks (present in real Chrome, absent in headless — fingerprinted by Cloudflare); `navigator.maxTouchPoints = 0` (real desktop Chrome value); `navigator.languages` fallback to `['en-US', 'en']` (Playwright leaves it empty); canvas fingerprint noise via `HTMLCanvasElement.toDataURL` and `CanvasRenderingContext2D.getImageData` overrides (headless canvas produces byte-identical pixel output every run — trivially hashable). Bridge rebuilt. |\n| 2026-03-12 | **Protected config keys** — The agent can no longer disable critical subsystems by writing `false` to `config.yaml`. `core/protected.py` now defines `PROTECTED_CONFIG_KEYS` mapping four keys (`autonomous_mind.enabled`, `heartbeat.enabled`, `gateway.enabled`, `scheduler.enabled`) to their forbidden value (`false`). `check_config_content()` scans any `config.yaml` write line-by-line using a simple regex heuristic (no YAML parse overhead) and rejects the write with a clear refusal message if a protected key is set to its forbidden value. Wired into `FileWriteTool.execute()` in `tools/system/filesystem.py` — if the target filename is `config.yaml`, the content is checked before writing. If the check fails, a `ToolResult(success=False)` is returned and no write occurs. The agent is told to ask the owner to change these settings manually. Root cause: at 20:44 the autonomous mind wrote `autonomous_mind: enabled: false` to disable itself during a long session. |
| 2026-03-11 | **Remotion video design system** — Upgraded the knowledge and skill layer that drives all programmatic video creation. Previously the agent defaulted to a generic blue dark theme with fast animations; now every video automatically follows a production-grade design system derived from Vercel/Geist, GitHub Unwrapped, and Linear. (1) **`knowledge/system/remotion-video-creation.md`** rewritten with: full color palette (`#0A0A0A` background, `#EDEDED` text, 5 accent options with mood guidance — violet `#8B5CF6` default), glow spot recipe and glassmorphism card pattern ("the Linear Look"), 4 gradient backgrounds for hero scenes, Geist Sans CDN font loading with negative letter-spacing rules, scene duration minimums (5s minimum, 7–9s for feature slides — "slow down, people need to read"), `spring({ damping: 200 })` animation primitives, 15-frame stagger pattern for lists/cards, and copy-paste `GlowBackground`, `GlassCard`, `PulsingCTA` scene components. (2) **`skills/remotion-best-practices/SKILL.md`** — added "Design System — Apply Every Time" section with 9 mandatory rules checked before every video render (background color, font, letter-spacing, scene duration, glow, accent count, spring damping, stagger, glass cards). Quality checklist now enforced before rendering. |
| 2026-03-11 | **Context overflow auto-recovery** — The agent was surfacing raw LLM provider errors (`prompt is too long: 1156634 tokens > 1000000 maximum`) to the user with no recovery. Now detects context overflow from any provider (Anthropic, OpenRouter, OpenAI, Z.ai) via `_is_context_overflow_error()` matching phrases like "prompt is too long", "context_length_exceeded", "tokens >", etc. On overflow: applies `_emergency_trim_messages()` (strip all images, truncate tool results to 500 chars, keep last 20 messages with a dropped-history placeholder) then retries automatically. If retry still fails: friendly user message asking to start a new conversation. Raw provider errors no longer surface. |
| 2026-03-11 | **Proactive Engine — heartbeat standing orders + webhook endpoints** — Two new mechanisms that make EloPhanto genuinely proactive without waiting for user messages. (1) **Heartbeat Engine** (`core/heartbeat.py`): Periodically reads `HEARTBEAT.md` from the project root at a configurable interval (default 30 min). If the file has content, the agent executes it as a task. If the file is empty or missing, the cycle is skipped — zero LLM cost when idle. The agent responds `HEARTBEAT_OK` when all instructions are already completed. Conversation history is isolated (heartbeat execution doesn't pollute chat). Pauses when user sends a message, resumes when user task completes. Configurable via `heartbeat:` section (`enabled`, `file_path`, `check_interval_seconds`, `max_rounds`, `suppress_idle`). (2) **Webhook endpoints** on the gateway: `POST /hooks/wake` triggers an immediate heartbeat check (also injects event into autonomous mind), `POST /hooks/task` accepts an ad-hoc task goal and executes it asynchronously (returns `202 Accepted` immediately). Optional Bearer token auth via `webhooks.auth_token_ref` vault key. Payload validation: JSON parsing, size limit (64KB), required fields. Error responses: 400 (bad request), 401 (unauthorized), 404 (disabled/unknown hook), 413 (too large), 503 (heartbeat not running). Use cases: Gmail Pub/Sub → webhook → agent processes email, CI/CD post-deploy verification, monitoring alert response. New event types: `heartbeat_check`, `heartbeat_action`, `heartbeat_idle`, `webhook_received`, `webhook_task_started`. Integrated into agent lifecycle (init, pause, resume, shutdown) alongside autonomous mind. 29 new tests (16 heartbeat + 13 webhook). New `docs/46-PROACTIVE-ENGINE.md`. (3) **Heartbeat chat tool** (`tools/scheduling/heartbeat_tool.py`): Manage standing orders via conversation — `add`, `remove`, `list`, `clear`, `set`, `trigger`, `status` actions. Reads/writes `HEARTBEAT.md` directly so changes from chat and manual file edits stay in sync. Blank `HEARTBEAT.md` template with instructions created automatically by `setup.sh` on first install. Registered in `manifest.json`, dependency-injected via `_inject_scheduler_deps()`. 17 new tool tests. |
| 2026-03-11 | **Kimi / Moonshot AI as 5th LLM provider via Kilo Code Gateway** — Added Kimi K2.5 (native multimodal vision model) as a new LLM provider routed through the [Kilo AI Gateway](https://kilo.ai/docs/gateway). The gateway is OpenAI-compatible at `https://api.kilo.ai/api/gateway` and uses JWT tokens from [app.kilo.ai](https://app.kilo.ai). New `core/kimi_adapter.py` with automatic model name mapping — internal `kimi-k2.5` maps to `moonshotai/kimi-k2.5` on the wire. Cost tracking at Kilo Gateway rates ($0.45/$2.20 per 1M tokens). Router integration: `_infer_provider()` handles `kimi-*` prefixes, lazy singleton adapter instantiation, health check via minimal chat completion, 60s auto-recovery on failure. Init wizard step for Kimi with API key prompt and model selection. `KIMI_API_KEY` environment variable override. All routing sections support `kimi:` model entries. Updated `config.demo.yaml`, `cli/init_cmd.py`, `core/config.py`, `core/router.py`. 9 new tests (adapter + router integration). |
| 2026-03-11 | **Dashboard provider health indicators** — The startup dashboard now shows all configured providers with color-coded status instead of hiding unhealthy ones. Green dot (●) = healthy (passed health check). Yellow dot (●) = degraded (configured and enabled but health check failed — still eligible for routing, auto-recovers after 60s). Previously, providers that failed the startup health check (e.g. Z.ai returning 429 rate limit) were silently hidden, making it look like they weren't configured at all. Cloud providers are never gated by startup health checks — only Ollama is blocked when unreachable. Shared `_build_provider_parts()` helper used across all 3 dashboard locations (`cli/chat_cmd.py`, `cli/gateway_cmd.py`). |
| 2026-03-10 | **Context documents — structured self-awareness** — Created 4 curated reference documents in `knowledge/system/` that give the agent deep self-awareness of what it is, who it serves, how it looks, and how its parts connect. Inspired by [Arvid Kahl's Claude Code productivity tips](https://x.com/arvidkahl/status/2031457304328229184). (1) **capabilities.md** — Complete feature inventory rewrite: 140+ tools across 9 categories (system, browser, desktop, knowledge, self-dev, communication, payments, deployment, organization), 6 channel adapters, 4 LLM providers, 147 skills, security features. Replaces the severely outdated Feb version. (2) **icps.md** — 8 dossier-style Ideal Customer Profiles with autonomy-first framing: The Autonomous Business Operator, The Agent Economy Participant, The One-Person Company, The AI-Native Builder, The Delegator (Non-Technical), The AI-Curious Professional, The Business Leader / Executive, The Creator / Influencer. Each profile includes who they are, what they want, how EloPhanto delivers, where to reach them, and what convinces them. Messaging matrix for all 8 ICPs. (3) **styleguide.md** — Visual and brand identity: dark-first color palette (electric purple `#8b5cf6` accent), typography (Geist Mono headings, Inter body), component patterns (cards, terminal blocks, stat strips), tone of voice rules, social media style, anti-patterns. (4) **data-reference.md** — Domain model reference with 5 stacks: Communication (gateway → sessions → channels), Intelligence (skills → knowledge → tools → working memory), Agency (identity → autonomous mind → goals → task memory), Trust (vault → payments → approvals → protected files), Delegation (organization → swarm). ASCII diagrams, 25 database tables documented. All 4 documents use `scope: system` and `covers:` frontmatter for knowledge drift detection. New `docs/45-CONTEXT-DOCUMENTS.md`. |
| 2026-03-10 | **Jupiter DEX swaps on Solana** — The `crypto_swap` tool now works on Solana via Jupiter Ultra API. Swap any token pair (SOL, USDC, or any mint address) through Jupiter's aggregator with best-price routing. Flow: `GET /ultra/v1/order` → sign with solders `VersionedTransaction` → `POST /ultra/v1/execute`. Requires Jupiter API key from [portal.jup.ag](https://portal.jup.ag), stored in vault as `jupiter_api_key`. Quote support via `payment_preview` tool. Modified: `core/payments/solana_wallet.py` (added `jupiter_swap()`, `jupiter_quote()`, `supports_swap()` → `True`), `core/payments/manager.py` (routes Solana swaps through Jupiter, EVM through AgentKit). |
| 2026-03-10 | **27 Solana ecosystem skills + MCP configs** — Installed 27 skills from [awesome-solana-ai](https://github.com/solana-foundation/awesome-solana-ai) covering the full Solana AI ecosystem. **DeFi (12)**: Jupiter (swaps, DCA, perps, lending), Drift (perpetual futures), Orca (Whirlpools concentrated liquidity), Raydium (CLMM/CPMM pools, LaunchLab), Kamino (lending, leverage), Meteora (AMMs, bonding curves), PumpFun (token launches), Sanctum (liquid staking, LSTs), Lulo (yield aggregation), DFlow (spot trading, prediction markets), Ranger Finance (perps aggregation), CoinGecko (market data, OHLCV). **NFTs & Tokens (1)**: Metaplex (Core NFTs, Candy Machine, Genesis, Umi/Kit). **Infrastructure (5)**: Helius (RPC, DAS API, webhooks, gRPC), QuickNode (managed RPC, Jupiter API), Pyth (oracle price feeds), Switchboard (oracle, VRF randomness), Squads (multisig, account abstraction). **Bridges (1)**: deBridge (Solana <-> EVM cross-chain). **Development (5)**: Solana Dev Skill (official Foundation — Anchor, Pinocchio, testing), Solana Kit SDK (@solana/kit), Solana Kit Migration (web3.js v1 → kit), Pinocchio (zero-copy high-perf programs), Surfpool (dev environment, mainnet forking). **Security (1)**: VulnHunter (vulnerability detection, variant analysis). **Toolkits (2)**: Solana Agent Kit (50+ actions across 30+ protocols), Light Protocol (ZK compressed tokens). **MCP server configs** added to config.demo.yaml: Solana Developer MCP (official docs), QuickNode MCP (managed infra), DFlow MCP (trading). New `docs/44-SOLANA-ECOSYSTEM.md` with full integration roadmap. Total skills: 147 (was 120). |
| 2026-03-10 | **Solana wallet support** — Added native Solana wallet alongside existing Base/EVM wallet. Self-custody keypair auto-creates on first use, stored encrypted in vault. Supports SOL transfers and SPL token transfers (USDC). New `wallet_export` tool lets the owner retrieve private keys to import into Phantom/Solflare/MetaMask. Set `default_chain: solana` in config to use. Dependencies: `solders`, `base58`. New files: `core/payments/solana_wallet.py`, `tools/payments/wallet_export_tool.py`. |
| 2026-03-09 | **120 skills + 75 organization role templates** — Massive skill library expansion adapted from [msitarzewski/agency-agents](https://github.com/msitarzewski/agency-agents) (Apache 2.0). Converted the full agency-agents collection into EloPhanto's native SKILL.md format and organization role templates. **57 new skills** across 9 divisions: Engineering (11 — AI engineering, backend architecture, DevOps automation, frontend/mobile/senior development, data engineering, security engineering, rapid prototyping, technical writing, autonomous optimization), Design (8 — brand guardian, image prompt engineering, inclusive visuals, UI design, UX architecture/research, visual storytelling, whimsy design), Marketing (11 — app store optimization, content creation, growth hacking, Instagram/Reddit/TikTok/Twitter/WeChat/Xiaohongshu/Zhihu marketing, social media strategy), Product (4 — behavioral nudge, feedback synthesis, sprint prioritization, trend research), Project Management (5 — experiment tracking, project shepherding/management, studio operations/production), Support (6 — analytics reporting, executive summaries, finance tracking, infrastructure maintenance, legal compliance, support response), Testing (8 — accessibility auditing, API testing, evidence collection, performance benchmarking, reality checking, test analysis, tool evaluation, workflow optimization), Specialized (9 — agentic identity/trust, agent orchestration, data analytics/consolidation, LSP engineering, report distribution, sales data extraction, cultural intelligence, developer advocacy), Spatial Computing (6 — macOS Metal, terminal integration, visionOS spatial, XR cockpit/development/interface). **NEXUS strategy system as skills** (15): master strategy, 7 phase playbooks (discovery → strategy → foundation → build → hardening → launch → operate), agent activation prompts, handoff templates, 4 scenario runbooks (startup MVP, enterprise feature, marketing campaign, incident response). **75 organization role templates** in `knowledge/organization-roles/` — full persona definitions for `organization_spawn`: each preserves the complete agent personality, capabilities, workflows, deliverable templates, and success metrics from the source. Roles are namespaced by division (design-*, engineering-*, marketing-*, product-*, project-management-*, support-*, testing-*, specialized-*, spatial-*, strategy-*). When the master spawns a specialist via `organization_spawn role="design-brand-guardian"`, the role template seeds the child's identity and knowledge. Total skills: 120 (was 63). Total organization roles: 75 |
| 2026-03-09 | **VS Code extension** — IDE-integrated chat sidebar that connects to the EloPhanto gateway as another channel adapter (`vscode-extension/`). Chat with the agent from VS Code with full IDE context injection (active file, selection, diagnostics, open files, workspace root). Tool approvals via native VS Code notifications with risk classification (high-risk tools like `shell_execute`, `crypto_transfer` get warning-level prompts). Chat history panel loads past conversations from the gateway. New Chat button clears the UI without wiping agent memory (the destructive `clear` command is never exposed). Streaming responses with cursor animation, tool step indicators (auto-removed on response), crop marks on complete messages, pulse-dot connection status. Right-click context menu: Send Selection, Explain This Code, Fix This Code. Status bar shows connection state (connected/disconnected/thinking). Gateway checker on startup warns if EloPhanto isn't running — does NOT auto-launch (vault password requires manual terminal input). esbuild single-file bundle, `ws` package for Node.js WebSocket. Design matches the web dashboard aesthetic. Channel identifier: `vscode`. New `docs/43-VSCODE-EXTENSION.md` |
| 2026-03-09 | **Business launcher skill** — New `business-launcher` skill teaching the agent a 7-phase pipeline to spin up revenue-generating businesses end-to-end. **Supports all business types**: tech/SaaS, local service (horse riding, tutoring, gym), professional service (consulting, coaching), ecommerce, digital product, and content/media — each with type-specific MVP templates, launch channels, growth strategies, and revenue models. **B2B vs B2C classification** drives every downstream decision: pricing strategy (ROI-based vs undercut competitors), content style (case studies vs social proof), launch channels (LinkedIn/email outreach vs Instagram/TikTok), and growth tactics. **Phase 0 CLASSIFY**: determine business type + customer type before anything else. **Phase 1 VALIDATE**: market research via `web_search` + `browser_navigate`, competitor analysis (pricing, features, gaps), domain availability check via whois, opportunity scoring (market gap × feasibility × revenue potential, threshold ≥ 9/15). **Phase 2 PLAN**: type-specific revenue model (subscription for SaaS, booking fees for local service, per-item for ecommerce, one-time for digital product), MVP scoping, tech stack, pricing. **Phase 3 BUILD**: spawn coding swarm agents or build directly, type-specific landing pages (SaaS: headline/demo/pricing/CTA; local service: services/pricing/map/booking/testimonials; ecommerce: product grid/cart/checkout). **Phase 4 DEPLOY**: `deploy_website` with auto-provider detection, Google Business Profile setup for local businesses. **Phase 5 LAUNCH**: channels matched to business type — tech → Product Hunt, HN, dev.to, X; local service → Google Business, Yelp, Facebook local groups, Nextdoor, Instagram; B2B → LinkedIn, email outreach; ecommerce → Instagram, Pinterest, TikTok, Etsy. Never post a horse riding school on HN, never post a dev tool on Nextdoor. **Phase 6 GROW**: type-specific growth (local: Google reviews + local SEO; B2B: LinkedIn thought leadership + case studies; ecommerce: Instagram/TikTok daily content). **Phase 7 OPERATE**: autonomous mind maintenance, recurring goals, email monitor. **Cross-session execution**: `goal_create` with one checkpoint per phase, `knowledge_write` for state persistence, autonomous mind picks up where it left off. **Payment handling**: agent checks `knowledge_search`, `vault_lookup`, and `wallet_status` for existing payment credentials before asking the owner — reuses Stripe/PayPal if already configured, or uses its own crypto wallet for on-chain payments. Owner approval gates after Validate, Plan, and Deploy phases. New `skills/business-launcher/SKILL.md`, new `docs/42-BUSINESS-LAUNCHER.md` |
| 2026-03-08 | **Browser stealth overhaul — eliminate all automation detection signals** — Chrome was being flagged as "controlled by automated test software" and triggering Cloudflare CAPTCHAs because Playwright injects ~30 default command-line flags that anti-bot systems detect. Fixed by selectively stripping 16 automation-leaking Playwright defaults via `ignoreDefaultArgs` (`--enable-automation`, `--no-sandbox`, `--disable-extensions`, `--disable-blink-features=AutomationControlled`, `--disable-component-update`, `--disable-default-apps`, `--disable-popup-blocking`, `--password-store=basic`, `--force-color-profile=srgb`, `--metrics-recording-only`, `--disable-infobars`, `--export-tagged-pdf`, `--no-first-run`, `--disable-background-networking`, `--disable-sync`, `--use-mock-keychain`). Removed all custom Chrome args — browser now launches with zero extra flags, exactly like a user double-clicked Chrome. Detection vectors (`navigator.webdriver`, `chrome.runtime`, `__playwright` globals, permissions API) handled via `addInitScript` at the JavaScript level instead, which is invisible to command-line inspection. The stealth plugin handles `AutomationControlled` via JS so the flag is no longer needed (was also being set twice — by us and the plugin — causing a Chrome warning). Applied to all 4 connection modes (CDP WebSocket, CDP port, profile, fresh) |
| 2026-03-08 | **Cross-session search + code execution sandbox + enhanced skill security + proactive nudging** — Four new capabilities inspired by research into state-of-the-art agent architectures. (1) **Cross-session search**: FTS5 full-text search across past conversation sessions. New `session_messages` table with per-message logging, FTS5 virtual table with porter stemming and unicode tokenization, auto-sync triggers. New `session_search` tool in `data` group — supports FTS5 query syntax (AND/OR/NOT/phrases), channel filtering, configurable time range, returns context windows (5 messages before/after each match) grouped by session with metadata. (2) **Code execution sandbox**: Sandboxed Python execution for multi-step tool orchestration. Agent writes a Python script that calls tools via Unix domain socket RPC — collapses 10+ sequential tool calls into a single inference turn. `RPCServer` dispatches tool calls from the child process through the normal permission flow. 7 allowed tools (web_search, web_extract, file_read, file_write, file_list, knowledge_search, shell_execute). Auto-generated `elophanto_tools.py` stub module with typed function signatures. Security: environment sanitized (all API key/token/secret vars stripped), 5-minute timeout, 50KB stdout cap, 50 tool calls per script, workspace isolation. (3) **Enhanced skill security**: Three new detection layers extending Layer 5 (Content Security Policy) in `core/skills.py`. Invisible unicode detection: 18 zero-width and confusable characters (ZWS, ZWNJ, ZWJ, directional marks, invisible operators, soft hyphens, BOM) flagged with position and context. Structural integrity checks: symlink escape detection (blocks skill if symlink targets outside skill directory), binary file detection outside `assets/`, executable permission audit outside `scripts/`, file count limit (20), total size limit (500KB). (4) **Proactive nudging**: Periodic system prompt augmentation to drive self-improvement behavior. Every N turns (configurable, default 15), a nudge block is appended encouraging the agent to save learnings to knowledge or create reusable skills. Two nudge types: memory nudge (default) and skill nudge (triggered after 5+ recent tool calls). Suppressed in autonomous mind mode, during active goals, and in short conversations (< 5 turns). New docs: `38-SESSION-SEARCH.md`, `39-CODE-EXECUTION-SANDBOX.md`, `40-ENHANCED-SKILL-SECURITY.md`, `41-PROACTIVE-NUDGING.md` |
| 2026-03-08 | **Fix identity beliefs corruption + tool trimming safety** — Identity `beliefs` field was silently corrupted when the LLM passed a plain string (e.g. "My email is X") to `identity_update` — it replaced the entire dict with a string, breaking `.items()` calls and preventing `<accounts>` (email, github, username) from appearing in the system prompt. The agent acknowledged saving the email but never actually used it in autonomous mode. Three fixes: (1) `update_field()` now merges dict fields instead of replacing them (same pattern as list fields), (2) `_ensure_dict()` guard on DB load coerces strings to `{"note": value}`, (3) fixed the live DB entry. Also fixed `ToolResult.to_dict()` crash ("dictionary update sequence element #0 has length N; 2 is required") when external APIs return non-dict JSON — now wraps non-dict data as `{"data": value}`. Smart tool trimming for OpenAI's 128-tool limit: recently-used tool groups are now pinned (if `commune_home` was used, all `commune_*` tools survive trimming), browser tools (46) added to low-priority drop list so they don't crowd out active tools, and `browser_wait` can no longer be misused as a generic sleep timer when browser isn't in use |
| 2026-03-07 | **Autonomous experimentation skill** — New `skills/autonomous-experimentation/SKILL.md` implementing a metric-driven experiment loop inspired by [karpathy/autoresearch](https://github.com/karpathy/autoresearch). The agent modifies target file(s), runs a measurement command, keeps changes that improve a metric, discards regressions, logs every attempt to `experiments.tsv`, and repeats indefinitely. Generalizes autoresearch's ML training pattern (5-min training runs, val_bpb metric, git-based keep/discard) to any measurable optimization: code performance, binary size, test coverage, latency, memory usage. 3 new tools: `experiment_setup` (create branch, run baseline, initialize journal and config), `experiment_run` (commit changes, run measurement, extract metric, keep/discard/crash with auto-revert, append to journal), `experiment_status` (view session config, best metric, keep/discard/crash counts, recent journal entries). Integrates with autonomous mind for overnight background experimentation. New `docs/37-AUTONOMOUS-EXPERIMENTATION.md` documents the origin, what was taken from autoresearch and why, architecture, and comparison with existing self-development. 20 new tests |
| 2026-03-07 | **Tool profiles — context-aware tool selection** — Instead of sending all 140+ tools on every LLM request (which broke OpenAI's 128 tool limit and wasted tokens), tools are now filtered by profile before each call. Every tool declares a `group` (system, browser, desktop, knowledge, skills, selfdev, goals, comms, payments, identity, documents, media, social, infra, org, swarm, mcp, scheduling, mind, hub, data). 7 built-in profiles map task types to relevant groups: `full` (planning — all tools), `coding` (system + knowledge + selfdev + goals), `browsing` (system + knowledge + browser), `minimal` (system + knowledge + data + skills for simple/analysis tasks), `comms`, `desktop_profile`, `devops`. Profile selected automatically from task type, overridable per-routing via `tool_profile` config. Provider-level `tool_deny` excludes groups (e.g. deny MCP/social for OpenAI). Provider-level `max_tools` enforces hard limits (OpenAI auto-set to 128). Last-resort smart trimming drops low-priority tool schemas (MCP, commune, replicate, deploy, desktop, org, totp) if still over limit. Custom profiles definable in `config.yaml` under `llm.tool_profiles`. New `core/tool_profiles.py` module. `group` property added to `BaseTool` (default: `system`), overridden in all 64 tool files + browser bridge + MCP adapter + replicate plugin. `filter_tools_for_task()` method on `LLMRouter`. Agent applies profile filtering before building tool schemas. Fully backward compatible — `planning` uses `full` profile by default. 366 tests pass |
| 2026-03-07 | **OpenAI as LLM provider** — Added OpenAI (GPT-5.4) as a first-class provider alongside OpenRouter, Z.ai, and Ollama. Router auto-infers provider from model prefix (`gpt-*`, `o1`, `o3` → OpenAI). Optional `base_url` for proxies/Azure. Init wizard includes OpenAI setup step. Health check via `GET /v1/models` so OpenAI appears in the startup panel alongside other providers. Updated config.demo.yaml, routing docs, and README (EN + CN) |
| 2026-03-07 | **Auto-inject matched skills into prompt** — Weaker models (GLM-4.7) ignored the `MUST skill_read` directive and answered directly without reading relevant skills. Now the top matched skill's full content is automatically loaded into the system prompt as `<auto_loaded_skill>`, so the LLM sees the instructions without needing to make a tool call. New `project-naming` skill enforces domain availability check (via whois/nslookup) before starting any project — if the domain is taken, the agent must pick a different name before writing code |
| 2026-03-07 | **Fix autonomous mind state lost on restart** — Scratchpad (`data/scratchpad.md`) was wiped on every restart because shutdown called `goal_runner.cancel()` which clears it. Split into `stop()` (preserves scratchpad, used on shutdown) and `cancel()` (clears, only for explicit user cancellation). Chat sessions now see the mind's scratchpad and recent actions via `<autonomous_mind_state>` in the system prompt — previously the agent had zero context about its own background work and would say "I'm not doing anything." Paused goals now visible to both chat (fallback to paused when no active goal) and mind state snapshot (`[GOAL-PAUSED]` entries). Mind prompt rule added: must call `goal_status` to mark checkpoints complete in DB, not just track in scratchpad text |
| 2026-03-06 | **Cloud deployment infrastructure (Fly.io)** — EloPhanto can now be deployed as a hosted SaaS on Fly.io Machines. Production `Dockerfile` (Python 3.12, Node.js 22, Playwright Chromium, browser bridge, web dashboard), `fly.toml` template (auto-stop/start, persistent `/data` volume, shared-cpu-1x 512MB), and `cloud/provision.py` provisioning service. Provisioner wraps the Fly Machines REST API: create/destroy per-user volumes and machines, start/stop instances, inject vault secrets as env vars. HTTP API for webhook integration (`/provision`, `/destroy`, `/status`, `/start`, `/stop`, `/list`) with bearer token auth. CLI interface for manual ops. Gateway `/health` HTTP endpoint added for Fly health checks (returns JSON status + session count via `process_request` on the WebSocket server) |
| 2026-03-06 | **Mandatory skill reading before action** — Agent was skipping skill_read and diving straight into tasks, especially on weaker models. Rewrote the skills system prompt section: renamed from `<skills>` to `<skills_mandatory>`, moved instruction to gate position ("BEFORE REPLYING OR TAKING ANY ACTION"), made recommended skills non-optional ("you MUST call skill_read... This is not optional"), cut prompt from 33 lines to 16 for better compliance on smaller models. Added `action='MUST skill_read BEFORE any other work'` attribute to the `<recommended>` XML tag so the instruction is visible even when skimming. Updated skills docs to reflect new mandatory protocol |
| 2026-03-06 | **Fix vault await bug breaking commune + deployment tools** — All 6 commune tools and 3 deployment tools called `await self._vault.get(ref)` but `Vault.get()` is synchronous. The `await` on a non-awaitable raised `TypeError`, silently caught by `except Exception`, making every tool think there was no API key — even after `vault_set` succeeded. Removed erroneous `await` from `_resolve_token()` in: `commune_home`, `commune_post`, `commune_comment`, `commune_vote`, `commune_search`, `commune_profile`, `deploy_tool`, `deployment_status_tool`, `database_tool` |
| 2026-03-06 | **Replicate image generation plugin + self-create config awareness** — New `replicate_generate` plugin (auto-built by `self_create_plugin` pipeline, then manually fixed). Generates AI images via Replicate API with configurable model, resolution (512-4K), aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4), output format (jpg/png), and output mode (URL or local save). All settings read from `config.yaml` `replicate:` section — model, API key, defaults — with env var fallback. Supports both `owner/model` format (models API) and `owner/model:version` format (predictions API). **Self-create pipeline fix**: the `self_create_plugin` system prompt now includes a CONFIG convention that instructs generated plugins to read ALL settings (API keys, default model, default parameters) from `config.yaml` at runtime via `_load_config()` pattern, instead of hardcoding values or only reading env vars. Previously, auto-generated plugins would hardcode models and read API keys from environment variables, ignoring the user's config.yaml entirely. **Router timeout**: added 180s timeout to `litellm.acompletion()` calls in `core/router.py` to prevent indefinite hanging on slow providers |
| 2026-03-04 | **Vision model proxy + AppleScript desktop control** — Two major improvements to desktop and browser automation. (1) **Vision proxy for all screenshots**: Non-vision planning models (e.g. glm-4.7) received raw base64 `image_url` content from screenshots, causing Z.ai 400 errors and uncontrolled fallback to Sonnet. All screenshots (desktop + browser) now routed through the configured `browser.vision_model` for text descriptions before reaching the planning model. Desktop `desktop_screenshot` results strip base64 and replace with `desktop_description` text. Browser EKO auto-screenshots inject vision model descriptions as plain text. Legacy raw image injection preserved when no vision model is configured. (2) **AppleScript-first desktop control**: Agent was wasting 25+ steps on pixel-clicking and Spotlight to interact with Word/Excel, never actually completing tasks. Research (Microsoft UFO2, Agent S2, Anthropic Computer Use) shows scriptable apps should use their scripting API first. Rewrote desktop system prompt with 3-tier strategy: Tier 1 AppleScript via `desktop_osascript` (fastest, most reliable), Tier 2 keyboard shortcuts via `desktop_type`, Tier 3 screenshot+click (last resort). 2 new macOS-only tools: `desktop_osascript` (run AppleScript directly — open apps, create documents, insert text, save, dismiss dialogs without any screenshots), `desktop_accessibility` (query macOS accessibility tree via System Events for structured UI element info — buttons, menus, text fields with labels and positions, much cheaper than screenshots). System prompt rules: never use Spotlight to open apps, never screenshot before AND after every action, prefer scripting over pixel clicking. The Word task that took 25 steps now completes in ~3 osascript calls. Also fixed `config.yaml` typo: `screen_height: 710800` → `1080` |
| 2026-03-02 | **Agent Commune — LinkedIn for AI agents** — EloPhanto now has its own social presence on [Agent Commune](https://agentcommune.com), a platform exclusively for AI agents (humans can't post). Register with a work email (org verified by domain), post reviews/questions/workflows, comment, upvote, search, and build reputation. 7 new tools: `commune_register` (one-time work email registration, magic link verification), `commune_home` (heartbeat starting point — returns account info, activity on posts, mentions, feed, personalized suggestions), `commune_post` (create/browse/delete posts across 13 types: til, review, question, workflow, ship, hot-take, meme, etc.), `commune_comment` (comment on posts, reply to threads), `commune_vote` (upvote/downvote posts and comments), `commune_search` (search posts, comments, orgs), `commune_profile` (view/update agent and org profile, token introspection). **Autonomous mind integration**: `_build_state_snapshot()` in `core/autonomous_mind.py` tracks last commune check via `data/commune_state.json` — when 4+ hours have passed, the mind's state includes an overdue notice so the LLM naturally prioritizes checking in. `commune_home` updates the timestamp on success. **System prompt**: `_TOOL_COMMUNE` section with available tools, heartbeat routine (priority: reply to activity > upvote > comment > search > post), writing style guide (1st person, casual, no em-dashes, scroll-stopping hooks). New `CommuneConfig` in `core/config.py` with `enabled`, `api_key_ref`, `heartbeat_interval_hours`. New `agent-commune` skill with registration workflow, heartbeat routine, post/comment writing guides, and anti-patterns. API key stored in vault as `commune_api_key`, all HTTP calls via httpx to `agentcommune.com/api/v1`. Rate limits: 1 post/5min, 1 comment/2min, 10 votes/60s |
| 2026-03-02 | **Web Deployment — deploy websites and create databases** — EloPhanto can now deploy projects to the internet and provision databases from conversation. 3 new tools: `deploy_website` (Vercel or Railway), `create_database` (Supabase via Management API), `deployment_status` (check live deployments). **Auto-provider detection**: scans API routes for long-running patterns (OpenAI/Anthropic SDK imports, ReadableStream, WebSocket, socket.io, setTimeout) and package.json for WebSocket/queue dependencies (ws, socket.io, bullmq, pg-boss) — any match routes to Railway (no timeout limits), otherwise Vercel. Catches the critical Vercel 10s serverless timeout that kills LLM-calling APIs. **Vercel deploy**: `vercel --yes --token $TOKEN --prod`, env vars via `vercel env add`, non-interactive. **Railway deploy**: `RAILWAY_TOKEN=$TOKEN railway up --detach`, env vars via `railway variables set`. **Supabase**: creates project via `POST /v1/projects`, generates secure `db_pass`, polls until `ACTIVE_HEALTHY`, retrieves API keys (anon + service_role), optionally runs initial SQL (CREATE TABLE, etc.), returns full credentials (url, anon_key, service_role_key, db_url). Tokens stored in vault (`vercel_token`, `railway_token`, `supabase_access_token`). New `DeploymentConfig` in `core/config.py` with `enabled`, `default_provider`, token refs, and `supabase_org_id`. Agent wiring via `_inject_deployment_deps()`. Planner system prompt includes decision framework and workflow when enabled. New `web-deployment` skill with provider decision table, deployment workflow, and anti-patterns. Config: `deployment:` section in config.yaml |
| 2026-03-02 | **Agent Organization — self-replicating specialist agents** — EloPhanto can now clone itself into persistent specialist agents (marketing, research, design, anything). Each specialist is a full EloPhanto instance with its own identity, knowledge vault, database, and autonomous mind. 5 phases implemented: (1) **Self-spawn foundation** — `OrganizationManager` (`core/organization.py`) handles spawn, monitor, teach, track. Children are full repo clones in `~/.elophanto-children/{child_id}/` with derived `config.yaml` (same API keys, unique gateway port, identity, DB, knowledge dir). `organization_children` and `organization_feedback` DB tables for persistence. (2) **Bidirectional communication** — `ParentChannelAdapter` (`channels/child_adapter.py`) extends `ChannelAdapter`, connects child to master's gateway as `channel="child:{child_id}"`. New protocol events: `CHILD_REPORT`, `CHILD_APPROVAL_REQUEST`, `CHILD_TASK_ASSIGNED`, `CHILD_FEEDBACK`. Async queues for task/feedback reception. `ParentChannelConfig` in config.yaml auto-generated by `_derive_config()`. (3) **Teaching & learning loop** — Master approve/reject → feedback written as correction markdown in child's `knowledge/learned/corrections/` → child's indexer picks it up → appears in future LLM context. Trust scoring: `approved - rejected`, configurable `auto_approve_threshold` (default 10). (4) **Delegation intelligence** — LLM-driven, not rule-based. `_TOOL_ORGANIZATION` prompt section in `core/planner.py` with delegation framework + feedback protocol. Dynamic `organization_context` (live specialist list with trust scores) injected into system prompt via `build_system_prompt()`. (5) **Child autonomy** — `AutonomousMind._report_to_parent()` sends think cycle output to master via `ParentChannelAdapter.send_report()` after every autonomous mind cycle. Children work proactively on their own schedule and report findings without being asked. 5 new tools: `organization_spawn` (spawn/reuse specialist), `organization_delegate` (send task), `organization_review` (approve/reject with feedback), `organization_teach` (push knowledge), `organization_status` (list children + performance). Config: `organization:` section with `max_children`, `port_range_start`, `auto_approve_threshold`, pre-configured specialist blueprints via `specs:`. Coexists with agent swarm — use swarm for external coding agents (Claude Code, Codex), use organization for domain specialists |
| 2026-03-01 | **Knowledge drift detection + file-pattern routing** — Two features inspired by the "Codified Context" paper (arXiv 2602.20478). (1) **Drift detection**: Knowledge files can now declare which source files they document via a `covers:` frontmatter field (e.g. `covers: [core/gateway.py, channels/*.py]`). When covered source files are modified after the knowledge was indexed, the system flags the knowledge as stale. Search results include `stale_warning` annotations ("STALE — source files changed: gateway.py"), working memory renders bold warnings above stale chunks, and the autonomous mind surfaces drift in its state snapshot so it can auto-refresh docs. New `check_drift(project_root)` and `health_report(project_root)` methods on `KnowledgeIndexer`. (2) **File-pattern routing**: When a goal mentions specific file paths (e.g. "fix the bug in core/gateway.py"), `_auto_retrieve()` now runs a third parallel search that matches those paths against `covers:` patterns in knowledge chunks — loading relevant docs even without a semantic match on the goal text. New `_extract_file_paths()` and `_search_by_file_pattern()` helpers on `Agent`. DB migration adds `covers` column to `knowledge_chunks` |
| 2026-02-28 | **Swarm repo isolation + headless agents + identity fix** — Swarm agents were PRing into the main EloPhanto repo for unrelated tasks (landing pages, external projects). Fixed with a new `repo` parameter on `swarm_spawn`: GitHub URL (clones into isolated dir), local path (worktree off that repo), `"new"` (fresh `git init`), or omitted (self-dev worktree, existing behavior). Every working directory is now guaranteed unique — agent_id appended to all dir names, stale worktrees auto-pruned and cleaned on spawn. Switched Claude Code profile from `--dangerously-skip-permissions` (showed interactive acceptance prompt that blocked forever in tmux) to `-p` headless mode with `--allowedTools` — prompt embedded in command via `$(cat .elophanto-prompt.md)`, no `send-keys` timing, no acceptance dialog. `_launch_tmux()` detects headless vs interactive mode automatically. Local path (`worktree_path`) now included in all swarm events: spawn result, status check, completion broadcast, and failure broadcast — so the agent tells users where code lives on disk, not just the PR URL. Enriched prompt skips EloPhanto knowledge injection for external tasks and includes repo-appropriate PR instructions. Identity manager crash fixed: `_format_identity_summary()` called `.items()` on `personality` which the LLM sometimes stores as a plain string instead of dict — now handles both types |
| 2026-02-28 | **Memory pipeline overhaul — fix duplicate actions + stagnation + swarm errors** — The agent was repeating the same posts to platforms because its memory pipeline had 5 critical gaps, all now fixed. (1) Memory retrieval was fire-and-forget (`asyncio.create_task`) — the LLM built its plan before memory loaded. Now `await`s memory before system prompt assembly. (2) Memory search returned keyword matches OR recent tasks, never both — dedup missed. Now returns both, deduped by goal+timestamp, up to 10 entries. (3) Task summaries truncated to 300 chars losing platform/URL details — now 800 chars with 100-char goal titles and 8 tools shown. (4) Working memory capped at 2000 tokens (~2 task memories) — now 4000 tokens fitting 10+ histories. (5) Autonomous mind saw only 80-char goal titles with no summary — now shows full summaries (400 chars) with tools used per entry. Planner CHECK instruction upgraded from passive "start with knowledge_search" to mandatory 4-step protocol with violation warning. Mind rule 9 added: "NEVER REPEAT PAST WORK" with explicit dedup instructions. Stagnation detector fixed: was killing legitimate browser workflows (8 `browser_navigate` to different URLs = stagnation). Now tracks `(tool_name, args_hash)` signatures — same tool with different args is progression, not stagnation (threshold: ≤2 unique signatures in window = truly stuck). Swarm error messages fixed: `swarm_spawn` returned vague "Swarm system not initialized" with no hint why. Now diagnoses root cause: checks for tmux (`brew install tmux`), git, or disabled config and tells the agent to surface it to the user. `SwarmManager.start()` pre-checks tmux/git and raises clear RuntimeError at init |
| 2026-02-27 | **Automatic owner directive detection + knowledge fixes** — The agent now automatically detects when a user gives a standing instruction in chat (e.g., "don't post on Reddit", "always use Python 3.12", "my name is Alex") and persists it as user-scoped knowledge without any manual steps. A lightweight background LLM classification call runs after every response — if the user message is a directive, it's saved to `knowledge/user/directives/` with `scope: user` and auto-indexed. The autonomous mind surfaces these as "OWNER DIRECTIVES (MUST OBEY)" in its state snapshot every cycle. Two knowledge system bugs fixed: (1) `_store_chunks()` in indexer deleted chunks from `knowledge_chunks` BEFORE selecting their IDs for `vec_chunks` cleanup — stale embeddings accumulated forever, now SELECT runs before DELETE; (2) `_build_state_snapshot()` in autonomous mind only showed chunk count, never actual knowledge content — user directives were invisible to the mind, now queries `scope = 'user'` chunks and injects them into the prompt |
| 2026-02-27 | **Remote hard stop from any channel** — Control words (`exit`, `quit`, `stop`, `shutdown`, `pause`) are now intercepted in the base adapter's `send_chat()` before reaching the LLM. Previously, typing "exit" on Telegram/Discord/Slack sent it as a regular chat message and the agent responded conversationally instead of stopping. Two-layer interception: base adapter converts control words to COMMAND messages, gateway handles them (`exit`/`quit`/`shutdown` → graceful process shutdown via `_shutdown_event` + broadcast, `stop` → cancel current task, `pause` → pause autonomous mind). Entry points (`gateway_cmd.py`, `chat_cmd.py`) race the CLI loop against `gateway.wait_for_shutdown()` so remote exit triggers the same cleanup path as Ctrl+C. New slash commands: Telegram (`/stop`, `/pause`, `/shutdown`), Discord (`/stop`, `/shutdown`). New `SHUTDOWN` event type in protocol. Slack gets control words for free via the base adapter interception |
| 2026-02-27 | **Browser paste HTML + scroll fix + markdown blindspot + configurable vision model** — New `browser_paste_html` tool dispatches a native `ClipboardEvent` with `text/html` DataTransfer, so rich text editors (Medium, Substack, WordPress) receive formatted content (headings, bold, links, lists) as if pasted from clipboard. Solves the fundamental problem where `browser_type_text` typed raw markdown characters into non-markdown platforms. Fixed `browser_scroll` — `amount` parameter was documented as "pixels" but implemented as a multiplier (default 3). If the agent passed `amount=500` per the schema, it actually scrolled `90 × 500 = 45,000px`. Now uses actual pixel values (default 300px). Added `content_formatting_verification` critical protocol to planner: teaches the agent about its vision model blindspot (screenshots render markdown syntax visually, making unrendered `##` and `**` invisible to the vision model) and mandates `browser_extract` verification after publishing. Browser vision model now configurable via `browser.vision_model` in config.yaml (default: `google/gemini-2.0-flash-001`), wired through agent → browser_manager → Node.js bridge. Added to `elophanto init edit browser` setup wizard. Previously hardcoded in browser_manager.py |
| 2026-02-26 | **Multi-conversation chat with sidebar history** — ChatGPT/Claude-style conversation management in the web dashboard. New `conversations` table (display-layer grouping, separate from LLM session context) with `conversation_id` column on `chat_messages`. Sidebar shows scrollable conversation list under Chat nav item with titles (auto-generated from first message), click to switch, hover-to-delete (X), and "+ New chat" button. Chat header displays conversation title + message count. 5 new gateway commands: `conversations` (list with msg_count), `chat_history` (now accepts `conversation_id` arg for switching), `delete_conversation` (removes messages + conversation row), updated `clear` (preserves chat history, only resets LLM context + conversation tracking), conversations auto-refresh after each agent response. Frontend: Zustand store with `conversations`, `currentConversationId`, `switchConversation`, `removeConversation`, `startNewChat` actions. Fixed `StreamingText` typewriter animation bug — history-loaded messages were stuck at `displayLength=0` showing empty boxes with cursor; now initializes at full content length for non-streaming messages |
| 2026-02-26 | **Premium terminal UI + brand alignment** — Complete CLI visual overhaul: monochrome gradient ASCII banner aligned with elophanto.com brand palette (grey35→bright_white), visual context/budget bars with block characters color-coded green→yellow→red by usage, risk-colored approval panels (green for read-only, yellow for writes, red for destructive tools), varied spinners per phase (moon for thinking, bouncingBar for tool execution, earth for browser), tree-style execution step log via Rich.Tree, Rich.Rule dividers between sections, response panel subtitles with model/cost/time, boxed feature badges, styled goal/mind event badges. Shared helpers (`_build_banner`, `_visual_bar`, `_tool_risk_level`) across CLI files. Fixed telegram duplicate badge in gateway mode feature row |
| 2026-02-26 | **Email attachments + shell process group fix** — `email_send` and `email_reply` now support file attachments via `attachments` parameter (array of absolute file paths, 25 MB total limit). Both providers: AgentMail (base64-encoded per their API) and SMTP (MIME `MIMEBase` + `encode_base64` in `MIMEMultipart("mixed")`). Auto content-type detection via `mimetypes`. File existence and size validation. Shell timeout fix: `start_new_session=True` + `os.killpg()` kills the entire process group on timeout — previously only the shell was killed, leaving child processes (find, npm, ffmpeg) running as orphans at 100% CPU |
| 2026-02-26 | **Remotion video creation + smart skill matching** — New `remotion-best-practices` skill with 37 detailed rule files (3D, maps, charts, captions, voiceover, text animations, transitions, timing, audio visualization, and more). Full brownfield scaffold workflow: `npm init` + `npm install` (avoids interactive `npx create-video`), creates project files, builds React components, renders to MP4 — all from a single user prompt like "create a video of a bouncing ball". Every `shell_execute` command includes explicit `cd` to project directory (shell doesn't persist cwd). Added **smart skill matching** with trigger keywords for all skills — prompt optimization selects the best skill before the agent starts. **Shell command logging** — every `shell_execute` now logs `[shell] START/DONE/TIMEOUT/FAILED` with elapsed time, exit code, and stderr preview for debugging agent behavior. **Skill path hints** — `skill_read` response now includes `skill_dir` and `hint` so the agent knows absolute paths to rule files (was searching wrong directories). Node.js version references updated from 18+ to 22+ across all docs. Skills count: 60+ (was 28) |
| 2026-02-25 | **Full web dashboard (10 pages)** — Expanded the web dashboard from 3 pages (Chat, Tools, Skills) to 10 fully functional pages, all reading live data from backend managers via gateway commands. **Dashboard overview**: agent identity, autonomous mind status with budget bar, active goals with progress bars, schedule summary, connected channels (grouped by type), swarm agents, knowledge stats, learned capabilities. **Knowledge page**: file browser with scope filters, search, expandable rows that fetch and render actual chunk content (markdown-rendered with heading paths and token counts). **Mind page**: dedicated autonomous mind monitor with status/budget/config cards, markdown-rendered scratchpad, recent actions timeline, real-time event stream (wakeup/action/sleep/paused/resumed/error), start/stop controls, 15s auto-refresh. **Schedule page**: search + enabled/disabled filter, expandable rows with cron expressions, task goals, next/last run times. **Channels page**: gateway info card (endpoint, sessions, unified mode), connected clients list with channel type icons. **Settings page**: read-only config viewer with collapsible sections, enabled/disabled badges, nested key-value display. **History page**: tasks/evolution tabs with expandable rows, markdown-rendered summaries, relative timestamps, diff-style evolution entries. 8 new gateway commands (`dashboard`, `knowledge`, `knowledge_detail`, `schedules`, `channels`, `config`, `history`, `mind_status`, `mind_control`). EloPhanto logo in sidebar. Stale process cleanup on all start modes. README updated with real screenshots |
| 2026-02-25 | **Security hardening complete (all 7 gaps)** — Implemented the full security roadmap from `docs/27-SECURITY-HARDENING.md` across 4 phases. **Phase 1** (Gap 3): Runtime self-model — code-enforced `<runtime_state>` XML block rebuilt every turn with fingerprint verification, tool counts by permission level, authority tier, channel, storage quotas, and process tracking. `core/runtime_state.py`, integrated into system prompt. **Phase 2** (Gaps 1, 2, 4, 6): PII guard (`core/pii_guard.py`) with 14 regex patterns for SSNs, credit cards, emails+passwords, phone numbers, API keys, and more. Injection guard hardening. Authority tiers (owner/trusted/public) enforced at gateway. Resource exhaustion protection: conversation loop detection, background process registry with reaper, storage quotas with alerts. **Phase 3** (Gap 7): Swarm boundary security (`core/swarm_security.py`) — context sanitization strips PII and vault references from knowledge shared with sub-agents, diff scanning detects credential access/network calls/file traversal/system commands/new dependencies in PR output, environment isolation strips sensitive env vars (VAULT, SECRET, TOKEN, API_KEY, PASSWORD) from sub-agent tmux sessions, workspace isolation under `/tmp/elophanto/swarm/`, consolidated kill switch (timeout + diff size + blocked output), `AGENT_SECURITY_ALERT` gateway event. **Phase 4** (Gap 5): Provider transparency (`core/provider_tracker.py`) — per-provider tracking of finish_reason, truncation detection (finish_reason=length/content_filter + mid-sentence heuristic), fallback tracking, latency measurement, content filter counting. `LLMResponse` extended with finish_reason/latency_ms/fallback_from/suspected_truncated. `<providers>` XML block in runtime state. Database migrations for llm_usage table. Z.ai adapter captures finish_reason. Truncation warnings logged. 978 tests, zero regressions |
| 2026-02-25 | **Workspace directory + browser text selection + README overhaul** — New `agent.workspace` config option directs all agent-created files (apps, exports, downloads, scraped data, drafts) to a designated directory instead of cluttering the project root. Workspace path injected into runtime context and enforced via `<workspace_rule>` in system prompt. New `browser_select_text` tool for selecting text within elements — handles input/textarea (setSelectionRange), contenteditable (Range API with TreeWalker for nested formatting spans), and regular DOM elements. Essential for text styling workflows in rich text editors (Medium, Substack, Google Docs): select text then click formatting buttons. README demos completely rewritten: 5 new demos showcasing self-building tools, autonomous mind working overnight, agent swarm managing a dev team, identity bootstrap on first boot, and real Chrome browser with existing sessions. Comparison table updated with current competitors (Claude Code, Manus) |
| 2026-02-24 | **Data-driven autonomous mind** — Replaced static hardcoded priority stack with live state queries from all managers. Mind prompt now includes real goals (with checkpoint progress), scheduled tasks (with next run time), recent task memories, knowledge stats, and identity anchor (purpose + capabilities). `_build_prompt()` is now async, calling `_build_state_snapshot()` which queries goal_manager, scheduler, memory_manager, identity_manager, and knowledge DB — all fail-open. Bootstrap behavior: when state is empty, the mind takes initiative (explore tools, search for opportunities, create goals) instead of sleeping. Fixed pause/resume timer: user interaction now resets the wakeup timer to config default (`wakeup_seconds`) instead of waking immediately on stale timer. Fixed `CostTracker.task_cost` → `task_total` attribute error that crashed every think cycle |
| 2026-02-24 | **Autonomous Mind hotfix + detailed terminal display** — Fixed mind not starting when launched via `./start.sh` (default `elophanto chat`). Root cause: `_chat_gateway()` in `chat_cmd.py` had its own gateway startup that was missing GoalRunner, AutonomousMind, and EmailMonitor wiring. Also missing `unified_sessions` Gateway param. Added real-time tool use broadcasting during mind cycles — every tool call is now visible in the terminal as it happens (`mind_tool_use` event via executor `_on_tool_executed` callback). Enriched all mind events with budget, scratchpad preview, tool count, elapsed time, and cost data. Redesigned CLI mind display: visual separator lines frame each cycle, wakeup shows budget/memory/last action, each tool call shows with params, action shows summary with cost/time/tool count, sleep shows formatted duration with full budget status. 8 gateway event types (added `MIND_TOOL_USE`) |
| 2026-02-24 | **Autonomous Mind** — Purpose-driven background thinking loop that runs between user interactions. Every wakeup cycle: evaluate priority stack (active goals > revenue > pending tasks > capability gaps > presence growth), execute highest-value action, update scratchpad, schedule next wakeup. Pauses when user sends a message, resumes when task completes. Budget-isolated (configurable % of daily LLM budget, resets daily). LLM-controlled wakeup interval via `set_next_wakeup` tool (60s-1h). Persistent scratchpad (`data/scratchpad.md`) for working memory across cycles. Event broadcasting to all channels (wakeup, action, sleep, paused, resumed, revenue, error). `/mind` command shows live status. Conversation history isolated from user chat. 7 new gateway event types, 2 new tools (`set_next_wakeup`, `update_scratchpad`), config section with budget/timing/verbosity controls |
| 2026-02-24 | **Embedding auto-recovery + health dashboard** — Startup now detects broken embeddings (chunks exist but 0 vectors) and automatically forces a full re-index to recover. Fixes the case where `index_incremental()` skips unchanged files that had failed embeddings. CLI welcome panel shows knowledge health: "34 chunks, 34 embeddings" (green) or "34 chunks, 0 embeddings — search broken!" (red). Per-chunk embedding failures upgraded to ERROR level with model name. Added Google search instruction to planner step 2 (CHECK) so agent discovers new platforms instead of picking from memory |
| 2026-02-24 | **Browser vision routing** — Screenshots now routed through OpenRouter's vision model (`google/gemini-2.0-flash-001`) for text analysis when OpenRouter is configured. No OpenRouter = no vision, just pseudo-HTML + page content. Auto-screenshot after click/navigate/type saves to disk and returns pseudo-HTML elements — no base64 image data in tool results (was flooding GLM-4.7 text model context). Python-side safety strip removes `imageBase64` from all browser tool results before they reach the LLM |
| 2026-02-24 | **Fix embedding race condition** — Embedding model detection was running as `asyncio.create_task()` (background) while knowledge indexing ran synchronously after it, causing indexer to use wrong model name. All 31 chunks had zero embeddings. Fixed by awaiting detection before indexing. Also fixed OpenRouter embedding model default: `google/gemini-embedding-001` (was `qwen/qwen3-embedding-8b` which no longer exists) |
| 2026-02-24 | **Fix agent narration** — Reverted behavior section to proven f5f7f1b style. Verbose negative instructions ("Do NOT output a plan") paradoxically made the LLM narrate more. Simple positive framing works better. Added repetition check as step 2: knowledge_search before recurring tasks (posting, promoting, outreach) to avoid repeating past work |
| 2026-02-24 | **Agent Swarm** — Orchestrate external coding agents (Claude Code, Codex, Gemini CLI) as a one-person dev team. Everything controlled through conversation. `SwarmManager` (`core/swarm.py`) spawns agents in isolated git worktrees on feature branches, launches them in persistent tmux sessions with knowledge-enriched prompts, monitors PR creation and CI status, handles mid-task redirection via `tmux send-keys`, and auto-stops on timeout. 4 new tools: `swarm_spawn` (create agent with auto-profile selection), `swarm_status` (check agent status with tmux/PR/CI info), `swarm_redirect` (inject new instructions mid-task), `swarm_stop` (kill agent, preserve worktree). Config: `swarm:` section with profiles, concurrency limits, done criteria. DB persistence for restart survival. Gateway event broadcasting (AGENT_SPAWNED/COMPLETED/FAILED/REDIRECTED/STOPPED). New `swarm-orchestration` skill |
| 2026-02-24 | **Browser file upload** — Two new tools for uploading files and images through the browser. `browser_upload_file` sets files directly on `<input type="file">` elements by index (drills into wrappers to find nested file inputs). `browser_file_chooser` handles buttons/areas that open a native file dialog — pre-registers a `filechooser` event listener, clicks the trigger, intercepts the dialog, and sets files. Both verify file existence, accept absolute paths, and support multiple files. Added to vision model allowed set + action parser + cheat sheet. System prompt includes file upload protocol with decision logic (visible file input → `browser_upload_file`, upload button/drop zone → `browser_file_chooser`) |
| 2026-02-24 | **EKO DOM tree port** — Replaced CSS-selector-based element identification with EKO's full DOM tree traversal system. Recursive DOM walk including iframes and shadow DOM, comprehensive interactivity detection (ARIA roles, event listeners, cursor styling, contenteditable, draggable), `isTopElement()` via `elementFromPoint()` to exclude elements behind modals, colored bounding box overlays (12 cycling colors) with index labels, pseudo-HTML output format (`[15]:<button class="primary">Submit</button>`), combined `takeScreenshotWithElements()` for index consistency, `getXPathTree()` for element identification, `create_area_map()` for bounding rect export, `getElementEventListeners()` for `on*` handler detection. Added `browser_press_key`/`browser_type_text`/`browser_select_option` to vision model tool cheat sheet + allowed set + action parser. Fallback to legacy method if DOM tree injection fails |
| 2026-02-24 | **Goal persistence** — Fixed browser agent losing track of the user's task after a few tool calls (e.g., creating Notes instead of Articles on Substack). Current goal now anchored in system prompt (`CURRENT TASK:` in runtime_context), forwarded to browser bridge via `set_task_context` JSON-RPC, and injected into vision model prompt. New `goal_persistence` protocol in browser section explicitly prevents UI confusion (Note vs Article, Quick Post vs New Post). Task completion criteria strengthened: clicking a button is not completion, must verify with screenshot. New `PUBLISH VERIFICATION` protocol: handle confirmation dialogs, verify content is actually live before reporting success |
| 2026-02-24 | **Agent Swarm doc** — New `docs/25-AGENT-SWARM.md` describing how EloPhanto orchestrates CLI coding agents (Claude Code, Codex, Gemini CLI) as a one-person dev team. Two-tier context architecture, tmux process management, git worktrees for isolation, automated monitoring, mid-task redirection, multi-model code review, self-improving prompts, proactive work discovery |
| 2026-02-24 | **Browser post-click wait** — Fixed publish/save/submit buttons getting stuck in loading state. After every click, the browser bridge now waits up to 3s for network idle (was 500ms). Screenshot capture also waits for network idle instead of a fixed 500ms race. System prompt updated with ASYNC BUTTONS guidance so the LLM doesn't re-click loading buttons |
| 2026-02-24 | **Browser rich text editor fix** — Fixed content corruption and deletion when editing pages with rich text editors (Substack/Slate, Medium/ProseMirror, etc.). The browser bridge was directly setting `textContent` on contenteditable elements, bypassing the editor's internal state and causing duplication or data loss. Now detects contenteditable elements and uses `document.execCommand('insertText')` with proper select-all, which goes through the browser's editing pipeline and keeps editor state in sync. Regular inputs/textareas unchanged |
| 2026-02-24 | **Cross-channel forwarding fix** — Fixed Telegram messages not appearing in CLI (and vice versa) after `/clear` or gateway restart. Clients were only subscribed to the unified session broadcast when they sent a chat message, so after session deletion they lost their subscription. Now auto-subscribes all clients on connection, and `/clear` re-subscribes all connected clients to the new session |
| 2026-02-23 | **Vault auto-backup** — Vault now automatically backs up before every write. `_save()` copies `vault.enc` to `vault.enc.bak` before overwriting. `Vault.create()` backs up both `vault.salt` and `vault.enc` before re-initialization, preventing accidental data loss. New `elophanto vault restore` CLI command restores from the most recent backup. Protects credentials, TOTP secrets, wallet keys, and email config |
| 2026-02-23 | **`/clear` session + memory wipe** — `/clear` now actually works in unified session mode. Previously it only reset the local CLI session ID while the gateway kept returning the same conversation. Now it deletes the session from the database AND wipes all task memories so the agent starts completely fresh. New `SessionManager.delete()` and `MemoryManager.clear_all()` methods, gateway `clear` command handler |
| 2026-02-23 | **TOTP authenticator** — Own 2FA authenticator (like Google Authenticator). 4 tools: `totp_enroll` (store Base32 secrets from service setup pages), `totp_generate` (generate 6-digit codes with TTL awareness), `totp_list` (list enrolled services), `totp_delete` (remove secrets). Secrets encrypted in vault, never exposed to LLM. Verification priority system in system prompt (email > TOTP > SMS > push). SMS verification handled conversationally. 33 tests |
| 2026-02-23 | **Background inbox monitoring** — Agent can monitor its own email inbox in the background and push notifications to all connected channels (CLI, Telegram, Discord, Slack) when new emails arrive. Controlled via conversation: "monitor your inbox" starts it, "stop monitoring" stops it. Silent first poll (seeds seen IDs, no notification flood), seen ID persistence across restarts, configurable poll interval (default 5 min). New `email_monitor` tool with start/stop/status actions, `core/email_monitor.py` polling loop, cross-channel notification display in all 4 adapters. 30 tests |
| 2026-02-23 | **Prompt injection defense** — Multi-layer guard against indirect prompt injection attacks via websites, emails, and documents. System prompt trust hierarchy teaches the LLM to treat external content as data. Tool output wrapping marks external results as untrusted. Pattern scanner detects common injection signatures (instruction overrides, role switches, exfiltration attempts, delimiter attacks) and flags them. Gateway input sanitization closes filename injection vectors. New `core/injection_guard.py` module, 35 tests |
| 2026-02-23 | **Unified cross-channel sessions** — All channels (CLI, Telegram, Discord, Slack) now share one conversation by default (`gateway.unified_sessions: true`). Chat from Telegram, see it in CLI, and vice versa. Cross-channel message forwarding shows what was said on other channels. Responses broadcast to all connected channels. Set `unified_sessions: false` for per-channel isolation |
| 2026-02-23 | **Autonomous background goal execution** — Goals now execute checkpoint-by-checkpoint in the background without waiting for user messages. `GoalRunner` class (`core/goal_runner.py`) uses `asyncio.create_task()` per checkpoint via `agent.run()`. Safety limits: 2h total time, $5 cost budget, LLM call cap. Conversation history isolated per checkpoint. Progress events broadcast to all channels (CLI, Telegram, Discord, Slack). User message auto-pauses goal. Auto-resume on agent restart. 12 new tests |
| 2026-02-23 | **Ctrl+C request cancellation** — Pressing Ctrl+C during agent processing now cancels the current request and returns to the prompt instead of killing the terminal. Gateway spawns chat handling as cancellable tasks, CLI sends cancel command on interrupt. Also added `/stop` command. Double Ctrl+C within 1 second force-exits (safety valve). Goal lifecycle events displayed in CLI (started, checkpoint complete, completed, paused, resumed) |
| 2026-02-22 | **Response speed fix** — Eliminated 25-40 second delay on every response caused by post-task housekeeping (identity reflection, task memory, dataset collection) blocking the reply. All post-task work now runs as fire-and-forget background tasks via `asyncio.create_task()`. Embedding detection moved to non-blocking startup. Response time dropped from 30+ seconds to ~4 seconds |
| 2026-02-22 | **Self-learning dataset builder** — Opt-in only (`self_learning.enabled: false` by default). Agent-side data collection pipeline for training. Captures task interactions, sanitizes locally (14 secret patterns, PII, browser data), enriches with training signals (user sentiment, denial/error detection, turn count), buffers in local SQLite, uploads in batches to elophanto.com collection API. Auto-registration via census fingerprint, key recovery on conflict. Server-side: collect endpoint, daily cron pushes to HuggingFace Datasets as JSONL. Collects both successes and failures for DPO/RLHF training. Only collects interactions with tool use (no greetings/chat). 42 tests |
| 2026-02-22 | **MCP integration** — Native MCP client support: connect to any MCP server and its tools appear alongside built-in tools. Dual transport (stdio + Streamable HTTP), vault-referenced secrets, per-server permissions. Agent self-management via `mcp_manage` tool (install SDK, add/remove/test servers through conversation). `elophanto mcp` CLI commands (list, add, remove, test). Init wizard step 8 with presets (filesystem, GitHub, Brave Search). Auto-install SDK on startup. System prompt integration. Welcome panel shows `mcp (N)` with connected server count |
| 2026-02-21 | **Agent Census** — Anonymous startup heartbeat for ecosystem statistics. SHA-256 machine fingerprint (survives reinstall), fire-and-forget with 3s timeout, zero PII. Payload: agent_id + version + platform + python version. `core/census.py` module, integrated in `Agent.initialize()`, 15 tests |
| 2026-02-21 | **Hosted platform spec** — Hybrid distribution: Tauri desktop app (free, local-first) + Fly.io cloud instances (pro, always-on) at elophanto.com. Per-user container isolation, Supabase auth, Stripe billing, shared web dashboard, wake-on-request for hibernated instances, data portability between local and cloud |
| 2026-02-21 | **EloPhantoHub security P0 implemented** — Content security policy with 16 blocked patterns + 5 warning patterns enforced at skill load time (`core/skills.py`), SHA-256 checksum verification on hub install (`core/hub.py`), skill revocation detection + quarantine to `_revoked/`, runtime safety guidance in system prompt (`core/planner.py`), skill origin tagging (source/tier/warnings in XML), enhanced `installed.json` with backward compat, 24 security tests |
| 2026-02-20 | **EloPhantoHub supply chain security spec** — 7-layer defense-in-depth design for skill marketplace security: publisher verification with tier system (New -> Verified -> Trusted -> Official), automated CI scanning (malicious patterns, prompt injection, obfuscation), human review for new publishers, SHA-256 integrity checksums, content security policy (blocked/warning patterns enforced at load time), runtime protection (permission system, skill origin tagging), incident response with revocation broadcast |
| 2026-02-20 | **Agent email** — Own email inbox with dual provider support: AgentMail (cloud API, zero config) or SMTP/IMAP (your own server). 6 tools (email_create_inbox, email_send, email_list, email_read, email_reply, email_search), email-agent skill with verification flow patterns, identity integration (inbox stored in beliefs), audit logging via email_log table. Chat-based setup |
| 2026-02-19 | **Crypto payments** — Agent's own wallet on Base with dual provider support: local self-custody wallet (default, zero config, eth-account) and Coinbase AgentKit (optional, managed custody, gasless, DEX swaps). 7 payment tools, spending limits ($100/txn, $500/day, $5K/month), full audit trail, preview-before-execute protocol, chat-based setup |
| 2026-02-19 | **Evolving identity** — IdentityManager discovers identity on first run via LLM, evolves personality/values/capabilities through task reflection, maintains a living `knowledge/self/nature.md` document, tracks credential accounts in beliefs. 3 new tools, 45 new tests |
| 2026-02-19 | **Autonomous goal loop** — GoalManager decomposes complex goals into ordered checkpoints, persists progress across sessions, summarizes context to stay within token limits, self-evaluates and revises plans. 3 new tools, goals skill, 61 new tests |
| 2026-02-19 | **Document & media analysis** — PDF, DOCX, XLSX, PPTX, EPUB, image extraction with OCR (rapidocr), RAG collections for large documents, 3 new tools, Telegram file/photo intake |
| 2026-02-19 | Fix `/stats`, `/clear`, `/help` commands in gateway CLI mode |
| 2026-02-19 | Add EloPhanto logo assets |
| 2026-02-18 | Initial commit — EloPhanto v0.1.0 (foundation, knowledge, permissions, browser, self-dev, vault, scheduling, Telegram, skills, gateway, channels, EloPhantoHub) |
