opencode-agent-skill 13.0.0-beta.2 → 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.
- package/README.md +1556 -607
- package/bin/ocskill.mjs +172 -24
- package/docs/OPENCODE-COMPAT.md +34 -97
- package/docs/PI-COMPAT.md +188 -0
- package/docs/V14-CONTEXT-MEMORY-FABRIC.md +70 -0
- package/docs/V14.1-QUALITY-PERFORMANCE-FABRIC.md +114 -0
- package/docs/V14.2-TURBO-WEAK-MODEL-RUNTIME.md +448 -0
- package/evals/v14/tasks.json +46 -0
- package/global-config/agents/executor.md +7 -0
- package/global-config/agents/visual-verifier.md +22 -3
- package/global-config/plugins/ues-router/index.js +13 -8
- package/global-config/plugins/ues-router/policy-runtime.js +7 -0
- package/global-config/plugins/ues-router/router.js +12 -2
- package/global-config/skills/ecommerce-engineering/SKILL.md +1 -1
- package/global-config/skills/file-upload-engineering/SKILL.md +1 -1
- package/global-config/skills/git-safety/SKILL.md +1 -1
- package/global-config/skills/nestjs-engineering/SKILL.md +1 -1
- package/global-config/skills/performance-engineering/SKILL.md +1 -1
- package/global-config/skills/react-native-engineering/SKILL.md +1 -1
- package/global-config/skills/rest-api-design/SKILL.md +1 -1
- package/global-config/skills/ui-ux-engineering/SKILL.md +1 -1
- package/lib/adaptive-context-budget.mjs +97 -0
- package/lib/affected-tests.mjs +260 -0
- package/lib/benchmark-confidence.mjs +41 -2
- package/lib/browser-mcp-routing.mjs +166 -0
- package/lib/capability-fabric.mjs +336 -0
- package/lib/capability-registry.mjs +9 -0
- package/lib/context-engine-v11.mjs +65 -1
- package/lib/context-graph-rank.mjs +118 -0
- package/lib/context-manifest.mjs +97 -18
- package/lib/control-center.mjs +19 -1
- package/lib/dynamic-workflow.mjs +3 -1
- package/lib/evidence-store.mjs +82 -1
- package/lib/hierarchical-context.mjs +215 -0
- package/lib/memory-engine.mjs +465 -0
- package/lib/model-performance.mjs +33 -8
- package/lib/model-policy.mjs +3 -3
- package/lib/orchestrator-policy.mjs +5 -209
- package/lib/performance-fabric.mjs +229 -0
- package/lib/pi-rpc-pool.mjs +433 -0
- package/lib/process-hang-detector.mjs +83 -0
- package/lib/process-supervisor.mjs +193 -0
- package/lib/prompt-cache.mjs +2 -0
- package/lib/repo-graph.mjs +53 -2
- package/lib/runtime-config.mjs +31 -0
- package/lib/safety.mjs +132 -0
- package/lib/semantic-index.mjs +52 -3
- package/lib/skill-compiler.mjs +128 -0
- package/lib/skill-quality.mjs +48 -2
- package/lib/task-engine.mjs +66 -5
- package/lib/task-policy.mjs +235 -0
- package/lib/verification-broker.mjs +284 -0
- package/lib/verification-command.mjs +111 -0
- package/lib/windows-shim.mjs +35 -0
- package/lib/workspace-fingerprint.mjs +198 -0
- package/package.json +52 -42
- package/pi/extensions/ues-child-runtime.ts +238 -0
- package/pi/extensions/ues.ts +3200 -0
- package/pi/prompts/ues-audit.md +9 -0
- package/pi/prompts/ues-critique.md +9 -0
- package/pi/prompts/ues-debug.md +9 -0
- package/pi/prompts/ues-feature.md +9 -0
- package/pi/prompts/ues-fix.md +9 -0
- package/pi/prompts/ues-plan.md +9 -0
- package/pi/prompts/ues-research.md +9 -0
- package/pi/prompts/ues-resume.md +9 -0
- package/pi/prompts/ues-review.md +7 -0
- package/pi/prompts/ues-run.md +17 -0
- package/pi/prompts/ues-verify.md +9 -0
- package/scripts/check-release-consistency.mjs +119 -185
- package/scripts/check-runtime-exports.mjs +66 -0
- package/scripts/check-source-integrity.mjs +184 -0
- package/scripts/eval-pi.mjs +492 -0
- package/scripts/install.mjs +16 -0
- package/scripts/smoke-package-closure.mjs +110 -0
- package/scripts/smoke-packed-install.mjs +24 -11
- package/scripts/smoke-pi-extension.mjs +144 -0
- package/scripts/uninstall.mjs +44 -0
- package/CHANGELOG.md +0 -415
- package/docs/DETERMINISTIC-TOOLS.md +0 -105
- package/docs/ENGINEERING-DESIGN.md +0 -194
- package/docs/EVALS.md +0 -158
- package/docs/GITHUB-RULESET.md +0 -50
- package/docs/NPM-PUBLISH.md +0 -116
- package/docs/RESEARCH-SOURCES.md +0 -37
- package/docs/TRACE-SCHEMA.md +0 -122
- package/docs/V11-PERCEPTION-ADAPTIVE-EXECUTION.md +0 -75
- package/docs/V11-PERCEPTION-ADAPTIVE.md +0 -220
- package/docs/V12-WEAK-MODEL-INTELLIGENCE.md +0 -27
- package/docs/V13-PARALLEL-WEAK-MODEL-RUNTIME.md +0 -86
- package/docs/V7-INTELLIGENCE-RUNTIME.md +0 -166
- package/docs/V8-INTELLIGENCE-RELIABILITY.md +0 -206
- package/docs/V9-SPEED-INTELLIGENCE.md +0 -102
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
function normalizePath(value) {
|
|
2
|
+
return String(value || "").replaceAll("\\", "/")
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function normalizeSeeds(nodes, semanticResults = [], declared = [], changed = []) {
|
|
6
|
+
const raw = new Map(nodes.map((node) => [node, 0]))
|
|
7
|
+
const semanticMax = Math.max(1, ...semanticResults.map((item) => Number(item.score || 0)))
|
|
8
|
+
for (const item of semanticResults) {
|
|
9
|
+
const file = normalizePath(item.path)
|
|
10
|
+
if (!raw.has(file)) continue
|
|
11
|
+
raw.set(file, raw.get(file) + Number(item.score || 0) / semanticMax)
|
|
12
|
+
}
|
|
13
|
+
for (const file of declared.map(normalizePath)) {
|
|
14
|
+
if (raw.has(file)) raw.set(file, raw.get(file) + 1.25)
|
|
15
|
+
}
|
|
16
|
+
for (const file of changed.map(normalizePath)) {
|
|
17
|
+
if (raw.has(file)) raw.set(file, raw.get(file) + 0.75)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let total = [...raw.values()].reduce((sum, value) => sum + value, 0)
|
|
21
|
+
if (total <= 0) {
|
|
22
|
+
const uniform = nodes.length ? 1 / nodes.length : 0
|
|
23
|
+
return new Map(nodes.map((node) => [node, uniform]))
|
|
24
|
+
}
|
|
25
|
+
return new Map([...raw.entries()].map(([key, value]) => [key, value / total]))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function rankContextGraph(graph = {}, options = {}) {
|
|
29
|
+
const nodes = [...new Set((graph.nodes || []).map((node) => normalizePath(node.path)).filter(Boolean))]
|
|
30
|
+
if (!nodes.length) return []
|
|
31
|
+
|
|
32
|
+
const nodeSet = new Set(nodes)
|
|
33
|
+
const neighbors = new Map(nodes.map((node) => [node, new Map()]))
|
|
34
|
+
const addEdge = (from, to, weight) => {
|
|
35
|
+
if (!nodeSet.has(from) || !nodeSet.has(to) || from === to) return
|
|
36
|
+
const row = neighbors.get(from)
|
|
37
|
+
row.set(to, Number(row.get(to) || 0) + weight)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const edge of graph.edges || []) {
|
|
41
|
+
const from = normalizePath(edge.from)
|
|
42
|
+
const to = normalizePath(edge.to)
|
|
43
|
+
// Imports carry relevance in both directions: the dependency is relevant to
|
|
44
|
+
// its caller, while callers are useful when a dependency itself is seeded.
|
|
45
|
+
addEdge(from, to, 1)
|
|
46
|
+
addEdge(to, from, 0.55)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const seeds = normalizeSeeds(
|
|
50
|
+
nodes,
|
|
51
|
+
options.semanticResults || [],
|
|
52
|
+
options.declared || [],
|
|
53
|
+
options.changed || [],
|
|
54
|
+
)
|
|
55
|
+
const damping = Math.max(0.5, Math.min(0.95, Number(options.damping || 0.85)))
|
|
56
|
+
const iterations = Math.max(4, Math.min(40, Number(options.iterations || 14)))
|
|
57
|
+
let scores = new Map(seeds)
|
|
58
|
+
|
|
59
|
+
for (let iteration = 0; iteration < iterations; iteration += 1) {
|
|
60
|
+
const next = new Map(nodes.map((node) => [node, (1 - damping) * Number(seeds.get(node) || 0)]))
|
|
61
|
+
let dangling = 0
|
|
62
|
+
|
|
63
|
+
for (const node of nodes) {
|
|
64
|
+
const score = Number(scores.get(node) || 0)
|
|
65
|
+
const row = neighbors.get(node)
|
|
66
|
+
const totalWeight = [...row.values()].reduce((sum, weight) => sum + weight, 0)
|
|
67
|
+
if (totalWeight <= 0) {
|
|
68
|
+
dangling += score
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
for (const [target, weight] of row) {
|
|
72
|
+
next.set(target, Number(next.get(target) || 0) + damping * score * (weight / totalWeight))
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (dangling > 0) {
|
|
77
|
+
for (const node of nodes) {
|
|
78
|
+
next.set(
|
|
79
|
+
node,
|
|
80
|
+
Number(next.get(node) || 0) + damping * dangling * Number(seeds.get(node) || 0),
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
scores = next
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const semantic = new Map(
|
|
88
|
+
(options.semanticResults || []).map((item) => [normalizePath(item.path), Number(item.score || 0)]),
|
|
89
|
+
)
|
|
90
|
+
const maxRank = Math.max(1e-12, ...scores.values())
|
|
91
|
+
const maxSemantic = Math.max(1, ...semantic.values())
|
|
92
|
+
|
|
93
|
+
return nodes
|
|
94
|
+
.map((file) => {
|
|
95
|
+
const pageRank = Number(scores.get(file) || 0)
|
|
96
|
+
const seed = Number(seeds.get(file) || 0)
|
|
97
|
+
const semanticScore = Number(semantic.get(file) || 0)
|
|
98
|
+
const normalizedRank = pageRank / maxRank
|
|
99
|
+
const normalizedSemantic = semanticScore / maxSemantic
|
|
100
|
+
const score = normalizedRank * 60 + normalizedSemantic * 40
|
|
101
|
+
return {
|
|
102
|
+
path: file,
|
|
103
|
+
score: Number(score.toFixed(6)),
|
|
104
|
+
pageRank: Number(pageRank.toFixed(10)),
|
|
105
|
+
personalization: Number(seed.toFixed(10)),
|
|
106
|
+
semanticScore,
|
|
107
|
+
reasons: [
|
|
108
|
+
semanticScore > 0 ? "semantic-seed" : null,
|
|
109
|
+
(options.declared || []).map(normalizePath).includes(file) ? "declared" : null,
|
|
110
|
+
(options.changed || []).map(normalizePath).includes(file) ? "changed" : null,
|
|
111
|
+
Number(neighbors.get(file)?.size || 0) > 0 ? "dependency-graph" : null,
|
|
112
|
+
].filter(Boolean),
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
.filter((item) => item.score > 0)
|
|
116
|
+
.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path))
|
|
117
|
+
.slice(0, Math.max(1, Math.min(100, Number(options.limit || 32))))
|
|
118
|
+
}
|