@wirecat/cli-core 0.562.0 → 0.564.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 (142) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/dist/calendar/mcp/index.d.ts +12 -12
  3. package/dist/cli/config-command.js +18 -1
  4. package/dist/cli/envelope.js +13 -1
  5. package/dist/cli/execution.js +1 -0
  6. package/dist/cli/messenger/archive-commands.js +17 -0
  7. package/dist/cli/messenger/catch-up-answers.d.ts +5 -0
  8. package/dist/cli/messenger/catch-up-answers.js +3 -0
  9. package/dist/cli/messenger/reply-stats-command.d.ts +4 -0
  10. package/dist/cli/messenger/reply-stats-command.js +57 -0
  11. package/dist/cli/messenger/search-answers.d.ts +1 -0
  12. package/dist/cli/messenger/search-answers.js +1 -1
  13. package/dist/cli/messenger/search-command.js +8 -0
  14. package/dist/cli/messenger/stats-answers.d.ts +6 -3
  15. package/dist/cli/messenger/stats-answers.js +39 -9
  16. package/dist/cli/messenger/stats-command.js +5 -0
  17. package/dist/cli/messenger/tasks-command.js +35 -14
  18. package/dist/cli/output-budget.d.ts +26 -0
  19. package/dist/cli/output-budget.js +73 -0
  20. package/dist/cli/program.js +14 -1
  21. package/dist/cli/settings.d.ts +5 -0
  22. package/dist/cli/settings.js +88 -5
  23. package/dist/cli/shared-command-exports.d.ts +8 -0
  24. package/dist/conversations/mail-text.d.ts +6 -0
  25. package/dist/conversations/mail-text.js +73 -0
  26. package/dist/domain/admin-statistics.d.ts +5 -0
  27. package/dist/domain/admin-statistics.js +4 -3
  28. package/dist/domain/calendar.d.ts +2 -0
  29. package/dist/domain/calendar.js +6 -0
  30. package/dist/entities/chat-stats.d.ts +16 -2
  31. package/dist/entities/chat-stats.js +18 -3
  32. package/dist/entities/index.d.ts +80 -802
  33. package/dist/entities/index.js +26 -16
  34. package/dist/entities/mail-counts.d.ts +15 -0
  35. package/dist/entities/mail-counts.js +16 -0
  36. package/dist/entities/meeting.d.ts +1 -1
  37. package/dist/entities/meeting.js +1 -1
  38. package/dist/entities/reply-stats.d.ts +33 -0
  39. package/dist/entities/reply-stats.js +19 -0
  40. package/dist/entities/store-records.d.ts +8 -0
  41. package/dist/entities/store-records.js +8 -0
  42. package/dist/errors/reasons.d.ts +1 -1
  43. package/dist/errors/reasons.js +7 -3
  44. package/dist/mail/attachments.d.ts +15 -2
  45. package/dist/mail/attachments.js +19 -3
  46. package/dist/mail/cli/index.d.ts +22 -1
  47. package/dist/mail/cli/index.js +28 -4
  48. package/dist/mail/cli/review.d.ts +5 -2
  49. package/dist/mail/cli/review.js +58 -43
  50. package/dist/mail/index.d.ts +4 -0
  51. package/dist/mail/index.js +4 -0
  52. package/dist/mail/partial.d.ts +3 -1
  53. package/dist/mail/partial.js +2 -2
  54. package/dist/mail/retry.d.ts +3 -1
  55. package/dist/mail/retry.js +3 -3
  56. package/dist/mail/stats.d.ts +50 -0
  57. package/dist/mail/stats.js +60 -0
  58. package/dist/mail/sync-types.d.ts +3 -0
  59. package/dist/mail/sync.js +5 -3
  60. package/dist/mail/whole.d.ts +28 -0
  61. package/dist/mail/whole.js +70 -0
  62. package/dist/meetings/index.d.ts +2 -2
  63. package/dist/meetings/index.js +2 -2
  64. package/dist/meetings/mcp/index.d.ts +15 -15
  65. package/dist/meetings/read-answers.d.ts +22 -1
  66. package/dist/meetings/read-answers.js +38 -0
  67. package/dist/meetings/stats.d.ts +44 -0
  68. package/dist/meetings/stats.js +77 -7
  69. package/dist/search/lucene/registry.js +2 -1
  70. package/dist/search/question-plan.d.ts +16 -0
  71. package/dist/search/question-plan.js +34 -0
  72. package/dist/services/events-search.d.ts +5 -1
  73. package/dist/services/events-search.js +10 -4
  74. package/dist/services/index.d.ts +3 -1
  75. package/dist/services/index.js +3 -1
  76. package/dist/services/meeting-stats.d.ts +12 -1
  77. package/dist/services/meeting-stats.js +12 -0
  78. package/dist/services/messages-discovery.d.ts +1 -5
  79. package/dist/services/messages-discovery.js +5 -18
  80. package/dist/services/messages-meaning.js +25 -3
  81. package/dist/services/messages.js +4 -3
  82. package/dist/services/notes-search.d.ts +5 -1
  83. package/dist/services/notes-search.js +16 -5
  84. package/dist/services/proposal-tasks.js +3 -12
  85. package/dist/services/proposals/ids.d.ts +2 -2
  86. package/dist/services/proposals/ids.js +5 -3
  87. package/dist/services/proposals/kinds/action.js +3 -2
  88. package/dist/services/proposals/kinds/findings.d.ts +1 -1
  89. package/dist/services/proposals/kinds/findings.js +1 -1
  90. package/dist/services/proposals/kinds/meeting-findings.d.ts +7 -0
  91. package/dist/services/proposals/kinds/meeting-findings.js +73 -0
  92. package/dist/services/proposals/kinds/task-close.d.ts +7 -0
  93. package/dist/services/proposals/kinds/task-close.js +76 -0
  94. package/dist/services/proposals/registry.js +12 -4
  95. package/dist/services/proposals/types.d.ts +2 -0
  96. package/dist/services/reply-stats.d.ts +48 -0
  97. package/dist/services/reply-stats.js +137 -0
  98. package/dist/services/search-all.d.ts +5 -0
  99. package/dist/services/search-all.js +47 -14
  100. package/dist/services/task-stats.d.ts +41 -0
  101. package/dist/services/task-stats.js +126 -0
  102. package/dist/services/tasks.d.ts +17 -4
  103. package/dist/services/tasks.js +31 -14
  104. package/dist/store/chats.d.ts +3 -1
  105. package/dist/store/chats.js +6 -0
  106. package/dist/store/decisions.d.ts +16 -11
  107. package/dist/store/decisions.js +11 -2
  108. package/dist/store/exports.d.ts +1 -1
  109. package/dist/store/exports.js +1 -1
  110. package/dist/store/index.d.ts +5 -4
  111. package/dist/store/index.js +3 -3
  112. package/dist/store/queries.d.ts +4 -0
  113. package/dist/store/queries.js +3 -0
  114. package/dist/store/sqlite/completeness.d.ts +17 -0
  115. package/dist/store/sqlite/completeness.js +49 -13
  116. package/dist/store/sqlite/email-locations.d.ts +4 -0
  117. package/dist/store/sqlite/email-locations.js +26 -11
  118. package/dist/store/sqlite/lucene-fields.d.ts +2 -0
  119. package/dist/store/sqlite/lucene-fields.js +5 -1
  120. package/dist/store/sqlite/lucene-groups.d.ts +15 -0
  121. package/dist/store/sqlite/lucene-groups.js +21 -0
  122. package/dist/store/sqlite/lucene.d.ts +2 -9
  123. package/dist/store/sqlite/lucene.js +1 -12
  124. package/dist/store/sqlite/mail-findings.js +1 -1
  125. package/dist/store/sqlite/meeting-findings.js +1 -1
  126. package/dist/store/sqlite/meeting-stats.d.ts +2 -0
  127. package/dist/store/sqlite/meeting-stats.js +27 -1
  128. package/dist/store/sqlite/proposed-actions.d.ts +2 -0
  129. package/dist/store/sqlite/proposed-actions.js +24 -17
  130. package/dist/store/sqlite/reply-times.d.ts +57 -0
  131. package/dist/store/sqlite/reply-times.js +113 -0
  132. package/dist/store/sqlite/retention.js +2 -7
  133. package/dist/store/sqlite/task-board.js +2 -0
  134. package/dist/store/sqlite/task-stats.d.ts +32 -0
  135. package/dist/store/sqlite/task-stats.js +45 -0
  136. package/dist/store/sqlite/tasks.d.ts +2 -1
  137. package/dist/store/sqlite/tasks.js +2 -0
  138. package/dist/store/sqlite/work-tasks.d.ts +12 -2
  139. package/dist/store/sqlite/work-tasks.js +35 -26
  140. package/dist/store/store.d.ts +1 -0
  141. package/package.json +1 -1
  142. package/parity.json +37 -1
