mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
agent: record raw REPL input
- Add `recordRaw` to record raw JS lines in the REPL. - Only record commands if they succeed without error. - Fix kitty terminal cursor keys by forcing legacy arrow encoding.
This commit is contained in:
@@ -483,6 +483,8 @@ fn runRepl(self: *Agent) void {
|
||||
self.terminal.printError("{s}", .{result.text});
|
||||
} else {
|
||||
self.printData(result.text);
|
||||
if (self.recorder) |*r| r.recordRaw(line);
|
||||
self.recordSaveRaw(line);
|
||||
}
|
||||
continue :repl;
|
||||
}
|
||||
@@ -530,8 +532,10 @@ fn runRepl(self: *Agent) void {
|
||||
const result = self.runCommand(aa, cmd);
|
||||
self.terminal.endTool();
|
||||
self.printCommandResult(cmd, result);
|
||||
if (self.recorder) |*r| r.record(cmd);
|
||||
self.recordSaveCommand(cmd);
|
||||
if (!result.is_error) {
|
||||
if (self.recorder) |*r| r.record(cmd);
|
||||
self.recordSaveCommand(cmd);
|
||||
}
|
||||
self.recordSlashToolCall(trimmed, tc.name(), tc.args, result) catch |err| {
|
||||
self.terminal.printWarning("LLM conversation out of sync (/{s}: {s}); next prompt may not see this action", .{ tc.name(), @errorName(err) });
|
||||
};
|
||||
@@ -784,6 +788,12 @@ fn recordSaveComment(self: *Agent, comment: []const u8) void {
|
||||
};
|
||||
}
|
||||
|
||||
fn recordSaveRaw(self: *Agent, line: []const u8) void {
|
||||
self.save_buffer.recordRaw(line) catch |err| {
|
||||
self.terminal.printError("save buffer disabled: {s}", .{@errorName(err)});
|
||||
};
|
||||
}
|
||||
|
||||
fn printSlashHelp(self: *Agent, arena: std.mem.Allocator, target: []const u8) void {
|
||||
if (target.len == 0) {
|
||||
const all = Schema.all();
|
||||
|
||||
@@ -859,10 +859,15 @@ const RawTerminal = struct {
|
||||
raw.cc[@intFromEnum(std.c.V.MIN)] = 0;
|
||||
raw.cc[@intFromEnum(std.c.V.TIME)] = 1;
|
||||
try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, raw);
|
||||
// Under the REPL's kitty "disambiguate" flag, cursor keys arrive as
|
||||
// CSI-u the byte reader can't parse; push flag 0 to force legacy arrow
|
||||
// encoding. restore() pops back to the REPL's flag.
|
||||
_ = std.posix.write(std.posix.STDOUT_FILENO, "\x1b[>0u") catch {};
|
||||
return .{ .original = original };
|
||||
}
|
||||
|
||||
fn restore(self: *const RawTerminal) void {
|
||||
_ = std.posix.write(std.posix.STDOUT_FILENO, "\x1b[<u") catch {};
|
||||
std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, self.original) catch {};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -95,6 +95,18 @@ pub fn recordComment(self: *Recorder, comment: []const u8) void {
|
||||
self.tryRecordComment(comment) catch |err| self.disable(err);
|
||||
}
|
||||
|
||||
pub fn recordRaw(self: *Recorder, line: []const u8) void {
|
||||
if (self.file == null) return;
|
||||
self.tryRecordRaw(line) catch |err| self.disable(err);
|
||||
}
|
||||
|
||||
fn tryRecordRaw(self: *Recorder, line: []const u8) !void {
|
||||
self.buf.clearRetainingCapacity();
|
||||
try self.buf.writer.writeAll(line);
|
||||
try self.buf.writer.writeByte('\n');
|
||||
try self.writeScrubbed();
|
||||
}
|
||||
|
||||
fn tryRecordComment(self: *Recorder, comment: []const u8) !void {
|
||||
self.buf.clearRetainingCapacity();
|
||||
try writeCommentLines(&self.buf.writer, comment);
|
||||
@@ -191,6 +203,13 @@ pub const Memory = struct {
|
||||
try self.appendScrubbed();
|
||||
}
|
||||
|
||||
pub fn recordRaw(self: *Memory, line: []const u8) !void {
|
||||
self.buf.clearRetainingCapacity();
|
||||
try self.buf.writer.writeAll(line);
|
||||
try self.buf.writer.writeByte('\n');
|
||||
try self.appendScrubbed();
|
||||
}
|
||||
|
||||
fn appendScrubbed(self: *Memory) !void {
|
||||
_ = self.arena.reset(.retain_capacity);
|
||||
const scrubbed = try lp.tools.reverseSubstituteEnvVars(self.arena.allocator(), self.buf.written());
|
||||
@@ -248,6 +267,24 @@ test "record writes state-mutating commands" {
|
||||
try std.testing.expect(std.mem.indexOf(u8, content, "markdown(") == null);
|
||||
}
|
||||
|
||||
test "recordRaw writes the JS line verbatim" {
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
var recorder = try Recorder.init(std.testing.allocator, tmp.dir, "raw.js");
|
||||
defer recorder.deinit();
|
||||
|
||||
recorder.recordRaw("document.title");
|
||||
recorder.recordRaw("window.scrollTo(0, 100)");
|
||||
|
||||
const file = tmp.dir.openFile("raw.js", .{}) catch unreachable;
|
||||
defer file.close();
|
||||
var buf: [256]u8 = undefined;
|
||||
const n = file.readAll(&buf) catch unreachable;
|
||||
|
||||
try std.testing.expectEqualStrings("document.title\nwindow.scrollTo(0, 100)\n", buf[0..n]);
|
||||
}
|
||||
|
||||
test "record skips empty and comment lines" {
|
||||
var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
Reference in New Issue
Block a user