- Início
- Documentation
- Sessões
- Alternância de sessões e listagem de sessões recentes
Alternância de sessões e listagem de sessões recentes
Este documento descreve como o coding-agent descobre sessões recentes, resolve alvos de --resume, apresenta seletores de sessão e alterna a sessão ativa em tempo de execução.
O foco está no comportamento da implementação atual, incluindo caminhos de fallback e ressalvas.
Arquivos de implementação
Seção intitulada “Arquivos de implementação”../src/session/session-manager.ts../src/session/agent-session.ts../src/cli/session-picker.ts../src/modes/components/session-selector.ts../src/modes/controllers/selector-controller.ts../src/main.ts../src/sdk.ts../src/modes/interactive-mode.ts../src/modes/utils/ui-helpers.ts
Descoberta de sessões recentes
Seção intitulada “Descoberta de sessões recentes”Escopo de diretório
Seção intitulada “Escopo de diretório”O SessionManager armazena sessões em um diretório com escopo de cwd por padrão:
~/.xcsh/agent/sessions/--<cwd-encoded>--/*.jsonl
SessionManager.list(cwd, sessionDir?) lê apenas esse diretório, a menos que um sessionDir explícito seja fornecido.
Dois caminhos de listagem com payloads diferentes
Seção intitulada “Dois caminhos de listagem com payloads diferentes”Existem dois pipelines de listagem diferentes:
-
getRecentSessions(sessionDir, limit)(visão de boas-vindas/resumo)- Lê apenas um prefixo de 4KB (
readTextPrefix(..., 4096)) de cada arquivo. - Analisa o cabeçalho + prévia do texto mais antigo do usuário.
- Retorna
RecentSessionInfoleve com getters lazy denameetimeAgo. - Ordena por
mtimedo arquivo em ordem decrescente.
- Lê apenas um prefixo de 4KB (
-
SessionManager.list(...)/SessionManager.listAll()(seletores de retomada e correspondência por ID)- Lê os arquivos de sessão completos.
- Constrói objetos
SessionInfo(id,cwd,title,messageCount,firstMessage,allMessagesText, timestamps). - Descarta sessões com zero entradas
message. - Ordena por
modifiedem ordem decrescente.
Comportamento de fallback de metadados
Seção intitulada “Comportamento de fallback de metadados”Para resumos recentes (RecentSessionInfo):
- preferência de nome de exibição:
header.title-> primeiro prompt do usuário ->header.id-> nome do arquivo - o nome é truncado para 40 caracteres em exibições compactas
- caracteres de controle/quebras de linha são removidos/sanitizados de nomes derivados do título
Para entradas de lista SessionInfo:
titleéheader.titleou oshortSummaryda compactação mais recentefirstMessageé o texto da primeira mensagem do usuário ou"(no messages)"
Resolução de --continue e preferência de breadcrumb do terminal
Seção intitulada “Resolução de --continue e preferência de breadcrumb do terminal”SessionManager.continueRecent(cwd, sessionDir?) resolve o alvo nesta ordem:
- Lê o breadcrumb com escopo de terminal (
~/.xcsh/agent/terminal-sessions/<terminal-id>) - Valida o breadcrumb:
- o terminal atual pode ser identificado
- o cwd do breadcrumb corresponde ao cwd atual (comparação de caminho resolvido)
- o arquivo referenciado ainda existe
- Se o breadcrumb for inválido/ausente, faz fallback para o arquivo mais recente por mtime no diretório de sessões (
findMostRecentSession) - Se nenhum for encontrado, cria uma nova sessão
A derivação do ID do terminal prefere o caminho TTY e faz fallback para identificadores baseados em variáveis de ambiente (KITTY_WINDOW_ID, TMUX_PANE, TERM_SESSION_ID, WT_SESSION).
As escritas de breadcrumb são best-effort e não fatais.
Resolução do alvo de retomada no tempo de inicialização (main.ts)
Seção intitulada “Resolução do alvo de retomada no tempo de inicialização (main.ts)”--resume <valor>
Seção intitulada “--resume <valor>”createSessionManager(...) trata --resume com valor string em dois modos:
-
Valor semelhante a caminho (contém
/,\\, ou termina com.jsonl)SessionManager.open(sessionArg, parsed.sessionDir)direto
-
Valor de prefixo de ID
- encontra correspondência em
SessionManager.list(cwd, sessionDir)porid.startsWith(sessionArg) - se não houver correspondência local e
sessionDirnão for forçado, tentaSessionManager.listAll() - a primeira correspondência é usada (sem prompt de ambiguidade)
- encontra correspondência em
Comportamento de correspondência entre projetos:
- se o cwd da sessão correspondente difere do cwd atual, o CLI pergunta se deseja fazer fork para o projeto atual
- sim ->
SessionManager.forkFrom(...) - não -> lança erro (
Session "..." is in another project (...))
Sem correspondência -> lança erro (Session "..." not found.).
--resume (sem valor)
Seção intitulada “--resume (sem valor)”Tratado após a construção inicial do session-manager:
- lista sessões locais com
SessionManager.list(cwd, parsed.sessionDir) - se vazio: imprime
No sessions founde encerra antecipadamente - abre o seletor TUI (
selectSession) - se cancelado: imprime
No session selectede encerra antecipadamente - se selecionado:
SessionManager.open(selectedPath)
--continue
Seção intitulada “--continue”Usa SessionManager.continueRecent(...) diretamente (comportamento breadcrumb-first descrito acima).
Detalhes internos da seleção por picker
Seção intitulada “Detalhes internos da seleção por picker”Picker CLI (src/cli/session-picker.ts)
Seção intitulada “Picker CLI (src/cli/session-picker.ts)”selectSession(sessions) cria uma TUI standalone com SessionSelectorComponent e resolve exatamente uma vez:
- seleção -> resolve o caminho selecionado
- cancelar (Esc) -> resolve
null - saída forçada (caminho Ctrl+C) -> para a TUI e
process.exit(0)
Picker interativo em sessão (SelectorController.showSessionSelector)
Seção intitulada “Picker interativo em sessão (SelectorController.showSessionSelector)”Fluxo:
- busca sessões do diretório de sessão atual via
SessionManager.list(currentCwd, currentSessionDir) - monta
SessionSelectorComponentna área do editor usandoshowSelector(...) - callbacks:
- seleção -> fecha o seletor e chama
handleResumeSession(sessionPath) - cancelar -> restaura o editor e rerenderiza
- sair ->
ctx.shutdown()
- seleção -> fecha o seletor e chama
Comportamento do componente seletor de sessão
Seção intitulada “Comportamento do componente seletor de sessão”SessionList suporta:
- navegação por setas/página
- Enter para selecionar
- Esc para cancelar
- Ctrl+C para sair
- busca fuzzy entre id/title/cwd/primeira mensagem/todas as mensagens/caminho da sessão
Comportamento de renderização com lista vazia:
- renderiza uma mensagem em vez de falhar
- Enter em lista vazia não faz nada (sem callback)
- Esc/Ctrl+C continuam funcionando
Ressalva: O texto da UI diz Press Tab to view all, mas este componente atualmente não possui handler para Tab e a conexão atual lista apenas sessões do escopo atual.
Execução da alternância em tempo de execução (AgentSession.switchSession)
Seção intitulada “Execução da alternância em tempo de execução (AgentSession.switchSession)”switchSession(sessionPath) é o caminho principal de alternância em processo.
Ciclo de vida/transição de estado:
- captura
previousSessionFile - emite evento de hook
session_before_switch(reason: "resume", cancelável) - se cancelado -> retorna
falsesem alternância - desconecta do stream de eventos do agente atual
- aborta geração/fluxo de ferramenta ativo
- limpa buffers de mensagens enfileiradas de steering/follow-up/próximo turno
- faz flush do writer de sessão (
sessionManager.flush()) para persistir escritas pendentes sessionManager.setSessionFile(sessionPath)- atualiza o ponteiro do arquivo de sessão
- escreve breadcrumb do terminal
- carrega entradas / migra / resolve blobs / reindexa
- se dados do arquivo estiverem ausentes/inválidos: inicializa uma nova sessão naquele caminho e reescreve o cabeçalho
- atualiza
agent.sessionId - reconstrói o contexto via
buildSessionContext() - emite evento de hook
session_switch(reason: "resume",previousSessionFile) - substitui mensagens do agente pelo contexto reconstruído
- restaura o modelo padrão de
sessionContext.models.defaultse disponível e presente no registro de modelos - restaura o nível de pensamento:
- se o branch já possui
thinking_level_change, aplica o nível de sessão salvo - caso contrário, deriva o nível de pensamento padrão das configurações, limita à capacidade do modelo, define-o e adiciona uma nova entrada
thinking_level_change
- se o branch já possui
- reconecta listeners do agente e retorna
true
Reconstrução do estado da UI após alternância interativa
Seção intitulada “Reconstrução do estado da UI após alternância interativa”SelectorController.handleResumeSession realiza reset da UI ao redor de switchSession:
- para a animação de carregamento
- limpa o contêiner de status
- limpa a UI de mensagem pendente e o mapa de ferramentas pendentes
- reseta referências de componente/mensagem de streaming
- chama
session.switchSession(...) - limpa o contêiner de chat e rerenderiza a partir do contexto da sessão (
renderInitialMessages) - recarrega todos a partir dos artefatos da nova sessão
- exibe
Resumed session
Portanto, o estado visível de conversa/todos é reconstruído a partir do novo arquivo de sessão.
Retomada na inicialização vs alternância em sessão
Seção intitulada “Retomada na inicialização vs alternância em sessão”Retomada na inicialização (--continue, --resume, abertura direta)
Seção intitulada “Retomada na inicialização (--continue, --resume, abertura direta)”- O arquivo de sessão é escolhido antes de
createAgentSession(...). sdk.tsconstróiexistingSession = sessionManager.buildSessionContext().- As mensagens do agente são restauradas uma vez durante a criação da sessão.
- Modelo/pensamento são selecionados durante a criação (incluindo lógica de restauração/fallback).
- O modo interativo então executa
#restoreModeFromSession()para reentrar no estado de modo persistido (atualmente plan/plan_paused).
Alternância em sessão (caminho do seletor estilo /resume)
Seção intitulada “Alternância em sessão (caminho do seletor estilo /resume)”- Usa
AgentSession.switchSession(...)em umaAgentSessionjá em execução. - Mensagens/modelo/pensamento são reconstruídos imediatamente no local.
- Eventos de hook
session_before_switch/session_switchsão emitidos. - Chat/todos da UI são atualizados.
- Nenhuma chamada dedicada de restauração de modo pós-alternância é feita no fluxo do seletor; o comportamento de reentrada de modo não é simétrico com o
#restoreModeFromSession()da inicialização.
Comportamento de falha e casos extremos
Seção intitulada “Comportamento de falha e casos extremos”Caminhos de cancelamento
Seção intitulada “Caminhos de cancelamento”- Cancelamento do picker CLI -> retorna
null, o chamador imprimeNo session selected, o processo encerra antecipadamente. - Cancelamento do picker interativo -> editor restaurado, sem mudança de sessão.
- Cancelamento por hook (
session_before_switch) ->switchSession()retornafalse.
Caminhos de lista vazia
Seção intitulada “Caminhos de lista vazia”- CLI
--resume(sem valor): lista vazia imprimeNo sessions founde encerra. - Seletor interativo: lista vazia renderiza mensagem e permanece cancelável.
Arquivo de sessão alvo ausente/inválido
Seção intitulada “Arquivo de sessão alvo ausente/inválido”Ao abrir/alternar para um caminho específico (setSessionFile):
- ENOENT -> tratado como vazio -> nova sessão inicializada naquele caminho exato e persistida.
- cabeçalho malformado/inválido (ou entradas analisadas efetivamente ilegíveis) -> tratado como vazio -> nova sessão inicializada e persistida.
Este é um comportamento de recuperação, não uma falha fatal.
Falhas fatais
Seção intitulada “Falhas fatais”Alternância/abertura ainda pode lançar exceção em falhas de I/O verdadeiras (erros de permissão, falhas de reescrita, etc.), que são propagadas aos chamadores.
Ressalvas da correspondência por prefixo de ID
Seção intitulada “Ressalvas da correspondência por prefixo de ID”- A correspondência por ID usa
startsWithe pega a primeira correspondência na lista ordenada. - Nenhuma UI de ambiguidade se múltiplas sessões compartilham o mesmo prefixo.
SessionManager.list(...)exclui sessões com zero mensagens, portanto essas sessões não são retomáveis via correspondência por ID/picker de lista.