agent-harness 0.28.2 → 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 +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +7 -0
- data/lib/agent_harness/providers/kilocode.rb +82 -24
- data/lib/agent_harness/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d913e0f2d865b5da590d18bd9b1a62dfc6c2620996c374b844eaff5401b040b2
|
|
4
|
+
data.tar.gz: ef5f41a73da2695a54ca6a8a4c30b169c6c17ec284c5ea718b2cb0eb60edaf9d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0838d91fa198bed8eac755a4e8204afbf788c583f4beb6b9e5535337f4b332fe23f9cfb6fc897464002808032c66846f775abafcd6accd967609b89117a25d01'
|
|
7
|
+
data.tar.gz: 8d8e3670c0ae6936ed508851de2693a13a4fb17207ec6105e6f4128857b226beb6de6a75638c177c15a933b3fb8d654988a48d3e7c61c8cffa3a765246deb5b8
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,13 @@
|
|
|
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
|
+
|
|
9
16
|
## [0.28.2](https://github.com/viamin/agent-harness/compare/agent-harness/v0.28.1...agent-harness/v0.28.2) (2026-07-10)
|
|
10
17
|
|
|
11
18
|
|
|
@@ -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
|
|
@@ -106,21 +118,6 @@ module AgentHarness
|
|
|
106
118
|
end
|
|
107
119
|
end
|
|
108
120
|
|
|
109
|
-
# Kilo CLI (an opencode fork) ships the same `external_directory`
|
|
110
|
-
# permission category as OpenCode, defaulting to `ask` for anything
|
|
111
|
-
# outside the project dir. In non-interactive execution there is no human
|
|
112
|
-
# to answer the resulting permission prompt, so reads/writes of scratch
|
|
113
|
-
# files under `/tmp/*` via the dedicated read/write/edit tools are
|
|
114
|
-
# auto-rejected — even though bash I/O to the same paths succeeds (shell
|
|
115
|
-
# redirection is not a tool call). The agent then silently loses access to
|
|
116
|
-
# its own scratch output and never completes the task. This default rule
|
|
117
|
-
# broadens the allowlist to all of `/tmp` so the agent can read back files
|
|
118
|
-
# it (or its sub-agents) wrote there.
|
|
119
|
-
DEFAULT_PERMISSION_EXTERNAL_DIRECTORY_PATTERN = "/tmp/**"
|
|
120
|
-
DEFAULT_PERMISSION_RULE = {
|
|
121
|
-
"external_directory" => {DEFAULT_PERMISSION_EXTERNAL_DIRECTORY_PATTERN => "allow"}
|
|
122
|
-
}.freeze
|
|
123
|
-
|
|
124
121
|
def name
|
|
125
122
|
"kilocode"
|
|
126
123
|
end
|
|
@@ -185,10 +182,9 @@ module AgentHarness
|
|
|
185
182
|
provider_name = options[:provider_name] || "openai"
|
|
186
183
|
model_id = options[:model_id]
|
|
187
184
|
|
|
188
|
-
config = {provider
|
|
189
|
-
config[
|
|
190
|
-
config
|
|
191
|
-
apply_default_external_directory_permission(config)
|
|
185
|
+
config = {"provider" => {provider_name => {}}}
|
|
186
|
+
config["model"] = "#{provider_name}/#{model_id}" if model_id
|
|
187
|
+
apply_default_external_directory_permission(config, options)
|
|
192
188
|
|
|
193
189
|
config.to_json
|
|
194
190
|
end
|
|
@@ -219,6 +215,31 @@ module AgentHarness
|
|
|
219
215
|
cmd
|
|
220
216
|
end
|
|
221
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
|
+
|
|
222
243
|
def parse_response(result, duration:)
|
|
223
244
|
output = result.stdout
|
|
224
245
|
tokens = nil
|
|
@@ -349,12 +370,49 @@ module AgentHarness
|
|
|
349
370
|
|
|
350
371
|
private
|
|
351
372
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
|
356
393
|
|
|
357
|
-
|
|
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
|
|
358
416
|
end
|
|
359
417
|
|
|
360
418
|
def heartbeat_hook_script(heartbeat_file_path)
|