0sec-cli 0.22.1 → 0.22.4

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.
Files changed (107) hide show
  1. package/0.js +56 -45
  2. package/README.md +12 -0
  3. package/chunks/{adapt-loop-J6QCZZTB.js → adapt-loop-3CCOWE43.js} +4 -4
  4. package/chunks/{artifact-scraper-MFKT6OI4.js → artifact-scraper-Z4UUIOH3.js} +2 -2
  5. package/chunks/{assumption-mining-646LYSJY.js → assumption-mining-2UAKQB6V.js} +18 -14
  6. package/chunks/{chunk-OHHCGDJ2.js → chunk-2WWAPKAW.js} +126 -1089
  7. package/chunks/{chunk-XDKB2P5X.js → chunk-3BPX75OR.js} +1 -1
  8. package/chunks/chunk-4ZYYX5GY.js +74 -0
  9. package/chunks/{chunk-3E2KALZ5.js → chunk-5JNTMWPK.js} +2 -2
  10. package/chunks/{chunk-CPWA6RWE.js → chunk-5WAJGLCW.js} +3 -3
  11. package/chunks/{chunk-KAU6NLGP.js → chunk-6KFB6AYN.js} +515 -20
  12. package/chunks/{chunk-XA2O4WXA.js → chunk-6WQDXWQC.js} +169 -15
  13. package/chunks/chunk-6YZE552K.js +5603 -0
  14. package/chunks/chunk-BGX53365.js +309 -0
  15. package/chunks/{chunk-X7LAEEUW.js → chunk-C6JPA32H.js} +1 -1
  16. package/chunks/chunk-CF3MNS3O.js +990 -0
  17. package/chunks/{chunk-4VGLO2YS.js → chunk-CPCHLL6A.js} +5 -2
  18. package/chunks/{chunk-ZKQ2I3S2.js → chunk-DPUXXMJW.js} +1 -1
  19. package/chunks/{chunk-XCISCI4T.js → chunk-E3OOBVOK.js} +30 -12
  20. package/chunks/{chunk-4G7OVYJX.js → chunk-E74LXQFY.js} +1 -1
  21. package/chunks/{chunk-OSBPGWTE.js → chunk-ELI3BVIK.js} +3 -3
  22. package/chunks/chunk-FFHRANYC.js +547 -0
  23. package/chunks/{chunk-WSDDGFA6.js → chunk-G2BE2FC7.js} +1 -1
  24. package/chunks/chunk-GQ3DGHWE.js +1659 -0
  25. package/chunks/{chunk-V36HVTUL.js → chunk-GVY3S5E5.js} +21 -18
  26. package/chunks/{chunk-5BTJMINS.js → chunk-H4DH7COV.js} +192 -40
  27. package/chunks/{chunk-HHPWRY2M.js → chunk-H6Q2QAIY.js} +23475 -22747
  28. package/chunks/{chunk-7XCKYNLO.js → chunk-IHPTOTLL.js} +1 -1
  29. package/chunks/chunk-IXAHQICR.js +31 -0
  30. package/chunks/{chunk-TH2LW437.js → chunk-KCA35IL3.js} +2 -190
  31. package/chunks/{chunk-VWITLWXB.js → chunk-L2CCO4KE.js} +282 -34
  32. package/chunks/{chunk-6YNKRUMC.js → chunk-NJLKFC66.js} +321 -5
  33. package/chunks/{chunk-W6UWZFKL.js → chunk-O6KPVYOI.js} +100 -5
  34. package/chunks/{chunk-LGCASF2C.js → chunk-OAS5SAVZ.js} +6767 -2100
  35. package/chunks/chunk-PS5E7LUQ.js +277 -0
  36. package/chunks/{chunk-AV2LG2L7.js → chunk-QVIRWJ45.js} +257 -145
  37. package/chunks/{chunk-TV27OWIW.js → chunk-QWK4R3DB.js} +9 -9
  38. package/chunks/{chunk-WZRVS7SB.js → chunk-RCRTSKQN.js} +1 -1
  39. package/chunks/chunk-RS4MAN3Q.js +73 -0
  40. package/chunks/{chunk-V6GHX34G.js → chunk-RTBZ36BD.js} +8 -274
  41. package/chunks/{chunk-YL6YAGLL.js → chunk-SCWWLABR.js} +1 -1
  42. package/chunks/chunk-VDOWARMJ.js +63 -0
  43. package/chunks/{chunk-IFME676H.js → chunk-WBRGXCU2.js} +79 -18
  44. package/chunks/{chunk-IVQV574Y.js → chunk-WHUAUFSX.js} +1 -1
  45. package/chunks/{chunk-GOEA3NW6.js → chunk-XODDLNBI.js} +1534 -1305
  46. package/chunks/chunk-YAZARMJY.js +355 -0
  47. package/chunks/{chunk-NLH4LMWH.js → chunk-YED25PA4.js} +1 -1
  48. package/chunks/{codex-models-NHQW45SO.js → codex-models-Z43V3427.js} +9 -3
  49. package/chunks/{commands-N6GN5SBF.js → commands-EUGF4FUZ.js} +5842 -4093
  50. package/chunks/{db-5IWEJUYV.js → db-NVAW4WWR.js} +3 -3
  51. package/chunks/{dist-L3XJWWZ6.js → dist-27BNG6WD.js} +193 -112
  52. package/chunks/{dist-FSS7MBDF.js → dist-7RUOKPRJ.js} +12 -2
  53. package/chunks/{dist-KEULE5IQ.js → dist-LNXGATR2.js} +2 -2
  54. package/chunks/{dist-XZB53PS5.js → dist-M2X367VW.js} +2 -2
  55. package/chunks/{dist-BS4CY4RG.js → dist-NG2HT5XU.js} +27 -1
  56. package/chunks/{eval-runner-EMEGF24P.js → eval-runner-6Q3H52SP.js} +13 -9
  57. package/chunks/{exploit-agent-KW4EB7BE.js → exploit-agent-FCCR2HYX.js} +1 -1
  58. package/chunks/{exploit-autoclimb-CXJ2ERRO.js → exploit-autoclimb-X5ZS5FAO.js} +1 -1
  59. package/chunks/{exploit-climb-TJWLY4XC.js → exploit-climb-3JHGEO36.js} +2 -2
  60. package/chunks/{fix-UUKPXCFB.js → fix-NSNPU33B.js} +12 -6
  61. package/chunks/{harness-C5HOKIEV.js → harness-KXQOVIDQ.js} +3 -3
  62. package/chunks/{hunt-scan-KZZ444V2.js → hunt-scan-ELTKGKC4.js} +16 -12
  63. package/chunks/{kernel-primitive-Z77IR7YS.js → kernel-primitive-MT7YZSIG.js} +2 -2
  64. package/chunks/{kernel-vm-runner-JW77P276.js → kernel-vm-runner-YFRHYC6G.js} +2 -2
  65. package/chunks/{native-loop-P6P5GYVJ.js → native-loop-OFJDRHPI.js} +18 -8
  66. package/chunks/orchestrate-DRZ2SVCP.js +62 -0
  67. package/chunks/{prepare-KYHGFWCJ.js → prepare-72GWTRVI.js} +6 -2
  68. package/chunks/{process-N245QIQG.js → process-MJYJ2K5E.js} +2 -1
  69. package/chunks/{run-GAFKBCTN.js → run-WTJCZZHV.js} +1901 -4816
  70. package/chunks/{runtime-H7YBR5IZ.js → runtime-FCKOUB7V.js} +10 -4
  71. package/chunks/{runtime-XFWJTBUO.js → runtime-LCJ4XSKE.js} +5 -1
  72. package/chunks/{scan-stream-BMOCPKQX.js → scan-stream-GT6GKQ3U.js} +7 -1
  73. package/chunks/{scope-QWY4VQZ7.js → scope-E5QZUOOH.js} +2 -2
  74. package/chunks/service-plugins-MSK2EQP6.js +62 -0
  75. package/chunks/{session-store-XIDFGG7K.js → session-store-H3Q7KCYG.js} +6 -4
  76. package/chunks/{variant-candidates-7D4F5E4H.js → variant-candidates-SZKDCOLK.js} +9 -3
  77. package/chunks/{web-recon-prepass-VXXBPDAV.js → web-recon-prepass-IO3GXQMW.js} +11 -6
  78. package/dashboard/apple-touch-icon.png +0 -0
  79. package/dashboard/assets/operations-BvB5Qpmk.css +1 -0
  80. package/dashboard/assets/operations-D8Ii01SV.js +114 -0
  81. package/dashboard/assets/zero-peek-Dzm3MT3X.png +0 -0
  82. package/dashboard/favicon.ico +0 -0
  83. package/dashboard/favicon.svg +10 -0
  84. package/dashboard/index.html +5 -3
  85. package/package.json +1 -1
  86. package/chunks/chunk-6GHSR47F.js +0 -147
  87. package/chunks/chunk-BCSRRQG5.js +0 -3514
  88. package/chunks/chunk-BOUGKEKA.js +0 -193
  89. package/chunks/chunk-GHWODZMR.js +0 -16
  90. package/chunks/orchestrate-DWWUT5RC.js +0 -58
  91. package/dashboard/assets/bot-Bg1Tp9_d.js +0 -1
  92. package/dashboard/assets/dist-B_dDCPbu.js +0 -1
  93. package/dashboard/assets/findings-page-mayGhNx6.js +0 -5
  94. package/dashboard/assets/format--susmHX7.js +0 -1
  95. package/dashboard/assets/input-TuVPR3Zl.js +0 -1
  96. package/dashboard/assets/jsx-runtime-MGseXjj5.js +0 -3
  97. package/dashboard/assets/live-page-HWWHX99U.js +0 -1
  98. package/dashboard/assets/meta-tile-ckHo-w7k.js +0 -1
  99. package/dashboard/assets/operations-BxZ0OoXO.js +0 -10
  100. package/dashboard/assets/operations-CMFVrXEe.css +0 -2
  101. package/dashboard/assets/operations-app-D3d35rgo.js +0 -46
  102. package/dashboard/assets/overview-page-W_4IteEr.js +0 -1
  103. package/dashboard/assets/page-header-Bw9AJgBV.js +0 -1
  104. package/dashboard/assets/scans-page-CgzVXHnn.js +0 -1
  105. package/dashboard/assets/siren-KQj8QAnJ.js +0 -1
  106. package/dashboard/assets/table-8EDklulN.js +0 -1
  107. package/dashboard/assets/tabs-CFHPOwg8.js +0 -1
