opencode-agent-skill 14.4.0 → 15.1.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/README.md +34 -16
- package/docs/PI-COMPAT.md +151 -2
- package/docs/V15-MANAGED-RUNTIME.md +128 -0
- package/global-config/agents/architect.md +63 -1
- package/global-config/agents/executor.md +7 -0
- package/global-config/agents/plan-checker.md +8 -1
- package/global-config/agents/verifier.md +3 -3
- package/global-config/skills/database-engineering/references/workflow.md +15 -0
- package/global-config/skills/engineering-orchestrator/references/long-horizon.md +21 -11
- package/lib/activity-deadline.mjs +70 -0
- package/lib/adaptive-context-budget.mjs +25 -5
- package/lib/affected-tests.mjs +38 -5
- package/lib/capability-fabric.mjs +17 -0
- package/lib/code-intelligence/index.mjs +3 -2
- package/lib/code-intelligence/lsp-provider.mjs +326 -52
- package/lib/compaction-resume-guard.mjs +255 -0
- package/lib/completion-auditor.mjs +25 -5
- package/lib/context-engine-v11.mjs +20 -7
- package/lib/evidence-store.mjs +92 -8
- package/lib/execution-contract.mjs +592 -0
- package/lib/leaf-runtime-optimizer.mjs +76 -0
- package/lib/memory-engine.mjs +189 -16
- package/lib/performance-fabric.mjs +262 -107
- package/lib/pi-child-invocation.mjs +65 -0
- package/lib/pi-rpc-pool.mjs +61 -9
- package/lib/plan-salvage.mjs +124 -0
- package/lib/planning-speed-policy.mjs +131 -0
- package/lib/repo-graph.mjs +1 -1
- package/lib/repo-inspect.mjs +1 -0
- package/lib/runtime-artifacts.mjs +42 -0
- package/lib/runtime-events.mjs +33 -1
- package/lib/safety.mjs +1 -0
- package/lib/semantic-index.mjs +1 -1
- package/lib/service-manager.mjs +526 -0
- package/lib/session-display.mjs +49 -0
- package/lib/subagent-artifacts.mjs +182 -0
- package/lib/task-graph.mjs +87 -0
- package/lib/task-policy.mjs +150 -4
- package/lib/trajectory.mjs +111 -1
- package/lib/turbo-fast-path.mjs +44 -0
- package/lib/verification-broker.mjs +32 -27
- package/lib/workspace-fingerprint.mjs +45 -19
- package/lib/workspace-hygiene.mjs +481 -0
- package/lib/workspace-root.mjs +49 -0
- package/lib/worktree-sandbox.mjs +234 -11
- package/package.json +8 -5
- package/pi/extensions/ues-child-runtime.ts +170 -4
- package/pi/extensions/ues.ts +2063 -188
- package/scripts/check-release-consistency.mjs +25 -2
- package/scripts/check-source-integrity.mjs +471 -4
- package/scripts/eval-pi.mjs +76 -36
- package/scripts/smoke-pi-extension.mjs +1 -1
- package/pi/prompts/ues-run.md +0 -17
|
@@ -13,6 +13,11 @@ When the user explicitly chooses the long-horizon workflow (for example with `/u
|
|
|
13
13
|
STATE.json
|
|
14
14
|
EVIDENCE.json
|
|
15
15
|
EVENTS.jsonl
|
|
16
|
+
EXECUTION_CONTRACT.json
|
|
17
|
+
FINAL_VERDICTS.json
|
|
18
|
+
phases/
|
|
19
|
+
MANIFEST.json
|
|
20
|
+
phase-XX-<slug>.json
|
|
16
21
|
tasks/
|
|
17
22
|
reports/
|
|
18
23
|
```
|
|
@@ -21,11 +26,15 @@ The directory is git-ignored execution state, not hidden reasoning. Store requir
|
|
|
21
26
|
|
|
22
27
|
## Machine-enforced gates
|
|
23
28
|
|
|
24
|
-
The
|
|
29
|
+
The runtime enforces seven boundaries:
|
|
25
30
|
|
|
26
|
-
1. **
|
|
27
|
-
2. **
|
|
28
|
-
3. **
|
|
31
|
+
1. **Inherited-work gate** — snapshot source-facing dirty paths before planning. UES children may not discard them with restore/checkout/stash/clean/reset, and planned writers must declare every touched file.
|
|
32
|
+
2. **Local-env gate** — local `.env*` runtime inputs are no-write by default; only explicit user authorization permits local env mutation. Repository templates remain writable.
|
|
33
|
+
3. **Plan gate** — `work plan` leaves the item in `awaiting-plan-approval`. Long/high-risk plans require a structured `plan-verification` receipt bound to the current plan hash before `work approve-plan` succeeds.
|
|
34
|
+
4. **Phase gate** — explicit execution phases become deterministic previous-phase barriers. Constraint-only phases stay global invariants.
|
|
35
|
+
5. **Concurrent state gate** — all mutable `STATE.json` and `EVIDENCE.json` operations use a per-work-item lock plus atomic file replacement, preventing safe-wave executors from losing each other's state.
|
|
36
|
+
6. **Integration gate** — long/high-risk source completion requires a structured `integration-verification` receipt bound to the current workspace fingerprint. Finalization rejects any later workspace change.
|
|
37
|
+
7. **Verdict matrix gate** — source, runtime, database cleanup and real-device proof remain independent. Missing required evidence is reported as not verified rather than promoted to PASS.
|
|
29
38
|
|
|
30
39
|
## Pipeline
|
|
31
40
|
|
|
@@ -33,18 +42,19 @@ The V6 engine enforces three boundaries:
|
|
|
33
42
|
2. Write observable acceptance criteria into `SPEC.md`.
|
|
34
43
|
3. Initialize state with `ocskill work init`.
|
|
35
44
|
4. Create `PLAN.json` following the plan schema and import it with `ocskill work plan`.
|
|
36
|
-
5.
|
|
37
|
-
6.
|
|
38
|
-
7.
|
|
45
|
+
5. If the user supplied explicit `PHASE N` sections, preserve every execution phase in the plan and let UES add phase barriers; constraint-only phases remain invariants.
|
|
46
|
+
6. Run `ues-plan-checker`. If it returns PASS, create `work gate-receipt <slug> plan` and persist approval with `work approve-plan --receipt-file ...`.
|
|
47
|
+
7. Persist the execution contract/phase artifacts and use `ocskill task-graph` to compute dependency-safe waves.
|
|
48
|
+
8. For each ready task:
|
|
39
49
|
- on OpenCode V2, if two or more approved tasks are independent, prefer `ues.dispatch_parallel` so one shared model can execute them concurrently with isolated worktrees, leases, independent verifier sessions and serialized integration;
|
|
40
50
|
- otherwise prefer `ues.dispatch_task`, which performs `work start`, creates a fresh `ues-executor` session, selects the configured attempt-based model tier, prompts it with a bounded context pack and waits for completion;
|
|
41
51
|
- inspect the child diff and verification;
|
|
42
52
|
- record at least one successful `work verify-command` receipt for long/high-risk work;
|
|
43
53
|
- persist `work complete --evidence ...` or `work fail --reason ...`.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
9. On failure, re-diagnose rather than stacking patches. A later `ues.dispatch_task` attempt can escalate from standard to heavy when configured.
|
|
55
|
+
10. After all tasks complete, run `ues-integration-verifier`.
|
|
56
|
+
11. Persist the verifier's actual result with a structured integration receipt plus `ocskill work verify-integration --receipt-file ...`.
|
|
57
|
+
12. Emit the independent final verdict matrix and persist `FINAL_VERDICTS.json`. Only a recorded PASS with an unchanged workspace can be finalized.
|
|
48
58
|
|
|
49
59
|
## Parallelism
|
|
50
60
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
function finite(value, fallback, min = 0, max = Number.MAX_SAFE_INTEGER) {
|
|
2
|
+
const parsed = Number(value)
|
|
3
|
+
if (!Number.isFinite(parsed)) return fallback
|
|
4
|
+
return Math.max(min, Math.min(max, Math.trunc(parsed)))
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function createAdaptiveDeadline(options = {}, startedAt = Date.now()) {
|
|
8
|
+
const initialMs = finite(options.hardTimeoutMs, 60_000, 1_000)
|
|
9
|
+
const absoluteMs = Math.max(
|
|
10
|
+
initialMs,
|
|
11
|
+
finite(options.absoluteHardTimeoutMs, initialMs, initialMs),
|
|
12
|
+
)
|
|
13
|
+
const extensionMs = finite(options.activityExtensionMs, 0, 0, absoluteMs)
|
|
14
|
+
const activityWindowMs = finite(
|
|
15
|
+
options.activityWindowMs,
|
|
16
|
+
Math.max(5_000, Math.min(extensionMs || initialMs, 30_000)),
|
|
17
|
+
1_000,
|
|
18
|
+
absoluteMs,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
let deadlineAt = startedAt + initialMs
|
|
22
|
+
const absoluteAt = startedAt + absoluteMs
|
|
23
|
+
let extensions = 0
|
|
24
|
+
|
|
25
|
+
function maybeExtend(now = Date.now(), lastActivityAt = 0) {
|
|
26
|
+
if (extensionMs <= 0 || deadlineAt >= absoluteAt) {
|
|
27
|
+
return { extended: false, deadlineAt, absoluteAt, extensions }
|
|
28
|
+
}
|
|
29
|
+
if (now - Number(lastActivityAt || 0) > activityWindowMs) {
|
|
30
|
+
return { extended: false, deadlineAt, absoluteAt, extensions }
|
|
31
|
+
}
|
|
32
|
+
const next = Math.min(absoluteAt, Math.max(deadlineAt, now + extensionMs))
|
|
33
|
+
if (next <= deadlineAt) {
|
|
34
|
+
return { extended: false, deadlineAt, absoluteAt, extensions }
|
|
35
|
+
}
|
|
36
|
+
deadlineAt = next
|
|
37
|
+
extensions += 1
|
|
38
|
+
return { extended: true, deadlineAt, absoluteAt, extensions }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function shouldAbort(now = Date.now(), lastActivityAt = 0) {
|
|
42
|
+
if (now < deadlineAt) {
|
|
43
|
+
return { abort: false, deadlineAt, absoluteAt, extensions }
|
|
44
|
+
}
|
|
45
|
+
const extension = maybeExtend(now, lastActivityAt)
|
|
46
|
+
if (extension.extended) {
|
|
47
|
+
return { abort: false, ...extension }
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
abort: true,
|
|
51
|
+
reason: now >= absoluteAt ? "absolute-hard-timeout" : "hard-timeout",
|
|
52
|
+
deadlineAt,
|
|
53
|
+
absoluteAt,
|
|
54
|
+
extensions,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
startedAt,
|
|
60
|
+
initialMs,
|
|
61
|
+
absoluteMs,
|
|
62
|
+
extensionMs,
|
|
63
|
+
activityWindowMs,
|
|
64
|
+
get deadlineAt() { return deadlineAt },
|
|
65
|
+
get absoluteAt() { return absoluteAt },
|
|
66
|
+
get extensions() { return extensions },
|
|
67
|
+
maybeExtend,
|
|
68
|
+
shouldAbort,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -59,20 +59,35 @@ export function adaptiveContextBudget(taskPolicy = {}, role = "executor", attemp
|
|
|
59
59
|
const profile = String(taskPolicy.executionProfile || taskPolicy.profile?.name || "standard")
|
|
60
60
|
const highRisk = taskPolicy.risk === "high" || taskPolicy.risk === "critical"
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
// away security/payment/schema evidence for latency.
|
|
64
|
-
if (highRisk || options.disabled === true) {
|
|
62
|
+
if (options.disabled === true) {
|
|
65
63
|
return {
|
|
66
64
|
schemaVersion: 1,
|
|
67
65
|
budget: base,
|
|
68
66
|
baseBudget: base,
|
|
69
67
|
adaptive: false,
|
|
70
|
-
reason:
|
|
68
|
+
reason: "disabled",
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
const table = ROLE_TARGETS[profile] || ROLE_TARGETS.standard
|
|
75
73
|
const target = clamp(table?.[role], base, 4_000, base)
|
|
74
|
+
const plannerRole = role === "architect" || role === "plan-checker"
|
|
75
|
+
|
|
76
|
+
// High-risk execution/verification keeps the full evidence ceiling. Read-only
|
|
77
|
+
// planning roles are allowed a bounded context because they can use targeted
|
|
78
|
+
// repository tools and are independently plan-gated before any write begins.
|
|
79
|
+
if (highRisk && !plannerRole) {
|
|
80
|
+
return {
|
|
81
|
+
schemaVersion: 1,
|
|
82
|
+
budget: base,
|
|
83
|
+
baseBudget: base,
|
|
84
|
+
adaptive: false,
|
|
85
|
+
reason: "high-risk-execution-preserves-base-budget",
|
|
86
|
+
profile,
|
|
87
|
+
role,
|
|
88
|
+
attempt: Math.max(1, Math.trunc(Number(attempt || 1))),
|
|
89
|
+
}
|
|
90
|
+
}
|
|
76
91
|
const normalizedAttempt = Math.max(1, Math.trunc(Number(attempt || 1)))
|
|
77
92
|
|
|
78
93
|
// Failed attempts expand deterministically back toward the policy ceiling.
|
|
@@ -92,6 +107,11 @@ export function adaptiveContextBudget(taskPolicy = {}, role = "executor", attemp
|
|
|
92
107
|
profile,
|
|
93
108
|
role,
|
|
94
109
|
attempt: normalizedAttempt,
|
|
95
|
-
reason:
|
|
110
|
+
reason:
|
|
111
|
+
highRisk && plannerRole
|
|
112
|
+
? "high-risk-planner-role-bounded"
|
|
113
|
+
: budget < base
|
|
114
|
+
? "role-bounded-expand-on-failure"
|
|
115
|
+
: "policy-ceiling",
|
|
96
116
|
}
|
|
97
117
|
}
|
package/lib/affected-tests.mjs
CHANGED
|
@@ -6,9 +6,10 @@ import { runtimeWorkspaceFingerprint } from "./workspace-fingerprint.mjs"
|
|
|
6
6
|
|
|
7
7
|
const TEST_RE = /(?:^|[/.\\])(?:__tests__|tests?|spec)(?:[/.\\]|$)|\.(?:test|spec|e2e-spec)\.[cm]?[jt]sx?$/i
|
|
8
8
|
const SOURCE_EXT = new Set([".js",".jsx",".mjs",".cjs",".ts",".tsx",".py",".java",".kt",".kts",".cs",".go",".rs",".dart",".swift"])
|
|
9
|
-
const SKIP = new Set([".git","node_modules",".next","dist","build","coverage",".venv","venv","target","Pods","DerivedData",".gradle",".ues-cache",".ues-work",".ues-sandboxes",".ues-traces",".ues-learning",".ues-dashboard",".ues-memory",".ues-evals"])
|
|
9
|
+
const SKIP = new Set([".git","node_modules",".next","dist","build","coverage",".venv","venv","target","Pods","DerivedData",".gradle",".ues-cache",".ues-work",".ues-sandboxes",".ues-traces",".ues-learning",".ues-dashboard",".ues-memory",".ues-evals",".ues-services"])
|
|
10
10
|
const AFFECTED_TEST_CACHE = new Map()
|
|
11
11
|
const AFFECTED_TEST_INFLIGHT = new Map()
|
|
12
|
+
const TEST_CONTENT_CACHE = new Map()
|
|
12
13
|
|
|
13
14
|
function git(root, args) {
|
|
14
15
|
return spawnSync("git", args, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 })
|
|
@@ -47,6 +48,37 @@ async function walk(root, options = {}) {
|
|
|
47
48
|
return rows
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
async function projectFiles(root, options = {}) {
|
|
52
|
+
const indexed = git(root, ["ls-files", "-co", "--exclude-standard", "-z"])
|
|
53
|
+
if (indexed.status === 0) {
|
|
54
|
+
const files = String(indexed.stdout || "")
|
|
55
|
+
.split("\0")
|
|
56
|
+
.map((value) => normalize(value))
|
|
57
|
+
.filter(Boolean)
|
|
58
|
+
.filter((relative) => !relative.split("/").some((part) => SKIP.has(part)))
|
|
59
|
+
.map((relative) => path.resolve(root, relative))
|
|
60
|
+
return { files: [...new Set(files)], source: "git-index" }
|
|
61
|
+
}
|
|
62
|
+
return { files: await walk(root, options), source: "filesystem-walk" }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function cachedTestContent(file, info) {
|
|
66
|
+
if (!info?.size || info.size > 512 * 1024) return ""
|
|
67
|
+
const key = path.resolve(file)
|
|
68
|
+
const signature = String(info.size) + ":" + String(Math.trunc(Number(info.mtimeMs || 0)))
|
|
69
|
+
const cached = TEST_CONTENT_CACHE.get(key)
|
|
70
|
+
if (cached?.signature === signature) return cached.content
|
|
71
|
+
const content = await readFile(key, "utf8").catch(() => "")
|
|
72
|
+
TEST_CONTENT_CACHE.delete(key)
|
|
73
|
+
TEST_CONTENT_CACHE.set(key, { signature, content })
|
|
74
|
+
while (TEST_CONTENT_CACHE.size > 1200) {
|
|
75
|
+
const oldest = TEST_CONTENT_CACHE.keys().next().value
|
|
76
|
+
if (!oldest) break
|
|
77
|
+
TEST_CONTENT_CACHE.delete(oldest)
|
|
78
|
+
}
|
|
79
|
+
return content
|
|
80
|
+
}
|
|
81
|
+
|
|
50
82
|
export function gitChangedFiles(root = process.cwd()) {
|
|
51
83
|
root = path.resolve(root)
|
|
52
84
|
const inside = git(root, ["rev-parse", "--is-inside-work-tree"])
|
|
@@ -190,7 +222,8 @@ export async function resolveAffectedTests(root = process.cwd(), options = {}) {
|
|
|
190
222
|
}
|
|
191
223
|
|
|
192
224
|
const compute = async () => {
|
|
193
|
-
const
|
|
225
|
+
const inventory = await projectFiles(root, options)
|
|
226
|
+
const files = inventory.files
|
|
194
227
|
const tests = files
|
|
195
228
|
.map((file) => normalize(path.relative(root, file)))
|
|
196
229
|
.filter((file) => TEST_RE.test(file))
|
|
@@ -198,9 +231,7 @@ export async function resolveAffectedTests(root = process.cwd(), options = {}) {
|
|
|
198
231
|
const ranked = []
|
|
199
232
|
for (const testFile of tests) {
|
|
200
233
|
const info = await stat(path.join(root, testFile)).catch(() => null)
|
|
201
|
-
const content =
|
|
202
|
-
? await readFile(path.join(root, testFile), "utf8").catch(() => "")
|
|
203
|
-
: ""
|
|
234
|
+
const content = await cachedTestContent(path.join(root, testFile), info)
|
|
204
235
|
let best = { score: 0, reasons: [], changedFile: null }
|
|
205
236
|
for (const changedFile of changed) {
|
|
206
237
|
const current = scoreTest(testFile, changedFile, content)
|
|
@@ -232,6 +263,7 @@ export async function resolveAffectedTests(root = process.cwd(), options = {}) {
|
|
|
232
263
|
suggestedCommands,
|
|
233
264
|
truncated: ranked.length > selected.length,
|
|
234
265
|
cacheHit: false,
|
|
266
|
+
inventorySource: inventory.source,
|
|
235
267
|
}
|
|
236
268
|
}
|
|
237
269
|
|
|
@@ -257,4 +289,5 @@ export async function resolveAffectedTests(root = process.cwd(), options = {}) {
|
|
|
257
289
|
export function clearAffectedTestCache() {
|
|
258
290
|
AFFECTED_TEST_CACHE.clear()
|
|
259
291
|
AFFECTED_TEST_INFLIGHT.clear()
|
|
292
|
+
TEST_CONTENT_CACHE.clear()
|
|
260
293
|
}
|
|
@@ -129,6 +129,23 @@ export function defaultCapabilityRegistry(root = process.cwd()) {
|
|
|
129
129
|
"context.reversible": [
|
|
130
130
|
{ id: "ues-evidence-context", kind: "builtin", priority: 120, quality: 0.98, costClass: "low", latencyClass: "fast", metadata: { reversible: true, searchable: true, levels: ["T1", "T2", "T3", "raw"] } },
|
|
131
131
|
],
|
|
132
|
+
"runtime.service": [
|
|
133
|
+
{
|
|
134
|
+
id: "ues-managed-service",
|
|
135
|
+
kind: "builtin",
|
|
136
|
+
priority: 125,
|
|
137
|
+
quality: 0.98,
|
|
138
|
+
costClass: "low",
|
|
139
|
+
latencyClass: "fast",
|
|
140
|
+
metadata: {
|
|
141
|
+
shellFree: true,
|
|
142
|
+
readiness: ["port", "log", "process"],
|
|
143
|
+
actions: ["start", "wait-ready", "status", "logs", "stop", "restart"],
|
|
144
|
+
boundedLifetime: true,
|
|
145
|
+
evidenceLogs: true,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
],
|
|
132
149
|
memory: [
|
|
133
150
|
{ id: "ues-memory", kind: "builtin", priority: 100, quality: 0.9, costClass: "low", latencyClass: "fast" },
|
|
134
151
|
],
|
|
@@ -3,7 +3,7 @@ import { spawnSync } from "node:child_process"
|
|
|
3
3
|
import path from "node:path"
|
|
4
4
|
import { querySemanticIndex } from "../semantic-index.mjs"
|
|
5
5
|
import { anchoredLines, applyAnchoredEdits } from "./edit-anchor.mjs"
|
|
6
|
-
import { diagnoseCode, lspProviderStatus } from "./lsp-provider.mjs"
|
|
6
|
+
import { diagnoseCode, lspOperation, lspProviderStatus, LSP_OPERATIONS } from "./lsp-provider.mjs"
|
|
7
7
|
|
|
8
8
|
const AST_COMMANDS = ["ast-grep", "sg"]
|
|
9
9
|
const commandCache = new Map()
|
|
@@ -40,6 +40,7 @@ export function probeCodeIntelligence(file = "") {
|
|
|
40
40
|
semanticIndex: true,
|
|
41
41
|
astProvider,
|
|
42
42
|
lsp: lspProviderStatus(file),
|
|
43
|
+
lspOperations: [...LSP_OPERATIONS],
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -92,4 +93,4 @@ export async function searchCodeIntelligence(root, query, options = {}) {
|
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
export { diagnoseCode, lspProviderStatus } from "./lsp-provider.mjs"
|
|
96
|
+
export { diagnoseCode, lspOperation, lspProviderStatus, LSP_OPERATIONS } from "./lsp-provider.mjs"
|