@wirecat/cli-core 0.550.0 → 0.551.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 (128) hide show
  1. package/CHANGELOG.md +176 -0
  2. package/THIRD_PARTY_NOTICES +41 -0
  3. package/dist/calendar/ics.d.ts +1 -1
  4. package/dist/calendar/ics.js +5 -2
  5. package/dist/calendar/windows-zones.generated.d.ts +1 -0
  6. package/dist/calendar/windows-zones.generated.js +146 -0
  7. package/dist/cli/compact.js +2 -0
  8. package/dist/cli/index.d.ts +3 -0
  9. package/dist/cli/index.js +3 -0
  10. package/dist/cli/meeting-findings-input.js +4 -3
  11. package/dist/cli/messenger/backfill-command.js +28 -1
  12. package/dist/cli/messenger/context.js +1 -0
  13. package/dist/cli/messenger/history-gaps.js +4 -4
  14. package/dist/cli/messenger/mcp-command.js +1 -0
  15. package/dist/cli/messenger/messages-delete-command.js +7 -8
  16. package/dist/cli/messenger/projects-command.js +47 -2
  17. package/dist/cli/payment-query.d.ts +8 -0
  18. package/dist/cli/payment-query.js +27 -0
  19. package/dist/cli/project-status-rows.d.ts +8 -0
  20. package/dist/cli/project-status-rows.js +102 -0
  21. package/dist/cli/project-status.d.ts +132 -0
  22. package/dist/cli/project-status.js +248 -0
  23. package/dist/cli/steps.js +4 -1
  24. package/dist/domain/knowledge.d.ts +1 -1
  25. package/dist/domain/knowledge.js +1 -1
  26. package/dist/domain/status-words.d.ts +3 -0
  27. package/dist/domain/status-words.js +44 -0
  28. package/dist/entities/fact.d.ts +12 -1
  29. package/dist/entities/fact.js +13 -0
  30. package/dist/entities/index.d.ts +18 -2
  31. package/dist/entities/meeting-findings.d.ts +1 -1
  32. package/dist/entities/message-transfer.d.ts +16 -0
  33. package/dist/entities/message-transfer.js +6 -0
  34. package/dist/mcp/app-server.js +1 -0
  35. package/dist/mcp/http/oauth.d.ts +2 -1
  36. package/dist/mcp/http/oauth.js +2 -2
  37. package/dist/mcp/http/serve.d.ts +2 -0
  38. package/dist/mcp/http/serve.js +2 -1
  39. package/dist/mcp/server.js +1 -0
  40. package/dist/mcp/surface.js +1 -1
  41. package/dist/meetings/findings-rules.d.ts +14 -3
  42. package/dist/meetings/findings-rules.js +16 -2
  43. package/dist/meetings/index.d.ts +3 -2
  44. package/dist/meetings/index.js +2 -1
  45. package/dist/meetings/meeting-context.d.ts +2 -3
  46. package/dist/meetings/meeting-context.js +3 -4
  47. package/dist/meetings/payment.d.ts +27 -0
  48. package/dist/meetings/payment.js +81 -0
  49. package/dist/services/archive.d.ts +13 -0
  50. package/dist/services/archive.js +56 -5
  51. package/dist/services/deps.d.ts +3 -0
  52. package/dist/services/history-limits.d.ts +2 -0
  53. package/dist/services/index.d.ts +4 -1
  54. package/dist/services/index.js +4 -1
  55. package/dist/services/job-query.d.ts +5 -2
  56. package/dist/services/job-query.js +5 -2
  57. package/dist/services/link-scorers/alias-hits.d.ts +7 -0
  58. package/dist/services/link-scorers/alias-hits.js +54 -0
  59. package/dist/services/link-scorers/chat-senders.d.ts +6 -0
  60. package/dist/services/link-scorers/chat-senders.js +56 -0
  61. package/dist/services/link-scorers/chat-title.d.ts +6 -0
  62. package/dist/services/link-scorers/chat-title.js +44 -0
  63. package/dist/services/link-scorers/email-domain.d.ts +6 -0
  64. package/dist/services/link-scorers/email-domain.js +64 -0
  65. package/dist/services/link-scorers/index.d.ts +9 -0
  66. package/dist/services/link-scorers/index.js +8 -0
  67. package/dist/services/link-scorers/mail-thread.d.ts +3 -0
  68. package/dist/services/link-scorers/mail-thread.js +74 -0
  69. package/dist/services/link-scorers/member-chats.d.ts +3 -0
  70. package/dist/services/link-scorers/member-chats.js +40 -0
  71. package/dist/services/link-scorers/member-dialog.d.ts +3 -0
  72. package/dist/services/link-scorers/member-dialog.js +34 -0
  73. package/dist/services/link-scorers/scan.d.ts +41 -0
  74. package/dist/services/link-scorers/scan.js +244 -0
  75. package/dist/services/link-scorers/shared.d.ts +26 -0
  76. package/dist/services/link-scorers/shared.js +48 -0
  77. package/dist/services/link-scorers/types.d.ts +77 -0
  78. package/dist/services/link-scorers/types.js +1 -0
  79. package/dist/services/project-status/index.d.ts +71 -0
  80. package/dist/services/project-status/index.js +196 -0
  81. package/dist/services/project-status/items.d.ts +10 -0
  82. package/dist/services/project-status/items.js +23 -0
  83. package/dist/services/project-status/signals/amounts.d.ts +20 -0
  84. package/dist/services/project-status/signals/amounts.js +91 -0
  85. package/dist/services/project-status/signals/children.d.ts +13 -0
  86. package/dist/services/project-status/signals/children.js +28 -0
  87. package/dist/services/project-status/signals/coverage.d.ts +13 -0
  88. package/dist/services/project-status/signals/coverage.js +30 -0
  89. package/dist/services/project-status/signals/index.d.ts +3 -0
  90. package/dist/services/project-status/signals/index.js +21 -0
  91. package/dist/services/project-status/signals/last-message.d.ts +4 -0
  92. package/dist/services/project-status/signals/last-message.js +22 -0
  93. package/dist/services/project-status/signals/payments.d.ts +9 -0
  94. package/dist/services/project-status/signals/payments.js +24 -0
  95. package/dist/services/project-status/signals/promises.d.ts +17 -0
  96. package/dist/services/project-status/signals/promises.js +109 -0
  97. package/dist/services/project-status/signals/questions.d.ts +8 -0
  98. package/dist/services/project-status/signals/questions.js +38 -0
  99. package/dist/services/project-status/signals/reply-next.d.ts +19 -0
  100. package/dist/services/project-status/signals/reply-next.js +75 -0
  101. package/dist/services/project-status/signals/topics.d.ts +10 -0
  102. package/dist/services/project-status/signals/topics.js +50 -0
  103. package/dist/services/project-status/types.d.ts +84 -0
  104. package/dist/services/project-status/types.js +1 -0
  105. package/dist/services/proposals/index.d.ts +1 -0
  106. package/dist/services/proposals/index.js +1 -0
  107. package/dist/services/proposals/kinds/findings.d.ts +3 -0
  108. package/dist/services/proposals/kinds/findings.js +8 -1
  109. package/dist/store/decisions.d.ts +5 -1
  110. package/dist/store/decisions.js +5 -1
  111. package/dist/store/evidence-findings.js +1 -0
  112. package/dist/store/index.d.ts +2 -1
  113. package/dist/store/index.js +1 -0
  114. package/dist/store/project-transfer/request.d.ts +1 -1
  115. package/dist/store/project-transfer/schema.d.ts +2 -2
  116. package/dist/store/project-transfer/snapshot-model.d.ts +2 -2
  117. package/dist/store/sqlite/evidence-findings.js +5 -1
  118. package/dist/store/sqlite/facts.d.ts +13 -0
  119. package/dist/store/sqlite/facts.js +64 -6
  120. package/dist/store/sqlite/findings-core.js +14 -1
  121. package/dist/store/sqlite/knowledge.d.ts +16 -2
  122. package/dist/store/sqlite/knowledge.js +1 -0
  123. package/dist/store/sqlite/project-transfer/snapshot.d.ts +1 -1
  124. package/dist/store/sqlite/relation-proposals.d.ts +29 -2
  125. package/dist/store/sqlite/relation-proposals.js +75 -4
  126. package/dist/store/store.d.ts +1 -0
  127. package/package.json +3 -3
  128. package/parity.json +11 -0
@@ -1,5 +1,5 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
- import { findingFingerprint, findingHash, } from "../../meetings/index.js";
2
+ import { findingFingerprint, findingHash, paymentTitle, } from "../../meetings/index.js";
3
3
  import { factOperationsOver } from "./facts.js";
4
4
  import { linkEvidence } from "./memories.js";
5
5
  import { requiredThing } from "./notes.js";
@@ -81,6 +81,19 @@ export const findingsCoreOver = (context, kind) => {
81
81
  });
82
82
  return facts.acceptChecked(fact.ref, decidedBy).ref;
83
83
  }
84
+ if (finding.kind === "payment") {
85
+ const fact = facts.add({
86
+ type: "payment",
87
+ statement: finding.statement ?? paymentTitle(finding),
88
+ payment: finding.payment,
89
+ ...(finding.project === undefined ? {} : { project: finding.project }),
90
+ ...(finding.subject === undefined ? {} : { subject: finding.subject }),
91
+ ...(finding.supersedes === undefined ? {} : { supersedes: finding.supersedes }),
92
+ evidence,
93
+ by: agent,
94
+ });
95
+ return facts.acceptChecked(fact.ref, decidedBy).ref;
96
+ }
84
97
  if (finding.project === undefined)
85
98
  return invalid("a task finding names the project the task goes in");
