@wirecat/cli-core 0.454.0 → 0.456.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 +59 -0
  2. package/dist/calendar/cli/index.d.ts +24 -0
  3. package/dist/calendar/cli/index.js +173 -0
  4. package/dist/calendar/index.d.ts +3 -0
  5. package/dist/calendar/index.js +1 -0
  6. package/dist/calendar/mcp/index.d.ts +307 -0
  7. package/dist/calendar/mcp/index.js +132 -0
  8. package/dist/calendar/reads.d.ts +149 -0
  9. package/dist/calendar/reads.js +214 -0
  10. package/dist/calendar/source.d.ts +54 -0
  11. package/dist/calendar/source.js +1 -0
  12. package/dist/calendar/sync.d.ts +65 -0
  13. package/dist/calendar/sync.js +167 -0
  14. package/dist/calendar/testing/index.d.ts +5 -0
  15. package/dist/calendar/testing/index.js +5 -0
  16. package/dist/calendar/testing/memory.d.ts +6 -0
  17. package/dist/calendar/testing/memory.js +478 -0
  18. package/dist/calendar/testing/source-contract.d.ts +31 -0
  19. package/dist/calendar/testing/source-contract.js +171 -0
  20. package/dist/calendar/testing/source.d.ts +38 -0
  21. package/dist/calendar/testing/source.js +110 -0
  22. package/dist/calendar/testing/sync-contract.d.ts +15 -0
  23. package/dist/calendar/testing/sync-contract.js +211 -0
  24. package/dist/cli/index.d.ts +2 -1
  25. package/dist/cli/index.js +2 -1
  26. package/dist/cli/messenger/ask.js +1 -1
  27. package/dist/errors/reasons.js +1 -0
  28. package/dist/meetings/calendar-text.d.ts +1 -1
  29. package/dist/meetings/calendar-text.js +0 -1
  30. package/dist/meetings/event-store.d.ts +42 -15
  31. package/dist/meetings/index.d.ts +1 -1
  32. package/dist/meetings/testing/event-store-contract.js +83 -4
  33. package/dist/sends/guard.d.ts +2 -0
  34. package/dist/sends/guard.js +1 -0
  35. package/dist/services/index.d.ts +1 -1
  36. package/dist/services/index.js +1 -1
  37. package/dist/services/people.d.ts +6 -1
  38. package/dist/services/people.js +14 -11
  39. package/dist/store/sqlite/calendar.js +68 -31
  40. package/dist/store/sqlite/manifest.js +2 -0
  41. package/dist/store/sqlite/migrations.generated.js +17 -0
  42. package/dist/store/sqlite/note-index.d.ts +1 -1
  43. package/dist/store/sqlite/note-index.js +6 -1
  44. package/dist/store/sqlite/schema.d.ts +16 -0
  45. package/dist/store/sqlite/schema.js +4 -0
  46. package/package.json +17 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,65 @@ shape of this file.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.456.0 — 27.10.2026
