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
-
#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
-
#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.
42
43
44
45
46
|
# File 'lib/harnex/adapters/base.rb', line 42
def agent_version
return @agent_version if defined?(@agent_version)
@agent_version = probe_agent_version
end
|
#base_command ⇒ Object
56
57
58
|
# File 'lib/harnex/adapters/base.rb', line 56
def base_command
raise NotImplementedError, "#{self.class} must define #base_command"
end
|
#build_command ⇒ Object
52
53
54
|
# File 'lib/harnex/adapters/base.rb', line 52
def build_command
base_command + @extra_args
end
|
#build_send_payload(text:, submit:, enter_only:, screen_text:, force: false) ⇒ Object
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
# File 'lib/harnex/adapters/base.rb', line 103
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
|
#describe ⇒ Object
48
49
50
|
# File 'lib/harnex/adapters/base.rb', line 48
def describe
{ transport: transport }
end
|
#infer_repo_path(_argv) ⇒ Object
60
61
62
|
# File 'lib/harnex/adapters/base.rb', line 60
def infer_repo_path(_argv)
Dir.pwd
end
|
#inject_exit(writer, delay_ms: 0) ⇒ Object
120
121
122
123
124
125
126
|
# File 'lib/harnex/adapters/base.rb', line 120
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
|
64
65
66
67
68
69
|
# File 'lib/harnex/adapters/base.rb', line 64
def input_state(screen_text)
{
state: "unknown",
input_ready: nil
}
end
|
#parse_session_summary(_transcript_tail) ⇒ Object
71
72
73
|
# File 'lib/harnex/adapters/base.rb', line 71
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”).
35
36
37
|
# File 'lib/harnex/adapters/base.rb', line 35
def provider
nil
end
|
#send_wait_seconds(submit:, enter_only:) ⇒ Object
75
76
77
|
# File 'lib/harnex/adapters/base.rb', line 75
def send_wait_seconds(submit:, enter_only:)
0.0
end
|
#transport ⇒ Object
Default transport. Adapters speaking JSON-RPC override to :stdio_jsonrpc; Session#run uses this to pick the I/O path.
29
30
31
|
# File 'lib/harnex/adapters/base.rb', line 29
def transport
:pty
end
|
#wait_for_sendable(screen_snapshot_fn, submit:, enter_only:, force:) ⇒ Object
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
# File 'lib/harnex/adapters/base.rb', line 83
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
79
80
81
|
# File 'lib/harnex/adapters/base.rb', line 79
def wait_for_sendable_state?(_state, submit:, enter_only:)
false
end
|