@wirecat/cli-core 0.549.0 → 0.551.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 (137) hide show
  1. package/CHANGELOG.md +223 -22
  2. package/THIRD_PARTY_NOTICES +41 -0
  3. package/dist/calendar/ics.d.ts +1 -1
  4. package/dist/calendar/ics.js +5 -2
  5. package/dist/calendar/windows-zones.generated.d.ts +1 -0
  6. package/dist/calendar/windows-zones.generated.js +146 -0
  7. package/dist/cli/compact.js +2 -0
  8. package/dist/cli/index.d.ts +3 -0
  9. package/dist/cli/index.js +3 -0
  10. package/dist/cli/meeting-findings-input.js +4 -3
  11. package/dist/cli/messenger/backfill-command.js +28 -1
  12. package/dist/cli/messenger/context.js +1 -0
  13. package/dist/cli/messenger/history-gaps.js +4 -4
  14. package/dist/cli/messenger/mcp-command.js +1 -0
  15. package/dist/cli/messenger/messages-delete-command.js +7 -8
  16. package/dist/cli/messenger/projects-command.js +47 -2
  17. package/dist/cli/payment-query.d.ts +8 -0
  18. package/dist/cli/payment-query.js +27 -0
  19. package/dist/cli/project-status-rows.d.ts +8 -0
  20. package/dist/cli/project-status-rows.js +102 -0
  21. package/dist/cli/project-status.d.ts +132 -0
  22. package/dist/cli/project-status.js +248 -0
  23. package/dist/cli/steps.js +4 -1
  24. package/dist/domain/knowledge.d.ts +1 -1
  25. package/dist/domain/knowledge.js +1 -1
  26. package/dist/domain/status-words.d.ts +3 -0
  27. package/dist/domain/status-words.js +44 -0
  28. package/dist/entities/fact.d.ts +12 -1
  29. package/dist/entities/fact.js +13 -0
  30. package/dist/entities/index.d.ts +18 -2
  31. package/dist/entities/meeting-findings.d.ts +1 -1
  32. package/dist/entities/message-transfer.d.ts +16 -0
  33. package/dist/entities/message-transfer.js +6 -0
  34. package/dist/errors/reasons.js +1 -0
  35. package/dist/mail/partial.d.ts +9 -3
  36. package/dist/mail/partial.js +35 -10
  37. package/dist/mail/source.d.ts +7 -1
  38. package/dist/mail/sync.js +1 -1
  39. package/dist/mcp/app-server.js +1 -0
  40. package/dist/mcp/http/oauth.d.ts +2 -1
  41. package/dist/mcp/http/oauth.js +2 -2
  42. package/dist/mcp/http/serve.d.ts +2 -0
  43. package/dist/mcp/http/serve.js +2 -1
  44. package/dist/mcp/server.js +1 -0
  45. package/dist/mcp/surface.js +1 -1
  46. package/dist/meetings/findings-rules.d.ts +14 -3
  47. package/dist/meetings/findings-rules.js +16 -2
  48. package/dist/meetings/index.d.ts +3 -2
  49. package/dist/meetings/index.js +2 -1
  50. package/dist/meetings/meeting-context.d.ts +2 -3
  51. package/dist/meetings/meeting-context.js +3 -4
  52. package/dist/meetings/payment.d.ts +27 -0
  53. package/dist/meetings/payment.js +81 -0
  54. package/dist/services/archive.d.ts +13 -0
  55. package/dist/services/archive.js +56 -5
  56. package/dist/services/deps.d.ts +3 -0
  57. package/dist/services/history-limits.d.ts +2 -0
  58. package/dist/services/index.d.ts +4 -1
  59. package/dist/services/index.js +4 -1
  60. package/dist/services/job-query.d.ts +5 -2
  61. package/dist/services/job-query.js +5 -2
  62. package/dist/services/link-scorers/alias-hits.d.ts +7 -0
  63. package/dist/services/link-scorers/alias-hits.js +54 -0
  64. package/dist/services/link-scorers/chat-senders.d.ts +6 -0
  65. package/dist/services/link-scorers/chat-senders.js +56 -0
  66. package/dist/services/link-scorers/chat-title.d.ts +6 -0
  67. package/dist/services/link-scorers/chat-title.js +44 -0
  68. package/dist/services/link-scorers/email-domain.d.ts +6 -0
  69. package/dist/services/link-scorers/email-domain.js +64 -0
  70. package/dist/services/link-scorers/index.d.ts +9 -0
  71. package/dist/services/link-scorers/index.js +8 -0
  72. package/dist/services/link-scorers/mail-thread.d.ts +3 -0
  73. package/dist/services/link-scorers/mail-thread.js +74 -0
  74. package/dist/services/link-scorers/member-chats.d.ts +3 -0
  75. package/dist/services/link-scorers/member-chats.js +40 -0
  76. package/dist/services/link-scorers/member-dialog.d.ts +3 -0
  77. package/dist/services/link-scorers/member-dialog.js +34 -0
  78. package/dist/services/link-scorers/scan.d.ts +41 -0
  79. package/dist/services/link-scorers/scan.js +244 -0
  80. package/dist/services/link-scorers/shared.d.ts +26 -0
  81. package/dist/services/link-scorers/shared.js +48 -0
  82. package/dist/services/link-scorers/types.d.ts +77 -0
  83. package/dist/services/link-scorers/types.js +1 -0
  84. package/dist/services/project-status/index.d.ts +71 -0
  85. package/dist/services/project-status/index.js +196 -0
  86. package/dist/services/project-status/items.d.ts +10 -0
  87. package/dist/services/project-status/items.js +23 -0
  88. package/dist/services/project-status/signals/amounts.d.ts +20 -0
  89. package/dist/services/project-status/signals/amounts.js +91 -0
  90. package/dist/services/project-status/signals/children.d.ts +13 -0
  91. package/dist/services/project-status/signals/children.js +28 -0
  92. package/dist/services/project-status/signals/coverage.d.ts +13 -0
  93. package/dist/services/project-status/signals/coverage.js +30 -0
  94. package/dist/services/project-status/signals/index.d.ts +3 -0
  95. package/dist/services/project-status/signals/index.js +21 -0
  96. package/dist/services/project-status/signals/last-message.d.ts +4 -0
  97. package/dist/services/project-status/signals/last-message.js +22 -0
  98. package/dist/services/project-status/signals/payments.d.ts +9 -0
  99. package/dist/services/project-status/signals/payments.js +24 -0
  100. package/dist/services/project-status/signals/promises.d.ts +17 -0
  101. package/dist/services/project-status/signals/promises.js +109 -0
  102. package/dist/services/project-status/signals/questions.d.ts +8 -0
  103. package/dist/services/project-status/signals/questions.js +38 -0
  104. package/dist/services/project-status/signals/reply-next.d.ts +19 -0
  105. package/dist/services/project-status/signals/reply-next.js +75 -0
  106. package/dist/services/project-status/signals/topics.d.ts +10 -0
  107. package/dist/services/project-status/signals/topics.js +50 -0
  108. package/dist/services/project-status/types.d.ts +84 -0
  109. package/dist/services/project-status/types.js +1 -0
  110. package/dist/services/proposals/index.d.ts +1 -0
  111. package/dist/services/proposals/index.js +1 -0
  112. package/dist/services/proposals/kinds/findings.d.ts +3 -0
  113. package/dist/services/proposals/kinds/findings.js +8 -1
  114. package/dist/store/decisions.d.ts +5 -1
  115. package/dist/store/decisions.js +5 -1
  116. package/dist/store/evidence-findings.js +1 -0
  117. package/dist/store/index.d.ts +2 -1
  118. package/dist/store/index.js +1 -0
  119. package/dist/store/project-transfer/request.d.ts +1 -1
  120. package/dist/store/project-transfer/schema.d.ts +2 -2
  121. package/dist/store/project-transfer/snapshot-model.d.ts +2 -2
  122. package/dist/store/sqlite/email-locations.d.ts +22 -1
  123. package/dist/store/sqlite/email-locations.js +29 -2
  124. package/dist/store/sqlite/emails.d.ts +3 -7
  125. package/dist/store/sqlite/emails.js +4 -2
  126. package/dist/store/sqlite/evidence-findings.js +5 -1
  127. package/dist/store/sqlite/facts.d.ts +13 -0
  128. package/dist/store/sqlite/facts.js +64 -6
  129. package/dist/store/sqlite/findings-core.js +14 -1
  130. package/dist/store/sqlite/knowledge.d.ts +16 -2
  131. package/dist/store/sqlite/knowledge.js +1 -0
  132. package/dist/store/sqlite/project-transfer/snapshot.d.ts +1 -1
  133. package/dist/store/sqlite/relation-proposals.d.ts +29 -2
  134. package/dist/store/sqlite/relation-proposals.js +75 -4
  135. package/dist/store/store.d.ts +1 -0
  136. package/package.json +3 -3
  137. package/parity.json +11 -0