11
+
12
+ ### Added
13
+
14
+ - **A calendar store for any provider.** `EventStore.removeEntries(calendarId, externalIds, now)` cancels entries a
15
+ provider names only by id (CalDAV, Outlook) under the same history rules as a cancelled entry: a past event stays,
16
+ a future one cancels, a series cancels its occurrences. `EventStore.cursor(calendarId)` and
17
+ `setCursor(calendarId, value, now)` keep where a calendar's sync stopped, in `sync_cursors` under
18
+ `calendar:<id>`; `null` forgets it. `ical_uid` is defined as the RFC 5545 UID of the series, and an all-day slot's
19
+ `originalStartsAt` as midnight UTC of its date.
20
+ - **An occurrence names its series in a column (store version 35).** `calendar_entries.series_external_id` holds an
21
+ occurrence's series id, so an occurrence stored before its series still finds it; `StoredEntry.seriesExternalId`
22
+ answers it. The migration fills it from `metadata.recurring_event_id`, which the store no longer writes and
23
+ `ENTRY_METADATA_KEYS` no longer keeps, and rewrites calendar words already stored in Google's spelling into the
24
+ neutral one (`outOfOffice` → `out-of-office`, `focusTime` → `focus`, `workingLocation` → `working-location`,
25
+ `fromGmail` → `from-mail`, `needsAction` → `needs-action`, `freeBusyReader` → `free-busy`). Builds that speak
26
+ version 34 still open the store.
27
+ - **`@wirecat/cli-core/calendar`: one sync for every calendar provider.** A calendar CLI writes a `CalendarSource`
28
+ (`calendars`, `changes` by page with a cursor or `{ reset: true }`, and `occurrences` when the provider expands a
29
+ series on request) and calls `syncCalendars(source, store, { accountId })`: the calendar list, then each synced
30
+ calendar's changes or its whole window (a year back, three months ahead, a full list once a day), the slots of the
31
+ series that changed, the marking after a full list, and the cursor saved last, so a failed calendar retries the
32
+ same changes. The traits `expands`, `cursorHoldsWindow` and `fullListOnly` cover Google, Outlook and ICS/CalDAV.
33
+ `@wirecat/cli-core/calendar/testing` adds `fakeCalendarSource`, `memoryEventStore`, `calendarSourceContract` for an
34
+ adapter and `calendarSyncContract` for a store, over all twelve trait combinations. The `EventStore` types are
35
+ exported from `./calendar` too ([Calendars](docs/dev/CALENDARS.md)).
36
+ - **Shared calendar commands and MCP reads.** `addCalendarCommands(program, deps)` (`./calendar/cli`) mounts
37
+ `calendars list|sync|unsync`, `sync [--full] [--embed]`, `events list [today|week|from:… to:…] [person:…]
38
+ [calendar:…]`, `events show`, `search` (the events search, over the profile's account), `people list [<event>]`,
39
+ `people link`, and `links review|resolve`, all read only towards the provider. `calendarTools` and
40
+ `callCalendarTool` (`./calendar/mcp`) give the reads as MCP tools; without `account_id` they read every account the
41
+ host serves and name them, and each answer marks the fields an invitation's sender wrote as `untrusted`.
42
+ - **`linkPeople(store, account, person, other)`** (`./services`) is the people service's link as a function, for a
43
+ tool with no messenger.
44
+
45
+ ### Changed — may break callers
46
+
47
+ - **Calendar words are neutral, RFC 5545 where it has a word, lower case with hyphens.** `eventType` takes
48
+ `default`, `out-of-office`, `focus`, `working-location`, `birthday`, `from-mail`; `ownResponse` and a participant's
49
+ `response` take `needs-action`, `accepted`, `tentative`, `declined`, `delegated`; `accessRole` takes `owner`,
50
+ `writer`, `reader`, `free-busy`; `transparency` and `visibility` keep their words, now typed. The new types
51
+ `EntryEventType`, `ParticipantResponse`, `CalendarAccessRole`, `EntryTransparency` and `EntryVisibility` say so,
52
+ and an adapter translates its provider's words (Google's `outOfOffice`, `needsAction`, `freeBusyReader` …).
53
+ `UNSEARCHED_EVENT_TYPES` holds the neutral words.
54
+ - **`CalendarInput.syncByDefault` is required** and decides whether a calendar seen for the first time syncs. The
55
+ store no longer reads Google's `owner` role for it; an adapter sets it (Google: the owner owns the calendar).
56
+ - **`EventStore` gains `removeEntries`, `cursor` and `setCursor`, and `StoredEntry` gains `seriesExternalId`.** A
57
+ fake event store adds them.
58
+
59
+ ## 0.455.0 — 27.10.2026
60
+
61
+ ### Added
62
+
63
+ - **A CLI that is not a messenger reuses the shared permission helpers.** `./cli` exports `terminalAsker(command)`,
64
+ the guard's question at a terminal, and `refuseUnknownPermissionKeys`, the unknown-key refusal for a command the
65
+ shared check does not key (`api call`, `mcp`). `guardFor` takes `permissionDefaults` on its settings, so a key
66
+ the CLI defaults to `ask` asks instead of being allowed; without them it keeps the messenger defaults.
67
+ `terminalAsker`'s `confirmation_required` now names the key in `details.permission`.
68
+
10
69
  ## 0.454.0 — 27.10.2026
11
70
 
12
71
  ### Added
