@wirecat/cli-core 0.477.0 → 0.478.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 CHANGED
@@ -5,6 +5,18 @@ Notable changes to `@wirecat/cli-core` (`@wirecat/cli-messaging` up to 0.261.0,
5
5
  minor version may break callers, and says how under "Changed — may break callers". `pnpm docs:check` checks the
6
6
  shape of this file.
7
7
 
8
+ ## Unreleased
9
+
10
+ ## 0.478.0 — 29.10.2026
11
+
12
+ ### Fixed
13
+
14
+ - **`sender_is_me` on `inbox`, `review`, `search messages` and `search all`**, as 0.473.0 promised. The catch-up
15
+ answers and the search hits are built field by field, not from `messageItem`, and left it out; only `messages list`
16
+ and the other `messageItem` answers had it. A `search all` hit carries it for a message or a mail, and the picture
17
+ lane's hits of `search messages` carry it too. Every answer applies the one rule: `outgoing`, or the sender id is
18
+ the reading account's own.
19
+
8
20
  ## 0.477.0 — 29.10.2026
9
21
 
10
22
  ### Added
@@ -20,6 +20,7 @@ export declare const inboxAnswer: (inbox: Inbox, hearing: Hearing | undefined, r
20
20
  chat_title: string | null | undefined;
21
21
  timestamp: string;
22
22
  sender_name: string | null;
23
+ sender_is_me: boolean;
23
24
  outgoing: boolean | null;
24
25
  text: string;
25
26
  transcript?: string | undefined;
@@ -74,6 +75,7 @@ export declare const reviewAnswer: (found: Review, hearing: Hearing | undefined,
74
75
  chat_title: string | null | undefined;
75
76
  timestamp: string;
76
77
  sender_name: string | null;
78
+ sender_is_me: boolean;
77
79
  outgoing: boolean | null;
78
80
  text: string;
79
81
  transcript?: string | undefined;
@@ -34,13 +34,14 @@ export const catchUpQuery = (words, registry, command) => {
34
34
  };
35
35
  };
36
36
  const summaryOf = (message, reader) => {
37
- const { ref, chat_id, chat_title, timestamp, sender_name, outgoing, text, transcript, attachments } = messageItem(message, reader);
37
+ const { ref, chat_id, chat_title, timestamp, sender_name, sender_is_me, outgoing, text, transcript, attachments } = messageItem(message, reader);
38
38
  return {
39
39
  ref,
40
40
  chat_id,
41
41
  chat_title,
42
42
  timestamp,
43
43
  sender_name,
44
+ sender_is_me,
44
45
  outgoing,
45
46
  text,
46
47
  ...(transcript === undefined ? {} : { transcript }),
@@ -1,5 +1,5 @@
1
1
  import type * as v from "valibot";
2
- import type { Message, MessageHit } from "../../domain/models.js";
2
+ import { type Message, type MessageHit } from "../../domain/models.js";
3
3
  import type { message as messageSchema } from "../../entities/message.js";
4
4
  import type { MessengerAdapter } from "../../ports/messenger.js";
5
5
  import type { AccountKey } from "../../store/store.js";
@@ -1,5 +1,6 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { formatLocator, parseLocator } from "../../domain/locator.js";
3
+ import { senderIsMe, } from "../../domain/models.js";
3
4
  const defined = (value) => Object.fromEntries(Object.entries(value).filter(([, one]) => one !== undefined));
4
5
  const attachmentItem = (attachment) => defined({
5
6
  kind: attachment.kind,
@@ -50,8 +51,7 @@ export const messageItem = (message, reader) => {
50
51
  sender_name: message.senderName,
51
52
  sender_username: message.senderUsername,
52
53
  sender_is_chat: message.senderIsChat,
53
- // MAX reports the owner's own messages with no sender id, so `outgoing` counts as well as the id.
54
- sender_is_me: message.outgoing === true || (message.senderId !== null && String(message.senderId) === account),
54
+ sender_is_me: senderIsMe(message, account),
55
55
  mentions: message.mentions,
56
56
  timestamp: message.timestamp,
57
57
  edited_at: message.editedAt,
@@ -1,4 +1,5 @@
1
1
  import { formatLocator, parseLocator } from "../../domain/locator.js";
2
+ import { senderIsMe } from "../../domain/models.js";
2
3
  import { DEFAULT_TEXT_MODEL } from "../../embeddings/models.js";
3
4
  import { excerpt } from "../../services/excerpt.js";
4
5
  import { MAX_SEARCH_OFFSET } from "../../services/messages-archive-search.js";
@@ -28,6 +29,7 @@ export const messageHitItem = (hit, query = "") => {
28
29
  chat_title: hit.chatTitle ?? undefined,
29
30
  timestamp: hit.timestamp,
30
31
  sender_name: hit.senderName,
32
+ sender_is_me: senderIsMe(hit, at.account),
31
33
  excerpt: hit.excerpt ?? excerpt(hit.text, query),
32
34
  matched_in: hit.matchedIn,
33
35
  score: hit.score ?? undefined,
@@ -165,10 +167,11 @@ const pictureItems = (found) => {
165
167
  const shown = new Set(found.items.map(({ locator }) => locator));
166
168
  return (found.pictures ?? [])
167
169
  .filter(({ locator }) => !shown.has(locator))
168
- .map(({ locator, timestamp, senderName, score }) => ({
170
+ .map(({ locator, timestamp, senderName, senderIsMe, score }) => ({
169
171
  ref: locator,
170
172
  timestamp,
171
173
  sender_name: senderName,
174
+ sender_is_me: senderIsMe,
172
175
  matched_in: "picture",
173
176
  score,
174
177
  untrusted: ["sender_name"],
@@ -226,6 +229,7 @@ export const allHitItem = (item) => item.kind === "meeting"
226
229
  url: item.url,
227
230
  excerpt: item.text,
228
231
  sender_name: item.from?.name,
232
+ sender_is_me: item.senderIsMe,
229
233
  sender_ref: item.from?.ref,
230
234
  found_by: item.foundBy,
231
235
  untrusted: ALL_HIT_UNTRUSTED[item.kind],
@@ -366,6 +366,8 @@ export interface Message {
366
366
  */
367
367
  delivery?: Delivery;
368
368
  }
369
+ /** MAX reports the owner's own messages with no sender id, so `outgoing` counts as well as the id. */
370
+ export declare const senderIsMe: (message: Pick<Message, "outgoing" | "senderId">, account: string) => boolean;
369
371
  /** `delivered` reached the other person's phone; `read` they opened it (a voice note or view-once file played counts). */
370
372
  export type Delivery = "delivered" | "read";
371
373
  /**
@@ -9,5 +9,7 @@ export const FOLDER_KINDS = ["contacts", "non-contacts", "groups", "channels", "
9
9
  export const FOLDER_SKIPS = ["muted", "read", "archived"];
10
10
  /** What an admin may do, in max-cli's words; a messenger maps them onto its own. */
11
11
  export const ADMIN_RIGHTS = ["read", "members", "admins", "info", "pin", "link", "post", "edit", "delete"];
12
+ /** MAX reports the owner's own messages with no sender id, so `outgoing` counts as well as the id. */
13
+ export const senderIsMe = (message, account) => message.outgoing === true || (message.senderId !== null && String(message.senderId) === account);
12
14
  /** Kinds of attachment `chats media` filters by. */
13
15
  export const MEDIA_KINDS = ["photo", "video", "file", "audio", "link"];
@@ -13,6 +13,8 @@ export declare const hit: v.StrictObjectSchema<{
13
13
  readonly timestamp: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
14
14
  readonly excerpt: v.StringSchema<undefined>;
15
15
  readonly sender_name: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
16
+ /** A message's or a mail's: whether the owner wrote it. */
17
+ readonly sender_is_me: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
16
18
  /** The writer's `person:` reference, where the store has one. */
17
19
  readonly sender_ref: v.OptionalSchema<v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>, undefined>;
18
20
  readonly found_by: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<["title", "words", "meaning"], undefined>, undefined>, undefined>;
@@ -14,6 +14,8 @@ export const hit = v.strictObject({
14
14
  timestamp: v.nullable(time),
15
15
  excerpt: text,
16
16
  sender_name: v.optional(v.nullable(text)),
17
+ /** A message's or a mail's: whether the owner wrote it. */
18
+ sender_is_me: v.optional(v.boolean()),
17
19
  /** The writer's `person:` reference, where the store has one. */
18
20
  sender_ref: v.optional(v.nullable(reference)),
19
21
  found_by: v.optional(v.array(v.picklist(["title", "words", "meaning"]))),
@@ -1141,6 +1141,7 @@ export declare const ENTITIES: {
1141
1141
  readonly ref: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
1142
1142
  readonly chat_title: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
1143
1143
  readonly sender_name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
1144
+ readonly sender_is_me: v.BooleanSchema<undefined>;
1144
1145
  readonly timestamp: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
1145
1146
  readonly excerpt: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
1146
1147
  readonly matched_in: v.OptionalSchema<v.PicklistSchema<["text", "transcript", "filename", "file", "context", "picture"], undefined>, undefined>;
@@ -1623,12 +1624,13 @@ export declare const ENTITIES: {
1623
1624
  }, undefined>, undefined>;
1624
1625
  }, undefined>, undefined>;
1625
1626
  readonly provider_metadata: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
1626
- }, "attachments" | "chat_id" | "chat_title" | "outgoing" | "ref" | "sender_name" | "text" | "timestamp" | "transcript">;
1627
+ }, "attachments" | "chat_id" | "chat_title" | "outgoing" | "ref" | "sender_is_me" | "sender_name" | "text" | "timestamp" | "transcript">;
1627
1628
  readonly "~standard": v.StandardProps<{
1628
1629
  ref: string;
1629
1630
  chat_id: string;
1630
1631
  chat_title?: string | null | undefined;
1631
1632
  sender_name: string | null;
1633
+ sender_is_me: boolean;
1632
1634
  timestamp: string;
1633
1635
  text: string;
1634
1636
  transcript?: string | undefined;
@@ -1671,6 +1673,7 @@ export declare const ENTITIES: {
1671
1673
  chat_id: string;
1672
1674
  chat_title?: string | null | undefined;
1673
1675
  sender_name: string | null;
1676
+ sender_is_me: boolean;
1674
1677
  timestamp: string;
1675
1678
  text: string;
1676
1679
  transcript?: string | undefined;
@@ -1714,6 +1717,7 @@ export declare const ENTITIES: {
1714
1717
  chat_id: string;
1715
1718
  chat_title?: string | null | undefined;
1716
1719
  sender_name: string | null;
1720
+ sender_is_me: boolean;
1717
1721
  timestamp: string;
1718
1722
  text: string;
1719
1723
  transcript?: string | undefined;
@@ -1758,6 +1762,7 @@ export declare const ENTITIES: {
1758
1762
  chat_id: string;
1759
1763
  chat_title?: string | null | undefined;
1760
1764
  sender_name: string | null;
1765
+ sender_is_me: boolean;
1761
1766
  timestamp: string;
1762
1767
  text: string;
1763
1768
  transcript?: string | undefined;
@@ -1801,6 +1806,7 @@ export declare const ENTITIES: {
1801
1806
  chat_id: string;
1802
1807
  chat_title?: string | null | undefined;
1803
1808
  sender_name: string | null;
1809
+ sender_is_me: boolean;
1804
1810
  timestamp: string;
1805
1811
  text: string;
1806
1812
  transcript?: string | undefined;
@@ -4469,6 +4475,7 @@ export declare const ENTITIES: {
4469
4475
  readonly timestamp: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
4470
4476
  readonly excerpt: v.StringSchema<undefined>;
4471
4477
  readonly sender_name: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
4478
+ readonly sender_is_me: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
4472
4479
  readonly sender_ref: v.OptionalSchema<v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>, undefined>;
4473
4480
  readonly found_by: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<["title", "words", "meaning"], undefined>, undefined>, undefined>;
4474
4481
  readonly offset_ms: v.OptionalSchema<v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>, undefined>;
@@ -108,6 +108,7 @@ export const ENTITIES = {
108
108
  "chat_title",
109
109
  "timestamp",
110
110
  "sender_name",
111
+ "sender_is_me",
111
112
  "excerpt",
112
113
  "matched_in",
113
114
  "score",
@@ -126,6 +127,7 @@ export const ENTITIES = {
126
127
  "chat_title",
127
128
  "timestamp",
128
129
  "sender_name",
130
+ "sender_is_me",
129
131
  "outgoing",
130
132
  "text",
131
133
  "transcript",
@@ -1,4 +1,4 @@
1
- import type { Id } from "../domain/models.js";
1
+ import { type Id } from "../domain/models.js";
2
2
  import type { ServiceDeps } from "./deps.js";
3
3
  import type { MessagesService } from "./messages.js";
4
4
  /** A bigger file is no photo a chat sends: skipping it costs a vector, reading it costs memory. */
@@ -20,6 +20,7 @@ export interface PictureHit {
20
20
  messageId: Id;
21
21
  timestamp: string;
22
22
  senderName: string | null;
23
+ senderIsMe: boolean;
23
24
  score: number;
24
25
  }
25
26
  export interface PictureLane {
@@ -3,6 +3,7 @@ import { readFile, stat } from "node:fs/promises";
3
3
  import { setTimeout as sleep } from "node:timers/promises";
4
4
  import { CliError } from "@wirecat/cli-base";
5
5
  import { formatLocator } from "../domain/locator.js";
6
+ import { senderIsMe } from "../domain/models.js";
6
7
  import { DEFAULT_IMAGE_MODEL, imageModel, imageModelsDirectory, isImageModelInstalled, } from "../images/picture/models.js";
7
8
  import { capability } from "../ports/messenger.js";
8
9
  import { refreshedFiles } from "./file-download.js";
@@ -218,6 +219,7 @@ export const picturesService = (deps) => {
218
219
  messageId,
219
220
  timestamp: message.timestamp,
220
221
  senderName: message.senderName,
222
+ senderIsMe: senderIsMe(message, account.account),
221
223
  score: Number(score.toFixed(4)),
222
224
  },
223
225
  ];
@@ -1,5 +1,5 @@
1
1
  import { type ErrorCode } from "@wirecat/cli-base";
2
- import type { MailFields } from "../domain/models.js";
2
+ import { type MailFields } from "../domain/models.js";
3
3
  import type { MessengerTraits } from "../ports/traits.js";
4
4
  import type { AccountKey, MessageStore } from "../store/store.js";
5
5
  import type { SearchFound, SearchQuery } from "./messages.js";
@@ -25,6 +25,8 @@ export interface SearchAllItem {
25
25
  name: string | null;
26
26
  ref: string | null;
27
27
  };
28
+ /** A message's or a mail's: whether the owner wrote it. */
29
+ senderIsMe?: boolean;
28
30
  /** A mail's people, labels and thread, as `search mail` gives them. */
29
31
  email?: MailFields;
30
32
  /** Which search found it: words, meaning or both. */
@@ -1,6 +1,7 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { parseLucene } from "@wirecat/cli-base/query";
3
3
  import { parseLocator } from "../domain/locator.js";
4
+ import { senderIsMe } from "../domain/models.js";
4
5
  import { INTERNAL_NOTES, kindIn } from "../domain/providers.js";
5
6
  import { runEmbedders } from "../embeddings/embed.js";
6
7
  import { DEFAULT_TEXT_MODEL } from "../embeddings/models.js";
@@ -96,6 +97,7 @@ export const searchAll = async (store, account, request, messenger = {}, searchM
96
97
  from: hit.email?.from
97
98
  ? { name: hit.email.from.name ?? hit.email.from.address, ref: hit.email.from.ref }
98
99
  : { name: hit.senderName, ref: null },
100
+ senderIsMe: senderIsMe(hit, at.account),
99
101
  ...(hit.email ? { email: hit.email } : {}),
100
102
  foundBy,
101
103
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.477.0",
3
+ "version": "0.478.0",
4
4
  "description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Viacheslav Ptsarev",