86
99
  const task = tasks.create({
@@ -6,7 +6,7 @@ import type { Author } from "./memories.js";
6
6
  import { type Link } from "./notes.js";
7
7
  import type { StoreContext } from "./open.js";
8
8
  import type { ProposedAction } from "./proposed-actions.js";
9
- import { type KnowledgeRelation, type LinkDecision, type RelateInput, type RelationUpdate } from "./relation-proposals.js";
9
+ import { type KnowledgeRelation, type LinkDecision, type RelateInput, type RelationUpdate, type Scored } from "./relation-proposals.js";
10
10
  import type { TagCreationOptions } from "./tag-creation.js";
11
11
  import { type Label, type TagKind, type TagRow } from "./tags.js";
12
12
  import { type ThingState } from "./things.js";
@@ -190,7 +190,21 @@ export interface KnowledgeStore extends VocabularyStore, MeetingProjectsStore {
190
190
  purpose?: string;
191
191
  evidence?: string[];
192
192
  reason?: string;
193
- }, by: Exclude<Author, "owner">): Promise<ProposedAction>;
193
+ } & Scored, by: Exclude<Author, "owner">): Promise<ProposedAction>;
194
+ /**
195
+ * Where a pair stands for a scan: `linked` when a link already joins it, `rejected` when any `relationship`
196
+ * proposal for it was rejected, and the pending proposal if one waits. `undefined` when either end is not stored.
197
+ */
198
+ relationState(key: KnowledgeScope, input: {
199
+ from: string;
200
+ to: string;
201
+ kind: KnowledgeRelation["kind"];
202
+ }): Promise<{
203
+ linked: boolean;
204
+ confirmed: boolean;
205
+ rejected: boolean;
206
+ pending: ProposedAction | null;
207
+ } | undefined>;
194
208
  /** The owner sets a relationship's role and purpose; `null` clears one. */
195
209
  updateRelation(key: KnowledgeScope, id: string, input: RelationUpdate & {
196
210
  by: "owner";
@@ -562,6 +562,7 @@ export const knowledgeStoreOver = (context) => {
562
562
  });
563
563
  return proposals.propose(from, input, by);
564
564
  },