@@ -0,0 +1,24 @@
1
+ import { type Command } from "commander";
2
+ import type { EventStore } from "../../meetings/index.js";
3
+ import type { AccountKey, MessageStore } from "../../store/store.js";
4
+ import type { CalendarExpander, CalendarSource } from "../source.js";
5
+ export interface CalendarCommandDeps {
6
+ store: EventStore;
7
+ /** The selected profile's calendar account: every command reads and syncs only it. */
8
+ accountId: number;
9
+ /** The same account as the shared store names it, for search and people. */
10
+ account: AccountKey;
11
+ /** The shared store: `search`, `people link` and the person of an attendee read it. */
12
+ messages?: MessageStore;
13
+ /** The provider; `sync` and `calendars list` read it. */
14
+ source?: CalendarSource;
15
+ /** For a source with `expands: "local"`. */
16
+ expand?: CalendarExpander;
17
+ /** After `sync --embed`: embeds new events for search by meaning (`embedEvents`), as drive's `sync --embed`. */
18
+ embed?: (max: number) => Promise<unknown>;
19
+ write: (value: unknown, format: "json" | "jsonl" | "text") => void;
20
+ now?: () => number;
21
+ /** The zone `today`, `week` and a bare date are read in; the computer's when absent. */
22
+ timezone?: string;
23
+ }
24
+ export declare const addCalendarCommands: (program: Command, deps: CalendarCommandDeps) => void;
@@ -0,0 +1,173 @@
1
+ import { CliError } from "@wirecat/cli-base";
2
+ import { annotate, sharedCommand } from "@wirecat/cli-base/commands";
3
+ import { timezoneOf } from "@wirecat/cli-base/query";
4
+ import { InvalidArgumentError } from "commander";
5
+ import { searchEvents } from "../../services/events-search.js";
6
+ import { linkPeople } from "../../services/people.js";
7
+ import { calendarIn, calendarItem, EVENTS_QUERY_HELP, eventAnswer, eventIdOf, eventOf, eventsAnswer, eventsFilter, peopleAnswer, resolveAnswer, reviewsAnswer, } from "../reads.js";
8
+ import { syncCalendars } from "../sync.js";
9
+ const LOCAL_READ = { remote: "none", local: "read" };
10
+ const LOCAL_WRITE = { remote: "none", local: "write" };
11
+ const FETCH = { remote: "read", local: "write" };
12
+ const GROUP = { remote: "none", local: "none" };
13
+ /** How many pieces one `sync --embed` embeds, as drive's. */
14
+ const EMBED_MAX = 600;
15
+ const integer = (text) => {
16
+ if (!/^[1-9]\d*$/.test(text) || !Number.isSafeInteger(Number(text)))
17
+ throw new InvalidArgumentError("expected a positive integer");
18
+ return Number(text);
19
+ };
20
+ const addCalendarCommandsDefinition = (program, deps) => {
21
+ const now = () => deps.now?.() ?? Date.now();
22
+ const zone = () => timezoneOf(deps.timezone);
23
+ const output = (command) => command
24
+ .option("--json", "one JSON value")
25
+ .option("--jsonl", "one JSON object per item")
26
+ .hook("preAction", (cmd) => {
27
+ const opts = cmd.optsWithGlobals();
28
+ if (opts.json && opts.jsonl)
29
+ throw new CliError("validation_error", "choose --json or --jsonl");
30
+ });
31
+ const print = (value, command) => {
32
+ const opts = command.optsWithGlobals();
33
+ deps.write(value, opts.json ? "json" : opts.jsonl ? "jsonl" : "text");
34
+ };
35
+ const messages = (what) => {
36
+ if (!deps.messages)
37
+ throw new CliError("configuration_error", `${what} needs the shared store`);
38
+ return deps.messages;
39
+ };
40
+ const personOfEmail = deps.messages
41
+ ? async (email) => {
42
+ const person = await messages("a person").people.personOf({ provider: "email", id: email });
43
+ return person ? `person:${person.uid}` : null;
44
+ }
45
+ : undefined;
46
+ const personId = async (reference) => {
47
+ const text = reference.replace(/^person:/u, "");
48
+ if (/^[1-9]\d*$/u.test(text))
49
+ return Number(text);
50
+ const person = await messages("person:").people.personOf({ provider: "email", id: text.trim().toLowerCase() });
51
+ return person ? Number(person.uid) : null;
52
+ };
53
+ const example = (command, ...lines) => annotate(command, { examples: lines.map((line) => `${program.name()} personal ${line}`) });
54
+ const source = () => {
55
+ if (!deps.source)
56
+ throw new CliError("configuration_error", "the calendar provider is unavailable");
57
+ return deps.source;
58
+ };
59
+ const calendars = annotate(program.command("calendars").description("the account's calendars"), { effects: GROUP });
60
+ output(example(annotate(calendars.command("list"), { effects: FETCH, offline: false }).description("list the account's calendars from the provider, and which of them sync"), "calendars list --json")).action(async (_opts, command) => {
61
+ const listed = await deps.store.saveCalendars(deps.accountId, await source().calendars(), now());
62
+ print({ items: listed.map(calendarItem), page: 1, limit: listed.length, hasMore: false }, command);
63
+ });
64
+ for (const [name, synced] of [
65
+ ["sync", true],
66
+ ["unsync", false],
67
+ ])
68
+ output(example(annotate(calendars.command(name), { effects: LOCAL_WRITE })
69
+ .description(synced ? "sync this calendar from now on" : "stop syncing this calendar; what is stored stays")
70
+ .argument("<calendar>", "the calendar's id, or the provider's id"), `calendars ${name} 3`)).action(async (reference, _opts, command) => {
71
+ const calendar = await calendarIn(deps.store, deps.accountId, reference);
72
+ await deps.store.setCalendarSynced(calendar.id, synced, now());
73
+ print(calendarItem({ ...calendar, synced }), command);
74
+ });
75
+ output(example(annotate(program.command("sync"), { effects: FETCH, offline: false }).description("bring the synced calendars up to date: what changed since the last sync, or everything with --full"), "sync", "sync --full --embed"))
76
+ .option("--full", "list every synced calendar's window again instead of reading changes")
77
+ .option("--embed", "also index new events for meaning search, with a model already downloaded")
78
+ .action(async (opts, command) => {
79
+ const report = await syncCalendars(source(), deps.store, {
80
+ accountId: deps.accountId,
81
+ now: now(),
82
+ ...(opts.full ? { full: true } : {}),
83
+ ...(deps.expand ? { expand: deps.expand } : {}),
84
+ });
85
+ if (opts.embed && !deps.embed)
86
+ throw new CliError("configuration_error", "this tool cannot embed events");
87
+ const embeddings = opts.embed ? await deps.embed?.(EMBED_MAX) : undefined;
88
+ print(embeddings === undefined ? report : { ...report, embeddings }, command);
89
+ if (!report.complete)
90
+ throw new CliError("provider_error", "some calendars did not sync; run sync again to retry them", {
91
+ reason: "sync.partial",
92
+ });
93
+ });
94
+ const events = annotate(program.command("events").description("the account's stored events"), { effects: GROUP });
95
+ output(example(annotate(events.command("list"), { effects: LOCAL_READ, output: { entity: "event" } })
96
+ .description("list stored events, earliest first")
97
+ .argument("[words...]", "today, week, from:<date>, to:<date>, person:<who>, calendar:<id>")
98
+ .addHelpText("after", EVENTS_QUERY_HELP), "events list week --json", "events list from:2026-11-02 to:2026-11-06 person:ana@example.com"))
99
+ .option("--limit <n>", "events at most, 1–1000", integer, 100)
100
+ .action(async (words, opts, command) => {
101
+ const filter = await eventsFilter(words, {
102
+ store: deps.store,
103
+ accountId: deps.accountId,
104
+ zone: zone(),
105
+ now: now(),
106
+ personOf: personId,
107
+ });
108
+ print(await eventsAnswer(deps.store, filter && { ...filter, limit: opts.limit }), command);
109
+ });
110
+ output(example(annotate(events.command("show"), { effects: LOCAL_READ })
111
+ .description("one event: its calendar copies, attendees with their person, Drive files and history")
112
+ .argument("<event>", "the event's id, or event:<id>"), "events show event:42 --json")).action(async (reference, _opts, command) => {
113
+ print(await eventAnswer(await eventOf(deps.store, deps.accountId, eventIdOf(reference)), personOfEmail), command);
114
+ });
115
+ output(example(annotate(program.command("search"), { effects: LOCAL_READ })
116
+ .description("find stored events by words, and by meaning when its model is downloaded")
117
+ .argument("<words...>", "what to find in titles, descriptions, places and attendees"), "search roadmap planning --json"))
118
+ .option("--limit <n>", "events per page", integer, 20)
119
+ .option("--cursor <cursor>", "continue from a page's next_offset", (text) => (text === "0" ? 0 : integer(text)), 0)
120
+ .option("--exact", "every word as written, no word stems")
121
+ .option("--newest", "latest start first instead of best first")
122
+ .action(async (words, opts, command) => {
123
+ print(await searchEvents(messages("search"), words.join(" "), {
124
+ accounts: [deps.account],
125
+ limit: opts.limit,
126
+ offset: opts.cursor,
127
+ timezone: zone(),
128
+ command: program.name(),
129
+ ...(opts.exact ? { exact: true } : {}),
130
+ ...(opts.newest ? { newest: true } : {}),
131
+ }), command);
132
+ });
133
+ const people = annotate(program.command("people").description("the people in the account's events"), {
134
+ effects: GROUP,
135
+ });
136
+ output(example(annotate(people.command("list"), { effects: LOCAL_READ })
137
+ .description("one event's attendees, or everyone attending this week's events")
138
+ .argument("[event]", "the event's id, or event:<id>"), "people list --json", "people list event:42")).action(async (reference, _opts, command) => {
139
+ const event = reference === undefined ? undefined : eventIdOf(reference);
140
+ const filter = event === undefined
141
+ ? await eventsFilter(["week"], { store: deps.store, accountId: deps.accountId, zone: zone(), now: now() })
142
+ : undefined;
143
+ print(await peopleAnswer(deps.store, deps.accountId, {
144
+ ...(event === undefined ? {} : { event }),
145
+ ...(filter ? { filter } : {}),
146
+ ...(personOfEmail ? { personOf: personOfEmail } : {}),
147
+ }), command);
148
+ });
149
+ output(example(annotate(people.command("link"), { effects: LOCAL_WRITE })
150
+ .description("join an attendee to a person the owner knows elsewhere")
151
+ .argument("<person>", "an attendee's address as email:<address>, or a person:<id>")
152
+ .argument("<other>", "the same person in another tool, as <provider>:<person>, or me"), "people link email:ana@example.com telegram:12345")).action(async (person, other, _opts, command) => {
153
+ const linked = await linkPeople(messages("people link"), deps.account, person, other);
154
+ print({
155
+ person: `person:${linked.uid}`,
156
+ name: linked.name,
157
+ identities: linked.identities.map(({ provider, id }) => `${provider}:${id}`),
158
+ }, command);
159
+ });
160
+ const links = annotate(program.command("links").description("calendar copies the rules could not join alone"), {
161
+ effects: GROUP,
162
+ });
163
+ output(example(annotate(links.command("review"), { effects: LOCAL_READ }).description("copies that fit two or more events equally well, waiting for the owner's choice"), "links review --json")).action(async (_opts, command) => {
164
+ print(await reviewsAnswer(deps.store), command);
165
+ });
166
+ output(example(annotate(links.command("resolve"), { effects: LOCAL_WRITE })
167
+ .description("join a copy to the event you choose, or keep it apart")
168
+ .argument("<review>", "the review's id from links review")
169
+ .argument("<choice>", "one of its candidates, as event:<id>, or apart"), "links resolve 7 event:42")).action(async (review, choice, _opts, command) => {
170
+ print(await resolveAnswer(deps.store, review, choice, now()), command);
171
+ });
172
+ };
173
+ export const addCalendarCommands = sharedCommand(addCalendarCommandsDefinition);
@@ -0,0 +1,3 @@
1
+ export type { CalendarAccessRole, CalendarInput, EntryAttachment, EntryAttachmentInput, EntryEventType, EntryInput, EntryKind, EntryParticipantInput, EntryRevision, EntryStatus, EntryTransparency, EntryVisibility, EventDetails, EventFilter, EventStore, LinkReview, ParticipantResponse, ReviewDecision, StoredCalendar, StoredEntry, StoredParticipant, } from "../meetings/index.js";
2
+ export type { CalendarExpander, CalendarSource, CalendarSourceTraits, ChangePage, ChangeResult, SyncWindow, } from "./source.js";
3
+ export { type CalendarCursor, type CalendarSyncOptions, type CalendarSyncReport, type CalendarSyncResult, calendarSyncWindow, parseCalendarCursor, syncCalendars, } from "./sync.js";
@@ -0,0 +1 @@
1
+ export { calendarSyncWindow, parseCalendarCursor, syncCalendars, } from "./sync.js";
@@ -0,0 +1,307 @@
1
+ import { type FailureContext } from "@wirecat/cli-base";
2
+ import { type EventStore } from "../../meetings/index.js";
3
+ /** Fields whose text whoever sent the invitation wrote: an agent reads them as data. */
4
+ export declare const CALENDAR_UNTRUSTED_FIELDS: string[];
5
+ export declare const calendarTools: ({
6
+ name: string;
7
+ description: string;
8
+ inputSchema: {
9
+ type: string;
10
+ properties: {
11
+ account_id: {
12
+ type: string;
13
+ minimum: number;
14
+ };
15
+ words?: undefined;
16
+ timezone?: undefined;
17
+ limit?: undefined;
18
+ event?: undefined;
19
+ } | {
20
+ account_id: {
21
+ type: string;
22
+ minimum: number;
23
+ };
24
+ words: {
25
+ type: string;
26
+ minLength: number;
27
+ };
28
+ timezone: {
29
+ type: string;
30
+ minLength: number;
31
+ };
32
+ limit: {
33
+ type: string;
34
+ minimum: number;
35
+ maximum: number;
36
+ };
37
+ event?: undefined;
38
+ } | {
39
+ words?: undefined;
40
+ timezone?: undefined;
41
+ limit?: undefined;
42
+ event: {
43
+ type: string;
44
+ minLength: number;
45
+ };
46
+ account_id: {
47
+ type: string;
48
+ minimum: number;
49
+ };
50
+ } | {
51
+ words?: undefined;
52
+ timezone?: undefined;
53
+ limit?: undefined;
54
+ event?: undefined;
55
+ account_id?: undefined;
56
+ };
57
+ required: string[] | never[];
58
+ additionalProperties: boolean;
59
+ };
60
+ annotations: {
61
+ readOnlyHint: boolean;
62
+ destructiveHint: boolean;
63
+ idempotentHint: boolean;
64
+ openWorldHint: boolean;
65
+ };
66
+ } | {
67
+ name: string;
68
+ description: string;
69
+ inputSchema: {
70
+ type: string;
71
+ properties: {
72
+ account_id: {
73
+ type: string;
74
+ minimum: number;
75
+ };
76
+ words?: undefined;
77
+ timezone?: undefined;
78
+ limit?: undefined;
79
+ event?: undefined;
80
+ } | {
81
+ account_id: {
82
+ type: string;
83
+ minimum: number;
84
+ };
85
+ words: {
86
+ type: string;
87
+ minLength: number;
88
+ };
89
+ timezone: {
90
+ type: string;
91
+ minLength: number;
92
+ };
93
+ limit: {
94
+ type: string;
95
+ minimum: number;
96
+ maximum: number;
97
+ };
98
+ event?: undefined;
99
+ } | {
100
+ words?: undefined;
101
+ timezone?: undefined;
102
+ limit?: undefined;
103
+ event: {
104
+ type: string;
105
+ minLength: number;
106
+ };
107
+ account_id: {
108
+ type: string;
109
+ minimum: number;
110
+ };
111
+ } | {
112
+ words?: undefined;
113
+ timezone?: undefined;
114
+ limit?: undefined;
115
+ event?: undefined;
116
+ account_id?: undefined;
117
+ };
118
+ required: string[] | never[];
119
+ additionalProperties: boolean;
120
+ };
121
+ annotations: {
122
+ readOnlyHint: boolean;
123
+ destructiveHint: boolean;
124
+ idempotentHint: boolean;
125
+ openWorldHint: boolean;
126
+ };
127
+ } | {
128
+ name: string;
129
+ description: string;
130
+ inputSchema: {
131
+ type: string;
132
+ properties: {
133
+ account_id: {
134
+ type: string;
135
+ minimum: number;
136
+ };
137
+ words?: undefined;
138
+ timezone?: undefined;
139
+ limit?: undefined;
140
+ event?: undefined;
141
+ } | {
142
+ account_id: {
143
+ type: string;
144
+ minimum: number;
145
+ };
146
+ words: {
147
+ type: string;
148
+ minLength: number;
149
+ };
150
+ timezone: {
151
+ type: string;
152
+ minLength: number;
153
+ };
154
+ limit: {
155
+ type: string;
156
+ minimum: number;
157
+ maximum: number;
158
+ };
159
+ event?: undefined;
160
+ } | {
161
+ words?: undefined;
162
+ timezone?: undefined;
163
+ limit?: undefined;
164
+ event: {
165
+ type: string;
166
+ minLength: number;
167
+ };
168
+ account_id: {
169
+ type: string;
170
+ minimum: number;
171
+ };
172
+ } | {
173
+ words?: undefined;
174
+ timezone?: undefined;
175
+ limit?: undefined;
176
+ event?: undefined;
177
+ account_id?: undefined;
178
+ };
179
+ required: string[] | never[];
180
+ additionalProperties: boolean;
181
+ };
182
+ annotations: {
183
+ readOnlyHint: boolean;
184
+ destructiveHint: boolean;
185
+ idempotentHint: boolean;
186
+ openWorldHint: boolean;
187
+ };
188
+ } | {
189
+ name: string;
190
+ description: string;
191
+ inputSchema: {
192
+ type: string;
193
+ properties: {
194
+ account_id: {
195
+ type: string;
196
+ minimum: number;
197
+ };
198
+ words?: undefined;
199
+ timezone?: undefined;
200
+ limit?: undefined;
201
+ event?: undefined;
202
+ } | {
203
+ account_id: {
204
+ type: string;
205
+ minimum: number;
206
+ };
207
+ words: {
208
+ type: string;
209
+ minLength: number;
210
+ };
211
+ timezone: {
212
+ type: string;
213
+ minLength: number;
214
+ };
215
+ limit: {
216
+ type: string;
217
+ minimum: number;
218
+ maximum: number;
219
+ };
220
+ event?: undefined;
221
+ } | {
222
+ words?: undefined;
223
+ timezone?: undefined;
224
+ limit?: undefined;
225
+ event: {
226
+ type: string;
227
+ minLength: number;
228
+ };
229
+ account_id: {
230
+ type: string;
231
+ minimum: number;
232
+ };
233
+ } | {
234
+ words?: undefined;
235
+ timezone?: undefined;
236
+ limit?: undefined;
237
+ event?: undefined;
238
+ account_id?: undefined;
239
+ };
240
+ required: string[] | never[];
241
+ additionalProperties: boolean;
242
+ };
243
+ annotations: {
244
+ readOnlyHint: boolean;
245
+ destructiveHint: boolean;
246
+ idempotentHint: boolean;
247
+ openWorldHint: boolean;
248
+ };
249
+ })[];
250
+ /**
251
+ * Runs one calendar tool over the accounts the host serves. Without `account_id` a tool reads every one of them and
252
+ * names them in `accounts`; with one it must be among them.
253
+ */
254
+ export declare const callCalendarTool: (store: EventStore, name: string, input: unknown, { accountIds, failure, now, }: {
255
+ accountIds: readonly number[];
256
+ failure?: FailureContext;
257
+ now?: number;
258
+ }) => Promise<{
259
+ content: {
260
+ type: "text";
261
+ text: string;
262
+ }[];
263
+ structuredContent: {
264
+ error: {
265
+ code: "authentication_error" | "cancelled" | "configuration_error" | "confirmation_required" | "internal_error" | "invalid_response" | "network_error" | "not_found" | "outcome_unknown" | "permission_error" | "provider_error" | "provider_unavailable" | "rate_limited" | "timeout" | "validation_error";
266
+ reason: string;
267
+ message: string;
268
+ param?: string | undefined;
269
+ details?: {
270
+ [x: string]: unknown;
271
+ } | undefined;
272
+ next?: ({
273
+ kind: "command";
274
+ step: string;
275
+ command: string;
276
+ args: {
277
+ [x: string]: unknown;
278
+ };
279
+ line?: string | undefined;
280
+ why: string;
281
+ safe_to_auto_execute: boolean;
282
+ } | {
283
+ kind: "user_action";
284
+ step: string;
285
+ why: string;
286
+ safe_to_auto_execute: false;
287
+ })[] | undefined;
288
+ retry: {
289
+ safe: boolean;
290
+ after_ms?: number | undefined;
291
+ };
292
+ run_id?: string | undefined;
293
+ };
294
+ };
295
+ isError: true;
296
+ } | ({
297
+ content: {
298
+ type: "text";
299
+ text: string;
300
+ }[];
301
+ structuredContent: {
302
+ accounts: number[];
303
+ untrusted: string[];
304
+ };
305
+ } & {
306
+ isError?: boolean;
307
+ })>;