opencode-agent-skill 14.4.0 → 15.1.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 +34 -16
- package/docs/PI-COMPAT.md +151 -2
- package/docs/V15-MANAGED-RUNTIME.md +128 -0
- package/global-config/agents/architect.md +63 -1
- package/global-config/agents/executor.md +7 -0
- package/global-config/agents/plan-checker.md +8 -1
- package/global-config/agents/verifier.md +3 -3
- package/global-config/skills/database-engineering/references/workflow.md +15 -0
- package/global-config/skills/engineering-orchestrator/references/long-horizon.md +21 -11
- package/lib/activity-deadline.mjs +70 -0
- package/lib/adaptive-context-budget.mjs +25 -5
- package/lib/affected-tests.mjs +38 -5
- package/lib/capability-fabric.mjs +17 -0
- package/lib/code-intelligence/index.mjs +3 -2
- package/lib/code-intelligence/lsp-provider.mjs +326 -52
- package/lib/compaction-resume-guard.mjs +255 -0
- package/lib/completion-auditor.mjs +25 -5
- package/lib/context-engine-v11.mjs +20 -7
- package/lib/evidence-store.mjs +92 -8
- package/lib/execution-contract.mjs +592 -0
- package/lib/leaf-runtime-optimizer.mjs +76 -0
- package/lib/memory-engine.mjs +189 -16
- package/lib/performance-fabric.mjs +262 -107
- package/lib/pi-child-invocation.mjs +65 -0
- package/lib/pi-rpc-pool.mjs +61 -9
- package/lib/plan-salvage.mjs +124 -0
- package/lib/planning-speed-policy.mjs +131 -0
- package/lib/repo-graph.mjs +1 -1
- package/lib/repo-inspect.mjs +1 -0
- package/lib/runtime-artifacts.mjs +42 -0
- package/lib/runtime-events.mjs +33 -1
- package/lib/safety.mjs +1 -0
- package/lib/semantic-index.mjs +1 -1
- package/lib/service-manager.mjs +526 -0
- package/lib/session-display.mjs +49 -0
- package/lib/subagent-artifacts.mjs +182 -0
- package/lib/task-graph.mjs +87 -0
- package/lib/task-policy.mjs +150 -4
- package/lib/trajectory.mjs +111 -1
- package/lib/turbo-fast-path.mjs +44 -0
- package/lib/verification-broker.mjs +32 -27
- package/lib/workspace-fingerprint.mjs +45 -19
- package/lib/workspace-hygiene.mjs +481 -0
- package/lib/workspace-root.mjs +49 -0
- package/lib/worktree-sandbox.mjs +234 -11
- package/package.json +8 -5
- package/pi/extensions/ues-child-runtime.ts +170 -4
- package/pi/extensions/ues.ts +2063 -188
- package/scripts/check-release-consistency.mjs +25 -2
- package/scripts/check-source-integrity.mjs +471 -4
- package/scripts/eval-pi.mjs +76 -36
- package/scripts/smoke-pi-extension.mjs +1 -1
- package/pi/prompts/ues-run.md +0 -17
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizePlanForValidation,
|
|
3
|
+
validatePlan,
|
|
4
|
+
} from "./task-graph.mjs"
|
|
5
|
+
|
|
6
|
+
function balancedObject(source, start) {
|
|
7
|
+
let depth = 0
|
|
8
|
+
let inString = false
|
|
9
|
+
let escaped = false
|
|
10
|
+
for (let index = start; index < source.length; index++) {
|
|
11
|
+
const char = source[index]
|
|
12
|
+
if (inString) {
|
|
13
|
+
if (escaped) escaped = false
|
|
14
|
+
else if (char === "\\") escaped = true
|
|
15
|
+
else if (char === "\"") inString = false
|
|
16
|
+
continue
|
|
17
|
+
}
|
|
18
|
+
if (char === "\"") {
|
|
19
|
+
inString = true
|
|
20
|
+
continue
|
|
21
|
+
}
|
|
22
|
+
if (char === "{") depth += 1
|
|
23
|
+
else if (char === "}") {
|
|
24
|
+
depth -= 1
|
|
25
|
+
if (depth === 0) {
|
|
26
|
+
const raw = source.slice(start, index + 1)
|
|
27
|
+
try {
|
|
28
|
+
return { value: JSON.parse(raw), raw, end: index + 1 }
|
|
29
|
+
} catch {
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function markedCandidate(source, marker) {
|
|
39
|
+
const markerIndex = source.lastIndexOf(marker)
|
|
40
|
+
if (markerIndex < 0) return null
|
|
41
|
+
const start = source.indexOf("{", markerIndex + marker.length)
|
|
42
|
+
if (start < 0) return null
|
|
43
|
+
const parsed = balancedObject(source, start)
|
|
44
|
+
return parsed ? { ...parsed, source: "marked" } : null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function fencedCandidates(source) {
|
|
48
|
+
const results = []
|
|
49
|
+
const fence = String.fromCharCode(96, 96, 96)
|
|
50
|
+
let offset = 0
|
|
51
|
+
while (offset < source.length) {
|
|
52
|
+
const open = source.indexOf(fence, offset)
|
|
53
|
+
if (open < 0) break
|
|
54
|
+
const bodyStart = source.indexOf("\n", open + fence.length)
|
|
55
|
+
if (bodyStart < 0) break
|
|
56
|
+
const close = source.indexOf(fence, bodyStart + 1)
|
|
57
|
+
if (close < 0) break
|
|
58
|
+
const body = source.slice(bodyStart + 1, close).trim()
|
|
59
|
+
if (body.startsWith("{")) {
|
|
60
|
+
try { results.push({ value: JSON.parse(body), raw: body, source: "fenced" }) } catch {}
|
|
61
|
+
}
|
|
62
|
+
offset = close + fence.length
|
|
63
|
+
}
|
|
64
|
+
return results
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function balancedCandidates(source, limit = 24) {
|
|
68
|
+
const starts = []
|
|
69
|
+
for (let i = source.length - 1; i >= 0 && starts.length < limit; i--) {
|
|
70
|
+
if (source[i] === "{") starts.push(i)
|
|
71
|
+
}
|
|
72
|
+
const results = []
|
|
73
|
+
for (const start of starts) {
|
|
74
|
+
const parsed = balancedObject(source, start)
|
|
75
|
+
if (parsed) results.push({ ...parsed, source: "unmarked-balanced" })
|
|
76
|
+
}
|
|
77
|
+
return results
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function planLike(value) {
|
|
81
|
+
return Boolean(
|
|
82
|
+
value &&
|
|
83
|
+
typeof value === "object" &&
|
|
84
|
+
Array.isArray(value.tasks) &&
|
|
85
|
+
(typeof value.goal === "string" || value.schemaVersion === 1)
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function extractValidatedPlan(output, options = {}) {
|
|
90
|
+
const source = String(output || "")
|
|
91
|
+
const marker = String(options.marker || "UES_PLAN_JSON:")
|
|
92
|
+
const candidates = []
|
|
93
|
+
const marked = markedCandidate(source, marker)
|
|
94
|
+
if (marked) candidates.push(marked)
|
|
95
|
+
candidates.push(...fencedCandidates(source))
|
|
96
|
+
candidates.push(...balancedCandidates(source))
|
|
97
|
+
|
|
98
|
+
const seen = new Set()
|
|
99
|
+
let bestInvalid = null
|
|
100
|
+
for (const candidate of candidates) {
|
|
101
|
+
if (!planLike(candidate.value)) continue
|
|
102
|
+
let normalized
|
|
103
|
+
try { normalized = normalizePlanForValidation(candidate.value) } catch { continue }
|
|
104
|
+
const key = JSON.stringify(normalized)
|
|
105
|
+
if (seen.has(key)) continue
|
|
106
|
+
seen.add(key)
|
|
107
|
+
const validation = validatePlan(normalized)
|
|
108
|
+
const result = {
|
|
109
|
+
plan: normalized,
|
|
110
|
+
validation,
|
|
111
|
+
source: candidate.source,
|
|
112
|
+
salvaged: candidate.source !== "marked",
|
|
113
|
+
}
|
|
114
|
+
if (validation && validation.valid === true) return result
|
|
115
|
+
if (!bestInvalid) bestInvalid = result
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return bestInvalid || {
|
|
119
|
+
plan: null,
|
|
120
|
+
validation: null,
|
|
121
|
+
source: null,
|
|
122
|
+
salvaged: false,
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
function bounded(value, fallback, min, max) {
|
|
2
|
+
const parsed = Number(value)
|
|
3
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return fallback
|
|
4
|
+
return Math.max(min, Math.min(max, Math.trunc(parsed)))
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function planningRuntimeBudget(role, attempt = 1, options = {}) {
|
|
8
|
+
const normalizedRole = String(role || "").replace(/^ues-/, "")
|
|
9
|
+
const normalizedAttempt = Math.max(1, Math.trunc(Number(attempt) || 1))
|
|
10
|
+
|
|
11
|
+
if (normalizedRole === "architect") {
|
|
12
|
+
const recovery = normalizedAttempt > 1
|
|
13
|
+
const deep = String(options.executionProfile || "") === "deep"
|
|
14
|
+
const largePrompt = Number(options.taskChars || 0) >= 2500
|
|
15
|
+
const initialHard = recovery ? 50_000 : 60_000
|
|
16
|
+
const absoluteHard = recovery
|
|
17
|
+
? 95_000
|
|
18
|
+
: deep || largePrompt
|
|
19
|
+
? 150_000
|
|
20
|
+
: 120_000
|
|
21
|
+
return {
|
|
22
|
+
hardTimeoutMs: bounded(
|
|
23
|
+
recovery ? options.architectRecoveryHardTimeoutMs : options.architectHardTimeoutMs,
|
|
24
|
+
initialHard,
|
|
25
|
+
30_000,
|
|
26
|
+
5 * 60_000,
|
|
27
|
+
),
|
|
28
|
+
absoluteHardTimeoutMs: bounded(
|
|
29
|
+
recovery ? options.architectRecoveryAbsoluteHardTimeoutMs : options.architectAbsoluteHardTimeoutMs,
|
|
30
|
+
absoluteHard,
|
|
31
|
+
initialHard,
|
|
32
|
+
6 * 60_000,
|
|
33
|
+
),
|
|
34
|
+
activityExtensionMs: bounded(
|
|
35
|
+
recovery ? options.architectRecoveryActivityExtensionMs : options.architectActivityExtensionMs,
|
|
36
|
+
recovery ? 25_000 : 35_000,
|
|
37
|
+
10_000,
|
|
38
|
+
90_000,
|
|
39
|
+
),
|
|
40
|
+
activityWindowMs: bounded(
|
|
41
|
+
options.architectActivityWindowMs,
|
|
42
|
+
20_000,
|
|
43
|
+
5_000,
|
|
44
|
+
60_000,
|
|
45
|
+
),
|
|
46
|
+
idleTimeoutMs: bounded(
|
|
47
|
+
recovery ? options.architectRecoveryIdleTimeoutMs : options.architectIdleTimeoutMs,
|
|
48
|
+
recovery ? 28_000 : 40_000,
|
|
49
|
+
10_000,
|
|
50
|
+
2 * 60_000,
|
|
51
|
+
),
|
|
52
|
+
softSteerMs: bounded(
|
|
53
|
+
options.architectSoftSteerMs,
|
|
54
|
+
30_000,
|
|
55
|
+
15_000,
|
|
56
|
+
2 * 60_000,
|
|
57
|
+
),
|
|
58
|
+
maxExplorationTools: bounded(options.architectMaxExplorationTools, 16, 8, 80),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (normalizedRole === "plan-checker") {
|
|
63
|
+
const recovery = normalizedAttempt > 1
|
|
64
|
+
const initialHard = recovery ? 45_000 : 70_000
|
|
65
|
+
return {
|
|
66
|
+
hardTimeoutMs: bounded(
|
|
67
|
+
recovery ? options.planCheckerRecoveryHardTimeoutMs : options.planCheckerHardTimeoutMs,
|
|
68
|
+
initialHard,
|
|
69
|
+
30_000,
|
|
70
|
+
5 * 60_000,
|
|
71
|
+
),
|
|
72
|
+
absoluteHardTimeoutMs: bounded(
|
|
73
|
+
recovery ? options.planCheckerRecoveryAbsoluteHardTimeoutMs : options.planCheckerAbsoluteHardTimeoutMs,
|
|
74
|
+
recovery ? 80_000 : 130_000,
|
|
75
|
+
initialHard,
|
|
76
|
+
5 * 60_000,
|
|
77
|
+
),
|
|
78
|
+
activityExtensionMs: bounded(
|
|
79
|
+
recovery ? options.planCheckerRecoveryActivityExtensionMs : options.planCheckerActivityExtensionMs,
|
|
80
|
+
recovery ? 20_000 : 30_000,
|
|
81
|
+
10_000,
|
|
82
|
+
60_000,
|
|
83
|
+
),
|
|
84
|
+
activityWindowMs: bounded(
|
|
85
|
+
options.planCheckerActivityWindowMs,
|
|
86
|
+
20_000,
|
|
87
|
+
5_000,
|
|
88
|
+
60_000,
|
|
89
|
+
),
|
|
90
|
+
idleTimeoutMs: bounded(
|
|
91
|
+
recovery ? options.planCheckerRecoveryIdleTimeoutMs : options.planCheckerIdleTimeoutMs,
|
|
92
|
+
recovery ? 24_000 : 38_000,
|
|
93
|
+
10_000,
|
|
94
|
+
2 * 60_000,
|
|
95
|
+
),
|
|
96
|
+
softSteerMs: bounded(
|
|
97
|
+
recovery ? options.planCheckerRecoverySoftSteerMs : options.planCheckerSoftSteerMs,
|
|
98
|
+
recovery ? 18_000 : 35_000,
|
|
99
|
+
10_000,
|
|
100
|
+
2 * 60_000,
|
|
101
|
+
),
|
|
102
|
+
maxExplorationTools: bounded(
|
|
103
|
+
recovery ? options.planCheckerRecoveryMaxExplorationTools : options.planCheckerMaxExplorationTools,
|
|
104
|
+
recovery ? 6 : 10,
|
|
105
|
+
4,
|
|
106
|
+
40,
|
|
107
|
+
),
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function shouldSoftSteerArchitect(progress = {}, budget = null) {
|
|
115
|
+
if (!budget) return false
|
|
116
|
+
const elapsedMs = Number(progress.elapsedMs || 0)
|
|
117
|
+
const toolCalls = Number(progress.toolCalls || 0)
|
|
118
|
+
const idleMs = Number(progress.idleMs || 0)
|
|
119
|
+
|
|
120
|
+
if (budget.softSteerMs && elapsedMs >= budget.softSteerMs) return true
|
|
121
|
+
if (budget.maxExplorationTools && toolCalls >= budget.maxExplorationTools) return true
|
|
122
|
+
|
|
123
|
+
// If the model has gone quiet after doing real exploration, ask it to emit
|
|
124
|
+
// the plan before the hard idle watchdog has to kill the worker.
|
|
125
|
+
return toolCalls > 0 && idleMs >= Math.max(10_000, Math.floor((budget.idleTimeoutMs || 30_000) * 0.65))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
export function shouldSoftSteerPlanningRole(progress = {}, budget = null) {
|
|
130
|
+
return shouldSoftSteerArchitect(progress, budget)
|
|
131
|
+
}
|
package/lib/repo-graph.mjs
CHANGED
|
@@ -6,7 +6,7 @@ const SKIP = new Set([
|
|
|
6
6
|
".git", "node_modules", ".next", "dist", "build", "coverage", ".venv", "venv",
|
|
7
7
|
"Pods", "DerivedData", ".gradle", ".idea", ".cache", ".turbo", "target", "obj",
|
|
8
8
|
".ues-work", ".ues-cache", ".ues-traces", ".ues-learning", ".ues-dashboard",
|
|
9
|
-
".ues-sandboxes", ".ues-memory", ".ues-evals",
|
|
9
|
+
".ues-sandboxes", ".ues-memory", ".ues-evals", ".ues-services",
|
|
10
10
|
])
|
|
11
11
|
|
|
12
12
|
const SOURCE_EXTENSIONS = new Set([
|
package/lib/repo-inspect.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { spawnSync } from "node:child_process"
|
|
|
6
6
|
const SKIP_DIRS = new Set([
|
|
7
7
|
".git", "node_modules", ".next", "dist", "build", "coverage", ".venv", "venv",
|
|
8
8
|
"Pods", "DerivedData", ".gradle", ".idea", ".cache", ".turbo", "target", "bin", "obj",
|
|
9
|
+
".ues-services",
|
|
9
10
|
])
|
|
10
11
|
|
|
11
12
|
const TEXT_EXTENSIONS = new Set([
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const UES_RUNTIME_DIRS = Object.freeze([
|
|
2
|
+
".ues-cache",
|
|
3
|
+
".ues-traces",
|
|
4
|
+
".ues-work",
|
|
5
|
+
".ues-learning",
|
|
6
|
+
".ues-dashboard",
|
|
7
|
+
".ues-sandboxes",
|
|
8
|
+
".ues-memory",
|
|
9
|
+
".ues-evals",
|
|
10
|
+
".ues-services",
|
|
11
|
+
])
|
|
12
|
+
|
|
13
|
+
export function normalizeRuntimePath(value) {
|
|
14
|
+
return String(value || "")
|
|
15
|
+
.replaceAll("\\", "/")
|
|
16
|
+
.replace(/^\.\//, "")
|
|
17
|
+
.replace(/^\/+/, "")
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isUesRuntimeArtifactPath(value) {
|
|
21
|
+
const normalized = normalizeRuntimePath(value)
|
|
22
|
+
if (!normalized) return false
|
|
23
|
+
return UES_RUNTIME_DIRS.some(
|
|
24
|
+
(dir) => normalized === dir || normalized.startsWith(dir + "/"),
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function sourceFacingPaths(values = []) {
|
|
29
|
+
return [...new Set(
|
|
30
|
+
(values || [])
|
|
31
|
+
.map(normalizeRuntimePath)
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.filter((value) => !isUesRuntimeArtifactPath(value)),
|
|
34
|
+
)]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function sourceGitPathspecs() {
|
|
38
|
+
return [
|
|
39
|
+
".",
|
|
40
|
+
...UES_RUNTIME_DIRS.map((dir) => ":(exclude)" + dir + "/**"),
|
|
41
|
+
]
|
|
42
|
+
}
|
package/lib/runtime-events.mjs
CHANGED
|
@@ -1,7 +1,39 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto"
|
|
2
|
-
import { appendFile, readFile } from "node:fs/promises"
|
|
2
|
+
import { appendFile, readFile, stat, writeFile } from "node:fs/promises"
|
|
3
|
+
|
|
4
|
+
const DEFAULT_MAX_EVENT_FILE_BYTES = 4 * 1024 * 1024
|
|
5
|
+
const DEFAULT_RETAIN_EVENT_BYTES = 2 * 1024 * 1024
|
|
6
|
+
|
|
7
|
+
async function compactRuntimeEventFile(file, options = {}) {
|
|
8
|
+
const maxBytes = Math.max(
|
|
9
|
+
256 * 1024,
|
|
10
|
+
Math.min(64 * 1024 * 1024, Number(options.maxBytes || process.env.UES_EVENT_LOG_MAX_BYTES || DEFAULT_MAX_EVENT_FILE_BYTES)),
|
|
11
|
+
)
|
|
12
|
+
const retainBytes = Math.max(
|
|
13
|
+
128 * 1024,
|
|
14
|
+
Math.min(maxBytes, Number(options.retainBytes || DEFAULT_RETAIN_EVENT_BYTES)),
|
|
15
|
+
)
|
|
16
|
+
const info = await stat(file).catch(() => null)
|
|
17
|
+
if (!info?.isFile() || info.size < maxBytes) return false
|
|
18
|
+
|
|
19
|
+
const raw = await readFile(file)
|
|
20
|
+
let kept = raw.subarray(Math.max(0, raw.length - retainBytes))
|
|
21
|
+
const newline = kept.indexOf(0x0a)
|
|
22
|
+
if (newline >= 0 && raw.length > retainBytes) kept = kept.subarray(newline + 1)
|
|
23
|
+
const marker = Buffer.from(JSON.stringify({
|
|
24
|
+
schemaVersion: 1,
|
|
25
|
+
id: randomUUID(),
|
|
26
|
+
type: "runtime-events.compacted",
|
|
27
|
+
at: new Date().toISOString(),
|
|
28
|
+
previousBytes: raw.length,
|
|
29
|
+
retainedBytes: kept.length,
|
|
30
|
+
}) + "\n")
|
|
31
|
+
await writeFile(file, Buffer.concat([marker, kept]))
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
3
34
|
|
|
4
35
|
export async function appendRuntimeEvent(file, type, data = {}) {
|
|
36
|
+
await compactRuntimeEventFile(file).catch(() => false)
|
|
5
37
|
const event = {
|
|
6
38
|
schemaVersion: 1,
|
|
7
39
|
id: randomUUID(),
|
package/lib/safety.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const RULES = [
|
|
2
2
|
{ id: "git-force", pattern: /\bgit\s+(?:push\b[^\n]*(?:--force(?:-with-lease)?|(?:^|\s)-f(?:\s|$))|reset\s+--hard|clean\s+-[^\n]*f)/i },
|
|
3
|
+
{ id: "git-discard-work", pattern: /\bgit\s+(?:restore\b|checkout\s+--(?:\s|$)|stash(?:\s|$))/i },
|
|
3
4
|
{ id: "history-rewrite", pattern: /\bgit\s+(?:rebase\b|filter-branch\b|filter-repo\b)/i },
|
|
4
5
|
{ id: "publish", pattern: /\b(?:npm\s+publish|pnpm\s+publish|yarn\s+npm\s+publish)\b/i },
|
|
5
6
|
{ id: "destructive-files", pattern: /(?:^|[;&|]\s*)(?:rm\s+-[^\n]*r[^\n]*f|rmdir\s+\/s|del\s+\/s|remove-item\b[^\n]*-recurse[^\n]*-force)/i },
|
package/lib/semantic-index.mjs
CHANGED
|
@@ -12,7 +12,7 @@ const SOURCE_EXTENSIONS = new Set([
|
|
|
12
12
|
const SKIP_DIRS = new Set([
|
|
13
13
|
".git", "node_modules", ".next", "dist", "build", "coverage", ".venv", "venv",
|
|
14
14
|
"Pods", "DerivedData", ".gradle", ".idea", ".cache", ".turbo", "target", "obj",
|
|
15
|
-
".ues-work", ".ues-learning", ".ues-memory", ".ues-dashboard", ".ues-sandboxes", ".ues-cache", ".ues-traces",
|
|
15
|
+
".ues-work", ".ues-learning", ".ues-memory", ".ues-dashboard", ".ues-sandboxes", ".ues-cache", ".ues-traces", ".ues-services",
|
|
16
16
|
])
|
|
17
17
|
const STOP_IDENTIFIERS = new Set([
|
|
18
18
|
"const","let","var","function","class","interface","type","export","import","from","return",
|