opencode-agent-skill 13.0.0-beta.1 → 14.2.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +1557 -606
  2. package/bin/ocskill.mjs +172 -24
  3. package/docs/OPENCODE-COMPAT.md +34 -95
  4. package/docs/PI-COMPAT.md +188 -0
  5. package/docs/V14-CONTEXT-MEMORY-FABRIC.md +70 -0
  6. package/docs/V14.1-QUALITY-PERFORMANCE-FABRIC.md +114 -0
  7. package/docs/V14.2-TURBO-WEAK-MODEL-RUNTIME.md +448 -0
  8. package/evals/v14/tasks.json +46 -0
  9. package/global-config/agents/executor.md +7 -0
  10. package/global-config/agents/visual-verifier.md +22 -3
  11. package/global-config/commands/run.md +27 -20
  12. package/global-config/plugins/ues-router/command-runtime.js +54 -0
  13. package/global-config/plugins/ues-router/index.js +30 -24
  14. package/global-config/plugins/ues-router/policy-runtime.js +7 -0
  15. package/global-config/plugins/ues-router/router.js +12 -2
  16. package/global-config/skills/ecommerce-engineering/SKILL.md +1 -1
  17. package/global-config/skills/file-upload-engineering/SKILL.md +1 -1
  18. package/global-config/skills/git-safety/SKILL.md +1 -1
  19. package/global-config/skills/nestjs-engineering/SKILL.md +1 -1
  20. package/global-config/skills/performance-engineering/SKILL.md +1 -1
  21. package/global-config/skills/react-native-engineering/SKILL.md +1 -1
  22. package/global-config/skills/rest-api-design/SKILL.md +1 -1
  23. package/global-config/skills/ui-ux-engineering/SKILL.md +1 -1
  24. package/lib/adaptive-context-budget.mjs +97 -0
  25. package/lib/affected-tests.mjs +260 -0
  26. package/lib/benchmark-confidence.mjs +41 -2
  27. package/lib/browser-mcp-routing.mjs +166 -0
  28. package/lib/capability-fabric.mjs +336 -0
  29. package/lib/capability-registry.mjs +9 -0
  30. package/lib/context-engine-v11.mjs +65 -1
  31. package/lib/context-graph-rank.mjs +118 -0
  32. package/lib/context-manifest.mjs +97 -18
  33. package/lib/control-center.mjs +19 -1
  34. package/lib/dynamic-workflow.mjs +3 -1
  35. package/lib/evidence-store.mjs +82 -1
  36. package/lib/hierarchical-context.mjs +215 -0
  37. package/lib/memory-engine.mjs +465 -0
  38. package/lib/model-performance.mjs +33 -8
  39. package/lib/model-policy.mjs +3 -3
  40. package/lib/orchestrator-policy.mjs +5 -209
  41. package/lib/performance-fabric.mjs +229 -0
  42. package/lib/pi-rpc-pool.mjs +433 -0
  43. package/lib/process-hang-detector.mjs +83 -0
  44. package/lib/process-supervisor.mjs +193 -0
  45. package/lib/prompt-cache.mjs +2 -0
  46. package/lib/repo-graph.mjs +53 -2
  47. package/lib/runtime-config.mjs +31 -0
  48. package/lib/safety.mjs +132 -0
  49. package/lib/semantic-index.mjs +52 -3
  50. package/lib/skill-compiler.mjs +128 -0
  51. package/lib/skill-quality.mjs +48 -2
  52. package/lib/task-engine.mjs +66 -5
  53. package/lib/task-policy.mjs +235 -0
  54. package/lib/verification-broker.mjs +284 -0
  55. package/lib/verification-command.mjs +111 -0
  56. package/lib/windows-shim.mjs +35 -0
  57. package/lib/workspace-fingerprint.mjs +198 -0
  58. package/package.json +52 -42
  59. package/pi/extensions/ues-child-runtime.ts +238 -0
  60. package/pi/extensions/ues.ts +3200 -0
  61. package/pi/prompts/ues-audit.md +9 -0
  62. package/pi/prompts/ues-critique.md +9 -0
  63. package/pi/prompts/ues-debug.md +9 -0
  64. package/pi/prompts/ues-feature.md +9 -0
  65. package/pi/prompts/ues-fix.md +9 -0
  66. package/pi/prompts/ues-plan.md +9 -0
  67. package/pi/prompts/ues-research.md +9 -0
  68. package/pi/prompts/ues-resume.md +9 -0
  69. package/pi/prompts/ues-review.md +7 -0
  70. package/pi/prompts/ues-run.md +17 -0
  71. package/pi/prompts/ues-verify.md +9 -0
  72. package/scripts/check-release-consistency.mjs +119 -185
  73. package/scripts/check-runtime-exports.mjs +66 -0
  74. package/scripts/check-source-integrity.mjs +184 -0
  75. package/scripts/eval-pi.mjs +492 -0
  76. package/scripts/install.mjs +16 -0
  77. package/scripts/smoke-package-closure.mjs +110 -0
  78. package/scripts/smoke-packed-install.mjs +24 -11
  79. package/scripts/smoke-pi-extension.mjs +144 -0
  80. package/scripts/uninstall.mjs +44 -0
  81. package/CHANGELOG.md +0 -405
  82. package/docs/DETERMINISTIC-TOOLS.md +0 -105
  83. package/docs/ENGINEERING-DESIGN.md +0 -194
  84. package/docs/EVALS.md +0 -158
  85. package/docs/GITHUB-RULESET.md +0 -50
  86. package/docs/NPM-PUBLISH.md +0 -116
  87. package/docs/RESEARCH-SOURCES.md +0 -37
  88. package/docs/TRACE-SCHEMA.md +0 -122
  89. package/docs/V11-PERCEPTION-ADAPTIVE-EXECUTION.md +0 -75
  90. package/docs/V11-PERCEPTION-ADAPTIVE.md +0 -220
  91. package/docs/V12-WEAK-MODEL-INTELLIGENCE.md +0 -27
  92. package/docs/V13-PARALLEL-WEAK-MODEL-RUNTIME.md +0 -75
  93. package/docs/V7-INTELLIGENCE-RUNTIME.md +0 -166
  94. package/docs/V8-INTELLIGENCE-RELIABILITY.md +0 -206
  95. package/docs/V9-SPEED-INTELLIGENCE.md +0 -102
