claude_swarm 1.0.6 → 1.0.7

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +14 -0
  4. data/README.md +336 -1037
  5. data/docs/V1_TO_V2_MIGRATION_GUIDE.md +1120 -0
  6. data/docs/v1/README.md +1195 -0
  7. data/docs/v2/CHANGELOG.swarm_cli.md +22 -0
  8. data/docs/v2/CHANGELOG.swarm_memory.md +20 -0
  9. data/docs/v2/CHANGELOG.swarm_sdk.md +287 -10
  10. data/docs/v2/README.md +32 -6
  11. data/docs/v2/guides/complete-tutorial.md +133 -37
  12. data/docs/v2/guides/composable-swarms.md +1178 -0
  13. data/docs/v2/guides/getting-started.md +42 -1
  14. data/docs/v2/guides/snapshots.md +1498 -0
  15. data/docs/v2/reference/architecture-flow.md +5 -3
  16. data/docs/v2/reference/event_payload_structures.md +249 -12
  17. data/docs/v2/reference/execution-flow.md +1 -1
  18. data/docs/v2/reference/ruby-dsl.md +368 -22
  19. data/docs/v2/reference/yaml.md +314 -63
  20. data/examples/snapshot_demo.rb +119 -0
  21. data/examples/v2/dsl/01_basic.rb +0 -2
  22. data/examples/v2/dsl/02_core_parameters.rb +0 -2
  23. data/examples/v2/dsl/03_capabilities.rb +0 -2
  24. data/examples/v2/dsl/04_llm_parameters.rb +0 -2
  25. data/examples/v2/dsl/05_advanced_flags.rb +0 -3
  26. data/examples/v2/dsl/06_permissions.rb +0 -4
  27. data/examples/v2/dsl/07_mcp_server.rb +0 -2
  28. data/examples/v2/dsl/08_swarm_hooks.rb +0 -2
  29. data/examples/v2/dsl/09_agent_hooks.rb +0 -2
  30. data/examples/v2/dsl/10_all_agents_hooks.rb +0 -3
  31. data/examples/v2/dsl/11_delegation.rb +0 -2
  32. data/examples/v2/dsl/12_complete_integration.rb +2 -6
  33. data/examples/v2/node_context_demo.rb +1 -1
  34. data/examples/v2/node_workflow.rb +2 -4
  35. data/examples/v2/plan_and_execute.rb +157 -0
  36. data/lib/claude_swarm/configuration.rb +28 -4
  37. data/lib/claude_swarm/version.rb +1 -1
  38. data/lib/swarm_cli/formatters/human_formatter.rb +103 -0
  39. data/lib/swarm_cli/interactive_repl.rb +9 -3
  40. data/lib/swarm_cli/version.rb +1 -1
  41. data/lib/swarm_memory/core/storage_read_tracker.rb +51 -14
  42. data/lib/swarm_memory/integration/cli_registration.rb +3 -2
  43. data/lib/swarm_memory/integration/sdk_plugin.rb +11 -5
  44. data/lib/swarm_memory/tools/memory_edit.rb +2 -2
  45. data/lib/swarm_memory/tools/memory_multi_edit.rb +2 -2
  46. data/lib/swarm_memory/tools/memory_read.rb +3 -3
  47. data/lib/swarm_memory/version.rb +1 -1
  48. data/lib/swarm_memory.rb +5 -0
  49. data/lib/swarm_sdk/agent/builder.rb +33 -0
  50. data/lib/swarm_sdk/agent/chat/context_tracker.rb +33 -0
  51. data/lib/swarm_sdk/agent/chat/hook_integration.rb +49 -3
  52. data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +11 -27
  53. data/lib/swarm_sdk/agent/chat.rb +200 -51
  54. data/lib/swarm_sdk/agent/context.rb +6 -2
  55. data/lib/swarm_sdk/agent/context_manager.rb +6 -0
  56. data/lib/swarm_sdk/agent/definition.rb +14 -2
  57. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +180 -0
  58. data/lib/swarm_sdk/configuration.rb +387 -94
  59. data/lib/swarm_sdk/events_to_messages.rb +181 -0
  60. data/lib/swarm_sdk/log_collector.rb +31 -5
  61. data/lib/swarm_sdk/log_stream.rb +37 -8
  62. data/lib/swarm_sdk/model_aliases.json +4 -1
  63. data/lib/swarm_sdk/node/agent_config.rb +33 -8
  64. data/lib/swarm_sdk/node/builder.rb +39 -18
  65. data/lib/swarm_sdk/node_orchestrator.rb +293 -26
  66. data/lib/swarm_sdk/proc_helpers.rb +53 -0
  67. data/lib/swarm_sdk/providers/openai_with_responses.rb +22 -15
  68. data/lib/swarm_sdk/restore_result.rb +65 -0
  69. data/lib/swarm_sdk/snapshot.rb +156 -0
  70. data/lib/swarm_sdk/snapshot_from_events.rb +386 -0
  71. data/lib/swarm_sdk/state_restorer.rb +491 -0
  72. data/lib/swarm_sdk/state_snapshot.rb +369 -0
  73. data/lib/swarm_sdk/swarm/agent_initializer.rb +360 -55
  74. data/lib/swarm_sdk/swarm/all_agents_builder.rb +28 -1
  75. data/lib/swarm_sdk/swarm/builder.rb +208 -12
  76. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +67 -0
  77. data/lib/swarm_sdk/swarm/tool_configurator.rb +46 -11
  78. data/lib/swarm_sdk/swarm.rb +338 -42
  79. data/lib/swarm_sdk/swarm_loader.rb +145 -0
  80. data/lib/swarm_sdk/swarm_registry.rb +136 -0
  81. data/lib/swarm_sdk/tools/delegate.rb +92 -7
  82. data/lib/swarm_sdk/tools/read.rb +17 -5
  83. data/lib/swarm_sdk/tools/stores/read_tracker.rb +47 -12
  84. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +45 -0
  85. data/lib/swarm_sdk/utils.rb +18 -0
  86. data/lib/swarm_sdk/validation_result.rb +33 -0
  87. data/lib/swarm_sdk/version.rb +1 -1
  88. data/lib/swarm_sdk.rb +40 -8
  89. data/swarm_cli.gemspec +1 -1
  90. data/swarm_memory.gemspec +2 -2
  91. data/swarm_sdk.gemspec +2 -2
  92. metadata +21 -13
  93. data/examples/learning-assistant/assistant.md +0 -7
  94. data/examples/learning-assistant/example-memories/concept-example.md +0 -90
  95. data/examples/learning-assistant/example-memories/experience-example.md +0 -66
  96. data/examples/learning-assistant/example-memories/fact-example.md +0 -76
  97. data/examples/learning-assistant/example-memories/memory-index.md +0 -78
  98. data/examples/learning-assistant/example-memories/skill-example.md +0 -168
  99. data/examples/learning-assistant/learning_assistant.rb +0 -34
  100. data/examples/learning-assistant/learning_assistant.yml +0 -20
  101. data/lib/swarm_sdk/mcp.rb +0 -16
  102. data/llm.v2.txt +0 -13407
  103. /data/docs/v2/guides/{MEMORY_DEFRAG_GUIDE.md → memory-defrag-guide.md} +0 -0
  104. /data/{llms.txt → llms.claude-swarm.txt} +0 -0
