@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
package/docs/publishing.md
CHANGED
|
@@ -72,10 +72,12 @@ existing GitHub Apps, rulesets, protected refs, status contexts, environment
|
|
|
72
72
|
reviewers, workflow IDs, Vercel project, and production branch unchanged. The
|
|
73
73
|
canonical source identity for new release and promotion runs is
|
|
74
74
|
`hraness/textbutler`; an old-name redirect is not authority for a new run.
|
|
75
|
-
The npm
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
The npm name `@hraness/message-like-me`, its tag namespace, the
|
|
76
|
+
`messagelikeme` command, wire schemas, and historical release receipts remain
|
|
77
|
+
unchanged. `@hraness/agentmixer` publishes independently from the
|
|
78
|
+
`hraness/agentmixer` repository under its own `v*` tag namespace; this
|
|
79
|
+
repository consumes it only as a pinned immutable release artifact. Do not
|
|
80
|
+
rewrite an existing tag, npm version, or provenance statement.
|
|
79
81
|
|
|
80
82
|
Merge this version-neutral control migration independently before the next
|
|
81
83
|
product/version change. Refresh the complete administrative controls census
|
|
@@ -114,32 +116,15 @@ denial, expected-old lease, and provider readback are machine gates. An agent
|
|
|
114
116
|
may perform the independent review and exact dispatch required for a changed
|
|
115
117
|
workflow-control epoch; that review precedes dispatch.
|
|
116
118
|
|
|
117
|
-
##
|
|
118
|
-
|
|
119
|
-
`@hraness/
|
|
120
|
-
tag
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
release workflows share concurrency group `stable-release`, so a root and an
|
|
127
|
-
AgentRouter release can never interleave their GitHub Latest assertions. The
|
|
128
|
-
AgentRouter channel has no site or production-promotion coupling; its releases
|
|
129
|
-
distribute the package only.
|
|
130
|
-
|
|
131
|
-
Owner-side controls required once before the first AgentRouter tag:
|
|
132
|
-
|
|
133
|
-
- Extend the immutable-tag ruleset coverage to `refs/tags/agentrouter-v*` with
|
|
134
|
-
the same update and deletion restrictions and no bypass actors as the
|
|
135
|
-
existing `refs/tags/v*` scope.
|
|
136
|
-
- Ensure `@hraness/agentrouter` exists publicly under the Hraness npm scope,
|
|
137
|
-
then configure its sole trusted publisher as GitHub Actions repository
|
|
138
|
-
`hraness/textbutler`, workflow file `release-agentrouter.yml`. Require
|
|
139
|
-
the exact permission set `createPackage` plus npm's provider-imposed
|
|
140
|
-
`createStagedPackage`, and keep the staged-package inventory exactly empty.
|
|
141
|
-
Once trusted publishing is proven, disallow traditional token publication
|
|
142
|
-
for the package.
|
|
119
|
+
## AgentMixer package consumption
|
|
120
|
+
|
|
121
|
+
`@hraness/agentmixer` is published independently from the `hraness/agentmixer`
|
|
122
|
+
repository; its tag namespace, release workflow, provenance identity, and npm
|
|
123
|
+
trusted publisher live there and are governed by that repository's runbook.
|
|
124
|
+
This repository consumes it only as a pinned, immutable GitHub Release
|
|
125
|
+
tarball. Upgrading the pin is a reviewed `package.json`/`bun.lock` change
|
|
126
|
+
against an already-admitted upstream release; it never re-runs, rewrites, or
|
|
127
|
+
co-signs an upstream release.
|
|
143
128
|
|
|
144
129
|
## Establish the production controls once
|
|
145
130
|
|
|
@@ -892,8 +877,14 @@ admission both succeeded, so a skipped tail cannot make the workflow green.
|
|
|
892
877
|
If the ref is already exact, the baseline marks advancement false, skips the
|
|
893
878
|
entire `production-ref-writer-key` job, and mints no App token. A separate
|
|
894
879
|
read-only job accepts only the unique latest exact-SHA Production deployment in
|
|
895
|
-
the stable baseline that postdates the immutable Release
|
|
896
|
-
|
|
880
|
+
the stable baseline that postdates the immutable Release, or, when the
|
|
881
|
+
separately admitted site route already advanced the ref to that exact commit
|
|
882
|
+
before the Release was published, that postdates the status App's admitted
|
|
883
|
+
`success` of the consumed site authority on that commit. That consumed
|
|
884
|
+
authority must already carry the App's terminal `error`; any other authority
|
|
885
|
+
shape, actor, or ordering keeps the Release publication as the boundary, so
|
|
886
|
+
this route can only admit a deployment that an admitted site promotion
|
|
887
|
+
created. That newest attempt itself must be provider-accepted. A newer terminal failure, error, or inactive
|
|
897
888
|
attempt blocks recovery instead of allowing an older success to be reused.
|
|
898
889
|
Recovery then repeats the terminal authority readbacks. A missing ref is a hard
|
|
899
890
|
failure and must not be recreated by the workflow. If the desired transition
|
|
@@ -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,6 +1,6 @@
|
|
|
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
|
|
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
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
|
|
|
@@ -10,9 +10,11 @@ The source includes the owner daemon, contact reply loop, versioned Ghostget aut
|
|
|
10
10
|
flowchart LR
|
|
11
11
|
Menu[Menu companion] --> Control[Owner-only control socket]
|
|
12
12
|
Control --> Butler[Textbutler daemon]
|
|
13
|
-
Butler -->
|
|
14
|
-
|
|
15
|
-
Provider -->
|
|
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.
|
|
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 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
|
|
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
|
|
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
|
|
@@ -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
|
-
|
|
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,35 +84,80 @@ 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
|
|
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
|
-
|
|
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
|
-
##
|
|
97
|
+
## Owner reply triage
|
|
94
98
|
|
|
95
|
-
The
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
+
## Hooks and plugins
|
|
106
110
|
|
|
107
|
-
The
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
|
@@ -134,14 +181,14 @@ Ghostget currently imports published Message Like Me bundle contracts. Keep that
|
|
|
134
181
|
|
|
135
182
|
## macOS menu companion
|
|
136
183
|
|
|
137
|
-
The
|
|
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
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.
|
|
144
|
-
2.
|
|
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.
|
|
145
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
|
|