roast-ai 1.2.0 → 1.3.0

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +8 -8
  3. data/README.md +26 -14
  4. data/examples/demo/Gemfile.lock +2 -2
  5. data/examples/plugin-gem-example/Gemfile.lock +4 -4
  6. data/examples/simple_agent.rb +1 -1
  7. data/examples/targets_and_params.rb +12 -0
  8. data/internal/documentation/comments/doc-comments-external.md +5 -3
  9. data/internal/documentation/comments/doc-comments.md +2 -1
  10. data/lib/roast/cog_input_manager.rb +2 -44
  11. data/lib/roast/cogs/agent/config.rb +60 -4
  12. data/lib/roast/cogs/agent/providers/claude/claude_invocation.rb +5 -1
  13. data/lib/roast/cogs/agent/providers/pi/messages/tool_call_message.rb +218 -18
  14. data/lib/roast/cogs/agent/providers/pi/messages/tool_result_message.rb +208 -13
  15. data/lib/roast/cogs/agent/providers/pi/pi_invocation.rb +2 -0
  16. data/lib/roast/cogs/chat/config.rb +53 -2
  17. data/lib/roast/cogs/chat.rb +13 -5
  18. data/lib/roast/config_manager.rb +29 -2
  19. data/lib/roast/version.rb +1 -1
  20. data/lib/roast/workflow.rb +10 -7
  21. data/lib/roast/workflow_param_accessors.rb +53 -0
  22. data/sorbet/rbi/shims/lib/roast/cog/config.rbi +251 -0
  23. data/sorbet/rbi/shims/lib/roast/config_context.rbi +5 -1
  24. data/tutorial/01_your_first_workflow/README.md +3 -2
  25. data/tutorial/01_your_first_workflow/configured_chat.rb +1 -1
  26. data/tutorial/02_chaining_cogs/README.md +23 -2
  27. data/tutorial/02_chaining_cogs/code_review.rb +2 -2
  28. data/tutorial/02_chaining_cogs/session_resumption.rb +1 -0
  29. data/tutorial/03_targets_and_params/README.md +10 -1
  30. data/tutorial/04_configuration_options/README.md +19 -0
  31. data/tutorial/README.md +1 -1
  32. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6062453625ebfdec9aa48a51e5fa8e99deef186142adad84038a6587eba49e5
4
- data.tar.gz: c95063f6f8ad36084ee36b73e8830f50147e65970e7005a26eea375e33f1a4a1
3
+ metadata.gz: 6e2c7eb637034ea1c7a97776161d901ddcce583600bb465fd1f403a4866203c4
4
+ data.tar.gz: 194f9efd30453dbf123f16abab80bce99cf713dee8077858c10fa3e80c53d7cb
5
5
  SHA512:
6
- metadata.gz: 8272e051e7a093aa6486d9ece57ec3981a93ca92266464ffc72fb2db85f56bf7f27dd9b9be47b3c327771e384e2fa7758b9172f90e51d88ae36ff7bd3a66ed27
7
- data.tar.gz: fd1c2df5ce3fe87049d30212cfcd6d93f5bb79519a0efc2a519a57a6bb1e1b29837aa0cb4ac80f30cebe0cfab5ac40994ce1beaec2f16ee0fdc1db713b0ffcb4
6
+ metadata.gz: 92f4fc82409d7b59fed96a2ba31ac9c9c4dc9b9744a8645b934223c2fad8f09f5e960dcb8629963af619310516931391b90e0692162dbe42028553e311cb9eb9
7
+ data.tar.gz: 5d12e5e669c9acc5ac3372f621c2627735c0edaf96bf2c674c56ebbee458b69d3c1fc3dea30073045541e06b01c6ece526bcd597de184e5ea6b84fca8a67f2cb
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- roast-ai (1.2.0)
4
+ roast-ai (1.3.0)
5
5
  activesupport (~> 8.0)
6
6
  async (>= 2.34)
7
7
  rainbow (>= 3.0.0)
@@ -193,8 +193,8 @@ GEM
193
193
  rexml (>= 3.2.6)
194
194
  sorbet-static-and-runtime (>= 0.5.10187)
195
195
  thor (>= 0.19.2)
196
- sqlite3 (2.9.0-arm64-darwin)
197
- sqlite3 (2.9.0-x86_64-linux-gnu)
196
+ sqlite3 (2.9.5-arm64-darwin)
197
+ sqlite3 (2.9.5-x86_64-linux-gnu)
198
198
  tapioca (0.17.10)
199
199
  benchmark
200
200
  bundler (>= 2.2.25)
@@ -224,7 +224,7 @@ GEM
224
224
  addressable (>= 2.8.0)
225
225
  crack (>= 0.3.2)
226
226
  hashdiff (>= 0.4.0, < 2.0.0)
227
- yard (0.9.42)
227
+ yard (0.9.44)
228
228
  yard-sorbet (0.9.0)
229
229
  sorbet-runtime
230
230
  yard
@@ -317,7 +317,7 @@ CHECKSUMS
317
317
  regexp_parser (2.10.0) sha256=cb6f0ddde88772cd64bff1dbbf68df66d376043fe2e66a9ef77fcb1b0c548c61
318
318
  require-hooks (0.2.3) sha256=224be5b4be0fd2a47cb73286c500da366704a54ec195b6627366380c950efac8
319
319
  rexml (3.4.2) sha256=1384268554a37af5da5279431ca3f2f37d46f09ffdd6c95e17cc84c83ea7c417
320
- roast-ai (1.2.0)
320
+ roast-ai (1.3.0)
321
321
  rubocop (1.77.0) sha256=1f360b4575ef7a124be27b0dfffa227a2b2d9420d22d4fd8bf179d702bcc88c0
322
322
  rubocop-ast (1.45.1) sha256=94042e49adc17f187ba037b33f941ba7398fede77cdf4bffafba95190a473a3e
323
323
  rubocop-shopify (2.17.1) sha256=03850eb1a9c4d1f9f0ac1d8d5aa51bb47a149e532cfb5e8d02ac6a90c8800a5f
@@ -337,8 +337,8 @@ CHECKSUMS
337
337
  sorbet-static (0.6.12984-x86_64-linux) sha256=39c03af15badb204d640fbc6398bee171f4026377f31cba08cdb381091159c57
338
338
  sorbet-static-and-runtime (0.6.12984) sha256=281b39ef61aee83551333df581a8d53737bc39df19e09e9cbe01ca8f8b93826f
339
339
  spoom (1.7.11) sha256=4e27384af6d3fde5aadc0287c51e6f76c0802259cbb3b6a67603bf718352f4cf
340
- sqlite3 (2.9.0-arm64-darwin) sha256=a917bd9b84285766ff3300b7d79cd583f5a067594c8c1263e6441618c04a6ed3
341
- sqlite3 (2.9.0-x86_64-linux-gnu) sha256=72fff9bd750070ba3af695511ba5f0e0a2d8a9206f84869640b3e99dfaf3d5a5
340
+ sqlite3 (2.9.5-arm64-darwin) sha256=d0cf444a70fc9395d513cfbcc1e6719e224aa645314e3824cb0474c721425aa2
341
+ sqlite3 (2.9.5-x86_64-linux-gnu) sha256=233dbcb6714148dd23bc5aeb33e8efd6eac974969564ddd5794c23d5f52b231e
342
342
  tapioca (0.17.10) sha256=880a682ca8314f798dd09e9f104134fbf1a713c13be51f7dd4741dd434e6471b
343
343
  thor (1.4.0) sha256=8763e822ccb0f1d7bee88cde131b19a65606657b847cc7b7b4b82e772bcd8a3d
344
344
  traces (0.18.2) sha256=80f1649cb4daace1d7174b81f3b3b7427af0b93047759ba349960cb8f315e214
@@ -350,7 +350,7 @@ CHECKSUMS
350
350
  uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
351
351
  vcr (6.3.1) sha256=37b56e157e720446a3f4d2d39919cabef8cb7b6c45936acffd2ef8229fec03ed
352
352
  webmock (3.25.1) sha256=ab9d5d9353bcbe6322c83e1c60a7103988efc7b67cd72ffb9012629c3d396323
353
- yard (0.9.42) sha256=4e2be01f8623556093497731d44c801e600d7c9759ec7a35a2dd5dd83bbbba68
353
+ yard (0.9.44) sha256=eb087e9b631ccd887b049f303d489963945452d5e2a7eb49a5a74a7cf6887f28
354
354
  yard-sorbet (0.9.0) sha256=03d1aa461b9e9c82b886919a13aa3e09fcf4d1852239d2967ed97e92723ffe21
355
355
  zeitwerk (2.7.3) sha256=b2e86b4a9b57d26ba68a15230dcc7fe6f040f06831ce64417b0621ad96ba3e85
356
356
 
data/README.md CHANGED
@@ -9,7 +9,7 @@ A Ruby-based domain-specific language for creating structured AI workflows. Buil
9
9
  Roast lets you orchestrate AI workflows by combining "cogs" - building blocks that interact with LLMs, run code, execute commands, and process data. Write workflows that:
10
10
 
11
11
  - **Chain AI steps together** - Output from one cog flows seamlessly to the next
12
- - **Run coding agents locally** - Full filesystem access with Claude Code or other providers
12
+ - **Run coding agents locally** - Full filesystem access with Pi, Claude Code, or other providers
13
13
  - **Process collections** - Map operations over arrays with serial or parallel execution
14
14
  - **Control flow intelligently** - Conditional execution, iteration, and error handling
15
15
  - **Reuse workflow components** - Create modular, parameterized scopes
@@ -47,8 +47,8 @@ bin/roast execute analyze_codebase.rb
47
47
 
48
48
  ## Core Cogs
49
49
 
50
- - **`chat`** - Send prompts to cloud-based LLMs (OpenAI, Anthropic, Perplexity & Gemini)
51
- - **`agent`** - Run local coding agents with filesystem access (Claude Code CLI, etc.)
50
+ - **`chat`** - Send prompts to cloud-based LLMs (OpenAI, Anthropic, Perplexity, Gemini & Amazon Bedrock)
51
+ - **`agent`** - Run local coding agents with filesystem access (Pi CLI, Claude Code CLI, etc.)
52
52
  - **`ruby`** - Execute custom Ruby code within workflows
53
53
  - **`cmd`** - Run shell commands and capture output
54
54
  - **`map`** - Process collections in serial or parallel
@@ -70,34 +70,45 @@ gem 'roast-ai'
70
70
 
71
71
  - Ruby 3.0+
72
72
  - API keys or local credentials for your AI provider
73
- - Claude Code CLI installed (for the default agent provider)
73
+ - Pi CLI installed (for the default agent provider)
74
74
 
75
75
  ## Provider Configuration
76
76
 
77
- Roast provider settings are configured in workflow `config` blocks. There is not currently a CLI flag or environment variable that changes the default provider globally; edit the workflow config to select a different provider.
77
+ Roast provider settings are configured in workflow `config` blocks. To change the default **chat** provider without editing each workflow, set the `ROAST_DEFAULT_CHAT_PROVIDER` environment variable (e.g. `export ROAST_DEFAULT_CHAT_PROVIDER=anthropic`). To change the default **agent** provider, set `ROAST_DEFAULT_AGENT_PROVIDER`. A `provider` set in a workflow's `config` always takes precedence over these variables, and an invalid value raises an error.
78
78
 
79
79
  ### Chat cog
80
80
 
81
- The `chat` cog currently supports the OpenAI provider. It uses `OPENAI_API_KEY` by default, and `OPENAI_API_BASE` can override the default base URL (`https://api.openai.com/v1`).
81
+ The `chat` cog supports **OpenAI**, **Anthropic**, **Perplexity**, **Gemini**, and **Amazon Bedrock**. It defaults to `:openai` (override globally with `ROAST_DEFAULT_CHAT_PROVIDER`). Each provider reads its API key from a provider-specific environment variable:
82
+
83
+ | Provider | API key env var | Base URL env var |
84
+ |---|---|---|
85
+ | `:openai` | `OPENAI_API_KEY` | `OPENAI_API_BASE` |
86
+ | `:anthropic` | `ANTHROPIC_API_KEY` | `ANTHROPIC_API_BASE` |
87
+ | `:perplexity` | `PERPLEXITY_API_KEY` | — |
88
+ | `:gemini` | `GEMINI_API_KEY` | `GEMINI_API_BASE` |
89
+ | `:bedrock` | `AWS_ACCESS_KEY_ID` | — |
90
+
91
+ The `:bedrock` provider also reads `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` (for temporary credentials) and `AWS_REGION` from the environment. It does not read AWS profiles. It reads every credential from the environment, so do not use `api_key` with `:bedrock`: it replaces only the access key ID and can pair it with a mismatched `AWS_SECRET_ACCESS_KEY`. The default model is a `us.` inference profile, so accounts outside the US must set `model` in the `chat` config block. To use AWS SSO, export temporary credentials first:
82
92
 
83
93
  ```bash
84
- export OPENAI_API_KEY=...
94
+ eval "$(aws configure export-credentials --profile my-profile --format env)"
95
+ export AWS_REGION=us-west-2
85
96
  ```
86
97
 
87
- You can also configure chat settings directly in a workflow:
98
+ You can configure the provider directly in a workflow:
88
99
 
89
100
  ```ruby
90
101
  config do
91
102
  chat do
92
- provider :openai
93
- model "gpt-4o-mini"
103
+ provider :anthropic
104
+ model "claude-haiku-4-5"
94
105
  end
95
106
  end
96
107
  ```
97
108
 
98
109
  ### Agent cog
99
110
 
100
- The `agent` cog runs local agent CLIs. It defaults to `:claude` and currently supports:
111
+ The `agent` cog runs local agent CLIs. It defaults to `:pi` (override globally with `ROAST_DEFAULT_AGENT_PROVIDER`) and currently supports:
101
112
 
102
113
  - `:claude` - Claude Code CLI
103
114
  - `:pi` - Pi CLI
@@ -116,14 +127,14 @@ Agent providers must be installed and authenticated according to their own CLI r
116
127
 
117
128
  ## Configuration
118
129
 
119
- Roast currently supports four LLM providers for the `chat` cog: **OpenAI**, **Anthropic**, **Perplexity** and **Gemini**.
130
+ Roast currently supports five LLM providers for the `chat` cog: **OpenAI**, **Anthropic**, **Perplexity**, **Gemini** and **Amazon Bedrock**.
120
131
 
121
- - Set `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `PERPLEXITY_API_KEY` and/or `GEMINI_API_KEY` in your environment.
132
+ - Set `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `PERPLEXITY_API_KEY` and/or `GEMINI_API_KEY` in your environment. For Bedrock, set `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION` instead.
122
133
  - Optionally set `OPENAI_API_BASE`, `ANTHROPIC_API_BASE` and/or `GEMINI_API_BASE` to override the default endpoint. Perplexity does not support base URL override.
123
134
 
124
135
  The default model is set per-provider and can only be overridden inside a `config` block. See the [tutorial](https://github.com/Shopify/roast/blob/main/tutorial/01_your_first_workflow/README.md#adding-configuration) for examples.
125
136
 
126
- The `agent` cog is powered by the Claude Code CLI by default, which handles its own authentication.
137
+ The `agent` cog is powered by the Pi CLI by default, which handles its own authentication.
127
138
 
128
139
  ## Getting Started
129
140
 
@@ -159,6 +170,7 @@ class and method comments on the relevant classes.
159
170
  * [Additional Example Workflows]([https://github.com/Shopify/roast/tree/main/examples](https://github.com/Shopify/roast/tree/main/examples)) (these comprise the Roast end-to-end test suite)
160
171
  * __Configuation__
161
172
  * [General configuration block: `config-context.rbi`](https://github.com/Shopify/roast/blob/main/sorbet/rbi/shims/lib/roast/config_context.rbi)
173
+ * [Workflow params in cog config blocks: `cog/config.rbi`](https://github.com/Shopify/roast/blob/main/sorbet/rbi/shims/lib/roast/cog/config.rbi)
162
174
  * [Global cog configuration: `cog/config.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/cog/config.rb)
163
175
  * [Agent cog configuration: `agent/config.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/cogs/agent/config.rb)
164
176
  * [Chat cog configuration: `chat/config.rb`](https://github.com/Shopify/roast/blob/main/lib/roast/cogs/chat/config.rb)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- roast-ai (1.2.0)
4
+ roast-ai (1.3.0)
5
5
  activesupport (~> 8.0)
6
6
  async (>= 2.34)
7
7
  rainbow (>= 3.0.0)
@@ -66,7 +66,7 @@ GEM
66
66
  i18n (1.14.8)
67
67
  concurrent-ruby (~> 1.0)
68
68
  io-event (1.14.0)
69
- json (2.20.0)
69
+ json (2.21.2)
70
70
  language_server-protocol (3.17.0.5)
71
71
  lint_roller (1.1.0)
72
72
  logger (1.7.0)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- roast-ai (1.2.0)
4
+ roast-ai (1.3.0)
5
5
  activesupport (~> 8.0)
6
6
  async (>= 2.34)
7
7
  rainbow (>= 3.0.0)
@@ -51,13 +51,13 @@ GEM
51
51
  drb (2.2.3)
52
52
  erb (6.0.1.1)
53
53
  event_stream_parser (1.0.0)
54
- faraday (2.14.2)
54
+ faraday (2.14.3)
55
55
  faraday-net_http (>= 2.0, < 3.5)
56
56
  json
57
57
  logger
58
58
  faraday-multipart (1.1.1)
59
59
  multipart-post (~> 2.0)
60
- faraday-net_http (3.4.2)
60
+ faraday-net_http (3.4.4)
61
61
  net-http (~> 0.5)
62
62
  faraday-retry (2.3.2)
63
63
  faraday (~> 2.0)
@@ -73,7 +73,7 @@ GEM
73
73
  pp (>= 0.6.0)
74
74
  rdoc (>= 4.0.0)
75
75
  reline (>= 0.4.2)
76
- json (2.19.5)
76
+ json (2.21.2)
77
77
  language_server-protocol (3.17.0.5)
78
78
  lint_roller (1.1.0)
79
79
  logger (1.7.0)
@@ -6,7 +6,7 @@
6
6
  config do
7
7
  agent do
8
8
  provider :claude
9
- model "haiku"
9
+ model "anthropic/haiku"
10
10
  append_system_prompt "Always respond in haiku form"
11
11
  show_prompt!
12
12
  dump_raw_agent_messages_to "tmp/claude-messages.log"
@@ -4,6 +4,18 @@
4
4
  #: self as Roast::Workflow
5
5
 
6
6
  config do
7
+ # Workflow params are available inside cog config blocks and the `global` block, so you can
8
+ # configure cogs based on how the workflow was invoked.
9
+ chat do
10
+ # e.g., pick a cheaper model when the workflow is invoked with the `fast` flag:
11
+ # roast execute examples/targets_and_params.rb -- fast
12
+ model(arg?(:fast) ? "gpt-5.4-nano" : "gpt-5")
13
+ end
14
+
15
+ # `global` config applies to every cog; the same param accessors work here too.
16
+ global do
17
+ abort_on_failure! if arg?(:strict)
18
+ end
7
19
  end
8
20
 
9
21
  execute do
@@ -165,7 +165,8 @@ shortened names can be confusing. Use the complete module path.
165
165
  ```ruby
166
166
  # Configure the cog to use the default provider when invoking an agent
167
167
  #
168
- # The default provider used by Roast is Anthropic Claude Code (`:claude`).
168
+ # The default provider is the one named by the `ROAST_DEFAULT_AGENT_PROVIDER` environment variable,
169
+ # or Pi (`:pi`) when that variable is unset.
169
170
  #: () -> void
170
171
  def use_default_provider!
171
172
  @values[:provider] = nil
@@ -386,7 +387,8 @@ end
386
387
  # Configure the cog to use a specified provider when invoking an agent
387
388
  #
388
389
  # The provider is the source of the agent tool itself.
389
- # If no provider is specified, Anthropic Claude Code (`:claude`) will be used as the default provider.
390
+ # If no provider is specified, Roast uses the provider named by the `ROAST_DEFAULT_AGENT_PROVIDER`
391
+ # environment variable, or Pi (`:pi`) when that variable is unset.
390
392
  #
391
393
  # A provider must be properly installed on your system in order for Roast to be able to use it.
392
394
  #
@@ -519,7 +521,7 @@ Methods in `config_context.rbi` expose cog configuration interfaces and are the
519
521
  #
520
522
  # #### Configure the LLM provider
521
523
  # - `provider(symbol)` - Set the agent provider (e.g., `:claude`)
522
- # - `use_default_provider!` - Use the default provider (`:claude`)
524
+ # - `use_default_provider!` - Use the default provider (`:pi`, or `$ROAST_DEFAULT_AGENT_PROVIDER` when set)
523
525
  #
524
526
  # #### Configure the base command used to run the coding agent
525
527
  # - `command(string_or_array)` - Set the base command for invoking the agent
@@ -70,7 +70,8 @@ These files provide the primary interface between users and Roast. The documenta
70
70
  # Configure the cog to use a specified provider when invoking an agent
71
71
  #
72
72
  # The provider is the source of the agent tool itself.
73
- # If no provider is specified, Anthropic Claude Code (`:claude`) will be used as the default provider.
73
+ # If no provider is specified, Roast uses the provider named by the `ROAST_DEFAULT_AGENT_PROVIDER`
74
+ # environment variable, or Pi (`:pi`) when that variable is unset.
74
75
  #
75
76
  # A provider must be properly installed on your system in order for Roast to be able to use it.
76
77
  #
@@ -4,6 +4,8 @@
4
4
  module Roast
5
5
  # Context in which an individual cog block within the `execute` block of a workflow is evaluated
6
6
  class CogInputManager
7
+ include WorkflowParamAccessors
8
+
7
9
  class CogOutputAccessError < Roast::Error; end
8
10
 
9
11
  class CogDoesNotExistError < CogOutputAccessError; end
@@ -104,50 +106,6 @@ module Roast
104
106
  end
105
107
  end
106
108
 
107
- #: () -> String
108
- def target!
109
- raise ArgumentError, "expected exactly one target" unless @workflow_context.params.targets.length == 1
110
-
111
- @workflow_context.params.targets.first #: as String
112
- end
113
-
114
- #: () -> Array[String]
115
- def targets
116
- @workflow_context.params.targets.dup
117
- end
118
-
119
- #: (Symbol) -> bool
120
- def arg?(value)
121
- @workflow_context.params.args.include?(value)
122
- end
123
-
124
- #: () -> Array[Symbol]
125
- def args
126
- @workflow_context.params.args.dup
127
- end
128
-
129
- #: (Symbol) -> String?
130
- def kwarg(key)
131
- @workflow_context.params.kwargs[key]
132
- end
133
-
134
- #: (Symbol) -> String
135
- def kwarg!(key)
136
- raise ArgumentError, "expected keyword argument '#{key}' to be present" unless @workflow_context.params.kwargs.include?(key)
137
-
138
- @workflow_context.params.kwargs[key] #: as String
139
- end
140
-
141
- #: (Symbol) -> bool
142
- def kwarg?(key)
143
- @workflow_context.params.kwargs.include?(key)
144
- end
145
-
146
- #: () -> Hash[Symbol, String]
147
- def kwargs
148
- @workflow_context.params.kwargs.dup
149
- end
150
-
151
109
  #: () -> Pathname
152
110
  def tmpdir
153
111
  Pathname.new(@workflow_context.tmpdir).realpath
@@ -5,12 +5,21 @@ module Roast
5
5
  module Cogs
6
6
  class Agent < Cog
7
7
  class Config < Cog::Config
8
- VALID_PROVIDERS = [:claude, :pi].freeze #: Array[Symbol]
8
+ VALID_PROVIDERS = [:pi, :claude].freeze #: Array[Symbol]
9
+
10
+ # Environment variable that overrides the built-in default agent provider.
11
+ #
12
+ # When an agent cog does not explicitly configure a provider, Roast uses the provider named by
13
+ # this variable, falling back to the built-in default (`VALID_PROVIDERS.first`, i.e. `:pi`) when it
14
+ # is unset or blank. The value is normalized (surrounding whitespace stripped, then downcased)
15
+ # before lookup, and an explicit `provider` configured on the cog always takes precedence over it.
16
+ DEFAULT_PROVIDER_ENV_VAR = "ROAST_DEFAULT_AGENT_PROVIDER" #: String
9
17
 
10
18
  # Configure the cog to use a specified provider when invoking an agent
11
19
  #
12
20
  # The provider is the source of the agent tool itself.
13
- # If no provider is specified, Anthropic Claude Code (`:claude`) will be used as the default provider.
21
+ # If no provider is specified, Roast uses the provider named by the `ROAST_DEFAULT_AGENT_PROVIDER`
22
+ # environment variable, or Pi (`:pi`) when that variable is unset.
14
23
  #
15
24
  # A provider must be properly installed on your system in order for Roast to be able to use it.
16
25
  #
@@ -24,7 +33,8 @@ module Roast
24
33
 
25
34
  # Configure the cog to use the default provider when invoking an agent
26
35
  #
27
- # The default provider used by Roast is Anthropic Claude Code (`:claude`).
36
+ # The default provider is the one named by the `ROAST_DEFAULT_AGENT_PROVIDER` environment variable,
37
+ # or Pi (`:pi`) when that variable is unset.
28
38
  #
29
39
  # The provider must be properly installed on your system in order for Roast to be able to use it.
30
40
  #
@@ -38,6 +48,10 @@ module Roast
38
48
 
39
49
  # Get the validated provider name that the cog is configured to use when invoking an agent
40
50
  #
51
+ # The provider is resolved in order of precedence: the provider explicitly configured on the cog,
52
+ # then the `ROAST_DEFAULT_AGENT_PROVIDER` environment variable (normalized by stripping surrounding
53
+ # whitespace and downcasing), then the built-in default (`VALID_PROVIDERS.first`, i.e. `:pi`).
54
+ #
41
55
  # Note: this method will return the name of a valid provider or raise an `InvalidConfigError`.
42
56
  # It will __not__, however, validate that the agent is properly installed on your system.
43
57
  # If the agent is not properly installed, you will likely experience a failure when Roast attempts to
@@ -49,7 +63,8 @@ module Roast
49
63
  #
50
64
  #: () -> Symbol
51
65
  def valid_provider!
52
- provider = @values[:provider] || VALID_PROVIDERS.first
66
+ env_default = ENV[DEFAULT_PROVIDER_ENV_VAR].presence&.strip&.downcase&.to_sym
67
+ provider = @values[:provider] || env_default || VALID_PROVIDERS.first
53
68
  unless VALID_PROVIDERS.include?(provider)
54
69
  raise InvalidConfigError, "'#{provider}' is not a valid provider. Available providers include: #{VALID_PROVIDERS.join(", ")}"
55
70
  end
@@ -131,6 +146,47 @@ module Roast
131
146
  @values[:model].presence
132
147
  end
133
148
 
149
+ # Configure the cog to stop the agent if it runs longer than the given number of seconds
150
+ #
151
+ # When the limit is reached, the agent process is killed and the cog raises
152
+ # `Roast::CommandRunner::TimeoutError`. By default there is no timeout.
153
+ #
154
+ # #### See Also
155
+ # - `no_timeout!`
156
+ #
157
+ #: (Integer) -> void
158
+ def timeout(seconds)
159
+ @values[:timeout] = seconds
160
+ end
161
+
162
+ # Configure the cog to let the agent run for as long as it takes (the default)
163
+ #
164
+ # #### See Also
165
+ # - `timeout`
166
+ #
167
+ #: () -> void
168
+ def no_timeout!
169
+ @values[:timeout] = nil
170
+ end
171
+
172
+ # Get the validated number of seconds the agent may run before it is stopped
173
+ #
174
+ # Returns `nil` if the agent has no timeout. Raises an `InvalidConfigError` if the configured
175
+ # timeout is not positive.
176
+ #
177
+ # #### See Also
178
+ # - `timeout`
179
+ # - `no_timeout!`
180
+ #
181
+ #: () -> Integer?
182
+ def valid_timeout
183
+ seconds = @values[:timeout]
184
+ return if seconds.nil?
185
+ return seconds if seconds.positive?
186
+
187
+ raise InvalidConfigError, "timeout must be a positive number of seconds, got #{seconds.inspect}"
188
+ end
189
+
134
190
  # Configure the cog with a custom system prompt that will completely replace the agent's
135
191
  # default system prompt every time the agent is invoked
136
192
  #
@@ -56,11 +56,14 @@ module Roast
56
56
  #: (Agent::Config, String, String?, ?fork_session: bool) -> void
57
57
  def initialize(config, prompt, session, fork_session: true)
58
58
  @base_command = config.valid_command #: (String | Array[String])?
59
- @model = config.valid_model #: String?
59
+ # The Claude CLI expects a bare model name, while Pi expects a fully-qualified
60
+ # `anthropic/<model>` id. Strip the prefix so the same model string works for either provider.
61
+ @model = config.valid_model&.delete_prefix("anthropic/") #: String?
60
62
  @append_system_prompt = config.valid_append_system_prompt #: String?
61
63
  @replace_system_prompt = config.valid_replace_system_prompt #: String?
62
64
  @apply_permissions = config.apply_permissions? #: bool
63
65
  @working_directory = config.valid_working_directory #: Pathname?
66
+ @timeout = config.valid_timeout #: Integer?
64
67
  @context = Context.new #: Context
65
68
  @result = Result.new #: Result
66
69
  @raw_dump_file = config.valid_dump_raw_agent_messages_to_path #: Pathname?
@@ -82,6 +85,7 @@ module Roast
82
85
  command_line,
83
86
  working_directory: @working_directory,
84
87
  stdin_content: @prompt,
88
+ timeout: @timeout,
85
89
  stdout_handler: lambda { |line| handle_stdout(line) },
86
90
  )
87
91