@@ -49,12 +49,23 @@ SwarmSDK.configure {|config| ... } → void
49
49
  - `webfetch_model` (String): Model name for WebFetch tool (e.g., "claude-3-5-haiku-20241022")
50
50
  - `webfetch_base_url` (String, optional): Custom base URL for the provider
51
51
  - `webfetch_max_tokens` (Integer): Maximum tokens for WebFetch LLM responses (default: 4096)
52
+ - `allow_filesystem_tools` (Boolean): Enable/disable filesystem tools globally (default: true)
52
53
 
53
54
  **Description:**
54
- Global configuration that applies to all swarms. Currently used to configure the WebFetch tool's LLM processing behavior.
55
+ Global configuration that applies to all swarms.
55
56
 
57
+ **WebFetch Configuration:**
56
58
  When `webfetch_provider` and `webfetch_model` are set, the WebFetch tool will process fetched web content using the configured LLM. Without this configuration, WebFetch returns raw markdown.
57
59
 
60
+ **Filesystem Tools Security:**
61
+ When `allow_filesystem_tools` is set to `false`, all filesystem tools (Read, Write, Edit, MultiEdit, Grep, Glob, Bash) are globally disabled across all swarms. This is a security boundary that cannot be overridden by swarm configurations. Non-filesystem tools (Scratchpad tools, Memory tools) continue to work normally.
62
+
63
+ Use cases:
64
+ - **Multi-tenant platforms**: Prevent user-provided swarms from accessing the filesystem
65
+ - **Sandboxed execution**: Containerized environments with read-only filesystems
66
+ - **Compliance requirements**: Data analysis workloads that forbid file operations
67
+ - **Production security**: CI/CD pipelines where agents should only interact via APIs
68
+
58
69
  **Example:**
59
70
  ```ruby
60
71
  # Configure WebFetch to use Anthropic's Claude Haiku
@@ -71,7 +82,18 @@ SwarmSDK.configure do |config|
71
82
  config.webfetch_base_url = "http://localhost:11434"
72
83
  end
73
84
 
74
- # Reset to defaults (disables WebFetch LLM processing)
85
+ # Disable filesystem tools globally (security)
86
+ SwarmSDK.configure do |config|
87
+ config.allow_filesystem_tools = false
88
+ end
89
+
90
+ # Can also set directly
91
+ SwarmSDK.settings.allow_filesystem_tools = false
92
+
93
+ # Or via environment variable
94
+ ENV['SWARM_SDK_ALLOW_FILESYSTEM_TOOLS'] = 'false'
95
+
96
+ # Reset to defaults (disables WebFetch LLM processing, enables filesystem tools)
75
97
  SwarmSDK.reset_settings!
76
98
  ```
77
99
 
@@ -83,10 +105,11 @@ Build a swarm using the DSL.
83
105
 
84
106
  **Signature:**
85
107
  ```ruby
86
- SwarmSDK.build(&block) → Swarm | NodeOrchestrator
108
+ SwarmSDK.build(allow_filesystem_tools: nil, &block) → Swarm | NodeOrchestrator
87
109
  ```
88
110
 
89
111
  **Parameters:**
112
+ - `allow_filesystem_tools` (Boolean, optional): Override global setting to enable/disable filesystem tools (default: nil, uses global setting)
90
113
  - `block` (required): Configuration block
91
114
 
92
115
  **Returns:**
@@ -104,6 +127,17 @@ swarm = SwarmSDK.build do
104
127
  tools :Read, :Write, :Bash
105
128
  end
106
129
  end
