@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
@@ -4,7 +4,7 @@ import type { MessengerAdapter } from "../../ports/messenger.js";
4
4
  import { type Messenger, type MessengerContext } from "./context.js";
5
5
  /**
6
6
  * New messages as they arrive, until Ctrl-C or `--timeout` — both a normal end, exit 0: listening
7
- * for a minute is a complete answer. One `MessageHit` per line with `--jsonl`; `--json` is refused,
7
+ * for a minute is a complete answer. Canonical item records and one end record with `--jsonl`; `--json` is refused,
8
8
  * because a stream is not one value. Nothing is marked read.
9
9
  */
10
10
  export declare const watchCommand: (messenger: Messenger) => Command;
@@ -4,21 +4,25 @@ import { Command } from "commander";
4
4
  import { renderMessages } from "../../render/messages.js";
5
5
  import { environmentOf } from "../context.js";
6
6
  import { REMOTE_READ } from "../effects.js";
7
+ import { jsonlRecords, listOf } from "../envelope.js";
7
8
  import { withoutProviderRefs } from "../provider-refs.js";
8
9
  import { messengerContext } from "./context.js";
10
+ import { selfOf } from "./message-items.js";
11
+ import { watchEventItem, watchedMessageItem } from "./watch-answers.js";
9
12
  /**
10
13
  * New messages as they arrive, until Ctrl-C or `--timeout` — both a normal end, exit 0: listening
11
- * for a minute is a complete answer. One `MessageHit` per line with `--jsonl`; `--json` is refused,
14
+ * for a minute is a complete answer. Canonical item records and one end record with `--jsonl`; `--json` is refused,
12
15
  * because a stream is not one value. Nothing is marked read.
13
16
  */
14
17
  export const watchCommand = (messenger) => annotate(new Command("watch"), {
18
+ output: { entity: "watch_item" },
15
19
  effects: REMOTE_READ,
16
20
  lifetime: "persistent",
17
21
  examples: [`${messenger.app.command} personal watch --jsonl --timeout 30s`],
18
22
  })
19
23
  .summary("stream new messages until interrupted or timed out")
20
24
  .description("print new messages as they arrive, until Ctrl-C or --timeout (either ends it normally)")
