Skip to content

feat: interactive enhancements — AskUserQuestion, export, browse, cost, triggers#1

Merged
Nathan Schram (nathanschram) merged 1 commit intomasterfrom
feature/interactive-enhancements
Feb 25, 2026
Merged

feat: interactive enhancements — AskUserQuestion, export, browse, cost, triggers#1
Nathan Schram (nathanschram) merged 1 commit intomasterfrom
feature/interactive-enhancements

Conversation

@nathanschram
Copy link
Copy Markdown
Member

Summary

Major feature batch adding interactive UX, cost visibility, and file browsing to Untether:

  • AskUserQuestion support (A1) — Claude's clarifying questions shown in Telegram; user replies with text, answer routed back via control channel. Supports both {"question": "..."} and {"questions": [{"question": "..."}]} input formats
  • /export command (A3) — Session transcript export as markdown or JSON (/export json). Records up to 20 sessions with full event timeline and usage stats
  • Cost budget system (A4) — Configurable per-run/daily budgets via [cost_budget] in untether.toml with warning thresholds and optional auto-cancel
  • Cost display (CC5) — Running cost shown in progress footer: 💰 $0.37 · 9 turns · 1m 47s API · 11 in / 1.2k out
  • Diff preview (CC4) — Compact Edit/Write/Bash previews in tool approval messages
  • /browse command (CC6) — Navigate project files via inline keyboard buttons. Project-aware per chat route, 2-per-row button layout, path registry for 64-byte callback_data limit
  • Trigger system — Webhook HTTP server (aiohttp) + cron scheduler, Jinja2 templating, HMAC/bearer auth, rate limiting
  • Inline cancel button (A5) — Cancel button alongside approval buttons on progress messages

Test plan

  • 812 tests passing, 80.69% coverage
  • 0 ruff lint errors
  • Live tested via Telegram MCP: /browse, /export, /export json, AskUserQuestion flow, cost display, approval buttons, chat routing across multiple project groups
  • Docs updated: CLAUDE.md, runner docs, rules

🤖 Generated with Claude Code

…ost tracking, diff preview, triggers

Phase 1 — Quick wins:
- Inline cancel button on progress messages (A5)
- Cost display in progress footer: 💰 $X.XX · N turns · Xs API (CC5)
- /export command for markdown/JSON session transcripts (A3)

Phase 2 — Interactive enhancements:
- AskUserQuestion support: Claude's questions shown in Telegram, user replies
  with text, answer routed back via control channel (A1)
- Diff preview in tool approval messages: compact Edit/Write/Bash previews (CC4)
- Cost budget system: per-run/daily budgets with warning thresholds (A4)

Phase 3 — File browser & triggers:
- /browse command: navigate project files via inline keyboard buttons (CC6)
  - Project-aware root resolution per chat route
  - 2-per-row button layout, path registry for 64-byte callback_data limit
- Trigger system: webhook HTTP server + cron scheduler, Jinja2 templating,
  HMAC/bearer auth, rate limiting

Tests: 812 passing, 80.69% coverage, 0 lint errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@nathanschram Nathan Schram (nathanschram) merged commit e0673dc into master Feb 25, 2026
2 checks passed
@nathanschram Nathan Schram (nathanschram) deleted the feature/interactive-enhancements branch February 25, 2026 07:11
Nathan Schram (nathanschram) added a commit that referenced this pull request Mar 10, 2026
…sions