130
+
131
+ # Override global setting to disable filesystem tools for this specific swarm
132
+ swarm = SwarmSDK.build(allow_filesystem_tools: false) do
133
+ name "Restricted Analyst"
134
+ lead :analyst
135
+
136
+ agent :analyst do
137
+ model "gpt-5"
138
+ tools :Think, :WebFetch # Only non-filesystem tools
139
+ end
140
+ end
107
141
  ```
108
142
 
109
143
  ---
@@ -126,6 +160,39 @@ Updates RubyLLM's model registry to ensure latest model information is available
126
160
 
127
161
  Methods available in the `SwarmSDK.build` block.
128
162
 
163
+ ### id
164
+
165
+ Set the swarm ID (unique identifier).
166
+
167
+ **Signature:**
168
+ ```ruby
169
+ id(swarm_id) → void
170
+ ```
171
+
172
+ **Parameters:**
173
+ - `swarm_id` (String, required): Unique swarm identifier
174
+
175
+ **When required:**
176
+ - **Required** when using composable swarms (`swarms {}` block)
177
+ - **Optional** otherwise (auto-generates if omitted)
178
+
179
+ **Description:**
180
+ Sets a unique identifier for the swarm. This ID is used for:
181
+ - Hierarchical swarm tracking in events (`swarm_id`, `parent_swarm_id`)
182
+ - Building parent/child relationships in composable swarms
183
+ - Identifying swarms in logs and monitoring
184
+
185
+ If omitted and not using composable swarms, an ID is auto-generated from the swarm name with a random suffix (e.g., `"development_team_a3f2b1c8"`).
186
+
187
+ **Example:**
188
+ ```ruby
189
+ id "development_team"
190
+ id "code_review_v2"
191
+ id "main_app"
192
+ ```
193
+
194
+ ---
195
+
129
196
  ### name
130
197
 
131
198
  Set the swarm name.
@@ -146,6 +213,161 @@ name "Code Review Swarm"
146
213
 
147
214
  ---
148
215
 
216
+ ### swarms
217
+
218
+ Register external swarms for composable swarms feature.
219
+
220
+ **Signature:**
221
+ ```ruby
222
+ swarms(&block) → void
223
+ ```
224
+
225
+ **Parameters:**
226
+ - `block` (required): Block containing `register()` calls
227
+
228
+ **Description:**
229
+ Enables composable swarms - the ability to delegate to other swarms as if they were agents. Each registered swarm can be referenced in `delegates_to` just like regular agents.
230
+
231
+ **A swarm IS an agent** - delegating to a child swarm is identical to delegating to an agent. The child swarm's lead agent serves as its public interface.
232
+
233
+ **Three registration methods:**
234
+ 1. **File path**: Load from .rb or .yml file
235
+ 2. **YAML string**: Pass YAML content directly
236
+ 3. **Inline block**: Define swarm inline with Ruby DSL
237
+
238
+ **Example:**
239
+ ```ruby
240
+ swarms do
241
+ # Method 1: From file
242
+ register "code_review", file: "./swarms/code_review.rb"
243
+
244
+ # Method 2: From YAML string
245
+ yaml_content = File.read("testing.yml")
246
+ register "testing", yaml: yaml_content, keep_context: false
247
+
248
+ # Method 3: Inline block
249
+ register "deployment" do
250
+ id "deploy_team"
251
+ name "Deployment Team"
252
+ lead :deployer
253
+
254
+ agent :deployer do
255
+ model "gpt-4o"
256
+ system "You handle deployments"
257
+ end
258
+ end
259
+ end
260
+ ```
261
+
262
+ ---
263
+
264
+ ### swarms.register
265
+
266
+ Register a sub-swarm within the `swarms {}` block.
267
+
268
+ **Signature:**
269
+ ```ruby
270
+ register(name, file: nil, yaml: nil, keep_context: true, &block) → void
271
+ ```
272
+
273
+ **Parameters:**
274
+ - `name` (String, required): Registration name for the swarm
275
+ - `file` (String, optional): Path to swarm file (.rb or .yml)
276
+ - `yaml` (String, optional): YAML configuration content
277
+ - `keep_context` (Boolean, optional): Preserve conversation between calls (default: true)
278
+ - `block` (Proc, optional): Inline swarm definition
279
+
280
+ **Rules:**
281
+ - Exactly ONE of `file:`, `yaml:`, or `&block` must be provided
282
+ - Cannot provide multiple sources (raises ArgumentError)
283
+ - Must provide at least one source (raises ArgumentError)
284
+
285
+ **Description:**
286
+ Registers a sub-swarm that can be delegated to like a regular agent. The swarm is lazy-loaded on first access and cached for reuse.
287
+
288
+ **Keep Context:**
289
+ - `keep_context: true` (default): Swarm maintains conversation history across delegations
290
+ - `keep_context: false`: Swarm context is reset after each delegation completes
291
+
292
+ **Hierarchical IDs:**
293
+ Sub-swarms automatically get hierarchical IDs: `"parent_id/registration_name"`
294
+
295
+ **Example - From File:**
296
+ ```ruby
297
+ swarms do
298
+ register "code_review", file: "./swarms/code_review.rb"
299
+ register "testing", file: "./swarms/testing.yml", keep_context: false
300
+ end
301
+ ```
302
+
303
+ **Example - From YAML String:**
304
+ ```ruby
305
+ # Load YAML from anywhere (API, database, etc.)
306
+ yaml_config = HTTP.get("https://api.example.com/swarms/testing.yml").body
307
+
308
+ swarms do
309
+ register "testing", yaml: yaml_config, keep_context: false
310
+ end
311
+ ```
312
+
313
+ **Example - Inline Block:**
314
+ ```ruby
315
+ swarms do
316
+ register "testing", keep_context: false do
317
+ id "testing_team"
318
+ name "Testing Team"
319
+ lead :tester
320
+
321
+ agent :tester do
322
+ model "gpt-4o-mini"
323
+ description "Test specialist"
324
+ system "You write and run tests"
325
+ tools :Think, :Bash
326
+ end
327
+
328
+ agent :qa do
329
+ model "gpt-4o"
330
+ description "QA specialist"
331
+ system "You validate quality"
332
+ end
333
+ end
334
+ end
335
+ ```
336
+
337
+ **Example - Mixing Sources:**
338
+ ```ruby
339
+ swarms do
340
+ # From file
341
+ register "code_review", file: "./swarms/code_review.rb"
342
+
343
+ # From YAML string (fetched dynamically)
344
+ testing_yaml = SwarmConfig.find("testing").yaml_content
345
+ register "testing", yaml: testing_yaml
346
+
347
+ # Inline definition
348
+ register "deployment" do
349
+ id "deploy_team"
350
+ name "Deployment"
351
+ lead :deployer
352
+ agent :deployer do
353
+ model "gpt-4o"
354
+ system "Deploy code"
355
+ end
356
+ end
357
+ end
358
+ ```
359
+
360
+ **Usage with Delegation:**
361
+ ```ruby
362
+ agent :backend do
363
+ # Delegate to both local agents and registered swarms
364
+ delegates_to "database", "code_review", "testing"
365
+ # backend can delegate to database (local agent) and code_review/testing (swarms)
366
+ end
367
+ ```
368
+
369
+ ---
370
+
149
371
  ### lead
150
372
 
151
373
  Set the lead agent (entry point for execution).
@@ -166,30 +388,58 @@ lead :coordinator
166
388
 
167
389
  ---
168
390
 
169
- ### use_scratchpad
391
+ ### scratchpad
170
392
 
171
- Enable or disable shared scratchpad tools for all agents.
393
+ Configure scratchpad mode for the swarm or workflow.
172
394
 
173
395
  **Signature:**
174
396
  ```ruby
