libretto 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,24 +6,6 @@ import { LIBRETTO_CONFIG_PATH } from "./context.js";
6
6
 
7
7
  export const CURRENT_CONFIG_VERSION = 1;
8
8
 
9
- /**
10
- * AI configuration schema.
11
- *
12
- * The `model` field is a provider/model-id string (e.g. "openai/gpt-5.4",
13
- * "anthropic/claude-sonnet-4-6", "google/gemini-3-flash-preview", "vertex/gemini-2.5-pro").
14
- *
15
- * Legacy note: earlier versions stored a `preset` (codex|claude|gemini) and
16
- * `commandPrefix` (CLI args to spawn a sub-agent process). That approach has
17
- * been replaced by direct API calls via the Vercel AI SDK. The legacy CLI-agent
18
- * code is preserved in snapshot-analyzer.ts but is not wired into the snapshot
19
- * command.
20
- */
21
- export const AiConfigSchema = z.object({
22
- model: z.string().min(1),
23
- updatedAt: z.string(),
24
- });
25
- export type AiConfig = z.infer<typeof AiConfigSchema>;
26
-
27
9
  export const ViewportConfigSchema = z.object({
28
10
  width: z.number().int().min(1),
29
11
  height: z.number().int().min(1),
@@ -39,7 +21,7 @@ export type WindowPositionConfig = z.infer<typeof WindowPositionConfigSchema>;
39
21
  export const LibrettoConfigSchema = z
40
22
  .object({
41
23
  version: z.literal(CURRENT_CONFIG_VERSION),
42
- ai: AiConfigSchema.optional(),
24
+ snapshotModel: z.string().min(1).optional(),
43
25
  viewport: ViewportConfigSchema.optional(),
44
26
  windowPosition: WindowPositionConfigSchema.optional(),
45
27
  provider: z.string().optional(),
@@ -58,10 +40,7 @@ function formatExpectedConfigExample(): string {
58
40
  return JSON.stringify(
59
41
  {
60
42
  version: CURRENT_CONFIG_VERSION,
61
- ai: {
62
- model: "openai/gpt-5.4",
63
- updatedAt: "2026-01-01T00:00:00.000Z",
64
- },
43
+ snapshotModel: "openai/gpt-5.4",
65
44
  viewport: {
66
45
  width: 1280,
67
46
  height: 800,
@@ -80,13 +59,13 @@ function formatExpectedConfigExample(): string {
80
59
  function invalidConfigError(configPath: string, detail?: string): Error {
81
60
  return new Error(
82
61
  [
83
- `AI config is invalid at ${configPath}.`,
62
+ `Config is invalid at ${configPath}.`,
84
63
  detail ? `Problems:\n${detail}` : null,
85
64
  "Expected config example:",
86
65
  formatExpectedConfigExample(),
87
66
  "Notes:",
88
- ' - "ai", "viewport", "windowPosition", and "sessionMode" are optional.',
89
- ' - "ai.model" must be a provider/model string like "openai/gpt-5.4" or "anthropic/claude-sonnet-4-6".',
67
+ ' - "snapshotModel", "viewport", "windowPosition", and "sessionMode" are optional.',
68
+ ' - "snapshotModel" must be a provider/model string like "openai/gpt-5.4" or "anthropic/claude-sonnet-4-6".',
90
69
  "Fix the file to match this shape, or delete it and rerun:",
91
70
  ` npx libretto ai configure openai | anthropic | gemini | vertex`,
92
71
  ]
@@ -132,16 +111,16 @@ export function writeLibrettoConfig(
132
111
  return parsed;
133
112
  }
134
113
 
135
- export function readAiConfig(
114
+ export function readSnapshotModel(
136
115
  configPath: string = LIBRETTO_CONFIG_PATH,
137
- ): AiConfig | null {
138
- return readLibrettoConfig(configPath).ai ?? null;
116
+ ): string | null {
117
+ return readLibrettoConfig(configPath).snapshotModel ?? null;
139
118
  }
140
119
 
141
- export function writeAiConfig(
120
+ export function writeSnapshotModel(
142
121
  model: string,
143
122
  configPath: string = LIBRETTO_CONFIG_PATH,
144
- ): AiConfig {
123
+ ): string {
145
124
  let librettoConfig: LibrettoConfig;
146
125
  try {
147
126
  librettoConfig = readLibrettoConfig(configPath);
@@ -150,27 +129,23 @@ export function writeAiConfig(
150
129
  // overwrite a broken file instead of throwing.
151
130
  librettoConfig = { version: CURRENT_CONFIG_VERSION };
152
131
  }
153
- const ai = AiConfigSchema.parse({
154
- model,
155
- updatedAt: new Date().toISOString(),
156
- });
157
132
  writeLibrettoConfig(
158
133
  {
159
134
  ...librettoConfig,
160
135
  version: CURRENT_CONFIG_VERSION,
161
- ai,
136
+ snapshotModel: model,
162
137
  },
163
138
  configPath,
164
139
  );
165
- return ai;
140
+ return model;
166
141
  }
167
142
 
168
- export function clearAiConfig(
143
+ export function clearSnapshotModel(
169
144
  configPath: string = LIBRETTO_CONFIG_PATH,
170
145
  ): boolean {
171
146
  const librettoConfig = readLibrettoConfig(configPath);
172
- if (!librettoConfig.ai) return false;
173
- const { ai: _ai, ...rest } = librettoConfig;
147
+ if (!librettoConfig.snapshotModel) return false;
148
+ const { snapshotModel: _, ...rest } = librettoConfig;
174
149
  writeLibrettoConfig(
175
150
  {
176
151
  ...rest,
@@ -1265,14 +1265,17 @@ function buildInputNormalizer<
1265
1265
  key,
1266
1266
  ].filter((candidate) => candidate.length > 0);
1267
1267
 
1268
- let value: unknown = undefined;
1268
+ let found = false;
1269
1269
  for (const candidate of normalizedCandidates) {
1270
1270
  if (Object.prototype.hasOwnProperty.call(named, candidate)) {
1271
- value = named[candidate];
1271
+ output[key] = named[candidate];
1272
+ found = true;
1272
1273
  break;
1273
1274
  }
1274
1275
  }
1275
- output[key] = value;
1276
+ if (!found && !Object.prototype.hasOwnProperty.call(output, key)) {
1277
+ output[key] = undefined;
1278
+ }
1276
1279
  }
1277
1280
 
1278
1281
  return output as InputObjectFor<TPositionals, TNamed>;