orizu 0.5.30 → 0.5.32

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 (82) hide show
  1. package/dist/app-commands.d.ts +2 -0
  2. package/dist/app-commands.d.ts.map +1 -1
  3. package/dist/app-commands.js +10 -4
  4. package/dist/gepa-engine-dispatch.d.ts.map +1 -1
  5. package/dist/gepa-engine-dispatch.js +16 -1
  6. package/dist/help.js +7 -7
  7. package/dist/hosted-agent-prompt.d.ts +14 -0
  8. package/dist/hosted-agent-prompt.d.ts.map +1 -0
  9. package/dist/hosted-agent-prompt.js +17 -0
  10. package/dist/hosted-gepa-cli.d.ts.map +1 -1
  11. package/dist/hosted-gepa-cli.js +7 -1
  12. package/dist/hosted-harness-claude.d.ts.map +1 -1
  13. package/dist/hosted-harness-claude.js +4 -1
  14. package/dist/hosted-harness-opencode.d.ts +1 -0
  15. package/dist/hosted-harness-opencode.d.ts.map +1 -1
  16. package/dist/hosted-harness-opencode.js +27 -7
  17. package/dist/hosted-harness.d.ts +30 -1
  18. package/dist/hosted-harness.d.ts.map +1 -1
  19. package/dist/hosted-harness.js +5 -0
  20. package/dist/hosted-optimization-prebaked-marker.d.ts +9 -0
  21. package/dist/hosted-optimization-prebaked-marker.d.ts.map +1 -0
  22. package/dist/hosted-optimization-prebaked-marker.js +22 -0
  23. package/dist/hosted-tool-hints.d.ts +56 -0
  24. package/dist/hosted-tool-hints.d.ts.map +1 -0
  25. package/dist/hosted-tool-hints.js +105 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +2 -5
  28. package/dist/instruction-set-gepa-launch.d.ts +21 -2
  29. package/dist/instruction-set-gepa-launch.d.ts.map +1 -1
  30. package/dist/instruction-set-gepa-launch.js +123 -3
  31. package/dist/markdown-report-input.d.ts.map +1 -1
  32. package/dist/markdown-report-input.js +1 -1
  33. package/dist/optimizations-promote-cli.d.ts.map +1 -1
  34. package/dist/optimizations-promote-cli.js +19 -0
  35. package/dist/preview-runtime.d.ts +7 -0
  36. package/dist/preview-runtime.d.ts.map +1 -1
  37. package/dist/preview-runtime.js +49 -5
  38. package/dist/provider-refusals.js +2 -2
  39. package/dist/report-markdown-blankness.d.ts +6 -0
  40. package/dist/report-markdown-blankness.d.ts.map +1 -1
  41. package/dist/report-markdown-blankness.js +47 -4
  42. package/dist/seed-against-production.d.ts +11 -0
  43. package/dist/seed-against-production.d.ts.map +1 -0
  44. package/dist/seed-against-production.js +1 -0
  45. package/dist/setup-and-skills-commands.d.ts.map +1 -1
  46. package/dist/setup-and-skills-commands.js +123 -9
  47. package/dist/skill-installer.d.ts +32 -0
  48. package/dist/skill-installer.d.ts.map +1 -1
  49. package/dist/skill-installer.js +228 -1
  50. package/package.json +1 -1
  51. package/src/app-commands.ts +13 -3
  52. package/src/gepa-engine-dispatch.ts +18 -1
  53. package/src/help.ts +7 -7
  54. package/src/hosted-agent-prompt.ts +18 -0
  55. package/src/hosted-gepa-cli.ts +7 -1
  56. package/src/hosted-harness-claude.ts +4 -1
  57. package/src/hosted-harness-opencode.ts +28 -7
  58. package/src/hosted-harness.ts +32 -1
  59. package/src/hosted-optimization-prebaked-marker.ts +20 -0
  60. package/src/hosted-tool-hints.ts +153 -0
  61. package/src/index.ts +2 -5
  62. package/src/instruction-set-gepa-launch.ts +161 -3
  63. package/src/markdown-report-input.ts +3 -1
  64. package/src/optimizations-promote-cli.ts +20 -2
  65. package/src/preview-runtime.ts +44 -5
  66. package/src/provider-refusals.ts +2 -2
  67. package/src/report-markdown-blankness.ts +59 -4
  68. package/src/seed-against-production.ts +10 -0
  69. package/src/setup-and-skills-commands.ts +160 -8
  70. package/src/skill-installer.ts +315 -0
  71. package/vendor/skills/orizu/SKILL.md +3 -3
  72. package/vendor/skills/orizu/flows/first-win.md +3 -1
  73. package/vendor/skills/orizu/flows/promote.md +2 -1
  74. package/vendor/skills/orizu/references/building-apps.md +2 -0
  75. package/vendor/skills/orizu/references/building-judges.md +138 -8
  76. package/vendor/skills/orizu/references/cli-reference.md +2 -2
  77. package/vendor/skills/orizu/references/migrate-existing-gepa-setup.md +1 -2
  78. package/vendor/skills/orizu/references/optimization-reports.md +1 -1
  79. package/vendor/skills/orizu/references/optimization-with-gepa.md +22 -4
  80. package/vendor/skills/orizu/references/primer.md +2 -2
  81. package/vendor/skills/orizu/references/prompt-control-plane.md +6 -1
  82. package/vendor/skills/orizu/scripts/test-app.mjs +53 -4