21
- .option("--events", "also edits, deletions, reactions, poll votes and chat events; every line then names its event")
25
+ .option("--events", "include changes and receipts; each item names its event")
22
26
  .action(async function () {
23
27
  const events = this.opts().events === true;
24
28
  const context = messengerContext(this, messenger);
@@ -35,19 +39,59 @@ export const watchCommand = (messenger) => annotate(new Command("watch"), {
35
39
  provider: messenger.provider,
36
40
  locale: messenger.app.locale,
37
41
  })}`;
38
- // Without --events the stream is bare messages, as it always was: a reader of it never meets
39
- // a line of another shape.
42
+ // Event-tagged payloads are opt-in; ordinary items remain message values.
40
43
  const stop = new AbortController();
44
+ let connection;
45
+ let printed = 0;
46
+ const account = () => {
47
+ if (!connection)
48
+ throw new Error("watch has no connected account");
49
+ return selfOf(messenger, connection);
50
+ };
41
51
  const print = (event) => {
42
52
  if (stop.signal.aborted || (!events && event.event !== "message"))
43
53
  return;
44
54
  if (context.format === "jsonl") {
45
- context.streams.data(JSON.stringify(withoutProviderRefs(events ? event : event.message)));
55
+ const data = events
56
+ ? watchEventItem(event, account())
57
+ : event.event === "message"
58
+ ? watchedMessageItem(event.message, account())
59
+ : undefined;
60
+ if (data === undefined)
61
+ return;
62
+ context.streams.data(JSON.stringify({ type: "item", data: withoutProviderRefs(data) }));
63
+ printed++;
46
64
  }
47
65
  else
48
66
  context.streams.data(describe(event, render));
49
67
  };
50
- await listenUntilStopped(this, context, messenger, print, { stop, pipe: true });
68
+ const finish = (complete, error) => {
69
+ if (context.format !== "jsonl" || environmentOf(this).commandSignal?.aborted)
70
+ return;
71
+ const issues = !complete && error instanceof CliError ? [{ code: error.code, source: "watch" }] : undefined;
72
+ for (const record of jsonlRecords(listOf("watch", {
73
+ items: [],
74
+ limit: Math.max(1, printed),
75
+ has_more: false,
76
+ paging: "cursor",
77
+ meta: { complete, ...(issues ? { issues } : {}) },
78
+ })))
79
+ context.streams.data(JSON.stringify(record));
80
+ };
81
+ try {
82
+ await listenUntilStopped(this, context, messenger, print, {
83
+ stop,
84
+ pipe: true,
85
+ connected: (open) => {
86
+ connection = open;
87
+ },
88
+ });
89
+ }
90
+ catch (error) {
91
+ finish(false, error);
92
+ throw error;
93
+ }
94
+ finish(true);
51
95
  });
52
96
  /**
53
97
  * Listens until Ctrl-C, SIGTERM, `--timeout` or — with `pipe` — a reader that has gone away.
@@ -55,7 +99,11 @@ export const watchCommand = (messenger) => annotate(new Command("watch"), {
55
99
  * `feed` runs it beside `watch`, so what the messenger pushes reaches the store (`stored`).
56
100
  */
57
101
  export const listenUntilStopped = async (command, context, messenger, onEvent, { stop, pipe = false, catchUp = false, onReady = () => context.renderer.note("listening — Ctrl-C to stop"), connected, }) => {
58
- const given = environmentOf(command).signal;
102
+ const environment = environmentOf(command);
103
+ const phase = environment.untilStopped?.();
104
+ const given = phase
105
+ ? AbortSignal.any([phase, ...[environment.signal].filter((one) => one !== undefined)])
106
+ : environment.signal;
59
107
  const end = () => stop.abort();
60
108
  given?.addEventListener("abort", end, { once: true });
61
109
  const signals = given ? [] : ["SIGINT", "SIGTERM"];
@@ -96,6 +144,7 @@ export const listenUntilStopped = async (command, context, messenger, onEvent, {
96
144
  if (pipe && !given)
97
145
  process.stdout.off("error", closed);
98
146
  given?.removeEventListener("abort", end);
147
+ environment.endUntilStopped?.();
99
148
  }
100
149
  };
101
150
  const describe = (event, render) => {
@@ -11,7 +11,7 @@ import { failureContext } from "../errors/reasons.js";
11
11
  import { envName, readOnlyLockOf, readOnlyRefusal } from "../ports/app.js";
12
12
  import { recorded, wasSettled } from "../runs/recording.js";
13
13
  import { processRun, useAsProcessRun } from "../runs/scope.js";
14
- import { keyForCommand } from "../sends/permissions.js";
14
+ import { AUTHORITY_KEY, keyForCommand } from "../sends/permissions.js";
15
15
  import { logParsedArgv, logVersion } from "./argv-log.js";
16
16
  import { commandPathOf } from "./command-contract.js";
17
17
  import { outputFor, provide } from "./context.js";
@@ -254,6 +254,10 @@ const runOnce = async (argv, definition, options) => {
254
254
  if (lock && changesAuthorityNow(action) && !flags.dryRun)
255
255
  throw new CliError("permission_error", `this process is read-only (${lock}) — \`${command} ${path.join(" ")}\` changes what a later run may do; ` +
256
256
  `run it without ${lock}`);
257
+ if (options.agent && options.agent.authority !== true && changesAuthorityNow(action))
258
+ throw new CliError("permission_error", `\`${command} ${path.join(" ")}\` changes what a later run may do, and this profile keeps that from agents ` +
259
+ `(${AUTHORITY_KEY}) — the owner turns it on in a terminal: ` +
260
+ `\`${command} <profile> config set${path[0] === "bot" ? " --bot" : ""} permissions.${AUTHORITY_KEY} allow\``, { permission: AUTHORITY_KEY, sent: false });
257
261
  const { lifetime, cooperative: stopsAtItsPace } = metaOf(action);
258
262
  const persistent = lifetime === "persistent";
259
263
  listens = persistent;
@@ -1,13 +1,14 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { annotate } from "@wirecat/cli-base/commands";
3
- import { Command, Option } from "commander";
3
+ import { Command } from "commander";
4
4
  import { findRun, listRuns, readEvents, runsDirFor } from "../../runs/run.js";
5
5
  import { environmentOf, outputFor } from "../context.js";
