orizu 0.5.20 → 0.5.21
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/archive-artifacts-cli.d.ts +50 -0
- package/dist/archive-artifacts-cli.d.ts.map +1 -0
- package/dist/artifact-archive.d.ts +48 -0
- package/dist/artifact-archive.d.ts.map +1 -0
- package/dist/artifact-pull.d.ts +22 -0
- package/dist/artifact-pull.d.ts.map +1 -0
- package/dist/banner.d.ts +3 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/connectors-cli.d.ts +31 -0
- package/dist/connectors-cli.d.ts.map +1 -0
- package/dist/credentials.d.ts +46 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/csv.d.ts +49 -0
- package/dist/csv.d.ts.map +1 -0
- package/dist/dataset-download.d.ts +6 -0
- package/dist/dataset-download.d.ts.map +1 -0
- package/dist/dataset-edit-rows.d.ts +10 -0
- package/dist/dataset-edit-rows.d.ts.map +1 -0
- package/dist/dataset-upload-snapshot.d.ts +13 -0
- package/dist/dataset-upload-snapshot.d.ts.map +1 -0
- package/dist/daytona-slice-rehearsal.d.ts +89 -0
- package/dist/daytona-slice-rehearsal.d.ts.map +1 -0
- package/dist/daytona-slice.d.ts +106 -0
- package/dist/daytona-slice.d.ts.map +1 -0
- package/dist/deploy-key-merge-core.d.ts +121 -0
- package/dist/deploy-key-merge-core.d.ts.map +1 -0
- package/dist/diff-comments-cli.d.ts +12 -0
- package/dist/diff-comments-cli.d.ts.map +1 -0
- package/dist/diff-comments-cli.js +3 -0
- package/dist/egress-allowlist-cli.d.ts +23 -0
- package/dist/egress-allowlist-cli.d.ts.map +1 -0
- package/dist/egress-policy.d.ts +108 -0
- package/dist/egress-policy.d.ts.map +1 -0
- package/dist/error-response.d.ts +9 -0
- package/dist/error-response.d.ts.map +1 -0
- package/dist/file-parser.d.ts +5 -0
- package/dist/file-parser.d.ts.map +1 -0
- package/dist/gepa-engine-dispatch.d.ts +9 -0
- package/dist/gepa-engine-dispatch.d.ts.map +1 -0
- package/dist/gepa-engine-dispatch.js +48 -2
- package/dist/gepa-python-command.d.ts +6 -0
- package/dist/gepa-python-command.d.ts.map +1 -0
- package/dist/gepa-python-paths.d.ts +6 -0
- package/dist/gepa-python-paths.d.ts.map +1 -0
- package/dist/gepa-python-paths.js +6 -3
- package/dist/git-credential.d.ts +69 -0
- package/dist/git-credential.d.ts.map +1 -0
- package/dist/git-recovery-environment.d.ts +31 -0
- package/dist/git-recovery-environment.d.ts.map +1 -0
- package/dist/github-setup.d.ts +122 -0
- package/dist/github-setup.d.ts.map +1 -0
- package/dist/global-flags.d.ts +12 -0
- package/dist/global-flags.d.ts.map +1 -0
- package/dist/help.d.ts +46 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +91 -23
- package/dist/hosted-boot.d.ts +273 -0
- package/dist/hosted-boot.d.ts.map +1 -0
- package/dist/hosted-bootstrap-rehearsal.d.ts +86 -0
- package/dist/hosted-bootstrap-rehearsal.d.ts.map +1 -0
- package/dist/hosted-bootstrap.d.ts +188 -0
- package/dist/hosted-bootstrap.d.ts.map +1 -0
- package/dist/hosted-broker-server.d.ts +50 -0
- package/dist/hosted-broker-server.d.ts.map +1 -0
- package/dist/hosted-git-http-server.d.ts +36 -0
- package/dist/hosted-git-http-server.d.ts.map +1 -0
- package/dist/hosted-harness-claude.d.ts +195 -0
- package/dist/hosted-harness-claude.d.ts.map +1 -0
- package/dist/hosted-harness-opencode.d.ts +227 -0
- package/dist/hosted-harness-opencode.d.ts.map +1 -0
- package/dist/hosted-harness.d.ts +118 -0
- package/dist/hosted-harness.d.ts.map +1 -0
- package/dist/hosted-harvest.d.ts +68 -0
- package/dist/hosted-harvest.d.ts.map +1 -0
- package/dist/hosted-headless.d.ts +72 -0
- package/dist/hosted-headless.d.ts.map +1 -0
- package/dist/hosted-loop-lifecycle.d.ts +206 -0
- package/dist/hosted-loop-lifecycle.d.ts.map +1 -0
- package/dist/hosted-loop.d.ts +170 -0
- package/dist/hosted-loop.d.ts.map +1 -0
- package/dist/hosted-run-event-sink.d.ts +222 -0
- package/dist/hosted-run-event-sink.d.ts.map +1 -0
- package/dist/hosted-runtime-assets.d.ts +186 -0
- package/dist/hosted-runtime-assets.d.ts.map +1 -0
- package/dist/hosted-session-cli.d.ts +258 -0
- package/dist/hosted-session-cli.d.ts.map +1 -0
- package/dist/hosted-skill-staging.d.ts +73 -0
- package/dist/hosted-skill-staging.d.ts.map +1 -0
- package/dist/http.d.ts +8 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +88 -92
- package/dist/instruction-set-gepa-launch.d.ts +3 -0
- package/dist/instruction-set-gepa-launch.d.ts.map +1 -0
- package/dist/instruction-set-gepa-launch.js +15 -0
- package/dist/instruction-set-loader.d.ts +6 -0
- package/dist/instruction-set-loader.d.ts.map +1 -0
- package/dist/instruction-set-loader.js +59 -0
- package/dist/instruction-set-manifest.d.ts +14 -0
- package/dist/instruction-set-manifest.d.ts.map +1 -0
- package/dist/instruction-set-manifest.js +162 -0
- package/dist/instruction-sets-cli.d.ts +40 -0
- package/dist/instruction-sets-cli.d.ts.map +1 -0
- package/dist/instruction-sets-cli.js +361 -0
- package/dist/json-response.d.ts +4 -0
- package/dist/json-response.d.ts.map +1 -0
- package/dist/jsonl-stream.d.ts +8 -0
- package/dist/jsonl-stream.d.ts.map +1 -0
- package/dist/kill-switch-cli.d.ts +30 -0
- package/dist/kill-switch-cli.d.ts.map +1 -0
- package/dist/loopback-serve.d.ts +25 -0
- package/dist/loopback-serve.d.ts.map +1 -0
- package/dist/manifests-cli.d.ts +22 -0
- package/dist/manifests-cli.d.ts.map +1 -0
- package/dist/markdown-report-input.d.ts +7 -0
- package/dist/markdown-report-input.d.ts.map +1 -0
- package/dist/merge-job.d.ts +155 -0
- package/dist/merge-job.d.ts.map +1 -0
- package/dist/model-configs-cli.d.ts +19 -0
- package/dist/model-configs-cli.d.ts.map +1 -0
- package/dist/model-configs-cli.js +104 -0
- package/dist/optimization-export-cli.d.ts +19 -0
- package/dist/optimization-export-cli.d.ts.map +1 -0
- package/dist/optimizations-list-cli.d.ts +20 -0
- package/dist/optimizations-list-cli.d.ts.map +1 -0
- package/dist/optimizations-promote-cli.d.ts +8 -0
- package/dist/optimizations-promote-cli.d.ts.map +1 -0
- package/dist/optimizations-promote-cli.js +34 -0
- package/dist/option-validation.d.ts +15 -0
- package/dist/option-validation.d.ts.map +1 -0
- package/dist/preview-runtime.d.ts +63 -0
- package/dist/preview-runtime.d.ts.map +1 -0
- package/dist/prompt-draft-push.d.ts +60 -0
- package/dist/prompt-draft-push.d.ts.map +1 -0
- package/dist/prompt-label-cli.d.ts +11 -0
- package/dist/prompt-label-cli.d.ts.map +1 -0
- package/dist/prompt-label-cli.js +29 -0
- package/dist/prompt-length-wire.d.ts +28 -0
- package/dist/prompt-length-wire.d.ts.map +1 -0
- package/dist/prompt-report-cli.d.ts +10 -0
- package/dist/prompt-report-cli.d.ts.map +1 -0
- package/dist/prompt-summary-table.d.ts +15 -0
- package/dist/prompt-summary-table.d.ts.map +1 -0
- package/dist/prompt-summary-table.js +6 -0
- package/dist/registration-tag-warning.d.ts +32 -0
- package/dist/registration-tag-warning.d.ts.map +1 -0
- package/dist/report-comments-cli.d.ts +16 -0
- package/dist/report-comments-cli.d.ts.map +1 -0
- package/dist/report-markdown-blankness.d.ts +2 -0
- package/dist/report-markdown-blankness.d.ts.map +1 -0
- package/dist/report-replacement-warning.d.ts +17 -0
- package/dist/report-replacement-warning.d.ts.map +1 -0
- package/dist/repository-backup-core.d.ts +225 -0
- package/dist/repository-backup-core.d.ts.map +1 -0
- package/dist/repository-recovery-manifest.d.ts +83 -0
- package/dist/repository-recovery-manifest.d.ts.map +1 -0
- package/dist/run-event-digest.d.ts +74 -0
- package/dist/run-event-digest.d.ts.map +1 -0
- package/dist/runner-dir-verify.d.ts +66 -0
- package/dist/runner-dir-verify.d.ts.map +1 -0
- package/dist/runner-instruction-set-contract.d.ts +24 -0
- package/dist/runner-instruction-set-contract.d.ts.map +1 -0
- package/dist/runner-instruction-set-contract.js +50 -0
- package/dist/sandbox-hygiene.d.ts +45 -0
- package/dist/sandbox-hygiene.d.ts.map +1 -0
- package/dist/sandbox-provider.d.ts +193 -0
- package/dist/sandbox-provider.d.ts.map +1 -0
- package/dist/scorer-draft-push.d.ts +50 -0
- package/dist/scorer-draft-push.d.ts.map +1 -0
- package/dist/scorers-cli.d.ts +20 -0
- package/dist/scorers-cli.d.ts.map +1 -0
- package/dist/scorers-cli.js +22 -0
- package/dist/scorers-verify-parity-bridge.d.ts +16 -0
- package/dist/scorers-verify-parity-bridge.d.ts.map +1 -0
- package/dist/scorers-verify-parity-bridge.js +90 -0
- package/dist/scorers-verify-parity.d.ts +81 -0
- package/dist/scorers-verify-parity.d.ts.map +1 -0
- package/dist/scorers-verify-parity.js +502 -0
- package/dist/scores-accept-cli.d.ts +21 -0
- package/dist/scores-accept-cli.d.ts.map +1 -0
- package/dist/secret-redaction.d.ts +47 -0
- package/dist/secret-redaction.d.ts.map +1 -0
- package/dist/session-timeline.d.ts +54 -0
- package/dist/session-timeline.d.ts.map +1 -0
- package/dist/setup-prompt.d.ts +5 -0
- package/dist/setup-prompt.d.ts.map +1 -0
- package/dist/skill-installer.d.ts +68 -0
- package/dist/skill-installer.d.ts.map +1 -0
- package/dist/skilled-proposer-launch.d.ts +7 -0
- package/dist/skilled-proposer-launch.d.ts.map +1 -0
- package/dist/skilled-proposer-launch.js +60 -0
- package/dist/smoke.d.ts +102 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/start-intent.d.ts +12 -0
- package/dist/start-intent.d.ts.map +1 -0
- package/dist/task-assignment-manifest.d.ts +15 -0
- package/dist/task-assignment-manifest.d.ts.map +1 -0
- package/dist/task-create-error.d.ts +18 -0
- package/dist/task-create-error.d.ts.map +1 -0
- package/dist/team-connectors-cli.d.ts +29 -0
- package/dist/team-connectors-cli.d.ts.map +1 -0
- package/dist/types.d.ts +39 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/vercel-sandbox-provider.d.ts +156 -0
- package/dist/vercel-sandbox-provider.d.ts.map +1 -0
- package/dist/workbench-cli.d.ts +233 -0
- package/dist/workbench-cli.d.ts.map +1 -0
- package/dist/workspace-sync.d.ts +207 -0
- package/dist/workspace-sync.d.ts.map +1 -0
- package/dist/workspace.d.ts +92 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/zip-draft-push.d.ts +55 -0
- package/dist/zip-draft-push.d.ts.map +1 -0
- package/package.json +14 -1
- package/requirements/README.md +138 -0
- package/requirements/skilled-proposer.in +66 -0
- package/requirements/skilled-proposer.lock +265 -0
- package/scripts/ensure-skilled-proposer-venv.mjs +5 -0
- package/scripts/refresh-skilled-proposer-lock.mjs +256 -0
- package/src/diff-comments-cli.ts +4 -0
- package/src/gepa-engine-dispatch.ts +42 -2
- package/src/gepa-python-paths.ts +6 -3
- package/src/help.ts +93 -23
- package/src/index.ts +99 -126
- package/src/instruction-set-gepa-launch.ts +28 -0
- package/src/instruction-set-loader.ts +37 -0
- package/src/instruction-set-manifest.ts +120 -0
- package/src/instruction-sets-cli.ts +306 -0
- package/src/model-configs-cli.ts +119 -0
- package/src/optimizations-promote-cli.ts +41 -0
- package/src/prompt-label-cli.ts +55 -0
- package/src/prompt-summary-table.ts +12 -0
- package/src/runner-instruction-set-contract.ts +64 -0
- package/src/scorers-cli.ts +40 -0
- package/src/scorers-verify-parity-bridge.ts +90 -0
- package/src/scorers-verify-parity.ts +564 -0
- package/src/skilled-proposer-launch.ts +89 -0
- package/src/skilled-proposer-venv-manager.mjs +757 -0
- package/tsconfig.json +2 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/__init__.py +2 -1
- package/vendor/orizu-gepa/src/orizu_gepa_connector/adapter.py +8 -2
- package/vendor/orizu-gepa/src/orizu_gepa_connector/callbacks.py +17 -3
- package/vendor/orizu-gepa/src/orizu_gepa_connector/engine.py +100 -8
- package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +52 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/reflection.py +92 -12
- package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +100 -22
- package/vendor/orizu-gepa/src/orizu_gepa_connector/skilled_proposer_bridge.py +381 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/stop_conditions.py +17 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/translator.py +2 -1
- package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +52 -7
- package/vendor/orizu-gepa-python/src/orizu_gepa/local_log.py +4 -1
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +10 -5
- package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +323 -68
- package/vendor/orizu-gepa-python/src/orizu_gepa/runner.py +207 -13
- package/vendor/skills/orizu-cli/SKILL.md +2 -0
- package/vendor/skills/orizu-cli/references/cli-reference.md +85 -1
- package/vendor/skills/orizu-cli/references/migrate-existing-gepa-setup.md +303 -0
- package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +25 -0
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +108 -8
package/src/index.ts
CHANGED
|
@@ -66,11 +66,15 @@ import {
|
|
|
66
66
|
workspaceExists,
|
|
67
67
|
} from './workspace.js'
|
|
68
68
|
import { connectorsCommand } from './connectors-cli.js'
|
|
69
|
+
import { modelConfigsCommand } from './model-configs-cli.js'
|
|
69
70
|
import { ARTIFACT_MAX_BYTES, runnerOptimizerCommand } from './artifact-pull.js'
|
|
70
71
|
import { killSwitchCommand } from './kill-switch-cli.js'
|
|
71
72
|
import { egressAllowlistCommand } from './egress-allowlist-cli.js'
|
|
72
73
|
import { acceptScoreRunCommand } from './scores-accept-cli.js'
|
|
73
74
|
import { listOptimizationRunsCommand } from './optimizations-list-cli.js'
|
|
75
|
+
import { promoteOptimizationCommand } from './optimizations-promote-cli.js'
|
|
76
|
+
import { scorersCommand } from './scorers-cli.js'
|
|
77
|
+
import { verifyScorerParityCommand } from './scorers-verify-parity.js'
|
|
74
78
|
import {
|
|
75
79
|
archiveArtifactCommand,
|
|
76
80
|
listAssignmentsCommand,
|
|
@@ -85,9 +89,12 @@ import { workbenchCommand } from './workbench-cli.js'
|
|
|
85
89
|
import { hostedCommand } from './hosted-session-cli.js'
|
|
86
90
|
import { workspaceSyncCommand } from './workspace-sync.js'
|
|
87
91
|
import { runGitCredentialInvocation } from './git-credential.js'
|
|
92
|
+
import { instructionSetsCommand, syncToDisk } from './instruction-sets-cli.js'; export { loadInstructionSet } from './instruction-set-loader.js'
|
|
93
|
+
import { runnerInputInstructionSet, runnerInputPrompt, runnerInstructionSetSyncSet, type RunnerInstructionSet } from './runner-instruction-set-contract.js'
|
|
88
94
|
import { pushPromptDraft } from './prompt-draft-push.js'
|
|
89
95
|
import { readMarkdownReportInput } from './markdown-report-input.js'
|
|
90
96
|
import { promptPushErrorMessage, promptReportCommand } from './prompt-report-cli.js'
|
|
97
|
+
import { setPromptLabel } from './prompt-label-cli.js'
|
|
91
98
|
import { printPromptSummaryTable } from './prompt-summary-table.js'
|
|
92
99
|
import type {
|
|
93
100
|
CliLengthMeasurementUnavailableReason,
|
|
@@ -107,8 +114,10 @@ import {
|
|
|
107
114
|
import { diffCommentsCommand } from './diff-comments-cli.js'
|
|
108
115
|
import { exportOptimizationRunCommand } from './optimization-export-cli.js'
|
|
109
116
|
import { dispatchGepaEngine } from './gepa-engine-dispatch.js'
|
|
117
|
+
import { resolveGepaInstructionSetProfileVersion } from './instruction-set-gepa-launch.js'
|
|
110
118
|
import { getGepaPythonCommand } from './gepa-python-command.js'
|
|
111
119
|
import { getGepaPythonPathEntries } from './gepa-python-paths.js'
|
|
120
|
+
import { prepareSkilledProposerLaunch } from './skilled-proposer-launch.js'
|
|
112
121
|
|
|
113
122
|
export { parseJsonResponse, sanitizeTerminalText } from './json-response.js'
|
|
114
123
|
|
|
@@ -195,6 +204,10 @@ interface PromptSummary {
|
|
|
195
204
|
lengthStatsUnavailableReason?: CliLengthMeasurementUnavailableReason
|
|
196
205
|
lengthStatsVersionId?: string
|
|
197
206
|
lengthStatsVersionNumber?: number
|
|
207
|
+
owner?: {
|
|
208
|
+
instructionSetSlug: string
|
|
209
|
+
componentKey: string
|
|
210
|
+
} | null
|
|
198
211
|
}
|
|
199
212
|
|
|
200
213
|
interface TaskReportPayload {
|
|
@@ -229,7 +242,7 @@ interface ScoreSubmitInput {
|
|
|
229
242
|
|
|
230
243
|
interface RunnerExecContext {
|
|
231
244
|
prompt: {
|
|
232
|
-
body
|
|
245
|
+
body?: string | null
|
|
233
246
|
bodyKind: string
|
|
234
247
|
providerSettings: Record<string, unknown>
|
|
235
248
|
promptId?: string
|
|
@@ -241,6 +254,7 @@ interface RunnerExecContext {
|
|
|
241
254
|
metricKey: string
|
|
242
255
|
higherIsBetter: boolean
|
|
243
256
|
} | null
|
|
257
|
+
instructionSet?: RunnerInstructionSet
|
|
244
258
|
rows: Array<{
|
|
245
259
|
id: string
|
|
246
260
|
row: Record<string, unknown>
|
|
@@ -1634,6 +1648,10 @@ async function pullPromptArtifact(kind: 'prompt' | 'judge') {
|
|
|
1634
1648
|
metricKey: string
|
|
1635
1649
|
higherIsBetter: boolean
|
|
1636
1650
|
}>
|
|
1651
|
+
owner?: {
|
|
1652
|
+
instructionSetSlug: string
|
|
1653
|
+
componentKey: string
|
|
1654
|
+
} | null
|
|
1637
1655
|
}>(response, `${kind} pull`)
|
|
1638
1656
|
|
|
1639
1657
|
const targetDir = expandHomePath(outDir)
|
|
@@ -1720,6 +1738,7 @@ async function pullPromptArtifact(kind: 'prompt' | 'judge') {
|
|
|
1720
1738
|
scorer_version_id: data.scorerVersionId || undefined,
|
|
1721
1739
|
scorer_version_ids: scorerVersions.map(item => item.id),
|
|
1722
1740
|
path: targetDir,
|
|
1741
|
+
owner: data.owner || undefined,
|
|
1723
1742
|
})
|
|
1724
1743
|
return
|
|
1725
1744
|
}
|
|
@@ -1735,73 +1754,17 @@ async function pullPromptArtifact(kind: 'prompt' | 'judge') {
|
|
|
1735
1754
|
`Warning: prompt version ${sanitizeTerminalText(data.version.id)} does not map to an executable scorer version.`
|
|
1736
1755
|
)
|
|
1737
1756
|
}
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
const project = getArg('--project') || await resolveProjectSlug(null)
|
|
1744
|
-
const promptVersionId = getArg('--version')
|
|
1745
|
-
|
|
1746
|
-
if (!promptName || !label || !promptVersionId) {
|
|
1747
|
-
throw new Error('Usage: orizu prompts labels set <prompt-name> <label> --version <prompt-version-id> [--project <team/project>] [--json]')
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
const response = await authedFetch(`/api/cli/prompts/labels?project=${encodeURIComponent(project)}`, {
|
|
1751
|
-
method: 'POST',
|
|
1752
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1753
|
-
body: JSON.stringify({
|
|
1754
|
-
promptName,
|
|
1755
|
-
label,
|
|
1756
|
-
promptVersionId,
|
|
1757
|
-
}),
|
|
1758
|
-
})
|
|
1759
|
-
|
|
1760
|
-
if (!response.ok) {
|
|
1761
|
-
throw new Error(`Failed to set prompt label: ${await response.text()}`)
|
|
1762
|
-
}
|
|
1763
|
-
|
|
1764
|
-
const data = await parseJsonResponse<Record<string, unknown>>(response, 'Prompt label set')
|
|
1765
|
-
if (hasJsonFlag()) {
|
|
1766
|
-
printJson(data)
|
|
1767
|
-
return
|
|
1757
|
+
if (kind === 'prompt' && data.owner) {
|
|
1758
|
+
printLine(
|
|
1759
|
+
`Owner: ${sanitizeTerminalText(data.prompt.name)} (${sanitizeTerminalText(data.prompt.id)}) -> ` +
|
|
1760
|
+
`${sanitizeTerminalText(data.owner.instructionSetSlug)} / ${sanitizeTerminalText(data.owner.componentKey)}`
|
|
1761
|
+
)
|
|
1768
1762
|
}
|
|
1769
|
-
|
|
1770
|
-
printLine(`Moved ${sanitizeTerminalText(label)} to ${sanitizeTerminalText(promptVersionId)}`)
|
|
1771
1763
|
}
|
|
1772
1764
|
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
const command = archived ? 'archive' : 'restore'
|
|
1777
|
-
|
|
1778
|
-
if (!promptRef) {
|
|
1779
|
-
throw new Error(`Usage: orizu prompts ${command} <prompt-id-or-name> --project <team/project> [--json]`)
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
const response = await authedFetch(`/api/cli/prompts/${encodeURIComponent(promptRef)}?project=${encodeURIComponent(project)}`, {
|
|
1783
|
-
method: 'PATCH',
|
|
1784
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1785
|
-
body: JSON.stringify({ archived }),
|
|
1786
|
-
})
|
|
1787
|
-
|
|
1788
|
-
if (!response.ok) {
|
|
1789
|
-
throw new Error(`Failed to ${command} prompt: ${await response.text()}`)
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
const data = await parseJsonResponse<{
|
|
1793
|
-
prompt: {
|
|
1794
|
-
id: string
|
|
1795
|
-
name: string
|
|
1796
|
-
status: string
|
|
1797
|
-
}
|
|
1798
|
-
}>(response, `Prompt ${command}`)
|
|
1799
|
-
if (hasJsonFlag()) {
|
|
1800
|
-
printJson(data)
|
|
1801
|
-
return
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
printLine(`${archived ? 'Archived' : 'Restored'} prompt ${sanitizeTerminalText(data.prompt.name)} (${sanitizeTerminalText(data.prompt.id)})`)
|
|
1765
|
+
function refusePromptMutation(replacement: string): never {
|
|
1766
|
+
process.exitCode = 1
|
|
1767
|
+
throw new Error(`Use: orizu instructions ${replacement}`)
|
|
1805
1768
|
}
|
|
1806
1769
|
|
|
1807
1770
|
async function bindPromptScorer(role: 'headline' | 'tracked') {
|
|
@@ -1830,13 +1793,25 @@ async function bindPromptScorer(role: 'headline' | 'tracked') {
|
|
|
1830
1793
|
throw new Error(`Failed to bind prompt scorer: ${await response.text()}`)
|
|
1831
1794
|
}
|
|
1832
1795
|
|
|
1833
|
-
const data = await parseJsonResponse<
|
|
1796
|
+
const data = await parseJsonResponse<{
|
|
1797
|
+
promptScorer: Record<string, unknown>
|
|
1798
|
+
owner?: {
|
|
1799
|
+
instructionSetSlug: string
|
|
1800
|
+
componentKey: string
|
|
1801
|
+
}
|
|
1802
|
+
}>(response, 'Prompt scorer bind')
|
|
1834
1803
|
if (hasJsonFlag()) {
|
|
1835
1804
|
printJson(data)
|
|
1836
1805
|
return
|
|
1837
1806
|
}
|
|
1838
1807
|
|
|
1839
1808
|
printLine(`${role === 'headline' ? 'Set headline' : 'Added'} scorer ${sanitizeTerminalText(scorerVersionId)} for ${sanitizeTerminalText(promptId)}`)
|
|
1809
|
+
if (data.owner) {
|
|
1810
|
+
printLine(
|
|
1811
|
+
`Owner: ${sanitizeTerminalText(promptId)} -> ` +
|
|
1812
|
+
`${sanitizeTerminalText(data.owner.instructionSetSlug)} / ${sanitizeTerminalText(data.owner.componentKey)}`
|
|
1813
|
+
)
|
|
1814
|
+
}
|
|
1840
1815
|
}
|
|
1841
1816
|
|
|
1842
1817
|
function getPositionalArg(index: number): string | null {
|
|
@@ -2522,14 +2497,12 @@ async function updateOptimizationRunLifecycle(action: OptimizationLifecycleActio
|
|
|
2522
2497
|
|
|
2523
2498
|
// ALI-1073 list moved to optimizations-list-cli.ts (line ratchet, ALI-976);
|
|
2524
2499
|
// ALI-1175 labels its best scores as agent-reported evidence there.
|
|
2525
|
-
|
|
2526
2500
|
async function runGepaOptimization() {
|
|
2527
2501
|
const project = getArg('--project') || await resolveProjectSlug(null)
|
|
2528
2502
|
const baseUrl = getBaseUrl()
|
|
2529
2503
|
const runGepa = getGepaPythonCommand(cliArgs.slice(2), process.env)
|
|
2530
2504
|
const python = runGepa.python
|
|
2531
2505
|
let forwardedArgs = runGepa.args
|
|
2532
|
-
|
|
2533
2506
|
// ALI-1159 (ADR-007): GEPA executes ad-hoc local runner dirs while
|
|
2534
2507
|
// attributing records to registered runner version ids — verify the bytes
|
|
2535
2508
|
// ARE those versions before the optimizer spawns. Scans the forwarded argv
|
|
@@ -2538,7 +2511,6 @@ async function runGepaOptimization() {
|
|
|
2538
2511
|
// so a post-hash mutation of the original dirs cannot change what runs.
|
|
2539
2512
|
const verified = await verifyGepaRunnerDirsFromArgs(forwardedArgs)
|
|
2540
2513
|
forwardedArgs = verified.args
|
|
2541
|
-
|
|
2542
2514
|
let result
|
|
2543
2515
|
let selectedEngine = 'official'
|
|
2544
2516
|
try {
|
|
@@ -2548,11 +2520,9 @@ async function runGepaOptimization() {
|
|
|
2548
2520
|
// rotation crossing the hash + lookup round-trips cannot hand the child a
|
|
2549
2521
|
// stale bearer.
|
|
2550
2522
|
const token = resolveAuthTokenForBaseUrl(baseUrl)
|
|
2551
|
-
|
|
2552
2523
|
if (!forwardedArgs.includes('--project')) {
|
|
2553
2524
|
forwardedArgs = ['--project', project, ...forwardedArgs]
|
|
2554
2525
|
}
|
|
2555
|
-
|
|
2556
2526
|
const dispatch = dispatchGepaEngine(forwardedArgs, project, {
|
|
2557
2527
|
...process.env,
|
|
2558
2528
|
ORIZU_API_URL: baseUrl,
|
|
@@ -2562,16 +2532,17 @@ async function runGepaOptimization() {
|
|
|
2562
2532
|
PYTHONPATH: getGepaPythonPathEntries(process.env.PYTHONPATH).join(delimiter),
|
|
2563
2533
|
PYTHONUNBUFFERED: process.env.PYTHONUNBUFFERED || '1',
|
|
2564
2534
|
})
|
|
2535
|
+
const instructionSetName = dispatch.environment.ORIZU_INSTRUCTION_SET_NAME
|
|
2536
|
+
const modelConfigIdentity = dispatch.environment.ORIZU_MODEL_CONFIG_IDENTITY
|
|
2537
|
+
const instructionSetProfileVersionId = instructionSetName && modelConfigIdentity
|
|
2538
|
+
? await resolveGepaInstructionSetProfileVersion(instructionSetName, modelConfigIdentity, project) : undefined
|
|
2539
|
+
if (instructionSetProfileVersionId) dispatch.environment.ORIZU_INSTRUCTION_SET_PROFILE_VERSION_ID = instructionSetProfileVersionId
|
|
2565
2540
|
selectedEngine = dispatch.engine
|
|
2566
|
-
|
|
2567
|
-
result = spawnSync(python, ['-m', dispatch.module, ...dispatch.args], {
|
|
2568
|
-
stdio: 'inherit',
|
|
2569
|
-
env: dispatch.environment,
|
|
2570
|
-
})
|
|
2541
|
+
const launch = prepareSkilledProposerLaunch(python, dispatch.engine, dispatch.environment)
|
|
2542
|
+
result = spawnSync(launch.python, ['-m', dispatch.module, ...dispatch.args], { stdio: 'inherit', env: launch.environment })
|
|
2571
2543
|
} finally {
|
|
2572
2544
|
verified.cleanup()
|
|
2573
2545
|
}
|
|
2574
|
-
|
|
2575
2546
|
if (result.error) {
|
|
2576
2547
|
throw new Error(`${selectedEngine} GEPA engine failed: ${result.error.message}`, { cause: result.error })
|
|
2577
2548
|
}
|
|
@@ -5777,7 +5748,7 @@ async function downloadAnnotations() {
|
|
|
5777
5748
|
printLine(`Saved ${format.toUpperCase()} export to ${sanitizeTerminalText(filename)}`)
|
|
5778
5749
|
}
|
|
5779
5750
|
|
|
5780
|
-
function readRunnerManifest(runnerDir: string): { command: string[]; supports_body_kind?: string[] } {
|
|
5751
|
+
export function readRunnerManifest(runnerDir: string): { command: string[]; supports_body_kind?: string[] } {
|
|
5781
5752
|
const manifestPath = join(runnerDir, 'manifest.json')
|
|
5782
5753
|
const raw = readSourceFile(manifestPath)
|
|
5783
5754
|
const manifest = JSON.parse(raw) as {
|
|
@@ -5805,9 +5776,9 @@ function readRunnerManifest(runnerDir: string): { command: string[]; supports_bo
|
|
|
5805
5776
|
}
|
|
5806
5777
|
}
|
|
5807
5778
|
|
|
5808
|
-
const RUNNER_TIMEOUT_MS = 120_000
|
|
5809
|
-
const RUNNER_OUTPUT_MAX_BYTES = 2 * 1024 * 1024
|
|
5810
|
-
const RUNNER_ENV_ALLOWLIST = new Set([
|
|
5779
|
+
export const RUNNER_TIMEOUT_MS = 120_000
|
|
5780
|
+
export const RUNNER_OUTPUT_MAX_BYTES = 2 * 1024 * 1024
|
|
5781
|
+
export const RUNNER_ENV_ALLOWLIST = new Set([
|
|
5811
5782
|
'PATH',
|
|
5812
5783
|
'SystemRoot',
|
|
5813
5784
|
'WINDIR',
|
|
@@ -5825,7 +5796,7 @@ const RUNNER_ENV_ALLOWLIST = new Set([
|
|
|
5825
5796
|
'GOOGLE_API_KEY',
|
|
5826
5797
|
])
|
|
5827
5798
|
|
|
5828
|
-
function runnerSubprocessEnv(inputPath: string, outputPath: string): NodeJS.ProcessEnv {
|
|
5799
|
+
export function runnerSubprocessEnv(inputPath: string, outputPath: string, instructionSetDir?: string): NodeJS.ProcessEnv {
|
|
5829
5800
|
const env = {} as NodeJS.ProcessEnv
|
|
5830
5801
|
for (const key of RUNNER_ENV_ALLOWLIST) {
|
|
5831
5802
|
const value = process.env[key]
|
|
@@ -5835,6 +5806,8 @@ function runnerSubprocessEnv(inputPath: string, outputPath: string): NodeJS.Proc
|
|
|
5835
5806
|
}
|
|
5836
5807
|
env.ORIZU_RUNNER_INPUT_PATH = inputPath
|
|
5837
5808
|
env.ORIZU_RUNNER_OUTPUT_PATH = outputPath
|
|
5809
|
+
delete env.ORIZU_INSTRUCTION_SET_DIR
|
|
5810
|
+
if (instructionSetDir) env.ORIZU_INSTRUCTION_SET_DIR = instructionSetDir
|
|
5838
5811
|
return env
|
|
5839
5812
|
}
|
|
5840
5813
|
|
|
@@ -5847,7 +5820,7 @@ function boundedRunnerOutput(value: string | Buffer | null | undefined): string
|
|
|
5847
5820
|
return `${buffer.subarray(0, RUNNER_OUTPUT_MAX_BYTES).toString('utf8')}\n[truncated]`
|
|
5848
5821
|
}
|
|
5849
5822
|
|
|
5850
|
-
async function materializeRunnerVersion(runnerVersionId: string): Promise<{ runnerDir: string; cleanup: () => void }> {
|
|
5823
|
+
export async function materializeRunnerVersion(runnerVersionId: string): Promise<{ runnerDir: string; cleanup: () => void }> {
|
|
5851
5824
|
const response = await authedFetch(`/api/cli/runner-versions/${encodeURIComponent(runnerVersionId)}/download`)
|
|
5852
5825
|
if (!response.ok) {
|
|
5853
5826
|
throw new Error(`Failed to download runner version: ${await response.text()}`)
|
|
@@ -5941,7 +5914,6 @@ async function runnersExec() {
|
|
|
5941
5914
|
})()
|
|
5942
5915
|
: await materializeRunnerVersion(resolvedRunnerVersionId)
|
|
5943
5916
|
const runnerDir = materializedRunner.runnerDir
|
|
5944
|
-
|
|
5945
5917
|
try {
|
|
5946
5918
|
assertSnapshotManifestConfined(runnerDir, runnerDirArg ? '--runner-dir' : '--runner-version')
|
|
5947
5919
|
// Inside the try (codex round-6 P3): a bad manifest must not leak the dir.
|
|
@@ -5951,22 +5923,19 @@ async function runnersExec() {
|
|
|
5951
5923
|
`Runner does not support prompt body kind '${context.prompt.bodyKind}'. Supported kinds: ${manifest.supports_body_kind.join(', ')}`
|
|
5952
5924
|
)
|
|
5953
5925
|
}
|
|
5954
|
-
|
|
5955
5926
|
const resultLines: string[] = []
|
|
5956
5927
|
for (const row of context.rows) {
|
|
5957
5928
|
const tempDir = mkdtempSync(join(tmpdir(), 'orizu-runner-'))
|
|
5958
5929
|
const inputPath = join(tempDir, 'input.json')
|
|
5959
5930
|
const outputPath = join(tempDir, 'output.json')
|
|
5960
|
-
|
|
5931
|
+
const instructionSetDir = context.instructionSet ? join(tempDir, 'instruction-set') : undefined
|
|
5961
5932
|
try {
|
|
5962
5933
|
const modelOutput = row.row.model_output ?? row.row.modelOutput ?? row.row.output ?? null
|
|
5934
|
+
if (instructionSetDir && context.instructionSet) syncToDisk(instructionSetDir, runnerInstructionSetSyncSet(context.instructionSet))
|
|
5963
5935
|
writeFileSync(inputPath, JSON.stringify({
|
|
5964
5936
|
row: row.row,
|
|
5965
|
-
prompt:
|
|
5966
|
-
|
|
5967
|
-
body_kind: context.prompt.bodyKind,
|
|
5968
|
-
provider_settings: context.prompt.providerSettings,
|
|
5969
|
-
},
|
|
5937
|
+
prompt: runnerInputPrompt(context.prompt),
|
|
5938
|
+
...(context.instructionSet ? { instruction_set: runnerInputInstructionSet(context.instructionSet) } : {}),
|
|
5970
5939
|
subject: scorerVersion
|
|
5971
5940
|
? {
|
|
5972
5941
|
type: 'scorer_row',
|
|
@@ -5994,7 +5963,7 @@ async function runnersExec() {
|
|
|
5994
5963
|
|
|
5995
5964
|
const result = spawnSync(manifest.command[0], manifest.command.slice(1), {
|
|
5996
5965
|
cwd: runnerDir,
|
|
5997
|
-
env: runnerSubprocessEnv(inputPath, outputPath),
|
|
5966
|
+
env: runnerSubprocessEnv(inputPath, outputPath, instructionSetDir),
|
|
5998
5967
|
encoding: 'utf8',
|
|
5999
5968
|
maxBuffer: RUNNER_OUTPUT_MAX_BYTES,
|
|
6000
5969
|
timeout: RUNNER_TIMEOUT_MS,
|
|
@@ -6226,8 +6195,11 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6226
6195
|
}
|
|
6227
6196
|
|
|
6228
6197
|
if (command === 'prompts' && subcommand === 'push') {
|
|
6229
|
-
|
|
6230
|
-
|
|
6198
|
+
if (getArg('--session')) {
|
|
6199
|
+
await pushPromptArtifact('prompt')
|
|
6200
|
+
return
|
|
6201
|
+
}
|
|
6202
|
+
refusePromptMutation('push <manifest> --project <team/project>')
|
|
6231
6203
|
}
|
|
6232
6204
|
|
|
6233
6205
|
if (
|
|
@@ -6241,17 +6213,25 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6241
6213
|
}
|
|
6242
6214
|
|
|
6243
6215
|
if (command === 'prompts' && subcommand === 'archive') {
|
|
6244
|
-
|
|
6245
|
-
return
|
|
6216
|
+
refusePromptMutation('archive <slug-or-exact-name> --project <team/project>')
|
|
6246
6217
|
}
|
|
6247
6218
|
|
|
6248
6219
|
if (command === 'prompts' && subcommand === 'restore') {
|
|
6249
|
-
|
|
6250
|
-
return
|
|
6220
|
+
refusePromptMutation('restore <slug-or-exact-name> --project <team/project>')
|
|
6251
6221
|
}
|
|
6252
6222
|
|
|
6253
6223
|
if (command === 'prompts' && subcommand === 'labels' && cliArgs[2] === 'set') {
|
|
6254
|
-
|
|
6224
|
+
if (getPositionalArg(4) === 'production') {
|
|
6225
|
+
refusePromptMutation('profiles promote <set> --project <team/project> --model-config <identity> --version <n>')
|
|
6226
|
+
}
|
|
6227
|
+
await setPromptLabel({
|
|
6228
|
+
getArg,
|
|
6229
|
+
getPositionalArg,
|
|
6230
|
+
json: hasJsonFlag(),
|
|
6231
|
+
printJson,
|
|
6232
|
+
printLine,
|
|
6233
|
+
resolveProjectSlug,
|
|
6234
|
+
})
|
|
6255
6235
|
return
|
|
6256
6236
|
}
|
|
6257
6237
|
|
|
@@ -6302,29 +6282,17 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6302
6282
|
return
|
|
6303
6283
|
}
|
|
6304
6284
|
|
|
6305
|
-
if (command === 'scorers'
|
|
6306
|
-
await
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
await showScorerDetail()
|
|
6317
|
-
return
|
|
6318
|
-
}
|
|
6319
|
-
|
|
6320
|
-
if (command === 'scorers' && subcommand === 'labels' && cliArgs[2] === 'set') {
|
|
6321
|
-
await setScorerLabel()
|
|
6322
|
-
return
|
|
6323
|
-
}
|
|
6324
|
-
|
|
6325
|
-
if (command === 'scorers' && subcommand === 'exec') {
|
|
6326
|
-
await execScorer()
|
|
6327
|
-
return
|
|
6285
|
+
if (command === 'scorers') {
|
|
6286
|
+
const scorersExit = await scorersCommand(cliArgs, {
|
|
6287
|
+
list: listScorers, register: registerScorer, detail: showScorerDetail,
|
|
6288
|
+
labelsSet: setScorerLabel, exec: execScorer,
|
|
6289
|
+
verifyParity: args => verifyScorerParityCommand(args, {
|
|
6290
|
+
json: hasJsonFlag(), print: printLine, printErr: printError,
|
|
6291
|
+
materializeRunnerVersion, readRunnerManifest, runnerSubprocessEnv,
|
|
6292
|
+
timeoutMs: RUNNER_TIMEOUT_MS, maxOutputBytes: RUNNER_OUTPUT_MAX_BYTES,
|
|
6293
|
+
}),
|
|
6294
|
+
})
|
|
6295
|
+
if (scorersExit !== null) { process.exitCode = scorersExit; return }
|
|
6328
6296
|
}
|
|
6329
6297
|
|
|
6330
6298
|
if ((command === 'runners' || command === 'optimizers') && subcommand === 'push') {
|
|
@@ -6385,7 +6353,12 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6385
6353
|
await listOptimizationRunsCommand(cliArgs.slice(2), { json: hasJsonFlag(), print: printLine, resolveProjectSlug })
|
|
6386
6354
|
return
|
|
6387
6355
|
}
|
|
6388
|
-
|
|
6356
|
+
if (command === 'optimizations' && subcommand === 'promote') {
|
|
6357
|
+
await promoteOptimizationCommand(cliArgs.slice(2), {
|
|
6358
|
+
json: hasJsonFlag(), print: printLine, resolveProjectSlug, fetcher: authedFetch,
|
|
6359
|
+
})
|
|
6360
|
+
return
|
|
6361
|
+
}
|
|
6389
6362
|
if (
|
|
6390
6363
|
command === 'optimizations' &&
|
|
6391
6364
|
(
|
|
@@ -6549,11 +6522,11 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6549
6522
|
}
|
|
6550
6523
|
if (command === 'git-credential') { process.exitCode = await runGitCredentialInvocation(cliArgs.slice(1), { stdin: readFileSync(0, 'utf8'), cwd: process.cwd(), print: printLine, printErr: printError }); return }
|
|
6551
6524
|
if (command === 'github' && subcommand === 'link') { const noInput = hasArg('--no-input') || hasArg('--non-interactive') || !isInteractiveTerminal(); const team = getOptionalArgValue('--team') || (await resolveSetupTeam(null, noInput, false)).slug; await runGithubLink(team, { print: printLine, fetcher: authedFetch, openUrl: openInBrowser, confirm: (org) => askYesNo(`Connect this team to GitHub org '${org}'?`, true) }); return }
|
|
6552
|
-
if (command === 'connectors' || command === 'manifests') {
|
|
6553
|
-
|
|
6554
|
-
process.exitCode = await moduleCommand(cliArgs.slice(1), { json: hasJsonFlag(), print: printLine, resolveProjectSlug })
|
|
6525
|
+
if (command === 'connectors' || command === 'manifests' || command === 'model-configs') {
|
|
6526
|
+
process.exitCode = await (command === 'connectors' ? connectorsCommand : command === 'manifests' ? manifestsCommand : modelConfigsCommand)(cliArgs.slice(1), { json: hasJsonFlag(), print: printLine, resolveProjectSlug })
|
|
6555
6527
|
return
|
|
6556
6528
|
}
|
|
6529
|
+
if (command === 'instructions' || command === 'instruction-sets') { process.exitCode = await instructionSetsCommand(cliArgs.slice(1), { json: hasJsonFlag(), print: printLine, resolveProjectSlug }); return }
|
|
6557
6530
|
if (command === 'team' && (subcommand === 'kill-agents' || subcommand === 'release-agents')) {
|
|
6558
6531
|
process.exitCode = await killSwitchCommand(cliArgs.slice(1), { json: hasJsonFlag(), print: printLine, printErr: printError })
|
|
6559
6532
|
return
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { authedFetch } from './http.js'
|
|
2
|
+
import { parseJsonResponse } from './json-response.js'
|
|
3
|
+
|
|
4
|
+
interface InstructionSetResolution {
|
|
5
|
+
instructionSet?: {
|
|
6
|
+
default?: { profileVersionId?: string } | null
|
|
7
|
+
profiles?: Array<{
|
|
8
|
+
modelConfigIdentity?: string | null
|
|
9
|
+
production?: { profileVersionId?: string } | null
|
|
10
|
+
}>
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Resolve the same production-or-default tuple served for a model identity. */
|
|
15
|
+
export async function resolveGepaInstructionSetProfileVersion(
|
|
16
|
+
instructionSetName: string,
|
|
17
|
+
modelConfigIdentity: string,
|
|
18
|
+
project: string,
|
|
19
|
+
): Promise<string> {
|
|
20
|
+
const response = await authedFetch(`/api/cli/instruction-sets/${encodeURIComponent(instructionSetName)}?project=${encodeURIComponent(project)}`)
|
|
21
|
+
if (!response.ok) throw new Error(`Instruction set resolution failed: ${await response.text()}`)
|
|
22
|
+
const payload = await parseJsonResponse<InstructionSetResolution>(response, 'Instruction set resolution')
|
|
23
|
+
const instructionSet = payload.instructionSet
|
|
24
|
+
const profile = instructionSet?.profiles?.find(candidate => candidate.modelConfigIdentity === modelConfigIdentity)
|
|
25
|
+
const profileVersionId = profile?.production?.profileVersionId || instructionSet?.default?.profileVersionId
|
|
26
|
+
if (!profileVersionId) throw new Error('instruction_set_profile_version_unresolvable')
|
|
27
|
+
return profileVersionId
|
|
28
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { existsSync, realpathSync, readFileSync, statSync } from 'node:fs'
|
|
2
|
+
import { resolve, sep } from 'node:path'
|
|
3
|
+
|
|
4
|
+
export class InstructionSetLoaderError extends Error {
|
|
5
|
+
constructor(readonly code: string) { super(code); this.name = 'InstructionSetLoaderError' }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Material { files: Record<string, string>; pinnedComponents?: Record<string, unknown> }
|
|
9
|
+
interface Manifest { manifestVersion: number; name: string; shape: string[]; default: Material; profiles: Array<{ modelConfigIdentity: string; production: Material | null }>; filteredTo?: string[] }
|
|
10
|
+
|
|
11
|
+
function inside(root: string, candidate: string) { return candidate === root || candidate.startsWith(`${root}${sep}`) }
|
|
12
|
+
|
|
13
|
+
export function loadInstructionSet(directory: string, name: string, modelConfigIdentity: string): Record<string, string> {
|
|
14
|
+
const unresolvedRoot = resolve(directory, name)
|
|
15
|
+
if (!existsSync(unresolvedRoot) || !statSync(unresolvedRoot).isDirectory()) throw new InstructionSetLoaderError('instruction_set_not_synced')
|
|
16
|
+
const setRoot = realpathSync(unresolvedRoot)
|
|
17
|
+
const manifestPath = resolve(setRoot, 'manifest.json')
|
|
18
|
+
if (!existsSync(manifestPath)) throw new InstructionSetLoaderError('instruction_set_manifest_missing')
|
|
19
|
+
let manifest: Manifest
|
|
20
|
+
try { manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as Manifest } catch { throw new InstructionSetLoaderError('instruction_set_manifest_missing') }
|
|
21
|
+
if (manifest.manifestVersion !== 1) throw new InstructionSetLoaderError('instruction_set_manifest_version_unsupported')
|
|
22
|
+
if (manifest.filteredTo && !manifest.filteredTo.includes(modelConfigIdentity)) throw new InstructionSetLoaderError('instruction_set_profile_not_synced')
|
|
23
|
+
const selected = manifest.profiles.find(profile => profile.modelConfigIdentity === modelConfigIdentity)?.production || manifest.default
|
|
24
|
+
const result: Record<string, string> = {}
|
|
25
|
+
for (const key of manifest.shape) {
|
|
26
|
+
if (selected.pinnedComponents?.[key]) throw new InstructionSetLoaderError('instruction_set_component_unavailable')
|
|
27
|
+
const relativePath = selected.files?.[key]
|
|
28
|
+
if (!relativePath) throw new InstructionSetLoaderError('instruction_set_profile_key_missing')
|
|
29
|
+
const unresolvedPath = resolve(setRoot, relativePath)
|
|
30
|
+
if (!inside(setRoot, unresolvedPath)) throw new InstructionSetLoaderError('instruction_set_path_unsafe')
|
|
31
|
+
let path: string
|
|
32
|
+
try { path = realpathSync(unresolvedPath) } catch { throw new InstructionSetLoaderError('instruction_set_component_unreadable') }
|
|
33
|
+
if (!inside(setRoot, path)) throw new InstructionSetLoaderError('instruction_set_path_unsafe')
|
|
34
|
+
try { result[key] = readFileSync(path, 'utf8') } catch { throw new InstructionSetLoaderError('instruction_set_component_unreadable') }
|
|
35
|
+
}
|
|
36
|
+
return result
|
|
37
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs'
|
|
2
|
+
import { dirname, isAbsolute, relative, resolve } from 'node:path'
|
|
3
|
+
import { createHash } from 'node:crypto'
|
|
4
|
+
|
|
5
|
+
export interface InstructionSetComponent { key: string; modelConfig?: string; text: string }
|
|
6
|
+
export interface InstructionSetManifest { name: string; description?: string | null; shape: string[]; components: InstructionSetComponent[] }
|
|
7
|
+
|
|
8
|
+
// Keep file-backed component reads within the same text budget used by prompt
|
|
9
|
+
// tooling; request assembly never asks Node to allocate an unbounded body.
|
|
10
|
+
export const MAX_INSTRUCTION_SET_COMPONENT_BYTES = 512 * 1024
|
|
11
|
+
|
|
12
|
+
const fail = (code: string): never => { throw new Error(code) }
|
|
13
|
+
const sha256 = (value: string) => createHash('sha256').update(value, 'utf8').digest('hex')
|
|
14
|
+
|
|
15
|
+
function hasPinnedComponents(material: unknown): boolean {
|
|
16
|
+
if (!material || typeof material !== 'object') return false
|
|
17
|
+
const pinned = (material as Record<string, unknown>).pinnedComponents
|
|
18
|
+
return Boolean(pinned && typeof pinned === 'object' && Object.keys(pinned).length > 0)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function loadInstructionSetManifest(path: string): InstructionSetManifest {
|
|
22
|
+
let value: unknown
|
|
23
|
+
let source: string
|
|
24
|
+
try { source = readFileSync(path, 'utf8') } catch { return fail('instruction_set_path_not_found') }
|
|
25
|
+
try { value = JSON.parse(source) } catch { return fail('instruction_set_manifest_invalid_json') }
|
|
26
|
+
if (!value || typeof value !== 'object') return fail('instruction_set_manifest_invalid')
|
|
27
|
+
const input = value as Record<string, unknown>
|
|
28
|
+
if (typeof input.name !== 'string' || input.name.trim() === '') return fail('instruction_set_manifest_invalid_name')
|
|
29
|
+
const hasDescription = Object.hasOwn(input, 'description')
|
|
30
|
+
if (hasDescription && input.description !== null && typeof input.description !== 'string') return fail('instruction_set_manifest_invalid_description')
|
|
31
|
+
const description = hasDescription
|
|
32
|
+
? input.description === '' ? null : input.description as string | null
|
|
33
|
+
: undefined
|
|
34
|
+
if (hasPinnedComponents(input.default)) return fail('instruction_set_component_unavailable')
|
|
35
|
+
if (!Array.isArray(input.shape) || input.shape.length === 0 || input.shape.some(key => typeof key !== 'string' || key === '') || new Set(input.shape).size !== input.shape.length) return fail('instruction_set_manifest_invalid_shape')
|
|
36
|
+
if (!Array.isArray(input.components)) return fail('instruction_set_manifest_invalid_components')
|
|
37
|
+
let root: string
|
|
38
|
+
try { root = realpathSync(dirname(path)) } catch { return fail('instruction_set_path_not_found') }
|
|
39
|
+
const seen = new Set<string>()
|
|
40
|
+
const loadedComponents: Array<InstructionSetComponent & { changedSinceSync: boolean; hasSyncBaseline: boolean }> = []
|
|
41
|
+
for (const raw of input.components) {
|
|
42
|
+
if (!raw || typeof raw !== 'object') return fail('instruction_set_manifest_invalid_component')
|
|
43
|
+
const component = raw as Record<string, unknown>
|
|
44
|
+
if (typeof component.key !== 'string' || component.key === '' || !input.shape.includes(component.key)) return fail(component.key && !input.shape.includes(component.key) ? 'instruction_set_key_outside_shape' : 'instruction_set_manifest_invalid_component')
|
|
45
|
+
const modelConfig = component.modelConfig === undefined ? undefined : component.modelConfig
|
|
46
|
+
if (modelConfig !== undefined && (typeof modelConfig !== 'string' || modelConfig === '')) return fail('instruction_set_manifest_invalid_component')
|
|
47
|
+
const identity = `${component.key}\u0000${modelConfig ?? ''}`; if (seen.has(identity)) return fail('instruction_set_duplicate_component'); seen.add(identity)
|
|
48
|
+
let text: string
|
|
49
|
+
if (typeof component.text === 'string' && component.path === undefined) text = component.text
|
|
50
|
+
else if (typeof component.path === 'string' && component.text === undefined) {
|
|
51
|
+
if (isAbsolute(component.path)) return fail('instruction_set_path_escape')
|
|
52
|
+
const candidate = resolve(root, component.path)
|
|
53
|
+
if (relative(root, candidate).startsWith('..')) return fail('instruction_set_path_escape')
|
|
54
|
+
if (!existsSync(candidate)) return fail('instruction_set_path_not_found')
|
|
55
|
+
let target: string; try { target = realpathSync(candidate) } catch { return fail('instruction_set_path_not_found') }
|
|
56
|
+
if (relative(root, target).startsWith('..')) return fail('instruction_set_path_escape')
|
|
57
|
+
try { text = readFileSync(target, 'utf8') } catch { return fail('instruction_set_path_not_found') }
|
|
58
|
+
if (Buffer.byteLength(text, 'utf8') > MAX_INSTRUCTION_SET_COMPONENT_BYTES) return fail('instruction_set_component_too_large')
|
|
59
|
+
} else return fail('instruction_set_manifest_invalid_component')
|
|
60
|
+
if (Buffer.byteLength(text, 'utf8') > MAX_INSTRUCTION_SET_COMPONENT_BYTES) return fail('instruction_set_component_too_large')
|
|
61
|
+
const hasSyncBaseline = typeof component.syncedContentSha256 === 'string'
|
|
62
|
+
if (component.syncedContentSha256 !== undefined && (!hasSyncBaseline || !/^[a-f0-9]{64}$/u.test(component.syncedContentSha256 as string))) return fail('instruction_set_manifest_invalid_component')
|
|
63
|
+
loadedComponents.push({
|
|
64
|
+
key: component.key,
|
|
65
|
+
...(modelConfig === undefined ? {} : { modelConfig }),
|
|
66
|
+
text,
|
|
67
|
+
hasSyncBaseline,
|
|
68
|
+
changedSinceSync: hasSyncBaseline && sha256(text) !== component.syncedContentSha256,
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
for (const key of input.shape) if (!loadedComponents.some(component => component.key === key)) return fail('instruction_set_missing_shape_key')
|
|
72
|
+
const defaultIdentity = input.default && typeof input.default === 'object'
|
|
73
|
+
&& typeof (input.default as Record<string, unknown>).modelConfigIdentity === 'string'
|
|
74
|
+
? (input.default as Record<string, unknown>).modelConfigIdentity as string
|
|
75
|
+
: null
|
|
76
|
+
let components = loadedComponents
|
|
77
|
+
if (defaultIdentity) {
|
|
78
|
+
if (input.manifestVersion === 1 && (input.shape as string[]).some(key => {
|
|
79
|
+
const bare = loadedComponents.find(component => component.key === key && component.modelConfig === undefined)
|
|
80
|
+
const tagged = loadedComponents.find(component => component.key === key && component.modelConfig === defaultIdentity)
|
|
81
|
+
return bare && tagged && (!bare.hasSyncBaseline || !tagged.hasSyncBaseline)
|
|
82
|
+
})) return fail('instruction_set_sync_baseline_missing')
|
|
83
|
+
const changedDefaultKeys = new Set(loadedComponents.flatMap(component => (
|
|
84
|
+
component.modelConfig === undefined && component.hasSyncBaseline && component.changedSinceSync
|
|
85
|
+
? [component.key]
|
|
86
|
+
: []
|
|
87
|
+
)))
|
|
88
|
+
const changedSameIdentityKeys = new Set(loadedComponents.flatMap(component => (
|
|
89
|
+
component.modelConfig === defaultIdentity && component.hasSyncBaseline && component.changedSinceSync
|
|
90
|
+
? [component.key]
|
|
91
|
+
: []
|
|
92
|
+
)))
|
|
93
|
+
if ([...changedDefaultKeys].some(key => changedSameIdentityKeys.has(key))) return fail('instruction_set_conflicting_synced_edits')
|
|
94
|
+
if (changedDefaultKeys.size > 0) {
|
|
95
|
+
components = loadedComponents.filter(component => (
|
|
96
|
+
component.modelConfig !== defaultIdentity || !changedDefaultKeys.has(component.key)
|
|
97
|
+
))
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const namedProfiles = new Set([
|
|
101
|
+
...(components.some(component => component.modelConfig === undefined) && defaultIdentity
|
|
102
|
+
? [defaultIdentity]
|
|
103
|
+
: []),
|
|
104
|
+
...components.flatMap(component => component.modelConfig ? [component.modelConfig] : []),
|
|
105
|
+
])
|
|
106
|
+
const namedProfileHasPins = Array.isArray(input.profiles) && input.profiles.some(profile => {
|
|
107
|
+
if (!profile || typeof profile !== 'object') return false
|
|
108
|
+
const value = profile as Record<string, unknown>
|
|
109
|
+
return typeof value.modelConfigIdentity === 'string'
|
|
110
|
+
&& namedProfiles.has(value.modelConfigIdentity)
|
|
111
|
+
&& hasPinnedComponents(value.production)
|
|
112
|
+
})
|
|
113
|
+
if (namedProfileHasPins) return fail('instruction_set_component_unavailable')
|
|
114
|
+
return {
|
|
115
|
+
name: input.name,
|
|
116
|
+
...(hasDescription ? { description } : {}),
|
|
117
|
+
shape: input.shape as string[],
|
|
118
|
+
components: components.map(({ key, modelConfig, text }) => ({ key, ...(modelConfig === undefined ? {} : { modelConfig }), text })),
|
|
119
|
+
}
|
|
120
|
+
}
|