@wirecat/cli-core 0.544.0 → 0.546.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 (114) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/dist/calendar/busy.js +4 -1
  3. package/dist/calendar/cli/answers.d.ts +31 -0
  4. package/dist/calendar/cli/answers.js +8 -0
  5. package/dist/calendar/cli/index.d.ts +6 -0
  6. package/dist/calendar/cli/index.js +43 -4
  7. package/dist/calendar/cli/remind.js +2 -1
  8. package/dist/calendar/expand.d.ts +17 -0
  9. package/dist/calendar/expand.js +340 -0
  10. package/dist/calendar/feeds.d.ts +96 -0
  11. package/dist/calendar/feeds.js +324 -0
  12. package/dist/calendar/ics.d.ts +15 -4
  13. package/dist/calendar/ics.js +18 -7
  14. package/dist/calendar/index.d.ts +3 -1
  15. package/dist/calendar/index.js +2 -0
  16. package/dist/calendar/server-search.js +3 -1
  17. package/dist/calendar/writes.js +15 -4
  18. package/dist/cli/after-dashes.d.ts +16 -0
  19. package/dist/cli/after-dashes.js +41 -0
  20. package/dist/cli/bot/context.d.ts +1 -1
  21. package/dist/cli/bot/context.js +9 -1
  22. package/dist/cli/bot/messages.js +2 -0
  23. package/dist/cli/bot/setup.js +1 -0
  24. package/dist/cli/contract-templates.js +2 -2
  25. package/dist/cli/entry.d.ts +2 -0
  26. package/dist/cli/entry.js +3 -2
  27. package/dist/cli/index.d.ts +2 -1
  28. package/dist/cli/index.js +2 -1
  29. package/dist/cli/messenger/admin-commands.js +5 -1
  30. package/dist/cli/messenger/admin-contacts-command.js +5 -1
  31. package/dist/cli/messenger/admin-folders-command.js +5 -1
  32. package/dist/cli/messenger/archive-commands.js +20 -2
  33. package/dist/cli/messenger/backfill-command.js +13 -2
  34. package/dist/cli/messenger/complete-command.js +1 -0
  35. package/dist/cli/messenger/context.js +3 -0
  36. package/dist/cli/messenger/history-gaps.d.ts +12 -0
  37. package/dist/cli/messenger/history-gaps.js +41 -0
  38. package/dist/cli/messenger/messages-command.js +9 -22
  39. package/dist/cli/messenger/messages-edit-command.js +1 -0
  40. package/dist/cli/messenger/messages-press-command.js +1 -0
  41. package/dist/cli/messenger/messages-search-command.js +5 -1
  42. package/dist/cli/messenger/messages-send-command.js +1 -0
  43. package/dist/cli/messenger/observed.js +2 -2
  44. package/dist/cli/messenger/polls-command.js +1 -0
  45. package/dist/cli/messenger/private-people-command.js +5 -1
  46. package/dist/cli/messenger/search-answers.d.ts +3 -2
  47. package/dist/cli/messenger/search-answers.js +12 -5
  48. package/dist/cli/messenger/search-command.js +3 -1
  49. package/dist/cli/messenger/stored.d.ts +6 -1
  50. package/dist/cli/messenger/stored.js +94 -5
  51. package/dist/cli/messenger/topics-command.js +1 -1
  52. package/dist/cli/program.js +17 -6
  53. package/dist/cli/schedule/schedule-command.js +1 -0
  54. package/dist/cli/settings.d.ts +9 -0
  55. package/dist/cli/settings.js +20 -0
  56. package/dist/cli/shared-command-exports.d.ts +24 -0
  57. package/dist/cli/steps.js +1 -0
  58. package/dist/domain/message-link.d.ts +3 -0
  59. package/dist/domain/message-link.js +9 -14
  60. package/dist/domain/money-words.d.ts +11 -0
  61. package/dist/domain/money-words.js +38 -0
  62. package/dist/domain/providers.d.ts +2 -0
  63. package/dist/domain/providers.js +15 -3
  64. package/dist/domain/ref-arguments.d.ts +31 -0
  65. package/dist/domain/ref-arguments.js +79 -0
  66. package/dist/domain/references.d.ts +20 -2
  67. package/dist/domain/references.js +132 -47
  68. package/dist/entities/index.d.ts +1 -0
  69. package/dist/entities/message-transfer.d.ts +4 -0
  70. package/dist/entities/message-transfer.js +2 -0
  71. package/dist/errors/reasons.js +18 -0
  72. package/dist/index.d.ts +1 -1
  73. package/dist/index.js +1 -1
  74. package/dist/meetings/mcp/index.d.ts +15 -15
  75. package/dist/oauth/client-file.d.ts +14 -0
  76. package/dist/oauth/client-file.js +28 -0
  77. package/dist/oauth/index.d.ts +1 -0
  78. package/dist/oauth/index.js +1 -0
  79. package/dist/ports/messenger.d.ts +3 -2
  80. package/dist/resolve.d.ts +3 -1
  81. package/dist/resolve.js +8 -0
  82. package/dist/search/task-signature.d.ts +5 -1
  83. package/dist/search/task-signature.js +27 -9
  84. package/dist/services/agenda.d.ts +5 -0
  85. package/dist/services/agenda.js +3 -1
  86. package/dist/services/history-limits.d.ts +45 -0
  87. package/dist/services/history-limits.js +50 -0
  88. package/dist/services/index.d.ts +5 -1
  89. package/dist/services/index.js +5 -1
  90. package/dist/services/message-resolution.d.ts +4 -1
  91. package/dist/services/message-resolution.js +19 -3
  92. package/dist/services/messages.js +15 -13
  93. package/dist/services/money-words.d.ts +3 -0
  94. package/dist/services/money-words.js +5 -0
  95. package/dist/services/questions.d.ts +7 -2
  96. package/dist/services/questions.js +13 -4
  97. package/dist/services/stored-project-gathering.d.ts +9 -0
  98. package/dist/services/stored-project-gathering.js +9 -4
  99. package/dist/services/task-categories.d.ts +2 -0
  100. package/dist/services/task-categories.js +19 -8
  101. package/dist/store/chats.d.ts +10 -0
  102. package/dist/store/chats.js +25 -0
  103. package/dist/store/sqlite/chats.js +2 -1
  104. package/dist/store/sqlite/evidence-sources.js +2 -2
  105. package/dist/store/sqlite/history-skips.d.ts +24 -0
  106. package/dist/store/sqlite/history-skips.js +95 -0
  107. package/dist/store/sqlite/identities.js +1 -1
  108. package/dist/store/sqlite/knowledge.js +7 -2
  109. package/dist/store/sqlite/notes.js +4 -4
  110. package/dist/store/sqlite/things.d.ts +0 -1
  111. package/dist/store/sqlite/things.js +25 -24
  112. package/dist/store/store.d.ts +1 -0
  113. package/package.json +1 -1
  114. package/parity.json +4 -0
