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.
Files changed (93) hide show
  1. package/README.md +1556 -607
  2. package/bin/ocskill.mjs +172 -24
  3. package/docs/OPENCODE-COMPAT.md +34 -97
  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/plugins/ues-router/index.js +13 -8
  12. package/global-config/plugins/ues-router/policy-runtime.js +7 -0
  13. package/global-config/plugins/ues-router/router.js +12 -2
  14. package/global-config/skills/ecommerce-engineering/SKILL.md +1 -1
  15. package/global-config/skills/file-upload-engineering/SKILL.md +1 -1
  16. package/global-config/skills/git-safety/SKILL.md +1 -1
  17. package/global-config/skills/nestjs-engineering/SKILL.md +1 -1
  18. package/global-config/skills/performance-engineering/SKILL.md +1 -1
  19. package/global-config/skills/react-native-engineering/SKILL.md +1 -1
  20. package/global-config/skills/rest-api-design/SKILL.md +1 -1
  21. package/global-config/skills/ui-ux-engineering/SKILL.md +1 -1
  22. package/lib/adaptive-context-budget.mjs +97 -0
  23. package/lib/affected-tests.mjs +260 -0
  24. package/lib/benchmark-confidence.mjs +41 -2
  25. package/lib/browser-mcp-routing.mjs +166 -0
  26. package/lib/capability-fabric.mjs +336 -0
  27. package/lib/capability-registry.mjs +9 -0
  28. package/lib/context-engine-v11.mjs +65 -1
  29. package/lib/context-graph-rank.mjs +118 -0
  30. package/lib/context-manifest.mjs +97 -18
  31. package/lib/control-center.mjs +19 -1
  32. package/lib/dynamic-workflow.mjs +3 -1
  33. package/lib/evidence-store.mjs +82 -1
  34. package/lib/hierarchical-context.mjs +215 -0
  35. package/lib/memory-engine.mjs +465 -0
  36. package/lib/model-performance.mjs +33 -8
  37. package/lib/model-policy.mjs +3 -3
  38. package/lib/orchestrator-policy.mjs +5 -209
  39. package/lib/performance-fabric.mjs +229 -0
  40. package/lib/pi-rpc-pool.mjs +433 -0
  41. package/lib/process-hang-detector.mjs +83 -0
  42. package/lib/process-supervisor.mjs +193 -0
  43. package/lib/prompt-cache.mjs +2 -0
  44. package/lib/repo-graph.mjs +53 -2
  45. package/lib/runtime-config.mjs +31 -0
  46. package/lib/safety.mjs +132 -0
  47. package/lib/semantic-index.mjs +52 -3
  48. package/lib/skill-compiler.mjs +128 -0
  49. package/lib/skill-quality.mjs +48 -2
  50. package/lib/task-engine.mjs +66 -5
  51. package/lib/task-policy.mjs +235 -0
  52. package/lib/verification-broker.mjs +284 -0
  53. package/lib/verification-command.mjs +111 -0
  54. package/lib/windows-shim.mjs +35 -0
  55. package/lib/workspace-fingerprint.mjs +198 -0
  56. package/package.json +52 -42
  57. package/pi/extensions/ues-child-runtime.ts +238 -0
  58. package/pi/extensions/ues.ts +3200 -0
  59. package/pi/prompts/ues-audit.md +9 -0
  60. package/pi/prompts/ues-critique.md +9 -0
  61. package/pi/prompts/ues-debug.md +9 -0
  62. package/pi/prompts/ues-feature.md +9 -0
  63. package/pi/prompts/ues-fix.md +9 -0
  64. package/pi/prompts/ues-plan.md +9 -0
  65. package/pi/prompts/ues-research.md +9 -0
  66. package/pi/prompts/ues-resume.md +9 -0
  67. package/pi/prompts/ues-review.md +7 -0
  68. package/pi/prompts/ues-run.md +17 -0
  69. package/pi/prompts/ues-verify.md +9 -0
  70. package/scripts/check-release-consistency.mjs +119 -185
  71. package/scripts/check-runtime-exports.mjs +66 -0
  72. package/scripts/check-source-integrity.mjs +184 -0
  73. package/scripts/eval-pi.mjs +492 -0
  74. package/scripts/install.mjs +16 -0
  75. package/scripts/smoke-package-closure.mjs +110 -0
  76. package/scripts/smoke-packed-install.mjs +24 -11
  77. package/scripts/smoke-pi-extension.mjs +144 -0
  78. package/scripts/uninstall.mjs +44 -0
  79. package/CHANGELOG.md +0 -415
  80. package/docs/DETERMINISTIC-TOOLS.md +0 -105
  81. package/docs/ENGINEERING-DESIGN.md +0 -194
  82. package/docs/EVALS.md +0 -158
  83. package/docs/GITHUB-RULESET.md +0 -50
  84. package/docs/NPM-PUBLISH.md +0 -116
  85. package/docs/RESEARCH-SOURCES.md +0 -37
  86. package/docs/TRACE-SCHEMA.md +0 -122
  87. package/docs/V11-PERCEPTION-ADAPTIVE-EXECUTION.md +0 -75
  88. package/docs/V11-PERCEPTION-ADAPTIVE.md +0 -220
  89. package/docs/V12-WEAK-MODEL-INTELLIGENCE.md +0 -27
  90. package/docs/V13-PARALLEL-WEAK-MODEL-RUNTIME.md +0 -86
  91. package/docs/V7-INTELLIGENCE-RUNTIME.md +0 -166
  92. package/docs/V8-INTELLIGENCE-RELIABILITY.md +0 -206
  93. 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
+ }