@wirecat/cli-core 0.406.0 → 0.408.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 (46) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cli/answer-entity.d.ts +4 -1
  3. package/dist/cli/answer-entity.js +8 -2
  4. package/dist/cli/command-availability.d.ts +14 -0
  5. package/dist/cli/command-availability.js +25 -0
  6. package/dist/cli/command-contract.js +4 -2
  7. package/dist/cli/commands-command.d.ts +1 -1
  8. package/dist/cli/commands-command.js +17 -4
  9. package/dist/cli/context.d.ts +1 -0
  10. package/dist/cli/effects.d.ts +1 -1
  11. package/dist/cli/effects.js +1 -1
  12. package/dist/cli/help/command-help.js +18 -1
  13. package/dist/cli/help/commands-text.d.ts +1 -1
  14. package/dist/cli/help/commands-text.js +3 -2
  15. package/dist/cli/index.d.ts +6 -2
  16. package/dist/cli/index.js +5 -2
  17. package/dist/cli/json-fields.js +2 -2
  18. package/dist/cli/meeting-batch.d.ts +1 -1
  19. package/dist/cli/meeting-batch.js +20 -2
  20. package/dist/cli/meeting-results.d.ts +3 -0
  21. package/dist/cli/meeting-results.js +31 -0
  22. package/dist/cli/program.d.ts +2 -1
  23. package/dist/cli/program.js +24 -1
  24. package/dist/cli/result-fields.js +19 -1
  25. package/dist/entities/contact.d.ts +6 -0
  26. package/dist/entities/contact.js +3 -0
  27. package/dist/entities/index.d.ts +139 -0
  28. package/dist/entities/index.js +4 -1
  29. package/dist/entities/store-records.d.ts +11 -0
  30. package/dist/entities/store-records.js +1 -0
  31. package/dist/mcp/agent.d.ts +11 -11
  32. package/dist/mcp/agent.js +13 -27
  33. package/dist/mcp/bot/instructions.js +2 -1
  34. package/dist/mcp/bot/server.d.ts +2 -2
  35. package/dist/mcp/bot/server.js +29 -7
  36. package/dist/mcp/index.d.ts +1 -1
  37. package/dist/mcp/instructions.js +2 -1
  38. package/dist/mcp/server.js +33 -7
  39. package/dist/mcp/surface.d.ts +3 -1
  40. package/dist/mcp/surface.js +8 -2
  41. package/dist/meetings/cli/index.js +10 -3
  42. package/dist/meetings/index.d.ts +1 -0
  43. package/dist/meetings/index.js +1 -0
  44. package/dist/meetings/result-items.d.ts +127 -0
  45. package/dist/meetings/result-items.js +125 -0
  46. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,30 @@ 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
+ ## 0.408.0 — 24.10.2026
9
+
10
+ ### Fixed
11
+
12
+ - **MCP discovery retains permission-denied commands.** Full listings and searches return each command's
13
+ current `availability`, including a reason and permission key when blocked. With a named profile,
14
+ CLI help and command discovery also show current availability without hiding commands. Read-only profiles and process
15
+ locks keep writing commands discoverable. Calls still recheck permissions and refuse before execution;
16
+ seeing a command does not grant access. Bot MCP also lists bot-scoped `config show|set|unset` behind the
17
+ existing authority rules, without exposing options that could select personal-account settings. Consumers
18
+ that pin MCP catalogues should refresh them and use availability rather than absence to determine access.
19
+
20
+ ## 0.407.0 — 24.10.2026
21
+
22
+ ### Added
23
+
24
+ - The `contact` entity has optional `email` and `phones` (number and label), for a source that knows them, such as zm's Zoom contacts.
25
+
26
+ - Command discovery projections preserve the CLI document and command identities while selecting fields inside its command tree.
27
+
28
+ - Portable ingestion, export, association, transcription and embedding receipts have shared entities and `meetingResultItem` serializers. Public IDs are strings; stored numeric IDs remain internal.
29
+
30
+ - Meeting batch results declare their correlated success/error schemas. `answerEntity` can name a separate typed projection payload, preserving batch field selection inside each answer without weakening its outer result contract.
31
+
8
32
  ## 0.406.0 — 24.10.2026
9
33
 
10
34
  ### Added
@@ -1,5 +1,8 @@
1
1
  import type { Command } from "commander";
2
2
  import type * as v from "valibot";
3
3
  /** Provider-specific wire fields must not redefine an entity shared by every CLI. */
4
- export declare const answerEntity: (command: Command, name: string, schema: v.GenericSchema) => Command;
4
+ export declare const answerEntity: (command: Command, name: string, schema: v.GenericSchema, options?: {
5
+ project?: v.GenericSchema;
6
+ }) => Command;
5
7
  export declare const answerEntitySchema: (command: Command | undefined) => v.GenericSchema | undefined;
8
+ export declare const answerProjectionSchema: (command: Command | undefined) => v.GenericSchema | undefined;
@@ -2,14 +2,16 @@ import { annotate, metaOf } from "@wirecat/cli-base/commands";
2
2
  import { ENTITIES } from "../entities/index.js";
3
3
  const schemas = new WeakMap();
4
4
  /** Provider-specific wire fields must not redefine an entity shared by every CLI. */
