claude_swarm 1.0.0 → 1.0.2

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 (267) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/release.md +1 -1
  3. data/.claude/hooks/lint-code-files.rb +65 -0
  4. data/.rubocop.yml +22 -2
  5. data/CHANGELOG.md +21 -1
  6. data/CLAUDE.md +1 -1
  7. data/CONTRIBUTING.md +69 -0
  8. data/README.md +27 -2
  9. data/Rakefile +71 -3
  10. data/analyze_coverage.rb +94 -0
  11. data/docs/v2/CHANGELOG.swarm_cli.md +43 -0
  12. data/docs/v2/CHANGELOG.swarm_memory.md +379 -0
  13. data/docs/v2/CHANGELOG.swarm_sdk.md +362 -0
  14. data/docs/v2/README.md +308 -0
  15. data/docs/v2/guides/claude-code-agents.md +262 -0
  16. data/docs/v2/guides/complete-tutorial.md +3088 -0
  17. data/docs/v2/guides/getting-started.md +1456 -0
  18. data/docs/v2/guides/memory-adapters.md +998 -0
  19. data/docs/v2/guides/plugins.md +816 -0
  20. data/docs/v2/guides/quick-start-cli.md +1745 -0
  21. data/docs/v2/guides/rails-integration.md +1902 -0
  22. data/docs/v2/guides/swarm-memory.md +599 -0
  23. data/docs/v2/reference/cli.md +729 -0
  24. data/docs/v2/reference/ruby-dsl.md +2154 -0
  25. data/docs/v2/reference/yaml.md +1835 -0
  26. data/docs-team-swarm.yml +2222 -0
  27. data/examples/learning-assistant/assistant.md +7 -0
  28. data/examples/learning-assistant/example-memories/concept-example.md +90 -0
  29. data/examples/learning-assistant/example-memories/experience-example.md +66 -0
  30. data/examples/learning-assistant/example-memories/fact-example.md +76 -0
  31. data/examples/learning-assistant/example-memories/memory-index.md +78 -0
  32. data/examples/learning-assistant/example-memories/skill-example.md +168 -0
  33. data/examples/learning-assistant/learning_assistant.rb +34 -0
  34. data/examples/learning-assistant/learning_assistant.yml +20 -0
  35. data/examples/v2/dsl/01_basic.rb +44 -0
  36. data/examples/v2/dsl/02_core_parameters.rb +59 -0
  37. data/examples/v2/dsl/03_capabilities.rb +71 -0
  38. data/examples/v2/dsl/04_llm_parameters.rb +56 -0
  39. data/examples/v2/dsl/05_advanced_flags.rb +73 -0
  40. data/examples/v2/dsl/06_permissions.rb +80 -0
  41. data/examples/v2/dsl/07_mcp_server.rb +62 -0
  42. data/examples/v2/dsl/08_swarm_hooks.rb +53 -0
  43. data/examples/v2/dsl/09_agent_hooks.rb +67 -0
  44. data/examples/v2/dsl/10_all_agents_hooks.rb +67 -0
  45. data/examples/v2/dsl/11_delegation.rb +60 -0
  46. data/examples/v2/dsl/12_complete_integration.rb +137 -0
  47. data/examples/v2/file_tools_swarm.yml +102 -0
  48. data/examples/v2/hooks/01_basic_hooks.rb +133 -0
  49. data/examples/v2/hooks/02_usage_tracking.rb +201 -0
  50. data/examples/v2/hooks/03_production_monitoring.rb +429 -0
  51. data/examples/v2/hooks/agent_stop_exit_0.yml +21 -0
  52. data/examples/v2/hooks/agent_stop_exit_1.yml +21 -0
  53. data/examples/v2/hooks/agent_stop_exit_2.yml +26 -0
  54. data/examples/v2/hooks/multiple_hooks_all_pass.yml +37 -0
  55. data/examples/v2/hooks/multiple_hooks_first_fails.yml +37 -0
  56. data/examples/v2/hooks/multiple_hooks_second_fails.yml +37 -0
  57. data/examples/v2/hooks/multiple_hooks_warnings.yml +37 -0
  58. data/examples/v2/hooks/post_tool_use_exit_0.yml +24 -0
  59. data/examples/v2/hooks/post_tool_use_exit_1.yml +24 -0
  60. data/examples/v2/hooks/post_tool_use_exit_2.yml +24 -0
  61. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_0.yml +26 -0
  62. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_1.yml +26 -0
  63. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_2.yml +26 -0
  64. data/examples/v2/hooks/pre_tool_use_exit_0.yml +24 -0
  65. data/examples/v2/hooks/pre_tool_use_exit_1.yml +24 -0
  66. data/examples/v2/hooks/pre_tool_use_exit_2.yml +24 -0
  67. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_0.yml +26 -0
  68. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_1.yml +26 -0
  69. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_2.yml +27 -0
  70. data/examples/v2/hooks/swarm_summary.sh +44 -0
  71. data/examples/v2/hooks/user_prompt_exit_0.yml +21 -0
  72. data/examples/v2/hooks/user_prompt_exit_1.yml +21 -0
  73. data/examples/v2/hooks/user_prompt_exit_2.yml +21 -0
  74. data/examples/v2/hooks/validate_bash.rb +59 -0
  75. data/examples/v2/multi_directory_permissions.yml +221 -0
  76. data/examples/v2/node_context_demo.rb +127 -0
  77. data/examples/v2/node_workflow.rb +173 -0
  78. data/examples/v2/path_resolution_demo.rb +216 -0
  79. data/examples/v2/simple-swarm-v2.rb +90 -0
  80. data/examples/v2/simple-swarm-v2.yml +62 -0
  81. data/examples/v2/swarm.yml +71 -0
  82. data/examples/v2/swarm_with_hooks.yml +61 -0
  83. data/examples/v2/swarm_with_hooks_simple.yml +25 -0
  84. data/examples/v2/think_tool_demo.rb +62 -0
  85. data/exe/swarm +6 -0
  86. data/lib/claude_swarm/claude_mcp_server.rb +0 -6
  87. data/lib/claude_swarm/cli.rb +10 -3
  88. data/lib/claude_swarm/commands/ps.rb +19 -20
  89. data/lib/claude_swarm/commands/show.rb +1 -1
  90. data/lib/claude_swarm/configuration.rb +10 -12
  91. data/lib/claude_swarm/mcp_generator.rb +10 -1
  92. data/lib/claude_swarm/orchestrator.rb +73 -49
  93. data/lib/claude_swarm/system_utils.rb +37 -11
  94. data/lib/claude_swarm/version.rb +1 -1
  95. data/lib/claude_swarm/worktree_manager.rb +1 -0
  96. data/lib/claude_swarm/yaml_loader.rb +22 -0
  97. data/lib/claude_swarm.rb +7 -3
  98. data/lib/swarm_cli/cli.rb +201 -0
  99. data/lib/swarm_cli/command_registry.rb +61 -0
  100. data/lib/swarm_cli/commands/mcp_serve.rb +130 -0
  101. data/lib/swarm_cli/commands/mcp_tools.rb +148 -0
  102. data/lib/swarm_cli/commands/migrate.rb +55 -0
  103. data/lib/swarm_cli/commands/run.rb +173 -0
  104. data/lib/swarm_cli/config_loader.rb +97 -0
  105. data/lib/swarm_cli/formatters/human_formatter.rb +711 -0
  106. data/lib/swarm_cli/formatters/json_formatter.rb +51 -0
  107. data/lib/swarm_cli/interactive_repl.rb +918 -0
  108. data/lib/swarm_cli/mcp_serve_options.rb +44 -0
  109. data/lib/swarm_cli/mcp_tools_options.rb +59 -0
  110. data/lib/swarm_cli/migrate_options.rb +54 -0
  111. data/lib/swarm_cli/migrator.rb +132 -0
  112. data/lib/swarm_cli/options.rb +151 -0
  113. data/lib/swarm_cli/ui/components/agent_badge.rb +33 -0
  114. data/lib/swarm_cli/ui/components/content_block.rb +120 -0
  115. data/lib/swarm_cli/ui/components/divider.rb +57 -0
  116. data/lib/swarm_cli/ui/components/panel.rb +62 -0
  117. data/lib/swarm_cli/ui/components/usage_stats.rb +70 -0
  118. data/lib/swarm_cli/ui/formatters/cost.rb +49 -0
  119. data/lib/swarm_cli/ui/formatters/number.rb +58 -0
  120. data/lib/swarm_cli/ui/formatters/text.rb +77 -0
  121. data/lib/swarm_cli/ui/formatters/time.rb +73 -0
  122. data/lib/swarm_cli/ui/icons.rb +59 -0
  123. data/lib/swarm_cli/ui/renderers/event_renderer.rb +188 -0
  124. data/lib/swarm_cli/ui/state/agent_color_cache.rb +45 -0
  125. data/lib/swarm_cli/ui/state/depth_tracker.rb +40 -0
  126. data/lib/swarm_cli/ui/state/spinner_manager.rb +170 -0
  127. data/lib/swarm_cli/ui/state/usage_tracker.rb +62 -0
  128. data/lib/swarm_cli/version.rb +5 -0
  129. data/lib/swarm_cli.rb +44 -0
  130. data/lib/swarm_memory/adapters/base.rb +141 -0
  131. data/lib/swarm_memory/adapters/filesystem_adapter.rb +845 -0
  132. data/lib/swarm_memory/chat_extension.rb +34 -0
  133. data/lib/swarm_memory/cli/commands.rb +306 -0
  134. data/lib/swarm_memory/core/entry.rb +37 -0
  135. data/lib/swarm_memory/core/frontmatter_parser.rb +108 -0
  136. data/lib/swarm_memory/core/metadata_extractor.rb +68 -0
  137. data/lib/swarm_memory/core/path_normalizer.rb +75 -0
  138. data/lib/swarm_memory/core/semantic_index.rb +244 -0
  139. data/lib/swarm_memory/core/storage.rb +288 -0
  140. data/lib/swarm_memory/core/storage_read_tracker.rb +63 -0
  141. data/lib/swarm_memory/dsl/builder_extension.rb +40 -0
  142. data/lib/swarm_memory/dsl/memory_config.rb +113 -0
  143. data/lib/swarm_memory/embeddings/embedder.rb +36 -0
  144. data/lib/swarm_memory/embeddings/informers_embedder.rb +152 -0
  145. data/lib/swarm_memory/errors.rb +21 -0
  146. data/lib/swarm_memory/integration/cli_registration.rb +30 -0
  147. data/lib/swarm_memory/integration/configuration.rb +43 -0
  148. data/lib/swarm_memory/integration/registration.rb +31 -0
  149. data/lib/swarm_memory/integration/sdk_plugin.rb +531 -0
  150. data/lib/swarm_memory/optimization/analyzer.rb +244 -0
  151. data/lib/swarm_memory/optimization/defragmenter.rb +863 -0
  152. data/lib/swarm_memory/prompts/memory.md.erb +109 -0
  153. data/lib/swarm_memory/prompts/memory_assistant.md.erb +181 -0
  154. data/lib/swarm_memory/prompts/memory_researcher.md.erb +281 -0
  155. data/lib/swarm_memory/prompts/memory_retrieval.md.erb +78 -0
  156. data/lib/swarm_memory/search/semantic_search.rb +112 -0
  157. data/lib/swarm_memory/search/text_search.rb +42 -0
  158. data/lib/swarm_memory/search/text_similarity.rb +80 -0
  159. data/lib/swarm_memory/skills/meta/deep-learning.md +101 -0
  160. data/lib/swarm_memory/skills/meta/deep-learning.yml +14 -0
  161. data/lib/swarm_memory/tools/load_skill.rb +313 -0
  162. data/lib/swarm_memory/tools/memory_defrag.rb +382 -0
  163. data/lib/swarm_memory/tools/memory_delete.rb +99 -0
  164. data/lib/swarm_memory/tools/memory_edit.rb +185 -0
  165. data/lib/swarm_memory/tools/memory_glob.rb +160 -0
  166. data/lib/swarm_memory/tools/memory_grep.rb +247 -0
  167. data/lib/swarm_memory/tools/memory_multi_edit.rb +281 -0
  168. data/lib/swarm_memory/tools/memory_read.rb +123 -0
  169. data/lib/swarm_memory/tools/memory_write.rb +231 -0
  170. data/lib/swarm_memory/utils.rb +50 -0
  171. data/lib/swarm_memory/version.rb +5 -0
  172. data/lib/swarm_memory.rb +166 -0
  173. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +127 -0
  174. data/lib/swarm_sdk/agent/builder.rb +461 -0
  175. data/lib/swarm_sdk/agent/chat/context_tracker.rb +314 -0
  176. data/lib/swarm_sdk/agent/chat/hook_integration.rb +372 -0
  177. data/lib/swarm_sdk/agent/chat/logging_helpers.rb +116 -0
  178. data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +152 -0
  179. data/lib/swarm_sdk/agent/chat.rb +1159 -0
  180. data/lib/swarm_sdk/agent/context.rb +112 -0
  181. data/lib/swarm_sdk/agent/context_manager.rb +309 -0
  182. data/lib/swarm_sdk/agent/definition.rb +556 -0
  183. data/lib/swarm_sdk/claude_code_agent_adapter.rb +205 -0
  184. data/lib/swarm_sdk/configuration.rb +296 -0
  185. data/lib/swarm_sdk/context_compactor/metrics.rb +147 -0
  186. data/lib/swarm_sdk/context_compactor/token_counter.rb +106 -0
  187. data/lib/swarm_sdk/context_compactor.rb +340 -0
  188. data/lib/swarm_sdk/hooks/adapter.rb +359 -0
  189. data/lib/swarm_sdk/hooks/context.rb +197 -0
  190. data/lib/swarm_sdk/hooks/definition.rb +80 -0
  191. data/lib/swarm_sdk/hooks/error.rb +29 -0
  192. data/lib/swarm_sdk/hooks/executor.rb +146 -0
  193. data/lib/swarm_sdk/hooks/registry.rb +147 -0
  194. data/lib/swarm_sdk/hooks/result.rb +150 -0
  195. data/lib/swarm_sdk/hooks/shell_executor.rb +254 -0
  196. data/lib/swarm_sdk/hooks/tool_call.rb +35 -0
  197. data/lib/swarm_sdk/hooks/tool_result.rb +62 -0
  198. data/lib/swarm_sdk/log_collector.rb +51 -0
  199. data/lib/swarm_sdk/log_stream.rb +69 -0
  200. data/lib/swarm_sdk/markdown_parser.rb +75 -0
  201. data/lib/swarm_sdk/model_aliases.json +5 -0
  202. data/lib/swarm_sdk/models.json +1 -0
  203. data/lib/swarm_sdk/models.rb +120 -0
  204. data/lib/swarm_sdk/node/agent_config.rb +49 -0
  205. data/lib/swarm_sdk/node/builder.rb +439 -0
  206. data/lib/swarm_sdk/node/transformer_executor.rb +248 -0
  207. data/lib/swarm_sdk/node_context.rb +170 -0
  208. data/lib/swarm_sdk/node_orchestrator.rb +384 -0
  209. data/lib/swarm_sdk/permissions/config.rb +239 -0
  210. data/lib/swarm_sdk/permissions/error_formatter.rb +121 -0
  211. data/lib/swarm_sdk/permissions/path_matcher.rb +35 -0
  212. data/lib/swarm_sdk/permissions/validator.rb +173 -0
  213. data/lib/swarm_sdk/permissions_builder.rb +122 -0
  214. data/lib/swarm_sdk/plugin.rb +147 -0
  215. data/lib/swarm_sdk/plugin_registry.rb +101 -0
  216. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +243 -0
  217. data/lib/swarm_sdk/providers/openai_with_responses.rb +582 -0
  218. data/lib/swarm_sdk/result.rb +97 -0
  219. data/lib/swarm_sdk/swarm/agent_initializer.rb +334 -0
  220. data/lib/swarm_sdk/swarm/all_agents_builder.rb +140 -0
  221. data/lib/swarm_sdk/swarm/builder.rb +586 -0
  222. data/lib/swarm_sdk/swarm/mcp_configurator.rb +151 -0
  223. data/lib/swarm_sdk/swarm/tool_configurator.rb +419 -0
  224. data/lib/swarm_sdk/swarm.rb +982 -0
  225. data/lib/swarm_sdk/tools/bash.rb +274 -0
  226. data/lib/swarm_sdk/tools/clock.rb +44 -0
  227. data/lib/swarm_sdk/tools/delegate.rb +164 -0
  228. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +83 -0
  229. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +99 -0
  230. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +101 -0
  231. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +78 -0
  232. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +194 -0
  233. data/lib/swarm_sdk/tools/edit.rb +150 -0
  234. data/lib/swarm_sdk/tools/glob.rb +158 -0
  235. data/lib/swarm_sdk/tools/grep.rb +228 -0
  236. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +43 -0
  237. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +163 -0
  238. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +65 -0
  239. data/lib/swarm_sdk/tools/multi_edit.rb +232 -0
  240. data/lib/swarm_sdk/tools/path_resolver.rb +43 -0
  241. data/lib/swarm_sdk/tools/read.rb +251 -0
  242. data/lib/swarm_sdk/tools/registry.rb +93 -0
  243. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +96 -0
  244. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +76 -0
  245. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +91 -0
  246. data/lib/swarm_sdk/tools/stores/read_tracker.rb +61 -0
  247. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +224 -0
  248. data/lib/swarm_sdk/tools/stores/storage.rb +148 -0
  249. data/lib/swarm_sdk/tools/stores/todo_manager.rb +65 -0
  250. data/lib/swarm_sdk/tools/think.rb +95 -0
  251. data/lib/swarm_sdk/tools/todo_write.rb +216 -0
  252. data/lib/swarm_sdk/tools/web_fetch.rb +261 -0
  253. data/lib/swarm_sdk/tools/write.rb +117 -0
  254. data/lib/swarm_sdk/utils.rb +50 -0
  255. data/lib/swarm_sdk/version.rb +5 -0
  256. data/lib/swarm_sdk.rb +157 -0
  257. data/llm.v2.txt +13407 -0
  258. data/rubocop/cop/security/no_reflection_methods.rb +47 -0
  259. data/rubocop/cop/security/no_ruby_llm_logger.rb +32 -0
  260. data/swarm_cli.gemspec +57 -0
  261. data/swarm_memory.gemspec +28 -0
  262. data/swarm_sdk.gemspec +41 -0
  263. data/team.yml +1 -1
  264. data/team_full.yml +1875 -0
  265. data/{team_v2.yml → team_sdk.yml} +121 -52
  266. metadata +247 -4
  267. data/EXAMPLES.md +0 -164
