opencode-agent-skill 13.0.0-beta.2 → 14.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1571 -607
- package/bin/ocskill.mjs +172 -24
- package/docs/OPENCODE-COMPAT.md +34 -97
- package/docs/PI-COMPAT.md +203 -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 +359 -0
- package/lib/capability-registry.mjs +9 -0
- package/lib/code-intelligence/edit-anchor.mjs +102 -0
- package/lib/code-intelligence/index.mjs +95 -0
- package/lib/code-intelligence/lsp-provider.mjs +189 -0
- package/lib/completion-auditor.mjs +82 -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/document-ingestion.mjs +60 -0
- package/lib/dynamic-workflow.mjs +3 -1
- package/lib/evidence-store.mjs +82 -1
- package/lib/fast-verification-gate.mjs +69 -0
- package/lib/hierarchical-context.mjs +215 -0
- package/lib/mcp-health.mjs +144 -0
- package/lib/mcp-tool-policy.mjs +19 -0
- package/lib/memory-engine.mjs +493 -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 +27 -11
- package/lib/repo-graph.mjs +53 -2
- package/lib/reversible-context.mjs +49 -0
- 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 +62 -5
- package/lib/task-policy.mjs +291 -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 +393 -0
- package/pi/extensions/ues.ts +3327 -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 +246 -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
package/lib/task-engine.mjs
CHANGED
|
@@ -15,6 +15,8 @@ import { putEvidence } from "./evidence-store.mjs"
|
|
|
15
15
|
import { inferTaskCapabilities } from "./capability-registry.mjs"
|
|
16
16
|
import { buildPromptEnvelope } from "./prompt-cache.mjs"
|
|
17
17
|
import { externalizeContextExcerpts } from "./context-engine-v11.mjs"
|
|
18
|
+
import { buildMemorySnapshot, recordVerifiedTaskMemory, retrieveMemories } from "./memory-engine.mjs"
|
|
19
|
+
import { capabilityFabricStatus } from "./capability-fabric.mjs"
|
|
18
20
|
import { assertActivePlanScope, persistPlanScope } from "./work-plan-scope.mjs"
|
|
19
21
|
import { classifyDecisionPolicy } from "./decision-policy.mjs"
|
|
20
22
|
import { readTextAuto } from "./text-encoding.mjs"
|
|
@@ -174,7 +176,7 @@ function gitCapture(root, args) {
|
|
|
174
176
|
}
|
|
175
177
|
|
|
176
178
|
const FINGERPRINT_SKIP_DIRS = new Set([
|
|
177
|
-
".git", ".ues-work", ".ues-learning", ".ues-dashboard", ".ues-sandboxes", ".ues-cache", ".ues-traces",
|
|
179
|
+
".git", ".ues-work", ".ues-learning", ".ues-dashboard", ".ues-sandboxes", ".ues-cache", ".ues-traces", ".ues-memory",
|
|
178
180
|
"node_modules", ".next", "dist", "build", "coverage", ".venv", "venv",
|
|
179
181
|
"Pods", "DerivedData", ".gradle", ".cache", ".turbo", "target", "bin", "obj",
|
|
180
182
|
])
|
|
@@ -256,9 +258,9 @@ export function workspaceFingerprint(root) {
|
|
|
256
258
|
const parts = []
|
|
257
259
|
const commands = [
|
|
258
260
|
["rev-parse", "HEAD"],
|
|
259
|
-
["status", "--porcelain=v1", "--untracked-files=all", "--", ".", ":(exclude).ues-work", ":(exclude).ues-learning", ":(exclude).ues-dashboard", ":(exclude).ues-sandboxes", ":(exclude).ues-cache", ":(exclude).ues-traces"],
|
|
260
|
-
["diff", "--binary", "--no-ext-diff", "--", ".", ":(exclude).ues-work", ":(exclude).ues-learning", ":(exclude).ues-dashboard", ":(exclude).ues-sandboxes", ":(exclude).ues-cache", ":(exclude).ues-traces"],
|
|
261
|
-
["diff", "--cached", "--binary", "--no-ext-diff", "--", ".", ":(exclude).ues-work", ":(exclude).ues-learning", ":(exclude).ues-dashboard", ":(exclude).ues-sandboxes", ":(exclude).ues-cache", ":(exclude).ues-traces"],
|
|
261
|
+
["status", "--porcelain=v1", "--untracked-files=all", "--", ".", ":(exclude).ues-work", ":(exclude).ues-learning", ":(exclude).ues-dashboard", ":(exclude).ues-sandboxes", ":(exclude).ues-cache", ":(exclude).ues-traces", ":(exclude).ues-memory"],
|
|
262
|
+
["diff", "--binary", "--no-ext-diff", "--", ".", ":(exclude).ues-work", ":(exclude).ues-learning", ":(exclude).ues-dashboard", ":(exclude).ues-sandboxes", ":(exclude).ues-cache", ":(exclude).ues-traces", ":(exclude).ues-memory"],
|
|
263
|
+
["diff", "--cached", "--binary", "--no-ext-diff", "--", ".", ":(exclude).ues-work", ":(exclude).ues-learning", ":(exclude).ues-dashboard", ":(exclude).ues-sandboxes", ":(exclude).ues-cache", ":(exclude).ues-traces", ":(exclude).ues-memory"],
|
|
262
264
|
]
|
|
263
265
|
for (const args of commands) {
|
|
264
266
|
const result = gitCapture(root, args)
|
|
@@ -1087,7 +1089,20 @@ export async function finalizeWork(root, slug, evidence) {
|
|
|
1087
1089
|
await writeJson(loaded.paths.evidence, loaded.evidence)
|
|
1088
1090
|
await writeJson(loaded.paths.state, loaded.state)
|
|
1089
1091
|
await journal(loaded.paths, "work.finalized", { fingerprint: currentFingerprint })
|
|
1090
|
-
|
|
1092
|
+
|
|
1093
|
+
const memory = await recordVerifiedTaskMemory(loaded.paths.root, {
|
|
1094
|
+
task: loaded.state.goal || loaded.plan.goal || `Completed durable work: ${slug}`,
|
|
1095
|
+
files: [...new Set(loaded.plan.tasks.flatMap((task) => taskFiles(task)))],
|
|
1096
|
+
verifier: verification.receipt?.verifier || "ues-integration-verifier",
|
|
1097
|
+
verifierOutput: [verification.evidence, verification.report].filter(Boolean).join("\n"),
|
|
1098
|
+
integrationOutput: evidenceText,
|
|
1099
|
+
sourceTask: `work:${slug}`,
|
|
1100
|
+
taskClass: "long-horizon",
|
|
1101
|
+
type: "episodic",
|
|
1102
|
+
scope: "project",
|
|
1103
|
+
}).catch(() => null)
|
|
1104
|
+
|
|
1105
|
+
return { state: loaded.state, evidence: loaded.evidence.entries.at(-1), memory }
|
|
1091
1106
|
})
|
|
1092
1107
|
}
|
|
1093
1108
|
|
|
@@ -1161,6 +1176,25 @@ async function buildContextPack(loaded, taskID) {
|
|
|
1161
1176
|
[task.title, task.summary, ...(task.acceptance || [])].join(" "),
|
|
1162
1177
|
).catch(() => [])
|
|
1163
1178
|
|
|
1179
|
+
const memoryRetrieval = await retrieveMemories(loaded.paths.root, taskText, {
|
|
1180
|
+
files: taskFiles(task),
|
|
1181
|
+
limit: 6,
|
|
1182
|
+
taskClass: contextPolicy.executionProfile || contextPolicy.profile?.name || contextPolicy.mode || null,
|
|
1183
|
+
touch: true,
|
|
1184
|
+
}).catch(() => ({ schemaVersion: 1, query: taskText, eligible: 0, results: [] }))
|
|
1185
|
+
const memories = memoryRetrieval.results || []
|
|
1186
|
+
const memorySnapshot = memoryRetrieval.snapshot || buildMemorySnapshot(memories)
|
|
1187
|
+
const fabric = await capabilityFabricStatus(loaded.paths.root).catch(() => null)
|
|
1188
|
+
const providerHints = (fabric?.rows || [])
|
|
1189
|
+
.filter((row) => row.selected)
|
|
1190
|
+
.slice(0, 12)
|
|
1191
|
+
.map((row) => ({
|
|
1192
|
+
capability: row.capability,
|
|
1193
|
+
selected: row.selected,
|
|
1194
|
+
status: row.status,
|
|
1195
|
+
fallbacks: (row.fallbacks || []).slice(0, 3),
|
|
1196
|
+
}))
|
|
1197
|
+
|
|
1164
1198
|
const specStored = spec
|
|
1165
1199
|
? await putEvidence(loaded.paths.root, spec, {
|
|
1166
1200
|
kind: "work-spec",
|
|
@@ -1183,6 +1217,15 @@ async function buildContextPack(loaded, taskID) {
|
|
|
1183
1217
|
...(contextManifest?.evidencePointers || []).map((item) => item.ref),
|
|
1184
1218
|
...(contextManifest?.rankedReferences || []).slice(0, 12).map((item) => item.path),
|
|
1185
1219
|
],
|
|
1220
|
+
memorySnapshot,
|
|
1221
|
+
contextHints: {
|
|
1222
|
+
hierarchy: (contextManifest?.hierarchy?.scopes || []).slice(0, 6).map((item) => ({
|
|
1223
|
+
path: item.path,
|
|
1224
|
+
score: item.score,
|
|
1225
|
+
l0: item.l0,
|
|
1226
|
+
})),
|
|
1227
|
+
providers: providerHints,
|
|
1228
|
+
},
|
|
1186
1229
|
recentFailure: recovery.stage === "initial" ? null : loaded.state.tasks?.[taskID]?.lastError || null,
|
|
1187
1230
|
nextAction: loaded.state.checkpoint?.nextAction || loaded.state.nextAction || null,
|
|
1188
1231
|
})
|
|
@@ -1198,7 +1241,9 @@ async function buildContextPack(loaded, taskID) {
|
|
|
1198
1241
|
dynamicHash: promptEnvelope.dynamicHash,
|
|
1199
1242
|
stableChars: promptEnvelope.stableChars,
|
|
1200
1243
|
dynamicChars: promptEnvelope.dynamicChars,
|
|
1244
|
+
cacheableChars: promptEnvelope.cacheableChars,
|
|
1201
1245
|
cacheableRatio: promptEnvelope.cacheableRatio,
|
|
1246
|
+
memoryGeneration: memorySnapshot.generation,
|
|
1202
1247
|
},
|
|
1203
1248
|
attempt,
|
|
1204
1249
|
slug: loaded.state.slug,
|
|
@@ -1220,6 +1265,18 @@ async function buildContextPack(loaded, taskID) {
|
|
|
1220
1265
|
planApproval: loaded.state.planApproval || null,
|
|
1221
1266
|
contextManifest,
|
|
1222
1267
|
acceptedLearnings: learnings,
|
|
1268
|
+
memories,
|
|
1269
|
+
memoryRetrieval: {
|
|
1270
|
+
schemaVersion: memoryRetrieval.schemaVersion || 1,
|
|
1271
|
+
eligible: memoryRetrieval.eligible || 0,
|
|
1272
|
+
returned: memories.length,
|
|
1273
|
+
generation: memorySnapshot.generation,
|
|
1274
|
+
},
|
|
1275
|
+
capabilityFabric: {
|
|
1276
|
+
healthyCapabilities: fabric?.healthyCapabilities || 0,
|
|
1277
|
+
totalCapabilities: fabric?.totalCapabilities || 0,
|
|
1278
|
+
providers: providerHints,
|
|
1279
|
+
},
|
|
1223
1280
|
workingState: {
|
|
1224
1281
|
status: loaded.state.status,
|
|
1225
1282
|
task: loaded.state.tasks?.[taskID],
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
const SENSITIVE_DOMAIN = /(\bauth\b|authorization|authentication|security|permission|payment|schema|database|production|deploy|public api|secret|credential|phân quyền|bảo mật|thanh toán|cơ sở dữ liệu|triển khai|api công khai|bí mật|thông tin xác thực)/i
|
|
2
|
+
const HIGH_RISK_MUTATION = /((?:fix|change|modify|update|alter|migrate|drop|truncate|delete|remove|rotate|deploy|publish|push|sửa|thay đổi|cập nhật|xóa|xoá|di trú|chuyển đổi|triển khai).{0,64}(?:\bauth\b|authorization|authentication|security|permission|payment(?: handling| flow)?|schema|database|production|public api|secret|credential|phân quyền|bảo mật|thanh toán|cơ sở dữ liệu|api công khai|bí mật|thông tin xác thực)|(?:\bauth\b|authorization|authentication|security|permission|payment(?: handling| flow)?|schema|database|production|public api|secret|credential|phân quyền|bảo mật|thanh toán|cơ sở dữ liệu|api công khai|bí mật|thông tin xác thực).{0,64}(?:fix|change|modify|update|alter|migrate|drop|truncate|delete|remove|rotate|deploy|publish|push|sửa|thay đổi|cập nhật|xóa|xoá|di trú|chuyển đổi|triển khai)|database migration|schema migration|migrate database|migrate schema|drop table|truncate table|deploy(?:ment)?\s+(?:to\s+)?production|production\s+deploy(?:ment)?|rotate\s+(?:secret|credential)|breaking\s+(?:change\s+to\s+)?(?:public\s+)?api|npm publish|git push|force push|reset --hard|git clean)/i
|
|
3
|
+
const LONG = /(whole repo|whole repository|whole project|entire repo|entire project|large refactor|major refactor|long[- ](?:running|horizon)|durable state|dependency graph|integration verification|resume|refactor all|multi[- ]step migration|migration across|toàn bộ repo|toàn bộ repository|toàn bộ dự án|toàn bộ project|refactor lớn|tác vụ dài|nhiều file|nhiều module|tiếp tục công việc|refactor toàn bộ|xác minh tích hợp|kiểm tra tích hợp|chia (?:công việc|task|tác vụ).*(?:dependency|phụ thuộc))/i
|
|
4
|
+
const DEBUG = /(fix|bug|debug|crash|regression|failure|error|broken|sửa lỗi|lỗi|điều tra lỗi|không chạy)/i
|
|
5
|
+
const CONTRACT = /(public api|api contract|openapi|response schema|request schema|breaking api|hợp đồng api|api công khai)/i
|
|
6
|
+
const DATA = /(database|sql|migration|schema|transaction|index|cơ sở dữ liệu|dữ liệu|migrate)/i
|
|
7
|
+
const BROAD_FILE_SCOPE = /(across\s+(?:multiple|several|many)\s+(?:files|modules|packages)|multiple\s+(?:files|modules|packages)|several\s+(?:files|modules|packages)|many\s+(?:files|modules|packages)|nhiều\s+(?:file|tệp|module)|toàn bộ\s+(?:repo|repository|project|dự án))/i
|
|
8
|
+
const CRITICAL_LOCAL_MUTATION = /(drop\s+table|truncate\s+table|reset\s+--hard|git\s+clean|force\s+push|npm\s+publish|deploy(?:ment)?\s+(?:to\s+)?production|production\s+deploy(?:ment)?|rotate\s+(?:secret|credential)|(?:secret|credential|token).{0,48}(?:rotate|replace|revoke|delete)|(?:authorization|authentication|permission|phân quyền|xác thực).{0,64}(?:change|modify|update|bypass|remove|delete|sửa|thay đổi)|(?:database|sql|schema).{0,64}(?:migration|migrate|drop|truncate|alter)|(?:migration|migrate).{0,64}(?:database|sql|schema)|payment.{0,64}(?:charge|capture|refund|webhook|checkout)|(?:charge|capture|refund|webhook|checkout).{0,64}payment|breaking\s+(?:change\s+to\s+)?(?:public\s+)?api)/i
|
|
9
|
+
const SOURCE_FILE_RE = /(?:^|[\s("'\x60])((?:[A-Za-z]:)?(?:[./\\\w@-]+[\\/])*[\w@.-]+\.(?:[cm]?[jt]sx?|py|go|rs|java|kt|kts|cs|rb|php|swift|dart|vue|svelte))(?=$|[\s"'\x60),:;.])/gi
|
|
10
|
+
|
|
11
|
+
function referencedSourceFiles(value) {
|
|
12
|
+
const files = []
|
|
13
|
+
const seen = new Set()
|
|
14
|
+
SOURCE_FILE_RE.lastIndex = 0
|
|
15
|
+
let match
|
|
16
|
+
while ((match = SOURCE_FILE_RE.exec(String(value || "")))) {
|
|
17
|
+
const normalized = String(match[1] || "").replaceAll("\\", "/").toLowerCase()
|
|
18
|
+
if (!normalized || seen.has(normalized)) continue
|
|
19
|
+
seen.add(normalized)
|
|
20
|
+
files.push(normalized)
|
|
21
|
+
}
|
|
22
|
+
return files
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function singleFileBoundedTask(value, facts = {}) {
|
|
26
|
+
const declaredFiles = Array.isArray(facts.declaredFiles) ? facts.declaredFiles.map(String).filter(Boolean) : []
|
|
27
|
+
const changedFileCount = Array.isArray(facts.changedFiles)
|
|
28
|
+
? facts.changedFiles.length
|
|
29
|
+
: Number.isFinite(Number(facts.changedFiles)) ? Number(facts.changedFiles) : 0
|
|
30
|
+
const referencedFiles = referencedSourceFiles(value)
|
|
31
|
+
const counts = [declaredFiles.length, changedFileCount, referencedFiles.length].filter((count) => count > 0)
|
|
32
|
+
if (!counts.length || counts.some((count) => count > 1)) return false
|
|
33
|
+
if (BROAD_FILE_SCOPE.test(String(value || "")) || LONG.test(String(value || ""))) return false
|
|
34
|
+
return counts.some((count) => count === 1) && (
|
|
35
|
+
declaredFiles.length === 1 ||
|
|
36
|
+
changedFileCount === 1 ||
|
|
37
|
+
/\b(?:fix|repair|change|update|modify|rename|implement|correct|sửa|chỉnh sửa|thay đổi|cập nhật)\b/i.test(String(value || ""))
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
function riskTextFor(value) {
|
|
43
|
+
return String(value || "")
|
|
44
|
+
.replace(/\b(?:do not|don't|without)\s+(?:edit|modify|change|write|delete|remove)[^.\n]*/gi, "")
|
|
45
|
+
.replace(/\b(?:no|read[- ]only)\s+(?:edits?|changes?|writes?)[^.\n]*/gi, "")
|
|
46
|
+
.replace(/không\s+(?:sửa|chỉnh sửa|thay đổi|ghi|xóa|xoá)[^.\n]*/gi, "")
|
|
47
|
+
.replace(/chỉ\s+đọc[^.\n]*/gi, "")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function signal(name, matched, weight) {
|
|
51
|
+
return matched ? { name, weight } : null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function profileFor(mode, risk) {
|
|
55
|
+
if (mode === "inline" && risk === "low") {
|
|
56
|
+
return {
|
|
57
|
+
name: "fast",
|
|
58
|
+
maxSkills: 2,
|
|
59
|
+
contextBudget: 8_000,
|
|
60
|
+
contextStrategy: "incremental-semantic",
|
|
61
|
+
skillLoading: "direct-only",
|
|
62
|
+
durableState: false,
|
|
63
|
+
worktree: "off",
|
|
64
|
+
critic: "off",
|
|
65
|
+
verification: "targeted",
|
|
66
|
+
fullCI: false,
|
|
67
|
+
containerVerification: "off",
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (mode === "long-horizon" || risk === "high") {
|
|
71
|
+
return {
|
|
72
|
+
name: "deep",
|
|
73
|
+
maxSkills: 5,
|
|
74
|
+
contextBudget: 48_000,
|
|
75
|
+
contextStrategy: "semantic+graph+git",
|
|
76
|
+
skillLoading: "orchestrated",
|
|
77
|
+
durableState: true,
|
|
78
|
+
worktree: "auto-writers",
|
|
79
|
+
critic: "required-for-high-risk-or-final",
|
|
80
|
+
verification: "targeted+integration",
|
|
81
|
+
fullCI: true,
|
|
82
|
+
containerVerification: risk === "high" ? "preferred-if-capable" : "optional",
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
name: "standard",
|
|
87
|
+
maxSkills: 4,
|
|
88
|
+
contextBudget: 20_000,
|
|
89
|
+
contextStrategy: "incremental-semantic+git",
|
|
90
|
+
skillLoading: "selective",
|
|
91
|
+
durableState: false,
|
|
92
|
+
worktree: "auto-on-conflict",
|
|
93
|
+
critic: "on-failure-or-elevated-risk",
|
|
94
|
+
verification: "targeted+affected",
|
|
95
|
+
fullCI: false,
|
|
96
|
+
containerVerification: "off",
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function boundedInt(value, fallback, min, max) {
|
|
101
|
+
const parsed = Number(value)
|
|
102
|
+
if (!Number.isFinite(parsed)) return fallback
|
|
103
|
+
return Math.min(max, Math.max(min, Math.round(parsed)))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function recoveryPolicyForAttempt(taskPolicy = {}, attempt = 1) {
|
|
107
|
+
const normalizedAttempt = boundedInt(attempt, 1, 1, 99)
|
|
108
|
+
const baseBudget = boundedInt(
|
|
109
|
+
taskPolicy.contextBudget ?? taskPolicy.profile?.contextBudget,
|
|
110
|
+
20_000,
|
|
111
|
+
4_000,
|
|
112
|
+
48_000,
|
|
113
|
+
)
|
|
114
|
+
const baseSkills = boundedInt(
|
|
115
|
+
taskPolicy.maxSkills ?? taskPolicy.profile?.maxSkills,
|
|
116
|
+
4,
|
|
117
|
+
1,
|
|
118
|
+
5,
|
|
119
|
+
)
|
|
120
|
+
const baseStrategy = taskPolicy.profile?.contextStrategy || "incremental-semantic+git"
|
|
121
|
+
|
|
122
|
+
if (normalizedAttempt <= 1) {
|
|
123
|
+
return {
|
|
124
|
+
schemaVersion: 1,
|
|
125
|
+
stage: "initial",
|
|
126
|
+
attempt: normalizedAttempt,
|
|
127
|
+
contextBudget: baseBudget,
|
|
128
|
+
maxSkills: baseSkills,
|
|
129
|
+
contextStrategy: baseStrategy,
|
|
130
|
+
requireDiagnosis: false,
|
|
131
|
+
requireCritic: false,
|
|
132
|
+
modelEscalation: false,
|
|
133
|
+
directives: [],
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (normalizedAttempt === 2) {
|
|
138
|
+
return {
|
|
139
|
+
schemaVersion: 1,
|
|
140
|
+
stage: "diagnose",
|
|
141
|
+
attempt: normalizedAttempt,
|
|
142
|
+
contextBudget: Math.min(48_000, Math.max(baseBudget, Math.round(baseBudget * 1.35))),
|
|
143
|
+
maxSkills: Math.min(5, baseSkills + 1),
|
|
144
|
+
contextStrategy:
|
|
145
|
+
taskPolicy.executionProfile === "fast"
|
|
146
|
+
? "incremental-semantic+git"
|
|
147
|
+
: baseStrategy,
|
|
148
|
+
requireDiagnosis: true,
|
|
149
|
+
requireCritic: false,
|
|
150
|
+
modelEscalation: true,
|
|
151
|
+
directives: [
|
|
152
|
+
"reproduce or capture the exact previous failure before editing",
|
|
153
|
+
"inspect the direct caller, nearest test and failure-adjacent evidence",
|
|
154
|
+
"do not stack another speculative patch on top of the failed attempt",
|
|
155
|
+
],
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
schemaVersion: 1,
|
|
161
|
+
stage: "deep-recovery",
|
|
162
|
+
attempt: normalizedAttempt,
|
|
163
|
+
contextBudget: Math.min(48_000, Math.max(20_000, Math.round(baseBudget * 1.75))),
|
|
164
|
+
maxSkills: Math.min(5, baseSkills + 2),
|
|
165
|
+
contextStrategy: "semantic+graph+git",
|
|
166
|
+
requireDiagnosis: true,
|
|
167
|
+
requireCritic: true,
|
|
168
|
+
modelEscalation: true,
|
|
169
|
+
directives: [
|
|
170
|
+
"re-investigate from fresh evidence and explicitly reject the failed hypothesis",
|
|
171
|
+
"expand to callers, dependencies, tests and boundary contracts before editing",
|
|
172
|
+
"challenge the architecture or coupling if repeated fixes expose a wider problem",
|
|
173
|
+
"run an independent critic or review pass before accepting the recovery",
|
|
174
|
+
],
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function shouldRunDedicatedDiagnosis(taskPolicy = {}, attempt = 1) {
|
|
179
|
+
const hasDebugSignal = Array.isArray(taskPolicy.signals) &&
|
|
180
|
+
taskPolicy.signals.some((item) => item?.name === "debugging")
|
|
181
|
+
if (!hasDebugSignal) return false
|
|
182
|
+
|
|
183
|
+
const normalizedAttempt = boundedInt(attempt, 1, 1, 99)
|
|
184
|
+
if (normalizedAttempt > 1) {
|
|
185
|
+
return recoveryPolicyForAttempt(taskPolicy, normalizedAttempt).requireDiagnosis === true
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return taskPolicy.executionProfile !== "fast" || taskPolicy.risk !== "low"
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function classifyEngineeringTask(text, facts = {}) {
|
|
192
|
+
const value = String(text || "")
|
|
193
|
+
const riskText = riskTextFor(value)
|
|
194
|
+
const sensitiveDomain = SENSITIVE_DOMAIN.test(value)
|
|
195
|
+
const singleFileBounded = singleFileBoundedTask(value, facts)
|
|
196
|
+
const rawSensitiveMutation = HIGH_RISK_MUTATION.test(riskText)
|
|
197
|
+
const sensitiveMutation =
|
|
198
|
+
rawSensitiveMutation &&
|
|
199
|
+
(!singleFileBounded || CRITICAL_LOCAL_MUTATION.test(riskText))
|
|
200
|
+
const dataMigration =
|
|
201
|
+
Boolean(facts.hasMigration) ||
|
|
202
|
+
(
|
|
203
|
+
!singleFileBounded &&
|
|
204
|
+
DATA.test(riskText) &&
|
|
205
|
+
/migration|schema|migrate|di trú|chuyển đổi/i.test(riskText)
|
|
206
|
+
)
|
|
207
|
+
const declaredHighRisk =
|
|
208
|
+
["high", "critical"].includes(String(facts.risk || "").toLowerCase()) ||
|
|
209
|
+
/\b(?:risk|rủi ro)\s*[:=\/-]?\s*(?:high|critical|cao|nghiêm trọng)\b/i.test(value) ||
|
|
210
|
+
/\b(?:high|critical)[-\s]?(?:risk|rủi ro)\b/i.test(value)
|
|
211
|
+
const declaredLongHorizon =
|
|
212
|
+
facts.longHorizon === true ||
|
|
213
|
+
["long", "long-horizon", "deep"].includes(String(facts.mode || "").toLowerCase())
|
|
214
|
+
const compoundLongRisk =
|
|
215
|
+
/\blong\s*[/|,+]\s*(?:high|critical)[-\s]?risk\b/i.test(value) ||
|
|
216
|
+
/\b(?:high|critical)[-\s]?risk\s*[/|,+]\s*long\b/i.test(value)
|
|
217
|
+
const explicitLongHorizon = declaredLongHorizon || compoundLongRisk || LONG.test(value)
|
|
218
|
+
const signals = [
|
|
219
|
+
signal("long-request-text", value.length > 700, 1),
|
|
220
|
+
signal("medium-request-text", value.length > 250, 1),
|
|
221
|
+
signal("high-risk-domain", sensitiveDomain, 1),
|
|
222
|
+
signal("high-risk-operation", sensitiveMutation, 2),
|
|
223
|
+
signal("declared-high-risk", declaredHighRisk, 2),
|
|
224
|
+
signal("explicit-long-horizon", explicitLongHorizon, 2),
|
|
225
|
+
signal("debugging", DEBUG.test(value), 1),
|
|
226
|
+
signal("single-file-bounded", singleFileBounded, -2),
|
|
227
|
+
signal("public-contract", CONTRACT.test(value) || facts.hasPublicContract, 2),
|
|
228
|
+
signal("data-migration", dataMigration, 2),
|
|
229
|
+
signal("many-changed-files", Number(facts.changedFiles || 0) > 5, 1),
|
|
230
|
+
signal("very-many-changed-files", Number(facts.changedFiles || 0) > 12, 1),
|
|
231
|
+
signal("large-repository", Number(facts.repoFiles || 0) > 1500, 1),
|
|
232
|
+
signal("monorepo", facts.monorepo === true, 1),
|
|
233
|
+
].filter(Boolean)
|
|
234
|
+
|
|
235
|
+
const score = Math.max(0, signals.reduce((sum, item) => sum + item.weight, 0))
|
|
236
|
+
const highRisk = declaredHighRisk || sensitiveMutation || Boolean(facts.hasMigration) || Boolean(facts.hasPublicContract)
|
|
237
|
+
const risk = highRisk ? "high" : singleFileBounded ? "low" : score >= 3 ? "medium" : "low"
|
|
238
|
+
const mode =
|
|
239
|
+
explicitLongHorizon
|
|
240
|
+
? "long-horizon"
|
|
241
|
+
: singleFileBounded && !highRisk
|
|
242
|
+
? "inline"
|
|
243
|
+
: score >= 4
|
|
244
|
+
? "long-horizon"
|
|
245
|
+
: score >= 2
|
|
246
|
+
? "standard"
|
|
247
|
+
: "inline"
|
|
248
|
+
const modelTier = risk === "high" || mode === "long-horizon" ? "heavy" : score >= 2 ? "standard" : "light"
|
|
249
|
+
const maxAttempts = risk === "high" ? 2 : 3
|
|
250
|
+
const profile = profileFor(mode, risk)
|
|
251
|
+
|
|
252
|
+
const domains = []
|
|
253
|
+
if (/(auth|permission|tenant|token|session|phân quyền|xác thực)/i.test(value)) domains.push("auth-security")
|
|
254
|
+
if (/(payment|checkout|refund|webhook|thanh toán|hoàn tiền)/i.test(value)) domains.push("payment")
|
|
255
|
+
if (DATA.test(value)) domains.push("database")
|
|
256
|
+
if (CONTRACT.test(value)) domains.push("api-contract")
|
|
257
|
+
if (/(react native|expo|android|ios|gradle|xcode)/i.test(value)) domains.push("react-native")
|
|
258
|
+
else if (/(next\.js|nextjs|app router|server component)/i.test(value)) domains.push("nextjs")
|
|
259
|
+
else if (/\breact\b|useeffect|usestate|component/i.test(value)) domains.push("react")
|
|
260
|
+
if (/(docker|kubernetes|terraform|github actions|ci\/cd|deploy|triển khai)/i.test(value)) domains.push("devops")
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
schemaVersion: 4,
|
|
264
|
+
score,
|
|
265
|
+
signals,
|
|
266
|
+
risk,
|
|
267
|
+
mode,
|
|
268
|
+
executionProfile: profile.name,
|
|
269
|
+
modelTier,
|
|
270
|
+
maxAttempts,
|
|
271
|
+
contextBudget: profile.contextBudget,
|
|
272
|
+
maxSkills: profile.maxSkills,
|
|
273
|
+
requirePlanCheck: profile.durableState || risk === "high",
|
|
274
|
+
requireIntegrationVerification: mode !== "inline" || risk === "high",
|
|
275
|
+
requireFreshEvidence: true,
|
|
276
|
+
singleFileBounded,
|
|
277
|
+
profile,
|
|
278
|
+
domains: [...new Set(domains)],
|
|
279
|
+
recovery: {
|
|
280
|
+
escalateAfterFailure: true,
|
|
281
|
+
diagnosisBeforePatch: true,
|
|
282
|
+
deepRecoveryFromAttempt: 3,
|
|
283
|
+
},
|
|
284
|
+
antiHallucination: {
|
|
285
|
+
evidenceFirst: true,
|
|
286
|
+
noCompletionWithoutVerification: mode !== "inline" || risk === "high",
|
|
287
|
+
noUnsupportedSemanticClaims: true,
|
|
288
|
+
failClosedOnMissingCapability: risk === "high",
|
|
289
|
+
},
|
|
290
|
+
}
|
|
291
|
+
}
|