@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.
- package/CHANGELOG.md +9 -0
- package/README.md +99 -43
- package/dist/cli.js +3399 -3088
- package/docs/publishing.md +23 -32
- package/docs/textbutler/agent-cli.md +123 -0
- package/docs/textbutler/architecture.md +71 -24
- package/docs/textbutler/getting-started.md +277 -0
- package/docs/textbutler/ghostget-contract.md +18 -6
- package/docs/textbutler/local-data.md +50 -0
- package/docs/textbutler/messaging-apps.md +121 -0
- package/docs/textbutler/native-process-plan.md +12 -11
- package/docs/textbutler/native-subscription.md +111 -0
- package/docs/textbutler/readiness.md +78 -0
- package/package.json +12 -5
|
@@ -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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
70
|
-
|
|
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
|
-
#
|
|
1
|
+
# AgentMixer native process boundary
|
|
2
2
|
|
|
3
|
-
|
|
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`.
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
- `
|
|
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
|
-
- `
|
|
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
|
-
- `
|
|
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
|
-
- `
|
|
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
|
|
204
|
-
bun x --no-install tsc --noEmit -p
|
|
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).
|