Commit Graph

139 Commits

Author SHA1 Message Date
b3840238cb [loop-cycle-52] feat: response audit trail with inputs, confidence, errors (#144) (#191) 2026-03-15 12:34:48 -04:00
96c7e6deae [loop-cycle-52] fix: remove all qwen3.5 references (#182) (#190) 2026-03-15 12:34:21 -04:00
766add6415 [loop-cycle-52] test: comprehensive session_logger.py coverage (#175) (#187) 2026-03-15 12:26:50 -04:00
e8dd065ad7 [loop-cycle-51] perf: mock subprocess in slow introspection test (#172) (#184) 2026-03-15 12:17:50 -04:00
5b57bf3dd0 [loop-cycle-50] fix: agent retry uses exponential backoff instead of fixed 1s delay (#174) (#181) 2026-03-15 12:08:30 -04:00
bcd6d7e321 [loop-cycle-50] refactor: replace bare sqlite3.connect() with context managers batch 2 (#157) (#180) 2026-03-15 11:58:43 -04:00
ca01ce62ad [loop-cycle-49] fix: mock _warmup_model in agent tests to prevent Ollama network calls (#159) (#177) 2026-03-15 11:46:20 -04:00
f15ad3375a [loop-cycle-47] feat: add confidence signaling module (#143) (#161) 2026-03-15 11:20:30 -04:00
466db7aed2 [loop-cycle-44] refactor: remove dead code batch 2 — agent_core + test_agent_core (#147) (#150) 2026-03-15 10:22:41 -04:00
d2c51763d0 [loop-cycle-43] refactor: remove 1035 lines of dead code (#136) (#146) 2026-03-15 10:10:12 -04:00
48c8efb2fb [loop-cycle-40] fix: use get_system_prompt() in cloud backends (#135) (#138)
## What

Cloud backends (Grok, Claude, AirLLM) were importing SYSTEM_PROMPT directly, which is always SYSTEM_PROMPT_LITE and contains unformatted {model_name} and {session_id} placeholders.

## Changes

- backends.py: Replace `from timmy.prompts import SYSTEM_PROMPT` with `from timmy.prompts import get_system_prompt`
- AirLLM: uses `get_system_prompt(tools_enabled=False, session_id="airllm")` (LITE tier, correct)
- Grok: uses `get_system_prompt(tools_enabled=True, session_id="grok")` (FULL tier)
- Claude: uses `get_system_prompt(tools_enabled=True, session_id="claude")` (FULL tier)
- 9 new tests verify formatted model names, correct tier selection, and session_id formatting

## Tests

1508 passed, 0 failed (41 new tests this cycle)

Fixes #135

Co-authored-by: Kimi Agent <kimi@timmy.local>
Reviewed-on: http://localhost:3000/rockachopa/Timmy-time-dashboard/pulls/138
Reviewed-by: rockachopa <alexpaynex@gmail.com>
Co-authored-by: hermes <hermes@timmy.local>
Co-committed-by: hermes <hermes@timmy.local>
2026-03-15 09:44:43 -04:00
d48d56ecc0 [loop-cycle-38] fix: add soul identity to system prompts (#127) (#134)
Co-authored-by: hermes <hermes@timmy.local>
Co-committed-by: hermes <hermes@timmy.local>
2026-03-15 09:42:57 -04:00
76df262563 [loop-cycle-38] fix: add retry logic for Ollama 500 errors (#131) (#133)
Co-authored-by: hermes <hermes@timmy.local>
Co-committed-by: hermes <hermes@timmy.local>
2026-03-15 09:38:21 -04:00
92e123c9e5 [loop-cycle-36] fix: create soul.md and wire into system context (#125) (#130) 2026-03-15 08:37:24 -04:00
466ad08d7d [loop-cycle-34] fix: mock Ollama model resolution in create_timmy tests (#121) (#126) 2026-03-15 08:20:00 -04:00
cf48b7d904 [loop-cycle-1] fix: lint errors — ambiguous vars + unused import (#123) (#124) 2026-03-15 08:07:19 -04:00
66544d52ed feat: workspace heartbeat monitoring for thinking engine (#28)
- Add src/timmy/workspace.py: WorkspaceMonitor tracks correspondence.md
  line count and inbox file list via data/workspace_state.json
- Wire workspace checks into _gather_system_snapshot() so Timmy sees
  new workspace activity in his thinking context
- Add 'workspace' seed type for workspace-triggered reflections
- Add _check_workspace() post-hook to mark items as seen after processing
- 16 tests covering detection, mark_seen, persistence, edge cases
2026-03-14 21:51:36 -04:00
a57fd7ea09 [loop-cycle-30] fix: gitea-mcp binary name + test stabilization
1. gitea-mcp → gitea-mcp-server (brew binary name). Fixes Timmy's
   Gitea triage — MCP server can now be found on PATH.
2. Mark test_returns_dict_with_expected_keys as @pytest.mark.slow —
   it runs pytest recursively and always exceeds the 30s timeout.
3. Fix ruff F841 lint in test_cli.py (unused result= variable).
2026-03-14 21:32:39 -04:00
750659630b policy: enforce PR-only merges to main + fix broken repl tests
Branch protection enabled on Gitea: direct push to main now rejected.
AGENTS.md updated with Merge Policy section documenting the workflow.

Also fixes bbbbdcd breakage: restores result= in repl test functions
which were dropped by Kimi's 'remove unused variable' commit.

RCA: Kimi Agent pushed directly to main without running tests.
2026-03-14 21:14:34 -04:00
b9b78adaa2 perf: eliminate redundant LLM calls in agentic loop (#24)
Three optimizations to the agentic loop:
1. Cache loop agent as singleton (avoid repeated warmups)
2. Sliding window for step context (last 2 results, not all)
3. Replace summary LLM call with deterministic summary

Saves 1 full LLM inference call per agentic loop invocation
(30-60s on local models) and reduces context window pressure.

Also fixes pre-existing test_cli.py repl test bugs (missing result= assignment).
2026-03-14 20:55:52 -04:00
bbbbdcdfa9 fix: remove unused variable in repl test 2026-03-14 20:45:25 -04:00
65e5e7786f feat: REPL mode, stdin support, multi-word fix for CLI (#26) 2026-03-14 20:45:25 -04:00
3e7a35b3df Merge pull request '[loop-cycle-12] feat: Kimi delegation tool for coding tasks (#67)' (#112) from fix/kimi-delegation-67 into main 2026-03-14 20:31:08 -04:00
453c9a0694 feat: add delegate_to_kimi() tool for coding delegation (#67)
Timmy can now delegate coding tasks to Kimi CLI (262K context).
Includes timeout handling, workdir validation, output truncation.
Sovereign division of labor — Timmy plans, Kimi codes.
2026-03-14 20:29:03 -04:00
2fb104528f feat: add run_self_tests() tool for self-verification (#65)
Timmy can now run his own test suite via the run_self_tests() tool.
Supports 'fast' (unit only), 'full', or specific path scopes.
Returns structured results with pass/fail counts.

Sovereign self-verification — a fundamental capability.
2026-03-14 20:28:24 -04:00
ddb872d3b0 fix: enrich self-knowledge with architecture map and self-modification pathway
- Replace flat file list with layered architecture map (config→agent→prompt→tool→memory→interface)
- Add SELF-MODIFICATION section: Timmy knows he can edit his own config and code
- Remove false limitation 'cannot modify own source code'
- Update tests to match new section headers, add self-modification tests

Closes #81 (reasoning depth)
Closes #86 (self-modification awareness)

[loop-cycle-11]
2026-03-14 20:15:30 -04:00
ffae5aa7c6 feat: add codebase self-knowledge to system prompts (#78, #80)
Adds SELF-KNOWLEDGE section to both SYSTEM_PROMPT_LITE and
SYSTEM_PROMPT_FULL with:
- Codebase map (all src/timmy/ modules with descriptions)
- Current capabilities list (grounded, not generic)
- Known limitations (real gaps, not LLM platitudes)

Lite prompt gets condensed version; full prompt gets detailed.
Timmy can now answer 'what does tool_safety.py do?' and give
grounded answers about his actual limitations.

10 new tests. 1456 total passing.
2026-03-14 19:58:10 -04:00
0204ecc520 Merge pull request '[loop-cycle-9] fix: CLI multi-word messages (#26)' (#107) from fix/cli-multiword-messages into main 2026-03-14 19:48:28 -04:00
9171d93ef9 fix: CLI chat accepts multi-word messages without quotes
Changed message param from str to list[str] in chat() and route() commands.
Words are joined with spaces, so 'timmy chat hello how are you' works without
quoting. Single-word messages still work as before.
- chat(): message: list[str], joined to full_message
- route(): message: list[str], joined to full_message
- 7 new tests in test_cli_multiword.py

Closes #26
2026-03-14 19:43:52 -04:00
f8f3b9b81f feat: inject session_id into system prompt for session identity awareness
Timmy can now introspect which session he's running in (cli, dashboard, loop).
- Add {session_id} placeholder to both lite and full system prompts
- get_system_prompt() accepts session_id param (default: 'unknown')
- create_timmy() accepts session_id param, forwards to prompt
- CLI chat/think/status pass their session_id to create_timmy()
- session.py passes _DEFAULT_SESSION_ID to create_timmy()
- 7 new tests in test_session_identity.py
- Updated 2 existing CLI test mocks

Closes #64
2026-03-14 19:43:11 -04:00
4b553fa0ed Merge pull request 'fix: word-boundary routing + debug route command (#31)' (#102) from fix/routing-patterns into main 2026-03-14 19:24:16 -04:00
67497133fd fix: word-boundary routing + debug route command (#31)
- Replace substring matching with word-boundary regex in route_request()
- "fix the bug" now correctly routes to coder
- Multi-word patterns match if all words appear (any order)
- Add "timmy route" CLI command for debugging routing
- Add route_request_with_match() for pattern visibility
- Expand routing keywords in agents.yaml
- 22 new routing tests, all passing
2026-03-14 19:21:30 -04:00
415938c9a3 test: add 86 tests for semantic_memory.py (#54)
Comprehensive test coverage for the semantic memory module:
- _simple_hash_embedding determinism and normalization
- cosine_similarity including zero vectors
- SemanticMemory: init, index_file, index_vault, search, stats
- _split_into_chunks with various sizes
- memory_search, memory_read, memory_write, memory_forget tools
- MemorySearcher class
- Edge cases: empty DB, unicode, very long text, special chars
- All tests use tmp_path for isolation, no sentence-transformers needed

86 tests, all passing. 1393 total tests passing.
2026-03-14 19:15:55 -04:00
9c59b386d8 feat: add OLLAMA_NUM_CTX config to cap context window (#83)
- Add ollama_num_ctx setting (default 4096) to config.py
- Pass num_ctx option to Ollama in agent.py and agents/base.py
- Add OLLAMA_NUM_CTX to .env.example with usage docs
- Add context_window note in providers.yaml
- Fix mock_settings in test_agent.py for new attribute
- qwen3:30b with 4096 ctx uses ~19GB vs 45GB default
2026-03-14 18:54:43 -04:00
bce6e7d030 fix: log Ollama disconnections with specific error handling (#92)
- BaseAgent.run(): catch httpx.ConnectError/ReadError/ConnectionError,
  log 'Ollama disconnected: <error>' at ERROR level, then re-raise
- session.py: distinguish Ollama disconnects from other errors in
  chat(), chat_with_tools(), continue_chat() — return specific message
  'Ollama appears to be disconnected' instead of generic error
- 11 new tests covering all disconnect paths
2026-03-14 18:40:15 -04:00
d1a8b16cd7 Merge pull request '[loop-cycle-5] test: skip voice_loop tests when numpy missing (#48)' (#94) from fix/skip-voice-tests-no-numpy into main 2026-03-14 18:26:40 -04:00
bf30d26dd1 test: skip voice_loop tests gracefully when numpy unavailable
Wrap numpy and voice_loop imports in try/except with pytestmark skipif.
Tests skip cleanly instead of ImportError when numpy not in dev deps.

Closes #48
2026-03-14 18:24:56 -04:00
b3a1e0ce36 fix: prune dead web_search tool — ddgs never installed (#87)
Remove DuckDuckGoTools import, all web_search registrations across 4 toolkit
factories, catalog entry, safety classification, prompt references, and
session regex. Total: -41 lines of dead code.

consult_grok is functional (grok_enabled=True, API key set) and opt-in,
so it stays — but Timmy never calls it autonomously, which is correct
sovereign behavior (no cloud calls unless user permits).

Closes #87
2026-03-14 18:13:51 -04:00
7132b42ff3 fix: model introspection uses exact match, queries /api/ps first
_get_ollama_model() used prefix match (startswith) on /api/tags,
causing qwen3:30b to match qwen3.5:latest. Now:
1. Queries /api/ps (loaded models) first — most accurate
2. Falls back to /api/tags with exact name match
3. Reports actual running model, not just configured one

Updated test_get_system_info_contains_model to not assume model==config.

Fixes #77. 5 regression tests added.
2026-03-14 18:03:59 -04:00
74e426c63b [loop-cycle-2] fix: suppress confirmation tool WARNING spam (#79) (#89) 2026-03-14 17:54:58 -04:00
09fcf956ec Merge pull request '[loop-cycle-1] feat: tool allowlist for autonomous operation (#69)' (#88) from fix/tool-allowlist-autonomous into main 2026-03-14 17:41:56 -04:00
d28e2f4a7e [loop-cycle-1] feat: tool allowlist for autonomous operation (#69)
Add config/allowlist.yaml — YAML-driven gate that auto-approves bounded
tool calls when no human is present.

When Timmy runs with --autonomous or stdin is not a terminal, tool calls
are checked against allowlist: matched → auto-approved, else → rejected.

Changes:
  - config/allowlist.yaml: shell prefixes, deny patterns, path rules
  - tool_safety.py: is_allowlisted() checks tools against YAML rules
  - cli.py: --autonomous flag, _is_interactive() detection
  - 44 new allowlist tests, 8 updated CLI tests

Closes #69
2026-03-14 17:39:48 -04:00
94cd1a9840 fix: make model fallback chains configurable (#53)
Move hardcoded model fallback lists from module-level constants into
settings.fallback_models and settings.vision_fallback_models (pydantic
Settings fields). Can now be overridden via env vars
FALLBACK_MODELS / VISION_FALLBACK_MODELS or config/providers.yaml.

Removed:
- OLLAMA_MODEL_PRIMARY / OLLAMA_MODEL_FALLBACK from config.py
- DEFAULT_MODEL_FALLBACKS / VISION_MODEL_FALLBACKS from agent.py

get_effective_ollama_model() and _resolve_model_with_fallback() now
walk the configurable chains instead of hardcoded constants.

5 new tests guard the configurable behavior and prevent regression
to hardcoded constants.
2026-03-14 17:26:47 -04:00
061c8f6628 fix: brevity tuning — plain text prompts, markdown=False, front-loaded brevity
Closes #71: Timmy was responding with elaborate markdown formatting
(tables, headers, emoji, bullet lists) for simple questions.

Root causes fixed:
1. Agno Agent markdown=True flag explicitly told the model to format
   responses as markdown. Set to False in both agent.py and agents/base.py.
2. SYSTEM_PROMPT_FULL used ## and ### markdown headers, bold (**), and
   numbered lists — teaching by example that markdown is expected.
   Rewritten to plain text with labeled sections.
3. Brevity instructions were buried at the bottom of the full prompt.
   Moved to immediately after the opening line as 'VOICE AND BREVITY'
   with explicit override priority.
4. Orchestrator prompt in agents.yaml was silent on response style.
   Added 'Voice: brief, plain, direct' with concrete examples.

The full prompt is now 41 lines shorter (124 → 83). The prompt itself
practices the brevity it preaches.

SOUL.md alignment:
- 'Brevity is a kindness' — now front-loaded in both base and agent prompt
- 'I do not fill silence with noise' — explicit in both tiers
- 'I speak plainly. I prefer short sentences.' — structural enforcement

4 new tests guard against regression:
- test_full_prompt_brevity_first: brevity section before tools/memory
- test_full_prompt_no_markdown_headers: no ## or ### in prompt text
- test_full_prompt_plain_text_brevity: 'plain text' instruction present
- test_lite_prompt_brevity: lite tier also instructs brevity
2026-03-14 17:15:56 -04:00
rockachopa
927e25cc40 Merge pull request 'fix: replace print() with proper logging (#29, #51)' (#59) from fix/print-to-logging into main 2026-03-14 16:50:04 -04:00
b30b5c6b57 [loop-cycle-6] Break thinking rumination loop — semantic dedup (#38)
Add post-generation similarity check to ThinkingEngine.think_once().

Problem: Timmy's thinking engine generates repetitive thoughts because
small local models ignore 'don't repeat' instructions in the prompt.
The same observation ('still no chat messages', 'Alexander's name is in
profile') would appear 14+ times in a single day's journal.

Fix: After generating a thought, compare it against the last 5 thoughts
using SequenceMatcher. If similarity >= 0.6, retry with a new seed up to
2 times. If all retries produce repetitive content, discard rather than
store. Uses stdlib difflib — no new dependencies.

Changes:
- thinking.py: Add _is_too_similar() method with SequenceMatcher
- thinking.py: Wrap generation in retry loop with dedup check
- test_thinking.py: 7 new tests covering exact match, near match,
  different thoughts, retry behavior, and max-retry discard

+96/-20 lines in thinking.py, +87 lines in tests.
2026-03-14 16:21:16 -04:00
70d5dc5ce1 fix: replace eval() with AST-walking safe evaluator in calculator
Fixes #52

- Replace eval() in calculator() with _safe_eval() that walks the AST
  and only permits: numeric constants, arithmetic ops (+,-,*,/,//,%,**),
  unary +/-, math module access, and whitelisted builtins (abs, round,
  min, max)
- Reject all other syntax: imports, attribute access on non-math objects,
  lambdas, comprehensions, string literals, etc.
- Add 39 tests covering arithmetic, precedence, math functions,
  allowed builtins, error handling, and 14 injection prevention cases
2026-03-14 15:51:35 -04:00
782218aa2c fix: voice loop — persistent event loop, markdown stripping, MCP noise
Three fixes from real-world testing:

1. Event loop: replaced asyncio.run() with a persistent loop so
   Agno's MCP sessions survive across conversation turns. No more
   'Event loop is closed' errors on turn 2+.

2. Markdown stripping: voice preamble tells Timmy to respond in
   natural spoken language, plus _strip_markdown() as a safety net
   removes **bold**, *italic*, bullets, headers, code fences, etc.
   TTS no longer reads 'asterisk asterisk'.

3. MCP noise: _suppress_mcp_noise() quiets mcp/agno/httpx loggers
   during voice mode so the terminal shows clean transcript only.

32 tests (12 new for markdown stripping + persistent loop).
2026-03-14 14:05:24 -04:00
dbadfc425d feat: sovereign voice loop — timmy voice command
Adds fully local listen-think-speak voice interface.
STT: Whisper, LLM: Ollama, TTS: Piper. No cloud, no network.

- src/timmy/voice_loop.py: VoiceLoop with VAD, Whisper, Piper
- src/timmy/cli.py: new voice command
- pyproject.toml: voice extras updated
- 20 new tests
2026-03-14 13:58:56 -04:00
2f623826bd cleanup: delete dead modules — ~7,900 lines removed
Closes #22, Closes #23

Deleted: brain/, swarm/, openfang/, paperclip/, cascade_adapter,
memory_migrate, agents/timmy.py, dead routes + all corresponding tests.

Updated pyproject.toml, app.py, loop_qa.py for removed imports.
2026-03-14 09:49:24 -04:00