mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
Agent: simplify save handling and recorder logic
- Remove unused parameters from save helper functions. - Inline and simplify save path duplication and file writing. - Clean up optional unwrapping for the recorder.
This commit is contained in:
@@ -551,13 +551,12 @@ fn handleVerbosity(self: *Agent, rest: []const u8) void {
|
||||
const SaveMode = enum { replace, append };
|
||||
|
||||
fn handleSave(self: *Agent, arena: std.mem.Allocator, rest: []const u8) void {
|
||||
const filename = self.parseSaveFilename(arena, rest) catch |err| {
|
||||
const filename = parseSaveFilename(rest) catch |err| {
|
||||
const msg: []const u8 = switch (err) {
|
||||
error.TooManyArguments => "usage: /save [filename.lp]",
|
||||
error.UnterminatedQuote => "unterminated filename quote",
|
||||
error.EmptyFilename => "filename cannot be empty",
|
||||
error.InvalidFilename => "filename must be a local file name, not a path",
|
||||
error.OutOfMemory => "out of memory",
|
||||
};
|
||||
self.terminal.printError("{s}", .{msg});
|
||||
return;
|
||||
@@ -572,11 +571,11 @@ fn handleSave(self: *Agent, arena: std.mem.Allocator, rest: []const u8) void {
|
||||
}
|
||||
break :blk .{ saved, .append };
|
||||
} else blk: {
|
||||
const path = filename orelse self.randomSaveFilename(arena) catch |err| {
|
||||
const path = filename orelse randomSaveFilename(arena) catch |err| {
|
||||
self.terminal.printError("failed to choose save filename: {s}", .{@errorName(err)});
|
||||
return;
|
||||
};
|
||||
const exists = self.fileExists(path) catch |err| {
|
||||
const exists = fileExists(path) catch |err| {
|
||||
self.terminal.printError("failed to inspect {s}: {s}", .{ path, @errorName(err) });
|
||||
return;
|
||||
};
|
||||
@@ -587,10 +586,16 @@ fn handleSave(self: *Agent, arena: std.mem.Allocator, rest: []const u8) void {
|
||||
break :blk .{ path, mode };
|
||||
};
|
||||
|
||||
var new_save_path = self.dupeSavePathIfNeeded(path) catch |err| {
|
||||
self.terminal.printError("failed to remember save destination {s}: {s}", .{ path, @errorName(err) });
|
||||
return;
|
||||
};
|
||||
// `path` aliases either an arena-owned string (first save) or
|
||||
// `self.save_path` (subsequent saves to the same destination); only
|
||||
// the former needs to be persisted into agent-owned memory.
|
||||
var new_save_path: ?[]u8 = if (self.save_path == null)
|
||||
self.allocator.dupe(u8, path) catch |err| {
|
||||
self.terminal.printError("failed to remember save destination {s}: {s}", .{ path, @errorName(err) });
|
||||
return;
|
||||
}
|
||||
else
|
||||
null;
|
||||
defer if (new_save_path) |p| self.allocator.free(p);
|
||||
|
||||
self.writeSaveFile(path, mode) catch |err| {
|
||||
@@ -599,7 +604,6 @@ fn handleSave(self: *Agent, arena: std.mem.Allocator, rest: []const u8) void {
|
||||
};
|
||||
|
||||
if (new_save_path) |p| {
|
||||
if (self.save_path) |prev| self.allocator.free(prev);
|
||||
self.save_path = p;
|
||||
new_save_path = null;
|
||||
}
|
||||
@@ -608,8 +612,7 @@ fn handleSave(self: *Agent, arena: std.mem.Allocator, rest: []const u8) void {
|
||||
self.terminal.printInfo("Saved {d} line(s) to {s}", .{ saved_lines, self.save_path.? });
|
||||
}
|
||||
|
||||
fn parseSaveFilename(self: *Agent, arena: std.mem.Allocator, rest: []const u8) !?[]const u8 {
|
||||
_ = self;
|
||||
fn parseSaveFilename(rest: []const u8) !?[]const u8 {
|
||||
const trimmed = std.mem.trim(u8, rest, &std.ascii.whitespace);
|
||||
if (trimmed.len == 0) return null;
|
||||
|
||||
@@ -628,20 +631,19 @@ fn parseSaveFilename(self: *Agent, arena: std.mem.Allocator, rest: []const u8) !
|
||||
if (std.mem.indexOfScalar(u8, name, '/') != null) return error.InvalidFilename;
|
||||
if (std.mem.indexOfScalar(u8, name, '\\') != null) return error.InvalidFilename;
|
||||
if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) return error.InvalidFilename;
|
||||
return try arena.dupe(u8, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
fn randomSaveFilename(self: *Agent, arena: std.mem.Allocator) ![]const u8 {
|
||||
fn randomSaveFilename(arena: std.mem.Allocator) ![]const u8 {
|
||||
for (0..100) |_| {
|
||||
const n = std.crypto.random.int(u64);
|
||||
const path = try std.fmt.allocPrint(arena, "session-{x}.lp", .{n});
|
||||
if (!(try self.fileExists(path))) return path;
|
||||
if (!(try fileExists(path))) return path;
|
||||
}
|
||||
return error.NameCollision;
|
||||
}
|
||||
|
||||
fn fileExists(self: *Agent, path: []const u8) !bool {
|
||||
_ = self;
|
||||
fn fileExists(path: []const u8) !bool {
|
||||
std.fs.cwd().access(path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => return false,
|
||||
else => return err,
|
||||
@@ -663,29 +665,14 @@ fn promptSaveMode(self: *Agent, path: []const u8) ?SaveMode {
|
||||
|
||||
fn writeSaveFile(self: *Agent, path: []const u8, mode: SaveMode) !void {
|
||||
const content = self.save_buffer.bytes();
|
||||
const cwd = std.fs.cwd();
|
||||
switch (mode) {
|
||||
.replace => {
|
||||
const file = try cwd.createFile(path, .{ .truncate = true });
|
||||
defer file.close();
|
||||
try file.writeAll(content);
|
||||
},
|
||||
.append => {
|
||||
const file = try cwd.createFile(path, .{ .truncate = false });
|
||||
defer file.close();
|
||||
try file.seekFromEnd(0);
|
||||
const pos = try file.getPos();
|
||||
if (pos > 0 and content.len > 0) try file.writeAll("\n");
|
||||
try file.writeAll(content);
|
||||
},
|
||||
const file = try std.fs.cwd().createFile(path, .{ .truncate = mode == .replace });
|
||||
defer file.close();
|
||||
if (mode == .append) {
|
||||
try file.seekFromEnd(0);
|
||||
const pos = try file.getPos();
|
||||
if (pos > 0 and content.len > 0) try file.writeAll("\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn dupeSavePathIfNeeded(self: *Agent, path: []const u8) !?[]u8 {
|
||||
if (self.save_path) |prev| {
|
||||
if (prev.ptr == path.ptr and prev.len == path.len) return null;
|
||||
}
|
||||
return try self.allocator.dupe(u8, path);
|
||||
try file.writeAll(content);
|
||||
}
|
||||
|
||||
fn recordSaveCommand(self: *Agent, cmd: Command) void {
|
||||
@@ -1290,9 +1277,9 @@ fn processUserMessage(self: *Agent, input: TurnInput) !?[]const u8 {
|
||||
|
||||
if (result.cancelled) return self.drainCancellation(msg_baseline);
|
||||
|
||||
const record_to_file = if (self.recorder) |*r| r.isActive() else false;
|
||||
const file_recorder: ?*Recorder = if (self.recorder) |*r| (if (r.isActive()) r else null) else null;
|
||||
const record_to_memory = input.capture_for_save;
|
||||
if (record_to_file or record_to_memory) {
|
||||
if (file_recorder != null or record_to_memory) {
|
||||
// When the LLM tries multiple `extract` schemas in one turn, only the
|
||||
// last successful one is the answer — earlier probes are noise.
|
||||
var last_extract_idx: ?usize = null;
|
||||
@@ -1311,15 +1298,15 @@ fn processUserMessage(self: *Agent, input: TurnInput) !?[]const u8 {
|
||||
if (!cmd.isRecorded()) continue;
|
||||
if (!recorded_any) {
|
||||
if (input.record_comment) |c| {
|
||||
if (record_to_file) if (self.recorder) |*r| r.recordComment(c);
|
||||
if (file_recorder) |r| r.recordComment(c);
|
||||
if (record_to_memory) self.recordSaveComment(c);
|
||||
}
|
||||
recorded_any = true;
|
||||
}
|
||||
if (record_to_file) if (self.recorder) |*r| r.record(cmd);
|
||||
if (file_recorder) |r| r.record(cmd);
|
||||
if (record_to_memory) self.recordSaveCommand(cmd);
|
||||
}
|
||||
if (record_to_file) if (self.recorder) |*r| if (!r.isActive()) {
|
||||
if (file_recorder) |r| if (!r.isActive()) {
|
||||
self.terminal.printError("recording disabled (write failed); see logs", .{});
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user