@@ -125,8 +125,36 @@ export function pairedBenchmarkConfidence(results, options = {}) {
125
125
  initialInputAcceptable,
126
126
  tokensAcceptable,
127
127
  }
128
+
129
+ const baselineContaminated = pairs.filter((pair) => pair.baseline?.baselineIsolated === false).length
130
+ const baselineIsolationUnknown = pairs.filter((pair) => pair.baseline?.baselineIsolated !== true).length - baselineContaminated
131
+ const uesControllerUnproven = pairs.filter((pair) => pair.ues?.telemetry?.controllerUsed !== true).length
132
+ const uesFalsePasses = pairs.filter((pair) =>
133
+ pair.ues?.telemetry?.controllerPass === true &&
134
+ Number(pair.ues?.graderExit) !== 0
135
+ ).length
136
+ const qualityRegressionTolerance = Math.max(0, Number(options.qualityRegressionTolerance || 0))
137
+ const qualityNonRegression = delta >= -qualityRegressionTolerance
138
+ const efficiencySignals = [
139
+ durationRatio == null ? null : durationRatio < 1,
140
+ initialInputRatio == null ? null : initialInputRatio < 1,
141
+ tokenRatio == null ? null : tokenRatio < 1,
142
+ ].filter((value) => value !== null)
143
+ const efficiencyImproved = efficiencySignals.some(Boolean)
144
+ const turboChecks = {
145
+ pairedCoverage: total >= minPairs,
146
+ baselineIsolated: baselineContaminated === 0 && baselineIsolationUnknown === 0,
147
+ uesControllerUsed: uesControllerUnproven === 0,
148
+ qualityNonRegression,
149
+ noSuiteRegression,
150
+ noControllerFalsePass: uesFalsePasses === 0,
151
+ speedAcceptable,
152
+ initialInputAcceptable,
153
+ tokensAcceptable,
154
+ efficiencyImproved,
155
+ }
128
156
  return {
129
- schemaVersion: 2,
157
+ schemaVersion: 3,
130
158
  kind: "ues-paired-benchmark-confidence",
131
159
  pairs: total,
132
160
  bothPass,
@@ -169,5 +197,16 @@ export function pairedBenchmarkConfidence(results, options = {}) {
169
197
  suites,
170
198
  checks,
171
199
  promotionEligible: Object.values(checks).every(Boolean),
200
+ turbo: {
201
+ policy: "quality-non-regression-with-efficiency-gain",
202
+ qualityRegressionTolerance,
203
+ baselineContaminated,
204
+ baselineIsolationUnknown,
205
+ uesControllerUnproven,
206
+ uesFalsePasses,
207
+ efficiencyImproved,
208
+ checks: turboChecks,
209
+ promotionEligible: Object.values(turboChecks).every(Boolean),
210
+ },
172
211
  }
173
- }
212
+ }
@@ -0,0 +1,166 @@
1
+ import { inferTaskCapabilities } from "./capability-registry.mjs"
2
+
3
+ const BROWSER_ROLES = new Set([
4
+ "executor",
5
+ "debugger",
6
+ "verifier",
7
+ "integration-verifier",
8
+ "visual-verifier",
9
+ ])
10
+
11
+ const VISUAL_SIGNAL =
12
+ /(visual fidelity|visual regression|screenshot|figma|pixel|layout|responsive|viewport|accessibility snapshot|giao diện|\bui\b|\bcss\b|styling|kiểm tra hiển thị)/i
13
+
14
+ const PROVIDER_SIGNAL =
15
+ /(playwright|browser automation|browser mcp|chromium|webkit|firefox)/i
16
+
17
+ const BROWSER_TOOL_NAME =
18
+ /(^|[_:.])(browser|playwright)([_:.]|$)|^browser_|^playwright_|mcp.*(?:browser|playwright)/i
19
+
20
+ const ACTION_PRIORITY = [
21
+ ["snapshot", 18],
22
+ ["screenshot", 17],
23
+ ["console", 16],
24
+ ["network", 15],
25
+ ["navigate", 14],
26
+ ["viewport", 13],
27
+ ["click", 12],
28
+ ["fill", 11],
29
+ ["type", 10],
30
+ ["select", 9],
31
+ ["press", 8],
32
+ ["wait", 7],
33
+ ["hover", 6],
34
+ ["locator", 5],
35
+ ["evaluate", 4],
36
+ ["close", 1],
37
+ ]
38
+
39
+ const BLOCKED_BUILTINS = new Set([
40
+ "read",
41
+ "grep",
42
+ "find",
43
+ "ls",
44
+ "bash",
45
+ "powershell",
46
+ "edit",
47
+ "write",
48
+ "ues_cli",
49
+ "ues_execute",
50
+ "ues_dispatch",
51
+ ])
52
+
53
+ function textForTool(tool = {}) {
54
+ return [
55
+ tool.name,
56
+ tool.label,
57
+ tool.description,
58
+ ].filter(Boolean).join(" ")
59
+ }
60
+
61
+ function normalizeExplicitNames(values = []) {
62
+ if (typeof values === "string") values = values.split(",")
63
+ return [...new Set(
64
+ (Array.isArray(values) ? values : [])
65
+ .map((value) => String(value || "").trim())
66
+ .filter(Boolean),
67
+ )]
68
+ }
69
+
70
+ export function visualEvidenceNeeded(text = "") {
71
+ const value = String(text || "")
72
+ const capabilities = inferTaskCapabilities(value, { coding: true })
73
+ return capabilities.required.vision === true || VISUAL_SIGNAL.test(value)
74
+ }
75
+
76
+ export function browserEvidenceNeeded(text = "", role = "") {
77
+ const normalizedRole = String(role || "").replace(/^ues-/, "")
78
+ if (!BROWSER_ROLES.has(normalizedRole)) return false
79
+ if (normalizedRole === "visual-verifier") return true
80
+
81
+ const capabilities = inferTaskCapabilities(String(text || ""), { coding: true })
82
+ return (
83
+ capabilities.required.browser === true ||
84
+ capabilities.required.vision === true ||
85
+ VISUAL_SIGNAL.test(String(text || ""))
86
+ )
87
+ }
88
+
89
+ export function selectBrowserMcpToolNames(tools = [], options = {}) {
90
+ const limit = Math.max(1, Math.min(Number(options.limit || 14), 32))
91
+ const explicit = normalizeExplicitNames(options.explicitNames)
92
+ const rows = []
93
+ const available = new Map()
94
+
95
+ for (const raw of Array.isArray(tools) ? tools : []) {
96
+ const name = String(raw?.name || "").trim()
97
+ if (!name || BLOCKED_BUILTINS.has(name) || name.startsWith("ues_")) continue
98
+ available.set(name, raw)
99
+
100
+ const descriptor = textForTool(raw)
101
+ const providerHit = PROVIDER_SIGNAL.test(descriptor)
102
+ const nameHit = BROWSER_TOOL_NAME.test(name)
103
+ if (!providerHit && !nameHit) continue
104
+
105
+ let score = providerHit ? 100 : 70
106
+ if (/playwright/i.test(descriptor)) score += 30
107
+ if (/^browser_|(?:^|[_:.])browser[_:.]/i.test(name)) score += 20
108
+ for (const [signal, weight] of ACTION_PRIORITY) {
109
+ if (name.toLowerCase().includes(signal)) {
110
+ score += weight
111
+ break
112
+ }
113
+ }
114
+ rows.push({ name, score })
115
+ }
116
+
117
+ const selected = []
118
+ for (const name of explicit) {
119
+ if (available.has(name) && !selected.includes(name)) selected.push(name)
120
+ }
121
+
122
+ for (const row of rows.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name))) {
123
+ if (selected.length >= limit) break
124
+ if (!selected.includes(row.name)) selected.push(row.name)
125
+ }
126
+
127
+ return selected.slice(0, limit)
128
+ }
129
+
130
+ export function selectBrowserToolsForTask(names = [], text = "", role = "") {
131
+ const value = String(text || "").toLowerCase()
132
+ const normalizedRole = String(role || "").replace(/^ues-/, "")
133
+ const visual = visualEvidenceNeeded(text) || normalizedRole === "visual-verifier"
134
+ const interactive = /(click|fill|type|select|press|submit|login|checkout|flow|interaction|form|navigate|đăng nhập|nhấn|điền|chọn)/i.test(value)
135
+ const diagnostics = /(console|network|request|response|error|failed|api|socket|log)/i.test(value)
136
+
137
+ const keep = []
138
+ const addIf = (name, predicate) => {
139
+ const lower = name.toLowerCase()
140
+ if (predicate(lower) && !keep.includes(name)) keep.push(name)
141
+ }
142
+
143
+ for (const name of names) {
144
+ addIf(name, (lower) => /snapshot|accessibility|navigate|close/.test(lower))
145
+ }
146
+ if (visual) {
147
+ for (const name of names) addIf(name, (lower) => /screenshot|viewport|snapshot|accessibility/.test(lower))
148
+ }
149
+ if (interactive) {
150
+ for (const name of names) addIf(name, (lower) => /click|fill|type|select|press|wait|hover|locator|navigate/.test(lower))
151
+ }
152
+ if (diagnostics || normalizedRole === "verifier" || normalizedRole === "integration-verifier") {
153
+ for (const name of names) addIf(name, (lower) => /console|network|request|response/.test(lower))
154
+ }
155
+
156
+ // Keep a small escape hatch for providers whose naming does not expose action semantics.
157
+ if (keep.length < 3) {
158
+ for (const name of names) {
159
+ if (!keep.includes(name)) keep.push(name)
160
+ if (keep.length >= Math.min(6, names.length)) break
161
+ }
162
+ }
163
+
164
+ const limit = visual || interactive ? 10 : 6
165
+ return keep.slice(0, limit)
166
+ }
@@ -0,0 +1,336 @@
1
+ import { existsSync } from "node:fs"
2
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises"
3
+ import { spawnSync } from "node:child_process"
4
+ import path from "node:path"
5
+
6
+ const HEALTH_WEIGHT = { healthy: 40, degraded: 15, unknown: 0, unavailable: -1000 }
7
+ const COST_PENALTY = { low: 0, medium: 5, high: 12 }
8
+ const LATENCY_PENALTY = { fast: 0, medium: 3, slow: 8 }
9
+ const OBSERVATION_FILE = "CAPABILITY-OBSERVATIONS.json"
10
+
11
+ function observationPath(root) {
12
+ return path.join(path.resolve(root), ".ues-learning", OBSERVATION_FILE)
13
+ }
14
+ async function atomicJson(file, value) {
15
+ await mkdir(path.dirname(file), { recursive: true })
16
+ const temp = file + "." + process.pid + "." + Date.now() + ".tmp"
17
+ await writeFile(temp, JSON.stringify(value, null, 2) + "\n", "utf8")
18
+ try { await rename(temp, file) } catch (error) { await rm(temp, { force: true }).catch(() => {}); throw error }
19
+ }
20
+ export async function readCapabilityObservations(root = process.cwd()) {
21
+ try {
22
+ const parsed = JSON.parse(await readFile(observationPath(root), "utf8"))
23
+ return { schemaVersion: 1, updatedAt: parsed.updatedAt || null, capabilities: parsed.capabilities && typeof parsed.capabilities === "object" ? parsed.capabilities : {} }
24
+ } catch { return { schemaVersion: 1, updatedAt: null, capabilities: {} } }
25
+ }
26
+ export async function recordCapabilityObservation(root, capability, providerId, input = {}) {
27
+ if (!capability || !providerId) throw new Error("capability observation requires capability and provider id")
28
+ const state = await readCapabilityObservations(root)
29
+ const previous = state.capabilities?.[capability]?.[providerId] || {}
30
+ const success = input.success === true, failure = input.success === false
31
+ const samples = Number(previous.samples || 0) + (success || failure ? 1 : 0)
32
+ const successes = Number(previous.successes || 0) + (success ? 1 : 0)
33
+ const failures = Number(previous.failures || 0) + (failure ? 1 : 0)
34
+ const latencyMs = Number.isFinite(Number(input.latencyMs)) ? Math.max(0, Number(input.latencyMs)) : null
35
+ const previousLatencySamples = Number(previous.latencySamples || 0)
36
+ const latencySamples = previousLatencySamples + (latencyMs == null ? 0 : 1)
37
+ const avgLatencyMs = latencyMs == null ? (previous.avgLatencyMs ?? null) : ((Number(previous.avgLatencyMs || 0) * previousLatencySamples) + latencyMs) / Math.max(1, latencySamples)
38
+ const next = {
39
+ samples, successes, failures,
40
+ successRate: samples ? successes / samples : 0,
41
+ failureRate: samples ? failures / samples : 0,
42
+ latencySamples, avgLatencyMs: avgLatencyMs == null ? null : Number(avgLatencyMs.toFixed(2)),
43
+ lastLatencyMs: latencyMs, lastObservedAt: new Date().toISOString(),
44
+ lastError: input.error ? String(input.error).slice(0, 500) : null,
45
+ }
46
+ const capabilities = { ...state.capabilities, [capability]: { ...(state.capabilities?.[capability] || {}), [providerId]: next } }
47
+ await atomicJson(observationPath(root), { schemaVersion: 1, updatedAt: next.lastObservedAt, capabilities })
48
+ return next
49
+ }
50
+
51
+ function clamp(value, min = 0, max = 1, fallback = 0) {
52
+ const number = Number(value)
53
+ return Number.isFinite(number) ? Math.max(min, Math.min(max, number)) : fallback
54
+ }
55
+
56
+ const COMMAND_PROBE_TTL_MS = 60_000
57
+ const commandProbeCache = new Map()
58
+
59
+ function commandExists(command) {
60
+ if (!command) return false
61
+ const now = Date.now()
62
+ const cached = commandProbeCache.get(command)
63
+ if (cached && now - cached.checkedAt < COMMAND_PROBE_TTL_MS) return cached.available
64
+ const finder = process.platform === "win32" ? "where" : "which"
65
+ const result = spawnSync(finder, [command], { stdio: "ignore", windowsHide: true })
66
+ const available = result.status === 0
67
+ commandProbeCache.set(command, { available, checkedAt: now })
68
+ return available
69
+ }
70
+
71
+ async function packageDeclared(root, name) {
72
+ try {
73
+ const pkg = JSON.parse(await readFile(path.join(root, "package.json"), "utf8"))
74
+ return Boolean(
75
+ pkg.dependencies?.[name] ||
76
+ pkg.devDependencies?.[name] ||
77
+ pkg.optionalDependencies?.[name] ||
78
+ pkg.peerDependencies?.[name],
79
+ )
80
+ } catch {
81
+ return false
82
+ }
83
+ }
84
+
85
+ function normalizeProvider(provider = {}, index = 0) {
86
+ return {
87
+ id: String(provider.id || `provider-${index + 1}`),
88
+ kind: provider.kind || "builtin",
89
+ capability: provider.capability || null,
90
+ priority: Number.isFinite(Number(provider.priority)) ? Number(provider.priority) : 50,
91
+ quality: clamp(provider.quality, 0, 1, 0.5),
92
+ costClass: ["low", "medium", "high"].includes(provider.costClass) ? provider.costClass : "medium",
93
+ latencyClass: ["fast", "medium", "slow"].includes(provider.latencyClass) ? provider.latencyClass : "medium",
94
+ command: provider.command || null,
95
+ path: provider.path || null,
96
+ package: provider.package || null,
97
+ metadata: provider.metadata || {},
98
+ }
99
+ }
100
+
101
+ export function defaultCapabilityRegistry(root = process.cwd()) {
102
+ return {
103
+ schemaVersion: 1,
104
+ root: path.resolve(root),
105
+ capabilities: {
106
+ "code.search": [
107
+ { id: "ues-semantic-index", kind: "builtin", priority: 100, quality: 0.9, costClass: "low", latencyClass: "fast" },
108
+ ],
109
+ memory: [
110
+ { id: "ues-memory", kind: "builtin", priority: 100, quality: 0.9, costClass: "low", latencyClass: "fast" },
111
+ ],
112
+ evidence: [
113
+ { id: "ues-evidence-store", kind: "builtin", priority: 100, quality: 0.95, costClass: "low", latencyClass: "fast" },
114
+ ],
115
+ "output.compaction": [
116
+ {
117
+ id: "ues-reversible-compactor",
118
+ kind: "builtin",
119
+ priority: 100,
120
+ quality: 0.98,
121
+ costClass: "low",
122
+ latencyClass: "fast",
123
+ metadata: { default: true, reversible: true, lossy: false, scope: "model-visible UES output" },
124
+ },
125
+ {
126
+ id: "rtk-cli",
127
+ kind: "command",
128
+ command: "rtk",
129
+ priority: 82,
130
+ quality: 0.9,
131
+ costClass: "low",
132
+ latencyClass: "fast",
133
+ metadata: { default: false, external: true, experimental: false, scope: "shell-output", upstream: "rtk-ai/rtk" },
134
+ },
135
+ {
136
+ id: "caveman-cli",
137
+ kind: "command",
138
+ command: "caveman",
139
+ priority: 55,
140
+ quality: 0.82,
141
+ costClass: "medium",
142
+ latencyClass: "medium",
143
+ metadata: {
144
+ default: false,
145
+ external: true,
146
+ experimental: true,
147
+ reversible: true,
148
+ upstream: "JuliusBrussee/caveman",
149
+ licenseNote: "External runtime includes BSL-1.1 engine components; do not vendor them into the MIT core.",
150
+ },
151
+ },
152
+ {
153
+ id: "headroom-cli",
154
+ kind: "command",
155
+ command: "headroom",
156
+ priority: 50,
157
+ quality: 0.82,
158
+ costClass: "medium",
159
+ latencyClass: "medium",
160
+ metadata: {
161
+ default: false,
162
+ external: true,
163
+ experimental: true,
164
+ reversible: true,
165
+ outputShaper: false,
166
+ upstream: "headroomlabs-ai/headroom",
167
+ },
168
+ },
169
+ ],
170
+ filesystem: [
171
+ { id: "node-fs", kind: "builtin", priority: 100, quality: 0.95, costClass: "low", latencyClass: "fast" },
172
+ ],
173
+ git: [
174
+ { id: "git-cli", kind: "command", command: "git", priority: 100, quality: 0.95, costClass: "low", latencyClass: "fast" },
175
+ ],
176
+ "agent.host": [
177
+ { id: "pi-cli", kind: "command", command: "pi", priority: 100, quality: 0.95, costClass: "low", latencyClass: "fast" },
178
+ ],
179
+ github: [
180
+ { id: "gh-cli", kind: "command", command: "gh", priority: 80, quality: 0.85, costClass: "low", latencyClass: "fast" },
181
+ ],
182
+ browser: [
183
+ { id: "project-playwright", kind: "package", package: "playwright", priority: 90, quality: 0.9, costClass: "medium", latencyClass: "medium" },
184
+ { id: "project-playwright-core", kind: "package", package: "playwright-core", priority: 70, quality: 0.75, costClass: "medium", latencyClass: "medium" },
185
+ ],
186
+ },
187
+ }
188
+ }
189
+
190
+ async function readCapabilityConfig(root, file) {
191
+ const target = file
192
+ ? path.resolve(file)
193
+ : process.env.UES_CAPABILITY_CONFIG
194
+ ? path.resolve(process.env.UES_CAPABILITY_CONFIG)
195
+ : path.join(path.resolve(root), ".ues-capabilities.json")
196
+ if (!existsSync(target)) return null
197
+ try {
198
+ const parsed = JSON.parse(await readFile(target, "utf8"))
199
+ return parsed && typeof parsed === "object" ? parsed : null
200
+ } catch {
201
+ return null
202
+ }
203
+ }
204
+
205
+ function mergeRegistry(base, extra) {
206
+ if (!extra?.capabilities || typeof extra.capabilities !== "object") return base
207
+ const capabilities = { ...base.capabilities }
208
+ for (const [name, values] of Object.entries(extra.capabilities)) {
209
+ const custom = Array.isArray(values) ? values : []
210
+ const seen = new Set(custom.map((item) => item?.id).filter(Boolean))
211
+ const inherited = (capabilities[name] || []).filter((item) => !seen.has(item.id))
212
+ capabilities[name] = [...custom, ...inherited]
213
+ }
214
+ return { ...base, capabilities }
215
+ }
216
+
217
+ export async function loadCapabilityRegistry(root = process.cwd(), options = {}) {
218
+ root = path.resolve(root)
219
+ const base = options.registry || defaultCapabilityRegistry(root)
220
+ if (options.registry) return base
221
+ return mergeRegistry(base, await readCapabilityConfig(root, options.configFile))
222
+ }
223
+
224
+ export async function probeCapabilityProvider(provider, root = process.cwd()) {
225
+ const normalized = normalizeProvider(provider)
226
+ const checkedAt = new Date().toISOString()
227
+ if (normalized.kind === "builtin") {
228
+ return { ...normalized, status: "healthy", reason: "built-in", checkedAt }
229
+ }
230
+ if (normalized.kind === "command") {
231
+ const available = commandExists(normalized.command)
232
+ return {
233
+ ...normalized,
234
+ status: available ? "healthy" : "unavailable",
235
+ reason: available ? `command:${normalized.command}` : `missing-command:${normalized.command}`,
236
+ checkedAt,
237
+ }
238
+ }
239
+ if (normalized.kind === "path") {
240
+ const target = normalized.path ? path.resolve(root, normalized.path) : null
241
+ const available = Boolean(target && existsSync(target))
242
+ return {
243
+ ...normalized,
244
+ status: available ? "healthy" : "unavailable",
245
+ reason: available ? `path:${normalized.path}` : `missing-path:${normalized.path || ""}`,
246
+ checkedAt,
247
+ }
248
+ }
249
+ if (normalized.kind === "package") {
250
+ const declared = normalized.package ? await packageDeclared(root, normalized.package) : false
251
+ const installed = normalized.package
252
+ ? existsSync(path.join(root, "node_modules", normalized.package))
253
+ : false
254
+ const available = installed || declared
255
+ return {
256
+ ...normalized,
257
+ status: installed ? "healthy" : declared ? "degraded" : "unavailable",
258
+ reason: installed
259
+ ? `installed-package:${normalized.package}`
260
+ : declared
261
+ ? `declared-package:${normalized.package}`
262
+ : `missing-package:${normalized.package || ""}`,
263
+ checkedAt,
264
+ }
265
+ }
266
+ return { ...normalized, status: "unknown", reason: `unknown-kind:${normalized.kind}`, checkedAt }
267
+ }
268
+
269
+ export function selectCapabilityProvider(capability, providers = [], options = {}) {
270
+ const observations = options.observations || {}
271
+ const allowUnknown = options.allowUnknown === true
272
+ const rows = providers.map((provider, index) => {
273
+ const normalized = normalizeProvider(provider, index)
274
+ const status = provider.status || "unknown"
275
+ const observation = observations[normalized.id] || {}
276
+ const failureRate = clamp(observation.failureRate, 0, 1, 0)
277
+ const successRate = clamp(observation.successRate, 0, 1, 0)
278
+ const samples = Math.max(0, Number(observation.samples || 0))
279
+ const observationConfidence = samples > 0 ? Math.min(1, samples / 5) : 0
280
+ const successBoost = successRate * 12 * observationConfidence
281
+ const failurePenalty = failureRate * 35 * observationConfidence
282
+ const healthWeight = HEALTH_WEIGHT[status] ?? HEALTH_WEIGHT.unknown
283
+ const eligible = status === "healthy" || status === "degraded" || (allowUnknown && status === "unknown")
284
+ const score =
285
+ normalized.priority +
286
+ normalized.quality * 20 +
287
+ successBoost +
288
+ healthWeight -
289
+ (COST_PENALTY[normalized.costClass] ?? 5) -
290
+ (LATENCY_PENALTY[normalized.latencyClass] ?? 3) -
291
+ failurePenalty
292
+ return { ...normalized, status, eligible, samples, successRate, failureRate, observationConfidence: Number(observationConfidence.toFixed(3)), score: Number(score.toFixed(3)), reason: provider.reason || null }
293
+ })
294
+ const eligible = rows.filter((item) => item.eligible).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
295
+ return {
296
+ schemaVersion: 1,
297
+ capability,
298
+ selected: eligible[0] || null,
299
+ fallbacks: eligible.slice(1),
300
+ candidates: rows.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id)),
301
+ fallbackNeeded: eligible.length === 0,
302
+ }
303
+ }
304
+
305
+ export async function capabilityFabricStatus(root = process.cwd(), options = {}) {
306
+ root = path.resolve(root)
307
+ const registry = await loadCapabilityRegistry(root, options)
308
+ const persistedObservations = options.observations ? null : await readCapabilityObservations(root)
309
+ const capabilities = {}
310
+ const rows = []
311
+ for (const [name, providers] of Object.entries(registry.capabilities || {})) {
312
+ const probed = []
313
+ for (const provider of providers || []) probed.push(await probeCapabilityProvider(provider, root))
314
+ const selected = selectCapabilityProvider(name, probed, {
315
+ observations: options.observations?.[name] || persistedObservations?.capabilities?.[name] || {},
316
+ allowUnknown: options.allowUnknown,
317
+ })
318
+ capabilities[name] = selected
319
+ rows.push({
320
+ capability: name,
321
+ selected: selected.selected?.id || null,
322
+ status: selected.selected?.status || "unavailable",
323
+ fallbacks: selected.fallbacks.map((item) => item.id),
324
+ })
325
+ }
326
+ return {
327
+ schemaVersion: 1,
328
+ root,
329
+ generatedAt: new Date().toISOString(),
330
+ rows,
331
+ capabilities,
332
+ healthyCapabilities: rows.filter((item) => item.selected).length,
333
+ totalCapabilities: rows.length,
334
+ observationState: persistedObservations ? { updatedAt: persistedObservations.updatedAt, capabilities: Object.keys(persistedObservations.capabilities || {}).length } : null,
335
+ }
336
+ }
@@ -107,3 +107,12 @@ export function modelCandidatesFromPolicy(policy = {}) {
107
107
  export function roleCapabilityRequirements(role) {
108
108
  return { ...(ROLE_REQUIREMENTS[role] || {}) }
109
109
  }
110
+
111
+
112
+ export {
113
+ capabilityFabricStatus,
114
+ defaultCapabilityRegistry,
115
+ loadCapabilityRegistry,
116
+ probeCapabilityProvider,
117
+ selectCapabilityProvider,
118
+ } from "./capability-fabric.mjs"
@@ -4,6 +4,8 @@ import { putEvidence } from "./evidence-store.mjs"
4
4
  import { inferTaskCapabilities } from "./capability-registry.mjs"
5
5
  import { buildPromptEnvelope, comparePromptEnvelopes } from "./prompt-cache.mjs"
6
6
  import { measureContextQuality } from "./context-quality.mjs"
7
+ import { retrieveMemories } from "./memory-engine.mjs"
8
+ import { capabilityFabricStatus } from "./capability-fabric.mjs"
7
9
 
8
10
  function taskText(task = {}) {
9
11
  return [
@@ -14,6 +16,20 @@ function taskText(task = {}) {
14
16
  ].filter(Boolean).join(" ")
15
17
  }
16
18
 
19
+ function taskFilesForMemory(task = {}) {
20
+ const files = task.files
21
+ const values = []
22
+ if (Array.isArray(files)) values.push(...files)
23
+ else if (files && typeof files === "object") {
24
+ for (const value of Object.values(files)) {
25
+ if (Array.isArray(value)) values.push(...value)
26
+ else if (typeof value === "string") values.push(value)
27
+ }
28
+ }
29
+ if (Array.isArray(task.requiredFiles)) values.push(...task.requiredFiles)
30
+ return [...new Set(values.filter(Boolean))]
31
+ }
32
+
17
33
  function rankExcerpt(item = {}, task = {}) {
18
34
  const text = taskText(task).toLowerCase()
19
35
  const pathValue = String(item.path || "").toLowerCase()
@@ -91,6 +107,8 @@ export async function buildAdaptiveTaskContext(root, task, options = {}) {
91
107
  strategy: options.strategy || policy?.profile?.contextStrategy || "incremental-semantic+git",
92
108
  semanticMaxFiles: options.semanticMaxFiles,
93
109
  maxFiles: options.maxFiles,
110
+ changedFiles: options.changedFiles,
111
+ workspaceFingerprint: options.workspaceFingerprint,
94
112
  })
95
113
 
96
114
  const externalized = await externalizeContextExcerpts(root, manifest, {
@@ -99,6 +117,24 @@ export async function buildAdaptiveTaskContext(root, task, options = {}) {
99
117
  inlineChars: options.inlineChars,
100
118
  })
101
119
 
120
+ const memoryRetrieval = await retrieveMemories(root, taskText(task), {
121
+ files: taskFilesForMemory(task),
122
+ limit: options.memoryLimit ?? 6,
123
+ taskClass: options.taskClass || task.type || policy.executionProfile || policy.profile?.name || null,
124
+ touch: options.touchMemories !== false,
125
+ }).catch(() => ({ schemaVersion: 1, query: taskText(task), eligible: 0, results: [] }))
126
+ const memories = memoryRetrieval.results || []
127
+ const fabric = await capabilityFabricStatus(root).catch(() => null)
128
+ const providerHints = (fabric?.rows || [])
129
+ .filter((row) => row.selected)
130
+ .slice(0, 12)
131
+ .map((row) => ({
132
+ capability: row.capability,
133
+ selected: row.selected,
134
+ status: row.status,
135
+ fallbacks: (row.fallbacks || []).slice(0, 3),
136
+ }))
137
+
102
138
  const promptEnvelope = buildPromptEnvelope({
103
139
  invariants: options.invariants || "evidence-first; scoped edits; fresh verification; no unsupported completion claims",
104
140
  role: options.role || "executor",
@@ -114,6 +150,23 @@ export async function buildAdaptiveTaskContext(root, task, options = {}) {
114
150
  ...(externalized.manifest?.rankedReferences || []).slice(0, 12).map((item) => item.path),
115
151
  ...(options.evidence || []),
116
152
  ],
153
+ memories: memories.map((item) => ({
154
+ id: item.id,
155
+ type: item.type,
156
+ scope: item.scope,
157
+ content: item.content,
158
+ confidence: item.confidence,
159
+ files: item.files,
160
+ retrieval: item.retrieval,
161
+ })),
162
+ contextHints: {
163
+ hierarchy: (externalized.manifest?.hierarchy?.scopes || []).slice(0, 6).map((item) => ({
164
+ path: item.path,
165
+ score: item.score,
166
+ l0: item.l0,
167
+ })),
168
+ providers: providerHints,
169
+ },
117
170
  recentFailure: options.recentFailure || null,
118
171
  nextAction: options.nextAction || null,
119
172
  recentMessages: options.recentMessages || [],
@@ -132,6 +185,17 @@ export async function buildAdaptiveTaskContext(root, task, options = {}) {
132
185
  evidenceBudget,
133
186
  contextManifest: externalized.manifest,
134
187
  contextQuality,
188
+ memories,
189
+ memoryRetrieval: {
190
+ schemaVersion: memoryRetrieval.schemaVersion || 1,
191
+ eligible: memoryRetrieval.eligible || 0,
192
+ returned: memories.length,
193
+ },
194
+ capabilityFabric: {
195
+ healthyCapabilities: fabric?.healthyCapabilities || 0,
196
+ totalCapabilities: fabric?.totalCapabilities || 0,
197
+ providers: providerHints,
198
+ },
135
199
  evidenceStore: {
136
200
  refs: externalized.externalized.length,
137
201
  externalizedBytes: externalized.externalizedBytes,
@@ -147,4 +211,4 @@ export async function buildAdaptiveTaskContext(root, task, options = {}) {
147
211
  ...(cache || {}),
148
212
  },
149
213
  }
150
- }
214
+ }