@@ -44,9 +44,10 @@ import { joinRequest } from "./join-request.js";
44
44
  import { organization, project, reminder } from "./knowledge-lists.js";
45
45
  import { botAdmin, botMenuEntry, botProfile, botSeenChat, botTemplate, botWebhook, callRecord, scheduledJob, stickerInventory, topicIcon, } from "./list-results.js";
46
46
  import { messageEvent, serveSummary, watchedMessage, watchItem } from "./listener.js";
47
+ import { mailCounts } from "./mail-counts.js";
47
48
  import { mailFindings } from "./mail-findings.js";
48
49
  import { mediaArchive, mediaArchiveCheck, mediaArchiveMove, mediaArchiveTimer } from "./media-archive.js";
49
- import { event, meeting, meetingDetails, meetingHit, meetingLink, meetingParticipants, meetingPassage, meetingProjectEvidence, meetingReview, meetingSpeaker, meetingStats, meetingSummary, meetingTranscript, } from "./meeting.js";
50
+ import { event, meeting, meetingCounts, meetingDetails, meetingHit, meetingLink, meetingParticipants, meetingPassage, meetingProjectEvidence, meetingReview, meetingSpeaker, meetingStats, meetingSummary, meetingTranscript, } from "./meeting.js";
50
51
  import { meetingEvidence, meetingPersonContext } from "./meeting-evidence.js";
