@wirecat/cli-core 0.530.0 → 0.532.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 (67) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/cli/index.d.ts +2 -2
  3. package/dist/cli/index.js +1 -1
  4. package/dist/cli/list-query.d.ts +0 -4
  5. package/dist/cli/list-query.js +19 -1
  6. package/dist/cli/messenger/attachments-command.d.ts +1 -0
  7. package/dist/cli/messenger/attachments-command.js +1 -1
  8. package/dist/cli/messenger/download-command.js +37 -10
  9. package/dist/cli/messenger/schedule-command.d.ts +2 -19
  10. package/dist/cli/messenger/schedule-command.js +2 -231
  11. package/dist/cli/messenger/search-answers.js +7 -2
  12. package/dist/cli/messenger/topics-command.js +22 -5
  13. package/dist/cli/schedule/schedule-command.d.ts +19 -0
  14. package/dist/cli/schedule/schedule-command.js +261 -0
  15. package/dist/cli/shared-command-exports.d.ts +1 -1
  16. package/dist/cli/shared-command-exports.js +2 -1
  17. package/dist/domain/attachments.d.ts +9 -1
  18. package/dist/domain/attachments.js +12 -0
  19. package/dist/domain/message-link.d.ts +14 -0
  20. package/dist/domain/message-link.js +8 -0
  21. package/dist/domain/models.d.ts +2 -0
  22. package/dist/entities/attachment.d.ts +7 -0
  23. package/dist/entities/attachment.js +2 -0
  24. package/dist/entities/hit.d.ts +7 -0
  25. package/dist/entities/hit.js +3 -0
  26. package/dist/entities/index.d.ts +61 -2
  27. package/dist/entities/index.js +6 -3
  28. package/dist/entities/list-results.d.ts +3 -0
  29. package/dist/entities/list-results.js +1 -0
  30. package/dist/entities/message-link.d.ts +8 -0
  31. package/dist/entities/message-link.js +9 -1
  32. package/dist/entities/task-results.d.ts +30 -0
  33. package/dist/entities/task-results.js +1 -0
  34. package/dist/entities/task.d.ts +7 -0
  35. package/dist/entities/task.js +2 -0
  36. package/dist/errors/reasons.js +1 -0
  37. package/dist/mail/account-key.d.ts +2 -0
  38. package/dist/mail/account-key.js +1 -0
  39. package/dist/mail/cli/index.js +10 -10
  40. package/dist/mail/index.d.ts +1 -0
  41. package/dist/mail/index.js +1 -0
  42. package/dist/mail/retry.js +2 -1
  43. package/dist/mail/rule-stored.js +2 -1
  44. package/dist/mail/sender-scores.js +2 -1
  45. package/dist/mail/sync.js +3 -2
  46. package/dist/ports/messenger.d.ts +3 -0
  47. package/dist/services/attachments.d.ts +11 -0
  48. package/dist/services/attachments.js +45 -2
  49. package/dist/services/deps.d.ts +1 -1
  50. package/dist/services/deps.js +1 -1
  51. package/dist/services/messages.d.ts +2 -3
  52. package/dist/services/messages.js +27 -8
  53. package/dist/services/search-all.d.ts +3 -0
  54. package/dist/services/search-all.js +2 -0
  55. package/dist/services/tasks.d.ts +4 -2
  56. package/dist/services/tasks.js +2 -2
  57. package/dist/services/topics.d.ts +3 -0
  58. package/dist/services/topics.js +20 -6
  59. package/dist/store/index.d.ts +1 -0
  60. package/dist/store/sqlite/task-board.js +3 -1
  61. package/dist/store/sqlite/task-comments.d.ts +23 -0
  62. package/dist/store/sqlite/task-comments.js +41 -0
  63. package/dist/store/sqlite/things.js +8 -1
  64. package/dist/store/sqlite/work-tasks.d.ts +13 -2
  65. package/dist/store/sqlite/work-tasks.js +52 -1
  66. package/package.json +1 -1
  67. package/parity.json +3 -0