565
+ relationState: async (key, input) => proposals.state(() => pair(key, input), input),
565
566
  updateRelation: (async (key, id, input) => {
566
567
  checked(key);
567
568
  const link = linkOf(database, relation(id));
@@ -5,7 +5,7 @@ export declare const projectAnnotationSnapshot: (database: CacheDatabase, projec
5
5
  format: "wirecat.project-annotations";
6
6
  facts: {
7
7
  ref: string;
8
- type: "decision" | "fact" | "promise" | "question" | "statement";
8
+ type: "decision" | "fact" | "payment" | "promise" | "question" | "statement";
9
9
  statement: string;
10
10
  status: "accepted" | "proposed" | "rejected" | "reversed" | "superseded";
11
11
  subject: string | null;
@@ -5,6 +5,7 @@ import { type Link } from "./notes.js";
5
5
  import type { StoreContext } from "./open.js";
6
6
  import { type ProposedAction } from "./proposed-actions.js";
7
7
  import { type Thing } from "./things.js";
8
+ type Row = Record<string, unknown>;
8
9
  export interface KnowledgeRelation {
9
10
  id: string;
10
11
  from: string;
@@ -32,7 +33,23 @@ export interface RelateInput {
32
33
  export declare const relationOf: (link: Link) => KnowledgeRelation;
33
34
  /** `relationship` proposals in `proposed_actions`: an agent's or a rule's, and the rejected ones a scan skips. */
34
35
  export declare const relationProposalsOver: (context: StoreContext) => {
35
- /** The same pending pair returns the proposal already held. */
36
+ /**
37
+ * Where a pair stands before a scan proposes it: a link joins it, a `relationship` proposal for it was rejected,
38
+ * or one is pending. A rejection is matched by the pair alone, whoever proposed it and with whatever scorer.
39
+ */
40
+ state: (paired: () => {
41
+ from: Thing;
42
+ existing: Row | undefined;
43
+ }, { to, kind }: {
44
+ to: string;
45
+ kind: string;
46
+ }) => {
47
+ linked: boolean;
48
+ confirmed: boolean;
49
+ rejected: boolean;
50
+ pending: ProposedAction | null;
51
+ } | undefined;
52
+ /** The same pending pair returns the proposal already held; a scored one updates its score in place. */
36
53
  propose: (from: Thing, input: {
37
54
  from: string;
38
55
  to: string;
@@ -41,7 +58,7 @@ export declare const relationProposalsOver: (context: StoreContext) => {
41
58
  purpose?: string;
42
59
  evidence?: string[];
43
60
  reason?: string;
44
- }, by: Exclude<Author, "owner">) => ProposedAction;
61
+ } & Scored, by: Exclude<Author, "owner">) => ProposedAction;
45
62
  rejected: (link: Link, by: Decider, reason?: string) => void;
46
63
  update: (link: Link, input: RelationUpdate) => Link;
47
64
  proposeUpdate: (link: Link, input: RelationUpdate & {
@@ -62,6 +79,15 @@ export declare const relationProposalsOver: (context: StoreContext) => {
62
79
  /** Decides an unconfirmed link written before proposals were stored; only the owner may, as its author is unknown. */
63
80
  decideLink: (id: string, decision: LinkDecision) => Link | null;
64
81
  };
82
+ /** What a scan adds to a relationship proposal: the combined score and each scorer's part in it. */
83
+ export interface Scored {
84
+ score?: number;
85
+ signals?: {
86
+ scorer: string;
87
+ weight: number;
88
+ why: string;
89
+ }[];
90
+ }
65
91
  /** A relationship's role and purpose to set; `null` clears one. */
66
92
  export interface RelationUpdate {
67
93
  role?: string | null;
@@ -72,3 +98,4 @@ export interface LinkDecision {
72
98
  by: Decider;
73
99
  reason?: string;
74
100
  }
101
+ export {};
@@ -1,5 +1,5 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
- import { assertMayDecide, deciderAuthor } from "../decisions.js";
2
+ import { assertMayDecide, botKey, deciderAuthor } from "../decisions.js";
3
3
  import { actorOfOrigin, botNamed, ownerPerson } from "./actors.js";
4
4
  import { insertLink, linkOf, purposeOf } from "./notes.js";
5
5
  import { proposalOperationsOver, proposerOfAction } from "./proposed-actions.js";
@@ -47,19 +47,74 @@ export const relationProposalsOver = (context) => {
47
47
  .run(Number("role" in changes), changes.role ?? null, Number("purpose" in changes), changes.purpose ?? null, now(), Number(link.id));
48
48
  return linkOf(database, database.prepare("SELECT * FROM links WHERE id = ?").get(Number(link.id)));
49
49
  };
50
+ /**
51
+ * A scan's newer score on a pending proposal, in place under the same id. A rule's own row takes the new score,
52
+ * signals, evidence and purpose; another proposer's row keeps its author, evidence and purpose and gains the signals.
53
+ */
54
+ const rescored = (action, input, scored, by) => {
55
+ const proposer = proposerOfAction(action);
56
+ const own = proposer.type === "bot" && proposer.kind === "rule" && botKey(proposer.name) === botKey(by.bot);
57
+ const before = action.payload;
58
+ const payload = own
59
+ ? {
60
+ ...before,
61
+ ...scored,
62
+ evidence: input.evidence ?? [],
63
+ ...(input.purpose === undefined ? {} : { purpose: purposeOf(input.purpose) }),
64
+ }
65
+ : { ...before, ...scored };
66
+ const reason = own && input.reason !== undefined ? input.reason : action.reason;
67
+ if (JSON.stringify(payload) === JSON.stringify(before) && reason === action.reason)
68
+ return action;
69
+ database
70
+ .prepare("UPDATE proposed_actions SET payload = ?, reason = ?, updated_at = ? WHERE id = ? AND status = 'proposed'")
71
+ .run(JSON.stringify(payload), reason, now(), Number(action.id));
72
+ return operations.get(action.id);
73
+ };
50
74
  return {
51
- /** The same pending pair returns the proposal already held. */
75
+ /**
76
+ * Where a pair stands before a scan proposes it: a link joins it, a `relationship` proposal for it was rejected,
77
+ * or one is pending. A rejection is matched by the pair alone, whoever proposed it and with whatever scorer.
78
+ */
79
+ state: (paired, { to, kind }) => {
80
+ let found;
81
+ try {
82
+ found = paired();
83
+ }
84
+ catch (error) {
85
+ if (error instanceof CliError && (error.code === "not_found" || error.code === "validation_error"))
86
+ return;
87
+ throw error;
88
+ }
89
+ const { from, existing } = found;
90
+ const rows = database
91
+ .prepare("SELECT id, status FROM proposed_actions WHERE kind='relationship' AND status IN ('proposed','rejected') " +
92
+ "AND target_type=? AND target_id=? AND json_extract(payload,'$.to')=? AND json_extract(payload,'$.kind')=? " +
93
+ "ORDER BY id")
94
+ .all(from.type, from.id, to, kind);
95
+ const pending = rows.find((row) => row.status === "proposed");
96
+ return {
97
+ linked: existing !== undefined,
98
+ confirmed: existing !== undefined && Number(existing.confirmed) === 1,
99
+ rejected: rows.some((row) => row.status === "rejected"),
100
+ pending: pending ? operations.get(String(pending.id)) : null,
101
+ };
102
+ },
103
+ /** The same pending pair returns the proposal already held; a scored one updates its score in place. */
52
104
  propose: (from, input, by) => {
53
105
  const evidence = input.evidence ?? [];
54
106
  if (!Array.isArray(evidence) || evidence.length > 5 || evidence.some((ref) => typeof ref !== "string"))
55
107
  throw new CliError("validation_error", "a relationship proposal cites up to 5 references");
108
+ const scored = scoredOf(input);
56
109
  return write(database, () => {
57
110
  const held = database
58
111
  .prepare("SELECT id FROM proposed_actions WHERE kind='relationship' AND status='proposed' AND target_type=? AND target_id=? " +
59
112
  "AND json_extract(payload,'$.to')=? AND json_extract(payload,'$.kind')=? ORDER BY id LIMIT 1")
60
113
  .get(from.type, from.id, input.to, input.kind);
61
- if (held)
62
- return operations.get(String(held.id));
114
+ if (held) {
115
+ const action = operations.get(String(held.id));
116
+ return scored ? rescored(action, input, scored, by) : action;
117
+ }
63
118
  return operations.propose({
64
119
  kind: "relationship",
65
120
  by,
@@ -71,6 +126,7 @@ export const relationProposalsOver = (context) => {
71
126
  ...(input.role === undefined ? {} : { role: input.role }),
72
127
  ...(input.purpose === undefined ? {} : { purpose: purposeOf(input.purpose) }),
73
128
  evidence,
129
+ ...scored,
74
130
  },
75
131
  ...(input.reason === undefined ? {} : { reason: input.reason }),
76
132
  });
@@ -174,6 +230,21 @@ export const relationProposalsOver = (context) => {
174
230
  }),
175
231
  };
176
232
  };
233
+ const scoredOf = (input) => {
234
+ if (input.score === undefined && input.signals === undefined)
235
+ return undefined;
236
+ const { score, signals } = input;
237
+ if (typeof score !== "number" || !(score >= 0 && score <= 1) || !Array.isArray(signals) || signals.length > 20)
238
+ throw new CliError("validation_error", "a scored proposal takes a score of 0–1 and up to 20 signals");
239
+ for (const signal of signals)
240
+ if (typeof signal?.scorer !== "string" ||
241
+ typeof signal.why !== "string" ||
242
+ signal.why.length > 500 ||
243
+ typeof signal.weight !== "number" ||
244
+ !(signal.weight >= 0 && signal.weight <= 1))
245
+ throw new CliError("validation_error", "a signal names its scorer, a weight of 0–1 and why, up to 500 characters");
246
+ return { score, signals: signals.map(({ scorer, weight, why }) => ({ scorer, weight, why })) };
247
+ };
177
248
  /** What a relationship update sets, checked; `null` clears a field and an absent one is left alone. */
178
249
  const changesOf = (input) => {
179
250
  const changes = {};
@@ -112,6 +112,7 @@ export interface MessageStore {
112
112
  forAccount(key: AccountKey): AccountStore;
113
113
  close(): Promise<void>;
114
114
  }
115
+ export type { PaymentFilter } from "./sqlite/facts.js";
115
116
  export type { ChatHistoryShape, StoredHistorySkip } from "./sqlite/history-skips.js";
116
117
  export { BACKFILL_ON_OPEN, type StoreOptions } from "./sqlite/open-store.js";
117
118
  export { fileFacts, readStoreTables, type TableKind } from "./sqlite/store-tables.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.550.0",
3
+ "version": "0.551.0",
4
4
  "description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Viacheslav Ptsarev",
@@ -251,7 +251,7 @@
251
251
  "@types/node": "^22.20.5",
252
252
  "@types/pngjs": "6.0.5",
253
253
  "@vitest/coverage-v8": "^5.0.5",
254
- "@wirecat/cli-base": "0.74.0",
254
+ "@wirecat/cli-base": "0.75.0",
255
255
  "@wirecat/cli-core-legacy-tags": "npm:@wirecat/cli-core@0.414.0",
256
256
  "commander": "15.0.0",
257
257
  "cspell": "10.4.0",
@@ -271,7 +271,7 @@
271
271
  },
272
272
  "peerDependencies": {
273
273
  "@napi-rs/canvas": ">=0.1.69 <2",
274
- "@wirecat/cli-base": ">=0.74.0 <0.75.0",
274
+ "@wirecat/cli-base": ">=0.75.0 <0.76.0",
275
275
  "commander": ">=15"
276
276
  },
277
277
  "bin": {
package/parity.json CHANGED
@@ -3715,6 +3715,17 @@
3715
3715
  "--sync-time": "all"
3716
3716
  }
3717
3717
  },
3718
+ "projects status": {
3719
+ "in": ["max", "tg"],
3720
+ "planned": {
3721
+ "wa": "wa backlog: mount projectsCommand once wa is on cli-core 0.504.0"
3722
+ },
3723
+ "options": {
3724
+ "--before-time": "all",
3725
+ "--context-query": "all",
3726
+ "--since-time": "all"
3727
+ }
3728
+ },
3718
3729
  "reactions": {
3719
3730
  "in": "all"
3720
3731
  },