@@ -0,0 +1,105 @@
1
+ const OPENCODE = 'opencode';
2
+ const CLAUDE_AGENT_SDK = 'claude-agent-sdk';
3
+ /** What kind of thing a tool does — the renderer picks an icon/layout from
4
+ * this, never from the tool name. */
5
+ export const TOOL_CATEGORIES = {
6
+ file: 'file',
7
+ command: 'command',
8
+ search: 'search',
9
+ network: 'network',
10
+ question: 'question',
11
+ subAgent: 'sub-agent',
12
+ list: 'list',
13
+ plug: 'plug',
14
+ other: 'other',
15
+ };
16
+ /** How the tool's output text should be shown. */
17
+ export const TOOL_OUTPUT_KINDS = {
18
+ text: 'text',
19
+ diff: 'diff',
20
+ markdown: 'markdown',
21
+ json: 'json',
22
+ list: 'list',
23
+ };
24
+ // Argument names that look like they hold a credential. Redacted for every
25
+ // tool, and never used as the fallback summary.
26
+ const SECRET_LOOKING_KEY = /token|secret|password|passwd|api[_-]?key|authorization|credential/i;
27
+ /**
28
+ * Look a tool up in a driver's hint table. A tool the table does not know (an
29
+ * MCP tool, a plug-in) falls back to the `plug` category, summarized by its
30
+ * first non-empty string argument that does not look like a secret.
31
+ */
32
+ export function resolveToolHint(table, tool, args = {}) {
33
+ const known = Object.hasOwn(table, tool) ? table[tool] : undefined;
34
+ const secretKeys = Object.keys(args).filter(key => SECRET_LOOKING_KEY.test(key));
35
+ const redactKeys = [...new Set([...(known?.redactKeys ?? []), ...secretKeys])];
36
+ if (known)
37
+ return { ...known, redactKeys };
38
+ const summaryKey = Object.keys(args).find(key => {
39
+ const value = args[key];
40
+ return typeof value === 'string' && value.length > 0 && !redactKeys.includes(key);
41
+ }) ?? null;
42
+ return { category: TOOL_CATEGORIES.plug, summaryKey, outputKind: TOOL_OUTPUT_KINDS.text, redactKeys };
43
+ }
44
+ // OpenCode's built-in tools, as the pinned opencode-ai@1.14.41 tool registry
45
+ // lists them, with the input key that summarizes each call. Tools not listed
46
+ // here (MCP tools, plug-ins) resolve to `plug` through resolveToolHint.
47
+ export const OPENCODE_TOOL_HINTS = {
48
+ read: { category: 'file', label: 'Read', summaryKey: 'filePath', outputKind: 'text', redactKeys: [] },
49
+ edit: { category: 'file', label: 'Edited', summaryKey: 'filePath', outputKind: 'diff', redactKeys: ['oldString', 'newString'] },
50
+ write: { category: 'file', label: 'Wrote', summaryKey: 'filePath', outputKind: 'text', redactKeys: ['content'] },
51
+ apply_patch: { category: 'file', label: 'Edited', summaryKey: 'patchText', outputKind: 'diff', redactKeys: [] },
52
+ lsp: { category: 'file', label: 'Checked', summaryKey: 'filePath', outputKind: 'json', redactKeys: [] },
53
+ bash: { category: 'command', label: 'Ran', summaryKey: 'command', outputKind: 'text', redactKeys: [] },
54
+ glob: { category: 'search', label: 'Listed', summaryKey: 'pattern', outputKind: 'list', redactKeys: [] },
55
+ grep: { category: 'search', label: 'Searched', summaryKey: 'pattern', outputKind: 'text', redactKeys: [] },
56
+ webfetch: { category: 'network', label: 'Fetched', summaryKey: 'url', outputKind: 'markdown', redactKeys: [] },
57
+ websearch: { category: 'network', label: 'Searched', summaryKey: 'query', outputKind: 'text', redactKeys: [] },
58
+ question: { category: 'question', label: 'Asked', summaryKey: 'questions', outputKind: 'text', redactKeys: [] },
59
+ plan_exit: { category: 'question', label: 'Asked', summaryKey: null, outputKind: 'text', redactKeys: [] },
60
+ task: { category: 'sub-agent', label: 'Sub-agent', summaryKey: 'description', outputKind: 'markdown', redactKeys: [] },
61
+ todowrite: { category: 'list', label: 'Planned', summaryKey: 'todos', outputKind: 'json', redactKeys: [] },
62
+ skill: { category: 'other', label: 'Loaded', summaryKey: 'name', outputKind: 'markdown', redactKeys: [] },
63
+ invalid: { category: 'other', label: 'Invalid', summaryKey: 'tool', outputKind: 'text', redactKeys: [] },
64
+ };
65
+ // The Claude Agent SDK's built-in tools (SDK 0.3.201: input types in
66
+ // sdk-tools.d.ts, names from the SDK; `Task` is the older name of `Agent`),
67
+ // with the input key that summarizes each call. MCP tools
68
+ // (`mcp__<server>__<tool>`) are not listed and resolve to `plug` through
69
+ // resolveToolHint. This driver sends no `title` or `metadata`: the SDK has no
70
+ // equivalent of OpenCode's.
71
+ export const CLAUDE_TOOL_HINTS = {
72
+ Read: { category: 'file', label: 'Read', summaryKey: 'file_path', outputKind: 'text', redactKeys: [] },
73
+ Edit: { category: 'file', label: 'Edited', summaryKey: 'file_path', outputKind: 'text', redactKeys: ['old_string', 'new_string'] },
74
+ Write: { category: 'file', label: 'Wrote', summaryKey: 'file_path', outputKind: 'text', redactKeys: ['content'] },
75
+ NotebookEdit: { category: 'file', label: 'Edited', summaryKey: 'notebook_path', outputKind: 'text', redactKeys: ['new_source'] },
76
+ Bash: { category: 'command', label: 'Ran', summaryKey: 'command', outputKind: 'text', redactKeys: [] },
77
+ TaskOutput: { category: 'command', label: 'Checked', summaryKey: 'task_id', outputKind: 'text', redactKeys: [] },
78
+ TaskStop: { category: 'command', label: 'Stopped', summaryKey: 'task_id', outputKind: 'text', redactKeys: [] },
79
+ Glob: { category: 'search', label: 'Listed', summaryKey: 'pattern', outputKind: 'list', redactKeys: [] },
80
+ Grep: { category: 'search', label: 'Searched', summaryKey: 'pattern', outputKind: 'text', redactKeys: [] },
81
+ WebFetch: { category: 'network', label: 'Fetched', summaryKey: 'url', outputKind: 'markdown', redactKeys: [] },
82
+ WebSearch: { category: 'network', label: 'Searched', summaryKey: 'query', outputKind: 'text', redactKeys: [] },
83
+ AskUserQuestion: { category: 'question', label: 'Asked', summaryKey: 'questions', outputKind: 'text', redactKeys: [] },
84
+ ExitPlanMode: { category: 'question', label: 'Asked', summaryKey: null, outputKind: 'markdown', redactKeys: [] },
85
+ Agent: { category: 'sub-agent', label: 'Sub-agent', summaryKey: 'description', outputKind: 'markdown', redactKeys: [] },
86
+ Task: { category: 'sub-agent', label: 'Sub-agent', summaryKey: 'description', outputKind: 'markdown', redactKeys: [] },
87
+ TodoWrite: { category: 'list', label: 'Planned', summaryKey: 'todos', outputKind: 'text', redactKeys: [] },
88
+ ListMcpResourcesTool: { category: 'plug', label: 'Listed', summaryKey: 'server', outputKind: 'json', redactKeys: [] },
89
+ ReadMcpResourceTool: { category: 'plug', label: 'Read', summaryKey: 'uri', outputKind: 'text', redactKeys: [] },
90
+ Skill: { category: 'other', label: 'Loaded', summaryKey: 'skill', outputKind: 'markdown', redactKeys: [] },
91
+ };
92
+ const NO_HINTS = {};
93
+ /**
94
+ * The hint table for the harness that produced an event. Events recorded
95
+ * before the seam named its harness came from OpenCode, the default driver.
96
+ * A harness with no table here (a future pi driver, say) gets none, so every
97
+ * tool falls back to its own name and first string argument.
98
+ */
99
+ export function toolHintsFor(harness) {
100
+ if (harness === undefined || harness === OPENCODE)
101
+ return OPENCODE_TOOL_HINTS;
102
+ if (harness === CLAUDE_AGENT_SDK)
103
+ return CLAUDE_TOOL_HINTS;
104
+ return NO_HINTS;
105
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAsLA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAEhE,eAAO,MAAM,wBAAwB,GAAI,iBAAiB,MAAM;;;EACJ,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AA+tCtD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CA0B1G;AAED,eAAO,MAAM,iBAAiB,SAAU,CAAA;AACxC,eAAO,MAAM,uBAAuB,QAAkB,CAAA;AACtD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAexH;AAmMD,wBAAsB,IAAI,CAAC,OAAO,WAAwB,iBAyhBzD;AA6DD,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAEhF,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAsLA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAEhE,eAAO,MAAM,wBAAwB,GAAI,iBAAiB,MAAM;;;EACJ,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AA4tCtD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CA0B1G;AAED,eAAO,MAAM,iBAAiB,SAAU,CAAA;AACxC,eAAO,MAAM,uBAAuB,QAAkB,CAAA;AACtD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAexH;AAmMD,wBAAsB,IAAI,CAAC,OAAO,WAAwB,iBAyhBzD;AA6DD,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAEhF,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA"}
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ import { resolveAuthTokenForBaseUrl } from './credentials.js';
31
31
  import { applyHostedInstructionSetProfileOverride, dispatchHostedCommands } from './hosted-commands.js';
32
32
  import { dispatchGepaEngine } from './gepa-engine-dispatch.js';
33
33
  import { getGepaPythonPathEntries } from './gepa-python-paths.js';
34
- import { resolveGepaInstructionSetProfileVersion } from './instruction-set-gepa-launch.js';
34
+ import { resolveGepaSeedFromEnvironment } from './instruction-set-gepa-launch.js';
35
35
  import { prepareSkilledProposerLaunch, spawnSkilledProposerChild } from './skilled-proposer-launch.js';
36
36
  import { reportCommentsCommand, throwDeprecatedPromptCommentsCommand } from './report-comments-cli.js';
37
37
  import { getCapabilities, renderHelpForArgs } from './help.js';
@@ -997,10 +997,7 @@ async function runGepaOptimization() {
997
997
  PYTHONPATH: getGepaPythonPathEntries(process.env.PYTHONPATH).join(delimiter),
998
998
  PYTHONUNBUFFERED: process.env.PYTHONUNBUFFERED || '1',
999
999
  }));