@@ -0,0 +1,429 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Production Monitoring with Hooks - Advanced Level
5
+ #
6
+ # This example demonstrates production-ready patterns with the NEW architecture:
7
+ # - Structured logging (JSON format)
8
+ # - Metrics collection (Prometheus/StatsD compatible)
9
+ # - Error tracking and alerting
10
+ # - Performance monitoring
11
+ # - Audit trails
12
+ # - Cost tracking per user/tenant using agent_step
13
+ #
14
+ # NEW ARCHITECTURE: Usage tracking uses agent_step and agent_stop!
15
+ #
16
+ # Run: bundle exec ruby -Ilib lib/swarm_sdk/examples/hooks/03_production_monitoring.rb
17
+
18
+ require "swarm_sdk"
19
+ require "json"
20
+ require "logger"
21
+
22
+ puts "=" * 80
23
+ puts "PRODUCTION MONITORING EXAMPLE"
24
+ puts "=" * 80
25
+ puts ""
26
+
27
+ # Setup structured logging
28
+ @logger = Logger.new($stdout)
29
+ @logger.level = Logger::INFO
30
+ @logger.formatter = proc do |severity, datetime, _progname, msg|
31
+ {
32
+ timestamp: datetime.utc.iso8601,
33
+ severity: severity,
34
+ message: msg,
35
+ }.to_json + "\n"
36
+ end
37
+
38
+ # Metrics collector (simulates StatsD/Prometheus)
39
+ class MetricsCollector
40
+ def initialize
41
+ @metrics = Hash.new { |h, k| h[k] = [] }
42
+ end
43
+
44
+ def increment(metric, value = 1, tags = {})
45
+ @metrics[metric] << { value: value, tags: tags, timestamp: Time.now }
46
+ end
47
+
48
+ def timing(metric, duration_ms, tags = {})
49
+ @metrics[metric] << { duration_ms: duration_ms, tags: tags, timestamp: Time.now }
50
+ end
51
+
52
+ def gauge(metric, value, tags = {})
53
+ @metrics[metric] << { value: value, tags: tags, timestamp: Time.now }
54
+ end
55
+
56
+ def report
57
+ @metrics.each do |metric, values|
58
+ puts "\n#{metric}:"
59
+ if metric.include?("timing")
60
+ durations = values.map { |v| v[:duration_ms] }
61
+ puts " Count: #{durations.size}"
62
+ puts " Min: #{durations.min}ms"
63
+ puts " Max: #{durations.max}ms"
64
+ puts " Avg: #{(durations.sum / durations.size.to_f).round(2)}ms"
65
+ else
66
+ puts " Count: #{values.size}"
67
+ puts " Total: #{values.map { |v| v[:value] }.sum}"
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ @metrics = MetricsCollector.new
74
+
75
+ # Track operation timings
76
+ @operation_start_times = {}
77
+
78
+ # User/tenant context (for multi-tenant systems)
79
+ USER_ID = ENV["USER_ID"] || "user_123"
80
+ TENANT_ID = ENV["TENANT_ID"] || "tenant_abc"
81
+
82
+ # Cost limits per user
83
+ COST_LIMITS = {
84
+ "user_123" => { daily: 10.0, per_request: 1.0 },
85
+ }.freeze
86
+
87
+ # Track costs per user
88
+ @user_costs = Hash.new { |h, k| h[k] = { daily: 0.0, current_request: 0.0 } }
89
+
90
+ swarm = SwarmSDK.build do
91
+ name("Production Monitor")
92
+ lead(:worker)
93
+
94
+ # ============================================================================
95
+ # 1. REQUEST TRACKING
96
+ # ============================================================================
97
+
98
+ hook(:swarm_start) do |context|
99
+ request_id = SecureRandom.uuid
100
+
101
+ @logger.info({
102
+ event: "request_start",
103
+ request_id: request_id,
104
+ user_id: USER_ID,
105
+ tenant_id: TENANT_ID,
106
+ agent: context.agent_name,
107
+ prompt_length: context.metadata[:prompt]&.length,
108
+ })
109
+
110
+ # Store request_id in metadata for other hooks
111
+ context.metadata[:request_id] = request_id
112
+ context.metadata[:request_start] = Time.now
113
+
114
+ @metrics.increment("swarm.requests", 1, user_id: USER_ID, tenant_id: TENANT_ID)
115
+ end
116
+
117
+ hook(:swarm_stop) do |context|
118
+ duration_ms = ((Time.now - context.metadata[:request_start]) * 1000).round(2)
119
+
120
+ @logger.info({
121
+ event: "request_complete",
122
+ request_id: context.metadata[:request_id],
123
+ user_id: USER_ID,
124
+ tenant_id: TENANT_ID,
125
+ duration_ms: duration_ms,
126
+ success: context.metadata[:success],
127
+ total_cost: context.metadata[:total_cost],
128
+ })
129
+
130
+ @metrics.timing("swarm.request_duration", duration_ms, user_id: USER_ID)
131
+ @metrics.gauge("swarm.request_cost", context.metadata[:total_cost], user_id: USER_ID)
132
+ end
133
+
134
+ # ============================================================================
135
+ # 2. TOOL MONITORING (NO USAGE HERE!)
136
+ # ============================================================================
137
+
138
+ hook(:pre_tool_use) do |context|
139
+ @operation_start_times[context.tool_call.id] = Time.now
140
+
141
+ @logger.info({
142
+ event: "tool_start",
143
+ tool: context.tool_name,
144
+ tool_call_id: context.tool_call.id,
145
+ agent: context.agent_name,
146
+ user_id: USER_ID,
147
+ parameters: context.tool_call.parameters.keys,
148
+ })
149
+
150
+ @metrics.increment("tools.invocations", 1, tool: context.tool_name, agent: context.agent_name)
151
+ end
152
+
153
+ hook(:post_tool_use) do |context|
154
+ # Calculate tool execution time
155
+ start_time = @operation_start_times[context.tool_result.tool_call_id]
156
+ duration_ms = start_time ? ((Time.now - start_time) * 1000).round(2) : 0
157
+
158
+ # NO USAGE DATA HERE! (moved to agent_step)
159
+ log_data = {
160
+ event: "tool_complete",
161
+ tool: context.tool_name,
162
+ tool_call_id: context.tool_result.tool_call_id,
163
+ agent: context.agent_name,
164
+ user_id: USER_ID,
165
+ tenant_id: TENANT_ID,
166
+ success: context.tool_result.success?,
167
+ duration_ms: duration_ms,
168
+ }
169
+
170
+ # Log errors
171
+ unless context.tool_result.success?
172
+ log_data[:error] = context.tool_result.error
173
+ @metrics.increment("tools.errors", 1, tool: context.tool_name)
174
+ end
175
+
176
+ @logger.info(log_data)
177
+ @metrics.timing("tools.duration", duration_ms, tool: context.tool_name)
178
+
179
+ # Cleanup
180
+ @operation_start_times.delete(context.tool_result.tool_call_id)
181
+ end
182
+
183
+ # ============================================================================
184
+ # 3. AGENT STEP MONITORING (USAGE IS HERE!)
185
+ # ============================================================================
186
+
187
+ # NEW: agent_step hook for usage tracking!
188
+ hook(:agent_step) do |context|
189
+ usage = context.metadata[:usage]
190
+
191
+ if usage
192
+ cost = usage[:total_cost]
193
+ tool_calls_count = context.metadata[:tool_calls]&.size || 0
194
+
195
+ @logger.info({
196
+ event: "agent_step",
197
+ agent: context.agent_name,
198
+ user_id: USER_ID,
199
+ tenant_id: TENANT_ID,
200
+ model: context.metadata[:model],
201
+ tokens_total: usage[:total_tokens],
202
+ tokens_input: usage[:input_tokens],
203
+ tokens_output: usage[:output_tokens],
204
+ cost: cost,
205
+ context_usage_percent: usage[:tokens_used_percentage],
206
+ tool_calls_count: tool_calls_count,
207
+ })
208
+
209
+ # Track costs per user
210
+ @user_costs[USER_ID][:daily] += cost
211
+ @user_costs[USER_ID][:current_request] += cost
212
+
213
+ # Check cost limits
214
+ limits = COST_LIMITS[USER_ID]
215
+ if limits
216
+ if @user_costs[USER_ID][:current_request] > limits[:per_request]
217
+ @logger.error({
218
+ event: "cost_limit_exceeded",
219
+ limit_type: "per_request",
220
+ user_id: USER_ID,
221
+ cost: @user_costs[USER_ID][:current_request],
222
+ limit: limits[:per_request],
223
+ })
224
+
225
+ SwarmSDK::Hooks::Result.halt("Request cost limit exceeded: $#{limits[:per_request]}")
226
+ end
227
+
228
+ if @user_costs[USER_ID][:daily] > limits[:daily]
229
+ @logger.error({
230
+ event: "cost_limit_exceeded",
231
+ limit_type: "daily",
232
+ user_id: USER_ID,
233
+ cost: @user_costs[USER_ID][:daily],
234
+ limit: limits[:daily],
235
+ })
236
+
237
+ SwarmSDK::Hooks::Result.halt("Daily cost limit exceeded: $#{limits[:daily]}")
238
+ end
239
+ end
240
+
241
+ # Metrics
242
+ @metrics.gauge("agent.step_cost", cost, agent: context.agent_name, user_id: USER_ID)
243
+ @metrics.gauge("agent.step_tokens", usage[:total_tokens], agent: context.agent_name)
244
+ @metrics.increment("agent.steps", 1, agent: context.agent_name)
245
+ end
246
+ end
247
+
248
+ # ============================================================================
249
+ # 4. CONTEXT MONITORING
250
+ # ============================================================================
251
+
252
+ hook(:context_warning) do |context|
253
+ threshold = context.metadata[:threshold]
254
+ percentage = context.metadata[:percentage]
255
+
256
+ @logger.warn({
257
+ event: "context_warning",
258
+ agent: context.agent_name,
259
+ user_id: USER_ID,
260
+ threshold: threshold,
261
+ percentage: percentage,
262
+ tokens_used: context.metadata[:tokens_used],
263
+ tokens_remaining: context.metadata[:tokens_remaining],
264
+ })
265
+
266
+ @metrics.gauge("context.usage_percent", percentage, agent: context.agent_name)
267
+
268
+ # Alert if critical
269
+ if percentage > 95
270
+ @logger.error({
271
+ event: "context_critical",
272
+ agent: context.agent_name,
273
+ percentage: percentage,
274
+ message: "Context window nearly full - execution may fail",
275
+ })
276
+ end
277
+ end
278
+
279
+ # ============================================================================
280
+ # 5. AGENT LIFECYCLE
281
+ # ============================================================================
282
+
283
+ hook(:agent_stop) do |context|
284
+ usage = context.metadata[:usage]
285
+
286
+ @logger.info({
287
+ event: "agent_complete",
288
+ agent: context.agent_name,
289
+ user_id: USER_ID,
290
+ model: context.metadata[:model],
291
+ finish_reason: context.metadata[:finish_reason],
292
+ tokens_total: usage[:total_tokens],
293
+ cost: usage[:total_cost],
294
+ context_usage: usage[:tokens_used_percentage],
295
+ })
296
+
297
+ # Track final step cost
298
+ if usage
299
+ cost = usage[:total_cost]
300
+ @user_costs[USER_ID][:daily] += cost
301
+ @user_costs[USER_ID][:current_request] += cost
302
+ end
303
+
304
+ @metrics.increment("agent.completions", 1, agent: context.agent_name)
305
+ @metrics.gauge("agent.final_cost", usage[:total_cost], agent: context.agent_name)
306
+ end
307
+
308
+ # ============================================================================
309
+ # 6. ERROR TRACKING
310
+ # ============================================================================
311
+
312
+ # NOTE: In production, you'd integrate with Sentry, Rollbar, etc.
313
+ # This is a simplified example.
314
+
315
+ agent(:worker) do
316
+ description("Worker agent with comprehensive monitoring")
317
+ model("gpt-4o-mini")
318
+ system_prompt("You are a helpful worker. Complete tasks efficiently.")
319
+ tools(:Write)
320
+ end
321
+ end
322
+
323
+ # Execute task
324
+ puts "\n--- Executing Monitored Task ---"
325
+ puts "User: #{USER_ID}"
326
+ puts "Tenant: #{TENANT_ID}"
327
+ puts ""
328
+
329
+ begin
330
+ result = swarm.execute("Create a file called report.txt with a brief summary of monitoring best practices")
331
+
332
+ puts "\n--- Execution Complete ---"
333
+ puts "Success: #{result.success?}"
334
+ puts "Cost: $#{format("%.6f", @user_costs[USER_ID][:current_request])}"
335
+ puts ""
336
+
337
+ # Show metrics report
338
+ puts "\n--- Metrics Report ---"
339
+ @metrics.report
340
+
341
+ # Show user cost tracking
342
+ puts "\n--- User Cost Tracking ---"
343
+ @user_costs.each do |user_id, costs|
344
+ puts "#{user_id}:"
345
+ puts " Current request: $#{format("%.6f", costs[:current_request])}"
346
+ puts " Daily total: $#{format("%.6f", costs[:daily])}"
347
+
348
+ next unless (limits = COST_LIMITS[user_id])
349
+
350
+ puts " Limits:"
351
+ puts " Per request: $#{limits[:per_request]} (#{(costs[:current_request] / limits[:per_request] * 100).round}% used)"
352
+ puts " Daily: $#{limits[:daily]} (#{(costs[:daily] / limits[:daily] * 100).round}% used)"
353
+ end
354
+ rescue => e
355
+ @logger.error({
356
+ event: "execution_error",
357
+ user_id: USER_ID,
358
+ error_class: e.class.name,
359
+ error_message: e.message,
360
+ backtrace: e.backtrace[0..5],
361
+ })
362
+
363
+ puts "\nError: #{e.message}"
364
+ end
365
+
366
+ puts "\n" + "=" * 80
367
+ puts "PRODUCTION PATTERNS DEMONSTRATED"
368
+ puts "=" * 80
369
+ puts <<~SUMMARY
370
+
371
+ 1. **Structured Logging**
372
+ - JSON format for easy parsing
373
+ - Rich context (user_id, tenant_id, request_id)
374
+ - Event-based structure
375
+ - Integration-ready (Elasticsearch, CloudWatch, etc.)
376
+
377
+ 2. **Metrics Collection**
378
+ - Counters (requests, tool invocations, agent steps, errors)
379
+ - Timings (request duration, tool execution time)
380
+ - Gauges (costs, token usage, context percentage)
381
+ - Tags for grouping (user, tenant, tool, agent)
382
+
383
+ 3. **Cost Management (NEW ARCHITECTURE!)**
384
+ - Per-user cost tracking in agent_step hook
385
+ - Daily and per-request limits
386
+ - Automatic limit enforcement
387
+ - Cost attribution (which user/tenant spent what)
388
+
389
+ 4. **Performance Monitoring**
390
+ - Request duration tracking
391
+ - Tool execution timing
392
+ - Agent step tracking
393
+ - Context usage monitoring
394
+
395
+ 5. **Error Tracking**
396
+ - Tool failure logging
397
+ - Context overflow warnings
398
+ - Cost limit violations
399
+ - Integration-ready for Sentry/Rollbar
400
+
401
+ 6. **Audit Trail**
402
+ - Complete request lifecycle tracking
403
+ - Tool parameter logging (keys only for security)
404
+ - User/tenant attribution
405
+ - Compliance-ready logging
406
+
407
+ **KEY ARCHITECTURAL CHANGE:**
408
+ - Usage tracking moved from post_tool_use to agent_step!
409
+ - post_tool_use: Tracks tool execution (duration, success/failure)
410
+ - agent_step: Tracks LLM usage (tokens, costs)
411
+ - One agent_step may trigger multiple tool calls
412
+ - Usage reflects the cost of the LLM response, not individual tools
413
+
414
+ **Integration Points:**
415
+ - Logs → Elasticsearch, CloudWatch, Datadog
416
+ - Metrics → Prometheus, StatsD, Datadog
417
+ - Errors → Sentry, Rollbar, Bugsnag
418
+ - Costs → Internal billing systems
419
+
420
+ **Production Considerations:**
421
+ - Implement log sampling for high-volume systems
422
+ - Use async metrics shipping (avoid blocking hooks)
423
+ - Set up alerting on critical thresholds
424
+ - Configure log retention policies
425
+ - Implement PII scrubbing in logs
426
+
427
+ SUMMARY
428
+
429
+ puts "=" * 80
@@ -0,0 +1,21 @@
1
+ # Test: agent_stop with exit 0
2
+ # Expected: stdout added to log stream, agent stops normally
3
+ # Run: bundle exec exe/swarm run lib/swarm_sdk/examples/hooks/agent_stop_exit_0.yml -p "What is 2+2?"
4
+
5
+ version: 2
6
+ swarm:
7
+ name: "agent_stop Exit 0 Test"
8
+ lead: agent
9
+
10
+ agents:
11
+ agent:
12
+ description: "Test agent"
13
+ model: gpt-5
14
+ provider: openai
15
+ system_prompt: "You are a test agent."
16
+
17
+ hooks:
18
+ agent_stop:
19
+ - type: command
20
+ command: "echo 'Agent completed task successfully - logged for audit' && exit 0"
21
+ timeout: 5
@@ -0,0 +1,21 @@
1
+ # Test: agent_stop with exit 1
2
+ # Expected: stderr added to log stream, agent stops normally (non-blocking error)
3
+ # Run: bundle exec exe/swarm run lib/swarm_sdk/examples/hooks/agent_stop_exit_1.yml -p "What is 2+2?"
4
+
5
+ version: 2
6
+ swarm:
7
+ name: "agent_stop Exit 1 Test"
8
+ lead: agent
9
+
10
+ agents:
11
+ agent:
12
+ description: "Test agent"
13
+ model: gpt-5
14
+ provider: openai
15
+ system_prompt: "You are a test agent."
16
+
17
+ hooks:
18
+ agent_stop:
19
+ - type: command
20
+ command: "echo 'Warning: Failed to save agent metrics to database' >&2 && exit 1"
21
+ timeout: 5
@@ -0,0 +1,26 @@
1
+ # Test: agent_stop with exit 2
2
+ # Expected: Blocks stoppage ONCE, sends stderr to agent, agent continues
3
+ # Note: This uses a Ruby callback (not shell hook) to implement "block once" behavior
4
+ # Run: bundle exec exe/swarm run lib/swarm_sdk/examples/hooks/agent_stop_exit_2.yml -p "What is 2+2?"
5
+
6
+ version: 2
7
+ swarm:
8
+ name: "agent_stop Exit 2 Test"
9
+ lead: agent
10
+
11
+ agents:
12
+ agent:
13
+ description: "Test agent"
14
+ model: gpt-5
15
+ provider: openai
16
+ system_prompt: "You are a test agent."
17
+
18
+ # NOTE: agent_stop exit 2 behavior requires stateful "block once" logic
19
+ # This is better implemented via Ruby callbacks, not shell hooks
20
+ # See docs/hooks.md for agent_stop reprompting examples with callbacks
21
+
22
+ hooks:
23
+ agent_stop:
24
+ - type: command
25
+ command: "echo 'Response quality check: Please provide more detail in your answer' >&2 && exit 2"
26
+ timeout: 5
@@ -0,0 +1,37 @@
1
+ # Test: Multiple hooks for same event - all pass
2
+ # Expected: All three hooks run sequentially, tool executes
3
+ # Run: bundle exec exe/swarm run lib/swarm_sdk/examples/hooks/multiple_hooks_all_pass.yml -p "Run 'ls' command" --verbose
4
+
5
+ version: 2
6
+ swarm:
7
+ name: "Multiple Hooks - All Pass"
8
+ lead: agent
9
+
10
+ agents:
11
+ agent:
12
+ description: "Test agent"
13
+ model: gpt-5
14
+ provider: openai
15
+ system_prompt: "You are a test agent."
16
+ tools:
17
+ - Bash
18
+
19
+ hooks:
20
+ pre_tool_use:
21
+ # Hook 1: Succeeds
22
+ - matcher: "Bash"
23
+ type: command
24
+ command: "echo 'Hook 1: validation passed' && echo 'Hook 1 executed' >> tmp/hooks_log.txt && exit 0"
25
+ timeout: 5
26
+
27
+ # Hook 2: Succeeds
28
+ - matcher: "Bash"
29
+ type: command
30
+ command: "echo 'Hook 2: security check passed' && echo 'Hook 2 executed' >> tmp/hooks_log.txt && exit 0"
31
+ timeout: 5
32
+
33
+ # Hook 3: Succeeds
34
+ - matcher: "Bash"
35
+ type: command
36
+ command: "echo 'Hook 3: audit logged' && echo 'Hook 3 executed' >> tmp/hooks_log.txt && exit 0"
37
+ timeout: 5
@@ -0,0 +1,37 @@
1
+ # Test: Multiple hooks for same event - first one fails with exit 2
2
+ # Expected: First hook blocks, remaining hooks DON'T run
3
+ # Run: bundle exec exe/swarm run lib/swarm_sdk/examples/hooks/multiple_hooks_first_fails.yml -p "Run 'ls' command" --verbose
4
+
5
+ version: 2
6
+ swarm:
7
+ name: "Multiple Hooks - First Fails"
8
+ lead: agent
9
+
10
+ agents:
11
+ agent:
12
+ description: "Test agent"
13
+ model: gpt-5
14
+ provider: openai
15
+ system_prompt: "You are a test agent."
16
+ tools:
17
+ - Bash
18
+
19
+ hooks:
20
+ pre_tool_use:
21
+ # Hook 1: Fails immediately (exit 2)
22
+ - matcher: "Bash"
23
+ type: command
24
+ command: "echo 'Hook 1: BLOCKING' >&2 && echo 'Hook 1 executed' >> tmp/hooks_log.txt && exit 2"
25
+ timeout: 5
26
+
27
+ # Hook 2: Would succeed (but shouldn't run because hook 1 blocks)
28
+ - matcher: "Bash"
29
+ type: command
30
+ command: "echo 'Hook 2: This should NOT run' && echo 'Hook 2 executed' >> tmp/hooks_log.txt && exit 0"
31
+ timeout: 5
32
+
33
+ # Hook 3: Would succeed (but shouldn't run)
34
+ - matcher: "Bash"
35
+ type: command
36
+ command: "echo 'Hook 3: This should NOT run' && echo 'Hook 3 executed' >> tmp/hooks_log.txt && exit 0"
37
+ timeout: 5
@@ -0,0 +1,37 @@
1
+ # Test: Multiple hooks for same event - second one fails with exit 2
2
+ # Expected: First hook runs, second blocks, third doesn't run
3
+ # Run: bundle exec exe/swarm run lib/swarm_sdk/examples/hooks/multiple_hooks_second_fails.yml -p "Run 'ls' command" --verbose
4
+
5
+ version: 2
6
+ swarm:
7
+ name: "Multiple Hooks - Second Fails"
8
+ lead: agent
9
+
10
+ agents:
11
+ agent:
12
+ description: "Test agent"
13
+ model: gpt-5
14
+ provider: openai
15
+ system_prompt: "You are a test agent."
16
+ tools:
17
+ - Bash
18
+
19
+ hooks:
20
+ pre_tool_use:
21
+ # Hook 1: Succeeds (exit 0)
22
+ - matcher: "Bash"
23
+ type: command
24
+ command: "echo 'Hook 1: passed' && echo 'Hook 1 executed' >> tmp/hooks_log.txt && exit 0"
25
+ timeout: 5
26
+
27
+ # Hook 2: Fails (exit 2)
28
+ - matcher: "Bash"
29
+ type: command
30
+ command: "echo 'Hook 2: BLOCKING' >&2 && echo 'Hook 2 executed' >> tmp/hooks_log.txt && exit 2"
31
+ timeout: 5
32
+
33
+ # Hook 3: Would succeed (but shouldn't run because hook 2 blocks)
34
+ - matcher: "Bash"
35
+ type: command
36
+ command: "echo 'Hook 3: This should NOT run' && echo 'Hook 3 executed' >> tmp/hooks_log.txt && exit 0"
37
+ timeout: 5
@@ -0,0 +1,37 @@
1
+ # Test: Multiple hooks with warnings (exit 1)
2
+ # Expected: All hooks run with warnings, execution continues
3
+ # Run: bundle exec exe/swarm run lib/swarm_sdk/examples/hooks/multiple_hooks_warnings.yml -p "Run 'ls' command" --verbose
4
+
5
+ version: 2
6
+ swarm:
7
+ name: "Multiple Hooks - Warnings"
8
+ lead: agent
9
+
10
+ agents:
11
+ agent:
12
+ description: "Test agent"
13
+ model: gpt-5
14
+ provider: openai
15
+ system_prompt: "You are a test agent."
16
+ tools:
17
+ - Bash
18
+
19
+ hooks:
20
+ pre_tool_use:
21
+ # Hook 1: Warning (exit 1)
22
+ - matcher: "Bash"
23
+ type: command
24
+ command: "echo 'Warning 1: Metrics collection unavailable' >&2 && echo 'Hook 1 executed' >> tmp/hooks_log.txt && exit 1"
25
+ timeout: 5
26
+
27
+ # Hook 2: Warning (exit 1)
28
+ - matcher: "Bash"
29
+ type: command
30
+ command: "echo 'Warning 2: Audit log write failed' >&2 && echo 'Hook 2 executed' >> tmp/hooks_log.txt && exit 1"
31
+ timeout: 5
32
+
33
+ # Hook 3: Succeeds (exit 0)
34
+ - matcher: "Bash"
35
+ type: command
36
+ command: "echo 'Hook 3: Final validation passed' && echo 'Hook 3 executed' >> tmp/hooks_log.txt && exit 0"
37
+ timeout: 5
@@ -0,0 +1,24 @@
1
+ # Test: post_tool_use with exit 0
2
+ # Expected: stdout added to log stream, execution continues
3
+ # Run: bundle exec exe/swarm run lib/swarm_sdk/examples/hooks/post_tool_use_exit_0.yml -p "Run 'ls' command"
4
+
5
+ version: 2
6
+ swarm:
7
+ name: "post_tool_use Exit 0 Test"
8
+ lead: agent
9
+
10
+ agents:
11
+ agent:
12
+ description: "Test agent"
13
+ model: gpt-5
14
+ provider: openai
15
+ system_prompt: "You are a test agent."
16
+ tools:
17
+ - Bash
18
+
19
+ hooks:
20
+ post_tool_use:
21
+ - matcher: "Bash"
22
+ type: command
23
+ command: "echo 'Bash tool completed successfully - logged for audit' && exit 0"
24
+ timeout: 5