@open330/oac-budget 2026.2.17
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/LICENSE +21 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +391 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Open330
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
type AgentProviderId = "claude-code" | "codex-cli" | "opencode" | string;
|
|
2
|
+
type TaskSource = "lint" | "todo" | "test-gap" | "dead-code" | "github-issue" | "custom";
|
|
3
|
+
type TaskComplexity = "trivial" | "simple" | "moderate" | "complex";
|
|
4
|
+
type ExecutionMode = "new-pr" | "update-pr" | "direct-commit";
|
|
5
|
+
interface Task {
|
|
6
|
+
id: string;
|
|
7
|
+
source: TaskSource;
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
targetFiles: string[];
|
|
11
|
+
priority: number;
|
|
12
|
+
complexity: TaskComplexity;
|
|
13
|
+
executionMode: ExecutionMode;
|
|
14
|
+
linkedIssue?: {
|
|
15
|
+
number: number;
|
|
16
|
+
url: string;
|
|
17
|
+
labels: string[];
|
|
18
|
+
};
|
|
19
|
+
metadata: Record<string, unknown>;
|
|
20
|
+
discoveredAt: string;
|
|
21
|
+
}
|
|
22
|
+
interface TokenEstimate {
|
|
23
|
+
taskId: string;
|
|
24
|
+
providerId: AgentProviderId;
|
|
25
|
+
contextTokens: number;
|
|
26
|
+
promptTokens: number;
|
|
27
|
+
expectedOutputTokens: number;
|
|
28
|
+
totalEstimatedTokens: number;
|
|
29
|
+
confidence: number;
|
|
30
|
+
feasible: boolean;
|
|
31
|
+
estimatedCostUsd?: number;
|
|
32
|
+
}
|
|
33
|
+
interface TokenCounter {
|
|
34
|
+
countTokens(text: string): number;
|
|
35
|
+
readonly invocationOverhead: number;
|
|
36
|
+
readonly maxContextTokens: number;
|
|
37
|
+
}
|
|
38
|
+
declare function estimateTokens(task: Task, provider: AgentProviderId): Promise<TokenEstimate>;
|
|
39
|
+
|
|
40
|
+
declare function estimateLocChanges(task: Task): number;
|
|
41
|
+
declare function analyzeTaskComplexity(task: Task): TaskComplexity;
|
|
42
|
+
|
|
43
|
+
type DeferredReason = "budget_exceeded" | "low_confidence" | "too_complex";
|
|
44
|
+
interface ExecutionPlan {
|
|
45
|
+
totalBudget: number;
|
|
46
|
+
selectedTasks: Array<{
|
|
47
|
+
task: Task;
|
|
48
|
+
estimate: TokenEstimate;
|
|
49
|
+
cumulativeBudgetUsed: number;
|
|
50
|
+
}>;
|
|
51
|
+
deferredTasks: Array<{
|
|
52
|
+
task: Task;
|
|
53
|
+
estimate: TokenEstimate;
|
|
54
|
+
reason: DeferredReason;
|
|
55
|
+
}>;
|
|
56
|
+
reserveTokens: number;
|
|
57
|
+
remainingTokens: number;
|
|
58
|
+
}
|
|
59
|
+
declare function buildExecutionPlan(tasks: Task[], estimates: Map<string, TokenEstimate>, budget: number): ExecutionPlan;
|
|
60
|
+
|
|
61
|
+
declare class ClaudeTokenCounter {
|
|
62
|
+
readonly invocationOverhead = 1500;
|
|
63
|
+
readonly maxContextTokens = 200000;
|
|
64
|
+
readonly encoding = "cl100k_base";
|
|
65
|
+
countTokens(text: string): number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
declare const PRIMARY_ENCODING = "o200k_base";
|
|
69
|
+
declare const FALLBACK_ENCODING = "cl100k_base";
|
|
70
|
+
type SupportedCodexEncoding = typeof PRIMARY_ENCODING | typeof FALLBACK_ENCODING;
|
|
71
|
+
declare class CodexTokenCounter {
|
|
72
|
+
readonly invocationOverhead = 1000;
|
|
73
|
+
readonly maxContextTokens = 200000;
|
|
74
|
+
get encoding(): SupportedCodexEncoding;
|
|
75
|
+
countTokens(text: string): number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { type AgentProviderId, ClaudeTokenCounter, CodexTokenCounter, type ExecutionMode, type ExecutionPlan, type Task, type TaskComplexity, type TaskSource, type TokenCounter, type TokenEstimate, analyzeTaskComplexity, buildExecutionPlan, estimateLocChanges, estimateTokens };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
// src/complexity.ts
|
|
2
|
+
var SOURCE_LOC_BASELINE = {
|
|
3
|
+
lint: 8,
|
|
4
|
+
todo: 16,
|
|
5
|
+
"test-gap": 48,
|
|
6
|
+
"dead-code": 36,
|
|
7
|
+
"github-issue": 88,
|
|
8
|
+
custom: 40
|
|
9
|
+
};
|
|
10
|
+
var SOURCE_COMPLEXITY_SCORE = {
|
|
11
|
+
lint: 0,
|
|
12
|
+
todo: 0,
|
|
13
|
+
"test-gap": 1,
|
|
14
|
+
"dead-code": 1,
|
|
15
|
+
"github-issue": 2,
|
|
16
|
+
custom: 1
|
|
17
|
+
};
|
|
18
|
+
var ESTIMATED_LOC_KEYS = [
|
|
19
|
+
"estimatedLoc",
|
|
20
|
+
"estimatedLOC",
|
|
21
|
+
"estimatedLocChanges",
|
|
22
|
+
"estimatedDiffSize",
|
|
23
|
+
"loc",
|
|
24
|
+
"locChanges",
|
|
25
|
+
"linesChanged",
|
|
26
|
+
"lineCount",
|
|
27
|
+
"diffSize",
|
|
28
|
+
"changeSize"
|
|
29
|
+
];
|
|
30
|
+
function parseNumericValue(value) {
|
|
31
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
if (typeof value === "string") {
|
|
35
|
+
const parsed = Number.parseFloat(value);
|
|
36
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
37
|
+
return parsed;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
function readMetadataLocEstimate(metadata) {
|
|
43
|
+
for (const key of ESTIMATED_LOC_KEYS) {
|
|
44
|
+
const directValue = parseNumericValue(metadata[key]);
|
|
45
|
+
if (directValue !== void 0) {
|
|
46
|
+
return directValue;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const metrics = metadata.metrics;
|
|
50
|
+
if (metrics && typeof metrics === "object" && !Array.isArray(metrics)) {
|
|
51
|
+
const metricsRecord = metrics;
|
|
52
|
+
for (const key of ESTIMATED_LOC_KEYS) {
|
|
53
|
+
const metricValue = parseNumericValue(metricsRecord[key]);
|
|
54
|
+
if (metricValue !== void 0) {
|
|
55
|
+
return metricValue;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
function estimateLocChanges(task) {
|
|
62
|
+
const metadataEstimate = readMetadataLocEstimate(task.metadata);
|
|
63
|
+
if (metadataEstimate !== void 0) {
|
|
64
|
+
return Math.max(1, Math.round(metadataEstimate));
|
|
65
|
+
}
|
|
66
|
+
const sourceBaseline = SOURCE_LOC_BASELINE[task.source] ?? SOURCE_LOC_BASELINE.custom;
|
|
67
|
+
const fileAdjustment = Math.max(task.targetFiles.length, 1) * 8;
|
|
68
|
+
return Math.max(sourceBaseline, fileAdjustment);
|
|
69
|
+
}
|
|
70
|
+
function analyzeTaskComplexity(task) {
|
|
71
|
+
const fileCount = task.targetFiles.length;
|
|
72
|
+
const locChanges = estimateLocChanges(task);
|
|
73
|
+
const fileScore = fileCount <= 1 ? 0 : fileCount <= 3 ? 1 : fileCount <= 6 ? 2 : 3;
|
|
74
|
+
const locScore = locChanges <= 20 ? 0 : locChanges <= 80 ? 1 : locChanges <= 200 ? 2 : 3;
|
|
75
|
+
const sourceScore = SOURCE_COMPLEXITY_SCORE[task.source] ?? SOURCE_COMPLEXITY_SCORE.custom;
|
|
76
|
+
const totalScore = fileScore + locScore + sourceScore;
|
|
77
|
+
if (totalScore <= 1) {
|
|
78
|
+
return "trivial";
|
|
79
|
+
}
|
|
80
|
+
if (totalScore <= 3) {
|
|
81
|
+
return "simple";
|
|
82
|
+
}
|
|
83
|
+
if (totalScore <= 6) {
|
|
84
|
+
return "moderate";
|
|
85
|
+
}
|
|
86
|
+
return "complex";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/estimator.ts
|
|
90
|
+
import { readFile } from "fs/promises";
|
|
91
|
+
import { isAbsolute, resolve } from "path";
|
|
92
|
+
|
|
93
|
+
// src/providers/claude-counter.ts
|
|
94
|
+
import { get_encoding } from "tiktoken";
|
|
95
|
+
var CLAUDE_ENCODING = "cl100k_base";
|
|
96
|
+
var CLAUDE_INVOCATION_OVERHEAD = 1500;
|
|
97
|
+
var CLAUDE_MAX_CONTEXT_TOKENS = 2e5;
|
|
98
|
+
var encoder;
|
|
99
|
+
function getEncoder() {
|
|
100
|
+
encoder ??= get_encoding(CLAUDE_ENCODING);
|
|
101
|
+
return encoder;
|
|
102
|
+
}
|
|
103
|
+
var ClaudeTokenCounter = class {
|
|
104
|
+
invocationOverhead = CLAUDE_INVOCATION_OVERHEAD;
|
|
105
|
+
maxContextTokens = CLAUDE_MAX_CONTEXT_TOKENS;
|
|
106
|
+
encoding = CLAUDE_ENCODING;
|
|
107
|
+
countTokens(text) {
|
|
108
|
+
return getEncoder().encode(text).length;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/providers/codex-counter.ts
|
|
113
|
+
import { get_encoding as get_encoding2 } from "tiktoken";
|
|
114
|
+
var CODEX_INVOCATION_OVERHEAD = 1e3;
|
|
115
|
+
var CODEX_MAX_CONTEXT_TOKENS = 2e5;
|
|
116
|
+
var PRIMARY_ENCODING = "o200k_base";
|
|
117
|
+
var FALLBACK_ENCODING = "cl100k_base";
|
|
118
|
+
var encoder2;
|
|
119
|
+
var selectedEncoding;
|
|
120
|
+
function getEncoder2() {
|
|
121
|
+
if (encoder2) {
|
|
122
|
+
return encoder2;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
encoder2 = get_encoding2(PRIMARY_ENCODING);
|
|
126
|
+
selectedEncoding = PRIMARY_ENCODING;
|
|
127
|
+
} catch {
|
|
128
|
+
encoder2 = get_encoding2(FALLBACK_ENCODING);
|
|
129
|
+
selectedEncoding = FALLBACK_ENCODING;
|
|
130
|
+
}
|
|
131
|
+
return encoder2;
|
|
132
|
+
}
|
|
133
|
+
var CodexTokenCounter = class {
|
|
134
|
+
invocationOverhead = CODEX_INVOCATION_OVERHEAD;
|
|
135
|
+
maxContextTokens = CODEX_MAX_CONTEXT_TOKENS;
|
|
136
|
+
get encoding() {
|
|
137
|
+
getEncoder2();
|
|
138
|
+
return selectedEncoding ?? FALLBACK_ENCODING;
|
|
139
|
+
}
|
|
140
|
+
countTokens(text) {
|
|
141
|
+
return getEncoder2().encode(text).length;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// src/estimator.ts
|
|
146
|
+
var ESTIMATION_PADDING_MULTIPLIER = 1.2;
|
|
147
|
+
var FALLBACK_CONFIDENCE = 0.5;
|
|
148
|
+
var COMPLEXITY_MULTIPLIERS = {
|
|
149
|
+
trivial: 0.5,
|
|
150
|
+
simple: 1,
|
|
151
|
+
moderate: 2,
|
|
152
|
+
complex: 3.5
|
|
153
|
+
};
|
|
154
|
+
var COMPLEXITY_CONFIDENCE = {
|
|
155
|
+
trivial: 0.9,
|
|
156
|
+
simple: 0.75,
|
|
157
|
+
moderate: 0.6,
|
|
158
|
+
complex: 0.4
|
|
159
|
+
};
|
|
160
|
+
var COMPLEXITY_ORDER = {
|
|
161
|
+
trivial: 0,
|
|
162
|
+
simple: 1,
|
|
163
|
+
moderate: 2,
|
|
164
|
+
complex: 3
|
|
165
|
+
};
|
|
166
|
+
var claudeCounter = new ClaudeTokenCounter();
|
|
167
|
+
var codexCounter = new CodexTokenCounter();
|
|
168
|
+
function getTokenCounter(provider) {
|
|
169
|
+
if (provider === "claude-code") {
|
|
170
|
+
return claudeCounter;
|
|
171
|
+
}
|
|
172
|
+
return codexCounter;
|
|
173
|
+
}
|
|
174
|
+
function approximateTokenCount(text) {
|
|
175
|
+
if (text.length === 0) {
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
return Math.max(1, Math.ceil(text.length / 4));
|
|
179
|
+
}
|
|
180
|
+
function countTokensWithFallback(text, counter) {
|
|
181
|
+
try {
|
|
182
|
+
return {
|
|
183
|
+
tokens: counter.countTokens(text),
|
|
184
|
+
usedFallback: false
|
|
185
|
+
};
|
|
186
|
+
} catch {
|
|
187
|
+
return {
|
|
188
|
+
tokens: approximateTokenCount(text),
|
|
189
|
+
usedFallback: true
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function chooseConservativeComplexity(declaredComplexity, analyzedComplexity) {
|
|
194
|
+
return COMPLEXITY_ORDER[declaredComplexity] >= COMPLEXITY_ORDER[analyzedComplexity] ? declaredComplexity : analyzedComplexity;
|
|
195
|
+
}
|
|
196
|
+
function clamp(value, min, max) {
|
|
197
|
+
if (value < min) {
|
|
198
|
+
return min;
|
|
199
|
+
}
|
|
200
|
+
if (value > max) {
|
|
201
|
+
return max;
|
|
202
|
+
}
|
|
203
|
+
return value;
|
|
204
|
+
}
|
|
205
|
+
function resolveTargetFilePath(targetFile) {
|
|
206
|
+
return isAbsolute(targetFile) ? targetFile : resolve(process.cwd(), targetFile);
|
|
207
|
+
}
|
|
208
|
+
function safeStringify(value) {
|
|
209
|
+
try {
|
|
210
|
+
const serialized = JSON.stringify(value);
|
|
211
|
+
return serialized ?? "null";
|
|
212
|
+
} catch {
|
|
213
|
+
return "[unserializable]";
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async function readContextFile(targetFile, counter) {
|
|
217
|
+
const resolvedPath = resolveTargetFilePath(targetFile);
|
|
218
|
+
try {
|
|
219
|
+
const content = await readFile(resolvedPath, "utf8");
|
|
220
|
+
const counted = countTokensWithFallback(content, counter);
|
|
221
|
+
return {
|
|
222
|
+
...counted,
|
|
223
|
+
missing: false
|
|
224
|
+
};
|
|
225
|
+
} catch {
|
|
226
|
+
return {
|
|
227
|
+
tokens: 0,
|
|
228
|
+
usedFallback: false,
|
|
229
|
+
missing: true
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async function estimateTokens(task, provider) {
|
|
234
|
+
const counter = getTokenCounter(provider);
|
|
235
|
+
const uniqueTargetFiles = [...new Set(task.targetFiles)];
|
|
236
|
+
const fileResults = await Promise.all(
|
|
237
|
+
uniqueTargetFiles.map((targetFile) => readContextFile(targetFile, counter))
|
|
238
|
+
);
|
|
239
|
+
const repoStructureSeed = uniqueTargetFiles.join("\n");
|
|
240
|
+
const repoStructureCount = countTokensWithFallback(repoStructureSeed, counter);
|
|
241
|
+
const contextTokens = repoStructureCount.tokens + fileResults.reduce((sum, result) => sum + result.tokens, 0);
|
|
242
|
+
const promptSeed = [
|
|
243
|
+
`Task ID: ${task.id}`,
|
|
244
|
+
`Title: ${task.title}`,
|
|
245
|
+
`Source: ${task.source}`,
|
|
246
|
+
`Priority: ${task.priority}`,
|
|
247
|
+
`Description:
|
|
248
|
+
${task.description}`,
|
|
249
|
+
`Target Files:
|
|
250
|
+
${uniqueTargetFiles.join("\n") || "(none)"}`,
|
|
251
|
+
`Metadata: ${safeStringify(task.metadata)}`
|
|
252
|
+
].join("\n\n");
|
|
253
|
+
const promptContentCount = countTokensWithFallback(promptSeed, counter);
|
|
254
|
+
const promptTokens = counter.invocationOverhead + promptContentCount.tokens;
|
|
255
|
+
const analyzedComplexity = analyzeTaskComplexity(task);
|
|
256
|
+
const effectiveComplexity = chooseConservativeComplexity(task.complexity, analyzedComplexity);
|
|
257
|
+
const expectedOutputTokens = Math.ceil(
|
|
258
|
+
contextTokens * COMPLEXITY_MULTIPLIERS[effectiveComplexity]
|
|
259
|
+
);
|
|
260
|
+
const rawTotalTokens = contextTokens + promptTokens + expectedOutputTokens;
|
|
261
|
+
const totalEstimatedTokens = Math.ceil(rawTotalTokens * ESTIMATION_PADDING_MULTIPLIER);
|
|
262
|
+
const usedFallback = repoStructureCount.usedFallback || promptContentCount.usedFallback || fileResults.some((result) => result.usedFallback);
|
|
263
|
+
const missingFileCount = fileResults.filter((result) => result.missing).length;
|
|
264
|
+
let confidence = COMPLEXITY_CONFIDENCE[effectiveComplexity];
|
|
265
|
+
if (usedFallback) {
|
|
266
|
+
confidence = Math.min(confidence, FALLBACK_CONFIDENCE);
|
|
267
|
+
}
|
|
268
|
+
if (missingFileCount > 0) {
|
|
269
|
+
confidence -= Math.min(0.25, missingFileCount * 0.05);
|
|
270
|
+
}
|
|
271
|
+
if (uniqueTargetFiles.length === 0) {
|
|
272
|
+
confidence -= 0.1;
|
|
273
|
+
}
|
|
274
|
+
if (task.complexity !== analyzedComplexity) {
|
|
275
|
+
confidence -= 0.05;
|
|
276
|
+
}
|
|
277
|
+
const feasible = totalEstimatedTokens <= counter.maxContextTokens;
|
|
278
|
+
return {
|
|
279
|
+
taskId: task.id,
|
|
280
|
+
providerId: provider,
|
|
281
|
+
contextTokens,
|
|
282
|
+
promptTokens,
|
|
283
|
+
expectedOutputTokens,
|
|
284
|
+
totalEstimatedTokens,
|
|
285
|
+
confidence: clamp(confidence, 0.1, 0.95),
|
|
286
|
+
feasible
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/planner.ts
|
|
291
|
+
var DEFAULT_RESERVE_PERCENT = 0.1;
|
|
292
|
+
var MIN_CONFIDENCE_THRESHOLD = 0.5;
|
|
293
|
+
var TOO_COMPLEX_BUDGET_SHARE = 0.6;
|
|
294
|
+
function normalizeBudget(budget) {
|
|
295
|
+
if (!Number.isFinite(budget) || budget <= 0) {
|
|
296
|
+
return 0;
|
|
297
|
+
}
|
|
298
|
+
return Math.floor(budget);
|
|
299
|
+
}
|
|
300
|
+
function getFallbackEstimate(task) {
|
|
301
|
+
return {
|
|
302
|
+
taskId: task.id,
|
|
303
|
+
providerId: "unknown",
|
|
304
|
+
contextTokens: 0,
|
|
305
|
+
promptTokens: 0,
|
|
306
|
+
expectedOutputTokens: 0,
|
|
307
|
+
totalEstimatedTokens: Number.MAX_SAFE_INTEGER,
|
|
308
|
+
confidence: 0,
|
|
309
|
+
feasible: false
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function classifyDeferredReason(task, estimate, effectiveBudget) {
|
|
313
|
+
if (!estimate.feasible) {
|
|
314
|
+
return "budget_exceeded";
|
|
315
|
+
}
|
|
316
|
+
if (estimate.confidence < MIN_CONFIDENCE_THRESHOLD) {
|
|
317
|
+
return "low_confidence";
|
|
318
|
+
}
|
|
319
|
+
if (task.complexity === "complex" && effectiveBudget > 0 && estimate.totalEstimatedTokens > effectiveBudget * TOO_COMPLEX_BUDGET_SHARE) {
|
|
320
|
+
return "too_complex";
|
|
321
|
+
}
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
function scoreByPriorityPerToken(task, estimate) {
|
|
325
|
+
if (estimate.totalEstimatedTokens <= 0) {
|
|
326
|
+
return task.priority;
|
|
327
|
+
}
|
|
328
|
+
return task.priority / estimate.totalEstimatedTokens;
|
|
329
|
+
}
|
|
330
|
+
function buildExecutionPlan(tasks, estimates, budget) {
|
|
331
|
+
const totalBudget = normalizeBudget(budget);
|
|
332
|
+
const reserveTokens = Math.floor(totalBudget * DEFAULT_RESERVE_PERCENT);
|
|
333
|
+
const effectiveBudget = Math.max(0, totalBudget - reserveTokens);
|
|
334
|
+
const deferredTasks = [];
|
|
335
|
+
const candidates = [];
|
|
336
|
+
for (const task of tasks) {
|
|
337
|
+
const estimate = estimates.get(task.id) ?? getFallbackEstimate(task);
|
|
338
|
+
const reason = classifyDeferredReason(task, estimate, effectiveBudget);
|
|
339
|
+
if (reason) {
|
|
340
|
+
deferredTasks.push({ task, estimate, reason });
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
candidates.push({ task, estimate });
|
|
344
|
+
}
|
|
345
|
+
candidates.sort((left, right) => {
|
|
346
|
+
const ratioDifference = scoreByPriorityPerToken(right.task, right.estimate) - scoreByPriorityPerToken(left.task, left.estimate);
|
|
347
|
+
if (ratioDifference !== 0) {
|
|
348
|
+
return ratioDifference;
|
|
349
|
+
}
|
|
350
|
+
const priorityDifference = right.task.priority - left.task.priority;
|
|
351
|
+
if (priorityDifference !== 0) {
|
|
352
|
+
return priorityDifference;
|
|
353
|
+
}
|
|
354
|
+
return left.estimate.totalEstimatedTokens - right.estimate.totalEstimatedTokens;
|
|
355
|
+
});
|
|
356
|
+
const selectedTasks = [];
|
|
357
|
+
let budgetUsed = 0;
|
|
358
|
+
for (const candidate of candidates) {
|
|
359
|
+
const nextBudgetUsed = budgetUsed + candidate.estimate.totalEstimatedTokens;
|
|
360
|
+
if (nextBudgetUsed <= effectiveBudget) {
|
|
361
|
+
budgetUsed = nextBudgetUsed;
|
|
362
|
+
selectedTasks.push({
|
|
363
|
+
task: candidate.task,
|
|
364
|
+
estimate: candidate.estimate,
|
|
365
|
+
cumulativeBudgetUsed: budgetUsed
|
|
366
|
+
});
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
deferredTasks.push({
|
|
370
|
+
task: candidate.task,
|
|
371
|
+
estimate: candidate.estimate,
|
|
372
|
+
reason: "budget_exceeded"
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
totalBudget,
|
|
377
|
+
selectedTasks,
|
|
378
|
+
deferredTasks,
|
|
379
|
+
reserveTokens,
|
|
380
|
+
remainingTokens: Math.max(0, effectiveBudget - budgetUsed)
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
export {
|
|
384
|
+
ClaudeTokenCounter,
|
|
385
|
+
CodexTokenCounter,
|
|
386
|
+
analyzeTaskComplexity,
|
|
387
|
+
buildExecutionPlan,
|
|
388
|
+
estimateLocChanges,
|
|
389
|
+
estimateTokens
|
|
390
|
+
};
|
|
391
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/complexity.ts","../src/estimator.ts","../src/providers/claude-counter.ts","../src/providers/codex-counter.ts","../src/planner.ts"],"sourcesContent":["import type { Task, TaskComplexity, TaskSource } from \"./estimator.js\";\n\nconst SOURCE_LOC_BASELINE: Record<TaskSource, number> = {\n lint: 8,\n todo: 16,\n \"test-gap\": 48,\n \"dead-code\": 36,\n \"github-issue\": 88,\n custom: 40,\n};\n\nconst SOURCE_COMPLEXITY_SCORE: Record<TaskSource, number> = {\n lint: 0,\n todo: 0,\n \"test-gap\": 1,\n \"dead-code\": 1,\n \"github-issue\": 2,\n custom: 1,\n};\n\nconst ESTIMATED_LOC_KEYS = [\n \"estimatedLoc\",\n \"estimatedLOC\",\n \"estimatedLocChanges\",\n \"estimatedDiffSize\",\n \"loc\",\n \"locChanges\",\n \"linesChanged\",\n \"lineCount\",\n \"diffSize\",\n \"changeSize\",\n] as const;\n\nfunction parseNumericValue(value: unknown): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value) && value >= 0) {\n return value;\n }\n\n if (typeof value === \"string\") {\n const parsed = Number.parseFloat(value);\n if (Number.isFinite(parsed) && parsed >= 0) {\n return parsed;\n }\n }\n\n return undefined;\n}\n\nfunction readMetadataLocEstimate(metadata: Record<string, unknown>): number | undefined {\n for (const key of ESTIMATED_LOC_KEYS) {\n const directValue = parseNumericValue(metadata[key]);\n if (directValue !== undefined) {\n return directValue;\n }\n }\n\n const metrics = metadata.metrics;\n if (metrics && typeof metrics === \"object\" && !Array.isArray(metrics)) {\n const metricsRecord = metrics as Record<string, unknown>;\n for (const key of ESTIMATED_LOC_KEYS) {\n const metricValue = parseNumericValue(metricsRecord[key]);\n if (metricValue !== undefined) {\n return metricValue;\n }\n }\n }\n\n return undefined;\n}\n\nexport function estimateLocChanges(task: Task): number {\n const metadataEstimate = readMetadataLocEstimate(task.metadata);\n if (metadataEstimate !== undefined) {\n return Math.max(1, Math.round(metadataEstimate));\n }\n\n const sourceBaseline = SOURCE_LOC_BASELINE[task.source] ?? SOURCE_LOC_BASELINE.custom;\n const fileAdjustment = Math.max(task.targetFiles.length, 1) * 8;\n\n return Math.max(sourceBaseline, fileAdjustment);\n}\n\nexport function analyzeTaskComplexity(task: Task): TaskComplexity {\n const fileCount = task.targetFiles.length;\n const locChanges = estimateLocChanges(task);\n\n const fileScore = fileCount <= 1 ? 0 : fileCount <= 3 ? 1 : fileCount <= 6 ? 2 : 3;\n const locScore = locChanges <= 20 ? 0 : locChanges <= 80 ? 1 : locChanges <= 200 ? 2 : 3;\n const sourceScore = SOURCE_COMPLEXITY_SCORE[task.source] ?? SOURCE_COMPLEXITY_SCORE.custom;\n\n const totalScore = fileScore + locScore + sourceScore;\n\n if (totalScore <= 1) {\n return \"trivial\";\n }\n\n if (totalScore <= 3) {\n return \"simple\";\n }\n\n if (totalScore <= 6) {\n return \"moderate\";\n }\n\n return \"complex\";\n}\n","import { readFile } from \"node:fs/promises\";\nimport { isAbsolute, resolve } from \"node:path\";\n\nimport { analyzeTaskComplexity } from \"./complexity.js\";\nimport { ClaudeTokenCounter } from \"./providers/claude-counter.js\";\nimport { CodexTokenCounter } from \"./providers/codex-counter.js\";\n\nexport type AgentProviderId = \"claude-code\" | \"codex-cli\" | \"opencode\" | string;\n\nexport type TaskSource = \"lint\" | \"todo\" | \"test-gap\" | \"dead-code\" | \"github-issue\" | \"custom\";\n\nexport type TaskComplexity = \"trivial\" | \"simple\" | \"moderate\" | \"complex\";\n\nexport type ExecutionMode = \"new-pr\" | \"update-pr\" | \"direct-commit\";\n\nexport interface Task {\n id: string;\n source: TaskSource;\n title: string;\n description: string;\n targetFiles: string[];\n priority: number;\n complexity: TaskComplexity;\n executionMode: ExecutionMode;\n linkedIssue?: {\n number: number;\n url: string;\n labels: string[];\n };\n metadata: Record<string, unknown>;\n discoveredAt: string;\n}\n\nexport interface TokenEstimate {\n taskId: string;\n providerId: AgentProviderId;\n contextTokens: number;\n promptTokens: number;\n expectedOutputTokens: number;\n totalEstimatedTokens: number;\n confidence: number;\n feasible: boolean;\n estimatedCostUsd?: number;\n}\n\nexport interface TokenCounter {\n countTokens(text: string): number;\n readonly invocationOverhead: number;\n readonly maxContextTokens: number;\n}\n\ninterface TokenCountResult {\n tokens: number;\n usedFallback: boolean;\n}\n\ninterface ContextFileResult extends TokenCountResult {\n missing: boolean;\n}\n\nconst ESTIMATION_PADDING_MULTIPLIER = 1.2;\nconst FALLBACK_CONFIDENCE = 0.5;\n\nconst COMPLEXITY_MULTIPLIERS: Record<TaskComplexity, number> = {\n trivial: 0.5,\n simple: 1,\n moderate: 2,\n complex: 3.5,\n};\n\nconst COMPLEXITY_CONFIDENCE: Record<TaskComplexity, number> = {\n trivial: 0.9,\n simple: 0.75,\n moderate: 0.6,\n complex: 0.4,\n};\n\nconst COMPLEXITY_ORDER: Record<TaskComplexity, number> = {\n trivial: 0,\n simple: 1,\n moderate: 2,\n complex: 3,\n};\n\nconst claudeCounter = new ClaudeTokenCounter();\nconst codexCounter = new CodexTokenCounter();\n\nfunction getTokenCounter(provider: AgentProviderId): TokenCounter {\n if (provider === \"claude-code\") {\n return claudeCounter;\n }\n\n return codexCounter;\n}\n\nfunction approximateTokenCount(text: string): number {\n if (text.length === 0) {\n return 0;\n }\n\n return Math.max(1, Math.ceil(text.length / 4));\n}\n\nfunction countTokensWithFallback(text: string, counter: TokenCounter): TokenCountResult {\n try {\n return {\n tokens: counter.countTokens(text),\n usedFallback: false,\n };\n } catch {\n return {\n tokens: approximateTokenCount(text),\n usedFallback: true,\n };\n }\n}\n\nfunction chooseConservativeComplexity(\n declaredComplexity: TaskComplexity,\n analyzedComplexity: TaskComplexity,\n): TaskComplexity {\n return COMPLEXITY_ORDER[declaredComplexity] >= COMPLEXITY_ORDER[analyzedComplexity]\n ? declaredComplexity\n : analyzedComplexity;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n if (value < min) {\n return min;\n }\n\n if (value > max) {\n return max;\n }\n\n return value;\n}\n\nfunction resolveTargetFilePath(targetFile: string): string {\n return isAbsolute(targetFile) ? targetFile : resolve(process.cwd(), targetFile);\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n const serialized = JSON.stringify(value);\n return serialized ?? \"null\";\n } catch {\n return \"[unserializable]\";\n }\n}\n\nasync function readContextFile(\n targetFile: string,\n counter: TokenCounter,\n): Promise<ContextFileResult> {\n const resolvedPath = resolveTargetFilePath(targetFile);\n\n try {\n const content = await readFile(resolvedPath, \"utf8\");\n const counted = countTokensWithFallback(content, counter);\n\n return {\n ...counted,\n missing: false,\n };\n } catch {\n return {\n tokens: 0,\n usedFallback: false,\n missing: true,\n };\n }\n}\n\nexport async function estimateTokens(\n task: Task,\n provider: AgentProviderId,\n): Promise<TokenEstimate> {\n const counter = getTokenCounter(provider);\n const uniqueTargetFiles = [...new Set(task.targetFiles)];\n\n const fileResults = await Promise.all(\n uniqueTargetFiles.map((targetFile) => readContextFile(targetFile, counter)),\n );\n\n const repoStructureSeed = uniqueTargetFiles.join(\"\\n\");\n const repoStructureCount = countTokensWithFallback(repoStructureSeed, counter);\n\n const contextTokens =\n repoStructureCount.tokens + fileResults.reduce((sum, result) => sum + result.tokens, 0);\n\n const promptSeed = [\n `Task ID: ${task.id}`,\n `Title: ${task.title}`,\n `Source: ${task.source}`,\n `Priority: ${task.priority}`,\n `Description:\\n${task.description}`,\n `Target Files:\\n${uniqueTargetFiles.join(\"\\n\") || \"(none)\"}`,\n `Metadata: ${safeStringify(task.metadata)}`,\n ].join(\"\\n\\n\");\n\n const promptContentCount = countTokensWithFallback(promptSeed, counter);\n const promptTokens = counter.invocationOverhead + promptContentCount.tokens;\n\n const analyzedComplexity = analyzeTaskComplexity(task);\n const effectiveComplexity = chooseConservativeComplexity(task.complexity, analyzedComplexity);\n const expectedOutputTokens = Math.ceil(\n contextTokens * COMPLEXITY_MULTIPLIERS[effectiveComplexity],\n );\n\n const rawTotalTokens = contextTokens + promptTokens + expectedOutputTokens;\n const totalEstimatedTokens = Math.ceil(rawTotalTokens * ESTIMATION_PADDING_MULTIPLIER);\n\n const usedFallback =\n repoStructureCount.usedFallback ||\n promptContentCount.usedFallback ||\n fileResults.some((result) => result.usedFallback);\n\n const missingFileCount = fileResults.filter((result) => result.missing).length;\n\n let confidence = COMPLEXITY_CONFIDENCE[effectiveComplexity];\n if (usedFallback) {\n confidence = Math.min(confidence, FALLBACK_CONFIDENCE);\n }\n\n if (missingFileCount > 0) {\n confidence -= Math.min(0.25, missingFileCount * 0.05);\n }\n\n if (uniqueTargetFiles.length === 0) {\n confidence -= 0.1;\n }\n\n if (task.complexity !== analyzedComplexity) {\n confidence -= 0.05;\n }\n\n const feasible = totalEstimatedTokens <= counter.maxContextTokens;\n\n return {\n taskId: task.id,\n providerId: provider,\n contextTokens,\n promptTokens,\n expectedOutputTokens,\n totalEstimatedTokens,\n confidence: clamp(confidence, 0.1, 0.95),\n feasible,\n };\n}\n","import { type Tiktoken, get_encoding } from \"tiktoken\";\n\nconst CLAUDE_ENCODING = \"cl100k_base\";\nconst CLAUDE_INVOCATION_OVERHEAD = 1_500;\nconst CLAUDE_MAX_CONTEXT_TOKENS = 200_000;\n\nlet encoder: Tiktoken | undefined;\n\nfunction getEncoder(): Tiktoken {\n encoder ??= get_encoding(CLAUDE_ENCODING);\n return encoder;\n}\n\nexport class ClaudeTokenCounter {\n readonly invocationOverhead = CLAUDE_INVOCATION_OVERHEAD;\n readonly maxContextTokens = CLAUDE_MAX_CONTEXT_TOKENS;\n readonly encoding = CLAUDE_ENCODING;\n\n countTokens(text: string): number {\n return getEncoder().encode(text).length;\n }\n}\n","import { type Tiktoken, get_encoding } from \"tiktoken\";\n\nconst CODEX_INVOCATION_OVERHEAD = 1_000;\nconst CODEX_MAX_CONTEXT_TOKENS = 200_000;\nconst PRIMARY_ENCODING = \"o200k_base\";\nconst FALLBACK_ENCODING = \"cl100k_base\";\n\ntype SupportedCodexEncoding = typeof PRIMARY_ENCODING | typeof FALLBACK_ENCODING;\n\nlet encoder: Tiktoken | undefined;\nlet selectedEncoding: SupportedCodexEncoding | undefined;\n\nfunction getEncoder(): Tiktoken {\n if (encoder) {\n return encoder;\n }\n\n try {\n encoder = get_encoding(PRIMARY_ENCODING);\n selectedEncoding = PRIMARY_ENCODING;\n } catch {\n encoder = get_encoding(FALLBACK_ENCODING);\n selectedEncoding = FALLBACK_ENCODING;\n }\n\n return encoder;\n}\n\nexport class CodexTokenCounter {\n readonly invocationOverhead = CODEX_INVOCATION_OVERHEAD;\n readonly maxContextTokens = CODEX_MAX_CONTEXT_TOKENS;\n\n get encoding(): SupportedCodexEncoding {\n getEncoder();\n return selectedEncoding ?? FALLBACK_ENCODING;\n }\n\n countTokens(text: string): number {\n return getEncoder().encode(text).length;\n }\n}\n","import type { AgentProviderId, Task, TokenEstimate } from \"./estimator.js\";\n\nconst DEFAULT_RESERVE_PERCENT = 0.1;\nconst MIN_CONFIDENCE_THRESHOLD = 0.5;\nconst TOO_COMPLEX_BUDGET_SHARE = 0.6;\n\ntype DeferredReason = \"budget_exceeded\" | \"low_confidence\" | \"too_complex\";\n\nexport interface ExecutionPlan {\n totalBudget: number;\n selectedTasks: Array<{\n task: Task;\n estimate: TokenEstimate;\n cumulativeBudgetUsed: number;\n }>;\n deferredTasks: Array<{\n task: Task;\n estimate: TokenEstimate;\n reason: DeferredReason;\n }>;\n reserveTokens: number;\n remainingTokens: number;\n}\n\ninterface CandidateTask {\n task: Task;\n estimate: TokenEstimate;\n}\n\nfunction normalizeBudget(budget: number): number {\n if (!Number.isFinite(budget) || budget <= 0) {\n return 0;\n }\n\n return Math.floor(budget);\n}\n\nfunction getFallbackEstimate(task: Task): TokenEstimate {\n return {\n taskId: task.id,\n providerId: \"unknown\" as AgentProviderId,\n contextTokens: 0,\n promptTokens: 0,\n expectedOutputTokens: 0,\n totalEstimatedTokens: Number.MAX_SAFE_INTEGER,\n confidence: 0,\n feasible: false,\n };\n}\n\nfunction classifyDeferredReason(\n task: Task,\n estimate: TokenEstimate,\n effectiveBudget: number,\n): DeferredReason | undefined {\n if (!estimate.feasible) {\n return \"budget_exceeded\";\n }\n\n if (estimate.confidence < MIN_CONFIDENCE_THRESHOLD) {\n return \"low_confidence\";\n }\n\n if (\n task.complexity === \"complex\" &&\n effectiveBudget > 0 &&\n estimate.totalEstimatedTokens > effectiveBudget * TOO_COMPLEX_BUDGET_SHARE\n ) {\n return \"too_complex\";\n }\n\n return undefined;\n}\n\nfunction scoreByPriorityPerToken(task: Task, estimate: TokenEstimate): number {\n if (estimate.totalEstimatedTokens <= 0) {\n return task.priority;\n }\n\n return task.priority / estimate.totalEstimatedTokens;\n}\n\nexport function buildExecutionPlan(\n tasks: Task[],\n estimates: Map<string, TokenEstimate>,\n budget: number,\n): ExecutionPlan {\n const totalBudget = normalizeBudget(budget);\n const reserveTokens = Math.floor(totalBudget * DEFAULT_RESERVE_PERCENT);\n const effectiveBudget = Math.max(0, totalBudget - reserveTokens);\n\n const deferredTasks: ExecutionPlan[\"deferredTasks\"] = [];\n const candidates: CandidateTask[] = [];\n\n for (const task of tasks) {\n const estimate = estimates.get(task.id) ?? getFallbackEstimate(task);\n const reason = classifyDeferredReason(task, estimate, effectiveBudget);\n\n if (reason) {\n deferredTasks.push({ task, estimate, reason });\n continue;\n }\n\n candidates.push({ task, estimate });\n }\n\n candidates.sort((left, right) => {\n const ratioDifference =\n scoreByPriorityPerToken(right.task, right.estimate) -\n scoreByPriorityPerToken(left.task, left.estimate);\n\n if (ratioDifference !== 0) {\n return ratioDifference;\n }\n\n const priorityDifference = right.task.priority - left.task.priority;\n if (priorityDifference !== 0) {\n return priorityDifference;\n }\n\n return left.estimate.totalEstimatedTokens - right.estimate.totalEstimatedTokens;\n });\n\n const selectedTasks: ExecutionPlan[\"selectedTasks\"] = [];\n let budgetUsed = 0;\n\n for (const candidate of candidates) {\n const nextBudgetUsed = budgetUsed + candidate.estimate.totalEstimatedTokens;\n\n if (nextBudgetUsed <= effectiveBudget) {\n budgetUsed = nextBudgetUsed;\n selectedTasks.push({\n task: candidate.task,\n estimate: candidate.estimate,\n cumulativeBudgetUsed: budgetUsed,\n });\n continue;\n }\n\n deferredTasks.push({\n task: candidate.task,\n estimate: candidate.estimate,\n reason: \"budget_exceeded\",\n });\n }\n\n return {\n totalBudget,\n selectedTasks,\n deferredTasks,\n reserveTokens,\n remainingTokens: Math.max(0, effectiveBudget - budgetUsed),\n };\n}\n"],"mappings":"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","names":["get_encoding","encoder","getEncoder"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@open330/oac-budget",
|
|
3
|
+
"version": "2026.2.17",
|
|
4
|
+
"description": "Token estimation and execution planning for OAC",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/Open330/open-agent-contribution.git",
|
|
9
|
+
"directory": "packages/budget"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22.0.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"tiktoken": "^1.0.18",
|
|
31
|
+
"@open330/oac-core": "2026.2.17"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"tsup": "^8.3.6",
|
|
35
|
+
"typescript": "^5.7.3"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"typecheck": "tsc --noEmit"
|
|
41
|
+
}
|
|
42
|
+
}
|