@@ -0,0 +1,19 @@
1
+ import { Command } from "commander";
2
+ import { type AppIdentity } from "../../ports/app.js";
3
+ import { type AnswerOutput } from "../envelope.js";
4
+ /** What the schedule command needs of a CLI: who it is and, per command, where it prints and which profile runs. */
5
+ export interface ScheduleHost {
6
+ app: AppIdentity;
7
+ context(command: Command, resolving?: {
8
+ needsProfile?: boolean;
9
+ }): AnswerOutput & {
10
+ env: NodeJS.ProcessEnv;
11
+ profile: string;
12
+ };
13
+ }
14
+ /**
15
+ * Runs the CLI's own commands, or an agent, later or on a timer — through systemd (Linux), launchd
16
+ * (macOS) or Task Scheduler (Windows). Every timer runs `schedule exec <file>`; the schedule file holds
17
+ * the rest, so what runs and with which variables is the same on all three.
18
+ */
19
+ export declare const scheduleCommandFor: ({ app, context: contextOf }: ScheduleHost) => Command;
@@ -0,0 +1,261 @@
1
+ import { existsSync, rmSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { CliError, resolvePaths, writeSecurely } from "@wirecat/cli-base";
4
+ import { annotate } from "@wirecat/cli-base/commands";
5
+ import { Argument, Command, Option } from "commander";
6
+ import { describeWhen, findProgram, lastRun, listSchedules, readSchedule, runSchedule, scheduleFile, scheduleName, timerFor, } from "../../background/schedules.js";
7
+ import { thisMachine } from "../../background/system.js";
8
+ import { locationVariables } from "../../background/units.js";
9
+ import { parseDuration } from "../../domain/duration.js";
10
+ import { readOnlyLockOf } from "../../ports/app.js";
11
+ import { aheadOf } from "../../services/moment.js";
12
+ import { storePath } from "../../store/path.js";
13
+ import { environmentOf, setExitCode } from "../context.js";
14
+ import { grantsAuthority, LOCAL_READ, LOCAL_WRITE, REMOTE_WRITE } from "../effects.js";
15
+ import { itemOf, listOf, renderAnswer, snakeKeys } from "../envelope.js";
16
+ import { withQuery } from "../list-query.js";
17
+ import { SCHEDULES_QUERY, scheduleFilter } from "../messenger/list-result-queries.js";
18
+ import { lineOf } from "../steps.js";
19
+ /** Each run of an agent is a paid model call that reads chats; keeping the store current is `serve`'s job. */
20
+ const AGENT_MINIMUM_MINUTES = 60;
21
+ const COMMAND_MINIMUM_MINUTES = 1;
22
+ const HARNESSES = ["claude", "codex"];
23
+ const whenOf = ({ every, at, atTime }, minimum) => {
24
+ if ([every ?? at, atTime].filter(Boolean).length !== 1) {
25
+ throw new CliError("validation_error", "say when: --every <30m|2h|1d>, --at HH:MM (daily), or --at-time <30m|2d|ISO time>");
26
+ }
27
+ if (atTime) {
28
+ const time = aheadOf(atTime, "--at-time");
29
+ if (time <= Date.now())
30
+ throw new CliError("validation_error", "--at-time is in the past");
31
+ return { once: new Date(time).toISOString() };
32
+ }
33
+ if (at) {
34
+ if (!/^([01]\d|2[0-3]):[0-5]\d$/.test(at))
35
+ throw new CliError("validation_error", `--at takes HH:MM, not "${at}"`);
36
+ if (every && parseDuration(every, "--every") !== 86_400_000) {
37
+ throw new CliError("validation_error", "--at runs once a day; leave out --every, or use --every 1d");
38
+ }
39
+ return { daily: at };
40
+ }
41
+ const minutes = parseDuration(every ?? "", "--every") / 60_000;
42
+ if (!Number.isInteger(minutes) || minutes < minimum) {
43
+ throw new CliError("validation_error", `--every is at least ${minimum} minute${minimum === 1 ? "" : "s"} here, in whole minutes`);
44
+ }
45
+ return { every: minutes };
46
+ };
47
+ /** A schedule as answered: snake_case, but its environment keeps the variables' own names. */
48
+ const scheduleAnswer = ({ environment, createdAt, ...schedule }, extra) => ({
49
+ ...snakeKeys(schedule),
50
+ environment,
51
+ created_at: createdAt,
52
+ ...snakeKeys(extra),
53
+ });
54
+ const withWhen = (command) => command
55
+ .option("--every <duration>", "repeat: 30m, 2h, 1d")
56
+ .option("--at <HH:MM>", "every day at this local time")
57
+ .option("--at-time <time>", "one run: 30m, 2d from now, or an ISO time; the schedule removes itself after it");
58
+ /**
59
+ * Runs the CLI's own commands, or an agent, later or on a timer — through systemd (Linux), launchd
60
+ * (macOS) or Task Scheduler (Windows). Every timer runs `schedule exec <file>`; the schedule file holds
61
+ * the rest, so what runs and with which variables is the same on all three.
62
+ */
63
+ export const scheduleCommandFor = ({ app, context: contextOf }) => {
64
+ const command = new Command("schedule").description(`run a ${app.command} command or an agent later or on a timer (systemd, launchd, Task Scheduler); an agent cannot send`);
65
+ const prepare = (self, resolving = {}) => {
66
+ const context = contextOf(self, resolving);
67
+ const system = environmentOf(self).system ?? thisMachine();
68
+ const state = resolvePaths({ appName: app.appName, prefix: app.envPrefix, env: context.env }).state;
69
+ const say = (value, lines) => renderAnswer(self, context, itemOf(value), () => lines.join("\n"));
70
+ const timer = () => timerFor(app, system, context.env);
71
+ // The command line refuses a schedule under the lock; a caller that gets here still passes the lock on.
72
+ const readOnly = readOnlyLockOf(app, context.env);
73
+ const environment = () => ({
74
+ [`${app.envPrefix}_PROFILE`]: context.profile,
75
+ ...locationVariables(app, context.env),
76
+ // The timer starts with almost no PATH; an agent runs `max` and its own tools by name.
77
+ ...(context.env.PATH ? { PATH: context.env.PATH } : {}),
78
+ ...(readOnly ? { [readOnly]: "1" } : {}),
79
+ });
80
+ const save = async (schedule) => {
81
+ const file = scheduleFile(state, schedule.name);
82
+ const replaced = existsSync(file);
83
+ if (replaced) {
84
+ const owner = readSchedule(file).environment[`${app.envPrefix}_PROFILE`];
85
+ if (owner && owner !== context.profile) {
86
+ throw new CliError("validation_error", `schedule ${schedule.name} already runs for profile ${owner}; name this one for ${context.profile}, e.g. ${schedule.name}-${context.profile}`);
87
+ }
88
+ }
89
+ writeSecurely(file, `${JSON.stringify(schedule, null, 2)}\n`, 0o600);
90
+ await timer().install(schedule, [...system.entry, "schedule", "exec", file]);
91
+ say(scheduleAnswer(schedule, { file, timer: timer().where(schedule.name), replaced }), [
92
+ `${replaced ? "Replaced" : "Added"} ${schedule.name}: ${describeWhen(schedule.when)}.`,
93
+ `It runs: ${schedule.kind === "agent" ? schedule.argv.join(" ") : `${app.command} ${schedule.argv.join(" ")}`}`,
94
+ `Output: ${join(dirname(file), "runs")} · now: ${lineOf(self, `schedule run ${schedule.name}`)} · stop: ${lineOf(self, `schedule remove ${schedule.name}`)}`,
95
+ ]);
96
+ };
97
+ return { context, system, state, say, timer, environment, save };
98
+ };
99
+ withWhen(grantsAuthority(annotate(command.command("command"), {
100
+ effects: LOCAL_WRITE,
101
+ examples: [
102
+ `${app.command} personal schedule command health --every 1h -- doctor`,
103
+ `${app.command} personal schedule command morning-check --at 08:00 -- doctor`,
104
+ ],
105
+ }))
106
+ .description(`run ${app.command} <args…> on a timer — reads save what they read, so this is how a chat's history fills the store`)
107
+ .argument("<name>", "lowercase letters, digits and dashes")
108
+ .argument("<args...>", `the ${app.command} command after its name, e.g. -- messages list <chat> --limit 50`)).action(async function (name, args) {
109
+ const { environment, save } = prepare(this);
110
+ if (args[0] === "schedule")
111
+ throw new CliError("validation_error", "a schedule cannot run schedule");
112
+ await save({
113
+ name: scheduleName(name),
114
+ kind: "command",
115
+ argv: args,
116
+ when: whenOf(this.opts(), COMMAND_MINIMUM_MINUTES),
117
+ environment: environment(),
118
+ createdAt: new Date().toISOString(),
119
+ });
120
+ });
121
+ withWhen(grantsAuthority(annotate(command.command("agent"), {
122
+ effects: LOCAL_WRITE,
123
+ examples: [
124
+ `${app.command} personal schedule agent review claude "review what came in overnight and draft replies" --at 08:30`,
125
+ `${app.command} personal schedule agent triage codex "list what needs an answer today" --every 2h --max-cost 1`,
126
+ ],
127
+ }))
128
+ .description(`start an agent (claude or codex) with a prompt on a timer; it runs with ${app.envPrefix}_READ_ONLY=1, so it reads and drafts and cannot send — its answer is kept and you are notified`)
129
+ .argument("<name>", "lowercase letters, digits and dashes")
130
+ .addArgument(new Argument("<harness>", "the agent to start").choices(HARNESSES))
131
+ .argument("<prompt>", `what to do, e.g. "run the daily follow-up review: ${app.command} skill show follow-ups"`)
132
+ .addOption(new Option("--max-cost <usd>", "claude only: stop a run that costs more than this, in US dollars").argParser(Number))).action(async function (name, harness, prompt) {
133
+ const { context, system, environment, save } = prepare(this);
134
+ const options = this.opts();
135
+ const program = findProgram(harness, context.env, system.platform);
136
+ if (!program)
137
+ throw new CliError("not_found", `${harness} is not on PATH`);
138
+ const paths = resolvePaths({ appName: app.appName, prefix: app.envPrefix, env: context.env });
139
+ const argv = harness === "claude"
140
+ ? [
141
+ program,
142
+ "-p",
143
+ prompt,
144
+ // The owner's own settings may let every tool run (bypassPermissions); here only the CLI may.
145
+ "--permission-mode",
146
+ "dontAsk",
147
+ "--allowedTools",
148
+ `Bash(${app.command}:*)`,
149
+ "--no-session-persistence",
150
+ ...(options.maxCost ? ["--max-budget-usd", String(options.maxCost)] : []),
151
+ ]
152
+ : [
153
+ program,
154
+ "exec",
155
+ "--skip-git-repo-check",
156
+ "--ephemeral",
157
+ "--sandbox",
158
+ "workspace-write",
159
+ // The CLI reaches the messenger and writes its store and state, and nothing else.
160
+ "-c",
161
+ "sandbox_workspace_write.network_access=true",
162
+ ...[dirname(storePath(context.env)), paths.state, paths.config, paths.cache].flatMap((dir) => [
163
+ "--add-dir",
164
+ dir,
165
+ ]),
166
+ prompt,
167
+ ];
168
+ await save({
169
+ name: scheduleName(name),
170
+ kind: "agent",
171
+ argv,
172
+ when: whenOf(options, AGENT_MINIMUM_MINUTES),
173
+ environment: environment(),
174
+ createdAt: new Date().toISOString(),
175
+ });
176
+ });
177
+ withQuery(annotate(command.command("list"), {
178
+ effects: LOCAL_READ,
179
+ examples: [`${app.command} personal schedule list`, `${app.command} personal schedule list kind:agent`],
180
+ }), SCHEDULES_QUERY)
181
+ .description("the schedules, when they run and how their last run went")
182
+ .action(function (words) {
183
+ const filter = scheduleFilter(words);
184
+ const { context, state } = prepare(this);
185
+ const rows = listSchedules(state, filter).map((schedule) => ({
186
+ name: schedule.name,
187
+ kind: schedule.kind,
188
+ when: describeWhen(schedule.when),
189
+ last: lastRun(scheduleFile(state, schedule.name)) ?? null,
190
+ }));
191
+ const answer = listOf("schedule list", {
192
+ items: rows.map((row) => snakeKeys(row)),
193
+ limit: Math.max(1, rows.length),
194
+ has_more: false,
195
+ });
196
+ renderAnswer(this, context, answer, () => {
197
+ if (rows.length === 0) {
198
+ context.renderer.note(`no schedules — \`${lineOf(this, "schedule command|agent")}\` adds one`);
199
+ return "";
200
+ }
201
+ return rows
202
+ .map(({ name, kind, when, last }) => [
203
+ name,
204
+ kind,
205
+ when,
206
+ last
207
+ ? `last ${last.at.slice(0, 16).replace("T", " ")} exit ${last.exitCode}` +
208
+ (last.failures
209
+ ? ` — failing since ${(last.failingSince ?? last.at).slice(0, 16).replace("T", " ")}, ` +
210
+ `${last.failures} in a row${last.reason ? `: ${last.reason}` : ""} — ${last.output}`
211
+ : "")
212
+ : "not run yet",
213
+ ].join(" "))
214
+ .join("\n");
215
+ });
216
+ });
217
+ annotate(command.command("run"), {
218
+ effects: REMOTE_WRITE,
219
+ examples: [`${app.command} personal schedule run health`],
220
+ })
221
+ .description("run a schedule now, as its timer would, and wait for it")
222
+ .argument("<name>", "the schedule's name, as `schedule list` shows it")
223
+ .action(async function (name) {
224
+ const { context, system, state, say, timer } = prepare(this);
225
+ const last = await runSchedule(app, system, context.env, scheduleFile(state, scheduleName(name)), {
226
+ timer: timer(),
227
+ });
228
+ say(snakeKeys({ name, ...last }), [`${name}: exit ${last.exitCode} — ${last.output}`]);
229
+ if (last.exitCode !== 0)
230
+ setExitCode(this, 1);
231
+ });
232
+ grantsAuthority(annotate(command.command("remove"), {
233
+ effects: LOCAL_WRITE,
234
+ examples: [`${app.command} personal schedule remove health`],
235
+ }))
236
+ .description("stop a schedule and remove its timer; its past output stays")
237
+ .argument("<name>", "the schedule's name, as `schedule list` shows it")
238
+ .action(async function (name) {
239
+ const { state, say, timer } = prepare(this);
240
+ const file = scheduleFile(state, scheduleName(name));
241
+ if (!existsSync(file))
242
+ throw new CliError("not_found", `no schedule ${name} — \`${lineOf(this, "schedule list")}\``);
243
+ await timer().remove(name);
244
+ rmSync(file, { force: true });
245
+ say({ name, removed: true }, [`Removed ${name}.`]);
246
+ });
247
+ annotate(command.command("exec"), {
248
+ effects: REMOTE_WRITE,
249
+ examples: [`${app.command} schedule exec ~/.local/share/${app.appName}/schedules/health/schedule.json`],
250
+ })
251
+ .description("what a timer runs, given the schedule's file; to run one by hand, use run")
252
+ .argument("<file>", "the schedule's schedule.json in the state folder, as the timer passes it")
253
+ .action(async function (file) {
254
+ // A timer starts it with no profile named; the schedule's own environment carries the profile.
255
+ const { context, system, timer } = prepare(this, { needsProfile: false });
256
+ const last = await runSchedule(app, system, context.env, file, { timer: timer() });
257
+ if (last.exitCode !== 0)
258
+ setExitCode(this, 1);
259
+ });
260
+ return command;
261
+ };
@@ -765,7 +765,7 @@ export declare const stickersCommand: (messenger: import("./index.js").Messenger
765
765
  export declare const repliesCommand: (messenger: import("./index.js").Messenger) => import("commander").Command;
766
766
  export declare const reviewCommand: (messenger: import("./index.js").Messenger) => import("commander").Command;
767
767
  export declare const scheduleCommand: (messenger: import("./index.js").Messenger) => import("commander").Command;
768
- export declare const scheduleCommandFor: (args_0: import("./messenger/schedule-command.js").ScheduleHost) => import("commander").Command;
768
+ export declare const scheduleCommandFor: (args_0: import("./schedule/schedule-command.js").ScheduleHost) => import("commander").Command;
769
769
  export declare const searchCommand: (messenger: import("./index.js").Messenger, args_1?: {
770
770
  topics?: boolean;
771
771
  } | undefined) => import("commander").Command;
@@ -41,7 +41,7 @@ import { reactionsCommand as reactionsCommandDefinition } from "./messenger/reac
41
41
  import { callsCommand as callsCommandDefinition, stickersCommand as stickersCommandDefinition, } from "./messenger/records-command.js";
42
42
  import { repliesCommand as repliesCommandDefinition } from "./messenger/replies-command.js";
43
43
  import { reviewCommand as reviewCommandDefinition } from "./messenger/review.js";
44
- import { scheduleCommand as scheduleCommandDefinition, scheduleCommandFor as scheduleCommandForDefinition, } from "./messenger/schedule-command.js";
44
+ import { scheduleCommand as scheduleCommandDefinition } from "./messenger/schedule-command.js";
45
45
  import { searchCommand as searchCommandDefinition } from "./messenger/search-command.js";
46
46
  import { searchesCommand as searchesCommandDefinition } from "./messenger/searches-command.js";
47
47
  import { serveCommand as serveCommandDefinition } from "./messenger/serve-command.js";
@@ -52,6 +52,7 @@ import { tasksCommand as tasksCommandDefinition, tasksStatsCommand as tasksStats
52
52
  import { topicsCommand as topicsCommandDefinition } from "./messenger/topics-command.js";
53
53
  import { watchCommand as watchCommandDefinition } from "./messenger/watch-command.js";
54
54
  import { runsCommand as runsCommandDefinition } from "./runs/command.js";
55
+ import { scheduleCommandFor as scheduleCommandForDefinition } from "./schedule/schedule-command.js";
55
56
  import { sharedCommand as protectedCommand } from "./shared-contracts.js";
56
57
  import { skillCommand as skillCommandDefinition } from "./skill-command.js";
57
58
  import { upgradeCommand as upgradeCommandDefinition } from "./upgrade-command.js";
@@ -1,6 +1,14 @@
1
- import type { ProviderMetadata } from "./models.js";
1
+ import type { Attachment, ProviderMetadata } from "./models.js";
2
2
  /** Attachments that are never a file, so their messages cost no download request. */
3
3
  export declare const NOT_FILES: ReadonlySet<string>;
4
+ /** A file a message carries, as a search hit names it; `position` counts every attachment, from 1. */
5
+ export interface HitFile {
6
+ position: number;
7
+ kind: string;
8
+ name?: string;
9
+ size?: number;
10
+ }
11
+ export declare const hitFiles: (attachments: readonly Attachment[] | undefined) => HitFile[];
4
12
  /** The stored kinds `has:<kind>` matches: MAX stores a file as `file`, Telegram as `document`. */
5
13
  export declare const storedKinds: (kind: string) => string[];
6
14
  /** One file `messages download` saved, as the messenger described it. */
@@ -9,6 +9,18 @@ export const NOT_FILES = new Set([
9
9
  "call",
10
10
  "inline_keyboard",
11
11
  ]);
12
+ // The position is taken before the filter: the store numbers every attachment, and `--attachment` takes that number.
13
+ // A server search hit built by an adapter may come without attachments at all.
14
+ export const hitFiles = (attachments) => (attachments ?? []).flatMap(({ kind, name, size }, index) => NOT_FILES.has(kind)
15
+ ? []
16
+ : [
17
+ {
18
+ position: index + 1,
19
+ kind,
20
+ ...(name === undefined ? {} : { name }),
21
+ ...(size === undefined ? {} : { size }),
22
+ },
23
+ ]);
12
24
  /** The stored kinds `has:<kind>` matches: MAX stores a file as `file`, Telegram as `document`. */
13
25
  export const storedKinds = (kind) => (kind === "file" ? ["file", "document"] : [kind]);
14
26
  /**
@@ -1,3 +1,4 @@
1
+ import type { Attachment } from "./models.js";
1
2
  export type MessagePermalink = {
2
3
  url: string;
3
4
  access: "public" | "restricted" | "unknown";
@@ -7,9 +8,22 @@ export type MessagePermalink = {
7
8
  access: "unavailable";
8
9
  reason: "unsupported_chat" | "unsupported_provider" | "offline";
9
10
  };
11
+ /**
12
+ * A link to one file of the message, apart from the message's own. No messenger gives one yet: Telegram and
13
+ * WhatsApp have none, and MAX's are short-lived download addresses that hand the bytes to whoever holds them.
14
+ */
15
+ export interface FileLink {
16
+ position: number;
17
+ kind: string;
18
+ name?: string;
19
+ url: null;
20
+ reason: "unsupported_provider";
21
+ }
10
22
  export type MessageLink = MessagePermalink & {
11
23
  locator: string;
24
+ files?: FileLink[];
12
25
  };
26
+ export declare const fileLinks: (attachments: readonly Attachment[]) => FileLink[];
13
27
  export declare const messageLinkTarget: (reference: string, message: string | undefined, provider: string) => {
14
28
  chat: string;
15
29
  message: string;
@@ -1,5 +1,13 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
+ import { hitFiles } from "./attachments.js";
2
3
  import { isLocator, parseLocator } from "./locator.js";
4
+ export const fileLinks = (attachments) => hitFiles(attachments).map(({ position, kind, name }) => ({
5
+ position,
6
+ kind,
7
+ ...(name === undefined ? {} : { name }),
8
+ url: null,
9
+ reason: "unsupported_provider",
10
+ }));
3
11
  export const messageLinkTarget = (reference, message, provider) => {
4
12
  if (isLocator(reference)) {
5
13
  if (message !== undefined)
@@ -608,6 +608,8 @@ export interface TopicChange {
608
608
  pinned?: boolean;
609
609
  /** Only a forum's General topic can be hidden from the topic list. */
610
610
  hidden?: boolean;
611
+ /** An emoji from the messenger's topic icon set; `null` removes the icon. */
612
+ icon?: string | null;
611
613
  }
612
614
  /** Someone who asked to join a group or channel that needs an admin's approval. */
613
615
  export interface JoinRequest {
@@ -39,3 +39,10 @@ export declare const attachment: v.StrictObjectSchema<{
39
39
  readonly url: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
40
40
  }, undefined>, undefined>, undefined>, undefined>;
41
41
  }, undefined>;
42
+ /** A file named on a search hit: `position` is the one `attachments show` and `messages download` take. */
43
+ export declare const hitFile: v.StrictObjectSchema<{
44
+ readonly position: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
45
+ readonly kind: v.StringSchema<undefined>;
46
+ readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
47
+ readonly size: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
48
+ }, undefined>;
@@ -35,3 +35,5 @@ export const attachment = v.strictObject({
35
35
  url: v.optional(text),
36
36
  })))),
37
37
  });
38
+ /** A file named on a search hit: `position` is the one `attachments show` and `messages download` take. */
39
+ export const hitFile = v.strictObject({ position: count, kind: text, name: v.optional(text), size: v.optional(count) });
@@ -24,6 +24,13 @@ export declare const hit: v.StrictObjectSchema<{
24
24
  readonly scope: v.OptionalSchema<v.PicklistSchema<["transcript", "chat", "summary"], undefined>, undefined>;
25
25
  /** A meeting passage's stored line, so two passages of one meeting stay apart; `search meetings` gives the same. */
26
26
  readonly id: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
27
+ /** A message's files; absent when it has none. */
28
+ readonly attachments: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
29
+ readonly position: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
30
+ readonly kind: v.StringSchema<undefined>;
31
+ readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
32
+ readonly size: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
33
+ }, undefined>, undefined>, undefined>;
27
34
  /** Absent on a note hit: the owner wrote it. */
28
35
  readonly untrusted: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
29
36
  }, undefined>;
