@wirecat/cli-core 0.409.0 → 0.411.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 (70) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/cli/bot/command.js +1 -0
  3. package/dist/cli/bot/port.d.ts +1 -1
  4. package/dist/cli/bot/setup.js +11 -5
  5. package/dist/cli/commands-command.js +18 -12
  6. package/dist/cli/contract-evidence.d.ts +26 -0
  7. package/dist/cli/contract-evidence.js +58 -0
  8. package/dist/cli/contract-templates.d.ts +4 -0
  9. package/dist/cli/contract-templates.js +83 -0
  10. package/dist/cli/discovery-descriptions.d.ts +4 -0
  11. package/dist/cli/discovery-descriptions.js +4 -0
  12. package/dist/cli/index.d.ts +22 -55
  13. package/dist/cli/index.js +19 -55
  14. package/dist/cli/messenger/chat-answers.d.ts +1 -1
  15. package/dist/cli/messenger/chat-answers.js +1 -1
  16. package/dist/cli/messenger/chat-lists.d.ts +1 -1
  17. package/dist/cli/messenger/chats-command.js +3 -0
  18. package/dist/cli/messenger/contacts-command.js +23 -15
  19. package/dist/cli/messenger/guard-commands.d.ts +5 -1
  20. package/dist/cli/messenger/guard-commands.js +5 -3
  21. package/dist/cli/messenger/messages-press-command.js +4 -1
  22. package/dist/cli/messenger/models-command.js +3 -11
  23. package/dist/cli/messenger/records-command.js +1 -1
  24. package/dist/cli/messenger/report.d.ts +21 -6
  25. package/dist/cli/messenger/report.js +20 -5
  26. package/dist/cli/messenger/tasks-command.js +14 -4
  27. package/dist/cli/model-catalogue-command.d.ts +15 -0
  28. package/dist/cli/model-catalogue-command.js +110 -0
  29. package/dist/cli/program.js +2 -0
  30. package/dist/cli/runs/search-command.js +2 -3
  31. package/dist/cli/runs/search-query.d.ts +1 -1
  32. package/dist/cli/runs/search-query.js +18 -2
  33. package/dist/cli/runs/search.d.ts +2 -1
  34. package/dist/cli/runs/search.js +4 -3
  35. package/dist/cli/shared-command-exports.d.ts +644 -0
  36. package/dist/cli/shared-command-exports.js +114 -0
  37. package/dist/cli/shared-contracts.d.ts +1 -0
  38. package/dist/cli/shared-contracts.js +1 -0
  39. package/dist/domain/attachments.js +1 -0
  40. package/dist/entities/chat.d.ts +3 -3
  41. package/dist/entities/chat.js +2 -2
  42. package/dist/entities/contact-context.d.ts +8 -8
  43. package/dist/entities/contact.d.ts +4 -4
  44. package/dist/entities/index.d.ts +11 -12
  45. package/dist/entities/list-results.d.ts +1 -1
  46. package/dist/entities/list-results.js +1 -1
  47. package/dist/entities/send-attempt.d.ts +0 -1
  48. package/dist/entities/send-attempt.js +0 -1
  49. package/dist/errors/reasons.d.ts +1 -1
  50. package/dist/errors/reasons.js +7 -0
  51. package/dist/meetings/cli/index.d.ts +1 -1
  52. package/dist/meetings/cli/index.js +25 -9
  53. package/dist/meetings/cli/passage.d.ts +0 -1
  54. package/dist/meetings/cli/passage.js +5 -3
  55. package/dist/meetings/cli/project-evidence.js +3 -2
  56. package/dist/meetings/cli/search.d.ts +2 -2
  57. package/dist/meetings/cli/search.js +14 -12
  58. package/dist/meetings/event-query.d.ts +1 -0
  59. package/dist/meetings/event-query.js +15 -0
  60. package/dist/meetings/index.d.ts +1 -0
  61. package/dist/meetings/index.js +1 -0
  62. package/dist/meetings/store.d.ts +2 -0
  63. package/dist/meetings/testing/memory.js +5 -0
  64. package/dist/services/tasks.d.ts +2 -0
  65. package/dist/services/tasks.js +6 -4
  66. package/dist/store/sqlite/events.d.ts +2 -1
  67. package/dist/store/sqlite/events.js +16 -1
  68. package/dist/store/sqlite/meetings.js +3 -0
  69. package/package.json +3 -3
  70. package/parity.json +61 -76
package/dist/cli/index.js CHANGED
@@ -32,106 +32,70 @@ export { openRequestTask } from "../services/task-rules.js";
32
32
  export { TASK_PAIR_QUERY, TASK_QUERY } from "../services/tasks.js";
33
33
  export { evaluateAgent, } from "./agent-evaluation.js";
34
34
  export { answerEntity } from "./answer-entity.js";
35
- export { botAdminsCommand, botMembersCommand } from "./bot/admins.js";
36
- export { generatedApiCommand } from "./bot/api.js";
37
35
  export { apiFlagOf, apiJson, apiOptionKey, apiPlainJson, checkApiBody, checkApiParameter, parseApiJson, readApiBody, } from "./bot/api-input.js";
38
36
  export { prepareRpcApiBody } from "./bot/api-rpc.js";
39
- export { botCommand } from "./bot/command.js";
40
37
  export { botContext, botWords } from "./bot/context.js";
