@hraness/message-like-me 0.8.0

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +151 -0
  2. package/LICENSE +21 -0
  3. package/README.md +698 -0
  4. package/SECURITY.md +318 -0
  5. package/dist/agentic-messaging-v1.d.ts +179 -0
  6. package/dist/agentic-messaging-v1.js +52 -0
  7. package/dist/canonical-json.d.ts +3 -0
  8. package/dist/cli-bs3db5jr.js +643 -0
  9. package/dist/cli-d7qv38ab.js +485 -0
  10. package/dist/cli-kw20gkk3.js +5 -0
  11. package/dist/cli-qqafdvz9.js +5 -0
  12. package/dist/cli-ry4128kz.js +584 -0
  13. package/dist/cli-ththzwja.js +20 -0
  14. package/dist/cli-x1qncxm7.js +1078 -0
  15. package/dist/cli.js +9436 -0
  16. package/dist/ensoul-source-v1.d.ts +121 -0
  17. package/dist/ensoul-source-v1.js +24 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +47 -0
  20. package/dist/message-bundle-v1-identity.d.ts +2 -0
  21. package/dist/message-bundle-v1.d.ts +205 -0
  22. package/dist/message-bundle-v1.js +37 -0
  23. package/dist/message-bundle-v2-identity.d.ts +2 -0
  24. package/dist/message-bundle-v2.d.ts +215 -0
  25. package/dist/message-bundle-v2.js +43 -0
  26. package/dist/metrics.d.ts +41 -0
  27. package/dist/types.d.ts +568 -0
  28. package/docs/local-message-bundle-v1.md +245 -0
  29. package/docs/local-message-bundle-v2.md +167 -0
  30. package/docs/methodology.md +322 -0
  31. package/docs/research.md +164 -0
  32. package/package.json +82 -0
  33. package/schema/ensoul-messages-source-v1.schema.json +248 -0
  34. package/schema/local-message-bundle-v1.schema.json +449 -0
  35. package/schema/local-message-bundle-v2.schema.json +462 -0
  36. package/schema/style-profile-v1.schema.json +223 -0
  37. package/schema/style-profile-v2.schema.json +202 -0
  38. package/skills/ensoul/LICENSE +23 -0
  39. package/skills/ensoul/NOTICE.md +7 -0
  40. package/skills/ensoul/SKILL.md +226 -0
  41. package/skills/ensoul/VENDORED_FROM.md +7 -0
  42. package/skills/ensoul/agents/openai.yaml +4 -0
  43. package/skills/ensoul/references/ensoul-source-packet-v1.schema.json +187 -0
  44. package/skills/ensoul/references/evidence-method.md +148 -0
  45. package/skills/ensoul/references/output-blueprint.md +143 -0
  46. package/skills/ensoul/references/source-packets.md +139 -0
  47. package/skills/ensoul/scripts/prepare_x_archive.py +467 -0
  48. package/skills/ensoul/scripts/validate_source_packet.py +477 -0
  49. package/skills/message-like-me/SKILL.md +229 -0
  50. package/skills/message-like-me/agents/openai.yaml +4 -0
  51. package/skills/message-like-me/references/analysis.md +159 -0
  52. package/skills/message-like-me/references/drafting.md +86 -0
  53. package/skills/message-like-me/references/ensoul.md +94 -0
  54. package/skills/message-like-me/references/evaluation.md +81 -0
  55. package/skills/message-like-me/references/privacy.md +106 -0
  56. package/skills/message-like-me/references/profile-schema.md +148 -0