@@ -1,4 +1,5 @@
1
1
  import * as v from "valibot";
2
+ import { hitFile } from "./attachment.js";
2
3
  import { count, reference, text, time, untrusted } from "./fields.js";
3
4
  /** One result of `search all`: a message, a mail, a note, a calendar event or a meeting passage, best first. */
4
5
  export const hit = v.strictObject({
@@ -25,6 +26,8 @@ export const hit = v.strictObject({
25
26
  scope: v.optional(v.picklist(["transcript", "chat", "summary"])),
26
27
  /** A meeting passage's stored line, so two passages of one meeting stay apart; `search meetings` gives the same. */
27
28
  id: v.optional(v.pipe(v.string(), v.minLength(1))),
29
+ /** A message's files; absent when it has none. */
30
+ attachments: v.optional(v.array(hitFile)),
28
31
  /** Absent on a note hit: the owner wrote it. */
29
32
  untrusted,
30
33
  });
@@ -40,7 +40,7 @@ import { inviteLink } from "./invite-link.js";
40
40
  import { job } from "./job.js";
41
41
  import { joinRequest } from "./join-request.js";
42
42
  import { organization, project, reminder } from "./knowledge-lists.js";
43
- import { botAdmin, botMenuEntry, botProfile, botSeenChat, botTemplate, botWebhook, callRecord, scheduledJob, stickerInventory } from "./list-results.js";
43
+ import { botAdmin, botMenuEntry, botProfile, botSeenChat, botTemplate, botWebhook, callRecord, scheduledJob, stickerInventory, topicIcon } from "./list-results.js";
44
44
  import { messageEvent, serveSummary, watchedMessage, watchItem } from "./listener.js";
45
45
  import { mediaArchive, mediaArchiveCheck, mediaArchiveMove, mediaArchiveTimer } from "./media-archive.js";
46
46
  import { meeting } from "./meeting.js";
@@ -88,7 +88,7 @@ import { topic } from "./topic.js";
88
88
  * subset of its fields, never a renamed one. Commands move onto these one by one; until then, a schema
89
89
  * is the target, not a description of today's output.
90
90
  */
91
- export { account, accountChange, accountPrivacy, accountPrivacyChange, accountProfile, accountSession, adminReport, agenda, analysisConsent, analysisConsentRevocation, attachment, attachmentBytes, attachmentExtraction, attachmentTextChange, automaticTags, botAdmin, botMenuEntry, botProfile, botSeenChat, botTemplate, botWebhook, cachedChatMetadata, callRecord, chart, chat, chatAdminChange, chatArchive, chatEvent, chatFolderChange, chatFolderOrder, chatFolderRemoval, chatGroupChange, chatInspection, chatInvite, chatInviteChange, chatMembersChange, chatMembersFetch, chatMetadata, chatMetadataRefresh, chatModeration, chatMuting, chatPin, chatRead, chatRemoval, chatRequestChange, chatRules, chatSender, chatStats, chatTracking, chatUnread, contact, contactActivity, contactAlias, contactCheck, contactContext, contactImport, contactLookup, contactNote, contactNoteRemoval, contactPreparation, contactStateChange, contactSync, contactUpdate, conversation, conversationBatch, conversationBatchStatus, conversationBuildResult, conversationChange, conversationDetails, conversationEmbedding, conversationEmbeddingStatus, conversationHit, conversationPreparation, conversationReadiness, counterRefresh, counters, document, documentLink, endedAccountSessions, evidence, evidencePacket, fact, folder, hit, identity, imageEmbedding, inviteLink, job, joinRequest, linkedPerson, mediaArchive, mediaArchiveCheck, mediaArchiveMove, mediaArchiveTimer, meeting, meetingProjectCandidates, meetingSeriesInventory, member, memory, mention, mentionDecision, mentionDetails, mentionDomain, mentionRebuild, message, messageContext, messageCounts, messageDeletion, messageDownload, messageEvent, messageLink, messageLinks, messagePin, messageStar, messageSync, messageTranscript, messageWrite, model, modelKey, note, officialStats, organization, person, place, poll, pollChange, pollVote, project, ranking, reaction, reactionChange, recipient, recipientAdded, recipientChange, recipientOff, recipientRemoved, recordedRun, reference, relationship, reminder, removedModelKey, retention, runDetails, runHit, runLocation, savedSearch, scheduledJob, searchHistoryClear, sendAttempt, serveSummary, stickerInventory, storeBackup, storeChatStatus, storeCheck, storeClear, storeCopyRemoval, storeDecrypt, storedModelKey, storeExport, storeGapPlan, storeGapRepair, storeInfo, storeJobCancel, storeJobClear, storeJobDetails, storeJobRetry, storeMigration, storeReindex, storeRepair, storeReset, storeRestore, storeTable, tagChange, tagging, task, taskCard, taskCategories, taskClosed, taskCreated, taskLinks, taskMerge, taskPair, taskScan, taskSimilar, taskStats, taskUnmerge, topic, trackedChat, trackedChatHistory, watchedMessage, watchItem, };
91
+ export { account, accountChange, accountPrivacy, accountPrivacyChange, accountProfile, accountSession, adminReport, agenda, analysisConsent, analysisConsentRevocation, attachment, attachmentBytes, attachmentExtraction, attachmentTextChange, automaticTags, botAdmin, botMenuEntry, botProfile, botSeenChat, botTemplate, botWebhook, cachedChatMetadata, callRecord, chart, chat, chatAdminChange, chatArchive, chatEvent, chatFolderChange, chatFolderOrder, chatFolderRemoval, chatGroupChange, chatInspection, chatInvite, chatInviteChange, chatMembersChange, chatMembersFetch, chatMetadata, chatMetadataRefresh, chatModeration, chatMuting, chatPin, chatRead, chatRemoval, chatRequestChange, chatRules, chatSender, chatStats, chatTracking, chatUnread, contact, contactActivity, contactAlias, contactCheck, contactContext, contactImport, contactLookup, contactNote, contactNoteRemoval, contactPreparation, contactStateChange, contactSync, contactUpdate, conversation, conversationBatch, conversationBatchStatus, conversationBuildResult, conversationChange, conversationDetails, conversationEmbedding, conversationEmbeddingStatus, conversationHit, conversationPreparation, conversationReadiness, counterRefresh, counters, document, documentLink, endedAccountSessions, evidence, evidencePacket, fact, folder, hit, identity, imageEmbedding, inviteLink, job, joinRequest, linkedPerson, mediaArchive, mediaArchiveCheck, mediaArchiveMove, mediaArchiveTimer, meeting, meetingProjectCandidates, meetingSeriesInventory, member, memory, mention, mentionDecision, mentionDetails, mentionDomain, mentionRebuild, message, messageContext, messageCounts, messageDeletion, messageDownload, messageEvent, messageLink, messageLinks, messagePin, messageStar, messageSync, messageTranscript, messageWrite, model, modelKey, note, officialStats, organization, person, place, poll, pollChange, pollVote, project, ranking, reaction, reactionChange, recipient, recipientAdded, recipientChange, recipientOff, recipientRemoved, recordedRun, reference, relationship, reminder, removedModelKey, retention, runDetails, runHit, runLocation, savedSearch, scheduledJob, searchHistoryClear, sendAttempt, serveSummary, stickerInventory, storeBackup, storeChatStatus, storeCheck, storeClear, storeCopyRemoval, storeDecrypt, storedModelKey, storeExport, storeGapPlan, storeGapRepair, storeInfo, storeJobCancel, storeJobClear, storeJobDetails, storeJobRetry, storeMigration, storeReindex, storeRepair, storeReset, storeRestore, storeTable, tagChange, tagging, task, taskCard, taskCategories, taskClosed, taskCreated, taskLinks, taskMerge, taskPair, taskScan, taskSimilar, taskStats, taskUnmerge, topic, topicIcon, trackedChat, trackedChatHistory, watchedMessage, watchItem, };
92
92
  export declare const ENTITIES: {
93
93
  readonly meeting_ingestion: v.StrictObjectSchema<{
94
94
  readonly meetings: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
@@ -1231,6 +1231,13 @@ export declare const ENTITIES: {
1231
1231
  readonly demoted: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
1232
1232
  }, undefined>, undefined>;
1233
1233
  }, undefined>, undefined>;
1234
+ /** The files the message carries; absent when it has none. */
1235
+ readonly attachments: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
1236
+ readonly position: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
1237
+ readonly kind: v.StringSchema<undefined>;
1238
+ readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
1239
+ readonly size: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
1240
+ }, undefined>, undefined>, undefined>;
1234
1241
  readonly untrusted: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
1235
1242
  }, undefined>;
1236
1243
  readonly "message.summary": Omit<v.StrictObjectSchema<{
@@ -1895,6 +1902,13 @@ export declare const ENTITIES: {
1895
1902
  readonly url: v.NullableSchema<v.StringSchema<undefined>, undefined>;
1896
1903
  readonly access: v.PicklistSchema<["public", "restricted", "unknown", "unavailable"], undefined>;
1897
1904
  readonly reason: v.NullableSchema<v.PicklistSchema<["unsupported_chat", "unsupported_provider", "offline"], undefined>, undefined>;
1905
+ readonly files: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
1906
+ readonly position: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
1907
+ readonly kind: v.StringSchema<undefined>;
1908
+ readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
1909
+ readonly url: v.NullableSchema<v.StringSchema<undefined>, undefined>;
1910
+ readonly reason: v.NullableSchema<v.PicklistSchema<["unsupported_provider"], undefined>, undefined>;
1911
+ }, undefined>, undefined>, undefined>;
1898
1912
  }, undefined>;
1899
1913
  readonly message_write: v.StrictObjectSchema<{
1900
1914
  readonly operation_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
@@ -4441,6 +4455,9 @@ export declare const ENTITIES: {
4441
4455
  readonly name: v.StringSchema<undefined>;
4442
4456
  readonly description: v.NullableSchema<v.StringSchema<undefined>, undefined>;
4443
4457
  }, undefined>;
4458
+ readonly topic_icon: v.StrictObjectSchema<{
4459
+ readonly emoji: v.StringSchema<undefined>;
4460
+ }, undefined>;
4444
4461
  readonly bot_webhook: v.StrictObjectSchema<{
4445
4462
  readonly url: v.StringSchema<undefined>;
4446
4463
  readonly types: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
@@ -4551,6 +4568,12 @@ export declare const ENTITIES: {
4551
4568
  readonly offset_ms: v.OptionalSchema<v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>, undefined>;
4552
4569
  readonly scope: v.OptionalSchema<v.PicklistSchema<["transcript", "chat", "summary"], undefined>, undefined>;
4553
4570
  readonly id: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
4571
+ readonly attachments: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
4572
+ readonly position: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
4573
+ readonly kind: v.StringSchema<undefined>;
4574
+ readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
4575
+ readonly size: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
4576
+ }, undefined>, undefined>, undefined>;
4554
4577
  readonly untrusted: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
4555
4578
  }, undefined>;
4556
4579
  readonly document: v.StrictObjectSchema<{
@@ -4763,6 +4786,12 @@ export declare const ENTITIES: {
4763
4786
  readonly project: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
4764
4787
  readonly project_key: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
4765
4788
  readonly project_name: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
4789
+ readonly created_by: v.OptionalSchema<v.NullableSchema<v.StrictObjectSchema<{
4790
+ readonly type: v.PicklistSchema<["person", "bot"], undefined>;
4791
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
4792
+ readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
4793
+ readonly owner: v.BooleanSchema<undefined>;
4794
+ }, undefined>, undefined>, undefined>;
4766
4795
  readonly account: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
4767
4796
  readonly chat: v.OptionalSchema<v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>, undefined>;
4768
4797
  readonly source_message: v.OptionalSchema<v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>, undefined>;
@@ -4833,6 +4862,12 @@ export declare const ENTITIES: {
4833
4862
  readonly project: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
4834
4863
  readonly project_key: v.StringSchema<undefined>;
4835
4864
  readonly project_name: v.StringSchema<undefined>;
4865
+ readonly created_by: v.NullableSchema<v.StrictObjectSchema<{
4866
+ readonly type: v.PicklistSchema<["person", "bot"], undefined>;
4867
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
4868
+ readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
4869
+ readonly owner: v.BooleanSchema<undefined>;
4870
+ }, undefined>, undefined>;
4836
4871
  readonly title: v.StringSchema<undefined>;
4837
4872
  readonly description: v.NullableSchema<v.StringSchema<undefined>, undefined>;
4838
4873
  readonly type: v.PicklistSchema<readonly ["bug", "feature", "chore", "question", "request", "mention", "promise"], undefined>;
@@ -4939,6 +4974,12 @@ export declare const ENTITIES: {
4939
4974
  readonly project: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
4940
4975
  readonly project_key: v.StringSchema<undefined>;
4941
4976
  readonly project_name: v.StringSchema<undefined>;
4977
+ readonly created_by: v.NullableSchema<v.StrictObjectSchema<{
4978
+ readonly type: v.PicklistSchema<["person", "bot"], undefined>;
4979
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
4980
+ readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
4981
+ readonly owner: v.BooleanSchema<undefined>;
4982
+ }, undefined>, undefined>;
4942
4983
  readonly title: v.StringSchema<undefined>;
4943
4984
  readonly description: v.NullableSchema<v.StringSchema<undefined>, undefined>;
4944
4985
  readonly type: v.PicklistSchema<readonly ["bug", "feature", "chore", "question", "request", "mention", "promise"], undefined>;
@@ -5046,6 +5087,12 @@ export declare const ENTITIES: {
5046
5087
  readonly project: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
5047
5088
  readonly project_key: v.StringSchema<undefined>;
5048
5089
  readonly project_name: v.StringSchema<undefined>;
5090
+ readonly created_by: v.NullableSchema<v.StrictObjectSchema<{
5091
+ readonly type: v.PicklistSchema<["person", "bot"], undefined>;
5092
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
5093
+ readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5094
+ readonly owner: v.BooleanSchema<undefined>;
5095
+ }, undefined>, undefined>;
5049
5096
  readonly title: v.StringSchema<undefined>;
5050
5097
  readonly description: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5051
5098
  readonly type: v.PicklistSchema<readonly ["bug", "feature", "chore", "question", "request", "mention", "promise"], undefined>;
@@ -5181,6 +5228,12 @@ export declare const ENTITIES: {
5181
5228
  readonly project: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
5182
5229
  readonly project_key: v.StringSchema<undefined>;
5183
5230
  readonly project_name: v.StringSchema<undefined>;
5231
+ readonly created_by: v.NullableSchema<v.StrictObjectSchema<{
5232
+ readonly type: v.PicklistSchema<["person", "bot"], undefined>;
5233
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
5234
+ readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5235
+ readonly owner: v.BooleanSchema<undefined>;
5236
+ }, undefined>, undefined>;
5184
5237
  readonly title: v.StringSchema<undefined>;
5185
5238
  readonly description: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5186
5239
  readonly type: v.PicklistSchema<readonly ["bug", "feature", "chore", "question", "request", "mention", "promise"], undefined>;
@@ -5281,6 +5334,12 @@ export declare const ENTITIES: {
5281
5334
  readonly project: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
5282
5335
  readonly project_key: v.StringSchema<undefined>;
5283
5336
  readonly project_name: v.StringSchema<undefined>;
5337
+ readonly created_by: v.NullableSchema<v.StrictObjectSchema<{
5338
+ readonly type: v.PicklistSchema<["person", "bot"], undefined>;
5339
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
5340
+ readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5341
+ readonly owner: v.BooleanSchema<undefined>;
5342
+ }, undefined>, undefined>;
5284
5343
  readonly title: v.StringSchema<undefined>;
5285
5344
  readonly description: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5286
5345
  readonly type: v.PicklistSchema<readonly ["bug", "feature", "chore", "question", "request", "mention", "promise"], undefined>;