- Add CodeQL code scanning workflow (Python + Actions) on push, PRs, and weekly schedule
- Add Dependabot config for weekly github-actions and pip dependency updates
- Add permissions: {} to notify-website.yml (resolves code scanning alert #1)
- Update CLAUDE.md CI pipeline table with CodeQL entry

Also configured via GitHub API (not in this commit):
- Repo ruleset "CI Required Checks" requiring all 14 CI checks to pass before merge
- Secret scanning AI detection enabled
- Code scanning alerts #2 and #3 dismissed as "used in tests" (false positives)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Nathan Schram (nathanschram) added a commit that referenced this pull request Apr 15, 2026
…rs, runner, telegram, docs

CodeRabbit flagged 1 critical + 15 major + 4 outside-diff issues during
review of the v0.35.1 release PR. After triage, 15 are real bugs worth
fixing before release; 3 are false positives and 2 are deferred.

P0 — Critical functional regressions in v0.35.1 features:
- triggers/cron.py: daily/weekly crons stop after first day. last_fired
  was keyed by (hour, minute) only, so tomorrow's 09:00 looked identical
  to today's and got suppressed forever. Now keyed by full date+time. (#11)
- runner_bridge.py: tree_active subagent runs were force-killed at
  STALL_MAX_WARNINGS because the auto-cancel exemption only checked
  cpu_active. Now exempts tree_active when active children present. (#4)
- triggers/actions.py: append_timestamp on_conflict used second-resolution
  names so two requests in the same second clobbered each other (the
  feature is meant to PREVENT that). Now uses time.time_ns() with a
  collision probe loop. (#9)

P1 — Functional bugs in supporting features:
- runners/claude.py: _PLAN_EXIT_APPROVED was only set in interactive
  approval. Auto-approve drain (auto permission mode + post-discuss
  approval) skipped the bookkeeping, defeating #283 in those flows. (#5)
- telegram/parsing.py: offset was persisted before yield, so a crash
  between yield and consumer could record an unprocessed update as done.
  Now persists after yield. (#8)
- triggers/rate_limit.py: per-hit warning logs flooded structured output
  and fed the issue watcher. Dropped to debug. (#16)
- telegram/commands/config.py: reasoning levels weren't validated against
  engine support — manual callback_data could persist e.g. `max` on
  Codex. Defensive validation added. (#20)
- telegram/bridge.py: docstring claimed chat_ids was hot-reloadable but
  it's never sourced from settings. Corrected docstring (#6).

P2 — Doc/display quality:
- triggers/describe.py: invalid DOW values were normalised via % 7 (8 →
  Mon); stepped patterns like */2 in dom/mon were rendered as "daily"
  due to substring check. Both now fall back to raw schedule. (#13, #14)
- config.py: rs:max action had no toast entry. Added. (#19)
- README.md: access matrix overstated isolation, omitted /file put,
  outbox, and webhook file_write/http_forward actions. Now accurate. (#3)
- docs/reference/integration-testing.md: bot ID was reused as DM chat
  ID. Clarified that DM target is the user's chat ID. (#1)
- docs/reference/specification.md: header said v0.35.1 but body said
  "Untether v0.35.0 specifies:". Synced to v0.35.1. (#17)
- tests/test_claude_control.py: cleanup fixture only ran post-yield.
  Now runs pre- and post-yield. (#18)

Skipped (false positives or out of scope):
- #2 (version bump suggestion): user decision, not auto-applicable.
- #7 (restart-only keys hot-reloaded): false positive — update_from is
  selective and only touches hot fields.
- #12 (cron snapshot): false positive — remove_cron replaces the list,
  doesn't mutate in place.

Deferred to follow-up issues:
- #10 (Content-Type override on http_forward raw bodies)
- #15 (duplicate cron ID/webhook path validation)

Test additions:
- test_trigger_cron.py: regression test for daily cron firing on
  consecutive days (covers #11)
- test_describe_cron.py: 6 regression tests for #13/#14 fallback paths
- test_config_command.py: split per-engine reasoning level test +
  regression test for codex/rs:max rejection (covers #20)
- test_claude_control.py: clarified pre/post-yield cleanup (covers #18)

Verified: 2172 tests pass (was 2164, +8 new), 81.51% coverage,
ruff format/lint clean, build successful.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nathan Schram (nathanschram) added a commit that referenced this pull request Apr 15, 2026
…rs, runner, telegram, docs (#311)

CodeRabbit flagged 1 critical + 15 major + 4 outside-diff issues during
review of the v0.35.1 release PR. After triage, 15 are real bugs worth
fixing before release; 3 are false positives and 2 are deferred.

P0 — Critical functional regressions in v0.35.1 features:
- triggers/cron.py: daily/weekly crons stop after first day. last_fired
  was keyed by (hour, minute) only, so tomorrow's 09:00 looked identical
  to today's and got suppressed forever. Now keyed by full date+time. (#11)
- runner_bridge.py: tree_active subagent runs were force-killed at
  STALL_MAX_WARNINGS because the auto-cancel exemption only checked
  cpu_active. Now exempts tree_active when active children present. (#4)
- triggers/actions.py: append_timestamp on_conflict used second-resolution
  names so two requests in the same second clobbered each other (the
  feature is meant to PREVENT that). Now uses time.time_ns() with a
  collision probe loop. (#9)

P1 — Functional bugs in supporting features:
- runners/claude.py: _PLAN_EXIT_APPROVED was only set in interactive
  approval. Auto-approve drain (auto permission mode + post-discuss
  approval) skipped the bookkeeping, defeating #283 in those flows. (#5)
- telegram/parsing.py: offset was persisted before yield, so a crash
  between yield and consumer could record an unprocessed update as done.
  Now persists after yield. (#8)
- triggers/rate_limit.py: per-hit warning logs flooded structured output
  and fed the issue watcher. Dropped to debug. (#16)
- telegram/commands/config.py: reasoning levels weren't validated against
  engine support — manual callback_data could persist e.g. `max` on
  Codex. Defensive validation added. (#20)
- telegram/bridge.py: docstring claimed chat_ids was hot-reloadable but
  it's never sourced from settings. Corrected docstring (#6).

P2 — Doc/display quality:
- triggers/describe.py: invalid DOW values were normalised via % 7 (8 →
  Mon); stepped patterns like */2 in dom/mon were rendered as "daily"
  due to substring check. Both now fall back to raw schedule. (#13, #14)
- config.py: rs:max action had no toast entry. Added. (#19)
- README.md: access matrix overstated isolation, omitted /file put,
  outbox, and webhook file_write/http_forward actions. Now accurate. (#3)
- docs/reference/integration-testing.md: bot ID was reused as DM chat
  ID. Clarified that DM target is the user's chat ID. (#1)
- docs/reference/specification.md: header said v0.35.1 but body said
  "Untether v0.35.0 specifies:". Synced to v0.35.1. (#17)
- tests/test_claude_control.py: cleanup fixture only ran post-yield.
  Now runs pre- and post-yield. (#18)

Skipped (false positives or out of scope):
- #2 (version bump suggestion): user decision, not auto-applicable.
- #7 (restart-only keys hot-reloaded): false positive — update_from is
  selective and only touches hot fields.
- #12 (cron snapshot): false positive — remove_cron replaces the list,
  doesn't mutate in place.

Deferred to follow-up issues:
- #10 (Content-Type override on http_forward raw bodies)
- #15 (duplicate cron ID/webhook path validation)

Test additions:
- test_trigger_cron.py: regression test for daily cron firing on
  consecutive days (covers #11)
- test_describe_cron.py: 6 regression tests for #13/#14 fallback paths
- test_config_command.py: split per-engine reasoning level test +
  regression test for codex/rs:max rejection (covers #20)
- test_claude_control.py: clarified pre/post-yield cleanup (covers #18)

Verified: 2172 tests pass (was 2164, +8 new), 81.51% coverage,
ruff format/lint clean, build successful.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant