opencode-agent-skill 7.7.0 → 9.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.
Files changed (63) hide show
  1. package/CHANGELOG.md +63 -3
  2. package/README.md +675 -581
  3. package/bin/ocskill.mjs +358 -156
  4. package/docs/DETERMINISTIC-TOOLS.md +25 -8
  5. package/docs/ENGINEERING-DESIGN.md +31 -13
  6. package/docs/EVALS.md +34 -12
  7. package/docs/NPM-PUBLISH.md +6 -6
  8. package/docs/OPENCODE-COMPAT.md +11 -8
  9. package/docs/TRACE-SCHEMA.md +15 -2
  10. package/docs/V8-INTELLIGENCE-RELIABILITY.md +206 -0
  11. package/docs/V9-SPEED-INTELLIGENCE.md +102 -0
  12. package/evals/live/tasks.json +6 -6
  13. package/evals/polyglot/fixtures/polyglot-bench/api/generated/client.ts +2 -0
  14. package/evals/polyglot/fixtures/polyglot-bench/api/openapi.json +25 -0
  15. package/evals/polyglot/fixtures/polyglot-bench/db/migrations/20260920_add_order_key.sql +1 -0
  16. package/evals/polyglot/fixtures/polyglot-bench/dotnet/OrderService.cs +8 -0
  17. package/evals/polyglot/fixtures/polyglot-bench/java/PriceService.java +5 -0
  18. package/evals/polyglot/fixtures/polyglot-bench/monorepo/package.json +6 -0
  19. package/evals/polyglot/fixtures/polyglot-bench/monorepo/packages/api/package.json +4 -0
  20. package/evals/polyglot/fixtures/polyglot-bench/monorepo/packages/web/package.json +7 -0
  21. package/evals/polyglot/fixtures/polyglot-bench/monorepo/pnpm-lock.yaml +5 -0
  22. package/evals/polyglot/fixtures/polyglot-bench/next/app/api/products/route.ts +7 -0
  23. package/evals/polyglot/fixtures/polyglot-bench/python/tenant_auth.py +4 -0
  24. package/evals/polyglot/fixtures/polyglot-bench/react-native/keyboard.ts +3 -0
  25. package/evals/polyglot/graders/polyglot-bench.mjs +101 -0
  26. package/evals/polyglot/tasks.json +54 -0
  27. package/global-config/AGENTS.md +10 -7
  28. package/global-config/agents/integration-verifier.md +1 -1
  29. package/global-config/agents/plan-checker.md +1 -1
  30. package/global-config/commands/run.md +9 -5
  31. package/global-config/plugins/ues-router/capabilities.js +4 -0
  32. package/global-config/plugins/ues-router/index.js +414 -21
  33. package/global-config/plugins/ues-router/router.js +140 -23
  34. package/global-config/skills/engineering-orchestrator/references/long-horizon.md +6 -4
  35. package/lib/aci.mjs +128 -0
  36. package/lib/benchmark-confidence.mjs +135 -0
  37. package/lib/cli-utils.mjs +41 -0
  38. package/lib/container-sandbox.mjs +102 -0
  39. package/lib/context-manifest.mjs +300 -22
  40. package/lib/control-center.mjs +36 -3
  41. package/lib/eval-order.mjs +9 -0
  42. package/lib/eval-telemetry.mjs +5 -2
  43. package/lib/gate-receipt.mjs +52 -0
  44. package/lib/installer.mjs +39 -25
  45. package/lib/learning-engine.mjs +236 -38
  46. package/lib/opencode-compat.mjs +25 -10
  47. package/lib/orchestrator-policy.mjs +101 -20
  48. package/lib/process-runner.mjs +30 -9
  49. package/lib/runtime-events.mjs +31 -0
  50. package/lib/semantic-index.mjs +318 -0
  51. package/lib/task-engine.mjs +416 -24
  52. package/lib/trajectory.mjs +89 -0
  53. package/lib/windows-shim.mjs +227 -0
  54. package/lib/worktree-sandbox.mjs +85 -3
  55. package/package.json +10 -4
  56. package/scripts/check-release-tag.mjs +22 -0
  57. package/scripts/control-center.mjs +25 -0
  58. package/scripts/eval-live.mjs +39 -58
  59. package/scripts/eval-matrix.mjs +155 -0
  60. package/scripts/smoke-packed-install.mjs +138 -4
  61. package/scripts/smoke-plain-install.mjs +91 -0
  62. package/scripts/validate-live-suite.mjs +3 -3
  63. package/scripts/validate.mjs +27 -5
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync } from "node:fs"
1
+ import { existsSync } from "node:fs"
2
2
  import { cp, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises"
3
3
  import os from "node:os"
4
4
  import path from "node:path"
@@ -7,9 +7,11 @@ import { fileURLToPath } from "node:url"
7
7
  import { installResources } from "../lib/installer.mjs"
8
8
  import { copyCurrentOpenCodeAuth } from "../lib/eval-auth.mjs"
9
9
  import { parseOpenCodeTelemetry } from "../lib/eval-telemetry.mjs"
10
+ import { evalModeOrder } from "../lib/eval-order.mjs"
10
11
  import { buildOpenCodeRunArgs, parseOpenCodeMajor } from "../lib/opencode-compat.mjs"
11
12
  import { snapshotWorkspace, diffWorkspaceSnapshots } from "../lib/workspace-snapshot.mjs"
12
13
  import { runProcess } from "../lib/process-runner.mjs"
14
+ import { resolveWindowsCommand } from "../lib/windows-shim.mjs"
13
15
 
14
16
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
15
17
  const args = process.argv.slice(2)
@@ -35,69 +37,39 @@ function positiveInt(value, fallback) {
35
37
  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback
36
38
  }
37
39
 
38
- function findWindowsCommand(name) {
39
- if (path.isAbsolute(name) && existsSync(name)) return name
40
- const result = spawnSync("where", [name], { encoding: "utf8" })
41
- if (result.status !== 0 || !result.stdout) return null
42
- const matches = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean)
43
- return matches.find((item) => /\.(exe|cmd|bat)$/i.test(item)) || matches[0] || null
44
- }
45
-
46
- function quoteCmd(value) {
47
- if (/^[A-Za-z0-9_@%+=:,./\\-]+$/.test(value)) return value
48
- return '"' + value.replaceAll('"', '""') + '"'
49
- }
50
-
51
- function findNodeShimEntry(cmdPath) {
52
- const dir = path.dirname(cmdPath)
53
- let shim = ""
54
- try {
55
- shim = readFileSync(cmdPath, "utf8")
56
- } catch {
57
- return null
58
- }
59
- const match = shim.match(/node_modules[\\/][^\s"]+?\.(?:js|mjs)/gi)?.at(-1)
60
- if (!match) return null
61
- const entry = path.resolve(dir, match)
62
- return existsSync(entry) ? entry : null
63
- }
64
40
 
65
41
  function runCommand(executable, commandArgs, options = {}) {
66
42
  if (process.platform !== "win32") {
67
43
  return spawnSync(executable, commandArgs, options)
68
44
  }
69
45
 
70
- const resolved = findWindowsCommand(executable)
46
+ const resolved = resolveWindowsCommand(executable)
71
47
  if (!resolved) {
72
- return { status: 127, stdout: "", stderr: "Command not found: " + executable }
73
- }
74
-
75
- if (/\.(cmd|bat)$/i.test(resolved)) {
76
- const entry = findNodeShimEntry(resolved)
77
- if (entry) return spawnSync(process.execPath, [entry, ...commandArgs], options)
78
-
79
- const line = [resolved, ...commandArgs].map(quoteCmd).join(" ")
80
- return spawnSync(
81
- process.env.ComSpec || "cmd.exe",
82
- ["/d", "/s", "/c", line],
83
- options,
84
- )
48
+ return {
49
+ status: 127,
50
+ stdout: "",
51
+ stderr: "No safely executable Windows command found for: " + executable,
52
+ }
85
53
  }
86
54
 
87
- return spawnSync(resolved, commandArgs, options)
55
+ return spawnSync(
56
+ resolved.executable,
57
+ [...resolved.argsPrefix, ...commandArgs],
58
+ options,
59
+ )
88
60
  }
89
61
 
90
62
 
91
63
  function runAsyncCommand(executable, commandArgs, options = {}) {
92
64
  if (process.platform !== "win32") return runProcess(executable, commandArgs, options)
93
65
 
94
- const resolved = findWindowsCommand(executable)
66
+ const resolved = resolveWindowsCommand(executable)
95
67
  if (!resolved) {
96
68
  return Promise.resolve({
97
69
  status: 127,
98
70
  signal: null,
99
71
  stdout: "",
100
- stderr: "Command not found: " + executable,
72
+ stderr: "No safely executable Windows command found for: " + executable,
101
73
  durationMs: 0,
102
74
  timedOut: false,
103
75
  idleTimedOut: false,
@@ -105,14 +77,11 @@ function runAsyncCommand(executable, commandArgs, options = {}) {
105
77
  })
106
78
  }
107
79
 
108
- if (/\.(cmd|bat)$/i.test(resolved)) {
109
- const entry = findNodeShimEntry(resolved)
110
- if (entry) return runProcess(process.execPath, [entry, ...commandArgs], options)
111
- const line = [resolved, ...commandArgs].map(quoteCmd).join(" ")
112
- return runProcess(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", line], options)
113
- }
114
-
115
- return runProcess(resolved, commandArgs, options)
80
+ return runProcess(
81
+ resolved.executable,
82
+ [...resolved.argsPrefix, ...commandArgs],
83
+ options,
84
+ )
116
85
  }
117
86
 
118
87
  function excerpt(value, limit = 12000) {
@@ -145,6 +114,10 @@ async function inspectLongOrchestration(workspace) {
145
114
  slug: entry.name,
146
115
  taskCount: Array.isArray(plan.tasks) ? plan.tasks.length : 0,
147
116
  planApproved: state.planApproval?.status === "passed",
117
+ structuredPlanReceipt:
118
+ state.planApproval?.receipt?.kind === "plan-verification" &&
119
+ state.planApproval?.receipt?.verdict === "PASS" &&
120
+ state.planApproval?.receipt?.planHash === state.planHash,
148
121
  attemptedTasks: tasks.filter((task) => Number(task.attempts || 0) > 0).length,
149
122
  completedTasks: tasks.filter((task) => task.status === "completed").length,
150
123
  receiptBackedTasks: plannedIDs.filter((id) => receiptBackedTasks.has(id)).length,
@@ -152,6 +125,10 @@ async function inspectLongOrchestration(workspace) {
152
125
  ? plannedIDs.filter((id) => receiptBackedTasks.has(id)).length / plannedIDs.length
153
126
  : 0,
154
127
  integrationPassed: state.integrationVerification?.status === "PASS",
128
+ structuredIntegrationReceipt:
129
+ state.integrationVerification?.receipt?.kind === "integration-verification" &&
130
+ state.integrationVerification?.receipt?.verdict === "PASS" &&
131
+ state.integrationVerification?.receipt?.workspaceFingerprint === state.integrationVerification?.fingerprint,
155
132
  integrationEvidence: evidenceTasks.has("__integration_verification__"),
156
133
  finalizedEvidence: evidenceTasks.has("__integration__"),
157
134
  completed: state.status === "completed",
@@ -159,10 +136,12 @@ async function inspectLongOrchestration(workspace) {
159
136
  item.valid =
160
137
  item.taskCount >= 2 &&
161
138
  item.planApproved &&
139
+ item.structuredPlanReceipt &&
162
140
  item.attemptedTasks === item.taskCount &&
163
141
  item.completedTasks === item.taskCount &&
164
142
  item.receiptBackedTasks === item.taskCount &&
165
143
  item.integrationPassed &&
144
+ item.structuredIntegrationReceipt &&
166
145
  item.integrationEvidence &&
167
146
  item.finalizedEvidence &&
168
147
  item.completed
@@ -193,7 +172,7 @@ const heartbeatMs = positiveInt(argValue("--heartbeat-ms"), 30_000)
193
172
  const suiteRoot = path.join(root, "evals", suiteName)
194
173
 
195
174
  if (!model) {
196
- console.error("Usage: node scripts/eval-live.mjs --model provider/model [--suite live|long] [--variant high] [--trials N] [--task id] [--mode baseline|ues|both] [--auth env-only|current] [--output-dir path] [--keep] [--timeout-ms N] [--idle-timeout-ms N] [--heartbeat-ms N]")
175
+ console.error("Usage: node scripts/eval-live.mjs --model provider/model [--suite live|long|polyglot] [--variant high] [--trials N] [--task id] [--mode baseline|ues|both] [--auth env-only|current] [--output-dir path] [--keep] [--timeout-ms N] [--idle-timeout-ms N] [--heartbeat-ms N]")
197
176
  console.error("You can also set UES_EVAL_MODEL and UES_EVAL_VARIANT.")
198
177
  process.exit(2)
199
178
  }
@@ -221,8 +200,8 @@ if (parsedOpenCodeMajor === null) {
221
200
  console.warn("[eval] could not parse OpenCode version; using the conservative OpenCode 1.x invocation.")
222
201
  }
223
202
 
224
- if (!["live", "long"].includes(suiteName)) {
225
- console.error("--suite must be live or long")
203
+ if (!["live", "long", "polyglot"].includes(suiteName)) {
204
+ console.error("--suite must be live, long, or polyglot")
226
205
  process.exit(2)
227
206
  }
228
207
 
@@ -246,11 +225,12 @@ const results = []
246
225
  const oldConfigDir = process.env.OPENCODE_CONFIG_DIR
247
226
 
248
227
  try {
249
- for (const task of tasks) {
228
+ for (const [taskIndex, task] of tasks.entries()) {
250
229
  if (interrupted) break
251
- for (const mode of modes) {
230
+ for (let trial = 1; trial <= trials; trial += 1) {
252
231
  if (interrupted) break
253
- for (let trial = 1; trial <= trials; trial += 1) {
232
+ const trialModes = evalModeOrder(requestedMode, taskIndex, trial)
233
+ for (const mode of trialModes) {
254
234
  if (interrupted) break
255
235
  const isolatedRoot = path.join(runRoot, task.id + "-" + mode + "-" + trial)
256
236
  const workspace = path.join(isolatedRoot, "workspace")
@@ -419,6 +399,7 @@ await writeFile(
419
399
  opencodeVersion,
420
400
  opencodeMajor,
421
401
  modes,
402
+ ordering: requestedMode === "both" ? "counterbalanced-task-trial" : "single-mode",
422
403
  summary,
423
404
  results,
424
405
  },
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "node:child_process"
3
+ import { mkdir, readdir, readFile, writeFile } from "node:fs/promises"
4
+ import path from "node:path"
5
+ import { fileURLToPath } from "node:url"
6
+ import { summarizeEvalResults } from "../lib/eval-report.mjs"
7
+ import { pairedBenchmarkConfidence } from "../lib/benchmark-confidence.mjs"
8
+
9
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
10
+ const args = process.argv.slice(2)
11
+
12
+ function argValue(name, fallback = null) {
13
+ const index = args.indexOf(name)
14
+ return index >= 0 && index + 1 < args.length ? args[index + 1] : fallback
15
+ }
16
+ function has(name) { return args.includes(name) }
17
+ function positiveInt(value, fallback) {
18
+ const parsed = Number.parseInt(value || "", 10)
19
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback
20
+ }
21
+
22
+ const model = argValue("--model", process.env.UES_EVAL_MODEL)
23
+ const variant = argValue("--variant", process.env.UES_EVAL_VARIANT)
24
+ const trials = Math.min(positiveInt(argValue("--trials"), 3), 10)
25
+ const auth = argValue("--auth", "current")
26
+ const outputDir = path.resolve(argValue("--output-dir", path.join(process.cwd(), ".ues-evals", "matrix")))
27
+ const timeoutMs = positiveInt(argValue("--timeout-ms"), 20 * 60_000)
28
+ const idleTimeoutMs = positiveInt(argValue("--idle-timeout-ms"), 5 * 60_000)
29
+ const heartbeatMs = positiveInt(argValue("--heartbeat-ms"), 30_000)
30
+ const onlyLong = has("--long-only")
31
+ const onlyLive = has("--standard-only")
32
+ const onlyPolyglot = has("--polyglot-only")
33
+ const withoutPolyglot = has("--without-polyglot")
34
+ const requireConfidence = has("--require-confidence")
35
+
36
+ if (!model) {
37
+ console.error("Usage: node scripts/eval-matrix.mjs --model provider/model [--trials 3] [--auth current|env-only] [--variant high] [--without-polyglot|--long-only|--standard-only|--polyglot-only]")
38
+ process.exit(2)
39
+ }
40
+ const exclusiveCount = [onlyLong, onlyLive, onlyPolyglot].filter(Boolean).length
41
+ if (exclusiveCount > 1) {
42
+ console.error("--long-only, --standard-only and --polyglot-only are mutually exclusive")
43
+ process.exit(2)
44
+ }
45
+
46
+ await mkdir(outputDir, { recursive: true })
47
+ const suites = onlyLong
48
+ ? ["long"]
49
+ : onlyLive
50
+ ? ["live"]
51
+ : onlyPolyglot
52
+ ? ["polyglot"]
53
+ : withoutPolyglot
54
+ ? ["long", "live"]
55
+ : ["long", "live", "polyglot"]
56
+ const startedAt = new Date().toISOString()
57
+ const before = new Set(await readdir(outputDir).catch(() => []))
58
+
59
+ for (const suite of suites) {
60
+ const command = [
61
+ path.join(root, "scripts", "eval-live.mjs"),
62
+ "--suite", suite,
63
+ "--model", model,
64
+ "--mode", "both",
65
+ "--trials", String(trials),
66
+ "--auth", auth,
67
+ "--output-dir", outputDir,
68
+ "--timeout-ms", String(timeoutMs),
69
+ "--idle-timeout-ms", String(idleTimeoutMs),
70
+ "--heartbeat-ms", String(heartbeatMs),
71
+ ]
72
+ if (variant) command.push("--variant", variant)
73
+
74
+ console.log("\n[matrix] Running " + suite + " suite: " + trials + " trial(s) per task per mode")
75
+ const result = spawnSync(process.execPath, command, { cwd: process.cwd(), stdio: "inherit" })
76
+ if ((result.status ?? 1) !== 0) {
77
+ console.error("[matrix] " + suite + " suite failed with exit code " + (result.status ?? 1))
78
+ process.exit(result.status ?? 1)
79
+ }
80
+ }
81
+
82
+ const after = await readdir(outputDir)
83
+ const created = after
84
+ .filter((name) => name.endsWith(".json") && !before.has(name))
85
+ .sort()
86
+
87
+ const runs = []
88
+ const results = []
89
+ for (const name of created) {
90
+ const file = path.join(outputDir, name)
91
+ const payload = JSON.parse(await readFile(file, "utf8"))
92
+ if (!suites.includes(payload.suite) || payload.model !== model) continue
93
+ runs.push({
94
+ file: name,
95
+ suite: payload.suite,
96
+ suiteVersion: payload.suiteVersion,
97
+ trials: payload.trials,
98
+ results: payload.results?.length || 0,
99
+ })
100
+ results.push(...(payload.results || []).map((item) => ({ ...item, suite: payload.suite })))
101
+ }
102
+
103
+ const expectedPerMode = suites.reduce((sum, suite) => {
104
+ if (suite === "long") return sum + 5 * trials
105
+ if (suite === "live") return sum + 20 * trials
106
+ if (suite === "polyglot") return sum + 8 * trials
107
+ return sum
108
+ }, 0)
109
+ const baselineCount = results.filter((item) => item.mode === "baseline").length
110
+ const uesCount = results.filter((item) => item.mode === "ues").length
111
+ const coverageComplete = baselineCount === expectedPerMode && uesCount === expectedPerMode
112
+ const summary = summarizeEvalResults(results)
113
+ const confidence = pairedBenchmarkConfidence(results)
114
+
115
+ const report = {
116
+ schemaVersion: 1,
117
+ kind: "ues-benchmark-matrix",
118
+ startedAt,
119
+ finishedAt: new Date().toISOString(),
120
+ model,
121
+ variant: variant || null,
122
+ trials,
123
+ auth,
124
+ suites,
125
+ expectedPerMode,
126
+ baselineCount,
127
+ uesCount,
128
+ coverageComplete,
129
+ runs,
130
+ pairedResults: results.map((item) => ({
131
+ suite: item.suite || null,
132
+ task: item.task,
133
+ trial: item.trial,
134
+ mode: item.mode,
135
+ passed: item.passed === true,
136
+ durationMs: item.durationMs ?? null,
137
+ telemetry: item.telemetry?.costSamples > 0 ? { cost: item.telemetry.cost } : {},
138
+ })),
139
+ summary,
140
+ confidence,
141
+ }
142
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-")
143
+ const reportFile = path.join(outputDir, "matrix-summary-" + stamp + ".json")
144
+ await writeFile(reportFile, JSON.stringify(report, null, 2) + "\n", "utf8")
145
+
146
+ console.log("\n[matrix] Benchmark matrix summary")
147
+ console.log("- baseline: " + (summary.modes.baseline?.passed || 0) + "/" + (summary.modes.baseline?.total || 0))
148
+ console.log("- UES: " + (summary.modes.ues?.passed || 0) + "/" + (summary.modes.ues?.total || 0))
149
+ console.log("- pass-rate delta: " + (summary.passRateDelta == null ? "n/a" : (summary.passRateDelta * 100).toFixed(1) + " pp"))
150
+ console.log("- coverage: " + (coverageComplete ? "COMPLETE" : "INCOMPLETE"))
151
+ console.log("- paired confidence: " + (confidence.promotionEligible ? "SUPPORTED" : "NOT YET SUPPORTED") + " (pairs=" + confidence.pairs + ", p=" + confidence.pValue.toFixed(4) + ")")
152
+ console.log("- confidence gate: " + (requireConfidence ? "REQUIRED" : "report-only"))
153
+ console.log("- report: " + reportFile)
154
+
155
+ if (!coverageComplete || (requireConfidence && !confidence.promotionEligible)) process.exitCode = 1
@@ -105,7 +105,7 @@ try {
105
105
  assert.ok(existsSync(routerPlugin), "v2 router plugin was not installed from packed package")
106
106
  assert.ok(existsSync(path.join(configDir, "plugins", "ues-router", "router.js")), "v2 router helper was not installed from packed package")
107
107
  assert.ok(existsSync(path.join(configDir, "plugins", "ues-router", "safety.js")), "v2 router safety gate was not installed from packed package")
108
- assert.ok(existsSync(path.join(configDir, "plugins", "ues-router", "capabilities.js")), "v7 router capability probe was not installed from packed package")
108
+ assert.ok(existsSync(path.join(configDir, "plugins", "ues-router", "capabilities.js")), "v8 router capability probe was not installed from packed package")
109
109
  const routerSource = await readFile(routerPlugin, "utf8")
110
110
  assert.match(routerSource, /name: "dispatch_task"/)
111
111
  assert.match(routerSource, /ctx\.session\.create/)
@@ -113,12 +113,15 @@ try {
113
113
  assert.match(routerSource, /ctx\.session\.switchModel/)
114
114
  assert.match(routerSource, /name: "capabilities"/)
115
115
  assert.match(routerSource, /name: "task_policy"/)
116
+ assert.match(routerSource, /name: "cancel_task"/)
117
+ assert.match(routerSource, /name: "recover_task"/)
118
+ assert.match(routerSource, /ctx\.session\.interrupt/)
116
119
  const reviewer = await readFile(path.join(configDir, "agents", "ues-reviewer.md"), "utf8")
117
120
  assert.match(reviewer, /permissions:/)
118
121
  assert.doesNotMatch(reviewer, /^permission:/m)
119
122
 
120
123
  for (const name of ["ues-codebase-mapper.md", "ues-plan-checker.md", "ues-executor.md", "ues-integration-verifier.md"]) {
121
- assert.ok(existsSync(path.join(configDir, "agents", name)), "missing packed V6 subagent " + name)
124
+ assert.ok(existsSync(path.join(configDir, "agents", name)), "missing packed V8 subagent " + name)
122
125
  }
123
126
  const executor = await readFile(path.join(configDir, "agents", "ues-executor.md"), "utf8")
124
127
  assert.match(executor, /permissions:/)
@@ -136,7 +139,7 @@ try {
136
139
  const planFile = path.join(temp, "PLAN.json")
137
140
  await writeFile(planFile, JSON.stringify({
138
141
  schemaVersion: 1,
139
- goal: "Packed V6 smoke",
142
+ goal: "Packed V8 smoke",
140
143
  tasks: [{
141
144
  id: "T1",
142
145
  title: "Smoke",
@@ -157,12 +160,24 @@ try {
157
160
  requireSuccess(graph, "ocskill task-graph from packed copy")
158
161
  assert.match(graph.stdout, /"valid": true/)
159
162
 
160
- const workInit = spawnSync(process.execPath, [cli, "work", "init", "packed-smoke", temp, "--goal", "Packed V6 work smoke"], {
163
+ const workInit = spawnSync(process.execPath, [cli, "work", "init", "packed-smoke", temp, "--goal", "Packed V8 work smoke"], {
161
164
  cwd: temp,
162
165
  env,
163
166
  encoding: "utf8",
164
167
  })
165
168
  requireSuccess(workInit, "ocskill work init from packed copy")
169
+ assert.ok(
170
+ existsSync(path.join(temp, ".ues-work", "packed-smoke", "EVENTS.jsonl")),
171
+ "packed V8 work item did not create EVENTS.jsonl",
172
+ )
173
+
174
+ const workEvents = spawnSync(process.execPath, [cli, "work", "events", "packed-smoke", temp], {
175
+ cwd: temp,
176
+ env,
177
+ encoding: "utf8",
178
+ })
179
+ requireSuccess(workEvents, "ocskill work events from packed copy")
180
+ assert.match(workEvents.stdout, /"work\.init"/)
166
181
 
167
182
  const workPlan = spawnSync(process.execPath, [cli, "work", "plan", "packed-smoke", planFile, temp], {
168
183
  cwd: temp,
@@ -266,6 +281,125 @@ try {
266
281
  requireSuccess(workFinalize, "ocskill work finalize from packed copy")
267
282
  assert.match(workFinalize.stdout, /"status": "completed"/)
268
283
 
284
+ const strictPlanFile = path.join(temp, "STRICT-PLAN.json")
285
+ await writeFile(strictPlanFile, JSON.stringify({
286
+ schemaVersion: 1,
287
+ goal: "Packed V8 strict evidence smoke",
288
+ tasks: [
289
+ {
290
+ id: "S1",
291
+ title: "Strict one",
292
+ summary: "Exercise structured plan/task evidence",
293
+ files: { modify: ["src/one.js"] },
294
+ dependsOn: [],
295
+ acceptance: ["S1 is verified"],
296
+ verification: ["node --version"],
297
+ risk: "medium",
298
+ },
299
+ {
300
+ id: "S2",
301
+ title: "Strict two",
302
+ summary: "Exercise structured integration evidence",
303
+ files: { modify: ["src/two.js"] },
304
+ dependsOn: ["S1"],
305
+ acceptance: ["S2 is verified"],
306
+ verification: ["node --version"],
307
+ risk: "medium",
308
+ },
309
+ ],
310
+ }, null, 2) + "\n", "utf8")
311
+
312
+ requireSuccess(
313
+ spawnSync(process.execPath, [cli, "work", "init", "packed-strict", temp, "--goal", "Packed V8 strict evidence smoke"], {
314
+ cwd: temp, env, encoding: "utf8",
315
+ }),
316
+ "strict work init from packed copy",
317
+ )
318
+ requireSuccess(
319
+ spawnSync(process.execPath, [cli, "work", "plan", "packed-strict", strictPlanFile, temp], {
320
+ cwd: temp, env, encoding: "utf8",
321
+ }),
322
+ "strict work plan from packed copy",
323
+ )
324
+
325
+ const plainStrictApproval = spawnSync(process.execPath, [
326
+ cli, "work", "approve-plan", "packed-strict", temp, "--evidence", "plain PASS",
327
+ ], { cwd: temp, env, encoding: "utf8" })
328
+ assert.notEqual(plainStrictApproval.status, 0)
329
+ assert.match(plainStrictApproval.stderr, /structured plan-verification receipt/)
330
+
331
+ const strictPlanReceipt = path.join(temp, ".ues-work", "packed-strict", "reports", "plan-receipt.json")
332
+ requireSuccess(
333
+ spawnSync(process.execPath, [
334
+ cli, "work", "gate-receipt", "packed-strict", "plan", temp,
335
+ "--verifier", "ues-plan-checker",
336
+ "--evidence", "strict plan PASS",
337
+ "--out", strictPlanReceipt,
338
+ ], { cwd: temp, env, encoding: "utf8" }),
339
+ "strict plan receipt from packed copy",
340
+ )
341
+ requireSuccess(
342
+ spawnSync(process.execPath, [
343
+ cli, "work", "approve-plan", "packed-strict", temp,
344
+ "--evidence", "strict plan PASS",
345
+ "--receipt-file", strictPlanReceipt,
346
+ ], { cwd: temp, env, encoding: "utf8" }),
347
+ "strict plan approval from packed copy",
348
+ )
349
+
350
+ for (const taskID of ["S1", "S2"]) {
351
+ const strictStart = spawnSync(process.execPath, [cli, "work", "start", "packed-strict", taskID, temp], {
352
+ cwd: temp, env, encoding: "utf8",
353
+ })
354
+ requireSuccess(strictStart, "strict " + taskID + " start from packed copy")
355
+ const strictStarted = JSON.parse(strictStart.stdout)
356
+ const runId = strictStarted.record.runId
357
+
358
+ requireSuccess(
359
+ spawnSync(process.execPath, [
360
+ cli, "work", "verify-command", "packed-strict", taskID, temp,
361
+ "--run-id", runId,
362
+ "--", process.execPath, "--version",
363
+ ], { cwd: temp, env, encoding: "utf8" }),
364
+ "strict " + taskID + " verification from packed copy",
365
+ )
366
+ requireSuccess(
367
+ spawnSync(process.execPath, [
368
+ cli, "work", "complete", "packed-strict", taskID, temp,
369
+ "--run-id", runId,
370
+ "--evidence", taskID + " verification PASS",
371
+ ], { cwd: temp, env, encoding: "utf8" }),
372
+ "strict " + taskID + " completion from packed copy",
373
+ )
374
+ }
375
+
376
+ const strictIntegrationReceipt = path.join(temp, ".ues-work", "packed-strict", "reports", "integration-receipt.json")
377
+ requireSuccess(
378
+ spawnSync(process.execPath, [
379
+ cli, "work", "gate-receipt", "packed-strict", "integration", temp,
380
+ "--verifier", "ues-integration-verifier",
381
+ "--verdict", "PASS",
382
+ "--evidence", "strict integration PASS",
383
+ "--out", strictIntegrationReceipt,
384
+ ], { cwd: temp, env, encoding: "utf8" }),
385
+ "strict integration receipt from packed copy",
386
+ )
387
+ requireSuccess(
388
+ spawnSync(process.execPath, [
389
+ cli, "work", "verify-integration", "packed-strict", temp,
390
+ "--verdict", "PASS",
391
+ "--evidence", "strict integration PASS",
392
+ "--receipt-file", strictIntegrationReceipt,
393
+ ], { cwd: temp, env, encoding: "utf8" }),
394
+ "strict integration verification from packed copy",
395
+ )
396
+ const strictFinalize = spawnSync(process.execPath, [
397
+ cli, "work", "finalize", "packed-strict", temp,
398
+ "--evidence", "strict final acceptance PASS",
399
+ ], { cwd: temp, env, encoding: "utf8" })
400
+ requireSuccess(strictFinalize, "strict finalization from packed copy")
401
+ assert.match(strictFinalize.stdout, /"status": "completed"/)
402
+
269
403
  const adaptivePolicy = spawnSync(process.execPath, [
270
404
  cli, "task-policy",
271
405
  "Refactor the entire repository authentication schema migration and public API contracts",
@@ -0,0 +1,91 @@
1
+ import assert from "node:assert/strict"
2
+ import { existsSync } from "node:fs"
3
+ import { mkdir, mkdtemp, readFile, readdir, realpath, rm } from "node:fs/promises"
4
+ import { spawnSync } from "node:child_process"
5
+ import os from "node:os"
6
+ import path from "node:path"
7
+ import { fileURLToPath } from "node:url"
8
+
9
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
10
+ const packageJson = JSON.parse(await readFile(path.join(repoRoot, "package.json"), "utf8"))
11
+ const packageName = packageJson.name
12
+
13
+ function runNpm(args, options = {}) {
14
+ const common = {
15
+ cwd: options.cwd ?? repoRoot,
16
+ env: options.env ?? process.env,
17
+ encoding: "utf8",
18
+ }
19
+ const npmExecPath = process.env.npm_execpath
20
+ if (npmExecPath && existsSync(npmExecPath)) {
21
+ return spawnSync(process.execPath, [npmExecPath, ...args], common)
22
+ }
23
+ return spawnSync("npm", args, { ...common, shell: process.platform === "win32" })
24
+ }
25
+
26
+ function requireSuccess(result, label) {
27
+ if (result.status === 0) return
28
+ throw new Error(
29
+ label + " failed with exit code " + (result.status ?? "unknown") + "\n" +
30
+ [result.stdout, result.stderr].filter(Boolean).join("\n"),
31
+ )
32
+ }
33
+
34
+ const temp = await mkdtemp(path.join(os.tmpdir(), "ocskill-plain-install-"))
35
+ const packDir = path.join(temp, "pack")
36
+ const prefix = path.join(temp, "prefix")
37
+ const configDir = path.join(temp, "opencode")
38
+
39
+ try {
40
+ await mkdir(packDir, { recursive: true })
41
+ const pack = runNpm(["pack", "--pack-destination", packDir])
42
+ requireSuccess(pack, "npm pack")
43
+ const tarballs = (await readdir(packDir)).filter((name) => name.endsWith(".tgz"))
44
+ assert.equal(tarballs.length, 1)
45
+
46
+ const npmVersionRun = runNpm(["--version"], { cwd: temp })
47
+ requireSuccess(npmVersionRun, "npm --version")
48
+ const npmVersion = npmVersionRun.stdout.trim()
49
+ const npmMajor = Number.parseInt(npmVersion.split(".")[0], 10)
50
+
51
+ // Intentionally mirrors the user's exact install shape: no --allow-scripts.
52
+ const install = runNpm(["install", "-g", tarballs.map((name) => path.join(packDir, name))[0], "--prefix", prefix], {
53
+ cwd: temp,
54
+ env: { ...process.env, OPENCODE_CONFIG_DIR: configDir, UES_OPENCODE_MAJOR: "2" },
55
+ })
56
+ requireSuccess(install, "plain global install")
57
+
58
+ const root = runNpm(["root", "-g", "--prefix", prefix], { cwd: temp })
59
+ requireSuccess(root, "npm root -g")
60
+ const packageDir = path.join(root.stdout.trim(), ...packageName.split("/"))
61
+ assert.equal((await realpath(packageDir)).toLowerCase() === (await realpath(repoRoot)).toLowerCase(), false)
62
+
63
+ const cli = path.join(packageDir, "bin", "ocskill.mjs")
64
+ assert.ok(existsSync(cli), "plain install did not install the ocskill CLI")
65
+
66
+ const stateFile = path.join(configDir, ".ues", "state.json")
67
+ const autoSynced = existsSync(stateFile)
68
+ assert.equal(
69
+ autoSynced,
70
+ true,
71
+ "plain npm install installed the CLI but did not auto-sync OpenCode resources; one-command installation is not release-safe",
72
+ )
73
+
74
+ const state = JSON.parse(await readFile(stateFile, "utf8"))
75
+ assert.equal(state.package, packageName)
76
+ assert.ok(state.skills.length >= 39)
77
+ assert.ok(state.commands.length >= 11)
78
+ assert.ok(state.agents.length >= 10)
79
+
80
+ console.log(
81
+ "Plain npm install compatibility smoke passed for " + packageName + "@" + packageJson.version +
82
+ ": npm " + npmVersion + ", lifecycle auto-sync=" + autoSynced +
83
+ ", final resources=" + state.skills.length + "/" + state.commands.length + "/" + state.agents.length + ".",
84
+ )
85
+
86
+ if (Number.isFinite(npmMajor) && npmMajor >= 11) {
87
+ console.warn("[smoke] npm 11+ completed the one-command install and resource auto-sync contract.")
88
+ }
89
+ } finally {
90
+ await rm(temp, { recursive: true, force: true })
91
+ }
@@ -8,8 +8,8 @@ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
8
8
  const args = process.argv.slice(2)
9
9
  const suiteIndex = args.indexOf("--suite")
10
10
  const suiteName = suiteIndex >= 0 ? args[suiteIndex + 1] : "live"
11
- if (!["live", "long"].includes(suiteName)) {
12
- console.error("--suite must be live or long")
11
+ if (!["live", "long", "polyglot"].includes(suiteName)) {
12
+ console.error("--suite must be live, long or polyglot")
13
13
  process.exit(2)
14
14
  }
15
15
  const suiteRoot = path.join(root, "evals", suiteName)
@@ -17,7 +17,7 @@ const suite = JSON.parse(await readFile(path.join(suiteRoot, "tasks.json"), "utf
17
17
  const errors = []
18
18
  const ids = new Set()
19
19
 
20
- const minimumTasks = suiteName === "long" ? 5 : 20
20
+ const minimumTasks = suiteName === "long" ? 5 : suiteName === "polyglot" ? 8 : 20
21
21
  if (!Array.isArray(suite.tasks) || suite.tasks.length < minimumTasks) {
22
22
  errors.push(suiteName + " eval suite must contain at least " + minimumTasks + " tasks")
23
23
  }