175
- use_scratchpad(enabled) → void
397
+ scratchpad(mode) → void
176
398
  ```
177
399
 
178
400
  **Parameters:**
179
- - `enabled` (Boolean, required): Whether to enable scratchpad tools
401
+ - `mode` (Symbol, required): Scratchpad mode
402
+ - For regular Swarms: `:enabled` or `:disabled`
403
+ - For NodeOrchestrator (workflows with nodes): `:enabled`, `:per_node`, or `:disabled`
180
404
 
181
- **Default:** `true` (scratchpad tools enabled)
405
+ **Default:** `:disabled`
182
406
 
183
407
  **Description:**
184
- Controls whether agents have access to scratchpad tools (ScratchpadWrite, ScratchpadRead, ScratchpadList). Scratchpad is volatile (in-memory only) and shared across all agents in the swarm.
408
+ Controls scratchpad availability and sharing behavior:
185
409
 
186
- **Example:**
410
+ - **`:enabled`**: Scratchpad tools available (ScratchpadWrite, ScratchpadRead, ScratchpadList)
411
+ - Regular Swarm: All agents share one scratchpad
412
+ - NodeOrchestrator: All nodes share one scratchpad across the workflow
413
+ - **`:per_node`**: (NodeOrchestrator only) Each node gets isolated scratchpad storage
414
+ - **`:disabled`**: No scratchpad tools available
415
+
416
+ Scratchpad is volatile (in-memory only) and provides temporary storage for cross-agent or cross-node communication.
417
+
418
+ **Examples:**
187
419
  ```ruby