1000
- const instructionSetName = dispatch.environment.ORIZU_INSTRUCTION_SET_NAME;
1001
- const modelConfigIdentity = dispatch.environment.ORIZU_MODEL_CONFIG_IDENTITY;
1002
- const instructionSetProfileVersionId = instructionSetName && modelConfigIdentity
1003
- ? await resolveGepaInstructionSetProfileVersion(instructionSetName, modelConfigIdentity, project) : undefined;
1000
+ const instructionSetProfileVersionId = await resolveGepaSeedFromEnvironment(dispatch.environment, project, 'production');
1004
1001
  if (instructionSetProfileVersionId)
1005
1002
  dispatch.environment.ORIZU_INSTRUCTION_SET_PROFILE_VERSION_ID = instructionSetProfileVersionId;
1006
1003
  selectedEngine = dispatch.engine;
@@ -1,3 +1,22 @@
1
- /** Resolve the requested Profile's Production tuple for optimization. */
2
- export declare function resolveGepaInstructionSetProfileVersion(instructionSetName: string, modelConfigIdentity: string, project: string): Promise<string>;
1
+ /** The Seed a run-gepa launch names: set reference, profile, and optional version. */
2
+ export interface GepaSeedSelector {
3
+ instructionSetName: string;
4
+ modelConfigIdentity?: string;
5
+ versionNumber?: number;
6
+ }
7
+ /**
8
+ * Combine `--instruction-set`, `--model-config` and `--profile-version` into
9
+ * one Seed. A value with `/` or `@` is a specifier (`set/profile@vN`); any
10
+ * other value is a set slug or exact name, passed through unchanged. Values
11
+ * named twice must agree, the same rule `sync` applies to `--version`.
12
+ */
13
+ export declare function gepaSeedSelector(instructionSetArg: string, modelConfigFlag: string | undefined, versionFlag: string | undefined): GepaSeedSelector;
14
+ /**
15
+ * Resolve the Seed that dispatch normalised into the connector environment.
16
+ * Returns undefined when no instruction set was selected. When no version is
17
+ * named, `unversioned: 'production'` resolves the profile's Production here
18
+ * (local runs); `unversioned: 'server'` leaves it to the hosted launch route,
19
+ * which resolves Production itself in the same call that queues the run.
20
+ */
21
+ export declare function resolveGepaSeedFromEnvironment(environment: NodeJS.ProcessEnv, project: string, unversioned: 'production' | 'server'): Promise<string | undefined>;
3
22
  //# sourceMappingURL=instruction-set-gepa-launch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instruction-set-gepa-launch.d.ts","sourceRoot":"","sources":["../src/instruction-set-gepa-launch.ts"],"names":[],"mappings":"AAYA,yEAAyE;AACzE,wBAAsB,uCAAuC,CAC3D,kBAAkB,EAAE,MAAM,EAC1B,mBAAmB,EAAE,MAAM,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAUjB"}
