orizu 0.5.14 → 0.5.16
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.
- package/dist/artifact-archive.js +20 -4
- package/dist/egress-allowlist-cli.js +72 -10
- package/dist/github-setup.js +60 -6
- package/dist/help.js +26 -7
- package/dist/hosted-runtime-assets.js +8 -1
- package/dist/hosted-session-cli.js +197 -22
- package/dist/index.js +108 -68
- package/dist/kill-switch-cli.js +10 -0
- package/dist/merge-job.js +26 -3
- package/dist/optimizations-list-cli.js +85 -0
- package/dist/option-validation.js +54 -0
- package/dist/preview-runtime.js +93 -0
- package/dist/prompt-draft-push.js +3 -2
- package/dist/registration-tag-warning.js +49 -0
- package/dist/run-event-digest.js +319 -0
- package/dist/runner-dir-verify.js +848 -0
- package/dist/scorer-draft-push.js +3 -2
- package/dist/scores-accept-cli.js +87 -0
- package/dist/workbench-cli.js +35 -7
- package/dist/zip-draft-push.js +3 -2
- package/package.json +1 -1
- package/src/artifact-archive.ts +29 -4
- package/src/egress-allowlist-cli.ts +87 -13
- package/src/github-setup.ts +66 -7
- package/src/help.ts +26 -7
- package/src/hosted-runtime-assets.ts +14 -1
- package/src/hosted-session-cli.ts +215 -22
- package/src/index.ts +114 -80
- package/src/kill-switch-cli.ts +10 -0
- package/src/merge-job.ts +25 -6
- package/src/optimizations-list-cli.ts +117 -0
- package/src/option-validation.ts +63 -0
- package/src/preview-runtime.ts +94 -0
- package/src/prompt-draft-push.ts +5 -2
- package/src/registration-tag-warning.ts +58 -0
- package/src/run-event-digest.ts +333 -0
- package/src/runner-dir-verify.ts +1029 -0
- package/src/scorer-draft-push.ts +5 -2
- package/src/scores-accept-cli.ts +124 -0
- package/src/workbench-cli.ts +38 -7
- package/src/zip-draft-push.ts +5 -2
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +80 -3
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +288 -15
- package/vendor/orizu-gepa-python/src/orizu_gepa/runner.py +133 -8
- package/vendor/skills/orizu-cli/SKILL.md +4 -2
- package/vendor/skills/orizu-cli/references/building-apps.md +131 -1
- package/vendor/skills/orizu-cli/references/building-judges.md +18 -0
- package/vendor/skills/orizu-cli/references/cli-reference.md +2 -1
- package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +3 -1
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +75 -0
- package/vendor/skills/orizu-cli/scripts/test-app.mjs +3 -0
package/dist/artifact-archive.js
CHANGED
|
@@ -11,7 +11,9 @@ const ARTIFACT_EXCLUDED_PATH_NAMES = new Set([
|
|
|
11
11
|
function isNodeError(error) {
|
|
12
12
|
return error instanceof Error;
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
/** Exported for runner-dir-verify (ALI-1159): the pre-exec scan must resolve
|
|
15
|
+
* the same directory this module hashes and the runner later executes. */
|
|
16
|
+
export function expandHomePath(path) {
|
|
15
17
|
if (path.startsWith('~/')) {
|
|
16
18
|
const home = process.env.HOME || '';
|
|
17
19
|
return `${home}/${path.slice(2)}`;
|
|
@@ -123,7 +125,13 @@ export function createStoredZip(entries) {
|
|
|
123
125
|
endOfCentralDirectory.writeUInt16LE(0, 20);
|
|
124
126
|
return Buffer.concat([...localParts, centralDirectory, endOfCentralDirectory]);
|
|
125
127
|
}
|
|
126
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Read the exact entry set the deterministic archive covers (sorted relative
|
|
130
|
+
* paths + bytes). Exported for runner-dir-verify (ALI-1159): the verified
|
|
131
|
+
* snapshot must be materialized from the SAME in-memory bytes that were
|
|
132
|
+
* hashed, so a post-hash mutation of the directory cannot change what runs.
|
|
133
|
+
*/
|
|
134
|
+
export function readArtifactEntries(dirArg) {
|
|
127
135
|
const sourceDir = expandHomePath(dirArg);
|
|
128
136
|
try {
|
|
129
137
|
const stats = statSync(sourceDir);
|
|
@@ -141,12 +149,20 @@ export function zipDirectoryToBase64(dirArg) {
|
|
|
141
149
|
if (files.length === 0) {
|
|
142
150
|
throw new Error(`Directory contains no artifact files: ${sourceDir}`);
|
|
143
151
|
}
|
|
144
|
-
|
|
152
|
+
return files.map(relativePath => ({
|
|
145
153
|
path: relativePath,
|
|
146
154
|
data: readFileSync(join(sourceDir, relativePath)),
|
|
147
|
-
|
|
155
|
+
mode: statSync(join(sourceDir, relativePath)).mode,
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
/** Deterministic archive of an already-read entry set. */
|
|
159
|
+
export function archiveArtifactEntries(entries) {
|
|
160
|
+
const bytes = createStoredZip(entries);
|
|
148
161
|
return {
|
|
149
162
|
zipBase64: bytes.toString('base64'),
|
|
150
163
|
contentSha256: createHash('sha256').update(bytes).digest('hex'),
|
|
151
164
|
};
|
|
152
165
|
}
|
|
166
|
+
export function zipDirectoryToBase64(dirArg) {
|
|
167
|
+
return archiveArtifactEntries(readArtifactEntries(dirArg));
|
|
168
|
+
}
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
* with an injected fetcher.
|
|
14
14
|
*/
|
|
15
15
|
import { authedFetch } from './http.js';
|
|
16
|
+
/** Mirrors MAX_TEAM_EGRESS_DOMAINS in the shared read-side validator
|
|
17
|
+
* (workers/session-coordinator/src/egress-domains.ts). The coordinator only
|
|
18
|
+
* applies the first this-many UNIQUE valid domains at policy compose; the
|
|
19
|
+
* CLI is a standalone package and cannot import the worker module, so the
|
|
20
|
+
* cap constant is duplicated here (kept in sync via that file's tests). */
|
|
21
|
+
const MAX_ADDITIVE_EGRESS_DOMAINS = 100;
|
|
16
22
|
function fetcherFrom(io) {
|
|
17
23
|
return io.fetcher ?? authedFetch;
|
|
18
24
|
}
|
|
@@ -55,22 +61,72 @@ function splitDomains(value) {
|
|
|
55
61
|
.map(d => d.trim())
|
|
56
62
|
.filter(d => d.length > 0);
|
|
57
63
|
}
|
|
64
|
+
function stringArray(value) {
|
|
65
|
+
return Array.isArray(value) ? value.filter((d) => typeof d === 'string') : [];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Re-derive the "beyond the 100-domain cap" entries from a stored set the
|
|
69
|
+
* SAME way the GET route does (positional over UNIQUE VALID domains): the
|
|
70
|
+
* coordinator applies only the first MAX_ADDITIVE_EGRESS_DOMAINS valid
|
|
71
|
+
* domains, so every valid entry past that index is configured-but-inert.
|
|
72
|
+
* Stored entries are already canonical + deduped by the write RPC, and the
|
|
73
|
+
* invalid (grandfathered) ones — flagged by the server — are not "valid"
|
|
74
|
+
* and so never consume a cap slot. Used for the PUT response, where a
|
|
75
|
+
* shrink-by-one on an oversized legacy row leaves the returned set still
|
|
76
|
+
* over-cap and the server does not send cappedDomains back.
|
|
77
|
+
*/
|
|
78
|
+
function deriveCapped(stored, invalid) {
|
|
79
|
+
const invalidSet = new Set(invalid);
|
|
80
|
+
const capped = [];
|
|
81
|
+
let validCount = 0;
|
|
82
|
+
for (const d of stored) {
|
|
83
|
+
if (invalidSet.has(d))
|
|
84
|
+
continue;
|
|
85
|
+
validCount += 1;
|
|
86
|
+
if (validCount > MAX_ADDITIVE_EGRESS_DOMAINS)
|
|
87
|
+
capped.push(d);
|
|
88
|
+
}
|
|
89
|
+
return capped;
|
|
90
|
+
}
|
|
58
91
|
async function fetchCurrent(fetcher, path) {
|
|
59
92
|
const response = await fetcher(path, { method: 'GET' });
|
|
60
93
|
if (!response.ok) {
|
|
61
94
|
throw new Error(`show failed (${response.status}): ${await responseMessage(response)}`);
|
|
62
95
|
}
|
|
63
96
|
const data = (await response.json());
|
|
64
|
-
return
|
|
97
|
+
return {
|
|
98
|
+
domains: stringArray(data.extraDomains),
|
|
99
|
+
invalid: stringArray(data.invalidDomains),
|
|
100
|
+
capped: stringArray(data.cappedDomains),
|
|
101
|
+
};
|
|
65
102
|
}
|
|
66
|
-
function emit(io, teamSlug,
|
|
103
|
+
function emit(io, teamSlug, current) {
|
|
67
104
|
if (io.json) {
|
|
68
|
-
io.print(JSON.stringify({
|
|
105
|
+
io.print(JSON.stringify({
|
|
106
|
+
teamSlug,
|
|
107
|
+
extraDomains: current.domains,
|
|
108
|
+
invalidDomains: current.invalid,
|
|
109
|
+
cappedDomains: current.capped,
|
|
110
|
+
}));
|
|
69
111
|
return;
|
|
70
112
|
}
|
|
71
|
-
|
|
113
|
+
const invalid = new Set(current.invalid);
|
|
114
|
+
const capped = new Set(current.capped);
|
|
115
|
+
const lines = current.domains.map((d) => {
|
|
116
|
+
// Round 4 (P3): re-adding a non-grandfathered invalid entry is
|
|
117
|
+
// guaranteed to fail the write RPC's tightened validator (grandfathering
|
|
118
|
+
// is retention-only — see set_team_egress_allowlist), so "remove and
|
|
119
|
+
// re-add" was actively misleading. The only viable remediation is
|
|
120
|
+
// removal.
|
|
121
|
+
if (invalid.has(d))
|
|
122
|
+
return `${d} (invalid — will not take effect; remove it (this value is no longer valid and cannot be re-added))`;
|
|
123
|
+
if (capped.has(d))
|
|
124
|
+
return `${d} (beyond the 100-domain cap — will not take effect)`;
|
|
125
|
+
return d;
|
|
126
|
+
});
|
|
127
|
+
io.print(lines.length === 0
|
|
72
128
|
? `${teamSlug}: no additive egress domains (base allowlist only)`
|
|
73
|
-
: `${teamSlug} additive egress domains:\n ${
|
|
129
|
+
: `${teamSlug} additive egress domains:\n ${lines.join('\n ')}`);
|
|
74
130
|
}
|
|
75
131
|
export async function egressAllowlistCommand(args, io) {
|
|
76
132
|
const teamSlug = firstPositional(args);
|
|
@@ -100,7 +156,7 @@ export async function egressAllowlistCommand(args, io) {
|
|
|
100
156
|
}
|
|
101
157
|
else {
|
|
102
158
|
const current = await fetchCurrent(fetcher, path);
|
|
103
|
-
const set = new Set(current);
|
|
159
|
+
const set = new Set(current.domains);
|
|
104
160
|
if (addValue)
|
|
105
161
|
for (const d of splitDomains(addValue))
|
|
106
162
|
set.add(d);
|
|
@@ -119,10 +175,16 @@ export async function egressAllowlistCommand(args, io) {
|
|
|
119
175
|
return 1;
|
|
120
176
|
}
|
|
121
177
|
const data = (await response.json());
|
|
122
|
-
const stored = Array.isArray(data.extraDomains)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
178
|
+
const stored = Array.isArray(data.extraDomains) ? stringArray(data.extraDomains) : next;
|
|
179
|
+
const invalid = stringArray(data.invalidDomains);
|
|
180
|
+
// Grandfathered retention: a successful PUT may still STORE legacy
|
|
181
|
+
// invalid entries the admin retained — the server flags them and we show
|
|
182
|
+
// it. Round 5 (P2): a >100-entry legacy row can only shrink one entry per
|
|
183
|
+
// PUT (the write RPC's GREATEST(100, stored-1) bound), so the returned
|
|
184
|
+
// set can still be OVER the cap. Re-derive the cap annotations from the
|
|
185
|
+
// returned set — same positional logic GET uses — instead of assuming
|
|
186
|
+
// the RPC left it at/under 100.
|
|
187
|
+
emit(io, teamSlug, { domains: stored, invalid, capped: deriveCapped(stored, invalid) });
|
|
126
188
|
return 0;
|
|
127
189
|
}
|
|
128
190
|
catch (error) {
|
package/dist/github-setup.js
CHANGED
|
@@ -17,7 +17,7 @@ import { spawnSync } from 'child_process';
|
|
|
17
17
|
import { existsSync, mkdirSync } from 'fs';
|
|
18
18
|
import { join } from 'path';
|
|
19
19
|
import { authedFetch } from './http.js';
|
|
20
|
-
import { assertWorkspaceDirUsable } from './workspace.js';
|
|
20
|
+
import { assertWorkspaceDirUsable, workspaceExists } from './workspace.js';
|
|
21
21
|
/**
|
|
22
22
|
* Strip terminal control characters from server-supplied strings before they
|
|
23
23
|
* are printed (mirrors index.ts `sanitizeTerminalText`), so a hostile or
|
|
@@ -28,7 +28,36 @@ function sanitizeServerText(value) {
|
|
|
28
28
|
return String(value).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, '');
|
|
29
29
|
}
|
|
30
30
|
const DEFAULT_POLL_INTERVAL_MS = 2000;
|
|
31
|
-
|
|
31
|
+
// ALI-1141: long enough for a slow org-picker + repo-selection walk, but the
|
|
32
|
+
// wait MUST be finite — when the App is already installed in the target org,
|
|
33
|
+
// GitHub only offers "Configure" and never redirects to our setup callback, so
|
|
34
|
+
// an unbounded poll would spin forever. (Still under the server's 15-minute
|
|
35
|
+
// nonce TTL, so the poll times out before the server would report `expired`.)
|
|
36
|
+
const DEFAULT_POLL_TIMEOUT_MS = 600000;
|
|
37
|
+
// ALI-1141: after this long with no callback, the likeliest cause is the
|
|
38
|
+
// already-installed org above — surface it instead of heartbeating silently.
|
|
39
|
+
const ALREADY_INSTALLED_HINT_MS = 90000;
|
|
40
|
+
/**
|
|
41
|
+
* Why the poll can never succeed when the App is already installed in the org
|
|
42
|
+
* (ALI-1141): GitHub's `installations/new?state=` page then only offers
|
|
43
|
+
* "Configure", and completing configuration does NOT redirect to the App's
|
|
44
|
+
* Setup URL with our state nonce — the callback that flips the row out of
|
|
45
|
+
* `pending` never fires. Printed as a mid-poll hint and folded into the
|
|
46
|
+
* timeout error so the user is never left with an unexplained hang.
|
|
47
|
+
*/
|
|
48
|
+
function alreadyInstalledHintLines(teamSlug) {
|
|
49
|
+
return [
|
|
50
|
+
' ⚠ GitHub has not called back yet. The most likely cause: the Orizu GitHub',
|
|
51
|
+
' App is ALREADY INSTALLED in that org — GitHub then only offers',
|
|
52
|
+
' "Configure", and finishing configuration never notifies Orizu, so this',
|
|
53
|
+
' wait cannot succeed. Remedies:',
|
|
54
|
+
' • Install the App into a DIFFERENT GitHub org for this team (each org',
|
|
55
|
+
' installation can be linked to exactly one Orizu team).',
|
|
56
|
+
' • If the org is not linked to any Orizu team, uninstall the Orizu App',
|
|
57
|
+
' from the org (GitHub → org Settings → GitHub Apps) and re-run',
|
|
58
|
+
` \`orizu github link --team ${teamSlug}\` to do a fresh install.`,
|
|
59
|
+
];
|
|
60
|
+
}
|
|
32
61
|
function defaultSleep(ms) {
|
|
33
62
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
34
63
|
}
|
|
@@ -83,10 +112,12 @@ export async function runGithubLink(teamSlug, io) {
|
|
|
83
112
|
const sleep = io.sleep ?? defaultSleep;
|
|
84
113
|
const interval = io.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
85
114
|
const timeout = io.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
|
|
86
|
-
const
|
|
115
|
+
const started = now();
|
|
116
|
+
const deadline = started + timeout;
|
|
87
117
|
// Periodic heartbeat so a long install does not look hung; never echoes state.
|
|
88
118
|
const HEARTBEAT_MS = 15000;
|
|
89
|
-
let lastHeartbeat =
|
|
119
|
+
let lastHeartbeat = started;
|
|
120
|
+
let printedAlreadyInstalledHint = false;
|
|
90
121
|
for (;;) {
|
|
91
122
|
const poll = await fetcher(`/api/cli/github/link?state=${encodeURIComponent(state)}`);
|
|
92
123
|
if (poll.ok) {
|
|
@@ -107,9 +138,20 @@ export async function runGithubLink(teamSlug, io) {
|
|
|
107
138
|
// status 'pending' → keep waiting.
|
|
108
139
|
}
|
|
109
140
|
if (now() >= deadline) {
|
|
110
|
-
|
|
141
|
+
// Same actionable message as the mid-poll hint: an already-installed org
|
|
142
|
+
// is the one known way to reach this timeout with nothing else wrong.
|
|
143
|
+
throw new Error(`Timed out waiting for the GitHub App install to complete after ${Math.round(timeout / 60000)} minutes. ` +
|
|
144
|
+
'If the Orizu GitHub App is already installed in that org, GitHub never notifies Orizu when you finish the "Configure" flow — ' +
|
|
145
|
+
'install the App into a DIFFERENT GitHub org for this team (or uninstall it from the org first if no other Orizu team uses it). ' +
|
|
111
146
|
`Resume with \`orizu github link --team ${teamSlug}\` after finishing the install.`);
|
|
112
147
|
}
|
|
148
|
+
if (!printedAlreadyInstalledHint && now() - started >= ALREADY_INSTALLED_HINT_MS) {
|
|
149
|
+
for (const line of alreadyInstalledHintLines(teamSlug))
|
|
150
|
+
io.print(line);
|
|
151
|
+
io.print(` Still waiting (Ctrl+C to abort; resume later with \`orizu github link --team ${teamSlug}\`)…`);
|
|
152
|
+
printedAlreadyInstalledHint = true;
|
|
153
|
+
lastHeartbeat = now();
|
|
154
|
+
}
|
|
113
155
|
if (now() - lastHeartbeat >= HEARTBEAT_MS) {
|
|
114
156
|
io.print(' …still waiting for the GitHub install to complete.');
|
|
115
157
|
lastHeartbeat = now();
|
|
@@ -216,7 +258,7 @@ export async function runHostedAttach(options, io) {
|
|
|
216
258
|
io.print(` Cloning ${repoFullName}...`);
|
|
217
259
|
// Clone with the helper inline + the workspace id in the env so the token is
|
|
218
260
|
// brokered on demand and never materializes in the URL or on disk.
|
|
219
|
-
|
|
261
|
+
const clone = io.git([
|
|
220
262
|
'-c',
|
|
221
263
|
`credential.helper=${CREDENTIAL_HELPER}`,
|
|
222
264
|
'-c',
|
|
@@ -225,6 +267,18 @@ export async function runHostedAttach(options, io) {
|
|
|
225
267
|
cleanUrl,
|
|
226
268
|
options.targetDir,
|
|
227
269
|
], { env: { ORIZU_WORKSPACE_ID: options.workspaceId } });
|
|
270
|
+
if (clone.status !== 0) {
|
|
271
|
+
const detail = clone.stderr || `exit ${clone.status}`;
|
|
272
|
+
// A local-only workspace (`orizu setup --local`) has orizu.team.json but no
|
|
273
|
+
// `.git`, so it passes `assertWorkspaceDirUsable` yet fails the clone —
|
|
274
|
+
// `git clone` refuses the non-empty dir up front and writes nothing, so
|
|
275
|
+
// every byte in there is the user's workspace, not an incomplete clone.
|
|
276
|
+
// Never advise deleting it.
|
|
277
|
+
if (workspaceExists(options.targetDir)) {
|
|
278
|
+
throw new Error(`git clone failed: ${detail} ${options.targetDir} already holds a local Orizu workspace (orizu.team.json) without a hosted clone, so the workbench repo can't be cloned into it. Don't delete it — move it aside or re-run \`orizu setup\` with \`--workspace <empty-path>\` (or keep it local with \`--local\`).`);
|
|
279
|
+
}
|
|
280
|
+
throw new Error(`git clone failed: ${detail} Remove or empty the incomplete clone directory ${options.targetDir}, then re-run \`orizu setup\`.`);
|
|
281
|
+
}
|
|
228
282
|
// Persist the helper repo-locally for future pull/push (cwd-resolved).
|
|
229
283
|
gitOrThrow(io.git, ['config', 'credential.helper', CREDENTIAL_HELPER], { cwd: options.targetDir });
|
|
230
284
|
gitOrThrow(io.git, ['config', 'credential.useHttpPath', 'true'], { cwd: options.targetDir });
|
package/dist/help.js
CHANGED
|
@@ -394,6 +394,12 @@ export const COMMAND_DOCS = [
|
|
|
394
394
|
summary: 'Submit score results for a scorer against a prompt version or optimization candidate.',
|
|
395
395
|
group: 'Scorers and runners',
|
|
396
396
|
},
|
|
397
|
+
{
|
|
398
|
+
path: ['scores', 'accept'],
|
|
399
|
+
usage: 'orizu scores accept <score-run-id> [--project <team/project>] [--json]',
|
|
400
|
+
summary: 'Record a human curator acceptance of an agent-reported score as trusted evidence (human-only).',
|
|
401
|
+
group: 'Scorers and runners',
|
|
402
|
+
},
|
|
397
403
|
{
|
|
398
404
|
path: ['runners', 'push'],
|
|
399
405
|
usage: 'orizu runners push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]',
|
|
@@ -415,7 +421,7 @@ export const COMMAND_DOCS = [
|
|
|
415
421
|
{
|
|
416
422
|
path: ['runners', 'exec'],
|
|
417
423
|
usage: 'orizu runners exec (--prompt <prompt-version-id> | --prompt-version <id> --runner-version <id> | --scorer-version <id>) --dataset-version <id> --split-set <id-or-name> --split <name> [--runner-dir <dir>] --out <results.jsonl|results.jsonl.gz>',
|
|
418
|
-
summary: 'Execute a runner locally against a dataset split and write row results.',
|
|
424
|
+
summary: 'Execute a runner locally against a dataset split and write row results. --runner-dir bytes must match the registered runner version (ADR-007) — register with `orizu runners push` first.',
|
|
419
425
|
group: 'Scorers and runners',
|
|
420
426
|
},
|
|
421
427
|
{
|
|
@@ -450,8 +456,8 @@ export const COMMAND_DOCS = [
|
|
|
450
456
|
},
|
|
451
457
|
{
|
|
452
458
|
path: ['optimizations', 'run-gepa'],
|
|
453
|
-
usage: 'orizu optimizations run-gepa --project <team/project> --optimizer-version-id <id> --candidate-version-id <id> --runner-version-id <id> --candidate-runner-dir <dir> --scorer-version-id <id> --scorer-runner-version-id <id> --scorer-runner-dir <dir> --dataset-version-id <id> --split-set-id <id> [--train-split train] [--val-split validation] [--budget auto|light|medium|heavy | --max-metric-calls <n> | --max-full-evals <n> | --max-iterations <n>] [--num-threads auto|N] [--reflection-retry-attempts N] [--reflection-http-timeout-seconds N] [--log-dir logs]',
|
|
454
|
-
summary: 'Run the bundled GEPA-style optimizer locally and stream events.',
|
|
459
|
+
usage: 'orizu optimizations run-gepa --project <team/project> --optimizer-version-id <id> --candidate-version-id <id> --runner-version-id <id> --candidate-runner-dir <dir> --scorer-version-id <id> --scorer-runner-version-id <id> --scorer-runner-dir <dir> [--scorer-input-contract gepa|flat_row] [--scorer-candidate-field <row-field>] [--allow-degenerate-seed] --dataset-version-id <id> --split-set-id <id> [--train-split train] [--val-split validation] [--budget auto|light|medium|heavy | --max-metric-calls <n> | --max-full-evals <n> | --max-iterations <n>] [--num-threads auto|N] [--reflection-retry-attempts N] [--reflection-http-timeout-seconds N] [--log-dir logs]',
|
|
460
|
+
summary: 'Run the bundled GEPA-style optimizer locally and stream events. Validates the scorer contract on the seed before iterating; judge runners built for flat-row score runs need --scorer-input-contract flat_row (ALI-1158).',
|
|
455
461
|
group: 'Optimizations',
|
|
456
462
|
},
|
|
457
463
|
{
|
|
@@ -747,15 +753,28 @@ export const COMMAND_DOCS = [
|
|
|
747
753
|
},
|
|
748
754
|
{
|
|
749
755
|
path: ['session', 'start'],
|
|
750
|
-
usage: 'orizu session start [--project <team/project>] [--workspace <dir>] [--json]',
|
|
751
|
-
summary: 'Start a durable workspace session
|
|
756
|
+
usage: 'orizu session start [--project <team/project>] [--workspace <dir>] [--json] | orizu session start --hosted (--task <prompt> | --task-file <path>) [--duration <min>] [--project <team/project>] [--tail [--quiet]]',
|
|
757
|
+
summary: 'Start a durable workspace session, or add --hosted with a task prompt to start a coordinator-managed hosted agent. Plain sessions check out their remote session branch locally when run inside the workbench clone (or with --workspace).',
|
|
752
758
|
group: 'Sessions',
|
|
753
759
|
options: [
|
|
754
760
|
{ name: '--project <team/project>', help: 'Optionally scope the session to a project in the attached workspace.' },
|
|
755
761
|
{ name: '--workspace <dir>', help: 'Workbench clone directory when not running from inside it.' },
|
|
762
|
+
{ name: '--hosted', help: 'Start a coordinator-managed hosted agent session instead of a plain durable session.' },
|
|
763
|
+
{ name: '--task <prompt>', help: 'Hosted agent task prompt. Mutually exclusive with --task-file.' },
|
|
764
|
+
{ name: '--task-file <path>', help: 'Read the hosted agent task prompt from a UTF-8 file. Mutually exclusive with --task.' },
|
|
765
|
+
{ name: '--duration <min>', help: 'Hosted sandbox duration in minutes (default 60, maximum 1440).' },
|
|
766
|
+
{ name: '--model <provider/model>', help: 'Optional hosted model override.' },
|
|
767
|
+
{ name: '--reasoning-effort <level>', help: 'Optional hosted model reasoning-effort override.' },
|
|
768
|
+
{ name: '--title <title>', help: 'Optional hosted run title.' },
|
|
769
|
+
{ name: '--tail', help: 'Follow the hosted run until it reaches a terminal state, streaming compact per-event digests (agent text, tool calls with args, results).' },
|
|
770
|
+
{ name: '--quiet', help: 'With --tail: stream event types only (the pre-digest output).' },
|
|
756
771
|
{ name: '--json', help: 'Emit the machine-readable session payload.' },
|
|
757
772
|
],
|
|
758
|
-
examples: [
|
|
773
|
+
examples: [
|
|
774
|
+
'orizu session start',
|
|
775
|
+
'orizu session start --project highlight/hip --json',
|
|
776
|
+
'orizu session start --hosted --task-file ./task.md --project highlight/hip --duration 90',
|
|
777
|
+
],
|
|
759
778
|
},
|
|
760
779
|
{
|
|
761
780
|
path: ['session', 'status'],
|
|
@@ -823,7 +842,7 @@ export const COMMAND_DOCS = [
|
|
|
823
842
|
{
|
|
824
843
|
path: ['run', 'tail'],
|
|
825
844
|
usage: 'orizu run tail --run <id> [--after <seq>] [--interval <seconds>] [--once] [--json]',
|
|
826
|
-
summary: 'Cursor-poll a workbench run event log; JSON mode emits one event object per line.',
|
|
845
|
+
summary: 'Cursor-poll a workbench run event log as compact per-event digest lines; JSON mode emits one full event object per line.',
|
|
827
846
|
group: 'Workbench runs',
|
|
828
847
|
options: [
|
|
829
848
|
{ name: '--run <id>', help: 'Workbench run id.', required: true },
|
|
@@ -101,7 +101,14 @@ export function parsePrebakedMarker(raw) {
|
|
|
101
101
|
typeof claudeSdkVersion !== 'string') {
|
|
102
102
|
return null;
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
const marker = { cliVersion, opencodeVersion, claudeSdkVersion, builtFor: 'vercel-sandbox' };
|
|
105
|
+
// Optional Braintrust pins (ALI-1048): carried through when present as strings;
|
|
106
|
+
// absent/odd-typed values are simply omitted (never invalidate the marker).
|
|
107
|
+
if (typeof rec.braintrustPyVersion === 'string')
|
|
108
|
+
marker.braintrustPyVersion = rec.braintrustPyVersion;
|
|
109
|
+
if (typeof rec.braintrustNpmVersion === 'string')
|
|
110
|
+
marker.braintrustNpmVersion = rec.braintrustNpmVersion;
|
|
111
|
+
return marker;
|
|
105
112
|
}
|
|
106
113
|
export function serializeBootContext(ctx) {
|
|
107
114
|
return `${JSON.stringify(ctx, null, 2)}\n`;
|