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.
- package/README.md +1557 -606
- package/bin/ocskill.mjs +172 -24
- package/docs/OPENCODE-COMPAT.md +34 -95
- 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/commands/run.md +27 -20
- package/global-config/plugins/ues-router/command-runtime.js +54 -0
- package/global-config/plugins/ues-router/index.js +30 -24
- 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 -405
- 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 -75
- 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,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
|
+
}
|
package/lib/context-manifest.mjs
CHANGED
|
@@ -2,9 +2,11 @@ import { existsSync } from "node:fs"
|
|
|
2
2
|
import { readFile, stat } from "node:fs/promises"
|
|
3
3
|
import { spawnSync } from "node:child_process"
|
|
4
4
|
import path from "node:path"
|
|
5
|
-
import {
|
|
5
|
+
import { buildRepoGraphCached } from "./repo-graph.mjs"
|
|
6
6
|
import { taskFiles } from "./task-graph.mjs"
|
|
7
|
-
import { querySemanticIndex } from "./semantic-index.mjs"
|
|
7
|
+
import { buildSemanticIndexCached, querySemanticIndex } from "./semantic-index.mjs"
|
|
8
|
+
import { queryContextHierarchy } from "./hierarchical-context.mjs"
|
|
9
|
+
import { rankContextGraph } from "./context-graph-rank.mjs"
|
|
8
10
|
|
|
9
11
|
const ROOT_INSTRUCTIONS = [
|
|
10
12
|
"AGENTS.md",
|
|
@@ -247,7 +249,9 @@ export async function buildContextManifest(root, task, options = {}) {
|
|
|
247
249
|
const evidenceBudget = options.evidenceBudget || null
|
|
248
250
|
const declared = taskFiles(task)
|
|
249
251
|
const terms = taskTerms(task)
|
|
250
|
-
const changed =
|
|
252
|
+
const changed = Array.isArray(options.changedFiles)
|
|
253
|
+
? options.changedFiles.map((file) => String(file || "").replaceAll("\\", "/")).filter(Boolean)
|
|
254
|
+
: gitChangedFiles(root)
|
|
251
255
|
const strategy = String(options.strategy || "semantic+graph+git")
|
|
252
256
|
const queryText = [
|
|
253
257
|
task?.title,
|
|
@@ -256,15 +260,45 @@ export async function buildContextManifest(root, task, options = {}) {
|
|
|
256
260
|
...(task?.verification || []),
|
|
257
261
|
].filter(Boolean).join(" ")
|
|
258
262
|
|
|
263
|
+
const semanticSnapshot = await buildSemanticIndexCached(root, {
|
|
264
|
+
maxFiles: options.semanticMaxFiles ?? 6000,
|
|
265
|
+
maxDepth: options.semanticMaxDepth ?? 14,
|
|
266
|
+
workspaceFingerprint: options.workspaceFingerprint,
|
|
267
|
+
}).catch(() => null)
|
|
268
|
+
|
|
269
|
+
const hierarchy = await queryContextHierarchy(root, queryText, {
|
|
270
|
+
declaredFiles: declared,
|
|
271
|
+
maxScopes: options.hierarchyMaxScopes ?? 6,
|
|
272
|
+
maxFiles: options.semanticMaxFiles ?? 6000,
|
|
273
|
+
builtIndex: semanticSnapshot || undefined,
|
|
274
|
+
}).catch(() => null)
|
|
275
|
+
const hierarchyScopes = hierarchy?.scopes?.map((item) => item.path) || []
|
|
276
|
+
|
|
259
277
|
const semantic = await querySemanticIndex(root, queryText, {
|
|
260
278
|
limit: 32,
|
|
261
279
|
maxFiles: options.semanticMaxFiles ?? 6000,
|
|
280
|
+
builtIndex: semanticSnapshot || undefined,
|
|
262
281
|
}).catch(() => null)
|
|
263
|
-
const
|
|
282
|
+
const scopedSemanticResults = (semantic?.results || []).map((item) => {
|
|
283
|
+
const scopeRank = hierarchyScopes.findIndex((scope) =>
|
|
284
|
+
scope === "." || item.path === scope || item.path.startsWith(scope + "/"),
|
|
285
|
+
)
|
|
286
|
+
const scopeBoost = scopeRank >= 0 ? Math.max(2, 12 - scopeRank * 2) : 0
|
|
287
|
+
return {
|
|
288
|
+
...item,
|
|
289
|
+
baseScore: item.score,
|
|
290
|
+
scopeBoost,
|
|
291
|
+
score: Number((Number(item.score || 0) + scopeBoost).toFixed(3)),
|
|
292
|
+
}
|
|
293
|
+
}).sort((a, b) => b.score - a.score || a.path.localeCompare(b.path))
|
|
294
|
+
const semanticPaths = scopedSemanticResults.map((item) => item.path)
|
|
264
295
|
|
|
265
296
|
const useGraph = strategy.includes("graph")
|
|
266
297
|
const graph = useGraph
|
|
267
|
-
? await
|
|
298
|
+
? await buildRepoGraphCached(root, {
|
|
299
|
+
maxFiles: options.maxFiles ?? 2500,
|
|
300
|
+
workspaceFingerprint: options.workspaceFingerprint,
|
|
301
|
+
})
|
|
268
302
|
: { nodes: [], edges: [], hotspots: [], scannedFiles: 0, truncated: false }
|
|
269
303
|
const nodeMap = new Map(graph.nodes.map((node) => [node.path, node]))
|
|
270
304
|
const incoming = new Map()
|
|
@@ -297,17 +331,46 @@ export async function buildContextManifest(root, task, options = {}) {
|
|
|
297
331
|
.map((item) => item.file)
|
|
298
332
|
|
|
299
333
|
const instructions = await nearbyInstructions(root, declared)
|
|
300
|
-
const
|
|
301
|
-
?
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
334
|
+
const graphRanked = useGraph
|
|
335
|
+
? rankContextGraph(graph, {
|
|
336
|
+
semanticResults: scopedSemanticResults,
|
|
337
|
+
declared,
|
|
338
|
+
changed,
|
|
339
|
+
limit: 32,
|
|
340
|
+
})
|
|
341
|
+
: []
|
|
342
|
+
|
|
343
|
+
const semanticByPath = new Map(scopedSemanticResults.map((item) => [item.path, item]))
|
|
344
|
+
const ranked = graphRanked.length
|
|
345
|
+
? graphRanked.map((item) => {
|
|
346
|
+
const semanticItem = semanticByPath.get(item.path)
|
|
347
|
+
return {
|
|
348
|
+
path: item.path,
|
|
349
|
+
score: item.score,
|
|
350
|
+
pageRank: item.pageRank,
|
|
351
|
+
personalization: item.personalization,
|
|
352
|
+
baseScore: semanticItem?.baseScore ?? semanticItem?.score ?? item.semanticScore,
|
|
353
|
+
scopeBoost: semanticItem?.scopeBoost || 0,
|
|
354
|
+
matches: [...new Set([...(semanticItem?.reasons || []), ...(item.reasons || [])])],
|
|
355
|
+
symbolHits: semanticItem?.definitions || [],
|
|
356
|
+
evidenceLevel: semanticItem
|
|
357
|
+
? String(semantic?.evidenceLevel || "syntax-aware-lexical") + "+dependency-graph-rank"
|
|
358
|
+
: "dependency-graph-rank",
|
|
359
|
+
}
|
|
360
|
+
})
|
|
361
|
+
: scopedSemanticResults.length
|
|
362
|
+
? scopedSemanticResults.map((item) => ({
|
|
363
|
+
path: item.path,
|
|
364
|
+
score: item.score,
|
|
365
|
+
baseScore: item.baseScore ?? item.score,
|
|
366
|
+
scopeBoost: item.scopeBoost || 0,
|
|
367
|
+
matches: item.reasons || [],
|
|
368
|
+
symbolHits: item.definitions || [],
|
|
369
|
+
evidenceLevel: semantic.evidenceLevel,
|
|
370
|
+
}))
|
|
371
|
+
: useGraph
|
|
372
|
+
? await rankedReferences(root, graph.nodes, terms, declared, changed)
|
|
373
|
+
: []
|
|
311
374
|
const changedRelevant = changed
|
|
312
375
|
.filter((file) =>
|
|
313
376
|
declared.includes(file) ||
|
|
@@ -372,6 +435,18 @@ export async function buildContextManifest(root, task, options = {}) {
|
|
|
372
435
|
rankedReferences: ranked,
|
|
373
436
|
files,
|
|
374
437
|
excerpts,
|
|
438
|
+
hierarchy: hierarchy ? {
|
|
439
|
+
schemaVersion: hierarchy.schemaVersion,
|
|
440
|
+
levels: hierarchy.levels,
|
|
441
|
+
scopes: hierarchy.scopes.slice(0, 6).map((item) => ({
|
|
442
|
+
path: item.path,
|
|
443
|
+
score: item.score,
|
|
444
|
+
reasons: item.reasons,
|
|
445
|
+
fileCount: item.fileCount,
|
|
446
|
+
l0: item.l0,
|
|
447
|
+
l1: item.l1,
|
|
448
|
+
})),
|
|
449
|
+
} : null,
|
|
375
450
|
semantic: semantic ? {
|
|
376
451
|
evidenceLevel: semantic.evidenceLevel,
|
|
377
452
|
resultCount: semantic.results.length,
|
|
@@ -382,11 +457,15 @@ export async function buildContextManifest(root, task, options = {}) {
|
|
|
382
457
|
scannedFiles: graph.scannedFiles,
|
|
383
458
|
truncated: graph.truncated,
|
|
384
459
|
hotspots: graph.hotspots.slice(0, 12),
|
|
460
|
+
ranking: graphRanked.length ? {
|
|
461
|
+
strategy: "personalized-dependency-graph-rank",
|
|
462
|
+
resultCount: graphRanked.length,
|
|
463
|
+
top: graphRanked.slice(0, 12),
|
|
464
|
+
} : null,
|
|
385
465
|
},
|
|
386
466
|
budget,
|
|
387
467
|
evidenceBudget,
|
|
388
468
|
categoryRemaining,
|
|
389
469
|
used: budget - remaining,
|
|
390
470
|
}
|
|
391
|
-
}
|
|
392
|
-
|
|
471
|
+
}
|
package/lib/control-center.mjs
CHANGED
|
@@ -4,6 +4,8 @@ import path from "node:path"
|
|
|
4
4
|
import { readLearningState } from "./learning-engine.mjs"
|
|
5
5
|
import { readRuntimeEvents } from "./runtime-events.mjs"
|
|
6
6
|
import { evidenceStoreStatus } from "./evidence-store.mjs"
|
|
7
|
+
import { memoryStatus } from "./memory-engine.mjs"
|
|
8
|
+
import { capabilityFabricStatus } from "./capability-fabric.mjs"
|
|
7
9
|
|
|
8
10
|
function escapeHtml(value) {
|
|
9
11
|
return String(value ?? "")
|
|
@@ -65,11 +67,13 @@ async function collectEvalSummary(root) {
|
|
|
65
67
|
|
|
66
68
|
export async function collectControlCenterData(root = process.cwd()) {
|
|
67
69
|
root = path.resolve(root)
|
|
68
|
-
const [work, learning, evals, evidenceStore] = await Promise.all([
|
|
70
|
+
const [work, learning, evals, evidenceStore, memory, capabilityFabric] = await Promise.all([
|
|
69
71
|
collectWork(root),
|
|
70
72
|
readLearningState(root),
|
|
71
73
|
collectEvalSummary(root),
|
|
72
74
|
evidenceStoreStatus(root),
|
|
75
|
+
memoryStatus(root),
|
|
76
|
+
capabilityFabricStatus(root),
|
|
73
77
|
])
|
|
74
78
|
return {
|
|
75
79
|
schemaVersion: 2,
|
|
@@ -82,6 +86,16 @@ export async function collectControlCenterData(root = process.cwd()) {
|
|
|
82
86
|
evidenceStore,
|
|
83
87
|
runtime: "perception-adaptive-execution",
|
|
84
88
|
},
|
|
89
|
+
v14: {
|
|
90
|
+
memory,
|
|
91
|
+
capabilityFabric: {
|
|
92
|
+
healthyCapabilities: capabilityFabric.healthyCapabilities,
|
|
93
|
+
totalCapabilities: capabilityFabric.totalCapabilities,
|
|
94
|
+
rows: capabilityFabric.rows,
|
|
95
|
+
observationState: capabilityFabric.observationState,
|
|
96
|
+
},
|
|
97
|
+
runtime: "context-memory-capability-fabric",
|
|
98
|
+
},
|
|
85
99
|
}
|
|
86
100
|
}
|
|
87
101
|
|
|
@@ -110,6 +124,7 @@ details{margin-top:10px;border-top:1px solid #243049;padding-top:8px}summary{cur
|
|
|
110
124
|
<div class="section"><h2>Long-horizon work</h2><div class="grid" id="work"></div></div>
|
|
111
125
|
<div class="section"><h2>Learning loop</h2><div class="grid" id="learning"></div></div>
|
|
112
126
|
<div class="section"><h2>V11 runtime efficiency</h2><div class="grid" id="v11"></div></div>
|
|
127
|
+
<div class="section"><h2>V14 context & memory fabric</h2><div class="grid" id="v14"></div></div>
|
|
113
128
|
<div class="section"><h2>Recent runtime events</h2><div class="card"><table><thead><tr><th>Work</th><th>Event</th><th>Task</th><th>Time</th></tr></thead><tbody id="events"></tbody></table></div></div>
|
|
114
129
|
<div class="section"><h2>Recent evaluations</h2><div class="card"><table><thead><tr><th>Suite</th><th>Model</th><th>Baseline</th><th>UES</th></tr></thead><tbody id="evals"></tbody></table></div></div>
|
|
115
130
|
</div>
|
|
@@ -136,6 +151,9 @@ function render(next){
|
|
|
136
151
|
const v11=document.querySelector("#v11");
|
|
137
152
|
const store=data.v11?.evidenceStore||{};
|
|
138
153
|
v11.innerHTML='<div class="card"><h3>Evidence store</h3><strong>'+esc(store.entries||0)+'</strong><div class="muted">'+esc(store.bytes||0)+' bytes externalized</div></div><div class="card"><h3>Runtime</h3><strong>V11</strong><div class="muted">'+esc(data.v11?.runtime||'adaptive')+'</div></div>';
|
|
154
|
+
const v14=document.querySelector("#v14");
|
|
155
|
+
const memory=data.v14?.memory||{};
|
|
156
|
+
v14.innerHTML='<div class="card"><h3>Verified memory</h3><strong>'+esc(memory.byStatus?.verified||0)+'</strong><div class="muted">'+esc(memory.entries||0)+' total entries</div></div><div class="card"><h3>Runtime</h3><strong>V14</strong><div class="muted">'+esc(data.v14?.runtime||'context-memory-fabric')+'</div></div>';
|
|
139
157
|
const eventBody=document.querySelector("#events");
|
|
140
158
|
eventBody.innerHTML="";
|
|
141
159
|
for(const item of data.work){
|
package/lib/dynamic-workflow.mjs
CHANGED
|
@@ -50,6 +50,7 @@ function conflict(a, b) {
|
|
|
50
50
|
|
|
51
51
|
function executionMode(classification, options) {
|
|
52
52
|
if (classification.kind === "deterministic") return "deterministic"
|
|
53
|
+
if (options.allowInline === false) return "agent"
|
|
53
54
|
if (classification.kind === "vision") {
|
|
54
55
|
return classification.estimatedCost >= options.minVisionAgentCost ? "agent" : "inline"
|
|
55
56
|
}
|
|
@@ -81,6 +82,7 @@ export function planDynamicWorkflow(tasks = [], inputOptions = {}) {
|
|
|
81
82
|
minAgentCost: clampInt(inputOptions.minAgentCost, 5, 2, 12),
|
|
82
83
|
minVisionAgentCost: clampInt(inputOptions.minVisionAgentCost, 4, 2, 12),
|
|
83
84
|
deterministicFirst: inputOptions.deterministicFirst !== false,
|
|
85
|
+
allowInline: inputOptions.allowInline !== false,
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
const byID = new Map(tasks.map((task) => [task.id, task]))
|
|
@@ -176,4 +178,4 @@ export function planDynamicWorkflow(tasks = [], inputOptions = {}) {
|
|
|
176
178
|
visionAgentTaskCount: flat.filter((task) => task.spawnAgent && task.kind === "vision").length,
|
|
177
179
|
estimatedCoordinationSaved: flat.filter((task) => task.execution !== "agent").reduce((sum, task) => sum + task.estimatedCost, 0),
|
|
178
180
|
}
|
|
179
|
-
}
|
|
181
|
+
}
|
package/lib/evidence-store.mjs
CHANGED
|
@@ -99,6 +99,87 @@ export async function getEvidence(root, ref, options = {}) {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
|
|
103
|
+
function evidenceSelector(ref) {
|
|
104
|
+
const value = String(ref || "").trim()
|
|
105
|
+
const index = value.indexOf("#")
|
|
106
|
+
if (index < 0) return { ref: value, selector: null }
|
|
107
|
+
return {
|
|
108
|
+
ref: value.slice(0, index),
|
|
109
|
+
selector: value.slice(index + 1),
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function selectorParts(selector) {
|
|
114
|
+
const value = String(selector || "").trim()
|
|
115
|
+
if (!value) return []
|
|
116
|
+
if (value.startsWith("/")) {
|
|
117
|
+
return value
|
|
118
|
+
.split("/")
|
|
119
|
+
.slice(1)
|
|
120
|
+
.map((part) => part.replaceAll("~1", "/").replaceAll("~0", "~"))
|
|
121
|
+
}
|
|
122
|
+
return value
|
|
123
|
+
.replace(/\[(\d+)\]/g, ".$1")
|
|
124
|
+
.split(".")
|
|
125
|
+
.map((part) => part.trim())
|
|
126
|
+
.filter(Boolean)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function selectJsonValue(value, selector) {
|
|
130
|
+
let current = value
|
|
131
|
+
for (const part of selectorParts(selector)) {
|
|
132
|
+
if (Array.isArray(current)) {
|
|
133
|
+
if (!/^\d+$/.test(part)) throw new Error("Invalid array selector segment: " + part)
|
|
134
|
+
const index = Number(part)
|
|
135
|
+
if (index < 0 || index >= current.length) throw new Error("Evidence selector index out of range: " + part)
|
|
136
|
+
current = current[index]
|
|
137
|
+
continue
|
|
138
|
+
}
|
|
139
|
+
if (!current || typeof current !== "object" || !Object.hasOwn(current, part)) {
|
|
140
|
+
throw new Error("Evidence selector path not found: " + part)
|
|
141
|
+
}
|
|
142
|
+
current = current[part]
|
|
143
|
+
}
|
|
144
|
+
return current
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function getEvidenceSelected(root, refWithSelector, options = {}) {
|
|
148
|
+
const split = evidenceSelector(refWithSelector)
|
|
149
|
+
if (!split.selector) return getEvidence(root, split.ref, options)
|
|
150
|
+
|
|
151
|
+
const hash = normalizeHash(split.ref)
|
|
152
|
+
const target = evidencePaths(root, hash)
|
|
153
|
+
const metadata = JSON.parse(await readFile(target.meta, "utf8"))
|
|
154
|
+
if (metadata.encoding !== "utf8" || !String(metadata.mediaType || "").includes("json")) {
|
|
155
|
+
throw new Error("Evidence selectors require JSON evidence")
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const maxObjectBytes = Math.max(1024, Math.min(16 * 1024 * 1024, Number(options.maxObjectBytes || 4 * 1024 * 1024)))
|
|
159
|
+
if (Number(metadata.bytes || 0) > maxObjectBytes) {
|
|
160
|
+
throw new Error("JSON evidence is too large for selector retrieval; use byte slices instead")
|
|
161
|
+
}
|
|
162
|
+
const source = await readFile(target.data, "utf8")
|
|
163
|
+
const selected = selectJsonValue(JSON.parse(source), split.selector)
|
|
164
|
+
const encoded = typeof selected === "string"
|
|
165
|
+
? selected
|
|
166
|
+
: JSON.stringify(stableValue(selected), null, 2)
|
|
167
|
+
const maxBytes = Math.max(1, Number(options.maxBytes || options.maxChars || 24_000))
|
|
168
|
+
const bytes = Buffer.from(encoded, "utf8")
|
|
169
|
+
const start = Math.max(0, Number(options.start || 0))
|
|
170
|
+
const slice = bytes.subarray(start, Math.min(bytes.length, start + maxBytes))
|
|
171
|
+
return {
|
|
172
|
+
...metadata,
|
|
173
|
+
ref: split.ref + "#" + split.selector,
|
|
174
|
+
selector: split.selector,
|
|
175
|
+
selectedBytes: bytes.length,
|
|
176
|
+
truncated: start + slice.length < bytes.length,
|
|
177
|
+
start,
|
|
178
|
+
returnedBytes: slice.length,
|
|
179
|
+
content: slice.toString("utf8"),
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
102
183
|
async function metadataFiles(root) {
|
|
103
184
|
const base = evidenceStoreRoot(root)
|
|
104
185
|
const prefixes = await readdir(base, { withFileTypes: true }).catch(() => [])
|
|
@@ -175,4 +256,4 @@ export async function evidenceExists(root, ref) {
|
|
|
175
256
|
} catch {
|
|
176
257
|
return false
|
|
177
258
|
}
|
|
178
|
-
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import path from "node:path"
|
|
2
|
+
import { buildSemanticIndex } from "./semantic-index.mjs"
|
|
3
|
+
|
|
4
|
+
const DEFAULT_L0 = 256
|
|
5
|
+
const DEFAULT_L1 = 4000
|
|
6
|
+
const STOP = new Set([
|
|
7
|
+
"this","that","with","from","into","then","than","when","where","what","your","have","will",
|
|
8
|
+
"task","code","file","files","fix","update","change","changes","được","các","cho","với","trong","này","một","những","không","theo",
|
|
9
|
+
])
|
|
10
|
+
|
|
11
|
+
function terms(value) {
|
|
12
|
+
return [...new Set(
|
|
13
|
+
String(value || "")
|
|
14
|
+
.toLowerCase()
|
|
15
|
+
.split(/[^\p{L}\p{N}_$.-]+/u)
|
|
16
|
+
.map((item) => item.replace(/^[-.$]+|[-.$]+$/g, ""))
|
|
17
|
+
.filter((item) => item.length >= 2 && !STOP.has(item)),
|
|
18
|
+
)].slice(0, 32)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function ancestors(file) {
|
|
22
|
+
const parts = String(file || "").replaceAll("\\", "/").split("/").filter(Boolean)
|
|
23
|
+
const result = ["."]
|
|
24
|
+
let current = ""
|
|
25
|
+
for (let index = 0; index < parts.length - 1; index += 1) {
|
|
26
|
+
current = current ? `${current}/${parts[index]}` : parts[index]
|
|
27
|
+
result.push(current)
|
|
28
|
+
}
|
|
29
|
+
return result
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parentDir(dir) {
|
|
33
|
+
if (!dir || dir === ".") return null
|
|
34
|
+
const value = path.posix.dirname(dir)
|
|
35
|
+
return value === "." ? "." : value
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function clip(value, limit) {
|
|
39
|
+
const text = String(value || "")
|
|
40
|
+
return text.length <= limit ? text : text.slice(0, Math.max(0, limit - 15)) + "...[truncated]"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function topCounts(map, limit) {
|
|
44
|
+
return [...map.entries()]
|
|
45
|
+
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
|
|
46
|
+
.slice(0, limit)
|
|
47
|
+
.map(([name, count]) => ({ name, count }))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function buildNodeSummary(node, children, options = {}) {
|
|
51
|
+
const l0Limit = Math.max(96, Number(options.l0Chars || DEFAULT_L0))
|
|
52
|
+
const l1Limit = Math.max(512, Number(options.l1Chars || DEFAULT_L1))
|
|
53
|
+
const symbols = topCounts(node.symbols, 18)
|
|
54
|
+
const extensions = topCounts(node.extensions, 10)
|
|
55
|
+
const files = [...node.files].sort()
|
|
56
|
+
const childNames = (children.get(node.path) || []).sort()
|
|
57
|
+
const l0 = clip([
|
|
58
|
+
`${node.path}: ${files.length} source file(s), ${node.bytes} bytes.`,
|
|
59
|
+
symbols.length ? `Key symbols: ${symbols.slice(0, 8).map((item) => item.name).join(", ")}.` : "",
|
|
60
|
+
childNames.length ? `Child areas: ${childNames.slice(0, 6).join(", ")}.` : "",
|
|
61
|
+
].filter(Boolean).join(" "), l0Limit)
|
|
62
|
+
const l1 = clip([
|
|
63
|
+
l0,
|
|
64
|
+
childNames.length ? `Children: ${childNames.join(", ")}` : "",
|
|
65
|
+
extensions.length ? `Extensions: ${extensions.map((item) => `${item.name || "(none)"}:${item.count}`).join(", ")}` : "",
|
|
66
|
+
symbols.length ? `Symbols: ${symbols.map((item) => `${item.name}:${item.count}`).join(", ")}` : "",
|
|
67
|
+
files.length ? `Files: ${files.slice(0, 60).join(", ")}` : "",
|
|
68
|
+
].filter(Boolean).join("\n"), l1Limit)
|
|
69
|
+
return {
|
|
70
|
+
path: node.path,
|
|
71
|
+
fileCount: files.length,
|
|
72
|
+
bytes: node.bytes,
|
|
73
|
+
files,
|
|
74
|
+
symbols,
|
|
75
|
+
children: childNames,
|
|
76
|
+
l0,
|
|
77
|
+
l1,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function buildContextHierarchy(root = process.cwd(), options = {}) {
|
|
82
|
+
root = path.resolve(root)
|
|
83
|
+
const built = options.builtIndex || await buildSemanticIndex(root, {
|
|
84
|
+
maxFiles: options.maxFiles ?? 6000,
|
|
85
|
+
maxDepth: options.maxDepth ?? 14,
|
|
86
|
+
})
|
|
87
|
+
const nodes = new Map()
|
|
88
|
+
const children = new Map()
|
|
89
|
+
const ensure = (dir) => {
|
|
90
|
+
if (!nodes.has(dir)) {
|
|
91
|
+
nodes.set(dir, { path: dir, files: new Set(), bytes: 0, symbols: new Map(), extensions: new Map() })
|
|
92
|
+
}
|
|
93
|
+
return nodes.get(dir)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
ensure(".")
|
|
97
|
+
for (const [file, entry] of Object.entries(built.index.files || {})) {
|
|
98
|
+
const dirs = ancestors(file)
|
|
99
|
+
const extension = path.posix.extname(file).toLowerCase()
|
|
100
|
+
for (const dir of dirs) {
|
|
101
|
+
const node = ensure(dir)
|
|
102
|
+
node.files.add(file)
|
|
103
|
+
node.bytes += Number(entry.bytes || 0)
|
|
104
|
+
node.extensions.set(extension, (node.extensions.get(extension) || 0) + 1)
|
|
105
|
+
for (const symbol of entry.symbols || []) {
|
|
106
|
+
const name = String(symbol.name || "")
|
|
107
|
+
if (!name) continue
|
|
108
|
+
node.symbols.set(name, (node.symbols.get(name) || 0) + 1)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (const dir of nodes.keys()) {
|
|
114
|
+
const parent = parentDir(dir)
|
|
115
|
+
if (!parent) continue
|
|
116
|
+
const list = children.get(parent) || []
|
|
117
|
+
if (!list.includes(dir)) list.push(dir)
|
|
118
|
+
children.set(parent, list)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const summaries = [...nodes.values()]
|
|
122
|
+
.map((node) => buildNodeSummary(node, children, options))
|
|
123
|
+
.sort((a, b) => a.path.localeCompare(b.path))
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
schemaVersion: 1,
|
|
127
|
+
kind: "ues-context-hierarchy",
|
|
128
|
+
root,
|
|
129
|
+
generatedAt: new Date().toISOString(),
|
|
130
|
+
levels: {
|
|
131
|
+
L0: { maxChars: Math.max(96, Number(options.l0Chars || DEFAULT_L0)), purpose: "routing abstract" },
|
|
132
|
+
L1: { maxChars: Math.max(512, Number(options.l1Chars || DEFAULT_L1)), purpose: "scope overview" },
|
|
133
|
+
L2: { purpose: "source excerpts loaded on demand by the context manifest" },
|
|
134
|
+
},
|
|
135
|
+
nodes: summaries,
|
|
136
|
+
stats: built.stats,
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function containsFile(scope, file) {
|
|
141
|
+
if (scope === ".") return true
|
|
142
|
+
return file === scope || file.startsWith(scope + "/")
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function scopesOverlap(left, right) {
|
|
146
|
+
if (left === "." || right === ".") return true
|
|
147
|
+
return left === right || left.startsWith(right + "/") || right.startsWith(left + "/")
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function selectDiverseHierarchyScopes(rows = [], maxScopes = 6) {
|
|
151
|
+
const limit = Math.max(1, Math.min(Number(maxScopes || 6), 20))
|
|
152
|
+
const selected = []
|
|
153
|
+
for (const row of rows) {
|
|
154
|
+
if (selected.length >= limit) break
|
|
155
|
+
if (selected.some((item) => scopesOverlap(item.path, row.path))) continue
|
|
156
|
+
selected.push(row)
|
|
157
|
+
}
|
|
158
|
+
if (!selected.length && rows.length) selected.push(rows[0])
|
|
159
|
+
return selected
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function queryContextHierarchy(root, query, options = {}) {
|
|
163
|
+
const hierarchy = await buildContextHierarchy(root, options)
|
|
164
|
+
const wanted = terms(query)
|
|
165
|
+
const declared = (options.declaredFiles || []).map((file) => String(file || "").replaceAll("\\", "/")).filter(Boolean)
|
|
166
|
+
const scored = hierarchy.nodes.map((node) => {
|
|
167
|
+
const lowerPath = node.path.toLowerCase()
|
|
168
|
+
const symbolText = node.symbols.map((item) => item.name.toLowerCase()).join(" ")
|
|
169
|
+
const fileText = node.files.slice(0, 100).join(" ").toLowerCase()
|
|
170
|
+
let score = node.path === "." ? 0.25 : 0
|
|
171
|
+
const reasons = []
|
|
172
|
+
for (const term of wanted) {
|
|
173
|
+
if (lowerPath.includes(term)) {
|
|
174
|
+
score += 8
|
|
175
|
+
reasons.push("path:" + term)
|
|
176
|
+
}
|
|
177
|
+
if (symbolText.includes(term)) {
|
|
178
|
+
score += 5
|
|
179
|
+
reasons.push("symbol:" + term)
|
|
180
|
+
}
|
|
181
|
+
if (fileText.includes(term)) {
|
|
182
|
+
score += 2
|
|
183
|
+
reasons.push("file:" + term)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const declaredHits = declared.filter((file) => containsFile(node.path, file)).length
|
|
187
|
+
if (declaredHits) {
|
|
188
|
+
score += 20 + declaredHits * 4
|
|
189
|
+
reasons.push("declared:" + declaredHits)
|
|
190
|
+
}
|
|
191
|
+
const depth = node.path === "." ? 0 : node.path.split("/").length
|
|
192
|
+
if (score > 0) score += Math.min(3, depth * 0.4)
|
|
193
|
+
score -= Math.min(2.5, Math.log1p(node.fileCount) * 0.15)
|
|
194
|
+
return { ...node, score: Number(score.toFixed(3)), reasons: [...new Set(reasons)].slice(0, 12) }
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
const maxScopes = Math.max(1, Math.min(Number(options.maxScopes || 6), 20))
|
|
198
|
+
const positive = scored
|
|
199
|
+
.filter((item) => item.score > 0)
|
|
200
|
+
.sort((a, b) => b.score - a.score || a.fileCount - b.fileCount || a.path.localeCompare(b.path))
|
|
201
|
+
const scopes = selectDiverseHierarchyScopes(
|
|
202
|
+
positive.length ? positive : scored.filter((item) => item.path === "."),
|
|
203
|
+
maxScopes,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
schemaVersion: 1,
|
|
208
|
+
kind: "ues-context-hierarchy-query",
|
|
209
|
+
query: String(query || ""),
|
|
210
|
+
terms: wanted,
|
|
211
|
+
levels: hierarchy.levels,
|
|
212
|
+
scopes,
|
|
213
|
+
stats: hierarchy.stats,
|
|
214
|
+
}
|
|
215
|
+
}
|