opencode-agent-skill 13.0.0-beta.2 → 14.4.0
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/README.md +1571 -607
- package/bin/ocskill.mjs +172 -24
- package/docs/OPENCODE-COMPAT.md +34 -97
- package/docs/PI-COMPAT.md +203 -0
- package/docs/V14-CONTEXT-MEMORY-FABRIC.md +70 -0
- package/docs/V14.1-QUALITY-PERFORMANCE-FABRIC.md +114 -0
- package/docs/V14.2-TURBO-WEAK-MODEL-RUNTIME.md +448 -0
- package/evals/v14/tasks.json +46 -0
- package/global-config/agents/executor.md +7 -0
- package/global-config/agents/visual-verifier.md +22 -3
- package/global-config/plugins/ues-router/index.js +13 -8
- package/global-config/plugins/ues-router/policy-runtime.js +7 -0
- package/global-config/plugins/ues-router/router.js +12 -2
- package/global-config/skills/ecommerce-engineering/SKILL.md +1 -1
- package/global-config/skills/file-upload-engineering/SKILL.md +1 -1
- package/global-config/skills/git-safety/SKILL.md +1 -1
- package/global-config/skills/nestjs-engineering/SKILL.md +1 -1
- package/global-config/skills/performance-engineering/SKILL.md +1 -1
- package/global-config/skills/react-native-engineering/SKILL.md +1 -1
- package/global-config/skills/rest-api-design/SKILL.md +1 -1
- package/global-config/skills/ui-ux-engineering/SKILL.md +1 -1
- package/lib/adaptive-context-budget.mjs +97 -0
- package/lib/affected-tests.mjs +260 -0
- package/lib/benchmark-confidence.mjs +41 -2
- package/lib/browser-mcp-routing.mjs +166 -0
- package/lib/capability-fabric.mjs +359 -0
- package/lib/capability-registry.mjs +9 -0
- package/lib/code-intelligence/edit-anchor.mjs +102 -0
- package/lib/code-intelligence/index.mjs +95 -0
- package/lib/code-intelligence/lsp-provider.mjs +189 -0
- package/lib/completion-auditor.mjs +82 -0
- package/lib/context-engine-v11.mjs +65 -1
- package/lib/context-graph-rank.mjs +118 -0
- package/lib/context-manifest.mjs +97 -18
- package/lib/control-center.mjs +19 -1
- package/lib/document-ingestion.mjs +60 -0
- package/lib/dynamic-workflow.mjs +3 -1
- package/lib/evidence-store.mjs +82 -1
- package/lib/fast-verification-gate.mjs +69 -0
- package/lib/hierarchical-context.mjs +215 -0
- package/lib/mcp-health.mjs +144 -0
- package/lib/mcp-tool-policy.mjs +19 -0
- package/lib/memory-engine.mjs +493 -0
- package/lib/model-performance.mjs +33 -8
- package/lib/model-policy.mjs +3 -3
- package/lib/orchestrator-policy.mjs +5 -209
- package/lib/performance-fabric.mjs +229 -0
- package/lib/pi-rpc-pool.mjs +433 -0
- package/lib/process-hang-detector.mjs +83 -0
- package/lib/process-supervisor.mjs +193 -0
- package/lib/prompt-cache.mjs +27 -11
- package/lib/repo-graph.mjs +53 -2
- package/lib/reversible-context.mjs +49 -0
- package/lib/runtime-config.mjs +31 -0
- package/lib/safety.mjs +132 -0
- package/lib/semantic-index.mjs +52 -3
- package/lib/skill-compiler.mjs +128 -0
- package/lib/skill-quality.mjs +48 -2
- package/lib/task-engine.mjs +62 -5
- package/lib/task-policy.mjs +291 -0
- package/lib/verification-broker.mjs +284 -0
- package/lib/verification-command.mjs +111 -0
- package/lib/windows-shim.mjs +35 -0
- package/lib/workspace-fingerprint.mjs +198 -0
- package/package.json +52 -42
- package/pi/extensions/ues-child-runtime.ts +393 -0
- package/pi/extensions/ues.ts +3327 -0
- package/pi/prompts/ues-audit.md +9 -0
- package/pi/prompts/ues-critique.md +9 -0
- package/pi/prompts/ues-debug.md +9 -0
- package/pi/prompts/ues-feature.md +9 -0
- package/pi/prompts/ues-fix.md +9 -0
- package/pi/prompts/ues-plan.md +9 -0
- package/pi/prompts/ues-research.md +9 -0
- package/pi/prompts/ues-resume.md +9 -0
- package/pi/prompts/ues-review.md +7 -0
- package/pi/prompts/ues-run.md +17 -0
- package/pi/prompts/ues-verify.md +9 -0
- package/scripts/check-release-consistency.mjs +119 -185
- package/scripts/check-runtime-exports.mjs +66 -0
- package/scripts/check-source-integrity.mjs +246 -0
- package/scripts/eval-pi.mjs +492 -0
- package/scripts/install.mjs +16 -0
- package/scripts/smoke-package-closure.mjs +110 -0
- package/scripts/smoke-packed-install.mjs +24 -11
- package/scripts/smoke-pi-extension.mjs +144 -0
- package/scripts/uninstall.mjs +44 -0
- package/CHANGELOG.md +0 -415
- package/docs/DETERMINISTIC-TOOLS.md +0 -105
- package/docs/ENGINEERING-DESIGN.md +0 -194
- package/docs/EVALS.md +0 -158
- package/docs/GITHUB-RULESET.md +0 -50
- package/docs/NPM-PUBLISH.md +0 -116
- package/docs/RESEARCH-SOURCES.md +0 -37
- package/docs/TRACE-SCHEMA.md +0 -122
- package/docs/V11-PERCEPTION-ADAPTIVE-EXECUTION.md +0 -75
- package/docs/V11-PERCEPTION-ADAPTIVE.md +0 -220
- package/docs/V12-WEAK-MODEL-INTELLIGENCE.md +0 -27
- package/docs/V13-PARALLEL-WEAK-MODEL-RUNTIME.md +0 -86
- package/docs/V7-INTELLIGENCE-RUNTIME.md +0 -166
- package/docs/V8-INTELLIGENCE-RELIABILITY.md +0 -206
- package/docs/V9-SPEED-INTELLIGENCE.md +0 -102
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"
|
|
3
|
+
import os from "node:os"
|
|
4
|
+
import path from "node:path"
|
|
5
|
+
import { spawnSync } from "node:child_process"
|
|
6
|
+
import { fileURLToPath } from "node:url"
|
|
7
|
+
import { evalModeOrder } from "../lib/eval-order.mjs"
|
|
8
|
+
import { runProcess } from "../lib/process-runner.mjs"
|
|
9
|
+
import { snapshotWorkspace, diffWorkspaceSnapshots } from "../lib/workspace-snapshot.mjs"
|
|
10
|
+
import { resolveManagedPiCommand, resolveWindowsCommand } from "../lib/windows-shim.mjs"
|
|
11
|
+
import { summarizeEvalResults } from "../lib/eval-report.mjs"
|
|
12
|
+
import { pairedBenchmarkConfidence } from "../lib/benchmark-confidence.mjs"
|
|
13
|
+
|
|
14
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
15
|
+
const args = process.argv.slice(2)
|
|
16
|
+
const abortController = new AbortController()
|
|
17
|
+
let interrupted = false
|
|
18
|
+
|
|
19
|
+
process.once("SIGINT", () => {
|
|
20
|
+
interrupted = true
|
|
21
|
+
console.warn("\n[pi-eval] interrupt requested; stopping the active Pi process tree...")
|
|
22
|
+
abortController.abort()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
function argValue(name, fallback = null) {
|
|
26
|
+
const index = args.indexOf(name)
|
|
27
|
+
return index >= 0 && index + 1 < args.length ? args[index + 1] : fallback
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function hasArg(name) {
|
|
31
|
+
return args.includes(name)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function argValues(name) {
|
|
35
|
+
const values = []
|
|
36
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
37
|
+
if (args[index] === name && index + 1 < args.length) {
|
|
38
|
+
values.push(args[index + 1])
|
|
39
|
+
index += 1
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return values
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function defaultProviderExtensions(modelRef) {
|
|
46
|
+
const provider = String(modelRef || "").split("/", 1)[0].toLowerCase()
|
|
47
|
+
if (provider !== "kilo") return []
|
|
48
|
+
|
|
49
|
+
const installed = path.join(
|
|
50
|
+
os.homedir(),
|
|
51
|
+
".pi",
|
|
52
|
+
"agent",
|
|
53
|
+
"git",
|
|
54
|
+
"github.com",
|
|
55
|
+
"Kilo-Org",
|
|
56
|
+
"kilo-pi-provider",
|
|
57
|
+
)
|
|
58
|
+
if (existsSync(installed)) return [installed]
|
|
59
|
+
return ["git:github.com/Kilo-Org/kilo-pi-provider"]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function positiveInt(value, fallback) {
|
|
63
|
+
const parsed = Number.parseInt(value ?? "", 10)
|
|
64
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function excerpt(value, limit = 16000) {
|
|
68
|
+
const text = String(value || "")
|
|
69
|
+
return text.length <= limit ? text : text.slice(0, limit) + "\n...[truncated]"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function resolveCommand(executable) {
|
|
73
|
+
if (process.platform !== "win32") return { executable, argsPrefix: [] }
|
|
74
|
+
const resolved = resolveWindowsCommand(executable)
|
|
75
|
+
if (resolved) return resolved
|
|
76
|
+
if (String(executable).toLowerCase() === "pi") return resolveManagedPiCommand()
|
|
77
|
+
return null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function runSync(executable, commandArgs, options = {}) {
|
|
81
|
+
const resolved = resolveCommand(executable)
|
|
82
|
+
if (!resolved) {
|
|
83
|
+
return {
|
|
84
|
+
status: 127,
|
|
85
|
+
stdout: "",
|
|
86
|
+
stderr: "No safely executable command found for: " + executable,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return spawnSync(resolved.executable, [...resolved.argsPrefix, ...commandArgs], options)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function runAsync(executable, commandArgs, options = {}) {
|
|
93
|
+
const resolved = resolveCommand(executable)
|
|
94
|
+
if (!resolved) {
|
|
95
|
+
return Promise.resolve({
|
|
96
|
+
status: 127,
|
|
97
|
+
signal: null,
|
|
98
|
+
stdout: "",
|
|
99
|
+
stderr: "No safely executable command found for: " + executable,
|
|
100
|
+
durationMs: 0,
|
|
101
|
+
timedOut: false,
|
|
102
|
+
idleTimedOut: false,
|
|
103
|
+
aborted: false,
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
return runProcess(resolved.executable, [...resolved.argsPrefix, ...commandArgs], options)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function zeroUsage() {
|
|
110
|
+
return {
|
|
111
|
+
input: 0,
|
|
112
|
+
output: 0,
|
|
113
|
+
cacheRead: 0,
|
|
114
|
+
cacheWrite: 0,
|
|
115
|
+
totalTokens: 0,
|
|
116
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function addUsage(target, usage) {
|
|
121
|
+
if (!usage || typeof usage !== "object") return target
|
|
122
|
+
for (const key of ["input", "output", "cacheRead", "cacheWrite", "totalTokens"]) {
|
|
123
|
+
target[key] += Number(usage[key] || 0)
|
|
124
|
+
}
|
|
125
|
+
const cost = usage.cost || {}
|
|
126
|
+
for (const key of ["input", "output", "cacheRead", "cacheWrite", "total"]) {
|
|
127
|
+
target.cost[key] += Number(cost[key] || 0)
|
|
128
|
+
}
|
|
129
|
+
return target
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function parsePiTelemetry(stdout) {
|
|
133
|
+
const parentUsage = zeroUsage()
|
|
134
|
+
const childUsage = zeroUsage()
|
|
135
|
+
const toolNames = {}
|
|
136
|
+
let parentUsageSamples = 0
|
|
137
|
+
let childUsageSamples = 0
|
|
138
|
+
let parentToolCalls = 0
|
|
139
|
+
let childToolCalls = 0
|
|
140
|
+
let controllerUsed = false
|
|
141
|
+
let controllerPass = false
|
|
142
|
+
let finalAssistant = ""
|
|
143
|
+
let jsonLines = 0
|
|
144
|
+
const turbo = {
|
|
145
|
+
routedAgentSteps: 0,
|
|
146
|
+
rpcRuns: 0,
|
|
147
|
+
cliRuns: 0,
|
|
148
|
+
warmWorkerReuses: 0,
|
|
149
|
+
contextCacheHits: 0,
|
|
150
|
+
microSkillCacheHits: 0,
|
|
151
|
+
affectedTestCacheHits: 0,
|
|
152
|
+
reusableVerificationReceipts: 0,
|
|
153
|
+
compactedToolOutputSteps: 0,
|
|
154
|
+
adaptiveContextChars: 0,
|
|
155
|
+
baseContextChars: 0,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
for (const line of String(stdout || "").split(/\r?\n/)) {
|
|
159
|
+
if (!line.trim()) continue
|
|
160
|
+
let event
|
|
161
|
+
try {
|
|
162
|
+
event = JSON.parse(line)
|
|
163
|
+
} catch {
|
|
164
|
+
continue
|
|
165
|
+
}
|
|
166
|
+
jsonLines += 1
|
|
167
|
+
|
|
168
|
+
if (event.type === "tool_execution_start") {
|
|
169
|
+
parentToolCalls += 1
|
|
170
|
+
const name = String(event.toolName || "unknown")
|
|
171
|
+
toolNames[name] = Number(toolNames[name] || 0) + 1
|
|
172
|
+
if (name === "ues_execute") controllerUsed = true
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (event.type === "message_end" && event.message?.role === "assistant") {
|
|
176
|
+
if (event.message.usage) {
|
|
177
|
+
addUsage(parentUsage, event.message.usage)
|
|
178
|
+
parentUsageSamples += 1
|
|
179
|
+
}
|
|
180
|
+
if (Array.isArray(event.message.content)) {
|
|
181
|
+
const text = event.message.content
|
|
182
|
+
.filter((part) => part?.type === "text" && typeof part.text === "string")
|
|
183
|
+
.map((part) => part.text)
|
|
184
|
+
.join("\n")
|
|
185
|
+
if (text) finalAssistant = text
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (event.type === "tool_execution_end" && event.toolName === "ues_execute") {
|
|
190
|
+
const details = event.result?.details
|
|
191
|
+
const steps = Array.isArray(details?.steps) ? details.steps : []
|
|
192
|
+
for (const step of steps) {
|
|
193
|
+
const optimization = step?.optimizations
|
|
194
|
+
if (optimization && typeof optimization === "object") {
|
|
195
|
+
turbo.routedAgentSteps += 1
|
|
196
|
+
if (optimization.childRuntime === "rpc") turbo.rpcRuns += 1
|
|
197
|
+
if (optimization.childRuntime === "cli") turbo.cliRuns += 1
|
|
198
|
+
if (optimization.warmWorkerReused === true) turbo.warmWorkerReuses += 1
|
|
199
|
+
if (optimization.contextCacheHit === true) turbo.contextCacheHits += 1
|
|
200
|
+
if (optimization.microSkillCacheHit === true) turbo.microSkillCacheHits += 1
|
|
201
|
+
if (optimization.affectedTestCacheHit === true) turbo.affectedTestCacheHits += 1
|
|
202
|
+
turbo.reusableVerificationReceipts += Number(optimization.reusableVerificationReceipts || 0)
|
|
203
|
+
if (optimization.compactToolOutput === true) turbo.compactedToolOutputSteps += 1
|
|
204
|
+
turbo.adaptiveContextChars += Number(optimization.runtimeContextBudget || 0)
|
|
205
|
+
turbo.baseContextChars += Number(optimization.baseContextBudget || 0)
|
|
206
|
+
}
|
|
207
|
+
if (step?.usage) {
|
|
208
|
+
addUsage(childUsage, step.usage)
|
|
209
|
+
childUsageSamples += 1
|
|
210
|
+
}
|
|
211
|
+
childToolCalls += Number(step?.toolCalls || 0)
|
|
212
|
+
for (const name of step?.toolNames || []) {
|
|
213
|
+
const key = "child:" + String(name)
|
|
214
|
+
toolNames[key] = Number(toolNames[key] || 0) + 1
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (event.isError !== true && details?.mode === "execute") controllerPass = true
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const totalUsage = zeroUsage()
|
|
222
|
+
addUsage(totalUsage, parentUsage)
|
|
223
|
+
addUsage(totalUsage, childUsage)
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
schemaVersion: 1,
|
|
227
|
+
runtime: "pi",
|
|
228
|
+
jsonLines,
|
|
229
|
+
toolCalls: parentToolCalls + childToolCalls,
|
|
230
|
+
parentToolCalls,
|
|
231
|
+
childToolCalls,
|
|
232
|
+
toolNames,
|
|
233
|
+
usageSamples: parentUsageSamples + childUsageSamples,
|
|
234
|
+
parentUsageSamples,
|
|
235
|
+
childUsageSamples,
|
|
236
|
+
tokens: {
|
|
237
|
+
input: totalUsage.input,
|
|
238
|
+
output: totalUsage.output,
|
|
239
|
+
cacheRead: totalUsage.cacheRead,
|
|
240
|
+
cacheWrite: totalUsage.cacheWrite,
|
|
241
|
+
total: totalUsage.totalTokens,
|
|
242
|
+
},
|
|
243
|
+
costSamples: parentUsageSamples + childUsageSamples,
|
|
244
|
+
cost: totalUsage.cost.total,
|
|
245
|
+
parentUsage,
|
|
246
|
+
childUsage,
|
|
247
|
+
controllerUsed,
|
|
248
|
+
controllerPass,
|
|
249
|
+
finalAssistant,
|
|
250
|
+
turbo: {
|
|
251
|
+
...turbo,
|
|
252
|
+
warmWorkerReuseRate: turbo.rpcRuns > 0 ? turbo.warmWorkerReuses / turbo.rpcRuns : null,
|
|
253
|
+
contextCacheHitRate: turbo.routedAgentSteps > 0 ? turbo.contextCacheHits / turbo.routedAgentSteps : null,
|
|
254
|
+
adaptiveContextRatio:
|
|
255
|
+
turbo.baseContextChars > 0 ? turbo.adaptiveContextChars / turbo.baseContextChars : null,
|
|
256
|
+
},
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function safeName(value) {
|
|
261
|
+
return String(value || "model").replace(/[^a-z0-9._-]+/gi, "_").slice(0, 120)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const piCommand = argValue("--pi-command", process.env.UES_PI_COMMAND || "pi")
|
|
265
|
+
const model = argValue("--model", process.env.UES_EVAL_MODEL)
|
|
266
|
+
const thinking = argValue("--thinking", argValue("--variant", process.env.UES_EVAL_VARIANT))
|
|
267
|
+
const trials = Math.min(positiveInt(argValue("--trials"), 1), 20)
|
|
268
|
+
const taskFilter = argValue("--task")
|
|
269
|
+
const requestedMode = argValue("--mode", "both")
|
|
270
|
+
const suiteName = argValue("--suite", "live")
|
|
271
|
+
const keep = hasArg("--keep")
|
|
272
|
+
const timeoutMs = positiveInt(argValue("--timeout-ms"), 20 * 60_000)
|
|
273
|
+
const idleTimeoutMs = positiveInt(argValue("--idle-timeout-ms"), 7 * 60_000)
|
|
274
|
+
const heartbeatMs = positiveInt(argValue("--heartbeat-ms"), 30_000)
|
|
275
|
+
const explicitProviderExtensions = argValues("--provider-extension")
|
|
276
|
+
const providerExtensions = explicitProviderExtensions.length
|
|
277
|
+
? explicitProviderExtensions
|
|
278
|
+
: defaultProviderExtensions(model)
|
|
279
|
+
const suiteRoot = path.join(root, "evals", suiteName)
|
|
280
|
+
|
|
281
|
+
if (!model) {
|
|
282
|
+
console.error("Usage: node scripts/eval-pi.mjs --model provider/model [--pi-command path-or-command] [--provider-extension path|npm:spec|git:spec] [--thinking off|minimal|low|medium|high|xhigh] [--suite live] [--trials N] [--task id] [--mode baseline|ues|both] [--keep]")
|
|
283
|
+
process.exit(2)
|
|
284
|
+
}
|
|
285
|
+
if (!["baseline", "ues", "both"].includes(requestedMode)) {
|
|
286
|
+
console.error("--mode must be baseline, ues, or both")
|
|
287
|
+
process.exit(2)
|
|
288
|
+
}
|
|
289
|
+
if (!existsSync(path.join(suiteRoot, "tasks.json"))) {
|
|
290
|
+
console.error("Unknown eval suite or missing tasks.json: " + suiteName)
|
|
291
|
+
process.exit(2)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const probe = runSync(piCommand, ["--version"], { encoding: "utf8" })
|
|
295
|
+
if (probe.status !== 0) {
|
|
296
|
+
console.error("Pi CLI is required for Pi-native evals. Checked command: " + piCommand)
|
|
297
|
+
console.error(excerpt(probe.stderr || probe.stdout))
|
|
298
|
+
process.exit(2)
|
|
299
|
+
}
|
|
300
|
+
const piVersion = String(probe.stdout || probe.stderr || "").trim()
|
|
301
|
+
|
|
302
|
+
const suite = JSON.parse(await readFile(path.join(suiteRoot, "tasks.json"), "utf8"))
|
|
303
|
+
let tasks = suite.tasks || []
|
|
304
|
+
if (taskFilter) tasks = tasks.filter((task) => task.id === taskFilter)
|
|
305
|
+
if (tasks.length === 0) {
|
|
306
|
+
console.error(taskFilter ? "Unknown task: " + taskFilter : "No eval tasks found.")
|
|
307
|
+
process.exit(2)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const invocationDir = process.cwd()
|
|
311
|
+
const runRoot = await mkdtemp(path.join(os.tmpdir(), "ues-pi-eval-"))
|
|
312
|
+
const resultDir = path.resolve(argValue("--output-dir", path.join(invocationDir, ".ues-evals")))
|
|
313
|
+
await mkdir(resultDir, { recursive: true })
|
|
314
|
+
|
|
315
|
+
const results = []
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
for (const [taskIndex, task] of tasks.entries()) {
|
|
319
|
+
if (interrupted) break
|
|
320
|
+
for (let trial = 1; trial <= trials; trial += 1) {
|
|
321
|
+
if (interrupted) break
|
|
322
|
+
const trialModes = evalModeOrder(requestedMode, taskIndex, trial)
|
|
323
|
+
|
|
324
|
+
for (const mode of trialModes) {
|
|
325
|
+
if (interrupted) break
|
|
326
|
+
|
|
327
|
+
const isolatedRoot = path.join(runRoot, task.id + "-" + mode + "-" + trial)
|
|
328
|
+
const workspace = path.join(isolatedRoot, "workspace")
|
|
329
|
+
const uesConfigDir = path.join(isolatedRoot, "ues-config")
|
|
330
|
+
await mkdir(uesConfigDir, { recursive: true })
|
|
331
|
+
await cp(path.join(suiteRoot, task.fixture), workspace, { recursive: true })
|
|
332
|
+
|
|
333
|
+
const beforeSnapshot = await snapshotWorkspace(workspace)
|
|
334
|
+
const childEnv = {
|
|
335
|
+
...process.env,
|
|
336
|
+
UES_CONFIG_DIR: uesConfigDir,
|
|
337
|
+
PI_SKIP_VERSION_CHECK: "true",
|
|
338
|
+
NO_COLOR: "1",
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const piArgs = [
|
|
342
|
+
"--mode", "json",
|
|
343
|
+
"-p",
|
|
344
|
+
"--no-session",
|
|
345
|
+
"--no-extensions",
|
|
346
|
+
"--no-skills",
|
|
347
|
+
"--no-prompt-templates",
|
|
348
|
+
"--no-context-files",
|
|
349
|
+
"--model", model,
|
|
350
|
+
]
|
|
351
|
+
if (thinking) piArgs.push("--thinking", thinking)
|
|
352
|
+
for (const extension of providerExtensions) {
|
|
353
|
+
piArgs.push("--extension", extension)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
let prompt = task.prompt
|
|
357
|
+
if (mode === "ues") {
|
|
358
|
+
piArgs.push("--extension", path.join(root, "pi", "extensions", "ues.ts"))
|
|
359
|
+
prompt = [
|
|
360
|
+
"Use the UES runtime controller for this benchmark task.",
|
|
361
|
+
"You MUST call the ues_execute tool exactly once with the complete engineering task below and the current working directory.",
|
|
362
|
+
"Do not edit project files directly before that tool call. After the tool returns, report its verified result concisely.",
|
|
363
|
+
"",
|
|
364
|
+
"Engineering task:",
|
|
365
|
+
task.prompt,
|
|
366
|
+
].join("\n")
|
|
367
|
+
}
|
|
368
|
+
piArgs.push(prompt)
|
|
369
|
+
|
|
370
|
+
console.log(
|
|
371
|
+
"[" + mode + "] " + task.id + " trial " + trial + ": starting Pi " + piVersion +
|
|
372
|
+
(providerExtensions.length ? " with provider extension isolation" : ""),
|
|
373
|
+
)
|
|
374
|
+
const agentRun = await runAsync(piCommand, piArgs, {
|
|
375
|
+
cwd: workspace,
|
|
376
|
+
env: childEnv,
|
|
377
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
378
|
+
heartbeatMs,
|
|
379
|
+
timeoutMs,
|
|
380
|
+
idleTimeoutMs,
|
|
381
|
+
signal: abortController.signal,
|
|
382
|
+
onHeartbeat: ({ elapsedMs, idleMs }) => {
|
|
383
|
+
console.log(
|
|
384
|
+
"[" + mode + "] " + task.id + " trial " + trial +
|
|
385
|
+
": still running (" + Math.round(elapsedMs / 1000) + "s elapsed, " +
|
|
386
|
+
Math.round(idleMs / 1000) + "s since output)",
|
|
387
|
+
)
|
|
388
|
+
},
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
const graderPath = path.join(suiteRoot, task.grader)
|
|
392
|
+
const graderRun = spawnSync(process.execPath, [graderPath], {
|
|
393
|
+
cwd: workspace,
|
|
394
|
+
env: { ...childEnv, UES_EVAL_WORKSPACE: workspace, UES_EVAL_TASK: task.id },
|
|
395
|
+
encoding: "utf8",
|
|
396
|
+
maxBuffer: 1024 * 1024,
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
const afterSnapshot = await snapshotWorkspace(workspace)
|
|
400
|
+
const changedFiles = diffWorkspaceSnapshots(beforeSnapshot, afterSnapshot)
|
|
401
|
+
const telemetry = parsePiTelemetry(agentRun.stdout)
|
|
402
|
+
const baselineIsolated = mode !== "baseline" || telemetry.controllerUsed === false
|
|
403
|
+
const controllerValid =
|
|
404
|
+
mode === "baseline"
|
|
405
|
+
? baselineIsolated
|
|
406
|
+
: (telemetry.controllerUsed && telemetry.controllerPass)
|
|
407
|
+
const passed = agentRun.status === 0 && graderRun.status === 0 && controllerValid
|
|
408
|
+
|
|
409
|
+
results.push({
|
|
410
|
+
task: task.id,
|
|
411
|
+
mode,
|
|
412
|
+
runtime: "pi",
|
|
413
|
+
model,
|
|
414
|
+
variant: thinking || null,
|
|
415
|
+
trial,
|
|
416
|
+
passed,
|
|
417
|
+
agentExit: agentRun.status,
|
|
418
|
+
graderExit: graderRun.status,
|
|
419
|
+
controllerValid,
|
|
420
|
+
baselineIsolated,
|
|
421
|
+
durationMs: agentRun.durationMs,
|
|
422
|
+
timedOut: agentRun.timedOut,
|
|
423
|
+
idleTimedOut: agentRun.idleTimedOut,
|
|
424
|
+
aborted: agentRun.aborted,
|
|
425
|
+
piVersion,
|
|
426
|
+
telemetry,
|
|
427
|
+
changedFiles,
|
|
428
|
+
agentStdout: excerpt(agentRun.stdout),
|
|
429
|
+
agentStderr: excerpt(agentRun.stderr),
|
|
430
|
+
graderStdout: excerpt(graderRun.stdout),
|
|
431
|
+
graderStderr: excerpt(graderRun.stderr),
|
|
432
|
+
workspace: keep ? workspace : null,
|
|
433
|
+
timestamp: new Date().toISOString(),
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
console.log(
|
|
437
|
+
"[" + mode + "] " + task.id + " trial " + trial + ": " +
|
|
438
|
+
(passed ? "PASS" : "FAIL") +
|
|
439
|
+
" (agent=" + agentRun.status + ", grader=" + graderRun.status +
|
|
440
|
+
", controller=" + controllerValid +
|
|
441
|
+
(mode === "baseline" ? ", isolated=" + baselineIsolated : "") + ")",
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const summary = summarizeEvalResults(results)
|
|
448
|
+
const confidence = requestedMode === "both"
|
|
449
|
+
? pairedBenchmarkConfidence(results, {
|
|
450
|
+
minPairs: positiveInt(argValue("--min-pairs"), 20),
|
|
451
|
+
suiteRegressionTolerance: Number(argValue("--suite-regression-tolerance", "0")),
|
|
452
|
+
qualityRegressionTolerance: Number(argValue("--quality-regression-tolerance", "0")),
|
|
453
|
+
maxDurationRatio: Number(argValue("--max-duration-ratio", "1.75")),
|
|
454
|
+
maxInitialInputRatio: Number(argValue("--max-initial-input-ratio", "1.5")),
|
|
455
|
+
maxTokenRatio: Number(argValue("--max-token-ratio", "1.75")),
|
|
456
|
+
})
|
|
457
|
+
: null
|
|
458
|
+
const payload = {
|
|
459
|
+
schemaVersion: 1,
|
|
460
|
+
runtime: "pi",
|
|
461
|
+
suite: suiteName,
|
|
462
|
+
model,
|
|
463
|
+
variant: thinking || null,
|
|
464
|
+
trials,
|
|
465
|
+
requestedMode,
|
|
466
|
+
piVersion,
|
|
467
|
+
providerExtensionCount: providerExtensions.length,
|
|
468
|
+
providerExtensionSource: explicitProviderExtensions.length ? "explicit" : (providerExtensions.length ? "auto" : "none"),
|
|
469
|
+
interrupted,
|
|
470
|
+
generatedAt: new Date().toISOString(),
|
|
471
|
+
summary,
|
|
472
|
+
confidence,
|
|
473
|
+
results,
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-")
|
|
477
|
+
const outputFile = path.join(
|
|
478
|
+
resultDir,
|
|
479
|
+
"pi-" + safeName(suiteName) + "-" + safeName(model) + "-" + stamp + ".json",
|
|
480
|
+
)
|
|
481
|
+
await writeFile(outputFile, JSON.stringify(payload, null, 2) + "\n", "utf8")
|
|
482
|
+
|
|
483
|
+
console.log("\nPi-native eval summary")
|
|
484
|
+
console.log(JSON.stringify(summary, null, 2))
|
|
485
|
+
if (confidence) {
|
|
486
|
+
console.log("\nPi-native paired confidence")
|
|
487
|
+
console.log(JSON.stringify(confidence, null, 2))
|
|
488
|
+
}
|
|
489
|
+
console.log("Result file: " + outputFile)
|
|
490
|
+
} finally {
|
|
491
|
+
if (!keep) await rm(runRoot, { recursive: true, force: true }).catch(() => {})
|
|
492
|
+
}
|
package/scripts/install.mjs
CHANGED
|
@@ -5,6 +5,16 @@ import { installResources } from "../lib/installer.mjs"
|
|
|
5
5
|
|
|
6
6
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
7
7
|
|
|
8
|
+
function isPiPackageInstall() {
|
|
9
|
+
const normalized = packageRoot.replaceAll("\\", "/").toLowerCase()
|
|
10
|
+
return [
|
|
11
|
+
"/.pi/agent/git/",
|
|
12
|
+
"/.pi/agent/npm/",
|
|
13
|
+
"/.pi/git/",
|
|
14
|
+
"/.pi/npm/",
|
|
15
|
+
].some((marker) => normalized.includes(marker))
|
|
16
|
+
}
|
|
17
|
+
|
|
8
18
|
function npmGlobalPrefix() {
|
|
9
19
|
if (process.platform === "win32") {
|
|
10
20
|
const result = spawnSync(
|
|
@@ -30,6 +40,12 @@ function isGlobalInstall() {
|
|
|
30
40
|
return packageRoot.toLowerCase().startsWith(path.resolve(prefix).toLowerCase())
|
|
31
41
|
}
|
|
32
42
|
|
|
43
|
+
if (isPiPackageInstall()) {
|
|
44
|
+
console.log("[ocskill] Pi package install detected; Pi will load UES from the package manifest.")
|
|
45
|
+
console.log("[ocskill] Skipping OpenCode global resource setup.")
|
|
46
|
+
process.exit(0)
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
if (!isGlobalInstall()) {
|
|
34
50
|
console.log("[ocskill] Local npm install detected; skipping OpenCode global setup.")
|
|
35
51
|
console.log("[ocskill] Use 'node bin/ocskill.mjs install' to test installation manually.")
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import assert from "node:assert/strict"
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import { spawnSync } from "node:child_process"
|
|
5
|
+
import { fileURLToPath } from "node:url"
|
|
6
|
+
import { resolveWindowsCommand } from "../lib/windows-shim.mjs"
|
|
7
|
+
|
|
8
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
9
|
+
|
|
10
|
+
function run(executable, args, options = {}) {
|
|
11
|
+
if (process.platform !== "win32") return spawnSync(executable, args, options)
|
|
12
|
+
const resolved = resolveWindowsCommand(executable)
|
|
13
|
+
if (!resolved) {
|
|
14
|
+
return { status: 127, stdout: "", stderr: "Unable to resolve Windows command: " + executable }
|
|
15
|
+
}
|
|
16
|
+
return spawnSync(resolved.executable, [...resolved.argsPrefix, ...args], options)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseJsonOutput(result, label) {
|
|
20
|
+
assert.equal(result.status, 0, label + " failed: " + String(result.stderr || result.stdout || ""))
|
|
21
|
+
return JSON.parse(String(result.stdout || "").trim())
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const packed = parseJsonOutput(
|
|
25
|
+
run("npm", ["pack", "--dry-run", "--json", "--ignore-scripts"], {
|
|
26
|
+
cwd: root,
|
|
27
|
+
encoding: "utf8",
|
|
28
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
29
|
+
}),
|
|
30
|
+
"npm pack --dry-run",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
assert.ok(Array.isArray(packed) && packed.length >= 1, "npm pack did not return package metadata")
|
|
34
|
+
const files = new Set((packed[0].files || []).map((entry) => String(entry.path).replaceAll("\\", "/")))
|
|
35
|
+
|
|
36
|
+
for (const required of [
|
|
37
|
+
"bin/ocskill.mjs",
|
|
38
|
+
"pi/extensions/ues.ts",
|
|
39
|
+
"pi/extensions/ues-child-runtime.ts",
|
|
40
|
+
"docs/V14.2-TURBO-WEAK-MODEL-RUNTIME.md",
|
|
41
|
+
"lib/process-supervisor.mjs",
|
|
42
|
+
"lib/pi-rpc-pool.mjs",
|
|
43
|
+
"lib/adaptive-context-budget.mjs",
|
|
44
|
+
"lib/skill-compiler.mjs",
|
|
45
|
+
"lib/affected-tests.mjs",
|
|
46
|
+
"lib/verification-broker.mjs",
|
|
47
|
+
"lib/verification-command.mjs",
|
|
48
|
+
"lib/task-policy.mjs",
|
|
49
|
+
"lib/model-policy.mjs",
|
|
50
|
+
"lib/context-engine-v11.mjs",
|
|
51
|
+
"lib/runtime-config.mjs",
|
|
52
|
+
"scripts/eval-pi.mjs",
|
|
53
|
+
"scripts/eval-report.mjs",
|
|
54
|
+
"evals/live/tasks.json",
|
|
55
|
+
"global-config/AGENTS.md",
|
|
56
|
+
"global-config/commands/run.md",
|
|
57
|
+
"global-config/plugins/ues-router/index.js",
|
|
58
|
+
]) {
|
|
59
|
+
assert.ok(files.has(required), "packed package is missing required runtime file: " + required)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const controllerStat = (packed[0].files || []).find((entry) => String(entry.path).replaceAll("\\", "/") === "pi/extensions/ues.ts")
|
|
63
|
+
assert.ok(Number(controllerStat?.size || 0) > 80_000, "packed UES controller appears truncated")
|
|
64
|
+
|
|
65
|
+
const childRuntimeStat = (packed[0].files || []).find((entry) => String(entry.path).replaceAll("\\", "/") === "pi/extensions/ues-child-runtime.ts")
|
|
66
|
+
assert.ok(Number(childRuntimeStat?.size || 0) > 6_000, "packed UES child runtime appears truncated")
|
|
67
|
+
|
|
68
|
+
const codeFiles = [...files].filter((file) => /\.(?:mjs|js|ts)$/.test(file))
|
|
69
|
+
const missingRelativeImports = []
|
|
70
|
+
|
|
71
|
+
function resolvePackedImport(fromFile, specifier) {
|
|
72
|
+
const base = path.posix.normalize(path.posix.join(path.posix.dirname(fromFile), specifier))
|
|
73
|
+
const candidates = path.posix.extname(base)
|
|
74
|
+
? [base]
|
|
75
|
+
: [base, base + ".mjs", base + ".js", base + ".ts", path.posix.join(base, "index.mjs"), path.posix.join(base, "index.js")]
|
|
76
|
+
return candidates.find((candidate) => existsSync(path.join(root, ...candidate.split("/")))) || null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for (const file of codeFiles) {
|
|
80
|
+
const absolute = path.join(root, ...file.split("/"))
|
|
81
|
+
if (!existsSync(absolute)) continue
|
|
82
|
+
const source = readFileSync(absolute, "utf8")
|
|
83
|
+
const regex = /(?:\bfrom\s+|\bimport\s*\(\s*|\bimport\s+)[\"'](\.{1,2}\/[^\"']+)[\"']/g
|
|
84
|
+
let match
|
|
85
|
+
while ((match = regex.exec(source))) {
|
|
86
|
+
const resolved = resolvePackedImport(file, match[1])
|
|
87
|
+
if (resolved && !files.has(resolved)) {
|
|
88
|
+
missingRelativeImports.push({ from: file, import: match[1], resolved })
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
assert.deepEqual(
|
|
94
|
+
missingRelativeImports,
|
|
95
|
+
[],
|
|
96
|
+
"packed runtime has relative imports to files excluded from npm package: " + JSON.stringify(missingRelativeImports),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
const taskPolicy = parseJsonOutput(
|
|
100
|
+
run(process.execPath, [path.join(root, "bin", "ocskill.mjs"), "task-policy", "fix a high-risk authentication bug", "--json"], {
|
|
101
|
+
cwd: root,
|
|
102
|
+
encoding: "utf8",
|
|
103
|
+
maxBuffer: 1024 * 1024,
|
|
104
|
+
}),
|
|
105
|
+
"packaged task-policy smoke",
|
|
106
|
+
)
|
|
107
|
+
assert.equal(taskPolicy.risk, "high")
|
|
108
|
+
assert.equal(taskPolicy.modelTier, "heavy")
|
|
109
|
+
|
|
110
|
+
console.log(`Package closure smoke passed: ${files.size} packed file(s), ${codeFiles.length} code file(s) checked`)
|
|
@@ -38,6 +38,7 @@ const temp = await mkdtemp(path.join(os.tmpdir(), "ocskill-pack-smoke-"))
|
|
|
38
38
|
const packDir = path.join(temp, "pack")
|
|
39
39
|
const prefix = path.join(temp, "prefix")
|
|
40
40
|
const configDir = path.join(temp, "opencode")
|
|
41
|
+
const uesConfigDir = path.join(temp, "ues-config")
|
|
41
42
|
|
|
42
43
|
try {
|
|
43
44
|
await mkdir(packDir, { recursive: true })
|
|
@@ -49,19 +50,13 @@ try {
|
|
|
49
50
|
assert.equal(tarballs.length, 1, "expected exactly one packed tarball")
|
|
50
51
|
const tarball = path.join(packDir, tarballs[0])
|
|
51
52
|
|
|
52
|
-
const
|
|
53
|
-
requireSuccess(npmVersion, "npm --version")
|
|
54
|
-
const npmMajor = Number.parseInt(npmVersion.stdout.trim().split(".")[0], 10)
|
|
55
|
-
const installArgs = ["install", "-g", tarball, "--prefix", prefix]
|
|
56
|
-
if (Number.isFinite(npmMajor) && npmMajor >= 11) {
|
|
57
|
-
installArgs.push(`--allow-scripts=${packageName}`)
|
|
58
|
-
}
|
|
53
|
+
const installArgs = ["install", "-g", tarball, "--prefix", prefix, "--ignore-scripts"]
|
|
59
54
|
|
|
60
55
|
const install = runNpm(installArgs, {
|
|
61
56
|
cwd: temp,
|
|
62
|
-
env:
|
|
57
|
+
env: process.env,
|
|
63
58
|
})
|
|
64
|
-
requireSuccess(install, "packed global install
|
|
59
|
+
requireSuccess(install, "packed global install")
|
|
65
60
|
|
|
66
61
|
const root = runNpm(["root", "-g", "--prefix", prefix], { cwd: temp })
|
|
67
62
|
requireSuccess(root, "npm root -g")
|
|
@@ -81,7 +76,19 @@ try {
|
|
|
81
76
|
assert.equal(packageJson.name, packageName)
|
|
82
77
|
|
|
83
78
|
const cli = path.join(packageDir, "bin", "ocskill.mjs")
|
|
84
|
-
const env = {
|
|
79
|
+
const env = {
|
|
80
|
+
...process.env,
|
|
81
|
+
OPENCODE_CONFIG_DIR: configDir,
|
|
82
|
+
UES_CONFIG_DIR: uesConfigDir,
|
|
83
|
+
UES_OPENCODE_MAJOR: "2",
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const sync = spawnSync(process.execPath, [cli, "install"], {
|
|
87
|
+
cwd: temp,
|
|
88
|
+
env,
|
|
89
|
+
encoding: "utf8",
|
|
90
|
+
})
|
|
91
|
+
requireSuccess(sync, "explicit legacy OpenCode sync from packed copy")
|
|
85
92
|
|
|
86
93
|
const status = spawnSync(process.execPath, [cli, "status"], {
|
|
87
94
|
cwd: temp,
|
|
@@ -470,7 +477,13 @@ try {
|
|
|
470
477
|
encoding: "utf8",
|
|
471
478
|
})
|
|
472
479
|
requireSuccess(modelStatus, "ocskill models status from packed copy")
|
|
473
|
-
|
|
480
|
+
const initialModelStatus = JSON.parse(modelStatus.stdout)
|
|
481
|
+
assert.equal(initialModelStatus.enabled, false)
|
|
482
|
+
assert.equal(
|
|
483
|
+
path.resolve(initialModelStatus.file),
|
|
484
|
+
path.resolve(uesConfigDir, ".ues", "model-policy.json"),
|
|
485
|
+
"packed smoke must not read the user's real UES model policy",
|
|
486
|
+
)
|
|
474
487
|
|
|
475
488
|
const modelSet = spawnSync(process.execPath, [cli, "models", "set", "standard", "provider/mid"], {
|
|
476
489
|
cwd: temp,
|