blitzstrike 1.0.53 → 1.0.55
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/CHANGELOG.md +31 -0
- package/dist/index.js +114 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,37 @@ All notable changes to Blitz Strike are documented here.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [1.0.55] — 2026-09-13
|
|
9
|
+
|
|
10
|
+
### Added — Autonomous Security Engine (phase 15: termination engine)
|
|
11
|
+
|
|
12
|
+
- **Termination Engine** (`src/mission/termination.ts`, §24):
|
|
13
|
+
- `evaluateTermination()` — ordered hard stops: user_interrupted →
|
|
14
|
+
budget_exhausted → safety_violation → scope_violation → max_iterations →
|
|
15
|
+
no_progress.
|
|
16
|
+
- `objectiveComplete()` — all tasks in a terminal non-failed state.
|
|
17
|
+
- `applyTermination()` — records the reason + detail into `decision_log` and
|
|
18
|
+
sets `completed`/`terminated`.
|
|
19
|
+
- `NO_PROGRESS_LIMIT` — iterations without progress before declaring stuck.
|
|
20
|
+
- Mission Control now terminates on ANY traceable reason (not just budget) and
|
|
21
|
+
logs the termination decision with detail.
|
|
22
|
+
|
|
23
|
+
## [1.0.54] — 2026-09-13
|
|
24
|
+
|
|
25
|
+
### Added — Autonomous Security Engine (phase 14: budget controller)
|
|
26
|
+
|
|
27
|
+
- **Budget Controller** (`src/mission/budget.ts`, §22):
|
|
28
|
+
- `budgetStatus()` — tracks time / tasks / requests / tokens / cost with
|
|
29
|
+
per-dimension overrun flags.
|
|
30
|
+
- `budgetOverrun()` — detects overruns and returns a specific reason
|
|
31
|
+
(e.g. `budget overrun: tasks, requests`).
|
|
32
|
+
- `consumeTasks()` / `consumeTokens()` — increment consumption counters.
|
|
33
|
+
- `perTaskOverrun()` — enforces the per-task request budget.
|
|
34
|
+
- Extended the `Budget` type with `max_tokens`, `max_cost_usd`,
|
|
35
|
+
`per_task_budget`, `started_at`, and `used_tokens`/`used_cost_usd`.
|
|
36
|
+
- Mission Control now terminates gracefully on ANY budget dimension overrun
|
|
37
|
+
(not just tasks) and records a final budget snapshot in the decision log.
|
|
38
|
+
|
|
8
39
|
## [1.0.53] — 2026-09-13
|
|
9
40
|
|
|
10
41
|
### Added — Autonomous Security Engine (phase 13: model router)
|
package/dist/index.js
CHANGED
|
@@ -18097,10 +18097,16 @@ function newMission(id, target, scope, mode, budget) {
|
|
|
18097
18097
|
max_time_seconds: 3600,
|
|
18098
18098
|
max_tasks: 200,
|
|
18099
18099
|
max_requests: 1000,
|
|
18100
|
+
max_tokens: 2000000,
|
|
18101
|
+
max_cost_usd: 5,
|
|
18100
18102
|
max_parallel_workers: 4,
|
|
18101
18103
|
risk: "medium",
|
|
18104
|
+
per_task_budget: { max_time_seconds: 60, max_requests: 50 },
|
|
18105
|
+
started_at: Date.now(),
|
|
18102
18106
|
used_tasks: 0,
|
|
18103
18107
|
used_requests: 0,
|
|
18108
|
+
used_tokens: 0,
|
|
18109
|
+
used_cost_usd: 0,
|
|
18104
18110
|
...budget ?? {}
|
|
18105
18111
|
},
|
|
18106
18112
|
discovered_assets: [],
|
|
@@ -19134,6 +19140,94 @@ var init_router = __esm(() => {
|
|
|
19134
19140
|
};
|
|
19135
19141
|
});
|
|
19136
19142
|
|
|
19143
|
+
// src/mission/budget.ts
|
|
19144
|
+
function clampRatio(used, max) {
|
|
19145
|
+
if (max <= 0)
|
|
19146
|
+
return 0;
|
|
19147
|
+
return Math.max(0, Math.min(1, used / max));
|
|
19148
|
+
}
|
|
19149
|
+
function budgetStatus(budget, now = Date.now()) {
|
|
19150
|
+
const elapsed = Math.max(0, (now - budget.started_at) / 1000);
|
|
19151
|
+
const dims = [
|
|
19152
|
+
["time", "time (s)", elapsed, budget.max_time_seconds],
|
|
19153
|
+
["tasks", "tasks", budget.used_tasks, budget.max_tasks],
|
|
19154
|
+
["requests", "requests", budget.used_requests, budget.max_requests],
|
|
19155
|
+
["tokens", "tokens", budget.used_tokens, budget.max_tokens],
|
|
19156
|
+
["cost", "cost (USD)", budget.used_cost_usd, budget.max_cost_usd]
|
|
19157
|
+
];
|
|
19158
|
+
return dims.map(([key, label, used, max]) => ({
|
|
19159
|
+
key,
|
|
19160
|
+
label,
|
|
19161
|
+
used: Number(used.toFixed(2)),
|
|
19162
|
+
max,
|
|
19163
|
+
overrun: used >= max,
|
|
19164
|
+
ratio: clampRatio(used, max)
|
|
19165
|
+
}));
|
|
19166
|
+
}
|
|
19167
|
+
function budgetOverrun(budget, now = Date.now()) {
|
|
19168
|
+
const dims = budgetStatus(budget, now).filter((d) => d.overrun);
|
|
19169
|
+
if (dims.length === 0)
|
|
19170
|
+
return { overrun: false, reason: null, dimensions: [] };
|
|
19171
|
+
return {
|
|
19172
|
+
overrun: true,
|
|
19173
|
+
reason: `budget overrun: ${dims.map((d) => d.key).join(", ")}`,
|
|
19174
|
+
dimensions: dims.map((d) => d.key)
|
|
19175
|
+
};
|
|
19176
|
+
}
|
|
19177
|
+
function consumeTasks(budget, tasks, requests) {
|
|
19178
|
+
return {
|
|
19179
|
+
...budget,
|
|
19180
|
+
used_tasks: budget.used_tasks + tasks,
|
|
19181
|
+
used_requests: budget.used_requests + requests
|
|
19182
|
+
};
|
|
19183
|
+
}
|
|
19184
|
+
|
|
19185
|
+
// src/mission/termination.ts
|
|
19186
|
+
function evaluateTermination(mission, state) {
|
|
19187
|
+
if (state.user_interrupted) {
|
|
19188
|
+
return { terminate: true, reason: "user_interrupted", detail: "user requested stop" };
|
|
19189
|
+
}
|
|
19190
|
+
const overrun = budgetOverrun(mission.budget);
|
|
19191
|
+
if (overrun.overrun) {
|
|
19192
|
+
return { terminate: true, reason: "budget_exhausted", detail: overrun.reason ?? "budget limit" };
|
|
19193
|
+
}
|
|
19194
|
+
if (state.safety_violated) {
|
|
19195
|
+
return { terminate: true, reason: "safety_violation", detail: "a safety invariant was violated" };
|
|
19196
|
+
}
|
|
19197
|
+
if (state.scope_violated) {
|
|
19198
|
+
return { terminate: true, reason: "scope_violation", detail: "an agent attempted to leave scope" };
|
|
19199
|
+
}
|
|
19200
|
+
if (state.iterations >= state.max_iterations) {
|
|
19201
|
+
return { terminate: true, reason: "max_iterations", detail: `reached ${state.max_iterations} iterations` };
|
|
19202
|
+
}
|
|
19203
|
+
if (state.iterations - state.last_progress_iteration >= NO_PROGRESS_LIMIT) {
|
|
19204
|
+
return { terminate: true, reason: "no_progress", detail: `no progress for ${NO_PROGRESS_LIMIT} iterations` };
|
|
19205
|
+
}
|
|
19206
|
+
return { terminate: false, reason: null, detail: "" };
|
|
19207
|
+
}
|
|
19208
|
+
function objectiveComplete(mission) {
|
|
19209
|
+
const done = mission.tasks.every((t) => t.status === "success" || t.status === "skipped" || t.status === "cancelled");
|
|
19210
|
+
if (done && mission.tasks.length > 0) {
|
|
19211
|
+
return { terminate: true, reason: "objective_complete", detail: "all tasks resolved" };
|
|
19212
|
+
}
|
|
19213
|
+
return { terminate: false, reason: null, detail: "" };
|
|
19214
|
+
}
|
|
19215
|
+
function applyTermination(mission, decision) {
|
|
19216
|
+
if (!decision.reason)
|
|
19217
|
+
return mission;
|
|
19218
|
+
return {
|
|
19219
|
+
...mission,
|
|
19220
|
+
status: decision.reason === "objective_complete" ? "completed" : "terminated",
|
|
19221
|
+
termination_reason: decision.reason,
|
|
19222
|
+
decision_log: [
|
|
19223
|
+
...mission.decision_log,
|
|
19224
|
+
{ at: new Date().toISOString(), agent: "termination", decision: `${decision.reason}: ${decision.detail}` }
|
|
19225
|
+
]
|
|
19226
|
+
};
|
|
19227
|
+
}
|
|
19228
|
+
var NO_PROGRESS_LIMIT = 3;
|
|
19229
|
+
var init_termination = () => {};
|
|
19230
|
+
|
|
19137
19231
|
// src/mission/control.ts
|
|
19138
19232
|
import { existsSync as existsSync8, statSync as statSync4 } from "node:fs";
|
|
19139
19233
|
function scopeAllowed(mission) {
|
|
@@ -19203,9 +19297,6 @@ async function executeTask(task, mission, scope) {
|
|
|
19203
19297
|
return { task, ok: false, output: {}, hypotheses: [], graph: { nodes: [], edges: [] }, error: String(e) };
|
|
19204
19298
|
}
|
|
19205
19299
|
}
|
|
19206
|
-
function budgetExhausted(mission) {
|
|
19207
|
-
return mission.budget.used_tasks >= mission.budget.max_tasks;
|
|
19208
|
-
}
|
|
19209
19300
|
async function runMission(mission) {
|
|
19210
19301
|
if (/^https?:\/\//i.test(mission.target) && !scopeAllowed(mission)) {
|
|
19211
19302
|
return saveMission({ ...mission, status: "terminated", termination_reason: "scope_required" });
|
|
@@ -19221,11 +19312,13 @@ async function runMission(mission) {
|
|
|
19221
19312
|
m = saveMission(m);
|
|
19222
19313
|
const scope = m.scope.join(",");
|
|
19223
19314
|
let iterations = 0;
|
|
19315
|
+
let lastProgress = 0;
|
|
19224
19316
|
const maxIterations = m.budget.max_tasks + 10;
|
|
19225
19317
|
while (iterations < maxIterations) {
|
|
19226
19318
|
iterations += 1;
|
|
19227
|
-
|
|
19228
|
-
|
|
19319
|
+
const hard = evaluateTermination(m, { iterations, max_iterations: maxIterations, last_progress_iteration: lastProgress });
|
|
19320
|
+
if (hard.terminate && hard.reason) {
|
|
19321
|
+
m = applyTermination(m, hard);
|
|
19229
19322
|
break;
|
|
19230
19323
|
}
|
|
19231
19324
|
const { ready, blocked } = schedule(m.tasks);
|
|
@@ -19236,8 +19329,13 @@ async function runMission(mission) {
|
|
|
19236
19329
|
m = saveMission(m);
|
|
19237
19330
|
continue;
|
|
19238
19331
|
}
|
|
19239
|
-
|
|
19240
|
-
|
|
19332
|
+
const done = objectiveComplete(m);
|
|
19333
|
+
if (done.terminate) {
|
|
19334
|
+
m = applyTermination(m, done);
|
|
19335
|
+
} else if (blocked.length > 0) {
|
|
19336
|
+
m = applyTermination(m, { terminate: true, reason: "no_progress", detail: `blocked: ${blocked.length} task(s) with failed dependencies` });
|
|
19337
|
+
} else {
|
|
19338
|
+
m = applyTermination(m, { terminate: true, reason: "objective_complete", detail: "no ready tasks remain" });
|
|
19241
19339
|
}
|
|
19242
19340
|
break;
|
|
19243
19341
|
}
|
|
@@ -19286,17 +19384,18 @@ async function runMission(mission) {
|
|
|
19286
19384
|
}
|
|
19287
19385
|
}
|
|
19288
19386
|
}
|
|
19289
|
-
m.budget.
|
|
19387
|
+
m.budget = consumeTasks(m.budget, batch.length, 0);
|
|
19388
|
+
if (results.some((r) => r.ok))
|
|
19389
|
+
lastProgress = iterations;
|
|
19290
19390
|
m = saveMission(m);
|
|
19291
19391
|
}
|
|
19292
19392
|
return finalize2(m);
|
|
19293
19393
|
}
|
|
19294
19394
|
function finalize2(mission) {
|
|
19295
19395
|
let m = mission;
|
|
19296
|
-
if (
|
|
19297
|
-
|
|
19298
|
-
|
|
19299
|
-
m = { ...m, status: "completed", termination_reason: "all tasks resolved" };
|
|
19396
|
+
if (!m.termination_reason) {
|
|
19397
|
+
const done = objectiveComplete(m);
|
|
19398
|
+
m = done.terminate ? applyTermination(m, done) : applyTermination(m, { terminate: true, reason: "max_iterations", detail: "iteration cap reached" });
|
|
19300
19399
|
}
|
|
19301
19400
|
m.coverage.dimensions = {
|
|
19302
19401
|
...m.coverage.dimensions,
|
|
@@ -19307,6 +19406,8 @@ function finalize2(mission) {
|
|
|
19307
19406
|
if (mem.saved > 0) {
|
|
19308
19407
|
m = { ...m, memory_refs: [...m.memory_refs, `persisted:${mem.saved} (${mem.deduped} deduped)`] };
|
|
19309
19408
|
}
|
|
19409
|
+
const budgetSummary = budgetStatus(m.budget).map((d) => `${d.key}=${d.used}/${d.max}`).join(", ");
|
|
19410
|
+
m = { ...m, decision_log: [...m.decision_log, { at: new Date().toISOString(), agent: "budget", decision: `final budget: ${budgetSummary}` }] };
|
|
19310
19411
|
return saveMission(m);
|
|
19311
19412
|
}
|
|
19312
19413
|
async function startMission(target, mode, opts = {}) {
|
|
@@ -19331,6 +19432,7 @@ var init_control = __esm(() => {
|
|
|
19331
19432
|
init_recovery();
|
|
19332
19433
|
init_memory2();
|
|
19333
19434
|
init_router();
|
|
19435
|
+
init_termination();
|
|
19334
19436
|
MODE_PROFILES = {
|
|
19335
19437
|
web: { agents: ["recon", "web", "api", "auth", "validator", "reviewer", "reporter"], verify: true, risk: "medium" },
|
|
19336
19438
|
api: { agents: ["recon", "api", "auth", "validator", "reviewer", "reporter"], verify: true, risk: "medium" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.55",
|
|
4
4
|
"description": "Blitz Strike — a universal MCP security-audit toolbelt. BLITZ sweeps the attack surface, EAGLE-EYE traces source-to-sink, STRIKE verifies live. 57 attack chains, 130-tool catalog, intelligence data layer. One server, every agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|