opencode-agent-skill 13.0.0-beta.1 → 14.2.0-beta.1
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 +1557 -606
- package/bin/ocskill.mjs +172 -24
- package/docs/OPENCODE-COMPAT.md +34 -95
- package/docs/PI-COMPAT.md +188 -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/commands/run.md +27 -20
- package/global-config/plugins/ues-router/command-runtime.js +54 -0
- package/global-config/plugins/ues-router/index.js +30 -24
- 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 +336 -0
- package/lib/capability-registry.mjs +9 -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/dynamic-workflow.mjs +3 -1
- package/lib/evidence-store.mjs +82 -1
- package/lib/hierarchical-context.mjs +215 -0
- package/lib/memory-engine.mjs +465 -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 +2 -0
- package/lib/repo-graph.mjs +53 -2
- 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 +66 -5
- package/lib/task-policy.mjs +235 -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 +238 -0
- package/pi/extensions/ues.ts +3200 -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 +184 -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 -405
- 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 -75
- 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,184 @@
|
|
|
1
|
+
import assert from "node:assert/strict"
|
|
2
|
+
import { readFileSync, statSync } from "node:fs"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import { fileURLToPath } from "node:url"
|
|
5
|
+
|
|
6
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
7
|
+
|
|
8
|
+
const CONTRACTS = [
|
|
9
|
+
{
|
|
10
|
+
file: "pi/extensions/ues.ts",
|
|
11
|
+
minBytes: 80_000,
|
|
12
|
+
startsWith: "import { spawn }",
|
|
13
|
+
required: [
|
|
14
|
+
'name: "ues_cli"',
|
|
15
|
+
'name: "ues_execute"',
|
|
16
|
+
'name: "ues_dispatch"',
|
|
17
|
+
"runRoutedAgent",
|
|
18
|
+
"PiRpcWorkerPool",
|
|
19
|
+
"CHILD_RUNTIME_EXTENSION",
|
|
20
|
+
"isAbortedRun",
|
|
21
|
+
"abortedResponse",
|
|
22
|
+
'reason: "aborted"',
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
file: "pi/extensions/ues-child-runtime.ts",
|
|
27
|
+
minBytes: 6_000,
|
|
28
|
+
startsWith: 'import type { ExtensionAPI }',
|
|
29
|
+
required: [
|
|
30
|
+
'pi.on("tool_call"',
|
|
31
|
+
'pi.on("tool_result"',
|
|
32
|
+
'name: "ues_evidence_get"',
|
|
33
|
+
"recordVerification",
|
|
34
|
+
"canonicalVerificationCommand",
|
|
35
|
+
"destructiveShellRisk",
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
file: "bin/ocskill.mjs",
|
|
40
|
+
minBytes: 74_000,
|
|
41
|
+
startsWith: "#!/usr/bin/env node",
|
|
42
|
+
required: [
|
|
43
|
+
'case "context-pack"',
|
|
44
|
+
'case "work"',
|
|
45
|
+
'case "task-policy"',
|
|
46
|
+
'case "sandbox"',
|
|
47
|
+
'case "store"',
|
|
48
|
+
'case "capabilities"',
|
|
49
|
+
'case "capability-fabric"',
|
|
50
|
+
'case "hierarchy"',
|
|
51
|
+
'case "memory"',
|
|
52
|
+
'case "visual"',
|
|
53
|
+
'case "browser"',
|
|
54
|
+
'case "workflow-plan"',
|
|
55
|
+
'case "dashboard"',
|
|
56
|
+
'case "models"',
|
|
57
|
+
'case "update"',
|
|
58
|
+
'case "remove"',
|
|
59
|
+
'case "help"',
|
|
60
|
+
"getEvidenceSelected",
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
file: "lib/task-engine.mjs",
|
|
65
|
+
minBytes: 45_000,
|
|
66
|
+
startsWith: "import ",
|
|
67
|
+
required: ["workspaceFingerprint", "initWork", "finalizeWork"],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
file: "lib/model-performance.mjs",
|
|
71
|
+
minBytes: 5_500,
|
|
72
|
+
startsWith: "export const MODEL_TASK_CLASSES",
|
|
73
|
+
required: [
|
|
74
|
+
"recordPerformanceOutcome",
|
|
75
|
+
"wilsonLowerBound",
|
|
76
|
+
"rerankCapabilitySelection",
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
file: "lib/evidence-store.mjs",
|
|
81
|
+
minBytes: 7_500,
|
|
82
|
+
startsWith: "import ",
|
|
83
|
+
required: ["putEvidence", "getEvidence", "getEvidenceSelected"],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
file: "lib/process-supervisor.mjs",
|
|
87
|
+
minBytes: 4_500,
|
|
88
|
+
startsWith: "import ",
|
|
89
|
+
required: ["terminateProcessTree", "runSupervisedProcess", "drainTimeoutMs"],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
file: "lib/pi-rpc-pool.mjs",
|
|
93
|
+
minBytes: 8_000,
|
|
94
|
+
startsWith: "import ",
|
|
95
|
+
required: ["class RpcWorker", "steerActive", "abortActive", "abortTransport", "activeAbort", "uesRpcPhase", "stopAll"],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
file: "lib/workspace-fingerprint.mjs",
|
|
99
|
+
minBytes: 3_000,
|
|
100
|
+
startsWith: "import ",
|
|
101
|
+
required: ["runtimeWorkspaceFingerprint", "runtimeWorkspaceSnapshot", "changedFiles", "cacheable", "untrackedContentDigest", "untracked-total-too-large"],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
file: "lib/skill-compiler.mjs",
|
|
105
|
+
minBytes: 4_000,
|
|
106
|
+
startsWith: "import ",
|
|
107
|
+
required: ["compileSkillContext", "clearSkillCompilerCache", "COMPILED_SKILL_CACHE.set", "return result"],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
file: "lib/verification-command.mjs",
|
|
111
|
+
minBytes: 2_000,
|
|
112
|
+
startsWith: "export const VERIFICATION_COMMAND_RE",
|
|
113
|
+
required: [
|
|
114
|
+
"looksLikeVerificationCommand",
|
|
115
|
+
"canRecordReusableVerification",
|
|
116
|
+
"canonicalVerificationCommand",
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
file: "lib/verification-broker.mjs",
|
|
121
|
+
minBytes: 8_000,
|
|
122
|
+
startsWith: "import ",
|
|
123
|
+
required: [
|
|
124
|
+
"recordVerification",
|
|
125
|
+
"findReusableVerification",
|
|
126
|
+
"listReusableVerification",
|
|
127
|
+
"withCrossProcessCacheLock",
|
|
128
|
+
"entryMatchesKey",
|
|
129
|
+
"freshEnough",
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
file: "test/v14.2-runtime.test.mjs",
|
|
134
|
+
minBytes: 30_000,
|
|
135
|
+
startsWith: 'import assert from "node:assert/strict"',
|
|
136
|
+
required: [
|
|
137
|
+
'test("V14.2 hot-path helper exports are live"',
|
|
138
|
+
'test("external RPC abort rejects the active run instead of settling normally"',
|
|
139
|
+
'test("verification broker preserves concurrent receipts in one workspace"',
|
|
140
|
+
'test("RPC pool never evicts an active worker when an idle-capacity limit is exceeded"',
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
{
|
|
145
|
+
file: "lib/affected-tests.mjs",
|
|
146
|
+
minBytes: 7_500,
|
|
147
|
+
startsWith: "import ",
|
|
148
|
+
required: ["resolveAffectedTests", "clearAffectedTestCache", "AFFECTED_TEST_CACHE.set", "return result"],
|
|
149
|
+
},
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
const problems = []
|
|
153
|
+
for (const contract of CONTRACTS) {
|
|
154
|
+
const absolute = path.join(root, ...contract.file.split("/"))
|
|
155
|
+
let source = ""
|
|
156
|
+
let bytes = 0
|
|
157
|
+
try {
|
|
158
|
+
bytes = statSync(absolute).size
|
|
159
|
+
source = readFileSync(absolute, "utf8")
|
|
160
|
+
} catch (error) {
|
|
161
|
+
problems.push(`${contract.file}: unreadable (${error?.message || error})`)
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (bytes < contract.minBytes) {
|
|
166
|
+
problems.push(`${contract.file}: ${bytes} bytes < required ${contract.minBytes}; possible fragment overwrite`)
|
|
167
|
+
}
|
|
168
|
+
if (!source.startsWith(contract.startsWith)) {
|
|
169
|
+
problems.push(`${contract.file}: source prefix mismatch; possible missing file head`)
|
|
170
|
+
}
|
|
171
|
+
for (const marker of contract.required) {
|
|
172
|
+
if (!source.includes(marker)) {
|
|
173
|
+
problems.push(`${contract.file}: missing integrity marker ${JSON.stringify(marker)}`)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
assert.deepEqual(
|
|
179
|
+
problems,
|
|
180
|
+
[],
|
|
181
|
+
"Critical UES source-integrity validation failed:\n" + problems.map((item) => "- " + item).join("\n"),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
console.log(`Source integrity passed for ${CONTRACTS.length} critical runtime files.`)
|
|
@@ -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.")
|