blitzstrike 1.0.52 → 1.0.54

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,38 @@ 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.54] — 2026-09-13
9
+
10
+ ### Added — Autonomous Security Engine (phase 14: budget controller)
11
+
12
+ - **Budget Controller** (`src/mission/budget.ts`, §22):
13
+ - `budgetStatus()` — tracks time / tasks / requests / tokens / cost with
14
+ per-dimension overrun flags.
15
+ - `budgetOverrun()` — detects overruns and returns a specific reason
16
+ (e.g. `budget overrun: tasks, requests`).
17
+ - `consumeTasks()` / `consumeTokens()` — increment consumption counters.
18
+ - `perTaskOverrun()` — enforces the per-task request budget.
19
+ - Extended the `Budget` type with `max_tokens`, `max_cost_usd`,
20
+ `per_task_budget`, `started_at`, and `used_tokens`/`used_cost_usd`.
21
+ - Mission Control now terminates gracefully on ANY budget dimension overrun
22
+ (not just tasks) and records a final budget snapshot in the decision log.
23
+
24
+ ## [1.0.53] — 2026-09-13
25
+
26
+ ### Added — Autonomous Security Engine (phase 13: model router)
27
+
28
+ - **Model Router** (`src/mission/router.ts`, §21):
29
+ - Pure routing layer — never calls a model. Deterministic by default.
30
+ - `routeTask()` — maps an agent type to a model tier (`large`/`medium`/`small`)
31
+ only when a provider is configured; otherwise routes to `none`.
32
+ - `AGENT_TIER` — §21 mapping (planning/source/review → large; attack execution
33
+ → medium; verification → small).
34
+ - `loadRouterConfig()` — reads `BLITZSTRIKE_MODEL_PROVIDER` / `_LARGE` /
35
+ `_MEDIUM` / `_SMALL` env vars.
36
+ - `routingPlan()` — full plan (all `none` when deterministic).
37
+ - Mission Control records the routing decision (tier) per task and the overall
38
+ routing plan in the mission `decision_log`.
39
+
8
40
  ## [1.0.52] — 2026-09-13
9
41
 
10
42
  ### Added — Autonomous Security Engine (phase 12: browser automation agent)
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: [],
@@ -19085,6 +19091,97 @@ var init_memory2 = __esm(() => {
19085
19091
  init_memory();
19086
19092
  });
19087
19093
 
19094
+ // src/mission/router.ts
19095
+ function isModelConfigured(config) {
19096
+ return Boolean(config?.provider);
19097
+ }
19098
+ function routeTask(agent, config) {
19099
+ if (!isModelConfigured(config)) {
19100
+ return { agent, tier: "none", model: null, deterministic: true };
19101
+ }
19102
+ const tier = AGENT_TIER[agent] ?? "medium";
19103
+ const key = `model_${tier}`;
19104
+ const model = config?.[key] ?? null;
19105
+ return { agent, tier, model, deterministic: model === null };
19106
+ }
19107
+ function loadRouterConfig(env = process.env) {
19108
+ return {
19109
+ provider: env.BLITZSTRIKE_MODEL_PROVIDER || undefined,
19110
+ model_large: env.BLITZSTRIKE_MODEL_LARGE || undefined,
19111
+ model_medium: env.BLITZSTRIKE_MODEL_MEDIUM || undefined,
19112
+ model_small: env.BLITZSTRIKE_MODEL_SMALL || undefined
19113
+ };
19114
+ }
19115
+ function routingPlan(config) {
19116
+ if (!isModelConfigured(config)) {
19117
+ const out = {};
19118
+ for (const k of Object.keys(AGENT_TIER))
19119
+ out[k] = "none";
19120
+ return out;
19121
+ }
19122
+ return { ...AGENT_TIER };
19123
+ }
19124
+ var AGENT_TIER;
19125
+ var init_router = __esm(() => {
19126
+ AGENT_TIER = {
19127
+ commander: "large",
19128
+ source: "large",
19129
+ analysis: "large",
19130
+ reviewer: "large",
19131
+ recon: "medium",
19132
+ web: "medium",
19133
+ api: "medium",
19134
+ auth: "medium",
19135
+ mobile: "medium",
19136
+ cloud: "medium",
19137
+ reporter: "medium",
19138
+ validator: "small",
19139
+ browser: "small"
19140
+ };
19141
+ });
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
+
19088
19185
  // src/mission/control.ts
19089
19186
  import { existsSync as existsSync8, statSync as statSync4 } from "node:fs";
19090
19187
  function scopeAllowed(mission) {
@@ -19155,7 +19252,10 @@ async function executeTask(task, mission, scope) {
19155
19252
  }
19156
19253
  }
19157
19254
  function budgetExhausted(mission) {
19158
- return mission.budget.used_tasks >= mission.budget.max_tasks;
19255
+ return budgetOverrun(mission.budget).overrun;
19256
+ }
19257
+ function budgetReason(mission) {
19258
+ return budgetOverrun(mission.budget).reason ?? "budget_exhausted";
19159
19259
  }
