@wirecat/cli-core 0.389.0 → 0.391.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 +37 -0
- package/dist/cli/context.js +1 -0
- package/dist/cli/effects.d.ts +2 -0
- package/dist/cli/effects.js +8 -2
- package/dist/cli/messenger/catch-up.d.ts +0 -19
- package/dist/cli/messenger/catch-up.js +0 -43
- package/dist/cli/messenger/context.js +6 -3
- package/dist/cli/messenger/inbox.js +6 -11
- package/dist/cli/messenger/messages-command.js +5 -15
- package/dist/cli/messenger/review.d.ts +1 -1
- package/dist/cli/messenger/review.js +5 -10
- package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
- package/dist/cli/messenger/tags-command.js +2 -2
- package/dist/cli/program.js +10 -0
- package/dist/cli/settings.d.ts +0 -4
- package/dist/cli/settings.js +7 -5
- package/dist/meetings/index.d.ts +2 -0
- package/dist/meetings/index.js +2 -0
- package/dist/meetings/links.d.ts +18 -0
- package/dist/meetings/links.js +1 -0
- package/dist/meetings/review.d.ts +45 -0
- package/dist/meetings/review.js +1 -0
- package/dist/runs/recording.d.ts +5 -0
- package/dist/runs/recording.js +6 -3
- package/dist/runs/scope.d.ts +3 -1
- package/dist/runs/scope.js +2 -0
- package/dist/search/lucene/registry.d.ts +5 -1
- package/dist/search/lucene/registry.js +10 -4
- package/dist/search/lucene/types.d.ts +8 -0
- package/dist/search/lucene/types.js +4 -0
- package/dist/search/question-plan.js +3 -3
- package/dist/services/excerpt.js +4 -1
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +2 -0
- package/dist/services/meeting-links.d.ts +33 -0
- package/dist/services/meeting-links.js +41 -0
- package/dist/services/meeting-review.d.ts +12 -0
- package/dist/services/meeting-review.js +8 -0
- package/dist/services/messages-search.js +2 -2
- package/dist/services/searches.js +3 -3
- package/dist/store/sqlite/lucene-fields.d.ts +3 -1
- package/dist/store/sqlite/lucene-fields.js +78 -11
- package/dist/store/sqlite/lucene.js +16 -11
- package/dist/store/sqlite/meeting-chat-lines.d.ts +3 -0
- package/dist/store/sqlite/meeting-chat-lines.js +34 -0
- package/dist/store/sqlite/meeting-review.d.ts +4 -0
- package/dist/store/sqlite/meeting-review.js +97 -0
- package/dist/store/sqlite/meetings.d.ts +2 -2
- package/dist/store/sqlite/meetings.js +4 -0
- package/package.json +1 -1
- package/parity.json +0 -11
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ Notable changes to `@wirecat/cli-core` (`@wirecat/cli-messaging` up to 0.261.0,
|
|
|
5
5
|
minor version may break callers, and says how under "Changed — may break callers". `pnpm docs:check` checks the
|
|
6
6
|
shape of this file.
|
|
7
7
|
|
|
8
|
+
## Unreleased
|
|
9
|
+
|
|
10
|
+
## 0.391.0 — 23.10.2026
|
|
11
|
+
|
|
12
|
+
### Changed — may break callers
|
|
13
|
+
|
|
14
|
+
- Reads never mark chats read: `messages list`, `inbox` and `review` no longer take `--mark-read` or
|
|
15
|
+
`--no-mark-read`. Remove `catch_up_marks_read` from configuration; it is now refused. Use
|
|
16
|
+
`<cli> <profile> chats mark-read <chat>` when you intend to send a read receipt.
|
|
17
|
+
- `tags auto` declares its messenger read, including the preparatory read requested with `--sync-first`.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `readMeetingReview` (`@wirecat/cli-core/services`) and `MeetingStore.meetingReview`: the newest meetings of a
|
|
22
|
+
selection, each with its newest summary (overview, next steps), transcript lines and words, the three who talked
|
|
23
|
+
longest and its confirmed projects. No transcript text is read out.
|
|
24
|
+
- `readMeetingLinks` (`@wirecat/cli-core/services`) and `MeetingStore.meetingChatLines`: links shared in the chat of
|
|
25
|
+
the selected meetings, most shared first, with who shared them, in which meetings, and the Google file each names.
|
|
26
|
+
Found when read, not stored; at most 100000 chat messages, more is an error.
|
|
27
|
+
- A local read with an unknown profile now lists the profiles on this computer in its error and
|
|
28
|
+
`details.profiles`, so a typo does not suggest linking a new account. A configured profile with no
|
|
29
|
+
saved account still explains how to fill its local history.
|
|
30
|
+
|
|
31
|
+
- Unknown permission keys now refuse messenger commands with `configuration_error`, including typos after
|
|
32
|
+
the resource name such as `messages.sedn`. Correct the key or remove it with `config unset`; a warning
|
|
33
|
+
no longer leaves the intended restriction unenforced.
|
|
34
|
+
- **Message search reads `"a b"~N`: the words at most N words apart in one message, in any order.** It was a
|
|
35
|
+
`query.unsupported-operator` error; now it is syntax for `text:`, `exact:`, `transcript:`, `content:` and a bare
|
|
36
|
+
word, N up to 50 (`query.invalid-number` above). Words match in any form, as in a phrase; `exact:` keeps the typed
|
|
37
|
+
forms. Unlike Lucene, order costs nothing: `"a b"~0` finds "b a" too. `word~1` stays an error. A saved search may
|
|
38
|
+
now hold `~N`. `QUERY_OPERATORS` splits the `fuzzy / proximity` row into `proximity` and `fuzzy`.
|
|
39
|
+
|
|
8
40
|
## 0.389.0 — 22.10.2026
|
|
9
41
|
|
|
10
42
|
### Added
|
|
@@ -15,6 +47,11 @@ shape of this file.
|
|
|
15
47
|
so a local transcript beside the provider's does not count twice. `meetings.meetingStats` on the store and
|
|
16
48
|
`aggregateMeetingStats` for rows a host already has.
|
|
17
49
|
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- **A recorded one-shot command's `meta.run_id` names the run it kept**, so `runs show <run_id>` finds it. It named
|
|
53
|
+
the process's own run, which is never kept.
|
|
54
|
+
|
|
18
55
|
## 0.388.0 — 22.10.2026
|
|
19
56
|
|
|
20
57
|
### Added
|
package/dist/cli/context.js
CHANGED
package/dist/cli/effects.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export declare const isLogin: (command: Command) => boolean;
|
|
|
36
36
|
* (merge/unmerge, claim/release) or keeps the old value in the task's history; under the lock a close only suggests.
|
|
37
37
|
*/
|
|
38
38
|
export declare const destroysNothing: <T extends Command>(command: T) => T;
|
|
39
|
+
/** A local edit remains an edit even when it first fetches metadata from the messenger. */
|
|
40
|
+
export declare const changesStore: <T extends Command>(command: T) => T;
|
|
39
41
|
/**
|
|
40
42
|
* Whether `<PREFIX>_READ_ONLY` refuses the command as a change to the store. Every write that stays on this machine
|
|
41
43
|
* is refused unless `destroysNothing` marks it, so a new one fails closed (WC-PERM-12). A fetch (`remote: "read"`)
|
package/dist/cli/effects.js
CHANGED
|
@@ -52,6 +52,12 @@ export const destroysNothing = (command) => {
|
|
|
52
52
|
KEPT_UNDER_LOCK.add(command);
|
|
53
53
|
return command;
|
|
54
54
|
};
|
|
55
|
+
const STORE_WRITES = new WeakSet();
|
|
56
|
+
/** A local edit remains an edit even when it first fetches metadata from the messenger. */
|
|
57
|
+
export const changesStore = (command) => {
|
|
58
|
+
STORE_WRITES.add(command);
|
|
59
|
+
return command;
|
|
60
|
+
};
|
|
55
61
|
/**
|
|
56
62
|
* Whether `<PREFIX>_READ_ONLY` refuses the command as a change to the store. Every write that stays on this machine
|
|
57
63
|
* is refused unless `destroysNothing` marks it, so a new one fails closed (WC-PERM-12). A fetch (`remote: "read"`)
|
|
@@ -59,8 +65,8 @@ export const destroysNothing = (command) => {
|
|
|
59
65
|
*/
|
|
60
66
|
export const isStoreChange = (command) => {
|
|
61
67
|
const effects = metaOf(command).effects;
|
|
62
|
-
return (effects?.remote === "none" &&
|
|
63
|
-
effects
|
|
68
|
+
return ((effects?.remote === "none" || STORE_WRITES.has(command)) &&
|
|
69
|
+
effects?.local === "write" &&
|
|
64
70
|
!KEPT_UNDER_LOCK.has(command) &&
|
|
65
71
|
!changesAuthority(command));
|
|
66
72
|
};
|
|
@@ -1,22 +1,3 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
-
import type { Id, Message } from "../../domain/models.js";
|
|
3
|
-
import type { MarkedRead } from "../../services/chats.js";
|
|
4
|
-
import type { Services } from "../../services/index.js";
|
|
5
|
-
import type { Settings } from "../settings.js";
|
|
6
|
-
export declare const MARK_READ_OPTION: readonly ["--mark-read", "also mark each chat shown read, up to the newest message shown; the other side sees it"];
|
|
7
|
-
export declare const NO_MARK_READ_OPTION: readonly ["--no-mark-read", "do not, whatever the catch_up_marks_read setting says"];
|
|
8
|
-
/**
|
|
9
|
-
* The typed flag decides; without one, the owner's `catch_up_marks_read` setting, off unless turned on (NEED-566).
|
|
10
|
-
* An agent's run never marks read on that setting: the other side sees it, so only an agent that asks does.
|
|
11
|
-
*/
|
|
12
|
-
export declare const marksRead: (typed: boolean | undefined, settings: Settings, command: Command) => boolean;
|
|
13
2
|
/** Whose `--new` points a run moves: an agent's are its own, so the owner's `inbox --new` still shows everything. */
|
|
14
3
|
export declare const pointsOf: <N extends "inbox" | "review">(command: Command, name: N) => N | `mcp-${N}`;
|
|
15
|
-
/**
|
|
16
|
-
* Marks each chat read up to the newest message shown in it — never further, so what arrived after
|
|
17
|
-
* the read stays unread. Through the send guard, so `permissions.chats.mark-read` applies.
|
|
18
|
-
*/
|
|
19
|
-
export declare const markShown: (services: Services, chats: {
|
|
20
|
-
id: Id;
|
|
21
|
-
messages: Pick<Message, "id" | "timestamp">[];
|
|
22
|
-
}[], warn: (message: string) => void) => Promise<MarkedRead[]>;
|
|
@@ -1,46 +1,3 @@
|
|
|
1
|
-
import { CliError, isCliError } from "@wirecat/cli-base";
|
|
2
1
|
import { environmentOf } from "../context.js";
|
|
3
|
-
export const MARK_READ_OPTION = [
|
|
4
|
-
"--mark-read",
|
|
5
|
-
"also mark each chat shown read, up to the newest message shown; the other side sees it",
|
|
6
|
-
];
|
|
7
|
-
export const NO_MARK_READ_OPTION = ["--no-mark-read", "do not, whatever the catch_up_marks_read setting says"];
|
|
8
|
-
// Neither is offered to an agent (`ownerOnlyOption`): a read tool may run unasked, and the other side sees a mark.
|
|
9
|
-
// An agent marks read with `chats mark-read`, a write.
|
|
10
|
-
/**
|
|
11
|
-
* The typed flag decides; without one, the owner's `catch_up_marks_read` setting, off unless turned on (NEED-566).
|
|
12
|
-
* An agent's run never marks read on that setting: the other side sees it, so only an agent that asks does.
|
|
13
|
-
*/
|
|
14
|
-
export const marksRead = (typed, settings, command) => {
|
|
15
|
-
const marking = typed ?? (environmentOf(command).agent ? false : settings.catchUpMarksRead);
|
|
16
|
-
if (marking && settings.offline) {
|
|
17
|
-
throw new CliError("validation_error", "--mark-read tells the messenger; not with --offline");
|
|
18
|
-
}
|
|
19
|
-
return marking;
|
|
20
|
-
};
|
|
21
2
|
/** Whose `--new` points a run moves: an agent's are its own, so the owner's `inbox --new` still shows everything. */
|
|
22
3
|
export const pointsOf = (command, name) => environmentOf(command).agent ? `mcp-${name}` : name;
|
|
23
|
-
/**
|
|
24
|
-
* Marks each chat read up to the newest message shown in it — never further, so what arrived after
|
|
25
|
-
* the read stays unread. Through the send guard, so `permissions.chats.mark-read` applies.
|
|
26
|
-
*/
|
|
27
|
-
export const markShown = async (services, chats, warn) => {
|
|
28
|
-
const marked = [];
|
|
29
|
-
for (const { id, messages } of chats) {
|
|
30
|
-
const newest = messages.reduce((last, one) => (last === undefined || Date.parse(one.timestamp) >= Date.parse(last.timestamp) ? one : last), undefined);
|
|
31
|
-
if (newest === undefined)
|
|
32
|
-
continue;
|
|
33
|
-
try {
|
|
34
|
-
const { chatId, until } = await services.chats.markRead({ chat: id, until: newest.id });
|
|
35
|
-
marked.push({ chatId, until });
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
// A hold refuses every chat alike; what was read is still the answer, as reads go on during one.
|
|
39
|
-
if (!isCliError(error) || error.details.sendBlock === undefined)
|
|
40
|
-
throw error;
|
|
41
|
-
warn(`${error.message}; nothing more was marked read`);
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return marked;
|
|
46
|
-
};
|
|
@@ -282,7 +282,10 @@ export const connectedFor = async (command, messenger, base, options) => {
|
|
|
282
282
|
}),
|
|
283
283
|
};
|
|
284
284
|
};
|
|
285
|
-
const unrecorded = (command, messenger, profile, offline) => {
|
|
285
|
+
const unrecorded = (command, messenger, profile, { offline, profilesHere }) => {
|
|
286
|
+
if (!profilesHere.includes(profile)) {
|
|
287
|
+
return new CliError("not_found", `no profile named "${profile}" — profiles here: ${profilesHere.length > 0 ? profilesHere.join(", ") : "none"}`, { profiles: profilesHere });
|
|
288
|
+
}
|
|
286
289
|
const words = `${messenger.app.command} ${asFirstWord(profile)}`;
|
|
287
290
|
const setup = rootOf(command).commands.some((one) => one.name() === "setup") ? `\`${words}setup\`` : undefined;
|
|
288
291
|
if (messenger.history === "store")
|
|
@@ -346,14 +349,14 @@ export const messengerContext = (command, messenger, resolving = {}) => {
|
|
|
346
349
|
refuseDenied(command, base.settings);
|
|
347
350
|
refuseLacking(messenger, pathOf(command));
|
|
348
351
|
for (const key of unknownPermissionKeys(base.settings.permissions, knownPermissionKeys(command, messenger.permissionKey))) {
|
|
349
|
-
|
|
352
|
+
throw new CliError("configuration_error", `permissions.${key} names no command — remove it with \`${app.command} ${asFirstWord(profile)}config unset permissions.${key}\``, { permission: key });
|
|
350
353
|
}
|
|
351
354
|
// Read when warning, not now: serve swaps the renderer's methods for log records after this.
|
|
352
355
|
const warn = (text, warning) => base.renderer.warn(text, warning);
|
|
353
356
|
const accountFor = () => {
|
|
354
357
|
const account = messenger.accountOf?.(command) ?? recalledAccount(app, provider, profile, base.env);
|
|
355
358
|
if (!account)
|
|
356
|
-
throw unrecorded(command, messenger, profile, base.settings
|
|
359
|
+
throw unrecorded(command, messenger, profile, base.settings);
|
|
357
360
|
return account;
|
|
358
361
|
};
|
|
359
362
|
const guard = messenger.guard?.(command, base.settings, warn) ??
|
|
@@ -5,11 +5,11 @@ import { renderMessages } from "../../render/messages.js";
|
|
|
5
5
|
import { CHAT_KINDS } from "../../services/chats.js";
|
|
6
6
|
import { kindsOf } from "../../services/inbox.js";
|
|
7
7
|
import { modelWith } from "../../speech/hearing.js";
|
|
8
|
-
import {
|
|
8
|
+
import { REMOTE_READ } from "../effects.js";
|
|
9
9
|
import { renderAnswer } from "../envelope.js";
|
|
10
10
|
import { queryHelp } from "../list-query.js";
|
|
11
11
|
import { positiveCount } from "../paging.js";
|
|
12
|
-
import {
|
|
12
|
+
import { pointsOf } from "./catch-up.js";
|
|
13
13
|
import { catchUpQuery, INBOX_QUERY, inboxAnswer } from "./catch-up-answers.js";
|
|
14
14
|
import { messengerContext } from "./context.js";
|
|
15
15
|
import { hearForCommand, MODEL_OPTION, spokenItems, TRANSCRIBE_OPTION } from "./hearing-command.js";
|
|
@@ -42,10 +42,9 @@ export const inboxCommand = (messenger) => annotate(new Command("inbox"), {
|
|
|
42
42
|
.option("--kind <kinds>", `only chats of these kinds, comma-separated: ${CHAT_KINDS.join(", ")}`)
|
|
43
43
|
.option(...TRANSCRIBE_OPTION)
|
|
44
44
|
.option(...MODEL_OPTION)
|
|
45
|
-
.
|
|
46
|
-
.addOption(ownerOnlyOption(...NO_MARK_READ_OPTION))
|
|
45
|
+
.addHelpText("after", "To mark a chat read, use `chats mark-read <chat>`. The other side sees it.")
|
|
47
46
|
.action(async function (query) {
|
|
48
|
-
const { new: fresh, all, kind, transcribe, model,
|
|
47
|
+
const { new: fresh, all, kind, transcribe, model, } = this.opts();
|
|
49
48
|
const hearWith = modelWith(transcribe, model);
|
|
50
49
|
const context = messengerContext(this, messenger);
|
|
51
50
|
const { app } = messenger;
|
|
@@ -64,8 +63,7 @@ export const inboxCommand = (messenger) => annotate(new Command("inbox"), {
|
|
|
64
63
|
: undefined;
|
|
65
64
|
const from = since ?? saved?.first;
|
|
66
65
|
const points = saved?.chats;
|
|
67
|
-
const
|
|
68
|
-
const { inbox, hearing, marked, reader } = await context.withServices(async (services, connect) => {
|
|
66
|
+
const { inbox, hearing, reader } = await context.withServices(async (services, connect) => {
|
|
69
67
|
const inbox = await services.inbox.read({
|
|
70
68
|
...(from === undefined ? {} : { since: from }),
|
|
71
69
|
...(points === undefined ? {} : { points }),
|
|
@@ -76,11 +74,8 @@ export const inboxCommand = (messenger) => annotate(new Command("inbox"), {
|
|
|
76
74
|
const reader = await services.messages.reader();
|
|
77
75
|
const read = inbox.chats.flatMap((chat) => chat.messages.map((message) => ({ ...message, chatTitle: chat.title })));
|
|
78
76
|
const hearing = await hearForCommand(context, messenger, read, transcribe === true, hearWith, connect);
|
|
79
|
-
|
|
80
|
-
return { inbox, hearing, marked, reader };
|
|
77
|
+
return { inbox, hearing, reader };
|
|
81
78
|
});
|
|
82
|
-
if (marked !== undefined)
|
|
83
|
-
renderer.note(`marked ${marked.length} chat(s) read up to the newest message shown`);
|
|
84
79
|
if (inbox.partial && format === "pretty")
|
|
85
80
|
renderer.warn(PARTIAL_NOTE);
|
|
86
81
|
const answer = inboxAnswer(inbox, hearing, reader, transcribe === true, meta);
|
|
@@ -7,7 +7,7 @@ import { renderMessages } from "../../render/messages.js";
|
|
|
7
7
|
import { renderThreadLinks } from "../../render/thread-context.js";
|
|
8
8
|
import { readMail } from "../../services/mail-search.js";
|
|
9
9
|
import { modelWith, spoken } from "../../speech/hearing.js";
|
|
10
|
-
import {
|
|
10
|
+
import { REMOTE_READ } from "../effects.js";
|
|
11
11
|
import { itemOf, listOf, renderAnswer } from "../envelope.js";
|
|
12
12
|
import { queryHelp } from "../list-query.js";
|
|
13
13
|
import { positiveCount } from "../paging.js";
|
|
@@ -48,29 +48,19 @@ export const messagesCommand = (messenger) => {
|
|
|
48
48
|
.option("--cursor <cursor>", "go on from where the last page stopped; page.next gives it")
|
|
49
49
|
.option(...TRANSCRIBE_OPTION)
|
|
50
50
|
.option(...MODEL_OPTION)
|
|
51
|
-
.
|
|
51
|
+
.addHelpText("after", "To mark a chat read, use `chats mark-read <chat>`. The other side sees it.")
|
|
52
52
|
.action(async function (chat, query) {
|
|
53
53
|
const context = messengerContext(this, messenger);
|
|
54
|
-
const { cursor, transcribe, model
|
|
54
|
+
const { cursor, transcribe, model } = this.opts();
|
|
55
55
|
const window = historyWindow(query, cursor, "messages list", history);
|
|
56
56
|
const hearWith = modelWith(transcribe, model);
|
|
57
|
-
if (markRead && window.threadId !== undefined) {
|
|
58
|
-
throw new CliError("validation_error", "--mark-read marks the whole chat; use `chats mark-read --topic` for one");
|
|
59
|
-
}
|
|
60
|
-
if (markRead && context.settings.offline) {
|
|
61
|
-
throw new CliError("validation_error", "--mark-read tells the messenger; not with --offline");
|
|
62
|
-
}
|
|
63
57
|
const { limit } = context.settings;
|
|
64
|
-
const { page, hearing,
|
|
58
|
+
const { page, hearing, reader } = await context.withServices(async (services, connect) => {
|
|
65
59
|
const page = await services.messages.list(chat, { limit, ...window });
|
|
66
60
|
const reader = await services.messages.reader();
|
|
67
|
-
const newest = page.items.at(-1);
|
|
68
|
-
const marked = markRead && newest ? await services.chats.markRead({ chat, until: newest.id }) : undefined;
|
|
69
61
|
const hearing = await hearForCommand(context, messenger, page.items, transcribe === true, hearWith, connect);
|
|
70
|
-
return { page, hearing,
|
|
62
|
+
return { page, hearing, reader };
|
|
71
63
|
});
|
|
72
|
-
if (marked)
|
|
73
|
-
context.renderer.note(`marked read up to ${marked.until} (operation ${marked.operationId})`);
|
|
74
64
|
const next = page.hasMore
|
|
75
65
|
? cursorOf(window, { first: page.items[0]?.id, last: page.items.at(-1)?.id })
|
|
76
66
|
: undefined;
|
|
@@ -5,7 +5,7 @@ export declare const unansweredHours: (value: unknown, flag?: string) => number;
|
|
|
5
5
|
/**
|
|
6
6
|
* **Everything said since a point, both sides, in every chat that changed** — the reading half of a
|
|
7
7
|
* review of who owes what (max-cli's `review`). Sorting it is the reader's job, person or agent.
|
|
8
|
-
*
|
|
8
|
+
* Reading never marks a chat read, and `inbox --new` keeps its point. The rules open and close
|
|
9
9
|
* tasks in the local store from what it read (`../../services/task-rules.ts`); nothing reaches the messenger.
|
|
10
10
|
*/
|
|
11
11
|
export declare const reviewCommand: (messenger: Messenger) => Command;
|
|
@@ -6,10 +6,10 @@ import { renderMessages } from "../../render/messages.js";
|
|
|
6
6
|
import { CHAT_KINDS } from "../../services/chats.js";
|
|
7
7
|
import { kindsOf, REVIEW_DAYS, reviewStart, UNANSWERED_HOURS } from "../../services/inbox.js";
|
|
8
8
|
import { modelWith } from "../../speech/hearing.js";
|
|
9
|
-
import {
|
|
9
|
+
import { REMOTE_READ } from "../effects.js";
|
|
10
10
|
import { renderAnswer } from "../envelope.js";
|
|
11
11
|
import { queryHelp } from "../list-query.js";
|
|
12
|
-
import {
|
|
12
|
+
import { pointsOf } from "./catch-up.js";
|
|
13
13
|
import { catchUpQuery, REVIEW_QUERY, reviewAnswer } from "./catch-up-answers.js";
|
|
14
14
|
import { messengerContext } from "./context.js";
|
|
15
15
|
import { heardItems, hearForCommand, MODEL_OPTION, spokenItems, TRANSCRIBE_OPTION } from "./hearing-command.js";
|
|
@@ -21,7 +21,7 @@ export const unansweredHours = (value, flag = "--unanswered") => value === true
|
|
|
21
21
|
/**
|
|
22
22
|
* **Everything said since a point, both sides, in every chat that changed** — the reading half of a
|
|
23
23
|
* review of who owes what (max-cli's `review`). Sorting it is the reader's job, person or agent.
|
|
24
|
-
*
|
|
24
|
+
* Reading never marks a chat read, and `inbox --new` keeps its point. The rules open and close
|
|
25
25
|
* tasks in the local store from what it read (`../../services/task-rules.ts`); nothing reaches the messenger.
|
|
26
26
|
*/
|
|
27
27
|
export const reviewCommand = (messenger) => annotate(new Command("review"), {
|
|
@@ -38,8 +38,7 @@ export const reviewCommand = (messenger) => annotate(new Command("review"), {
|
|
|
38
38
|
.option(...TRANSCRIBE_OPTION)
|
|
39
39
|
.option(...MODEL_OPTION)
|
|
40
40
|
.option("--new", "what changed since the last `review --new`, a point per chat — for scheduled runs")
|
|
41
|
-
.
|
|
42
|
-
.addOption(ownerOnlyOption(...NO_MARK_READ_OPTION))
|
|
41
|
+
.addHelpText("after", "To mark a chat read, use `chats mark-read <chat>`. The other side sees it.")
|
|
43
42
|
.action(async function (query) {
|
|
44
43
|
const options = this.opts();
|
|
45
44
|
const hearWith = modelWith(options.transcribe, options.model);
|
|
@@ -59,12 +58,11 @@ export const reviewCommand = (messenger) => annotate(new Command("review"), {
|
|
|
59
58
|
})
|
|
60
59
|
: undefined;
|
|
61
60
|
const since = from ?? saved?.first ?? reviewStart();
|
|
62
|
-
const marking = marksRead(options.markRead, settings, this);
|
|
63
61
|
const hours = options.unanswered === undefined ? undefined : unansweredHours(options.unanswered);
|
|
64
62
|
const kinds = options.kind === undefined ? undefined : kindsOf(options.kind);
|
|
65
63
|
let hearing;
|
|
66
64
|
const transcribe = options.transcribe === true;
|
|
67
|
-
const { found,
|
|
65
|
+
const { found, reader } = await context.withServices(async (services, connect) => {
|
|
68
66
|
const found = await services.inbox.review({
|
|
69
67
|
since,
|
|
70
68
|
...(saved === undefined ? {} : { points: saved.chats }),
|
|
@@ -84,13 +82,10 @@ export const reviewCommand = (messenger) => annotate(new Command("review"), {
|
|
|
84
82
|
});
|
|
85
83
|
return {
|
|
86
84
|
found,
|
|
87
|
-
marked: marking ? await markShown(services, found.chats, context.renderer.warn) : undefined,
|
|
88
85
|
reader: await services.messages.reader(),
|
|
89
86
|
};
|
|
90
87
|
});
|
|
91
88
|
found.complete &&= (hearing?.unheard.length ?? 0) === 0;
|
|
92
|
-
if (marked !== undefined)
|
|
93
|
-
renderer.note(`marked ${marked.length} chat(s) read up to the newest message shown`);
|
|
94
89
|
notes(found, format === "pretty", renderer);
|
|
95
90
|
renderAnswer(this, context, reviewAnswer(found, hearing, reader, {
|
|
96
91
|
transcribe,
|
|
@@ -2,9 +2,9 @@ import { Command } from "commander";
|
|
|
2
2
|
import type { Messenger } from "./context.js";
|
|
3
3
|
/** `doctor`'s short summary of the store. Must answer for a broken file, so an error is a field. */
|
|
4
4
|
export declare const storeSummary: (env: NodeJS.ProcessEnv) => Promise<{
|
|
5
|
-
error?: undefined;
|
|
6
5
|
path: string;
|
|
7
6
|
exists: boolean;
|
|
7
|
+
error?: undefined;
|
|
8
8
|
} | {
|
|
9
9
|
path: string;
|
|
10
10
|
exists: boolean;
|
|
@@ -2,7 +2,7 @@ import { CliError } from "@wirecat/cli-base";
|
|
|
2
2
|
import { annotate } from "@wirecat/cli-base/commands";
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { TAGGING_QUERY } from "../../services/tags.js";
|
|
5
|
-
import { LOCAL_READ, LOCAL_WRITE } from "../effects.js";
|
|
5
|
+
import { changesStore, FETCH, LOCAL_READ, LOCAL_WRITE } from "../effects.js";
|
|
6
6
|
import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
|
|
7
7
|
import { listQuery, pagedList, pageOf, typedQuery, withQuery } from "../list-query.js";
|
|
8
8
|
import { positiveCount } from "../paging.js";
|
|
@@ -29,7 +29,7 @@ const sourceOf = (value) => {
|
|
|
29
29
|
/** The owner's labels, in the local store only: `tag:` in a search finds what they label. */
|
|
30
30
|
export const tagsCommand = (messenger) => {
|
|
31
31
|
const tags = new Command("tags").description("your own labels on chats, people, messages and tasks, kept in the local store and never sent; tag: in a search finds them");
|
|
32
|
-
annotate(tags.command("auto"), { effects:
|
|
32
|
+
changesStore(annotate(tags.command("auto"), { effects: FETCH }))
|
|
33
33
|
.description("derive local group/channel tags from cached metadata using keyword rules")
|
|
34
34
|
.option("--chat <chat>", "a stored group/channel; repeat to select several", (value, previous) => [...previous, value], [])
|
|
35
35
|
.option("--limit <number>", "process at most 1–500 chats", positiveCount("--limit"), 50)
|
package/dist/cli/program.js
CHANGED
|
@@ -10,6 +10,7 @@ import { isCliFailure, isCommanderFailure } from "../errors/failures.js";
|
|
|
10
10
|
import { failureContext } from "../errors/reasons.js";
|
|
11
11
|
import { envName, readOnlyLockOf, readOnlyRefusal } from "../ports/app.js";
|
|
12
12
|
import { recorded, wasSettled } from "../runs/recording.js";
|
|
13
|
+
import { processRun, useAsProcessRun } from "../runs/scope.js";
|
|
13
14
|
import { keyForCommand } from "../sends/permissions.js";
|
|
14
15
|
import { logParsedArgv, logVersion } from "./argv-log.js";
|
|
15
16
|
import { commandPathOf } from "./command-contract.js";
|
|
@@ -104,6 +105,15 @@ const RELAY_MARGIN_MS = 10_000;
|
|
|
104
105
|
* message: it tells a script nothing it can branch on, and exits 1 for every kind of failure alike.
|
|
105
106
|
*/
|
|
106
107
|
export const run = async (argv, definition, options = {}) => {
|
|
108
|
+
const outer = processRun();
|
|
109
|
+
try {
|
|
110
|
+
return await runOnce(argv, definition, options);
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
useAsProcessRun(outer);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const runOnce = async (argv, definition, options) => {
|
|
107
117
|
const streams = options.streams ?? processStreams;
|
|
108
118
|
if (!options.streams)
|
|
109
119
|
quietBrokenPipes();
|
package/dist/cli/settings.d.ts
CHANGED
|
@@ -78,8 +78,6 @@ export interface Settings extends AISettings {
|
|
|
78
78
|
/** Unset means "decide from the terminal", which is `resolveOutput`'s job, not this one's. */
|
|
79
79
|
color: boolean | undefined;
|
|
80
80
|
senderColors: boolean;
|
|
81
|
-
/** `inbox` and `review` mark each chat they show read, as `--mark-read` does; the other side sees it. Off unless set. */
|
|
82
|
-
catchUpMarksRead: boolean;
|
|
83
81
|
searchCatchUp?: boolean;
|
|
84
82
|
/** Search and `images embed` read the pictures themselves, with a model on this machine. Off until set. */
|
|
85
83
|
pictureSearch?: boolean;
|
|
@@ -297,7 +295,6 @@ export declare const settingsFor: <T extends object = Record<never, never>>(app:
|
|
|
297
295
|
requests_per_minute: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 0, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
|
|
298
296
|
transcribe_with: v.OptionalSchema<v.PicklistSchema<["auto", "messenger", "local"], (issue: v.BaseIssue<unknown>) => string>, undefined>;
|
|
299
297
|
speech_model: v.OptionalSchema<v.StringSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
|
|
300
|
-
catch_up_marks_read: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
|
|
301
298
|
search_catch_up: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
|
|
302
299
|
picture_search: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
|
|
303
300
|
update_check: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
|
|
@@ -427,7 +424,6 @@ export declare const settingsFor: <T extends object = Record<never, never>>(app:
|
|
|
427
424
|
requests_per_minute: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 0, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
|
|
428
425
|
transcribe_with: v.OptionalSchema<v.PicklistSchema<["auto", "messenger", "local"], (issue: v.BaseIssue<unknown>) => string>, undefined>;
|
|
429
426
|
speech_model: v.OptionalSchema<v.StringSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
|
|
430
|
-
catch_up_marks_read: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
|
|
431
427
|
search_catch_up: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
|
|
432
428
|
picture_search: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
|
|
433
429
|
}, (issue: v.StrictObjectIssue) => string>, "has to be an object of profiles, by name">, {}>;
|
package/dist/cli/settings.js
CHANGED
|
@@ -48,7 +48,6 @@ const SHARED_PROFILE_ENTRIES = {
|
|
|
48
48
|
requests_per_minute: v.optional(countOrZero),
|
|
49
49
|
transcribe_with: v.optional(v.picklist(["auto", "messenger", "local"], plain("has to be auto, messenger or local"))),
|
|
50
50
|
speech_model: v.optional(v.string(plain("has to be a model id from `models audio list`, in quotes"))),
|
|
51
|
-
catch_up_marks_read: v.optional(flag),
|
|
52
51
|
search_catch_up: v.optional(flag),
|
|
53
52
|
picture_search: v.optional(flag),
|
|
54
53
|
...AI_ENTRIES,
|
|
@@ -116,7 +115,10 @@ export const settingsFor = (app, extension = {}) => {
|
|
|
116
115
|
return loadSnakeCaseConfig(path, extension.schema ?? schema, () => ({ profiles: {} }));
|
|
117
116
|
}
|
|
118
117
|
catch (error) {
|
|
119
|
-
|
|
118
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
119
|
+
throw new CliError("configuration_error", message.includes("catch_up_marks_read")
|
|
120
|
+
? `${message}; remove catch_up_marks_read from the config file and use ${app.command} <profile> chats mark-read <chat>`
|
|
121
|
+
: message);
|
|
120
122
|
}
|
|
121
123
|
};
|
|
122
124
|
/**
|
|
@@ -187,13 +189,15 @@ export const settingsFor = (app, extension = {}) => {
|
|
|
187
189
|
[extension.sourcePaths ? "config file: defaults" : "config defaults", shared],
|
|
188
190
|
];
|
|
189
191
|
const fromLayers = (key, fallback, only = layers) => first(only.map(([from, scope]) => [from, scope?.[key]]), fallback);
|
|
192
|
+
if (fromLayers("catch_up_marks_read", undefined).value !== undefined) {
|
|
193
|
+
throw new CliError("configuration_error", `remove catch_up_marks_read from the config file and use ${app.command} <profile> chats mark-read <chat>`);
|
|
194
|
+
}
|
|
190
195
|
const kindLayers = layers.filter(([from]) => from.startsWith(`config file: ${kind}.`));
|
|
191
196
|
const limit = flags.limit === undefined ? fromLayers("limit", DEFAULT_LIMIT) : { value: flags.limit, from: "flag" };
|
|
192
197
|
const record = flags.record === undefined ? fromLayers("record", false) : { value: flags.record, from: "flag" };
|
|
193
198
|
const timeoutMs = fromLayers("timeout_ms", undefined);
|
|
194
199
|
const color = fromLayers("color", undefined);
|
|
195
200
|
const senderColors = fromLayers("sender_colors", false);
|
|
196
|
-
const catchUpMarksRead = fromLayers("catch_up_marks_read", false);
|
|
197
201
|
const searchCatchUp = fromLayers("search_catch_up", false);
|
|
198
202
|
const pictureSearch = fromLayers("picture_search", false);
|
|
199
203
|
const keepRunsForDays = fromLayers("keep_runs_for_days", DEFAULT_KEEP_RUNS_FOR_DAYS);
|
|
@@ -241,7 +245,6 @@ export const settingsFor = (app, extension = {}) => {
|
|
|
241
245
|
offline: flags.offline === true,
|
|
242
246
|
color: color.value,
|
|
243
247
|
senderColors: senderColors.value,
|
|
244
|
-
catchUpMarksRead: catchUpMarksRead.value,
|
|
245
248
|
searchCatchUp: searchCatchUp.value,
|
|
246
249
|
pictureSearch: pictureSearch.value,
|
|
247
250
|
limit: limit.value,
|
|
@@ -275,7 +278,6 @@ export const settingsFor = (app, extension = {}) => {
|
|
|
275
278
|
commandTimeoutMs: timeout.from,
|
|
276
279
|
color: color.from,
|
|
277
280
|
senderColors: senderColors.from,
|
|
278
|
-
catchUpMarksRead: catchUpMarksRead.from,
|
|
279
281
|
searchCatchUp: searchCatchUp.from,
|
|
280
282
|
pictureSearch: pictureSearch.from,
|
|
281
283
|
record: record.from,
|
package/dist/meetings/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { type Finding, type FindingsInput, type FindingsReceipt, type FindingsRu
|
|
|
9
9
|
export { MEETING_HIT_QUERY, meetingWordsQuery, parseMeetingQuery } from "./hit-query.js";
|
|
10
10
|
export { type ImportFile, importFiles } from "./import.js";
|
|
11
11
|
export { MEETING_ISSUES, MEETING_QUERY, type MeetingIssue, type MeetingItem, type MeetingOwner, meeting, meetingIssuesOf, meetingItem, } from "./item.js";
|
|
12
|
+
export { CHAT_MESSAGES_READ, type MeetingChatLine, type MeetingChatLinesInput, type MeetingChatLinesStore, } from "./links.js";
|
|
12
13
|
export { createMeetingContext, MEETING_FACT_TYPES, type MeetingContextAction, type MeetingContextChange, type MeetingContextOperation, type MeetingContextPorts, type MeetingContextReceipt, validateMeetingContextChange, } from "./meeting-context.js";
|
|
13
14
|
export { createMeetingManagement, dispatchMeetingMutation, type ManagedMeeting, type ManagedMeetingChange, type MeetingManagement, type MeetingManagementPorts, type MeetingMutationOptions, type MeetingMutationProposal, type MeetingMutationReceipt, type MeetingOperationOptions, type MeetingProviderContext, validateMeetingChange, } from "./meeting-management.js";
|
|
14
15
|
export type { Attachment, ChatLine, Event, EventSeries, JsonValue, Meeting, MeetingSeries, Metadata, Occurrence, Participant, RecordTimes, SourceChatLine, SourceMeeting, Summary, SummaryContent, Transcript, TranscriptLine, TranscriptRow, } from "./model.js";
|
|
@@ -32,6 +33,7 @@ export { parseRecordingSyncCheckpoint, type RecordingSyncCheckpoint, type Record
|
|
|
32
33
|
export { type RecordingPartial, RecordingTransferError, type RecordingTransferResponse, type RecordingTransferSource, type RecordingTransferStorage, type RecordingTransferWriter, strongRecordingValidator, transferRecording, } from "./recording-transfer.js";
|
|
33
34
|
export { canonicalMeetingReference, formatMeetingReference, type MeetingReference, parseMeetingReference, } from "./reference.js";
|
|
34
35
|
export { type MeetingRelatedHistoryEntry, type MeetingRelatedHistoryRequest, type MeetingRelatedHistoryStore, validateMeetingRelatedHistory, } from "./related-history.js";
|
|
36
|
+
export { type MeetingReview, type MeetingReviewInput, type MeetingReviewItem, type MeetingReviewSpeaker, type MeetingReviewStore, REVIEW_LIMIT, } from "./review.js";
|
|
35
37
|
export { SAVE_HELP, SAVE_INSTRUCTION } from "./save-hints.js";
|
|
36
38
|
export type { MeetingHitSearchRequest, MeetingSearchIndexInput, MeetingSearchIndexReceipt, MeetingSearchIndexStore, MeetingSearchOptions, MeetingSearchPage, MeetingSearchStore, } from "./search-store.js";
|
|
37
39
|
export { validateMeetingHitSearch } from "./search-validation.js";
|
package/dist/meetings/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export { findingFingerprint, validateFindings, } from "./findings-rules.js";
|
|
|
7
7
|
export { MEETING_HIT_QUERY, meetingWordsQuery, parseMeetingQuery } from "./hit-query.js";
|
|
8
8
|
export { importFiles } from "./import.js";
|
|
9
9
|
export { MEETING_ISSUES, MEETING_QUERY, meeting, meetingIssuesOf, meetingItem, } from "./item.js";
|
|
10
|
+
export { CHAT_MESSAGES_READ, } from "./links.js";
|
|
10
11
|
export { createMeetingContext, MEETING_FACT_TYPES, validateMeetingContextChange, } from "./meeting-context.js";
|
|
11
12
|
export { createMeetingManagement, dispatchMeetingMutation, validateMeetingChange, } from "./meeting-management.js";
|
|
12
13
|
export { normalizeMeetingParticipantQuery, validateMeetingParticipantSearch, } from "./participant-search.js";
|
|
@@ -23,6 +24,7 @@ export { parseRecordingSyncCheckpoint, RecordingSyncError, recordingAssetFingerp
|
|
|
23
24
|
export { RecordingTransferError, strongRecordingValidator, transferRecording, } from "./recording-transfer.js";
|
|
24
25
|
export { canonicalMeetingReference, formatMeetingReference, parseMeetingReference, } from "./reference.js";
|
|
25
26
|
export { validateMeetingRelatedHistory, } from "./related-history.js";
|
|
27
|
+
export { REVIEW_LIMIT, } from "./review.js";
|
|
26
28
|
export { SAVE_HELP, SAVE_INSTRUCTION } from "./save-hints.js";
|
|
27
29
|
export { validateMeetingHitSearch } from "./search-validation.js";
|
|
28
30
|
export { validateMeetingSeriesHistory, } from "./series-history.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { MeetingStatsSelection } from "./stats.js";
|
|
2
|
+
export interface MeetingChatLinesInput extends MeetingStatsSelection {
|
|
3
|
+
/** Most chat messages to read (default and at most 100000); more is an error, never a silent cut. */
|
|
4
|
+
maxMessages?: number;
|
|
5
|
+
}
|
|
6
|
+
/** One stored chat message with the meeting it was sent in. */
|
|
7
|
+
export interface MeetingChatLine {
|
|
8
|
+
meetingId: number;
|
|
9
|
+
accountId: number;
|
|
10
|
+
title: string | null;
|
|
11
|
+
sentAt: number;
|
|
12
|
+
sender: string | null;
|
|
13
|
+
text: string;
|
|
14
|
+
}
|
|
15
|
+
export interface MeetingChatLinesStore {
|
|
16
|
+
meetingChatLines(input: MeetingChatLinesInput): Promise<MeetingChatLine[]>;
|
|
17
|
+
}
|
|
18
|
+
export declare const CHAT_MESSAGES_READ = 100000;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const CHAT_MESSAGES_READ = 100_000;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { MeetingStatsSelection } from "./stats.js";
|
|
2
|
+
export interface MeetingReviewInput extends MeetingStatsSelection {
|
|
3
|
+
/** Most meetings, newest first (default 50, at most 200). */
|
|
4
|
+
limit?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface MeetingReviewSpeaker {
|
|
7
|
+
name: string | null;
|
|
8
|
+
talkMs: number;
|
|
9
|
+
}
|
|
10
|
+
/** One meeting as a review shows it: what the store holds about it, never transcript text. */
|
|
11
|
+
export interface MeetingReviewItem {
|
|
12
|
+
meetingId: number;
|
|
13
|
+
accountId: number;
|
|
14
|
+
title: string | null;
|
|
15
|
+
startedAt: number | null;
|
|
16
|
+
durationMs: number | null;
|
|
17
|
+
participantsCount: number | null;
|
|
18
|
+
projects: string[];
|
|
19
|
+
/** `null`: no current transcript. */
|
|
20
|
+
transcript: {
|
|
21
|
+
lines: number;
|
|
22
|
+
words: number;
|
|
23
|
+
} | null;
|
|
24
|
+
/** The three who talked longest, from the transcript. */
|
|
25
|
+
speakers: MeetingReviewSpeaker[];
|
|
26
|
+
/** The newest stored summary; `null` when the meeting has none. */
|
|
27
|
+
summary: {
|
|
28
|
+
source: string;
|
|
29
|
+
title: string | null;
|
|
30
|
+
overview: string | null;
|
|
31
|
+
nextSteps: string[];
|
|
32
|
+
url: string | null;
|
|
33
|
+
} | null;
|
|
34
|
+
}
|
|
35
|
+
export interface MeetingReview {
|
|
36
|
+
items: MeetingReviewItem[];
|
|
37
|
+
hasMore: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface MeetingReviewStore {
|
|
40
|
+
meetingReview(input: MeetingReviewInput): Promise<MeetingReview>;
|
|
41
|
+
}
|
|
42
|
+
export declare const REVIEW_LIMIT: {
|
|
43
|
+
readonly default: 50;
|
|
44
|
+
readonly most: 200;
|
|
45
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const REVIEW_LIMIT = { default: 50, most: 200 };
|
package/dist/runs/recording.d.ts
CHANGED
|
@@ -19,6 +19,11 @@ export interface RecordingOptions {
|
|
|
19
19
|
now?: () => Date;
|
|
20
20
|
/** The caller's trace; otherwise the enclosing run's, otherwise `TRACEPARENT` from `env`. */
|
|
21
21
|
traceId?: string;
|
|
22
|
+
/**
|
|
23
|
+
* A one-shot command prints its answer after the run ends, outside `inRun`: naming the run as the process's lets
|
|
24
|
+
* that answer's `meta.run_id` name the run `runs show` finds. Never in a server, where runs overlap.
|
|
25
|
+
*/
|
|
26
|
+
processRun?: boolean;
|
|
22
27
|
}
|
|
23
28
|
/**
|
|
24
29
|
* Runs one command with the diagnostics turned on, and **finalizes on every path**.
|