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.
- package/dist/app-commands.d.ts +2 -0
- package/dist/app-commands.d.ts.map +1 -1
- package/dist/app-commands.js +10 -4
- package/dist/gepa-engine-dispatch.d.ts.map +1 -1
- package/dist/gepa-engine-dispatch.js +16 -1
- package/dist/help.js +7 -7
- package/dist/hosted-agent-prompt.d.ts +14 -0
- package/dist/hosted-agent-prompt.d.ts.map +1 -0
- package/dist/hosted-agent-prompt.js +17 -0
- package/dist/hosted-gepa-cli.d.ts.map +1 -1
- package/dist/hosted-gepa-cli.js +7 -1
- package/dist/hosted-harness-claude.d.ts.map +1 -1
- package/dist/hosted-harness-claude.js +4 -1
- package/dist/hosted-harness-opencode.d.ts +1 -0
- package/dist/hosted-harness-opencode.d.ts.map +1 -1
- package/dist/hosted-harness-opencode.js +27 -7
- package/dist/hosted-harness.d.ts +30 -1
- package/dist/hosted-harness.d.ts.map +1 -1
- package/dist/hosted-harness.js +5 -0
- package/dist/hosted-optimization-prebaked-marker.d.ts +9 -0
- package/dist/hosted-optimization-prebaked-marker.d.ts.map +1 -0
- package/dist/hosted-optimization-prebaked-marker.js +22 -0
- package/dist/hosted-tool-hints.d.ts +56 -0
- package/dist/hosted-tool-hints.d.ts.map +1 -0
- package/dist/hosted-tool-hints.js +105 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -5
- package/dist/instruction-set-gepa-launch.d.ts +21 -2
- package/dist/instruction-set-gepa-launch.d.ts.map +1 -1
- package/dist/instruction-set-gepa-launch.js +123 -3
- package/dist/markdown-report-input.d.ts.map +1 -1
- package/dist/markdown-report-input.js +1 -1
- package/dist/optimizations-promote-cli.d.ts.map +1 -1
- package/dist/optimizations-promote-cli.js +19 -0
- package/dist/preview-runtime.d.ts +7 -0
- package/dist/preview-runtime.d.ts.map +1 -1
- package/dist/preview-runtime.js +49 -5
- package/dist/provider-refusals.js +2 -2
- package/dist/report-markdown-blankness.d.ts +6 -0
- package/dist/report-markdown-blankness.d.ts.map +1 -1
- package/dist/report-markdown-blankness.js +47 -4
- package/dist/seed-against-production.d.ts +11 -0
- package/dist/seed-against-production.d.ts.map +1 -0
- package/dist/seed-against-production.js +1 -0
- package/dist/setup-and-skills-commands.d.ts.map +1 -1
- package/dist/setup-and-skills-commands.js +123 -9
- package/dist/skill-installer.d.ts +32 -0
- package/dist/skill-installer.d.ts.map +1 -1
- package/dist/skill-installer.js +228 -1
- package/package.json +1 -1
- package/src/app-commands.ts +13 -3
- package/src/gepa-engine-dispatch.ts +18 -1
- package/src/help.ts +7 -7
- package/src/hosted-agent-prompt.ts +18 -0
- package/src/hosted-gepa-cli.ts +7 -1
- package/src/hosted-harness-claude.ts +4 -1
- package/src/hosted-harness-opencode.ts +28 -7
- package/src/hosted-harness.ts +32 -1
- package/src/hosted-optimization-prebaked-marker.ts +20 -0
- package/src/hosted-tool-hints.ts +153 -0
- package/src/index.ts +2 -5
- package/src/instruction-set-gepa-launch.ts +161 -3
- package/src/markdown-report-input.ts +3 -1
- package/src/optimizations-promote-cli.ts +20 -2
- package/src/preview-runtime.ts +44 -5
- package/src/provider-refusals.ts +2 -2
- package/src/report-markdown-blankness.ts +59 -4
- package/src/seed-against-production.ts +10 -0
- package/src/setup-and-skills-commands.ts +160 -8
- package/src/skill-installer.ts +315 -0
- package/vendor/skills/orizu/SKILL.md +3 -3
- package/vendor/skills/orizu/flows/first-win.md +3 -1
- package/vendor/skills/orizu/flows/promote.md +2 -1
- package/vendor/skills/orizu/references/building-apps.md +2 -0
- package/vendor/skills/orizu/references/building-judges.md +138 -8
- package/vendor/skills/orizu/references/cli-reference.md +2 -2
- package/vendor/skills/orizu/references/migrate-existing-gepa-setup.md +1 -2
- package/vendor/skills/orizu/references/optimization-reports.md +1 -1
- package/vendor/skills/orizu/references/optimization-with-gepa.md +22 -4
- package/vendor/skills/orizu/references/primer.md +2 -2
- package/vendor/skills/orizu/references/prompt-control-plane.md +6 -1
- package/vendor/skills/orizu/scripts/test-app.mjs +53 -4
package/src/help.ts
CHANGED
|
@@ -307,13 +307,13 @@ export const COMMAND_DOCS: CliCommandDoc[] = [
|
|
|
307
307
|
options: [
|
|
308
308
|
{ name: '--team <slug>', help: 'Choose an existing accessible team.' },
|
|
309
309
|
{ name: '--create-team <name>', help: 'Create and select a new team; mutually exclusive with --team.' },
|
|
310
|
-
{ name: '--project <slug>', help: 'Choose the project to
|
|
311
|
-
{ name: '--create-project <name>', help: 'Create and select a project in the chosen team; mutually exclusive with --project.' },
|
|
310
|
+
{ name: '--project <slug>', help: 'Choose the project to work in. Only that project is set up in the workspace, and the team-project checklist is skipped, interactive or not.' },
|
|
311
|
+
{ name: '--create-project <name>', help: 'Create and select a project in the chosen team, and set up only that project (no checklist); mutually exclusive with --project.' },
|
|
312
312
|
{ name: '--workspace [path]', help: 'Initialize or validate a workspace path, or the current directory when no path is provided. If provided, the value must be a filesystem path.' },
|
|
313
313
|
{ name: '--service-origin <url>', help: 'Service origin written to setup metadata. Defaults to the active CLI server.' },
|
|
314
314
|
{ name: '--attach-workspace <id>', help: 'Record an existing Orizu workspace id for future attachment.' },
|
|
315
315
|
{ name: '--validate', help: 'Inspect the workspace contract without writing files.' },
|
|
316
|
-
{ name: '--fix', help: 'Apply safe idempotent repairs only.' },
|
|
316
|
+
{ name: '--fix', help: 'Apply safe idempotent repairs only, restoring every team project.' },
|
|
317
317
|
{ name: '--no-symlinks', help: 'Write CLAUDE.md as a pointer file instead of a symlink to AGENTS.md.' },
|
|
318
318
|
{ name: '--verbose', help: 'Show per-file setup actions. Default setup output is summary-oriented.' },
|
|
319
319
|
{ name: '--agent <agent>', help: 'Non-interactive opt-in to install the shared global Orizu skill plus any applicable native target. Repeat for multiple agents.', repeatable: true, choices: ['claude', 'codex', 'pi', 'devin', 'droid', 'grok', 'windsurf', 'opencode'] },
|
|
@@ -682,8 +682,8 @@ export const COMMAND_DOCS: CliCommandDoc[] = [
|
|
|
682
682
|
},
|
|
683
683
|
{
|
|
684
684
|
path: ['optimizations', 'run-gepa'],
|
|
685
|
-
usage: 'orizu optimizations run-gepa [--hosted [--launch-intent-id <uuid>]] --project <team/project> --optimizer-version-id <id> (--candidate-version-id <id> | --instruction-set <
|
|
686
|
-
summary: 'Run official GEPA locally and stream events, or use --hosted for a queued Orizu-managed sandbox launch. Hosted eligibility requires a staff-enabled team, capacity below its concurrent-run cap, a launch-project optimizer version whose validated manifest.optimizer_family is gepa, registered candidate/scorer runners, a reflection model from a registered provider, and a named --budget preset within the team ceiling. Hosted eligibility catalog refusals use stable code/remediation JSON with optional bounded detail, and capacity refusals include monitor URLs for running runs. A successful launch prints a durable monitor URL; queued means accepted, not completed. Hosted mode does not require --candidate-runner-dir or --scorer-runner-dir; when supplied, those directories must byte-match their registered versions. Retry response loss with the same --launch-intent-id. Local runner version ids materialize registered bytes, while optional runner dirs are additionally byte-verified before execution. Pass --engine legacy only for the frozen safety hatch. Skilled-proposer is official-only, its config requires that selection, and the selection cannot be combined with --reflection-prompt-template. --instruction-set
|
|
685
|
+
usage: 'orizu optimizations run-gepa [--hosted [--launch-intent-id <uuid>]] --project <team/project> --optimizer-version-id <id> (--candidate-version-id <id> | --instruction-set <set[/profile[@vN]]> [--model-config <identity>] [--profile-version <N>] [--component-selector round-robin|all]) --runner-version-id <id> [--candidate-runner-dir <dir>] --scorer-version-id <id> --scorer-runner-version-id <id> [--scorer-runner-dir <dir>] [--engine official|legacy] [--candidate-proposer skilled-proposer [--candidate-proposer-config <@file|path>] [--proposal-max-calls <n>] [--proposal-max-tokens <n>]] [--scorer-input-contract gepa|flat_row] [--scorer-candidate-field <row-field>] [--allow-degenerate-seed] [--seed <n>] --dataset-version-id <id> --split-set-id <id> [--train-split train] [--val-split validation] [--minibatch-size <n>] [--candidate-selection-strategy pareto|current_best|epsilon_greedy] [--epsilon <n>] [--objective <text>] [--budget auto|light|medium|heavy | --max-metric-calls <n> | --max-full-evals <n> | --max-iterations <n> | --max-candidate-proposals <n>] [--num-threads auto|N] [--reflection-model <provider/model>] [--reflection-temperature <n>] [--reflection-prompt-template <text|@file>] [--reflection-max-tokens N] [--reflection-provider-settings <json|@file>] [--reflection-retry-attempts N] [--reflection-http-timeout-seconds N] [--skip-perfect-parent-reflection|--no-skip-perfect-parent-reflection] [--disable-evaluation-cache] [--auto-promote] [--promotion-label <label>] [--log-row-snapshots] [--metadata <json>] [--python <command>] [--log-dir logs] [--no-local-log] [--json]',
|
|
686
|
+
summary: 'Run official GEPA locally and stream events, or use --hosted for a queued Orizu-managed sandbox launch. Hosted eligibility requires a staff-enabled team, capacity below its concurrent-run cap, a launch-project optimizer version whose validated manifest.optimizer_family is gepa, registered candidate/scorer runners, a reflection model from a registered provider, and a named --budget preset within the team ceiling. Hosted eligibility catalog refusals use stable code/remediation JSON with optional bounded detail, and capacity refusals include monitor URLs for running runs. A successful launch prints a durable monitor URL; queued means accepted, not completed. Hosted mode does not require --candidate-runner-dir or --scorer-runner-dir; when supplied, those directories must byte-match their registered versions. Retry response loss with the same --launch-intent-id. Local runner version ids materialize registered bytes, while optional runner dirs are additionally byte-verified before execution. Pass --engine legacy only for the frozen safety hatch. Skilled-proposer is official-only, its config requires that selection, and the selection cannot be combined with --reflection-prompt-template. --instruction-set needs a profile, named by --model-config or a set/profile specifier, and cannot be combined with --candidate-version-id. The run starts from the production version of that profile unless @vN or --profile-version <N> names another version (the seed, promoted or not); a specifier and flags that name different values are refused; round-robin updates one component-map entry per round and all updates every component. --max-candidate-proposals is official-engine-only. 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).',
|
|
687
687
|
group: 'Optimizations',
|
|
688
688
|
},
|
|
689
689
|
{
|
|
@@ -775,8 +775,8 @@ export const COMMAND_DOCS: CliCommandDoc[] = [
|
|
|
775
775
|
},
|
|
776
776
|
{
|
|
777
777
|
path: ['apps', 'export'],
|
|
778
|
-
usage: 'orizu apps export [--app <appId>] [--project <team/project>] [--version <n>] [--out <
|
|
779
|
-
summary: 'Export app source for the selected app version.',
|
|
778
|
+
usage: 'orizu apps export [--app <appId>] [--project <team/project>] [--version <n>] [--out <file-or-directory>]',
|
|
779
|
+
summary: 'Export app source for the selected app version. --out is a file path, or a directory (the file is named <app>.v<version>.tsx inside it).',
|
|
780
780
|
group: 'Apps',
|
|
781
781
|
},
|
|
782
782
|
{
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The system prompt for the hosted agent (ORI-2207).
|
|
3
|
+
*
|
|
4
|
+
* OpenCode (the program that runs the agent in a hosted session) ships its own
|
|
5
|
+
* built-in prompt that starts "You are opencode, an interactive CLI tool…", so
|
|
6
|
+
* the agent used to introduce itself as OpenCode. We register our own "primary
|
|
7
|
+
* agent" in the OpenCode config with this text, and every prompt request picks
|
|
8
|
+
* it by name. That replaces OpenCode's built-in prompt entirely, so this text
|
|
9
|
+
* carries the working rules we still want: use the tools, keep answers short.
|
|
10
|
+
* AGENTS.md is still added after it, as before.
|
|
11
|
+
*/
|
|
12
|
+
export const HOSTED_AGENT_NAME = 'orizu'
|
|
13
|
+
|
|
14
|
+
export const HOSTED_AGENT_PROMPT = `You are the Orizu agent. You work in the person's repository inside an Orizu workbench, helping them with software engineering tasks. If asked who or what you are, say you are the Orizu agent. Never call yourself OpenCode.
|
|
15
|
+
|
|
16
|
+
- Do the work with your tools: read files, search, edit, and run commands. Do not guess at what a file contains when you can look.
|
|
17
|
+
- Keep answers short and plain. Say what you did and what the person needs to know next.
|
|
18
|
+
- Follow the repository's own conventions and any instructions in its AGENTS.md.`
|
package/src/hosted-gepa-cli.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto'
|
|
2
2
|
|
|
3
3
|
import { dispatchGepaEngine } from './gepa-engine-dispatch.js'
|
|
4
|
+
import { resolveGepaSeedFromEnvironment } from './instruction-set-gepa-launch.js'
|
|
4
5
|
import { parseHostedGepaNumericValue } from './hosted-gepa-numeric-values.js'
|
|
5
6
|
import { authedFetch } from './http.js'
|
|
6
7
|
import { hostedProviderFromModel, hostedProviderSettingsError, unsupportedHostedProviderMessage } from './hosted-provider-settings.js'
|
|
@@ -216,11 +217,16 @@ export async function runHostedGepaOptimization(options: HostedGepaOptions): Pro
|
|
|
216
217
|
: kind === 'json' ? JSON.parse(env[name]!) : kind === 'boolean' ? env[name] === '1' : env[name]
|
|
217
218
|
}
|
|
218
219
|
const launchIntentId = explicitLaunchIntent || randomUUID()
|
|
220
|
+
// ORI-2128: a named Seed version is resolved here exactly as a local run
|
|
221
|
+
// resolves it, and pinned in the body; without one the server keeps its
|
|
222
|
+
// one-call production lookup.
|
|
223
|
+
const seedProfileVersionId = await resolveGepaSeedFromEnvironment(env, options.project, 'server')
|
|
219
224
|
const body = {
|
|
220
225
|
hosted: true, launchIntentId, optimizerVersionId: env.ORIZU_OPTIMIZER_VERSION_ID,
|
|
221
226
|
promptVersionIds: env.ORIZU_PROMPT_VERSION_ID ? [env.ORIZU_PROMPT_VERSION_ID] : [],
|
|
222
227
|
...(env.ORIZU_INSTRUCTION_SET_NAME ? { instructionSetName: env.ORIZU_INSTRUCTION_SET_NAME,
|
|
223
|
-
modelConfigIdentity: env.ORIZU_MODEL_CONFIG_IDENTITY, componentSelector: env.ORIZU_COMPONENT_SELECTOR
|
|
228
|
+
modelConfigIdentity: env.ORIZU_MODEL_CONFIG_IDENTITY, componentSelector: env.ORIZU_COMPONENT_SELECTOR,
|
|
229
|
+
...(seedProfileVersionId ? { instructionSetProfileVersionId: seedProfileVersionId } : {}) } : {}),
|
|
224
230
|
runnerVersionId: env.ORIZU_RUNNER_VERSION_ID,
|
|
225
231
|
scorers: [{ scorerVersionId: env.ORIZU_SCORER_VERSION_ID,
|
|
226
232
|
scorerRunnerVersionId: env.ORIZU_SCORER_RUNNER_VERSION_ID, role: 'selection', scorerConfig: {} }],
|
|
@@ -95,6 +95,7 @@
|
|
|
95
95
|
|
|
96
96
|
import { parseModelIdentity } from './provider-registry.js'
|
|
97
97
|
|
|
98
|
+
import { HARNESS_NAMES } from './hosted-harness.js'
|
|
98
99
|
import type {
|
|
99
100
|
AgentHarness,
|
|
100
101
|
HarnessEvent,
|
|
@@ -323,7 +324,7 @@ export function createClaudeAgentHarness(
|
|
|
323
324
|
}
|
|
324
325
|
|
|
325
326
|
return {
|
|
326
|
-
name:
|
|
327
|
+
name: HARNESS_NAMES.claudeAgentSdk,
|
|
327
328
|
|
|
328
329
|
async start(opts: HarnessStartOptions): Promise<{ agentSessionId: string }> {
|
|
329
330
|
startModel = opts.model
|
|
@@ -471,6 +472,7 @@ export function createClaudeAgentHarness(
|
|
|
471
472
|
args: asRecord(block.input),
|
|
472
473
|
callId,
|
|
473
474
|
status: 'running',
|
|
475
|
+
harness: HARNESS_NAMES.claudeAgentSdk,
|
|
474
476
|
},
|
|
475
477
|
}
|
|
476
478
|
}
|
|
@@ -495,6 +497,7 @@ export function createClaudeAgentHarness(
|
|
|
495
497
|
callId,
|
|
496
498
|
status: block.is_error === true ? 'error' : 'completed',
|
|
497
499
|
output: toolResultOutput(block.content),
|
|
500
|
+
harness: HARNESS_NAMES.claudeAgentSdk,
|
|
498
501
|
},
|
|
499
502
|
}
|
|
500
503
|
}
|
|
@@ -51,13 +51,16 @@
|
|
|
51
51
|
|
|
52
52
|
import { readFileSync } from 'fs'
|
|
53
53
|
|
|
54
|
+
import { HOSTED_AGENT_NAME, HOSTED_AGENT_PROMPT } from './hosted-agent-prompt.js'
|
|
54
55
|
import { findProvider, parseModelIdentity } from './provider-registry.js'
|
|
55
56
|
|
|
57
|
+
import { HARNESS_NAMES } from './hosted-harness.js'
|
|
56
58
|
import type {
|
|
57
59
|
AgentHarness,
|
|
58
60
|
HarnessEvent,
|
|
59
61
|
HarnessPrompt,
|
|
60
62
|
HarnessStartOptions,
|
|
63
|
+
HarnessToolPayload,
|
|
61
64
|
} from './hosted-harness.js'
|
|
62
65
|
|
|
63
66
|
// OpenCode is HARD-PINNED to this version everywhere it is installed/launched.
|
|
@@ -121,7 +124,9 @@ class PromptAbortedError extends Error {
|
|
|
121
124
|
// -- OpenCode ascending id generator (port of OpenCode's id.ts) --------------
|
|
122
125
|
// Monotonic so our user-message id always sorts AFTER any prior assistant
|
|
123
126
|
// message id — this is what prevents OpenCode's prompt loop from early-exiting
|
|
124
|
-
// on `lastUser.id < lastAssistant.id`.
|
|
127
|
+
// on `lastUser.id < lastAssistant.id`. Shape (opencode-ai@1.14.41): prefix, `_`,
|
|
128
|
+
// 12 hex digits holding the low 48 bits of `ms * 4096 + counter`, then 14
|
|
129
|
+
// base62 characters. `nowMs` is a parameter only so tests can pin the clock.
|
|
125
130
|
const BASE62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
|
126
131
|
let idLastTimestamp = 0
|
|
127
132
|
let idCounter = 0
|
|
@@ -134,14 +139,15 @@ function randomBase62(length: number): string {
|
|
|
134
139
|
return out
|
|
135
140
|
}
|
|
136
141
|
|
|
137
|
-
function ascendingId(prefix: 'ses' | 'msg' | 'prt'): string {
|
|
138
|
-
const nowMs = Date.now()
|
|
142
|
+
export function ascendingId(prefix: 'ses' | 'msg' | 'prt', nowMs: number = Date.now()): string {
|
|
139
143
|
if (nowMs !== idLastTimestamp) {
|
|
140
144
|
idLastTimestamp = nowMs
|
|
141
145
|
idCounter = 0
|
|
142
146
|
}
|
|
143
147
|
idCounter += 1
|
|
144
|
-
|
|
148
|
+
// BigInt, as OpenCode does: the JS `&` operator works on 32 bits, which made
|
|
149
|
+
// short, sometimes negative ids that sorted before OpenCode's own (ORI-2116).
|
|
150
|
+
const encoded = (BigInt(nowMs) * BigInt(0x1000) + BigInt(idCounter)) & BigInt(0xffffffffffff)
|
|
145
151
|
const hex = encoded.toString(16).padStart(12, '0')
|
|
146
152
|
return `${prefix}_${hex}${randomBase62(14)}`
|
|
147
153
|
}
|
|
@@ -188,6 +194,7 @@ export function buildPromptRequestBody(
|
|
|
188
194
|
const body: Record<string, unknown> = {
|
|
189
195
|
parts: [{ type: 'text', text: content }],
|
|
190
196
|
messageID: opencodeMessageId,
|
|
197
|
+
agent: HOSTED_AGENT_NAME,
|
|
191
198
|
}
|
|
192
199
|
if (!model) return body
|
|
193
200
|
|
|
@@ -495,7 +502,7 @@ export function createOpenCodeHarness(options: OpenCodeHarnessOptions): AgentHar
|
|
|
495
502
|
}
|
|
496
503
|
|
|
497
504
|
return {
|
|
498
|
-
name:
|
|
505
|
+
name: HARNESS_NAMES.opencode,
|
|
499
506
|
|
|
500
507
|
async start(opts: HarnessStartOptions): Promise<{ agentSessionId: string }> {
|
|
501
508
|
startModel = opts.model
|
|
@@ -598,13 +605,25 @@ export function createOpenCodeHarness(options: OpenCodeHarnessOptions): AgentHar
|
|
|
598
605
|
if (emittedToolStates.has(toolKey)) return events
|
|
599
606
|
emittedToolStates.add(toolKey)
|
|
600
607
|
const terminal = status === 'completed' || status === 'error'
|
|
601
|
-
const payload:
|
|
608
|
+
const payload: HarnessToolPayload = {
|
|
602
609
|
tool: typeof part.tool === 'string' ? part.tool : '',
|
|
603
610
|
args: input,
|
|
604
611
|
callId,
|
|
605
612
|
status,
|
|
613
|
+
harness: HARNESS_NAMES.opencode,
|
|
614
|
+
}
|
|
615
|
+
// A failed call keeps its message in `state.error`, not `state.output`
|
|
616
|
+
// (opencode-ai@1.14.41 ToolStateError); it is that call's output.
|
|
617
|
+
if (terminal) {
|
|
618
|
+
payload.output = typeof state.output === 'string' ? state.output
|
|
619
|
+
: typeof state.error === 'string' ? state.error : ''
|
|
620
|
+
}
|
|
621
|
+
// OpenCode's own one-line title and structured extras (ORI-2160),
|
|
622
|
+
// passed through as given; absent when the state has none.
|
|
623
|
+
if (typeof state.title === 'string' && state.title.length > 0) payload.title = state.title
|
|
624
|
+
if (state.metadata && typeof state.metadata === 'object' && !Array.isArray(state.metadata)) {
|
|
625
|
+
payload.metadata = state.metadata as Record<string, unknown>
|
|
606
626
|
}
|
|
607
|
-
if (terminal) payload.output = typeof state.output === 'string' ? state.output : ''
|
|
608
627
|
events.push({ kind: terminal ? 'tool_result' : 'tool_call', messageId: msgId, payload })
|
|
609
628
|
} else if (partType === 'step-start') {
|
|
610
629
|
events.push({ kind: 'step_start', messageId: msgId, payload: {} })
|
|
@@ -1116,6 +1135,8 @@ export function buildOpenCodeConfigContent(opts: OpenCodeConfigOptions): string
|
|
|
1116
1135
|
return JSON.stringify({
|
|
1117
1136
|
model: opts.model,
|
|
1118
1137
|
permission: opts.permission ?? defaultHeadlessPermission(),
|
|
1138
|
+
// Replaces OpenCode's built-in "You are opencode…" prompt (ORI-2207).
|
|
1139
|
+
agent: { [HOSTED_AGENT_NAME]: { mode: 'primary', prompt: HOSTED_AGENT_PROMPT } },
|
|
1119
1140
|
})
|
|
1120
1141
|
}
|
|
1121
1142
|
|
package/src/hosted-harness.ts
CHANGED
|
@@ -117,8 +117,39 @@ export interface HarnessEvent {
|
|
|
117
117
|
critical?: boolean
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
/** The drivers' stable names. A tool event's `harness` field is one of these. */
|
|
121
|
+
export const HARNESS_NAMES = {
|
|
122
|
+
opencode: 'opencode',
|
|
123
|
+
claudeAgentSdk: 'claude-agent-sdk',
|
|
124
|
+
} as const
|
|
125
|
+
|
|
126
|
+
type HarnessName = (typeof HARNESS_NAMES)[keyof typeof HARNESS_NAMES]
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Payload of a `tool_call` / `tool_result` event (ORI-2160). One chat renderer
|
|
130
|
+
* shows every tool call whatever harness produced it, so the payload carries:
|
|
131
|
+
* • `title` — the harness's one-line summary of the call (OpenCode's
|
|
132
|
+
* `state.title`). Absent when the harness has none.
|
|
133
|
+
* • `metadata` — structured extras such as a diff, an exit code or match
|
|
134
|
+
* counts (OpenCode's `state.metadata`). Absent when none.
|
|
135
|
+
* • `harness` — which driver produced the event.
|
|
136
|
+
* All three are optional: an event without them is still a valid tool event.
|
|
137
|
+
* The sink and the digest pass them through unchanged.
|
|
138
|
+
*/
|
|
139
|
+
export interface HarnessToolPayload {
|
|
140
|
+
[key: string]: unknown
|
|
141
|
+
tool?: string
|
|
142
|
+
args?: Record<string, unknown>
|
|
143
|
+
callId: string
|
|
144
|
+
status: string
|
|
145
|
+
output?: string
|
|
146
|
+
title?: string
|
|
147
|
+
metadata?: Record<string, unknown>
|
|
148
|
+
harness?: HarnessName
|
|
149
|
+
}
|
|
150
|
+
|
|
120
151
|
export interface AgentHarness {
|
|
121
|
-
/** Stable driver name —
|
|
152
|
+
/** Stable driver name — one of HARNESS_NAMES. */
|
|
122
153
|
readonly name: string
|
|
123
154
|
/** Boot the agent process/session; returns the resumable agent session id. */
|
|
124
155
|
start(opts: HarnessStartOptions): Promise<{ agentSessionId: string }>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The check the session coordinator makes on `/opt/orizu/prebaked.json`, the
|
|
3
|
+
* first bootstrap command of a hosted optimization run. It lives here so the
|
|
4
|
+
* release bake's start check (hosted-runtime-image/check-snapshot-starts.mjs)
|
|
5
|
+
* rejects a snapshot on exactly the rules the coordinator would refuse it on.
|
|
6
|
+
* Worker code imports this module, so it must not use Node APIs.
|
|
7
|
+
*/
|
|
8
|
+
export function isHostedOptimizationPrebakedMarker(raw: string): boolean {
|
|
9
|
+
try {
|
|
10
|
+
const marker: unknown = JSON.parse(raw)
|
|
11
|
+
if (!marker || typeof marker !== 'object' || Array.isArray(marker)) return false
|
|
12
|
+
const value = marker as Record<string, unknown>
|
|
13
|
+
return value.builtFor === 'vercel-sandbox' &&
|
|
14
|
+
typeof value.cliVersion === 'string' && value.cliVersion.length > 0 &&
|
|
15
|
+
typeof value.opencodeVersion === 'string' && value.opencodeVersion.length > 0 &&
|
|
16
|
+
typeof value.claudeSdkVersion === 'string' && value.claudeSdkVersion.length > 0
|
|
17
|
+
} catch {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool display hints (ORI-2160, moved here for ORI-2167).
|
|
3
|
+
*
|
|
4
|
+
* Each harness driver has its own built-in tools. This module holds, per
|
|
5
|
+
* driver, what the chat needs to show a call without switching on its name:
|
|
6
|
+
* a category (icon), a label (the verb on the row), the input key that
|
|
7
|
+
* summarizes the call, how to show the output, and input keys to hide.
|
|
8
|
+
*
|
|
9
|
+
* It has no imports with side effects, so both the CLI and the web app
|
|
10
|
+
* (the chat renderer, and the activity projection that picks which input
|
|
11
|
+
* key reaches the browser) import it directly.
|
|
12
|
+
*/
|
|
13
|
+
// Type-only, so the web build never has to load the harness module itself.
|
|
14
|
+
import type { HARNESS_NAMES } from './hosted-harness.js'
|
|
15
|
+
|
|
16
|
+
type HarnessNames = typeof HARNESS_NAMES
|
|
17
|
+
const OPENCODE: HarnessNames['opencode'] = 'opencode'
|
|
18
|
+
const CLAUDE_AGENT_SDK: HarnessNames['claudeAgentSdk'] = 'claude-agent-sdk'
|
|
19
|
+
|
|
20
|
+
/** What kind of thing a tool does — the renderer picks an icon/layout from
|
|
21
|
+
* this, never from the tool name. */
|
|
22
|
+
export const TOOL_CATEGORIES = {
|
|
23
|
+
file: 'file',
|
|
24
|
+
command: 'command',
|
|
25
|
+
search: 'search',
|
|
26
|
+
network: 'network',
|
|
27
|
+
question: 'question',
|
|
28
|
+
subAgent: 'sub-agent',
|
|
29
|
+
list: 'list',
|
|
30
|
+
plug: 'plug',
|
|
31
|
+
other: 'other',
|
|
32
|
+
} as const
|
|
33
|
+
|
|
34
|
+
export type ToolCategory = (typeof TOOL_CATEGORIES)[keyof typeof TOOL_CATEGORIES]
|
|
35
|
+
|
|
36
|
+
/** How the tool's output text should be shown. */
|
|
37
|
+
export const TOOL_OUTPUT_KINDS = {
|
|
38
|
+
text: 'text',
|
|
39
|
+
diff: 'diff',
|
|
40
|
+
markdown: 'markdown',
|
|
41
|
+
json: 'json',
|
|
42
|
+
list: 'list',
|
|
43
|
+
} as const
|
|
44
|
+
|
|
45
|
+
type ToolOutputKind = (typeof TOOL_OUTPUT_KINDS)[keyof typeof TOOL_OUTPUT_KINDS]
|
|
46
|
+
|
|
47
|
+
/** Per-tool display hint. Each driver owns a table of these for its built-in
|
|
48
|
+
* tools, so tool-name knowledge lives behind the seam. */
|
|
49
|
+
export interface ToolHint {
|
|
50
|
+
category: ToolCategory
|
|
51
|
+
/** The verb the chat row shows ("Read", "Ran"). A tool without a hint shows
|
|
52
|
+
* its own name instead. */
|
|
53
|
+
label?: string
|
|
54
|
+
/** The input key whose value summarizes the call (e.g. `command`), or null
|
|
55
|
+
* when the tool takes no input worth summarizing. */
|
|
56
|
+
summaryKey: string | null
|
|
57
|
+
outputKind: ToolOutputKind
|
|
58
|
+
/** Input keys whose values must not be shown as-is (large bodies such as a
|
|
59
|
+
* file's full new content, or likely secrets). */
|
|
60
|
+
redactKeys: readonly string[]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type ToolHintTable = Readonly<Record<string, ToolHint>>
|
|
64
|
+
|
|
65
|
+
// Argument names that look like they hold a credential. Redacted for every
|
|
66
|
+
// tool, and never used as the fallback summary.
|
|
67
|
+
const SECRET_LOOKING_KEY = /token|secret|password|passwd|api[_-]?key|authorization|credential/i
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Look a tool up in a driver's hint table. A tool the table does not know (an
|
|
71
|
+
* MCP tool, a plug-in) falls back to the `plug` category, summarized by its
|
|
72
|
+
* first non-empty string argument that does not look like a secret.
|
|
73
|
+
*/
|
|
74
|
+
export function resolveToolHint(
|
|
75
|
+
table: ToolHintTable,
|
|
76
|
+
tool: string,
|
|
77
|
+
args: Record<string, unknown> = {}
|
|
78
|
+
): ToolHint {
|
|
79
|
+
const known = Object.hasOwn(table, tool) ? table[tool] : undefined
|
|
80
|
+
const secretKeys = Object.keys(args).filter(key => SECRET_LOOKING_KEY.test(key))
|
|
81
|
+
const redactKeys = [...new Set([...(known?.redactKeys ?? []), ...secretKeys])]
|
|
82
|
+
if (known) return { ...known, redactKeys }
|
|
83
|
+
const summaryKey =
|
|
84
|
+
Object.keys(args).find(key => {
|
|
85
|
+
const value = args[key]
|
|
86
|
+
return typeof value === 'string' && value.length > 0 && !redactKeys.includes(key)
|
|
87
|
+
}) ?? null
|
|
88
|
+
return { category: TOOL_CATEGORIES.plug, summaryKey, outputKind: TOOL_OUTPUT_KINDS.text, redactKeys }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// OpenCode's built-in tools, as the pinned opencode-ai@1.14.41 tool registry
|
|
92
|
+
// lists them, with the input key that summarizes each call. Tools not listed
|
|
93
|
+
// here (MCP tools, plug-ins) resolve to `plug` through resolveToolHint.
|
|
94
|
+
export const OPENCODE_TOOL_HINTS: ToolHintTable = {
|
|
95
|
+
read: { category: 'file', label: 'Read', summaryKey: 'filePath', outputKind: 'text', redactKeys: [] },
|
|
96
|
+
edit: { category: 'file', label: 'Edited', summaryKey: 'filePath', outputKind: 'diff', redactKeys: ['oldString', 'newString'] },
|
|
97
|
+
write: { category: 'file', label: 'Wrote', summaryKey: 'filePath', outputKind: 'text', redactKeys: ['content'] },
|
|
98
|
+
apply_patch: { category: 'file', label: 'Edited', summaryKey: 'patchText', outputKind: 'diff', redactKeys: [] },
|
|
99
|
+
lsp: { category: 'file', label: 'Checked', summaryKey: 'filePath', outputKind: 'json', redactKeys: [] },
|
|
100
|
+
bash: { category: 'command', label: 'Ran', summaryKey: 'command', outputKind: 'text', redactKeys: [] },
|
|
101
|
+
glob: { category: 'search', label: 'Listed', summaryKey: 'pattern', outputKind: 'list', redactKeys: [] },
|
|
102
|
+
grep: { category: 'search', label: 'Searched', summaryKey: 'pattern', outputKind: 'text', redactKeys: [] },
|
|
103
|
+
webfetch: { category: 'network', label: 'Fetched', summaryKey: 'url', outputKind: 'markdown', redactKeys: [] },
|
|
104
|
+
websearch: { category: 'network', label: 'Searched', summaryKey: 'query', outputKind: 'text', redactKeys: [] },
|
|
105
|
+
question: { category: 'question', label: 'Asked', summaryKey: 'questions', outputKind: 'text', redactKeys: [] },
|
|
106
|
+
plan_exit: { category: 'question', label: 'Asked', summaryKey: null, outputKind: 'text', redactKeys: [] },
|
|
107
|
+
task: { category: 'sub-agent', label: 'Sub-agent', summaryKey: 'description', outputKind: 'markdown', redactKeys: [] },
|
|
108
|
+
todowrite: { category: 'list', label: 'Planned', summaryKey: 'todos', outputKind: 'json', redactKeys: [] },
|
|
109
|
+
skill: { category: 'other', label: 'Loaded', summaryKey: 'name', outputKind: 'markdown', redactKeys: [] },
|
|
110
|
+
invalid: { category: 'other', label: 'Invalid', summaryKey: 'tool', outputKind: 'text', redactKeys: [] },
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// The Claude Agent SDK's built-in tools (SDK 0.3.201: input types in
|
|
114
|
+
// sdk-tools.d.ts, names from the SDK; `Task` is the older name of `Agent`),
|
|
115
|
+
// with the input key that summarizes each call. MCP tools
|
|
116
|
+
// (`mcp__<server>__<tool>`) are not listed and resolve to `plug` through
|
|
117
|
+
// resolveToolHint. This driver sends no `title` or `metadata`: the SDK has no
|
|
118
|
+
// equivalent of OpenCode's.
|
|
119
|
+
export const CLAUDE_TOOL_HINTS: ToolHintTable = {
|
|
120
|
+
Read: { category: 'file', label: 'Read', summaryKey: 'file_path', outputKind: 'text', redactKeys: [] },
|
|
121
|
+
Edit: { category: 'file', label: 'Edited', summaryKey: 'file_path', outputKind: 'text', redactKeys: ['old_string', 'new_string'] },
|
|
122
|
+
Write: { category: 'file', label: 'Wrote', summaryKey: 'file_path', outputKind: 'text', redactKeys: ['content'] },
|
|
123
|
+
NotebookEdit: { category: 'file', label: 'Edited', summaryKey: 'notebook_path', outputKind: 'text', redactKeys: ['new_source'] },
|
|
124
|
+
Bash: { category: 'command', label: 'Ran', summaryKey: 'command', outputKind: 'text', redactKeys: [] },
|
|
125
|
+
TaskOutput: { category: 'command', label: 'Checked', summaryKey: 'task_id', outputKind: 'text', redactKeys: [] },
|
|
126
|
+
TaskStop: { category: 'command', label: 'Stopped', summaryKey: 'task_id', outputKind: 'text', redactKeys: [] },
|
|
127
|
+
Glob: { category: 'search', label: 'Listed', summaryKey: 'pattern', outputKind: 'list', redactKeys: [] },
|
|
128
|
+
Grep: { category: 'search', label: 'Searched', summaryKey: 'pattern', outputKind: 'text', redactKeys: [] },
|
|
129
|
+
WebFetch: { category: 'network', label: 'Fetched', summaryKey: 'url', outputKind: 'markdown', redactKeys: [] },
|
|
130
|
+
WebSearch: { category: 'network', label: 'Searched', summaryKey: 'query', outputKind: 'text', redactKeys: [] },
|
|
131
|
+
AskUserQuestion: { category: 'question', label: 'Asked', summaryKey: 'questions', outputKind: 'text', redactKeys: [] },
|
|
132
|
+
ExitPlanMode: { category: 'question', label: 'Asked', summaryKey: null, outputKind: 'markdown', redactKeys: [] },
|
|
133
|
+
Agent: { category: 'sub-agent', label: 'Sub-agent', summaryKey: 'description', outputKind: 'markdown', redactKeys: [] },
|
|
134
|
+
Task: { category: 'sub-agent', label: 'Sub-agent', summaryKey: 'description', outputKind: 'markdown', redactKeys: [] },
|
|
135
|
+
TodoWrite: { category: 'list', label: 'Planned', summaryKey: 'todos', outputKind: 'text', redactKeys: [] },
|
|
136
|
+
ListMcpResourcesTool: { category: 'plug', label: 'Listed', summaryKey: 'server', outputKind: 'json', redactKeys: [] },
|
|
137
|
+
ReadMcpResourceTool: { category: 'plug', label: 'Read', summaryKey: 'uri', outputKind: 'text', redactKeys: [] },
|
|
138
|
+
Skill: { category: 'other', label: 'Loaded', summaryKey: 'skill', outputKind: 'markdown', redactKeys: [] },
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const NO_HINTS: ToolHintTable = {}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The hint table for the harness that produced an event. Events recorded
|
|
145
|
+
* before the seam named its harness came from OpenCode, the default driver.
|
|
146
|
+
* A harness with no table here (a future pi driver, say) gets none, so every
|
|
147
|
+
* tool falls back to its own name and first string argument.
|
|
148
|
+
*/
|
|
149
|
+
export function toolHintsFor(harness: string | undefined): ToolHintTable {
|
|
150
|
+
if (harness === undefined || harness === OPENCODE) return OPENCODE_TOOL_HINTS
|
|
151
|
+
if (harness === CLAUDE_AGENT_SDK) return CLAUDE_TOOL_HINTS
|
|
152
|
+
return NO_HINTS
|
|
153
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -112,7 +112,7 @@ import { resolveAuthTokenForBaseUrl } from './credentials.js'
|
|
|
112
112
|
import { applyHostedInstructionSetProfileOverride, dispatchHostedCommands } from './hosted-commands.js'
|
|
113
113
|
import { dispatchGepaEngine } from './gepa-engine-dispatch.js'
|
|
114
114
|
import { getGepaPythonPathEntries } from './gepa-python-paths.js'
|
|
115
|
-
import {
|
|
115
|
+
import { resolveGepaSeedFromEnvironment } from './instruction-set-gepa-launch.js'
|
|
116
116
|
import { prepareSkilledProposerLaunch, spawnSkilledProposerChild } from './skilled-proposer-launch.js'
|
|
117
117
|
import { reportCommentsCommand, throwDeprecatedPromptCommentsCommand } from './report-comments-cli.js'
|
|
118
118
|
import { getCapabilities, renderHelpForArgs } from './help.js'
|
|
@@ -1365,10 +1365,7 @@ async function runGepaOptimization() {
|
|
|
1365
1365
|
PYTHONPATH: getGepaPythonPathEntries(process.env.PYTHONPATH).join(delimiter),
|
|
1366
1366
|
PYTHONUNBUFFERED: process.env.PYTHONUNBUFFERED || '1',
|
|
1367
1367
|
}))
|
|
1368
|
-
const
|
|
1369
|
-
const modelConfigIdentity = dispatch.environment.ORIZU_MODEL_CONFIG_IDENTITY
|
|
1370
|
-
const instructionSetProfileVersionId = instructionSetName && modelConfigIdentity
|
|
1371
|
-
? await resolveGepaInstructionSetProfileVersion(instructionSetName, modelConfigIdentity, project) : undefined
|
|
1368
|
+
const instructionSetProfileVersionId = await resolveGepaSeedFromEnvironment(dispatch.environment, project, 'production')
|
|
1372
1369
|
if (instructionSetProfileVersionId) dispatch.environment.ORIZU_INSTRUCTION_SET_PROFILE_VERSION_ID = instructionSetProfileVersionId
|
|
1373
1370
|
selectedEngine = dispatch.engine
|
|
1374
1371
|
const launch = prepareSkilledProposerLaunch(python, dispatch.engine, dispatch.environment)
|