@@ -0,0 +1,245 @@
1
+ # Local message bundle version 1
2
+
3
+ `message-like-me.local-message-bundle` is a private directory interchange for
4
+ moving a bounded local provider observation into Message Like Me. It separates
5
+ provider capture from analysis: a producer handles provider access and writes
6
+ the bundle, while `messagelikeme ingest bundle` verifies and normalizes it. The
7
+ importer never receives provider credentials, starts Wrench, invokes a Beeper
8
+ operation, or sends a message.
9
+
10
+ The currently verified producer is the local Beeper export in the
11
+ [`@hraness/wrench@0.16.1`](https://www.npmjs.com/package/@hraness/wrench/v/0.16.1)
12
+ npm package:
13
+
14
+ ```sh
15
+ bun add --global @hraness/wrench@0.16.1
16
+ wrench beeper export-message-like-me \
17
+ --auth <beeper-auth-id> \
18
+ --output <normalized-absolute-new-directory> \
19
+ [--limit-chats <n>] \
20
+ [--limit-messages <n>] \
21
+ [--max-participants <n>] \
22
+ [--json]
23
+ ```
24
+
25
+ The JSON shape is published as
26
+ [`schema/local-message-bundle-v1.schema.json`](../schema/local-message-bundle-v1.schema.json).
27
+ Runtime validation also enforces UTF-8 byte bounds, canonical encoding,
28
+ filesystem identity, graph joins, and digest laws that JSON Schema cannot
29
+ express.
30
+
31
+ ## Compatibility coordinates
32
+
33
+ Message Like Me accepts schema version `1` with source ID `beeper-local` and
34
+ source-transform version `1.1.0`. Wrench v0.16.1 emits those coordinates with
35
+ the pinned Beeper CLI version `0.6.2`. A later Wrench package release remains
36
+ compatible only while its manifest still declares that schema, source ID, and
37
+ `source.version: "1.1.0"`; package age or a permissive version range never
38
+ overrides the manifest coordinates. The provider version records the pinned
39
+ Beeper CLI used for capture and may change without changing the bundle
40
+ contract.
41
+
42
+ The dependency-free package subpath is the executable contract authority for
43
+ producers and consumers:
44
+
45
+ ```ts
46
+ import {
47
+ LOCAL_MESSAGE_BUNDLE_V1_SOURCE_TRANSFORM_VERSION,
48
+ parseLocalMessageBundleV1Manifest,
49
+ parseLocalMessageBundleV1Record,
50
+ } from "@hraness/message-like-me/message-bundle-v1"
51
+ ```
52
+
53
+ It exports the exact readonly record and manifest types, fixed artifact
54
+ inventory, safety bounds, compatibility constants, canonical bundle-digest
55
+ helpers, and strict pure parsers. Those parsers operate on already decoded
56
+ unknown values and perform no filesystem, credential, provider, network, AI,
57
+ analytics, or message-sending work. The CLI adds private filesystem and graph
58
+ validation around the same parsers.
59
+
60
+ ## Directory inventory
61
+
62
+ The input is a normalized absolute path to a current-user-owned physical
63
+ directory with mode `0700`. It contains exactly these mode-`0600`, singly
64
+ linked physical files:
65
+
66
+ ```text
67
+ manifest.json
68
+ accounts.ndjson
69
+ participants.ndjson
70
+ conversations.ndjson
71
+ messages.ndjson
72
+ reactions.ndjson
73
+ tombstones.ndjson
74
+ ```
75
+
76
+ The six artifacts always exist, including when they are empty. Every artifact
77
+ uses canonical JSON, one object per line, and one final newline per record.
78
+ Empty artifacts contain zero bytes. `manifest.json` is canonical JSON followed
79
+ by one newline and is written last by the producer.
80
+
81
+ The importer rejects symbolic links, extra files, ownership or mode changes,
82
+ files that change while read, invalid UTF-8, noncanonical JSON, blank records,
83
+ missing final newlines, count or byte mismatches, and digest mismatches.
84
+
85
+ ## Bounds
86
+
87
+ Version one has these hard importer and producer ceilings:
88
+
89
+ - 128 connected accounts;
90
+ - 500,000 records across all six artifacts;
91
+ - 512 MiB across all six artifacts;
92
+ - 2 MiB for one encoded NDJSON record, including its final newline;
93
+ - 1 MiB of UTF-8 for one message body;
94
+ - 1,024 UTF-8 bytes for an identifier, sort key, or provider revision;
95
+ - 8 KiB of UTF-8 for a display name, handle, title, reaction body, or
96
+ attachment filename;
97
+ - 256 UTF-8 bytes for an attachment MIME type;
98
+ - 10,000 known participants in one conversation;
99
+ - 256 attachment metadata items in one message; and
100
+ - 128 unique categorical warning codes.
101
+
102
+ Custom producer limits may only lower the total record, byte, and line bounds.
103
+ All timestamps are canonical millisecond UTC strings equal to
104
+ `Date#toISOString()` output. Identifiers are nonempty and contain no ASCII
105
+ control characters. Network and warning values are bounded lowercase tokens.
106
+
107
+ ## Manifest integrity
108
+
109
+ The manifest declares source and provider versions, collection timestamps,
110
+ completeness, privacy guarantees, per-kind counts, and each artifact's exact
111
+ record count, byte length, and lowercase SHA-256.
112
+
113
+ Artifact SHA-256 covers the file's exact bytes, including every final newline.
114
+ Artifacts appear in the fixed directory order shown above. The bundle digest
115
+ is:
116
+
117
+ ```text
118
+ SHA256(UTF8(canonicalJson(manifest with the entire integrity property omitted)))
119
+ ```
120
+
121
+ The manifest file's own SHA-256 covers its exact canonical bytes plus final
122
+ newline. It is returned by the producer and recorded by Message Like Me, but is
123
+ not embedded in the manifest.
124
+
125
+ The privacy declaration is fixed to `private-local`, `metadata-only`
126
+ attachments, excluded provider URLs, and excluded credentials. This is an
127
+ interchange constraint, not anonymization. Bodies, timestamps, handles,
128
+ account coordinates, and relationship graphs remain private.
129
+
130
+ ## Account realms and provenance
131
+
132
+ Every line has `schemaVersion`, `kind`, a bundle-local `id`, `accountId`,
133
+ `network`, and provenance:
134
+
135
+ - `providerId` is the stable provider coordinate for that entity;
136
+ - `providerRevision` preserves a provider revision when one exists;
137
+ - `observedAt` records when the producer observed this record; and
138
+ - `connectedAccountProviderId` is the stable connected-account coordinate.
139
+
140
+ An account line has `id === accountId` and
141
+ `provenance.providerId === provenance.connectedAccountProviderId`. Every other
142
+ record must match one account line on `accountId`, `network`, and connected
143
+ account coordinate. Bundle-local IDs exist only for joins inside this one
144
+ bundle. Message Like Me derives its stored source and entity IDs from stable
145
+ provider, connected-account, and self-participant coordinates with a private
146
+ per-install HMAC key. The mutable network label is source metadata and never
147
+ part of that identity namespace.
148
+
149
+ Provider IDs must be unique within one entity kind and account. Validation
150
+ errors name the record kind and ordinal, never the foreign coordinate. Message
151
+ and reaction provider IDs are independent domains and may contain the same
152
+ value. Message Like Me assigns a separate internal timeline coordinate when a
153
+ dated reaction is represented in the normalized messages table; the raw
154
+ reaction coordinate remains in the reaction fact and private provenance.
155
+
156
+ ## Identity and conversation rosters
157
+
158
+ Each account names one self participant. Participants carry optional display
159
+ names and handles plus `isSelf`. Conversations carry their known participant
160
+ IDs and `participantsComplete`:
161
+
162
+ - `true` is the only positive assertion that the roster is complete;
163
+ - `false` or `null` means the producer cannot assert completeness; and
164
+ - a complete direct roster must contain exactly the account's one self
165
+ participant and one non-self participant.
166
+
167
+ Message senders and reaction actors must agree with direction and any complete
168
+ roster. Message Like Me may expose an exact email or E.164 handle from the one
169
+ non-self participant of a complete direct conversation to local Contacts
170
+ matching. It never uses an incomplete roster for that join.
171
+
172
+ ## Messages, replies, and attachments
173
+
174
+ `sentAt` is the message's actual temporal coordinate. `sortKey` is an opaque
175
+ provider ordering key. Within one account and conversation, Message Like Me
176
+ orders lexical `sortKey`, then `sentAt` and stable provider ID as deterministic
177
+ tie-breakers.
178
+
179
+ `bodyTruncated: true` means the body cannot be prose evidence. The record still
180
+ becomes a text bubble for tempo, reply, and response-shape analysis. A message
181
+ with deletion state must have a null body. Attachment entries contain metadata
182
+ only; they never contain paths, URLs, or media bytes.
183
+
184
+ A reply target has a required provider ID and an optional bundle-local ID. When
185
+ the local ID is present, it must resolve to the same provider coordinate in the
186
+ same account and conversation. A null local ID preserves a reply to a message
187
+ outside the bounded artifact.
188
+
189
+ ## Edits and deletion
190
+
191
+ Edits are discriminated:
192
+
193
+ - `in-place` records a terminal mutation under the same provider message ID and
194
+ never suppresses that message; and
195
+ - `replacement` identifies a different provider message in the same account
196
+ and conversation.
197
+
198
+ Replacement targets may be outside the bounded artifact. In-bundle targets
199
+ must agree on local and provider coordinates. Replacement graphs must be
200
+ non-self, single-terminal, and acyclic. A validated replacement suppresses the
201
+ older version as evidence.
202
+
203
+ Message deletion state is explicit and carries the observation time and
204
+ provider revision. Deleted bodies are null.
205
+
206
+ ## Reactions and tombstones
207
+
208
+ A reaction has a required target provider message ID and an optional
209
+ bundle-local target. When present, the local target must resolve to the same
210
+ provider coordinate. `reactedAt` is nullable because the provider may not
211
+ expose a reaction time. Producers never synthesize one. Active undated
212
+ reactions contribute to fixed aggregate reaction counts, direction counts, and
213
+ timestamp-coverage counts but never enter the message timeline, sessions,
214
+ bursts, response episodes, or latency metrics.
215
+
216
+ Tombstones identify a conversation, message, or reaction kind, required
217
+ provider coordinate, optional bundle-local coordinate, deletion time, scope,
218
+ and provider revision. Account and participant tombstones are outside the
219
+ version-one contract. A nonnull local coordinate must resolve inside the same
220
+ account and agree with
221
+ the provider coordinate. A null coordinate preserves deletion knowledge for
222
+ an entity outside the bounded artifact.
223
+
224
+ ## Reimport semantics
225
+
226
+ Version-one bundle completeness is `bounded-local`, `truncated`, or `unknown`.
227
+ None is authoritative for deletion by absence. Reimport therefore upserts
228
+ present records and retains prior records omitted by a later bundle. Explicit
229
+ message deletion, removed reaction state, replacement edges, and tombstones
230
+ are applied separately. A valid later reappearance clears the matching
231
+ suppression. Present and retained messages are reranked together by the provider
232
+ ordering coordinates, so a bounded backfill converges with a fresh import of
233
+ the same final records.
234
+
235
+ The manifest completeness kind and reason apply conservatively to every
236
+ account. Stored `observedFrom` and `observedTo` bounds are derived from the
237
+ dated message and reaction records for that account, so one account never
238
+ inherits another account's time range. An account with no dated timeline
239
+ records has null bounds.
240
+
241
+ `timestamps.createdAt` is monotonic within one stable connected-account source.
242
+ An older bundle is rejected. An equal-time replay is accepted only when its
243
+ manifest and input revision match exactly; an equal-time conflict is rejected.
244
+ Native iMessage replacement remains scoped to its own source and cannot remove
245
+ bundle history.
@@ -0,0 +1,167 @@
1
+ # Local message bundle v2
2
+
3
+ Local message bundle v2 is the native WhatsApp evidence boundary between a
4
+ Wrench-owned Wacli adapter and Message Like Me. Wrench owns Wacli discovery,
5
+ authentication, local synchronization, provider interpretation, and export.
6
+ Message Like Me reads only the finished caller-owned directory. It never starts
7
+ Wrench or Wacli, receives a WhatsApp credential or session database, accesses a
8
+ network, or sends a message.
9
+
10
+ The intended producer flow is:
11
+
12
+ ```sh
13
+ bun add --global @hraness/wrench@0.16.3
14
+ wrench whatsapp export-message-like-me \
15
+ --auth <whatsapp-auth-id> \
16
+ --output /absolute/private/path/whatsapp-bundle
17
+
18
+ messagelikeme ingest bundle \
19
+ --input /absolute/private/path/whatsapp-bundle \
20
+ --json
21
+ ```
22
+
23
+ The checked compatibility coordinates are Wrench v0.16.3 and official Wacli
24
+ v0.15.0. Wrench owns that executable dependency and its authentication state;
25
+ neither enters Message Like Me.
26
+
27
+ The normative object schema is
28
+ [`schema/local-message-bundle-v2.schema.json`](../schema/local-message-bundle-v2.schema.json).
29
+ The runtime parser is stricter than JSON Schema where byte length, canonical
30
+ timestamps, filesystem identity, exact JID semantics, account joins, digests,
31
+ and graph laws require executable checks.
32
+
33
+ ## Immutable identity
34
+
35
+ Message Like Me accepts exactly:
36
+
37
+ - schema version `2`;
38
+ - format `message-like-me.local-message-bundle`;
39
+ - source ID `wacli-local`;
40
+ - source-transform version `1.0.0`;
41
+ - provider `whatsapp@0.15.0` (ID `whatsapp`, version `0.15.0`);
42
+ - network `whatsapp` on every record; and
43
+ - exactly one connected WhatsApp account.
44
+
45
+ The manifest's provider coordinate is the exact official Wacli v0.15.0 used by
46
+ the producer, not a compatibility wildcard. A semantic producer change
47
+ requires a new supported source-transform version or bundle schema.
48
+
49
+ Producers and consumers can import the dependency-free contract directly:
50
+
51
+ ```ts
52
+ import {
53
+ LOCAL_MESSAGE_BUNDLE_V2_ARTIFACTS,
54
+ LOCAL_MESSAGE_BUNDLE_V2_SOURCE_TRANSFORM_VERSION,
55
+ parseLocalMessageBundleV2Manifest,
56
+ parseLocalMessageBundleV2Record,
57
+ parseLocalMessageBundleV2WhatsAppJid,
58
+ } from "@hraness/message-like-me/message-bundle-v2"
59
+ ```
60
+
61
+ Importing this module performs no filesystem, process, authentication, network,
62
+ or messaging work.
63
+
64
+ ## Fixed private directory
65
+
66
+ The bundle directory must be a normalized absolute, current-user-owned,
67
+ physical mode-`0700` directory. It contains exactly:
68
+
69
+ ```text
70
+ manifest.json
71
+ accounts.ndjson
72
+ participants.ndjson
73
+ conversations.ndjson
74
+ messages.ndjson
75
+ reactions.ndjson
76
+ tombstones.ndjson
77
+ ```
78
+
79
+ Every file is a singly linked, current-user-owned, physical mode-`0600` file.
80
+ The importer rejects symbolic links, hard links, inventory drift, concurrent
81
+ changes, invalid UTF-8, blank or oversized records, noncanonical JSON, count or
82
+ byte disagreement, and SHA-256 disagreement. The same public bounds as v1
83
+ apply, except v2 admits exactly one account.
84
+
85
+ `manifest.json` is canonical JSON followed by one newline. Every NDJSON record
86
+ is one canonical JSON object followed by one newline. The manifest integrity
87
+ digest covers its canonical projection without the `integrity` member;
88
+ individual artifact digests cover their exact bytes.
89
+
90
+ ## WhatsApp coordinates
91
+
92
+ The contract admits only canonical JIDs that establish a supported WhatsApp
93
+ realm:
94
+
95
+ - an E.164-backed user JID ending in `@s.whatsapp.net`;
96
+ - a numeric privacy-preserving LID ending in `@lid`; or
97
+ - a numeric group JID ending in `@g.us`.
98
+
99
+ Status, broadcast, newsletter, mixed-case, plus-prefixed JID local parts, and
100
+ unknown server forms are rejected. Accounts and participants use a user JID or
101
+ LID. Direct conversations use the exact non-self participant JID;
102
+ group conversations use a group JID. Complete direct rosters contain exactly
103
+ one self and one non-self participant.
104
+
105
+ A connected account or participant `handle` is the exact `+`-prefixed E.164
106
+ projection only when its JID is an E.164-backed user JID. LIDs and group
107
+ coordinates never mint a phone handle. This is the only v2 bridge to optional
108
+ macOS Contacts enrichment. Names,
109
+ titles, phone suffixes, timestamps, and LIDs are never used as fuzzy contact
110
+ matches.
111
+
112
+ Every message has a proven incoming or outgoing direction. Direct-message
113
+ senders are proven. A group or system row may retain a null sender when the
114
+ bounded local observation cannot prove one, and it cannot establish overlap.
115
+ Reply, edit, deletion, reaction, attachment, and tombstone fields retain the
116
+ same strict meanings as v1. Attachment bytes, provider URLs, credentials, Wacli
117
+ session state, database paths, and unmodeled provider payloads are excluded.
118
+ Unsupported status, broadcast, and newsletter records are not represented as
119
+ ordinary conversations.
120
+
121
+ ## Coverage and replay
122
+
123
+ A linked-device export is a bounded local observation. It does not prove that
124
+ all remote WhatsApp history exists locally. `bounded-local`, `truncated`, and
125
+ `unknown` completeness remain non-authoritative: omission in a later bundle
126
+ does not delete retained evidence. Only explicit deletion, replacement, or
127
+ tombstone records suppress evidence. A later explicit record reappearance
128
+ clears that suppression. Creation timestamps remain monotonic per source.
129
+
130
+ ## Beeper WhatsApp overlap
131
+
132
+ A native Wacli source and an older Beeper WhatsApp source have separate stable
133
+ namespaces. If the same exact account is already present, the importer stops and
134
+ requires the caller to name that source:
135
+
136
+ ```sh
137
+ messagelikeme ingest bundle \
138
+ --input /absolute/private/path/whatsapp-bundle \
139
+ --overlap-source <beeper-whatsapp-source-id> \
140
+ --json
141
+ ```
142
+
143
+ `--overlap-source` is not a fuzzy merge switch. Reconciliation requires the
144
+ same exact self E.164 account, complete one-to-one direct-peer E.164 identity,
145
+ and at least one unambiguous shared message with the same sender role,
146
+ timestamp, direction, text body, message kind, and attachment count. Bodyless
147
+ messages, groups, names, phone suffixes, approximate timestamps, and ambiguous
148
+ duplicates cannot prove equivalence.
149
+
150
+ Both source provenances and all source-unique history remain stored. Proven
151
+ message and reaction duplicates contribute once. The native Wacli conversation
152
+ is the preferred action route and carries the exact private `whatsappJid`
153
+ coordinate. Its proven Beeper duplicate remains evidence with reason
154
+ `superseded-route`. Reimport rechecks the named proof atomically and fails
155
+ closed on disagreement.
156
+
157
+ ## Privacy and action boundary
158
+
159
+ Ordinary source, contact, metrics, and CLI receipts expose only pseudonymous
160
+ IDs, counts, digests, coverage, and categorical health. Exact JIDs, E.164
161
+ handles, account coordinates, message bodies, and source metadata stay in the
162
+ private store or explicit owner-only artifacts.
163
+
164
+ Message Like Me may write an exact `whatsappJid` route into an explicit
165
+ mode-`0600` route inventory. That coordinate is evidence for a separate Wrench
166
+ binding and preview. Message Like Me does not authenticate, synchronize,
167
+ preview, submit, or send through WhatsApp.