sessions: vault discovery tools, shared stdin, ctrl-key fixes

New sessions/session_export tools plus a sessions_block vault map in the system prompt (tai.py, ported to detz); recall/search carry a session footer instead of letting the model grep sealed bytes. write_file refuses new markdown at working/repo roots, and the blanket *.md gitignore is gone so docs stay visible. One LineInput owns fd 0 for every prompt (ask, approvals, shell_ask, secrets), ending phantom-empty answers; Ctrl-C re-prompts (double exits), Ctrl-D exits, Ctrl-Z suspends; mid-turn Ctrl-C announces itself and kills foreground shell children via RunInterrupted. Covered by SessionsTests, repl_keys.py pty checks, agent_diff sessions_block cases.
This commit is contained in:
2026-10-28 13:11:45 +01:00
parent e5a6e05ab5
commit 4eff597be7
50 changed files with 1026 additions and 250 deletions
+8 -32
View File
@@ -1,48 +1,24 @@
#include "shellio.h"
#include "ansi.h"
#include "../repl/line_input.h"
#include "../util/utf8.h"
#include <cstdio>
#include <fcntl.h>
#include <iostream>
#include <termios.h>
#include <unistd.h>
namespace detz::shell {
namespace {
// getpass.getpass(prompt): prompt on the tty, no echo, newline after
// getpass.getpass(prompt): prompt on the tty, no echo, newline after.
// Both go through the one shared fd-0 reader: a second buffered reader here
// would steal the REPL's typeahead and poison EOF state.
std::optional<std::string> GetPass(const std::string& prompt) {
int fd = ::open("/dev/tty", O_RDWR | O_NOCTTY);
bool own = fd >= 0;
if (fd < 0) fd = 0;
struct termios old, quiet;
bool changed = false;
if (::tcgetattr(fd, &old) == 0) {
quiet = old;
quiet.c_lflag &= ~(tcflag_t)ECHO;
changed = ::tcsetattr(fd, TCSAFLUSH, &quiet) == 0;
}
int out = own ? fd : 2;
(void)!::write(out, prompt.data(), prompt.size());
std::string line;
bool got = false, eof = false;
char c;
while (true) {
ssize_t n = ::read(fd, &c, 1);
if (n <= 0) { eof = true; break; }
got = true;
if (c == '\n') break;
line.push_back(c);
}
if (changed) ::tcsetattr(fd, TCSAFLUSH, &old);
(void)!::write(out, "\n", 1);
if (own) ::close(fd);
if (eof && !got) return std::nullopt; // EOFError
auto got = repl::LineInput::shared().read_secret(prompt, line);
if (got != repl::LineInput::Status::Line) return std::nullopt; // EOFError / KeyboardInterrupt
return line;
}
std::optional<std::string> Input(const std::string& prompt) {
std::fputs(prompt.c_str(), stdout);
std::fflush(stdout);
std::string line;
if (!std::getline(std::cin, line)) return std::nullopt; // EOFError
auto got = repl::LineInput::shared().read(prompt, line);
if (got != repl::LineInput::Status::Line) return std::nullopt; // EOFError / KeyboardInterrupt
return line;
}
} // namespace