19160
19260
  async function runMission(mission) {
19161
19261
  if (/^https?:\/\//i.test(mission.target) && !scopeAllowed(mission)) {
@@ -19164,6 +19264,11 @@ async function runMission(mission) {
19164
19264
  let m = { ...mission, status: "running" };
19165
19265
  m.tasks = buildInitialTasks(m);
19166
19266
  m.graph = loadEscalationGraph(m.graph);
19267
+ const routerCfg = loadRouterConfig();
19268
+ m.decision_log = [
19269
+ ...m.decision_log,
19270
+ { at: new Date().toISOString(), agent: "router", decision: isModelConfigured(routerCfg) ? `model routing active: ${JSON.stringify(routingPlan(routerCfg))}` : "deterministic (no model provider configured)" }
19271
+ ];
19167
19272
  m = saveMission(m);
19168
19273
  const scope = m.scope.join(",");
19169
19274
  let iterations = 0;
@@ -19171,7 +19276,7 @@ async function runMission(mission) {
19171
19276
  while (iterations < maxIterations) {
19172
19277
  iterations += 1;
19173
19278
  if (budgetExhausted(m)) {
19174
- m = { ...m, termination_reason: "budget_exhausted" };
19279
+ m = { ...m, termination_reason: budgetReason(m) };
19175
19280
  break;
19176
19281
  }
19177
19282
  const { ready, blocked } = schedule(m.tasks);
@@ -19193,20 +19298,22 @@ async function runMission(mission) {
19193
19298
  const results = await Promise.all(batch.map((t) => executeTask(t, m, scope)));
19194
19299
  for (const r of results) {
19195
19300
  const decision = r.ok ? `${r.task.agent_type} completed (${Object.keys(r.output).length} output fields, ${r.hypotheses.length} hypotheses)` : `${r.task.agent_type} failed: ${r.error ?? "unknown"}`;
19301
+ const route = routeTask(r.task.agent_type, routerCfg);
19302
+ const logEntry = (entryDecision) => ({ at: new Date().toISOString(), agent: r.task.agent_type, decision: entryDecision, tier: route.tier });
19196
19303
  if (r.ok) {
19197
19304
  m = {
19198
19305
  ...m,
19199
19306
  tasks: setTask(m.tasks, r.task.task_id, { status: "success", result: r.output }),
19200
19307
  hypotheses: mergeHypotheses(m.hypotheses, r.hypotheses),
19201
19308
  graph: mergeGraphs(m.graph, r.graph),
19202
- decision_log: [...m.decision_log, { at: new Date().toISOString(), agent: r.task.agent_type, decision }]
19309
+ decision_log: [...m.decision_log, logEntry(decision)]
19203
19310
  };
19204
19311
  if (r.task.agent_type === "reporter") {
19205
19312
  m = { ...m, report: r.output };
19206
19313
  }
19207
19314
  } else {
19208
19315
  const rec = recoveryDecision(r.task, r.error ?? "unknown error");
19209
- const stamp = { at: new Date().toISOString(), agent: r.task.agent_type, decision };
19316
+ const stamp = logEntry(decision);
19210
19317
  if (rec.action === "retry") {
19211
19318
  const retried = setTask(m.tasks, r.task.task_id, { status: "retry", error: r.error ?? null });
19212
19319
  m = { ...m, tasks: retried, decision_log: [...m.decision_log, stamp] };
@@ -19230,7 +19337,7 @@ async function runMission(mission) {
19230
19337
  }
19231
19338
  }
19232
19339
  }
19233
- m.budget.used_tasks += batch.length;
19340
+ m.budget = consumeTasks(m.budget, batch.length, 0);
19234
19341
  m = saveMission(m);
19235
19342
  }
19236
19343
  return finalize2(m);
@@ -19238,7 +19345,7 @@ async function runMission(mission) {
19238
19345
  function finalize2(mission) {
19239
19346
  let m = mission;
19240
19347
  if (budgetExhausted(m)) {
19241
- m = { ...m, status: "terminated", termination_reason: "budget_exhausted" };
19348
+ m = { ...m, status: "terminated", termination_reason: budgetReason(m) };
19242
19349
  } else {
19243
19350
  m = { ...m, status: "completed", termination_reason: "all tasks resolved" };
19244
19351
  }
@@ -19251,6 +19358,8 @@ function finalize2(mission) {
19251
19358
  if (mem.saved > 0) {
19252
19359
  m = { ...m, memory_refs: [...m.memory_refs, `persisted:${mem.saved} (${mem.deduped} deduped)`] };
19253
19360
  }
19361
+ const budgetSummary = budgetStatus(m.budget).map((d) => `${d.key}=${d.used}/${d.max}`).join(", ");
19362
+ m = { ...m, decision_log: [...m.decision_log, { at: new Date().toISOString(), agent: "budget", decision: `final budget: ${budgetSummary}` }] };
19254
19363
  return saveMission(m);
19255
19364
  }
19256
19365
  async function startMission(target, mode, opts = {}) {
@@ -19274,6 +19383,7 @@ var init_control = __esm(() => {
19274
19383
  init_coverage();
19275
19384
  init_recovery();
19276
19385
  init_memory2();
19386
+ init_router();
19277
19387
  MODE_PROFILES = {
19278
19388
  web: { agents: ["recon", "web", "api", "auth", "validator", "reviewer", "reporter"], verify: true, risk: "medium" },
19279
19389
  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.52",
3
+ "version": "1.0.54",
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": {