1
+ {"version":3,"file":"instruction-set-gepa-launch.d.ts","sourceRoot":"","sources":["../src/instruction-set-gepa-launch.ts"],"names":[],"mappings":"AAsFA,sFAAsF;AACtF,MAAM,WAAW,gBAAgB;IAC/B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAWD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,gBAAgB,CAiBlB;AAqCD;;;;;;GAMG;AACH,wBAAsB,8BAA8B,CAClD,WAAW,EAAE,MAAM,CAAC,UAAU,EAC9B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,YAAY,GAAG,QAAQ,GACnC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAQ7B"}
@@ -1,17 +1,137 @@
1
1
  import { authedFetch } from './http.js';
2
+ import { MAX_VERSION_NUMBER, parseSpecifier } from './instruction-set-lock/index.js';
2
3
  import { parseJsonResponse } from './json-response.js';
3
- /** Resolve the requested Profile's Production tuple for optimization. */
4
- export async function resolveGepaInstructionSetProfileVersion(instructionSetName, modelConfigIdentity, project) {
4
+ /**
5
+ * A version runs and promotes only when its component keys equal the set's
6
+ * current shape exactly: promotion refuses any other key set
7
+ * (instruction_set_candidate_shape_mismatch). Returns null when they match.
8
+ */
9
+ function shapeDifference(shape, version) {
10
+ const keys = new Set(version.componentKeys ?? []);
11
+ const shapeKeys = new Set(shape);
12
+ const missing = shape.filter(key => !keys.has(key));
13
+ const extra = [...keys].filter(key => !shapeKeys.has(key)).sort();
14
+ return missing.length === 0 && extra.length === 0 ? null : { missing, extra };
15
+ }
16
+ function describeDifference({ missing, extra }) {
17
+ const quote = (keys) => keys.map(key => `"${key}"`).join(', ');
18
+ return [
19
+ ...(missing.length > 0 ? [`missing ${quote(missing)}`] : []),
20
+ ...(extra.length > 0 ? [`extra ${quote(extra)}`] : []),
21
+ ].join('; ');
22
+ }
23
+ function newestMatchingVersion(shape, versions) {
24
+ return versions
25
+ .filter(candidate => Array.isArray(candidate.componentKeys) && shapeDifference(shape, candidate) === null)
26
+ .reduce((max, candidate) => {
27
+ const number = candidate.versionNumber ?? 0;
28
+ return max === null || number > max ? number : max;
29
+ }, null);
30
+ }
31
+ /**
32
+ * ORI-2128: a version made before a shape change can lack a component of the
33
+ * set's current shape, or carry one the set has since removed. The runner or
34
+ * promotion would refuse it later; refuse it here, before a run is spawned or
35
+ * queued. A server that predates per-version component keys sends none; the
36
+ * runner remains the backstop.
37
+ */
38
+ function requireMatchingSeed(shape, versions, version, modelConfigIdentity, role) {
39
+ if (!Array.isArray(shape) || !Array.isArray(version.componentKeys))
40
+ return;
41
+ const difference = shapeDifference(shape, version);
42
+ if (difference === null)
43
+ return;
44
+ const newest = newestMatchingVersion(shape, versions);
45
+ const detail = describeDifference(difference);
46
+ if (role === 'production') {
47
+ const newestNote = newest === null ? 'no version of this profile matches it yet' : `newest matching: v${newest}`;
48
+ throw new Error(`instruction_set_profile_production_incomplete: ${modelConfigIdentity} production v${version.versionNumber} does not match the current shape (${detail}); promote a version that matches it, or name one with @vN (${newestNote})`);
49
+ }
50
+ const advice = newest === null
51
+ ? 'no version of this profile matches the current shape yet'
52
+ : `pick v${newest}`;
53
+ throw new Error(`instruction_set_profile_version_incomplete: ${modelConfigIdentity} v${version.versionNumber} does not match the current shape (${detail}); ${advice}`);
54
+ }
55
+ function profileVersionNumber(value) {
56
+ const match = /^v?([1-9][0-9]*)$/u.exec(value);
57
+ const versionNumber = match ? Number(match[1]) : Number.NaN;
58
+ if (!Number.isSafeInteger(versionNumber) || versionNumber > MAX_VERSION_NUMBER) {
59
+ throw new Error(`--profile-version must be a version number such as 3 or v3; received ${value}`);
60
+ }
61
+ return versionNumber;
62
+ }
63
+ /**
64
+ * Combine `--instruction-set`, `--model-config` and `--profile-version` into
65
+ * one Seed. A value with `/` or `@` is a specifier (`set/profile@vN`); any
66
+ * other value is a set slug or exact name, passed through unchanged. Values
67
+ * named twice must agree, the same rule `sync` applies to `--version`.
68
+ */
69
+ export function gepaSeedSelector(instructionSetArg, modelConfigFlag, versionFlag) {
70
+ const flagVersion = versionFlag === undefined ? undefined : profileVersionNumber(versionFlag);
71
+ if (!instructionSetArg.includes('/') && !instructionSetArg.includes('@')) {
72
+ return { instructionSetName: instructionSetArg, modelConfigIdentity: modelConfigFlag, versionNumber: flagVersion };
73
+ }
74
+ const parsed = parseSpecifier(instructionSetArg);
75
+ if (parsed.profile !== undefined && modelConfigFlag !== undefined && parsed.profile !== modelConfigFlag) {
76
+ throw new Error(`instruction_set_seed_conflict: specifier names profile ${parsed.profile} but --model-config is ${modelConfigFlag}`);
77
+ }
78
+ if (parsed.versionNumber !== undefined && flagVersion !== undefined && parsed.versionNumber !== flagVersion) {
79
+ throw new Error(`instruction_set_seed_conflict: specifier names v${parsed.versionNumber} but --profile-version is v${flagVersion}`);
80
+ }
81
+ return {
82
+ instructionSetName: parsed.set,
83
+ modelConfigIdentity: parsed.profile ?? modelConfigFlag,
84
+ versionNumber: parsed.versionNumber ?? flagVersion,
85
+ };
86
+ }
87
+ /**
88
+ * Resolve the Seed's profile version id: the named version when one is
89
+ * given, otherwise the requested Profile's Production.
90
+ */
91
+ async function resolveGepaInstructionSetProfileVersion(instructionSetName, modelConfigIdentity, project, versionNumber) {
5
92
  const response = await authedFetch(`/api/cli/instruction-sets/${encodeURIComponent(instructionSetName)}?project=${encodeURIComponent(project)}`);
6
93
  if (!response.ok)
7
94
  throw new Error(`Instruction set resolution failed: ${await response.text()}`);
8
95
  const payload = await parseJsonResponse(response, 'Instruction set resolution');
9
96
  const instructionSet = payload.instructionSet;
10
97
  const profile = instructionSet?.profiles?.find(candidate => candidate.modelConfigIdentity === modelConfigIdentity);
11
- if (!profile)
98
+ // An archived model config's profile is not resolvable: the same refusal
99
+ // the server's resolve_instruction_set gives hosted runs.
100
+ if (!profile || profile.modelConfigArchivedAt)
12
101
  throw new Error(`instruction_set_profile_not_found: ${modelConfigIdentity}`);
102
+ const versions = profile.versions ?? [];
103
+ if (versionNumber !== undefined) {
104
+ const version = versions.find(candidate => candidate.versionNumber === versionNumber);
105
+ if (version?.profileVersionId) {
106
+ requireMatchingSeed(instructionSet?.shape, versions, version, modelConfigIdentity, 'named');
107
+ return version.profileVersionId;
108
+ }
109
+ const newest = versions.reduce((max, candidate) => Math.max(max, candidate.versionNumber ?? 0), 0);
110
+ throw new Error(`instruction_set_profile_version_not_found: ${modelConfigIdentity} has no v${versionNumber}; ${newest > 0 ? `newest is v${newest}` : 'it has no versions'}`);
111
+ }
13
112
  const profileVersionId = profile.production?.profileVersionId;
14
113
  if (!profileVersionId)
15
114
  throw new Error(`instruction_set_profile_not_promoted: ${modelConfigIdentity}`);
115
+ const production = versions.find(candidate => candidate.profileVersionId === profileVersionId);
116
+ if (production)
117
+ requireMatchingSeed(instructionSet?.shape, versions, production, modelConfigIdentity, 'production');
16
118
  return profileVersionId;
17
119
  }
120
+ /**
121
+ * Resolve the Seed that dispatch normalised into the connector environment.
122
+ * Returns undefined when no instruction set was selected. When no version is
123
+ * named, `unversioned: 'production'` resolves the profile's Production here
124
+ * (local runs); `unversioned: 'server'` leaves it to the hosted launch route,
125
+ * which resolves Production itself in the same call that queues the run.
126
+ */
127
+ export async function resolveGepaSeedFromEnvironment(environment, project, unversioned) {
128
+ const instructionSetName = environment.ORIZU_INSTRUCTION_SET_NAME;
129
+ const modelConfigIdentity = environment.ORIZU_MODEL_CONFIG_IDENTITY;
130
+ if (!instructionSetName || !modelConfigIdentity)
131
+ return undefined;
132
+ const rawVersion = environment.ORIZU_INSTRUCTION_SET_PROFILE_VERSION_NUMBER;
133
+ const versionNumber = rawVersion === undefined ? undefined : profileVersionNumber(rawVersion);
134
+ if (versionNumber === undefined && unversioned === 'server')
135
+ return undefined;
136
+ return resolveGepaInstructionSetProfileVersion(instructionSetName, modelConfigIdentity, project, versionNumber);
137
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-report-input.d.ts","sourceRoot":"","sources":["../src/markdown-report-input.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,yBAAyB,QAAkB,CAAA;AAsCxD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,WAAW,EAAE,cAAc,GAAG,QAAQ,GAAG,MAAM,GAC9C,mBAAmB,GAAG,IAAI,CA8C5B"}
1
+ {"version":3,"file":"markdown-report-input.d.ts","sourceRoot":"","sources":["../src/markdown-report-input.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,yBAAyB,QAAkB,CAAA;AAsCxD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,WAAW,EAAE,cAAc,GAAG,QAAQ,GAAG,MAAM,GAC9C,mBAAmB,GAAG,IAAI,CAgD5B"}
@@ -72,7 +72,7 @@ export function readMarkdownReportInput(args, reportLabel) {
72
72
  sourceName = 'inline';
73
73
  }
74
74
  if (!hasCanonicalReportMarkdownContent(markdown)) {
75
- throw new Error(`${reportLabel} report markdown must not be blank`);
75
+ throw new Error(`${reportLabel} report has no visible content: it is empty or contains only whitespace`);
76
76
  }
77
77
  if (Buffer.byteLength(markdown, 'utf8') > MARKDOWN_REPORT_MAX_BYTES) {
78
78
  throw new Error(`${reportLabel} report exceeds ${MARKDOWN_REPORT_MAX_BYTES} bytes`);
@@ -1 +1 @@
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
+ {"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;AAGpD,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;AAqED,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD1G"}
@@ -54,6 +54,17 @@ function productionPromotionReplay(runId, candidateId, project, origin) {
54
54
  : '';
55
55
  return `Promote this profile with: orizu${originArguments} optimizations promote ${shellArgument(runId)} --candidate ${shellArgument(candidateId)} --label production --project ${shellArgument(project)}`;
56
56
  }
57
+ /**
58
+ * ORI-2128: a candidate grown from a Seed older than Production does not
59
+ * contain the changes made since that Seed. Warn; never refuse (ORI-2132).
60
+ */
61
+ function staleSeedWarningFor(data) {
62
+ const seed = data.runSeedVersionNumber;
63
+ const production = data.productionVersionNumber;
64
+ if (typeof seed !== 'number' || typeof production !== 'number' || seed >= production)
65
+ return null;
66
+ return `Warning: this run started from v${seed}; production is v${production}. Changes made since v${seed} are not in this candidate.`;
67
+ }
57
68
  export async function promoteOptimizationCommand(args, io) {
58
69
  const runId = args.find(arg => !arg.startsWith('--') && arg !== option(args, '--candidate') && arg !== option(args, '--label') && arg !== option(args, '--project'));
59
70
  const candidateId = option(args, '--candidate');
@@ -72,15 +83,21 @@ export async function promoteOptimizationCommand(args, io) {
72
83
  const replay = isProfilePromotion && data.appliedLabel !== 'production'
73
84
  ? productionPromotionReplay(runId, candidateId, project, io.origin)
74
85
  : null;
86
+ const staleSeedWarning = isProfilePromotion ? staleSeedWarningFor(data) : null;
75
87
  if (io.json) {
76
88
  io.print(JSON.stringify(isProfilePromotion
77
89
  ? {
78
90
  profileVersionId: data.profileVersionId,
79
91
  versionNumber: data.versionNumber,
80
92
  appliedLabel: data.appliedLabel,
93
+ // JSON.stringify omits these when an older server did not send them.
94
+ runSeedVersionNumber: data.runSeedVersionNumber,
95
+ productionVersionNumber: data.productionVersionNumber,
81
96
  components: data.components,
82
97
  }
83
98
  : { promptVersionId: data.promptVersionId }));
99
+ if (staleSeedWarning)
100
+ io.printErr(staleSeedWarning);
84
101
  if (isProfilePromotion && data.appliedLabel === undefined) {
85
102
  io.printErr(APPLIED_LABEL_VERSION_SKEW_NOTE);
86
103
  io.printErr(replay);
@@ -88,6 +105,8 @@ export async function promoteOptimizationCommand(args, io) {
88
105
  return;
89
106
  }
90
107
  if (isProfilePromotion) {
108
+ if (staleSeedWarning)
109
+ io.printErr(staleSeedWarning);
91
110
  io.print(`Promoted profile version ${sanitizeTerminalText(data.profileVersionId)}`);
92
111
  if (data.appliedLabel === undefined) {
93
112
  io.printErr(APPLIED_LABEL_VERSION_SKEW_NOTE);
@@ -47,6 +47,13 @@ interface PreviewPage {
47
47
  on?: (event: string, listener: () => void) => unknown;
48
48
  off?: (event: string, listener: () => void) => unknown;
49
49
  }
50
+ /**
51
+ * Removes line and block comments, walking left to right so a `//` or `/*`
52
+ * inside a quoted string or template literal is left alone. A block comment
53
+ * keeps its newlines, so line numbers in later messages stay true.
54
+ * Regex literals are not tracked; a quote inside one can hide a comment.
55
+ */
56
+ export declare function stripComments(code: string): string;
50
57
  export declare function validatePreviewInputs(options: AppPreviewOptions): Issue[];
51
58
  export declare function runLocalAppPreview(options: AppPreviewOptions): Promise<AppPreviewResult>;
52
59
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"preview-runtime.d.ts","sourceRoot":"","sources":["../src/preview-runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"preview-runtime.d.ts","sourceRoot":"","sources":["../src/preview-runtime.ts"],"names":[],"mappings":"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"}
@@ -57,9 +57,56 @@ function isNodeError(error) {
57
57
  return error instanceof Error;
58
58
  }
59
59
  const SUPPORTED_SCHEMA_KEYS = new Set(['type', 'required', 'properties', 'items', 'enum']);
60
+ /**
61
+ * Removes line and block comments, walking left to right so a `//` or `/*`
62
+ * inside a quoted string or template literal is left alone. A block comment
63
+ * keeps its newlines, so line numbers in later messages stay true.
64
+ * Regex literals are not tracked; a quote inside one can hide a comment.
65
+ */
66
+ export function stripComments(code) {
67
+ let out = '';
68
+ let quote = '';
69
+ let i = 0;
70
+ while (i < code.length) {
71
+ const char = code[i];
72
+ const next = code[i + 1];
73
+ if (quote) {
74
+ out += char;
75
+ if (char === '\\' && next !== undefined) {
76
+ out += next;
77
+ i += 2;
78
+ continue;
79
+ }
80
+ // Single and double quoted strings end at the line's end; templates may span lines.
81
+ if (char === quote || (char === '\n' && quote !== '`'))
82
+ quote = '';
83
+ i++;
84
+ }
85
+ else if (char === '"' || char === "'" || char === '`') {
86
+ quote = char;
87
+ out += char;
88
+ i++;
89
+ }
90
+ else if (char === '/' && next === '/' && code[i - 1] !== ':') {
91
+ while (i < code.length && code[i] !== '\n')
92
+ i++;
93
+ }
94
+ else if (char === '/' && next === '*') {
95
+ const close = code.indexOf('*/', i + 2);
96
+ const end = close === -1 ? code.length : close + 2;
97
+ out += code.slice(i, end).replace(/[^\n]/g, ' ');
98
+ i = end;
99
+ }
100
+ else {
101
+ out += char;
102
+ i++;
103
+ }
104
+ }
105
+ return out;
106
+ }
60
107
  export function validatePreviewInputs(options) {
61
108
  const issues = [];
62
- const source = readFileSync(options.filePath, 'utf8');
109
+ const source = stripComments(readFileSync(options.filePath, 'utf8'));
63
110
  validateImports(source, issues);
64
111
  validateDefaultExport(source, options.componentName, issues);
65
112
  validateSchema(options.inputSchema, 'input schema', issues);
@@ -296,10 +343,7 @@ function resolveDefaultExport(source) {
296
343
  return null;
297
344
  }
298
345
  function unwrapDefaultExportExpression(expression) {
299
- const trimmed = expression
300
- .replace(/\/\*[\s\S]*?\*\/\s*$/, '')
301
- .replace(/\/\/.*$/, '')
302
- .trim();
346
+ const trimmed = expression.trim();
303
347
  if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(trimmed)) {
304
348
  return trimmed;
305
349
  }
@@ -13,11 +13,11 @@ export const RESERVED_CREDENTIAL_ENVS = new Set([
13
13
  'GEMINI_API_KEY', // Historical SQL reservation; removing local forwarding does not release this name.
14
14
  'GOOGLE_API_KEY', // Historical SQL reservation for the Google alias, independent of run providers.
15
15
  'ALI_1505_ENDPOINT_OVERRIDE_API_KEY', // hosted-optimization.ts: runner-injected secret.
16
- 'BRAINTRUST_API_KEY', // app/api/chat/route.ts: workbench connector.
16
+ 'BRAINTRUST_API_KEY', // lib/hosted-connectors.ts: workbench connector.
17
17
  'CF_API_KEY', // test/with-cloudflare-env.test.ts: Cloudflare spelling.
18
18
  'CLOUDFLARE_API_KEY', // test/with-cloudflare-env.test.ts: Cloudflare spelling.
19
19
  'DAYTONA_API_KEY', // scripts/daytona-workbench-slice.mjs: hosted sandbox.
20
- 'INTERNAL_API_KEY', // app/api/compile/route.ts: internal compilation.
20
+ 'INTERNAL_API_KEY', // Historical SQL reservation; the internal compile route is gone (ORI-2305).
21
21
  'LINEAR_API_KEY', // lib/services/linear-feedback.ts: issue service.
22
22
  'RESEND_API_KEY', // lib/services/email.ts: email delivery.
23
23
  ]);
@@ -1,2 +1,8 @@
1
+ /**
2
+ * Matches any content character. `\uXXXX` names each code point, so
3
+ * JavaScript (with the `u` flag) and PostgreSQL's `~` read it identically and
4
+ * no locale is involved.
5
+ */
6
+ export declare const REPORT_MARKDOWN_CONTENT_PATTERN: string;
1
7
  export declare function hasCanonicalReportMarkdownContent(markdown: string): boolean;
2
8
  //# sourceMappingURL=report-markdown-blankness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"report-markdown-blankness.d.ts","sourceRoot":"","sources":["../src/report-markdown-blankness.ts"],"names":[],"mappings":"AAGA,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE3E"}
1
+ {"version":3,"file":"report-markdown-blankness.d.ts","sourceRoot":"","sources":["../src/report-markdown-blankness.ts"],"names":[],"mappings":"AA+CA;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,QAE/B,CAAA;AAIb,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE3E"}
@@ -1,6 +1,49 @@
1
- // JavaScript's \s is broader than the database locale's POSIX [:space:]
2
- // predicate. These four code points are content in the measured PostgreSQL
3
- // boundary, so retain them explicitly when validating CLI report input.
1
+ // ADR-036: the one definition of an empty report, shared by the CLI, the web
2
+ // app's readers and upload route, and the timeline's Data API filter. The
3
+ // database writer and comment trigger use private.report_markdown_has_content,
4
+ // whose pattern is REPORT_MARKDOWN_CONTENT_PATTERN verbatim (a database test
5
+ // pins that). Before ADR-036 these sites copied PostgreSQL 15's locale answer
6
+ // while production ran 17, so the CLI accepted reports the writer rejected.
7
+ /**
8
+ * The characters a report may consist of and still be empty: Unicode's
9
+ * White_Space property plus the four C0 information separators. Everything
10
+ * else, including invisible format characters such as U+FEFF, is content.
11
+ */
12
+ const REPORT_BLANK_CODE_POINTS = [
13
+ // Tab, line feed, vertical tab, form feed, carriage return.
14
+ { first: 0x0009, last: 0x000d },
15
+ // File, group, record and unit separators: not Unicode whitespace, but
16
+ // invisible, and blank in production since it has run PostgreSQL 17.
17
+ { first: 0x001c, last: 0x001f },
18
+ // Space.
19
+ { first: 0x0020, last: 0x0020 },
20
+ // Next line.
21
+ { first: 0x0085, last: 0x0085 },
22
+ // No-break space.
23
+ { first: 0x00a0, last: 0x00a0 },
24
+ // Ogham space mark.
25
+ { first: 0x1680, last: 0x1680 },
26
+ // En quad through hair space, including figure space U+2007.
27
+ { first: 0x2000, last: 0x200a },
28
+ // Line separator and paragraph separator.
29
+ { first: 0x2028, last: 0x2029 },
30
+ // Narrow no-break space.
31
+ { first: 0x202f, last: 0x202f },
32
+ // Medium mathematical space.
33
+ { first: 0x205f, last: 0x205f },
34
+ // Ideographic space.
35
+ { first: 0x3000, last: 0x3000 },
36
+ ];
37
+ function escapeCodePoint(codePoint) {
38
+ return `\\u${codePoint.toString(16).padStart(4, '0')}`;
39
+ }
40
+ /**
41
+ * Matches any content character. `\uXXXX` names each code point, so
42
+ * JavaScript (with the `u` flag) and PostgreSQL's `~` read it identically and
43
+ * no locale is involved.
44
+ */
45
+ export const REPORT_MARKDOWN_CONTENT_PATTERN = `[^${REPORT_BLANK_CODE_POINTS.map(({ first, last }) => first === last ? escapeCodePoint(first) : `${escapeCodePoint(first)}-${escapeCodePoint(last)}`).join('')}]`;
46
+ const REPORT_MARKDOWN_CONTENT = new RegExp(REPORT_MARKDOWN_CONTENT_PATTERN, 'u');
4
47
  export function hasCanonicalReportMarkdownContent(markdown) {
5
- return /(?:[^\s]|[\u00a0\u2007\u202f\ufeff])/u.test(markdown);
48
+ return REPORT_MARKDOWN_CONTENT.test(markdown);
6
49
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * ORI-2128: what `optimizations promote` needs to warn about a stale seed.
3
+ * The run's seed version number, and its profile's production version number
4
+ * as it stood before this promotion (null when the profile had none).
5
+ * Shared by the promote handler, both promote routes, and the CLI.
6
+ */
7
+ export interface SeedAgainstProduction {
8
+ runSeedVersionNumber: number;
9
+ productionVersionNumber: number | null;
10
+ }
11
+ //# sourceMappingURL=seed-against-production.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed-against-production.d.ts","sourceRoot":"","sources":["../src/seed-against-production.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,oBAAoB,EAAE,MAAM,CAAA;IAC5B,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAA;CACvC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"setup-and-skills-commands.d.ts","sourceRoot":"","sources":["../src/setup-and-skills-commands.ts"],"names":[],"mappings":"AA0EA,OAAO,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAW,MAAM,wBAAwB,CAAA;AAMjF,iBAAe,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAY/E;AAkcD,iBAAe,mBAAmB,kBAuBjC;AAUD,iBAAS,mBAAmB,SA+B3B;AAED,iBAAe,mBAAmB,kBAuDjC;AAED,iBAAS,iBAAiB,SAgBzB;AAMD,iBAAe,gBAAgB,CAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,OAAO,EACf,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,OAAO,GAAE,oBAAkC,GAC1C,OAAO,CAAC,IAAI,CAAC,CAiDf;AAmHD,iBAAe,YAAY,kBAyW1B;AAED,iBAAS,kBAAkB,SAS1B;AAED,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,QAAQ,GACT,CAAA"}
1
+ {"version":3,"file":"setup-and-skills-commands.d.ts","sourceRoot":"","sources":["../src/setup-and-skills-commands.ts"],"names":[],"mappings":"AA+EA,OAAO,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAW,MAAM,wBAAwB,CAAA;AAMjF,iBAAe,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAY/E;AA0cD,iBAAe,mBAAmB,kBA+BjC;AAwFD,iBAAS,mBAAmB,SAmD3B;AAED,iBAAe,mBAAmB,kBA0DjC;AAED,iBAAS,iBAAiB,SAgBzB;AAMD,iBAAe,gBAAgB,CAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,OAAO,EACf,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,OAAO,GAAE,oBAAkC,GAC1C,OAAO,CAAC,IAAI,CAAC,CAiDf;AAqID,iBAAe,YAAY,kBAqX1B;AAED,iBAAS,kBAAkB,SAS1B;AAED,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,QAAQ,GACT,CAAA"}