188
- # Enable scratchpad (default)
189
- use_scratchpad true
420
+ # Regular Swarm - enable or disable
421
+ SwarmSDK.build do
422
+ scratchpad :enabled # Enable scratchpad
423
+ scratchpad :disabled # Disable scratchpad (default)
424
+ end
425
+
426
+ # NodeOrchestrator - shared across nodes
427
+ SwarmSDK.build do
428
+ scratchpad :enabled # All nodes share one scratchpad
190
429
 
191
- # Disable scratchpad
192
- use_scratchpad false
430
+ node :planning { agent(:planner) }
431
+ node :implementation { agent(:coder) }
432
+ start_node :planning
433
+ end
434
+
435
+ # NodeOrchestrator - isolated per node
436
+ SwarmSDK.build do
437
+ scratchpad :per_node # Each node gets its own scratchpad
438
+
439
+ node :planning { agent(:planner) }
440
+ node :implementation { agent(:coder) }
441
+ start_node :planning
442
+ end
193
443
  ```
194
444
 
195
445
  ---
@@ -671,6 +921,87 @@ delegates_to :frontend # Cumulative - adds to existing list
671
921
 
672
922
  ---
673
923
 
924
+ ### shared_across_delegations
925
+
926
+ Control whether multiple agents share the same instance when delegating to this agent.
927
+
928
+ **Signature:**
929
+ ```ruby
930
+ shared_across_delegations(enabled) → self
931
+ ```
932
+
933
+ **Parameters:**
934
+ - `enabled` (Boolean, required):
935
+ - `false` (default): Create isolated instances per delegator (recommended)
936
+ - `true`: Share the same instance across all delegators
937
+
938
+ **Default:** `false` (isolated mode)
939
+
940
+ **Description:**
941
+
942
+ By default, when multiple agents delegate to the same target agent, each delegator gets its own isolated instance with a separate conversation history. This prevents context mixing and ensures clean delegation boundaries.
943
+
944
+ **Isolated Mode (default):**
945
+ ```
946
+ frontend → tester@frontend (isolated instance)
947
+ backend → tester@backend (isolated instance)
948
+ ```
949
+
950
+ **Shared Mode (opt-in):**
951
+ ```
952
+ frontend → database (shared primary)
953
+ backend → database (shared primary)
954
+ ```
955
+
956
+ **When to use shared mode:**
957
+ - Stateful coordination agents that need shared state
958
+ - Database agents that maintain transaction state
959
+ - Agents that benefit from seeing all delegation contexts
960
+
961
+ **When to use isolated mode (default):**
962
+ - Testing agents that analyze different codebases
963
+ - Review agents that evaluate different PRs
964
+ - Any agent where context mixing would be problematic
965
+
966
+ **Example:**
967
+ ```ruby
968
+ # Default: isolated instances (recommended)
969
+ agent :tester do
970
+ description "Testing agent"
971
+ # Each delegator gets separate tester instance
972
+ end
973
+
974
+ # Opt-in: shared instance
975
+ agent :database do
976
+ description "Database coordination agent"
977
+ shared_across_delegations true # All delegators share this instance
978
+ end
979
+
980
+ # Usage
981
+ agent :frontend do
982
+ delegates_to :tester # Gets tester@frontend
983
+ delegates_to :database # Gets shared database
984
+ end
985
+
986
+ agent :backend do
987
+ delegates_to :tester # Gets tester@backend (separate from frontend's)
988
+ delegates_to :database # Gets same shared database as frontend
989
+ end
990
+ ```
991
+
992
+ **Memory Sharing:**
993
+
994
+ Plugin storage (like SwarmMemory) is **always shared** by base agent name, regardless of isolation mode:
995
+ - `tester@frontend` and `tester@backend` share the same memory storage
996
+ - Only conversation history and tool state (TodoWrite, etc.) are isolated
997
+ - This allows instances to share knowledge while maintaining separate conversations
998
+
999
+ **Concurrency Protection:**
1000
+
1001
+ When using shared mode, SwarmSDK automatically serializes concurrent calls to the same agent instance using fiber-safe semaphores. This prevents message corruption when multiple delegation instances call the same shared agent in parallel.
1002
+
1003
+ ---
1004
+
674
1005
  ### memory
675
1006
 
676
1007
  Configure persistent memory storage for this agent.
@@ -972,7 +1303,7 @@ max_concurrent_tools 20 # Allow more parallelism
972
1303
 
973
1304
  ### disable_default_tools
974
1305
 
975
- Include default tools (Read, Grep, Glob, TodoWrite, Think, and scratchpad tools).
1306
+ Include default tools (Read, Grep, Glob, and scratchpad tools).
976
1307
 
977
1308
  **Signature:**
978
1309
  ```ruby
@@ -1391,7 +1722,7 @@ agent(name, reset_context: true) → AgentConfig
1391
1722
  - `true` (default): Fresh context for each node execution
1392
1723
  - `false`: Preserve conversation history from previous nodes
1393
1724
 
1394
- **Returns:** `AgentConfig` with `.delegates_to(*names)` method
1725
+ **Returns:** `AgentConfig` with `.delegates_to(*names)` and `.tools(*names)` methods
1395
1726
 
1396
1727
  **Example:**
1397
1728
  ```ruby
@@ -1403,6 +1734,12 @@ agent(:backend, reset_context: false).delegates_to(:tester)
1403
1734
 
1404
1735
  # Without delegation, preserving context
1405
1736
  agent(:planner, reset_context: false)
1737
+
1738
+ # Override tools for this node
1739
+ agent(:backend).tools(:Read, :Think)
1740
+
1741
+ # Combine delegation and tool override
1742
+ agent(:backend).delegates_to(:tester).tools(:Read, :Edit, :Write)
1406
1743
  ```
1407
1744
 
1408
1745
  **When to use `reset_context: false`:**
@@ -1498,17 +1835,17 @@ input do |ctx|
1498
1835
  "Implement:\nPlan: #{plan}\nDesign: #{design}"
1499
1836
  end
1500
1837
 
1501
- # Skip execution (caching)
1838
+ # Skip execution (caching) - using return
1502
1839
  input do |ctx|
1503
1840
  cached = check_cache(ctx.content)
1504
- if cached
1505
- { skip_execution: true, content: cached }
1506
- else
1507
- ctx.content
1508
- end
1841
+ return ctx.skip_execution(content: cached) if cached
1842
+
1843
+ ctx.content
1509
1844
  end
1510
1845
  ```
1511
1846
 
1847
+ **Return statements work safely**: Input blocks are automatically converted to lambdas, allowing you to use `return` for clean early exits without exiting your program.
1848
+
1512
1849
  ---
1513
1850
 
1514
1851
  ### input_command
@@ -1576,8 +1913,17 @@ output do |ctx|
1576
1913
  impl = ctx.content
1577
1914
  "Completed:\nPlan: #{plan}\nImpl: #{impl}"
1578
1915
  end
1916
+
1917
+ # Early exit with return
1918
+ output do |ctx|
1919
+ return ctx.halt_workflow(content: ctx.content) if converged?(ctx.content)
1920
+
1921
+ ctx.content
1922
+ end
1579
1923
  ```
1580
1924
 
1925
+ **Return statements work safely**: Output blocks are automatically converted to lambdas, allowing you to use `return` for clean early exits without exiting your program.
1926
+
1581
1927
  ---
1582
1928
 
1583
1929
  ### output_command