51
52
  import { meetingFindings } from "./meeting-findings.js";
52
53
  import { meetingPreparation } from "./meeting-preparation.js";
@@ -78,11 +79,12 @@ import { reactionChange } from "./reaction-change.js";
78
79
  import { recipient, recipientAdded, recipientChange, recipientOff, recipientRemoved } from "./recipient.js";
79
80
  import { recordedRun, runDetails, runHit, runLocation } from "./recorded-run.js";
80
81
  import { relationship } from "./relationship.js";
82
+ import { responseTimes, waitingChats } from "./reply-stats.js";
81
83
  import { retention } from "./retention.js";
82
84
  import { savedSearch, searchHistoryClear } from "./saved-search.js";
83
85
  import { sendAttempt } from "./send-attempt.js";
84
86
  import { storeBackup, storeCheck, storeCopyRemoval, storeDecrypt, storeInfo, storeMigration, storeReindex, storeRepair, storeReset, storeRestore, } from "./store-maintenance.js";
85
- import { storeChatStatus, storeClear, storeExport, storeGapPlan, storeGapRepair, storeJobCancel, storeJobClear, storeJobDetails, storeJobRetry, syncProgress, } from "./store-records.js";
87
+ import { chatCoverage, storeChatStatus, storeClear, storeExport, storeGapPlan, storeGapRepair, storeJobCancel, storeJobClear, storeJobDetails, storeJobRetry, syncProgress, } from "./store-records.js";
86
88
  import { storeTable } from "./store-table.js";
87
89
  import { automaticTags, tagChange } from "./tag-change.js";
88
90
  import { tagging } from "./tagging.js";
@@ -95,7 +97,7 @@ import { topic } from "./topic.js";
95
97
  * subset of its fields, never a renamed one. Commands move onto these one by one; until then, a schema
96
98
  * is the target, not a description of today's output.
97
99
  */
98
- 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, taskAnalysis, taskAnalysisMark, taskCard, taskCategories, taskClosed, taskCreated, taskLinks, taskMerge, taskPair, taskScan, taskSimilar, taskStats, taskUnmerge, topic, topicIcon, trackedChat, trackedChatHistory, watchedMessage, watchItem, };
100
+ 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, chatCoverage, 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, mailCounts, 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, responseTimes, 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, taskAnalysis, taskAnalysisMark, taskCard, taskCategories, taskClosed, taskCreated, taskLinks, taskMerge, taskPair, taskScan, taskSimilar, taskStats, taskUnmerge, topic, topicIcon, trackedChat, trackedChatHistory, waitingChats, watchedMessage, watchItem, };
99
101
  export const ENTITIES = {
100
102
  sync_progress: syncProgress,
101
103
  ...MEETING_RESULT_ENTITIES,
@@ -125,19 +127,22 @@ export const ENTITIES = {
125
127
  attachments: v.optional(v.array(hitFile)),
126
128
  untrusted,
127
129
  }),
128
- "message.summary": v.pick(message, [
129
- "ref",
130
- "chat_id",
131
- "chat_title",
132
- "timestamp",
133
- "sender_name",
134
- "sender_is_me",
135
- "outgoing",
136
- "text",
137
- "transcript",
138
- "attachments",
139
- "email",
140
- ]),
130
+ "message.summary": v.strictObject({
131
+ ...v.pick(message, [
132
+ "ref",
133
+ "chat_id",
134
+ "chat_title",
135
+ "timestamp",
136
+ "sender_name",
137
+ "sender_is_me",
138
+ "outgoing",
139
+ "text",
140
+ "transcript",
141
+ "attachments",
142
+ "email",
143
+ ]).entries,
144
+ untrusted,
145
+ }),
141
146
  message_link: messageLink,
142
147
  message_write: messageWrite,
143
148
  message_send: messageSend,
@@ -256,6 +261,7 @@ export const ENTITIES = {
256
261
  meeting_review: meetingReview,
257
262
  meeting_link: meetingLink,
258
263
  meeting_stats: meetingStats,
264
+ meeting_counts: meetingCounts,
259
265
  meeting_speaker: meetingSpeaker,
260
266
  meeting_participants: meetingParticipants,
261
267
  meeting_passage: meetingPassage,
@@ -293,6 +299,7 @@ export const ENTITIES = {
293
299
  job,
294
300
  store_chat_status: storeChatStatus,
295
301
  store_clear: storeClear,
302
+ chat_coverage: chatCoverage,
296
303
  store_export: storeExport,
297
304
  store_gap_plan: storeGapPlan,
298
305
  store_gap_repair: storeGapRepair,
@@ -348,6 +355,9 @@ export const ENTITIES = {
348
355
  topic,
349
356
  folder,
350
357
  message_counts: messageCounts,
358
+ response_times: responseTimes,
359
+ waiting_chats: waitingChats,
360
+ mail_counts: mailCounts,
351
361
  ranking,
352
362
  admin_report: adminReport,
353
363
  analysis_consent: analysisConsent,
@@ -0,0 +1,15 @@
1
+ import * as v from "valibot";
2
+ /** Stored mail received and sent, one row per calendar day, week (by its Monday) or sender. */
3
+ export declare const mailCounts: v.StrictObjectSchema<{
4
+ readonly by: v.PicklistSchema<["day", "week", "sender"], undefined>;
5
+ readonly received: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
6
+ readonly sent: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
7
+ readonly rows: v.ArraySchema<v.StrictObjectSchema<{
8
+ /** `YYYY-MM-DD`, or the sender's address. */
9
+ readonly key: v.StringSchema<undefined>;
10
+ readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
11
+ readonly received: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
12
+ /** Always 0 by sender: the owner sends that mail. */
13
+ readonly sent: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
14
+ }, undefined>, undefined>;
15
+ }, undefined>;
@@ -0,0 +1,16 @@
1
+ import * as v from "valibot";
2
+ import { count, text } from "./fields.js";
3
+ /** Stored mail received and sent, one row per calendar day, week (by its Monday) or sender. */
4
+ export const mailCounts = v.strictObject({
5
+ by: v.picklist(["day", "week", "sender"]),
6
+ received: count,
7
+ sent: count,
8
+ rows: v.array(v.strictObject({
9
+ /** `YYYY-MM-DD`, or the sender's address. */
10
+ key: text,
11
+ name: v.nullable(text),
12
+ received: count,
13
+ /** Always 0 by sender: the owner sends that mail. */
14
+ sent: count,
15
+ })),
16
+ });
@@ -1 +1 @@
1
- export { event, meeting, meetingDetails, meetingHit, meetingLink, meetingParticipants, meetingPassage, meetingProjectEvidence, meetingReview, meetingSpeaker, meetingStats, meetingSummary, meetingTranscript, } from "../meetings/index.js";
1
+ export { event, meeting, meetingCounts, meetingDetails, meetingHit, meetingLink, meetingParticipants, meetingPassage, meetingProjectEvidence, meetingReview, meetingSpeaker, meetingStats, meetingSummary, meetingTranscript, } from "../meetings/index.js";
@@ -1 +1 @@
1
- export { event, meeting, meetingDetails, meetingHit, meetingLink, meetingParticipants, meetingPassage, meetingProjectEvidence, meetingReview, meetingSpeaker, meetingStats, meetingSummary, meetingTranscript, } from "../meetings/index.js";
1
+ export { event, meeting, meetingCounts, meetingDetails, meetingHit, meetingLink, meetingParticipants, meetingPassage, meetingProjectEvidence, meetingReview, meetingSpeaker, meetingStats, meetingSummary, meetingTranscript, } from "../meetings/index.js";
@@ -0,0 +1,33 @@
1
+ import * as v from "valibot";
2
+ /** How long replies took in one-to-one chats: the owner's (`me`) or the other side's (`others`). */
3
+ export declare const responseTimes: v.StrictObjectSchema<{
4
+ readonly replies: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
5
+ readonly median_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
6
+ readonly p90_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
7
+ readonly mean_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
8
+ readonly who: v.PicklistSchema<["me", "others"], undefined>;
9
+ readonly buckets: v.StrictObjectSchema<{
10
+ readonly under_5m: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
11
+ readonly under_1h: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
12
+ readonly under_1d: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
13
+ readonly over_1d: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
14
+ }, undefined>;
15
+ /** One row per calendar day of the reply; empty with `--by total`. */
16
+ readonly rows: v.ArraySchema<v.StrictObjectSchema<{
17
+ readonly replies: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
18
+ readonly median_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
19
+ readonly p90_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
20
+ readonly mean_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
21
+ readonly key: v.StringSchema<undefined>;
22
+ }, undefined>, undefined>;
23
+ }, undefined>;
24
+ /** Chats whose newest message asks the owner something, and how much waits there. */
25
+ export declare const waitingChats: v.StrictObjectSchema<{
26
+ readonly chats: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
27
+ readonly messages: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
28
+ readonly oldest_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>;
29
+ readonly by_kind: v.StrictObjectSchema<{
30
+ readonly private: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
31
+ readonly group: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
32
+ }, undefined>;
33
+ }, undefined>;
@@ -0,0 +1,19 @@
1
+ import * as v from "valibot";
2
+ import { count, text, time } from "./fields.js";
3
+ const milliseconds = v.nullable(v.pipe(v.number(), v.minValue(0)));
4
+ const figures = { replies: count, median_ms: milliseconds, p90_ms: milliseconds, mean_ms: milliseconds };
5
+ /** How long replies took in one-to-one chats: the owner's (`me`) or the other side's (`others`). */
6
+ export const responseTimes = v.strictObject({
7
+ who: v.picklist(["me", "others"]),
8
+ ...figures,
9
+ buckets: v.strictObject({ under_5m: count, under_1h: count, under_1d: count, over_1d: count }),
10
+ /** One row per calendar day of the reply; empty with `--by total`. */
11
+ rows: v.array(v.strictObject({ key: text, ...figures })),
12
+ });
13
+ /** Chats whose newest message asks the owner something, and how much waits there. */
14
+ export const waitingChats = v.strictObject({
15
+ chats: count,
16
+ messages: count,
17
+ oldest_at: v.nullable(time),
18
+ by_kind: v.strictObject({ private: count, group: count }),
19
+ });
@@ -12,6 +12,14 @@ export declare const storeChatStatus: v.StrictObjectSchema<{
12
12
  readonly to: v.NumberSchema<undefined>;
13
13
  }, undefined>, undefined>;
14
14
  }, undefined>;
15
+ /** `store chats status`: chats = fetched + partial + never, over the account's searchable chats of every kind. */
16
+ export declare const chatCoverage: v.StrictObjectSchema<{
17
+ readonly chats: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
18
+ readonly fetched: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
19
+ readonly partial: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
20
+ readonly never: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
21
+ readonly oldest_message_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>;
22
+ }, undefined>;
15
23
  export declare const storeClear: v.StrictObjectSchema<{
16
24
  readonly cleared: v.BooleanSchema<undefined>;
17
25
  readonly dry_run: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
@@ -17,6 +17,14 @@ export const storeChatStatus = v.strictObject({
17
17
  last_stored_at: v.nullable(time),
18
18
  held: v.array(range),
19
19
  });
20
+ /** `store chats status`: chats = fetched + partial + never, over the account's searchable chats of every kind. */
21
+ export const chatCoverage = v.strictObject({
22
+ chats: count,
23
+ fetched: count,
24
+ partial: count,
25
+ never: count,
26
+ oldest_message_at: v.nullable(time),
27
+ });
20
28
  export const storeClear = v.strictObject({
21
29
  cleared: v.boolean(),
22
30
  dry_run: v.optional(v.literal(true)),
@@ -7,7 +7,7 @@ export declare const REASONS: ErrorReason[];
7
7
  * Detail keys an error from this package may carry, until each is registered on its own reason. Not `cause`:
8
8
  * that can hold a provider's or SQLite's raw text.
9
9
  */
10
- export declare const DETAILS: readonly ["contracts", "calendars", "allowed", "alternative", "arguments", "at", "available", "batch", "budget", "built", "candidates", "chat", "chat_id", "cli", "column", "complete", "emitted_bytes", "exact", "file", "forum", "guide", "hint", "id", "index", "kind", "limit", "limit_source", "line", "max_bytes", "model", "name", "operation_id", "operation_ids", "text_limit", "store_refused_in", "subtasks", "partial_output", "parent", "path", "permission", "person_ids", "previous_chat_id", "profiles", "provider_error", "provider", "recovery", "reference", "remembered", "retry_at", "retry_after_ms", "runtime", "saved", "scheduled_for", "script", "sent", "sent_count", "settlement_failed", "span", "sqlite", "stage", "standing", "state", "stderr", "term", "total", "upgraded", "wanted"];
10
+ export declare const DETAILS: readonly ["contracts", "calendars", "allowed", "alternative", "arguments", "at", "available", "batch", "budget", "built", "candidates", "chat", "chat_id", "cli", "column", "complete", "emitted_bytes", "exact", "file", "forum", "guide", "hint", "id", "index", "kind", "limit", "limit_source", "line", "max_bytes", "model", "name", "operation_id", "operation_ids", "text_limit", "store_refused_in", "partial_output", "parent", "path", "permission", "person_ids", "previous_chat_id", "profiles", "provider_error", "provider", "recovery", "reference", "remembered", "retry_at", "retry_after_ms", "runtime", "saved", "scheduled_for", "script", "sent", "sent_count", "settlement_failed", "span", "sqlite", "stage", "standing", "state", "stderr", "term", "total", "upgraded", "wanted"];
11
11
  /** What a CLI adds to the shared registry: its own reasons, and the detail keys its adapters raise. */
12
12
  export interface AppErrors {
13
13
  reasons?: readonly ErrorReason[];
@@ -47,6 +47,7 @@ const OTHERS = [
47
47
  validation("mail-findings.stale", "mail findings cite mail that changed since it was read; stage a new batch"),
48
48
  validation("mail-findings.partial", "a cited email is not stored whole yet; cite it once it is"),
49
49
  validation("evidence-findings.dedicated-review", "review evidence findings through the dedicated findings command"),
50
+ validation("mail-findings.dedicated-review", "review mail findings through the dedicated findings command"),
50
51
  validation("document.changed", "the document changed while it was read; nothing was saved"),
51
52
  ["document.not-imported", "not_found", "the document is not imported in this source account"],
52
53
  ["extraction.invalid-proposals", "invalid_response", "the model's memory proposals could not be used"],
@@ -54,6 +55,11 @@ const OTHERS = [
54
55
  validation("file.locked", "another process holds the file's lock"),
55
56
  ["mail.source-failed", "provider_error", "the mail server refused or did not complete a request"],
56
57
  ["mail.search-refused", "provider_error", "the mail server refused to search a folder"],
58
+ [
59
+ "mail.attachments-unread",
60
+ "provider_unavailable",
61
+ "the email could not be read again, so its skipped attachments have no text",
62
+ ],
57
63
  ["mirror.partial", "provider_error", "some mirrored files were not synced"],
58
64
  ["profile.wrong-account", "authentication_error", "the signed-in source account does not match this profile"],
59
65
  validation("push.too-many-files", "the folder holds more files than one push takes"),
@@ -218,8 +224,7 @@ const OTHERS = [
218
224
  ["task.stale", "configuration_error", "the task changed since it was read; read it again"],
219
225
  validation("task.column", "the task beside which it moves is in another column than the status asked for"),
220
226
  validation("task.dependency-cycle", "the task would depend on itself"),
221
- validation("task.has-subtasks", "the task has subtasks; move or detach them before it moves to another project"),
222
- validation("task.is-subtask", "a subtask moves to another project only when it is detached from its parent in the same write"),
227
+ validation("task.is-subtask", "a subtask moves to another project alone only when it is detached from its parent in the same write"),
223
228
  validation("task.duplicate-candidates", "open tasks may be the same job: attach to one, or add it as new"),
224
229
  validation("task.already-read", "the message was already read for tasks: a read again only updates and closes tasks unless --open-new"),
225
230
  validation("task.certainty-required", "an agent closes a task with its certainty, 0 to 1"),
@@ -300,7 +305,6 @@ export const DETAILS = [
300
305
  "operation_ids",
301
306
  "text_limit",
302
307
  "store_refused_in",
303
- "subtasks",
304
308
  "partial_output",
305
309
  "parent",
306
310
  "path",
@@ -3,14 +3,27 @@ import type { AttachmentInput, EmailAddress } from "../store/index.js";
3
3
  import type { Address, ParsedMail } from "./parse.js";
4
4
  import type { MailSyncResult } from "./sync.js";
5
5
  export declare const MAX_ATTACHMENT_CHARS = 200000;
6
+ /** Whether a sync reads an email's attachments into the index, and the largest file it reads. */
7
+ export interface MailAttachmentPolicy {
8
+ read: boolean;
9
+ maxBytes: number;
10
+ }
11
+ /** On, up to 10 MB a file: ordinary documents and scans are read, a large archive or video is not. */
12
+ export declare const DEFAULT_ATTACHMENT_POLICY: MailAttachmentPolicy;
13
+ /** Why a policy left an attachment unread; `readEmailWhole` reads it anyway. */
14
+ export declare const POLICY_SKIPS: readonly ["not-read", "too-large"];
6
15
  /** One chunk of raw mail is held in memory at a time. */
7
16
  export declare const FETCH_CHUNK = 50;
8
17
  export declare const addressOf: ({ email, name }: Address) => EmailAddress;
9
18
  export type AttachmentLog = NonNullable<MailSyncResult["attachments"]>;
10
- /** A parsed email's attachments as the store keeps them, each with its text or why it has none. */
11
- export declare const readAttachments: (mail: ParsedMail, messageId: string, { loadEngine, maxAttachmentChars, at, signal, }: {
19
+ /**
20
+ * A parsed email's attachments as the store keeps them, each with its text or why it has none. Without `policy` every
21
+ * attachment is read, whatever its size.
22
+ */
23
+ export declare const readAttachments: (mail: ParsedMail, messageId: string, { loadEngine, maxAttachmentChars, at, signal, policy, }: {
12
24
  loadEngine: LoadEngine;
13
25
  maxAttachmentChars: number;
14
26
  at: number;
15
27
  signal?: AbortSignal;
28
+ policy?: MailAttachmentPolicy;
16
29
  }, log: AttachmentLog) => Promise<AttachmentInput[]>;
@@ -1,17 +1,33 @@
1
1
  import { extractText } from "../attachments/extract.js";
2
2
  export const MAX_ATTACHMENT_CHARS = 200_000;
3
+ /** On, up to 10 MB a file: ordinary documents and scans are read, a large archive or video is not. */
4
+ export const DEFAULT_ATTACHMENT_POLICY = { read: true, maxBytes: 10 * 1024 * 1024 };
5
+ /** Why a policy left an attachment unread; `readEmailWhole` reads it anyway. */
6
+ export const POLICY_SKIPS = ["not-read", "too-large"];
3
7
  /** One chunk of raw mail is held in memory at a time. */
4
8
  export const FETCH_CHUNK = 50;
5
9
  export const addressOf = ({ email, name }) => ({ address: email, name });
6
- /** A parsed email's attachments as the store keeps them, each with its text or why it has none. */
7
- export const readAttachments = async (mail, messageId, { loadEngine, maxAttachmentChars, at, signal, }, log) => {
10
+ /**
11
+ * A parsed email's attachments as the store keeps them, each with its text or why it has none. Without `policy` every
12
+ * attachment is read, whatever its size.
13
+ */
14
+ export const readAttachments = async (mail, messageId, { loadEngine, maxAttachmentChars, at, signal, policy, }, log) => {
8
15
  const files = [];
9
16
  for (const [position, part] of mail.attachments.entries()) {
10
17
  if (signal?.aborted)
11
18
  throw signal.reason;
19
+ const skipped = part.bytes === null || policy === undefined
20
+ ? undefined
21
+ : !policy.read
22
+ ? "not-read"
23
+ : part.bytes.byteLength > policy.maxBytes
24
+ ? "too-large"
25
+ : undefined;
12
26
  const extraction = part.bytes === null
13
27
  ? { status: "unavailable" }
14
- : await extractText(part.bytes, { kind: "file", name: part.name, mime: part.mime, path: part.name }, loadEngine, signal);
28
+ : skipped
29
+ ? { status: skipped }
30
+ : await extractText(part.bytes, { kind: "file", name: part.name, mime: part.mime, path: part.name }, loadEngine, signal);
15
31
  log.push({ message: messageId, part: part.id, status: extraction.status });
16
32
  const extracted = extraction.status === "extracted" ? extraction : undefined;
17
33
  files.push({
@@ -3,7 +3,22 @@ import { type MailServerAccess } from "../../mail/server-search.js";
3
3
  import type { AppIdentity } from "../../ports/app.js";
4
4
  import { type AccountKey, type MessageStore } from "../../store/store.js";
5
5
  export { mailMark, markItem, markText } from "./marks.js";
6
- export { MAIL_REVIEW_QUERY, MAIL_REVIEW_STEPS } from "./review.js";
6
+ export { MAIL_REVIEW_QUERY, MAIL_REVIEW_STEPS, mailReviewSteps } from "./review.js";
7
+ /**
8
+ * Where a host mounted `review`, `messages acknowledge` and `conversations show`, and the option that names one
9
+ * mailbox, so the steps a review hands on run there. A mail CLI leaves it out: its profile names the mailbox.
10
+ */
11
+ export interface MailMount {
12
+ review?: string;
13
+ acknowledge?: string;
14
+ /** `null`: the host mounts no thread read, so a review offers none. */
15
+ conversation?: string | null;
16
+ /** In snake_case, as a step names it: memo's `source`, with `email:<account>`. */
17
+ mailbox?: {
18
+ option: string;
19
+ value: (mailbox: AccountKey) => string;
20
+ };
21
+ }
7
22
  /** What a mail CLI hands the shared commands: how to open the store for one run, and whose mail it reads. */
8
23
  export interface MailReads {
9
24
  app: AppIdentity;
@@ -14,8 +29,14 @@ export interface MailReads {
14
29
  read: <T>(command: Command, work: (store: MessageStore, mailbox: AccountKey) => Promise<T>) => Promise<T>;
15
30
  /** The mailbox's server, for `search messages --backend`; without it the search reads the store only. */
16
31
  server?: (command: Command) => MailServerAccess;
32
+ mount?: MailMount;
17
33
  /** How `--server-time` help states its default when a provider's own differs (`MailServerAccess.timeMs`). */
18
34
  serverTimeDefault?: string;
35
+ /**
36
+ * Reads one stored email again from the mailbox's server, every attachment whatever its size (`readEmailWhole`).
37
+ * With it, `messages show` reads an email whose attachment a sync's policy left unread before it answers.
38
+ */
39
+ whole?: (command: Command, store: MessageStore, mailbox: AccountKey, emailId: string) => Promise<void>;
19
40
  }
20
41
  export declare const THREAD_QUERY: {
21
42
  readonly entity: "mail_thread";
@@ -5,7 +5,7 @@ import { Command, InvalidArgumentError } from "commander";
5
5
  import * as v from "valibot";
6
6
  import { answerEntity } from "../../cli/answer-entity.js";
7
7
  import { environmentOf, outputFor } from "../../cli/context.js";
8
- import { LOCAL_READ, REMOTE_READ } from "../../cli/effects.js";
8
+ import { FETCH, LOCAL_READ, REMOTE_READ } from "../../cli/effects.js";
9
9
  import { itemOf, listOf, renderAnswer, snakeKeys } from "../../cli/envelope.js";
10
10
  import { listQuery, typedQuery, withQuery } from "../../cli/list-query.js";
11
11
  import { messagesAnswer, offsetOf, shownOf } from "../../cli/messenger/search-answers.js";
@@ -13,6 +13,7 @@ import { backendRequest, noteServer, serverSearchOptions } from "../../cli/messe
13
13
  import { positiveCount } from "../../cli/paging.js";
14
14
  import { formatReference, parseReference } from "../../domain/references.js";
15
15
  import { count, reference, text, time, untrusted } from "../../entities/fields.js";
16
+ import { POLICY_SKIPS } from "../../mail/attachments.js";
16
17
  import { attachmentsText, hitText, searchMail, showMail, withAttachmentText } from "../../mail/read.js";
17
18
  import { searchMailServer } from "../../mail/server-search.js";
18
19
  import { atDetail } from "../../output-detail.js";
@@ -21,13 +22,21 @@ import { queryFilter, THREAD_COLUMNS } from "../../store/store.js";
21
22
  import { acknowledgeCommand } from "./marks.js";
22
23
  import { mailReviewCommand } from "./review.js";
23
24
  export { mailMark, markItem, markText } from "./marks.js";
24
- export { MAIL_REVIEW_QUERY, MAIL_REVIEW_STEPS } from "./review.js";
25
+ export { MAIL_REVIEW_QUERY, MAIL_REVIEW_STEPS, mailReviewSteps } from "./review.js";
25
26
  /** A locked or broken store must fail the command, not read as "nothing stored yet". */
26
27
  const absentAccount = (error) => {
27
28
  if (error instanceof CliError && error.code === "not_found")
28
29
  return undefined;
29
30
  throw error;
30
31
  };
32
+ /** The shown email whose attachment a sync's policy left without text, if any. */
33
+ const policySkipped = (emails) => {
34
+ const shown = emails.find((email) => email.anchor) ?? emails[0];
35
+ const skips = POLICY_SKIPS;
36
+ return shown?.attachments?.some((file) => skips.includes(String(file.extraction)))
37
+ ? shown
38
+ : undefined;
39
+ };
31
40
  const DEFAULT_LIMIT = 20;
32
41
  const MAX_LIMIT = 100;
33
42
  const limitOf = (value) => {
@@ -203,7 +212,7 @@ const shownText = (emails) => `${emails.map((email) => [hitText(email, "full"),
203
212
  const messagesCommand = (mail) => {
204
213
  const messages = new Command("messages").summary("read stored emails").description("read the emails sync stored");
205
214
  annotate(messages.command("show"), {
206
- effects: LOCAL_READ,
215
+ effects: mail.whole ? FETCH : LOCAL_READ,
207
216
  examples: [
208
217
  `${mail.app.command} personal messages show 'msg:email/me%40example.com/thread-1/%3Cid%40example.com%3E'`,
209
218
  `${mail.app.command} personal messages show 'msg:email/me%40example.com/thread-1/%3Cid%40example.com%3E' --before-n 2`,
@@ -218,15 +227,30 @@ const messagesCommand = (mail) => {
218
227
  .action(async function (reference) {
219
228
  const { beforeN, afterN } = this.opts();
220
229
  const full = detailOf(this) === "full";
230
+ let unread;
221
231
  const emails = await mail.read(this, async (store, mailbox) => {
222
232
  threadOf(mailbox, reference);
233
+ const shown = await showMail(store, reference, { before: beforeN, after: afterN, full });
234
+ const skipped = mail.whole ? policySkipped(shown) : undefined;
235
+ if (!skipped || !mail.whole)
236
+ return shown;
237
+ try {
238
+ await mail.whole(this, store, mailbox, skipped.id);
239
+ }
240
+ catch (error) {
241
+ if (!(error instanceof CliError))
242
+ throw error;
243
+ // The stored email is still worth showing; the answer says its attachments were not read.
244
+ unread = { code: error.code, reason: "mail.attachments-unread", source: "server" };
245
+ return shown;
246
+ }
223
247
  return showMail(store, reference, { before: beforeN, after: afterN, full });
224
248
  });
225
249
  const output = outputFor(this);
226
250
  const shown = emails.map((email) => snakeKeys(atDetail(email)));
227
251
  renderAnswer(this, output, beforeN || afterN
228
252
  ? listOf("messages show", { items: shown, limit: Math.max(1, shown.length), has_more: false })
229
- : itemOf(shown.find((_, at) => emails[at]?.anchor) ?? shown[0]), () => shownText(emails));
253
+ : itemOf(shown.find((_, at) => emails[at]?.anchor) ?? shown[0], unread ? { meta: { complete: false, issues: [unread] } } : undefined), () => shownText(emails));
230
254
  });
231
255
  messages.addCommand(acknowledgeCommand(mail));
232
256
  return messages;
@@ -1,8 +1,11 @@
1
+ import { type StepDefinition } from "@wirecat/cli-base/standards";
1
2
  import { Command } from "commander";
2
- import type { MailReads } from "./index.js";
3
+ import type { MailMount, MailReads } from "./index.js";
3
4
  /** Where a mail review starts: a time only; one thread is `conversations show`. */
4
5
  export declare const MAIL_REVIEW_QUERY: import("@wirecat/cli-base/query").QueryRegistry;
5
- /** The mail review's steps; a host lists them beside its own so its step checks know them. */
6
+ /** The mail review's steps where a host mounted them; a host lists them beside its own so its step checks know them. */
7
+ export declare const mailReviewSteps: (mount?: MailMount) => StepDefinition[];
8
+ /** The steps of a mail CLI, whose profile names the mailbox. */
6
9
  export declare const MAIL_REVIEW_STEPS: ({
7
10
  id: string;
8
11
  kind: "command";