blitzstrike 1.0.54 → 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 CHANGED
@@ -5,6 +5,21 @@ 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
+
8
23
  ## [1.0.54] — 2026-09-13
9
24
 
10
25
  ### Added — Autonomous Security Engine (phase 14: budget controller)
package/dist/index.js CHANGED
@@ -19182,6 +19182,52 @@ function consumeTasks(budget, tasks, requests) {
19182
19182
  };
19183
19183
  }
19184
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
+
19185
19231
  // src/mission/control.ts
19186
19232
  import { existsSync as existsSync8, statSync as statSync4 } from "node:fs";
19187
19233
  function scopeAllowed(mission) {
@@ -19251,12 +19297,6 @@ async function executeTask(task, mission, scope) {
19251
19297
  return { task, ok: false, output: {}, hypotheses: [], graph: { nodes: [], edges: [] }, error: String(e) };
19252
19298
  }
19253
19299
  }
19254
- function budgetExhausted(mission) {
19255
- return budgetOverrun(mission.budget).overrun;
19256
- }
19257
- function budgetReason(mission) {
19258
- return budgetOverrun(mission.budget).reason ?? "budget_exhausted";
19259
- }
19260
19300
  async function runMission(mission) {
19261
19301
  if (/^https?:\/\//i.test(mission.target) && !scopeAllowed(mission)) {
19262
19302
  return saveMission({ ...mission, status: "terminated", termination_reason: "scope_required" });
@@ -19272,11 +19312,13 @@ async function runMission(mission) {
19272
19312
  m = saveMission(m);
19273
19313
  const scope = m.scope.join(",");
19274
19314
  let iterations = 0;
19315
+ let lastProgress = 0;
19275
19316
  const maxIterations = m.budget.max_tasks + 10;
19276
19317
  while (iterations < maxIterations) {
19277
19318
  iterations += 1;
19278
- if (budgetExhausted(m)) {
19279
- m = { ...m, termination_reason: budgetReason(m) };
19319
+ const hard = evaluateTermination(m, { iterations, max_iterations: maxIterations, last_progress_iteration: lastProgress });
19320
+ if (hard.terminate && hard.reason) {
19321
+ m = applyTermination(m, hard);
19280
19322
  break;
19281
19323
  }
19282
19324
  const { ready, blocked } = schedule(m.tasks);
@@ -19287,8 +19329,13 @@ async function runMission(mission) {
19287
19329
  m = saveMission(m);
19288
19330
  continue;
19289
19331
  }
19290
- if (blocked.length > 0) {
19291
- m = { ...m, termination_reason: `blocked: ${blocked.length} task(s) with failed dependencies` };
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" });
19292
19339
  }
19293
19340
  break;
19294
19341
  }
@@ -19338,16 +19385,17 @@ async function runMission(mission) {
19338
19385
  }
19339
19386
  }
19340
19387
  m.budget = consumeTasks(m.budget, batch.length, 0);
19388
+ if (results.some((r) => r.ok))
19389
+ lastProgress = iterations;
19341
19390
  m = saveMission(m);
19342
19391
  }
19343
19392
  return finalize2(m);
19344
19393
  }
19345
19394
  function finalize2(mission) {
19346
19395
  let m = mission;
19347
- if (budgetExhausted(m)) {
19348
- m = { ...m, status: "terminated", termination_reason: budgetReason(m) };
19349
- } else {
19350
- 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" });
19351
19399
  }
19352
19400
  m.coverage.dimensions = {
19353
19401
  ...m.coverage.dimensions,
@@ -19384,6 +19432,7 @@ var init_control = __esm(() => {
19384
19432
  init_recovery();
19385
19433
  init_memory2();
19386
19434
  init_router();
19435
+ init_termination();
19387
19436
  MODE_PROFILES = {
19388
19437
  web: { agents: ["recon", "web", "api", "auth", "validator", "reviewer", "reporter"], verify: true, risk: "medium" },
19389
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.54",
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": {