@@ -1,8 +1,10 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
+ import { chatReferenceOf, ensureAccount } from "../../domain/ref-arguments.js";
2
3
  import { capability, throughWrapper, } from "../../ports/messenger.js";
3
4
  import { foldName, isId, pickPerson } from "../../resolve.js";
4
5
  import { codeOf } from "../../sends/guarded.js";
5
- import { chatIdIn } from "../../services/message-resolution.js";
6
+ import { limitBatch } from "../../services/history-limits.js";
7
+ import { chatIdIn, profilesOnAccount } from "../../services/message-resolution.js";
6
8
  const REKEY_PACING = { lockMs: 50, pauseMs: 25 };
7
9
  /**
8
10
  * The adapter, with what each read answers written to the store. **A failed write never fails the
@@ -12,8 +14,37 @@ const REKEY_PACING = { lockMs: 50, pauseMs: 25 };
12
14
  * would erase what `chats list` stored. Nor are `chat` and `contact`: a card's member list is not
13
15
  * known to be whole, and `saveMembers` replaces the list.
14
16
  */
15
- export const stored = (messenger, { account, store, warn, events, pending, announcing, deletedWithoutChat, profile, rekeyed, rekeyPacing = REKEY_PACING, closing, namesFromStore, recipientIds, countsUnread = false, }) => {
17
+ export const stored = (messenger, { account, store, warn, events, pending, announcing, deletedWithoutChat, profile, rekeyed, rekeyPacing = REKEY_PACING, closing, namesFromStore, recipientIds, countsUnread = false, historyLimits, command = "cli", }) => {
16
18
  let warned = false;
19
+ let limitedNoted = false;
20
+ const limited = async (opened, batch) => {
21
+ if (!historyLimits || !batch.messages?.length)
22
+ return { batch, skipped: new Map() };
23
+ const shapes = await opened.chats.historyShapes(account, [...new Set(batch.messages.map((one) => one.chatId))]);
24
+ return limitBatch(batch, (chatId) => shapes.get(chatId), (chatId) => shapes.get(chatId)?.included === true, historyLimits);
25
+ };
26
+ const noteLimited = (skipped) => {
27
+ if (skipped.size === 0)
28
+ return;
29
+ const counts = {
30
+ chats: skipped.size,
31
+ messages: 0,
32
+ group_too_large: 0,
33
+ older_than_window: 0,
34
+ };
35
+ for (const skip of skipped.values()) {
36
+ counts.messages = (counts.messages ?? 0) + skip.messages;
37
+ counts[skip.reason] = (counts[skip.reason] ?? 0) + 1;
38
+ }
39
+ const warning = { event: "warning", code: "history_limited", operation: "history.save", counts };
40
+ events(warning);
41
+ if (limitedNoted)
42
+ return;
43
+ limitedNoted = true;
44
+ warn(`history limits left out ${counts.messages} pushed messages of ${skipped.size} chats (big groups, older ` +
45
+ `than the window) — \`${command} store status\` lists them; \`${command} messages sync <chat> --full-history\` ` +
46
+ "fetches one and keeps it in full", warning);
47
+ };
17
48
  let recorded = profile === undefined;
18
49
  const notWritten = (operation, error) => {
19
50
  const warning = { event: "warning", code: "store_not_written", operation };
@@ -155,7 +186,13 @@ export const stored = (messenger, { account, store, warn, events, pending, annou
155
186
  // A refused re-key leaves its chat split, never the others or the batch's history unsaved.
156
187
  const saving = previous.then(async () => {
157
188
  await saveRekeys(rekeys, round);
158
- return attempt(operation, (opened) => fill(opened, account, batch));
189
+ return attempt(operation, async (opened) => {
190
+ const { batch: kept, skipped } = await limited(opened, batch);
191
+ await fill(opened, account, kept);
192
+ if (skipped.size > 0)
193
+ await opened.chats.recordHistorySkips(account, skipped);
194
+ noteLimited(skipped);
195
+ });
159
196
  });
160
197
  previous = saving;
161
198
  pending?.add(saving);
@@ -165,12 +202,12 @@ export const stored = (messenger, { account, store, warn, events, pending, annou
165
202
  pending?.delete(saving);
166
203
  rekeysOnly.delete(saving);
167
204
  });
168
- void onBatch(batch);
205
+ void onBatch(batch, saving);
169
206
  return saving;
170
207
  };
171
208
  };
172
209
  // A method not listed here saves nothing and passes through; a lane that should save adds its line.
173
- return throughWrapper(messenger, {
210
+ const wrapped = throughWrapper(messenger, {
174
211
  ...(messenger.forumState
175
212
  ? {
176
213
  forumState: async (chatId) => {
@@ -429,6 +466,58 @@ export const stored = (messenger, { account, store, warn, events, pending, annou
429
466
  return sent;
430
467
  },
431
468
  });
469
+ const chatOf = async (typed) => {
470
+ const named = chatReferenceOf(typed, account.provider);
471
+ if (!named)
472
+ return typed;
473
+ let known;
474
+ try {
475
+ known = account.account;
476
+ }
477
+ catch { }
478
+ await ensureAccount(named, { account: known, ...(profile ? { profile: profile.name } : {}) }, async (other) => {
479
+ const opened = await store();
480
+ return opened && profilesOnAccount(opened, profile?.app, account.provider)(other);
481
+ });
482
+ return named.chat;
483
+ };
484
+ return withChatReferences(wrapped, chatOf);
485
+ };
486
+ /** The adapter methods whose first argument is a chat as typed: an id, a title, or a `chat:` or `msg:` reference. */
487
+ const TYPED_CHAT = [
488
+ "resolve",
489
+ "chat",
490
+ "history",
491
+ "around",
492
+ "historyAfter",
493
+ "historyBefore",
494
+ "historyFrom",
495
+ "topics",
496
+ "topic",
497
+ "topicHistory",
498
+ "download",
499
+ "transcribe",
500
+ "scheduled",
501
+ "admins",
502
+ "chatEvents",
503
+ "leave",
504
+ "group",
505
+ "officialChatStats",
506
+ ];
507
+ /**
508
+ * A reference is read here, not in each adapter: an adapter resolves a typed chat as an id or a title, and would take
509
+ * `chat:max/…` for a title. One of another account is refused before the messenger is asked.
510
+ */
511
+ const withChatReferences = (adapter, chatOf) => {
512
+ const methods = adapter;
513
+ const own = Object.fromEntries(TYPED_CHAT.flatMap((name) => {
514
+ const method = methods[name];
515
+ if (typeof method !== "function")
516
+ return [];
517
+ const call = async (typed, ...rest) => method.call(adapter, await chatOf(typed), ...rest);
518
+ return [[name, call]];
519
+ }));
520
+ return throughWrapper(adapter, own);
432
521
  };
433
522
  /**
434
523
  * A write is saved under the chat the messenger answered, not the one typed: WhatsApp answers a re-keyed person's
@@ -68,7 +68,7 @@ export const topicsCommand = (messenger) => {
68
68
  const done = await context.withServices((services) => services.topics.enable(chat, { upgrade: this.opts().upgrade === true }));
69
69
  renderAnswer(this, context, itemOf(snakeKeys(done)));
70
70
  });
71
- annotate(topics.command("create"), { effects: REMOTE_WRITE })
71
+ annotate(topics.command("create"), { effects: REMOTE_WRITE, literal_after_dashes: true })
72
72
  .description("create a named topic in an existing forum; never enable or upgrade a group implicitly")
73
73
  .argument("<chat>", messenger.chatArgument)
74
74
  .argument("<title>", "the topic title, at most 128 UTF-8 bytes")
@@ -13,6 +13,7 @@ import { recorded, wasSettled } from "../runs/recording.js";
13
13
  import { runContextOf } from "../runs/run-context.js";
14
14
  import { processRun, useAsProcessRun } from "../runs/scope.js";
15
15
  import { AUTHORITY_KEY, BOT_API_KEY, isBotApi, keyForCommand } from "../sends/permissions.js";
16
+ import { liftOptionsAfterDashes } from "./after-dashes.js";
16
17
  import { logParsedArgv, logVersion } from "./argv-log.js";
17
18
  import { commandAvailability } from "./command-availability.js";
18
19
  import { commandPathOf } from "./command-contract.js";
@@ -135,15 +136,16 @@ export const run = async (argv, definition, options = {}) => {
135
136
  useAsProcessRun(outer);
136
137
  }
137
138
  };
139
+ const isMachine = (words) => words.some((word) => word === "--json" || word.startsWith("--json=") || word === "--jsonl");
138
140
  const runOnce = async (argv, definition, options) => {
139
141
  const streams = options.streams ?? processStreams;
140
142
  if (!options.streams)
141
143
  quietBrokenPipes();
142
144
  const delimiter = argv.indexOf("--");
143
145
  const flags = argv.slice(0, delimiter < 0 ? undefined : delimiter);
144
- const machine = flags.some((word) => word === "--json" || word.startsWith("--json=") || word === "--jsonl");
145
- const debug = flags.includes("--debug");
146
- const reporting = machine ? { ...options, tty: false } : options;
146
+ let machine = isMachine(flags);
147
+ let debug = flags.includes("--debug");
148
+ let reporting = machine ? { ...options, tty: false } : options;
147
149
  const control = execution(streams, { maxOutputBytes: 0 });
148
150
  let restoreInput;
149
151
  let signalExit;
@@ -439,12 +441,21 @@ const runOnce = async (argv, definition, options) => {
439
441
  forEachCommand(program, (child) => child.exitOverride());
440
442
  // Before commander, not inside it: commander has no hook that runs before it decides which
441
443
  // subcommand it is looking at.
442
- const { profile, rest: lifted } = liftProfile(argv, commandWords(program), valuedOptions(program));
444
+ const profiled = liftProfile(argv, commandWords(program), valuedOptions(program));
445
+ const { profile } = profiled;
446
+ // An agent's call builds its own argv, with its values after `--` on purpose: none of them may become an option.
447
+ const lifted = options.agent
448
+ ? profiled.rest
449
+ : liftOptionsAfterDashes(profiled.rest, (words) => commandAt(program, commandPath(program, words)));
450
+ const liftedEnd = lifted.indexOf("--");
451
+ const liftedFlags = lifted.slice(0, liftedEnd < 0 ? undefined : liftedEnd);
452
+ machine ||= isMachine(liftedFlags);
453
+ debug ||= liftedFlags.includes("--debug");
454
+ reporting = machine ? { ...options, tty: false } : options;
443
455
  // `wa 'chats list' --help` reads "chats list" as a profile and prints the root help with exit 0.
444
456
  try {
445
457
  // Only before `--`: what follows is a positional, an agent's text among them, never a command word.
446
- const end = lifted.indexOf("--");
447
- for (const words of [flags, end < 0 ? lifted : lifted.slice(0, end)])
458
+ for (const words of [flags, liftedFlags])
448
459
  refuseJoinedPath(program, words.filter((word) => !word.startsWith("-")), profile !== undefined && !/\s/.test(profile) ? [profile] : []);
449
460
  }
450
461
  catch (failure) {
@@ -110,6 +110,7 @@ export const scheduleCommandFor = ({ app, context: contextOf }) => {
110
110
  return { context, system, state, say, timer, environment, save };
111
111
  };
112
112
  withWhen(grantsAuthority(annotate(command.command("command"), {
113
+ literal_after_dashes: true,
113
114
  effects: LOCAL_WRITE,
114
115
  examples: [
115
116
  `${app.command} personal schedule command health --every 1h -- doctor`,
@@ -4,6 +4,7 @@ import { type AISettings } from "../analysis/settings.js";
4
4
  import { type AppIdentity } from "../ports/app.js";
5
5
  import type { RecordInput } from "../runs/run-context.js";
6
6
  import { type Level, type PermissionKey } from "../sends/permissions.js";
7
+ import { type HistoryLimits } from "../services/history-limits.js";
7
8
  import { type ConfigIssue } from "./config-issues.js";
8
9
  import type { CommandPermissions } from "./permission-keys.js";
9
10
  export declare const plain: (rule: string) => (issue: v.BaseIssue<unknown>) => string;
@@ -94,6 +95,8 @@ export interface Settings extends AISettings {
94
95
  color: boolean | undefined;
95
96
  senderColors: boolean;
96
97
  searchCatchUp?: boolean;
98
+ /** How much pushed history the store keeps, by chat kind; a messenger that answers history on request ignores it. */
99
+ historyLimits?: HistoryLimits;
97
100
  /** Search and `images embed` read the pictures themselves, with a model on this machine. Off until set. */
98
101
  pictureSearch?: boolean;
99
102
  /** The reranker that orders `search messages --discover`'s first hits once it is downloaded, or `off`. */
@@ -300,6 +303,9 @@ export declare const settingsFor: <T extends object = Record<never, never>>(app:
300
303
  transcribe_with: v.OptionalSchema<v.PicklistSchema<["auto", "messenger", "local"], (issue: v.BaseIssue<unknown>) => string>, undefined>;
301
304
  speech_model: v.OptionalSchema<v.StringSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
302
305
  search_catch_up: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
306
+ history_dialog_days: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>, v.MaxValueAction<number, 1095, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
307
+ history_group_days: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>, v.MaxValueAction<number, 1095, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
308
+ history_group_members_max: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
303
309
  picture_search: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
304
310
  search_reranker: v.OptionalSchema<v.PicklistSchema<[...string[], "off"], (issue: v.BaseIssue<unknown>) => string>, undefined>;
305
311
  update_check: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
@@ -376,6 +382,9 @@ export declare const settingsFor: <T extends object = Record<never, never>>(app:
376
382
  transcribe_with: v.OptionalSchema<v.PicklistSchema<["auto", "messenger", "local"], (issue: v.BaseIssue<unknown>) => string>, undefined>;
377
383
  speech_model: v.OptionalSchema<v.StringSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
378
384
  search_catch_up: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
385
+ history_dialog_days: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>, v.MaxValueAction<number, 1095, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
386
+ history_group_days: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>, v.MaxValueAction<number, 1095, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
387
+ history_group_members_max: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
379
388
  picture_search: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
380
389
  search_reranker: v.OptionalSchema<v.PicklistSchema<[...string[], "off"], (issue: v.BaseIssue<unknown>) => string>, undefined>;
381
390
  }, (issue: v.StrictObjectIssue) => string>, "has to be an object of profiles, by name">, {}>;
@@ -7,6 +7,7 @@ import { DEFAULT_RERANK_MODEL, RERANK_MODELS } from "../embeddings/models.js";
7
7
  import { envName, readOnlyLockOf } from "../ports/app.js";
8
8
  import { KEEP_SUCCESSFUL_RUNS_FOR_DAYS, RUNS_SIZE_LIMIT_MB } from "../runs/run.js";
9
9
  import { assertOverridesTighten, CEILING_KEY, COMMAND_PATH_KEY, LEVELS, layerPermissions, permissionOverrides, RESOURCE_KEY, RESOURCES, } from "../sends/permissions.js";
10
+ import { DEFAULT_HISTORY_LIMITS, HISTORY_MAX_DAYS } from "../services/history-limits.js";
10
11
  import { storePath } from "../store/path.js";
11
12
  import { sharedConfigPath } from "../store/shared-config.js";
12
13
  import { checkConfigText, issueMessage, readConfigFile, readConfigText, savedPermissionKeys, schemaIssues, settingPathOf, withoutPath, } from "./config-issues.js";
@@ -34,6 +35,8 @@ export const count = v.pipe(v.number(wholeNumber), v.integer(wholeNumber), v.min
34
35
  const zeroOrMore = plain("has to be a whole number, 0 or more");
35
36
  const countOrZero = v.pipe(v.number(zeroOrMore), v.integer(zeroOrMore), v.minValue(0, zeroOrMore));
36
37
  export const flag = v.boolean(plain("has to be true or false"));
38
+ const historyDaysMessage = plain(`has to be a whole number of days, 1 to ${HISTORY_MAX_DAYS}`);
39
+ const historyDays = v.pipe(v.number(historyDaysMessage), v.integer(historyDaysMessage), v.minValue(1, historyDaysMessage), v.maxValue(HISTORY_MAX_DAYS, historyDaysMessage));
37
40
  /** `record_input`: true, false, or "failed" — the words typed kept only in a run that failed. */
38
41
  export const recordInputSetting = v.union([v.boolean(), v.literal("failed")], plain('has to be true, false or "failed"'));
39
42
  const wholeOrZero = (text, name) => {
@@ -71,6 +74,9 @@ const SHARED_PROFILE_ENTRIES = {
71
74
  transcribe_with: v.optional(v.picklist(["auto", "messenger", "local"], plain("has to be auto, messenger or local"))),
72
75
  speech_model: v.optional(v.string(plain("has to be a model id from `models list`, in quotes"))),
73
76
  search_catch_up: v.optional(flag),
77
+ history_dialog_days: v.optional(historyDays),
78
+ history_group_days: v.optional(historyDays),
79
+ history_group_members_max: v.optional(count),
74
80
  picture_search: v.optional(flag),
75
81
  search_reranker: v.optional(v.picklist([...RERANK_MODELS.map((model) => model.id), "off"], plain(`has to be off or a reranker id: ${RERANK_MODELS.map((model) => model.id).join(", ")}`))),
76
82
  ...AI_ENTRIES,
@@ -91,6 +97,9 @@ export const MESSENGER_ONLY_SETTINGS = [
91
97
  "sends_per_hour",
92
98
  "transcribe_with",
93
99
  "search_catch_up",
100
+ "history_dialog_days",
101
+ "history_group_days",
102
+ "history_group_members_max",
94
103
  "read_other_bots",
95
104
  ];
96
105
  /** One program, one version: whether to look for a newer one is not a per-profile matter. */
@@ -255,6 +264,9 @@ export const settingsFor = (app, extension = {}) => {
255
264
  const color = fromLayers("color", undefined);
256
265
  const senderColors = fromLayers("sender_colors", false);
257
266
  const searchCatchUp = fromLayers("search_catch_up", false);
267
+ const historyDialogDays = fromLayers("history_dialog_days", DEFAULT_HISTORY_LIMITS.dialogDays);
268
+ const historyGroupDays = fromLayers("history_group_days", DEFAULT_HISTORY_LIMITS.groupDays);
269
+ const historyGroupMembersMax = fromLayers("history_group_members_max", DEFAULT_HISTORY_LIMITS.groupMembersMax);
258
270
  const pictureSearch = fromLayers("picture_search", false);
259
271
  const searchReranker = fromLayers("search_reranker", DEFAULT_RERANK_MODEL);
260
272
  const recordInput = fromLayers("record_input", true);
@@ -306,6 +318,11 @@ export const settingsFor = (app, extension = {}) => {
306
318
  color: color.value,
307
319
  senderColors: senderColors.value,
308
320
  searchCatchUp: searchCatchUp.value,
321
+ historyLimits: {
322
+ dialogDays: historyDialogDays.value,
323
+ groupDays: historyGroupDays.value,
324
+ groupMembersMax: historyGroupMembersMax.value,
325
+ },
309
326
  pictureSearch: pictureSearch.value,
310
327
  searchReranker: searchReranker.value,
311
328
  limit: limit.value,
@@ -343,6 +360,9 @@ export const settingsFor = (app, extension = {}) => {
343
360
  color: color.from,
344
361
  senderColors: senderColors.from,
345
362
  searchCatchUp: searchCatchUp.from,
363
+ historyDialogDays: historyDialogDays.from,
364
+ historyGroupDays: historyGroupDays.from,
365
+ historyGroupMembersMax: historyGroupMembersMax.from,
346
366
  pictureSearch: pictureSearch.from,
347
367
  searchReranker: searchReranker.from,
348
368
  record: record.from,
@@ -82,6 +82,9 @@ export declare const appDoctorCommand: (app: import("./entry.js").AppIdentity, c
82
82
  transcribe_with: import("valibot").OptionalSchema<import("valibot").PicklistSchema<["auto", "messenger", "local"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
83
83
  speech_model: import("valibot").OptionalSchema<import("valibot").StringSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
84
84
  search_catch_up: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
85
+ history_dialog_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
86
+ history_group_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
87
+ history_group_members_max: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
85
88
  picture_search: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
86
89
  search_reranker: import("valibot").OptionalSchema<import("valibot").PicklistSchema<[...string[], "off"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
87
90
  update_check: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
@@ -158,6 +161,9 @@ export declare const appDoctorCommand: (app: import("./entry.js").AppIdentity, c
158
161
  transcribe_with: import("valibot").OptionalSchema<import("valibot").PicklistSchema<["auto", "messenger", "local"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
159
162
  speech_model: import("valibot").OptionalSchema<import("valibot").StringSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
160
163
  search_catch_up: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
164
+ history_dialog_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
165
+ history_group_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
166
+ history_group_members_max: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
161
167
  picture_search: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
162
168
  search_reranker: import("valibot").OptionalSchema<import("valibot").PicklistSchema<[...string[], "off"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
163
169
  }, (issue: import("valibot").StrictObjectIssue) => string>, "has to be an object of profiles, by name">, {}>;
@@ -256,6 +262,9 @@ export declare const appMcpCommand: (app: import("./entry.js").AppIdentity, conf
256
262
  transcribe_with: import("valibot").OptionalSchema<import("valibot").PicklistSchema<["auto", "messenger", "local"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
257
263
  speech_model: import("valibot").OptionalSchema<import("valibot").StringSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
258
264
  search_catch_up: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
265
+ history_dialog_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
266
+ history_group_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
267
+ history_group_members_max: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
259
268
  picture_search: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
260
269
  search_reranker: import("valibot").OptionalSchema<import("valibot").PicklistSchema<[...string[], "off"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
261
270
  update_check: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
@@ -332,6 +341,9 @@ export declare const appMcpCommand: (app: import("./entry.js").AppIdentity, conf
332
341
  transcribe_with: import("valibot").OptionalSchema<import("valibot").PicklistSchema<["auto", "messenger", "local"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
333
342
  speech_model: import("valibot").OptionalSchema<import("valibot").StringSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
334
343
  search_catch_up: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
344
+ history_dialog_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
345
+ history_group_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
346
+ history_group_members_max: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
335
347
  picture_search: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
336
348
  search_reranker: import("valibot").OptionalSchema<import("valibot").PicklistSchema<[...string[], "off"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
337
349
  }, (issue: import("valibot").StrictObjectIssue) => string>, "has to be an object of profiles, by name">, {}>;
@@ -437,6 +449,9 @@ export declare const configCommand: (app: import("./entry.js").AppIdentity, conf
437
449
  transcribe_with: import("valibot").OptionalSchema<import("valibot").PicklistSchema<["auto", "messenger", "local"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
438
450
  speech_model: import("valibot").OptionalSchema<import("valibot").StringSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
439
451
  search_catch_up: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
452
+ history_dialog_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
453
+ history_group_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
454
+ history_group_members_max: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
440
455
  picture_search: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
441
456
  search_reranker: import("valibot").OptionalSchema<import("valibot").PicklistSchema<[...string[], "off"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
442
457
  update_check: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
@@ -513,6 +528,9 @@ export declare const configCommand: (app: import("./entry.js").AppIdentity, conf
513
528
  transcribe_with: import("valibot").OptionalSchema<import("valibot").PicklistSchema<["auto", "messenger", "local"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
514
529
  speech_model: import("valibot").OptionalSchema<import("valibot").StringSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
515
530
  search_catch_up: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
531
+ history_dialog_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
532
+ history_group_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
533
+ history_group_members_max: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
516
534
  picture_search: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
517
535
  search_reranker: import("valibot").OptionalSchema<import("valibot").PicklistSchema<[...string[], "off"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
518
536
  }, (issue: import("valibot").StrictObjectIssue) => string>, "has to be an object of profiles, by name">, {}>;
@@ -646,6 +664,9 @@ export declare const completeCommand: (messenger: import("./index.js").Messenger
646
664
  transcribe_with: import("valibot").OptionalSchema<import("valibot").PicklistSchema<["auto", "messenger", "local"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
647
665
  speech_model: import("valibot").OptionalSchema<import("valibot").StringSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
648
666
  search_catch_up: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
667
+ history_dialog_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
668
+ history_group_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
669
+ history_group_members_max: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
649
670
  picture_search: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
650
671
  search_reranker: import("valibot").OptionalSchema<import("valibot").PicklistSchema<[...string[], "off"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
651
672
  update_check: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
@@ -722,6 +743,9 @@ export declare const completeCommand: (messenger: import("./index.js").Messenger
722
743
  transcribe_with: import("valibot").OptionalSchema<import("valibot").PicklistSchema<["auto", "messenger", "local"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
723
744
  speech_model: import("valibot").OptionalSchema<import("valibot").StringSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
724
745
  search_catch_up: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
746
+ history_dialog_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
747
+ history_group_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MaxValueAction<number, 1095, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
748
+ history_group_members_max: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
725
749
  picture_search: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
726
750
  search_reranker: import("valibot").OptionalSchema<import("valibot").PicklistSchema<[...string[], "off"], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
727
751
  }, (issue: import("valibot").StrictObjectIssue) => string>, "has to be an object of profiles, by name">, {}>;
package/dist/cli/steps.js CHANGED
@@ -16,6 +16,7 @@ export const STEPS = defineSteps([
16
16
  { name: "all", required: false },
17
17
  { name: "since_time", required: false },
18
18
  { name: "background", required: false },
19
+ { name: "full_history", required: false },
19
20
  ],
20
21
  why: "the local copy is missing messages; syncing brings them in",
21
22
  },
@@ -24,13 +24,16 @@ export type MessageLink = MessagePermalink & {
24
24
  files?: FileLink[];
25
25
  };
26
26
  export declare const fileLinks: (attachments: readonly Attachment[]) => FileLink[];
27
+ /** A chat and a message id, or one reference naming both; a reference's account is for the caller to check. */
27
28
  export declare const messageLinkTarget: (reference: string, message: string | undefined, provider: string) => {
28
29
  chat: string;
29
30
  message: string;
30
31
  account: string;
32
+ text: string;
31
33
  } | {
32
34
  chat: string;
33
35
  message: string;
34
36
  account: undefined;
37
+ text: string;
35
38
  };
36
39
  export declare const validatePermalink: (link: MessagePermalink) => MessagePermalink;
@@ -1,6 +1,6 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { hitFiles } from "./attachments.js";
3
- import { isLocator, parseLocator } from "./locator.js";
3
+ import { chatReferenceOf } from "./ref-arguments.js";
4
4
  export const fileLinks = (attachments) => hitFiles(attachments).map(({ position, kind, name }) => ({
5
5
  position,
6
6
  kind,
@@ -8,28 +8,23 @@ export const fileLinks = (attachments) => hitFiles(attachments).map(({ position,
8
8
  url: null,
9
9
  reason: "unsupported_provider",
10
10
  }));
11
+ /** A chat and a message id, or one reference naming both; a reference's account is for the caller to check. */
11
12
  export const messageLinkTarget = (reference, message, provider) => {
12
- if (isLocator(reference)) {
13
+ const named = chatReferenceOf(reference, provider, "message");
14
+ if (named?.message !== undefined) {
13
15
  if (message !== undefined)
14
16
  throw new CliError("validation_error", "a locator already names the message");
15
- let target;
16
- try {
17
- target = parseLocator(reference);
18
- }
19
- catch {
20
- throw new CliError("validation_error", "expected msg:<provider>/<account>/<chat>/<message>");
21
- }
22
- if (target.provider !== provider)
23
- throw new CliError("validation_error", "that locator belongs to another messenger");
24
- validMessageId(target.message);
25
- return { chat: target.chat, message: target.message, account: target.account };
17
+ validMessageId(named.message);
18
+ return { chat: named.chat, message: named.message, account: named.account, text: named.text };
26
19
  }
27
20
  if (message === undefined)
28
21
  throw new CliError("validation_error", "which message? give its id after the chat");
29
22
  validMessageId(message);
23
+ if (named)
24
+ return { chat: named.chat, message, account: named.account, text: named.text };
30
25
  if (!reference.trim())
31
26
  throw new CliError("validation_error", "which chat? give its id or title");
32
- return { chat: reference, message, account: undefined };
27
+ return { chat: reference, message, account: undefined, text: reference };
33
28
  };
34
29
  const validMessageId = (id) => {
35
30
  if (!id ||
@@ -0,0 +1,11 @@
1
+ import type { TaskCategory } from "./task-categories.js";
2
+ /**
3
+ * Verb classes for reading money in a chat, matched by stem as task categories are. A stem cannot tell the imperative
4
+ * from the past tense (`оплати`, `оплатил` → `оплат`) or from a promise (`pagué`, `pagaré` → `pag`), so a stem that
5
+ * several classes share is listed once, under the request, and a word only counts where its stem is its own. The
6
+ * signal that reads these keeps the sender's side (owner or other) to decide the direction.
7
+ */
8
+ export declare const MONEY_REQUEST = "money-request";
9
+ export declare const MONEY_DONE = "money-done";
10
+ export declare const PROMISE = "promise";
11
+ export declare const MONEY_WORDS: readonly TaskCategory[];
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Verb classes for reading money in a chat, matched by stem as task categories are. A stem cannot tell the imperative
3
+ * from the past tense (`оплати`, `оплатил` → `оплат`) or from a promise (`pagué`, `pagaré` → `pag`), so a stem that
4
+ * several classes share is listed once, under the request, and a word only counts where its stem is its own. The
5
+ * signal that reads these keeps the sender's side (owner or other) to decide the direction.
6
+ */
7
+ export const MONEY_REQUEST = "money-request";
8
+ export const MONEY_DONE = "money-done";
9
+ export const PROMISE = "promise";
10
+ export const MONEY_WORDS = [
11
+ {
12
+ name: MONEY_REQUEST,
13
+ meaning: "asks for a payment or sends an invoice",
14
+ triggers: {
15
+ en: ["pay", "send", "transfer", "invoice", "owe", "refund", "remit"],
16
+ ru: ["переведи", "оплати", "заплати", "вышли", "отправь", "скинь", "верни", "счёт", "выстави", "должен"],
17
+ es: ["envía", "transfiere", "factura", "debes", "devuelve", "abona"],
18
+ },
19
+ },
20
+ {
21
+ name: MONEY_DONE,
22
+ meaning: "says a payment was made or received",
23
+ triggers: {
24
+ en: ["paid", "sent", "received", "settled"],
25
+ ru: ["перевёл", "перевела", "выслал", "вернул", "скинул", "получил", "получила"],
26
+ es: ["recibido"],
27
+ },
28
+ },
29
+ {
30
+ name: PROMISE,
31
+ meaning: "promises to pay or send later",
32
+ triggers: {
33
+ en: ["promise", "promised", "guarantee"],
34
+ ru: ["оплачу", "заплачу", "отправлю", "обещаю", "гарантирую"],
35
+ es: ["prometo", "transferiré", "devolveré"],
36
+ },
37
+ },
38
+ ];
@@ -11,6 +11,8 @@ export interface ProviderEntry {
11
11
  }
12
12
  /** The seed: what cli-core knows before any CLI has written its entry into the store. */
13
13
  export declare const PROVIDERS: readonly ProviderEntry[];
14
+ /** How a text names a provider to a person: `MAX`, not `max`. */
15
+ export declare const providerLabel: (provider: Provider) => string;
14
16
  /**
15
17
  * A notes folder is an `accounts` row of one of these providers; people call every one `notes`. `obsidian` is a vault,
16
18
  * `local` a plain folder (store version 32). `folder` is what they were before, and what Drive's stand-in folders and
@@ -3,11 +3,11 @@ import { CliError } from "@wirecat/cli-base";
3
3
  export const KINDS = ["messages", "mail", "notes", "documents", "meetings", "events"];
4
4
  /** The seed: what cli-core knows before any CLI has written its entry into the store. */
5
5
  export const PROVIDERS = [
6
- { name: "telegram", kind: "messages", aliases: ["tg"] },
6
+ { name: "telegram", kind: "messages", aliases: ["tg"], command: "tg" },
7
7
  { name: "telegram-bot", kind: "messages", aliases: ["tg-bot"] },
8
- { name: "max", kind: "messages", aliases: [] },
8
+ { name: "max", kind: "messages", aliases: [], command: "max" },
9
9
  { name: "max-bot", kind: "messages", aliases: [] },
10
- { name: "whatsapp", kind: "messages", aliases: ["wa"] },
10
+ { name: "whatsapp", kind: "messages", aliases: ["wa"], command: "wa" },
11
11
  { name: "whatsapp-bot", kind: "messages", aliases: ["wa-bot"] },
12
12
  { name: "email", kind: "mail", aliases: [] },
13
13
  { name: "notes", kind: "notes", aliases: [] },
@@ -15,6 +15,18 @@ export const PROVIDERS = [
15
15
  { name: "obsidian", kind: "notes", aliases: [] },
16
16
  { name: "zoom", kind: "meetings", aliases: [] },
17
17
  ];
18
+ const LABELS = {
19
+ telegram: "Telegram",
20
+ "telegram-bot": "Telegram bot",
21
+ max: "MAX",
22
+ "max-bot": "MAX bot",
23
+ whatsapp: "WhatsApp",
24
+ "whatsapp-bot": "WhatsApp bot",
25
+ "google-drive": "Google Drive",
26
+ email: "mail",
27
+ };
28
+ /** How a text names a provider to a person: `MAX`, not `max`. */
29
+ export const providerLabel = (provider) => LABELS[provider] ?? provider;
18
30
  /**
19
31
  * A notes folder is an `accounts` row of one of these providers; people call every one `notes`. `obsidian` is a vault,
20
32
  * `local` a plain folder (store version 32). `folder` is what they were before, and what Drive's stand-in folders and