41
38
  export { botCopy } from "./bot/copy.js";
42
- export { botMcpCommand } from "./bot/mcp.js";
43
- export { botCan, botIdOf, botMessagesCommand } from "./bot/messages.js";
39
+ export { botCan, botIdOf } from "./bot/messages.js";
44
40
  export { BOT_ACTIONS, } from "./bot/port.js";
45
41
  export { botFiles, botsDirectory, ChatRegistry, registryProfiles } from "./bot/registry.js";
46
42
  export { BotTokenStore } from "./bot/token.js";
47
43
  export { PressLog, UpdatesCursor } from "./bot/updates.js";
48
44
  export { commandContract, SCHEMA_DIALECT } from "./command-contract.js";
49
- export { CONTRACT, commandsCommand } from "./commands-command.js";
50
- export { configCommand, refuseUnknownKey } from "./config-command.js";
45
+ export { CONTRACT } from "./commands-command.js";
46
+ export { refuseUnknownKey } from "./config-command.js";
51
47
  export { loadSnakeCaseConfig } from "./config-keys.js";
52
48
  export { baseContext, environmentOf, outputFor, provide, secretInputOf, } from "./context.js";
49
+ export { addContractInfo, contractEvidence } from "./contract-evidence.js";
50
+ export { sharedCommandTemplate } from "./contract-templates.js";
53
51
  export { withDeadline } from "./deadline.js";
54
52
  export { DEFAULT_CONFIG, ensureDefaultConfig } from "./default-config.js";
53
+ export { DISCOVERY_DESCRIPTIONS } from "./discovery-descriptions.js";
55
54
  export { changesAuthority, changesAuthorityAt, changesAuthorityNow, destroysNothing, FETCH, grantsAuthority, isLogin, isOtherServiceCall, isStoreChange, judgedEffects, judgedTargets, LOCAL_READ, LOCAL_WRITE, logsInOrOut, NO_EFFECTS, ownerOnlyOption, REMOTE_READ, REMOTE_WRITE, reachesOtherService, refuseUnderReadOnly, } from "./effects.js";
56
55
  export { answerSchema, isBody, itemAnswer, itemOf, jsonlRecords, judgedAnswer, listOf, metaNow, printItem, renderAnswer, sealed, snakeKeys, snakeKeysKeeping, } from "./envelope.js";
57
56
  export { withAbort } from "./execution.js";
58
57
  export { askSetupQuestions, asksAtStart, LANGUAGES, setupAtStart, systemLanguage, } from "./first-run.js";
59
- export { checkGuide, checkStep, guideCommand, loadScenarios, MEMO_SCENARIOS, shellProblems, } from "./guide.js";
58
+ export { checkGuide, checkStep, loadScenarios, MEMO_SCENARIOS, shellProblems, } from "./guide.js";
60
59
  export { commandHelp, commandHelpMarkdown, commandHelpText, exampleReport, nextUsually, } from "./help/command-help.js";
61
60
  export { commandsText } from "./help/commands-text.js";
62
- export { firstScreen, installHelp, WIRECAT_TOOLS, } from "./help/first-screen.js";
61
+ export { firstScreen, installHelp, WIRECAT_TOOLS } from "./help/first-screen.js";
63
62
  export { inSection, SECTIONS } from "./help/sections.js";
64
63
  export { bufferedInput, inputPolicy, MAX_BUFFERED_INPUT, MAX_SECRET_INPUT, provideInputPolicy, } from "./input-policy.js";
65
64
  export { answerFields, checkJson, entityFields, liftJson, } from "./json-fields.js";
66
65
  export { commandPath, LIST_QUERY_GUIDE, listQuery, nativeField, nativeTerms, pagedList, pageOf, queryArgument, queryHelp, queryTerms, searchText, termField, timeBounds, typedQuery, unfilteredQuery, withQuery, } from "./list-query.js";
67
- export { addMeetingBatchCommand, formatMeetingBatch, prepareMeetingBatch } from "./meeting-batch.js";
66
+ export { formatMeetingBatch, prepareMeetingBatch } from "./meeting-batch.js";
68
67
  export { meetingEvidenceItem, meetingPersonContextItem } from "./meeting-evidence-answer.js";
69
68
  export { parseEvidenceFindingsJson, parseMeetingFindingsJson } from "./meeting-findings-input.js";
70
69
  export { meetingPreparationAnswer } from "./meeting-preparation-answer.js";
71
- export { addMeetingPreparationCommand } from "./meeting-preparation-command.js";
72
70
  export { meetingPreparationInput, readMeetingPreparation, } from "./meeting-preparation-read.js";
73
- export { addMeetingProjectCandidatesCommand } from "./meeting-project-candidates-command.js";
74
71
  export { readMeetingProjectCandidates } from "./meeting-project-candidates-read.js";
75
72
  export { meetingResultItem } from "./meeting-results.js";
76
- export { addMeetingSeriesInventoryCommand } from "./meeting-series-inventory-command.js";
77
73
  export { readMeetingSeriesInventory } from "./meeting-series-inventory-read.js";
78
- export { accountCommand } from "./messenger/account-command.js";
79
74
  export { accountFileFor, profilesHere, rememberAccount } from "./messenger/accounts.js";
