@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.
- package/CHANGELOG.md +151 -0
- package/LICENSE +21 -0
- package/README.md +698 -0
- package/SECURITY.md +318 -0
- package/dist/agentic-messaging-v1.d.ts +179 -0
- package/dist/agentic-messaging-v1.js +52 -0
- package/dist/canonical-json.d.ts +3 -0
- package/dist/cli-bs3db5jr.js +643 -0
- package/dist/cli-d7qv38ab.js +485 -0
- package/dist/cli-kw20gkk3.js +5 -0
- package/dist/cli-qqafdvz9.js +5 -0
- package/dist/cli-ry4128kz.js +584 -0
- package/dist/cli-ththzwja.js +20 -0
- package/dist/cli-x1qncxm7.js +1078 -0
- package/dist/cli.js +9436 -0
- package/dist/ensoul-source-v1.d.ts +121 -0
- package/dist/ensoul-source-v1.js +24 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +47 -0
- package/dist/message-bundle-v1-identity.d.ts +2 -0
- package/dist/message-bundle-v1.d.ts +205 -0
- package/dist/message-bundle-v1.js +37 -0
- package/dist/message-bundle-v2-identity.d.ts +2 -0
- package/dist/message-bundle-v2.d.ts +215 -0
- package/dist/message-bundle-v2.js +43 -0
- package/dist/metrics.d.ts +41 -0
- package/dist/types.d.ts +568 -0
- package/docs/local-message-bundle-v1.md +245 -0
- package/docs/local-message-bundle-v2.md +167 -0
- package/docs/methodology.md +322 -0
- package/docs/research.md +164 -0
- package/package.json +82 -0
- package/schema/ensoul-messages-source-v1.schema.json +248 -0
- package/schema/local-message-bundle-v1.schema.json +449 -0
- package/schema/local-message-bundle-v2.schema.json +462 -0
- package/schema/style-profile-v1.schema.json +223 -0
- package/schema/style-profile-v2.schema.json +202 -0
- package/skills/ensoul/LICENSE +23 -0
- package/skills/ensoul/NOTICE.md +7 -0
- package/skills/ensoul/SKILL.md +226 -0
- package/skills/ensoul/VENDORED_FROM.md +7 -0
- package/skills/ensoul/agents/openai.yaml +4 -0
- package/skills/ensoul/references/ensoul-source-packet-v1.schema.json +187 -0
- package/skills/ensoul/references/evidence-method.md +148 -0
- package/skills/ensoul/references/output-blueprint.md +143 -0
- package/skills/ensoul/references/source-packets.md +139 -0
- package/skills/ensoul/scripts/prepare_x_archive.py +467 -0
- package/skills/ensoul/scripts/validate_source_packet.py +477 -0
- package/skills/message-like-me/SKILL.md +229 -0
- package/skills/message-like-me/agents/openai.yaml +4 -0
- package/skills/message-like-me/references/analysis.md +159 -0
- package/skills/message-like-me/references/drafting.md +86 -0
- package/skills/message-like-me/references/ensoul.md +94 -0
- package/skills/message-like-me/references/evaluation.md +81 -0
- package/skills/message-like-me/references/privacy.md +106 -0
- 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.
|