Skip to content

feat: /close slash command to manually terminate a thread session#42

Open
qijie850 wants to merge 4 commits intoopenabdev:mainfrom
qijie850:feat/close-command
Open

feat: /close slash command to manually terminate a thread session#42
qijie850 wants to merge 4 commits intoopenabdev:mainfrom
qijie850:feat/close-command

Conversation

@qijie850
Copy link
Copy Markdown

@qijie850 qijie850 commented Apr 4, 2026

Summary

Adds a Discord slash command /close that lets users manually terminate a coding session inside a thread.

Closes #40
Depends on #41 (per-thread working directories)

Changes

src/discord.rs:

  • Register /close as a global slash command on ready
  • Handle InteractionCreate events for the /close command
  • Validates it's called inside a thread before closing

src/acp/pool.rs:

  • Add close_session(thread_id) method that kills the ACP process and removes the per-thread working directory

Design Decision: Slash Command vs Text Matching

Per @thepagent's feedback on #40, this uses Discord slash commands (InteractionCreate event) instead of text matching to avoid shadowing agent-side /close commands:

Text message "/close"  ──> forwarded to ACP agent (agent's command)
Slash command /close   ──> handled by broker (Discord interaction)

Zero collision between broker commands and agent commands.

Behavior

  • /close in a thread → kills session + cleans workdir → "\u2705 Session closed."
  • /close with no active session → "\u2139\ufe0f No active session in this thread."
  • /close outside a thread → "\u26a0\ufe0f Only works inside a thread."

Testing

Compiled and verified against serenity 0.12 on EC2 (Ubuntu 24.04). Slash command registration and interaction handling tested locally.

Each Discord thread now gets its own working directory under
<working_dir>/sessions/<thread_id>/, preventing file conflicts
when multiple threads run concurrently.

Directories are automatically cleaned up when sessions are
reaped by cleanup_idle.

Closes openabdev#38

Co-authored-by: wsxqaza12 <wsxqaza12@users.noreply.github.com>
Copy link
Copy Markdown
Collaborator

@thepagent thepagent left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work using Discord slash commands — exactly the approach we discussed in #40. A few items:

Must fix:

  1. Overlapping diff with #41 — This PR includes the same pool.rs per-thread dir changes from #41. Please rebase onto #41's branch (or wait for #41 to merge, then rebase). Otherwise we'll get merge conflicts.

  2. Carry over #41 review fixes — The pool.rs changes here have the same issues flagged in #41 review:

    • Path traversal hardening (thread_id validation)
    • shutdown() directory cleanup
    • sync exists() → use tokio::fs or just handle NotFound
    • Extract thread_dir() helper (now duplicated 3 times)
  3. Make response ephemeral/close response is visible to everyone. Add .ephemeral(true) so only the caller sees it:

CreateInteractionResponseMessage::new()
    .content(response_content)
    .ephemeral(true)
  1. Global command registration on every restartcreate_global_command in ready runs on every bot restart. Global commands have rate limits and take up to 1 hour to propagate. Consider:
    • Use guild commands instead (instant propagation, registered per-server)
    • Or gate with a one-time check

Looks good otherwise:

  • Lock scope in close_session is well done (release write lock before async fs cleanup)
  • Thread detection via thread_metadata.is_some() is correct
  • Clean separation between slash command handling and text message flow

Suggested merge order: land #41 first → rebase this PR → address feedback → merge.

黃琪婕 and others added 2 commits April 5, 2026 08:24
- Add thread_id path traversal validation (ASCII digits only)
- Clean up session directories in shutdown()
- Replace sync exists() check with async NotFound handling
- Extract thread_dir() helper to deduplicate path construction

Co-authored-by: wsxqaza12 <wsxqaza12@users.noreply.github.com>
…, add sessions/ to .gitignore

Address review feedback from neilkuan (openabdev#41 comment):
- cleanup_idle/shutdown: collect paths and release write lock before
  async fs operations so concurrent get_or_create() calls aren't blocked
- get_or_create: clear old session directory when rebuilding a stale
  connection so the replacement agent starts clean
- Add sessions/ to .gitignore to prevent accidental staging

Co-authored-by: wsxqaza12 <wsxqaza12@users.noreply.github.com>
@qijie850
Copy link
Copy Markdown
Author

qijie850 commented Apr 5, 2026

Thanks for the review! Agreed on all points. Here's the plan:

Merge order: Will wait for #41 to land first, then rebase this PR on top — that automatically resolves #1 and #2 (the overlapping pool.rs changes + review fixes).

#3 — Ephemeral response: Will add .ephemeral(true) to the /close response. Makes sense since it's an admin action.

#4 — Guild commands: Good call on the rate limit issue. Will switch to guild commands (create_guild_command) for instant propagation and no 1-hour delay. We can gate it per-guild in ready.

Will push the fixes once #41 is merged and the rebase is clean!

Adds a Discord slash command /close that lets users manually close
a coding session inside a thread:
- Kills the ACP child process (frees RAM)
- Removes the per-thread working directory
- Responds with an ephemeral confirmation

Implementation notes:
- Uses guild commands (instant propagation) instead of global commands
- Response is ephemeral (only visible to the caller)
- close_session releases the write lock before async I/O
- Text messages with /close pass through to the agent untouched

Closes openabdev#40

Co-authored-by: wsxqaza12 <wsxqaza12@users.noreply.github.com>
@qijie850 qijie850 force-pushed the feat/close-command branch from ca2a736 to 735ad8c Compare April 5, 2026 06:36
@qijie850
Copy link
Copy Markdown
Author

qijie850 commented Apr 5, 2026

Rebased onto #41 (including all review fixes) and addressed all feedback:

Changes:

  1. Rebased onto feat: per-thread isolated working directories #41 — no more overlapping pool.rs diff. This PR now only adds close_session() to pool and the slash command handler to discord.

  2. Inherits feat: per-thread isolated working directories #41 fixes — path traversal hardening, shutdown() cleanup, thread_dir() helper, async-safe NotFound handling are all in the base.

  3. Ephemeral response — added .ephemeral(true) so only the caller sees the confirmation.

  4. Guild commands instead of global — registers /close per-guild in ready() using guild_id.create_command(). Instant propagation, no global rate limit concerns. Re-registration on restart is idempotent for guild commands.

  5. Lock hygiene in close_session — releases write lock before async remove_dir_all, consistent with the pattern established in feat: per-thread isolated working directories #41.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: /close command to manually terminate a thread session

3 participants