80
- export { storeCommand } from "./messenger/archive-commands.js";
81
- export { attachmentsCommand } from "./messenger/attachments-command.js";
82
- export { chatsCommand } from "./messenger/chats-command.js";
83
- export { markReadCommand } from "./messenger/chats-read-command.js";
84
- export { completeCommand } from "./messenger/complete-command.js";
85
- export { contactsCommand } from "./messenger/contacts-command.js";
86
75
  export { connectFor, messengerContext, } from "./messenger/context.js";
87
- export { conversationsCommand } from "./messenger/conversations-command.js";
88
- export { doctorCommand } from "./messenger/doctor-command.js";
89
- export { floodCommand } from "./messenger/flood-command.js";
90
- export { recipientsCommand, sendsCommand } from "./messenger/guard-commands.js";
91
- export { imagesCommand } from "./messenger/images-command.js";
92
- export { inboxCommand } from "./messenger/inbox.js";
93
76
  export { COMMAND_NEEDS } from "./messenger/lacks.js";
94
- export { httpTokenFile, mcpCommand, serverEntry } from "./messenger/mcp-command.js";
95
- export { mentionsCommand } from "./messenger/mentions-command.js";
77
+ export { httpTokenFile, serverEntry } from "./messenger/mcp-command.js";
96
78
  export { messageItem } from "./messenger/message-items.js";
97
- export { messagesCommand, sendCommand } from "./messenger/messages-command.js";
98
- export { deleteCommand } from "./messenger/messages-delete-command.js";
99
- export { editCommand } from "./messenger/messages-edit-command.js";
100
- export { forwardCommand } from "./messenger/messages-forward-command.js";
101
- export { pinCommand, starCommands, unpinCommand } from "./messenger/messages-pin-command.js";
102
- export { metadataCommand } from "./messenger/metadata-command.js";
103
- export { modelsCommand } from "./messenger/models-command.js";
104
- export { pollsCommand } from "./messenger/polls-command.js";
105
79
  export { privateFiles, withSqliteSidecars } from "./messenger/private-files.js";
106
- export { reactionsCommand } from "./messenger/reactions-command.js";
107
- export { callsCommand, stickersCommand } from "./messenger/records-command.js";
108
- export { repliesCommand } from "./messenger/replies-command.js";
109
- export { reviewCommand } from "./messenger/review.js";
110
- export { scheduleCommand } from "./messenger/schedule-command.js";
80
+ export { buildReport as buildDoctorReport, reportFileName as doctorReportFileName, } from "./messenger/report.js";
111
81
  export { allAnswer, allHitItem, messageHitItem, noteHitItem, } from "./messenger/search-answers.js";
112
- export { searchCommand } from "./messenger/search-command.js";
113
- export { SEARCHES_QUERY, searchesCommand } from "./messenger/searches-command.js";
114
- export { serveCommand, servingProfiles } from "./messenger/serve-command.js";
82
+ export { SEARCHES_QUERY } from "./messenger/searches-command.js";
83
+ export { servingProfiles } from "./messenger/serve-command.js";
115
84
  export { DAY_MS, FIRST_FETCH_MS, memberFetches } from "./messenger/serve-members.js";
116
85
  export { STEMS_EVERY_MS, STEMS_SLICE_MS, stemFills } from "./messenger/serve-stems.js";
117
- export { serverCommand, } from "./messenger/server-command.js";
118
- export { statsCommand } from "./messenger/stats-command.js";
119
86
  export { storeSummary } from "./messenger/store-maintenance-command.js";
120
87
  export { stored } from "./messenger/stored.js";
121
- export { tagsCommand } from "./messenger/tags-command.js";
122
- export { tasksCommand, tasksStatsCommand } from "./messenger/tasks-command.js";
123
- export { topicsCommand } from "./messenger/topics-command.js";
124
- export { watchCommand } from "./messenger/watch-command.js";
88
+ export { MODEL_CATALOGUE_QUERY } from "./model-catalogue-command.js";
125
89
  export { window, withPaging } from "./paging.js";
126
- export { knownBeside, knownPermissionKeys, unknownPermissionKeys, } from "./permission-keys.js";
127
- export { asFirstWord, commandWords, liftProfile, refuseCommandName, rootOf, usableProfileName, } from "./profile.js";
90
+ export { knownBeside, knownPermissionKeys, unknownPermissionKeys } from "./permission-keys.js";
91
+ export { asFirstWord, commandWords, liftProfile, refuseCommandName, rootOf, usableProfileName } from "./profile.js";
128
92
  export { createProgram, run } from "./program.js";
129
93
  export { fieldsOf, projectFields } from "./result-fields.js";
130
- export { runsCommand } from "./runs/command.js";
131
94
  export { count, first, flag, fromFile, NONE_NAMED, plain, settingsFor, } from "./settings.js";
132
95
  export { readSetupAnswers, setupPermissionCeiling } from "./setup-answers.js";
133
96
  export { AGENTS, agentFor, screenFor } from "./setup-screen.js";
