@wirecat/cli-core 0.405.0 → 0.407.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 (127) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/dist/botcheck/reasons.d.ts +1 -1
  3. package/dist/cli/analysis-choice.js +1 -1
  4. package/dist/cli/answer-entity.d.ts +8 -0
  5. package/dist/cli/answer-entity.js +31 -0
  6. package/dist/cli/command-contract.js +5 -6
  7. package/dist/cli/commands-command.js +4 -1
  8. package/dist/cli/effects.js +1 -1
  9. package/dist/cli/embedding-choice.d.ts +1 -1
  10. package/dist/cli/embedding-choice.js +2 -2
  11. package/dist/cli/help/command-help.js +2 -2
  12. package/dist/cli/index.d.ts +6 -2
  13. package/dist/cli/index.js +6 -2
  14. package/dist/cli/json-fields.js +2 -2
  15. package/dist/cli/meeting-batch.d.ts +1 -1
  16. package/dist/cli/meeting-batch.js +20 -2
  17. package/dist/cli/meeting-project-candidates-command.js +0 -1
  18. package/dist/cli/meeting-results.d.ts +3 -0
  19. package/dist/cli/meeting-results.js +31 -0
  20. package/dist/cli/meeting-series-inventory-command.js +0 -1
  21. package/dist/cli/messenger/admin-commands.js +14 -14
  22. package/dist/cli/messenger/archive-commands.js +2 -2
  23. package/dist/cli/messenger/attachments-command.js +7 -8
  24. package/dist/cli/messenger/backfill-command.js +1 -1
  25. package/dist/cli/messenger/chat-answers.js +3 -3
  26. package/dist/cli/messenger/chat-lists.js +3 -3
  27. package/dist/cli/messenger/chats-command.js +8 -8
  28. package/dist/cli/messenger/chats-members-command.js +1 -1
  29. package/dist/cli/messenger/contacts-command.js +10 -7
  30. package/dist/cli/messenger/context.d.ts +11 -4
  31. package/dist/cli/messenger/context.js +6 -0
  32. package/dist/cli/messenger/conversation-examples.js +7 -6
  33. package/dist/cli/messenger/conversations-command.d.ts +0 -4
  34. package/dist/cli/messenger/conversations-command.js +130 -135
  35. package/dist/cli/messenger/counters-command.js +4 -2
  36. package/dist/cli/messenger/doctor-command.js +8 -4
  37. package/dist/cli/messenger/gaps-command.js +2 -2
  38. package/dist/cli/messenger/hearing-command.d.ts +1 -1
  39. package/dist/cli/messenger/hearing-command.js +1 -1
  40. package/dist/cli/messenger/images-command.js +1 -1
  41. package/dist/cli/messenger/inbox.js +1 -3
  42. package/dist/cli/messenger/lacks.d.ts +1 -0
  43. package/dist/cli/messenger/lacks.js +1 -1
  44. package/dist/cli/messenger/mcp-command.js +4 -4
  45. package/dist/cli/messenger/messages-comments-command.js +10 -5
  46. package/dist/cli/messenger/metadata-command.js +5 -5
  47. package/dist/cli/messenger/models-command.js +101 -90
  48. package/dist/cli/messenger/private-people-command.js +5 -6
  49. package/dist/cli/messenger/replies-edit-command.js +0 -1
  50. package/dist/cli/messenger/review.js +1 -3
  51. package/dist/cli/messenger/serve-relay.d.ts +1 -0
  52. package/dist/cli/messenger/stats-command.js +1 -1
  53. package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
  54. package/dist/cli/messenger/store-maintenance-command.js +4 -4
  55. package/dist/cli/messenger/stored.js +5 -0
  56. package/dist/cli/messenger/tags-command.js +4 -10
  57. package/dist/cli/messenger/tasks-command.js +3 -3
  58. package/dist/cli/messenger/transcribe-command.js +1 -1
  59. package/dist/cli/moved.d.ts +2 -0
  60. package/dist/cli/moved.js +39 -1
  61. package/dist/cli/permission-keys.js +2 -0
  62. package/dist/cli/result-fields.js +19 -1
  63. package/dist/cli/settings.js +1 -1
  64. package/dist/cli/steps.js +4 -4
  65. package/dist/cli/store-settings.js +1 -1
  66. package/dist/cli/upgrade-command.js +0 -1
  67. package/dist/domain/models.d.ts +3 -3
  68. package/dist/domain/task-categories.js +1 -1
  69. package/dist/embeddings/remote.js +1 -1
  70. package/dist/entities/contact.d.ts +6 -0
  71. package/dist/entities/contact.js +3 -0
  72. package/dist/entities/index.d.ts +139 -0
  73. package/dist/entities/index.js +4 -1
  74. package/dist/entities/member.d.ts +2 -2
  75. package/dist/entities/member.js +2 -2
  76. package/dist/entities/store-records.d.ts +11 -0
  77. package/dist/entities/store-records.js +1 -0
  78. package/dist/mcp/agent.d.ts +1 -0
  79. package/dist/mcp/agent.js +1 -1
  80. package/dist/mcp/tools/attachments.js +1 -1
  81. package/dist/meetings/cli/index.js +10 -3
  82. package/dist/meetings/index.d.ts +1 -0
  83. package/dist/meetings/index.js +1 -0
  84. package/dist/meetings/result-items.d.ts +127 -0
  85. package/dist/meetings/result-items.js +125 -0
  86. package/dist/ports/app.d.ts +1 -1
  87. package/dist/ports/messenger.d.ts +6 -1
  88. package/dist/search/task-similarity.js +1 -1
  89. package/dist/sends/guard.js +2 -2
  90. package/dist/sends/permission-aliases.d.ts +5 -0
  91. package/dist/sends/permission-aliases.js +49 -0
  92. package/dist/sends/permissions.js +33 -25
  93. package/dist/services/admin.js +7 -7
  94. package/dist/services/chats.js +1 -1
  95. package/dist/services/conversations.d.ts +1 -1
  96. package/dist/services/conversations.js +1 -1
  97. package/dist/services/counters.js +1 -1
  98. package/dist/services/embeddings-maintenance.js +2 -2
  99. package/dist/services/embeddings-search.js +1 -1
  100. package/dist/services/embeddings.d.ts +4 -4
  101. package/dist/services/messages-search.d.ts +1 -1
  102. package/dist/services/messages-search.js +2 -2
  103. package/dist/services/metadata.js +2 -2
  104. package/dist/services/notes-search.js +3 -3
  105. package/dist/services/search-catchup.js +1 -1
  106. package/dist/speech/install.d.ts +4 -2
  107. package/dist/speech/install.js +16 -4
  108. package/dist/speech/local.js +1 -1
  109. package/dist/speech/transcribe.js +1 -1
  110. package/dist/store/search-index.d.ts +1 -1
  111. package/dist/store/sqlite/attachment-texts.d.ts +1 -1
  112. package/dist/store/sqlite/attachment-texts.js +1 -1
  113. package/dist/store/sqlite/batches.js +1 -1
  114. package/dist/store/sqlite/involvements.d.ts +1 -1
  115. package/dist/store/sqlite/note-index.d.ts +2 -2
  116. package/dist/store/sqlite/note-index.js +2 -2
  117. package/dist/store/sqlite/search-index.d.ts +1 -1
  118. package/dist/store/sqlite/search-index.js +1 -1
  119. package/dist/store/sqlite/stems-fill.d.ts +3 -3
  120. package/dist/store/sqlite/stems-fill.js +3 -3
  121. package/dist/store/sqlite/stems.d.ts +1 -1
  122. package/dist/store/sqlite/task-pairs.d.ts +1 -1
  123. package/dist/store/sqlite/task-pairs.js +1 -1
  124. package/package.json +1 -1
  125. package/parity.json +130 -139
  126. package/skills/follow-ups/SKILL.md +5 -5
  127. package/skills/link-conversations/SKILL.md +8 -8
@@ -0,0 +1,127 @@
1
+ import * as v from "valibot";
2
+ export declare const MEETING_RESULT_ENTITIES: {
3
+ readonly meeting_ingestion: v.StrictObjectSchema<{
4
+ readonly meetings: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
5
+ readonly participants: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
6
+ readonly transcript_rows: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
7
+ readonly summaries: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
8
+ readonly warnings: v.ArraySchema<v.StrictObjectSchema<{
9
+ readonly external_id: v.StringSchema<undefined>;
10
+ readonly message: v.StringSchema<undefined>;
11
+ }, undefined>, undefined>;
12
+ readonly timezone: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
13
+ }, undefined>;
14
+ readonly meeting_export: v.UnionSchema<[v.StrictObjectSchema<{
15
+ readonly meeting_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
16
+ readonly format: v.PicklistSchema<["markdown", "vtt"], undefined>;
17
+ readonly filename: v.StringSchema<undefined>;
18
+ readonly content: v.StringSchema<undefined>;
19
+ }, undefined>, v.StrictObjectSchema<{
20
+ readonly meeting_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
21
+ readonly format: v.PicklistSchema<["markdown", "vtt"], undefined>;
22
+ readonly output: v.StringSchema<undefined>;
23
+ }, undefined>], undefined>;
24
+ readonly meeting_association: v.StrictObjectSchema<{
25
+ readonly scope: v.StrictObjectSchema<{
26
+ readonly provider: v.StringSchema<undefined>;
27
+ readonly account: v.StringSchema<undefined>;
28
+ }, undefined>;
29
+ readonly account_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
30
+ readonly meeting_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
31
+ readonly change: v.UnionSchema<[v.StrictObjectSchema<{
32
+ readonly action: v.LiteralSchema<"event.link", undefined>;
33
+ readonly event_id: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
34
+ readonly expected_event_id: v.OptionalSchema<v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>, undefined>;
35
+ }, undefined>, v.StrictObjectSchema<{
36
+ readonly action: v.LiteralSchema<"person.link", undefined>;
37
+ readonly participant_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
38
+ readonly person_uid: v.StringSchema<undefined>;
39
+ readonly expected_person_uid: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
40
+ readonly target_account_ids: v.OptionalSchema<v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>, undefined>;
41
+ }, undefined>], undefined>;
42
+ readonly applied: v.BooleanSchema<undefined>;
43
+ readonly result: v.OptionalSchema<v.UnionSchema<[v.StrictObjectSchema<{
44
+ readonly meeting_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
45
+ readonly event_id: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
46
+ readonly previous_event_id: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
47
+ readonly changed: v.BooleanSchema<undefined>;
48
+ }, undefined>, v.StrictObjectSchema<{
49
+ readonly meeting_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
50
+ readonly participant_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
51
+ readonly person_uid: v.StringSchema<undefined>;
52
+ readonly previous_person_uid: v.NullableSchema<v.StringSchema<undefined>, undefined>;
53
+ readonly changed: v.BooleanSchema<undefined>;
54
+ }, undefined>], undefined>, undefined>;
55
+ }, undefined>;
56
+ readonly meeting_transcription: v.UnionSchema<[v.StrictObjectSchema<{
57
+ readonly applied: v.LiteralSchema<false, undefined>;
58
+ readonly account_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
59
+ readonly external_id: v.StringSchema<undefined>;
60
+ readonly segments: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
61
+ readonly media: v.StrictObjectSchema<{
62
+ readonly id: v.StringSchema<undefined>;
63
+ readonly content_hash: v.StringSchema<undefined>;
64
+ readonly size: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
65
+ readonly duration_ms: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
66
+ readonly format: v.StringSchema<undefined>;
67
+ readonly sample_rate: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
68
+ readonly channels: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
69
+ readonly recognition: v.StrictObjectSchema<{
70
+ readonly engine: v.StringSchema<undefined>;
71
+ readonly model: v.StringSchema<undefined>;
72
+ readonly version: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
73
+ readonly timing: v.OptionalSchema<v.PicklistSchema<["fixed-windows", "recognition-segments"], undefined>, undefined>;
74
+ }, undefined>;
75
+ }, undefined>;
76
+ }, undefined>, v.StrictObjectSchema<{
77
+ readonly applied: v.LiteralSchema<true, undefined>;
78
+ readonly account_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
79
+ readonly external_id: v.StringSchema<undefined>;
80
+ readonly meeting_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
81
+ readonly created: v.BooleanSchema<undefined>;
82
+ readonly inserted_transcripts: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
83
+ readonly replayed_transcripts: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
84
+ readonly superseded_transcripts: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
85
+ readonly segments: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
86
+ readonly media_sha256: v.StringSchema<undefined>;
87
+ readonly engine: v.StringSchema<undefined>;
88
+ readonly model: v.StringSchema<undefined>;
89
+ readonly model_version: v.NullableSchema<v.StringSchema<undefined>, undefined>;
90
+ readonly timing: v.PicklistSchema<["fixed-windows", "recognition-segments"], undefined>;
91
+ }, undefined>], undefined>;
92
+ readonly meeting_embedding: v.UnionSchema<[v.StrictObjectSchema<{
93
+ readonly account_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
94
+ readonly model: v.StrictObjectSchema<{
95
+ readonly key: v.StringSchema<undefined>;
96
+ readonly dims: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
97
+ readonly kind: v.PicklistSchema<["local", "remote"], undefined>;
98
+ readonly model: v.StringSchema<undefined>;
99
+ }, undefined>;
100
+ readonly status: v.StrictObjectSchema<{
101
+ readonly chunks: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
102
+ readonly embedded: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
103
+ }, undefined>;
104
+ readonly applied: v.LiteralSchema<false, undefined>;
105
+ readonly scope: v.LiteralSchema<"existing-current-chunks", undefined>;
106
+ readonly rebuild_required: v.LiteralSchema<"explicit", undefined>;
107
+ }, undefined>, v.StrictObjectSchema<{
108
+ readonly account_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
109
+ readonly model: v.StrictObjectSchema<{
110
+ readonly key: v.StringSchema<undefined>;
111
+ readonly dims: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
112
+ readonly kind: v.PicklistSchema<["local", "remote"], undefined>;
113
+ readonly model: v.StringSchema<undefined>;
114
+ }, undefined>;
115
+ readonly saved: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
116
+ readonly skipped: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
117
+ readonly has_more: v.BooleanSchema<undefined>;
118
+ readonly next_hash: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
119
+ readonly applied: v.LiteralSchema<true, undefined>;
120
+ readonly rebuild: v.StrictObjectSchema<{
121
+ readonly transcripts: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
122
+ readonly chunks: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
123
+ readonly has_more: v.BooleanSchema<undefined>;
124
+ readonly next_transcript_id: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
125
+ }, undefined>;
126
+ }, undefined>], undefined>;
127
+ };
@@ -0,0 +1,125 @@
1
+ import * as v from "valibot";
2
+ const text = v.string();
3
+ const identifier = v.pipe(text, v.minLength(1));
4
+ const nullableText = v.nullable(text);
5
+ const count = v.pipe(v.number(), v.integer(), v.minValue(0));
6
+ const flag = v.boolean();
7
+ const scope = v.strictObject({ provider: text, account: text });
8
+ const run = v.strictObject({
9
+ meetings: count,
10
+ participants: count,
11
+ transcript_rows: count,
12
+ summaries: count,
13
+ warnings: v.array(v.strictObject({ external_id: text, message: text })),
14
+ timezone: v.optional(text),
15
+ });
16
+ const exported = v.union([
17
+ v.strictObject({ meeting_id: identifier, format: v.picklist(["markdown", "vtt"]), filename: text, content: text }),
18
+ v.strictObject({ meeting_id: identifier, format: v.picklist(["markdown", "vtt"]), output: text }),
19
+ ]);
20
+ const eventChange = v.strictObject({
21
+ action: v.literal("event.link"),
22
+ event_id: v.nullable(identifier),
23
+ expected_event_id: v.optional(v.nullable(identifier)),
24
+ });
25
+ const personChange = v.strictObject({
26
+ action: v.literal("person.link"),
27
+ participant_id: identifier,
28
+ person_uid: text,
29
+ expected_person_uid: v.optional(nullableText),
30
+ target_account_ids: v.optional(v.array(identifier)),
31
+ });
32
+ const eventResult = v.strictObject({
33
+ meeting_id: identifier,
34
+ event_id: v.nullable(identifier),
35
+ previous_event_id: v.nullable(identifier),
36
+ changed: flag,
37
+ });
38
+ const personResult = v.strictObject({
39
+ meeting_id: identifier,
40
+ participant_id: identifier,
41
+ person_uid: text,
42
+ previous_person_uid: nullableText,
43
+ changed: flag,
44
+ });
45
+ const meetingChange = v.strictObject({
46
+ scope,
47
+ account_id: identifier,
48
+ meeting_id: identifier,
49
+ change: v.union([eventChange, personChange]),
50
+ applied: flag,
51
+ result: v.optional(v.union([eventResult, personResult])),
52
+ });
53
+ const recognition = v.strictObject({
54
+ engine: text,
55
+ model: text,
56
+ version: v.optional(text),
57
+ timing: v.optional(v.picklist(["fixed-windows", "recognition-segments"])),
58
+ });
59
+ const transcription = v.union([
60
+ v.strictObject({
61
+ applied: v.literal(false),
62
+ account_id: identifier,
63
+ external_id: text,
64
+ segments: count,
65
+ media: v.strictObject({
66
+ id: text,
67
+ content_hash: text,
68
+ size: count,
69
+ duration_ms: count,
70
+ format: text,
71
+ sample_rate: count,
72
+ channels: count,
73
+ recognition,
74
+ }),
75
+ }),
76
+ v.strictObject({
77
+ applied: v.literal(true),
78
+ account_id: identifier,
79
+ external_id: text,
80
+ meeting_id: identifier,
81
+ created: flag,
82
+ inserted_transcripts: count,
83
+ replayed_transcripts: count,
84
+ superseded_transcripts: count,
85
+ segments: count,
86
+ media_sha256: text,
87
+ engine: text,
88
+ model: text,
89
+ model_version: nullableText,
90
+ timing: v.picklist(["fixed-windows", "recognition-segments"]),
91
+ }),
92
+ ]);
93
+ const embeddingModel = v.strictObject({ key: text, dims: count, kind: v.picklist(["local", "remote"]), model: text });
94
+ const embedding = v.union([
95
+ v.strictObject({
96
+ account_id: identifier,
97
+ model: embeddingModel,
98
+ status: v.strictObject({ chunks: count, embedded: count }),
99
+ applied: v.literal(false),
100
+ scope: v.literal("existing-current-chunks"),
101
+ rebuild_required: v.literal("explicit"),
102
+ }),
103
+ v.strictObject({
104
+ account_id: identifier,
105
+ model: embeddingModel,
106
+ saved: count,
107
+ skipped: count,
108
+ has_more: flag,
109
+ next_hash: v.optional(text),
110
+ applied: v.literal(true),
111
+ rebuild: v.strictObject({
112
+ transcripts: count,
113
+ chunks: count,
114
+ has_more: flag,
115
+ next_transcript_id: v.optional(identifier),
116
+ }),
117
+ }),
118
+ ]);
119
+ export const MEETING_RESULT_ENTITIES = {
120
+ meeting_ingestion: run,
121
+ meeting_export: exported,
122
+ meeting_association: meetingChange,
123
+ meeting_transcription: transcription,
124
+ meeting_embedding: embedding,
125
+ };
@@ -10,7 +10,7 @@ export interface AppIdentity {
10
10
  envPrefix: string;
11
11
  description: string;
12
12
  version: string;
13
- /** Where a problem report goes — a new-issue page. `doctor report create` names it. */
13
+ /** Where a problem report goes — a new-issue page. `doctor reports create` names it. */
14
14
  issues?: string;
15
15
  /** The language of the transcript a person reads: its day headings and the word for "you". `ru-RU` when unset. */
16
16
  locale?: string;
@@ -377,6 +377,11 @@ export interface HistoryBatch {
377
377
  votes?: HistoryVote[];
378
378
  reactions?: HistoryReaction[];
379
379
  stars?: HistoryStar[];
380
+ /** Deletions learned from a synced snapshot, scoped to their chat and applied after the batch's messages. */
381
+ deletions?: {
382
+ chatId: Id;
383
+ messageId: Id;
384
+ }[];
380
385
  events?: HistoryChatEvent[];
381
386
  }
382
387
  /** For a messenger that pushes its history to the client instead of answering for it (`Messenger.history: "store"`). */
@@ -488,7 +493,7 @@ export interface AccountTools {
488
493
  /** The owner's contact list as the messenger keeps it — the address book, not the chats. */
489
494
  addressBook(): Promise<Member[]>;
490
495
  }
491
- /** Everything the messenger says about one person — what `contacts profile` shows. */
496
+ /** Everything the messenger says about one person — what `contacts info` shows. */
492
497
  export interface PersonProfiles {
493
498
  /** Reading it tells them nothing. A chat that is not a person is refused. */
494
499
  profile(person: string): Promise<ProfileFacts>;
@@ -2,7 +2,7 @@ import { normalize } from "../store/normalize.js";
2
2
  import { createStemmer, DEFAULT_STEMMERS } from "./stem.js";
3
3
  import { amountOf, dayOf, dueDayOf } from "./task-signature.js";
4
4
  /**
5
- * How alike two tasks are, field by field, as the tasks plan's duplicate chain scores them
5
+ * How alike two tasks are, field by field, as the tasks update's duplicate chain scores them
6
6
  * (`docs/dev/tasks-plan.md`, "Duplicates — the comparison chain" § 1, 4, 5). Ported from the scorer that
7
7
  * tuned the weights, WireCatLabs/cli-testing `performance/tasks/duplicates/` (cli-testing#60); the counts
8
8
  * quoted beside each weight are that run's duplicate / other candidate pairs at the level.
@@ -12,12 +12,12 @@ const HOUR_MS = 60 * 60 * 1000;
12
12
  */
13
13
  const READS_UNDER_HOLD = new Set([
14
14
  "attachments.extract",
15
- "conversations.embed",
15
+ "conversations.embeddings",
16
16
  "conversations.links",
17
17
  "messages.server-search",
18
18
  "messages.sync-first",
19
19
  "stats.contacts.top.sync-first",
20
- "stats.messages.counters.refresh",
20
+ "stats.messages.counters.sync",
21
21
  "stats.messages.show.sync-first",
22
22
  "stats.messages.top.sync-first",
23
23
  "store.gaps.repair",
@@ -0,0 +1,5 @@
1
+ import type { Level } from "./permissions.js";
2
+ /** Canonical names for internal permission keys; saved moved paths are refused below. */
3
+ export declare const canonicalPermissionKey: (key: string) => string;
4
+ /** WC-PERM-8: a moved saved path must name the replacement before any command runs. */
5
+ export declare const canonicalPermissions: (levels: Readonly<Record<string, Level>>) => Record<string, Level>;
@@ -0,0 +1,49 @@
1
+ import { CliError } from "@wirecat/cli-base";
2
+ const RENAMED = [
3
+ ["models.audio.download", "models.download"],
4
+ ["models.text.download", "models.download"],
5
+ ["models.text.key", "models.keys"],
6
+ ["models.audio", "models"],
7
+ ["models.text", "models"],
8
+ ["doctor.report", "doctor.reports"],
9
+ ["contacts.aliases.rm", "contacts.aliases.remove"],
10
+ ["contacts.alias.rm", "contacts.aliases.remove"],
11
+ ["contacts.alias", "contacts.aliases"],
12
+ ["contacts.profile", "contacts.info"],
13
+ ["contacts.timeline", "contacts.events"],
14
+ ["chats.links.revoke", "chats.links.remove"],
15
+ ["chats.link.revoke", "chats.links.remove"],
16
+ ["chats.link", "chats.links"],
17
+ ["chats.members.audit", "chats.members.check"],
18
+ ["chats.requests.accept", "chats.requests.confirm"],
19
+ ["chats.requests.decline", "chats.requests.reject"],
20
+ ["metadata.get", "metadata.show"],
21
+ ["metadata.refresh", "metadata.sync"],
22
+ ["tasks.plan", "tasks.update"],
23
+ ["tags.auto", "tags.rebuild"],
24
+ ["attachments.text.set", "attachments.update"],
25
+ ["attachments.text", "attachments.update"],
26
+ ["conversations.build", "conversations.links.rebuild"],
27
+ ["conversations.embed", "conversations.embeddings"],
28
+ ["conversations.batches.next", "conversations.batches.show"],
29
+ ["conversations.consents.revoke", "conversations.consents.remove"],
30
+ ["stats.messages.counters.refresh", "stats.messages.counters.sync"],
31
+ ["store.gaps.plan", "store.gaps.show"],
32
+ ["store.reindex", "store.rebuild"],
33
+ ];
34
+ /** Canonical names for internal permission keys; saved moved paths are refused below. */
35
+ export const canonicalPermissionKey = (key) => {
36
+ if (key.startsWith("bot."))
37
+ return `bot.${canonicalPermissionKey(key.slice(4))}`;
38
+ const renamed = RENAMED.find(([old]) => key === old || key.startsWith(`${old}.`));
39
+ return renamed ? renamed[1] + key.slice(renamed[0].length) : key;
40
+ };
41
+ /** WC-PERM-8: a moved saved path must name the replacement before any command runs. */
42
+ export const canonicalPermissions = (levels) => {
43
+ for (const key of Object.keys(levels)) {
44
+ const canonical = canonicalPermissionKey(key);
45
+ if (key !== canonical)
46
+ throw new CliError("configuration_error", `permission path moved — rename permissions.${key} to permissions.${canonical}`, { permission: key, replacement: canonical, retryable: false });
47
+ }
48
+ return { ...levels };
49
+ };
@@ -1,4 +1,5 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
+ import { canonicalPermissionKey, canonicalPermissions } from "./permission-aliases.js";
2
3
  /**
3
4
  * What a profile may be allowed to do (max-cli `CLI-37`, `NEED-251`). One name per thing the owner would
4
5
  * recognise, so `sessions` — which logs the owner out of their phone — never rides along with
@@ -91,12 +92,11 @@ export const DEFAULT_PERMISSIONS = {
91
92
  "server.start": "ask",
92
93
  "server.stop": "ask",
93
94
  "server.restart": "ask",
94
- "models.audio.download": "ask",
95
- "models.text.download": "ask",
95
+ "models.download": "ask",
96
96
  "images.models.download": "ask",
97
97
  "schedule.run": "ask",
98
98
  "schedule.exec": "ask",
99
- "doctor.report.create": "ask",
99
+ "doctor.reports.create": "ask",
100
100
  };
101
101
  /** Commands under a housekeeping word, or store maintenance, that still have a key of their own: their path. */
102
102
  const KEYED_UNDER_HOUSEKEEPING = new Set(Object.keys(DEFAULT_PERMISSIONS).filter((key) => /^(store|server|models|images|schedule|doctor)\./.test(key)));
@@ -163,7 +163,7 @@ const OLD_WORDS = {
163
163
  "chats.leave",
164
164
  "chats.members",
165
165
  "chats.admins",
166
- "chats.link",
166
+ "chats.links",
167
167
  "chats.requests",
168
168
  "chats.moderate",
169
169
  "topics.enable",
@@ -199,7 +199,7 @@ export const layerPermissions = (layers) => {
199
199
  const sources = {};
200
200
  for (const [from, layer] of layers) {
201
201
  const nearer = Object.keys(levels);
202
- for (const [key, level] of Object.entries(layer ?? {})) {
202
+ for (const [key, level] of Object.entries(canonicalPermissions(layer ?? {}))) {
203
203
  if (nearer.some((named) => key === named || key.startsWith(`${named}.`)))
204
204
  continue;
205
205
  levels[key] = level;
@@ -236,6 +236,8 @@ const FORMER_KEYS = { "chats.unmute": "chats.mute" };
236
236
  * A path nothing names is allowed.
237
237
  */
238
238
  export const levelFor = (permissions, key, defaults = DEFAULT_PERMISSIONS) => {
239
+ permissions = canonicalPermissions(permissions);
240
+ key = canonicalPermissionKey(key);
239
241
  const found = profileLevel(permissions, key, defaults);
240
242
  const ceiling = permissions[CEILING_KEY];
241
243
  return ceiling && ORDER[ceiling] < ORDER[found.level] ? { level: ceiling, key: CEILING_KEY } : found;
@@ -290,7 +292,7 @@ const KIND_KEYS = {
290
292
  /** The command path a guarded write belongs to, when the caller did not name it. */
291
293
  export const keyForWrite = (kind, action) => {
292
294
  if (kind === "chat")
293
- return CHAT_KEYS[action ?? ""] ?? `chats.${action ?? "update"}`;
295
+ return canonicalPermissionKey(CHAT_KEYS[action ?? ""] ?? `chats.${action ?? "update"}`);
294
296
  if (kind === "account") {
295
297
  const key = ACCOUNT_KEYS[action];
296
298
  if (!key)
@@ -301,6 +303,7 @@ export const keyForWrite = (kind, action) => {
301
303
  };
302
304
  /** `keyForWrite` read backwards, as far as a command's key says it: an unpin is a `pin`, a chat change names its action. */
303
305
  export const writeOfKey = (key) => {
306
+ key = canonicalPermissionKey(key);
304
307
  const account = Object.entries(ACCOUNT_KEYS).find(([, named]) => named === key)?.[0];
305
308
  if (account)
306
309
  return { kind: "account", action: account };
@@ -324,14 +327,14 @@ const NAMED_WRITE_KEYS = [
324
327
  "messages.press",
325
328
  "chats.start",
326
329
  "chats.app",
327
- "stats.messages.counters.refresh",
330
+ "stats.messages.counters.sync",
328
331
  "stats.messages.show.sync-first",
329
332
  "stats.messages.top.sync-first",
330
333
  "stats.contacts.top.sync-first",
331
334
  "account.sessions.list",
332
335
  "attachments.extract",
333
336
  "attachments.move",
334
- "attachments.text.set",
337
+ "attachments.update",
335
338
  "bot.callbacks.answer",
336
339
  "bot.chats.action",
337
340
  "bot.chats.admins.add",
@@ -346,14 +349,14 @@ const NAMED_WRITE_KEYS = [
346
349
  "bot.messages.unpin",
347
350
  "chats.admins.add",
348
351
  "chats.admins.remove",
349
- "chats.link.create",
350
- "chats.link.reset",
351
- "chats.link.revoke",
352
- "chats.link.update",
352
+ "chats.links.create",
353
+ "chats.links.reset",
354
+ "chats.links.remove",
355
+ "chats.links.update",
353
356
  "chats.members.add",
354
357
  "chats.members.remove",
355
358
  "chats.moderate",
356
- "conversations.embed",
359
+ "conversations.embeddings",
357
360
  "conversations.links",
358
361
  "polls.close",
359
362
  "polls.create",
@@ -361,13 +364,13 @@ const NAMED_WRITE_KEYS = [
361
364
  "searches.clear",
362
365
  "searches.create",
363
366
  "searches.delete",
364
- "contacts.alias.set",
365
- "contacts.alias.rm",
367
+ "contacts.aliases.set",
368
+ "contacts.aliases.remove",
366
369
  "contacts.notes.add",
367
370
  "contacts.notes.edit",
368
371
  "contacts.notes.remove",
369
- "tags.auto",
370
- "metadata.refresh",
372
+ "tags.rebuild",
373
+ "metadata.sync",
371
374
  "mentions.rebuild",
372
375
  "mentions.confirm",
373
376
  "mentions.reject",
@@ -380,7 +383,7 @@ const NAMED_WRITE_KEYS = [
380
383
  "tasks.duplicates.reject",
381
384
  "tasks.link",
382
385
  "tasks.merge",
383
- "tasks.plan",
386
+ "tasks.update",
384
387
  "tasks.release",
385
388
  "tasks.scan",
386
389
  "tasks.unmerge",
@@ -397,7 +400,7 @@ export const WRITE_KEYS = [
397
400
  ...Object.values(ACCOUNT_KEYS),
398
401
  ...Object.values(CHAT_KEYS),
399
402
  ...PERMISSIONS.flatMap((word) => [...OLD_WORDS[word], ...botKeysOf(word)]),
400
- ]),
403
+ ].map(canonicalPermissionKey)),
401
404
  ];
402
405
  /** Commands that look after the tool and its files and never show a message: no level stops them. */
403
406
  const HOUSEKEEPING = new Set([
@@ -483,6 +486,9 @@ export const readKeysForCommand = (path) => {
483
486
  * `undefined` for a path this package does not know — a CLI's own command, which its CLI maps.
484
487
  */
485
488
  export const keyForCommand = (path) => {
489
+ if (path.length === 0)
490
+ return null;
491
+ path = canonicalPermissionKey(path.join(".")).split(".");
486
492
  const [top, next] = path;
487
493
  if (top === "bot") {
488
494
  if (next === undefined || BOT_HOUSEKEEPING.has(next))
@@ -505,11 +511,12 @@ export const keyForCommand = (path) => {
505
511
  return null;
506
512
  // The agent's answers write only to the local store: their own key, so read-only messages can still link (phase 4 A10).
507
513
  if (top === "conversations" && next === "links")
508
- return "conversations.links";
514
+ return path[2] === "rebuild" ? "conversations.links.rebuild" : "conversations.links";
509
515
  // Vectors are written to the local store only, and the text never leaves the machine (phase 5 E10).
510
- if (((top === "conversations" || top === "images") && next === "embed") ||
516
+ if ((top === "conversations" && next === "embeddings") ||
517
+ (top === "images" && next === "embed") ||
511
518
  (top === "conversations" && next === "rebuild"))
512
- return "conversations.embed";
519
+ return "conversations.embeddings";
513
520
  // Which files a message has, and where they are saved, tells as much as a message does; so does who
514
521
  // shared which document link, and when.
515
522
  if ((top === "attachments" && next === "list") || (top === "chats" && next === "documents"))
@@ -521,8 +528,7 @@ export const keyForCommand = (path) => {
521
528
  return path.join(".");
522
529
  if (top === "messages" && next === "sync")
523
530
  return "messages";
524
- if (SHOW_MESSAGES.has(top) ||
525
- (top === "contacts" && (next === "context" || next === "prepare" || next === "timeline")))
531
+ if (SHOW_MESSAGES.has(top) || (top === "contacts" && (next === "context" || next === "prepare" || next === "events")))
526
532
  return "messages";
527
533
  if (RESOURCES.includes(top))
528
534
  return path.join(".");
@@ -538,10 +544,12 @@ export const permissionOverrides = (entries = []) => {
538
544
  throw new CliError("validation_error", "--permission takes a command path and level, e.g. messages.send=allow (deny, readonly, ask, allow)");
539
545
  levels[key] = level;
540
546
  }
541
- return levels;
547
+ return canonicalPermissions(levels);
542
548
  };
543
549
  /** No startup flag grants access: an override may narrow the profile's level, never lift it or a key under it. */
544
550
  export const assertOverridesTighten = (overrides, profile) => {
551
+ overrides = canonicalPermissions(overrides);
552
+ profile = canonicalPermissions(profile);
545
553
  for (const [key, level] of Object.entries(overrides)) {
546
554
  const under = Object.keys(profile).filter((named) => named.startsWith(`${key}.`));
547
555
  const lifted = [key, ...under].find((named) => ORDER[level] > ORDER[levelFor(profile, named).level]);
@@ -111,14 +111,14 @@ export const adminService = (deps) => {
111
111
  return { operationId, chat: card };
112
112
  },
113
113
  link: async (chat) => {
114
- const group = await capability(await online("chats link show"), "group", "read a group")(chat);
114
+ const group = await capability(await online("chats links show"), "group", "read a group")(chat);
115
115
  if (group.link === null) {
116
116
  throw new CliError("not_found", `${group.title ?? group.id} shows you no invite link — only admins, or members when the group allows it`);
117
117
  }
118
118
  return { chatId: group.id, title: group.title, link: group.link };
119
119
  },
120
120
  resetLink: async (chat) => {
121
- const connection = await online("chats link reset");
121
+ const connection = await online("chats links reset");
122
122
  const reset = capability(connection, "resetInviteLink", "replace an invite link");
123
123
  const { id: chatId } = await connection.resolve(chat);
124
124
  const operationId = newOperationId();
@@ -127,7 +127,7 @@ export const adminService = (deps) => {
127
127
  },
128
128
  createLink: async (chat, { approval, expires, maxUses }) => {
129
129
  const expiresAt = linkLimits({ approval, expires, maxUses });
130
- const connection = await online("chats link create");
130
+ const connection = await online("chats links create");
131
131
  const create = capability(connection, "createInviteLink", "make another invite link");
132
132
  const { id: chatId } = await connection.resolve(chat);
133
133
  const operationId = newOperationId();
@@ -193,8 +193,8 @@ export const adminService = (deps) => {
193
193
  },
194
194
  revokeLink: async (chat, link) => {
195
195
  if (link.trim() === "")
196
- throw new CliError("validation_error", "which link? give it as `chats link list` shows it");
197
- const connection = await online("chats link revoke");
196
+ throw new CliError("validation_error", "which link? give it as `chats links list` shows it");
197
+ const connection = await online("chats links remove");
198
198
  const revoke = capability(connection, "revokeInviteLink", "revoke an invite link");
199
199
  const { id: chatId } = await connection.resolve(chat);
200
200
  const operationId = newOperationId();
@@ -203,12 +203,12 @@ export const adminService = (deps) => {
203
203
  },
204
204
  updateLink: async (chat, link, { approval, expires, maxUses }) => {
205
205
  if (link.trim() === "")
206
- throw new CliError("validation_error", "which link? give it as `chats link list` shows it");
206
+ throw new CliError("validation_error", "which link? give it as `chats links list` shows it");
207
207
  if (approval === undefined && expires === undefined && maxUses === undefined)
208
208
  throw new CliError("validation_error", "nothing to change: give --approval, --no-approval, --expire-time or --max-uses");
209
209
  const limited = linkLimits({ approval, expires: expires === "never" ? undefined : expires, maxUses });
210
210
  const expiresAt = expires === "never" ? null : limited;
211
- const connection = await online("chats link update");
211
+ const connection = await online("chats links update");
212
212
  const update = capability(connection, "updateInviteLink", "change an invite link");
213
213
  const { id: chatId } = await connection.resolve(chat);
214
214
  const change = {
@@ -161,7 +161,7 @@ export const chatsService = (deps) => ({
161
161
  },
162
162
  audit: async (chat, { budget = AUDIT_BUDGET, minScore = AUDIT_MIN_SCORE, pauseMs = AUDIT_PAUSE_MS, deep = 0, registry }) => {
163
163
  if (deps.offline) {
164
- throw new CliError("validation_error", "`chats members audit` reads the member list from the messenger; not with --offline");
164
+ throw new CliError("validation_error", "`chats members check` reads the member list from the messenger; not with --offline");
165
165
  }
166
166
  const connection = await deps.connection();
167
167
  const { chatId: id, members: read, more, participants } = await readMembers(connection, chat, budget, pauseMs);
@@ -61,7 +61,7 @@ export interface ConversationsService {
61
61
  batchStatus(chat: string, size: number): Promise<BatchStatus>;
62
62
  /** The next window for the agent; `undefined` when every message is answered (A1–A4). */
63
63
  nextBatch(chat: string, size: number): Promise<LinkBatch | undefined>;
64
- /** Stores the agent's answer to a batch, all or nothing (A5); `conversations build` then uses it. */
64
+ /** Stores the agent's answer to a batch, all or nothing (A5); `conversations links rebuild` then uses it. */
65
65
  addAnswers(batch: string, answer: AgentAnswer): Promise<{
66
66
  chat: Id;
67
67
  stored: number;
@@ -39,7 +39,7 @@ export const conversationsService = (deps) => {
39
39
  const store = await accountStore(deps);
40
40
  return { store, chatId: await chatIdIn(deps.messenger, chat, store, { includeLeft: true }) };
41
41
  };
42
- const notBuilt = (chatId) => new CliError("not_found", `chat ${chatId} has no conversations yet — \`${typedCommand(deps.messenger.app.command, `conversations build --chat ${chatId}`)}\``);
42
+ const notBuilt = (chatId) => new CliError("not_found", `chat ${chatId} has no conversations yet — \`${typedCommand(deps.messenger.app.command, `conversations links rebuild --chat ${chatId}`)}\``);
43
43
  return {
44
44
  build: async (chat, { maxMessages, check } = {}) => {
45
45
  check?.();
@@ -115,7 +115,7 @@ export const countersService = (deps) => {
115
115
  return preview;
116
116
  if (deps.offline)
117
117
  throw new CliError("validation_error", "counter refresh requires an online session");
118
- const request = { chatId: null, key: "stats.messages.counters.refresh" };
118
+ const request = { chatId: null, key: "stats.messages.counters.sync" };
119
119
  await deps.guard.ask?.(request);
120
120
  deps.guard.check(request, { reserve: false });
121
121
  const controller = new AbortController(), timer = setTimeout(() => controller.abort(new Error("counter refresh time budget exhausted")), time);