@wirecat/cli-core 0.494.0 → 0.495.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 +14 -0
- package/dist/services/chat-coverage.d.ts +1 -1
- package/dist/services/chat-coverage.js +2 -1
- package/dist/services/project-gathering.d.ts +15 -4
- package/dist/services/project-gathering.js +49 -16
- package/dist/store/chats.d.ts +2 -0
- package/dist/store/chats.js +9 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ shape of this file.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## 0.495.0 — 31.10.2026
|
|
11
|
+
|
|
12
|
+
### Changed — may break callers
|
|
13
|
+
|
|
14
|
+
- **`gatherProjectEvidence` says what the store holds of each linked chat, and which ones the run never reached.** A new
|
|
15
|
+
optional port `coverage(account, chatIds)` (pass `chatCoverage` over the store) fills each `linkedChats` row with
|
|
16
|
+
`title` (the chat row's, also for a chat with no stored message), `completeness` and `held`; without it they are `null` and `completeness` stays `"unknown"`. The top-level
|
|
17
|
+
`completeness` is `partial` when any linked chat is `partial`, `unknown` when none is but one is `unknown` (a chat the
|
|
18
|
+
store has no row for counts), and `complete` only when every linked chat in scope is. `complete` keeps its meaning,
|
|
19
|
+
"the planned reads finished"; a host's `meta.complete` is `complete && completeness === "complete"`. A linked chat
|
|
20
|
+
none of whose reads has started is `state: "pending"` and has no `items` or `capped`, where it said `in-scope` with
|
|
21
|
+
`items: 0, capped: false`. `ChatCoverage.title` now comes from the chat row when the chat has no message, through the
|
|
22
|
+
new `store.chats.chatTitle`.
|
|
23
|
+
|
|
10
24
|
## 0.494.0 — 31.10.2026
|
|
11
25
|
|
|
12
26
|
### Security
|
|
@@ -3,7 +3,7 @@ import type { AccountKey, ChatCompleteness, MessageStore } from "../store/store.
|
|
|
3
3
|
/** What the store holds of one chat, and how far that goes: never message text. */
|
|
4
4
|
export interface ChatCoverage {
|
|
5
5
|
chatId: Id;
|
|
6
|
-
/** `null`
|
|
6
|
+
/** The chat row's title; `null` when the store has no row for the chat or the row has none. */
|
|
7
7
|
title: string | null;
|
|
8
8
|
completeness: ChatCompleteness;
|
|
9
9
|
held: {
|
|
@@ -9,6 +9,7 @@ export const chatCoverage = async (store, account, chatIds) => {
|
|
|
9
9
|
const found = new Map((await store.chats.chatCompleteness(account, ids)).map((one) => [one.chatId, one]));
|
|
10
10
|
return Promise.all(ids.map(async (chatId) => {
|
|
11
11
|
const [stats] = await store.chats.chatStats(account, chatId);
|
|
12
|
+
const title = stats?.title ?? (await store.chats.chatTitle(account, chatId));
|
|
12
13
|
const completeness = found.get(chatId) ?? {
|
|
13
14
|
chatId,
|
|
14
15
|
state: "unknown",
|
|
@@ -19,7 +20,7 @@ export const chatCoverage = async (store, account, chatIds) => {
|
|
|
19
20
|
};
|
|
20
21
|
return {
|
|
21
22
|
chatId,
|
|
22
|
-
title
|
|
23
|
+
title,
|
|
23
24
|
completeness,
|
|
24
25
|
held: { messages: stats?.messages ?? 0, oldestAt: stats?.oldestAt ?? null, newestAt: stats?.newestAt ?? null },
|
|
25
26
|
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ContextEvidenceItem, ContextEvidenceReader, ContextIndex, ContextSource } from "../context/evidence.js";
|
|
2
|
+
import type { AccountKey } from "../store/types.js";
|
|
3
|
+
import type { ChatCoverage } from "./chat-coverage.js";
|
|
2
4
|
import { type ProjectNeighbour, type ProjectNeighbourReader } from "./project-gathering-around.js";
|
|
3
5
|
import { type ProjectFetchState, type ProjectGatheringRequest, type ProjectLane } from "./project-gathering-request.js";
|
|
4
6
|
/** A connector's history fetch into the store; the store reads that follow find what it saved. */
|
|
@@ -18,6 +20,8 @@ export interface ProjectGatheringPorts {
|
|
|
18
20
|
fetcher?: ProjectSourceFetcher;
|
|
19
21
|
index?: ContextIndex;
|
|
20
22
|
neighbours?: ProjectNeighbourReader;
|
|
23
|
+
/** What the store holds of the named chats of one account, e.g. `chatCoverage` over the store. */
|
|
24
|
+
coverage?: (account: AccountKey, chatIds: string[]) => Promise<ChatCoverage[]>;
|
|
21
25
|
now?: () => number;
|
|
22
26
|
}
|
|
23
27
|
export interface ProjectIndexCatchUp {
|
|
@@ -88,9 +92,16 @@ export declare const gatherProjectEvidence: (ports: ProjectGatheringPorts, reque
|
|
|
88
92
|
ref: string;
|
|
89
93
|
source: number | null;
|
|
90
94
|
lane: string | null;
|
|
91
|
-
state: "in-scope" | "out-of-scope" | "unavailable";
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
state: "in-scope" | "out-of-scope" | "pending" | "unavailable";
|
|
96
|
+
title: string | null;
|
|
97
|
+
completeness: import("../store/exports.js").ChatCompleteness | null;
|
|
98
|
+
held: {
|
|
99
|
+
messages: number;
|
|
100
|
+
oldestAt: string | null;
|
|
101
|
+
newestAt: string | null;
|
|
102
|
+
} | null;
|
|
103
|
+
items?: number | undefined;
|
|
104
|
+
capped?: boolean | undefined;
|
|
94
105
|
}[];
|
|
95
106
|
fetch: {
|
|
96
107
|
state: ProjectFetchState;
|
|
@@ -136,7 +147,7 @@ export declare const gatherProjectEvidence: (ports: ProjectGatheringPorts, reque
|
|
|
136
147
|
};
|
|
137
148
|
stopped: ProjectGatheringStop;
|
|
138
149
|
complete: boolean;
|
|
139
|
-
completeness: "unknown";
|
|
150
|
+
completeness: "complete" | "partial" | "unknown";
|
|
140
151
|
checkpoint: string | null;
|
|
141
152
|
}>;
|
|
142
153
|
export type ProjectGathering = Awaited<ReturnType<typeof gatherProjectEvidence>>;
|
|
@@ -264,6 +264,20 @@ export const gatherProjectEvidence = async (ports, request) => {
|
|
|
264
264
|
},
|
|
265
265
|
}, (ref) => byRef.has(ref) || seen.has(seenKey(ref)), signal);
|
|
266
266
|
const people = peopleOf(items, senderOf);
|
|
267
|
+
const coverageOf = new Map();
|
|
268
|
+
if (ports.coverage) {
|
|
269
|
+
const byAccount = new Map();
|
|
270
|
+
for (const { provider, account, chat, source } of plan.linkedChats) {
|
|
271
|
+
if (source === null)
|
|
272
|
+
continue;
|
|
273
|
+
const group = byAccount.get(JSON.stringify([provider, account])) ?? { account: { provider, account }, chats: [] };
|
|
274
|
+
group.chats.push(chat);
|
|
275
|
+
byAccount.set(JSON.stringify([provider, account]), group);
|
|
276
|
+
}
|
|
277
|
+
for (const [key, { account, chats }] of byAccount)
|
|
278
|
+
for (const row of await ports.coverage(account, chats))
|
|
279
|
+
coverageOf.set(`${key}${row.chatId}`, row);
|
|
280
|
+
}
|
|
267
281
|
const totals = {
|
|
268
282
|
reads: (previous?.totals.reads ?? 0) + usage.reads,
|
|
269
283
|
readBytes: (previous?.totals.readBytes ?? 0) + usage.readBytes,
|
|
@@ -284,6 +298,39 @@ export const gatherProjectEvidence = async (ports, request) => {
|
|
|
284
298
|
seen: [...seen],
|
|
285
299
|
totals,
|
|
286
300
|
};
|
|
301
|
+
const linkedRows = plan.linkedChats.map(({ provider, account, chat, ref, source, lane }) => {
|
|
302
|
+
const laneUnits = units.flatMap((unit, index) => (unit.lane.id === lane ? [progress[index]] : []));
|
|
303
|
+
const state = source === null
|
|
304
|
+
? "out-of-scope"
|
|
305
|
+
: laneUnits.every((u) => u?.state === "unavailable")
|
|
306
|
+
? "unavailable"
|
|
307
|
+
: laneUnits.every((u) => u?.state === "pending")
|
|
308
|
+
? "pending"
|
|
309
|
+
: "in-scope";
|
|
310
|
+
const row = coverageOf.get(`${JSON.stringify([provider, account])}${chat}`);
|
|
311
|
+
return {
|
|
312
|
+
provider,
|
|
313
|
+
account,
|
|
314
|
+
chat,
|
|
315
|
+
ref,
|
|
316
|
+
source,
|
|
317
|
+
lane,
|
|
318
|
+
state,
|
|
319
|
+
title: row?.title ?? null,
|
|
320
|
+
completeness: row?.completeness ?? null,
|
|
321
|
+
held: row?.held ?? null,
|
|
322
|
+
...(state === "pending" ? {} : { items: lane === null ? 0 : (linkedTaken.get(lane) ?? 0) }),
|
|
323
|
+
...(state === "pending" ? {} : { capped: lane !== null && capped.has(lane) }),
|
|
324
|
+
};
|
|
325
|
+
});
|
|
326
|
+
const states = linkedRows.flatMap((row) => (row.completeness ? [row.completeness.state] : []));
|
|
327
|
+
const completeness = !states.length
|
|
328
|
+
? "unknown"
|
|
329
|
+
: states.includes("partial")
|
|
330
|
+
? "partial"
|
|
331
|
+
: states.includes("unknown") || states.length < linkedRows.filter((row) => row.source !== null).length
|
|
332
|
+
? "unknown"
|
|
333
|
+
: "complete";
|
|
287
334
|
return {
|
|
288
335
|
schemaVersion: 1,
|
|
289
336
|
runKey: plan.runKey,
|
|
@@ -300,21 +347,7 @@ export const gatherProjectEvidence = async (ports, request) => {
|
|
|
300
347
|
},
|
|
301
348
|
scope: { origin: plan.scope, sources: plan.sources },
|
|
302
349
|
lanes: plan.lanes,
|
|
303
|
-
linkedChats:
|
|
304
|
-
provider,
|
|
305
|
-
account,
|
|
306
|
-
chat,
|
|
307
|
-
ref,
|
|
308
|
-
source,
|
|
309
|
-
lane,
|
|
310
|
-
state: source === null
|
|
311
|
-
? "out-of-scope"
|
|
312
|
-
: units.every((unit, index) => unit.lane.id !== lane || progress[index]?.state === "unavailable")
|
|
313
|
-
? "unavailable"
|
|
314
|
-
: "in-scope",
|
|
315
|
-
items: lane === null ? 0 : (linkedTaken.get(lane) ?? 0),
|
|
316
|
-
capped: lane !== null && capped.has(lane),
|
|
317
|
-
})),
|
|
350
|
+
linkedChats: linkedRows,
|
|
318
351
|
fetch: fetched.map((f, index) => ({ source: index, ...f })),
|
|
319
352
|
index: indexed,
|
|
320
353
|
units: units.map((unit, index) => ({
|
|
@@ -335,7 +368,7 @@ export const gatherProjectEvidence = async (ports, request) => {
|
|
|
335
368
|
totals,
|
|
336
369
|
stopped,
|
|
337
370
|
complete: !resumable,
|
|
338
|
-
completeness
|
|
371
|
+
completeness,
|
|
339
372
|
checkpoint: resumable ? encodeCheckpoint(checkpoint) : null,
|
|
340
373
|
};
|
|
341
374
|
};
|
package/dist/store/chats.d.ts
CHANGED
|
@@ -66,6 +66,8 @@ export interface ChatsStore {
|
|
|
66
66
|
renamedChatId(key: AccountKey, chatId: Id): Promise<Id | undefined>;
|
|
67
67
|
/** A stored chat's kind, with or without messages; `undefined` when the store does not know the chat. */
|
|
68
68
|
chatKind(key: AccountKey, chatId: Id): Promise<ChatKind | undefined>;
|
|
69
|
+
/** A stored chat's title, with or without messages; `null` when it has none or the store does not know the chat. */
|
|
70
|
+
chatTitle(key: AccountKey, chatId: Id): Promise<string | null>;
|
|
69
71
|
/** Per chat that spoke since `since` (ms): its newest message and who sent it when stored, never the text. */
|
|
70
72
|
/** `where` is a `tasks agenda` query compiled over `AGENDA_COLUMNS`. */
|
|
71
73
|
chatActivity(key: AccountKey, since: number, where?: Where): Promise<{
|
package/dist/store/chats.js
CHANGED
|
@@ -94,6 +94,15 @@ export const chatsStoreOver = (context) => {
|
|
|
94
94
|
.get(accountKey, chatId);
|
|
95
95
|
return row ? String(row.kind) : undefined;
|
|
96
96
|
},
|
|
97
|
+
chatTitle: async (key, chatId) => {
|
|
98
|
+
const accountKey = findAccountPk(key);
|
|
99
|
+
if (accountKey === undefined)
|
|
100
|
+
return null;
|
|
101
|
+
const row = context.database
|
|
102
|
+
.prepare("SELECT title FROM chats WHERE account_id = ? AND external_id = ?")
|
|
103
|
+
.get(accountKey, chatId);
|
|
104
|
+
return row?.title == null ? null : String(row.title);
|
|
105
|
+
},
|
|
97
106
|
chatActivity: async (key, since, where) => {
|
|
98
107
|
const accountKey = findAccountPk(key);
|
|
99
108
|
return accountKey === undefined
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.495.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",
|