@wirecat/cli-core 0.399.0 → 0.400.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 (221) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/analysis/consents.d.ts +11 -6
  3. package/dist/analysis/consents.js +5 -2
  4. package/dist/cli/bot/mcp.js +2 -1
  5. package/dist/cli/config-command.js +20 -1
  6. package/dist/cli/context.d.ts +2 -0
  7. package/dist/cli/context.js +1 -1
  8. package/dist/cli/effects.d.ts +4 -1
  9. package/dist/cli/effects.js +10 -2
  10. package/dist/cli/index.d.ts +1 -1
  11. package/dist/cli/index.js +1 -1
  12. package/dist/cli/json-fields.js +12 -1
  13. package/dist/cli/messenger/account-command.js +15 -6
  14. package/dist/cli/messenger/account-profile-query.d.ts +2 -0
  15. package/dist/cli/messenger/account-profile-query.js +13 -0
  16. package/dist/cli/messenger/account-sessions-command.js +1 -0
  17. package/dist/cli/messenger/admin-commands.js +12 -12
  18. package/dist/cli/messenger/admin-contacts-command.js +6 -3
  19. package/dist/cli/messenger/admin-folders-command.js +5 -5
  20. package/dist/cli/messenger/admin-moderation-command.js +4 -4
  21. package/dist/cli/messenger/admin-statistics-command.js +6 -14
  22. package/dist/cli/messenger/analysis-consents-query.d.ts +5 -0
  23. package/dist/cli/messenger/analysis-consents-query.js +20 -0
  24. package/dist/cli/messenger/archive-commands.js +3 -3
  25. package/dist/cli/messenger/attachments-command.js +3 -0
  26. package/dist/cli/messenger/backfill-command.js +5 -4
  27. package/dist/cli/messenger/chat-queries.d.ts +1 -0
  28. package/dist/cli/messenger/chat-queries.js +12 -2
  29. package/dist/cli/messenger/chats-command.js +2 -2
  30. package/dist/cli/messenger/chats-documents-command.js +7 -5
  31. package/dist/cli/messenger/chats-members-command.js +7 -3
  32. package/dist/cli/messenger/chats-read-command.js +1 -1
  33. package/dist/cli/messenger/chats-tracking-command.js +14 -6
  34. package/dist/cli/messenger/contacts-command.js +9 -5
  35. package/dist/cli/messenger/conversations-command.js +20 -16
  36. package/dist/cli/messenger/doctor-command.js +5 -1
  37. package/dist/cli/messenger/document-links-query.d.ts +2 -0
  38. package/dist/cli/messenger/document-links-query.js +9 -0
  39. package/dist/cli/messenger/download-command.js +1 -0
  40. package/dist/cli/messenger/flood-command.d.ts +2 -2
  41. package/dist/cli/messenger/flood-command.js +3 -3
  42. package/dist/cli/messenger/gaps-command.js +2 -2
  43. package/dist/cli/messenger/guard-commands.js +35 -13
  44. package/dist/cli/messenger/images-command.js +1 -0
  45. package/dist/cli/messenger/media-archive-command.js +2 -0
  46. package/dist/cli/messenger/media-archive-move-command.js +1 -0
  47. package/dist/cli/messenger/media-archive-timer-command.js +1 -1
  48. package/dist/cli/messenger/mentions-command.js +15 -2
  49. package/dist/cli/messenger/messages-command.js +2 -0
  50. package/dist/cli/messenger/messages-delete-command.js +1 -0
  51. package/dist/cli/messenger/messages-edit-command.js +1 -0
  52. package/dist/cli/messenger/messages-evidence-command.js +3 -1
  53. package/dist/cli/messenger/messages-forward-command.js +1 -0
  54. package/dist/cli/messenger/messages-pin-command.js +3 -0
  55. package/dist/cli/messenger/messages-send-command.js +1 -0
  56. package/dist/cli/messenger/metadata-command.js +6 -1
  57. package/dist/cli/messenger/models-command.js +10 -2
  58. package/dist/cli/messenger/polls-command.js +4 -0
  59. package/dist/cli/messenger/private-people-command.js +3 -3
  60. package/dist/cli/messenger/rankings-command.js +6 -11
  61. package/dist/cli/messenger/reactions-command.js +2 -0
  62. package/dist/cli/messenger/recipients-query.d.ts +5 -0
  63. package/dist/cli/messenger/recipients-query.js +18 -0
  64. package/dist/cli/messenger/records-command.js +13 -6
  65. package/dist/cli/messenger/search-command.js +3 -1
  66. package/dist/cli/messenger/searches-command.js +5 -1
  67. package/dist/cli/messenger/sends-query.d.ts +5 -0
  68. package/dist/cli/messenger/sends-query.js +33 -0
  69. package/dist/cli/messenger/serve-command.js +1 -0
  70. package/dist/cli/messenger/statistics-query.d.ts +14 -0
  71. package/dist/cli/messenger/statistics-query.js +143 -0
  72. package/dist/cli/messenger/stats-command.js +5 -5
  73. package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
  74. package/dist/cli/messenger/store-maintenance-command.js +10 -10
  75. package/dist/cli/messenger/tags-command.js +7 -1
  76. package/dist/cli/messenger/tasks-command.js +47 -8
  77. package/dist/cli/messenger/transcribe-command.js +1 -0
  78. package/dist/cli/messenger/watch-answers.d.ts +6 -0
  79. package/dist/cli/messenger/watch-answers.js +69 -0
  80. package/dist/cli/messenger/watch-command.d.ts +1 -1
  81. package/dist/cli/messenger/watch-command.js +56 -7
  82. package/dist/cli/program.js +5 -1
  83. package/dist/cli/runs/command.js +25 -38
  84. package/dist/cli/runs/query.d.ts +5 -0
  85. package/dist/cli/runs/query.js +32 -0
  86. package/dist/cli/runs/search-command.d.ts +4 -0
  87. package/dist/cli/runs/search-command.js +27 -0
  88. package/dist/cli/runs/search-query.d.ts +4 -0
  89. package/dist/cli/runs/search-query.js +50 -0
  90. package/dist/cli/runs/search.d.ts +2 -1
  91. package/dist/cli/runs/search.js +6 -6
  92. package/dist/domain/attachments.js +9 -1
  93. package/dist/domain/rankings-options.js +3 -3
  94. package/dist/entities/account-change.d.ts +89 -0
  95. package/dist/entities/account-change.js +20 -0
  96. package/dist/entities/account-profile.d.ts +7 -0
  97. package/dist/entities/account-profile.js +8 -0
  98. package/dist/entities/analysis-consent.d.ts +9 -0
  99. package/dist/entities/analysis-consent.js +4 -0
  100. package/dist/entities/attachment-results.d.ts +96 -0
  101. package/dist/entities/attachment-results.js +31 -0
  102. package/dist/entities/batch-result.d.ts +27 -0
  103. package/dist/entities/batch-result.js +18 -0
  104. package/dist/entities/chat-admin.d.ts +234 -0
  105. package/dist/entities/chat-admin.js +84 -0
  106. package/dist/entities/chat-change.d.ts +25 -0
  107. package/dist/entities/chat-change.js +11 -0
  108. package/dist/entities/chat-metadata.d.ts +34 -0
  109. package/dist/entities/chat-metadata.js +15 -0
  110. package/dist/entities/chat-moderation.d.ts +44 -0
  111. package/dist/entities/chat-moderation.js +37 -0
  112. package/dist/entities/chat-tracking.d.ts +26 -0
  113. package/dist/entities/chat-tracking.js +23 -0
  114. package/dist/entities/contact-activity.d.ts +52 -0
  115. package/dist/entities/contact-activity.js +42 -0
  116. package/dist/entities/contact-context.d.ts +1568 -0
  117. package/dist/entities/contact-context.js +191 -0
  118. package/dist/entities/contact-write.d.ts +22 -0
  119. package/dist/entities/contact-write.js +6 -0
  120. package/dist/entities/conversation-preparation.d.ts +63 -0
  121. package/dist/entities/conversation-preparation.js +23 -0
  122. package/dist/entities/conversation-results.d.ts +567 -0
  123. package/dist/entities/conversation-results.js +94 -0
  124. package/dist/entities/document-link.d.ts +13 -0
  125. package/dist/entities/document-link.js +11 -0
  126. package/dist/entities/extraction-report.d.ts +95 -0
  127. package/dist/entities/extraction-report.js +34 -0
  128. package/dist/entities/image-embedding.d.ts +9 -0
  129. package/dist/entities/image-embedding.js +10 -0
  130. package/dist/entities/index.d.ts +8006 -1928
  131. package/dist/entities/index.js +174 -3
  132. package/dist/entities/listener.d.ts +2150 -0
  133. package/dist/entities/listener.js +46 -0
  134. package/dist/entities/media-archive.d.ts +43 -0
  135. package/dist/entities/media-archive.js +32 -0
  136. package/dist/entities/meeting-task-proposal.d.ts +14 -0
  137. package/dist/entities/meeting-task-proposal.js +19 -0
  138. package/dist/entities/meeting.d.ts +1 -1
  139. package/dist/entities/meeting.js +1 -1
  140. package/dist/entities/mention.d.ts +63 -0
  141. package/dist/entities/mention.js +44 -0
  142. package/dist/entities/message-change.d.ts +19 -0
  143. package/dist/entities/message-change.js +6 -0
  144. package/dist/entities/message-context.d.ts +617 -0
  145. package/dist/entities/message-context.js +20 -0
  146. package/dist/entities/message-read.d.ts +73 -0
  147. package/dist/entities/message-read.js +58 -0
  148. package/dist/entities/message-transfer.d.ts +379 -0
  149. package/dist/entities/message-transfer.js +83 -0
  150. package/dist/entities/message-write.d.ts +191 -0
  151. package/dist/entities/message-write.js +8 -0
  152. package/dist/entities/model-key.d.ts +14 -0
  153. package/dist/entities/model-key.js +10 -0
  154. package/dist/entities/poll.d.ts +40 -0
  155. package/dist/entities/poll.js +16 -0
  156. package/dist/entities/private-contact.d.ts +10 -0
  157. package/dist/entities/private-contact.js +4 -0
  158. package/dist/entities/reaction-change.d.ts +7 -0
  159. package/dist/entities/reaction-change.js +8 -0
  160. package/dist/entities/recipient.d.ts +29 -0
  161. package/dist/entities/recipient.js +11 -0
  162. package/dist/entities/recorded-run.d.ts +103 -0
  163. package/dist/entities/recorded-run.js +44 -0
  164. package/dist/entities/saved-search.d.ts +4 -0
  165. package/dist/entities/saved-search.js +2 -0
  166. package/dist/entities/send-attempt.d.ts +31 -0
  167. package/dist/entities/send-attempt.js +29 -0
  168. package/dist/entities/store-maintenance.d.ts +319 -0
  169. package/dist/entities/store-maintenance.js +145 -0
  170. package/dist/entities/store-records.d.ts +442 -0
  171. package/dist/entities/store-records.js +99 -0
  172. package/dist/entities/tag-change.d.ts +74 -0
  173. package/dist/entities/tag-change.js +31 -0
  174. package/dist/entities/task-pair.d.ts +31 -0
  175. package/dist/entities/task-pair.js +23 -0
  176. package/dist/entities/task-results.d.ts +524 -0
  177. package/dist/entities/task-results.js +142 -0
  178. package/dist/errors/reasons.js +1 -0
  179. package/dist/mcp/agent.d.ts +3 -1
  180. package/dist/mcp/agent.js +9 -6
  181. package/dist/mcp/bot/server.d.ts +2 -0
  182. package/dist/mcp/bot/server.js +1 -0
  183. package/dist/mcp/server.js +3 -1
  184. package/dist/mcp/tools/attachments.js +1 -0
  185. package/dist/meetings/cli/index.js +1 -1
  186. package/dist/meetings/cli/passage.js +1 -0
  187. package/dist/meetings/index.d.ts +2 -1
  188. package/dist/meetings/index.js +1 -0
  189. package/dist/meetings/links.d.ts +16 -0
  190. package/dist/meetings/mcp/index.d.ts +15 -15
  191. package/dist/meetings/read-answers.d.ts +167 -0
  192. package/dist/meetings/read-answers.js +183 -0
  193. package/dist/runs/run.d.ts +10 -2
  194. package/dist/runs/run.js +11 -3
  195. package/dist/sends/journal.d.ts +8 -1
  196. package/dist/sends/journal.js +9 -2
  197. package/dist/sends/permissions.d.ts +5 -1
  198. package/dist/sends/permissions.js +11 -1
  199. package/dist/sends/recipients.d.ts +8 -0
  200. package/dist/sends/recipients.js +9 -0
  201. package/dist/services/admin-statistics.js +3 -3
  202. package/dist/services/chat-documents.d.ts +1 -0
  203. package/dist/services/chat-documents.js +9 -3
  204. package/dist/services/chats.d.ts +1 -1
  205. package/dist/services/chats.js +5 -2
  206. package/dist/services/meeting-links.d.ts +2 -17
  207. package/dist/store/exports.d.ts +1 -1
  208. package/dist/store/exports.js +1 -1
  209. package/dist/store/messages.d.ts +3 -1
  210. package/dist/store/messages.js +2 -2
  211. package/dist/store/roster.d.ts +4 -1
  212. package/dist/store/roster.js +2 -2
  213. package/dist/store/sqlite/chats.d.ts +12 -0
  214. package/dist/store/sqlite/chats.js +1 -0
  215. package/dist/store/sqlite/document-links.d.ts +1 -1
  216. package/dist/store/sqlite/document-links.js +5 -5
  217. package/dist/store/sqlite/involvements.js +2 -1
  218. package/dist/store/sqlite/roster.d.ts +2 -1
  219. package/dist/store/sqlite/roster.js +3 -2
  220. package/package.json +1 -1
  221. package/parity.json +8 -66
