@hraness/message-like-me 0.8.11 → 0.8.12

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.
@@ -0,0 +1,277 @@
1
+ # Start using Textbutler
2
+
3
+ Textbutler is a local Mac assistant for selected conversations. Start with its
4
+ inbox and replies you write yourself. Automatic replies stay paused until you
5
+ choose a ready agent and explicitly enable a contact.
6
+
7
+ AI replies require a verified Textbutler bundle with reviewed composition
8
+ admission and use a separately installed [xcb](https://github.com/hraness/xcb)
9
+ native runtime and an explicitly connected subscription account. This local
10
+ pilot requires current xcb admission and a successful account check. Installation
11
+ does not enable replies, and live messaging still needs verification with your
12
+ chosen recipient. Claude API retains a separate trusted runtime admission gate.
13
+
14
+ ## Open the guided terminal
15
+
16
+ From your Textbutler checkout, with Bun 1.3.14:
17
+
18
+ ```sh
19
+ bun install --frozen-lockfile --ignore-scripts
20
+ bun run textbutler tui
21
+ ```
22
+
23
+ For daily use, you can install a local command that works outside the checkout:
24
+
25
+ ```sh
26
+ bun run textbutler:install
27
+ ~/.local/bin/textbutler
28
+ ```
29
+
30
+ The installer builds a self-contained local pilot, checks the reviewed
31
+ composition receipt against current source and contact profiles, and verifies
32
+ its contents and exact Bun runtime before use. Missing or stale composition
33
+ evidence blocks the build. Source daemon startup carries no such admission and
34
+ keeps subscription inference unavailable. It starts no services and connects no accounts.
35
+ An existing, different `textbutler` command is preserved. This is a local build,
36
+ not a signed public release. Connect xcb separately for AI replies. Keep the same
37
+ Bun runtime installed. To upgrade a verified existing installation, stop its
38
+ services and run `bun run textbutler:install --upgrade`. The installer checks
39
+ the existing launcher and complete installed version, preserves them for
40
+ rollback, and atomically switches the command. It never replaces an unrelated
41
+ command or changes your settings. Restart the installed daemon afterward.
42
+
43
+ The terminal has numbered actions for setup, app connections, conversations,
44
+ replies, contacts, pause and the menu bar. Enter goes back from a selection;
45
+ `q` or Ctrl-C closes the terminal. It does not stop an installed background
46
+ service. Commands below use `bun run textbutler`; the help abbreviates that
47
+ prefix to `textbutler`. You can use `~/.local/bin/textbutler` for these commands.
48
+ Use the installed terminal when setting up daemon startup for AI replies.
49
+
50
+ Choose **Setup & readiness** first. It creates private settings and points you
51
+ to messaging setup. After saving your connections, return to **Setup & readiness**
52
+ to start the service at login. Repeating setup preserves existing settings and
53
+ contact activation. Use `doctor` at any time to see connection status, remaining
54
+ setup steps and whether reply generation is actually available.
55
+
56
+ ## Connect your messaging apps
57
+
58
+ Textbutler uses an existing Ghostget installation for account sign-in, permissions
59
+ and messaging access. You need its physical executable path and the exact account
60
+ ID; Textbutler does not guess an identity. Native iMessage can use the app setup
61
+ flow below. Set up other messaging accounts in Ghostget first.
62
+
63
+ Choose **Connect messaging apps** in the terminal:
64
+
65
+ - **iMessage:** the native Mac Messages connection.
66
+ - **WhatsApp:** a native linked device; connecting explicitly starts sync.
67
+ - **Beeper:** linked apps such as Signal, Telegram, Instagram, WhatsApp and
68
+ iMessage. Keep Beeper Desktop open with its local API enabled. The current
69
+ Ghostget automation adapter supports direct conversations and text replies.
70
+
71
+ Beeper automation requires Ghostget 0.18.14 or later with the
72
+ `ghostget.messaging-automation/1` protocol. A connection being configured does
73
+ not prove that it is connected. Check it before selecting conversations.
74
+
75
+ Initial setup can also be scripted. Replace the paths and IDs with your own:
76
+
77
+ ```sh
78
+ bun run textbutler setup \
79
+ --ghostget /absolute/path/to/ghostget \
80
+ --account imessage:messages \
81
+ --account beeper:beeper-main
82
+ ```
83
+
84
+ If Ghostget's entrypoint is a TypeScript file, also pass
85
+ `--runtime /absolute/path/to/bun`. An optional `--state-home` selects its existing
86
+ state directory. Configuration is additive: this command preserves existing accounts and agent
87
+ settings and refuses to replace an account identity. Stop the service before
88
+ adding a connection, then run setup with the same connector paths and the new
89
+ account ID. To change existing bindings, stop the
90
+ foreground service or uninstall its login entry, review private
91
+ `state/host.json`, then restart or reinstall the service. Uninstall retains your
92
+ settings, contact memory and activity.
93
+
94
+ For foreground use with AI, run `~/.local/bin/textbutler daemon run` in another
95
+ terminal. For login startup, use `~/.local/bin/textbutler daemon install`. Use
96
+ your chosen installation prefix if different. Source daemon commands remain
97
+ available for the manual pilot; keep that checkout at its current path while a
98
+ source service is installed.
99
+
100
+ See [messaging app support](messaging-apps.md) for Beeper limitations and native
101
+ alternatives, including requirements that affect Telegram AI processing.
102
+
103
+ ## Give TextButler access to iMessage
104
+
105
+ Use the native app when you want macOS Full Disk Access to belong to TextButler.
106
+ The app supervises its pinned runtime and background service. Build it from an
107
+ already installed, verified payload on your Mac:
108
+
109
+ ```sh
110
+ bun run textbutler:app build \
111
+ --from /absolute/installed/textbutler/version \
112
+ --output /absolute/new/app-build-directory
113
+ bun run textbutler:app install --from /absolute/new/app-build-directory
114
+ ```
115
+
116
+ The default destination is `~/Applications/TextButler.app`. Building and
117
+ installing the app does not start replies or change macOS permissions. In
118
+ **System Settings → Privacy & Security → Full Disk Access**, click **+**, press
119
+ **Command-Shift-G**, enter `~/Applications/TextButler.app`, and choose **Open**.
120
+ Enable its switch. macOS may require your password in its own dialog.
121
+
122
+ Configure the exact Ghostget `src/cli.ts`, Bun runtime, private state directory
123
+ and `imessage:ACCOUNT` binding using `setup` above. This development version pins
124
+ Ghostget 0.18.21 and its reviewed `imsg` helper artifact. Native setup provisions
125
+ that pinned helper into the connector state directory (`imessage transport install`)
126
+ before linking; a missing or mismatched artifact stops setup instead of reaching
127
+ messaging. Setup links only
128
+ that account to this Mac's Messages store and
129
+ enables Ghostget's account-specific automation read, text and attachment-send capabilities.
130
+ Contact selection and automatic replies remain separate choices.
131
+
132
+ With the background service stopped, run the setup role through its verified
133
+ app launch:
134
+
135
+ ```sh
136
+ bun run textbutler:app imessage-setup \
137
+ --data-dir "$HOME/Library/Application Support/Textbutler"
138
+ ```
139
+
140
+ After app setup completes, use the installed `daemon install` command to
141
+ register its background service. If an older service is installed, first use
142
+ `daemon uninstall`; this preserves your settings and contacts. Startup verifies
143
+ the native app receipt and all pinned artifacts. A changed app or runtime
144
+ requires a verified rebuild and reinstall. Local apps use ad-hoc signatures,
145
+ so macOS may require permission again after a rebuild. Check `doctor` and the
146
+ messaging connection before enabling a contact.
147
+
148
+ To upgrade an installed app, stop the service and finish or reconcile any
149
+ pending setup attempt, then build from the new installed payload. Install it
150
+ with the following command:
151
+
152
+ ```sh
153
+ bun run textbutler:app install --from /absolute/new/app-build-directory --upgrade
154
+ ```
155
+
156
+ The upgrade verifies both versions and retains the previous signed
157
+ app and receipt. If it reports an uncertain transition, preserve its records
158
+ and reconcile that transition before retrying. Recheck Full Disk Access and
159
+ Messages Automation after the upgrade.
160
+
161
+ See [local data](local-data.md) for retained setup records and installation data
162
+ removal. Repeating setup preserves an already linked account and its identity.
163
+
164
+ For JSON commands to read, summarize, compose and send messages from another
165
+ agent, see the [agent CLI guide](agent-cli.md).
166
+
167
+ ## Connect your AI subscription
168
+
169
+ Install an xcb native build with `generate` support and follow its
170
+ [account setup](https://github.com/hraness/xcb#native-xcb). Sign in through xcb,
171
+ then use `xcb accounts` and `xcb models` to obtain the exact account ID and full
172
+ model key. Credentials remain in xcb's private state.
173
+
174
+ With the Textbutler daemon stopped, connect that installation:
175
+
176
+ ```sh
177
+ bun run textbutler setup \
178
+ --xcb /absolute/path/to/xcb \
179
+ --xcb-state /absolute/path/to/xcb-state \
180
+ --xcb-account claude:ACCOUNT_ID \
181
+ --xcb-model FULL_MODEL_KEY
182
+ ```
183
+
184
+ For Codex, use `--xcb-account codex:ACCOUNT_ID` and a matching observed model.
185
+ Repeat setup to add a second account. The command pins the executable bytes and
186
+ explicit routing; it does not activate a contact. Setup refuses changes to an
187
+ existing binary or account/model binding. After an xcb upgrade, stop the daemon
188
+ and review its private `state/host.json` binding before updating the executable
189
+ digest. Retain account and custody state.
190
+
191
+ Start or restart the installed daemon, then check the account:
192
+
193
+ ```sh
194
+ bun run textbutler providers list
195
+ bun run textbutler providers check TEXTBUTLER_ACCOUNT_ID
196
+ bun run textbutler doctor
197
+ ```
198
+
199
+ Use the account ID returned by `providers list`. This checks xcb's current
200
+ capabilities and admission without making a model turn. Resolve any unavailable
201
+ or recovery status before asking for an unsent suggestion. See the
202
+ [subscription connection](native-subscription.md) for the execution and custody
203
+ contract. Source and bundle integrity checks alone do not qualify an AI provider.
204
+
205
+ ## Add one conversation and try the inbox
206
+
207
+ Choose **Add a conversation**, select the exact person and app, and choose
208
+ whether to import recent text history. Importing history never sends anything.
209
+ The new contact has automatic replies off.
210
+
211
+ Choose **Inbox & replies**. Textbutler lists unanswered incoming messages in your
212
+ selected conversations. Choose **Type a reply**, review the recipient and the
213
+ complete disclosed text, then type `send` if you want to send it. This path does
214
+ not require an AI account. Leaving the review sends nothing.
215
+
216
+ After the connected agent passes its readiness check, choose it under **Manage a contact**.
217
+ A suggestion is an unsent draft. Review every action before sending. The CLI
218
+ supports the same review:
219
+
220
+ ```sh
221
+ bun run textbutler inbox
222
+ bun run textbutler replies suggest CONTACT
223
+ bun run textbutler replies show DRAFT
224
+ bun run textbutler replies send DRAFT DIGEST
225
+ ```
226
+
227
+ Use the exact digest shown by `replies show`. Changes to a draft or its attachment
228
+ bytes invalidate that review. A result of `submitted` means the transport
229
+ accepted the action; it is not proof that the recipient received or read it.
230
+
231
+ A pending command prints a job ID. Use `jobs show JOB_ID` with the same data
232
+ directory. Do not repeat an uncertain send or grant operation. Recovery fences
233
+ remain until the operation can be reconciled; restarting does not erase them.
234
+
235
+ ## Use the menu bar
236
+
237
+ Choose **Menu bar companion** in the terminal, or run:
238
+
239
+ ```sh
240
+ bun run textbutler menubar start
241
+ bun run textbutler menubar install
242
+ ```
243
+
244
+ `start` opens it now; `install` registers login startup. The first start retrieves
245
+ and verifies the pinned shared native companion. No local Rust build is needed.
246
+
247
+ The menu gives you pause, connection checks, conversation selection, per-contact
248
+ activation, agent selection, the reply inbox and recent activity. Draft previews
249
+ are intentionally labeled: use the terminal to review complete outgoing actions
250
+ before sending. The menu cannot send hidden or truncated draft content.
251
+
252
+ Menu startup and daemon startup are separate. Quitting the menu leaves the
253
+ installed daemon running. `menubar stop` closes the menu; `daemon uninstall`
254
+ unregisters the background service and retains your data.
255
+
256
+ ## Turn on automatic replies only when ready
257
+
258
+ Once a qualified agent and messaging connection are ready, select the agent,
259
+ choose the contact's response mode, enable that contact, then resume. These are
260
+ separate choices. The readiness view must show actual engine and transport
261
+ availability; successful setup alone is insufficient.
262
+
263
+ ```sh
264
+ bun run textbutler contacts account CONTACT ACCOUNT
265
+ bun run textbutler contacts mode CONTACT keyword --keyword butler
266
+ bun run textbutler contacts enable CONTACT
267
+ bun run textbutler resume
268
+ ```
269
+
270
+ `pause` stops automatic replies globally. `contacts disable CONTACT` also revokes
271
+ that contact's grant. Owner-confirmed replies remain a separate explicit action
272
+ while automatic replies are paused.
273
+
274
+ Your Mac must be awake and signed in. Begin with one conversation and confirm
275
+ behavior on an agreed test recipient before relying on automation. See
276
+ [agent setup](../../packages/textbutler/PROVIDERS.md) for the current engine
277
+ qualification requirements.
@@ -6,10 +6,18 @@ actions. Textbutler communicates with its own Ghostget owner process through
6
6
  `ghostget messaging automation serve --stdio`; it does not share the Ghostget
7
7
  menu companion's private helper or open provider databases.
8
8
 
9
- [Ghostget 0.18.2](https://github.com/hraness/ghostget/releases/tag/v0.18.2) is the
10
- verified published dependency for this contract. Its package includes both
11
- private messaging runtimes; installation is explicit and starts no provider.
12
- Older generic CLI routes do not become automation grants.
9
+ The automation contract was first admitted with
10
+ [Ghostget 0.18.2](https://github.com/hraness/ghostget/releases/tag/v0.18.2).
11
+ This development version of native TextButler iMessage setup pins Ghostget
12
+ 0.18.21. Its matching artifact and live conversation checks remain pending.
13
+ The required contract preserves the native helper's resource bundle, avoids
14
+ opening unrelated protected folders during state validation, and exposes bounded
15
+ discovery diagnostics without message bodies. Valid native chat rows without
16
+ usable participant metadata are omitted from partial discovery results; they
17
+ cannot become sending targets. Follow the
18
+ [current setup guide](getting-started.md#give-textbutler-access-to-imessage).
19
+ Installation is explicit and starts no provider. Older generic CLI routes do
20
+ not become automation grants.
13
21
 
14
22
  ## Owner process
15
23
 
@@ -66,8 +74,12 @@ account and managed permission admit them. Message targets must belong to the
66
74
  enrolled conversation. Attachment and sticker paths are resolved by Textbutler's
67
75
  contact file broker; Ghostget receives admitted bytes, not arbitrary paths.
68
76
 
69
- Every response starts with disclosed text. For a nontext response, Textbutler
70
- inserts a companion such as `🤖{ … }` before the rich actions. Execution stops
77
+ Every response starts with disclosed text while disclosure markers remain
78
+ configured. For a nontext response, Textbutler inserts a companion such as
79
+ `🤖{ … }` before the rich actions; when the owner clears all three disclosure
80
+ fields no companion is added and butler authorship is carried by the accepted
81
+ message IDs the run receipt returns to Textbutler's journal instead of by
82
+ visible text. Execution stops
71
83
  when a preceding action fails or the conversation changes. An accepted receipt
72
84
  does not claim delivery.
73
85
 
@@ -0,0 +1,50 @@
1
+ # Local data
2
+
3
+ Textbutler keeps settings, contact memory, reply journals and setup records in
4
+ `~/Library/Application Support/Textbutler`, unless you select another data
5
+ directory. These files are private to the Mac user. XCB and Ghostget keep their
6
+ own accounts and credentials in their separately configured state directories.
7
+
8
+ Explicit CLI media imports live in the selected contact's private `outbox`.
9
+ Each file is limited to 16 MiB and shares the contact workspace's 500-file
10
+ budget. Repeating an import of the same bytes and extension reuses its content
11
+ identity. Imports remain after a draft expires so the owner can review or reuse
12
+ them. A complete installation data erasure removes these copies without
13
+ changing their original source files.
14
+
15
+ iMessage setup retains one small account binding so a repeated setup cannot
16
+ silently authorize a different account under the same name. Failed attempts do
17
+ not replace that binding. Setup results contain bounded progress and digests;
18
+ they contain no message bodies or credentials.
19
+
20
+ An app upgrade retains the previous signed app and its receipt under
21
+ `~/Applications/.textbutler-app-upgrades`. Up to 32 transitions are retained.
22
+ A pending transition also keeps `state/macos-app-upgrade.json` in the data
23
+ directory until completion or rollback is proven. Keep both locations intact
24
+ while an upgrade is unresolved.
25
+
26
+ The explicit legacy iMessage crash reconciliation script accepts a private
27
+ witness for the Ghostget 0.18.16 startup failure. It checks the original crash,
28
+ app, connector, account and process state, then archives a bounded settlement
29
+ record before releasing that attempt's setup marker. It never retries setup or
30
+ changes accounts, permissions or messages. It refuses other failure types.
31
+
32
+ ## Remove Textbutler data
33
+
34
+ Use `textbutler daemon uninstall` and stop the menu companion before removing
35
+ local data. Confirm that Textbutler and its connector operations have stopped.
36
+ If an operation has an uncertain outcome, reconcile it and retain the evidence
37
+ needed to settle that operation first.
38
+
39
+ To erase an installation, the owner can then delete its complete Textbutler
40
+ data directory. This removes settings, contact memory, journals, setup results
41
+ and the iMessage account binding. It does not delete Messages history, Ghostget
42
+ or XCB accounts, or macOS permission grants. Removing individual binding or
43
+ custody records is not a supported way to replace an account or retry a failed
44
+ operation. Reinstalling the command and uninstalling the background service
45
+ both preserve data by default.
46
+
47
+ After all app transitions are settled and the installation has stopped, the
48
+ owner may also delete retained app upgrade directories when their rollback
49
+ copies are no longer needed. These directories contain app artifacts and
50
+ upgrade receipts; they do not contain message history or provider credentials.
@@ -0,0 +1,121 @@
1
+ # Messaging apps
2
+
3
+ Textbutler can use native iMessage and WhatsApp connections through Ghostget,
4
+ or a Beeper connection for several messaging apps at once. For a Mac with
5
+ iMessage, WhatsApp, Signal, Telegram and Instagram, Beeper is the simplest
6
+ shared connection. Each conversation still needs its own enrollment and reply
7
+ settings. An available connection does not by itself enable automatic replies.
8
+
9
+ ## Choose a connection
10
+
11
+ | App | Current route | Other options |
12
+ | --- | --- | --- |
13
+ | iMessage | Ghostget's native Mac connection, or Beeper on that Mac | Keep the native connection for users who do not use Beeper. Apple's Messages framework creates iOS conversation extensions; it is not a Mac inbox API. |
14
+ | WhatsApp | Ghostget's reviewed linked-device connection, or Beeper | The official WhatsApp Business Platform is a separate business integration, not a connection to an ordinary personal inbox. |
15
+ | Signal | Beeper | A future `signal-cli` connection is possible, but it is unofficial and needs separate maintenance and qualification. |
16
+ | Telegram | Beeper, subject to the content-use limits below | Telegram's official TDLib supports personal client sessions. A direct connector is a future option, not currently implemented in Textbutler. |
17
+ | Instagram | Beeper | Meta's official messaging API supports professional accounts. It does not cover the same personal-inbox use case. |
18
+
19
+ Beeper documents support for these networks, with iMessage limited to macOS.
20
+ Its Desktop API runs locally while Beeper Desktop is open. Beeper recommends
21
+ on-device connections for this API; initial history can be incomplete.
22
+ See [Beeper's Desktop API overview](https://developers.beeper.com/desktop-api/)
23
+ and [connection types](https://help.beeper.com/chat-networks/using-on-device-chat-network-connections-in-beeper).
24
+ Beeper labels the API a [public beta](https://www.beeper.com/desktop-api).
25
+ Apple describes its [Messages framework](https://developer.apple.com/documentation/messages/)
26
+ as a way to create sticker packs and iOS conversation extensions.
27
+
28
+ ## Connect Beeper
29
+
30
+ 1. Install Beeper Desktop and connect the messaging accounts you want to use.
31
+ Prefer on-device connections when using this Mac as your messaging host.
32
+ 2. Enable Beeper's local API. Its current authentication guide places approved
33
+ connections under **Settings → Integrations**; some releases use
34
+ **Settings → Developers**. Authorize Ghostget using its supported Beeper
35
+ account setup. Keep credentials out of contact folders.
36
+ 3. Use a Ghostget release that includes Beeper owner automation. This support
37
+ entered Ghostget's 0.18.14 source. It requires the reviewed Beeper adapter
38
+ and pinned Beeper CLI; an older read-only export setup is insufficient.
39
+ 4. Allow Ghostget's exact Beeper automation read and text-send operations for
40
+ that account. Then select its account ID in Textbutler setup.
41
+ 5. Enroll a single conversation, inspect its identity, and start with a reviewed
42
+ reply. Enable automatic replies separately after the connection and selected
43
+ agent account pass their checks.
44
+
45
+ The current Ghostget automation route supports Beeper text sends and bounded
46
+ conversation history. Beeper's wider API also offers attachments and other
47
+ actions, but those are not yet admitted through this Textbutler route. The
48
+ connection requires Beeper Desktop to remain open. Restart and reconnect should
49
+ finish catching up before new messages can trigger a reply.
50
+ See the [Ghostget owner contract](ghostget-contract.md) and
51
+ [Beeper authentication](https://developers.beeper.com/desktop-api/auth/).
52
+
53
+ iMessage in Beeper requires the Mac's Messages data, Automation, Accessibility
54
+ and Contacts permissions. Beeper may briefly bring Messages into view. That
55
+ connection remains on the Mac where it was configured.
56
+ See [Beeper's iMessage setup guide](https://help.beeper.com/en_US/chat-networks/new-imessage-on-macos-getting-started-guide).
57
+
58
+ ## What “sent” means
59
+
60
+ Beeper returns a pending message ID when it accepts a send request. That is not
61
+ proof of delivery. The API can resolve that ID through a subsequent message
62
+ read; delivery status is available only when the network reports it. Textbutler
63
+ must preserve an uncertain result without sending the message again.
64
+ See [Beeper's send contract](https://developers.beeper.com/desktop-api-reference/resources/messages/methods/send/).
65
+
66
+ The optional Beeper WebSocket stream is experimental. Its sequence numbers
67
+ apply to one connection, so they are not durable restart cursors. A production
68
+ connector needs bounded catch-up reads after reconnection. Ghostget currently
69
+ owns the durable observation boundary for Textbutler.
70
+ See [Beeper's event stream](https://developers.beeper.com/desktop-api/websocket-experimental/).
71
+
72
+ ## Expansion without Beeper
73
+
74
+ **iMessage and WhatsApp:** Improve the existing Ghostget setup and recovery
75
+ paths first. Text and file support on iMessage should work with ordinary Mac
76
+ permissions; advanced native actions depend on separately configured support.
77
+ The current WhatsApp connection uses a reviewed private build of
78
+ [wacli](https://github.com/openclaw/wacli), an unofficial linked-device client.
79
+ Its pairing and update requirements are part of the integration.
80
+
81
+ **Telegram:** TDLib is the strongest documented route to a direct personal
82
+ client. It supplies login state changes, local storage, `getChatHistory`, and
83
+ `sendMessage`. A shipped client needs its own application ID and hash, a user
84
+ login flow, and protected local session storage.
85
+ See [Telegram's TDLib guide](https://core.telegram.org/tdlib/getting-started)
86
+ and [application registration](https://core.telegram.org/api/obtaining_api_id).
87
+
88
+ Telegram's current terms restrict using platform content with AI. The content
89
+ license describes exceptions only when all relevant users provide explicit,
90
+ informed, affirmative and continued consent for the specific content and chat
91
+ context. A Beeper connection does not remove that restriction. Do not treat
92
+ owner login alone as authorization for unrestricted Telegram AI processing;
93
+ resolve the applicable consent requirements before enabling that workflow.
94
+ See [Telegram's API terms](https://core.telegram.org/api/terms) and
95
+ [content license](https://telegram.org/tos/content-licensing).
96
+
97
+ **Signal:** A future Ghostget adapter could link `signal-cli` to an existing
98
+ account, receive messages through its daemon, and submit exact recipient-bound
99
+ sends. The project explicitly calls itself unofficial and warns that versions
100
+ older than three months may stop working. This adds a linked device and a
101
+ maintenance obligation; it is not an official Signal integration.
102
+ See [the signal-cli project](https://github.com/AsamK/signal-cli).
103
+
104
+ **WhatsApp Business and Instagram professional accounts:** These are viable
105
+ future business connectors with their own setup. WhatsApp uses a business
106
+ account, registered business number, access tokens and webhook subscriptions.
107
+ Instagram requires a professional account and messaging permission; its Send
108
+ API generally replies after a person initiates contact and does not support
109
+ group messaging. Neither route should be offered as a replacement for a
110
+ personal inbox. See Meta's [WhatsApp Cloud API collection](https://www.postman.com/meta/whatsapp-business-platform/documentation/wlk6lh4/whatsapp-cloud-api)
111
+ and [Instagram Send API](https://www.postman.com/meta/instagram/folder/uxudqu0/send-api).
112
+
113
+ For personal Instagram, keep Beeper as the supported connection path. Avoid
114
+ adding an independent private-API or browser-session connector until it has a
115
+ maintained provider contract and reliable account recovery.
116
+
117
+ These recommendations reflect documentation checked on September 19, 2026.
118
+ Synthetic tests establish parser, permission and recovery behavior. A live
119
+ acceptance check still needs the intended account, one exact recipient and an
120
+ authorized message; it must verify reconnect and uncertain-send behavior as
121
+ well as the first successful request.
@@ -1,13 +1,14 @@
1
- # Agentrouter native process boundary
1
+ # AgentMixer native process boundary
2
2
 
3
- Agentrouter owns provider policy and account leases. A shared native process
3
+ AgentMixer owns provider policy and account leases. A shared native process
4
4
  transport owns an exact admitted process scope and byte streams. Applications
5
5
  own durable custody records, credentials, workspaces, artifact admission and
6
6
  recovery. Sharing the process implementation must preserve those owners.
7
7
 
8
- This plan starts from `a878d72d37ed86fd0cb3a3b78924a1221ce0135a`. Agentrouter is
9
- currently a private package under `packages/agentrouter`; this change does not
10
- publish it or activate a provider backend.
8
+ This plan starts from `a878d72d37ed86fd0cb3a3b78924a1221ce0135a`. AgentMixer now
9
+ lives in the standalone `hraness/agentmixer` repository as the published
10
+ `@hraness/agentmixer` package; `agentmixer/` paths below refer to that
11
+ repository's layout. This change does not activate a provider backend.
11
12
 
12
13
  ## Invariants
13
14
 
@@ -36,12 +37,12 @@ publish it or activate a provider backend.
36
37
  Implemented and independently reviewed source; repository integration remains
37
38
  pending:
38
39
 
39
- - `packages/agentrouter/src/process-port.ts` declares an in-memory
40
+ - `agentmixer/src/process-port.ts` declares an in-memory
40
41
  `ProviderProcessPort`: readiness, root observation, exact native settlement,
41
42
  operation completion, bounded backend byte streams, explicit write outcomes,
42
43
  input closure and synchronous stop fences. It is not another wire protocol,
43
44
  launch API, runtime qualification or saved-PID signaling API.
44
- - `packages/agentrouter/src/codex-account-process.ts` binds that port to the
45
+ - `agentmixer/src/codex-account-process.ts` binds that port to the
45
46
  existing account controller. It preserves native settlement independently of
46
47
  delivery success and joins delivery before reporting successful operation
47
48
  completion. Matching native `not-started` evidence needs no invented root
@@ -51,12 +52,12 @@ pending:
51
52
  each host write. An immutable binding alone cannot establish current authority.
52
53
  Rejected or asynchronous checks admit no bytes; any accidentally started
53
54
  promise remains owned until settlement. Late readiness after stop rejects.
54
- - `packages/agentrouter/src/codex-account-transport.ts` consumes explicit byte
55
+ - `agentmixer/src/codex-account-transport.ts` consumes explicit byte
55
56
  outcomes instead of Node writable callbacks. RPC success requires both the
56
57
  matched response and full write acceptance within the caller's deadline.
57
58
  Failed or uncertain writes close admission. Node `end`/`close` settle local
58
59
  consumer work; only the injected host receipt proves native EOF.
59
- - `packages/agentrouter/src/codex-account.ts` publishes its shared close attempt
60
+ - `agentmixer/src/codex-account.ts` publishes its shared close attempt
60
61
  before synchronously aborting the active request. Reentrant cleanup remains
61
62
  single-owned and an already queued write cannot run before that invalidation.
62
63
  - Synthetic tests cover pre-readiness cancellation, stale invocation and account
@@ -200,8 +201,8 @@ not establish shared artifact installation or live runtime qualification.
200
201
  Worker checks for phase 1 use synthetic streams only:
201
202
 
202
203
  ```sh
203
- bun test packages/agentrouter/test/codex-account-process.test.ts packages/agentrouter/test/codex-account-transport.test.ts packages/agentrouter/test/codex-account.test.ts
204
- bun x --no-install tsc --noEmit -p packages/agentrouter/tsconfig.json
204
+ bun test agentmixer/test/codex-account-process.test.ts agentmixer/test/codex-account-transport.test.ts agentmixer/test/codex-account.test.ts
205
+ bun x --no-install tsc --noEmit -p agentmixer/tsconfig.json
205
206
  git diff --check
206
207
  ```
207
208
 
@@ -0,0 +1,111 @@
1
+ # AI subscriptions through xcb
2
+
3
+ Textbutler uses [xcb (Excalibur)](https://github.com/hraness/xcb) to draft replies
4
+ through an explicitly connected Claude Code or Codex subscription. xcb owns
5
+ provider sign-in, runtime admission, operating-system confinement and account
6
+ custody. Textbutler owns contact context, response policy and every message send.
7
+ Textbutler is an MIT-licensed reference application for this separation.
8
+
9
+ The connection requires a verified Textbutler bundle with reviewed composition
10
+ admission and an xcb build with the native `generate` command. A source daemon
11
+ has no embedded Textbutler admission and keeps subscription inference unavailable.
12
+ A configured path, matching executable hash or signed-in account alone does not
13
+ establish readiness. Check the exact provider through Textbutler and verify the
14
+ selected messaging account before enabling replies. Synthetic tests do not
15
+ prove live inference, delivery or unattended operation on another installation.
16
+
17
+ ## Connect an account
18
+
19
+ Install xcb and connect the subscription account using its
20
+ [native setup guide](https://github.com/hraness/xcb#native-xcb). xcb keeps its own
21
+ private state; the native default is `~/.local/share/xcb`. Copy the exact account
22
+ ID and full observed model key from `xcb accounts` and `xcb models`.
23
+
24
+ Build and install Textbutler with `bun run textbutler:install`. Start its daemon
25
+ through the installed command for AI replies. The build refuses absent or stale
26
+ composition evidence; source startup cannot waive that check.
27
+
28
+ Stop the Textbutler daemon before changing host configuration. Supply physical
29
+ absolute paths and replace the example account and model with your observations:
30
+
31
+ ```sh
32
+ bun run textbutler setup \
33
+ --xcb /absolute/path/to/xcb \
34
+ --xcb-state /absolute/path/to/xcb-state \
35
+ --xcb-account claude:ACCOUNT_ID \
36
+ --xcb-model FULL_MODEL_KEY
37
+ ```
38
+
39
+ Repeat setup with `--xcb-account codex:ACCOUNT_ID` and its full model key to add
40
+ a Codex account. Setup records the xcb executable's SHA-256 and the explicit
41
+ account/model binding. It does not copy subscription credentials, create a
42
+ provider sign-in or enable a contact. Restart the installed daemon, run `providers list`,
43
+ and use the returned Textbutler account ID with `providers check ACCOUNT_ID`.
44
+ This reads current xcb capabilities and admission; it does not make a model turn
45
+ or prove reply quality. An unsent suggestion is the next inference check.
46
+
47
+ Setup preserves existing bindings and refuses a changed executable, hash, state
48
+ root, account or model. After upgrading xcb, stop the daemon and review the
49
+ private `state/host.json` xcb binding before updating its executable digest.
50
+ Retain account and custody state, then restart and check the account again.
51
+
52
+ Native subscription routing never substitutes Claude API or another account.
53
+ An unavailable, busy, stale or unsettled route remains unavailable until its
54
+ specific condition is resolved. See [getting started](getting-started.md) for
55
+ contact selection, reply review and explicit activation.
56
+
57
+ ## The application boundary
58
+
59
+ Each inference step invokes the pinned native `xcb generate` process with a
60
+ bounded JSON request on stdin. Private prompt content does not enter command
61
+ arguments. This application command supplies no provider tools, workspace
62
+ access, inherited coding session or executable hooks. Textbutler does not invoke
63
+ `xcb run`, which is the workspace-oriented coding interface.
64
+
65
+ The model returns a final JSON result or a proposal naming one advertised
66
+ Textbutler operation. The contact-scoped broker validates proposals and performs
67
+ only admitted operations: conditional contact-memory edits, bounded public web
68
+ reads and staged reply actions. Classification advertises no operations. The
69
+ provider cannot choose another contact, file root, recipient or credential.
70
+
71
+ The next step contains only the bounded operation history Textbutler supplies.
72
+ The native step loop permits at most 16 steps, 12 operations, 512 KiB of prompt,
73
+ 256 KiB per step and 1 MiB of transcript. Exceeding a bound stops the run. Failed
74
+ operations are not replayed because an effect may already have committed.
75
+
76
+ A proposed message never sends itself. Trusted Textbutler code applies
77
+ disclosure, human takeover, current enrollment, review or automation authority,
78
+ idempotency and the durable send journal immediately before dispatch.
79
+
80
+ ## Custody and recovery
81
+
82
+ xcb retains subscription credentials and provider state outside Textbutler's
83
+ contact folders. It serializes use of an account and releases custody only after
84
+ its provider process and controllers have joined. Textbutler validates the
85
+ generation result and its settlement facts before accepting output. Cancelling
86
+ a request or observing the xcb parent exit alone does not prove provider cleanup.
87
+
88
+ Textbutler also preserves an uncertain application invocation for recovery.
89
+ Restarting, reinstalling or repeating setup must not clear that record. Inspect
90
+ the exact xcb run and Textbutler diagnostic before recovery; never delete
91
+ custody state simply to make the account appear ready.
92
+
93
+ ## Evidence and distribution
94
+
95
+ The local Textbutler bundle contains its application code and pinned library
96
+ dependencies. Its build validates a separately reviewed composition receipt
97
+ against the exact source inventory and both contact capability profiles, then
98
+ embeds that admission. Missing evidence or source/profile drift rejects the
99
+ build; hashes and xcb sign-in cannot create this evidence. xcb and provider executables are installed separately. Its
100
+ `external-xcb` manifest value identifies this connection capability; it is not
101
+ a provider qualification, a signed release or a live messaging receipt.
102
+
103
+ xcb independently checks the selected provider's exact runtime and confinement
104
+ on each invocation. Its provider-specific readiness may differ by build and
105
+ host; consult the [xcb readiness table](https://github.com/hraness/xcb#readiness).
106
+ Textbutler must also prove its classifier/reply parsing, cancellation and broker
107
+ boundaries. Before relying on automatic replies, exercise one agreed recipient,
108
+ pause, owner takeover, transport loss, restart and grant expiry. The
109
+ [readiness page](readiness.md) separates these acceptance requirements from
110
+ source tests. The separately billed Claude API route retains its own trusted
111
+ runtime admission requirement in [provider setup](../../packages/textbutler/PROVIDERS.md).