orizu 0.5.21 → 0.5.23

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.
Files changed (87) hide show
  1. package/README.md +46 -15
  2. package/dist/cleanup.d.ts +6 -0
  3. package/dist/cleanup.d.ts.map +1 -0
  4. package/dist/cleanup.js +27 -0
  5. package/dist/gepa-engine-dispatch.d.ts.map +1 -1
  6. package/dist/gepa-engine-dispatch.js +20 -1
  7. package/dist/gepa-python-paths.d.ts.map +1 -1
  8. package/dist/gepa-python-paths.js +25 -6
  9. package/dist/help.d.ts.map +1 -1
  10. package/dist/help.js +37 -15
  11. package/dist/hosted-commands.d.ts +13 -0
  12. package/dist/hosted-commands.d.ts.map +1 -0
  13. package/dist/hosted-commands.js +10 -0
  14. package/dist/hosted-gepa-cli.d.ts +11 -0
  15. package/dist/hosted-gepa-cli.d.ts.map +1 -0
  16. package/dist/hosted-gepa-cli.js +110 -0
  17. package/dist/hosted-gepa-numeric-values.d.ts +3 -0
  18. package/dist/hosted-gepa-numeric-values.d.ts.map +1 -0
  19. package/dist/hosted-gepa-numeric-values.js +49 -0
  20. package/dist/hosted-provider-settings.d.ts +3 -0
  21. package/dist/hosted-provider-settings.d.ts.map +1 -0
  22. package/dist/hosted-provider-settings.js +69 -0
  23. package/dist/hosted-session-cli.d.ts.map +1 -1
  24. package/dist/hosted-session-cli.js +6 -1
  25. package/dist/http.d.ts +1 -0
  26. package/dist/http.d.ts.map +1 -1
  27. package/dist/http.js +7 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +72 -74
  30. package/dist/instruction-set-loader.d.ts.map +1 -1
  31. package/dist/instruction-set-loader.js +57 -5
  32. package/dist/instruction-sets-cli.d.ts +5 -2
  33. package/dist/instruction-sets-cli.d.ts.map +1 -1
  34. package/dist/instruction-sets-cli.js +134 -24
  35. package/dist/optimizations-promote-cli.d.ts +3 -0
  36. package/dist/optimizations-promote-cli.d.ts.map +1 -1
  37. package/dist/optimizations-promote-cli.js +75 -7
  38. package/dist/runner-dir-verify.d.ts.map +1 -1
  39. package/dist/runner-dir-verify.js +4 -7
  40. package/dist/skilled-proposer-bake-report.d.ts +2 -0
  41. package/dist/skilled-proposer-bake-report.d.ts.map +1 -0
  42. package/dist/skilled-proposer-bake-report.js +1 -0
  43. package/dist/skilled-proposer-config.d.ts +2 -0
  44. package/dist/skilled-proposer-config.d.ts.map +1 -0
  45. package/dist/skilled-proposer-config.js +212 -0
  46. package/dist/skilled-proposer-launch.d.ts +26 -0
  47. package/dist/skilled-proposer-launch.d.ts.map +1 -1
  48. package/dist/skilled-proposer-launch.js +103 -6
  49. package/package.json +1 -1
  50. package/scripts/vendor-gepa-python.mjs +81 -12
  51. package/src/cleanup.ts +37 -0
  52. package/src/gepa-engine-dispatch.ts +21 -1
  53. package/src/gepa-python-paths.ts +24 -6
  54. package/src/help.ts +37 -15
  55. package/src/hosted-commands.ts +4 -0
  56. package/src/hosted-gepa-cli.ts +107 -0
  57. package/src/hosted-gepa-numeric-values.ts +60 -0
  58. package/src/hosted-provider-settings.ts +69 -0
  59. package/src/hosted-session-cli.ts +4 -1
  60. package/src/http.ts +8 -0
  61. package/src/index.ts +112 -89
  62. package/src/instruction-set-loader.ts +55 -5
  63. package/src/instruction-sets-cli.ts +173 -19
  64. package/src/optimizations-promote-cli.ts +85 -7
  65. package/src/runner-dir-verify.ts +11 -6
  66. package/src/skilled-proposer-bake-report.ts +1 -0
  67. package/src/skilled-proposer-config.ts +191 -0
  68. package/src/skilled-proposer-launch.ts +96 -7
  69. package/src/skilled-proposer-venv-manager.mjs +44 -27
  70. package/vendor/orizu-gepa/src/orizu_gepa_connector/engine.py +36 -3
  71. package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +31 -3
  72. package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +7 -2
  73. package/vendor/orizu-gepa/src/orizu_gepa_connector/skilled_proposer_bridge.py +354 -5
  74. package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +1 -0
  75. package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +15 -0
  76. package/vendor/skills/orizu-cli/SKILL.md +94 -168
  77. package/vendor/skills/orizu-cli/references/assess-and-plan.md +107 -0
  78. package/vendor/skills/orizu-cli/references/building-judges.md +442 -180
  79. package/vendor/skills/orizu-cli/references/cli-reference.md +69 -39
  80. package/vendor/skills/orizu-cli/references/dataset-design.md +100 -0
  81. package/vendor/skills/orizu-cli/references/eval-strategy.md +64 -0
  82. package/vendor/skills/orizu-cli/references/instructions-after-prompts.md +115 -0
  83. package/vendor/skills/orizu-cli/references/migrate-existing-gepa-setup.md +150 -82
  84. package/vendor/skills/orizu-cli/references/optimization-reports.md +196 -104
  85. package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +57 -37
  86. package/vendor/skills/orizu-cli/references/primer.md +43 -18
  87. package/vendor/skills/orizu-cli/references/prompt-control-plane.md +199 -131
@@ -33,6 +33,15 @@ async function responsePayload(response, action) {
33
33
  const SAFE_SEGMENT = /^[A-Za-z0-9._-]+$/u;
34
34
  function safeSegment(value) { if (!SAFE_SEGMENT.test(value) || value === '.' || value === '..' || value.startsWith('.'))
35
35
  throw new Error('instruction_set_path_unsafe'); }
36
+ function isSafeSegment(value) {
37
+ try {
38
+ safeSegment(value);
39
+ return true;
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ }
36
45
  function slug(identity) { return identity.replaceAll('/', '__').replace(/[^A-Za-z0-9._-]/gu, '_'); }
37
46
  function stable(value) { return `${JSON.stringify(value, null, 2)}\n`; }
38
47
  function sha256(value) { return createHash('sha256').update(value, 'utf8').digest('hex'); }
@@ -65,19 +74,62 @@ function removeOwnScratch(paths) {
65
74
  if (path && existsSync(path))
66
75
  rmSync(path, { recursive: true, force: true });
67
76
  }
68
- function existingDestinationIsSynced(destination, name) {
77
+ function existingDestinationIsSynced(destination, set) {
78
+ if (set.slug &&
79
+ set.projectId === undefined &&
80
+ set.instructionSetId === undefined) {
81
+ return existingDestinationIsLegacySynced(destination, set.name, set.slug);
82
+ }
83
+ try {
84
+ if (!statSync(destination).isDirectory())
85
+ return false;
86
+ const manifest = JSON.parse(readFileSync(join(destination, 'manifest.json'), 'utf8'));
87
+ return manifest.manifestVersion === 1 && (set.slug
88
+ ? manifest.slug === set.slug &&
89
+ manifest.projectId === set.projectId &&
90
+ manifest.instructionSetId === set.instructionSetId
91
+ : manifest.name === set.name);
92
+ }
93
+ catch {
94
+ return false;
95
+ }
96
+ }
97
+ function existingDestinationIsLegacySynced(destination, name, slug) {
69
98
  try {
70
99
  if (!statSync(destination).isDirectory())
71
100
  return false;
72
101
  const manifest = JSON.parse(readFileSync(join(destination, 'manifest.json'), 'utf8'));
73
- return manifest.manifestVersion === 1 && manifest.name === name;
102
+ return manifest.manifestVersion === 1 &&
103
+ manifest.name === name &&
104
+ manifest.slug === slug;
74
105
  }
75
106
  catch {
76
107
  return false;
77
108
  }
78
109
  }
110
+ function removePublishedBackups(paths, remove) {
111
+ let firstError = null;
112
+ for (const path of paths) {
113
+ if (!existsSync(path))
114
+ continue;
115
+ try {
116
+ remove(path, { recursive: true, force: true });
117
+ }
118
+ catch (error) {
119
+ firstError ??= error;
120
+ }
121
+ }
122
+ if (firstError)
123
+ throw new Error('instruction_set_sync_cleanup_failed');
124
+ }
79
125
  export function syncToDisk(out, set, fileOps = { writeFileSync }) {
80
- safeSegment(set.name);
126
+ const directoryName = set.slug || set.name;
127
+ safeSegment(directoryName);
128
+ const hasProjectId = set.projectId !== undefined;
129
+ const hasInstructionSetId = set.instructionSetId !== undefined;
130
+ if (set.slug && hasProjectId !== hasInstructionSetId) {
131
+ throw new Error('instruction_set_sync_identity_missing');
132
+ }
81
133
  for (const key of set.shape)
82
134
  safeSegment(key);
83
135
  const profileSlugs = new Set();
@@ -89,12 +141,28 @@ export function syncToDisk(out, set, fileOps = { writeFileSync }) {
89
141
  profileSlugs.add(value);
90
142
  }
91
143
  mkdirSync(out, { recursive: true });
92
- const destination = join(out, set.name);
93
- if (existsSync(destination) && !existingDestinationIsSynced(destination, set.name))
144
+ const destination = join(out, directoryName);
145
+ const legacyDestination = set.slug && set.name !== set.slug && isSafeSegment(set.name)
146
+ ? join(out, set.name)
147
+ : null;
148
+ if (existsSync(destination)) {
149
+ if (set.slug && existingDestinationIsLegacySynced(destination, set.name)) {
150
+ throw new Error('instruction_set_sync_legacy_identity_required');
151
+ }
152
+ if (!existingDestinationIsSynced(destination, set)) {
153
+ throw new Error('instruction_set_sync_destination_not_synced');
154
+ }
155
+ }
156
+ if (legacyDestination && existsSync(legacyDestination)) {
157
+ if (existingDestinationIsLegacySynced(legacyDestination, set.name)) {
158
+ throw new Error('instruction_set_sync_legacy_identity_required');
159
+ }
94
160
  throw new Error('instruction_set_sync_destination_not_synced');
95
- const temp = join(out, `.${set.name}.${randomUUID()}.tmp`);
161
+ }
162
+ const priorDestinations = [destination];
163
+ const temp = join(out, `.${directoryName}.${randomUUID()}.tmp`);
96
164
  const move = fileOps.renameSync || renameSync;
97
- let backup = null;
165
+ const backups = [];
98
166
  mkdirSync(temp);
99
167
  try {
100
168
  const defaultMaterial = writeMaterial(temp, set.default, set.shape, 'default', fileOps);
@@ -108,28 +176,40 @@ export function syncToDisk(out, set, fileOps = { writeFileSync }) {
108
176
  ? Object.entries(profile.production.files).map(([key, path]) => ({ key, modelConfig: profile.modelConfigIdentity, path, syncedContentSha256: profile.production.syncedContentSha256[key] }))
109
177
  : []),
110
178
  ];
111
- const manifest = { manifestVersion: 1, name: set.name, ...(Object.hasOwn(set, 'description') ? { description: set.description ?? null } : {}), shape: set.shape, components, default: defaultMaterial, profiles, ...(set.filteredTo ? { filteredTo: [...set.filteredTo].sort() } : {}) };
179
+ const manifest = {
180
+ manifestVersion: 1,
181
+ ...(set.projectId ? { projectId: set.projectId } : {}),
182
+ ...(set.instructionSetId ? { instructionSetId: set.instructionSetId } : {}),
183
+ name: set.name,
184
+ ...(set.slug ? { slug: set.slug } : {}),
185
+ ...(Object.hasOwn(set, 'description') ? { description: set.description ?? null } : {}),
186
+ shape: set.shape,
187
+ components,
188
+ default: defaultMaterial,
189
+ profiles,
190
+ ...(set.filteredTo ? { filteredTo: [...set.filteredTo].sort() } : {}),
191
+ };
112
192
  fileOps.writeFileSync(join(temp, 'manifest.json'), stable(manifest));
113
- backup = join(out, `.${set.name}.${randomUUID()}.bak`);
114
- if (existsSync(destination))
115
- move(destination, backup);
116
- try {
117
- move(temp, destination);
118
- }
119
- catch (error) {
120
- if (existsSync(backup))
121
- move(backup, destination);
122
- throw error;
193
+ for (const prior of priorDestinations) {
194
+ if (!existsSync(prior))
195
+ continue;
196
+ const backup = join(out, `.${directoryName}.${randomUUID()}.bak`);
197
+ move(prior, backup);
198
+ backups.push({ original: prior, backup });
123
199
  }
124
- removeOwnScratch([backup]);
200
+ move(temp, destination);
125
201
  }
126
202
  catch (error) {
127
203
  removeOwnScratch([temp]);
128
- if (backup && existsSync(backup) && !existsSync(destination))
129
- move(backup, destination);
130
- removeOwnScratch([backup]);
204
+ for (const { original, backup } of [...backups].reverse()) {
205
+ if (existsSync(backup) && !existsSync(original))
206
+ move(backup, original);
207
+ }
208
+ removeOwnScratch(backups.map(entry => entry.backup));
131
209
  throw error;
132
210
  }
211
+ removePublishedBackups(backups.map(entry => entry.backup), fileOps.rmSync || rmSync);
212
+ return destination;
133
213
  }
134
214
  export async function instructionSetsCommand(args, io) {
135
215
  const positionals = positionalArgs(args);
@@ -148,6 +228,36 @@ export async function instructionSetsCommand(args, io) {
148
228
  writeManifest = loadInstructionSetManifest(reference);
149
229
  }
150
230
  const project = await io.resolveProjectSlug(argValue(args, '--project'));
231
+ if (subcommand === 'scorers') {
232
+ const action = reference;
233
+ const set = positionals[2];
234
+ const componentKey = argValue(args, '--key');
235
+ const scorerVersionId = argValue(args, '--scorer-version');
236
+ if ((action !== 'set-headline' && action !== 'add') ||
237
+ !set ||
238
+ !componentKey ||
239
+ !scorerVersionId) {
240
+ throw new Error('Usage: orizu instructions scorers set-headline|add <set> --key <component-key> --scorer-version <id> --project <team/project> [--dataset-version <id> --split-set <id> --split <name>] [--json]');
241
+ }
242
+ const payload = await responsePayload(await authedFetch(`/api/cli/instruction-sets/${encodeURIComponent(set)}/scorers?project=${encodeURIComponent(project)}`, {
243
+ method: 'POST',
244
+ headers: { 'content-type': 'application/json' },
245
+ body: JSON.stringify({
246
+ componentKey,
247
+ scorerVersionId,
248
+ role: action === 'set-headline' ? 'headline' : 'tracked',
249
+ datasetVersionId: argValue(args, '--dataset-version') || undefined,
250
+ splitSetId: argValue(args, '--split-set') || undefined,
251
+ splitName: argValue(args, '--split') || undefined,
252
+ }),
253
+ }), `Instruction sets scorers ${action}`);
254
+ if (io.json)
255
+ io.print(JSON.stringify(payload));
256
+ else {
257
+ io.print(`${action === 'set-headline' ? 'Set headline' : 'Added'} scorer ${sanitizeTerminalText(scorerVersionId)} for ${sanitizeTerminalText(set)} / ${sanitizeTerminalText(componentKey)}`);
258
+ }
259
+ return 0;
260
+ }
151
261
  if (subcommand === 'default') {
152
262
  const operation = reference;
153
263
  const set = positionals[2];
@@ -318,11 +428,11 @@ export async function instructionSetsCommand(args, io) {
318
428
  if (!set || (set.slug !== reference && set.name !== reference) || (modelConfig && (!Array.isArray(set.filteredTo) || set.filteredTo.length !== 1 || set.filteredTo[0] !== modelConfig))) {
319
429
  throw new Error('instruction_set_sync_response_mismatch');
320
430
  }
321
- syncToDisk(output, set);
431
+ const destination = syncToDisk(output, set);
322
432
  if (io.json)
323
433
  io.print(JSON.stringify(payload));
324
434
  else
325
- io.print(`Synced ${set.name} to ${join(output, set.name)}`);
435
+ io.print(`Synced ${set.name} to ${destination}`);
326
436
  return 0;
327
437
  }
328
438
  if (subcommand === 'show' && (!reference || reference.startsWith('--')))
@@ -1,6 +1,9 @@
1
+ import type { GlobalFlags } from './global-flags.js';
1
2
  export interface OptimizationsPromoteIo {
2
3
  json: boolean;
4
+ origin: GlobalFlags;
3
5
  print: (line: string) => void;
6
+ printErr: (line: string) => void;
4
7
  resolveProjectSlug: (projectArg: string | null) => Promise<string>;
5
8
  fetcher: (path: string, init?: RequestInit) => Promise<Response>;
6
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"optimizations-promote-cli.d.ts","sourceRoot":"","sources":["../src/optimizations-promote-cli.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAClE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CACjE;AAOD,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB1G"}
1
+ {"version":3,"file":"optimizations-promote-cli.d.ts","sourceRoot":"","sources":["../src/optimizations-promote-cli.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAClE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CACjE;AA0DD,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiD1G"}
@@ -1,9 +1,59 @@
1
1
  import { extractErrorMessage } from './error-response.js';
2
- import { sanitizeTerminalText } from './json-response.js';
2
+ import { sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js';
3
3
  function option(args, name) {
4
4
  const index = args.indexOf(name);
5
5
  return index === -1 || !args[index + 1] || args[index + 1].startsWith('--') ? null : args[index + 1];
6
6
  }
7
+ function shellArgument(value) {
8
+ if (/^[A-Za-z0-9._/:@-]+$/.test(value))
9
+ return value;
10
+ if (/[\u0000-\u001F\u007F-\u009F\u2028\u2029]/.test(value)) {
11
+ let escaped = '';
12
+ for (const character of value) {
13
+ if (character === '\\')
14
+ escaped += '\\\\';
15
+ else if (character === "'")
16
+ escaped += "\\'";
17
+ else if (character === '\n')
18
+ escaped += '\\n';
19
+ else if (character === '\r')
20
+ escaped += '\\r';
21
+ else if (character === '\t')
22
+ escaped += '\\t';
23
+ else {
24
+ const codePoint = character.codePointAt(0);
25
+ if (codePoint <= 0x1f || codePoint === 0x7f) {
26
+ escaped += `\\x${codePoint.toString(16).padStart(2, '0')}`;
27
+ }
28
+ else if ((codePoint >= 0x80 && codePoint <= 0x9f) || codePoint === 0x2028 || codePoint === 0x2029) {
29
+ for (const byte of new TextEncoder().encode(character)) {
30
+ escaped += `\\x${byte.toString(16).padStart(2, '0')}`;
31
+ }
32
+ }
33
+ else {
34
+ escaped += character;
35
+ }
36
+ }
37
+ }
38
+ return `$'${escaped}'`;
39
+ }
40
+ const safeValue = sanitizeTerminalText(value);
41
+ return `'${safeValue.replace(/'/g, `'\\''`)}'`;
42
+ }
43
+ function inlineTerminalText(value) {
44
+ return sanitizeHumanInlineText(sanitizeTerminalText, value)
45
+ .replaceAll('\u2028', '\\u2028')
46
+ .replaceAll('\u2029', '\\u2029');
47
+ }
48
+ const APPLIED_LABEL_VERSION_SKEW_NOTE = 'Server response predates the appliedLabel discriminator; printing the production-promotion follow-up as a fail-safe.';
49
+ function productionPromotionReplay(runId, candidateId, project, origin) {
50
+ const originArguments = origin.local
51
+ ? ' --local'
52
+ : origin.server
53
+ ? ` --server ${shellArgument(origin.server)}`
54
+ : '';
55
+ return `Promote this profile with: orizu${originArguments} optimizations promote ${shellArgument(runId)} --candidate ${shellArgument(candidateId)} --label production --project ${shellArgument(project)}`;
56
+ }
7
57
  export async function promoteOptimizationCommand(args, io) {
8
58
  const runId = args.find(arg => !arg.startsWith('--') && arg !== option(args, '--candidate') && arg !== option(args, '--label') && arg !== option(args, '--project'));
9
59
  const candidateId = option(args, '--candidate');
@@ -18,17 +68,35 @@ export async function promoteOptimizationCommand(args, io) {
18
68
  if (!response.ok)
19
69
  throw new Error(`Failed to promote optimization candidate: ${await extractErrorMessage(response)}`);
20
70
  const data = await response.json();
71
+ const isProfilePromotion = 'profileVersionId' in data;
72
+ const replay = isProfilePromotion && data.appliedLabel !== 'production'
73
+ ? productionPromotionReplay(runId, candidateId, project, io.origin)
74
+ : null;
21
75
  if (io.json) {
22
- io.print(JSON.stringify(data.profileVersionId
23
- ? { profileVersionId: data.profileVersionId, components: data.components ?? [] }
76
+ io.print(JSON.stringify(isProfilePromotion
77
+ ? {
78
+ profileVersionId: data.profileVersionId,
79
+ versionNumber: data.versionNumber,
80
+ appliedLabel: data.appliedLabel,
81
+ components: data.components,
82
+ }
24
83
  : { promptVersionId: data.promptVersionId }));
84
+ if (isProfilePromotion && data.appliedLabel === undefined) {
85
+ io.printErr(APPLIED_LABEL_VERSION_SKEW_NOTE);
86
+ io.printErr(replay);
87
+ }
25
88
  return;
26
89
  }
27
- if (data.profileVersionId) {
90
+ if (isProfilePromotion) {
28
91
  io.print(`Promoted profile version ${sanitizeTerminalText(data.profileVersionId)}`);
29
- for (const component of data.components ?? [])
30
- io.print(`${sanitizeTerminalText(component.key)}: ${sanitizeTerminalText(component.status)}`);
92
+ if (data.appliedLabel === undefined) {
93
+ io.printErr(APPLIED_LABEL_VERSION_SKEW_NOTE);
94
+ }
95
+ if (replay)
96
+ io.print(replay);
97
+ for (const component of data.components)
98
+ io.print(`${inlineTerminalText(component.key)}: ${inlineTerminalText(component.status)}`);
31
99
  return;
32
100
  }
33
- io.print(`Promoted prompt version ${sanitizeTerminalText(data.promptVersionId ?? '')}`);
101
+ io.print(`Promoted prompt version ${sanitizeTerminalText(data.promptVersionId)}`);
34
102
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runner-dir-verify.d.ts","sourceRoot":"","sources":["../src/runner-dir-verify.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;GAWG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,MAAM,CAAA;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CAClE;AA6DD,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,wBAAsB,yBAAyB,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA4F5G;AAonBD;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CA0EtF;AAYD;6EAC6E;AAC7E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAY5E;AAoBD,MAAM,WAAW,gBAAgB;IAC/B;gFAC4E;IAC5E,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,uEAAuE;IACvE,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,GAChE,OAAO,CAAC,gBAAgB,CAAC,CAmD3B"}
1
+ {"version":3,"file":"runner-dir-verify.d.ts","sourceRoot":"","sources":["../src/runner-dir-verify.ts"],"names":[],"mappings":"AAaA;;;;;;;;;;;GAWG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,MAAM,CAAA;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CAClE;AA6DD,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,wBAAsB,yBAAyB,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA4F5G;AAonBD;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CA0EtF;AAYD;6EAC6E;AAC7E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAY5E;AAoBD,MAAM,WAAW,gBAAgB;IAC/B;gFAC4E;IAC5E,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,uEAAuE;IACvE,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,GAChE,OAAO,CAAC,gBAAgB,CAAC,CAuD3B"}
@@ -3,6 +3,7 @@ import { tmpdir } from 'os';
3
3
  import { basename, dirname, isAbsolute, join, normalize } from 'path';
4
4
  import { archiveArtifactEntries, expandHomePath, readArtifactEntries, shouldExcludeArtifactPath, } from './artifact-archive.js';
5
5
  import { authedFetch } from './http.js';
6
+ import { cleanupAll, throwWithCleanup } from './cleanup.js';
6
7
  function scanRunnerDir(dir, relativeDir = '') {
7
8
  const absoluteDir = relativeDir ? join(dir, relativeDir) : dir;
8
9
  const result = { envFiles: [], symlinks: [] };
@@ -82,7 +83,7 @@ export async function verifyRunnerDirRegistered(input) {
82
83
  'cannot be verified against it. Drop the flag to execute the registered bytes.');
83
84
  }
84
85
  if (registeredSha.toLowerCase() !== localSha.toLowerCase()) {
85
- throw new Error(`Local runner bytes at ${dir} are not registered runner version ${input.runnerVersionId} ` +
86
+ throw new Error(`runner_version_bytes_mismatch: Local runner bytes at ${dir} are not registered runner version ${input.runnerVersionId} ` +
86
87
  `(content sha ${localSha} != ${registeredSha}). Runs may only execute registered runner bytes ` +
87
88
  '(ADR-007): register the directory first with `orizu runners push` ' +
88
89
  '(use --session <session-id> inside a workspace session) and re-run against the new version, ' +
@@ -802,10 +803,7 @@ export async function verifyGepaRunnerDirsFromArgs(args, fetcher) {
802
803
  let rewritten = [...args];
803
804
  const verifiedDirs = [];
804
805
  const cleanups = [];
805
- const cleanup = () => {
806
- for (const dispose of cleanups)
807
- dispose();
808
- };
806
+ const cleanup = () => cleanupAll(cleanups, 'Failed to clean verified runner snapshots');
809
807
  try {
810
808
  for (const pair of GEPA_RUNNER_FLAG_PAIRS) {
811
809
  // Duplicate flags are refused outright (codex round 3, P1): argparse
@@ -841,8 +839,7 @@ export async function verifyGepaRunnerDirsFromArgs(args, fetcher) {
841
839
  }
842
840
  }
843
841
  catch (error) {
844
- cleanup();
845
- throw error;
842
+ throwWithCleanup(error, cleanups, 'Runner verification failed and snapshot cleanup also failed');
846
843
  }
847
844
  return { args: rewritten, verifiedDirs, cleanup };
848
845
  }
@@ -0,0 +1,2 @@
1
+ export declare const SKILLED_PROPOSER_BAKE_REPORT_FIELDS: readonly ["venv", "publishKey", "publishedVenv", "waitedForPublishLock", "pythonPathEntries", "executedPipArgv", "sslCertFile", "warnings", "marker", "python", "packageRoot", "manager", "lock", "vendoredGepaPath", "launcherVenv", "launcherSslCertFile"];
2
+ //# sourceMappingURL=skilled-proposer-bake-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skilled-proposer-bake-report.d.ts","sourceRoot":"","sources":["../src/skilled-proposer-bake-report.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mCAAmC,8PAA+P,CAAA"}
@@ -0,0 +1 @@
1
+ export const SKILLED_PROPOSER_BAKE_REPORT_FIELDS = ['venv', 'publishKey', 'publishedVenv', 'waitedForPublishLock', 'pythonPathEntries', 'executedPipArgv', 'sslCertFile', 'warnings', 'marker', 'python', 'packageRoot', 'manager', 'lock', 'vendoredGepaPath', 'launcherVenv', 'launcherSslCertFile'];
@@ -0,0 +1,2 @@
1
+ export declare function normalizedSkilledProposerConfig(value: string): string;
2
+ //# sourceMappingURL=skilled-proposer-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skilled-proposer-config.d.ts","sourceRoot":"","sources":["../src/skilled-proposer-config.ts"],"names":[],"mappings":"AAoJA,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA0CrE"}
@@ -0,0 +1,212 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync, statSync } from 'node:fs';
3
+ import { basename, dirname, extname, relative, resolve, sep } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ const CONFIG_KEYS = new Set(['schemaVersion', 'skills', 'additionalInstructionsFile', 'baseInstructionsFile',
6
+ 'maxWords', 'maxTokens', 'maxExamples', 'onError',
7
+ ]);
8
+ const PATH_SKILL_KEYS = new Set(['path', 'name', 'description']);
9
+ const INLINE_SKILL_KEYS = new Set(['name', 'description', 'inline']);
10
+ function configError(field, detail) { throw new Error(`--candidate-proposer-config ${field} ${detail}`); }
11
+ function isRecord(value) {
12
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
13
+ }
14
+ function rejectUnknownKeys(value, allowed, field = '') {
15
+ const unknown = Object.keys(value).find(key => !allowed.has(key));
16
+ if (unknown)
17
+ configError(field ? `${field}.${unknown}` : unknown, 'is not supported');
18
+ }
19
+ function sha256(bytes) { return createHash('sha256').update(bytes).digest('hex'); }
20
+ function decodeUtf8(bytes, field) {
21
+ try {
22
+ return new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes);
23
+ }
24
+ catch {
25
+ configError(field, 'must contain valid UTF-8 text');
26
+ }
27
+ }
28
+ function readRegularFile(path, field) {
29
+ try {
30
+ if (!statSync(path).isFile())
31
+ configError(field, 'must resolve to a file');
32
+ return readFileSync(path);
33
+ }
34
+ catch (error) {
35
+ if (error instanceof Error && error.message.startsWith('--candidate-proposer-config'))
36
+ throw error;
37
+ configError(field, `is missing or unreadable: ${error instanceof Error ? error.message : String(error)}`);
38
+ }
39
+ }
40
+ function logicalSource(configDirectory, path) {
41
+ return relative(configDirectory, path).split(sep).join('/') || basename(path);
42
+ }
43
+ function normalizedText(bytes, source, field) {
44
+ const content = decodeUtf8(bytes, field);
45
+ if (!content.trim())
46
+ configError(field, 'must not be empty');
47
+ return { source, content, sha256: sha256(bytes), byteLength: bytes.byteLength };
48
+ }
49
+ function parseFrontmatter(content, field) {
50
+ const lines = content.split(/\r?\n/);
51
+ if (lines[0]?.replace(/^\uFEFF/, '').trim() !== '---')
52
+ return { body: content };
53
+ const end = lines.slice(1).findIndex(line => line.trim() === '---');
54
+ if (end < 0)
55
+ configError(field, 'has malformed frontmatter without a closing ---');
56
+ const metadata = {};
57
+ for (const raw of lines.slice(1, end + 1)) {
58
+ if (/^[ \t]/.test(raw) || !raw.includes(':'))
59
+ continue;
60
+ const colon = raw.indexOf(':');
61
+ metadata[raw.slice(0, colon).trim()] = raw.slice(colon + 1).trim().replace(/^(['"])(.*)\1$/, '$2');
62
+ }
63
+ return {
64
+ name: metadata.name || undefined,
65
+ description: metadata.description || undefined,
66
+ body: lines.slice(end + 2).join('\n'),
67
+ };
68
+ }
69
+ function wellFormedString(value, field) {
70
+ if (!value.isWellFormed()) {
71
+ configError(field, 'must contain well-formed Unicode text');
72
+ }
73
+ return value;
74
+ }
75
+ function nonEmptyString(value, field) {
76
+ if (typeof value !== 'string' || !value.trim())
77
+ configError(field, 'must be a non-empty string');
78
+ return wellFormedString(value, field);
79
+ }
80
+ function nullableDescription(value, field) {
81
+ if (value === null)
82
+ return null;
83
+ if (typeof value !== 'string')
84
+ configError(field, 'must be a string or null');
85
+ return wellFormedString(value, field) || null;
86
+ }
87
+ function normalizePathSkill(entry, index, configDirectory) {
88
+ const field = `skills[${index}]`;
89
+ rejectUnknownKeys(entry, PATH_SKILL_KEYS, field);
90
+ const configuredPath = nonEmptyString(entry.path, `${field}.path`);
91
+ let resolvedPath = resolve(configDirectory, configuredPath);
92
+ try {
93
+ if (statSync(resolvedPath).isDirectory()) {
94
+ resolvedPath = resolve(resolvedPath, 'SKILL.md');
95
+ try {
96
+ if (!statSync(resolvedPath).isFile())
97
+ configError(`${field}.path`, 'directory must contain SKILL.md');
98
+ }
99
+ catch (error) {
100
+ if (error instanceof Error && error.message.startsWith('--candidate-proposer-config'))
101
+ throw error;
102
+ configError(`${field}.path`, 'directory must contain SKILL.md');
103
+ }
104
+ }
105
+ }
106
+ catch (error) {
107
+ if (error instanceof Error && error.message.startsWith('--candidate-proposer-config'))
108
+ throw error;
109
+ configError(`${field}.path`, 'is missing or unreadable');
110
+ }
111
+ const text = normalizedText(readRegularFile(resolvedPath, `${field}.path`), logicalSource(configDirectory, resolvedPath), `${field}.path`);
112
+ const frontmatter = parseFrontmatter(text.content, `${field}.path`);
113
+ if (!frontmatter.body.trim())
114
+ configError(`${field}.path`, 'must contain non-empty skill content');
115
+ const fallbackName = extname(resolvedPath)
116
+ ? basename(resolvedPath, extname(resolvedPath))
117
+ : basename(resolvedPath);
118
+ const name = entry.name === undefined
119
+ ? frontmatter.name ?? (basename(resolvedPath) === 'SKILL.md' ? basename(dirname(resolvedPath)) : fallbackName)
120
+ : nonEmptyString(entry.name, `${field}.name`);
121
+ const description = entry.description === undefined
122
+ ? frontmatter.description ?? null
123
+ : nullableDescription(entry.description, `${field}.description`);
124
+ return { name, description, ...text };
125
+ }
126
+ function normalizeInlineSkill(entry, index) {
127
+ const field = `skills[${index}]`;
128
+ rejectUnknownKeys(entry, INLINE_SKILL_KEYS, field);
129
+ const name = nonEmptyString(entry.name, `${field}.name`);
130
+ const description = nonEmptyString(entry.description, `${field}.description`);
131
+ const content = nonEmptyString(entry.inline, `${field}.inline`);
132
+ const bytes = Buffer.from(content, 'utf8');
133
+ return { name, description, source: `inline:${name}`, content,
134
+ sha256: sha256(bytes), byteLength: bytes.byteLength };
135
+ }
136
+ function normalizeSkill(value, index, configDirectory) {
137
+ const field = `skills[${index}]`;
138
+ if (!isRecord(value))
139
+ configError(field, 'must be an object');
140
+ const hasPath = Object.hasOwn(value, 'path');
141
+ const hasInline = Object.hasOwn(value, 'inline');
142
+ if (hasPath === hasInline)
143
+ configError(field, 'must contain exactly one of path or inline');
144
+ return hasPath ? normalizePathSkill(value, index, configDirectory) : normalizeInlineSkill(value, index);
145
+ }
146
+ function optionalPositiveInteger(value, field) {
147
+ if (value === undefined || value === null)
148
+ return null;
149
+ if (!Number.isSafeInteger(value) || value <= 0)
150
+ configError(field, 'must be a positive integer or null');
151
+ return value;
152
+ }
153
+ function normalizeInstructionFile(value, field, configDirectory) {
154
+ if (value === undefined || value === null)
155
+ return null;
156
+ const configuredPath = nonEmptyString(value, field);
157
+ const resolvedPath = resolve(configDirectory, configuredPath);
158
+ return normalizedText(readRegularFile(resolvedPath, field), logicalSource(configDirectory, resolvedPath), field);
159
+ }
160
+ function lockedPackageVersion() {
161
+ const lockPath = fileURLToPath(new URL('../requirements/skilled-proposer.lock', import.meta.url));
162
+ const match = /^skilled-proposer==([^\s\\]+)/m.exec(readFileSync(lockPath, 'utf8'));
163
+ if (!match)
164
+ throw new Error('ALI_1505_SKILLED_PROPOSER_LOCK_VERSION_MISSING');
165
+ return match[1];
166
+ }
167
+ export function normalizedSkilledProposerConfig(value) {
168
+ const suppliedPath = nonEmptyString(value.startsWith('@') ? value.slice(1) : value, 'path');
169
+ const configPath = resolve(suppliedPath);
170
+ const configBytes = readRegularFile(configPath, 'path');
171
+ const raw = decodeUtf8(configBytes, 'path').replace(/^\uFEFF/, '');
172
+ let parsed;
173
+ try {
174
+ parsed = JSON.parse(raw);
175
+ }
176
+ catch (error) {
177
+ configError('path', `must contain valid JSON: ${error instanceof Error ? error.message : String(error)}`);
178
+ }
179
+ if (!isRecord(parsed))
180
+ configError('path', 'must contain a JSON object');
181
+ rejectUnknownKeys(parsed, CONFIG_KEYS);
182
+ if (parsed.schemaVersion !== 1)
183
+ configError('schemaVersion', 'must be 1');
184
+ if (!Array.isArray(parsed.skills))
185
+ configError('skills', 'must be an array');
186
+ const configDirectory = dirname(configPath);
187
+ const skills = parsed.skills.map((skill, index) => normalizeSkill(skill, index, configDirectory));
188
+ const names = new Set();
189
+ for (const skill of skills) {
190
+ if (names.has(skill.name))
191
+ configError('skills', `contains duplicate resolved name ${skill.name}`);
192
+ names.add(skill.name);
193
+ }
194
+ const onError = parsed.onError ?? 'keep';
195
+ if (onError !== 'keep' && onError !== 'raise')
196
+ configError('onError', 'must be keep or raise');
197
+ const normalized = {
198
+ schemaVersion: 1,
199
+ implementation: 'cmpnd-ai/skilled-proposer',
200
+ packageVersion: lockedPackageVersion(),
201
+ skills,
202
+ additionalInstructions: normalizeInstructionFile(parsed.additionalInstructionsFile, 'additionalInstructionsFile', configDirectory),
203
+ baseInstructions: normalizeInstructionFile(parsed.baseInstructionsFile, 'baseInstructionsFile', configDirectory),
204
+ maxWords: optionalPositiveInteger(parsed.maxWords, 'maxWords'),
205
+ maxTokens: optionalPositiveInteger(parsed.maxTokens, 'maxTokens'),
206
+ maxExamples: optionalPositiveInteger(parsed.maxExamples, 'maxExamples'),
207
+ onError,
208
+ };
209
+ const configFileSha256 = sha256(configBytes);
210
+ const configSha256 = sha256(Buffer.from(JSON.stringify({ configFileSha256, ...normalized }), 'utf8'));
211
+ return JSON.stringify({ ...normalized, configFileSha256, configSha256 });
212
+ }
@@ -1,7 +1,33 @@
1
1
  import type { GepaEngine } from './gepa-engine-dispatch.js';
2
+ interface SkilledProposerVenvReport {
3
+ venv: string;
4
+ publishKey: string;
5
+ sslCertFile: string | null;
6
+ pythonPathEntries: string[];
7
+ warnings: Array<{
8
+ code: string;
9
+ message: string;
10
+ }>;
11
+ waitedForPublishLock: boolean;
12
+ error?: string;
13
+ message?: string;
14
+ publishedVenv?: boolean | null;
15
+ executedPipArgv?: string[] | null;
16
+ }
2
17
  export interface SkilledProposerLaunch {
3
18
  python: string;
4
19
  environment: NodeJS.ProcessEnv;
20
+ report?: SkilledProposerVenvReport;
21
+ }
22
+ export declare function spawnSkilledProposerChild(command: string, args: string[], engine: GepaEngine, environment: NodeJS.ProcessEnv): Promise<{
23
+ status: number | null;
24
+ error?: Error;
25
+ }>;
26
+ interface BakeCommandIo {
27
+ print: (value: string) => void;
28
+ printErr?: (value: string) => void;
5
29
  }
30
+ export declare function skilledProposerBakeCommand(io: BakeCommandIo, verify?: boolean): number;
6
31
  export declare function prepareSkilledProposerLaunch(python: string, engine: GepaEngine, environment: NodeJS.ProcessEnv): SkilledProposerLaunch;
32
+ export {};
7
33
  //# sourceMappingURL=skilled-proposer-launch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skilled-proposer-launch.d.ts","sourceRoot":"","sources":["../src/skilled-proposer-launch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAa3D,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAC,UAAU,CAAA;CAC/B;AAwCD,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,MAAM,CAAC,UAAU,GAC7B,qBAAqB,CAwBvB"}
1
+ {"version":3,"file":"skilled-proposer-launch.d.ts","sourceRoot":"","sources":["../src/skilled-proposer-launch.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAK3D,UAAU,yBAAyB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClD,oBAAoB,EAAE,OAAO,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IAC9B,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CAClC;AACD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAC,UAAU,CAAA;IAC9B,MAAM,CAAC,EAAE,yBAAyB,CAAA;CACnC;AAiBD,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,UAAU;YA4B9C,MAAM,GAAG,IAAI;YAAU,KAAK;GAUlE;AAwCD,UAAU,aAAa;IAAG,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE;AAQ9F,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,aAAa,EAAE,MAAM,UAAQ,GAAG,MAAM,CAepF;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,MAAM,CAAC,UAAU,GAC7B,qBAAqB,CAyBvB"}