| 307 | } |
| 308 | |
| 309 | function aggregate(results: PerHookResult[]): AggregatedHookResult { |
| 310 | const out: AggregatedHookResult = { blocked: false, stopAll: false } |
| 311 | const contexts: string[] = [] |
| 312 | const blockReasons: string[] = [] |
| 313 | let deny = false |
| 314 | let ask = false |
| 315 | let allow = false |
| 316 | |
| 317 | for (const r of results) { |
| 318 | if (r.stopAll) { |
| 319 | out.stopAll = true |
| 320 | if (r.stopReason && !out.stopReason) out.stopReason = r.stopReason |
| 321 | } |
| 322 | if (r.blocked) { |
| 323 | out.blocked = true |
| 324 | if (r.blockReason) blockReasons.push(r.blockReason) |
| 325 | } |
| 326 | if (r.permissionDecision === "deny") deny = true |
| 327 | else if (r.permissionDecision === "ask") ask = true |
| 328 | else if (r.permissionDecision === "allow") allow = true |
| 329 | if (r.permissionReason && !out.permissionReason) out.permissionReason = r.permissionReason |
| 330 | if (r.updatedInput) out.updatedInput = r.updatedInput // last writer wins |
| 331 | if (r.additionalContext) contexts.push(r.additionalContext) |
| 332 | } |
| 333 | |
| 334 | // Most restrictive permission decision wins: deny > ask > allow. |
| 335 | out.permissionDecision = deny ? "deny" : ask ? "ask" : allow ? "allow" : undefined |
| 336 | if (blockReasons.length) out.blockReason = blockReasons.join("\n") |
| 337 | if (contexts.length) out.additionalContext = capContext(contexts.join("\n\n")) |
| 338 | return out |
| 339 | } |
| 340 | |
| 341 | function mergeContext(existing: string | undefined, addition: string): string { |
| 342 | return existing ? `${existing}\n\n${addition}` : addition |