@@ -5,6 +5,7 @@ import { REMOTE_READ } from "../effects.js";
5
5
  import { renderAnswer } from "../envelope.js";
6
6
  import { positiveCount } from "../paging.js";
7
7
  import { messengerContext } from "./context.js";
8
+ import { statisticsQuery, withStatisticsQuery } from "./statistics-query.js";
8
9
  import { adminAnswer, refuseJsonl } from "./stats-answers.js";
9
10
  const collect = (value, previous = []) => [...previous, value];
10
11
  export const adminStatisticsCommand = (messenger, report) => {
@@ -27,22 +28,17 @@ export const adminStatisticsCommand = (messenger, report) => {
27
28
  else
28
29
  command
29
30
  .argument("[query...]", "a strict Lucene query; none selects every stored message")
30
- .option("--chat <chat>", `only this chat; ${messenger.chatArgument}`)
31
- .option("--source <accounts>", "every held account of this messenger; personal, bots or all")
32
- .option("--exact", "bare words match exact forms rather than stems");
31
+ .option("--source <accounts>", "every held account of this messenger; personal, bots or all");
33
32
  command.option("--saved <name|id>", "run a saved report of this kind; typed report options replace stored options");
34
33
  command
35
34
  .option("--timezone <zone>", "the IANA timezone for calendar date boundaries")
36
35
  .option("--limit <n>", "report rows, 1–100; 20 if not given", positiveCount("--limit"));
37
36
  if (report !== "discussion")
38
37
  command.option("--answerer <person>", "stored name, alias, @username, ID or person:provider/account/id; ambiguous names require a choice; repeat for more", collect);
39
- if (report === "unanswered")
40
- command.option("--older-than <duration>", "minimum age of a question without an observed qualifying answer");
41
- if (report === "discussion")
42
- command
43
- .option("--min-views <n>", "minimum known cumulative views", Number)
44
- .option("--max-replies <n>", "maximum observed discussion replies", Number);
38
+ if (report !== "newcomers")
39
+ withStatisticsQuery(command, report);
45
40
  command.action(async function (argument) {
41
+ const filters = report === "newcomers" ? {} : statisticsQuery(argument, report);
46
42
  const context = messengerContext(this, messenger);
47
43
  refuseJsonl(context.format);
48
44
  const { answerer, ...options } = this.opts();
@@ -50,11 +46,7 @@ export const adminStatisticsCommand = (messenger, report) => {
50
46
  ...options,
51
47
  limit: options.limit ?? 20,
52
48
  ...(answerer ? { answerers: answerer } : {}),
53
- ...(report === "newcomers"
54
- ? { chat: argument }
55
- : argument.length
56
- ? { text: argument.join(" ") }
57
- : {}),
49
+ ...(report === "newcomers" ? { chat: argument } : filters),
58
50
  signal: environmentOf(this).commandSignal ?? environmentOf(this).signal,
59
51
  }));
60
52
  renderAnswer(this, context, adminAnswer(found));
@@ -0,0 +1,5 @@
1
+ import { type QueryRegistry } from "@wirecat/cli-base/query";
2
+ import type { AnalysisConsentFilter } from "../../analysis/consents.js";
3
+ import { type QueryWords } from "../list-query.js";
4
+ export declare const ANALYSIS_CONSENTS_QUERY: QueryRegistry;
5
+ export declare const analysisConsentFilter: (words: QueryWords) => AnalysisConsentFilter | undefined;
@@ -0,0 +1,20 @@
1
+ import { queryFieldsOf } from "@wirecat/cli-base/query";
2
+ import { analysisConsent } from "../../entities/index.js";
3
+ import { queryTerms } from "../list-query.js";
4
+ export const ANALYSIS_CONSENTS_QUERY = {
5
+ entity: "analysis_consent",
6
+ backend: "native",
7
+ fields: queryFieldsOf(analysisConsent, {
8
+ native: ["chat", "provider"],
9
+ examples: { chat: "chat:7", provider: 'provider:"openai:https://example.test/v1"' },
10
+ }).map((field) => field.native
11
+ ? { ...field, operators: field.operators.filter((operator) => operator === "term" || operator === "phrase") }
12
+ : field),
13
+ };
14
+ export const analysisConsentFilter = (words) => {
15
+ const terms = queryTerms(words, ANALYSIS_CONSENTS_QUERY, "conversations consents list");
16
+ if (terms.size === 0)
17
+ return undefined;
18
+ const chat = terms.get("chat"), provider = terms.get("provider");
19
+ return { ...(chat === undefined ? {} : { chat }), ...(provider === undefined ? {} : { provider }) };
20
+ };
@@ -64,7 +64,7 @@ export const storeCommand = (messenger) => {
64
64
  * What the local store holds, per chat — read from the store alone. Whether a fetch reached a
65
65
  * chat's very first message is not recorded, so it is not claimed: the stretches held are shown.
66
66
  */
67
- const statusCommand = (messenger) => annotate(new Command("status"), { effects: LOCAL_READ })
67
+ const statusCommand = (messenger) => annotate(new Command("status"), { output: { entity: "store_chat_status" }, effects: LOCAL_READ })
68
68
  .summary("what the local archive holds and what is missing")
69
69
  .description("per chat: messages stored, the oldest and newest, and the stretches held completely")
70
70
  .argument("[chat]", messenger.chatArgument)
@@ -93,7 +93,7 @@ const leftLines = (left) => {
93
93
  * store is not offered. The refusal names the chats to a person but keeps them out of its message,
94
94
  * which a scheduled run's `last.json` keeps.
95
95
  */
96
- const clearCommand = (messenger) => annotate(new Command("clear"), { effects: LOCAL_WRITE, mcp: false })
96
+ const clearCommand = (messenger) => annotate(new Command("clear"), { output: { entity: "store_clear" }, effects: LOCAL_WRITE, mcp: false })
97
97
  .description("delete from the store the chats this account has left, with their messages")
98
98
  .option("--left", "the chats this account has left — the only thing this clears")
99
99
  .option("--allow-dangerous", "yes, delete — it cannot be undone, and a chat you left cannot be fetched again")
@@ -132,7 +132,7 @@ const clearCommand = (messenger) => annotate(new Command("clear"), { effects: LO
132
132
  * `--format markdown` for a transcript a person reads. With `--output-dir`, any number of chats into a
133
133
  * folder, and each later run into the same folder adds only what changed.
134
134
  */
135
- const exportCommand = (messenger) => destroysNothing(annotate(new Command("export"), { effects: LOCAL_WRITE }))
135
+ const exportCommand = (messenger) => destroysNothing(annotate(new Command("export"), { output: { entity: "store_export" }, effects: LOCAL_WRITE }))
136
136
  .summary("export stored messages to files")
137
137
  .description("a chat's stored messages, oldest first: --jsonl one message per line, --json the list envelope of messages; never asks the messenger")
138
138
  .argument("[chat...]", `${messenger.chatArgument}; several with --output-dir`)
@@ -36,6 +36,7 @@ const summary = (run) => [
36
36
  .filter(Boolean)
37
37
  .join(", ");
38
38
  const extractCommand = (messenger) => annotate(new Command("extract"), {
39
+ output: { entity: "attachment_extraction" },
39
40
  effects: FETCH,
40
41
  examples: [`${messenger.app.command} personal attachments extract --chat "Book club" --limit 10`],
41
42
  })
@@ -138,6 +139,7 @@ const listCommand = (messenger) => withQuery(withPaging(annotate(new Command("li
138
139
  renderAnswer(this, context, pagedList("attachments list", context.settings, found), () => found.items.length === 0 ? "" : `${found.items.map(row).join("\n")}\n`);
139
140
  });
140
141
  const showCommand = (messenger) => annotate(new Command("show"), {
142
+ output: { entity: "attachment_bytes" },
141
143
  effects: REMOTE_READ,
142
144
  examples: [`${messenger.app.command} personal attachments show "Book club" 101 --attachment 1`],
143
145
  })
@@ -173,6 +175,7 @@ const showCommand = (messenger) => annotate(new Command("show"), {
173
175
  const textCommand = (messenger) => {
174
176
  const text = new Command("text").description("the text of one file, as an agent read it");
175
177
  readsStdin(annotate(text.command("set"), {
178
+ output: { entity: "attachment_text_change" },
176
179
  effects: LOCAL_WRITE,
177
180
  examples: [`${messenger.app.command} personal attachments text set "Book club" 101 --text-file ./transcript.txt`],
178
181
  }), {
@@ -35,6 +35,7 @@ const ALL_CHATS = "--all";
35
35
  export const syncCommand = (messenger) => {
36
36
  const fetching = messenger.fetching ?? FETCHING;
37
37
  return annotate(new Command("sync"), {
38
+ output: { entity: "message_sync" },
38
39
  examples: [`${messenger.app.command} personal messages sync "Book club" --last 100`],
39
40
  effects: FETCH,
40
41
  cooperative: true,
@@ -224,7 +225,7 @@ export const jobsCommand = (messenger) => {
224
225
  if (jobs.length === 0 && context.format === "pretty")
225
226
  context.renderer.note(`no ${query ? "matching " : ""}background sync jobs for this profile`);
226
227
  });
227
- annotate(command.command("show"), { effects: LOCAL_READ })
228
+ annotate(command.command("show"), { output: { entity: "store_job_details" }, effects: LOCAL_READ })
228
229
  .summary("one archive job's status and log")
229
230
  .description("one background job — the newest when none is named — and what the store now holds of its chat")
230
231
  .argument("[job]", "the job id `messages sync --background` printed")
@@ -237,7 +238,7 @@ export const jobsCommand = (messenger) => {
237
238
  : await context.withServices((services) => services.archive.held(chatId)).catch(() => undefined);
238
239
  renderAnswer(this, context, itemOf(snakeKeys({ ...jobDetails(job), ...(held ? { held } : {}), log: job.log })));
239
240
  });
240
- destroysNothing(annotate(command.command("cancel"), { effects: LOCAL_WRITE }))
241
+ destroysNothing(annotate(command.command("cancel"), { output: { entity: "store_job_cancel" }, effects: LOCAL_WRITE }))
241
242
  .summary("cancel a queued or running archive job")
242
243
  .description("stop a running background job after its current page; a later sync resumes where it stopped")
243
244
  .argument("<job>", "the job id")
@@ -251,7 +252,7 @@ export const jobsCommand = (messenger) => {
251
252
  process.kill(job.pid, "SIGTERM");
252
253
  renderAnswer(this, context, itemOf({ job: job.id, cancelled: true }));
253
254
  });
254
- destroysNothing(annotate(command.command("retry"), { effects: LOCAL_WRITE }))
255
+ destroysNothing(annotate(command.command("retry"), { output: { entity: "store_job_retry" }, effects: LOCAL_WRITE }))
255
256
  .summary("retry an archive job")
256
257
  .description("start a partial, failed or died job again, as a new job; the sync resumes where the store stopped")
257
258
  .argument("[job]", "the job id")
@@ -292,7 +293,7 @@ export const jobsCommand = (messenger) => {
292
293
  if (items.length === 0 && context.format === "pretty")
293
294
  context.renderer.note("no partial, failed or died jobs to retry");
294
295
  });
295
- destroysNothing(annotate(command.command("clear"), { effects: LOCAL_WRITE }))
296
+ destroysNothing(annotate(command.command("clear"), { output: { entity: "store_job_clear" }, effects: LOCAL_WRITE }))
296
297
  .description("forget finished jobs and remove their logs; a running job is kept")
297
298
  .action(function () {
298
299
  const context = messengerContext(this, messenger);
@@ -4,6 +4,7 @@ import { type QueryRegistry } from "@wirecat/cli-base/query";
4
4
  * there; `--offline`, or a messenger whose history is the store, answers them.
5
5
  */
6
6
  export declare const CHATS_QUERY: QueryRegistry;
7
+ export declare const TRACKING_QUERY: QueryRegistry;
7
8
  /** The messenger reads a chat's events back to a time; it cannot pick them by type. */
8
9
  export declare const EVENTS_QUERY: QueryRegistry;
9
10
  /** The store's member history is read back to a time. */
@@ -1,8 +1,8 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { queryError, queryFieldsOf } from "@wirecat/cli-base/query";
3
- import { chat, chatEvent, inviteLink, joinRequest, member } from "../../entities/index.js";
3
+ import { chat, chatEvent, ENTITIES, inviteLink, joinRequest, member } from "../../entities/index.js";
4
4
  import { AUDIT_MIN_SCORE } from "../../services/members-audit.js";
5
- import { CHAT_COLUMNS, titlePart } from "../../store/store.js";
5
+ import { CHAT_COLUMNS, TRACKED_CHAT_COLUMNS, titlePart } from "../../store/store.js";
6
6
  import { unfilteredQuery } from "../list-query.js";
7
7
  const titleWords = (node) => {
8
8
  const part = titlePart(node.value);
@@ -25,6 +25,16 @@ export const CHATS_QUERY = {
25
25
  validate: { title: titleWords },
26
26
  }),
27
27
  };
28
+ export const TRACKING_QUERY = {
29
+ entity: "tracked_chat",
30
+ backend: "sql",
31
+ fields: queryFieldsOf(ENTITIES.tracked_chat, {
32
+ exclude: Object.keys(ENTITIES.tracked_chat.entries).filter((name) => !(name in TRACKED_CHAT_COLUMNS)),
33
+ text: ["title"],
34
+ examples: { chat_id: "chat_id:7", title: "title:book" },
35
+ validate: { title: titleWords },
36
+ }),
37
+ };
28
38
  /** The messenger reads a chat's events back to a time; it cannot pick them by type. */
29
39
  export const EVENTS_QUERY = {
30
40
  entity: "chat_event",
@@ -48,7 +48,7 @@ export const chatsCommand = (messenger) => {
48
48
  if (body.meta?.truncated)
49
49
  context.renderer.note("more history than one run reads; a later time bound reads less");
50
50
  }));
51
- annotate(chats.command("inspect"), { effects: REMOTE_READ, offline: false })
51
+ annotate(chats.command("inspect"), { output: { entity: "chat_inspection" }, effects: REMOTE_READ, offline: false })
52
52
  .description("what an invite or public link leads to, without joining it")
53
53
  .argument("<link>", "an invite link or a public one")
54
54
  .action(async function (link) {
@@ -69,7 +69,7 @@ export const chatsCommand = (messenger) => {
69
69
  context.renderer.note(`you left this chat; its stored messages are kept — \`${typedCommand(messenger.app.command, "store clear --left --dry-run", context.settings.profile)}\` lists the chats you left and deletes nothing`);
70
70
  }));
71
71
  if (messenger.sendAs !== false)
72
- annotate(chats.command("send-as"), { effects: REMOTE_READ, offline: false })
72
+ annotate(chats.command("send-as"), { output: { entity: "chat_sender" }, effects: REMOTE_READ, offline: false })
73
73
  .description("who this account may post as in a chat; changes no saved choice")
74
74
  .argument("<chat>", messenger.chatArgument)
75
75
  .action(async function (chat) {
@@ -2,26 +2,28 @@ import { annotate } from "@wirecat/cli-base/commands";
2
2
  import { Command } from "commander";
3
3
  import { REMOTE_READ } from "../effects.js";
4
4
  import { listOf, renderAnswer } from "../envelope.js";
5
+ import { queryTerms, typedQuery, withQuery } from "../list-query.js";
5
6
  import { positiveCount, window } from "../paging.js";
6
7
  import { nextStep } from "../steps.js";
7
8
  import { documentLinkAnswer, lines, pageOf } from "./chat-answers.js";
8
9
  import { messengerContext } from "./context.js";
10
+ import { DOCUMENT_LINKS_QUERY } from "./document-links-query.js";
9
11
  import { syncsFirst } from "./search-sync-options.js";
10
12
  /** `chats documents …`: the Google documents linked in a chat's stored messages. */
11
13
  export const documentsCommand = (messenger) => {
12
14
  const documents = new Command("documents").description("the Google Docs and Drive documents linked in a chat's stored messages");
13
- documents.addCommand(annotate(new Command("list"), { effects: REMOTE_READ })
15
+ documents.addCommand(withQuery(annotate(new Command("list"), { effects: REMOTE_READ })
14
16
  .description("every document link in the chat, newest first, and whether the store has it")
15
17
  .argument("<chat>", messenger.chatArgument)
16
18
  .option("--limit <n>", "how many to show", positiveCount("--limit"))
17
- .option("--page <n>", "which page, starting at 1", positiveCount("--page"))
18
- .action(async function (chat) {
19
+ .option("--page <n>", "which page, starting at 1", positiveCount("--page")), DOCUMENT_LINKS_QUERY).action(async function (chat, words) {
20
+ const ref = queryTerms(words, DOCUMENT_LINKS_QUERY, "chats documents list").get("ref");
19
21
  const context = messengerContext(this, messenger);
20
- const found = await context.withServices((services) => services.chatDocuments.list(chat, window(context.settings)));
22
+ const found = await context.withServices((services) => services.chatDocuments.list(chat, { ...window(context.settings), ...(ref === undefined ? {} : { ref }) }));
21
23
  renderAnswer(this, context, listOf("chats documents list", {
22
24
  items: found.items.map(documentLinkAnswer),
23
25
  ...pageOf(context.settings, found),
24
- args: { chat },
26
+ args: { chat, ...(words.length ? { query: typedQuery(words) } : {}) },
25
27
  ...(found.empty && syncsFirst(messenger)
26
28
  ? { next: [nextStep("messages.sync", { args: { chat: found.chatId } })] }
27
29
  : {}),
@@ -71,7 +71,11 @@ export const membersCommand = (messenger) => {
71
71
  before ? [before.name, before.username ? `@${before.username}` : null].filter(Boolean).join(" ") : "",
72
72
  ].join(" ")));
73
73
  }));
74
- const fetch = annotate(new Command("fetch"), { effects: FETCH, offline: false })
74
+ const fetch = annotate(new Command("fetch"), {
75
+ output: { entity: "chat_members_fetch" },
76
+ effects: FETCH,
77
+ offline: false,
78
+ })
75
79
  .summary("save the group's current membership")
76
80
  .description("read a group's whole member list into the local store's member history: who joined, who left, daily " +
77
81
  "counts and profile changes; someone is recorded as gone only when every member was read")
@@ -96,7 +100,7 @@ export const membersCommand = (messenger) => {
96
100
  renderAnswer(this, context, itemOf(snakeKeys(fetched)), () => `${fetched.read} members read; ${fetched.joined.length} new, ${fetched.gone.length} gone, ` +
97
101
  `${fetched.changed.length} changed their profile${fetched.tracked ? "; tracked daily" : ""}\n`);
98
102
  }));
99
- const add = annotate(new Command("add"), { effects: REMOTE_WRITE })
103
+ const add = annotate(new Command("add"), { output: { entity: "chat_members_change" }, effects: REMOTE_WRITE })
100
104
  .description("add people; they are told")
101
105
  .argument("<chat>", messenger.chatArgument)
102
106
  .argument("<person...>", "an id, or part of a name");
@@ -109,7 +113,7 @@ export const membersCommand = (messenger) => {
109
113
  const added = await context.withServices((services) => services.admin.addMembers(chat, people, history ? { history } : {}));
110
114
  renderAnswer(this, context, itemOf(snakeKeys(added)));
111
115
  }));
112
- members.addCommand(annotate(new Command("remove"), { effects: REMOTE_WRITE })
116
+ members.addCommand(annotate(new Command("remove"), { output: { entity: "chat_members_change" }, effects: REMOTE_WRITE })
113
117
  .description("remove people; their messages stay")
114
118
  .argument("<chat>", messenger.chatArgument)
115
119
  .argument("<person...>", "an id, or part of a name")
@@ -4,7 +4,7 @@ import { REMOTE_WRITE } from "../effects.js";
4
4
  import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
5
5
  import { messengerContext } from "./context.js";
6
6
  export const markReadCommand = (messenger) => {
7
- const read = annotate(new Command("mark-read"), { effects: REMOTE_WRITE })
7
+ const read = annotate(new Command("mark-read"), { output: { entity: "chat_read" }, effects: REMOTE_WRITE })
8
8
  .description("mark a chat read; the other side sees that you read it")
9
9
  .argument("<chat>", messenger.chatArgument)
10
10
  .option("--until-id <id>", "only up to this message id; the newest by default");
@@ -3,19 +3,27 @@ import { Command } from "commander";
3
3
  import { TRACKED_DAYS } from "../../services/chats.js";
4
4
  import { LOCAL_READ, LOCAL_WRITE } from "../effects.js";
5
5
  import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
6
+ import { listQuery, typedQuery, withQuery } from "../list-query.js";
6
7
  import { lines, wholeList } from "./chat-answers.js";
8
+ import { TRACKING_QUERY } from "./chat-queries.js";
7
9
  import { messengerContext, refuseLocalWrite } from "./context.js";
8
10
  /** The chats whose member lists `serve` fetches daily; kept in the local store, never sent. */
9
11
  export const trackingCommand = (messenger) => {
10
12
  const tracking = new Command("tracking")
11
13
  .summary("chats whose member lists are fetched daily")
12
14
  .description("the chats whose member lists serve fetches daily into the local store — chats members fetch --track adds one");
13
- annotate(tracking.command("list"), { effects: LOCAL_READ })
15
+ withQuery(annotate(tracking.command("list"), { effects: LOCAL_READ }), TRACKING_QUERY)
14
16
  .description("every tracked chat: since when, and its last member count")
15
- .action(async function () {
17
+ .action(async function (words) {
18
+ const query = listQuery(typedQuery(words), TRACKING_QUERY, "chats tracking list");
16
19
  const context = messengerContext(this, messenger);
17
- const found = await context.withServices((services) => services.chats.tracked());
18
- renderAnswer(this, context, wholeList("chats tracking list", found.map((one) => snakeKeys(one))), lines(({ chat_id, title, tracked_at, last_count }) => [
20
+ const found = await context.withServices((services) => services.chats.tracked(query));
21
+ renderAnswer(this, context, wholeList("chats tracking list", found.map(({ chatId, title, trackedAt, lastCount }) => ({
22
+ chat_id: chatId,
23
+ title,
24
+ tracked_at: trackedAt,
25
+ last_count: lastCount,
26
+ }))), lines(({ chat_id, title, tracked_at, last_count }) => [
19
27
  title ? `${title} (${chat_id})` : chat_id,
20
28
  tracked_at.slice(0, 10),
21
29
  last_count
@@ -23,7 +31,7 @@ export const trackingCommand = (messenger) => {
23
31
  : "",
24
32
  ].join(" ")));
25
33
  });
26
- annotate(tracking.command("show"), { effects: LOCAL_READ })
34
+ annotate(tracking.command("show"), { effects: LOCAL_READ, output: { entity: "tracked_chat_history" } })
27
35
  .summary("one chat's member tracking and daily counts")
28
36
  .description(`one chat: whether it is tracked, and its member count per day for the last ${TRACKED_DAYS} days`)
29
37
  .argument("<chat>", messenger.chatArgument)
@@ -35,7 +43,7 @@ export const trackingCommand = (messenger) => {
35
43
  ["add", true, "fetch this chat's member list daily while serve runs, from its next run"],
36
44
  ["remove", false, "stop fetching it daily; the history already kept stays"],
37
45
  ]) {
38
- annotate(tracking.command(verb), { effects: LOCAL_WRITE })
46
+ annotate(tracking.command(verb), { effects: LOCAL_WRITE, output: { entity: "chat_tracking" } })
39
47
  .description(description)
40
48
  .argument("<chat>", messenger.chatArgument)
41
49
  .action(async function (chat) {
@@ -87,7 +87,10 @@ export const contactsCommand = (messenger) => {
87
87
  : [],
88
88
  }));
89
89
  });
90
- const contextCommand = annotate(contacts.command("context"), { effects: REMOTE_READ })
90
+ const contextCommand = annotate(contacts.command("context"), {
91
+ effects: REMOTE_READ,
92
+ output: { entity: "contact_context" },
93
+ })
91
94
  .summary("what the local archive knows about a person")
92
95
  .description("what the store holds about one person, in every messenger linked to them: shared chats, the last " +
93
96
  "messages each way, their recent messages, where others mentioned them — never connects")
@@ -142,6 +145,7 @@ export const contactsCommand = (messenger) => {
142
145
  });
143
146
  annotate(contacts.command("prepare"), {
144
147
  effects: REMOTE_READ,
148
+ output: { entity: "contact_preparation" },
145
149
  examples: [`${messenger.app.command} contacts prepare Ana`],
146
150
  })
147
151
  .summary("what to know before you talk to someone, each item with its reference")
@@ -169,7 +173,7 @@ export const contactsCommand = (messenger) => {
169
173
  meta: { ...window.meta, ...(hasMore ? { truncated: true } : {}) },
170
174
  }));
171
175
  });
172
- annotate(contacts.command("timeline"), { effects: REMOTE_READ })
176
+ annotate(contacts.command("timeline"), { effects: REMOTE_READ, output: { entity: "contact_activity" } })
173
177
  .summary("one person's activity across their linked identities")
174
178
  .description("everything one person took part in, in every messenger linked to them — messages they wrote or were " +
175
179
  "mentioned in, chats, mail, meetings, tasks — newest first, from the store; never connects")
@@ -211,7 +215,7 @@ export const contactsCommand = (messenger) => {
211
215
  meta: { ...window.meta, ...(pending > 0 ? { truncated: true } : {}) },
212
216
  }));
213
217
  });
214
- annotate(contacts.command("check"), { effects: REMOTE_READ })
218
+ annotate(contacts.command("check"), { effects: REMOTE_READ, output: { entity: "contact_check" } })
215
219
  .summary("check a person's profile and messages for signs of abuse")
216
220
  .description(registriesCover(messenger.provider)
217
221
  ? "whether one person looks like a bot, a fake or a spammer: their profile, what they wrote in the store, " +
@@ -239,7 +243,7 @@ export const contactsCommand = (messenger) => {
239
243
  const { notes: _, ...answer } = checked;
240
244
  renderAnswer(this, context, itemOf(snakeKeys(answer)));
241
245
  });
242
- annotate(contacts.command("link"), { effects: LOCAL_WRITE })
246
+ annotate(contacts.command("link"), { effects: LOCAL_WRITE, output: { entity: "linked_person" } })
243
247
  .summary("link identities that belong to one person")
244
248
  .description("record that two people in the store are one person — the same name is never enough")
245
249
  .argument("<person>", "their id, @username, or part of their name")
@@ -250,7 +254,7 @@ export const contactsCommand = (messenger) => {
250
254
  renderAnswer(this, context, itemOf(snakeKeys(linked)));
251
255
  context.renderer.success(`${linked.identities.length} identities are now one person`);
252
256
  });
253
- annotate(contacts.command("unlink"), { effects: LOCAL_WRITE })
257
+ annotate(contacts.command("unlink"), { effects: LOCAL_WRITE, output: { entity: "linked_person" } })
254
258
  .description("undo contacts link for one identity: it is a person of its own again")
255
259
  .argument("<person>", "their id, @username, or part of their name; <messenger>:<person> for another messenger")
256
260
  .action(async function (person) {
@@ -18,6 +18,7 @@ import { listQuery, pagedList, pageOf, typedQuery, withQuery } from "../list-que
18
18
  import { positiveCount } from "../paging.js";
19
19
  import { readsStdin } from "../stdin-field.js";
20
20
  import { nextStep, rootOf, runnable, stepLine } from "../steps.js";
21
+ import { ANALYSIS_CONSENTS_QUERY, analysisConsentFilter } from "./analysis-consents-query.js";
21
22
  import { answerOf as askOwner } from "./ask.js";
22
23
  import { messengerContext, refuseLocalWrite } from "./context.js";
23
24
  import { conversationShown, foundConversations, readinessList, refreshedAnswer } from "./conversation-answers.js";
@@ -33,7 +34,7 @@ export const conversationsCommand = (messenger) => {
33
34
  const conversations = new Command("conversations")
34
35
  .summary("group stored messages into conversations")
35
36
  .description("the conversations inside a chat, found in the stored messages by replies, mentions and who wrote next");
36
- boundOptions(reachesOtherService(annotate(conversations.command("build"), { effects: REMOTE_WRITE }))
37
+ boundOptions(reachesOtherService(annotate(conversations.command("build"), { output: { entity: "conversation_build" }, effects: REMOTE_WRITE }))
37
38
  .summary("build conversation links from stored messages")
38
39
  .description("find a chat's conversations in what the store holds, replacing the last build; without --chat, every " +
39
40
  "chat that changed since its build and every group never built; never asks the messenger")
@@ -130,7 +131,7 @@ export const conversationsCommand = (messenger) => {
130
131
  if (found.items.length === 0 && context.format === "pretty")
131
132
  context.renderer.note("no conversations match");
132
133
  });
133
- annotate(conversations.command("show"), { effects: LOCAL_READ })
134
+ annotate(conversations.command("show"), { output: { entity: "conversation", view: "details" }, effects: LOCAL_READ })
134
135
  .summary("one conversation and its messages")
135
136
  .description("one conversation's messages, oldest first — by its id, or the one a message is in")
136
137
  .argument("<conversation>", `a conversation id from \`conversations list\`; or ${messenger.chatArgument}, with a message`)
@@ -159,7 +160,7 @@ export const conversationsCommand = (messenger) => {
159
160
  else
160
161
  renderAnswer(this, context, conversationShown(account, { summary, messages }));
161
162
  });
162
- withModelOptions(annotate(conversations.command("related"), { effects: LOCAL_READ })
163
+ withModelOptions(annotate(conversations.command("related"), { output: { entity: "conversation_hit" }, effects: LOCAL_READ })
163
164
  .summary("conversations nearest in meaning to a message's conversation")
164
165
  .description("the conversations nearest in meaning to the one a message is in, in every built chat, best first — " +
165
166
  "from the vectors `conversations embed` stored; runs no model")
@@ -175,7 +176,7 @@ export const conversationsCommand = (messenger) => {
175
176
  if (found.hits.length === 0 && context.format === "pretty")
176
177
  context.renderer.note(`no other conversation has vectors of ${found.model}`);
177
178
  });
178
- withModelOptions(annotate(conversations.command("status"), { effects: LOCAL_READ })
179
+ withModelOptions(annotate(conversations.command("status"), { output: { entity: "conversation_readiness" }, effects: LOCAL_READ })
179
180
  .summary("which chats are ready for conversation search")
180
181
  .description("how fresh each built chat's conversations and vectors are: messages the build has not seen, chunks " +
181
182
  "with a current, stale or missing vector")
@@ -193,7 +194,7 @@ export const conversationsCommand = (messenger) => {
193
194
  .summary("batches waiting for an agent's conversation links")
194
195
  .description("windows of a chat for your own AI agent to link: which earlier message each one answers");
195
196
  const sizeOption = (command) => command.option("--size <n>", `messages to answer per batch, ${BATCH_SIZE.min}–${BATCH_SIZE.max}; ${BATCH_SIZE.default} by default`, positiveCount("--size"));
196
- sizeOption(annotate(batches.command("status"), { effects: LOCAL_READ })
197
+ sizeOption(annotate(batches.command("status"), { output: { entity: "conversation_batch_status" }, effects: LOCAL_READ })
197
198
  .summary("progress of conversation-link batches")
198
199
  .description("how many messages still wait for an answer, in how many batches, and how much text")
199
200
  .requiredOption("--chat <chat>", messenger.chatArgument)).action(async function () {
@@ -203,7 +204,7 @@ export const conversationsCommand = (messenger) => {
203
204
  renderAnswer(this, context, itemOf(snakeKeys(status)), () => `${status.messages} messages to answer, about ${status.batches} batches, ` +
204
205
  `${status.characters} characters (about ${status.tokensEstimate} tokens)\n`);
205
206
  });
206
- sizeOption(annotate(batches.command("next"), { effects: LOCAL_READ })
207
+ sizeOption(annotate(batches.command("next"), { output: { entity: "conversation_batch" }, effects: LOCAL_READ })
207
208
  .summary("get the next batch of messages to link")
208
209
  .description("the next window to answer, with the messages before it; message text goes to stdout only")
209
210
  .requiredOption("--chat <chat>", messenger.chatArgument)).action(async function () {
@@ -230,7 +231,7 @@ export const conversationsCommand = (messenger) => {
230
231
  const links = conversations
231
232
  .command("links")
232
233
  .description("your agent's answers: which earlier message each message of a batch answers");
233
- readsStdin(annotate(links.command("add"), { effects: LOCAL_WRITE }), {
234
+ readsStdin(annotate(links.command("add"), { output: { entity: "conversation_change" }, effects: LOCAL_WRITE }), {
234
235
  name: "answer",
235
236
  description: '{ "model", "skill"?, "answers": [{ "message", "parent", "confidence" }] }',
236
237
  json: true,
@@ -247,7 +248,7 @@ export const conversationsCommand = (messenger) => {
247
248
  const stored = await context.withServices((services) => services.conversations.addAnswers(batch, answer));
248
249
  renderAnswer(this, context, itemOf(stored, { next: [nextStep("conversations.build", { args: { chat: stored.chat } })] }), () => `${stored.stored} answers stored\n`);
249
250
  });
250
- annotate(links.command("clear"), { effects: LOCAL_WRITE })
251
+ annotate(links.command("clear"), { output: { entity: "conversation_change" }, effects: LOCAL_WRITE })
251
252
  .summary("remove an agent's saved conversation-link answers")
252
253
  .description("drop your agent's answers for a chat, or only one model's; messages are never touched")
253
254
  .requiredOption("--chat <chat>", messenger.chatArgument)
@@ -260,7 +261,7 @@ export const conversationsCommand = (messenger) => {
260
261
  const cleared = await context.withServices((services) => services.conversations.clearAnswers(chat, answerModel ?? model));
261
262
  renderAnswer(this, context, itemOf(cleared), () => `${cleared.cleared} answers dropped\n`);
262
263
  });
263
- const embed = withModelOptions(reachesOtherService(annotate(new Command("embed"), { effects: REMOTE_WRITE }))
264
+ const embed = withModelOptions(reachesOtherService(annotate(new Command("embed"), { output: { entity: "conversation_embedding" }, effects: REMOTE_WRITE }))
264
265
  .summary("prepare conversation vectors for meaning search")
265
266
  .description("compute a vector for each chunk of a chat's conversations for search by meaning — on this machine, or " +
266
267
  "with --embedding-provider through a service and your key; resumes where it stopped; without --chat, every " +
@@ -307,7 +308,7 @@ export const conversationsCommand = (messenger) => {
307
308
  next: done.skipped ? [nextStep("conversations.build", { args: { chat } })] : [],
308
309
  }), () => `${done.embedded} chunks embedded with ${done.model}${done.skipped ? `, ${done.skipped} changed since the build` : ""}\n`);
309
310
  });
310
- withModelOptions(annotate(embed.command("status"), { effects: LOCAL_READ })
311
+ withModelOptions(annotate(embed.command("status"), { output: { entity: "conversation_embedding_status" }, effects: LOCAL_READ })
311
312
  .summary("which conversation vectors are ready or stale")
312
313
  .description("how many chunks of a chat have a vector of the model, how many are left, and what is left costs")
313
314
  .option("--chat <chat>", messenger.chatArgument)).action(async function () {
@@ -321,7 +322,7 @@ export const conversationsCommand = (messenger) => {
321
322
  (status.priceAtMost ? `, at most ${dollars(status.priceAtMost.usd)}` : "") +
322
323
  "\n");
323
324
  });
324
- withModelOptions(destroysNothing(annotate(embed.command("clear"), { effects: LOCAL_WRITE }))
325
+ withModelOptions(destroysNothing(annotate(embed.command("clear"), { output: { entity: "conversation_change" }, effects: LOCAL_WRITE }))
325
326
  .summary("remove stored conversation vectors")
326
327
  .description("drop a chat's vectors, or only one model's; messages and conversations are never touched")
327
328
  .option("--chat <chat>", messenger.chatArgument)).action(async function () {
@@ -338,6 +339,7 @@ export const conversationsCommand = (messenger) => {
338
339
  renderAnswer(this, context, itemOf(cleared), () => `${cleared.cleared} vectors dropped\n`);
339
340
  });
340
341
  boundOptions(destroysNothing(annotate(conversations.command("rebuild"), {
342
+ output: { entity: "conversation_preparation" },
341
343
  effects: LOCAL_WRITE,
342
344
  examples: [`${messenger.app.command} conversations rebuild --max-chats 5`],
343
345
  }))
@@ -359,18 +361,19 @@ export const conversationsCommand = (messenger) => {
359
361
  const consents = conversations
360
362
  .command("consents")
361
363
  .description("remembered analysis permissions for this account's chats and provider endpoints");
362
- annotate(consents.command("list"), { effects: LOCAL_READ })
364
+ withQuery(annotate(consents.command("list"), { effects: LOCAL_READ }), ANALYSIS_CONSENTS_QUERY)
363
365
  .description("which chats and provider endpoints you allowed to analyse, and when")
364
- .action(async function () {
366
+ .action(async function (words) {
367
+ const filter = analysisConsentFilter(words);
365
368
  const context = messengerContext(this, messenger);
366
- const items = await context.withStore((store, account) => analysisConsents(store, account).list());
369
+ const items = await context.withStore((store, account) => analysisConsents(store, account).list(filter));
367
370
  renderAnswer(this, context, listOf("conversations consents list", {
368
371
  items: items.map(snakeKeys),
369
372
  limit: Math.max(1, items.length),
370
373
  has_more: false,
371
374
  }));
372
375
  });
373
- annotate(consents.command("revoke"), { effects: LOCAL_WRITE })
376
+ annotate(consents.command("revoke"), { effects: LOCAL_WRITE, output: { entity: "analysis_consent_revocation" } })
374
377
  .description("forget analysis permissions so the next analysis asks again")
375
378
  .option("--chat <chat>", "revoke only this chat's consents; defaults to every chat")
376
379
  .addOption(new Option("--consent-provider <identity>", "exact provider identity from consents list; defaults to every provider").conflicts("provider"))
@@ -505,6 +508,7 @@ const answerOf = (text) => {
505
508
  };
506
509
  /** `messages links`: why a message sits where it does in its conversation. */
507
510
  export const linksCommand = (messenger) => annotate(new Command("links"), {
511
+ output: { entity: "message_links" },
508
512
  examples: [`${messenger.app.command} personal messages links "Book club" 101`],
509
513
  effects: LOCAL_READ,
510
514
  })
@@ -540,7 +544,7 @@ const readinessLine = ({ chat, state, graph, pending, vectors }, model) => graph
540
544
  `${pending.deleted} deleted · ${model}: ${vectors.current} current, ${vectors.stale} stale, ` +
541
545
  `${vectors.missing} missing of ${vectors.chunks} chunks`;
542
546
  /** `search conversations`: the conversations nearest to a query, in meaning and in words. */
543
- export const conversationsSearchCommand = (messenger) => syncOptions(boundOptions(withModelOptions(reachesOtherService(annotate(new Command("conversations"), { effects: REMOTE_READ })))), messenger)
547
+ export const conversationsSearchCommand = (messenger) => syncOptions(boundOptions(withModelOptions(reachesOtherService(annotate(new Command("conversations"), { output: { entity: "conversation_hit" }, effects: REMOTE_READ })))), messenger)
544
548
  .summary("find conversations by words and meaning")
545
549
  .description("the conversations nearest to a query in meaning and in words, best first, in one chat or every one — " +
546
550
  "meaning after `conversations embed`; runs on this machine")
@@ -74,7 +74,11 @@ const reportCommand = (messenger) => {
74
74
  `About a given run: ${create} --run <id> (ids: ${lineOf(this, "runs list")})`,
75
75
  ].join("\n"));
76
76
  });
77
- destroysNothing(annotate(report.command("create"), { effects: LOCAL_WRITE, mcp: false }))
77
+ destroysNothing(annotate(report.command("create"), {
78
+ effects: LOCAL_WRITE,
79
+ mcp: false,
80
+ examples: [`${app.command} personal doctor report create --output ./problem-report.json`],
81
+ }))
78
82
  .description("write a problem report to a file, and say where to send it")
79
83
  .option("--run <id>", "the run the report is about; the newest failed one if not given")
80
84
  .option("--output <file>", "where to write it; a new file in this directory if not given")
@@ -0,0 +1,2 @@
1
+ import { type QueryRegistry } from "@wirecat/cli-base/query";
2
+ export declare const DOCUMENT_LINKS_QUERY: QueryRegistry;
@@ -0,0 +1,9 @@
1
+ import { queryFieldsOf } from "@wirecat/cli-base/query";
2
+ import { documentLink } from "../../entities/index.js";
3
+ export const DOCUMENT_LINKS_QUERY = {
4
+ entity: "document_link",
5
+ backend: "native",
6
+ fields: queryFieldsOf(documentLink, { native: ["ref"], examples: { ref: 'ref:"msg:chat/500/7/1"' } }).map((field) => field.native
7
+ ? { ...field, operators: field.operators.filter((operator) => operator === "term" || operator === "phrase") }
8
+ : field),
9
+ };
@@ -18,6 +18,7 @@ import { messengerContext, refuseLocalWrite } from "./context.js";
18
18
  import { stopOnSignal } from "./patience.js";
19
19
  /** `messages download`: every file of one message, or with `--all` of a whole chat, never over a file already there. */
20
20
  export const downloadSubcommand = (messages, messenger) => annotate(messages.command("download"), {
21
+ output: { entity: "message_download" },
21
22
  examples: [`${messenger.app.command} personal messages download "Book club" 101 --output-dir ./downloads`],
22
23
  effects: FETCH,
23
24
  })
@@ -1,7 +1,7 @@
1
1
  import { Command } from "commander";
2
2
  import { type Messenger } from "./context.js";
3
3
  /**
4
- * The owner's way out of a remembered wait or a hold on writes, once they know it is over. No MCP
5
- * tool on purpose: an agent that could lift a hold would send straight into a spam limit again.
4
+ * The owner's way out of a remembered wait or a hold on writes, once they know it is over. An agent gets it only
5
+ * under `mcp.authority`: one that could lift a hold would send straight into a spam limit again.
6
6
  */
7
7
  export declare const floodCommand: (messenger: Messenger) => Command;