6
6
  import { LOCAL_READ, NO_EFFECTS } from "../effects.js";
7
7
  import { itemOf, listOf, renderAnswer, snakeKeys } from "../envelope.js";
8
+ import { typedQuery, withQuery } from "../list-query.js";
8
9
  import { positiveCount } from "../paging.js";
9
10
  import { lineOf } from "../steps.js";
10
- import { runsFound, searchRuns } from "./search.js";
11
+ import { RUNS_QUERY, runListFilter } from "./query.js";
11
12
  /** Reading the log is not itself worth recording, so **nothing here starts a run**. */
12
13
  export const runsCommand = (app) => {
13
14
  const command = new Command("runs").description("recorded runs — what this tool did, and when");
@@ -18,24 +19,35 @@ export const runsCommand = (app) => {
18
19
  throw new CliError("not_found", `no recorded run "${id}" — \`${lineOf(from, "runs list")}\` shows them`);
19
20
  return found;
20
21
  };
21
- annotate(command.command("list"), {
22
- effects: LOCAL_READ,
22
+ withQuery(annotate(command.command("list"), {
23
23
  examples: [`${app.command} runs list --limit 10`],
24
- })
24
+ effects: LOCAL_READ,
25
+ }), RUNS_QUERY)
25
26
  .description("recorded runs, newest first")
26
27
  .option("--limit <n>", "how many to show", positiveCount("--limit"), 20)
27
28
  .option("--page <n>", "which page, starting at 1", positiveCount("--page"), 1)
28
- .action(function () {
29
+ .action(function (words) {
30
+ const filter = runListFilter(words);
29
31
  const { limit, page } = this.opts();
30
32
  const output = outputFor(this);
31
33
  const { renderer } = output;
32
- const runs = listRuns(dir(this), { atLeast: page * limit + 1 });
34
+ const runs = listRuns(dir(this), { atLeast: page * limit + 1, filter });
33
35
  const shown = runs.slice((page - 1) * limit, page * limit);
34
- renderAnswer(this, output, listOf("runs list", { items: shown.map(snakeKeys), limit, page, has_more: runs.length > page * limit }));
36
+ renderAnswer(this, output, listOf("runs list", {
37
+ items: shown.map(snakeKeys),
38
+ limit,
39
+ page,
40
+ has_more: runs.length > page * limit,
41
+ args: words.length ? { query: typedQuery(words) } : {},
42
+ }));
35
43
  if (runs.length === 0)
36
44
  renderer.note("nothing recorded — a run is kept with `--record`, or when it fails");
37
45
  });
38
- annotate(command.command("show"), { effects: LOCAL_READ, examples: [`${app.command} runs show run-101`] })
46
+ annotate(command.command("show"), {
47
+ examples: [`${app.command} runs show run-101`],
48
+ effects: LOCAL_READ,
49
+ output: { entity: "run", view: "details" },
50
+ })
39
51
  .argument("<run-id>", `an id from \`${app.command} runs list\``)
40
52
  .description("one run: what it was, how it ended, and every event it logged")
41
53
  .option("--problems", "only failed responses, warnings and crashes")
@@ -56,36 +68,11 @@ export const runsCommand = (app) => {
56
68
  if (shown.length < events.length)
57
69
  renderer.note(`${events.length - shown.length} other events — \`${lineOf(this, `runs show ${id}`)}\` shows them all`);
58
70
  });
59
- annotate(command.command("search"), {
60
- effects: LOCAL_READ,
61
- examples: [`${app.command} runs search timeout --limit 10`],
71
+ annotate(command.command("path"), {
72
+ examples: [`${app.command} runs path run-101`],
73
+ effects: NO_EFFECTS,
74
+ output: { entity: "run_location" },
62
75
  })
63
- .description("search recorded diagnostic metadata and events; no message contents")
64
- .argument("[query]", "case-insensitive literal diagnostic text")
65
- .addOption(new Option("--status <status>", "run outcome").choices(["success", "failed", "partial", "running"]))
66
- .option("--error-code <code>", "one stable error code")
67
- .option("--operation <name>", "one operation, such as messages.download")
68
- .option("--profile <name>", "one recorded profile")
69
- .option("--since-time <time>", "runs starting on or after an ISO time or 2h / 1d ago")
70
- .option("--limit <n>", "matches per page, at most 100", positiveCount("--limit"), 20)
71
- .option("--page <n>", "result page", positiveCount("--page"), 1)
72
- .action(function (query) {
73
- const options = this.opts();
74
- const found = searchRuns(dir(this), {
75
- ...options,
76
- since: options.sinceTime,
77
- ...(query === undefined ? {} : { query }),
78
- });
79
- renderAnswer(this, outputFor(this), runsFound(found, {
80
- query,
81
- status: options.status,
82
- error_code: options.errorCode,
83
- operation: options.operation,
84
- profile: options.profile,
85
- since_time: options.sinceTime,
86
- }));
87
- });
88
- annotate(command.command("path"), { effects: NO_EFFECTS, examples: [`${app.command} runs path run-101`] })
89
76
  .argument("<run-id>", `an id from \`${app.command} runs list\``)
90
77
  .description("the directory holding one run")
91
78
  .action(function (id) {
@@ -0,0 +1,5 @@
1
+ import { type QueryRegistry } from "@wirecat/cli-base/query";
2
+ import type { RunListFilter } from "../../runs/run.js";
3
+ import { type QueryWords } from "../list-query.js";
4
+ export declare const RUNS_QUERY: QueryRegistry;
5
+ export declare const runListFilter: (words: QueryWords) => RunListFilter;
@@ -0,0 +1,32 @@
1
+ import { queryFieldsOf } from "@wirecat/cli-base/query";
2
+ import { recordedRun } from "../../entities/index.js";
3
+ import { queryTerms } from "../list-query.js";
4
+ export const RUNS_QUERY = {
5
+ entity: "run",
6
+ backend: "native",
7
+ fields: queryFieldsOf(recordedRun, {
8
+ native: ["run_id", "command", "profile", "status", "error_code"],
9
+ examples: {
10
+ command: 'command:"messages send"',
11
+ profile: "profile:work",
12
+ status: "status:failed",
13
+ error_code: "error_code:timeout",
14
+ },
15
+ }).map((field) => field.native
16
+ ? {
17
+ ...field,
18
+ operators: field.operators.filter((operator) => operator === "term" || operator === "phrase"),
19
+ }
20
+ : field),
21
+ };
22
+ export const runListFilter = (words) => {
23
+ const terms = queryTerms(words, RUNS_QUERY, "runs list");
24
+ const status = recordedRun.entries.status.options.find((value) => value === terms.get("status"));
25
+ return {
26
+ ...(terms.has("run_id") ? { runId: terms.get("run_id") } : {}),
27
+ ...(terms.has("command") ? { command: terms.get("command") } : {}),
28
+ ...(terms.has("profile") ? { profile: terms.get("profile") } : {}),
29
+ ...(status === undefined ? {} : { status }),
30
+ ...(terms.has("error_code") ? { errorCode: terms.get("error_code") } : {}),
31
+ };
32
+ };
@@ -0,0 +1,4 @@
1
+ import { Command } from "commander";
2
+ import type { AppIdentity } from "../../ports/app.js";
3
+ /** Diagnostic files are independent of linked accounts and never start a recorded run themselves. */
4
+ export declare const runsSearchCommand: (app: AppIdentity) => Command;
@@ -0,0 +1,27 @@
1
+ import { annotate } from "@wirecat/cli-base/commands";
2
+ import { Command } from "commander";
3
+ import { runsDirFor } from "../../runs/run.js";
4
+ import { environmentOf, outputFor } from "../context.js";
5
+ import { LOCAL_READ } from "../effects.js";
6
+ import { renderAnswer } from "../envelope.js";
7
+ import { typedQuery, withQuery } from "../list-query.js";
8
+ import { positiveCount } from "../paging.js";
9
+ import { runsFound, searchRuns } from "./search.js";
10
+ import { RUN_SEARCH_QUERY, runSearchFilter } from "./search-query.js";
11
+ /** Diagnostic files are independent of linked accounts and never start a recorded run themselves. */
12
+ export const runsSearchCommand = (app) => annotate(withQuery(new Command("runs")
13
+ .summary("search recorded diagnostic metadata and events")
14
+ .description("search recorded diagnostic metadata and events; literal words and field:value terms joined by AND; no message contents")
15
+ .option("--since-time <time>", "runs starting on or after an ISO time or 2h / 1d ago")
16
+ .option("--limit <n>", "matches per page, at most 100", positiveCount("--limit"), 20)
17
+ .option("--page <n>", "result page", positiveCount("--page"), 1), RUN_SEARCH_QUERY), {
18
+ effects: LOCAL_READ,
19
+ output: { entity: "run", view: "hit" },
20
+ examples: [`${app.command} search runs timeout status:failed --limit 10`],
21
+ }).action(function (words) {
22
+ const filter = runSearchFilter(words);
23
+ const options = this.opts();
24
+ const dir = runsDirFor(app, environmentOf(this).env ?? process.env);
25
+ const found = searchRuns(dir, { ...filter, ...options, since: options.sinceTime });
26
+ renderAnswer(this, outputFor(this), runsFound(found, { query: typedQuery(words), since_time: options.sinceTime }));
27
+ });
@@ -0,0 +1,4 @@
1
+ import { type QueryWords } from "../list-query.js";
2
+ import type { RunSearch } from "./search.js";
3
+ export declare const RUN_SEARCH_QUERY: import("@wirecat/cli-base/query").QueryRegistry;
4
+ export declare const runSearchFilter: (words: QueryWords) => Pick<RunSearch, "terms" | "status" | "errorCode" | "operation" | "profile">;
@@ -0,0 +1,50 @@
1
+ import { defineQuery, parseLucene, queryError, validateQuery } from "@wirecat/cli-base/query";
2
+ import { LIST_QUERY_GUIDE, termField, typedQuery } from "../list-query.js";
3
+ export const RUN_SEARCH_QUERY = defineQuery({
4
+ entity: "run",
5
+ backend: "native",
6
+ fields: [
7
+ termField("text", "kw", "text:timeout"),
8
+ termField("status", "enum", "status:failed", { values: ["success", "failed", "partial", "running"] }),
9
+ termField("error_code", "kw", "error_code:rate_limited"),
10
+ termField("operation", "kw", "operation:messages.download"),
11
+ termField("profile", "kw", "profile:work"),
12
+ ],
13
+ });
14
+ export const runSearchFilter = (words) => {
15
+ const text = typedQuery(words).trim();
16
+ if (!text)
17
+ return {};
18
+ const context = { command: "search runs", guide: LIST_QUERY_GUIDE };
19
+ const ast = parseLucene(text, { defaultField: "text", ...context });
20
+ validateQuery(ast, RUN_SEARCH_QUERY, context);
21
+ const terms = [];
22
+ const fields = new Map();
23
+ const add = (node) => {
24
+ if (node.kind === "boolean") {
25
+ for (const clause of node.clauses) {
26
+ if (clause.occur !== "must")
27
+ queryError("query.unsupported-placement", clause.node.span, "run search takes terms joined by AND", context);
28
+ add(clause.node);
29
+ }
30
+ return;
31
+ }
32
+ if (node.operator !== "term" && node.operator !== "phrase")
33
+ queryError("query.unsupported-placement", node.span, "run search takes literal words and field:value terms", context);
34
+ if (node.field === "text")
35
+ terms.push(node.value);
36
+ else {
37
+ if (fields.has(node.field))
38
+ queryError("query.unsupported-placement", node.span, `${node.field} only once here`, context);
39
+ fields.set(node.field, node.value);
40
+ }
41
+ };
42
+ add(ast.root);
43
+ return {
44
+ ...(terms.length ? { terms } : {}),
45
+ ...(fields.has("status") ? { status: fields.get("status")?.toLowerCase() } : {}),
46
+ ...(fields.has("error_code") ? { errorCode: fields.get("error_code") } : {}),
47
+ ...(fields.has("operation") ? { operation: fields.get("operation") } : {}),
48
+ ...(fields.has("profile") ? { profile: fields.get("profile") } : {}),
49
+ };
50
+ };
@@ -1,5 +1,6 @@
1
1
  export interface RunSearch {
2
2
  query?: string;
3
+ terms?: readonly string[];
3
4
  status?: "success" | "failed" | "partial" | "running";
4
5
  errorCode?: string;
5
6
  operation?: string;
@@ -8,7 +9,7 @@ export interface RunSearch {
8
9
  limit?: number;
9
10
  page?: number;
10
11
  }
11
- export declare const searchRuns: (dir: string, { query, status, errorCode, operation, profile, since, limit, page }?: RunSearch) => {
12
+ export declare const searchRuns: (dir: string, { query, terms, status, errorCode, operation, profile, since, limit, page }?: RunSearch) => {
12
13
  items: {
13
14
  runId: string;
14
15
  command: string;
@@ -32,14 +32,14 @@ const safeEvent = (event) => Object.fromEntries(FIELDS.flatMap((name) => {
32
32
  }
33
33
  return typeof value === "string" || typeof value === "number" ? [[name, value]] : [];
34
34
  }));
35
- export const searchRuns = (dir, { query = "", status, errorCode, operation, profile, since, limit = 20, page = 1 } = {}) => {
35
+ export const searchRuns = (dir, { query = "", terms = [], status, errorCode, operation, profile, since, limit = 20, page = 1 } = {}) => {
36
36
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100 || !Number.isSafeInteger(page) || page < 1)
37
37
  throw new CliError("validation_error", "run search uses a limit from 1 to 100 and a positive page");
38
38
  const window = dateWindow(since === undefined ? {} : { sinceTime: since });
39
39
  const from = window.since;
40
40
  if (from !== undefined && !Number.isFinite(from))
41
41
  throw new CliError("validation_error", "--since-time must be a valid date or ISO timestamp");
42
- const needle = query.toLowerCase();
42
+ const needles = [...(query ? [query] : []), ...terms].map((term) => term.toLowerCase());
43
43
  const offset = (page - 1) * limit;
44
44
  const matches = [];
45
45
  for (const run of listRuns(dir, from === undefined ? {} : { since: from })) {
@@ -51,11 +51,11 @@ export const searchRuns = (dir, { query = "", status, errorCode, operation, prof
51
51
  run.errorCode === errorCode ||
52
52
  run.partial?.failures.some((failure) => failure.errorCode === errorCode)) &&
53
53
  !operation &&
54
- (!needle || JSON.stringify(run).toLowerCase().includes(needle));
54
+ needles.every((needle) => JSON.stringify(run).toLowerCase().includes(needle));
55
55
  // A finished run lists its operations and codes, so a run that cannot match is never opened.
56
56
  // One still running, or killed, has no such list and is read.
57
57
  const ruledOut = !metadataMatch &&
58
- !needle &&
58
+ needles.length === 0 &&
59
59
  ((operation !== undefined && run.operations !== undefined && !run.operations.includes(operation)) ||
60
60
  (errorCode !== undefined && run.codes !== undefined && !run.codes.includes(errorCode)));
61
61
  if (ruledOut)
@@ -64,7 +64,7 @@ export const searchRuns = (dir, { query = "", status, errorCode, operation, prof
64
64
  const events = found ? readEvents(found.dir).map(safeEvent) : [];
65
65
  const filtered = events.filter((event) => (!errorCode || event.error_code === errorCode || event.code === errorCode) &&
66
66
  (!operation || event.operation === operation) &&
67
- (!needle || JSON.stringify(event).toLowerCase().includes(needle)));
67
+ needles.every((needle) => JSON.stringify(event).toLowerCase().includes(needle)));
68
68
  if (!metadataMatch && filtered.length === 0)
69
69
  continue;
70
70
  const metadata = {
@@ -110,7 +110,7 @@ export const searchRuns = (dir, { query = "", status, errorCode, operation, prof
110
110
  };
111
111
  };
112
112
  /** A page of `searchRuns` in the list envelope; `args` are the search's own, so `page.next` reruns it. */
113
- export const runsFound = (found, args) => listOf("runs search", {
113
+ export const runsFound = (found, args) => listOf("search runs", {
114
114
  items: found.items.map(snakeKeys),
115
115
  limit: found.limit,
116
116
  has_more: found.hasMore,
@@ -1,5 +1,13 @@
1
1
  /** Attachments that are never a file, so their messages cost no download request. */
2
- export const NOT_FILES = new Set(["webpage", "share", "poll", "location", "contact"]);
2
+ export const NOT_FILES = new Set([
3
+ "webpage",
4
+ "share",
5
+ "poll",
6
+ "location",
7
+ "contact",
8
+ "control",
9
+ "inline_keyboard",
10
+ ]);
3
11
  /** The stored kinds `has:<kind>` matches: MAX stores a file as `file`, Telegram as `document`. */
4
12
  export const storedKinds = (kind) => (kind === "file" ? ["file", "document"] : [kind]);
5
13
  /**
@@ -33,11 +33,11 @@ export const rankingOptions = (target, input) => {
33
33
  const { measure, score, weights: given, minMessages } = input;
34
34
  const messageKind = input.messageKind ?? "all";
35
35
  if (!["all", "posts", "comments"].includes(messageKind))
36
- invalid("--message-kind takes all, posts or comments");
36
+ invalid("message_kind takes all, posts or comments");
37
37
  if (target === "messages" && minMessages !== undefined)
38
- invalid("--min-messages applies to stats contacts top");
38
+ invalid("min_messages applies to stats contacts top");
39
39
  if (minMessages !== undefined && (!Number.isSafeInteger(minMessages) || minMessages < 1))
40
- invalid("--min-messages takes a positive integer");
40
+ invalid("min_messages takes a positive integer");
41
41
  if (measure !== undefined && !measures.includes(measure))
42
42
  invalid(`--measure takes ${measures.join(", ")}`);
43
43
  if (measure !== undefined && (score !== undefined || given !== undefined))
@@ -0,0 +1,89 @@
1
+ import * as v from "valibot";
2
+ export declare const accountChange: v.StrictObjectSchema<{
3
+ readonly operation_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
4
+ readonly account: Omit<v.StrictObjectSchema<{
5
+ readonly provider: v.StringSchema<undefined>;
6
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
7
+ readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
8
+ readonly username: v.NullableSchema<v.StringSchema<undefined>, undefined>;
9
+ readonly phone: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
10
+ readonly scope: v.OptionalSchema<v.PicklistSchema<["personal", "work"], undefined>, undefined>;
11
+ }, undefined>, "entries" | "~run" | "~standard" | "~types"> & {
12
+ readonly entries: Omit<{
13
+ readonly provider: v.StringSchema<undefined>;
14
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
15
+ readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
16
+ readonly username: v.NullableSchema<v.StringSchema<undefined>, undefined>;
17
+ readonly phone: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
18
+ readonly scope: v.OptionalSchema<v.PicklistSchema<["personal", "work"], undefined>, undefined>;
19
+ }, "provider" | "scope">;
20
+ readonly "~standard": v.StandardProps<{
21
+ id: string;
22
+ name: string | null;
23
+ username: string | null;
24
+ phone?: string | null | undefined;
25
+ }, {
26
+ id: string;
27
+ name: string | null;
28
+ username: string | null;
29
+ phone?: string | null | undefined;
30
+ }>;
31
+ readonly "~run": (dataset: v.UnknownDataset, config: v.Config<v.BaseIssue<unknown>>) => v.OutputDataset<{
32
+ id: string;
33
+ name: string | null;
34
+ username: string | null;
35
+ phone?: string | null | undefined;
36
+ }, v.MinLengthIssue<string, 1> | v.StrictObjectIssue | v.StringIssue>;
37
+ readonly "~types"?: {
38
+ readonly input: {
39
+ id: string;
40
+ name: string | null;
41
+ username: string | null;
42
+ phone?: string | null | undefined;
43
+ };
44
+ readonly output: {
45
+ id: string;
46
+ name: string | null;
47
+ username: string | null;
48
+ phone?: string | null | undefined;
49
+ };
50
+ readonly issue: v.MinLengthIssue<string, 1> | v.StrictObjectIssue | v.StringIssue;
51
+ } | undefined;
52
+ };
53
+ }, undefined>;
54
+ export declare const endedAccountSessions: v.StrictObjectSchema<{
55
+ readonly operation_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
56
+ readonly sessions: v.ArraySchema<v.StrictObjectSchema<{
57
+ readonly current: v.BooleanSchema<undefined>;
58
+ readonly client: v.NullableSchema<v.StringSchema<undefined>, undefined>;
59
+ readonly device: v.NullableSchema<v.StringSchema<undefined>, undefined>;
60
+ readonly location: v.NullableSchema<v.StringSchema<undefined>, undefined>;
61
+ readonly last_active_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
62
+ readonly created_at: v.OptionalSchema<v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>, undefined>;
63
+ }, undefined>, undefined>;
64
+ }, undefined>;
65
+ export declare const accountPrivacy: v.StrictObjectSchema<{
66
+ readonly find_by_phone: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody"], undefined>, undefined>;
67
+ readonly phone_number: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody"], undefined>, undefined>;
68
+ readonly calls: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody"], undefined>, undefined>;
69
+ readonly chat_invites: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody"], undefined>, undefined>;
70
+ readonly hide_online: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
71
+ readonly read_receipts: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
72
+ readonly profile_photo: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody", "contacts_except"], undefined>, undefined>;
73
+ readonly about: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody", "contacts_except"], undefined>, undefined>;
74
+ readonly last_seen: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody", "contacts_except"], undefined>, undefined>;
75
+ }, undefined>;
76
+ export declare const accountPrivacyChange: v.StrictObjectSchema<{
77
+ readonly operation_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
78
+ readonly privacy: v.StrictObjectSchema<{
79
+ readonly find_by_phone: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody"], undefined>, undefined>;
80
+ readonly phone_number: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody"], undefined>, undefined>;
81
+ readonly calls: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody"], undefined>, undefined>;
82
+ readonly chat_invites: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody"], undefined>, undefined>;
83
+ readonly hide_online: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
84
+ readonly read_receipts: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
85
+ readonly profile_photo: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody", "contacts_except"], undefined>, undefined>;
86
+ readonly about: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody", "contacts_except"], undefined>, undefined>;
87
+ readonly last_seen: v.OptionalSchema<v.PicklistSchema<["everyone", "contacts", "nobody", "contacts_except"], undefined>, undefined>;
88
+ }, undefined>;
89
+ }, undefined>;
@@ -0,0 +1,20 @@
1
+ import * as v from "valibot";
2
+ import { account } from "./account.js";
3
+ import { accountSession } from "./account-session.js";
4
+ import { id } from "./fields.js";
5
+ export const accountChange = v.strictObject({ operation_id: id, account: v.omit(account, ["provider", "scope"]) });
6
+ export const endedAccountSessions = v.strictObject({ operation_id: id, sessions: v.array(accountSession) });
7
+ const audience = v.picklist(["everyone", "contacts", "nobody"]);
8
+ const shownAudience = v.picklist(["everyone", "contacts", "nobody", "contacts_except"]);
9
+ export const accountPrivacy = v.strictObject({
10
+ find_by_phone: v.optional(audience),
11
+ phone_number: v.optional(audience),
12
+ calls: v.optional(audience),
13
+ chat_invites: v.optional(audience),
14
+ hide_online: v.optional(v.boolean()),
15
+ read_receipts: v.optional(v.boolean()),
16
+ profile_photo: v.optional(shownAudience),
17
+ about: v.optional(shownAudience),
18
+ last_seen: v.optional(shownAudience),
19
+ });
20
+ export const accountPrivacyChange = v.strictObject({ operation_id: id, privacy: accountPrivacy });
@@ -0,0 +1,7 @@
1
+ import * as v from "valibot";
2
+ export declare const accountProfile: v.StrictObjectSchema<{
3
+ readonly profile: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
4
+ readonly current: v.BooleanSchema<undefined>;
5
+ readonly account: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
6
+ readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
7
+ }, undefined>;
@@ -0,0 +1,8 @@
1
+ import * as v from "valibot";
2
+ import { id, text } from "./fields.js";
3
+ export const accountProfile = v.strictObject({
4
+ profile: id,
5
+ current: v.boolean(),
6
+ account: v.nullable(id),
7
+ name: v.nullable(text),
8
+ });
@@ -0,0 +1,9 @@
1
+ import * as v from "valibot";
2
+ export declare const analysisConsent: v.StrictObjectSchema<{
3
+ readonly chat: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
4
+ readonly provider: v.StringSchema<undefined>;
5
+ readonly at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
6
+ }, undefined>;
7
+ export declare const analysisConsentRevocation: v.StrictObjectSchema<{
8
+ readonly revoked: v.BooleanSchema<undefined>;
9
+ }, undefined>;
@@ -0,0 +1,4 @@
1
+ import * as v from "valibot";
2
+ import { id, text, time } from "./fields.js";
3
+ export const analysisConsent = v.strictObject({ chat: id, provider: text, at: time });
4
+ export const analysisConsentRevocation = v.strictObject({ revoked: v.boolean() });