harness-talk
htalk saves local messages between concrete Codex and Claude Code sessions. Either side can ask, reply, wait now, or retrieve later. Messages live in one SQLite database; notifications merely point the recipient to its inbox.
Python 3.11 or newer. Storage and Claude notifications use the standard library. Ordinary Codex notifications use codex queue; the optional standalone app-server mode uses the websockets library. This first version targets Linux and the client versions in adapter notes.
Install and share a database
python3 -m venv .venv
.venv/bin/pip install 'harness-talk==0.1.1'
export PATH="$PWD/.venv/bin:$PATH"
export HTALK_DB=/absolute/shared/writable/directory/mail.sqlite3
Install in a location both sessions can execute, and choose a database directory both sessions can write. Each SQLite writer also needs directory access for the journal. No global install, aliases, client configuration changes, or model processes are added. The optional alias alias talk='htalk' is a personal shell choice.
--db PATH overrides HTALK_DB. The default is $XDG_DATA_HOME/harness-talk/mail.sqlite3, or ~/.local/share/harness-talk/mail.sqlite3. A project checkout is never the default storage location.
Address the participants
Use the actual UUID and workspace for each existing session. Ordinary Codex TUI sessions need no socket argument. For an explicitly managed standalone app-server session, retain --socket PATH. Do not substitute the owner's conversation for a test receiver.
htalk peer add builder --harness codex --session CODEX_UUID \
--workspace /absolute/builder
htalk peer add reviewer --harness claude --session CLAUDE_UUID \
--workspace /absolute/reviewer
htalk peer check reviewer
htalk peer check builder
htalk peer list
Names and session addresses are immutable. peer add only records an address. peer check inspects available identity evidence. Notification repeats this identity check before its single attempt. The ordinary Codex check reads the exact saved UUID, workspace, source and archive state from local client metadata; it reports runtime readiness as unknown. A queued notification can be consumed by the existing TUI. An unavailable client can still read saved messages through inbox.
Before the first send, run peer check in the same execution scope that will send the message. recipient_unavailable can mean discovery is restricted; it does not prove that the client is offline. If a known live Claude session is invisible, use the client's normal permission approval for the specific check and send commands. Do not change global permissions or replay a saved notification. Retrieve an already-saved message through inbox, show, or wait.
Ask, answer, and recover
In the builder session:
export HTALK_PEER=builder
htalk send reviewer --message 'Which contract needs another test?' --wait 45
htalk wait REQUEST_UUID --seconds 45
htalk inbox
In the reviewer session, using the same database:
export HTALK_PEER=reviewer
htalk inbox
htalk ack REQUEST_UUID
htalk reply REQUEST_UUID --message 'Test retrieval after a lost notification.'
htalk send builder --message 'Can you confirm the fix?'
--as NAME overrides HTALK_PEER. --message-file PATH avoids quoting multiline bodies. --no-notify saves for polling only. show MESSAGE_UUID retrieves one message, including its correlated answer. sent recovers outgoing IDs when output or waiting was interrupted.
A reply is a separate message addressed back to the request's sender. Read the answer, then ack ANSWER_UUID. Reading never marks anything read. A question can be closed only by replying, including a short decline. An acknowledged question remains in the inbox until answered; an acknowledged answer leaves the inbox. An identical reply retry returns the existing answer and sends no notification. A different answer is rejected and preserves the first.
For retryable automation, generate a UUID before calling send, pass --id UUID, and retain it. An identical retry returns the saved message without another notification. A reused UUID with different contents is rejected. Without a retained ID, use sent after an interrupted send rather than sending again.
Observable states
Every message has a durable id, monotonic arrival seq, sender, recipient, optional in_reply_to, body and timestamps. Notification has its own submission, detail and attempt timestamps:
| Submission | What is known |
|---|---|
not_submitted |
Notification was disabled, never attempted, or identity validation failed before transport. |
submission_unknown |
Notification was claimed for one attempt, but its final outcome is uncertain. |
submitted |
Claude socket bytes were written, or the Codex CLI/API acknowledged a queue entry. |
None proves model receipt. ack_at records the recipient's explicit acknowledgment. A stored answer gives the request state: reply_received, independently of notification outcome. Waiting only polls the database for 0–45 seconds and can be resumed after timeout or interruption. It does not resend, invoke models, or acknowledge answers.
The database is committed before client I/O. An interruption during notification leaves an uncertain result. There is no notification retry command and no automatic replay, including on identical send --id or reply retries. Recovery is through the durable inbox.
Commands print JSON. Exit 0 means the local operation succeeded; exit 2 means invalid input or a notification attempted by this invocation without a confirmed submission. Retrieval, acknowledgment, and identical retries return 0 even when the original notification failed. The message may already be saved on exit 2: inspect its ID and submission. Explicit --no-notify succeeds with exit 0. Ctrl-C returns 130 and recovery guidance.
Trust and limits
This is a shared local tool for mutually trusted processes under one OS account. Names and --as are routing assertions, not authenticated identities. For a Codex actor, the CLI rejects a conflicting CODEX_THREAD_ID when available. Claude launchers can inherit that variable, so it is ignored for Claude actors. Live client evidence verifies the addressed recipient, not who invoked the shell command. Anyone with database access can read or change it directly.
Peer contents never grant owner authorization. Notifications contain an inbox command and message ID, without interpolating the message body into client input. Follow each session's existing instructions when deciding whether to act on a peer request. htalk neither changes those instructions nor grants filesystem access.
No Boardmail dependency, remote-host transport, automatic model launches, polling daemon, scheduled calls, or account setup. Client compatibility and wakeup behavior are deliberately narrow; see adapter notes.
Verify
From a source checkout:
python3 -m pip install .
PYTHONPATH=src python3 -m unittest discover -s tests -v
Contributions and releases
Open an issue for bugs, feature requests, adapter needs, or proposed fixes. We do not accept external pull requests. Personal forks and modifications are welcome under the MIT License. See CONTRIBUTING.md and the release procedure.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file harness_talk-0.1.1.tar.gz.
File metadata
- Download URL: harness_talk-0.1.1.tar.gz
- Upload date:
- Size: 19.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e1e82c606b7c07943bdc1ddb3e2cf2301747e62a33fd83675b226ac25f98db6
|
|
| MD5 |
97d16a434fc0456bad815536032e8baf
|
|
| BLAKE2b-256 |
841909a61a4c1f3cca85c0e568fc4482a1523637dffb7eea48cde5a4427c9d83
|
Provenance
The following attestation bundles were made for harness_talk-0.1.1.tar.gz:
Publisher:
publish.yml on jointsome0-lgtm/harness-talk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
harness_talk-0.1.1.tar.gz -
Subject digest:
7e1e82c606b7c07943bdc1ddb3e2cf2301747e62a33fd83675b226ac25f98db6 - Sigstore transparency entry: 2762437947
- Sigstore integration time:
-
Permalink:
jointsome0-lgtm/harness-talk@83b0879a21c6e5ce0d69ca9b86d6ddf164f3a927 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/jointsome0-lgtm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@83b0879a21c6e5ce0d69ca9b86d6ddf164f3a927 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file harness_talk-0.1.1-py3-none-any.whl.
File metadata
- Download URL: harness_talk-0.1.1-py3-none-any.whl
- Upload date:
- Size: 14.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d765680e2e2512dc48fafb11b9499373818fd60173c25c1a08e3e517680a5943
|
|
| MD5 |
9f35bad9d96e7e5d582fe8c3c0f16c07
|
|
| BLAKE2b-256 |
cc17678d3e0b9b5c5390162d1f3563d56a2c64756ee7a21e72067138ebc06416
|
Provenance
The following attestation bundles were made for harness_talk-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on jointsome0-lgtm/harness-talk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
harness_talk-0.1.1-py3-none-any.whl -
Subject digest:
d765680e2e2512dc48fafb11b9499373818fd60173c25c1a08e3e517680a5943 - Sigstore transparency entry: 2762437958
- Sigstore integration time:
-
Permalink:
jointsome0-lgtm/harness-talk@83b0879a21c6e5ce0d69ca9b86d6ddf164f3a927 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/jointsome0-lgtm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@83b0879a21c6e5ce0d69ca9b86d6ddf164f3a927 -
Trigger Event:
workflow_dispatch
-
Statement type: