@hraness/message-like-me 0.8.10 → 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,28 @@
1
+ # Shared support protocol notice
2
+
3
+ The CLI includes `@hraness/support-foundation` 0.3.0 from reviewed commit
4
+ `2d034b357680353574411217d68b02b6755b07ed` of
5
+ [Hraness Support Foundation](https://github.com/hraness/support-foundation).
6
+ Its code is bundled only for the command-line support flow; public SDK exports do not include it.
7
+
8
+ MIT License
9
+
10
+ Copyright (c) 2026 Hraness
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
@@ -0,0 +1,123 @@
1
+ # Use TextButler from an agent
2
+
3
+ TextButler's CLI returns JSON for conversation reads, summaries, drafts and
4
+ sends. Run it as the signed-in Mac user with the TextButler daemon running.
5
+ Connect iMessage and your AI subscription using the [setup guide](getting-started.md).
6
+ Explicit owner commands work while automatic replies are paused and the
7
+ selected contact is disabled.
8
+
9
+ ## Select a conversation
10
+
11
+ ```sh
12
+ textbutler conversations list
13
+ textbutler contacts add CANDIDATE_ID
14
+ textbutler contacts list
15
+ textbutler messages capabilities CONTACT_ID
16
+ ```
17
+
18
+ Use the exact IDs returned by the preceding commands. A unique contact name
19
+ also works; ambiguous names fail. Adding a contact keeps automatic replies off.
20
+ Add `--history` to `contacts add` only when you want a retained history import.
21
+ Reading current history does not require that import.
22
+
23
+ ## Read and summarize
24
+
25
+ ```sh
26
+ textbutler messages history CONTACT_ID --limit 100
27
+ textbutler contacts account CONTACT_ID native-claude-code
28
+ textbutler messages summarize CONTACT_ID --limit 100
29
+ ```
30
+
31
+ History includes message IDs, authorship, time, text, related message IDs and
32
+ attachment metadata when the provider exposes it. Attachment metadata contains
33
+ names, media types and sizes; local file paths and contents are excluded.
34
+ Limits range from 1 to 200 messages. The response reports
35
+ shortened text and omitted records; it is a recent sample, not a complete
36
+ archive. It does not download or interpret attachment contents.
37
+
38
+ Summaries use the contact's selected, qualified subscription account. The
39
+ response includes source message IDs and the size of the sample used. Review
40
+ the generated interpretation against those messages. A calling agent can also
41
+ summarize the history JSON itself without starting another inference request.
42
+
43
+ ## Compose, review and send
44
+
45
+ Choose one way to create an unsent draft. For an AI suggestion:
46
+
47
+ ```sh
48
+ textbutler replies suggest CONTACT_ID
49
+ ```
50
+
51
+ Suggestions read the current conversation without requiring a history import.
52
+ If there is no unanswered incoming message, the command returns no draft.
53
+ To provide the content yourself instead:
54
+
55
+ ```sh
56
+ textbutler messages compose CONTACT_ID --text 'Tuesday works for me.'
57
+ ```
58
+
59
+ Use the returned draft ID to review it, then send only when instructed:
60
+
61
+ ```sh
62
+ textbutler replies show DRAFT_ID
63
+ textbutler replies send DRAFT_ID DIGEST
64
+ ```
65
+
66
+ Use the digest returned by the complete draft review. It binds the recipient,
67
+ content and imported media. Changed conversations or media can invalidate a
68
+ draft. Disclosure settings apply to the reviewed and sent content.
69
+ There is one active draft per contact; creating another replaces it. Drafts
70
+ expire after 15 minutes and are cleared when the daemon restarts. Use
71
+ `textbutler replies discard DRAFT_ID` to discard one explicitly.
72
+
73
+ An explicitly authorized literal text can be sent in one command:
74
+
75
+ ```sh
76
+ textbutler messages send CONTACT_ID --text 'I have arrived.'
77
+ ```
78
+
79
+ Treat that command as an outward action. A CLI's availability is not permission
80
+ for an agent to message someone without its user's instruction.
81
+
82
+ ## Media and reactions
83
+
84
+ Inspect `messages capabilities CONTACT_ID` before requesting an action.
85
+ These commands create unsent drafts for the same review and send flow:
86
+
87
+ ```sh
88
+ textbutler messages attach CONTACT_ID /absolute/photo.jpg --caption 'Our view today'
89
+ textbutler messages react CONTACT_ID MESSAGE_ID '👍'
90
+ textbutler messages react CONTACT_ID MESSAGE_ID '👍' --remove
91
+ ```
92
+
93
+ Media imports accept owned regular files up to 16 MiB and copy them into the
94
+ selected contact's private outbox. The draft records the imported bytes; later
95
+ changes to the original source file do not change that copy. Common image,
96
+ audio, video and document types are recognized. Transport support and current
97
+ account permissions still determine whether an attachment can be sent.
98
+
99
+ For an ordered batch, put 1–7 supported action objects in a JSON array and use
100
+ `messages compose CONTACT_ID --actions /absolute/actions.json`. The supported
101
+ shapes are defined by the [action types](../../packages/transport/src/types.ts).
102
+ Attachment and sticker paths in an action object are relative to that contact's
103
+ workspace. The daemon validates targets, capabilities and imported media.
104
+
105
+ The current connector supports ordinary text and media on a Mac with the
106
+ required permissions. Standard reactions, stickers, rich links and polls
107
+ require its separately configured Messages bridge and the corresponding
108
+ advertised capability. TextButler does not install that bridge or change macOS
109
+ security settings. Outgoing threaded reply targeting, App Clips and experiences
110
+ are unsupported by the iMessage connector. The CLI does not turn a requested
111
+ thread reply into an ordinary message. Incoming reply relationships remain
112
+ visible in history.
113
+
114
+ ## Read uncertain results
115
+
116
+ Long operations may return a job ID. Read that exact job with
117
+ `textbutler jobs show JOB_ID`; do not repeat the original send. A successful
118
+ transport receipt reports `submitted`. A `partial` or `indeterminate` outcome
119
+ needs reconciliation before another attempt. Commands return a nonzero exit
120
+ code for pending or unsuccessful sends.
121
+
122
+ Append `--data-dir /absolute/private/path` to use another installation.
123
+ `textbutler messages --help` lists the agent commands.
@@ -1,18 +1,20 @@
1
1
  # Textbutler architecture
2
2
 
3
- Textbutler is a personal message butler for macOS. An owner activates a bounded set of contacts. Each contact gets a private workspace that a coding agent can read and evolve. A separate daemon decides when to invoke that agent and controls every outward action.
3
+ Textbutler is a personal message butler for macOS. An owner activates a bounded set of contacts. Each contact gets a private workspace that a model can read and evolve through Textbutler's broker. A separate daemon decides when to invoke that agent and controls every outward action.
4
4
 
5
- The source includes the owner daemon, contact reply loop, versioned Ghostget automation protocol and Mac application. Synthetic tests establish their control and recovery behavior. Live provider delivery and signed distribution have separate acceptance requirements below.
5
+ The source includes the owner daemon, contact reply loop, versioned Ghostget automation protocol and macOS menu companion. Synthetic tests establish their control and recovery behavior. Live provider delivery has separate acceptance requirements below; the companion is a CLI artifact and has no signing or notarization gate.
6
6
 
7
7
  ## Ownership
8
8
 
9
9
  ```mermaid
10
10
  flowchart LR
11
- App[Mac settings app] --> Control[Owner-only control socket]
11
+ Menu[Menu companion] --> Control[Owner-only control socket]
12
12
  Control --> Butler[Textbutler daemon]
13
- Butler --> Router[Agentrouter]
14
- Router --> Provider[Admitted agent provider]
15
- Provider --> Tools[Contact-bound tool broker]
13
+ Butler --> Xcb[xcb zero-tool generate]
14
+ Xcb --> Provider[Admitted subscription provider]
15
+ Provider --> Proposal[Structured result or proposal]
16
+ Proposal --> Butler
17
+ Butler --> Tools[Contact-bound tool broker]
16
18
  Tools --> Memory[One contact workspace]
17
19
  Tools --> Web[Public web broker]
18
20
  Tools --> Intents[Proposed actions]
@@ -22,9 +24,9 @@ flowchart LR
22
24
  Ghostget --> Messages[iMessage and WhatsApp]
23
25
  ```
24
26
 
25
- Ghostget owns native permissions, message and contact acquisition, provider actions, and receipts. Textbutler does not open chat.db or automate Messages directly. Agentrouter owns provider selection, shared-account leases, cancellation, model catalogs, qualification evidence, and a bounded tool interface. Textbutler owns conversation policy and the durable send transaction. The Mac app changes owner settings through a small local protocol; it never gives a model arbitrary Tauri commands.
27
+ Ghostget owns native permissions, message and contact acquisition, provider actions, and receipts. Textbutler does not open chat.db or automate Messages directly. xcb owns subscription credentials, provider admission, model catalogs, confinement, cancellation and account custody. Textbutler invokes its native zero-tool generation API and interprets returned operation proposals through its own contact broker. Textbutler owns conversation policy and the durable send transaction. The menu companion changes owner settings through a small local protocol; it never gives a model arbitrary local commands.
26
28
 
27
- The background lifecycle uses a user LaunchAgent: native messaging belongs to the signed-in Mac user, and login persistence is independent of the settings window. Installation records the exact runtime, entrypoint, data directory and generation; removal verifies its private receipt and loaded service identity. Uninstall preserves contact data. New settings start paused. A private SQLite custody lock prevents duplicate daemon ownership and permits recovery only for a dead recorded process and its exact unserved socket. A temporary live launchd install/start/uninstall test passed while preserving synthetic owner data. Signed distribution remains a separate gate.
29
+ The background lifecycle uses a user LaunchAgent: native messaging belongs to the signed-in Mac user, and login persistence is independent of the menu companion. Installation records the exact runtime, entrypoint, data directory and generation; removal verifies its private receipt and loaded service identity. Uninstall preserves contact data. New settings start paused. A private SQLite custody lock prevents duplicate daemon ownership and permits recovery only for a dead recorded process and its exact unserved socket. A minimal native TextButler.app can supervise the verified daemon so Full Disk Access belongs to the app. Its private installation receipt pins the runtime, payload, native executable and signature resources; a changed identity blocks startup. The app accepts fixed daemon, menu and iMessage setup roles, with no arbitrary command passthrough. Local builds use an ad-hoc signature, so a rebuilt app may require a fresh macOS permission grant.
28
30
 
29
31
  The daemon serves owner controls, enrollment jobs and a reply loop through its own supervised Ghostget process. Startup, enablement and recovery establish a silent event boundary, refresh bounded history and admit only subsequent eligible inbound messages. A failed catch-up pauses the affected conversation. No provider work starts without explicit owner account configuration.
30
32
 
@@ -37,7 +39,7 @@ Textbutler/
37
39
  daemon.sock owner-only native control socket
38
40
  state/
39
41
  settings.json owner configuration and contact bindings
40
- host.json optional private installed Ghostget CLI/account binding
42
+ host.json private Ghostget and xcb executable/account bindings
41
43
  runs.sqlite private run and send journal
42
44
  daemon-custody.sqlite exclusive process/socket ownership
43
45
  launch-agent-custody.sqlite lifecycle serialization
@@ -62,7 +64,7 @@ Directory names use opaque identifiers, not contact names or phone numbers. File
62
64
 
63
65
  The owner chooses one verified direct conversation from a bounded Ghostget list. Enrollment rechecks the account incarnation and participant identity and creates a disabled contact. History import is a separate opt-in, limited to 200 recent, explicitly scoped messages, with message ID, time, and author preserved. The import records shortening and omissions; it does not fetch media. These records are context only, and historical automation may be unobservable. A later qualified initialization run may summarize preferences, conversational style, open tasks, and useful context into memory. It must distinguish evidence from inference and retain uncertainty. Later runs correct outdated notes and record sources. Proven butler output never becomes owner-style training evidence. No global person model or cross-contact retrieval is supplied by default.
64
66
 
65
- The original Message Like Me corpus and profile tools remain an optional bounded bootstrap source. They do not become the live message transport. Old databases are not reset or silently migrated. There is no need to carry every previous archive/source feature into the new UI.
67
+ The original Message Like Me corpus and profile tools remain an optional bounded bootstrap source. They do not become the live message transport. Old databases are not reset or silently migrated. There is no need to carry every previous archive/source feature into the menu companion.
66
68
 
67
69
  ## Reply admission
68
70
 
@@ -74,7 +76,7 @@ The daemon waits eight seconds after an incoming message to collect a burst. New
74
76
 
75
77
  A whole-word, case-insensitive `butler` invocation permits a response after those deterministic gates. In keyword mode, other messages stay silent. In smart mode, a tool-free cheap model returns a strict structured classification. It should answer useful assistance requests, and stay silent during ordinary conversation, acknowledgments, emotional exchanges, or uncertain intent. Confidence below 0.85 stays silent. Malformed results, exhausted accounts, stale model catalogs, or timeouts never escalate to a more expensive agent automatically.
76
78
 
77
- Classifier choice comes from the selected provider's fresh available-model catalog, with explicit cost metadata and structured-output capability. No permanently hard-coded "cheap" model alias is assumed. An owner can pin a classifier or reply model after availability validation. Classifier and responder share the chosen provider/account policy; one classifier gets no tool authority.
79
+ The xcb subscription route uses the owner-selected full model key for classification and replies; a subscription model is not assigned invented API prices. The separately billed API route selects its classifier from fresh availability and explicit cost metadata. Classifier and responder share the chosen provider/account policy, and classification receives no operation authority.
78
80
 
79
81
  No typing signal can prove the owner is absent. The current adapters expose no typing signal; the daemon retains its message-based cooldown and final revision checks. The selected provider/account must independently qualify before smart mode can invoke a model.
80
82
 
@@ -82,41 +84,86 @@ No typing signal can prove the owner is absent. The current adapters expose no t
82
84
 
83
85
  The model proposes actions. It does not dispatch them. Owner-installed hooks may shape the work or veto a reply, but all action validation, contact binding, limits, and disclosure run afterwards.
84
86
 
85
- Every text action is wrapped by trusted code with the contact's three symbols. Each field must be one visible grapheme; an empty or invisible disclosure is rejected. Default rendering is `🤖{ hello this is my response }`.
87
+ Every text action is wrapped by trusted code with the contact's three symbols. Each field is either cleared (empty) or exactly one visible grapheme; invisible or multi-grapheme fields are rejected. Default rendering is `🤖{ hello this is my response }`. Clearing all three fields removes the visible wrap entirely and sends plain text.
86
88
 
87
- Reactions, stickers, link previews, and app cards cannot literally carry that text prefix. A disclosed text companion is therefore the first action in a nontext response, and counts toward the eight-action maximum. The provider must execute in order and stop if that companion fails. App-specific cards may additionally identify Textbutler in their content, but never remove the companion requirement.
89
+ Clearing disclosure never makes butler output indistinguishable internally. The transport reports the provider-accepted message IDs for every submitted action, and the daemon records them in a private `sent_messages` journal table. History and attribution classify an outgoing message as butler-authored through that journal first, and through the configured visible wrap for sends that predate it. A cleared wrap simply has no visible form; provenance stays exact.
90
+
91
+ Reactions, stickers, link previews, and app cards cannot literally carry that text prefix. When visible markers remain configured, a disclosed text companion is therefore the first action in a nontext response, and counts toward the eight-action maximum. With disclosure fully cleared the companion would be unexplained extra text and is not added. The provider must execute in order and stop if that companion fails. App-specific cards may additionally identify Textbutler in their content, but never remove the companion requirement.
88
92
 
89
93
  Before send, the runtime rechecks owner activity, conversation revision, current settings, capability availability, cancellation, attachment ownership, target message membership, and the contact grant. It asks the transport to prepare an exact plan with an expiry and digest. Immediately before submission, it journals the dispatch intent. The transport must atomically validate the grant, contact route, context revision, and plan digest at its own effect boundary.
90
94
 
91
95
  `submitted` is not `delivered`. Partial and indeterminate outcomes pause further automated activity for that contact until explicit reconciliation. A crash while dispatching becomes indeterminate on recovery; it never causes a blind retry. A crash before dispatch abandons the run without sending. Provider failover cannot replay a possibly submitted action.
92
96
 
93
- ## Hooks and plugins
97
+ ## Owner reply triage
94
98
 
95
- The initial lifecycle is `message.received`, `reply.decide`, `reply.compose`, `reply.before-send`, `reply.sent`, `memory.updated`, and `run.failed`. Hooks have a named/versioned owner-installed extension, deterministic registration order, a deadline, and a cancellation signal. Failure before dispatch closes admission. A notification-hook failure after a receipt cannot change that receipt or trigger resend.
99
+ The same machinery serves an explicit owner workflow that is distinct from automatic replies. `textbutler inbox` (or the menu's **Replies → Check for replies**) runs a bounded read-only pass over every enrolled conversation and reports each trailing run of unanswered inbound messages: the contact, a bounded sanitized preview, the pending count, whether a send is currently possible, and why not when it is not. The automatic loop's live observations feed the same view, so the inbox reflects what the daemon already saw between scans.
96
100
 
97
- Executable extensions are application code with the daemon's trust. They are installed outside contact folders; the agent cannot write them or turn message text into imports. Agent self-evolution means revising guidance and memory, not installing executable code. A future untrusted plugin mode needs its own process or language sandbox and explicit capabilities. In-process hooks are never described as a plugin security boundary.
101
+ `textbutler replies suggest CONTACT` asks the contact's configured agent to draft a reply for that pending run. A suggestion is a bounded draft with a fifteen-minute expiry: summary, exact proposed actions, and the disclosed preview the send would carry. It never dispatches. Drafts bind the conversation revision and disclosure settings they were created against; a stale context, changed disclosure, or expired draft is rejected rather than silently sent.
98
102
 
99
- The daemon reads a bounded private `plugins/extensions.json` manifest and preflights its complete inventory before importing listed TypeScript/JavaScript entry modules. Each default export must match the manifest ID/version and known hook names. Source digests appear in the loaded extension metadata. No directory scanning, package installation or hot reload occurs; changes require a full daemon process restart. The routed agent emits `memory.updated` only after a successful conditional write, with its path and committed revision. A notification failure does not undo that write or replay it.
103
+ `textbutler replies show DRAFT` exposes every ordered disclosed action, exact recipient, attachment hash and review digest. `textbutler replies send DRAFT DIGEST` sends only that exact reviewed draft, and `textbutler replies send CONTACT TEXT...` sends literal owner text through the identical grant, plan, journal, disclosure and reconciliation discipline as an automatic reply. `textbutler replies discard DRAFT` drops a suggestion. The menu bar exposes scan, per-conversation suggestion, labeled draft previews and discard. Complete review and digest-bound sending happen in the terminal or CLI; a truncated preview cannot authorize a send.
100
104
 
101
- ## Agentrouter
105
+ An owner send reuses the contact's live standing grant when it covers the needed action kinds with remaining quota. Otherwise the daemon issues a tightly scoped grant: only the specific action kinds, ten-minute expiry, quota equal to the action count. The scoped grant is journaled with intent and pending state, published to the conversation, and revoked after the send when the contact is disabled. One serialized work registration covers grant issuance and dispatch together so delegated renewal and disable-revocation cannot race an in-flight send. The agent never sees this surface; it has no send authority in either direction.
102
106
 
103
- Agentrouter begins as an MIT-licensed source package independent of Textbutler's product model. Its account lease coordinates the selected provider account without embedding credentials in a contact workspace. Credential resolvers remain trusted host services. A lease cannot be stolen merely because its time elapsed while a process might still be alive.
107
+ `replies.send` returns `submitted`, `failed`, `partial`, `cancelled`, or `indeterminate`. An indeterminate owner send blocks the next reply for that contact — automatic or owner-initiated — until the journaled intent is reconciled, exactly like an automatic send.
104
108
 
105
- Provider adapters declare observed, exact-version qualification. A launch plan is not proof of a sandbox. Codex's shell-disable setting and Claude's exact tool list are useful inputs, but an adapter is not admitted until attempted shell/process calls, host file reads, inherited MCP/plugin configuration, auth-file access, alternate agents, and additional workspaces are demonstrably blocked. No bypass-permissions mode is acceptable.
109
+ ## Hooks and plugins
106
110
 
107
- The source implements a pinned Claude Agent SDK adapter with explicit API-key account binding, a private verified executable snapshot, isolated runtime directories, no built-in tools or inherited settings, broker-only MCP, bounded raw output, and joined process-group termination. Its production gate requires independent qualification of the exact executable and SDK identity. Synthetic protocol and native-runtime fixtures do not activate it. The experimental Codex app-server driver verifies each model request's tool inventory through a host relay and keeps contact storage outside native scratch. It has no live account transport or production registration; native confinement and adversarial custody qualification remain incomplete. See the [Agentrouter implementation and evidence](../../packages/agentrouter/README.md).
111
+ The initial lifecycle is `message.received`, `reply.decide`, `reply.compose`, `reply.before-send`, `reply.sent`, `memory.updated`, and `run.failed`. Hooks have a named/versioned owner-installed extension, deterministic registration order, a deadline, and a cancellation signal. Failure before dispatch closes admission. A notification-hook failure after a receipt cannot change that receipt or trigger resend.
108
112
 
109
- A separately selected Claude API adapter executes the bounded tool loop in the trusted host. It exposes only the six broker tools and never starts a model-selected process. Account checks bind the actual packaged runtime and private credential generation; replacing a credential invalidates old discovery and running account work. Fresh model availability and price metadata select a cheap classifier. This explicit API choice does not silently replace Claude Code or Codex and does not use their subscription authentication.
113
+ Executable extensions are application code with the daemon's trust. They are installed outside contact folders; the agent cannot write them or turn message text into imports. Agent self-evolution means revising guidance and memory, not installing executable code. A future untrusted plugin mode needs its own process or language sandbox and explicit capabilities. In-process hooks are never described as a plugin security boundary.
110
114
 
111
- The model receives a fixed contact/workspace identity and a fixed run ID. File operations are brokered and conditional. Public web requests are separately bounded and must not reach loopback, private networks, local sockets, or cloud metadata through DNS or redirects. Message tools stage recipient-free intents for the one conversation. Unknown tool names and unknown input fields fail. Credential/account services are never model tools.
115
+ The daemon reads a bounded private `plugins/extensions.json` manifest and preflights its complete inventory before importing listed TypeScript/JavaScript entry modules. Each default export must match the manifest ID/version and known hook names. Source digests appear in the loaded extension metadata. No directory scanning, package installation or hot reload occurs; changes require a full daemon process restart. The routed agent emits `memory.updated` only after a successful conditional write, with its path and committed revision. A notification failure does not undo that write or replay it.
112
116
 
113
- Oompa's existing runtime provider port and account/process-custody patterns are source references. Its ordinary workspace-write execution profile is not the requested contact-only sandbox. AI Charts is a prospective second consumer. Do not migrate either product to Agentrouter until an adapter has equivalent feature and recovery evidence; avoid changing their active work in this redesign.
117
+ ## xcb application contract
118
+
119
+ Textbutler is an MIT-licensed reference application for
120
+ [xcb](https://github.com/hraness/xcb). AI execution requires a verified Textbutler
121
+ bundle whose build validates independently reviewed composition evidence against
122
+ current source bytes and both contact capability profiles. The build embeds
123
+ this application admission separately from xcb's provider admission. Source
124
+ daemon startup carries no composition admission and cannot enable this route. The native `generate` process provides a
125
+ bounded application request/result interface over stdin and stdout. The owner
126
+ pins the physical xcb executable and selects an explicit private state root,
127
+ subscription account and full model key. xcb and the provider executables are
128
+ separate installations; contact memory cannot edit their configuration.
129
+
130
+ xcb generates with zero provider tools and no inherited coding session. It owns
131
+ provider credentials, runtime admission, operating-system confinement and
132
+ process cleanup. Textbutler sends bounded contact context, validates the
133
+ structured response and accepts output only with a settled execution receipt.
134
+ A hash match or a root process exit alone cannot establish this receipt.
135
+ Uncertain cleanup preserves custody and blocks another invocation.
136
+
137
+ For replies, the model can propose a Textbutler operation. The application
138
+ validates the exact operation and closed input, calls its contact-bound broker,
139
+ and includes a bounded result in the next inference step. Classification
140
+ advertises no operations. The native provider never receives a filesystem or
141
+ messaging tool. The [subscription guide](native-subscription.md) documents setup,
142
+ limits and recovery. Credentials remain in xcb; grant and send authority remain
143
+ in Textbutler and Ghostget.
144
+
145
+ The retained AgentMixer compatibility library supplies shared application types
146
+ and broker helpers. Its historical package identity remains pinned for
147
+ reproducible builds; applications need not import private xcb internals or share
148
+ a source checkout. The native process contract is the subscription boundary.
149
+
150
+ A separately selected Claude API adapter executes a bounded tool loop in the
151
+ trusted host. That route still requires independent packaged-runtime admission,
152
+ an explicit API credential and current model/price evidence. Neither source
153
+ startup nor local distribution integrity provides this admission, and an xcb
154
+ subscription failure cannot switch to API billing.
155
+
156
+ The model receives a fixed contact/workspace identity and run ID. File
157
+ operations are brokered and conditional. Public web requests are bounded and
158
+ cannot reach private networks, local sockets or cloud metadata through DNS or
159
+ redirects. Messaging operations stage recipient-bound intents. Credential and
160
+ account controls are never model tools.
114
161
 
115
162
  ## Ghostget contract and rich features
116
163
 
117
164
  WhatsApp follows the same Ghostget ownership boundary. Its private wacli-backed transport provides durable observations and recipient-bound actions; pairing, session state and synchronization stay in Ghostget. The [WhatsApp guide](whatsapp.md) describes setup and action support. Textbutler does not embed WPPConnect or invoke wacli directly.
118
165
 
119
- Current source inspection found Ghostget's Tauri 2 webview with a packaged Bun helper. That helper lives with the app, and its private socket handles approvals/control, not a public messaging subscription service. Textbutler must not couple to it.
166
+ Ghostget owns its provider process and private control socket; Textbutler consumes only Ghostget's documented CLI and automation contracts.
120
167
 
121
168
  The older generic messaging APIs retain expiring route references and owner-confirmed previews. Automation uses a separate explicit owner protocol with durable enrollment, revocable grants and event observations. The iMessage provider negotiates attachments, reactions, stickers, rich links and polls separately; unsupported App Clips and arbitrary experiences remain unavailable. Native Contacts directory discovery is not implemented in this protocol.
122
169
 
@@ -132,17 +179,17 @@ Linq's documented iMessage API includes attachments, reactions, stickers, rich l
132
179
 
133
180
  Ghostget currently imports published Message Like Me bundle contracts. Keep that immutable package a leaf. Do not repoint it at the Textbutler runtime. Extract the neutral bundle contracts before reversing a live package dependency, or consume Ghostget's installed CLI contract without a package import in the interim. Preserve historical wire-format identifiers.
134
181
 
135
- ## Mac application
182
+ ## macOS menu companion
136
183
 
137
- The app has an explicit conversation picker, optional history initialization, a contact list, per-contact activation and mode settings, disclosure preview, memory editing, activity, provider status, and global pause. Long provider reads use bounded asynchronous jobs; Pause stays available and preserves unsaved choices. Unsupported capabilities show their actual setup or transport limitation. A separate synthetic demo is clearly labeled and is not included in the native app's live data graph.
184
+ The status-item companion can be launched by the CLI or the native app. It exposes daemon state, contact and account readiness, capabilities, recent activity, pause/resume, conversation selection and the reply inbox. The terminal supports complete draft review and setup. The website action opens the informational textbutler.app page. The companion is not required to run the daemon.
138
185
 
139
- One narrow native command accepts the versioned control request. It connects to the private user socket, bounds requests/responses, applies timeouts, and verifies same-user ownership. The webview has no generic shell, filesystem, opener, or network plugin. The app does not inherit access to arbitrary Ghostget operations.
186
+ One narrow native command accepts the versioned control request. It connects to the private user socket, bounds requests/responses, applies timeouts, and verifies same-user ownership. The companion has no generic shell, filesystem, opener, or network plugin and does not inherit access to arbitrary Ghostget operations.
140
187
 
141
188
  ## Admission still required
142
189
 
143
- 1. Use the verified Ghostget 0.18.2 package, which includes the reviewed automation source and native helpers. Real account synchronization, recipient identity, rich actions and revocation still require a bounded owner-authorized live test; artifact admission and synthetic fixtures do not prove delivery.
144
- 2. Independently qualify the native Claude SDK and Codex adapters for the requested no-shell, contact-only profile before enabling those choices. The separate Claude API path requires explicit account setup and packaged-runtime admission.
145
- 3. Sign and notarize the exact Mac artifact with an available Apple Developer identity, then verify the final downloaded bytes and installation lifecycle. The unsigned local package and successful launchd test are not a signed release.
190
+ 1. This development version pins Ghostget 0.18.21 for native TextButler iMessage setup; matching artifact admission and live conversation checks remain pending. Preserve its native helper resource bundle, protected-folder startup fix, partial discovery that excludes chats without usable participant metadata, and bounded discovery diagnostics without message bodies. Real account synchronization, recipient identity, rich actions and revocation still require a bounded owner-authorized live test; artifact admission and synthetic fixtures do not prove delivery.
191
+ 2. Use a verified Textbutler bundle with reviewed composition admission and connect an admitted native xcb build through its zero-tool generation contract. Verify the exact provider/account, both classifier and reply behavior, cancellation and uncertain-custody recovery before enabling automatic replies. The separate Claude API path retains explicit account setup and packaged-runtime admission.
192
+ 3. Publish the CLI package with its pinned desktop-foundation SDK dependency. Verify the package bytes, the verified pinned runner download, singleton behavior, and the shared autostart install/uninstall lifecycle. A source checkout or missing companion must never trigger a build at launch.
146
193
  4. Keep historical repository and published package identities as compatibility and provenance anchors. The Textbutler site is assigned to `textbutler.app`; later identity migrations must preserve immutable artifacts and existing release protections.
147
194
 
148
195
  ## Sources