opencode-agent-skill 11.0.0 → 13.0.0-beta.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 +55 -0
- package/README.md +119 -14
- package/bin/ocskill.mjs +416 -83
- package/docs/DETERMINISTIC-TOOLS.md +1 -1
- package/docs/ENGINEERING-DESIGN.md +4 -4
- package/docs/EVALS.md +3 -3
- package/docs/GITHUB-RULESET.md +50 -0
- package/docs/NPM-PUBLISH.md +22 -8
- package/docs/OPENCODE-COMPAT.md +14 -4
- package/docs/TRACE-SCHEMA.md +1 -1
- package/docs/V11-PERCEPTION-ADAPTIVE.md +2 -2
- package/docs/V12-WEAK-MODEL-INTELLIGENCE.md +27 -0
- package/docs/V13-PARALLEL-WEAK-MODEL-RUNTIME.md +75 -0
- package/evals/repo-scale/tasks.json +62 -0
- package/global-config/AGENTS.md +4 -0
- package/global-config/commands/resume.md +4 -1
- package/global-config/commands/run.md +9 -7
- package/global-config/plugins/ues-router/capabilities.js +1 -0
- package/global-config/plugins/ues-router/command-runtime.js +79 -0
- package/global-config/plugins/ues-router/index.js +364 -43
- package/global-config/plugins/ues-router/parallel-runtime.js +271 -0
- package/global-config/plugins/ues-router/text-runtime.js +115 -0
- package/global-config/plugins/ues-router/verifier-runtime.js +33 -0
- package/global-config/skills/dynamic-workflow/SKILL.md +7 -6
- package/global-config/skills/dynamic-workflow/references/workflow.md +8 -6
- package/global-config/skills/engineering-orchestrator/references/long-horizon.md +7 -5
- package/lib/cli-utils.mjs +17 -4
- package/lib/context-engine-v11.mjs +4 -0
- package/lib/context-quality.mjs +59 -0
- package/lib/decision-policy.mjs +23 -0
- package/lib/installer.mjs +49 -16
- package/lib/model-config.mjs +13 -1
- package/lib/model-performance.mjs +113 -0
- package/lib/model-policy.mjs +9 -2
- package/lib/repo-scale-fixture.mjs +45 -0
- package/lib/task-engine.mjs +36 -12
- package/lib/task-graph.mjs +32 -0
- package/lib/text-encoding.mjs +74 -0
- package/lib/work-plan-scope.mjs +49 -0
- package/lib/worktree-sandbox.mjs +141 -12
- package/package.json +8 -4
- package/scripts/check-release-consistency.mjs +260 -0
- package/scripts/smoke-packed-install.mjs +39 -3
- package/scripts/smoke-plain-install.mjs +27 -7
- package/scripts/validate-repo-scale-suite.mjs +27 -0
- package/scripts/validate-v12-foundation.mjs +24 -0
- package/scripts/validate.mjs +18 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Plugin } from "@opencode/plugin"
|
|
2
1
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs"
|
|
3
2
|
import { fileURLToPath } from "node:url"
|
|
4
3
|
import path from "node:path"
|
|
@@ -6,6 +5,10 @@ import { spawnSync } from "node:child_process"
|
|
|
6
5
|
import { classifyIntent, routeSkillsForPolicy } from "./router.js"
|
|
7
6
|
import { destructiveShellRisk } from "./safety.js"
|
|
8
7
|
import { runtimeCapabilities } from "./capabilities.js"
|
|
8
|
+
import { parallelRootBaseline, runEventDrivenDAG } from "./parallel-runtime.js"
|
|
9
|
+
import { readProjectJson, readProjectText } from "./text-runtime.js"
|
|
10
|
+
import { extractVerifierVerdict } from "./verifier-runtime.js"
|
|
11
|
+
import { expandUesPromptAlias } from "./command-runtime.js"
|
|
9
12
|
import {
|
|
10
13
|
budgetToolResult,
|
|
11
14
|
classifyProviderFailure,
|
|
@@ -23,6 +26,11 @@ const CONFIG_FILE = path.resolve(
|
|
|
23
26
|
"router.json",
|
|
24
27
|
)
|
|
25
28
|
|
|
29
|
+
const COMMAND_TEMPLATE_DIR = path.resolve(
|
|
30
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
31
|
+
"command-templates",
|
|
32
|
+
)
|
|
33
|
+
|
|
26
34
|
function routerConfig() {
|
|
27
35
|
try {
|
|
28
36
|
const parsed = JSON.parse(readFileSync(CONFIG_FILE, "utf8"))
|
|
@@ -36,8 +44,15 @@ function routerConfig() {
|
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
|
|
47
|
+
function spawnHidden(command, args, options = {}) {
|
|
48
|
+
return spawnSync(command, args, {
|
|
49
|
+
...options,
|
|
50
|
+
windowsHide: true,
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
39
54
|
function findWindowsCommand(name) {
|
|
40
|
-
const result =
|
|
55
|
+
const result = spawnHidden("where", [name], { encoding: "utf8" })
|
|
41
56
|
if (result.status !== 0 || !result.stdout) return null
|
|
42
57
|
const matches = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean)
|
|
43
58
|
return matches.find((item) => /\.(cmd|bat)$/i.test(item)) || matches[0] || null
|
|
@@ -61,16 +76,16 @@ function runOcskill(args, cwd) {
|
|
|
61
76
|
}
|
|
62
77
|
let result
|
|
63
78
|
if (process.platform !== "win32") {
|
|
64
|
-
result =
|
|
79
|
+
result = spawnHidden("ocskill", args, common)
|
|
65
80
|
} else {
|
|
66
81
|
const resolved = findWindowsCommand("ocskill")
|
|
67
82
|
if (!resolved) throw new Error("ocskill command was not found on PATH")
|
|
68
83
|
if (/\.(cmd|bat)$/i.test(resolved)) {
|
|
69
84
|
const entry = findNodeShimEntry(resolved)
|
|
70
85
|
if (!entry) throw new Error("refusing to execute an unrecognized ocskill batch shim through cmd.exe")
|
|
71
|
-
result =
|
|
86
|
+
result = spawnHidden(process.execPath, [entry, ...args], common)
|
|
72
87
|
} else {
|
|
73
|
-
result =
|
|
88
|
+
result = spawnHidden(resolved, args, common)
|
|
74
89
|
}
|
|
75
90
|
}
|
|
76
91
|
if (result.status !== 0) {
|
|
@@ -80,11 +95,31 @@ function runOcskill(args, cwd) {
|
|
|
80
95
|
}
|
|
81
96
|
|
|
82
97
|
function runOcskillJSON(args, cwd) {
|
|
83
|
-
const
|
|
98
|
+
const separator = args.indexOf("--")
|
|
99
|
+
const machineArgs = separator >= 0
|
|
100
|
+
? [...args.slice(0, separator), "--json", ...args.slice(separator)]
|
|
101
|
+
: [...args, "--json"]
|
|
102
|
+
let output
|
|
103
|
+
try {
|
|
104
|
+
output = runOcskill(machineArgs, cwd)
|
|
105
|
+
} catch (error) {
|
|
106
|
+
let parsed = null
|
|
107
|
+
try { parsed = JSON.parse(String(error?.message || error)) } catch {}
|
|
108
|
+
if (parsed?.error?.message) {
|
|
109
|
+
const structured = new Error(parsed.error.message)
|
|
110
|
+
structured.code = parsed.error.code || "UES_ERROR"
|
|
111
|
+
structured.hint = parsed.error.hint || null
|
|
112
|
+
structured.recoverable = parsed.error.recoverable === true
|
|
113
|
+
throw structured
|
|
114
|
+
}
|
|
115
|
+
throw error
|
|
116
|
+
}
|
|
84
117
|
try {
|
|
85
118
|
return JSON.parse(output)
|
|
86
119
|
} catch {
|
|
87
|
-
|
|
120
|
+
const error = new Error("ocskill returned invalid JSON for: " + args.join(" "))
|
|
121
|
+
error.code = "UES_INVALID_JSON"
|
|
122
|
+
throw error
|
|
88
123
|
}
|
|
89
124
|
}
|
|
90
125
|
|
|
@@ -140,7 +175,7 @@ function workspaceSignal(root) {
|
|
|
140
175
|
]
|
|
141
176
|
const parts = []
|
|
142
177
|
for (const args of commands) {
|
|
143
|
-
const result =
|
|
178
|
+
const result = spawnHidden("git", args, {
|
|
144
179
|
cwd: root,
|
|
145
180
|
encoding: "utf8",
|
|
146
181
|
maxBuffer: 2 * 1024 * 1024,
|
|
@@ -245,7 +280,7 @@ function projectRoutingFacts(projectRoot) {
|
|
|
245
280
|
}
|
|
246
281
|
}
|
|
247
282
|
|
|
248
|
-
export default
|
|
283
|
+
export default {
|
|
249
284
|
id: "ues-router",
|
|
250
285
|
async setup(ctx) {
|
|
251
286
|
const projectRoot = ctx.location.project?.canonical || ctx.location.directory
|
|
@@ -499,6 +534,27 @@ export default Plugin.define({
|
|
|
499
534
|
options: { namespace: "ues", codemode: true },
|
|
500
535
|
execute: async () => ({ content: JSON.stringify(capabilities, null, 2) }),
|
|
501
536
|
})
|
|
537
|
+
editor.add({
|
|
538
|
+
name: "text_read",
|
|
539
|
+
description: "Read a project text or diff file with UTF-8/UTF-16 auto-detection when the normal reader reports binary or encoding trouble. Refuses paths outside the project and real binary data.",
|
|
540
|
+
input: {
|
|
541
|
+
type: "object",
|
|
542
|
+
properties: {
|
|
543
|
+
file: { type: "string" },
|
|
544
|
+
start: { type: "integer", minimum: 0 },
|
|
545
|
+
maxChars: { type: "integer", minimum: 256, maximum: 100000 },
|
|
546
|
+
},
|
|
547
|
+
required: ["file"],
|
|
548
|
+
additionalProperties: false,
|
|
549
|
+
},
|
|
550
|
+
options: { namespace: "ues", codemode: true },
|
|
551
|
+
execute: async (input) => ({
|
|
552
|
+
content: JSON.stringify(readProjectText(projectRoot, input.file, {
|
|
553
|
+
start: input.start,
|
|
554
|
+
maxChars: input.maxChars,
|
|
555
|
+
}), null, 2),
|
|
556
|
+
}),
|
|
557
|
+
})
|
|
502
558
|
editor.add({
|
|
503
559
|
name: "task_policy",
|
|
504
560
|
description: "Classify an engineering request into inline, standard or long-horizon mode with risk/model/context guidance.",
|
|
@@ -904,24 +960,7 @@ export default Plugin.define({
|
|
|
904
960
|
}
|
|
905
961
|
},
|
|
906
962
|
})
|
|
907
|
-
|
|
908
|
-
name: "dispatch_task",
|
|
909
|
-
description: "Start one approved UES task and execute it in a fresh ues-executor session with bounded runtime and interrupt-on-timeout. The parent must inspect the diff and record completion evidence separately.",
|
|
910
|
-
input: {
|
|
911
|
-
type: "object",
|
|
912
|
-
properties: {
|
|
913
|
-
slug: { type: "string" },
|
|
914
|
-
task: { type: "string" },
|
|
915
|
-
timeoutMs: { type: "integer", minimum: 30000, maximum: 3600000 },
|
|
916
|
-
stallMs: { type: "integer", minimum: 30000, maximum: 300000 },
|
|
917
|
-
isolate: { type: "boolean" },
|
|
918
|
-
integrate: { type: "boolean" },
|
|
919
|
-
},
|
|
920
|
-
required: ["slug", "task"],
|
|
921
|
-
additionalProperties: false,
|
|
922
|
-
},
|
|
923
|
-
options: { namespace: "ues", codemode: true },
|
|
924
|
-
execute: async (input, tool) => {
|
|
963
|
+
const dispatchApprovedTask = async (input, tool, executionOptions = {}) => {
|
|
925
964
|
if (!capabilities.freshDispatch) {
|
|
926
965
|
throw new Error("OpenCode runtime does not expose the fresh-session capabilities required by ues.dispatch_task")
|
|
927
966
|
}
|
|
@@ -977,22 +1016,21 @@ export default Plugin.define({
|
|
|
977
1016
|
const workingTree = runOcskillJSON(["working-tree", projectRoot], projectRoot)
|
|
978
1017
|
const rootClean = workingTree?.git === true && workingTree?.clean === true
|
|
979
1018
|
const writerTask = taskHasWrites(started?.contextPack?.task)
|
|
980
|
-
if (input.isolate === true && !rootClean) {
|
|
981
|
-
throw new Error("explicit sandbox isolation requires a clean root working tree
|
|
1019
|
+
if (input.isolate === true && !rootClean && executionOptions.inheritDirtyRoot !== true) {
|
|
1020
|
+
throw new Error("explicit sandbox isolation requires a clean root working tree unless inherited-root snapshot mode is enabled")
|
|
982
1021
|
}
|
|
983
|
-
if (input.isolate !== false && writerTask && !rootClean) {
|
|
984
|
-
throw new Error("writer dispatch requires a clean root
|
|
1022
|
+
if (input.isolate !== false && writerTask && !rootClean && executionOptions.inheritDirtyRoot !== true) {
|
|
1023
|
+
throw new Error("writer dispatch requires a clean root unless the parallel runtime enables inherited-root snapshot isolation")
|
|
985
1024
|
}
|
|
986
1025
|
const autoIsolate =
|
|
987
1026
|
input.isolate === true ||
|
|
988
|
-
(input.isolate !== false &&
|
|
1027
|
+
(input.isolate !== false && writerTask && (rootClean || executionOptions.inheritDirtyRoot === true))
|
|
989
1028
|
let sandbox = null
|
|
990
1029
|
let executionDir = projectRoot
|
|
991
1030
|
if (autoIsolate) {
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
)
|
|
1031
|
+
const sandboxArgs = ["sandbox", "create", input.slug, input.task, projectRoot]
|
|
1032
|
+
if (executionOptions.inheritDirtyRoot === true && !rootClean) sandboxArgs.push("--inherit-dirty-root")
|
|
1033
|
+
sandbox = runOcskillJSON(sandboxArgs, projectRoot)
|
|
996
1034
|
executionDir = sandbox.dir
|
|
997
1035
|
}
|
|
998
1036
|
const heartbeatStarted = Date.now()
|
|
@@ -1009,7 +1047,9 @@ export default Plugin.define({
|
|
|
1009
1047
|
const escalationArgs = ["model-policy", "executor", "--attempt", String(attempt + 1)]
|
|
1010
1048
|
if (taskText) escalationArgs.push("--text", taskText)
|
|
1011
1049
|
const escalatedPolicy = runOcskillJSON(escalationArgs, projectRoot)
|
|
1012
|
-
let selectedPolicy =
|
|
1050
|
+
let selectedPolicy = executionOptions.forceModel === true
|
|
1051
|
+
? { ...policy, model: executionOptions.modelOverride || null, forcedSingleModel: true }
|
|
1052
|
+
: policy
|
|
1013
1053
|
let created = null
|
|
1014
1054
|
let messages = null
|
|
1015
1055
|
const providerRecovery = []
|
|
@@ -1053,6 +1093,12 @@ export default Plugin.define({
|
|
|
1053
1093
|
}
|
|
1054
1094
|
|
|
1055
1095
|
await ctx.session.switchAgent({ sessionID: created.id, agent: "ues-executor" })
|
|
1096
|
+
if (capabilities.permissionRules) {
|
|
1097
|
+
await ctx.permission.rules({
|
|
1098
|
+
sessionID: created.id,
|
|
1099
|
+
permissions: [{ action: "subagent", resource: "*", effect: "deny" }],
|
|
1100
|
+
})
|
|
1101
|
+
}
|
|
1056
1102
|
const selectedModel = modelRef(selectedPolicy?.model)
|
|
1057
1103
|
if (selectedModel) {
|
|
1058
1104
|
await ctx.session.switchModel({ sessionID: created.id, model: selectedModel })
|
|
@@ -1092,6 +1138,7 @@ export default Plugin.define({
|
|
|
1092
1138
|
const failureKind = classifyProviderFailure(error)
|
|
1093
1139
|
const escalationModel = modelRef(escalatedPolicy?.model)
|
|
1094
1140
|
const hasEscalationModel = Boolean(
|
|
1141
|
+
executionOptions.disableModelEscalation !== true &&
|
|
1095
1142
|
escalationModel &&
|
|
1096
1143
|
escalatedPolicy?.model &&
|
|
1097
1144
|
escalatedPolicy.model !== selectedPolicy?.model,
|
|
@@ -1204,6 +1251,265 @@ export default Plugin.define({
|
|
|
1204
1251
|
} finally {
|
|
1205
1252
|
clearInterval(heartbeat)
|
|
1206
1253
|
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
editor.add({
|
|
1257
|
+
name: "dispatch_task",
|
|
1258
|
+
description: "Start one approved UES task and execute it in a fresh ues-executor session with bounded runtime and interrupt-on-timeout. The parent must inspect the diff and record completion evidence separately.",
|
|
1259
|
+
input: {
|
|
1260
|
+
type: "object",
|
|
1261
|
+
properties: {
|
|
1262
|
+
slug: { type: "string" },
|
|
1263
|
+
task: { type: "string" },
|
|
1264
|
+
timeoutMs: { type: "integer", minimum: 30000, maximum: 3600000 },
|
|
1265
|
+
stallMs: { type: "integer", minimum: 30000, maximum: 300000 },
|
|
1266
|
+
isolate: { type: "boolean" },
|
|
1267
|
+
integrate: { type: "boolean" },
|
|
1268
|
+
},
|
|
1269
|
+
required: ["slug", "task"],
|
|
1270
|
+
additionalProperties: false,
|
|
1271
|
+
},
|
|
1272
|
+
options: { namespace: "ues", codemode: true },
|
|
1273
|
+
execute: async (input, tool) => dispatchApprovedTask(input, tool),
|
|
1274
|
+
})
|
|
1275
|
+
|
|
1276
|
+
editor.add({
|
|
1277
|
+
name: "dispatch_parallel",
|
|
1278
|
+
description: "Execute an approved UES DAG with multiple fresh sessions of one shared model. Ready tasks run concurrently, each writer is isolated, a fresh same-model verifier must PASS, and integration is serialized with rollback on post-integration failure.",
|
|
1279
|
+
input: {
|
|
1280
|
+
type: "object",
|
|
1281
|
+
properties: {
|
|
1282
|
+
slug: { type: "string" },
|
|
1283
|
+
maxConcurrent: { type: "integer", minimum: 1, maximum: 8 },
|
|
1284
|
+
model: { type: "string" },
|
|
1285
|
+
timeoutMs: { type: "integer", minimum: 30000, maximum: 3600000 },
|
|
1286
|
+
stallMs: { type: "integer", minimum: 30000, maximum: 300000 },
|
|
1287
|
+
verifierTimeoutMs: { type: "integer", minimum: 30000, maximum: 1800000 },
|
|
1288
|
+
},
|
|
1289
|
+
required: ["slug"],
|
|
1290
|
+
additionalProperties: false,
|
|
1291
|
+
},
|
|
1292
|
+
options: { namespace: "ues", codemode: true },
|
|
1293
|
+
execute: async (input, tool) => {
|
|
1294
|
+
if (!capabilities.freshDispatch) {
|
|
1295
|
+
throw new Error("OpenCode runtime does not expose the fresh-session capabilities required by ues.dispatch_parallel")
|
|
1296
|
+
}
|
|
1297
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(String(input.slug || ""))) {
|
|
1298
|
+
throw new Error("work slug must use lowercase letters, numbers and dashes")
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
const initialTree = runOcskillJSON(["working-tree", projectRoot], projectRoot)
|
|
1302
|
+
const rootBaseline = parallelRootBaseline(initialTree)
|
|
1303
|
+
|
|
1304
|
+
const workDir = path.join(projectRoot, ".ues-work", input.slug)
|
|
1305
|
+
const planFile = path.join(workDir, "PLAN.json")
|
|
1306
|
+
const stateFile = path.join(workDir, "STATE.json")
|
|
1307
|
+
if (!existsSync(planFile) || !existsSync(stateFile)) {
|
|
1308
|
+
throw new Error("parallel dispatch requires an initialized work item with PLAN.json and STATE.json")
|
|
1309
|
+
}
|
|
1310
|
+
const plan = readProjectJson(projectRoot, path.relative(projectRoot, planFile)).value
|
|
1311
|
+
const state = readProjectJson(projectRoot, path.relative(projectRoot, stateFile)).value
|
|
1312
|
+
if (state?.planApproval?.status !== "passed") throw new Error("parallel dispatch requires an approved active plan")
|
|
1313
|
+
const running = Object.entries(state.tasks || {}).filter(([, record]) => record?.status === "running")
|
|
1314
|
+
if (running.length) throw new Error("parallel dispatch refuses to start while another durable task attempt is already running")
|
|
1315
|
+
|
|
1316
|
+
const completedTaskIds = Object.entries(state.tasks || {})
|
|
1317
|
+
.filter(([, record]) => record?.status === "completed")
|
|
1318
|
+
.map(([id]) => id)
|
|
1319
|
+
const tasks = (plan.tasks || []).filter((task) => !completedTaskIds.includes(task.id))
|
|
1320
|
+
const goalText = String(plan.goal || "parallel approved UES execution")
|
|
1321
|
+
const sharedPolicy = runOcskillJSON(["model-policy", "executor", "--attempt", "1", "--text", goalText], projectRoot)
|
|
1322
|
+
const sharedModel = input.model || sharedPolicy?.model || null
|
|
1323
|
+
if (input.model && !modelRef(input.model)) throw new Error("parallel model must use provider/model[#variant] syntax")
|
|
1324
|
+
if (sharedModel && !capabilities.modelSwitch) {
|
|
1325
|
+
throw new Error("same-model parallel execution selected " + sharedModel + " but this OpenCode runtime cannot switch child-session models")
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
const maxConcurrent = Math.max(1, Math.min(Number(input.maxConcurrent || 4), 8))
|
|
1329
|
+
const verifierTimeoutMs = Math.max(30000, Math.min(Number(input.verifierTimeoutMs || 600000), 1800000))
|
|
1330
|
+
await tool.progress({ status: "parallel UES starting " + tasks.length + " task(s), max " + maxConcurrent + " shared-model workers" })
|
|
1331
|
+
|
|
1332
|
+
const failRunningTask = (taskID, runId, reason) => {
|
|
1333
|
+
try {
|
|
1334
|
+
const failArgs = ["work", "fail", input.slug, taskID, projectRoot, "--reason", String(reason || "parallel task failed")]
|
|
1335
|
+
if (runId) failArgs.push("--run-id", runId)
|
|
1336
|
+
runOcskill(failArgs, projectRoot)
|
|
1337
|
+
} catch {}
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
const scheduler = await runEventDrivenDAG(tasks, {
|
|
1341
|
+
completedTaskIds,
|
|
1342
|
+
maxConcurrent,
|
|
1343
|
+
singleModel: true,
|
|
1344
|
+
model: sharedModel,
|
|
1345
|
+
worker: async (task, context) => {
|
|
1346
|
+
let payload = null
|
|
1347
|
+
try {
|
|
1348
|
+
const response = await dispatchApprovedTask({
|
|
1349
|
+
slug: input.slug,
|
|
1350
|
+
task: task.id,
|
|
1351
|
+
timeoutMs: input.timeoutMs,
|
|
1352
|
+
stallMs: input.stallMs,
|
|
1353
|
+
isolate: true,
|
|
1354
|
+
integrate: false,
|
|
1355
|
+
}, tool, {
|
|
1356
|
+
forceModel: true,
|
|
1357
|
+
modelOverride: context.model,
|
|
1358
|
+
disableModelEscalation: true,
|
|
1359
|
+
inheritDirtyRoot: true,
|
|
1360
|
+
})
|
|
1361
|
+
payload = JSON.parse(response.content)
|
|
1362
|
+
|
|
1363
|
+
const executionDir = payload.sandbox?.dir || payload.executionDir || projectRoot
|
|
1364
|
+
const beforeVerifierSignal = workspaceSignal(executionDir)
|
|
1365
|
+
const verifierSession = await ctx.session.create({
|
|
1366
|
+
title: "UES verify " + input.slug + " " + task.id,
|
|
1367
|
+
location: { directory: executionDir },
|
|
1368
|
+
})
|
|
1369
|
+
runtimeGuard.touch(verifierSession.id)
|
|
1370
|
+
await ctx.session.switchAgent({ sessionID: verifierSession.id, agent: "ues-verifier" })
|
|
1371
|
+
if (capabilities.permissionRules) {
|
|
1372
|
+
await ctx.permission.rules({
|
|
1373
|
+
sessionID: verifierSession.id,
|
|
1374
|
+
permissions: [
|
|
1375
|
+
{ action: "edit", resource: "*", effect: "deny" },
|
|
1376
|
+
{ action: "subagent", resource: "*", effect: "deny" },
|
|
1377
|
+
],
|
|
1378
|
+
})
|
|
1379
|
+
}
|
|
1380
|
+
const verifierModel = modelRef(context.model)
|
|
1381
|
+
if (verifierModel) await ctx.session.switchModel({ sessionID: verifierSession.id, model: verifierModel })
|
|
1382
|
+
await ctx.session.prompt({
|
|
1383
|
+
sessionID: verifierSession.id,
|
|
1384
|
+
text:
|
|
1385
|
+
"Independently verify this completed UES task. Do not edit files. Inspect the task-scoped diff, acceptance criteria and declared verification; run fresh verification commands where appropriate. " +
|
|
1386
|
+
"Your final line MUST be exactly one line beginning UES_VERDICT_JSON: followed by compact JSON with verdict PASS or FAIL and concise evidence. Use FAIL if any acceptance criterion or verification is not proven.\n\n" +
|
|
1387
|
+
JSON.stringify({
|
|
1388
|
+
task: task.id,
|
|
1389
|
+
title: task.title || null,
|
|
1390
|
+
summary: task.summary || null,
|
|
1391
|
+
acceptance: task.acceptance || [],
|
|
1392
|
+
verification: task.verification || [],
|
|
1393
|
+
verificationCommands: task.verificationCommands || [],
|
|
1394
|
+
files: task.files || null,
|
|
1395
|
+
risk: task.risk || "medium",
|
|
1396
|
+
}, null, 2),
|
|
1397
|
+
})
|
|
1398
|
+
|
|
1399
|
+
let verifierMessages
|
|
1400
|
+
try {
|
|
1401
|
+
verifierMessages = await waitForExecutorProgress(verifierSession.id, {
|
|
1402
|
+
timeoutMs: verifierTimeoutMs,
|
|
1403
|
+
stallMs: Math.min(Number(input.stallMs || 60000), 300000),
|
|
1404
|
+
})
|
|
1405
|
+
} catch (error) {
|
|
1406
|
+
try { await ctx.session.interrupt({ sessionID: verifierSession.id, continue: false }) } catch {}
|
|
1407
|
+
throw error
|
|
1408
|
+
} finally {
|
|
1409
|
+
runtimeGuard.clear(verifierSession.id)
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
if (beforeVerifierSignal !== workspaceSignal(executionDir)) {
|
|
1413
|
+
throw new Error("independent verifier modified the workspace; verifier sessions must be read-only")
|
|
1414
|
+
}
|
|
1415
|
+
const verdict = extractVerifierVerdict(verifierMessages)
|
|
1416
|
+
if (!verdict || verdict.verdict !== "PASS") {
|
|
1417
|
+
throw new Error("independent verifier did not produce PASS" + (verdict?.evidence ? ": " + verdict.evidence : ""))
|
|
1418
|
+
}
|
|
1419
|
+
return {
|
|
1420
|
+
...payload,
|
|
1421
|
+
verifier: {
|
|
1422
|
+
sessionID: verifierSession.id,
|
|
1423
|
+
verdict: verdict.verdict,
|
|
1424
|
+
evidence: verdict.evidence || "independent verifier PASS",
|
|
1425
|
+
},
|
|
1426
|
+
}
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
if (payload?.sandbox?.dir) {
|
|
1429
|
+
try { runOcskill(["sandbox", "remove", payload.sandbox.dir, projectRoot, "--force", "--delete-branch"], projectRoot) } catch {}
|
|
1430
|
+
}
|
|
1431
|
+
if (payload?.runId) failRunningTask(task.id, payload.runId, "parallel verification failed: " + String(error?.message || error))
|
|
1432
|
+
throw error
|
|
1433
|
+
}
|
|
1434
|
+
},
|
|
1435
|
+
integrate: async (task, result) => {
|
|
1436
|
+
let applied = false
|
|
1437
|
+
let durableCompleted = false
|
|
1438
|
+
try {
|
|
1439
|
+
let integration = null
|
|
1440
|
+
if (result.sandbox?.dir) {
|
|
1441
|
+
integration = runOcskillJSON(["sandbox", "integrate", result.sandbox.dir, projectRoot, "--keep"], projectRoot)
|
|
1442
|
+
applied = true
|
|
1443
|
+
}
|
|
1444
|
+
const deterministicReceipts = []
|
|
1445
|
+
for (const commandSpec of task.verificationCommands || []) {
|
|
1446
|
+
const command = String(commandSpec?.command || "").trim()
|
|
1447
|
+
if (!command) continue
|
|
1448
|
+
const commandArgs = Array.isArray(commandSpec?.args)
|
|
1449
|
+
? commandSpec.args.map((value) => String(value))
|
|
1450
|
+
: []
|
|
1451
|
+
const verified = runOcskillJSON([
|
|
1452
|
+
"work", "verify-command", input.slug, task.id, projectRoot,
|
|
1453
|
+
"--run-id", result.runId,
|
|
1454
|
+
"--", command, ...commandArgs,
|
|
1455
|
+
], projectRoot)
|
|
1456
|
+
deterministicReceipts.push(verified.receipt)
|
|
1457
|
+
}
|
|
1458
|
+
const receipt = runOcskillJSON([
|
|
1459
|
+
"work", "agent-receipt", input.slug, task.id, projectRoot,
|
|
1460
|
+
"--run-id", result.runId,
|
|
1461
|
+
"--verdict", "PASS",
|
|
1462
|
+
"--verifier", "ues-verifier",
|
|
1463
|
+
"--session-id", result.verifier.sessionID,
|
|
1464
|
+
"--evidence", result.verifier.evidence,
|
|
1465
|
+
], projectRoot)
|
|
1466
|
+
const completed = runOcskillJSON([
|
|
1467
|
+
"work", "complete", input.slug, task.id, projectRoot,
|
|
1468
|
+
"--run-id", result.runId,
|
|
1469
|
+
"--evidence", result.verifier.evidence,
|
|
1470
|
+
], projectRoot)
|
|
1471
|
+
durableCompleted = true
|
|
1472
|
+
|
|
1473
|
+
if (result.sandbox?.dir) {
|
|
1474
|
+
try { runOcskill(["sandbox", "remove", result.sandbox.dir, projectRoot, "--force", "--delete-branch"], projectRoot) } catch {}
|
|
1475
|
+
}
|
|
1476
|
+
try { void tool.progress({ status: "parallel task " + task.id + " verified and integrated" }) } catch {}
|
|
1477
|
+
return { integration, deterministicReceipts, receipt, completed }
|
|
1478
|
+
} catch (error) {
|
|
1479
|
+
if (!durableCompleted) {
|
|
1480
|
+
if (applied && result.sandbox?.dir) {
|
|
1481
|
+
try { runOcskill(["sandbox", "rollback", result.sandbox.dir, projectRoot], projectRoot) } catch {}
|
|
1482
|
+
} else if (result.sandbox?.dir) {
|
|
1483
|
+
try { runOcskill(["sandbox", "remove", result.sandbox.dir, projectRoot, "--force", "--delete-branch"], projectRoot) } catch {}
|
|
1484
|
+
}
|
|
1485
|
+
if (result.runId) failRunningTask(task.id, result.runId, "parallel integration failed: " + String(error?.message || error))
|
|
1486
|
+
}
|
|
1487
|
+
throw error
|
|
1488
|
+
}
|
|
1489
|
+
},
|
|
1490
|
+
onEvent: (event) => {
|
|
1491
|
+
if (["task.started", "task.completed", "task.failed"].includes(event.type)) {
|
|
1492
|
+
void tool.progress({ status: "parallel " + event.type + " " + event.task })
|
|
1493
|
+
}
|
|
1494
|
+
},
|
|
1495
|
+
})
|
|
1496
|
+
|
|
1497
|
+
const finalStatus = runOcskillJSON(["work", "status", input.slug, projectRoot], projectRoot)
|
|
1498
|
+
return {
|
|
1499
|
+
content: JSON.stringify({
|
|
1500
|
+
schemaVersion: 1,
|
|
1501
|
+
slug: input.slug,
|
|
1502
|
+
model: sharedModel || "opencode-default",
|
|
1503
|
+
singleModel: true,
|
|
1504
|
+
maxConcurrent,
|
|
1505
|
+
rootBaseline,
|
|
1506
|
+
scheduler,
|
|
1507
|
+
work: finalStatus,
|
|
1508
|
+
next: finalStatus.status === "integration-verification"
|
|
1509
|
+
? "Run ues-integration-verifier and record the final integration receipt before finalize."
|
|
1510
|
+
: finalStatus.nextAction,
|
|
1511
|
+
}, null, 2),
|
|
1512
|
+
}
|
|
1207
1513
|
},
|
|
1208
1514
|
})
|
|
1209
1515
|
})
|
|
@@ -1214,7 +1520,7 @@ export default Plugin.define({
|
|
|
1214
1520
|
await ctx.session.hook("context", (event) => {
|
|
1215
1521
|
event.system.push({
|
|
1216
1522
|
type: "text",
|
|
1217
|
-
text: "UES: use the minimum context that preserves correctness. FAST reads the target and nearest evidence with direct skills only; STANDARD/DEEP expand when risk or evidence requires it. Preserve exact contracts
|
|
1523
|
+
text: "UES: use the minimum context that preserves correctness. FAST reads the target and nearest evidence with direct skills only; STANDARD/DEEP expand when risk or evidence requires it. Preserve exact contracts and verify fresh behavior. On Windows, never redirect git diff through PowerShell into a text file; use ocskill diff . --out <file> so the result is UTF-8. If normal Read reports binary for a diff or known text file, use ues.text_read instead of inventing a converted copy. For independent approved tasks, prefer ues.dispatch_parallel when one shared model can safely work in isolated sessions.",
|
|
1218
1524
|
})
|
|
1219
1525
|
const assignment = sessionAssignments.get(event.sessionID)
|
|
1220
1526
|
if (assignment?.resumeRequired && assignment.checkpoint) {
|
|
@@ -1232,17 +1538,32 @@ export default Plugin.define({
|
|
|
1232
1538
|
const config = routerConfig()
|
|
1233
1539
|
if (!config.enabled) return
|
|
1234
1540
|
|
|
1541
|
+
const promptAlias = expandUesPromptAlias(event.prompt?.text, COMMAND_TEMPLATE_DIR)
|
|
1542
|
+
if (promptAlias && event.prompt) {
|
|
1543
|
+
event.prompt.text = promptAlias.text
|
|
1544
|
+
event.metadata = {
|
|
1545
|
+
...event.metadata,
|
|
1546
|
+
uesPromptAlias: {
|
|
1547
|
+
alias: promptAlias.alias,
|
|
1548
|
+
sourceName: promptAlias.sourceName,
|
|
1549
|
+
preferredAgent: promptAlias.agent,
|
|
1550
|
+
transport: "session.prompt",
|
|
1551
|
+
},
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
const promptText = String(event.prompt?.text || "")
|
|
1235
1556
|
let policy = null
|
|
1236
1557
|
try {
|
|
1237
|
-
policy = runOcskillJSON(["task-policy",
|
|
1558
|
+
policy = runOcskillJSON(["task-policy", promptText], projectRoot)
|
|
1238
1559
|
} catch {}
|
|
1239
|
-
const intent = classifyIntent(
|
|
1560
|
+
const intent = classifyIntent(promptText, routingFacts)
|
|
1240
1561
|
const effectiveMaxSkills = Math.max(
|
|
1241
1562
|
1,
|
|
1242
1563
|
Math.min(config.maxSkills, Number(policy?.maxSkills || config.maxSkills)),
|
|
1243
1564
|
)
|
|
1244
1565
|
const selected = []
|
|
1245
|
-
for (const id of [...routeSkillsForPolicy(
|
|
1566
|
+
for (const id of [...routeSkillsForPolicy(promptText, policy, effectiveMaxSkills, routingFacts), ...policySkills(policy)]) {
|
|
1246
1567
|
if (!selected.includes(id)) selected.push(id)
|
|
1247
1568
|
if (selected.length >= effectiveMaxSkills) break
|
|
1248
1569
|
}
|
|
@@ -1266,7 +1587,7 @@ export default Plugin.define({
|
|
|
1266
1587
|
feedbackDomains: routingFacts.feedbackDomains,
|
|
1267
1588
|
acceptedLearningCount: routingFacts.acceptedLearningCount,
|
|
1268
1589
|
},
|
|
1269
|
-
version:
|
|
1590
|
+
version: 13,
|
|
1270
1591
|
effectiveMaxSkills,
|
|
1271
1592
|
},
|
|
1272
1593
|
}
|
|
@@ -1291,4 +1612,4 @@ export default Plugin.define({
|
|
|
1291
1612
|
sessionAssignments.clear()
|
|
1292
1613
|
}
|
|
1293
1614
|
},
|
|
1294
|
-
}
|
|
1615
|
+
}
|