blitzstrike 1.0.86 → 1.0.88
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 +26 -0
- package/dist/index.js +106 -1
- package/package.json +1 -1
- package/skills/bs-orchestrate-engagement.md +28 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ 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.88] — 2026-09-13
|
|
9
|
+
|
|
10
|
+
### Added — hypothesis expansion doctrine (findings → follow-ups)
|
|
11
|
+
|
|
12
|
+
- `bs-orchestrate-engagement` gained a "Hypothesis expansion" section, and the
|
|
13
|
+
MCP `instructions` a matching "HYPOTHESIS EXPANSION" block: from each finding
|
|
14
|
+
or sink, enumerate every follow-up (escalate via list_chains/chain_links,
|
|
15
|
+
chain via chain_links, bypass via bypass_lookup, pivot to new endpoints) —
|
|
16
|
+
and turn each into a verified or rejected verdict before reporting.
|
|
17
|
+
- Codifies the anti-speculation rule: "might/could/possibly" belong in the
|
|
18
|
+
hypothesis list, never in the report; every report line is a verified fact.
|
|
19
|
+
|
|
20
|
+
## [1.0.87] — 2026-09-13
|
|
21
|
+
|
|
22
|
+
### Added — engagement state (deterministic orchestration bookkeeping)
|
|
23
|
+
|
|
24
|
+
- New `engagement.ts` module + 4 MCP tools: `engagement_start` (open an
|
|
25
|
+
engagement), `engagement_phase` (advance the lifecycle phase),
|
|
26
|
+
`engagement_track` (record each hypothesis as pending/confirmed/rejected),
|
|
27
|
+
`engagement_status` (deterministic "am I done?" — returns the exact unmet
|
|
28
|
+
termination criteria instead of a feeling).
|
|
29
|
+
- This is Blitz Strike's own orchestration-state answer: the LLM is the brain;
|
|
30
|
+
these tools are the hands that keep the phase + hypotheses + termination
|
|
31
|
+
state so the LLM never relies on its own memory for progress or completion.
|
|
32
|
+
- `bs-orchestrate-engagement` documents the track-state loop.
|
|
33
|
+
|
|
8
34
|
## [1.0.86] — 2026-09-13
|
|
9
35
|
|
|
10
36
|
### Added — orchestration framework (`orchestration`)
|
package/dist/index.js
CHANGED
|
@@ -31848,6 +31848,75 @@ async function activeScan(target, scope = "", mode = "bug-bounty", allowed = und
|
|
|
31848
31848
|
return result;
|
|
31849
31849
|
}
|
|
31850
31850
|
|
|
31851
|
+
// src/engagement.ts
|
|
31852
|
+
var PHASES = ["scope", "recon", "analyze", "deep", "verify", "report"];
|
|
31853
|
+
var current = null;
|
|
31854
|
+
function ts() {
|
|
31855
|
+
return new Date().toISOString();
|
|
31856
|
+
}
|
|
31857
|
+
function startEngagement(target, kind = "source") {
|
|
31858
|
+
current = {
|
|
31859
|
+
id: `eng-${Date.now().toString(36)}`,
|
|
31860
|
+
target,
|
|
31861
|
+
kind,
|
|
31862
|
+
phase: "scope",
|
|
31863
|
+
startedAt: ts(),
|
|
31864
|
+
hypotheses: []
|
|
31865
|
+
};
|
|
31866
|
+
return { started: true, ...current };
|
|
31867
|
+
}
|
|
31868
|
+
function setPhase(phase) {
|
|
31869
|
+
if (!current)
|
|
31870
|
+
return { found: false, error: "no active engagement — call engagement_start first" };
|
|
31871
|
+
const p = phase.toLowerCase().trim();
|
|
31872
|
+
if (!PHASES.includes(p)) {
|
|
31873
|
+
return { found: false, error: `unknown phase '${phase}'`, valid: PHASES };
|
|
31874
|
+
}
|
|
31875
|
+
current.phase = p;
|
|
31876
|
+
return { found: true, phase: current.phase };
|
|
31877
|
+
}
|
|
31878
|
+
function trackHypothesis(sink, status = "pending") {
|
|
31879
|
+
if (!current)
|
|
31880
|
+
return { found: false, error: "no active engagement — call engagement_start first" };
|
|
31881
|
+
const existing = current.hypotheses.find((h) => h.sink === sink);
|
|
31882
|
+
if (existing)
|
|
31883
|
+
existing.status = status;
|
|
31884
|
+
else
|
|
31885
|
+
current.hypotheses.push({ sink, status });
|
|
31886
|
+
return { found: true, tracked: current.hypotheses.length };
|
|
31887
|
+
}
|
|
31888
|
+
function engagementStatus() {
|
|
31889
|
+
if (!current)
|
|
31890
|
+
return { found: false, error: "no active engagement — call engagement_start first" };
|
|
31891
|
+
const pending = current.hypotheses.filter((h) => h.status === "pending").length;
|
|
31892
|
+
const confirmed = current.hypotheses.filter((h) => h.status === "confirmed").length;
|
|
31893
|
+
const rejected = current.hypotheses.filter((h) => h.status === "rejected").length;
|
|
31894
|
+
const unmet = [];
|
|
31895
|
+
if (current.phase !== "report") {
|
|
31896
|
+
unmet.push(`phase is '${current.phase}', not 'report' — the report has not been emitted`);
|
|
31897
|
+
}
|
|
31898
|
+
if (pending > 0) {
|
|
31899
|
+
unmet.push(`${pending} hypothesis(es) still pending — verify or reject each one`);
|
|
31900
|
+
}
|
|
31901
|
+
if (current.hypotheses.length === 0) {
|
|
31902
|
+
unmet.push("no hypotheses tracked — nothing was verified");
|
|
31903
|
+
}
|
|
31904
|
+
const idx = PHASES.indexOf(current.phase);
|
|
31905
|
+
const next = idx >= PHASES.length - 1 ? "done" : PHASES[idx + 1];
|
|
31906
|
+
return {
|
|
31907
|
+
found: true,
|
|
31908
|
+
id: current.id,
|
|
31909
|
+
target: current.target,
|
|
31910
|
+
kind: current.kind,
|
|
31911
|
+
phase: current.phase,
|
|
31912
|
+
startedAt: current.startedAt,
|
|
31913
|
+
hypotheses: current.hypotheses,
|
|
31914
|
+
counts: { pending, confirmed, rejected, total: current.hypotheses.length },
|
|
31915
|
+
termination: { done: unmet.length === 0, unmet },
|
|
31916
|
+
next_phase: next
|
|
31917
|
+
};
|
|
31918
|
+
}
|
|
31919
|
+
|
|
31851
31920
|
// src/dataflow.ts
|
|
31852
31921
|
init_scanner();
|
|
31853
31922
|
import { readFileSync as readFileSync9 } from "node:fs";
|
|
@@ -53465,7 +53534,7 @@ function createServer() {
|
|
|
53465
53534
|
version: "1.0.0"
|
|
53466
53535
|
}, {
|
|
53467
53536
|
instructions: "Blitz Strike is a universal penetration-testing toolbelt with three tiers — " + "BLITZ (reconnaissance / attack-surface mapping), EAGLE-EYE (source-to-sink " + "analysis), STRIKE (live validation) — plus a canonical evidence-first finding " + "engine and a multi-language taint engine.\\n\\n" + "CORE MENTAL MODEL:\\n" + "- A scanner hit is a HYPOTHESIS, not a finding. Live verification (or data-flow " + " proof) is the verdict. Never report an unverified hit as a vulnerability.\\n" + "- Distinguish REACHABLE from mere PRESENT: a sink is only a finding when tainted " + " input reaches it AND it is not neutralized by a sanitizer and not guarded by an " + " auth gate.\\n\\n" + "HOW TO RUN AN AUDIT (start here):\\n" + "0. ROUTE BY TARGET. Classify the target automatically - no need to ask. A " + " URL - with or without http/https (example.com, 192.168.1.1, host:8080) - " + " is LIVE (scope-gated, then live_recon + active_scan + strike_verify). A " + " filesystem path (exists on disk, or a code extension) is SOURCE audit " + " (blitz_scan + eagle_eye2 + complex_scan/route_scan). If the scheme is " + " missing, prepend https://. run_engagement auto-detects both internally.\\n" + "1. SCOPE FIRST. Call scope_check(target, scope, mode) before ANY active testing. " + " Passive analysis (reading source, fingerprinting public pages) needs no gate; " + " active probing (exploitation, fuzzing, path brute-force) is blocked until scope " + " authorizes it.\\n" + "2. MAP THE SURFACE (BLITZ). For a source tree use blitz_scan (tree) or blitz_file " + " (single file) then enrich_scan to match sinks to escalation chains. For a live " + " URL use live_recon for a full pass (fingerprint/WAF/tech/version/crawler/params/" + " subdomains/API endpoints/intel correlation) — it returns next_steps. active_scan " + " is the lighter gated alternative. Both re-check scope internally.\\n" + "3. TRACE REACHABILITY (EAGLE-EYE). For every sink, determine whether attacker " + " input actually reaches it:\\n" + " - PHP/JS/TS/Python/Java source: taint_file (auto-detects language) or " + " taint_scan/taint_tree (PHP AST). list_languages shows what's supported.\\n" + " - Any language: trace_data_flow (window heuristic) and eagle_eye/eagle_grep " + " (function-scoped view of sinks + auth gates). Beyond taint: route_scan (route confusion / dispatch abuse / batch forwarding) and complex_scan (deserialization->POP, type juggling, mass assignment) catch classes plain taint misses.\\n" + " A sink counts only if tainted input reaches it unsanitized and unguarded.\\n" + "4. VALIDATE (STRIKE). Confirm live with strike_verify using a MARKER plus a " + " NEGATIVE CONTROL plus a BASELINE. The verdict is deterministic: marker " + " reflected AND control inert = confirmed; both reflected = false_positive; " + " marker not reflected = unconfirmed. Feed the verdict to strike_resolve to " + " advance the finding lifecycle (hypothesis->validating->confirmed). Use " + " read_tool_manual for the exploit tool's manual, payload_lookup for payloads, " + " detect_waf/tech_correlation for context.\\n" + "5. RECORD + REPORT. Create a canonical finding with finding_create, advance it " + " through the lifecycle with finding_transition, attach confidence_score, and " + " redact any secrets before persisting. Only report findings that survived " + " verification.\\n\\n" + "SUPPORTING LAYERS:\\n" + "- run_engagement: ONE call does the whole audit (no babysitting). Source path -> " + " full static pipeline + inline report (markdown/JSON/SARIF). URL target -> " + " autonomous live marker sweep + confirmed findings + report. Prefer this first.\\n" + "- list_chains: 57 escalation chains (source -> sink -> impact).\\n" + "- list_languages / taint_file: multi-language taint (PHP, JS/TS, Python, Java).\\n" + "- tool_lookup / ensure_tool / list_tools: 130-tool catalog, auto-install.\\n" + "- read_playbook / list_playbooks: 17 engagement playbooks (web-app, api-security, " + " source-code-audit, active-directory, mobile, external-attack-surface, ...).\\n" + "- skill_lookup / read_skill / list_skills: 38 skills (6 bs-* agent doctrine + " + " technique playbooks: WP/PHP 0-day, AD, malware, mobile, cloud).\\n" + "- read_tool_manual / list_manuals: 317 tool manuals (sqlmap, nmap, commix, ...).\\n" + "- payload_lookup / read_payload: 66 payload collections (sqli, xss, ssti, xxe, lfi).\\n" + "- detect_waf / tech_correlation / cve_correlation / port_correlation: signature " + " + correlation intelligence.\\n" + "- nvd_lookup / fofa_search: CVE + asset reconnaissance (FOFA needs env creds).\\n" + "- remember / memory_lookup / memory_list: persist + recall verified knowledge.\\n\\n" + "RESOURCE MAP (know what to reach for):\\n" + "- ORCHESTRATION = the engagement framework. orchestration() returns the " + " lifecycle (scope->recon->analyze->deep->verify->report, each with " + " entry/exit criteria) + sub-agent team + handoff contract + termination " + " criteria. Load it at engagement start.\\n" + "- TOOL = acts (scan, taint, verify, record, correlate). Call it to DO something.\\n" + "- PLAYBOOK = campaign plan for a target TYPE. read_playbook(web-application) " + " BEFORE a full engagement of that type.\\n" + "- SKILL = procedure doctrine. Load the bs-* skill for the phase you are on " + " (orchestrate, scope, source-audit, verify, review, report); load a technique " + " skill for the specific technique (WP 0-day, AD, malware).\\n" + "- MANUAL = deep reference for ONE tool. read_tool_manual right before using it.\\n" + "- PAYLOAD = attack payloads for a class. payload_lookup before crafting a probe.\\n" + "- INTELLIGENCE = knowledge. list_attack_vectors (34 categories) maps the " + " full surface during RECON; list_chains (36) turns a finding into impact; " + " chain_links composes chains for complex bugs; bypass_lookup when blocked; " + " payload_lookup when crafting a probe. Reach for them at that moment.\\n\\n" + "NO-BABYSITTING (while scanning + testing):\\n" + "- Scan the WHOLE surface before reporting - every file, entry point, and sink. " + " Do not stop after the first hit to ask; keep enumerating the full tree.\\n" + "- Test every hypothesis through its full verification cycle - vary probes on " + " failure, apply bypasses when blocked, keep going. Do not pause to ask.\\n" + "- Persist through partial results: a scan that surfaces few sinks is NOT done - " + " widen the surface, escalate detectors, dig into each reachable sink.\\n" + "- Only surface to the user when: scope/authorization is genuinely unclear, a " + " destructive or DoS action needs explicit consent, or the engagement is complete.\\n\\n" + "PERSISTENCE (complex bugs - do not give up):\\n" + "- On any failure (empty output, blocked probe, failed sub-agent/model), call " + " retry_guidance(signal) for concrete recovery actions before giving up.\\n" + "- One failed probe != not vulnerable. Vary: alternative sinks, other params, " + " encoding/case/whitespace bypasses, different injection context.\\n" + "- Empty output != no findings. Re-run (server may be stale after an upgrade - " + " /reload-mcp), or escalate to route_scan / complex_scan for classes taint " + " misses (POP chains, type juggling, mass assignment, route confusion).\\n" + "- A 'sanitized' sink may not be safe. Challenge the sanitizer: loose == vs ===, " + " missing EXTR_SKIP, int cast truncation, weak regex.\\n" + "- An unconfirmed hypothesis is KEPT as hypothesis - try alternate verification, " + " then let adversarial review decide.\\n" + "- Complex chains (deserialization->POP, LFI->RCE) need the FULL chain traced, " + ` not just the first sink.\\n
|
|
53468
|
-
` + "- When a probe is BLOCKED by a WAF/filter/sanitizer, call bypass_lookup(defense) " + " for concrete techniques (encoding, case, comment insertion, chunked, null byte, " + " double-encoding, verb tampering, header spoofing) and apply them - do not give up.\\n" + "- CHAIN findings: a low-severity finding usually escalates. list_chains and match it " + " to a multi-stage chain (SSRF->metadata, SQLi->RCE, LFI->RCE, upload->shell) instead " + " of stopping at the first vuln.\\n\\n" + "- Complex bugs chain ACROSS chains: after confirming one chain, chain_links " + " shows what follows (SSRF->metadata then default-creds->pivot via stolen IAM). " + " Trace the full composition, not just a single chain.\\n\\n" + "IRON RULES:\\n" + "- Scope first; authorized targets only. Refuse out-of-scope or destructive/DoS work.\\n" + "- A hit is a hypothesis; a verified exploit (or proven data-flow) is the finding.\\n" + "- Prefer the one-call run_engagement path; fall back to granular tools when you " + " need finer control."
|
|
53537
|
+
` + "- When a probe is BLOCKED by a WAF/filter/sanitizer, call bypass_lookup(defense) " + " for concrete techniques (encoding, case, comment insertion, chunked, null byte, " + " double-encoding, verb tampering, header spoofing) and apply them - do not give up.\\n" + "- CHAIN findings: a low-severity finding usually escalates. list_chains and match it " + " to a multi-stage chain (SSRF->metadata, SQLi->RCE, LFI->RCE, upload->shell) instead " + " of stopping at the first vuln.\\n\\n" + "- Complex bugs chain ACROSS chains: after confirming one chain, chain_links " + " shows what follows (SSRF->metadata then default-creds->pivot via stolen IAM). " + " Trace the full composition, not just a single chain.\\n\\n" + "HYPOTHESIS EXPANSION (from each finding, enumerate + verify every follow-up):\\n" + "- A confirmed finding is the START, not the end. From each one, enumerate " + " the follow-ups: ESCALATE (list_chains + chain_links), CHAIN (chain_links " + " from->to), BYPASS (bypass_lookup), PIVOT (new endpoints/sinks now reachable).\\n" + "- Every follow-up (this might happen, this could chain, maybe bypassable) is " + " a HYPOTHESIS, not a finding. Verify EACH via strike_verify (marker + negative " + " control) before it enters the report.\\n" + "- Never report speculation: might / could / possibly live in the " + " hypothesis list, never in the report. Every report line is a verified fact.\\n\\n" + "IRON RULES:\\n" + "- Scope first; authorized targets only. Refuse out-of-scope or destructive/DoS work.\\n" + "- A hit is a hypothesis; a verified exploit (or proven data-flow) is the finding.\\n" + "- Prefer the one-call run_engagement path; fall back to granular tools when you " + " need finer control."
|
|
53469
53538
|
});
|
|
53470
53539
|
server.registerTool("blitz_scan", {
|
|
53471
53540
|
title: "BLITZ scan",
|
|
@@ -53986,6 +54055,42 @@ ${statusLine}` : statusLine },
|
|
|
53986
54055
|
}, async ({ part }) => {
|
|
53987
54056
|
return { content: [{ type: "text", text: JSON.stringify(orchestration(part)) }] };
|
|
53988
54057
|
});
|
|
54058
|
+
server.registerTool("engagement_start", {
|
|
54059
|
+
title: "Start an engagement",
|
|
54060
|
+
description: "ORCHESTRATION: open an engagement and track its state. Call at the start of a full audit so subsequent engagement_phase / engagement_track / engagement_status calls have a state to update. The LLM is the brain; this is the deterministic bookkeeping hand.",
|
|
54061
|
+
inputSchema: {
|
|
54062
|
+
target: string2().describe("The target (source path or live URL)."),
|
|
54063
|
+
kind: _enum(["source", "live"]).optional().describe("source path or live URL. Defaults to source.")
|
|
54064
|
+
}
|
|
54065
|
+
}, async ({ target, kind }) => {
|
|
54066
|
+
return { content: [{ type: "text", text: JSON.stringify(startEngagement(target, kind)) }] };
|
|
54067
|
+
});
|
|
54068
|
+
server.registerTool("engagement_phase", {
|
|
54069
|
+
title: "Advance the engagement phase",
|
|
54070
|
+
description: "ORCHESTRATION: move the engagement to the next lifecycle phase (scope, recon, analyze, deep, verify, report). The phase tracks where the audit is; engagement_status uses it for the termination check.",
|
|
54071
|
+
inputSchema: {
|
|
54072
|
+
phase: string2().describe("One of: scope, recon, analyze, deep, verify, report.")
|
|
54073
|
+
}
|
|
54074
|
+
}, async ({ phase }) => {
|
|
54075
|
+
return { content: [{ type: "text", text: JSON.stringify(setPhase(phase)) }] };
|
|
54076
|
+
});
|
|
54077
|
+
server.registerTool("engagement_track", {
|
|
54078
|
+
title: "Track a hypothesis",
|
|
54079
|
+
description: "ORCHESTRATION: record a hypothesis (a candidate finding keyed by its sink) with its status — pending, confirmed, or rejected. engagement_status derives the termination check from these; an untracked hypothesis is one the LLM has to remember, which is exactly what this avoids.",
|
|
54080
|
+
inputSchema: {
|
|
54081
|
+
sink: string2().describe("The sink / candidate identifier (e.g. 'POST /upload -> eval()')."),
|
|
54082
|
+
status: _enum(["pending", "confirmed", "rejected"]).optional().describe("Defaults to pending.")
|
|
54083
|
+
}
|
|
54084
|
+
}, async ({ sink, status }) => {
|
|
54085
|
+
return { content: [{ type: "text", text: JSON.stringify(trackHypothesis(sink, status)) }] };
|
|
54086
|
+
});
|
|
54087
|
+
server.registerTool("engagement_status", {
|
|
54088
|
+
title: "Engagement status + termination check",
|
|
54089
|
+
description: "ORCHESTRATION: return the current engagement state — phase, hypotheses, counts — and a deterministic termination check (done or the exact unmet criteria). Call this instead of asking yourself 'am I done?'. Also drives blitz_status-style progress.",
|
|
54090
|
+
inputSchema: {}
|
|
54091
|
+
}, async () => {
|
|
54092
|
+
return { content: [{ type: "text", text: JSON.stringify(engagementStatus()) }] };
|
|
54093
|
+
});
|
|
53989
54094
|
server.registerTool("read_payload", {
|
|
53990
54095
|
title: "Read a payload collection",
|
|
53991
54096
|
description: "INTEL: read the full payload collection for a category (or a specific file).",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.88",
|
|
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": {
|
|
@@ -63,6 +63,28 @@ step you are on. Do not improvise a plan the playbook already spells out.
|
|
|
63
63
|
|
|
64
64
|
6. **Report.** Emit `generate_report` + `cvss_score` + `write-report`.
|
|
65
65
|
|
|
66
|
+
## Hypothesis expansion (findings → follow-ups)
|
|
67
|
+
|
|
68
|
+
A confirmed finding is the START, not the end. From each finding or interesting
|
|
69
|
+
sink, enumerate EVERY follow-up as a hypothesis, then verify each one — never
|
|
70
|
+
stop at the first result:
|
|
71
|
+
|
|
72
|
+
1. **Escalate** — `list_chains` + `chain_links`: what does this chain into?
|
|
73
|
+
2. **Chain** — does it compose with another bug (`chain_links` from → to)?
|
|
74
|
+
3. **Bypass** — is there a defense in the way (`bypass_lookup`)? Can the
|
|
75
|
+
sanitizer/WAF/auth-gate be evaded to reach something deeper?
|
|
76
|
+
4. **Pivot** — what other endpoints/sinks/roles are now reachable?
|
|
77
|
+
|
|
78
|
+
Each follow-up ("this might happen", "this could chain", "maybe this is
|
|
79
|
+
bypassable") is a HYPOTHESIS — not a finding. Turn every one of them into a
|
|
80
|
+
verified or rejected verdict before the report:
|
|
81
|
+
|
|
82
|
+
- Confirmed → `finding_create` + `finding_transition` (evidence attached).
|
|
83
|
+
- Not confirmed → rejected, or kept as a hypothesis with `NEEDS_MORE_EVIDENCE`.
|
|
84
|
+
- **Never report speculation.** "Might", "could", "possibly" belong in the
|
|
85
|
+
hypothesis list, never in the report. Every line of the report is a verified
|
|
86
|
+
fact (marker + negative control), not a guess.
|
|
87
|
+
|
|
66
88
|
## Lifecycle, team, and termination
|
|
67
89
|
|
|
68
90
|
Load the full framework with `orchestration` (or focus it:
|
|
@@ -84,6 +106,12 @@ reference — do not improvise the shape of the engagement:
|
|
|
84
106
|
the surface is wider than the scan, hypotheses remain unverified, or a
|
|
85
107
|
low-severity finding hasn't been checked for escalation (`chain_links`).
|
|
86
108
|
|
|
109
|
+
Track the state deterministically so you never rely on your own memory:
|
|
110
|
+
`engagement_start` (open the engagement) → `engagement_phase` (advance the
|
|
111
|
+
phase) → `engagement_track` (record each hypothesis with pending/confirmed/
|
|
112
|
+
rejected) → `engagement_status` (deterministic "am I done?" — returns the exact
|
|
113
|
+
unmet criteria, not a feeling).
|
|
114
|
+
|
|
87
115
|
## Orchestration pattern (native plan + sub-agents)
|
|
88
116
|
|
|
89
117
|
You are the orchestrator — use your platform's native planning and delegation
|