Class: Harnex::Adapters::Base
- Inherits:
-
Object
- Object
- Harnex::Adapters::Base
show all
- Defined in:
- lib/harnex/adapters/base.rb
Constant Summary
collapse
- PROMPT_PREFIXES =
[">", "\u203A", "\u276F"].freeze
- AGENT_VERSION_TIMEOUT_SECONDS =
2.0
Instance Attribute Summary collapse
-
#key ⇒ Object
readonly
Adapter contract — subclasses MUST implement: base_command -> Array CLI args to spawn.
Instance Method Summary
collapse
-
#agent_version ⇒ Object
Probes <base_command.first> --version with a short timeout and memoizes the result for the adapter's lifetime.
-
#base_command ⇒ Object
-
#build_command ⇒ Object
-
#build_send_payload(text:, submit:, enter_only:, screen_text:, force: false) ⇒ Object
-
#context_telemetry_source ⇒ Object
-
#context_telemetry_supported? ⇒ Boolean
Whether this adapter exposes active context-window occupancy separately from cumulative usage.
-
#describe ⇒ Object
-
#infer_repo_path(_argv) ⇒ Object
-
#initialize(key, extra_args = []) ⇒ Base
constructor
-
#inject_exit(writer, delay_ms: 0) ⇒ Object
-
#input_state(screen_text) ⇒ Object
-
#parse_session_summary(_transcript_tail) ⇒ Object
-
#provider ⇒ Object
Vendor of the underlying agent — populates DISPATCH meta.agent_provider.
-
#send_wait_seconds(submit:, enter_only:) ⇒ Object
-
#transport ⇒ Object
-
#usage_telemetry_supported? ⇒ Boolean
Whether this adapter has a supported source for token/cost telemetry.
-
#wait_for_sendable(screen_snapshot_fn, submit:, enter_only:, force:) ⇒ Object
-
#wait_for_sendable_state?(_state, submit:, enter_only:) ⇒ Boolean
Constructor Details
#initialize(key, extra_args = []) ⇒ Base
Returns a new instance of Base.
22
23
24
25
|
# File 'lib/harnex/adapters/base.rb', line 22
def initialize(key, = [])
@key = key
@extra_args = .dup
end
|
Instance Attribute Details
#key ⇒ Object
Adapter contract — subclasses MUST implement:
base_command -> Array[String] CLI args to spawn
Subclasses MAY override:
input_state(text) -> Hash Parse screen for state
build_send_payload -> Hash Build injection payload
inject_exit(writer) -> void Send a stop/exit sequence
infer_repo_path(argv) -> String Extract repo path from CLI args
wait_for_sendable -> String Wait for a send-ready snapshot
20
21
22
|
# File 'lib/harnex/adapters/base.rb', line 20
def key
@key
end
|
Instance Method Details
#agent_version ⇒ Object
Probes <base_command.first> --version with a short timeout and
memoizes the result for the adapter's lifetime. Returns nil when
the binary is missing, exits non-zero, or stalls past the timeout.
43
44
45
46
47
|
# File 'lib/harnex/adapters/base.rb', line 43
def agent_version
return @agent_version if defined?(@agent_version)
@agent_version = probe_agent_version
end
|
#base_command ⇒ Object
57
58
59
|
# File 'lib/harnex/adapters/base.rb', line 57
def base_command
raise NotImplementedError, "#{self.class} must define #base_command"
end
|
#build_command ⇒ Object
53
54
55
|
# File 'lib/harnex/adapters/base.rb', line 53
def build_command
base_command + @extra_args
end
|
#build_send_payload(text:, submit:, enter_only:, screen_text:, force: false) ⇒ Object
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
# File 'lib/harnex/adapters/base.rb', line 120
def build_send_payload(text:, submit:, enter_only:, screen_text:, force: false)
state = input_state(screen_text)
if !force && blocked_state?(state, enter_only: enter_only)
raise ArgumentError, blocked_message(state, enter_only: enter_only)
end
payload = enter_only ? "" : text.to_s
payload << submit_bytes if submit || enter_only
{
text: payload,
newline: false,
input_state: state,
force: force
}
end
|
#context_telemetry_source ⇒ Object
88
89
90
|
# File 'lib/harnex/adapters/base.rb', line 88
def context_telemetry_source
nil
end
|
#context_telemetry_supported? ⇒ Boolean
Whether this adapter exposes active context-window occupancy separately
from cumulative usage. Supported sources can still yield no valid sample.
84
85
86
|
# File 'lib/harnex/adapters/base.rb', line 84
def context_telemetry_supported?
false
end
|
#describe ⇒ Object
49
50
51
|
# File 'lib/harnex/adapters/base.rb', line 49
def describe
{ transport: transport }
end
|
#infer_repo_path(_argv) ⇒ Object
61
62
63
|
# File 'lib/harnex/adapters/base.rb', line 61
def infer_repo_path(_argv)
Dir.pwd
end
|
#inject_exit(writer, delay_ms: 0) ⇒ Object
137
138
139
140
141
142
143
|
# File 'lib/harnex/adapters/base.rb', line 137
def inject_exit(writer, delay_ms: 0)
writer.write("/exit")
writer.flush
sleep(delay_ms / 1000.0) if delay_ms.positive?
writer.write(submit_bytes)
writer.flush
end
|
65
66
67
68
69
70
|
# File 'lib/harnex/adapters/base.rb', line 65
def input_state(screen_text)
{
state: "unknown",
input_ready: nil
}
end
|
#parse_session_summary(_transcript_tail) ⇒ Object
72
73
74
|
# File 'lib/harnex/adapters/base.rb', line 72
def parse_session_summary(_transcript_tail)
{}
end
|
#provider ⇒ Object
Vendor of the underlying agent — populates DISPATCH meta.agent_provider.
Subclasses override (claude → "anthropic", codex → "openai").
36
37
38
|
# File 'lib/harnex/adapters/base.rb', line 36
def provider
nil
end
|
#send_wait_seconds(submit:, enter_only:) ⇒ Object
92
93
94
|
# File 'lib/harnex/adapters/base.rb', line 92
def send_wait_seconds(submit:, enter_only:)
0.0
end
|
#transport ⇒ Object
Default transport. Structured adapters override to :stdio_jsonrpc
(Codex app-server) or :stdio_jsonl_rpc (Pi RPC); Session#run uses
this to pick the I/O path.
30
31
32
|
# File 'lib/harnex/adapters/base.rb', line 30
def transport
:pty
end
|
#usage_telemetry_supported? ⇒ Boolean
Whether this adapter has a supported source for token/cost telemetry.
A supported source can still be missing for a particular run.
78
79
80
|
# File 'lib/harnex/adapters/base.rb', line 78
def usage_telemetry_supported?
false
end
|
#wait_for_sendable(screen_snapshot_fn, submit:, enter_only:, force:) ⇒ Object
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
# File 'lib/harnex/adapters/base.rb', line 100
def wait_for_sendable(screen_snapshot_fn, submit:, enter_only:, force:)
snapshot = screen_snapshot_fn.call
return snapshot if force
wait_secs = send_wait_seconds(submit: submit, enter_only: enter_only).to_f
return snapshot unless wait_secs.positive?
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + wait_secs
state = input_state(snapshot)
while Process.clock_gettime(Process::CLOCK_MONOTONIC) < deadline &&
wait_for_sendable_state?(state, submit: submit, enter_only: enter_only)
sleep 0.05
snapshot = screen_snapshot_fn.call
state = input_state(snapshot)
end
snapshot
end
|
#wait_for_sendable_state?(_state, submit:, enter_only:) ⇒ Boolean
96
97
98
|
# File 'lib/harnex/adapters/base.rb', line 96
def wait_for_sendable_state?(_state, submit:, enter_only:)
false
end
|