sentinelayer-cli 0.1.2 → 0.3.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 +996 -996
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +62 -54
- package/src/agents/jules/config/definition.js +209 -209
- package/src/agents/jules/config/system-prompt.js +175 -175
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +377 -377
- package/src/agents/jules/loop.js +367 -367
- package/src/agents/jules/pulse.js +319 -319
- package/src/agents/jules/stream.js +186 -186
- package/src/agents/jules/swarm/file-scanner.js +74 -74
- package/src/agents/jules/swarm/index.js +11 -11
- package/src/agents/jules/swarm/orchestrator.js +362 -362
- package/src/agents/jules/swarm/pattern-hunter.js +123 -123
- package/src/agents/jules/swarm/sub-agent.js +308 -308
- package/src/agents/jules/tools/auth-audit.js +226 -222
- package/src/agents/jules/tools/dispatch.js +327 -327
- package/src/agents/jules/tools/file-edit.js +180 -180
- package/src/agents/jules/tools/file-read.js +100 -100
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +168 -168
- package/src/agents/jules/tools/grep.js +228 -228
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +161 -161
- package/src/agents/jules/tools/runtime-audit.js +493 -493
- package/src/agents/jules/tools/shell.js +383 -383
- package/src/ai/aidenid.js +972 -945
- package/src/ai/client.js +508 -508
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/site-store.js +145 -145
- package/src/audit/agents/architecture.js +180 -180
- package/src/audit/agents/compliance.js +179 -179
- package/src/audit/agents/documentation.js +165 -165
- package/src/audit/agents/performance.js +145 -145
- package/src/audit/agents/security.js +215 -215
- package/src/audit/agents/testing.js +172 -172
- package/src/audit/orchestrator.js +557 -557
- package/src/audit/package.js +204 -204
- package/src/audit/registry.js +284 -284
- package/src/audit/replay.js +103 -103
- package/src/auth/http.js +113 -113
- package/src/auth/service.js +891 -848
- package/src/auth/session-store.js +359 -345
- package/src/cli.js +252 -252
- package/src/commands/ai/identity-lifecycle.js +1338 -1337
- package/src/commands/ai/provision-governance.js +1272 -1246
- package/src/commands/ai/shared.js +147 -147
- package/src/commands/ai.js +11 -11
- package/src/commands/apply.js +12 -12
- package/src/commands/audit.js +1166 -1166
- package/src/commands/auth.js +375 -366
- package/src/commands/chat.js +191 -191
- package/src/commands/config.js +184 -184
- package/src/commands/cost.js +311 -311
- package/src/commands/daemon/core.js +850 -850
- package/src/commands/daemon/extended.js +1048 -1048
- package/src/commands/daemon/shared.js +213 -213
- package/src/commands/daemon.js +11 -11
- package/src/commands/guide.js +174 -174
- package/src/commands/ingest.js +58 -58
- package/src/commands/init.js +55 -55
- package/src/commands/legacy-args.js +10 -10
- package/src/commands/mcp.js +461 -404
- package/src/commands/omargate.js +15 -15
- package/src/commands/persona.js +20 -20
- package/src/commands/plugin.js +260 -260
- package/src/commands/policy.js +132 -132
- package/src/commands/prompt.js +238 -238
- package/src/commands/review.js +704 -704
- package/src/commands/scan.js +866 -788
- package/src/commands/spec.js +716 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +510 -510
- package/src/config/agent-dictionary.js +182 -182
- package/src/config/io.js +56 -56
- package/src/config/paths.js +18 -18
- package/src/config/schema.js +55 -55
- package/src/config/service.js +184 -184
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tracker.js +171 -171
- package/src/daemon/artifact-lineage.js +534 -534
- package/src/daemon/assignment-ledger.js +770 -770
- package/src/daemon/ast-parser-layer.js +258 -258
- package/src/daemon/budget-governor.js +633 -633
- package/src/daemon/callgraph-overlay.js +646 -646
- package/src/daemon/error-worker.js +626 -626
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/jira-lifecycle.js +632 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/watchdog.js +971 -971
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +918 -918
- package/src/legacy-cli.js +2548 -2435
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +118 -106
- package/src/review/ai-review.js +669 -669
- package/src/review/local-review.js +1284 -1284
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/spec-binding.js +487 -487
- package/src/scaffold/generator.js +67 -0
- package/src/scaffold/templates.js +150 -0
- package/src/scan/generator.js +418 -351
- package/src/scan/gh-secrets.js +107 -0
- package/src/spec/generator.js +519 -519
- package/src/spec/regenerate.js +237 -237
- package/src/spec/templates.js +91 -91
- package/src/swarm/dashboard.js +247 -247
- package/src/swarm/factory.js +363 -363
- package/src/swarm/pentest.js +934 -934
- package/src/swarm/registry.js +419 -419
- package/src/swarm/report.js +158 -158
- package/src/swarm/runtime.js +576 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/sync.js +96 -59
- package/src/ui/markdown.js +220 -220
package/src/cost/budget.js
CHANGED
|
@@ -1,235 +1,235 @@
|
|
|
1
|
-
function normalizeNumber(value, field) {
|
|
2
|
-
const normalized = Number(value || 0);
|
|
3
|
-
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
4
|
-
throw new Error(`${field} must be a non-negative number.`);
|
|
5
|
-
}
|
|
6
|
-
return normalized;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function formatNumber(value, decimals = 0) {
|
|
10
|
-
return Number(value || 0).toFixed(decimals);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function normalizeWarningThresholdPercent(value) {
|
|
14
|
-
const normalized = Number(value || 0);
|
|
15
|
-
if (!Number.isFinite(normalized) || normalized < 0 || normalized > 100) {
|
|
16
|
-
throw new Error("warningThresholdPercent must be between 0 and 100.");
|
|
17
|
-
}
|
|
18
|
-
return normalized;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function collectLimitStatusWithThreshold({
|
|
22
|
-
reasons,
|
|
23
|
-
warnings,
|
|
24
|
-
usageValue,
|
|
25
|
-
limitValue,
|
|
26
|
-
warningThresholdPercent,
|
|
27
|
-
stopCode,
|
|
28
|
-
warningCode,
|
|
29
|
-
stopMessage,
|
|
30
|
-
warningMessage,
|
|
31
|
-
}) {
|
|
32
|
-
const normalizedUsage = normalizeNumber(usageValue, "usageValue");
|
|
33
|
-
const normalizedLimit = normalizeNumber(limitValue, "limitValue");
|
|
34
|
-
const normalizedWarningThresholdPercent =
|
|
35
|
-
normalizeWarningThresholdPercent(warningThresholdPercent);
|
|
36
|
-
|
|
37
|
-
if (normalizedLimit <= 0) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (normalizedUsage > normalizedLimit) {
|
|
42
|
-
reasons.push({
|
|
43
|
-
code: stopCode,
|
|
44
|
-
message: stopMessage(normalizedUsage, normalizedLimit),
|
|
45
|
-
});
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (normalizedWarningThresholdPercent <= 0) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const thresholdValue = (normalizedWarningThresholdPercent / 100) * normalizedLimit;
|
|
54
|
-
if (normalizedUsage >= thresholdValue) {
|
|
55
|
-
warnings.push({
|
|
56
|
-
code: warningCode,
|
|
57
|
-
message: warningMessage(normalizedUsage, normalizedLimit, normalizedWarningThresholdPercent),
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Evaluate runtime/cost/token/tool/no-progress budgets and return deterministic stop/warning signals.
|
|
64
|
-
*
|
|
65
|
-
* @param {{
|
|
66
|
-
* sessionSummary?: {
|
|
67
|
-
* costUsd?: number,
|
|
68
|
-
* outputTokens?: number,
|
|
69
|
-
* noProgressStreak?: number,
|
|
70
|
-
* durationMs?: number,
|
|
71
|
-
* toolCalls?: number
|
|
72
|
-
* },
|
|
73
|
-
* maxCostUsd?: number,
|
|
74
|
-
* maxOutputTokens?: number,
|
|
75
|
-
* maxNoProgress?: number,
|
|
76
|
-
* maxRuntimeMs?: number,
|
|
77
|
-
* maxToolCalls?: number,
|
|
78
|
-
* warningThresholdPercent?: number
|
|
79
|
-
* }} [options]
|
|
80
|
-
* @returns {{
|
|
81
|
-
* blocking: boolean,
|
|
82
|
-
* warnings: Array<{ code: string, message: string }>,
|
|
83
|
-
* reasons: Array<{ code: string, message: string }>,
|
|
84
|
-
* limits: {
|
|
85
|
-
* maxCostUsd: number,
|
|
86
|
-
* maxOutputTokens: number,
|
|
87
|
-
* maxNoProgress: number,
|
|
88
|
-
* maxRuntimeMs: number,
|
|
89
|
-
* maxToolCalls: number,
|
|
90
|
-
* warningThresholdPercent: number
|
|
91
|
-
* },
|
|
92
|
-
* usage: {
|
|
93
|
-
* costUsd: number,
|
|
94
|
-
* outputTokens: number,
|
|
95
|
-
* noProgressStreak: number,
|
|
96
|
-
* runtimeMs: number,
|
|
97
|
-
* toolCalls: number
|
|
98
|
-
* }
|
|
99
|
-
* }}
|
|
100
|
-
*/
|
|
101
|
-
export function evaluateBudget({
|
|
102
|
-
sessionSummary = {},
|
|
103
|
-
maxCostUsd = 1.0,
|
|
104
|
-
maxOutputTokens = 0,
|
|
105
|
-
maxNoProgress = 3,
|
|
106
|
-
maxRuntimeMs = 0,
|
|
107
|
-
maxToolCalls = 0,
|
|
108
|
-
warningThresholdPercent = 80,
|
|
109
|
-
} = {}) {
|
|
110
|
-
const normalizedMaxCost = normalizeNumber(maxCostUsd, "maxCostUsd");
|
|
111
|
-
const normalizedMaxOutputTokens = normalizeNumber(maxOutputTokens, "maxOutputTokens");
|
|
112
|
-
const normalizedMaxNoProgress = Math.max(1, normalizeNumber(maxNoProgress, "maxNoProgress"));
|
|
113
|
-
const normalizedMaxRuntimeMs = normalizeNumber(maxRuntimeMs, "maxRuntimeMs");
|
|
114
|
-
const normalizedMaxToolCalls = normalizeNumber(maxToolCalls, "maxToolCalls");
|
|
115
|
-
const normalizedWarningThresholdPercent = normalizeWarningThresholdPercent(
|
|
116
|
-
warningThresholdPercent
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
const totalCostUsd = normalizeNumber(sessionSummary.costUsd || 0, "sessionSummary.costUsd");
|
|
120
|
-
const totalOutputTokens = normalizeNumber(
|
|
121
|
-
sessionSummary.outputTokens || 0,
|
|
122
|
-
"sessionSummary.outputTokens"
|
|
123
|
-
);
|
|
124
|
-
const noProgressStreak = normalizeNumber(
|
|
125
|
-
sessionSummary.noProgressStreak || 0,
|
|
126
|
-
"sessionSummary.noProgressStreak"
|
|
127
|
-
);
|
|
128
|
-
const totalRuntimeMs = normalizeNumber(sessionSummary.durationMs || 0, "sessionSummary.durationMs");
|
|
129
|
-
const totalToolCalls = normalizeNumber(
|
|
130
|
-
sessionSummary.toolCalls || 0,
|
|
131
|
-
"sessionSummary.toolCalls"
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
const reasons = [];
|
|
135
|
-
const warnings = [];
|
|
136
|
-
|
|
137
|
-
collectLimitStatusWithThreshold({
|
|
138
|
-
reasons,
|
|
139
|
-
warnings,
|
|
140
|
-
usageValue: totalCostUsd,
|
|
141
|
-
limitValue: normalizedMaxCost,
|
|
142
|
-
warningThresholdPercent: normalizedWarningThresholdPercent,
|
|
143
|
-
stopCode: "MAX_COST_EXCEEDED",
|
|
144
|
-
warningCode: "COST_BUDGET_NEAR_LIMIT",
|
|
145
|
-
stopMessage: (usage, limit) =>
|
|
146
|
-
`Cost budget exceeded (${formatNumber(usage, 6)} > ${formatNumber(limit, 6)}).`,
|
|
147
|
-
warningMessage: (usage, limit, thresholdPercent) =>
|
|
148
|
-
`Cost budget near limit (${formatNumber(usage, 6)} / ${formatNumber(limit, 6)} at ${formatNumber(
|
|
149
|
-
thresholdPercent,
|
|
150
|
-
0
|
|
151
|
-
)}%).`,
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
collectLimitStatusWithThreshold({
|
|
155
|
-
reasons,
|
|
156
|
-
warnings,
|
|
157
|
-
usageValue: totalOutputTokens,
|
|
158
|
-
limitValue: normalizedMaxOutputTokens,
|
|
159
|
-
warningThresholdPercent: normalizedWarningThresholdPercent,
|
|
160
|
-
stopCode: "MAX_OUTPUT_TOKENS_EXCEEDED",
|
|
161
|
-
warningCode: "OUTPUT_TOKENS_NEAR_LIMIT",
|
|
162
|
-
stopMessage: (usage, limit) =>
|
|
163
|
-
`Output token budget exceeded (${formatNumber(usage, 0)} > ${formatNumber(limit, 0)}).`,
|
|
164
|
-
warningMessage: (usage, limit, thresholdPercent) =>
|
|
165
|
-
`Output token budget near limit (${formatNumber(usage, 0)} / ${formatNumber(
|
|
166
|
-
limit,
|
|
167
|
-
0
|
|
168
|
-
)} at ${formatNumber(thresholdPercent, 0)}%).`,
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
collectLimitStatusWithThreshold({
|
|
172
|
-
reasons,
|
|
173
|
-
warnings,
|
|
174
|
-
usageValue: totalRuntimeMs,
|
|
175
|
-
limitValue: normalizedMaxRuntimeMs,
|
|
176
|
-
warningThresholdPercent: normalizedWarningThresholdPercent,
|
|
177
|
-
stopCode: "MAX_RUNTIME_MS_EXCEEDED",
|
|
178
|
-
warningCode: "RUNTIME_MS_NEAR_LIMIT",
|
|
179
|
-
stopMessage: (usage, limit) =>
|
|
180
|
-
`Runtime budget exceeded (${formatNumber(usage, 0)}ms > ${formatNumber(limit, 0)}ms).`,
|
|
181
|
-
warningMessage: (usage, limit, thresholdPercent) =>
|
|
182
|
-
`Runtime budget near limit (${formatNumber(usage, 0)}ms / ${formatNumber(
|
|
183
|
-
limit,
|
|
184
|
-
0
|
|
185
|
-
)}ms at ${formatNumber(thresholdPercent, 0)}%).`,
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
collectLimitStatusWithThreshold({
|
|
189
|
-
reasons,
|
|
190
|
-
warnings,
|
|
191
|
-
usageValue: totalToolCalls,
|
|
192
|
-
limitValue: normalizedMaxToolCalls,
|
|
193
|
-
warningThresholdPercent: normalizedWarningThresholdPercent,
|
|
194
|
-
stopCode: "MAX_TOOL_CALLS_EXCEEDED",
|
|
195
|
-
warningCode: "TOOL_CALLS_NEAR_LIMIT",
|
|
196
|
-
stopMessage: (usage, limit) =>
|
|
197
|
-
`Tool-call budget exceeded (${formatNumber(usage, 0)} > ${formatNumber(limit, 0)}).`,
|
|
198
|
-
warningMessage: (usage, limit, thresholdPercent) =>
|
|
199
|
-
`Tool-call budget near limit (${formatNumber(usage, 0)} / ${formatNumber(
|
|
200
|
-
limit,
|
|
201
|
-
0
|
|
202
|
-
)} at ${formatNumber(thresholdPercent, 0)}%).`,
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
if (noProgressStreak >= normalizedMaxNoProgress) {
|
|
206
|
-
reasons.push({
|
|
207
|
-
code: "DIMINISHING_RETURNS",
|
|
208
|
-
message: `No-progress streak reached ${formatNumber(noProgressStreak, 0)} (threshold ${formatNumber(
|
|
209
|
-
normalizedMaxNoProgress,
|
|
210
|
-
0
|
|
211
|
-
)}).`,
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return {
|
|
216
|
-
blocking: reasons.length > 0,
|
|
217
|
-
warnings,
|
|
218
|
-
reasons,
|
|
219
|
-
limits: {
|
|
220
|
-
maxCostUsd: normalizedMaxCost,
|
|
221
|
-
maxOutputTokens: normalizedMaxOutputTokens,
|
|
222
|
-
maxNoProgress: normalizedMaxNoProgress,
|
|
223
|
-
maxRuntimeMs: normalizedMaxRuntimeMs,
|
|
224
|
-
maxToolCalls: normalizedMaxToolCalls,
|
|
225
|
-
warningThresholdPercent: normalizedWarningThresholdPercent,
|
|
226
|
-
},
|
|
227
|
-
usage: {
|
|
228
|
-
costUsd: totalCostUsd,
|
|
229
|
-
outputTokens: totalOutputTokens,
|
|
230
|
-
noProgressStreak,
|
|
231
|
-
runtimeMs: totalRuntimeMs,
|
|
232
|
-
toolCalls: totalToolCalls,
|
|
233
|
-
},
|
|
234
|
-
};
|
|
235
|
-
}
|
|
1
|
+
function normalizeNumber(value, field) {
|
|
2
|
+
const normalized = Number(value || 0);
|
|
3
|
+
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
4
|
+
throw new Error(`${field} must be a non-negative number.`);
|
|
5
|
+
}
|
|
6
|
+
return normalized;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function formatNumber(value, decimals = 0) {
|
|
10
|
+
return Number(value || 0).toFixed(decimals);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizeWarningThresholdPercent(value) {
|
|
14
|
+
const normalized = Number(value || 0);
|
|
15
|
+
if (!Number.isFinite(normalized) || normalized < 0 || normalized > 100) {
|
|
16
|
+
throw new Error("warningThresholdPercent must be between 0 and 100.");
|
|
17
|
+
}
|
|
18
|
+
return normalized;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function collectLimitStatusWithThreshold({
|
|
22
|
+
reasons,
|
|
23
|
+
warnings,
|
|
24
|
+
usageValue,
|
|
25
|
+
limitValue,
|
|
26
|
+
warningThresholdPercent,
|
|
27
|
+
stopCode,
|
|
28
|
+
warningCode,
|
|
29
|
+
stopMessage,
|
|
30
|
+
warningMessage,
|
|
31
|
+
}) {
|
|
32
|
+
const normalizedUsage = normalizeNumber(usageValue, "usageValue");
|
|
33
|
+
const normalizedLimit = normalizeNumber(limitValue, "limitValue");
|
|
34
|
+
const normalizedWarningThresholdPercent =
|
|
35
|
+
normalizeWarningThresholdPercent(warningThresholdPercent);
|
|
36
|
+
|
|
37
|
+
if (normalizedLimit <= 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (normalizedUsage > normalizedLimit) {
|
|
42
|
+
reasons.push({
|
|
43
|
+
code: stopCode,
|
|
44
|
+
message: stopMessage(normalizedUsage, normalizedLimit),
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (normalizedWarningThresholdPercent <= 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const thresholdValue = (normalizedWarningThresholdPercent / 100) * normalizedLimit;
|
|
54
|
+
if (normalizedUsage >= thresholdValue) {
|
|
55
|
+
warnings.push({
|
|
56
|
+
code: warningCode,
|
|
57
|
+
message: warningMessage(normalizedUsage, normalizedLimit, normalizedWarningThresholdPercent),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Evaluate runtime/cost/token/tool/no-progress budgets and return deterministic stop/warning signals.
|
|
64
|
+
*
|
|
65
|
+
* @param {{
|
|
66
|
+
* sessionSummary?: {
|
|
67
|
+
* costUsd?: number,
|
|
68
|
+
* outputTokens?: number,
|
|
69
|
+
* noProgressStreak?: number,
|
|
70
|
+
* durationMs?: number,
|
|
71
|
+
* toolCalls?: number
|
|
72
|
+
* },
|
|
73
|
+
* maxCostUsd?: number,
|
|
74
|
+
* maxOutputTokens?: number,
|
|
75
|
+
* maxNoProgress?: number,
|
|
76
|
+
* maxRuntimeMs?: number,
|
|
77
|
+
* maxToolCalls?: number,
|
|
78
|
+
* warningThresholdPercent?: number
|
|
79
|
+
* }} [options]
|
|
80
|
+
* @returns {{
|
|
81
|
+
* blocking: boolean,
|
|
82
|
+
* warnings: Array<{ code: string, message: string }>,
|
|
83
|
+
* reasons: Array<{ code: string, message: string }>,
|
|
84
|
+
* limits: {
|
|
85
|
+
* maxCostUsd: number,
|
|
86
|
+
* maxOutputTokens: number,
|
|
87
|
+
* maxNoProgress: number,
|
|
88
|
+
* maxRuntimeMs: number,
|
|
89
|
+
* maxToolCalls: number,
|
|
90
|
+
* warningThresholdPercent: number
|
|
91
|
+
* },
|
|
92
|
+
* usage: {
|
|
93
|
+
* costUsd: number,
|
|
94
|
+
* outputTokens: number,
|
|
95
|
+
* noProgressStreak: number,
|
|
96
|
+
* runtimeMs: number,
|
|
97
|
+
* toolCalls: number
|
|
98
|
+
* }
|
|
99
|
+
* }}
|
|
100
|
+
*/
|
|
101
|
+
export function evaluateBudget({
|
|
102
|
+
sessionSummary = {},
|
|
103
|
+
maxCostUsd = 1.0,
|
|
104
|
+
maxOutputTokens = 0,
|
|
105
|
+
maxNoProgress = 3,
|
|
106
|
+
maxRuntimeMs = 0,
|
|
107
|
+
maxToolCalls = 0,
|
|
108
|
+
warningThresholdPercent = 80,
|
|
109
|
+
} = {}) {
|
|
110
|
+
const normalizedMaxCost = normalizeNumber(maxCostUsd, "maxCostUsd");
|
|
111
|
+
const normalizedMaxOutputTokens = normalizeNumber(maxOutputTokens, "maxOutputTokens");
|
|
112
|
+
const normalizedMaxNoProgress = Math.max(1, normalizeNumber(maxNoProgress, "maxNoProgress"));
|
|
113
|
+
const normalizedMaxRuntimeMs = normalizeNumber(maxRuntimeMs, "maxRuntimeMs");
|
|
114
|
+
const normalizedMaxToolCalls = normalizeNumber(maxToolCalls, "maxToolCalls");
|
|
115
|
+
const normalizedWarningThresholdPercent = normalizeWarningThresholdPercent(
|
|
116
|
+
warningThresholdPercent
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const totalCostUsd = normalizeNumber(sessionSummary.costUsd || 0, "sessionSummary.costUsd");
|
|
120
|
+
const totalOutputTokens = normalizeNumber(
|
|
121
|
+
sessionSummary.outputTokens || 0,
|
|
122
|
+
"sessionSummary.outputTokens"
|
|
123
|
+
);
|
|
124
|
+
const noProgressStreak = normalizeNumber(
|
|
125
|
+
sessionSummary.noProgressStreak || 0,
|
|
126
|
+
"sessionSummary.noProgressStreak"
|
|
127
|
+
);
|
|
128
|
+
const totalRuntimeMs = normalizeNumber(sessionSummary.durationMs || 0, "sessionSummary.durationMs");
|
|
129
|
+
const totalToolCalls = normalizeNumber(
|
|
130
|
+
sessionSummary.toolCalls || 0,
|
|
131
|
+
"sessionSummary.toolCalls"
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const reasons = [];
|
|
135
|
+
const warnings = [];
|
|
136
|
+
|
|
137
|
+
collectLimitStatusWithThreshold({
|
|
138
|
+
reasons,
|
|
139
|
+
warnings,
|
|
140
|
+
usageValue: totalCostUsd,
|
|
141
|
+
limitValue: normalizedMaxCost,
|
|
142
|
+
warningThresholdPercent: normalizedWarningThresholdPercent,
|
|
143
|
+
stopCode: "MAX_COST_EXCEEDED",
|
|
144
|
+
warningCode: "COST_BUDGET_NEAR_LIMIT",
|
|
145
|
+
stopMessage: (usage, limit) =>
|
|
146
|
+
`Cost budget exceeded (${formatNumber(usage, 6)} > ${formatNumber(limit, 6)}).`,
|
|
147
|
+
warningMessage: (usage, limit, thresholdPercent) =>
|
|
148
|
+
`Cost budget near limit (${formatNumber(usage, 6)} / ${formatNumber(limit, 6)} at ${formatNumber(
|
|
149
|
+
thresholdPercent,
|
|
150
|
+
0
|
|
151
|
+
)}%).`,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
collectLimitStatusWithThreshold({
|
|
155
|
+
reasons,
|
|
156
|
+
warnings,
|
|
157
|
+
usageValue: totalOutputTokens,
|
|
158
|
+
limitValue: normalizedMaxOutputTokens,
|
|
159
|
+
warningThresholdPercent: normalizedWarningThresholdPercent,
|
|
160
|
+
stopCode: "MAX_OUTPUT_TOKENS_EXCEEDED",
|
|
161
|
+
warningCode: "OUTPUT_TOKENS_NEAR_LIMIT",
|
|
162
|
+
stopMessage: (usage, limit) =>
|
|
163
|
+
`Output token budget exceeded (${formatNumber(usage, 0)} > ${formatNumber(limit, 0)}).`,
|
|
164
|
+
warningMessage: (usage, limit, thresholdPercent) =>
|
|
165
|
+
`Output token budget near limit (${formatNumber(usage, 0)} / ${formatNumber(
|
|
166
|
+
limit,
|
|
167
|
+
0
|
|
168
|
+
)} at ${formatNumber(thresholdPercent, 0)}%).`,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
collectLimitStatusWithThreshold({
|
|
172
|
+
reasons,
|
|
173
|
+
warnings,
|
|
174
|
+
usageValue: totalRuntimeMs,
|
|
175
|
+
limitValue: normalizedMaxRuntimeMs,
|
|
176
|
+
warningThresholdPercent: normalizedWarningThresholdPercent,
|
|
177
|
+
stopCode: "MAX_RUNTIME_MS_EXCEEDED",
|
|
178
|
+
warningCode: "RUNTIME_MS_NEAR_LIMIT",
|
|
179
|
+
stopMessage: (usage, limit) =>
|
|
180
|
+
`Runtime budget exceeded (${formatNumber(usage, 0)}ms > ${formatNumber(limit, 0)}ms).`,
|
|
181
|
+
warningMessage: (usage, limit, thresholdPercent) =>
|
|
182
|
+
`Runtime budget near limit (${formatNumber(usage, 0)}ms / ${formatNumber(
|
|
183
|
+
limit,
|
|
184
|
+
0
|
|
185
|
+
)}ms at ${formatNumber(thresholdPercent, 0)}%).`,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
collectLimitStatusWithThreshold({
|
|
189
|
+
reasons,
|
|
190
|
+
warnings,
|
|
191
|
+
usageValue: totalToolCalls,
|
|
192
|
+
limitValue: normalizedMaxToolCalls,
|
|
193
|
+
warningThresholdPercent: normalizedWarningThresholdPercent,
|
|
194
|
+
stopCode: "MAX_TOOL_CALLS_EXCEEDED",
|
|
195
|
+
warningCode: "TOOL_CALLS_NEAR_LIMIT",
|
|
196
|
+
stopMessage: (usage, limit) =>
|
|
197
|
+
`Tool-call budget exceeded (${formatNumber(usage, 0)} > ${formatNumber(limit, 0)}).`,
|
|
198
|
+
warningMessage: (usage, limit, thresholdPercent) =>
|
|
199
|
+
`Tool-call budget near limit (${formatNumber(usage, 0)} / ${formatNumber(
|
|
200
|
+
limit,
|
|
201
|
+
0
|
|
202
|
+
)} at ${formatNumber(thresholdPercent, 0)}%).`,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
if (noProgressStreak >= normalizedMaxNoProgress) {
|
|
206
|
+
reasons.push({
|
|
207
|
+
code: "DIMINISHING_RETURNS",
|
|
208
|
+
message: `No-progress streak reached ${formatNumber(noProgressStreak, 0)} (threshold ${formatNumber(
|
|
209
|
+
normalizedMaxNoProgress,
|
|
210
|
+
0
|
|
211
|
+
)}).`,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
blocking: reasons.length > 0,
|
|
217
|
+
warnings,
|
|
218
|
+
reasons,
|
|
219
|
+
limits: {
|
|
220
|
+
maxCostUsd: normalizedMaxCost,
|
|
221
|
+
maxOutputTokens: normalizedMaxOutputTokens,
|
|
222
|
+
maxNoProgress: normalizedMaxNoProgress,
|
|
223
|
+
maxRuntimeMs: normalizedMaxRuntimeMs,
|
|
224
|
+
maxToolCalls: normalizedMaxToolCalls,
|
|
225
|
+
warningThresholdPercent: normalizedWarningThresholdPercent,
|
|
226
|
+
},
|
|
227
|
+
usage: {
|
|
228
|
+
costUsd: totalCostUsd,
|
|
229
|
+
outputTokens: totalOutputTokens,
|
|
230
|
+
noProgressStreak,
|
|
231
|
+
runtimeMs: totalRuntimeMs,
|
|
232
|
+
toolCalls: totalToolCalls,
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
}
|