134
- export { skillCommand } from "./skill-command.js";
97
+ export { accountCommand, addMeetingBatchCommand, addMeetingPreparationCommand, addMeetingProjectCandidatesCommand, addMeetingSeriesInventoryCommand, attachmentsCommand, botAdminsCommand, botCommand, botMcpCommand, botMembersCommand, botMessagesCommand, callsCommand, chatsCommand, commandsCommand, completeCommand, configCommand, contactsCommand, conversationsCommand, deleteCommand, doctorCommand, editCommand, floodCommand, forwardCommand, generatedApiCommand, guideCommand, imagesCommand, inboxCommand, markReadCommand, mcpCommand, mentionsCommand, messagesCommand, metadataCommand, modelCatalogueCommand, modelsCommand, pinCommand, pollsCommand, reactionsCommand, recipientsCommand, repliesCommand, reviewCommand, runsCommand, scheduleCommand, searchCommand, searchesCommand, sendCommand, sendsCommand, serveCommand, serverCommand, skillCommand, starCommands, statsCommand, stickersCommand, storeCommand, tagsCommand, tasksCommand, tasksStatsCommand, topicsCommand, unpinCommand, upgradeCommand, watchCommand, } from "./shared-command-exports.js";
98
+ export { assertSharedContracts, customizeSharedCommand, hasSharedContract, overrideSharedContract, sharedContractOverrides, sharedContracts, } from "./shared-contracts.js";
135
99
  export { runnable, withSafety } from "./steps.js";
136
100
  export { changeStoreSetting, isStoreSetting, STORE_SETTINGS, storeSettings } from "./store-settings.js";
137
- export { restartServers, upgradeCommand } from "./upgrade-command.js";
101
+ export { restartServers } from "./upgrade-command.js";
@@ -101,7 +101,7 @@ export declare const chatShown: (account: AccountKey, card: Parameters<typeof ch
101
101
  check?: {
102
102
  [x: string]: unknown;
103
103
  } | undefined;
104
- }[] | null | undefined;
104
+ }[] | undefined;
105
105
  }>;
106
106
  /** The lowest score `score>=n` asks for; the audit's own floor when the query names none. */
107
107
  export declare const scoreFloor: (ast: QueryAst | undefined) => number;
@@ -38,7 +38,7 @@ export const chatAnswer = (account, chat) => defined({
38
38
  description: chat.description,
39
39
  invite_link: chat.link,
40
40
  settings: chat.settings ? settingsOf(chat.settings) : undefined,
41
- members: chat.members === undefined ? undefined : (chat.members?.map(memberAnswer) ?? null),
41
+ members: chat.members?.map(memberAnswer),
42
42
  });
43
43
  export const memberAnswer = (member) => {
44
44
  const group = member;
@@ -61,7 +61,7 @@ export declare const chatsList: (services: Services, query: QueryWords, paging:
61
61
  check?: {
62
62
  [x: string]: unknown;
63
63
  } | undefined;
64
- }[] | null | undefined;
64
+ }[] | undefined;
65
65
  }>>;
66
66
  export declare const chatEvents: (services: Services, chat: string, query: QueryWords) => Promise<import("../envelope.js").ListBody<{
67
67
  chat_id: string;
@@ -109,6 +109,8 @@ export const chatsCommand = (messenger) => {
109
109
  "admins add": `"Book club" "Alex Example"${messenger.pickAdminRights === false ? "" : ` --can ${messenger.adminRights?.[0] ?? "info"}`}`,
110
110
  "admins remove": '"Book club" "Alex Example"',
111
111
  create: '"Book club" "Alex Example"',
112
+ app: '"Example Bot"',
113
+ start: '"Example Bot"',
112
114
  "documents list": '"Project room" --limit 10',
113
115
  events: '"Book club"',
114
116
  "folders create": '"Reading" --chat "Book club"',
@@ -125,6 +127,7 @@ export const chatsCommand = (messenger) => {
125
127
  "links list": '"Book club"',
126
128
  "links reset": '"Book club"',
127
129
  "links remove": '"Book club" https://example.com/invite/book-club',
130
+ "links update": '"Book club" https://example.com/invite/book-club --max-uses 25',
128
131
  "links show": '"Book club"',
129
132
  list: "--limit 10",
130
133
  "mark-read": '"Book club" --until-id 101',
@@ -1,5 +1,6 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { annotate } from "@wirecat/cli-base/commands";
3
+ import { defineQuery } from "@wirecat/cli-base/query";
3
4
  import { Command } from "commander";
4
5
  import { REGISTRIES, registriesCover } from "../../botcheck/registries.js";
5
6
  import { levelFor } from "../../sends/permissions.js";
@@ -12,7 +13,7 @@ import { readSecret } from "../../terminal/prompt.js";
12
13
  import { secretInputOf } from "../context.js";
13
14
  import { FETCH, LOCAL_WRITE, REMOTE_READ } from "../effects.js";
14
15
  import { itemOf, listOf, renderAnswer, snakeKeys } from "../envelope.js";
15
- import { listQuery, nativeTerms, typedQuery, withQuery } from "../list-query.js";
16
+ import { listQuery, nativeField, nativeTerms, termField, timeBounds, typedQuery, withQuery } from "../list-query.js";
16
17
  import { positiveCount, window, withPaging } from "../paging.js";
17
18
  import { casKey } from "../registry-keys.js";
18
19
  import { commandAt, nextStep } from "../steps.js";
@@ -23,6 +24,19 @@ import { hideNever } from "./lacks.js";
23
24
  import { CONTACTS_QUERY, notesAllowed } from "./people-queries.js";
24
25
  import { privatePeopleCommands } from "./private-people-command.js";
25
26
  import { syncsFirst } from "./search-sync-options.js";
27
+ const CONTACT_ACTIVITY_QUERY = defineQuery({
28
+ entity: "contact_activity",
29
+ backend: "native",
30
+ fields: [
31
+ termField("scope", "enum", "scope:work", { values: SCOPES }),
32
+ nativeField({
33
+ name: "at",
34
+ type: "time",
35
+ operators: ["term", "phrase", "range"],
36
+ example: "at:[2026-10-01 TO 2026-10-31]",
37
+ }),
38
+ ],
39
+ });
26
40
  /** People this account has a one-to-one chat with, as the people service counts them. */
27
41
  const collect = (value, previous = []) => [...previous, value];
28
42
  export const contactsCommand = (messenger) => {
@@ -173,28 +187,24 @@ export const contactsCommand = (messenger) => {
173
187
  meta: { ...window.meta, ...(hasMore ? { truncated: true } : {}) },
174
188
  }));
175
189
  });
176
- annotate(contacts.command("events"), {
190
+ withQuery(annotate(contacts.command("events"), {
177
191
  effects: REMOTE_READ,
178
192
  output: { entity: "contact_activity" },
179
193
  })
180
194
  .summary("one person's activity across their linked identities")
181
195
  .description("everything one person took part in, in every messenger linked to them — messages they wrote or were " +
182
196
  "mentioned in, chats, mail, meetings, tasks — newest first, from the store; never connects")
183
- .argument("<person>", "their id, @username, or part of their name")
184
- .option("--scope <scope>", "only what belongs to personal or to work accounts: personal or work")
185
- .option("--since-time <time>", "nothing older than this ISO 8601 time, or 2h / 1d ago")
186
- .option("--until-time <time>", "through this ISO 8601 time, or 2h / 1d ago")
197
+ .argument("<person>", "their id, @username, or part of their name"), CONTACT_ACTIVITY_QUERY)
187
198
  .option("--limit <n>", `at most this many; ${TIMELINE_ITEMS} if not given`, positiveCount("--limit"))
188
199
  .option("--cursor <cursor>", "go on from where the last page stopped; page.next gives it, with the same filters")
189
- .action(async function (person) {
190
- const { scope, sinceTime, untilTime, limit, cursor } = this.opts();
200
+ .action(async function (person, words) {
201
+ const terms = nativeTerms(listQuery(typedQuery(words), CONTACT_ACTIVITY_QUERY, "contacts events"), "contacts events");
202
+ const scope = terms.get("scope")?.value.toLowerCase();
203
+ const { limit, cursor } = this.opts();
191
204
  if (scope !== undefined && !SCOPES.includes(scope))
192
205
  throw new CliError("validation_error", `--scope is personal or work, not "${scope}"`);
193
206
  const context = messengerContext(this, messenger);
194
- const window = dateWindow({
195
- ...(sinceTime === undefined ? {} : { sinceTime }),
196
- ...(untilTime === undefined ? {} : { untilTime }),
197
- });
207
+ const window = timeBounds(terms.get("at"));
198
208
  const found = await context.withServices((services) => services.people.timeline(person, {
199
209
  ...(scope === undefined ? {} : { scope: scope }),
200
210
  ...(window.since === undefined ? {} : { since: window.since }),
@@ -211,9 +221,7 @@ export const contactsCommand = (messenger) => {
211
221
  ...(found.cursor === undefined ? {} : { cursor: found.cursor }),
212
222
  args: {
213
223
  person,
214
- ...(scope === undefined ? {} : { scope }),
215
- ...(sinceTime === undefined ? {} : { since_time: sinceTime }),
216
- ...(untilTime === undefined ? {} : { until_time: untilTime }),
224
+ ...(words.length ? { query: typedQuery(words) } : {}),
217
225
  },
218
226
  meta: { ...window.meta, ...(pending > 0 ? { truncated: true } : {}) },
219
227
  }));
@@ -1,9 +1,13 @@
1
1
  import { Command } from "commander";
2
+ import { type SendEntry } from "../../sends/journal.js";
2
3
  import { type Messenger } from "./context.js";
3
4
  /**
4
5
  * The chats this profile may send to. Off until the first `add`; `clear` deletes it, which turns it off again. It stops
5
6
  * a model that a message talked into writing somewhere, not an agent set on getting around it.
6
7
  */
7
- export declare const recipientsCommand: (messenger: Messenger) => Command;
8
+ export declare const recipientsCommand: (messenger: Messenger, options?: {
9
+ resolve?: "chat-card";
10
+ }) => Command;
8
11
  /** Every attempt to send from this profile, kept whatever `--record` says. Never the text. */
9
12
  export declare const sendsCommand: (messenger: Messenger) => Command;
13
+ export declare const sendAttemptItem: ({ sendId, operationId, ...entry }: SendEntry) => unknown;
@@ -15,7 +15,7 @@ import { SENDS_QUERY, sendFilter } from "./sends-query.js";
15
15
  * The chats this profile may send to. Off until the first `add`; `clear` deletes it, which turns it off again. It stops
16
16
  * a model that a message talked into writing somewhere, not an agent set on getting around it.
17
17
  */
18
- export const recipientsCommand = (messenger) => {
18
+ export const recipientsCommand = (messenger, options = {}) => {
19
19
  const tool = `${messenger.app.command} personal`;
20
20
  const listFor = (profile, env) => new RecipientList(recipientsPathFor(messenger.app, profile, env), messenger.app.command);
21
21
  const command = new Command("recipients").description("the chats this profile may send to, when the list is on");
@@ -43,7 +43,8 @@ export const recipientsCommand = (messenger) => {
43
43
  .description("allow sending to this chat; the first add turns the list on")
44
44
  .action(async function (chat) {
45
45
  const context = messengerContext(this, messenger);
46
- const found = await context.withMessenger((connection) => connection.resolve(chat));
46
+ // Some transports resolve an id without reading its title or dialog partner.
47
+ const found = await context.withMessenger((connection) => options.resolve === "chat-card" ? connection.chat(chat) : connection.resolve(chat));
47
48
  const partnerId = messenger.partnerOf?.(found);
48
49
  const added = await listFor(context.settings.profile, context.env).addAsync({
49
50
  id: found.id,
@@ -93,10 +94,11 @@ export const sendsCommand = (messenger) => new Command("sends").description("eve
93
94
  const from = settings.all ? 0 : (settings.page - 1) * settings.limit;
94
95
  const shown = settings.all ? entries : entries.slice(from, from + settings.limit);
95
96
  renderAnswer(this, context, pagedList("sends list", settings, {
96
- items: shown.map(snakeKeys),
97
+ items: shown.map(sendAttemptItem),
97
98
  args: words.length ? { query: typedQuery(words) } : {},
98
99
  has_more: from + shown.length < entries.length,
99
100
  }));
100
101
  if (entries.length === 0)
101
102
  renderer.note(filter ? "no send attempts match the query" : `profile ${settings.profile} has not tried to send anything`);
102
103
  }));
104
+ export const sendAttemptItem = ({ sendId, operationId, ...entry }) => snakeKeys({ ...entry, ...((operationId ?? sendId) === undefined ? {} : { operationId: operationId ?? sendId }) });
@@ -5,7 +5,10 @@ import { REMOTE_WRITE } from "../effects.js";
5
5
  import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
6
6
  import { messengerContext } from "./context.js";
7
7
  /** `messages press` — one of a bot's callback buttons; the bot sees who pressed it. */
8
- export const pressCommand = (messenger) => annotate(new Command("press"), { effects: REMOTE_WRITE })
8
+ export const pressCommand = (messenger) => annotate(new Command("press"), {
9
+ effects: REMOTE_WRITE,
10
+ examples: [`${messenger.app.command} personal messages press "Example Bot" 101 1`],
11
+ })
9
12
  .description("press a bot's button under a message; the bot sees that you pressed it")
10
13
  .argument("<chat>", messenger.chatArgument)
11
14
  .argument("<message>", "the id of the message with the buttons")
@@ -1,7 +1,6 @@
1
1
  import { join } from "node:path";
2
2
  import { CliError } from "@wirecat/cli-base";
3
3
  import { annotate } from "@wirecat/cli-base/commands";
4
- import { defineQuery } from "@wirecat/cli-base/query";
5
4
  import { Command } from "commander";
6
5
  import { isTextModelInstalled, textModelsDirectory } from "../../embeddings/embed.js";
7
6
  import { embeddingKeys } from "../../embeddings/keys.js";
@@ -16,19 +15,12 @@ import { readSecret } from "../../terminal/prompt.js";
16
15
  import { baseContext, environmentOf } from "../context.js";
17
16
  import { FETCH, grantsAuthority, LOCAL_READ, LOCAL_WRITE, reachesOtherService } from "../effects.js";
18
17
  import { itemOf, listOf, renderAnswer } from "../envelope.js";
19
- import { listQuery, nativeTerms, termField, typedQuery, unfilteredQuery, withQuery } from "../list-query.js";
18
+ import { listQuery, nativeTerms, typedQuery, unfilteredQuery, withQuery } from "../list-query.js";
19
+ import { MODEL_CATALOGUE_QUERY } from "../model-catalogue-command.js";
20
20
  import { nextStep } from "../steps.js";
21
21
  /** A model list is the catalog this version ships. */
22
22
  const MODELS_QUERY = unfilteredQuery("model", modelEntity);
23
- const ROOT_MODELS_QUERY = defineQuery({
24
- entity: "model",
25
- backend: "native",
26
- fields: [
27
- termField("kind", "enum", "kind:audio", { values: ["audio", "text"] }),
28
- termField("id", "kw", "id:e5-small"),
29
- termField("downloaded", "bool", "downloaded:true"),
30
- ],
31
- });
23
+ const ROOT_MODELS_QUERY = MODEL_CATALOGUE_QUERY;
32
24
  /** Every model this version knows: a short, fixed list, never paged. */
33
25
  const everyModel = (command, items, next = []) => listOf(command, { items, limit: Math.max(1, items.length), has_more: false, next });
34
26
  const audioItem = (model, directory, chosen) => ({
@@ -22,7 +22,7 @@ const kindsOf = (typed) => {
22
22
  export const mediaCommand = (messenger) => annotate(new Command("media"), { effects: REMOTE_READ })
23
23
  .description("a chat's photos, videos, files, audio and links, from the messenger's server; reading marks nothing")
24
24
  .argument("<chat>", messenger.chatArgument)
25
- .option("--type <names>", `only these kinds, comma-separated: ${MEDIA_KINDS.join(", ")}`)
25
+ .option("--type <type>", `only these kinds, comma-separated: ${MEDIA_KINDS.join(", ")}`)
26
26
  .option("--limit <n>", "how many to show", positiveCount("--limit"))
27
27
  .option("--cursor <cursor>", "go on from where page.next says the last page stopped")
28
28
  .action(async function (chat) {
@@ -1,9 +1,9 @@
1
1
  import { type RunMetadata } from "../../runs/run.js";
2
2
  import type { SendEntry } from "../../sends/journal.js";
3
- export interface Report {
3
+ export interface Report<Doctor extends object = Record<string, unknown>> {
4
4
  createdAt: string;
5
5
  /** What `doctor --json` answers — versions, paths, counts and the messenger's own checks. */
6
- doctor: Record<string, unknown>;
6
+ doctor: Doctor;
7
7
  /** The run the report is about: the one asked for, or the newest that failed. */
8
8
  run: {
9
9
  metadata: RunMetadata;
@@ -12,13 +12,25 @@ export interface Report {
12
12
  /** The newest write attempts — outcomes and ids, never text (`src/sends/journal.ts`). */
13
13
  sends: SendEntry[];
14
14
  }
15
+ /** Optional technical context supplied by a consumer; it is redacted with the rest of the report. */
16
+ export interface ReportContext {
17
+ createdAt?: never;
18
+ doctor?: never;
19
+ run?: never;
20
+ sends?: never;
21
+ version?: string;
22
+ runtime?: string;
23
+ platform?: string;
24
+ arch?: string;
25
+ profile?: string | null;
26
+ }
15
27
  /**
16
28
  * **Everything a report holds is already free of content** — the doctor, the run log and the send
17
29
  * journal are built that way. Two things are added: every id becomes a label, and the home
18
- * directory is hidden, since paths name the person's account on this machine. Copied from max-cli.
30
+ * directory is hidden, since paths name the person's account on this machine.
19
31
  */
20
- export declare const buildReport: ({ doctor, runsDir, runId, command, sends, now, home, }: {
21
- doctor: Record<string, unknown>;
32
+ export declare const buildReport: <Doctor extends object, Context extends ReportContext = Record<never, never>>({ doctor, runsDir, runId, command, sends, now, home, context, eventFields, }: {
33
+ doctor: Doctor;
22
34
  runsDir: string;
23
35
  runId?: string;
24
36
  /** What to type to list runs, for the error when `runId` names none. */
@@ -26,7 +38,10 @@ export declare const buildReport: ({ doctor, runsDir, runId, command, sends, now
26
38
  sends: SendEntry[];
27
39
  now?: Date;
28
40
  home: string | undefined;
29
- }) => Report;
41
+ context?: Context;
42
+ /** Retain allowlisted recorded fields, including time and severity, for an existing consumer contract. */
43
+ eventFields?: "report" | "recorded";
44
+ }) => Report<Doctor> & Omit<Context, keyof Report>;
30
45
  /**
31
46
  * Every field of a journal line that names a chat, a message or a write. A send's `operationId` is
32
47
  * its `sendId`, so one left raw would undo the other's label.
@@ -1,6 +1,7 @@
1
1
  import { createHash, randomBytes } from "node:crypto";
2
2
  import { join } from "node:path";
3
3
  import { CliError } from "@wirecat/cli-base";
4
+ import { LOG_ATTRIBUTES } from "../../runs/log-attributes.js";
4
5
  import { findRun, listRuns, readEvents } from "../../runs/run.js";
5
6
  const RECENT_SENDS = 20;
6
7
  /** Only these fields of a run event travel; a field added later is left out until it is named here. */
@@ -18,21 +19,22 @@ const EVENT_FIELDS = [
18
19
  /**
19
20
  * **Everything a report holds is already free of content** — the doctor, the run log and the send
20
21
  * journal are built that way. Two things are added: every id becomes a label, and the home
21
- * directory is hidden, since paths name the person's account on this machine. Copied from max-cli.
22
+ * directory is hidden, since paths name the person's account on this machine.
22
23
  */
23
- export const buildReport = ({ doctor, runsDir, runId, command, sends, now = new Date(), home, }) => {
24
+ export const buildReport = ({ doctor, runsDir, runId, command, sends, now = new Date(), home, context, eventFields = "report", }) => {
24
25
  const chosen = runId === undefined ? newestFailed(runsDir) : findRun(runsDir, runId);
25
26
  if (runId !== undefined && !chosen)
26
27
  throw new CliError("not_found", `no run ${runId} — \`${command} runs list\` names them`);
27
28
  const label = labeller();
28
29
  const account = doctor.account;
29
30
  const report = {
31
+ ...context,
30
32
  createdAt: now.toISOString(),
31
33
  doctor: {
32
34
  ...doctor,
33
35
  ...(account?.remembered ? { account: { ...account, remembered: label(account.remembered) } } : {}),
34
36
  },
35
- run: chosen ? { metadata: chosen.metadata, events: readEvents(chosen.dir).map(eventOf(label)) } : null,
37
+ run: chosen ? { metadata: chosen.metadata, events: readEvents(chosen.dir).map(eventOf(label, eventFields)) } : null,
36
38
  sends: sends.slice(-RECENT_SENDS).map((entry) => labelled(entry, label)),
37
39
  };
38
40
  return JSON.parse(hideHome(JSON.stringify(report), home));
@@ -85,8 +87,21 @@ const labeller = () => {
85
87
  const salt = randomBytes(16);
86
88
  return (id) => `id:${createHash("sha256").update(salt).update(id).digest("hex").slice(0, 12)}`;
87
89
  };
88
- const eventOf = (label) => (event) => {
89
- const kept = Object.fromEntries(Object.entries(event).filter(([key]) => EVENT_FIELDS.includes(key)));
90
+ const RECORDED_FIELDS = new Set([...LOG_ATTRIBUTES, "time", "severity"]);
91
+ const RECORDED_ID_FIELDS = new Set([
92
+ ...SEND_ID_FIELDS.map((key) => key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)),
93
+ "actor_id",
94
+ "target_id",
95
+ ]);
96
+ const eventOf = (label, fields) => (event) => {
97
+ const kept = Object.fromEntries(Object.entries(event)
98
+ .filter(([key]) => (fields === "recorded" ? RECORDED_FIELDS.has(key) : EVENT_FIELDS.includes(key)))
99
+ .map(([key, value]) => [
100
+ key,
101
+ fields === "recorded" && RECORDED_ID_FIELDS.has(key) && value !== undefined && value !== null
102
+ ? label(String(value))
103
+ : value,
104
+ ]));
90
105
  const ids = kept.ids;
91
106
  if (typeof ids !== "object" || ids === null)
92
107
  return kept;
@@ -108,6 +108,10 @@ const holderOf = (command) => holderIn(environmentOf(command).env ?? process.env
108
108
  const HOLDER_HELP = "who you are for claims: a session id or a chat id; default WIRECAT_HOLDER, else Claude Code's session, else the agent's name";
109
109
  const planFrom = (options) => {
110
110
  const plan = {};
111
+ if (options.priority !== undefined)
112
+ plan.priority = options.priority;
113
+ if (options.parent !== undefined)
114
+ plan.parent = options.parent;
111
115
  if (options.category !== undefined)
112
116
  plan.category = options.category;
113
117
  if (options.title !== undefined)
@@ -190,15 +194,21 @@ export const tasksCommand = (messenger) => {
190
194
  effects: LOCAL_WRITE,
191
195
  examples: [`${tool} personal tasks update DOC-12 --title "Review draft"`],
192
196
  }))
193
- .summary("set a task's title, description, due date, next check, category and assignee")
194
- .description("set a task's title, description, due date, next check, category and assignee; nothing reaches the messenger")
197
+ .summary("update a task's text, schedule, category, assignees, priority or parent")
198
+ .description("update a task's text, schedule, category, assignees, priority or parent; nothing reaches the messenger")
195
199
  .argument("<task>", "the task's id or key, as tasks list shows it")
196
- .option("--category <name>", `${categoryHelp}; a change is kept in the task's history`), "send Alex the API token").action(async function (id) {
200
+ .option("--category <name>", `${categoryHelp}; a change is kept in the task's history`)
201
+ .option("--priority <n>", "priority from 0 (urgent) through 4 (low)", (value) => {
202
+ if (!/^[0-4]$/.test(value))
203
+ throw new CliError("validation_error", "--priority takes 0 through 4");
204
+ return Number(value);
205
+ })
206
+ .option("--parent <id>", "parent task in the same project; none clears it", (value) => value === "none" ? null : value), "send Alex the API token").action(async function (id) {
197
207
  const context = messengerContext(this, messenger);
198
208
  refuseLocalWrite(context, messenger.app.command, "tasks.update");
199
209
  const plan = planFrom(this.opts());
200
210
  if (Object.keys(plan).length === 0)
201
- throw new CliError("validation_error", "give at least one of --title, --description, --due, --remind-time, --category");
211
+ throw new CliError("validation_error", "give at least one of --title, --description, --due, --remind-time, --category, --priority, --parent");
202
212
  const planned = await context.withServices((services) => services.tasks.plan(id, plan, writerOf(this)));
203
213
  renderAnswer(this, context, itemOf(snakeKeys(planned)), () => `${card(planned)}\n`);
204
214
  });
@@ -0,0 +1,15 @@
1
+ import { Command } from "commander";
2
+ import type { AppIdentity } from "../ports/app.js";
3
+ import { type Body } from "./envelope.js";
4
+ export declare const MODEL_CATALOGUE_QUERY: import("@wirecat/cli-base/query").QueryRegistry;
5
+ export interface ModelCatalogueOptions {
6
+ kinds: readonly ("audio" | "text")[];
7
+ env: () => NodeJS.ProcessEnv;
8
+ output: (answer: Body, command: Command) => void;
9
+ diagnostic: (line: string) => void;
10
+ jsonOption?: boolean;
11
+ offline?: () => boolean;
12
+ defaultAudio?: () => string;
13
+ }
14
+ /** Provider composition supplies I/O and supported kinds; parsing, receipts and download policy stay shared. */
15
+ export declare const modelCatalogueCommand: (app: AppIdentity, options: ModelCatalogueOptions) => Command;