opencode-agent-skill 7.7.0 → 10.0.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/CHANGELOG.md +112 -3
- package/README.md +396 -281
- package/bin/ocskill.mjs +382 -156
- package/docs/DETERMINISTIC-TOOLS.md +25 -8
- package/docs/ENGINEERING-DESIGN.md +31 -13
- package/docs/EVALS.md +34 -12
- package/docs/NPM-PUBLISH.md +6 -6
- package/docs/OPENCODE-COMPAT.md +11 -8
- package/docs/TRACE-SCHEMA.md +15 -2
- package/docs/V8-INTELLIGENCE-RELIABILITY.md +206 -0
- package/docs/V9-SPEED-INTELLIGENCE.md +102 -0
- package/evals/live/tasks.json +6 -6
- package/evals/polyglot/fixtures/polyglot-bench/api/generated/client.ts +2 -0
- package/evals/polyglot/fixtures/polyglot-bench/api/openapi.json +25 -0
- package/evals/polyglot/fixtures/polyglot-bench/db/migrations/20260920_add_order_key.sql +1 -0
- package/evals/polyglot/fixtures/polyglot-bench/dotnet/OrderService.cs +8 -0
- package/evals/polyglot/fixtures/polyglot-bench/java/PriceService.java +5 -0
- package/evals/polyglot/fixtures/polyglot-bench/monorepo/package.json +6 -0
- package/evals/polyglot/fixtures/polyglot-bench/monorepo/packages/api/package.json +4 -0
- package/evals/polyglot/fixtures/polyglot-bench/monorepo/packages/web/package.json +7 -0
- package/evals/polyglot/fixtures/polyglot-bench/monorepo/pnpm-lock.yaml +5 -0
- package/evals/polyglot/fixtures/polyglot-bench/next/app/api/products/route.ts +7 -0
- package/evals/polyglot/fixtures/polyglot-bench/python/tenant_auth.py +4 -0
- package/evals/polyglot/fixtures/polyglot-bench/react-native/keyboard.ts +3 -0
- package/evals/polyglot/graders/polyglot-bench.mjs +101 -0
- package/evals/polyglot/tasks.json +54 -0
- package/global-config/AGENTS.md +78 -160
- package/global-config/agents/integration-verifier.md +1 -1
- package/global-config/agents/plan-checker.md +1 -1
- package/global-config/commands/run.md +9 -5
- package/global-config/plugins/ues-router/capabilities.js +4 -0
- package/global-config/plugins/ues-router/index.js +784 -37
- package/global-config/plugins/ues-router/router.js +175 -23
- package/global-config/plugins/ues-router/runtime-guard.js +265 -0
- package/global-config/skills/engineering-orchestrator/references/long-horizon.md +6 -4
- package/lib/aci.mjs +128 -0
- package/lib/benchmark-confidence.mjs +173 -0
- package/lib/cli-utils.mjs +41 -0
- package/lib/container-sandbox.mjs +102 -0
- package/lib/context-manifest.mjs +300 -22
- package/lib/control-center.mjs +36 -3
- package/lib/eval-ablation.mjs +104 -0
- package/lib/eval-order.mjs +9 -0
- package/lib/eval-report.mjs +11 -0
- package/lib/eval-telemetry.mjs +8 -2
- package/lib/gate-receipt.mjs +52 -0
- package/lib/installer.mjs +39 -25
- package/lib/learning-engine.mjs +236 -38
- package/lib/model-policy.mjs +6 -0
- package/lib/opencode-compat.mjs +25 -10
- package/lib/orchestrator-policy.mjs +195 -21
- package/lib/process-runner.mjs +30 -9
- package/lib/runtime-events.mjs +31 -0
- package/lib/semantic-index.mjs +318 -0
- package/lib/task-engine.mjs +557 -28
- package/lib/trajectory.mjs +89 -0
- package/lib/windows-shim.mjs +227 -0
- package/lib/worktree-sandbox.mjs +85 -3
- package/package.json +11 -4
- package/scripts/check-release-tag.mjs +22 -0
- package/scripts/control-center.mjs +25 -0
- package/scripts/eval-ablation.mjs +44 -0
- package/scripts/eval-live.mjs +39 -58
- package/scripts/eval-matrix.mjs +166 -0
- package/scripts/smoke-packed-install.mjs +138 -4
- package/scripts/smoke-plain-install.mjs +91 -0
- package/scripts/validate-live-suite.mjs +3 -3
- package/scripts/validate.mjs +27 -5
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
function asBool(value) {
|
|
2
|
+
return value === true
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function pairKey(item) {
|
|
6
|
+
return [item.suite || "", item.task || "", item.trial || 1].join("::")
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function exactSignPValue(a, b) {
|
|
10
|
+
const n = a + b
|
|
11
|
+
if (n <= 0) return 1
|
|
12
|
+
const k = Math.min(a, b)
|
|
13
|
+
let probability = 2 ** (-n)
|
|
14
|
+
let cumulative = probability
|
|
15
|
+
for (let i = 1; i <= k; i += 1) {
|
|
16
|
+
probability *= (n - i + 1) / i
|
|
17
|
+
cumulative += probability
|
|
18
|
+
}
|
|
19
|
+
return Math.min(1, cumulative * 2)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function mean(values) {
|
|
23
|
+
const usable = values.filter((value) => Number.isFinite(value))
|
|
24
|
+
return usable.length ? usable.reduce((sum, value) => sum + value, 0) / usable.length : null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function positiveMean(values) {
|
|
28
|
+
return mean(values.map(Number).filter((value) => Number.isFinite(value) && value > 0))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ratio(candidate, reference) {
|
|
32
|
+
return reference && candidate ? candidate / reference : null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function pairedBenchmarkConfidence(results, options = {}) {
|
|
36
|
+
const byKey = new Map()
|
|
37
|
+
for (const item of results || []) {
|
|
38
|
+
if (!["baseline", "ues"].includes(item?.mode)) continue
|
|
39
|
+
const key = pairKey(item)
|
|
40
|
+
const pair = byKey.get(key) || {}
|
|
41
|
+
pair[item.mode] = item
|
|
42
|
+
byKey.set(key, pair)
|
|
43
|
+
}
|
|
44
|
+
const pairs = [...byKey.entries()]
|
|
45
|
+
.filter(([, pair]) => pair.baseline && pair.ues)
|
|
46
|
+
.map(([key, pair]) => ({ key, ...pair }))
|
|
47
|
+
|
|
48
|
+
let bothPass = 0
|
|
49
|
+
let bothFail = 0
|
|
50
|
+
let baselineOnly = 0
|
|
51
|
+
let uesOnly = 0
|
|
52
|
+
for (const pair of pairs) {
|
|
53
|
+
const b = asBool(pair.baseline.passed)
|
|
54
|
+
const u = asBool(pair.ues.passed)
|
|
55
|
+
if (b && u) bothPass += 1
|
|
56
|
+
else if (!b && !u) bothFail += 1
|
|
57
|
+
else if (b) baselineOnly += 1
|
|
58
|
+
else uesOnly += 1
|
|
59
|
+
}
|
|
60
|
+
const total = pairs.length
|
|
61
|
+
const baselinePassed = bothPass + baselineOnly
|
|
62
|
+
const uesPassed = bothPass + uesOnly
|
|
63
|
+
const baselinePassRate = total ? baselinePassed / total : 0
|
|
64
|
+
const uesPassRate = total ? uesPassed / total : 0
|
|
65
|
+
const delta = uesPassRate - baselinePassRate
|
|
66
|
+
const pValue = exactSignPValue(uesOnly, baselineOnly)
|
|
67
|
+
|
|
68
|
+
const suites = {}
|
|
69
|
+
for (const pair of pairs) {
|
|
70
|
+
const suite = pair.baseline.suite || pair.ues.suite || "unknown"
|
|
71
|
+
suites[suite] ??= { total: 0, baselinePassed: 0, uesPassed: 0 }
|
|
72
|
+
suites[suite].total += 1
|
|
73
|
+
if (pair.baseline.passed) suites[suite].baselinePassed += 1
|
|
74
|
+
if (pair.ues.passed) suites[suite].uesPassed += 1
|
|
75
|
+
}
|
|
76
|
+
for (const item of Object.values(suites)) {
|
|
77
|
+
item.baselinePassRate = item.total ? item.baselinePassed / item.total : 0
|
|
78
|
+
item.uesPassRate = item.total ? item.uesPassed / item.total : 0
|
|
79
|
+
item.delta = item.uesPassRate - item.baselinePassRate
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const baselineDuration = mean(pairs.map((pair) => Number(pair.baseline.durationMs)))
|
|
83
|
+
const uesDuration = mean(pairs.map((pair) => Number(pair.ues.durationMs)))
|
|
84
|
+
const durationRatio = ratio(uesDuration, baselineDuration)
|
|
85
|
+
|
|
86
|
+
const baselineCost = positiveMean(pairs.map((pair) => pair.baseline.telemetry?.cost))
|
|
87
|
+
const uesCost = positiveMean(pairs.map((pair) => pair.ues.telemetry?.cost))
|
|
88
|
+
const costRatio = ratio(uesCost, baselineCost)
|
|
89
|
+
|
|
90
|
+
const baselineInitialInput = positiveMean(
|
|
91
|
+
pairs.map((pair) => pair.baseline.telemetry?.firstUsage?.input),
|
|
92
|
+
)
|
|
93
|
+
const uesInitialInput = positiveMean(
|
|
94
|
+
pairs.map((pair) => pair.ues.telemetry?.firstUsage?.input),
|
|
95
|
+
)
|
|
96
|
+
const initialInputRatio = ratio(uesInitialInput, baselineInitialInput)
|
|
97
|
+
|
|
98
|
+
const baselineTokens = positiveMean(
|
|
99
|
+
pairs.map((pair) => pair.baseline.telemetry?.tokens?.total),
|
|
100
|
+
)
|
|
101
|
+
const uesTokens = positiveMean(
|
|
102
|
+
pairs.map((pair) => pair.ues.telemetry?.tokens?.total),
|
|
103
|
+
)
|
|
104
|
+
const tokenRatio = ratio(uesTokens, baselineTokens)
|
|
105
|
+
|
|
106
|
+
const minPairs = Math.max(1, Number(options.minPairs || 20))
|
|
107
|
+
const alpha = Math.min(0.5, Math.max(0.0001, Number(options.alpha || 0.05)))
|
|
108
|
+
const minDelta = Math.max(0, Number(options.minDelta || 0))
|
|
109
|
+
const suiteRegressionTolerance = Math.max(0, Number(options.suiteRegressionTolerance || 0))
|
|
110
|
+
const maxDurationRatio = Math.max(1, Number(options.maxDurationRatio || 1.75))
|
|
111
|
+
const maxInitialInputRatio = Math.max(1, Number(options.maxInitialInputRatio || 1.5))
|
|
112
|
+
const maxTokenRatio = Math.max(1, Number(options.maxTokenRatio || 1.75))
|
|
113
|
+
const noSuiteRegression = Object.values(suites).every((item) => item.delta >= -suiteRegressionTolerance)
|
|
114
|
+
const speedAcceptable = durationRatio == null || durationRatio <= maxDurationRatio
|
|
115
|
+
const initialInputAcceptable = initialInputRatio == null || initialInputRatio <= maxInitialInputRatio
|
|
116
|
+
const tokensAcceptable = tokenRatio == null || tokenRatio <= maxTokenRatio
|
|
117
|
+
|
|
118
|
+
const checks = {
|
|
119
|
+
pairedCoverage: total >= minPairs,
|
|
120
|
+
positiveDelta: delta > minDelta,
|
|
121
|
+
moreWinsThanLosses: uesOnly > baselineOnly,
|
|
122
|
+
statisticallySupported: pValue <= alpha,
|
|
123
|
+
noSuiteRegression,
|
|
124
|
+
speedAcceptable,
|
|
125
|
+
initialInputAcceptable,
|
|
126
|
+
tokensAcceptable,
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
schemaVersion: 2,
|
|
130
|
+
kind: "ues-paired-benchmark-confidence",
|
|
131
|
+
pairs: total,
|
|
132
|
+
bothPass,
|
|
133
|
+
bothFail,
|
|
134
|
+
baselineOnly,
|
|
135
|
+
uesOnly,
|
|
136
|
+
baselinePassed,
|
|
137
|
+
uesPassed,
|
|
138
|
+
baselinePassRate,
|
|
139
|
+
uesPassRate,
|
|
140
|
+
delta,
|
|
141
|
+
pValue,
|
|
142
|
+
alpha,
|
|
143
|
+
minPairs,
|
|
144
|
+
minDelta,
|
|
145
|
+
suiteRegressionTolerance,
|
|
146
|
+
duration: {
|
|
147
|
+
baselineMeanMs: baselineDuration,
|
|
148
|
+
uesMeanMs: uesDuration,
|
|
149
|
+
ratio: durationRatio,
|
|
150
|
+
maxRatio: maxDurationRatio,
|
|
151
|
+
},
|
|
152
|
+
initialInput: {
|
|
153
|
+
baselineMeanTokens: baselineInitialInput,
|
|
154
|
+
uesMeanTokens: uesInitialInput,
|
|
155
|
+
ratio: initialInputRatio,
|
|
156
|
+
maxRatio: maxInitialInputRatio,
|
|
157
|
+
},
|
|
158
|
+
tokens: {
|
|
159
|
+
baselineMean: baselineTokens,
|
|
160
|
+
uesMean: uesTokens,
|
|
161
|
+
ratio: tokenRatio,
|
|
162
|
+
maxRatio: maxTokenRatio,
|
|
163
|
+
},
|
|
164
|
+
cost: {
|
|
165
|
+
baselineMean: baselineCost,
|
|
166
|
+
uesMean: uesCost,
|
|
167
|
+
ratio: costRatio,
|
|
168
|
+
},
|
|
169
|
+
suites,
|
|
170
|
+
checks,
|
|
171
|
+
promotionEligible: Object.values(checks).every(Boolean),
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
|
|
4
|
+
export function clipOutput(value, { head = 8000, tail = 8000 } = {}) {
|
|
5
|
+
const text = String(value ?? "")
|
|
6
|
+
if (text.length <= head + tail) return text
|
|
7
|
+
return text.slice(0, head) + "\n...[truncated]\n" + text.slice(-tail)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function positionalArg(args, index) {
|
|
11
|
+
const value = args[index]
|
|
12
|
+
if (value == null || value === "" || value === "--" || value.startsWith("--")) return null
|
|
13
|
+
return value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function readJsonFile(file) {
|
|
17
|
+
return JSON.parse(readFileSync(path.resolve(file), "utf8"))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function readTextFile(file) {
|
|
21
|
+
return readFileSync(path.resolve(file), "utf8")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function optionInt(args, name, fallback) {
|
|
25
|
+
const index = args.indexOf(name)
|
|
26
|
+
const raw = index >= 0 ? args[index + 1] : null
|
|
27
|
+
return Number.parseInt(raw || String(fallback), 10)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function optionIntOrUndefined(args, name) {
|
|
31
|
+
return optionInt(args, name, 0) || undefined
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function optionValue(args, name) {
|
|
35
|
+
const index = args.indexOf(name)
|
|
36
|
+
return index >= 0 ? args[index + 1] : null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function errorMessage(error) {
|
|
40
|
+
return error instanceof Error ? error.message : error
|
|
41
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
|
|
4
|
+
function probe(engine) {
|
|
5
|
+
const result = spawnSync(engine, ["--version"], { encoding: "utf8", timeout: 5000 })
|
|
6
|
+
return {
|
|
7
|
+
engine,
|
|
8
|
+
available: result.status === 0,
|
|
9
|
+
version: result.status === 0 ? String(result.stdout || result.stderr || "").trim() : null,
|
|
10
|
+
error: result.status === 0 ? null : String(result.stderr || result.stdout || "").trim() || "unavailable",
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function containerSandboxCapability(preferred = null) {
|
|
15
|
+
const names = preferred ? [preferred] : ["docker", "podman"]
|
|
16
|
+
const probes = names.map(probe)
|
|
17
|
+
const selected = probes.find((item) => item.available) || null
|
|
18
|
+
return {
|
|
19
|
+
schemaVersion: 1,
|
|
20
|
+
available: Boolean(selected),
|
|
21
|
+
selected,
|
|
22
|
+
probes,
|
|
23
|
+
guarantees: selected ? [
|
|
24
|
+
"network disabled by default",
|
|
25
|
+
"Linux capabilities dropped",
|
|
26
|
+
"no-new-privileges",
|
|
27
|
+
"read-only container root",
|
|
28
|
+
"bounded pids/memory/cpu",
|
|
29
|
+
"only requested workspace bind-mounted read-write",
|
|
30
|
+
"no host environment secrets forwarded by default",
|
|
31
|
+
] : [],
|
|
32
|
+
limitation: "This isolates deterministic verification commands, not the OpenCode model session itself.",
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function validImage(image) {
|
|
37
|
+
return /^[A-Za-z0-9][A-Za-z0-9._/:@-]{0,240}$/.test(String(image || ""))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function buildContainerSandboxArgs(root, image, command, commandArgs = [], options = {}) {
|
|
41
|
+
if (!validImage(image)) throw new Error("container image is required and must be a concrete image reference")
|
|
42
|
+
if (!command) throw new Error("sandbox command is required")
|
|
43
|
+
root = path.resolve(root)
|
|
44
|
+
const memory = String(options.memory || "2g")
|
|
45
|
+
const cpus = String(options.cpus || "2")
|
|
46
|
+
const pids = String(options.pids || "256")
|
|
47
|
+
const args = [
|
|
48
|
+
"run", "--rm", "--init",
|
|
49
|
+
"--network", options.network === "bridge" ? "bridge" : "none",
|
|
50
|
+
"--cap-drop", "ALL",
|
|
51
|
+
"--security-opt", "no-new-privileges",
|
|
52
|
+
"--read-only",
|
|
53
|
+
"--pids-limit", pids,
|
|
54
|
+
"--memory", memory,
|
|
55
|
+
"--cpus", cpus,
|
|
56
|
+
"--tmpfs", "/tmp:rw,noexec,nosuid,size=256m",
|
|
57
|
+
"--mount", "type=bind,source=" + root + ",target=/workspace",
|
|
58
|
+
"--workdir", "/workspace",
|
|
59
|
+
"--env", "CI=true",
|
|
60
|
+
]
|
|
61
|
+
if (process.platform !== "win32" && typeof process.getuid === "function" && typeof process.getgid === "function") {
|
|
62
|
+
args.push("--user", process.getuid() + ":" + process.getgid())
|
|
63
|
+
}
|
|
64
|
+
args.push(image, command, ...commandArgs)
|
|
65
|
+
return args
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function runContainerSandbox(root, input = {}) {
|
|
69
|
+
const capability = containerSandboxCapability(input.engine || null)
|
|
70
|
+
if (!capability.available) {
|
|
71
|
+
const error = new Error("container sandbox requested but Docker/Podman is unavailable")
|
|
72
|
+
error.capability = capability
|
|
73
|
+
throw error
|
|
74
|
+
}
|
|
75
|
+
const args = buildContainerSandboxArgs(root, input.image, input.command, input.args || [], input)
|
|
76
|
+
const started = Date.now()
|
|
77
|
+
const result = spawnSync(capability.selected.engine, args, {
|
|
78
|
+
cwd: path.resolve(root),
|
|
79
|
+
encoding: "utf8",
|
|
80
|
+
maxBuffer: Math.max(1024 * 1024, Number(input.maxBuffer || 8 * 1024 * 1024)),
|
|
81
|
+
timeout: Math.max(1000, Number(input.timeoutMs || 10 * 60_000)),
|
|
82
|
+
env: {
|
|
83
|
+
SystemRoot: process.env.SystemRoot,
|
|
84
|
+
WINDIR: process.env.WINDIR,
|
|
85
|
+
PATH: process.env.PATH,
|
|
86
|
+
PATHEXT: process.env.PATHEXT,
|
|
87
|
+
HOME: process.env.HOME,
|
|
88
|
+
USERPROFILE: process.env.USERPROFILE,
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
return {
|
|
92
|
+
schemaVersion: 1,
|
|
93
|
+
engine: capability.selected,
|
|
94
|
+
args,
|
|
95
|
+
status: result.status ?? 1,
|
|
96
|
+
signal: result.signal || null,
|
|
97
|
+
stdout: String(result.stdout || ""),
|
|
98
|
+
stderr: String(result.stderr || ""),
|
|
99
|
+
durationMs: Date.now() - started,
|
|
100
|
+
timedOut: Boolean(result.error?.code === "ETIMEDOUT"),
|
|
101
|
+
}
|
|
102
|
+
}
|