@@ -0,0 +1,355 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __0CreateRequire } from "node:module";
3
+ const require = __0CreateRequire(import.meta.url);
4
+ import {
5
+ eventBus
6
+ } from "./chunk-4ZYYX5GY.js";
7
+ import {
8
+ estimateCost,
9
+ modelProvider,
10
+ splitCost,
11
+ validateScanPlan
12
+ } from "./chunk-6KFB6AYN.js";
13
+
14
+ // packages/core/dist/agent/cost-ledger.js
15
+ var recordedResults = /* @__PURE__ */ new WeakMap();
16
+ function addRuntimeUsage(ledger, result, model) {
17
+ if (!ledger || recordedResults.get(result) === ledger || !result.usage && !result.usageByModel?.length)
18
+ return;
19
+ if (result.usageByModel?.length) {
20
+ for (const bucket of result.usageByModel)
21
+ ledger.add(bucket.usage, bucket.model);
22
+ } else if (result.usage)
23
+ ledger.add(result.usage, model);
24
+ recordedResults.set(result, ledger);
25
+ }
26
+ var ScanCostLedger = class _ScanCostLedger {
27
+ parent;
28
+ inputTokens = 0;
29
+ outputTokens = 0;
30
+ cachedInputTokens = 0;
31
+ cacheWriteTokens = 0;
32
+ perModel = /* @__PURE__ */ new Map();
33
+ listeners = /* @__PURE__ */ new Set();
34
+ unpricedUsage = false;
35
+ markUnpricedUsage() {
36
+ if (this.unpricedUsage)
37
+ return;
38
+ this.unpricedUsage = true;
39
+ this.parent?.markUnpricedUsage();
40
+ for (const listener of this.listeners)
41
+ listener();
42
+ }
43
+ hasUnpricedUsage() {
44
+ return this.unpricedUsage;
45
+ }
46
+ constructor(parent) {
47
+ this.parent = parent;
48
+ }
49
+ /** Isolated attribution, with the same root budget for every concurrent run. */
50
+ fork() {
51
+ return new _ScanCostLedger(this);
52
+ }
53
+ onSpend(listener) {
54
+ const root = this.parent ?? this;
55
+ if (root !== this)
56
+ return root.onSpend(listener);
57
+ this.listeners.add(listener);
58
+ return () => {
59
+ this.listeners.delete(listener);
60
+ };
61
+ }
62
+ /** Spend attributable to this run, not its siblings or earlier attempts. */
63
+ runCostUsd() {
64
+ return this.costBreakdown()?.costUsd ?? 0;
65
+ }
66
+ /**
67
+ * Fold one turn's token usage into the cumulative total. `model` (the
68
+ * session's pricing model) keys the per-model buckets the cost_breakdown
69
+ * is derived from; sessions without a model pick land in "unknown".
70
+ */
71
+ add(usage, model) {
72
+ this.inputTokens += usage.inputTokens;
73
+ this.outputTokens += usage.outputTokens;
74
+ this.cachedInputTokens += usage.cachedInputTokens ?? 0;
75
+ this.cacheWriteTokens += usage.cacheWriteTokens ?? 0;
76
+ const key = model ?? "unknown";
77
+ const bucket = this.perModel.get(key) ?? { inputTokens: 0, outputTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0 };
78
+ bucket.inputTokens += usage.inputTokens;
79
+ bucket.outputTokens += usage.outputTokens;
80
+ bucket.cachedInputTokens += usage.cachedInputTokens ?? 0;
81
+ bucket.cacheWriteTokens += usage.cacheWriteTokens ?? 0;
82
+ this.perModel.set(key, bucket);
83
+ this.parent?.add(usage, model);
84
+ for (const listener of this.listeners)
85
+ listener();
86
+ }
87
+ /** Cumulative estimated cost (USD) across every contributing session. */
88
+ costUsd(model) {
89
+ return estimateCost({
90
+ inputTokens: this.inputTokens,
91
+ outputTokens: this.outputTokens,
92
+ cachedInputTokens: this.cachedInputTokens,
93
+ cacheWriteTokens: this.cacheWriteTokens
94
+ }, model);
95
+ }
96
+ /** Exact cumulative spend derived from the per-model buckets. */
97
+ totalCostUsd() {
98
+ return this.parent?.totalCostUsd() ?? this.runCostUsd();
99
+ }
100
+ /** Cumulative usage for the final cloud cost_update event. */
101
+ tokenUsage() {
102
+ return {
103
+ inputTokens: this.inputTokens,
104
+ outputTokens: this.outputTokens,
105
+ cachedInputTokens: this.cachedInputTokens,
106
+ ...this.cacheWriteTokens > 0 ? { cacheWriteTokens: this.cacheWriteTokens } : {}
107
+ };
108
+ }
109
+ /**
110
+ * The model every contributing session used, when they all agree (and it
111
+ * is a real model id, not the "unknown" bucket). Undefined for multi-model
112
+ * runs and for runs whose sessions never named one — the caller then
113
+ * leaves the model field to its own resolution.
114
+ */
115
+ soleModel() {
116
+ if (this.perModel.size !== 1)
117
+ return void 0;
118
+ const [key] = this.perModel.keys();
119
+ return key === "unknown" ? void 0 : key;
120
+ }
121
+ /**
122
+ * Aggregate the recorded usage into the cloud's per-(provider, model)
123
+ * cost_breakdown shape — the same aggregation agentic-scanner's
124
+ * emitScanCompleted applies to its per-stage tally, sourced here from the
125
+ * true cross-session cumulative. Zero-usage buckets are skipped so the
126
+ * breakdown stays honest. Returns null when nothing was recorded at all,
127
+ * so callers omit cost_usd / cost_breakdown rather than emit a
128
+ * misleading $0.
129
+ */
130
+ costBreakdown() {
131
+ const acc = /* @__PURE__ */ new Map();
132
+ for (const [model, usage] of this.perModel) {
133
+ if (usage.inputTokens === 0 && usage.outputTokens === 0 && usage.cachedInputTokens === 0 && usage.cacheWriteTokens === 0) {
134
+ continue;
135
+ }
136
+ const priced = model === "unknown" ? void 0 : model;
137
+ const provider = modelProvider(priced);
138
+ const key = `${provider}${model}`;
139
+ const split = splitCost(usage, priced);
140
+ const existing = acc.get(key);
141
+ if (existing) {
142
+ existing.cost_in += split.cost_in;
143
+ existing.cost_out += split.cost_out;
144
+ if (split.cost_cache_read !== void 0) {
145
+ existing.cost_cache_read = (existing.cost_cache_read ?? 0) + split.cost_cache_read;
146
+ }
147
+ } else {
148
+ acc.set(key, {
149
+ provider,
150
+ model,
151
+ cost_in: split.cost_in,
152
+ cost_out: split.cost_out,
153
+ ...split.cost_cache_read !== void 0 ? { cost_cache_read: split.cost_cache_read } : {}
154
+ });
155
+ }
156
+ }
157
+ if (acc.size === 0)
158
+ return null;
159
+ const breakdown = Array.from(acc.values());
160
+ const costUsd = breakdown.reduce((sum, e) => sum + e.cost_in + e.cost_out + (e.cost_cache_read ?? 0), 0);
161
+ return { costUsd, breakdown };
162
+ }
163
+ };
164
+
165
+ // packages/core/dist/scan-plan.js
166
+ import { AsyncLocalStorage } from "node:async_hooks";
167
+ var executionBudget = new AsyncLocalStorage();
168
+ function scanExecutionSignal() {
169
+ return executionBudget.getStore()?.signal;
170
+ }
171
+ function scanExecutionTimeout(requestedMs) {
172
+ const budget = executionBudget.getStore();
173
+ if (!budget)
174
+ return requestedMs;
175
+ budget.signal.throwIfAborted();
176
+ const remainingMs = budget.deadline - Date.now();
177
+ if (remainingMs <= 0)
178
+ throw new ScanBudgetError("time_cap_exceeded", "Scan plan wall-clock limit reached before dispatch.");
179
+ return Math.min(requestedMs, remainingMs);
180
+ }
181
+ var ScanBudgetError = class extends Error {
182
+ status;
183
+ constructor(status, message) {
184
+ super(message);
185
+ this.status = status;
186
+ this.name = "ScanBudgetError";
187
+ }
188
+ };
189
+ function scanGoalPrompt(plan) {
190
+ if (!plan)
191
+ return "";
192
+ const objective = plan.goal === "known-vulnerabilities" ? "Investigate known vulnerabilities: identify exact component versions, match published advisories/CVEs, and reproduce applicability. Prioritize existing advisory and dependency-audit leads; do not spend this run on speculative novel vulnerability hunting." : plan.goal === "misconfigurations" ? "Investigate security misconfigurations: prioritize exposed services/debug features, unsafe defaults, authentication/authorization configuration, secrets, permissions and deployment trust boundaries. Demonstrate the configured exposure rather than claiming hypothetical vulnerabilities." : "Investigate unknown vulnerabilities: prioritize previously unreported logic, authorization, injection and memory-safety flaws. Use known advisories as context, but do not treat a known CVE match alone as completion of this objective.";
193
+ const depth = plan.depth === "quick" ? "Use a focused quick pass: highest-risk surfaces first, minimal non-destructive probes and one independent reproduction per credible lead." : plan.depth === "default" ? "Use a standard pass: cover the relevant attack surfaces and independently reproduce credible leads before reporting." : "Use a deep pass: trace trust boundaries and adjacent variants, investigate alternate paths and independently reproduce credible leads within the shared limits.";
194
+ return `
195
+
196
+ ## Operator scan execution policy
197
+ ${objective}
198
+ ${depth}
199
+ The shared wall-clock and spend limits include discovery, investigation, verification, reporting and all descendants. Preserve partial evidence when a limit interrupts work.
200
+ `;
201
+ }
202
+ function budgetNativeRuntime(runtime, ledger, signal, ceiling, plan) {
203
+ return {
204
+ type: runtime.type,
205
+ outputTokenLimit: runtime.outputTokenLimit,
206
+ isAvailable: () => runtime.isAvailable(),
207
+ resolvedModel: runtime.resolvedModel?.bind(runtime),
208
+ resolvedProvider: runtime.resolvedProvider?.bind(runtime),
209
+ resolvedPricingModel: runtime.resolvedPricingModel?.bind(runtime),
210
+ accessibleModels: runtime.accessibleModels?.bind(runtime),
211
+ reconfigure: runtime.reconfigure?.bind(runtime),
212
+ forkForSubagent: runtime.forkForSubagent ? async (timeout, selection) => budgetNativeRuntime(await runtime.forkForSubagent(timeout, selection), ledger, signal, ceiling, plan) : void 0,
213
+ async executeNative(system, messages, tools, callbacks, callSignal) {
214
+ const effectiveSignal = signal && callSignal ? AbortSignal.any([signal, callSignal]) : signal ?? callSignal;
215
+ effectiveSignal?.throwIfAborted();
216
+ if (ceiling !== void 0 && ledger.totalCostUsd() >= ceiling) {
217
+ throw new ScanBudgetError("cost_ceiling_exceeded", "Shared scan cost ceiling exhausted before model dispatch.");
218
+ }
219
+ let reportedUsage;
220
+ const result = await runtime.executeNative(system + scanGoalPrompt(plan), messages, tools, {
221
+ ...callbacks,
222
+ onUsage: (usage) => {
223
+ reportedUsage = usage;
224
+ callbacks?.onUsage?.(usage);
225
+ }
226
+ }, effectiveSignal);
227
+ if (!result.usage && reportedUsage)
228
+ result.usage = reportedUsage;
229
+ addRuntimeUsage(ledger, result, runtime.resolvedPricingModel?.() ?? runtime.resolvedModel?.());
230
+ if (plan && !result.usage) {
231
+ ledger.markUnpricedUsage();
232
+ throw new ScanBudgetError("failed", "Runtime did not report usage; cannot safely continue a cost-bounded scan plan.");
233
+ }
234
+ return result;
235
+ }
236
+ };
237
+ }
238
+ async function executeScanPlan(options) {
239
+ const { plan } = options;
240
+ validateScanPlan(plan);
241
+ const ledger = (options.ledger ?? new ScanCostLedger()).fork();
242
+ const ceiling = Math.min(options.costCeilingUsd ?? plan.costCapUsd, plan.costCapUsd);
243
+ const startedAt = Date.now();
244
+ const controller = new AbortController();
245
+ const signal = options.signal ? AbortSignal.any([controller.signal, options.signal]) : controller.signal;
246
+ const timer = setTimeout(() => controller.abort(new ScanBudgetError("time_cap_exceeded", "Scan plan wall-clock limit reached.")), plan.timeCapMs);
247
+ const unsubscribe = ledger.onSpend(() => {
248
+ if (ledger.hasUnpricedUsage() && !controller.signal.aborted) {
249
+ controller.abort(new ScanBudgetError("failed", "Runtime did not report usage; plan spend is unknown."));
250
+ }
251
+ if (ledger.totalCostUsd() >= ceiling && !controller.signal.aborted) {
252
+ controller.abort(new ScanBudgetError("cost_ceiling_exceeded", "Scan plan shared cost ceiling reached."));
253
+ }
254
+ });
255
+ const attempts = Array.from({ length: plan.runCount }, (_, index) => ({
256
+ runIndex: index + 1,
257
+ status: "rejected",
258
+ durationMs: 0,
259
+ costUsd: 0
260
+ }));
261
+ const reports = new Array(plan.runCount);
262
+ let next = 0;
263
+ const stopStatus = () => {
264
+ if (ledger.totalCostUsd() >= ceiling)
265
+ return "cost_ceiling_exceeded";
266
+ if (ledger.hasUnpricedUsage())
267
+ return "failed";
268
+ if (Date.now() - startedAt >= plan.timeCapMs)
269
+ return "time_cap_exceeded";
270
+ if (signal.aborted)
271
+ return signal.reason instanceof ScanBudgetError ? signal.reason.status : "cancelled";
272
+ return void 0;
273
+ };
274
+ const worker = async () => {
275
+ while (next < plan.runCount) {
276
+ const index = next++;
277
+ const attempt = attempts[index];
278
+ const stopped = stopStatus();
279
+ if (stopped) {
280
+ attempt.status = stopped;
281
+ attempt.error = "Attempt not dispatched: shared scan plan limit or cancellation.";
282
+ options.onAttempt?.({ runIndex: attempt.runIndex, phase: "settled", outcome: { ...attempt } });
283
+ continue;
284
+ }
285
+ const runLedger = ledger.fork();
286
+ const runStartedAt = Date.now();
287
+ const remainingMs = plan.timeCapMs - (runStartedAt - startedAt);
288
+ options.onAttempt?.({ runIndex: attempt.runIndex, phase: "started" });
289
+ try {
290
+ const report = await executionBudget.run({ signal, deadline: startedAt + plan.timeCapMs }, () => options.dispatch({ runIndex: index + 1, plan: { ...plan, runCount: 1, timeCapMs: remainingMs }, ledger: runLedger, signal, costCeilingUsd: ceiling }));
291
+ reports[index] = report;
292
+ attempt.status = report.costCeilingExceeded ? "cost_ceiling_exceeded" : report.exitReason === "time_cap_exceeded" ? "time_cap_exceeded" : report.exitReason === "cancelled" ? "cancelled" : report.researchFailed || report.executionSuccessful === false || report.exitReason === "failed" ? "failed" : stopStatus() ?? "completed";
293
+ if (attempt.status !== "completed")
294
+ attempt.error = report.error;
295
+ } catch (error) {
296
+ attempt.status = error instanceof ScanBudgetError ? error.status : stopStatus() ?? "rejected";
297
+ attempt.error = error instanceof Error ? error.message : String(error);
298
+ } finally {
299
+ attempt.durationMs = Date.now() - runStartedAt;
300
+ attempt.costUsd = runLedger.hasUnpricedUsage() ? void 0 : runLedger.runCostUsd();
301
+ options.onAttempt?.({ runIndex: attempt.runIndex, phase: "settled", outcome: { ...attempt } });
302
+ }
303
+ }
304
+ };
305
+ try {
306
+ const concurrency = plan.executionMode === "parallel" ? Math.min(3, plan.runCount) : 1;
307
+ await Promise.all(Array.from({ length: concurrency }, worker));
308
+ const fulfilled = reports.filter((report2) => report2 !== void 0);
309
+ const report = { ...fulfilled[0] ?? options.emptyReport() };
310
+ report.findings = reports.flatMap((current, index) => current?.findings.map((finding) => ({ ...finding, runIndex: index + 1 })) ?? []);
311
+ const keys = ["totalAttacks", "totalFindings", "critical", "high", "medium", "low", "info"];
312
+ report.summary = Object.fromEntries(keys.map((key) => [key, fulfilled.reduce((sum, current) => sum + current.summary[key], 0)]));
313
+ const incomplete = attempts.filter((attempt) => attempt.status !== "completed");
314
+ report.warnings = [...fulfilled.flatMap((current) => current.warnings), ...incomplete.map((attempt) => ({ stage: "report", message: `Run ${attempt.runIndex}/${plan.runCount}: ${attempt.status}${attempt.error ? ` \u2014 ${attempt.error}` : ""}. Findings are partial.` }))];
315
+ report.startedAt = new Date(startedAt).toISOString();
316
+ report.completedAt = (/* @__PURE__ */ new Date()).toISOString();
317
+ report.durationMs = Date.now() - startedAt;
318
+ report.plan = plan;
319
+ report.plannedRuns = plan.runCount;
320
+ report.completedRuns = attempts.filter((attempt) => attempt.status === "completed").length;
321
+ report.attempts = attempts;
322
+ report.estimatedCostUsd = ledger.hasUnpricedUsage() ? void 0 : ledger.runCostUsd();
323
+ report.usage = ledger.tokenUsage();
324
+ report.executionSuccessful = incomplete.length === 0;
325
+ report.costCeilingExceeded = attempts.some((attempt) => attempt.status === "cost_ceiling_exceeded");
326
+ report.researchFailed = attempts.some((attempt) => attempt.status === "failed" || attempt.status === "rejected");
327
+ report.error = incomplete.length ? incomplete.find((attempt) => attempt.error)?.error ?? `Scan plan interrupted: ${incomplete[0].status}.` : void 0;
328
+ report.exitReason = report.costCeilingExceeded ? "cost_ceiling_exceeded" : attempts.some((attempt) => attempt.status === "time_cap_exceeded") ? "time_cap_exceeded" : attempts.some((attempt) => attempt.status === "cancelled") ? "cancelled" : incomplete.length ? "partial" : "completed";
329
+ if (options.emitTerminalEvent !== false) {
330
+ const cost = ledger.hasUnpricedUsage() ? null : ledger.costBreakdown();
331
+ eventBus.emit("scan_completed", {
332
+ exit_reason: report.costCeilingExceeded ? "cost_exceeded" : incomplete.length ? "failed" : "completed",
333
+ duration_ms: report.durationMs,
334
+ findings_count: report.findings.length,
335
+ summary: `Scan plan ${report.completedRuns}/${plan.runCount} completed; ${report.exitReason}.`,
336
+ ...cost ? { cost_usd: cost.costUsd, cost_breakdown: cost.breakdown } : {}
337
+ });
338
+ }
339
+ return report;
340
+ } finally {
341
+ clearTimeout(timer);
342
+ unsubscribe();
343
+ }
344
+ }
345
+
346
+ export {
347
+ addRuntimeUsage,
348
+ ScanCostLedger,
349
+ scanExecutionSignal,
350
+ scanExecutionTimeout,
351
+ ScanBudgetError,
352
+ scanGoalPrompt,
353
+ budgetNativeRuntime,
354
+ executeScanPlan
355
+ };
@@ -3,7 +3,7 @@ import { createRequire as __0CreateRequire } from "node:module";
3
3
  const require = __0CreateRequire(import.meta.url);
