orizu 0.5.12 → 0.5.13

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.
@@ -53,13 +53,23 @@ export function assertCommitPreservingMergeArgs(args) {
53
53
  }
54
54
  const LOCAL_BASE_REF = 'refs/heads/__orizu_merge_base';
55
55
  const LOCAL_SOURCE_REF = 'refs/remotes/origin/__orizu_merge_source';
56
- // GitHub's published SSH host keys:
56
+ // GitHub's published SSH host keys (PINNED — never ssh-keyscan'd at runtime):
57
57
  // https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints
58
- const GITHUB_KNOWN_HOSTS = [
59
- 'github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl',
60
- 'github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=',
61
- 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=',
62
- ].join('\n') + '\n';
58
+ // (canonically republished at https://api.github.com/meta `ssh_keys`).
59
+ const GITHUB_HOST_KEYS = [
60
+ 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl',
61
+ 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=',
62
+ 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=',
63
+ ];
64
+ // Both SSH endpoints GitHub serves git over: the standard port AND the
65
+ // SSH-over-the-HTTPS-port endpoint (docs.github.com "Using SSH over the
66
+ // HTTPS port") the merge sandbox uses — its egress firewall enforces at
67
+ // SNI/CIDR level and raw SSH on :22 was reset in the first live run
68
+ // (ALI-1084 live-fire, 2026-07-15). GitHub publishes ONE key set for both
69
+ // hosts; OpenSSH matches a non-22 port with the `[host]:port` known_hosts
70
+ // form, so the 443 endpoint needs its own lines. Pinning discipline is
71
+ // unchanged: same keys, one more host alias.
72
+ const GITHUB_KNOWN_HOSTS = GITHUB_HOST_KEYS.flatMap(key => [`github.com ${key}`, `[ssh.github.com]:443 ${key}`]).join('\n') + '\n';
63
73
  /**
64
74
  * Strip git's repo-context env vars from a child git's environment. Git
65
75
  * EXPORTS these into hook subprocesses (from a linked worktree, GIT_DIR is an
@@ -104,6 +114,21 @@ export function sshRemote(repoFullName) {
104
114
  }
105
115
  return `git@github.com:${repoFullName}.git`;
106
116
  }
117
+ /**
118
+ * The SSH-over-the-HTTPS-port remote (GitHub-supported: `ssh.github.com:443`
119
+ * speaks the same SSH protocol, same auth, same host-key set). The MERGE core
120
+ * uses this endpoint because the merge sandbox's egress firewall enforces at
121
+ * the SNI/CIDR layer and reset raw SSH on :22 in the first live run
122
+ * (ALI-1084); :443 within GitHub's published git CIDRs is the compatible
123
+ * path. Equivalent from dev machines, so local/sandbox parity holds (D6).
124
+ * The push/read paths (D9/Phase 6) keep `sshRemote` untouched.
125
+ */
126
+ export function sshRemoteOverHttpsPort(repoFullName) {
127
+ if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repoFullName)) {
128
+ throw new Error(`Invalid repo full name: ${repoFullName}`);
129
+ }
130
+ return `ssh://git@ssh.github.com:443/${repoFullName}.git`;
131
+ }
107
132
  function shellQuote(value) {
108
133
  return `'${value.replace(/'/g, "'\\''")}'`;
109
134
  }
