feat(03-02): add /timeout and /sessions commands
- Added timeout_cmd() to set per-session idle timeout (1-120 min range) - Shows current timeout when called without args - Updates session metadata and existing idle timer - Added sessions_cmd() to list all sessions with status - Shows LIVE (subprocess running) or IDLE (suspended) status - Displays persona and relative last-active time (e.g., '2m ago') - Marks current active session with arrow - Registered both commands in main() - Commands already added to help text in Task 1 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6ebdb4a555
commit
06c52466f2
1 changed files with 94 additions and 0 deletions
|
|
@ -1025,6 +1025,98 @@ async def model_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||
await update.message.reply_text(f"Model set to {resolved} for session '{active_session}'.")
|
||||
logger.info(f"Model changed to {resolved} for session '{active_session}'")
|
||||
|
||||
async def timeout_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Set idle timeout for current session."""
|
||||
if not is_authorized(update.effective_user.id):
|
||||
return
|
||||
|
||||
active_session = session_manager.get_active_session()
|
||||
if not active_session:
|
||||
await update.message.reply_text("No active session. Use /new <name> to start one.")
|
||||
return
|
||||
|
||||
if not context.args:
|
||||
# Show current timeout
|
||||
timeout_secs = session_manager.get_session_timeout(active_session)
|
||||
minutes = timeout_secs // 60
|
||||
await update.message.reply_text(
|
||||
f"Idle timeout: {minutes} minutes\n\nUsage: /timeout <minutes> (1-120)"
|
||||
)
|
||||
return
|
||||
|
||||
# Parse and validate timeout value
|
||||
try:
|
||||
minutes = int(context.args[0])
|
||||
if minutes < 1 or minutes > 120:
|
||||
await update.message.reply_text("Timeout must be between 1 and 120 minutes")
|
||||
return
|
||||
except ValueError:
|
||||
await update.message.reply_text("Invalid number. Usage: /timeout <minutes>")
|
||||
return
|
||||
|
||||
# Convert to seconds and update
|
||||
timeout_seconds = minutes * 60
|
||||
session_manager.update_session(active_session, idle_timeout=timeout_seconds)
|
||||
|
||||
# Update existing idle timer if present
|
||||
if active_session in idle_timers:
|
||||
idle_timers[active_session].timeout_seconds = timeout_seconds
|
||||
idle_timers[active_session].reset()
|
||||
|
||||
await update.message.reply_text(
|
||||
f"Idle timeout set to {minutes} minutes for session '{active_session}'."
|
||||
)
|
||||
logger.info(f"Idle timeout set to {minutes} minutes for session '{active_session}'")
|
||||
|
||||
async def sessions_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""List all sessions with status."""
|
||||
if not is_authorized(update.effective_user.id):
|
||||
return
|
||||
|
||||
sessions = session_manager.list_sessions()
|
||||
if not sessions:
|
||||
await update.message.reply_text("No sessions. Use /new <name> to create one.")
|
||||
return
|
||||
|
||||
active_session = session_manager.get_active_session()
|
||||
|
||||
def format_relative_time(iso_str):
|
||||
"""Format ISO timestamp as relative time (e.g., '2m ago')."""
|
||||
dt = datetime.fromisoformat(iso_str)
|
||||
delta = datetime.now(timezone.utc) - dt
|
||||
secs = delta.total_seconds()
|
||||
if secs < 60:
|
||||
return "just now"
|
||||
if secs < 3600:
|
||||
return f"{int(secs/60)}m ago"
|
||||
if secs < 86400:
|
||||
return f"{int(secs/3600)}h ago"
|
||||
return f"{int(secs/86400)}d ago"
|
||||
|
||||
lines = []
|
||||
for s in sessions:
|
||||
name = s['name']
|
||||
persona = s.get('persona', 'default')
|
||||
|
||||
# Determine status
|
||||
if name in subprocesses and subprocesses[name].is_alive:
|
||||
status = "🟢 LIVE"
|
||||
elif s.get('status') == 'suspended':
|
||||
status = "⚪ IDLE"
|
||||
else:
|
||||
status = s.get('status', 'unknown').upper()
|
||||
|
||||
# Format last active time
|
||||
last_active = s.get('last_active', '')
|
||||
rel_time = format_relative_time(last_active) if last_active else "unknown"
|
||||
|
||||
# Mark current active session
|
||||
marker = "→ " if name == active_session else " "
|
||||
|
||||
lines.append(f"{marker}{status} `{name}` ({persona}) - {rel_time}")
|
||||
|
||||
await update.message.reply_text("\n".join(lines), parse_mode='Markdown')
|
||||
|
||||
async def unknown(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Handle unknown commands."""
|
||||
if not is_authorized(update.effective_user.id):
|
||||
|
|
@ -1113,8 +1205,10 @@ def main():
|
|||
app.add_handler(CommandHandler("chatid", chatid))
|
||||
app.add_handler(CommandHandler("new", new_session))
|
||||
app.add_handler(CommandHandler("session", switch_session_cmd))
|
||||
app.add_handler(CommandHandler("sessions", sessions_cmd))
|
||||
app.add_handler(CommandHandler("archive", archive_session_cmd))
|
||||
app.add_handler(CommandHandler("model", model_cmd))
|
||||
app.add_handler(CommandHandler("timeout", timeout_cmd))
|
||||
app.add_handler(CommandHandler("status", status))
|
||||
app.add_handler(CommandHandler("pbs", pbs))
|
||||
app.add_handler(CommandHandler("pbs_status", pbs_status))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue