agent-harness 0.28.1 → 0.28.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b96455925e7c106c8209663155c2a03e424119a29da0b5a646d3e4ccafc49e5
4
- data.tar.gz: 86ccd871badbfa21eec7f42f948c9756f7651b0619a5cb2f1cbc2766c0ae5ee7
3
+ metadata.gz: d913e0f2d865b5da590d18bd9b1a62dfc6c2620996c374b844eaff5401b040b2
4
+ data.tar.gz: ef5f41a73da2695a54ca6a8a4c30b169c6c17ec284c5ea718b2cb0eb60edaf9d
5
5
  SHA512:
6
- metadata.gz: 03b0f75fb46ab71d2602230e86add7e3ab7d00da531dc94264e544077c197ec5df85f113edad09213a8b9683caf156141566f8e3ff6ffd98ddb7c5ff8815a746
7
- data.tar.gz: 32df3df874d30e2ef7e072b4c55042766c3ac0bb3a64d77460c084d3e848ce598004be166f114c9c7cbc451b4b7390177110866a2a981661f3ce8abb5d58d2d8
6
+ metadata.gz: '0838d91fa198bed8eac755a4e8204afbf788c583f4beb6b9e5535337f4b332fe23f9cfb6fc897464002808032c66846f775abafcd6accd967609b89117a25d01'
7
+ data.tar.gz: 8d8e3670c0ae6936ed508851de2693a13a4fb17207ec6105e6f4128857b226beb6de6a75638c177c15a933b3fb8d654988a48d3e7c61c8cffa3a765246deb5b8
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.28.1"
2
+ ".": "0.28.3"
3
3
  }
data/CHANGELOG.md CHANGED
@@ -6,6 +6,20 @@
6
6
  * **auth:** add provider-owned PKCE code-exchange API for Claude OAuth (`AgentHarness::Authentication.exchange_code`). Takes an authorization code plus PKCE verifier (and `redirect_uri`/`client_id`), posts an `authorization_code` grant to the Claude token endpoint, and persists the resulting access/refresh tokens in the native `claudeAiOauth` shape. Adds `exchange_code_supported?` and a `code_exchange` key to `auth_capabilities` ([#266](https://github.com/viamin/agent-harness/issues/266)).
7
7
 
8
8
 
9
+ ## [0.28.3](https://github.com/viamin/agent-harness/compare/agent-harness/v0.28.2...agent-harness/v0.28.3) (2026-07-10)
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * Kilocode: default external_directory permission blocks /tmp scratch files, silently killing non-interactive runs ([#285](https://github.com/viamin/agent-harness/issues/285)) ([d703c0a](https://github.com/viamin/agent-harness/commit/d703c0abb7b91fb7a5ba8081e612486edff5e8f5))
15
+
16
+ ## [0.28.2](https://github.com/viamin/agent-harness/compare/agent-harness/v0.28.1...agent-harness/v0.28.2) (2026-07-10)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * Kilocode: default external_directory permission blocks /tmp scratch files, silently killing non-interactive runs ([#283](https://github.com/viamin/agent-harness/issues/283)) ([1ad518f](https://github.com/viamin/agent-harness/commit/1ad518f5c135c5c95d161b9d41a799692f470f76))
22
+
9
23
  ## [0.28.1](https://github.com/viamin/agent-harness/compare/agent-harness/v0.28.0...agent-harness/v0.28.1) (2026-07-08)
10
24
 
11
25
 
@@ -13,6 +13,18 @@ module AgentHarness
13
13
  DEFAULT_VERSION = "7.1.3"
14
14
  SUPPORTED_VERSION_REQUIREMENT = "= #{DEFAULT_VERSION}"
15
15
  STRUCTURED_EVENT_TYPES = %w[text error step_finish result usage].freeze
16
+ # Kilo CLI (an OpenCode fork) ships the same external_directory
17
+ # permission category as OpenCode, defaulting to "ask" for anything
18
+ # outside the project dir. In non-interactive execution there is no
19
+ # human to answer the prompt, so reads of scratch files under /tmp are
20
+ # auto-rejected and the agent silently loses access to its own scratch
21
+ # output, killing the run without a recoverable error. This default rule
22
+ # broadens the allowlist to all of /tmp so the agent can read back files
23
+ # it (or its sub-agents) wrote there. See #282 (precedent: #277/#280).
24
+ DEFAULT_PERMISSION_EXTERNAL_DIRECTORY_PATTERN = "/tmp/**"
25
+ DEFAULT_PERMISSION_CONFIG = {
26
+ "external_directory" => {DEFAULT_PERMISSION_EXTERNAL_DIRECTORY_PATTERN => "allow"}
27
+ }.freeze
16
28
  USAGE_EVENT_TYPES = %w[result usage].freeze
17
29
  TOKEN_USAGE_KEYS = %w[
18
30
  input_tokens
@@ -170,8 +182,9 @@ module AgentHarness
170
182
  provider_name = options[:provider_name] || "openai"
171
183
  model_id = options[:model_id]
172
184
 
173
- config = {provider: {provider_name => {}}}
174
- config[:model] = "#{provider_name}/#{model_id}" if model_id
185
+ config = {"provider" => {provider_name => {}}}
186
+ config["model"] = "#{provider_name}/#{model_id}" if model_id
187
+ apply_default_external_directory_permission(config, options)
175
188
 
176
189
  config.to_json
177
190
  end
@@ -202,6 +215,31 @@ module AgentHarness
202
215
  cmd
203
216
  end
204
217
 
218
+ # Materialize the permissive external_directory permission (and any other
219
+ # runtime config extras) to +~/.config/kilocode/kilo.json+ before the CLI
220
+ # boots. This is the runtime path that actually fixes #282: without it the
221
+ # permission lives only in {config_file_content}'s return value, which no
222
+ # execution caller invokes, so Kilo would boot with stock "ask" defaults
223
+ # and auto-reject /tmp reads in non-interactive runs. Mirrors the OpenCode
224
+ # precedent in #280.
225
+ def build_execution_preparation(options)
226
+ runtime = options[:provider_runtime]
227
+ return nil unless runtime
228
+
229
+ config_payload = kilocode_config_payload(runtime)
230
+ return nil unless config_payload
231
+
232
+ ExecutionPreparation.new(
233
+ file_writes: [
234
+ {
235
+ path: kilocode_config_path,
236
+ content: serialize_kilocode_config(config_payload),
237
+ mode: 0o600
238
+ }
239
+ ]
240
+ )
241
+ end
242
+
205
243
  def parse_response(result, duration:)
206
244
  output = result.stdout
207
245
  tokens = nil
@@ -332,6 +370,51 @@ module AgentHarness
332
370
 
333
371
  private
334
372
 
373
+ # Default-merge a permissive external_directory permission into the
374
+ # generated Kilo config unless the caller takes responsibility for
375
+ # permission config. A caller-supplied permission (passed via the
376
+ # +:permission+ option or already present on the config under the
377
+ # +"permission"+ key, e.g. from runtime metadata config extras) is
378
+ # honored verbatim and never overridden, and an invalid or empty
379
+ # caller permission is ignored in favor of the default rule.
380
+ def apply_default_external_directory_permission(config, options = {})
381
+ caller_permission = options[:permission] || options["permission"] || config["permission"]
382
+ if caller_permission.is_a?(Hash) && !caller_permission.empty?
383
+ config["permission"] = deep_dup(caller_permission)
384
+ return
385
+ end
386
+
387
+ config["permission"] = deep_dup(DEFAULT_PERMISSION_CONFIG)
388
+ end
389
+
390
+ def kilocode_config_path
391
+ "~/.config/kilocode/kilo.json"
392
+ end
393
+
394
+ def kilocode_config_payload(runtime)
395
+ metadata = runtime.metadata
396
+ config_extras = metadata[:config] || metadata["config"] || {}
397
+ unless config_extras.is_a?(Hash)
398
+ raise ArgumentError,
399
+ "Kilocode runtime metadata config must be a Hash of provider-specific extras (got #{config_extras.class})"
400
+ end
401
+
402
+ payload = stringify_keys(config_extras)
403
+ payload["model"] = runtime.model if runtime.model
404
+ apply_default_external_directory_permission(payload)
405
+ payload.empty? ? nil : payload
406
+ end
407
+
408
+ def serialize_kilocode_config(payload)
409
+ JSON.pretty_generate(payload)
410
+ end
411
+
412
+ def stringify_keys(hash)
413
+ hash.each_with_object({}) do |(key, value), result|
414
+ result[key.to_s] = value
415
+ end
416
+ end
417
+
335
418
  def heartbeat_hook_script(heartbeat_file_path)
336
419
  "touch #{Shellwords.escape(heartbeat_file_path)}"
337
420
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AgentHarness
4
- VERSION = "0.28.1"
4
+ VERSION = "0.28.3"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: agent-harness
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.28.1
4
+ version: 0.28.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan