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.
Files changed (102) hide show
  1. package/README.md +1571 -607
  2. package/bin/ocskill.mjs +172 -24
  3. package/docs/OPENCODE-COMPAT.md +34 -97
  4. package/docs/PI-COMPAT.md +203 -0
  5. package/docs/V14-CONTEXT-MEMORY-FABRIC.md +70 -0
  6. package/docs/V14.1-QUALITY-PERFORMANCE-FABRIC.md +114 -0
  7. package/docs/V14.2-TURBO-WEAK-MODEL-RUNTIME.md +448 -0
  8. package/evals/v14/tasks.json +46 -0
  9. package/global-config/agents/executor.md +7 -0
  10. package/global-config/agents/visual-verifier.md +22 -3
  11. package/global-config/plugins/ues-router/index.js +13 -8
  12. package/global-config/plugins/ues-router/policy-runtime.js +7 -0
  13. package/global-config/plugins/ues-router/router.js +12 -2
  14. package/global-config/skills/ecommerce-engineering/SKILL.md +1 -1
  15. package/global-config/skills/file-upload-engineering/SKILL.md +1 -1
  16. package/global-config/skills/git-safety/SKILL.md +1 -1
  17. package/global-config/skills/nestjs-engineering/SKILL.md +1 -1
  18. package/global-config/skills/performance-engineering/SKILL.md +1 -1
  19. package/global-config/skills/react-native-engineering/SKILL.md +1 -1
  20. package/global-config/skills/rest-api-design/SKILL.md +1 -1
  21. package/global-config/skills/ui-ux-engineering/SKILL.md +1 -1
  22. package/lib/adaptive-context-budget.mjs +97 -0
  23. package/lib/affected-tests.mjs +260 -0
  24. package/lib/benchmark-confidence.mjs +41 -2
  25. package/lib/browser-mcp-routing.mjs +166 -0
  26. package/lib/capability-fabric.mjs +359 -0
  27. package/lib/capability-registry.mjs +9 -0
  28. package/lib/code-intelligence/edit-anchor.mjs +102 -0
  29. package/lib/code-intelligence/index.mjs +95 -0
  30. package/lib/code-intelligence/lsp-provider.mjs +189 -0
  31. package/lib/completion-auditor.mjs +82 -0
  32. package/lib/context-engine-v11.mjs +65 -1
  33. package/lib/context-graph-rank.mjs +118 -0
  34. package/lib/context-manifest.mjs +97 -18
  35. package/lib/control-center.mjs +19 -1
  36. package/lib/document-ingestion.mjs +60 -0
  37. package/lib/dynamic-workflow.mjs +3 -1
  38. package/lib/evidence-store.mjs +82 -1
  39. package/lib/fast-verification-gate.mjs +69 -0
  40. package/lib/hierarchical-context.mjs +215 -0
  41. package/lib/mcp-health.mjs +144 -0
  42. package/lib/mcp-tool-policy.mjs +19 -0
  43. package/lib/memory-engine.mjs +493 -0
  44. package/lib/model-performance.mjs +33 -8
  45. package/lib/model-policy.mjs +3 -3
  46. package/lib/orchestrator-policy.mjs +5 -209
  47. package/lib/performance-fabric.mjs +229 -0
  48. package/lib/pi-rpc-pool.mjs +433 -0
  49. package/lib/process-hang-detector.mjs +83 -0
  50. package/lib/process-supervisor.mjs +193 -0
  51. package/lib/prompt-cache.mjs +27 -11
  52. package/lib/repo-graph.mjs +53 -2
  53. package/lib/reversible-context.mjs +49 -0
  54. package/lib/runtime-config.mjs +31 -0
  55. package/lib/safety.mjs +132 -0
  56. package/lib/semantic-index.mjs +52 -3
  57. package/lib/skill-compiler.mjs +128 -0
  58. package/lib/skill-quality.mjs +48 -2
  59. package/lib/task-engine.mjs +62 -5
  60. package/lib/task-policy.mjs +291 -0
  61. package/lib/verification-broker.mjs +284 -0
  62. package/lib/verification-command.mjs +111 -0
  63. package/lib/windows-shim.mjs +35 -0
  64. package/lib/workspace-fingerprint.mjs +198 -0
  65. package/package.json +52 -42
  66. package/pi/extensions/ues-child-runtime.ts +393 -0
  67. package/pi/extensions/ues.ts +3327 -0
  68. package/pi/prompts/ues-audit.md +9 -0
  69. package/pi/prompts/ues-critique.md +9 -0
  70. package/pi/prompts/ues-debug.md +9 -0
  71. package/pi/prompts/ues-feature.md +9 -0
  72. package/pi/prompts/ues-fix.md +9 -0
  73. package/pi/prompts/ues-plan.md +9 -0
  74. package/pi/prompts/ues-research.md +9 -0
  75. package/pi/prompts/ues-resume.md +9 -0
  76. package/pi/prompts/ues-review.md +7 -0
  77. package/pi/prompts/ues-run.md +17 -0
  78. package/pi/prompts/ues-verify.md +9 -0
  79. package/scripts/check-release-consistency.mjs +119 -185
  80. package/scripts/check-runtime-exports.mjs +66 -0
  81. package/scripts/check-source-integrity.mjs +246 -0
  82. package/scripts/eval-pi.mjs +492 -0
  83. package/scripts/install.mjs +16 -0
  84. package/scripts/smoke-package-closure.mjs +110 -0
  85. package/scripts/smoke-packed-install.mjs +24 -11
  86. package/scripts/smoke-pi-extension.mjs +144 -0
  87. package/scripts/uninstall.mjs +44 -0
  88. package/CHANGELOG.md +0 -415
  89. package/docs/DETERMINISTIC-TOOLS.md +0 -105
  90. package/docs/ENGINEERING-DESIGN.md +0 -194
  91. package/docs/EVALS.md +0 -158
  92. package/docs/GITHUB-RULESET.md +0 -50
  93. package/docs/NPM-PUBLISH.md +0 -116
  94. package/docs/RESEARCH-SOURCES.md +0 -37
  95. package/docs/TRACE-SCHEMA.md +0 -122
  96. package/docs/V11-PERCEPTION-ADAPTIVE-EXECUTION.md +0 -75
  97. package/docs/V11-PERCEPTION-ADAPTIVE.md +0 -220
  98. package/docs/V12-WEAK-MODEL-INTELLIGENCE.md +0 -27
  99. package/docs/V13-PARALLEL-WEAK-MODEL-RUNTIME.md +0 -86
  100. package/docs/V7-INTELLIGENCE-RUNTIME.md +0 -166
  101. package/docs/V8-INTELLIGENCE-RELIABILITY.md +0 -206
  102. 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
+ }
@@ -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 npmVersion = runNpm(["--version"], { cwd: temp })
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: { ...process.env, OPENCODE_CONFIG_DIR: configDir, UES_OPENCODE_MAJOR: "2" },
57
+ env: process.env,
63
58
  })
64
- requireSuccess(install, "packed global install with automatic OpenCode sync")
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 = { ...process.env, OPENCODE_CONFIG_DIR: configDir }
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
- assert.match(modelStatus.stdout, /"enabled": false/)
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,