Skip to content

test#1

Closed
Jack-261108 wants to merge 50 commits into
refactor/session-layer-decompositionfrom
master
Closed

test#1
Jack-261108 wants to merge 50 commits into
refactor/session-layer-decompositionfrom
master

Conversation

@Jack-261108

Copy link
Copy Markdown

No description provided.

Jack added 30 commits May 21, 2026 22:37
…de sessions

Implement full external session discovery, binding, and management:

- SessionOwnershipResolver: first gate in hook pipeline with strict
  priority (tmux-owned → external-bound → unbound). No workdir auto-match.
- ExternalSessionDiscoveryService: tracks unbound sessions from hook events
- ExternalBindingStore: persists bindings as JSON for restart survival
- ExternalSessionBinder: bind/unbind with immediate JSONL path resolution
- UnboundPermissionHandler: broadcasts to all allowed users, first-responder
  wins, auto-deny on TTL expiry
- ExternalSessionPushNotifier: independent Telegram push for bound sessions
- Telegram bot commands: /external list|bind|unbind|status
- Hook pipeline restructured to use ownership resolver as first gate

16 property-based tests (Hypothesis) + 4 integration tests + 8 unit tests.
550 tests pass.
Users can now type partial session IDs (e.g. '90fcaa30-564') and the
system will auto-resolve to the full session_id if unambiguous.
These were implemented but never instantiated in bootstrap, so bound
external session permission requests were silently not forwarded.
- Push notifier sends InlineKeyboardMarkup with Approve/Deny buttons
- UnboundPermissionHandler also uses inline buttons
- New callback handler routes ext_perm:{tool_use_id}:{decision} callbacks
- Edits original message to show decision result
- Prevents text-based permission responses being eaten by claude chat
Claude Code hook protocol expects 'allow'/'deny' in the decision field,
not 'approve'/'deny'. This was causing the terminal to not respond after
the user clicked Approve in Telegram.
AskUserQuestion is a special PermissionRequest where Claude just needs
permission to display a question. The user answers directly in the
terminal. Auto-allow these instead of showing Approve/Deny buttons.

For bound sessions: auto-allow + send info push '❓ 等待用户输入'
For unbound sessions: auto-allow (no broadcast needed)
When an external session receives an AskUserQuestion, display the
question text and numbered options in Telegram so the user knows
what to select in their terminal. Auto-allow is preserved — user
still answers directly in the terminal.
When an external Claude session asks a user question and is running
inside tmux, show option buttons in Telegram. When the user clicks:
1. Find the tmux pane containing Claude via PID process tree walk
2. Inject keystrokes (Down arrows + Enter) via tmux send-keys
3. Respond to the hook permission with 'allow'

Falls back to read-only notification if no tmux pane found.

New components:
- pty_injector: finds tmux panes and injects keystrokes
- ExternalUserQuestionState: tracks pending questions for callback routing
- ext_uq callback handler: processes button clicks and triggers injection
…auto-approve

- Register 11 bot commands via set_my_commands on startup
- /list shows external sessions with inline buttons (bind/unbind)
- Add PID liveness check to prune dead external sessions
- Add auto-approve button to all permission flows
- Auto-approve intercepts hook events before user notification
- Sends silent notification on auto-approved permissions
- /deny disables auto-approve for current session
- SessionEnd and health check cleanup auto-approve state
- Discover built-in commands (/compact, /clear, /resume, etc.)
- Scan ~/.claude/skills/ for user-level skills
- Scan <workdir>/.claude/commands/ and .claude/skills/ for project-level
- Display as categorized inline keyboard (builtin/user/skill/project)
- Clicking a button sends the slash command to the active Claude session
- Register /cmds in Telegram bot menu
/resume:
- SessionScanner scans ~/.claude/projects/<workdir>/ for past JSONL sessions
- Displays recent sessions as inline keyboard (date + first prompt summary)
- Clicking resumes via claude --resume <session-id> in tmux
- TmuxCommands extended with _build_interactive_claude_resume_command
- TerminalSessionService.open_claude_resume_session handles full lifecycle

/cmds:
- Discovers built-in Claude commands (/compact, /clear, /model)
- Scans ~/.claude/skills/ for user-level skills
- Scans <workdir>/.claude/commands/ and .claude/skills/ for project-level
- Renders as categorized inline keyboard, clicking sends to Claude session

Both registered in Telegram bot menu (now 13 commands total).
…ives

- FileSenderService detects PostToolUse Write events via hook pipeline
- Classifies files by extension (image → send_photo, document → send_document)
- Respects Telegram size limits (10MB photos, 50MB documents)
- Fire-and-forget via asyncio.create_task (non-blocking)
- Works for both owned (tmux) and bound (external) sessions
- Configurable via AUTO_FILE_SEND_ENABLED and AUTO_FILE_SEND_EXTENSIONS env vars
- Default extensions: png,jpg,jpeg,gif,webp,svg,pdf,docx,xlsx,csv,html,zip,tar.gz
Add configurable TTL and cleanup limits for task storage, rate-limit buckets, and async lock registries so long-running processes can shed stale in-memory state.
Jack added 20 commits May 26, 2026 16:34
…r automatic cleanup

TmuxRunner._session_locks was a plain dict[str, asyncio.Lock] that grew
without bound. Replace it with RefCountedLockRegistry so expired locks
are cleaned up after TTL. Pass lock settings from bootstrap.
- Add _clear_seen_mtimes_for_session for prefix-based mtime cleanup
- Add _cleanup_finished_session to properly clean tasks, mtime keys, and locks
- Fix forget() to clear all mtime keys for a session, not just exact match
- Fix forget() to defer lock cleanup for still-running watcher tasks
- Fix stop_all() to clear _session_locks after awaiting all tasks
- Fix _watch_session finally block to use _cleanup_finished_session
The test_run_prompt_and_stream_interactive_reports_pending_permission_once
test was using the old tool_use_id-based callback format. Updated to use
PermissionCallbackRegistry short tokens and verify token resolution.
- Bounded upload queue with per-user limits and pre-download rejection
- Permission callback short tokens (PermissionCallbackRegistry)
- Delayed queued upload processing after task completion
- Unbound permission pending cleanup with lock
- Tmux session lock cleanup via RefCountedLockRegistry
- AgentFileWatcher state cleanup (prefix mtime, deferred lock release)
Add TTL cleanup for queued uploads and tighten reattach/permission state handling so stale sessions do not block later runs.
Update stale queue, permission, and watcher expectations so the regression suite reflects the current cleanup flow.
@Jack-261108 Jack-261108 deleted the branch refactor/session-layer-decomposition May 27, 2026 14:32
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