4
4
  import {
5
5
  homeStateDir
6
- } from "./chunk-KAU6NLGP.js";
6
+ } from "./chunk-6KFB6AYN.js";
7
7
 
8
8
  // packages/core/dist/agent/journal/types.js
9
9
  var JOURNAL_SCHEMA_VERSION = 1;
@@ -5,9 +5,15 @@ import {
5
5
  CodexCatalogRefreshError,
6
6
  loadCodexModelCatalog,
7
7
  parseCodexModels
8
- } from "./chunk-WSDDGFA6.js";
9
- import "./chunk-AV2LG2L7.js";
10
- import "./chunk-KAU6NLGP.js";
8
+ } from "./chunk-G2BE2FC7.js";
9
+ import "./chunk-QVIRWJ45.js";
10
+ import "./chunk-2WWAPKAW.js";
11
+ import "./chunk-RS4MAN3Q.js";
12
+ import "./chunk-UELXHK2Y.js";
13
+ import "./chunk-4ZYYX5GY.js";
14
+ import "./chunk-C6JPA32H.js";
15
+ import "./chunk-5IBCBQEH.js";
16
+ import "./chunk-6KFB6AYN.js";
11
17
  import "./chunk-VIT5ALXF.js";
12
18
  import "./chunk-VCSFCHAJ.js";
13
19
  export {