5
- export const answerEntity = (command, name, schema) => {
5
+ export const answerEntity = (command, name, schema, options = {}) => {
6
6
  if (Object.hasOwn(ENTITIES, name))
7
7
  throw new Error(`${name} is a shared entity; use its existing schema`);
8
8
  if (!/^[a-z][a-z0-9_]*$/.test(name))
9
9
  throw new Error("an answer entity needs a lowercase name");
10
10
  if (!("entries" in schema) && schema.type !== "union")
11
11
  throw new Error("an answer entity needs an object schema or a union of object schemas");
12
- schemas.set(command, { name, schema });
12
+ if (options.project && !("entries" in options.project) && options.project.type !== "union")
13
+ throw new Error("projection needs an object schema or a union of object schemas");
14
+ schemas.set(command, { name, schema, ...options });
13
15
  return annotate(command, { output: { entity: name } });
14
16
  };
15
17
  export const answerEntitySchema = (command) => {
@@ -23,3 +25,7 @@ export const answerEntitySchema = (command) => {
23
25
  const own = schemas.get(command);
24
26
  return own?.name === name ? own.schema : undefined;
25
27
  };
28
+ export const answerProjectionSchema = (command) => {
29
+ const own = command && schemas.get(command);
30
+ return own && metaOf(command).output?.entity === own.name ? own.project : undefined;
31
+ };
@@ -0,0 +1,14 @@
1
+ import type { CommandInfo } from "@wirecat/cli-base/commands";
2
+ import { type Level, type PermissionKey } from "../sends/permissions.js";
3
+ export type AvailabilityCommand = Pick<CommandInfo, "path" | "effects" | "lifetime" | "reaches">;
4
+ export interface DiscoveredCommand extends CommandInfo {
5
+ availability?: CommandAvailability;
6
+ commands: readonly DiscoveredCommand[];
7
+ }
8
+ export interface CommandAvailability {
9
+ available: boolean;
10
+ reason?: string;
11
+ permission?: string;
12
+ }
13
+ /** Discovery and execution use the same permission decision. */
14
+ export declare const commandAvailability: (info: AvailabilityCommand, levels: Readonly<Record<PermissionKey, Level>>, keyOf?: (path: readonly string[]) => PermissionKey | null | undefined, authority?: (path: readonly string[]) => boolean, readOnly?: boolean, mcp?: boolean) => CommandAvailability;
@@ -0,0 +1,25 @@
1
+ import { writes } from "@wirecat/cli-base/standards";
2
+ import { AUTHORITY_KEY, authorityAllowed, BOT_API_KEY, botApiAllowed, isBotApi, keyForCommand, levelFor, readKeysForCommand, } from "../sends/permissions.js";
3
+ import { changesMessenger } from "./effects.js";
4
+ /** Discovery and execution use the same permission decision. */
5
+ export const commandAvailability = (info, levels, keyOf, authority, readOnly = false, mcp = true) => {
6
+ const blocked = (reason, permission) => ({
7
+ available: false,
8
+ reason,
9
+ ...(permission ? { permission } : {}),
10
+ });
11
+ if (readOnly && (changesMessenger(info) || authority?.(info.path)))
12
+ return blocked("the read-only lock blocks this change");
13
+ if (mcp && authority?.(info.path) && !authorityAllowed(levels))
14
+ return blocked(`the profile keeps ${AUTHORITY_KEY} off`, AUTHORITY_KEY);
15
+ if (mcp && isBotApi(info.path) && !botApiAllowed(levels))
16
+ return blocked(`the profile requires ${BOT_API_KEY}: allow`, BOT_API_KEY);
17
+ const key = keyOf?.(info.path) ?? keyForCommand(info.path);
18
+ const level = key ? levelFor(levels, key).level : "allow";
19
+ if (level === "deny")
20
+ return blocked(`the profile denies ${key}`, key ?? undefined);
21
+ if (level === "readonly" && writes(info.effects))
22
+ return blocked(`${key} is readonly in this profile`, key ?? undefined);
23
+ const denied = readKeysForCommand(info.path).find((read) => levelFor(levels, read).level === "deny");
24
+ return denied ? blocked(`the profile denies ${denied}`, denied) : { available: true };
25
+ };
@@ -2,7 +2,7 @@ import { CliError } from "@wirecat/cli-base";
2
2
  import { describeOptions, metaOf } from "@wirecat/cli-base/commands";
3
3
  import { jsonSchemaOf } from "@wirecat/cli-base/standards";
4
4
  import { keyForCommand } from "../sends/permissions.js";
5
- import { answerEntitySchema } from "./answer-entity.js";
5
+ import { answerEntitySchema, answerProjectionSchema } from "./answer-entity.js";
6
6
  import { answerSchema } from "./envelope.js";
7
7
  export const SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
8
8
  /**
@@ -97,7 +97,9 @@ export const commandContract = (command) => {
97
97
  outputSchemaCoverage: output.coverage,
98
98
  jsonlSchema: { $schema: SCHEMA_DIALECT, ...output.item },
99
99
  jsonlSchemaCoverage: output.coverage,
100
- projection: "--json=<fields> may omit item/object members; present members retain their types and envelopes are preserved",
100
+ projection: answerProjectionSchema(command)
101
+ ? "--json=<fields> selects inside each nested answer; correlation and envelopes are preserved"
102
+ : "--json=<fields> may omit item/object members; present members retain their types and envelopes are preserved",
101
103
  validation: {
102
104
  syntax: "declared",
103
105
  semantic: "command-and-provider-validation",
@@ -7,6 +7,6 @@ import type { AppIdentity } from "../ports/app.js";
7
7
  export declare const CONTRACT = 3;
8
8
  /**
9
9
  * The discovery surface an agent reads instead of `--help`. It opens no store, no session and no
10
- * settings file: which commands exist does not depend on whether this machine has logged in.
10
+ * settings file without a named profile. A named profile adds availability without hiding commands.
11
11
  */
12
12
  export declare const commandsCommand: (app: AppIdentity) => Command;
@@ -1,8 +1,8 @@
1
1
  import { CliError, EXIT_CODES, GENERIC_FAILURE } from "@wirecat/cli-base";
2
- import { annotate, describeOptions, describeProgram } from "@wirecat/cli-base/commands";
2
+ import { annotate, describeOptions, describeProgram, metaOf } from "@wirecat/cli-base/commands";
3
3
  import { CRASH_CODE } from "@wirecat/cli-base/standards";
4
4
  import { Command } from "commander";
5
- import { commandContract, findCommand } from "./command-contract.js";
5
+ import { commandContract, commandPathOf, findCommand } from "./command-contract.js";
6
6
  import { environmentOf, outputFor } from "./context.js";
7
7
  import { NO_EFFECTS } from "./effects.js";
8
8
  import { renderDocument } from "./envelope.js";
@@ -15,7 +15,7 @@ import { rootOf } from "./profile.js";
15
15
  export const CONTRACT = 3;
16
16
  /**
17
17
  * The discovery surface an agent reads instead of `--help`. It opens no store, no session and no
18
- * settings file: which commands exist does not depend on whether this machine has logged in.
18
+ * settings file without a named profile. A named profile adds availability without hiding commands.
19
19
  */
20
20
  export const commandsCommand = (app) => {
21
21
  const command = annotate(new Command("commands"), { effects: NO_EFFECTS })
@@ -30,7 +30,12 @@ export const commandsCommand = (app) => {
30
30
  .action(function (path) {
31
31
  const root = rootOf(this);
32
32
  const output = outputFor(this);
33
- const all = describeProgram(root);
33
+ const available = environmentOf(this).discoveryAvailability;
34
+ const annotateAvailability = (infos) => infos.map((info) => {
35
+ const availability = available?.(info);
36
+ return { ...info, ...(availability ? { availability } : {}), commands: annotateAvailability(info.commands) };
37
+ });
38
+ const all = annotateAvailability(describeProgram(root));
34
39
  const scoped = path.length > 0 ? scopeOf(root, all, path) : undefined;
35
40
  const commands = scoped ? [scoped.command] : all;
36
41
  const { json, jsonl } = this.optsWithGlobals();
@@ -62,6 +67,14 @@ export const commandsCommand = (app) => {
62
67
  version: app.version,
63
68
  contract: CONTRACT,
64
69
  ...commandContract(findCommand(rootOf(this), path)),
70
+ ...(() => {
71
+ const found = findCommand(rootOf(this), path);
72
+ const availability = environmentOf(this).discoveryAvailability?.({
73
+ ...metaOf(found),
74
+ path: commandPathOf(found),
75
+ });
76
+ return availability ? { availability } : {};
77
+ })(),
65
78
  });
66
79
  }));
67
80
  return command;
@@ -11,6 +11,7 @@ import { type Closeable } from "./deadline.js";
11
11
  import type { GlobalFlags, ResolveOptions, Settings } from "./settings.js";
12
12
  /** What every CLI's commands write to, when it is not the real terminal. A CLI adds its own fields. */
13
13
  export interface BaseEnvironment {
14
+ discoveryAvailability?: (command: import("./command-availability.js").AvailabilityCommand) => import("./command-availability.js").CommandAvailability | undefined;
14
15
  streams?: Streams;
15
16
  /** Whether a person is looking. Defaults to whether stdout is a terminal. */
16
17
  tty?: boolean;
@@ -14,7 +14,7 @@ export declare const NO_EFFECTS: Effects;
14
14
  /**
15
15
  * Marks a local write that changes what a later run may do: a schedule, a setting, the recipient list, an
16
16
  * auto-reply, an agent's setup. `<PREFIX>_READ_ONLY` refuses it, or an agent under the lock could hand an
17
- * unlocked run the send it may not make itself (SEC-47). For the same reason an MCP server offers it only where the
17
+ * unlocked run the send it may not make itself (SEC-47). For the same reason an MCP server permits execution only where the
18
18
  * profile allows `mcp.authority`, which is off until the owner turns it on (NEED-1662).
19
19
  * `unless` names a flag that makes the run only look (`upgrade --check`), which the lock lets through.
20
20
  */
@@ -16,7 +16,7 @@ const AUTHORITY = new WeakMap();
16
16
  /**
17
17
  * Marks a local write that changes what a later run may do: a schedule, a setting, the recipient list, an
18
18
  * auto-reply, an agent's setup. `<PREFIX>_READ_ONLY` refuses it, or an agent under the lock could hand an
19
- * unlocked run the send it may not make itself (SEC-47). For the same reason an MCP server offers it only where the
19
+ * unlocked run the send it may not make itself (SEC-47). For the same reason an MCP server permits execution only where the
20
20
  * profile allows `mcp.authority`, which is off until the owner turns it on (NEED-1662).
21
21
  * `unless` names a flag that makes the run only look (`upgrade --check`), which the lock lets through.
22
22
  */
@@ -1,6 +1,7 @@
1
1
  import { metaOf } from "@wirecat/cli-base/commands";
2
2
  import { writes } from "@wirecat/cli-base/standards";
3
3
  import { commandPathOf, outputOf } from "../command-contract.js";
4
+ import { environmentOf } from "../context.js";
4
5
  import { PROFILE_FREE } from "../profile.js";
5
6
  import { commandAt } from "../steps.js";
6
7
  import { short } from "./commands-text.js";
@@ -195,7 +196,23 @@ const LABEL = 15;
195
196
  export const commandHelpText = (command) => {
196
197
  const { returns, next, examples } = commandHelp(command);
197
198
  const rows = (label, values) => values.map((value, at) => `${(at === 0 ? label : "").padEnd(LABEL)}${value}`);
198
- return ["", ...rows("Returns:", [returns]), ...rows("Next usually:", next), ...rows("Example:", examples), ""].join("\n");
199
+ const availability = environmentOf(command).discoveryAvailability?.({
200
+ ...metaOf(command),
201
+ path: commandPathOf(command),
202
+ });
203
+ const access = availability
204
+ ? rows("Availability:", [
205
+ availability.available ? "available for this profile" : `unavailable — ${availability.reason}`,
206
+ ])
207
+ : [];
208
+ return [
209
+ "",
210
+ ...access,
211
+ ...rows("Returns:", [returns]),
212
+ ...rows("Next usually:", next),
213
+ ...rows("Example:", examples),
214
+ "",
215
+ ].join("\n");
199
216
  };
200
217
  /** The same, as Markdown lines for `docs/commands.md`: hand it to cli-base's `commandsPage` per command. */
201
218
  export const commandHelpMarkdown = (root, path) => {
@@ -1,5 +1,5 @@
1
- import type { CommandInfo } from "@wirecat/cli-base/commands";
2
1
  import type { Command } from "commander";
2
+ import type { DiscoveredCommand as CommandInfo } from "../command-availability.js";
3
3
  /** The first clause that fits: a line per command is the point, the whole text is in `--help`. */
4
4
  export declare const short: (text: string) => string;
5
5
  /**
@@ -33,10 +33,11 @@ export const commandsText = (root, commands, scope) => {
33
33
  if (infos.length === 0)
34
34
  continue;
35
35
  lines.push("", SECTIONS[section]);
36
- for (const { usage, summary, description, effects } of infos) {
36
+ for (const { usage, summary, description, effects, availability } of infos) {
37
37
  const what = short(summary ?? description);
38
38
  const mark = effects === undefined || writes(effects) ? " (writes)" : "";
39
- lines.push(` ${usage.padEnd(COLUMN)} ${what}${mark}`.trimEnd());
39
+ const access = availability?.available === false ? ` [unavailable: ${availability.reason}]` : "";
40
+ lines.push(` ${usage.padEnd(COLUMN)} ${what}${mark}${access}`.trimEnd());
40
41
  }
41
42
  }
42
43
  return `${lines.join("\n")}\n`;
@@ -1,5 +1,6 @@
1
1
  export { AI_ENTRIES, AI_SETTING_KEYS, type AISettings, resolveAISettings } from "../analysis/settings.js";
2
2
  export { parseDuration } from "../domain/duration.js";
3
+ export { syncProgress } from "../entities/store-records.js";
3
4
  export { isCliFailure, isCommanderFailure } from "../errors/failures.js";
4
5
  export { type AppErrors, failureContext } from "../errors/reasons.js";
5
6
  export { botInstructions } from "../mcp/bot/instructions.js";
@@ -10,6 +11,7 @@ export { callMeetingProjectCandidatesTool, meetingProjectCandidatesTool } from "
10
11
  export { callMeetingSeriesInventoryTool, meetingSeriesInventoryTool } from "../mcp/meeting-series-inventory.js";
11
12
  export { registerLinkConversationsPrompt } from "../mcp/prompts.js";
12
13
  export { agentBody, agentJson, agentResult, UNTRUSTED, UNTRUSTED_INSTRUCTION } from "../mcp/text.js";
14
+ export { MEETING_RESULT_ENTITIES } from "../meetings/index.js";
13
15
  export { type AppIdentity, envName, readOnlyLockOf, readOnlyRefusal } from "../ports/app.js";
14
16
  export type { AccountEditing, AccountHealth, AccountRecords, AccountSettings, AccountStanding, AccountTools, After, BotChats, ChannelComments, ChatDeletion, ChatFolders, ChatReading, ContactBook, Download, ForumControl, ForumState, GroupAdmin, GroupModeration, HistoryBatch, HistoryChatEvent, HistoryReaction, HistoryStar, HistoryVote, InviteLinks, JoinRequests, LiveUpdates, MessageButtons, MessageEditing, MessageMedia, MessagePermalinks, MessagePins, MessagePolls, MessageReactions, MessageSearch, MessageStars, MessengerAdapter, MessengerCore, NewPoll, OfficialStats, PassiveUpdates, PersonProfiles, ProfileChange, ProfilePhotos, PushedHistory, ReadState, RemoteFile, ScheduledMessages, SenderIdentities, SenderSearch, SendOptions, Sent, ServerQuery, ServerReads, ThreadAddressing, TopicEditing, TopicHistory, TopicReadState, Transcript, } from "../ports/messenger.js";
15
17
  export type { Fetching, MediaOption, MessengerTraits } from "../ports/traits.js";
@@ -47,6 +49,7 @@ export { BOT_ACTIONS, type BotAction, type BotAdapter, type BotCallbacks, type B
47
49
  export { botFiles, botsDirectory, ChatRegistry, registryProfiles, type SeenChat } from "./bot/registry.js";
48
50
  export { BotTokenStore, type BotTokenStoreOptions } from "./bot/token.js";
49
51
  export { PressLog, UpdatesCursor } from "./bot/updates.js";
52
+ export type { CommandAvailability, DiscoveredCommand } from "./command-availability.js";
50
53
  export { commandContract, type JsonSchema, SCHEMA_DIALECT } from "./command-contract.js";
51
54
  export { CONTRACT, commandsCommand } from "./commands-command.js";
52
55
  export { configCommand, refuseUnknownKey } from "./config-command.js";
@@ -54,8 +57,8 @@ export { loadSnakeCaseConfig } from "./config-keys.js";
54
57
  export { type AgentRun, type BaseContext, type BaseEnvironment, baseContext, environmentOf, outputFor, provide, secretInputOf, } from "./context.js";
55
58
  export { type Closeable, withDeadline } from "./deadline.js";
56
59
  export { DEFAULT_CONFIG, ensureDefaultConfig } from "./default-config.js";
57
- export { changesAuthority, changesAuthorityAt, destroysNothing, FETCH, grantsAuthority, isLogin, isOtherServiceCall, isStoreChange, judgedEffects, judgedTargets, LOCAL_READ, LOCAL_WRITE, logsInOrOut, NO_EFFECTS, ownerOnlyOption, REMOTE_READ, REMOTE_WRITE, reachesOtherService, refuseUnderReadOnly, } from "./effects.js";
58
- export { type AnswerOutput, answerSchema, type Body, type ItemBody, itemAnswer, itemOf, jsonlRecords, judgedAnswer, type ListBody, type ListInput, listOf, metaNow, printItem, renderAnswer, sealed, snakeKeys, snakeKeysKeeping, } from "./envelope.js";
60
+ export { changesAuthority, changesAuthorityAt, changesAuthorityNow, destroysNothing, FETCH, grantsAuthority, isLogin, isOtherServiceCall, isStoreChange, judgedEffects, judgedTargets, LOCAL_READ, LOCAL_WRITE, logsInOrOut, NO_EFFECTS, ownerOnlyOption, REMOTE_READ, REMOTE_WRITE, reachesOtherService, refuseUnderReadOnly, } from "./effects.js";
61
+ export { type AnswerOutput, answerSchema, type Body, type ItemBody, isBody, itemAnswer, itemOf, jsonlRecords, judgedAnswer, type ListBody, type ListInput, listOf, metaNow, printItem, renderAnswer, sealed, snakeKeys, snakeKeysKeeping, } from "./envelope.js";
59
62
  export { withAbort } from "./execution.js";
60
63
  export { askSetupQuestions, asksAtStart, type FirstRun, type FirstRunIo, LANGUAGES, setupAtStart, systemLanguage, } from "./first-run.js";
61
64
  export { checkGuide, checkStep, type GuideOptions, type GuideReference, type GuideStep, guideCommand, loadScenarios, MEMO_SCENARIOS, type Scenario, shellProblems, } from "./guide.js";
@@ -74,6 +77,7 @@ export { addMeetingPreparationCommand } from "./meeting-preparation-command.js";
74
77
  export { type MeetingPreparationReader, meetingPreparationInput, readMeetingPreparation, } from "./meeting-preparation-read.js";
75
78
  export { addMeetingProjectCandidatesCommand } from "./meeting-project-candidates-command.js";
76
79
  export { readMeetingProjectCandidates } from "./meeting-project-candidates-read.js";
80
+ export { meetingResultItem } from "./meeting-results.js";
77
81
  export { addMeetingSeriesInventoryCommand } from "./meeting-series-inventory-command.js";
78
82
  export { type MeetingSeriesInventoryReader, readMeetingSeriesInventory } from "./meeting-series-inventory-read.js";
79
83
  export { accountCommand } from "./messenger/account-command.js";
package/dist/cli/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { AI_ENTRIES, AI_SETTING_KEYS, resolveAISettings } from "../analysis/settings.js";
2
2
  export { parseDuration } from "../domain/duration.js";
3
+ export { syncProgress } from "../entities/store-records.js";
3
4
  export { isCliFailure, isCommanderFailure } from "../errors/failures.js";
4
5
  export { failureContext } from "../errors/reasons.js";
5
6
  export { botInstructions } from "../mcp/bot/instructions.js";
@@ -8,6 +9,7 @@ export { callMeetingProjectCandidatesTool, meetingProjectCandidatesTool } from "
8
9
  export { callMeetingSeriesInventoryTool, meetingSeriesInventoryTool } from "../mcp/meeting-series-inventory.js";
9
10
  export { registerLinkConversationsPrompt } from "../mcp/prompts.js";
10
11
  export { agentBody, agentJson, agentResult, UNTRUSTED, UNTRUSTED_INSTRUCTION } from "../mcp/text.js";
12
+ export { MEETING_RESULT_ENTITIES } from "../meetings/index.js";
11
13
  export { envName, readOnlyLockOf, readOnlyRefusal } from "../ports/app.js";
12
14
  export { senderFacts } from "../replies/dry-run.js";
13
15
  export { replyRenderer } from "../replies/rendering.js";
@@ -49,8 +51,8 @@ export { loadSnakeCaseConfig } from "./config-keys.js";
49
51
  export { baseContext, environmentOf, outputFor, provide, secretInputOf, } from "./context.js";
50
52
  export { withDeadline } from "./deadline.js";
51
53
  export { DEFAULT_CONFIG, ensureDefaultConfig } from "./default-config.js";
52
- export { changesAuthority, changesAuthorityAt, destroysNothing, FETCH, grantsAuthority, isLogin, isOtherServiceCall, isStoreChange, judgedEffects, judgedTargets, LOCAL_READ, LOCAL_WRITE, logsInOrOut, NO_EFFECTS, ownerOnlyOption, REMOTE_READ, REMOTE_WRITE, reachesOtherService, refuseUnderReadOnly, } from "./effects.js";
53
- export { answerSchema, itemAnswer, itemOf, jsonlRecords, judgedAnswer, listOf, metaNow, printItem, renderAnswer, sealed, snakeKeys, snakeKeysKeeping, } from "./envelope.js";
54
+ export { changesAuthority, changesAuthorityAt, changesAuthorityNow, destroysNothing, FETCH, grantsAuthority, isLogin, isOtherServiceCall, isStoreChange, judgedEffects, judgedTargets, LOCAL_READ, LOCAL_WRITE, logsInOrOut, NO_EFFECTS, ownerOnlyOption, REMOTE_READ, REMOTE_WRITE, reachesOtherService, refuseUnderReadOnly, } from "./effects.js";
55
+ export { answerSchema, isBody, itemAnswer, itemOf, jsonlRecords, judgedAnswer, listOf, metaNow, printItem, renderAnswer, sealed, snakeKeys, snakeKeysKeeping, } from "./envelope.js";
54
56
  export { withAbort } from "./execution.js";
55
57
  export { askSetupQuestions, asksAtStart, LANGUAGES, setupAtStart, systemLanguage, } from "./first-run.js";
56
58
  export { checkGuide, checkStep, guideCommand, loadScenarios, MEMO_SCENARIOS, shellProblems, } from "./guide.js";
@@ -69,6 +71,7 @@ export { addMeetingPreparationCommand } from "./meeting-preparation-command.js";
69
71
  export { meetingPreparationInput, readMeetingPreparation, } from "./meeting-preparation-read.js";
70
72
  export { addMeetingProjectCandidatesCommand } from "./meeting-project-candidates-command.js";
71
73
  export { readMeetingProjectCandidates } from "./meeting-project-candidates-read.js";
74
+ export { meetingResultItem } from "./meeting-results.js";
72
75
  export { addMeetingSeriesInventoryCommand } from "./meeting-series-inventory-command.js";
73
76
  export { readMeetingSeriesInventory } from "./meeting-series-inventory-read.js";
74
77
  export { accountCommand } from "./messenger/account-command.js";
@@ -1,7 +1,7 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { metaOf } from "@wirecat/cli-base/commands";
3
3
  import { ENTITIES } from "../entities/index.js";
4
- import { answerEntitySchema } from "./answer-entity.js";
4
+ import { answerEntitySchema, answerProjectionSchema } from "./answer-entity.js";
5
5
  const declaredEntity = (command) => {
6
6
  const output = command ? metaOf(command).output : undefined;
7
7
  return output && (output.view ? `${output.entity}.${output.view}` : output.entity);
@@ -38,7 +38,7 @@ export const answerFields = (command, resolve) => {
38
38
  const own = resolve?.(command);
39
39
  if (own !== undefined)
40
40
  return [...own];
41
- const schema = answerEntitySchema(command);
41
+ const schema = answerProjectionSchema(command) ?? answerEntitySchema(command);
42
42
  return schemaFields(schema);
43
43
  };
44
44
  const PATHS = /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*(,[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*)*$/;
@@ -1,5 +1,5 @@
1
1
  import { type BatchReport } from "@wirecat/cli-base";
2
- import type { Meta } from "@wirecat/cli-base/standards";
2
+ import { type Meta } from "@wirecat/cli-base/standards";
3
3
  import { Command } from "commander";
4
4
  import { type MeetingBatchReader, type MeetingPassageOutputFormat } from "../meetings/index.js";
5
5
  import { type Body } from "./envelope.js";
@@ -1,11 +1,16 @@
1
1
  import { BATCH_LIMITS, CliError, prepareBatch, runBatch, } from "@wirecat/cli-base";
2
2
  import { annotate } from "@wirecat/cli-base/commands";
3
+ import { errorObjectSchema } from "@wirecat/cli-base/standards";
3
4
  import { Command, Option } from "commander";
5
+ import * as v from "valibot";
6
+ import { ENTITIES } from "../entities/index.js";
4
7
  import { failureContext } from "../errors/reasons.js";
5
8
  import { createMeetingPassageCommand, createMeetingSearchCommand, meetingHitSearchRequest, meetingPassageRequest, meetingSearchArguments, meetingSearchMeta, } from "../meetings/cli/index.js";
6
9
  import { meetingHitsList, parseMeetingReference, } from "../meetings/index.js";
7
10
  import { formatMeetingEvidenceText, formatMeetingPassage } from "../services/meeting-passage-format.js";
8
- import { itemOf, listOf } from "./envelope.js";
11
+ import { answerEntity } from "./answer-entity.js";
12
+ import { destroysNothing } from "./effects.js";
13
+ import { answerSchema, itemOf, listOf } from "./envelope.js";
9
14
  const INDEX_BYTES = 4194304;
10
15
  const parse = (command, args) => {
11
16
  command
@@ -162,10 +167,23 @@ export const addMeetingBatchCommand = (program, deps) => {
162
167
  },
163
168
  }), report, format);
164
169
  });
165
- return annotate(command, {
170
+ destroysNothing(command);
171
+ annotate(command, {
166
172
  origin: "handwritten",
167
173
  partial: true,
168
174
  effects: { remote: "none", local: "write" },
169
175
  examples: [`${program.name()} batch requests.json --json`],
170
176
  });
177
+ return answerEntity(command, "meeting_batch_result", batchResult, { project: batchPayload });
171
178
  };
179
+ const batchPayload = v.union([ENTITIES.meeting_hit, ENTITIES.meeting_passage]);
180
+ const correlated = { id: v.string(), command: v.array(v.string()) };
181
+ const batchResult = v.union([
182
+ v.strictObject({ ...correlated, status: v.literal("ok"), answer: answerSchema(batchPayload), complete: v.boolean() }),
183
+ v.strictObject({
184
+ ...correlated,
185
+ status: v.picklist(["error", "not_run"]),
186
+ error: errorObjectSchema.entries.error,
187
+ complete: v.literal(false),
188
+ }),
189
+ ]);
@@ -0,0 +1,3 @@
1
+ import { MEETING_RESULT_ENTITIES } from "../meetings/index.js";
2
+ /** Store IDs stay numeric below the CLI; public receipts use the shared string IDs and validated wire fields. */
3
+ export declare const meetingResultItem: (entity: keyof typeof MEETING_RESULT_ENTITIES, value: unknown) => unknown;
@@ -0,0 +1,31 @@
1
+ import { CliError } from "@wirecat/cli-base";
2
+ import * as v from "valibot";
3
+ import { MEETING_RESULT_ENTITIES } from "../meetings/index.js";
4
+ import { snakeKeys } from "./envelope.js";
5
+ const ids = new Set([
6
+ "meeting_id",
7
+ "account_id",
8
+ "participant_id",
9
+ "event_id",
10
+ "previous_event_id",
11
+ "expected_event_id",
12
+ "next_transcript_id",
13
+ ]);
14
+ const publicValue = (value, key = "") => {
15
+ if (typeof value === "number" && ids.has(key))
16
+ return String(value);
17
+ if (Array.isArray(value))
18
+ return value.map((item) => publicValue(item, key === "target_account_ids" ? "account_id" : ""));
19
+ if (value && typeof value === "object")
20
+ return Object.fromEntries(Object.entries(value).map(([name, item]) => [name, publicValue(item, name)]));
21
+ return value;
22
+ };
23
+ /** Store IDs stay numeric below the CLI; public receipts use the shared string IDs and validated wire fields. */
24
+ export const meetingResultItem = (entity, value) => {
25
+ const parsed = v.safeParse(MEETING_RESULT_ENTITIES[entity], publicValue(snakeKeys(value)));
26
+ if (!parsed.success) {
27
+ const applied = value && typeof value === "object" && "applied" in value && value.applied === true;
28
+ throw new CliError(applied ? "outcome_unknown" : "invalid_response", `the command returned an invalid ${entity} answer`);
29
+ }
30
+ return parsed.output;
31
+ };
@@ -9,7 +9,8 @@ export interface ProgramDefinition {
9
9
  commands: () => Command[];
10
10
  /** The CLI's own settings, for keeping a failure that happened before its command could; plain ones without. */
11
11
  configuration?: {
12
- resolveSettings: (flags?: GlobalFlags, options?: ResolveOptions) => Pick<Settings, "profile" | "keepFailedRuns" | "keepRunsForDays" | "skillHint">;
12
+ permissionKey?: (path: readonly string[]) => string | null | undefined;
13
+ resolveSettings: (flags?: GlobalFlags, options?: ResolveOptions) => Pick<Settings, "profile" | "keepFailedRuns" | "keepRunsForDays" | "skillHint"> & Partial<Pick<Settings, "permissions">>;
13
14
  };
14
15
  configure?: (program: Command) => void;
15
16
  /** The tool's pitch and "what you can do" lines on `--help` and a bare run. */
@@ -13,10 +13,11 @@ import { recorded, wasSettled } from "../runs/recording.js";
13
13
  import { processRun, useAsProcessRun } from "../runs/scope.js";
14
14
  import { AUTHORITY_KEY, BOT_API_KEY, isBotApi, keyForCommand } from "../sends/permissions.js";
15
15
  import { logParsedArgv, logVersion } from "./argv-log.js";
16
+ import { commandAvailability } from "./command-availability.js";
16
17
  import { commandPathOf } from "./command-contract.js";
17
18
  import { outputFor, provide } from "./context.js";
18
19
  import { refuseWriteBeforeConnecting } from "./early-refusal.js";
19
- import { changesAuthorityNow, isLogin, isStoreChange, judgedEffects, judgedTargets, STORE_CHANGE_REFUSAL, } from "./effects.js";
20
+ import { changesAuthorityAt, changesAuthorityNow, isLogin, isStoreChange, judgedEffects, judgedTargets, STORE_CHANGE_REFUSAL, } from "./effects.js";
20
21
  import { itemOf, renderAnswer, snakeKeys } from "./envelope.js";
21
22
  import { byteCount, DEFAULT_COMMAND_MS, DEFAULT_OUTPUT_BYTES, execution } from "./execution.js";
22
23
  import { setupAtStart } from "./first-run.js";
@@ -193,6 +194,28 @@ const runOnce = async (argv, definition, options) => {
193
194
  platform: options.platform ?? process.platform,
194
195
  exit,
195
196
  runCommand: (words, given) => run(words, definition, given),
197
+ discoveryAvailability: (info) => {
198
+ const inspected = info.path.reduce((parent, word) => parent?.commands.find((one) => one.name() === word), program);
199
+ const flags = (inspected ?? program).optsWithGlobals();
200
+ const env = options.env ?? process.env;
201
+ if (!flags.profile && !env[envName(definition.app, "PROFILE")])
202
+ return undefined;
203
+ try {
204
+ const overrides = options.agent?.overrides ?? [];
205
+ const settings = (definition.configuration ?? settingsFor(definition.app)).resolveSettings(overrides.length ? { ...flags, permission: [...overrides, ...(flags.permission ?? [])] } : flags, {
206
+ env,
207
+ needsProfile: false,
208
+ kind: info.path[0] === "bot" || flags.bot ? "bot" : "personal",
209
+ });
210
+ if (!settings.permissions)
211
+ return undefined;
212
+ return commandAvailability(info, settings.permissions, definition.configuration?.permissionKey, changesAuthorityAt(program), readOnlyLockOf(definition.app, env) !== undefined, options.agent !== undefined);
213
+ }
214
+ catch {
215
+ // Help must still work when the configuration needs repair.
216
+ return undefined;
217
+ }
218
+ },
196
219
  untilStopped: () => {
197
220
  if (phased || finished)
198
221
  return phase.signal;
@@ -12,4 +12,22 @@ const named = (work) => {
12
12
  }
13
13
  };
14
14
  export const fieldsOf = (value) => named(() => coreFieldsOf(value));
15
- export const projectFields = (value, fields) => named(() => coreProjectFields(value, fields));
15
+ const projectCommands = (commands, fields) => commands.map((command) => ({
16
+ ...coreProjectFields(command, fields),
17
+ path: command.path,
18
+ name: command.name,
19
+ origin: command.origin,
20
+ effects: command.effects,
21
+ commands: projectCommands(command.commands, fields),
22
+ }));
23
+ export const projectFields = (value, fields) => named(() => {
24
+ if (value &&
25
+ typeof value === "object" &&
26
+ "cli" in value &&
27
+ typeof value.cli === "string" &&
28
+ "contract" in value &&
29
+ "commands" in value &&
30
+ Array.isArray(value.commands))
31
+ return { ...value, commands: projectCommands(value.commands, fields) };
32
+ return coreProjectFields(value, fields);
33
+ });
@@ -11,6 +11,12 @@ export declare const contact: v.StrictObjectSchema<{
11
11
  readonly description: v.NullableSchema<v.StringSchema<undefined>, undefined>;
12
12
  /** The newest message in a one-to-one chat with them. */
13
13
  readonly last_messaged_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
14
+ /** For a source that knows it, such as a Zoom contact. */
15
+ readonly email: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
16
+ readonly phones: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
17
+ readonly number: v.StringSchema<undefined>;
18
+ readonly label: v.NullableSchema<v.StringSchema<undefined>, undefined>;
19
+ }, undefined>, undefined>, undefined>;
14
20
  /** `contacts show`: the chats shared with them. */
15
21
  readonly chats: v.OptionalSchema<v.ArraySchema<Omit<v.StrictObjectSchema<{
16
22
  readonly ref: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
@@ -13,6 +13,9 @@ export const contact = v.strictObject({
13
13
  description: v.nullable(text),
14
14
  /** The newest message in a one-to-one chat with them. */
15
15
  last_messaged_at: v.nullable(time),
16
+ /** For a source that knows it, such as a Zoom contact. */
17
+ email: v.optional(v.nullable(text)),
18
+ phones: v.optional(v.array(v.strictObject({ number: text, label: v.nullable(text) }))),
16
19
  /** `contacts show`: the chats shared with them. */
17
20
  chats: v.optional(v.array(v.pick(chat, ["ref", "id", "title", "kind", "last_message_at"]))),
18
21
  /** `contacts show --with-notes`: the owner's private notes about them. */