harnex 0.6.5 → 0.7.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/CHANGELOG.md +138 -0
- data/README.md +71 -21
- data/TECHNICAL.md +23 -0
- data/guides/01_dispatch.md +2 -0
- data/lib/harnex/adapters/base.rb +33 -0
- data/lib/harnex/adapters/claude.rb +4 -0
- data/lib/harnex/adapters/codex.rb +4 -0
- data/lib/harnex/adapters/codex_appserver.rb +56 -230
- data/lib/harnex/adapters/opencode.rb +132 -0
- data/lib/harnex/adapters.rb +3 -1
- data/lib/harnex/cli.rb +8 -1
- data/lib/harnex/codex/app_server/client.rb +348 -0
- data/lib/harnex/commands/doctor.rb +95 -2
- data/lib/harnex/commands/history.rb +149 -0
- data/lib/harnex/commands/run.rb +44 -6
- data/lib/harnex/commands/wait.rb +77 -36
- data/lib/harnex/core.rb +3 -3
- data/lib/harnex/dispatch_history.rb +112 -0
- data/lib/harnex/runtime/session.rb +164 -23
- data/lib/harnex/version.rb +2 -2
- data/lib/harnex.rb +2 -0
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 315057f04a626949bd270491616595e98d3d298a4b49aa7d70d23aa0cda0501d
|
|
4
|
+
data.tar.gz: 06c539f5911aa58fa427c80570ca7998ce382d6077a015ae8dc4d181cf05a00f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 15b5c96adb86810626229e9d037992cce4566d8aee83856870735f9e28c8e61b63f567d6af18a946b98f9696b2387f9e6b1b77d65659989c749db5eb7de6ef1b
|
|
7
|
+
data.tar.gz: cdf1b6b3ce3c1934fd768b983df7db3e7aa296abd83e3c926d0a1b7760a5ff1cb9d433742b8edd15740ced1c0dc735d8027c6849eb474fa5049d73ae4246c5b2
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,144 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.7.3] - 2026-05-13 | 01:43 PM | IST
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `harnex run codex --fast` now opts Codex app-server runs into
|
|
10
|
+
`service_tier="fast"`. Default Codex runs now inject
|
|
11
|
+
`service_tier="flex"` unless the child CLI args already supply an
|
|
12
|
+
explicit `service_tier` config.
|
|
13
|
+
- First-class `opencode` PTY adapter. `harnex run opencode` now uses
|
|
14
|
+
`Harnex::Adapters::Opencode` instead of the generic fallback, with
|
|
15
|
+
an OpenCode-specific stop sequence (double Ctrl+C), repo path
|
|
16
|
+
inference (`--dir` or positional project path), and session-id
|
|
17
|
+
extraction from transcript tails (`Continue opencode -s ...`).
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- `harnex run` now rejects unknown long flags before spawning the
|
|
22
|
+
agent process, with a clear error pointing at `harnex run --help`.
|
|
23
|
+
Anything past the `--` separator continues to forward unchanged
|
|
24
|
+
to the agent CLI. Closes the trigger that correlated with the F23
|
|
25
|
+
auto-stop teardown leak. Closes harnex issue #38.
|
|
26
|
+
- Codex app-server contract fixtures are refreshed against
|
|
27
|
+
`codex-cli 0.130.0`; response builders now include the required
|
|
28
|
+
`thread.sessionId` field.
|
|
29
|
+
- Internal Codex app-server code now has the extracted
|
|
30
|
+
`Harnex::Codex::AppServer::Client` namespace and the subprocess
|
|
31
|
+
restart primitive needed by the deployment-fallback plan. No
|
|
32
|
+
public fallback CLI flag is exposed yet.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- `harnex run` now defaults dispatch summaries to
|
|
37
|
+
`<repo>/.harnex/dispatch.jsonl` for every resolved repo, regardless
|
|
38
|
+
of whether a legacy `koder/` directory exists. Closes harnex issue
|
|
39
|
+
#39.
|
|
40
|
+
- `harnex run` with `--auto-stop` now exits within a bounded grace
|
|
41
|
+
(default 5s, override via `HARNEX_AUTOSTOP_TEARDOWN_GRACE_SECONDS`)
|
|
42
|
+
after `task_complete`. Closes a leak where the wrapping Ruby parent
|
|
43
|
+
process could sleep on `futex_wait_queue` indefinitely during
|
|
44
|
+
teardown, surviving as `orphan_tmux` until manually swept (F09
|
|
45
|
+
detected; F23 remediates). Closes harnex issue #37.
|
|
46
|
+
|
|
47
|
+
## [0.7.2] - 2026-05-08
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- `harnex run` now resolves the repo-local
|
|
52
|
+
`<repo>/.harnex/dispatch.jsonl` write path from the supervisor's
|
|
53
|
+
launch cwd (captured at invocation time), not from the agent's
|
|
54
|
+
runtime cwd. Closes a regression where cross-repo dispatches
|
|
55
|
+
(supervisor in repo A, agent `cd`'s into repo B) wrote their
|
|
56
|
+
terminal row only to the global fallback, never to repo A.
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
|
|
60
|
+
- `harnex doctor --sweep` reports read-only harnex/tmux session drift,
|
|
61
|
+
including active registry rows, matching `cx-*` tmux windows, orphan
|
|
62
|
+
tmux windows, and stale session log files whose owning pid is gone.
|
|
63
|
+
|
|
64
|
+
## [0.7.1] - 2026-05-08
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
|
|
68
|
+
- `harnex run --auto-stop` now observes the terminal `task_complete`
|
|
69
|
+
event after the agent subprocess exits before tearing down, closing
|
|
70
|
+
a race where one-shot dispatches could report `timeout` even when
|
|
71
|
+
the agent finished the task cleanly. Adds a regression test that
|
|
72
|
+
exercises the post-exit event drain path.
|
|
73
|
+
|
|
74
|
+
- `harnex wait` predicates now classify dispatch progress by
|
|
75
|
+
structured event-record fields instead of regex-matching the
|
|
76
|
+
human-readable transcript, with the prior exact-marker text as a
|
|
77
|
+
legacy fallback. Eliminates a class of spurious matches where
|
|
78
|
+
prose containing a marker word was misread as a state transition.
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
|
|
82
|
+
- Dispatch summaries now preserve declared brief budget metadata
|
|
83
|
+
(`read_budget_lines`, `output_ceiling_lines`) from `--meta` and
|
|
84
|
+
record rough terminal measurements (`lines_changed`, `output_lines`,
|
|
85
|
+
`output_bytes`, `event_records`) for downstream budget enforcement.
|
|
86
|
+
|
|
87
|
+
## [0.7.0] - 2026-05-08
|
|
88
|
+
|
|
89
|
+
### Added
|
|
90
|
+
|
|
91
|
+
- `harnex run` now appends one terminal dispatch record to
|
|
92
|
+
`<repo>/.harnex/dispatch.jsonl`, falling back to
|
|
93
|
+
`~/.local/state/harnex/dispatch.jsonl` outside git repos. New
|
|
94
|
+
`harnex history` reads that log with `--limit`, `--since`, `--id`,
|
|
95
|
+
`--global`, `--json`, and `--all` filters.
|
|
96
|
+
- DISPATCH row `actual` now includes `turn_count`, `tool_calls`,
|
|
97
|
+
`commands_executed`, `rate_limits`, `output_log_path`, and
|
|
98
|
+
`events_log_path` — surfacing data harnex already tracked but did
|
|
99
|
+
not persist. `meta.parent_dispatch_id` now auto-derives from
|
|
100
|
+
`$HARNEX_ID` when not supplied via `--meta`. (#35 Tier 2)
|
|
101
|
+
- DISPATCH row `meta.agent_provider` and `meta.agent_version` now
|
|
102
|
+
populated. `provider` is a per-adapter constant
|
|
103
|
+
(claude → `anthropic`, codex → `openai`, generic → nil).
|
|
104
|
+
`agent_version` lazily probes `<base_command.first> --version`
|
|
105
|
+
with a 2s timeout, memoizes per adapter, and falls back to nil if
|
|
106
|
+
the binary is missing or stalls. (#35 Tier 3)
|
|
107
|
+
|
|
108
|
+
### Removed
|
|
109
|
+
|
|
110
|
+
- DISPATCH row dropped four always-null fields with no code path
|
|
111
|
+
populating them: `actual.cost_usd`, `actual.tests_run`,
|
|
112
|
+
`actual.tests_passed`, `actual.tests_failed`, and
|
|
113
|
+
`meta.agent_deployment`. Cost computation belongs to downstream
|
|
114
|
+
consumers (per-model rate tables change frequently); test-result
|
|
115
|
+
aggregation belongs to CI integrations, not the harness; and the
|
|
116
|
+
`agent_deployment` concept had no source of truth. JSON Lines
|
|
117
|
+
consumers should update column readers — schema is now stable but
|
|
118
|
+
smaller. (#35 Tier 3)
|
|
119
|
+
|
|
120
|
+
### Fixed
|
|
121
|
+
|
|
122
|
+
- `harnex wait` (default exit-watch mode) now blocks until the
|
|
123
|
+
exit-status file is on disk after the agent subprocess dies, with
|
|
124
|
+
a 5s grace bound (override via `HARNEX_EXIT_STATUS_GRACE_SECONDS`).
|
|
125
|
+
The exit-status file is written *after* the DISPATCH row in the
|
|
126
|
+
parent's teardown ensure block, so its presence guarantees the row
|
|
127
|
+
is on disk. Closes the race where `wait` could return between
|
|
128
|
+
`Process.wait2` unblocking and `Session#finalize_session!`
|
|
129
|
+
appending the row, which made dispatch-poll orchestrators flake
|
|
130
|
+
on "missing telemetry". (#36)
|
|
131
|
+
|
|
132
|
+
### Changed
|
|
133
|
+
|
|
134
|
+
- `koder/STATE.md` is now a thin past / present / future handoff
|
|
135
|
+
instead of a long-running project history. Durable change history
|
|
136
|
+
belongs in `CHANGELOG.md`, release verification in `koder/releases/`,
|
|
137
|
+
and issue or implementation detail in `koder/issues/` or
|
|
138
|
+
`koder/plans/`.
|
|
139
|
+
- Agent orientation and the repo-local `open` / `close` skills now
|
|
140
|
+
explicitly tell future sessions to keep `STATE.md` concise and route
|
|
141
|
+
detailed notes to the durable tracking document that owns them.
|
|
142
|
+
|
|
5
143
|
## [0.6.5] — 2026-05-07
|
|
6
144
|
|
|
7
145
|
### Added
|
data/README.md
CHANGED
|
@@ -2,15 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Run multiple AI coding agents from your terminal and coordinate them.
|
|
4
4
|
|
|
5
|
-
Harnex wraps Claude Code
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Harnex wraps Claude Code, OpenAI Codex, or any terminal CLI in a local
|
|
6
|
+
harness so you can launch agents, send them tasks, watch live panes or
|
|
7
|
+
transcripts, and stop them cleanly — all from the command line.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
gem install harnex
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Harnex itself requires **Ruby 3.x** and uses only the Ruby standard
|
|
14
|
+
library. Install the CLIs you want to wrap separately; Codex JSON-RPC
|
|
15
|
+
support requires Codex CLI **0.128.0 or newer**, and tmux-backed
|
|
16
|
+
workflows require `tmux`.
|
|
14
17
|
|
|
15
18
|
Then ask the CLI what to do next:
|
|
16
19
|
|
|
@@ -20,6 +23,9 @@ harnex --help
|
|
|
20
23
|
harnex agents-guide
|
|
21
24
|
```
|
|
22
25
|
|
|
26
|
+
If you use Codex, run `harnex doctor` after installing or upgrading the
|
|
27
|
+
Codex CLI. It verifies the local `codex app-server` prerequisite.
|
|
28
|
+
|
|
23
29
|
`harnex agents-guide` is the agent-facing reference for dispatch, chain,
|
|
24
30
|
buddy, monitoring, and naming patterns. It is packaged in the gem; no skills
|
|
25
31
|
or project-local docs are required.
|
|
@@ -28,7 +34,7 @@ or project-local docs are required.
|
|
|
28
34
|
|
|
29
35
|
```bash
|
|
30
36
|
# Start an agent in tmux
|
|
31
|
-
harnex run codex --id planner --tmux
|
|
37
|
+
harnex run codex --id planner --tmux planner
|
|
32
38
|
|
|
33
39
|
# Send it a task and wait for it to finish
|
|
34
40
|
harnex send --id planner --message "Write a plan to /tmp/plan.md" --wait-for-idle
|
|
@@ -50,7 +56,8 @@ job, watch it work, stop it when done.
|
|
|
50
56
|
findings. Each step is a fresh agent with clean context.
|
|
51
57
|
|
|
52
58
|
- **You want to see what agents are doing.** `harnex pane` shows
|
|
53
|
-
|
|
59
|
+
a tmux-backed agent's live terminal, or Codex's synthesized
|
|
60
|
+
JSON-RPC transcript. No black boxes.
|
|
54
61
|
|
|
55
62
|
- **You don't want to babysit.** Send a task with `--wait-for-idle`,
|
|
56
63
|
walk away, check back when it's done.
|
|
@@ -68,9 +75,19 @@ job, watch it work, stop it when done.
|
|
|
68
75
|
|
|
69
76
|
| Agent | Support |
|
|
70
77
|
|-------|---------|
|
|
71
|
-
| Claude Code |
|
|
72
|
-
| OpenAI Codex |
|
|
73
|
-
|
|
|
78
|
+
| Claude Code | PTY adapter with prompt detection, stop sequence, workspace trust, and vim mode handling |
|
|
79
|
+
| OpenAI Codex | JSON-RPC `codex app-server` adapter by default; `--legacy-pty` remains supported for TUI/interactive PTY use |
|
|
80
|
+
| OpenCode | PTY adapter with native Ctrl+C stop handling and OpenCode-specific prompt/readiness heuristics |
|
|
81
|
+
| Any terminal CLI | Generic PTY wrapping with local API, logs, status, and best-effort prompt detection |
|
|
82
|
+
|
|
83
|
+
`harnex run codex` uses JSON-RPC by default. That path provides
|
|
84
|
+
structured task-completion events, approval mediation, and token usage
|
|
85
|
+
capture. Default JSON-RPC Codex does not accept `-m` / `--model`; pass
|
|
86
|
+
model settings as child CLI config, for example `harnex run codex -- -c model=NAME`.
|
|
87
|
+
Harnex forces Codex app-server `service_tier="flex"` unless you opt into
|
|
88
|
+
`service_tier="fast"` with `harnex run codex --fast`.
|
|
89
|
+
Use `harnex run codex --legacy-pty` when you specifically want Codex's
|
|
90
|
+
terminal UI or legacy PTY flag behavior.
|
|
74
91
|
|
|
75
92
|
## Multi-agent workflows
|
|
76
93
|
|
|
@@ -78,21 +95,24 @@ The real power is chaining agents together:
|
|
|
78
95
|
|
|
79
96
|
```bash
|
|
80
97
|
# 1. Codex writes a plan
|
|
81
|
-
harnex run codex --id cx-plan --tmux
|
|
98
|
+
harnex run codex --id cx-plan --tmux cx-plan
|
|
82
99
|
harnex send --id cx-plan --message "Plan the auth module, write to /tmp/plan.md" --wait-for-idle
|
|
83
100
|
harnex stop --id cx-plan
|
|
84
101
|
|
|
85
102
|
# 2. Fresh Codex implements the plan
|
|
86
|
-
harnex run codex --id cx-impl --tmux
|
|
103
|
+
harnex run codex --id cx-impl --tmux cx-impl
|
|
87
104
|
harnex send --id cx-impl --message "Implement /tmp/plan.md, run tests" --wait-for-idle
|
|
88
105
|
harnex stop --id cx-impl
|
|
89
106
|
|
|
90
107
|
# 3. Claude reviews the implementation
|
|
91
|
-
harnex run claude --id cl-review --tmux
|
|
108
|
+
harnex run claude --id cl-review --tmux cl-review
|
|
92
109
|
harnex send --id cl-review --message "Review changes against /tmp/plan.md, write /tmp/review.md" --wait-for-idle
|
|
93
110
|
harnex stop --id cl-review
|
|
94
111
|
```
|
|
95
112
|
|
|
113
|
+
For delegated work, pass the same value to `--id` and `--tmux` so
|
|
114
|
+
`harnex status`, `harnex pane`, logs, and the tmux window name all line up.
|
|
115
|
+
|
|
96
116
|
Harnex ships CLI-readable agent guides for this pattern:
|
|
97
117
|
|
|
98
118
|
- **[Dispatch](guides/01_dispatch.md)** — the fire-and-watch pattern:
|
|
@@ -133,19 +153,47 @@ Presets map to stall policy defaults:
|
|
|
133
153
|
|
|
134
154
|
Explicit `--stall-after` and `--max-resumes` flags override preset defaults.
|
|
135
155
|
|
|
156
|
+
For one-shot startup prompts, add `--auto-stop`. It requires `--context`
|
|
157
|
+
and stops the session after the first task completion or PTY prompt return.
|
|
158
|
+
|
|
136
159
|
For structured subscriptions, stream JSONL events:
|
|
137
160
|
|
|
138
161
|
```bash
|
|
139
|
-
harnex events --id cx-impl-42
|
|
162
|
+
harnex events --id cx-impl-42
|
|
140
163
|
```
|
|
141
164
|
|
|
142
165
|
Schema details and compatibility policy are documented in
|
|
143
166
|
[docs/events.md](docs/events.md).
|
|
144
167
|
|
|
168
|
+
## Dispatch history
|
|
169
|
+
|
|
170
|
+
Every finished `harnex run` appends one JSON line to
|
|
171
|
+
`<repo>/.harnex/dispatch.jsonl`. Harnex finds the repo by walking up from the
|
|
172
|
+
run directory until it sees `.git/`; sessions outside a git repo write to
|
|
173
|
+
`~/.local/state/harnex/dispatch.jsonl`.
|
|
174
|
+
|
|
175
|
+
Use `harnex history` to inspect it:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
harnex history
|
|
179
|
+
harnex history --json | jq .
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Dispatch briefs can declare soft budget metadata through `--meta`:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
harnex run codex --meta '{"read_budget_lines":2000,"output_ceiling_lines":800}' ...
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Those declared values are copied into summary `meta`. Terminal summary
|
|
189
|
+
`actual` also records rough measurements for downstream enforcement:
|
|
190
|
+
`lines_changed`, `output_lines`, `output_bytes`, and `event_records`.
|
|
191
|
+
Harnex records the data only; consumers decide whether to fail closed.
|
|
192
|
+
|
|
145
193
|
## Long-running and overnight work
|
|
146
194
|
|
|
147
195
|
For plain "force-resume on stall" recovery, use
|
|
148
|
-
`harnex run --watch --preset impl`.
|
|
196
|
+
`harnex run codex --watch --preset impl --context "Read /tmp/task.md"`.
|
|
149
197
|
|
|
150
198
|
A **buddy** is for richer reasoning: doc drift checks, semantic sanity checks,
|
|
151
199
|
and multi-session correlation. It's still just another harnex session.
|
|
@@ -155,8 +203,8 @@ and multi-session correlation. It's still just another harnex session.
|
|
|
155
203
|
Spawn a buddy alongside a long-running implementation worker:
|
|
156
204
|
|
|
157
205
|
```bash
|
|
158
|
-
harnex run codex --id worker-42 --tmux
|
|
159
|
-
harnex run claude --id buddy-42 --tmux
|
|
206
|
+
harnex run codex --id worker-42 --tmux worker-42
|
|
207
|
+
harnex run claude --id buddy-42 --tmux buddy-42
|
|
160
208
|
harnex send --id buddy-42 --message "$(cat <<'EOF'
|
|
161
209
|
Watch harnex session worker-42.
|
|
162
210
|
Every 5 minutes: run `harnex pane --id worker-42 --lines 30`.
|
|
@@ -165,7 +213,7 @@ nudge it: `harnex send --id worker-42 --message "Continue your task."`.
|
|
|
165
213
|
When it exits, report back:
|
|
166
214
|
tmux send-keys -t "$HARNEX_SPAWNER_PANE" "worker-42 done" Enter
|
|
167
215
|
EOF
|
|
168
|
-
"
|
|
216
|
+
)"
|
|
169
217
|
```
|
|
170
218
|
|
|
171
219
|
### Example: watch for doc drift during implementation
|
|
@@ -174,8 +222,8 @@ A buddy that checks whether a worker's code changes have left
|
|
|
174
222
|
docs out of date:
|
|
175
223
|
|
|
176
224
|
```bash
|
|
177
|
-
harnex run codex --id worker-99 --tmux
|
|
178
|
-
harnex run claude --id buddy-99 --tmux
|
|
225
|
+
harnex run codex --id worker-99 --tmux worker-99
|
|
226
|
+
harnex run claude --id buddy-99 --tmux buddy-99
|
|
179
227
|
harnex send --id buddy-99 --message "$(cat <<'EOF'
|
|
180
228
|
Watch harnex session worker-99.
|
|
181
229
|
Every 5 minutes: run `harnex pane --id worker-99 --lines 30`.
|
|
@@ -187,7 +235,7 @@ inline comments) that are now stale. If so, nudge the worker:
|
|
|
187
235
|
When the worker exits, report a summary to the invoker:
|
|
188
236
|
tmux send-keys -t "$HARNEX_SPAWNER_PANE" "worker-99 done. Doc drift: <yes/no>" Enter
|
|
189
237
|
EOF
|
|
190
|
-
"
|
|
238
|
+
)"
|
|
191
239
|
```
|
|
192
240
|
|
|
193
241
|
### The invoker doesn't need to be a harnex session
|
|
@@ -213,7 +261,9 @@ See [recipes/03_buddy.md](recipes/03_buddy.md) for the full pattern.
|
|
|
213
261
|
| `harnex pane --id <id>` | Capture the agent's tmux screen (`--follow` for live) |
|
|
214
262
|
| `harnex logs --id <id>` | Read session transcript (`--follow` to tail) |
|
|
215
263
|
| `harnex events --id <id>` | Stream structured session events (`--snapshot` for non-blocking dump) |
|
|
216
|
-
| `harnex
|
|
264
|
+
| `harnex history` | List completed dispatches from `.harnex/dispatch.jsonl` |
|
|
265
|
+
| `harnex wait --id <id>` | Block until exit, a target state, or `--until task_complete` |
|
|
266
|
+
| `harnex doctor` | Run adapter dependency preflight checks; add `--sweep` for read-only session drift diagnostics |
|
|
217
267
|
| `harnex guide` | Getting started walkthrough |
|
|
218
268
|
| `harnex agents-guide` | Agent-facing dispatch, chain, buddy, monitoring, and naming guides |
|
|
219
269
|
| `harnex recipes` | Tested workflow patterns |
|
data/TECHNICAL.md
CHANGED
|
@@ -29,6 +29,7 @@ harnex run codex -- --cd ~/other/repo
|
|
|
29
29
|
| `--watch-file PATH` | Auto-send a file-change hook (`--watch PATH`/`--watch=PATH` legacy) |
|
|
30
30
|
| `--context TXT` | Give the agent a task on startup |
|
|
31
31
|
| `--auto-stop` | With `--context`, stop after the first task completion |
|
|
32
|
+
| `--fast` | For Codex, use `service_tier="fast"` instead of default `flex` |
|
|
32
33
|
| `--timeout SEC` | Wait budget for detached registration |
|
|
33
34
|
|
|
34
35
|
### `harnex send` — Talk to a running agent
|
|
@@ -88,6 +89,16 @@ harnex wait --id worker
|
|
|
88
89
|
harnex wait --id worker --until prompt --timeout 300
|
|
89
90
|
```
|
|
90
91
|
|
|
92
|
+
### `harnex history` — List completed dispatches
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
harnex history
|
|
96
|
+
harnex history --json | jq .
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Reads `<repo>/.harnex/dispatch.jsonl`, where `<repo>` is found by walking up
|
|
100
|
+
until `.git/` is present. Use `--global` for the no-repo fallback file.
|
|
101
|
+
|
|
91
102
|
### `harnex logs` — Read session transcripts
|
|
92
103
|
|
|
93
104
|
```bash
|
|
@@ -247,6 +258,7 @@ Schema details and compatibility guarantees are in [docs/events.md](docs/events.
|
|
|
247
258
|
├── base.rb adapter interface
|
|
248
259
|
├── generic.rb fallback adapter for any CLI
|
|
249
260
|
├── codex.rb codex-specific behavior
|
|
261
|
+
├── opencode.rb opencode-specific behavior
|
|
250
262
|
└── claude.rb claude-specific behavior
|
|
251
263
|
```
|
|
252
264
|
|
|
@@ -365,6 +377,17 @@ The adapter reads the screen and returns a state hash:
|
|
|
365
377
|
- Multi-step submit: types text, then sends Enter after a
|
|
366
378
|
short delay so pasted prompts are actually submitted
|
|
367
379
|
|
|
380
|
+
### OpenCode Adapter
|
|
381
|
+
|
|
382
|
+
- Uses optimistic PTY prompt detection to avoid inbox deadlock
|
|
383
|
+
when OpenCode's alternate-screen TUI omits stable plain-text
|
|
384
|
+
prompt markers in snapshots.
|
|
385
|
+
- Stop sequence sends a double Ctrl+C to match OpenCode's native
|
|
386
|
+
terminal shutdown path (interrupt first, force-quit second if
|
|
387
|
+
needed).
|
|
388
|
+
- Multi-step submit mirrors Claude/Codex PTY behavior: text first,
|
|
389
|
+
then Enter with a short delay.
|
|
390
|
+
|
|
368
391
|
## State Machine
|
|
369
392
|
|
|
370
393
|
`SessionState` tracks the agent's readiness:
|
data/guides/01_dispatch.md
CHANGED
|
@@ -86,6 +86,8 @@ Codex flag forms differ between transports. The default JSON-RPC adapter
|
|
|
86
86
|
`-c model="<name>"` instead. The legacy PTY adapter (`harnex run codex
|
|
87
87
|
--legacy-pty`) still accepts `-m`. Harnex rejects `-m` early on JSON-RPC
|
|
88
88
|
with an actionable error rather than letting the subprocess boot-disconnect.
|
|
89
|
+
Codex app-server runs also default to `service_tier="flex"`; add
|
|
90
|
+
`--fast` to use `service_tier="fast"`.
|
|
89
91
|
|
|
90
92
|
## Send
|
|
91
93
|
|
data/lib/harnex/adapters/base.rb
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
require "open3"
|
|
2
|
+
require "timeout"
|
|
3
|
+
|
|
1
4
|
module Harnex
|
|
2
5
|
module Adapters
|
|
3
6
|
class Base
|
|
4
7
|
PROMPT_PREFIXES = [">", "\u203A", "\u276F"].freeze
|
|
8
|
+
AGENT_VERSION_TIMEOUT_SECONDS = 2.0
|
|
5
9
|
|
|
6
10
|
# Adapter contract — subclasses MUST implement:
|
|
7
11
|
# base_command -> Array[String] CLI args to spawn
|
|
@@ -26,6 +30,21 @@ module Harnex
|
|
|
26
30
|
:pty
|
|
27
31
|
end
|
|
28
32
|
|
|
33
|
+
# Vendor of the underlying agent — populates DISPATCH meta.agent_provider.
|
|
34
|
+
# Subclasses override (claude → "anthropic", codex → "openai").
|
|
35
|
+
def provider
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Probes `<base_command.first> --version` with a short timeout and
|
|
40
|
+
# memoizes the result for the adapter's lifetime. Returns nil when
|
|
41
|
+
# the binary is missing, exits non-zero, or stalls past the timeout.
|
|
42
|
+
def agent_version
|
|
43
|
+
return @agent_version if defined?(@agent_version)
|
|
44
|
+
|
|
45
|
+
@agent_version = probe_agent_version
|
|
46
|
+
end
|
|
47
|
+
|
|
29
48
|
def describe
|
|
30
49
|
{ transport: transport }
|
|
31
50
|
end
|
|
@@ -108,6 +127,20 @@ module Harnex
|
|
|
108
127
|
|
|
109
128
|
protected
|
|
110
129
|
|
|
130
|
+
def probe_agent_version
|
|
131
|
+
cli = base_command.first
|
|
132
|
+
return nil unless cli
|
|
133
|
+
|
|
134
|
+
Timeout.timeout(AGENT_VERSION_TIMEOUT_SECONDS) do
|
|
135
|
+
stdout, status = Open3.capture2(cli, "--version", err: File::NULL, in: File::NULL)
|
|
136
|
+
return nil unless status.success?
|
|
137
|
+
|
|
138
|
+
stdout.to_s.lines.first&.strip
|
|
139
|
+
end
|
|
140
|
+
rescue Errno::ENOENT, Timeout::Error, StandardError
|
|
141
|
+
nil
|
|
142
|
+
end
|
|
143
|
+
|
|
111
144
|
def submit_bytes
|
|
112
145
|
"\r"
|
|
113
146
|
end
|