const std = @import("std");const app_permission_runtime = @import("app_permission_runtime.zig");const js_host_config_store = @import("../config/js_host_config_store.zig");const mode_registry = @import("../modes/mode_registry.zig");const runtime_profile = @import("../hosts/runtime_profile.zig");const debug_trace = @import("../shared/debug_trace.zig");const session_codec = @import("../session/session_codec.zig");const types = @import("../shared/types.zig"); const Allocator = std.mem.Allocator;const PermissionMode = types.PermissionMode; pub fn Runtime(comptime App: type) type { return struct { pub fn restore(app: *App, modes: mode_registry.Registry) !void { if (comptime !runtime_profile.allows(App, .js_host_config)) return; if (try loadValue(app, "model")) |model| { defer app.alloc.free(model); if (session_codec.validateModelPreference(model)) |_| { app.selected_model.clearRetainingCapacity(); try app.selected_model.appendSlice(app.alloc, model); try app.worker.syncQueuedPromptModel(std.heap.c_allocator, model); } else |err| { debug_trace.logf( "host_config", "restore_ignored id=model reason=invalid_value err={s}", .{@errorName(err)}, ); } } if (try loadValue(app, "mode")) |mode_id| { defer app.alloc.free(mode_id); if (modes.lookup(mode_id)) |mode| { app_permission_runtime.Runtime(App).setMode(app, mode.permission_mode); } else { debug_trace.logf( "host_config", "restore_ignored id=mode reason=unknown_value", .{}, ); } } } fn loadValue(app: *App, id: []const u8) Allocator.Error!?[]u8 { return js_host_config_store.load(app.alloc, id) catch |err| switch (err) { error.OutOfMemory => error.OutOfMemory, error.ValueTooLarge, error.HostFailure => { debug_trace.logf( "host_config", "restore_ignored id={s} reason=load_failed err={s}", .{ id, @errorName(err) }, ); return null; }, }; } pub fn persistModel(_: *App, model: []const u8) void { if (comptime !runtime_profile.allows(App, .js_host_config)) return; persist("model", model); } pub fn persistPermissionMode(_: *App, mode: PermissionMode) void { if (comptime !runtime_profile.allows(App, .js_host_config)) return; persist("mode", configIdForPermissionMode(mode)); } };} fn persist(id: []const u8, value: []const u8) void { js_host_config_store.setAccepted(id, value) catch |err| { debug_trace.logf( "host_config", "persist_ignored id={s} reason=host_failure err={s}", .{ id, @errorName(err) }, ); };} fn configIdForPermissionMode(mode: PermissionMode) []const u8 { return switch (mode) { .auto => "code", .ask => "ask", // The host persists product mode IDs. Yolo has no product mode, so its // non-interactive behavior restores through the closest mode, code. .yolo => "code", };} test "host config maps permission modes to restorable product modes" { try std.testing.expectEqualStrings("code", configIdForPermissionMode(.auto)); try std.testing.expectEqualStrings("ask", configIdForPermissionMode(.ask)); try std.testing.expectEqualStrings("code", configIdForPermissionMode(.yolo));}
const std = @import("std");
const app_permission_runtime = @import("app_permission_runtime.zig");
const js_host_config_store = @import("../config/js_host_config_store.zig");
const mode_registry = @import("../modes/mode_registry.zig");
const runtime_profile = @import("../hosts/runtime_profile.zig");
const debug_trace = @import("../shared/debug_trace.zig");
const session_codec = @import("../session/session_codec.zig");
const types = @import("../shared/types.zig");
const Allocator = std.mem.Allocator;
const PermissionMode = types.PermissionMode;
pub fn Runtime(comptime App: type) type {
return struct {
pub fn restore(app: *App, modes: mode_registry.Registry) !void {
if (comptime !runtime_profile.allows(App, .js_host_config)) return;
if (try loadValue(app, "model")) |model| {
defer app.alloc.free(model);
if (session_codec.validateModelPreference(model)) |_| {
app.selected_model.clearRetainingCapacity();
try app.selected_model.appendSlice(app.alloc, model);
try app.worker.syncQueuedPromptModel(std.heap.c_allocator, model);
} else |err| {
debug_trace.logf(
"host_config",
"restore_ignored id=model reason=invalid_value err={s}",
.{@errorName(err)},
);
}
}
if (try loadValue(app, "mode")) |mode_id| {
defer app.alloc.free(mode_id);
if (modes.lookup(mode_id)) |mode| {
app_permission_runtime.Runtime(App).setMode(app, mode.permission_mode);
} else {
debug_trace.logf(
"host_config",
"restore_ignored id=mode reason=unknown_value",
.{},
);
}
}
}
fn loadValue(app: *App, id: []const u8) Allocator.Error!?[]u8 {
return js_host_config_store.load(app.alloc, id) catch |err| switch (err) {
error.OutOfMemory => error.OutOfMemory,
error.ValueTooLarge, error.HostFailure => {
debug_trace.logf(
"host_config",
"restore_ignored id={s} reason=load_failed err={s}",
.{ id, @errorName(err) },
);
return null;
},
};
}
pub fn persistModel(_: *App, model: []const u8) void {
if (comptime !runtime_profile.allows(App, .js_host_config)) return;
persist("model", model);
}
pub fn persistPermissionMode(_: *App, mode: PermissionMode) void {
if (comptime !runtime_profile.allows(App, .js_host_config)) return;
persist("mode", configIdForPermissionMode(mode));
}
};
}
fn persist(id: []const u8, value: []const u8) void {
js_host_config_store.setAccepted(id, value) catch |err| {
debug_trace.logf(
"host_config",
"persist_ignored id={s} reason=host_failure err={s}",
.{ id, @errorName(err) },
);
};
}
fn configIdForPermissionMode(mode: PermissionMode) []const u8 {
return switch (mode) {
.auto => "code",
.ask => "ask",
// The host persists product mode IDs. Yolo has no product mode, so its
// non-interactive behavior restores through the closest mode, code.
.yolo => "code",
};
}
test "host config maps permission modes to restorable product modes" {
try std.testing.expectEqualStrings("code", configIdForPermissionMode(.auto));
try std.testing.expectEqualStrings("ask", configIdForPermissionMode(.ask));
try std.testing.expectEqualStrings("code", configIdForPermissionMode(.yolo));
}