@@ -0,0 +1,248 @@
1
+ import * as v from "valibot";
2
+ import { snakeKeys } from "./envelope.js";
3
+ import { heldPhrase } from "./messenger/search-answers.js";
4
+ import { STATUS_SIGNAL_ROWS } from "./project-status-rows.js";
5
+ const day = (at) => new Date(at).toISOString().slice(0, 10);
6
+ const listKey = (signal) => signal.replaceAll("-", "_");
7
+ const SECTIONS = {
8
+ signals: "status.signal",
9
+ money: "status.money",
10
+ chats: "status.chat",
11
+ out_of_scope: "status.out-of-scope",
12
+ failed: "status.failed",
13
+ };
14
+ /** The `project_status` answer drawn by `--compact`: each signal's list with that signal's row. */
15
+ export const projectStatusRow = (signals) => ({
16
+ columns: [],
17
+ sections: {
18
+ ...SECTIONS,
19
+ ...Object.fromEntries(signals.map(({ id }) => [listKey(id), `status.${id}`])),
20
+ },
21
+ });
22
+ const statusItem = v.looseObject({
23
+ signal: v.string(),
24
+ ref: v.nullable(v.string()),
25
+ chat: v.nullable(v.string()),
26
+ chat_title: v.nullable(v.string()),
27
+ at: v.nullable(v.string()),
28
+ who: v.nullable(v.strictObject({ ref: v.nullable(v.string()), name: v.nullable(v.string()), owner: v.boolean() })),
29
+ evidence: v.array(v.strictObject({ ref: v.string(), fingerprint: v.nullable(v.string()) })),
30
+ basis: v.picklist(["store", "fact", "task", "pattern"]),
31
+ data: v.record(v.string(), v.unknown()),
32
+ });
33
+ /** Where a project stands: per chat signals, open money, and what was left out. Empty lists are left out. */
34
+ export const projectStatusSchema = v.looseObject({
35
+ project: v.string(),
36
+ key: v.nullable(v.string()),
37
+ name: v.string(),
38
+ window: v.strictObject({ since: v.string(), until: v.string() }),
39
+ signals: v.array(v.strictObject({ id: v.string(), version: v.number() })),
40
+ money: v.array(v.strictObject({ direction: v.string(), currency: v.nullable(v.string()), amount: v.string(), rows: v.number() })),
41
+ chats: v.array(v.strictObject({
42
+ chat: v.string(),
43
+ title: v.nullable(v.string()),
44
+ kind: v.string(),
45
+ role: v.nullable(v.string()),
46
+ purpose: v.string(),
47
+ project: v.string(),
48
+ coverage: v.string(),
49
+ attention: v.number(),
50
+ why: v.nullable(v.string()),
51
+ })),
52
+ out_of_scope: v.optional(v.array(v.strictObject({ chat: v.string(), reason: v.string(), purpose: v.optional(v.string()) }))),
53
+ failed: v.optional(v.array(v.strictObject({ signal: v.string(), why: v.string() }))),
54
+ ...Object.fromEntries(Object.keys(STATUS_SIGNAL_ROWS).map((name) => [listKey(name.slice(7)), v.optional(v.array(statusItem))])),
55
+ });
56
+ const coverageOf = (found, chat) => found.items.find((one) => one.signal === "coverage" && one.chat === chat)?.data;
57
+ const partial = (found, chat) => {
58
+ const held = coverageOf(found, chat.ref);
59
+ return held ? held.state !== "complete" && held.beforeHeld : chat.coverage.completeness.state !== "complete";
60
+ };
61
+ const readFrom = (found, chat) => {
62
+ const reply = found.items.find((one) => one.signal === "reply-next" && one.chat === chat)?.data;
63
+ const money = found.items
64
+ .filter((one) => one.signal === "amounts" && one.chat === chat && one.data.state === "open")
65
+ .map((one) => one.data.since)
66
+ .sort()[0];
67
+ return day([money, reply?.waitingSince, found.window.since].find((one) => typeof one === "string"));
68
+ };
69
+ /** At most two: the chats `attention` weighs highest, a chat held in part synced first. */
70
+ const nextOf = (found, hints) => {
71
+ const byRef = new Map(found.chats.map((chat) => [chat.ref, chat]));
72
+ const top = found.attention.slice(0, 2).flatMap((one) => {
73
+ const chat = byRef.get(one.chat);
74
+ return chat ? [{ chat, why: one.why }] : [];
75
+ });
76
+ const because = (step, why) => (step ? [{ ...step, why }] : []);
77
+ const syncs = top.flatMap(({ chat, why }) => partial(found, chat) ? because(hints.sync(chat, day(found.window.since)), why) : []);
78
+ const reads = top.flatMap(({ chat, why }) => partial(found, chat) ? [] : because(hints.read(chat, readFrom(found, chat.ref)), why));
79
+ return [...syncs, ...reads].slice(0, 2);
80
+ };
81
+ const missingOf = (found) => {
82
+ const held = found.chats.filter((chat) => partial(found, chat));
83
+ const [first] = held;
84
+ return [
85
+ ...(first
86
+ ? [
87
+ {
88
+ field: "history",
89
+ why: `${held.length} of ${found.chats.length} chats hold only part of the window; ` +
90
+ `the first is ${heldPhrase(first.coverage)}`,
91
+ step: "messages.sync",
92
+ },
93
+ ]
94
+ : []),
95
+ ...(found.outOfScope.length
96
+ ? [
97
+ {
98
+ field: "chats",
99
+ why: `${found.outOfScope.length} linked chats were not read here (${[
100
+ ...new Set(found.outOfScope.map((one) => one.reason)),
101
+ ].join(", ")}); memo projects status reads every account`,
102
+ },
103
+ ]
104
+ : []),
105
+ ...found.failed.map((one) => ({ field: `signal:${one.signal}`, why: one.why })),
106
+ ];
107
+ };
108
+ const withPlace = (found, hints) => {
109
+ const titles = new Map(found.chats.map((chat) => [chat.ref, chat.title]));
110
+ const sync = (one) => {
111
+ const chat = found.chats.find((each) => each.ref === one.chat);
112
+ const step = chat && partial(found, chat) ? hints.sync(chat, day(found.window.since)) : null;
113
+ return step ? hints.line(step) : null;
114
+ };
115
+ return (one) => ({
116
+ ...one,
117
+ ref: one.evidence[0]?.ref ?? one.chat,
118
+ chatTitle: one.chat === null ? null : (titles.get(one.chat) ?? null),
119
+ ...(one.signal === "coverage" ? { syncCommand: sync(one) } : {}),
120
+ });
121
+ };
122
+ /** The answer of `projects status`: the item, two steps at most, what is missing, and the window. */
123
+ export const projectStatusAnswer = (found, hints) => {
124
+ const weights = new Map(found.attention.map((one) => [one.chat, one]));
125
+ const place = withPlace(found, hints);
126
+ const lists = Object.fromEntries(found.signals.flatMap(({ id }) => {
127
+ const items = found.items.filter((one) => one.signal === id).map(place);
128
+ return items.length ? [[listKey(id), items]] : [];
129
+ }));
130
+ const item = snakeKeys({
131
+ project: found.project.ref,
132
+ key: found.project.key,
133
+ name: found.project.name,
134
+ window: found.window,
135
+ signals: found.signals,
136
+ money: found.money.open,
137
+ chats: found.chats.map((chat) => ({
138
+ chat: chat.ref,
139
+ title: chat.title,
140
+ kind: chat.kind,
141
+ role: chat.role,
142
+ purpose: chat.purpose,
143
+ project: chat.project,
144
+ coverage: chat.coverage.completeness.state,
145
+ attention: weights.get(chat.ref)?.weight ?? 0,
146
+ why: weights.get(chat.ref)?.why ?? null,
147
+ })),
148
+ ...(found.outOfScope.length ? { outOfScope: found.outOfScope } : {}),
149
+ ...(found.failed.length ? { failed: found.failed } : {}),
150
+ ...lists,
151
+ });
152
+ const missing = missingOf(found);
153
+ const complete = found.failed.length === 0 && !found.chats.some((chat) => partial(found, chat));
154
+ return {
155
+ item,
156
+ next: nextOf(found, hints),
157
+ missing,
158
+ meta: { window: found.window, complete, ...(found.accounts.length ? { accounts: found.accounts } : {}) },
159
+ };
160
+ };
161
+ const NEXT_WORDS = { owner: "YOU", them: "them", nobody: "nobody" };
162
+ const chatLine = (found, chat) => {
163
+ const of = (signal) => found.items.filter((one) => one.signal === signal && one.chat === chat.ref);
164
+ const [last] = of("last-message");
165
+ const reply = of("reply-next")[0]?.data;
166
+ const head = [
167
+ `${chat.title ?? chat.ref} (${[chat.role ?? "no role", chat.kind].join(", ")})`,
168
+ last
169
+ ? `last ${day(last.at)} ${last.who?.owner ? "you" : (last.who?.name ?? "them")}`
170
+ : "nothing in the window",
171
+ ...(reply
172
+ ? [
173
+ `next: ${reply.next === "named" ? `${reply.addressed?.name ?? "someone"} (named)` : NEXT_WORDS[reply.next]}` +
174
+ (reply.waitingSince ? ` since ${day(reply.waitingSince)}` : ""),
175
+ ]
176
+ : []),
177
+ ...(partial(found, chat) ? [heldPhrase(chat.coverage)] : []),
178
+ ].join(" · ");
179
+ const open = [];
180
+ const asked = of("questions").map((one) => one.data);
181
+ const yours = asked.filter((one) => one.side === "yours").length;
182
+ const theirs = asked.length - yours;
183
+ if (theirs)
184
+ open.push(`${theirs} question${theirs === 1 ? "" : "s"} to you unanswered`);
185
+ if (yours)
186
+ open.push(`${yours} question${yours === 1 ? "" : "s"} of yours unanswered`);
187
+ for (const topic of of("topics")) {
188
+ const data = topic.data;
189
+ if (data.silentSince)
190
+ open.push(`"${data.query}" quiet on their side since ${day(data.silentSince)}`);
191
+ }
192
+ const promised = of("promises").length;
193
+ if (promised)
194
+ open.push(`${promised} promise${promised === 1 ? "" : "s"}`);
195
+ return [` ${head}`, ...(open.length ? [` open: ${open.join(" · ")}`] : [])];
196
+ };
197
+ const moneyLine = (one, titles) => {
198
+ const data = one.data;
199
+ const asks = data.asks > 1 && data.lastAsked ? ` (asked ${data.asks}×, last ${day(data.lastAsked)})` : "";
200
+ const where = one.chat === null ? "" : ` · ${titles.get(one.chat) ?? one.chat}`;
201
+ const words = data.direction.replace("_", " ");
202
+ const sum = `${data.amount} ${data.currency ?? "?"} ${data.state} since ${day(data.since)}`;
203
+ return ` ${words.padEnd(7)} ${sum}${asks}${where} · ${one.evidence[0]?.ref ?? ""} [${one.basis}]`;
204
+ };
205
+ /** The human form: header, money, a line per chat with what is open, then the steps. */
206
+ export const projectStatusText = (found, hints) => {
207
+ const titles = new Map(found.chats.map((chat) => [chat.ref, chat.title]));
208
+ const { next } = projectStatusAnswer(found, hints);
209
+ const money = found.items.filter((one) => one.signal === "amounts" && one.data.state !== "mentioned");
210
+ const payments = found.items.filter((one) => one.signal === "payments");
211
+ const children = found.items.filter((one) => one.signal === "children");
212
+ const key = found.project.key ? `${found.project.key} · ` : "";
213
+ const lines = [
214
+ `${key}${found.project.name} · ${found.chats.length} linked chats in ${found.accounts.length} accounts, ` +
215
+ `${day(found.window.since)} to ${day(found.window.until)}`,
216
+ ];
217
+ if (money.length || payments.length) {
218
+ lines.push("Money");
219
+ for (const one of money)
220
+ lines.push(moneyLine(one, titles));
221
+ for (const one of payments) {
222
+ const data = one.data;
223
+ lines.push(` payment ${data.title} · ${one.evidence[0]?.ref ?? ""} [${data.status}]`);
224
+ }
225
+ }
226
+ if (found.chats.length)
227
+ lines.push("Chats");
228
+ for (const chat of found.chats)
229
+ for (const line of chatLine(found, chat))
230
+ lines.push(line);
231
+ if (children.length)
232
+ lines.push("Children");
233
+ for (const one of children) {
234
+ const data = one.data;
235
+ const cut = one.data.cut;
236
+ lines.push(` ${cut ? `${data.child} not read (${cut})` : `${data.key ?? data.child} · ${data.name} · ${data.chats} chats`}`);
237
+ }
238
+ if (found.outOfScope.length)
239
+ lines.push(`Not read here: ${found.outOfScope.map((one) => `${one.chat} (${one.reason})`).join(", ")}`);
240
+ for (const one of found.failed)
241
+ lines.push(`Signal ${one.signal} failed: ${one.why}`);
242
+ if (next.length)
243
+ lines.push("Next");
244
+ next.forEach((step, index) => {
245
+ lines.push(` ${index + 1}. ${hints.line(step)}`);
246
+ });
247
+ return lines.join("\n");
248
+ };
package/dist/cli/steps.js CHANGED
@@ -48,7 +48,10 @@ export const STEPS = defineSteps([
48
48
  {
49
49
  id: "messages.list",
50
50
  command: "messages list",
51
- args: [{ name: "chat", required: true }],
51
+ args: [
52
+ { name: "chat", required: true },
53
+ { name: "query", required: false },
54
+ ],
52
55
  why: "only the newest were read here; this reads the chat itself",
53
56
  },
54
57
  {
@@ -1,4 +1,4 @@
1
- export declare const FACT_TYPES: readonly ["fact", "statement", "question", "decision", "promise"];
1
+ export { FACT_TYPES } from "../meetings/index.js";
2
2
  export declare const FACT_STATUSES: readonly ["proposed", "accepted", "superseded", "reversed", "rejected"];
3
3
  export declare const MEMORY_KINDS: readonly ["summary", "digest", "preference"];
4
4
  export declare const MEMORY_STATUSES: readonly ["proposed", "confirmed", "stale", "superseded"];
@@ -1,5 +1,5 @@
1
1
  import { TASK_KINDS } from "../tasks/index.js";
2
- export const FACT_TYPES = ["fact", "statement", "question", "decision", "promise"];
2
+ export { FACT_TYPES } from "../meetings/index.js";
3
3
  export const FACT_STATUSES = ["proposed", "accepted", "superseded", "reversed", "rejected"];
4
4
  export const MEMORY_KINDS = ["summary", "digest", "preference"];
5
5
  export const MEMORY_STATUSES = ["proposed", "confirmed", "stale", "superseded"];
@@ -0,0 +1,3 @@
1
+ export declare const MONEY_DONE_FORMS: RegExp;
2
+ /** First person future: "I'll send", "сделаю", "lo envío". Labelled a guess wherever it is shown. */
3
+ export declare const PROMISE_FORMS: RegExp;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Word patterns for project status, ru/en/es, matched on the text as written (case folded here). A stem cannot tell
3
+ * `оплатил` from `оплати`, so the past forms a money stem shares with a request are listed whole: a message with one
4
+ * of them reads as "done", whatever stems it also hits.
5
+ */
6
+ const words = (forms) => new RegExp(`(?<![\\p{L}\\p{N}])(?:${forms.join("|")})(?![\\p{L}\\p{N}])`, "iu");
7
+ export const MONEY_DONE_FORMS = words([
8
+ "оплатил[аи]?",
9
+ "заплатил[аи]?",
10
+ "перевел[аи]?",
11
+ "перевёл",
12
+ "отправил[аи]?",
13
+ "оплачено",
14
+ "переведено",
15
+ "paid",
16
+ "transferred",
17
+ "pagué",
18
+ "pagado",
19
+ "transferí",
20
+ ]);
21
+ /** First person future: "I'll send", "сделаю", "lo envío". Labelled a guess wherever it is shown. */
22
+ export const PROMISE_FORMS = words([
23
+ "сделаю",
24
+ "пришлю",
25
+ "отправлю",
26
+ "подготовлю",
27
+ "проверю",
28
+ "напишу",
29
+ "позвоню",
30
+ "скину",
31
+ "вышлю",
32
+ "оплачу",
33
+ "будет готов[аоы]?",
34
+ "i'?ll (?:send|do|check|call|write|pay|prepare|share|fix)",
35
+ "i will (?:send|do|check|call|write|pay|prepare|share|fix)",
36
+ "will be ready",
37
+ "lo envío",
38
+ "te envío",
39
+ "enviaré",
40
+ "haré",
41
+ "mandaré",
42
+ "te llamo",
43
+ "estará listo",
44
+ ]);
@@ -2,7 +2,7 @@ import * as v from "valibot";
2
2
  export declare const fact: v.StrictObjectSchema<{
3
3
  readonly ref: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
4
4
  readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
5
- readonly kind: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise"], undefined>;
5
+ readonly kind: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise", "payment"], undefined>;
6
6
  readonly text: v.StringSchema<undefined>;
7
7
  readonly status: v.PicklistSchema<readonly ["proposed", "accepted", "superseded", "reversed", "rejected"], undefined>;
8
8
  readonly project: v.NullableSchema<v.StringSchema<undefined>, undefined>;
@@ -29,6 +29,17 @@ export declare const fact: v.StrictObjectSchema<{
29
29
  /** Who recorded it: the owner, or an agent whose fact waits as proposed. */
30
30
  readonly origin: v.PicklistSchema<["owner", "agent"], undefined>;
31
31
  readonly evidence: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
32
+ /** A `payment` fact's fields. */
33
+ readonly payment: v.OptionalSchema<v.NullableSchema<v.StrictObjectSchema<{
34
+ readonly amount: v.StringSchema<undefined>;
35
+ readonly currency: v.StringSchema<undefined>;
36
+ readonly direction: v.PicklistSchema<readonly ["to_us", "from_us"], undefined>;
37
+ readonly counterparty: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
38
+ readonly counterparty_name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
39
+ readonly due_at: v.NullableSchema<v.StringSchema<undefined>, undefined>;
40
+ readonly state: v.PicklistSchema<readonly ["expected", "paid", "cancelled"], undefined>;
41
+ readonly item: v.NullableSchema<v.StringSchema<undefined>, undefined>;
42
+ }, undefined>, undefined>, undefined>;
32
43
  /** With `--sources`: each piece of evidence read back. */
33
44
  readonly sources: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
34
45
  readonly ref: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
@@ -1,6 +1,17 @@
1
1
  import * as v from "valibot";
2
2
  import { FACT_STATUSES, FACT_TYPES, MEMORY_SCOPES } from "../domain/knowledge.js";
3
+ import { PAYMENT_DIRECTIONS, PAYMENT_STATES } from "../meetings/index.js";
3
4
  import { author, evidenceSource, id, reference, text, time } from "./fields.js";
5
+ const payment = v.strictObject({
6
+ amount: text,
7
+ currency: text,
8
+ direction: v.picklist(PAYMENT_DIRECTIONS),
9
+ counterparty: v.nullable(reference),
10
+ counterparty_name: v.nullable(text),
11
+ due_at: v.nullable(text),
12
+ state: v.picklist(PAYMENT_STATES),
13
+ item: v.nullable(text),
14
+ });
4
15
  export const fact = v.strictObject({
5
16
  ref: reference,
6
17
  id,
@@ -24,6 +35,8 @@ export const fact = v.strictObject({
24
35
  /** Who recorded it: the owner, or an agent whose fact waits as proposed. */
25
36
  origin: v.picklist(["owner", "agent"]),
26
37
  evidence: v.array(reference),
38
+ /** A `payment` fact's fields. */
39
+ payment: v.optional(v.nullable(payment)),
27
40
  /** With `--sources`: each piece of evidence read back. */
28
41
  sources: v.optional(v.array(evidenceSource)),
29
42
  created_at: time,
@@ -1099,6 +1099,7 @@ export declare const ENTITIES: {
1099
1099
  readonly reached_since: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
1100
1100
  readonly reached_last: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
1101
1101
  readonly stopped: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
1102
+ readonly limited: v.OptionalSchema<v.PicklistSchema<["group_too_large", "older_than_window"], undefined>, undefined>;
1102
1103
  readonly tasks: v.OptionalSchema<v.StrictObjectSchema<{
1103
1104
  readonly added: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
1104
1105
  readonly closed: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
@@ -1145,6 +1146,11 @@ export declare const ENTITIES: {
1145
1146
  readonly retryable: v.BooleanSchema<undefined>;
1146
1147
  readonly retry_after_ms: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
1147
1148
  }, v.UnknownSchema, undefined>, undefined>;
1149
+ readonly limited: v.OptionalSchema<v.PicklistSchema<["group_too_large", "older_than_window"], undefined>, undefined>;
1150
+ }, undefined>, undefined>;
1151
+ readonly limited: v.OptionalSchema<v.StrictObjectSchema<{
1152
+ readonly group_too_large: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
1153
+ readonly older_than_window: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
1148
1154
  }, undefined>, undefined>;
1149
1155
  readonly tasks: v.OptionalSchema<v.StrictObjectSchema<{
1150
1156
  readonly added: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
@@ -5448,7 +5454,7 @@ export declare const ENTITIES: {
5448
5454
  readonly fact: v.StrictObjectSchema<{
5449
5455
  readonly ref: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
5450
5456
  readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
5451
- readonly kind: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise"], undefined>;
5457
+ readonly kind: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise", "payment"], undefined>;
5452
5458
  readonly text: v.StringSchema<undefined>;
5453
5459
  readonly status: v.PicklistSchema<readonly ["proposed", "accepted", "superseded", "reversed", "rejected"], undefined>;
5454
5460
  readonly project: v.NullableSchema<v.StringSchema<undefined>, undefined>;
@@ -5471,6 +5477,16 @@ export declare const ENTITIES: {
5471
5477
  readonly scope: v.NullableSchema<v.PicklistSchema<readonly ["personal", "work"], undefined>, undefined>;
5472
5478
  readonly origin: v.PicklistSchema<["owner", "agent"], undefined>;
5473
5479
  readonly evidence: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
5480
+ readonly payment: v.OptionalSchema<v.NullableSchema<v.StrictObjectSchema<{
5481
+ readonly amount: v.StringSchema<undefined>;
5482
+ readonly currency: v.StringSchema<undefined>;
5483
+ readonly direction: v.PicklistSchema<readonly ["to_us", "from_us"], undefined>;
5484
+ readonly counterparty: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
5485
+ readonly counterparty_name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5486
+ readonly due_at: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5487
+ readonly state: v.PicklistSchema<readonly ["expected", "paid", "cancelled"], undefined>;
5488
+ readonly item: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5489
+ }, undefined>, undefined>, undefined>;
5474
5490
  readonly sources: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
5475
5491
  readonly ref: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
5476
5492
  readonly kind: v.StringSchema<undefined>;
@@ -6257,7 +6273,7 @@ export declare const ENTITIES: {
6257
6273
  readonly fingerprint: v.StringSchema<undefined>;
6258
6274
  }, undefined>, undefined>;
6259
6275
  readonly kind: v.LiteralSchema<"fact", undefined>;
6260
- readonly type: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise"], undefined>;
6276
+ readonly type: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise", "payment"], undefined>;
6261
6277
  readonly statement: v.StringSchema<undefined>;
6262
6278
  }, undefined>, v.StrictObjectSchema<{
6263
6279
  readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
@@ -17,7 +17,7 @@ export declare const meetingFindings: v.StrictObjectSchema<{
17
17
  readonly fingerprint: v.StringSchema<undefined>;
18
18
  }, undefined>, undefined>;
19
19
  readonly kind: v.LiteralSchema<"fact", undefined>;
20
- readonly type: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise"], undefined>;
20
+ readonly type: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise", "payment"], undefined>;
21
21
  readonly statement: v.StringSchema<undefined>;
22
22
  }, undefined>, v.StrictObjectSchema<{
23
23
  readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
@@ -202,6 +202,8 @@ export declare const messageSyncChat: v.StrictObjectSchema<{
202
202
  readonly reached_since: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
203
203
  readonly reached_last: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
204
204
  readonly stopped: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
205
+ /** The history limits stopped the read: a group too large to read, or the window's start reached. */
206
+ readonly limited: v.OptionalSchema<v.PicklistSchema<["group_too_large", "older_than_window"], undefined>, undefined>;
205
207
  readonly tasks: v.OptionalSchema<v.StrictObjectSchema<{
206
208
  readonly added: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
207
209
  readonly closed: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
@@ -250,6 +252,12 @@ export declare const messageSyncAll: v.StrictObjectSchema<{
250
252
  readonly retryable: v.BooleanSchema<undefined>;
251
253
  readonly retry_after_ms: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
252
254
  }, v.UnknownSchema, undefined>, undefined>;
255
+ readonly limited: v.OptionalSchema<v.PicklistSchema<["group_too_large", "older_than_window"], undefined>, undefined>;
256
+ }, undefined>, undefined>;
257
+ /** How many chats the history limits left unread (big groups) or read only to the window's start. */
258
+ readonly limited: v.OptionalSchema<v.StrictObjectSchema<{
259
+ readonly group_too_large: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
260
+ readonly older_than_window: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
253
261
  }, undefined>, undefined>;
254
262
  readonly tasks: v.OptionalSchema<v.StrictObjectSchema<{
255
263
  readonly added: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
@@ -328,6 +336,8 @@ export declare const messageSync: v.UnionSchema<[v.StrictObjectSchema<{
328
336
  readonly reached_since: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
329
337
  readonly reached_last: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
330
338
  readonly stopped: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
339
+ /** The history limits stopped the read: a group too large to read, or the window's start reached. */
340
+ readonly limited: v.OptionalSchema<v.PicklistSchema<["group_too_large", "older_than_window"], undefined>, undefined>;
331
341
  readonly tasks: v.OptionalSchema<v.StrictObjectSchema<{
332
342
  readonly added: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
333
343
  readonly closed: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
@@ -375,6 +385,12 @@ export declare const messageSync: v.UnionSchema<[v.StrictObjectSchema<{
375
385
  readonly retryable: v.BooleanSchema<undefined>;
376
386
  readonly retry_after_ms: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
377
387
  }, v.UnknownSchema, undefined>, undefined>;
388
+ readonly limited: v.OptionalSchema<v.PicklistSchema<["group_too_large", "older_than_window"], undefined>, undefined>;
389
+ }, undefined>, undefined>;
390
+ /** How many chats the history limits left unread (big groups) or read only to the window's start. */
391
+ readonly limited: v.OptionalSchema<v.StrictObjectSchema<{
392
+ readonly group_too_large: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
393
+ readonly older_than_window: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
378
394
  }, undefined>, undefined>;
379
395
  readonly tasks: v.OptionalSchema<v.StrictObjectSchema<{
380
396
  readonly added: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
@@ -46,6 +46,7 @@ const tasks = v.optional(v.strictObject({
46
46
  held: v.optional(count),
47
47
  held_chats: v.optional(count),
48
48
  }));
49
+ const historyLimited = v.picklist(["group_too_large", "older_than_window"]);
49
50
  export const messageSyncChat = v.strictObject({
50
51
  issue: v.optional(actionableError),
51
52
  resume: v.optional(v.strictObject({ before: v.optional(text) })),
@@ -59,6 +60,8 @@ export const messageSyncChat = v.strictObject({
59
60
  reached_since: v.optional(v.literal(true)),
60
61
  reached_last: v.optional(v.literal(true)),
61
62
  stopped: v.optional(v.literal(true)),
63
+ /** The history limits stopped the read: a group too large to read, or the window's start reached. */
64
+ limited: v.optional(historyLimited),
62
65
  tasks,
63
66
  /** `--full-history`: the chat's pushed history is now kept in full; `false` when the store does not know the chat. */
64
67
  history_included: v.optional(v.boolean()),
@@ -78,7 +81,10 @@ export const messageSyncAll = v.strictObject({
78
81
  stopped: v.optional(v.literal(true)),
79
82
  error: v.optional(text),
80
83
  issue: v.optional(actionableError),
84
+ limited: v.optional(historyLimited),
81
85
  })),
86
+ /** How many chats the history limits left unread (big groups) or read only to the window's start. */
87
+ limited: v.optional(v.strictObject({ group_too_large: count, older_than_window: count })),
82
88
  tasks,
83
89
  });
84
90
  export const messageSyncStarted = v.strictObject({ job: id, pid: count, chat: text, log: text });
@@ -50,6 +50,7 @@ const OTHERS = [
50
50
  validation("extraction.text-too-long", "the text is too long for memory extraction"),
51
51
  validation("file.locked", "another process holds the file's lock"),
52
52
  ["mail.source-failed", "provider_error", "the mail server refused or did not complete a request"],
53
+ ["mail.search-refused", "provider_error", "the mail server refused to search a folder"],
53
54
  ["mirror.partial", "provider_error", "some mirrored files were not synced"],
54
55
  ["profile.wrong-account", "authentication_error", "the signed-in source account does not match this profile"],
55
56
  validation("push.too-many-files", "the folder holds more files than one push takes"),
@@ -6,16 +6,22 @@ import type { MailSource } from "./source.js";
6
6
  export declare const PARTIAL = "partial";
7
7
  /** How many partial emails one sync reads whole at most; the rest wait for the next run. */
8
8
  export declare const PARTIAL_PER_RUN = 100;
9
+ /** Failed reads after which a partial email is given up on (`partial.stuck`) and stays as it is. */
10
+ export declare const PARTIAL_TRIES = 3;
9
11
  export interface PartialOutcome {
10
12
  /** Read whole this run, the mark cleared. */
11
13
  completed: number;
12
- /** Still partial after this run. */
14
+ /** Still partial after this run, tried again by the next. */
13
15
  left: number;
16
+ /** Given up on: the server no longer gives this email at its stored place. They stay readable as they are. */
17
+ stuck: number;
14
18
  }
15
19
  /**
16
20
  * Reads whole the emails a server search stored from their first bytes, so the archive holds their attachments too.
17
- * Sync never reads stored mail again, so without this step a partial email would stay partial. Gmail's are found by
18
- * their id; elsewhere at a stored location, trusted only when the bytes read there carry the stored id.
21
+ * Sync never reads stored mail again, so without this step a partial email would stay partial. A source with `find`
22
+ * finds them by id; elsewhere they are read at their newest stored location under its UIDVALIDITY, trusted only when
23
+ * `identify` gives the stored id for the bytes read there. Each failed read is counted, and after `PARTIAL_TRIES`, or
24
+ * at once when the place holds another message, the email is left partial for good.
19
25
  */
20
26
  export declare const completePartial: ({ store, source, accountId, address, folders, maxBodyChars, maxAttachmentChars, loadEngine, now, take, signal, }: {
21
27
  store: MessageStore;