ace-hitl 0.23.1 → 0.24.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +12 -0
- data/docs/usage.md +14 -4
- data/lib/ace/hitl/atoms/hitl_address.rb +3 -2
- data/lib/ace/hitl/cli/commands/observe.rb +24 -0
- data/lib/ace/hitl/cli.rb +3 -0
- data/lib/ace/hitl/molecules/event_observation.rb +62 -0
- data/lib/ace/hitl/molecules/hitl_route_loader.rb +30 -0
- data/lib/ace/hitl/organisms/hitl_manager.rb +25 -2
- data/lib/ace/hitl/organisms/hitl_sender.rb +1 -0
- data/lib/ace/hitl/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 066770bf07c22e97f6d1571d8d7697a9681df2872102b9454e26dc934d494162
|
|
4
|
+
data.tar.gz: 398b6537945687a92f34a0284d9e687f7b41cfea777352c9b7046a07ef45cd9a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cb37ecff31019de3e8007964a8631e9f8e33c7dd5ba4be5505b84a4172c030e7eadf4580e8e70b962b8d26942a4b1a6221ed9a7641adb8726e4dce408b11c734
|
|
7
|
+
data.tar.gz: a7ecb1759adaa8e6ae5dae068eb556bc0f094cd905a11b12f30f1f1bd6012ea61f68487eeeddf48298ee92de212b7b5c57c893e371a2234b20b9ca18f1475035
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.24.0] - 2026-10-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Add read-only semantic event observations with configured-owner filtering; exclude delivery metadata and unrelated recipients.
|
|
15
|
+
|
|
16
|
+
## [0.23.2] - 2026-10-20
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Select addressed-event routes by configured recipient names resolved to the
|
|
21
|
+
exact stored address. Unrelated terminal/Telegram routes no longer send or
|
|
22
|
+
create false delivery failures. Zero matches fail before adapter IO.
|
|
23
|
+
**Configuration migration:** add `recipients: [name]` to each addressed route;
|
|
24
|
+
names must exist in `hitl.recipients`. Recipient-less local events retain their
|
|
25
|
+
explicitly configured route set.
|
|
26
|
+
- Accept native Herdr panel handles such as `w7:p1` without changing machine or
|
|
27
|
+
folder validation.
|
|
28
|
+
- Provide first-answer-wins `HitlManager#accept_answer` for native transport
|
|
29
|
+
imports; exact replays preserve bytes and conflicting answers cannot replace
|
|
30
|
+
an existing answer under the case write lock.
|
|
31
|
+
|
|
10
32
|
## [0.23.1] - 2026-10-18
|
|
11
33
|
|
|
12
34
|
### Fixed
|
data/README.md
CHANGED
|
@@ -78,3 +78,15 @@ bin/ace-test ace-hitl all
|
|
|
78
78
|
`ace-hitl` owns HITL event semantics and the markdown contract.
|
|
79
79
|
`ace-support-items` remains generic support infrastructure and does not absorb
|
|
80
80
|
HITL-specific behaviour.
|
|
81
|
+
|
|
82
|
+
### Read semantic event revisions
|
|
83
|
+
|
|
84
|
+
`ace-hitl observe --scope current --limit 500` prints one JSON object with schema
|
|
85
|
+
`ace.hitl.observation/v1` and a bounded `events` list. `--scope all` includes linked
|
|
86
|
+
worktrees of this repository. Each row contains a hashed canonical file identity,
|
|
87
|
+
event ID, semantic revision hash, type/status and task/assignment references.
|
|
88
|
+
It emits no private message or answer text. It reads active (non-special-folder) canonical events and
|
|
89
|
+
does not import transports, acknowledge, archive, resume or mutate events.
|
|
90
|
+
Delivery/poll timestamps and filesystem mtime are excluded from revisions;
|
|
91
|
+
content, answers and report/status changes are included. Exceeding the configured
|
|
92
|
+
limit refuses the entire projection rather than silently truncating it.
|
data/docs/usage.md
CHANGED
|
@@ -88,7 +88,7 @@ ace-hitl create "Blocked on auth" --kind decision --to overseer --task 8xg.t.sbz
|
|
|
88
88
|
original case and the right agent — task/assignment/step are context,
|
|
89
89
|
never an ID substitute.
|
|
90
90
|
- Route delivery (`send`) requires a panel-form recipient. A folder-form
|
|
91
|
-
recipient has no shipped delivery
|
|
91
|
+
recipient has no shipped delivery adapter: selected adapters refuse it per-route
|
|
92
92
|
with a clear `failed` reason (the event file already sitting in a folder
|
|
93
93
|
is a local write, not a delivery confirmation). Refusal semantics are
|
|
94
94
|
strict on both axes of the address: the terminal adapter delivers only
|
|
@@ -159,11 +159,13 @@ hitl:
|
|
|
159
159
|
adapters:
|
|
160
160
|
routes:
|
|
161
161
|
- route: overseer-terminal # display name; defaults to the adapter name
|
|
162
|
+
recipients: [overseer] # configured names resolved to the stored destination
|
|
162
163
|
adapter: terminal # built-in: any ace-runtime terminal
|
|
163
164
|
runtime: herdr # tmux | herdr
|
|
164
165
|
pane: p5 # opaque pane handle (runtime-owned)
|
|
165
166
|
machine: lab01 # defaults to hitl.machine; recipient machine must match
|
|
166
167
|
- route: captain-telegram
|
|
168
|
+
recipients: [captain]
|
|
167
169
|
adapter: hermes # ships in ace-hitl-hermes
|
|
168
170
|
channel: project
|
|
169
171
|
machine: lab01 # defaults to hitl.machine
|
|
@@ -176,14 +178,20 @@ ace-hitl send 8xhkep
|
|
|
176
178
|
```
|
|
177
179
|
|
|
178
180
|
```
|
|
179
|
-
HITL event 8xhkep sent through
|
|
181
|
+
HITL event 8xhkep sent through 1 route(s):
|
|
180
182
|
overseer-terminal (terminal): delivered - text pushed to herdr pane p5
|
|
181
|
-
captain-telegram (hermes): unavailable - ...
|
|
182
183
|
```
|
|
183
184
|
|
|
184
185
|
Rules:
|
|
185
186
|
|
|
186
|
-
-
|
|
187
|
+
- For an addressed event, only routes whose `recipients` resolve to its exact
|
|
188
|
+
stored machine and panel/folder are selected. Changing a configured alias does
|
|
189
|
+
not redirect an existing case. Missing selectors do not match addressed events;
|
|
190
|
+
no match fails before adapter invocation or delivery-state writes. Recipient-less
|
|
191
|
+
events use the explicitly configured route set.
|
|
192
|
+
- Several matching routes deliver to the same intended recipient independently.
|
|
193
|
+
Unmatched routes are not invoked or recorded as failures. One result line per
|
|
194
|
+
selected route; a route's success never hides another selected route's
|
|
187
195
|
failure. Exit 0 only when every route is `delivered`; exit 1 on any
|
|
188
196
|
`failed`/`unavailable` route, no configured routes (concrete error naming
|
|
189
197
|
`hitl.adapters.routes`), or unknown/ambiguous ref. An unknown adapter on
|
|
@@ -199,6 +207,8 @@ Rules:
|
|
|
199
207
|
on questions only, task/assignment/step context.
|
|
200
208
|
- `delivered` means accepted by the route's transport. An unavailable
|
|
201
209
|
adapter never reroutes to another recipient.
|
|
210
|
+
- Native terminal handles such as `w7:p1` are accepted unchanged as `panel`.
|
|
211
|
+
This does not relax the machine-token or folder-traversal rules.
|
|
202
212
|
- Adding an adapter requires no ace-hitl change: a gem ships
|
|
203
213
|
`lib/ace/hitl/adapters/<name>.rb` calling `Ace::Hitl::Adapters.register`
|
|
204
214
|
(duck contract: `factory.call(route)` → adapter with
|
|
@@ -11,6 +11,7 @@ module Ace
|
|
|
11
11
|
# Display strings align with the hermes <machine>/<name>/<id> shape.
|
|
12
12
|
class HitlAddress
|
|
13
13
|
TOKEN_PATTERN = /\A[a-z0-9][a-z0-9_-]*\z/i.freeze
|
|
14
|
+
PANEL_PATTERN = /\A[a-z0-9][a-z0-9_:-]{0,127}\z/i.freeze
|
|
14
15
|
|
|
15
16
|
class InvalidAddress < StandardError; end
|
|
16
17
|
|
|
@@ -48,8 +49,8 @@ module Ace
|
|
|
48
49
|
address["id"] = id.to_s
|
|
49
50
|
end
|
|
50
51
|
else
|
|
51
|
-
unless panel.to_s.match?(
|
|
52
|
-
raise InvalidAddress, "address panel '#{panel}' is not a valid
|
|
52
|
+
unless panel.to_s.match?(PANEL_PATTERN)
|
|
53
|
+
raise InvalidAddress, "address panel '#{panel}' is not a valid terminal handle"
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
address["panel"] = panel.to_s
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "ace/support/cli"
|
|
3
|
+
require_relative "../../molecules/event_observation"
|
|
4
|
+
module Ace
|
|
5
|
+
module Hitl
|
|
6
|
+
module CLI
|
|
7
|
+
module Commands
|
|
8
|
+
class Observe < Ace::Support::Cli::Command
|
|
9
|
+
desc "Read bounded semantic event revisions without private message text"
|
|
10
|
+
option :scope, type: :string, default: "current", desc: "current or all linked worktrees"
|
|
11
|
+
option :owner, type: :string, desc: "Configured recipient owning incoming events"
|
|
12
|
+
option :limit, type: :integer, default: 500, desc: "Maximum event count (1..2000)"
|
|
13
|
+
def call(**options)
|
|
14
|
+
scope = options[:scope] || "current"
|
|
15
|
+
raise Ace::Support::Cli::Error, "invalid scope" unless %w[current all].include?(scope)
|
|
16
|
+
puts JSON.generate(Molecules::EventObservation.new.call(scope: scope, limit: options[:limit] || 500, owner: options[:owner]))
|
|
17
|
+
rescue ArgumentError => e
|
|
18
|
+
raise Ace::Support::Cli::Error, e.message
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/ace/hitl/cli.rb
CHANGED
|
@@ -8,6 +8,7 @@ require_relative "cli/commands/operator_question"
|
|
|
8
8
|
require_relative "cli/commands/read"
|
|
9
9
|
require_relative "cli/commands/show"
|
|
10
10
|
require_relative "cli/commands/list"
|
|
11
|
+
require_relative "cli/commands/observe"
|
|
11
12
|
require_relative "cli/commands/update"
|
|
12
13
|
require_relative "cli/commands/wait"
|
|
13
14
|
require_relative "cli/commands/deliver"
|
|
@@ -31,6 +32,7 @@ module Ace
|
|
|
31
32
|
["read", "Read authenticated lifecycle request facts"],
|
|
32
33
|
["show", "Show HITL event details"],
|
|
33
34
|
["list", "List HITL events"],
|
|
35
|
+
["observe", "Read semantic event revisions"],
|
|
34
36
|
["update", "Update HITL event metadata or answer"],
|
|
35
37
|
["send", "Send a HITL event through the configured adapter routes"],
|
|
36
38
|
["wait", "Wait for an answer on a specific HITL event"],
|
|
@@ -58,6 +60,7 @@ module Ace
|
|
|
58
60
|
register "read", CLI::Commands::Read
|
|
59
61
|
register "show", CLI::Commands::Show
|
|
60
62
|
register "list", CLI::Commands::List
|
|
63
|
+
register "observe", CLI::Commands::Observe
|
|
61
64
|
register "update", CLI::Commands::Update
|
|
62
65
|
register "send", CLI::Commands::Send
|
|
63
66
|
register "wait", CLI::Commands::Wait
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "digest"
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Ace
|
|
6
|
+
module Hitl
|
|
7
|
+
module Molecules
|
|
8
|
+
# Read-only semantic projection. No private text is emitted; delivery
|
|
9
|
+
# timestamps, filesystem metadata and observer acknowledgements are not
|
|
10
|
+
# decision changes. Canonical HITL remains the only event owner.
|
|
11
|
+
class EventObservation
|
|
12
|
+
def initialize(manager: Organisms::HitlManager.new)
|
|
13
|
+
@manager = manager
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(scope: "current", limit: 500, owner: nil)
|
|
17
|
+
raise ArgumentError, "invalid observation limit" unless (1..2000).cover?(limit)
|
|
18
|
+
events = @manager.list(in_folder: "next", scope: scope, limit: limit, strict: true)
|
|
19
|
+
raise ArgumentError, "observation exceeds configured event budget" if events.size > limit
|
|
20
|
+
address = owner && Molecules::HitlAddressResolver.new(config: @manager.config).resolve(owner)
|
|
21
|
+
events = events.select { |event| incoming?(event, address) } if address
|
|
22
|
+
rows = events.map do |event|
|
|
23
|
+
semantic = [event.id, event.type, event.status, event.result, event.kind,
|
|
24
|
+
event.title, event.content, event.answer, event.questions,
|
|
25
|
+
Array(event.tags).sort, canonical(event.recipient), canonical(event.reply_to),
|
|
26
|
+
event.metadata.values_at("task", "assignment", "step", "step_name", "resume_instructions")]
|
|
27
|
+
{"key" => Digest::SHA256.hexdigest(event.file_path.to_s),
|
|
28
|
+
"id" => event.id, "revision" => Digest::SHA256.hexdigest(JSON.generate(canonical(semantic))),
|
|
29
|
+
"type" => event.type, "status" => event.status, "result" => event.result,
|
|
30
|
+
"task" => event.metadata["task"], "assignment" => event.metadata["assignment"]}
|
|
31
|
+
end
|
|
32
|
+
{"schema" => "ace.hitl.observation/v1", "events" => rows.sort_by { |row| row["key"] }}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# Questions created locally are outbound until actually answered. A
|
|
38
|
+
# worker's reply belongs to that worker, not every linked-worktree actor.
|
|
39
|
+
# Native instructions have no recipient; their explicit import root owns them.
|
|
40
|
+
def incoming?(event, address)
|
|
41
|
+
local = File.expand_path(event.file_path).start_with?(File.expand_path(@manager.root_dir) + File::SEPARATOR)
|
|
42
|
+
return local if Array(event.tags).include?("hermes-instruction")
|
|
43
|
+
if event.question? && event.answered?
|
|
44
|
+
return local && event.reply_to &&
|
|
45
|
+
event.reply_to["machine"] == @manager.config.dig("hitl", "machine") &&
|
|
46
|
+
File.expand_path(event.reply_to["folder"].to_s) == File.expand_path(@manager.root_dir)
|
|
47
|
+
end
|
|
48
|
+
recipient = event.recipient
|
|
49
|
+
recipient && address.all? { |key, value| recipient[key] == value }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def canonical(value)
|
|
53
|
+
case value
|
|
54
|
+
when Hash then value.sort.to_h.transform_values { |item| canonical(item) }
|
|
55
|
+
when Array then value.map { |item| canonical(item) }
|
|
56
|
+
else value
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "hitl_address_resolver"
|
|
4
|
+
|
|
3
5
|
module Ace
|
|
4
6
|
module Hitl
|
|
5
7
|
module Molecules
|
|
@@ -64,9 +66,37 @@ module Ace
|
|
|
64
66
|
|
|
65
67
|
normalized["route"] = route
|
|
66
68
|
normalized["adapter"] = adapter
|
|
69
|
+
if normalized.key?("recipients") &&
|
|
70
|
+
(!normalized["recipients"].is_a?(Array) || normalized["recipients"].empty? ||
|
|
71
|
+
!normalized["recipients"].all? { |name| name.is_a?(String) && !name.strip.empty? })
|
|
72
|
+
raise InvalidRouteError, "route '#{route}' recipients must be a non-empty list of configured recipient names"
|
|
73
|
+
end
|
|
67
74
|
normalized
|
|
68
75
|
end
|
|
69
76
|
|
|
77
|
+
# Selection happens before adapter construction or delivery-state writes.
|
|
78
|
+
# Names are configuration references, not labels copied onto an event:
|
|
79
|
+
# compare their resolved addresses to the event's immutable destination.
|
|
80
|
+
def self.for_recipient(routes, recipient:, config:)
|
|
81
|
+
return routes if recipient.nil?
|
|
82
|
+
|
|
83
|
+
resolver = HitlAddressResolver.new(config: config)
|
|
84
|
+
destination = recipient.reject { |key, _| key == "id" }
|
|
85
|
+
selected = routes.select do |route|
|
|
86
|
+
addresses = Array(route["recipients"]).map do |name|
|
|
87
|
+
resolver.resolve(name).reject { |key, _| key == "id" }
|
|
88
|
+
end
|
|
89
|
+
addresses.include?(destination)
|
|
90
|
+
end
|
|
91
|
+
if selected.empty?
|
|
92
|
+
raise NoRoutesError, "no adapter route matches recipient #{Atoms::HitlAddress.to_s(recipient)}; " \
|
|
93
|
+
"set route recipients to configured recipient names in #{CONFIG_LOCATION}"
|
|
94
|
+
end
|
|
95
|
+
selected
|
|
96
|
+
rescue HitlAddressResolver::UnknownRecipientError, HitlAddressResolver::InvalidAddressError => e
|
|
97
|
+
raise InvalidRouteError, e.message
|
|
98
|
+
end
|
|
99
|
+
|
|
70
100
|
def self.stringified(entry)
|
|
71
101
|
entry.each_with_object({}) do |(key, value), memo|
|
|
72
102
|
memo[key.to_s] = value
|
|
@@ -27,6 +27,7 @@ module Ace
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
class NotAnswerableError < StandardError; end
|
|
30
|
+
class AnswerConflictError < StandardError; end
|
|
30
31
|
|
|
31
32
|
attr_reader :root_dir, :last_list_total, :last_folder_counts, :config
|
|
32
33
|
|
|
@@ -93,10 +94,12 @@ module Ace
|
|
|
93
94
|
}
|
|
94
95
|
end
|
|
95
96
|
|
|
96
|
-
def list(status: nil, kind: nil, type: nil, delivery: nil, in_folder: "next", tags: [], scope: nil)
|
|
97
|
+
def list(status: nil, kind: nil, type: nil, delivery: nil, in_folder: "next", tags: [], scope: nil, limit: nil, strict: false)
|
|
97
98
|
effective_scope = @scope_resolver.effective_scope(scope)
|
|
98
99
|
scan_results = scan_results_for_scope(effective_scope, in_folder: in_folder)
|
|
100
|
+
raise ArgumentError, "observation exceeds configured event budget" if limit && scan_results.size > limit
|
|
99
101
|
events = load_events(scan_results)
|
|
102
|
+
raise ArgumentError, "incomplete HITL observation" if strict && events.size != scan_results.size
|
|
100
103
|
|
|
101
104
|
events = events.select { |event| event.status == status } if status
|
|
102
105
|
events = events.select { |event| event.kind == kind } if kind
|
|
@@ -153,6 +156,21 @@ module Ace
|
|
|
153
156
|
loader.load(current_path, id: event.id, special_folder: current_special)
|
|
154
157
|
end
|
|
155
158
|
|
|
159
|
+
# Transport imports are first-answer-wins. The case's own write lock
|
|
160
|
+
# checks the answer again, so replay cannot overwrite a human answer.
|
|
161
|
+
# Explicit `update --answer` remains an intentional editing operation.
|
|
162
|
+
def accept_answer(ref, answer:, scope: nil)
|
|
163
|
+
resolved = resolve_for_mutation(ref, scope: scope)
|
|
164
|
+
return nil unless resolved
|
|
165
|
+
event = resolved[:event]
|
|
166
|
+
raise NotAnswerableError, "reports do not await an answer" if event.report?
|
|
167
|
+
raise NotAnswerableError, "managed requests require their authenticated lifecycle" if event.metadata["lab_request_id"]
|
|
168
|
+
raise ArgumentError, "answer is empty" if answer.to_s.strip.empty?
|
|
169
|
+
apply_field_and_answer_updates(event.file_path, set: {}, add: {}, remove: {},
|
|
170
|
+
answer: answer, first_answer: true)
|
|
171
|
+
Molecules::HitlLoader.new.load(event.path, id: event.id, special_folder: event.special_folder)
|
|
172
|
+
end
|
|
173
|
+
|
|
156
174
|
def wait_for_answer(ref, scope: nil, poll_every: 600, timeout: 14_400, waiter: {}, now_proc: nil, sleeper: nil)
|
|
157
175
|
poll_every = normalize_poll_seconds(poll_every)
|
|
158
176
|
timeout = normalize_timeout_seconds(timeout)
|
|
@@ -408,7 +426,7 @@ module Ace
|
|
|
408
426
|
File.unlink(tmp_path) if tmp_path && File.exist?(tmp_path)
|
|
409
427
|
end
|
|
410
428
|
|
|
411
|
-
def apply_field_and_answer_updates(file_path, set:, add:, remove:, answer:)
|
|
429
|
+
def apply_field_and_answer_updates(file_path, set:, add:, remove:, answer:, first_answer: false)
|
|
412
430
|
File.open(file_path, File::RDWR) do |file|
|
|
413
431
|
file.flock(File::LOCK_EX)
|
|
414
432
|
file.rewind
|
|
@@ -416,6 +434,11 @@ module Ace
|
|
|
416
434
|
content = file.read
|
|
417
435
|
frontmatter, body = Ace::Support::Items::Atoms::FrontmatterParser.parse(content)
|
|
418
436
|
body = body.sub(/\A\n/, "")
|
|
437
|
+
previous = body.match(/^## Answer[ \t]*(?:\n(.*))?\z/m)&.[](1).to_s.strip
|
|
438
|
+
if first_answer && (frontmatter["answered"] == true || !previous.empty?)
|
|
439
|
+
raise AnswerConflictError, "case already has a different answer" unless previous == answer.to_s.strip
|
|
440
|
+
return
|
|
441
|
+
end
|
|
419
442
|
|
|
420
443
|
Ace::Support::Items::Molecules::FieldUpdater.apply_set(frontmatter, set)
|
|
421
444
|
Ace::Support::Items::Molecules::FieldUpdater.apply_add(frontmatter, add)
|
|
@@ -57,6 +57,7 @@ module Ace
|
|
|
57
57
|
raise Adapters::Error, "HITL event '#{ref}' not found" unless current
|
|
58
58
|
|
|
59
59
|
event = current[:event]
|
|
60
|
+
routes = Molecules::HitlRouteLoader.for_recipient(routes, recipient: event.recipient, config: @config)
|
|
60
61
|
text = Atoms::HitlEventText.call(event)
|
|
61
62
|
delivery = event.metadata["delivery"]
|
|
62
63
|
|
data/lib/ace/hitl/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-hitl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.24.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-10-
|
|
10
|
+
date: 2026-10-23 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-hitl-contract
|
|
@@ -160,6 +160,7 @@ files:
|
|
|
160
160
|
- lib/ace/hitl/cli/commands/duty.rb
|
|
161
161
|
- lib/ace/hitl/cli/commands/lifecycle_command.rb
|
|
162
162
|
- lib/ace/hitl/cli/commands/list.rb
|
|
163
|
+
- lib/ace/hitl/cli/commands/observe.rb
|
|
163
164
|
- lib/ace/hitl/cli/commands/operator_question.rb
|
|
164
165
|
- lib/ace/hitl/cli/commands/pending.rb
|
|
165
166
|
- lib/ace/hitl/cli/commands/read.rb
|
|
@@ -198,6 +199,7 @@ files:
|
|
|
198
199
|
- lib/ace/hitl/lifecycle/tls_service.rb
|
|
199
200
|
- lib/ace/hitl/live_client.rb
|
|
200
201
|
- lib/ace/hitl/models/hitl_event.rb
|
|
202
|
+
- lib/ace/hitl/molecules/event_observation.rb
|
|
201
203
|
- lib/ace/hitl/molecules/hitl_address_resolver.rb
|
|
202
204
|
- lib/ace/hitl/molecules/hitl_answer_editor.rb
|
|
203
205
|
- lib/ace/hitl/molecules/hitl_config_loader.rb
|