@wirecat/cli-core 0.562.0 → 0.563.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.
- package/CHANGELOG.md +62 -0
- package/dist/cli/config-command.js +18 -1
- package/dist/cli/envelope.js +13 -1
- package/dist/cli/execution.js +1 -0
- package/dist/cli/messenger/reply-stats-command.d.ts +4 -0
- package/dist/cli/messenger/reply-stats-command.js +57 -0
- package/dist/cli/messenger/search-command.js +8 -0
- package/dist/cli/messenger/stats-answers.d.ts +3 -0
- package/dist/cli/messenger/stats-answers.js +18 -0
- package/dist/cli/messenger/stats-command.js +5 -0
- package/dist/cli/output-budget.d.ts +26 -0
- package/dist/cli/output-budget.js +73 -0
- package/dist/cli/program.js +14 -1
- package/dist/cli/settings.d.ts +5 -0
- package/dist/cli/settings.js +88 -5
- package/dist/cli/shared-command-exports.d.ts +8 -0
- package/dist/domain/admin-statistics.d.ts +5 -0
- package/dist/domain/admin-statistics.js +4 -3
- package/dist/entities/index.d.ts +31 -1
- package/dist/entities/index.js +4 -1
- package/dist/entities/reply-stats.d.ts +33 -0
- package/dist/entities/reply-stats.js +19 -0
- package/dist/errors/reasons.d.ts +1 -1
- package/dist/errors/reasons.js +2 -3
- package/dist/mail/attachments.d.ts +15 -2
- package/dist/mail/attachments.js +19 -3
- package/dist/mail/cli/index.d.ts +17 -1
- package/dist/mail/cli/index.js +1 -1
- package/dist/mail/cli/review.d.ts +5 -2
- package/dist/mail/cli/review.js +47 -39
- package/dist/mail/index.d.ts +2 -0
- package/dist/mail/index.js +2 -0
- package/dist/mail/partial.d.ts +3 -1
- package/dist/mail/partial.js +2 -2
- package/dist/mail/retry.d.ts +3 -1
- package/dist/mail/retry.js +3 -3
- package/dist/mail/sync-types.d.ts +3 -0
- package/dist/mail/sync.js +5 -3
- package/dist/mail/whole.d.ts +28 -0
- package/dist/mail/whole.js +69 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +2 -0
- package/dist/services/proposal-tasks.js +3 -12
- package/dist/services/proposals/kinds/action.js +3 -2
- package/dist/services/proposals/registry.js +8 -4
- package/dist/services/proposals/types.d.ts +2 -0
- package/dist/services/reply-stats.d.ts +48 -0
- package/dist/services/reply-stats.js +137 -0
- package/dist/store/decisions.d.ts +14 -11
- package/dist/store/decisions.js +9 -2
- package/dist/store/index.d.ts +3 -3
- package/dist/store/index.js +2 -2
- package/dist/store/queries.d.ts +4 -0
- package/dist/store/queries.js +3 -0
- package/dist/store/sqlite/email-locations.d.ts +4 -0
- package/dist/store/sqlite/email-locations.js +26 -11
- package/dist/store/sqlite/lucene-fields.d.ts +2 -0
- package/dist/store/sqlite/lucene-fields.js +3 -1
- package/dist/store/sqlite/mail-findings.js +1 -1
- package/dist/store/sqlite/meeting-findings.js +1 -1
- package/dist/store/sqlite/proposed-actions.d.ts +2 -0
- package/dist/store/sqlite/proposed-actions.js +24 -17
- package/dist/store/sqlite/reply-times.d.ts +57 -0
- package/dist/store/sqlite/reply-times.js +113 -0
- package/dist/store/sqlite/work-tasks.d.ts +12 -2
- package/dist/store/sqlite/work-tasks.js +35 -26
- package/dist/store/store.d.ts +1 -0
- package/package.json +1 -1
- package/parity.json +24 -0
|
@@ -8,6 +8,8 @@ export declare const appDoctorCommand: (app: import("./entry.js").AppIdentity, c
|
|
|
8
8
|
kind?: import("./settings.js").ProfileKind | undefined;
|
|
9
9
|
root?: boolean;
|
|
10
10
|
}) => unknown;
|
|
11
|
+
migrate: (path: string) => string[];
|
|
12
|
+
declaredSettings: import("./setting-defaults.js").DeclaredSetting[];
|
|
11
13
|
allSettings: string[];
|
|
12
14
|
defaultsOnlyKeys: string[];
|
|
13
15
|
schema: import("valibot").StrictObjectSchema<{
|
|
@@ -188,6 +190,8 @@ export declare const appMcpCommand: (app: import("./entry.js").AppIdentity, conf
|
|
|
188
190
|
kind?: import("./settings.js").ProfileKind | undefined;
|
|
189
191
|
root?: boolean;
|
|
190
192
|
}) => unknown;
|
|
193
|
+
migrate: (path: string) => string[];
|
|
194
|
+
declaredSettings: import("./setting-defaults.js").DeclaredSetting[];
|
|
191
195
|
allSettings: string[];
|
|
192
196
|
defaultsOnlyKeys: string[];
|
|
193
197
|
schema: import("valibot").StrictObjectSchema<{
|
|
@@ -375,6 +379,8 @@ export declare const configCommand: (app: import("./entry.js").AppIdentity, conf
|
|
|
375
379
|
kind?: import("./settings.js").ProfileKind | undefined;
|
|
376
380
|
root?: boolean;
|
|
377
381
|
}) => unknown;
|
|
382
|
+
migrate: (path: string) => string[];
|
|
383
|
+
declaredSettings: import("./setting-defaults.js").DeclaredSetting[];
|
|
378
384
|
allSettings: string[];
|
|
379
385
|
defaultsOnlyKeys: string[];
|
|
380
386
|
schema: import("valibot").StrictObjectSchema<{
|
|
@@ -590,6 +596,8 @@ export declare const completeCommand: (messenger: import("./index.js").Messenger
|
|
|
590
596
|
kind?: import("./settings.js").ProfileKind | undefined;
|
|
591
597
|
root?: boolean;
|
|
592
598
|
}) => unknown;
|
|
599
|
+
migrate: (path: string) => string[];
|
|
600
|
+
declaredSettings: import("./setting-defaults.js").DeclaredSetting[];
|
|
593
601
|
allSettings: string[];
|
|
594
602
|
defaultsOnlyKeys: string[];
|
|
595
603
|
schema: import("valibot").StrictObjectSchema<{
|
|
@@ -75,4 +75,9 @@ export interface AdminCalculated {
|
|
|
75
75
|
graph: ReturnType<typeof rankReplyGraph>["quality"];
|
|
76
76
|
}
|
|
77
77
|
export declare const adminEntityKey: (row: Pick<AdminRow, "account" | "id">) => string;
|
|
78
|
+
/** Median and nearest-rank p90 of reply delays, the numbers every response-time answer reports. */
|
|
79
|
+
export declare const delayStats: (delays: number[]) => {
|
|
80
|
+
medianMilliseconds: number | null;
|
|
81
|
+
p90Milliseconds: number | null;
|
|
82
|
+
};
|
|
78
83
|
export declare const calculateAdminStatistics: (nodes: AdminNode[], stays: AdminStay[], options: AdminOptions, inspect: () => void) => AdminCalculated;
|
|
@@ -6,7 +6,8 @@ import { rankingQuestion } from "./rankings-options.js";
|
|
|
6
6
|
export const adminEntityKey = (row) => JSON.stringify([row.account.provider, row.account.account, row.id]);
|
|
7
7
|
const personKey = (node) => JSON.stringify([node.account.provider, node.account.account, node.sender]);
|
|
8
8
|
const locator = (node) => formatLocator({ ...node.account, chat: node.chatId, message: node.id });
|
|
9
|
-
|
|
9
|
+
/** Median and nearest-rank p90 of reply delays, the numbers every response-time answer reports. */
|
|
10
|
+
export const delayStats = (delays) => {
|
|
10
11
|
const sorted = delays.toSorted((a, b) => a - b);
|
|
11
12
|
const middle = Math.floor(sorted.length / 2);
|
|
12
13
|
return {
|
|
@@ -103,7 +104,7 @@ export const calculateAdminStatistics = (nodes, stays, options, inspect) => {
|
|
|
103
104
|
account: { provider: person.provider, account: person.account },
|
|
104
105
|
questions: eligible.filter((node) => node.account.provider === person.provider && node.account.account === person.account).length,
|
|
105
106
|
answered: rows.length,
|
|
106
|
-
...
|
|
107
|
+
...delayStats(rows.map((row) => row.contribution)),
|
|
107
108
|
status: "observed",
|
|
108
109
|
}, rows);
|
|
109
110
|
}
|
|
@@ -154,7 +155,7 @@ export const calculateAdminStatistics = (nodes, stays, options, inspect) => {
|
|
|
154
155
|
pending: end > options.cutoff,
|
|
155
156
|
questions: held.length,
|
|
156
157
|
answered: delays.length,
|
|
157
|
-
...
|
|
158
|
+
...delayStats(delays),
|
|
158
159
|
status: "partial",
|
|
159
160
|
}, contributions);
|
|
160
161
|
}
|
package/dist/entities/index.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ import { reactionChange } from "./reaction-change.js";
|
|
|
71
71
|
import { recipient, recipientAdded, recipientChange, recipientOff, recipientRemoved } from "./recipient.js";
|
|
72
72
|
import { recordedRun, runDetails, runHit, runLocation } from "./recorded-run.js";
|
|
73
73
|
import { relationship } from "./relationship.js";
|
|
74
|
+
import { responseTimes, waitingChats } from "./reply-stats.js";
|
|
74
75
|
import { retention } from "./retention.js";
|
|
75
76
|
import { savedSearch, searchHistoryClear } from "./saved-search.js";
|
|
76
77
|
import { sendAttempt } from "./send-attempt.js";
|
|
@@ -88,7 +89,7 @@ import { topic } from "./topic.js";
|
|
|
88
89
|
* subset of its fields, never a renamed one. Commands move onto these one by one; until then, a schema
|
|
89
90
|
* is the target, not a description of today's output.
|
|
90
91
|
*/
|
|
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, taskAnalysis, taskAnalysisMark, taskCard, taskCategories, taskClosed, taskCreated, taskLinks, taskMerge, taskPair, taskScan, taskSimilar, taskStats, taskUnmerge, topic, topicIcon, trackedChat, trackedChatHistory, watchedMessage, watchItem, };
|
|
92
|
+
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, 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, };
|
|
92
93
|
export declare const ENTITIES: {
|
|
93
94
|
readonly meeting_ingestion: v.StrictObjectSchema<{
|
|
94
95
|
readonly meetings: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
@@ -10860,6 +10861,35 @@ export declare const ENTITIES: {
|
|
|
10860
10861
|
readonly count: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
10861
10862
|
}, undefined>, undefined>;
|
|
10862
10863
|
}, undefined>;
|
|
10864
|
+
readonly response_times: v.StrictObjectSchema<{
|
|
10865
|
+
readonly replies: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
10866
|
+
readonly median_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
10867
|
+
readonly p90_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
10868
|
+
readonly mean_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
10869
|
+
readonly who: v.PicklistSchema<["me", "others"], undefined>;
|
|
10870
|
+
readonly buckets: v.StrictObjectSchema<{
|
|
10871
|
+
readonly under_5m: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
10872
|
+
readonly under_1h: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
10873
|
+
readonly under_1d: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
10874
|
+
readonly over_1d: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
10875
|
+
}, undefined>;
|
|
10876
|
+
readonly rows: v.ArraySchema<v.StrictObjectSchema<{
|
|
10877
|
+
readonly replies: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
10878
|
+
readonly median_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
10879
|
+
readonly p90_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
10880
|
+
readonly mean_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
10881
|
+
readonly key: v.StringSchema<undefined>;
|
|
10882
|
+
}, undefined>, undefined>;
|
|
10883
|
+
}, undefined>;
|
|
10884
|
+
readonly waiting_chats: v.StrictObjectSchema<{
|
|
10885
|
+
readonly chats: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
10886
|
+
readonly messages: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
10887
|
+
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>;
|
|
10888
|
+
readonly by_kind: v.StrictObjectSchema<{
|
|
10889
|
+
readonly private: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
10890
|
+
readonly group: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
10891
|
+
}, undefined>;
|
|
10892
|
+
}, undefined>;
|
|
10863
10893
|
readonly ranking: v.StrictObjectSchema<{
|
|
10864
10894
|
readonly target: v.PicklistSchema<["messages", "contacts"], undefined>;
|
|
10865
10895
|
readonly measure: v.StringSchema<undefined>;
|
package/dist/entities/index.js
CHANGED
|
@@ -78,6 +78,7 @@ import { reactionChange } from "./reaction-change.js";
|
|
|
78
78
|
import { recipient, recipientAdded, recipientChange, recipientOff, recipientRemoved } from "./recipient.js";
|
|
79
79
|
import { recordedRun, runDetails, runHit, runLocation } from "./recorded-run.js";
|
|
80
80
|
import { relationship } from "./relationship.js";
|
|
81
|
+
import { responseTimes, waitingChats } from "./reply-stats.js";
|
|
81
82
|
import { retention } from "./retention.js";
|
|
82
83
|
import { savedSearch, searchHistoryClear } from "./saved-search.js";
|
|
83
84
|
import { sendAttempt } from "./send-attempt.js";
|
|
@@ -95,7 +96,7 @@ import { topic } from "./topic.js";
|
|
|
95
96
|
* subset of its fields, never a renamed one. Commands move onto these one by one; until then, a schema
|
|
96
97
|
* is the target, not a description of today's output.
|
|
97
98
|
*/
|
|
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, };
|
|
99
|
+
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, 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
100
|
export const ENTITIES = {
|
|
100
101
|
sync_progress: syncProgress,
|
|
101
102
|
...MEETING_RESULT_ENTITIES,
|
|
@@ -348,6 +349,8 @@ export const ENTITIES = {
|
|
|
348
349
|
topic,
|
|
349
350
|
folder,
|
|
350
351
|
message_counts: messageCounts,
|
|
352
|
+
response_times: responseTimes,
|
|
353
|
+
waiting_chats: waitingChats,
|
|
351
354
|
ranking,
|
|
352
355
|
admin_report: adminReport,
|
|
353
356
|
analysis_consent: analysisConsent,
|
|
@@ -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
|
+
});
|
package/dist/errors/reasons.d.ts
CHANGED
|
@@ -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", "
|
|
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[];
|
package/dist/errors/reasons.js
CHANGED
|
@@ -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"],
|
|
@@ -218,8 +219,7 @@ const OTHERS = [
|
|
|
218
219
|
["task.stale", "configuration_error", "the task changed since it was read; read it again"],
|
|
219
220
|
validation("task.column", "the task beside which it moves is in another column than the status asked for"),
|
|
220
221
|
validation("task.dependency-cycle", "the task would depend on itself"),
|
|
221
|
-
validation("task.
|
|
222
|
-
validation("task.is-subtask", "a subtask moves to another project only when it is detached from its parent in the same write"),
|
|
222
|
+
validation("task.is-subtask", "a subtask moves to another project alone only when it is detached from its parent in the same write"),
|
|
223
223
|
validation("task.duplicate-candidates", "open tasks may be the same job: attach to one, or add it as new"),
|
|
224
224
|
validation("task.already-read", "the message was already read for tasks: a read again only updates and closes tasks unless --open-new"),
|
|
225
225
|
validation("task.certainty-required", "an agent closes a task with its certainty, 0 to 1"),
|
|
@@ -300,7 +300,6 @@ export const DETAILS = [
|
|
|
300
300
|
"operation_ids",
|
|
301
301
|
"text_limit",
|
|
302
302
|
"store_refused_in",
|
|
303
|
-
"subtasks",
|
|
304
303
|
"partial_output",
|
|
305
304
|
"parent",
|
|
306
305
|
"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
|
-
/**
|
|
11
|
-
|
|
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[]>;
|
package/dist/mail/attachments.js
CHANGED
|
@@ -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
|
-
/**
|
|
7
|
-
|
|
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
|
-
:
|
|
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({
|
package/dist/mail/cli/index.d.ts
CHANGED
|
@@ -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,6 +29,7 @@ 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;
|
|
19
35
|
}
|
package/dist/mail/cli/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import { queryFilter, THREAD_COLUMNS } from "../../store/store.js";
|
|
|
21
21
|
import { acknowledgeCommand } from "./marks.js";
|
|
22
22
|
import { mailReviewCommand } from "./review.js";
|
|
23
23
|
export { mailMark, markItem, markText } from "./marks.js";
|
|
24
|
-
export { MAIL_REVIEW_QUERY, MAIL_REVIEW_STEPS } from "./review.js";
|
|
24
|
+
export { MAIL_REVIEW_QUERY, MAIL_REVIEW_STEPS, mailReviewSteps } from "./review.js";
|
|
25
25
|
/** A locked or broken store must fail the command, not read as "nothing stored yet". */
|
|
26
26
|
const absentAccount = (error) => {
|
|
27
27
|
if (error instanceof CliError && error.code === "not_found")
|
|
@@ -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";
|
package/dist/mail/cli/review.js
CHANGED
|
@@ -29,31 +29,44 @@ export const MAIL_REVIEW_QUERY = defineQuery({
|
|
|
29
29
|
nativeField({ name: "bulk", type: "kw", operators: ["term"], example: "bulk:false" }),
|
|
30
30
|
],
|
|
31
31
|
});
|
|
32
|
-
/** The mail review's steps; a host lists them beside its own so its step checks know them. */
|
|
33
|
-
export const
|
|
34
|
-
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
{ name: "cursor", required: true },
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
32
|
+
/** The mail review's steps where a host mounted them; a host lists them beside its own so its step checks know them. */
|
|
33
|
+
export const mailReviewSteps = (mount = {}) => {
|
|
34
|
+
const mailbox = mount.mailbox ? [{ name: mount.mailbox.option, required: true }] : [];
|
|
35
|
+
return defineSteps([
|
|
36
|
+
{
|
|
37
|
+
id: "mail-messages.acknowledge",
|
|
38
|
+
command: mount.acknowledge ?? "messages acknowledge",
|
|
39
|
+
args: [{ name: "holder", required: true }, { name: "cursor", required: true }, ...mailbox],
|
|
40
|
+
why: "once these are processed, this moves the holder's mark past them",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "mail-review.next",
|
|
44
|
+
command: mount.review ?? "review",
|
|
45
|
+
args: [{ name: "query", required: true }, ...mailbox],
|
|
46
|
+
why: "the next review starts where this one ended",
|
|
47
|
+
},
|
|
48
|
+
...(mount.conversation === null
|
|
49
|
+
? []
|
|
50
|
+
: [
|
|
51
|
+
{
|
|
52
|
+
id: "mail-conversations.show",
|
|
53
|
+
command: mount.conversation ?? "conversations show",
|
|
54
|
+
args: [{ name: "conversation", required: true }, ...mailbox],
|
|
55
|
+
why: "only the newest emails of the thread were read here; this reads it whole",
|
|
56
|
+
},
|
|
57
|
+
]),
|
|
58
|
+
]);
|
|
59
|
+
};
|
|
60
|
+
/** The steps of a mail CLI, whose profile names the mailbox. */
|
|
61
|
+
export const MAIL_REVIEW_STEPS = mailReviewSteps();
|
|
62
|
+
const stepsFor = (mount, mailbox) => {
|
|
63
|
+
const definitions = mailReviewSteps(mount);
|
|
64
|
+
const where = mount?.mailbox ? { [mount.mailbox.option]: mount.mailbox.value(mailbox) } : {};
|
|
65
|
+
return (id, args) => {
|
|
66
|
+
const definition = definitions.find((one) => one.id === id);
|
|
67
|
+
return definition ? [stepOf(definition, { args: { ...args, ...where } })] : [];
|
|
68
|
+
};
|
|
69
|
+
};
|
|
57
70
|
const waitingHours = (value) => value === true ? AGENDA_DEFAULTS.waiting / 3_600_000 : parseDuration(String(value), "--waiting") / 3_600_000;
|
|
58
71
|
const quoted = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
59
72
|
/** A file name for the mailbox's points: they describe the mailbox, whichever profile reads it. */
|
|
@@ -67,20 +80,21 @@ const markText = (marks) => {
|
|
|
67
80
|
const shown = said.filter(Boolean).join(" · ");
|
|
68
81
|
return shown ? `\n [${shown}]` : "";
|
|
69
82
|
};
|
|
70
|
-
const reviewAnswer = (found, mailbox, fresh) => {
|
|
83
|
+
const reviewAnswer = (found, mailbox, fresh, step) => {
|
|
71
84
|
const read = found.chats
|
|
72
85
|
.flatMap((chat) => chat.messages.map((message) => ({ ...message, chatTitle: chat.title })))
|
|
73
86
|
.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
74
87
|
const cut = found.chats.filter((chat) => chat.more);
|
|
75
88
|
const ref = (thread) => formatReference({ type: "chat", ...mailbox, chat: thread });
|
|
76
89
|
const turns = found.unanswered !== undefined || found.waiting !== undefined;
|
|
90
|
+
const wholeThreads = cut.flatMap((chat) => step("mail-conversations.show", { conversation: ref(chat.id) }));
|
|
77
91
|
const missing = [
|
|
78
92
|
...(cut.length > 0
|
|
79
93
|
? [
|
|
80
94
|
{
|
|
81
95
|
field: "items",
|
|
82
96
|
why: `only the newest emails of ${cut.length} thread(s)`,
|
|
83
|
-
step: "mail-conversations.show",
|
|
97
|
+
...(wholeThreads.length ? { step: "mail-conversations.show" } : {}),
|
|
84
98
|
},
|
|
85
99
|
]
|
|
86
100
|
: []),
|
|
@@ -98,13 +112,9 @@ const reviewAnswer = (found, mailbox, fresh) => {
|
|
|
98
112
|
];
|
|
99
113
|
const next = [
|
|
100
114
|
...(!fresh && !turns
|
|
101
|
-
?
|
|
102
|
-
stepOf(step("mail-review.next"), {
|
|
103
|
-
args: { query: `date>${quoted(found.complete ? found.until : found.since)}` },
|
|
104
|
-
}),
|
|
105
|
-
]
|
|
115
|
+
? step("mail-review.next", { query: `date>${quoted(found.complete ? found.until : found.since)}` })
|
|
106
116
|
: []),
|
|
107
|
-
...
|
|
117
|
+
...wholeThreads,
|
|
108
118
|
];
|
|
109
119
|
return listOf("review", {
|
|
110
120
|
items: read.map((message) => ({
|
|
@@ -165,12 +175,10 @@ const holderReview = async (command, mail, holder, query) => {
|
|
|
165
175
|
partial: false,
|
|
166
176
|
quiet: 0,
|
|
167
177
|
};
|
|
168
|
-
const
|
|
178
|
+
const step = stepsFor(mail.mount, mailbox);
|
|
179
|
+
const answer = reviewAnswer(found, mailbox, true, step);
|
|
169
180
|
const skipped = Object.entries(read.skipped).filter(([, count]) => count > 0);
|
|
170
|
-
answer.next = [
|
|
171
|
-
stepOf(step("mail-messages.acknowledge"), { args: { holder, cursor: read.cursor } }),
|
|
172
|
-
...(answer.next ?? []),
|
|
173
|
-
];
|
|
181
|
+
answer.next = [...step("mail-messages.acknowledge", { holder, cursor: read.cursor }), ...(answer.next ?? [])];
|
|
174
182
|
answer.missing = [
|
|
175
183
|
...(answer.missing ?? []),
|
|
176
184
|
...(read.held > 0 ? [{ field: "items", why: `${read.held} partial emails held until they are whole` }] : []),
|
|
@@ -254,7 +262,7 @@ an agent over MCP, and each scheduled run.`)
|
|
|
254
262
|
return { found, mailbox, saved };
|
|
255
263
|
});
|
|
256
264
|
const output = outputFor(this);
|
|
257
|
-
const answer = reviewAnswer(found, mailbox, saved !== undefined);
|
|
265
|
+
const answer = reviewAnswer(found, mailbox, saved !== undefined, stepsFor(mail.mount, mailbox));
|
|
258
266
|
if (meta)
|
|
259
267
|
answer.meta = { ...meta, ...answer.meta };
|
|
260
268
|
renderAnswer(this, output, answer, () => {
|
package/dist/mail/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { mailAccountKey } from "./account-key.js";
|
|
2
|
+
export { DEFAULT_ATTACHMENT_POLICY, type MailAttachmentPolicy, POLICY_SKIPS } from "./attachments.js";
|
|
2
3
|
export { BULK, bulkBox } from "./bulk.js";
|
|
3
4
|
export { factsOf } from "./facts.js";
|
|
4
5
|
export { MAIL_FINDINGS_KIND, MAIL_FINDINGS_PER_RUN, MAIL_FINDINGS_PER_SENDER, type MailFinding, type MailFindingsInput, mailFindingFingerprint, validateMailFindings, } from "./findings.js";
|
|
@@ -29,3 +30,4 @@ export type { Listed, MailSource, Quick, Searched } from "./source.js";
|
|
|
29
30
|
export { SEARCH_REFUSED, searchRefused } from "./source.js";
|
|
30
31
|
export { listedOf, type StoredMail } from "./stored.js";
|
|
31
32
|
export { MAX_ATTACHMENT_CHARS, type MailAccount, type MailIndexed, type MailSyncOptions, type MailSyncResult, RETRYABLE_ATTACHMENTS, syncMail, } from "./sync.js";
|
|
33
|
+
export { readEmailWhole, type WholeAttachment } from "./whole.js";
|
package/dist/mail/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { mailAccountKey } from "./account-key.js";
|
|
2
|
+
export { DEFAULT_ATTACHMENT_POLICY, POLICY_SKIPS } from "./attachments.js";
|
|
2
3
|
export { BULK, bulkBox } from "./bulk.js";
|
|
3
4
|
export { factsOf } from "./facts.js";
|
|
4
5
|
export { MAIL_FINDINGS_KIND, MAIL_FINDINGS_PER_RUN, MAIL_FINDINGS_PER_SENDER, mailFindingFingerprint, validateMailFindings, } from "./findings.js";
|
|
@@ -28,3 +29,4 @@ export { searchMailServer } from "./server-search.js";
|
|
|
28
29
|
export { SEARCH_REFUSED, searchRefused } from "./source.js";
|
|
29
30
|
export { listedOf } from "./stored.js";
|
|
30
31
|
export { MAX_ATTACHMENT_CHARS, RETRYABLE_ATTACHMENTS, syncMail, } from "./sync.js";
|
|
32
|
+
export { readEmailWhole } from "./whole.js";
|
package/dist/mail/partial.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BudgetStop } from "../background/budget.js";
|
|
2
2
|
import type { LoadEngine } from "../readers/types.js";
|
|
3
3
|
import type { MessageStore } from "../store/index.js";
|
|
4
|
+
import { type MailAttachmentPolicy } from "./attachments.js";
|
|
4
5
|
import type { MailSource } from "./source.js";
|
|
5
6
|
/** The `emails.metadata` key of an email stored from its first bytes: `{ bytes }` read of its `size`. */
|
|
6
7
|
export declare const PARTIAL = "partial";
|
|
@@ -23,7 +24,7 @@ export interface PartialOutcome {
|
|
|
23
24
|
* `identify` gives the stored id for the bytes read there. Each failed read is counted, and after `PARTIAL_TRIES`, or
|
|
24
25
|
* at once when the place holds another message, the email is left partial for good.
|
|
25
26
|
*/
|
|
26
|
-
export declare const completePartial: ({ store, source, accountId, address, folders, maxBodyChars, maxAttachmentChars, loadEngine, now, take, signal, }: {
|
|
27
|
+
export declare const completePartial: ({ store, source, accountId, address, folders, maxBodyChars, maxAttachmentChars, policy, loadEngine, now, take, signal, }: {
|
|
27
28
|
store: MessageStore;
|
|
28
29
|
source: MailSource;
|
|
29
30
|
accountId: number;
|
|
@@ -32,6 +33,7 @@ export declare const completePartial: ({ store, source, accountId, address, fold
|
|
|
32
33
|
folders: () => Promise<readonly string[]>;
|
|
33
34
|
maxBodyChars: number;
|
|
34
35
|
maxAttachmentChars: number;
|
|
36
|
+
policy?: MailAttachmentPolicy;
|
|
35
37
|
loadEngine: LoadEngine;
|
|
36
38
|
now: number;
|
|
37
39
|
/** A budgeted run's next request, or why there is none. */
|
package/dist/mail/partial.js
CHANGED
|
@@ -15,7 +15,7 @@ export const PARTIAL_TRIES = 3;
|
|
|
15
15
|
* `identify` gives the stored id for the bytes read there. Each failed read is counted, and after `PARTIAL_TRIES`, or
|
|
16
16
|
* at once when the place holds another message, the email is left partial for good.
|
|
17
17
|
*/
|
|
18
|
-
export const completePartial = async ({ store, source, accountId, address, folders, maxBodyChars, maxAttachmentChars, loadEngine, now, take, signal, }) => {
|
|
18
|
+
export const completePartial = async ({ store, source, accountId, address, folders, maxBodyChars, maxAttachmentChars, policy, loadEngine, now, take, signal, }) => {
|
|
19
19
|
const { emails, total, stuck } = await store.mail.partialEmails(accountId, PARTIAL_PER_RUN);
|
|
20
20
|
if (emails.length === 0)
|
|
21
21
|
return { completed: 0, left: 0, stuck };
|
|
@@ -89,7 +89,7 @@ export const completePartial = async ({ store, source, accountId, address, folde
|
|
|
89
89
|
}
|
|
90
90
|
try {
|
|
91
91
|
const mail = await parseMail(raw, maxBodyChars);
|
|
92
|
-
const files = await readAttachments(mail, email.externalId, { loadEngine, maxAttachmentChars, at: now, ...(signal ? { signal } : {}) }, attachments);
|
|
92
|
+
const files = await readAttachments(mail, email.externalId, { loadEngine, maxAttachmentChars, at: now, ...(policy ? { policy } : {}), ...(signal ? { signal } : {}) }, attachments);
|
|
93
93
|
const { [PARTIAL]: _partial, ...metadata } = email.metadata ?? {};
|
|
94
94
|
const whole = { ...item, messageId: email.externalId, threadId: email.threadExternalId };
|
|
95
95
|
await store.mail.saveThread({
|