@@ -176,7 +201,10 @@ export async function mergeBranchWithDeployKey(repoFullName, input, deps = {}) {
176
201
  const runGit = deps.runGit ?? defaultRunGit;
177
202
  return withDeployKeyGit(input.privateKeyPem, runGit, 'orizu-merge-', async ({ git, gitOk }) => {
178
203
  await gitOk(['init'], 'git init');
179
- await gitOk(['remote', 'add', 'origin', sshRemote(repoFullName)], 'git remote add');
204
+ // SSH over the HTTPS port (ALI-1084 live-fire fix): the merge sandbox's
205
+ // egress firewall reset raw SSH on github.com:22; ssh.github.com:443 is
206
+ // the GitHub-supported equivalent (same protocol, same pinned host keys).
207
+ await gitOk(['remote', 'add', 'origin', sshRemoteOverHttpsPort(repoFullName)], 'git remote add');
180
208
  await gitOk(['fetch', '--no-tags', 'origin', `refs/heads/${input.base}:${LOCAL_BASE_REF}`], 'git fetch default branch');
181
209
  await gitOk(['checkout', '-B', '__orizu_merge_base', LOCAL_BASE_REF], 'git checkout default branch');
182
210
  await gitOk(['fetch', '--no-tags', 'origin', `refs/heads/${input.sourceBranch}:${LOCAL_SOURCE_REF}`], 'git fetch reviewed source branch');
@@ -54,10 +54,10 @@ export const VERCEL_IMAGE_NOT_READY_BACKOFF_MS = 5000;
54
54
  // constant-fold `import(specifier)` into a static dependency on the SDK — the
55
55
  // same discipline as the ALI-1031 server provider.
56
56
  const VERCEL_SDK_SPECIFIER = ['@vercel', 'sandbox'].join('/');
57
- async function loadVercelModule() {
57
+ export async function loadVercelModule(importModule = specifier => import(specifier)) {
58
58
  try {
59
59
  const specifier = VERCEL_SDK_SPECIFIER;
60
- const mod = (await import(specifier));
60
+ const mod = (await importModule(specifier));
61
61
  if (!mod || typeof mod.Sandbox?.create !== 'function') {
62
62
  throw new Error('module did not export a Sandbox class with create()');
63
63
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orizu",
3
- "version": "0.5.12",
3
+ "version": "0.5.13",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -112,13 +112,27 @@ export function assertCommitPreservingMergeArgs(args: readonly string[]): void {
112
112
 
113
113
  const LOCAL_BASE_REF = 'refs/heads/__orizu_merge_base'
114
114
  const LOCAL_SOURCE_REF = 'refs/remotes/origin/__orizu_merge_source'
115
- // GitHub's published SSH host keys:
115
+ // GitHub's published SSH host keys (PINNED — never ssh-keyscan'd at runtime):
116
116
  // https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints
117
- const GITHUB_KNOWN_HOSTS = [
118
- 'github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl',
119
- 'github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=',
120
- 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=',
121
- ].join('\n') + '\n'
117
+ // (canonically republished at https://api.github.com/meta `ssh_keys`).
118
+ const GITHUB_HOST_KEYS = [
119
+ 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl',
120
+ 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=',
121
+ 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=',
122
+ ] as const
123
+
124
+ // Both SSH endpoints GitHub serves git over: the standard port AND the
125
+ // SSH-over-the-HTTPS-port endpoint (docs.github.com "Using SSH over the
126
+ // HTTPS port") the merge sandbox uses — its egress firewall enforces at
127
+ // SNI/CIDR level and raw SSH on :22 was reset in the first live run
128
+ // (ALI-1084 live-fire, 2026-07-15). GitHub publishes ONE key set for both
129
+ // hosts; OpenSSH matches a non-22 port with the `[host]:port` known_hosts
130
+ // form, so the 443 endpoint needs its own lines. Pinning discipline is
131
+ // unchanged: same keys, one more host alias.
132
+ const GITHUB_KNOWN_HOSTS =
133
+ GITHUB_HOST_KEYS.flatMap(key => [`github.com ${key}`, `[ssh.github.com]:443 ${key}`]).join(
134
+ '\n'
135
+ ) + '\n'
122
136
 
123
137
  /**
124
138
  * Strip git's repo-context env vars from a child git's environment. Git
@@ -170,6 +184,22 @@ export function sshRemote(repoFullName: string): string {
170
184
  return `git@github.com:${repoFullName}.git`
171
185
  }
172
186
 
187
+ /**
188
+ * The SSH-over-the-HTTPS-port remote (GitHub-supported: `ssh.github.com:443`
189
+ * speaks the same SSH protocol, same auth, same host-key set). The MERGE core
190
+ * uses this endpoint because the merge sandbox's egress firewall enforces at
191
+ * the SNI/CIDR layer and reset raw SSH on :22 in the first live run
192
+ * (ALI-1084); :443 within GitHub's published git CIDRs is the compatible
193
+ * path. Equivalent from dev machines, so local/sandbox parity holds (D6).
194
+ * The push/read paths (D9/Phase 6) keep `sshRemote` untouched.
195
+ */
196
+ export function sshRemoteOverHttpsPort(repoFullName: string): string {
197
+ if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repoFullName)) {
198
+ throw new Error(`Invalid repo full name: ${repoFullName}`)
199
+ }
200
+ return `ssh://git@ssh.github.com:443/${repoFullName}.git`
201
+ }
202
+
173
203
  function shellQuote(value: string): string {
174
204
  return `'${value.replace(/'/g, "'\\''")}'`
175
205
  }
@@ -260,7 +290,10 @@ export async function mergeBranchWithDeployKey(
260
290
  const runGit = deps.runGit ?? defaultRunGit
261
291
  return withDeployKeyGit(input.privateKeyPem, runGit, 'orizu-merge-', async ({ git, gitOk }) => {
262
292
  await gitOk(['init'], 'git init')
263
- await gitOk(['remote', 'add', 'origin', sshRemote(repoFullName)], 'git remote add')
293
+ // SSH over the HTTPS port (ALI-1084 live-fire fix): the merge sandbox's
294
+ // egress firewall reset raw SSH on github.com:22; ssh.github.com:443 is
295
+ // the GitHub-supported equivalent (same protocol, same pinned host keys).
296
+ await gitOk(['remote', 'add', 'origin', sshRemoteOverHttpsPort(repoFullName)], 'git remote add')
264
297
  await gitOk(
265
298
  ['fetch', '--no-tags', 'origin', `refs/heads/${input.base}:${LOCAL_BASE_REF}`],
266
299
  'git fetch default branch'
@@ -135,10 +135,20 @@ export interface VercelSdkModule {
135
135
  // same discipline as the ALI-1031 server provider.
136
136
  const VERCEL_SDK_SPECIFIER = ['@vercel', 'sandbox'].join('/')
137
137
 
138
- async function loadVercelModule(): Promise<VercelSdkModule> {
138
+ /**
139
+ * Injectable dynamic-import seam: tests pass an importer that throws so the
140
+ * SDK-absent path is exercised deterministically, regardless of whether
141
+ * `@vercel/sandbox` happens to be resolvable on the host machine. Production
142
+ * callers never pass one — the default is a real dynamic `import()`.
143
+ */
144
+ export type VercelSdkImporter = (specifier: string) => Promise<unknown>
145
+
146
+ export async function loadVercelModule(
147
+ importModule: VercelSdkImporter = specifier => import(specifier)
148
+ ): Promise<VercelSdkModule> {
139
149
  try {
140
150
  const specifier: string = VERCEL_SDK_SPECIFIER
141
- const mod = (await import(specifier)) as unknown as VercelSdkModule
151
+ const mod = (await importModule(specifier)) as VercelSdkModule
142
152
  if (!mod || typeof mod.Sandbox?.create !== 'function') {
143
153
  throw new Error('module did not export a Sandbox class with create()')
144
154
  }
@@ -22,7 +22,7 @@ The current parameter value is:
22
22
  <current_candidate>
23
23
  ```
24
24
 
25
- Below is evaluation data showing how this parameter value performed across multiple test cases. The data contains performance metrics, model outputs, judge feedback, and any relevant diagnostic information from the evaluation:
25
+ Below is evaluation data showing how this parameter value performed across multiple test cases. The data contains performance metrics, scorer feedback, errors, and any relevant diagnostic information from the evaluation:
26
26
  ```
27
27
  <evaluation_data>
28
28
  ```
@@ -1867,10 +1867,12 @@ def optimize_loaded_text_candidate(
1867
1867
  def build_reflection_prompt(parent_text: str, parent_results: list[RowEvaluation], config: TextGepaConfig) -> str:
1868
1868
  examples = []
1869
1869
  for result in parent_results:
1870
+ if result.feedback is None or not result.feedback.strip():
1871
+ raise ValueError(
1872
+ f"Scorer feedback is required for reflection row {result.row_id}"
1873
+ )
1870
1874
  examples.append({
1871
1875
  "row_id": result.row_id,
1872
- "input": result.row,
1873
- "output": result.output,
1874
1876
  "score": result.score,
1875
1877
  "feedback": result.feedback,
1876
1878
  "error": result.error,