@mulmoclaude/core 0.23.1 → 0.25.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 (82) hide show
  1. package/assets/helps/collection-skills.md +27 -3
  2. package/assets/helps/google-calendar-collection.md +79 -0
  3. package/assets/helps/index.md +1 -0
  4. package/dist/collection/core/completion.d.ts +18 -0
  5. package/dist/collection/core/deriveAll.d.ts +14 -9
  6. package/dist/collection/core/schema.d.ts +9 -4
  7. package/dist/collection/core/schemaZ.d.ts +126 -43
  8. package/dist/collection/index.cjs +16 -14
  9. package/dist/collection/index.cjs.map +1 -1
  10. package/dist/collection/index.d.ts +1 -0
  11. package/dist/collection/index.js +3 -2
  12. package/dist/collection/index.js.map +1 -1
  13. package/dist/collection/paths.cjs +5 -55
  14. package/dist/collection/paths.js +1 -51
  15. package/dist/collection/registry/server/index.cjs +20 -19
  16. package/dist/collection/registry/server/index.cjs.map +1 -1
  17. package/dist/collection/registry/server/index.js +3 -2
  18. package/dist/collection/registry/server/index.js.map +1 -1
  19. package/dist/collection/server/index.cjs +41 -40
  20. package/dist/collection/server/index.js +4 -3
  21. package/dist/collection-watchers/index.cjs +15 -23
  22. package/dist/collection-watchers/index.cjs.map +1 -1
  23. package/dist/collection-watchers/index.js +3 -11
  24. package/dist/collection-watchers/index.js.map +1 -1
  25. package/dist/collection-watchers/reconciler.d.ts +2 -4
  26. package/dist/discovery-3K_Wtkvn.js +1636 -0
  27. package/dist/discovery-3K_Wtkvn.js.map +1 -0
  28. package/dist/discovery-BZ2ms1tE.cjs +1865 -0
  29. package/dist/discovery-BZ2ms1tE.cjs.map +1 -0
  30. package/dist/feeds/index.cjs +4 -4
  31. package/dist/feeds/index.js +2 -2
  32. package/dist/feeds/server/index.cjs +16 -15
  33. package/dist/feeds/server/index.cjs.map +1 -1
  34. package/dist/feeds/server/index.js +4 -3
  35. package/dist/feeds/server/index.js.map +1 -1
  36. package/dist/google/apiClient.d.ts +8 -1
  37. package/dist/google/calendar.d.ts +35 -0
  38. package/dist/google/calendarSyncStore.d.ts +7 -0
  39. package/dist/google/collectionSync.d.ts +70 -0
  40. package/dist/google/index.cjs +296 -4
  41. package/dist/google/index.cjs.map +1 -1
  42. package/dist/google/index.d.ts +4 -2
  43. package/dist/google/index.js +281 -5
  44. package/dist/google/index.js.map +1 -1
  45. package/dist/ids-BR4U1dJD.cjs +150 -0
  46. package/dist/ids-BR4U1dJD.cjs.map +1 -0
  47. package/dist/ids-D4e10971.js +85 -0
  48. package/dist/ids-D4e10971.js.map +1 -0
  49. package/dist/{ingestTypes-B-dXxUF9.js → ingestTypes-BsGOjR2l.js} +2 -2
  50. package/dist/{ingestTypes-B-dXxUF9.js.map → ingestTypes-BsGOjR2l.js.map} +1 -1
  51. package/dist/{ingestTypes-Cev9q77C.cjs → ingestTypes-CygUXsjf.cjs} +3 -3
  52. package/dist/{ingestTypes-Cev9q77C.cjs.map → ingestTypes-CygUXsjf.cjs.map} +1 -1
  53. package/dist/{promptSafety-rDWA9_JS.js → promptSafety-Boz5qOs-.js} +48 -102
  54. package/dist/promptSafety-Boz5qOs-.js.map +1 -0
  55. package/dist/{promptSafety-DRd15gQ6.cjs → promptSafety-Co4cYQ_Z.cjs} +49 -163
  56. package/dist/promptSafety-Co4cYQ_Z.cjs.map +1 -0
  57. package/dist/remote-host/server/index.cjs +6 -4
  58. package/dist/remote-host/server/index.cjs.map +1 -1
  59. package/dist/remote-host/server/index.js +6 -4
  60. package/dist/remote-host/server/index.js.map +1 -1
  61. package/dist/scheduler/index.cjs +3 -1
  62. package/dist/scheduler/index.cjs.map +1 -1
  63. package/dist/scheduler/index.js +3 -1
  64. package/dist/scheduler/index.js.map +1 -1
  65. package/dist/{server-Jaogu3Ky.cjs → server-DLRz-U7F.cjs} +80 -1806
  66. package/dist/server-DLRz-U7F.cjs.map +1 -0
  67. package/dist/{server-D2ibbixF.js → server-DZH7OjOD.js} +9 -1531
  68. package/dist/server-DZH7OjOD.js.map +1 -0
  69. package/dist/skill-bridge/index.cjs +2 -2
  70. package/dist/skill-bridge/index.cjs.map +1 -1
  71. package/dist/skill-bridge/index.js +1 -1
  72. package/dist/templatePath-27vUZowm.cjs +75 -0
  73. package/dist/templatePath-27vUZowm.cjs.map +1 -0
  74. package/dist/templatePath-k_WNbL_Q.js +52 -0
  75. package/dist/templatePath-k_WNbL_Q.js.map +1 -0
  76. package/package.json +1 -1
  77. package/dist/collection/paths.cjs.map +0 -1
  78. package/dist/collection/paths.js.map +0 -1
  79. package/dist/promptSafety-DRd15gQ6.cjs.map +0 -1
  80. package/dist/promptSafety-rDWA9_JS.js.map +0 -1
  81. package/dist/server-D2ibbixF.js.map +0 -1
  82. package/dist/server-Jaogu3Ky.cjs.map +0 -1
@@ -132,8 +132,8 @@ skipped, never crashes the host):
132
132
  | `singleton` | Optional. When set, at most one record exists, pinned to this exact id (e.g. `me`). Host pre-fills + locks the create form and hides Add once it exists. |
133
133
  | `fields` | Ordered map of field-name → field spec. **Insertion order = column order** in the table. Required. |
134
134
  | `actions` | Optional array of per-record buttons (see below). |
135
- | `completionField` | Optional. Name of the field whose value marks an item as "done" — when set, item-create fires a bell notification that clears once the field reaches one of `completionDoneValues`. Must name a real field in `fields`. Paired with `completionDoneValues` (both set, or both omitted). |
136
- | `completionDoneValues` | Optional. Non-empty array of values that count as "done" for `completionField` (e.g. `["Done"]`, `["paid", "void"]`). Compared as strings. |
135
+ | `completionField` | Optional. Name of the field whose value marks an item as "done" — when set, item-create fires a bell notification that clears once the item reads as done. Must name a real field in `fields`. Two forms: paired with `completionDoneValues` (done ⇔ the field's value is in that array), or naming a **`flag` field** (done ⇔ the flag's `where` matches — `completionDoneValues` must then be omitted). |
136
+ | `completionDoneValues` | Optional. Non-empty array of values that count as "done" for `completionField` (e.g. `["Done"]`, `["paid", "void"]`). Compared as strings. Omit when `completionField` names a `flag` field. |
137
137
  | `notifyWhen` | Optional. A `when` predicate (`{ "field": "...", "in": [...] }`) that **gates** the completion bell: fire it only for records matching the predicate (e.g. `{ "field": "priority", "in": ["high", "urgent"] }`). Requires `completionField`; `field` must name a real field. Absent ⇒ notify for every open record. |
138
138
  | `displayField` | Optional. Name of a field whose value is shown as the human-readable label in the completion notification's title (e.g. `Contacts: Jane Doe` instead of the opaque primaryKey). Must name a real field in `fields`. Falls back to the primaryKey value when unset or when the record's value is empty. |
139
139
  | `triggerField` | Optional. Name of a `date` field that **delays** the completion bell until that date arrives (instead of firing on create). Requires `completionField` / `completionDoneValues` (the bell still clears via the done value). Must name a real `date` field. See "Time-gated bells" below. |
@@ -149,7 +149,7 @@ skipped, never crashes the host):
149
149
 
150
150
  `string` · `text` (multi-line) · `email` · `number` · `date` (`YYYY-MM-DD`) ·
151
151
  `datetime` (`YYYY-MM-DDTHH:MM`) · `boolean` · `markdown` · `money` · `enum` ·
152
- `ref` · `embed` · `backlinks` · `rollup` · `table` · `derived` · `image` · `file` · `toggle`
152
+ `ref` · `embed` · `backlinks` · `rollup` · `table` · `derived` · `image` · `file` · `toggle` · `flag`
153
153
 
154
154
  Every field spec needs a `type` and a `label`. Extra keys by type:
155
155
 
@@ -249,6 +249,20 @@ Every field spec needs a `type` and a `label`. Extra keys by type:
249
249
  "done" checkbox. Without it, `status` only shows as a dropdown and a kanban
250
250
  column, with no checkbox to tick. `offValue` is the status to return to on
251
251
  uncheck (the default open column, e.g. `"Todo"`).
252
+ - **`flag`** — `where: [<conditions>]`. A **computed boolean**: true when the
253
+ record matches every condition (AND). **Never stored** (like `derived`); the
254
+ host recomputes it on every read, so you NEVER write flag values into the
255
+ JSON. Each condition is `{ "field": "<field>", "op": "<op>", "value": "..." }`
256
+ with ops `eq` / `ne` / `in` (array value) / `contains` / `gt` / `gte` / `lt` /
257
+ `lte`, or `{ ..., "valueFrom": { "field": "<sibling>" } }` to compare two
258
+ fields of the same record (`spent > budget`). Condition fields must name
259
+ declared fields; cross-record `valueFrom.record` is not allowed. A flag may
260
+ read `derived` / `rollup` values, and other flags via their stringified
261
+ boolean (`{ "field": "isDone", "op": "eq", "value": "true" }`). Use it for
262
+ generic state summaries — done-ness, pass/fail, qualification:
263
+ `{ "type": "flag", "label": "Passed", "where": [{ "field": "score", "op": "gte", "value": "60" }] }`.
264
+ A flag can also drive completion tracking (stored-field conditions only) —
265
+ see `completionField` above and the "Completion tracking" section below.
252
266
 
253
267
  ### Conditional field visibility (`when`)
254
268
 
@@ -474,6 +488,16 @@ with any field type whose stringified value is comparable (`enum`, `string`,
474
488
  ["paid", "void"]` on an invoice, or `completionField: "shipped"` +
475
489
  `completionDoneValues: ["true"]` on an order.
476
490
 
491
+ **Flag-form alternative**: `completionField` may instead name a **`flag`
492
+ field** — then done ⇔ the flag's `where` matches, and `completionDoneValues`
493
+ must be **omitted** (declaring it fails validation). Use it when done-ness is
494
+ richer than one field's membership (e.g. `"isDone": { "type": "flag", "where":
495
+ [{ "field": "score", "op": "gte", "value": "60" }] }` + `"completionField":
496
+ "isDone"`). Two extra rules apply only to a completion flag: its `where` may
497
+ reference **stored fields only** (no derived/rollup/toggle/flag/embed/backlinks
498
+ — completion is checked against the raw record), and combining it with `spawn`
499
+ requires an explicit `spawn.when`.
500
+
477
501
  Set `displayField` to make the bell title readable: with `displayField:
478
502
  "title"` the notification reads `Todos: Buy milk` instead of `Todos: t-0042`.
479
503
  It must name a real field; an empty value on a given record falls back to the
@@ -0,0 +1,79 @@
1
+ # Google Calendar sync — mirror a Google calendar into a collection
2
+
3
+ A collection can keep itself in sync with one of the user's Google calendars.
4
+ Add a `googleCalendar` block to its `schema.json` and the host pulls changed
5
+ events on a schedule and writes them as records — **without calling you**. No
6
+ tool call, no tokens spent per sync, so hourly syncing is free.
7
+
8
+ This is the only calendar mechanism: there is no calendar tool and no bundled
9
+ calendar collection. You author the schema when the user asks for one.
10
+
11
+ ## Requirements
12
+
13
+ The user's Google account must be linked (`google` tool, `kind: "status"`). If
14
+ it isn't, sync silently does nothing until they link it in settings.
15
+
16
+ ## The block
17
+
18
+ ```jsonc
19
+ {
20
+ "fields": {
21
+ "gid": { "type": "string", "label": "ID", "primary": true },
22
+ "title": { "type": "string", "label": "Event" },
23
+ "on": { "type": "datetime", "label": "Start" },
24
+ "until": { "type": "datetime", "label": "End" }
25
+ },
26
+ "primaryKey": "gid",
27
+ "displayField": "title",
28
+ "calendarField": "on",
29
+ "calendarEndField": "until",
30
+ "dataPath": "data/collections/my-schedule/items",
31
+
32
+ "googleCalendar": {
33
+ "calendarId": "primary",
34
+ "map": { "title": "summary", "on": "start", "until": "end" }
35
+ }
36
+ }
37
+ ```
38
+
39
+ - `calendarId` — the calendar to read. `"primary"` (as in the example) and
40
+ omitting the key entirely both mean the user's primary calendar. For any
41
+ other calendar, get its id from the `google` tool
42
+ (`kind: "calendarListCalendars"`).
43
+ - `map` — **your field name → the Google event field**. Pick whatever field
44
+ names suit the collection; the map absorbs the difference. Map at least one
45
+ field: an empty map syncs records that carry only the event id, so the user
46
+ would see rows with no content.
47
+
48
+ Mappable event fields: `summary`, `start`, `end`, `htmlLink`, `colorId`,
49
+ `status`.
50
+
51
+ ## The primary field is the event id
52
+
53
+ Do **not** map the primary field. It always receives the Google event id, which
54
+ is what lets a re-sync update an existing record instead of duplicating it.
55
+ Declaring it in `map` is a schema error.
56
+
57
+ Use `datetime` (not `date`) for start/end when events have real clock times —
58
+ the calendar day view then draws each record as a proportional time block.
59
+
60
+ ## What sync does
61
+
62
+ - New or edited events are written, keyed by event id (existing records are
63
+ replaced in place).
64
+ - Events deleted in Google are **deleted** from the collection.
65
+ - Only what changed since the last run is fetched, so a big calendar stays
66
+ cheap after the first sync.
67
+ - The first run walks the whole calendar to establish a starting point. Note
68
+ this covers **all** dates — Google does not allow a date window together with
69
+ incremental sync — so a calendar with years of history produces a lot of
70
+ records on that first pass.
71
+
72
+ Records are ordinary collection records: the user can open, filter, and view
73
+ them like any other. Edits they make locally are overwritten the next time
74
+ Google reports a change to that event.
75
+
76
+ ## Not for this
77
+
78
+ A `dataSource` (CSV-backed) collection is read-only and cannot declare
79
+ `googleCalendar`. Use a normal `dataPath` collection.
@@ -71,6 +71,7 @@ See [Wiki](config/helps/wiki.md) for details on how it works.
71
71
  - [Telegram Bridge](config/helps/telegram.md) — how to talk to MulmoClaude from the Telegram app: creating a bot, starting the bridge, allowlisting chat IDs, commands, and troubleshooting
72
72
  - [Remote host](config/helps/remote-host.md) — drive MulmoClaude from a phone at mulmoserver.web.app: Google sign-in connect, host online vs. offline (queued chats, 7-day expiry), photo attachments, and the security model
73
73
  - [Feeds](config/helps/feeds.md) — register a self-refreshing data feed (RSS/Atom/JSON) by authoring `feeds/<slug>/schema.json`: schema shape, the `ingest` block, raw-item field mapping, and `maxItems` retention
74
+ - [Google Calendar sync](config/helps/google-calendar-collection.md) — mirror a Google calendar into a collection with a `googleCalendar` block: field mapping, why the primary field holds the event id, and how deletions propagate
74
75
  - [GitHub repositories in the workspace](config/helps/github.md) — clone-destination rules under `github/<name>/` and how to handle existing directories with matching or different remotes
75
76
  - [Collection skills](config/helps/collection-skills.md) — build a data app (model + UI + relations + computed fields + action buttons) by authoring a `schema.json` collection skill: the DSL, field types, derived formulas, actions, records
76
77
  - [Custom views](config/helps/custom-view.md) — give a collection a view the built-ins don't cover (year/quarter overview, Gantt): an HTML file under `views/`, registered in `schema.json`, rendered in a sandboxed iframe over the records
@@ -0,0 +1,18 @@
1
+ import { Where } from './where';
2
+ /** The slice of a parsed schema the done predicate reads — minimal
3
+ * structural shape (like `DerivableSchema`) so the client and server
4
+ * `CollectionSchema` types both satisfy it as-is. */
5
+ export interface CompletionSchemaView {
6
+ /** Optional so legacy-pair callers (and their test fixtures) that
7
+ * never consult field specs keep working; only the flag form needs
8
+ * to look the completion field up. */
9
+ fields?: Record<string, {
10
+ type: string;
11
+ where?: Where;
12
+ }>;
13
+ completionField?: string;
14
+ completionDoneValues?: readonly string[];
15
+ }
16
+ /** True iff the schema declares completion tracking AND `item` is done
17
+ * under whichever completion form the schema uses (see module doc). */
18
+ export declare function itemIsDone(schema: CompletionSchemaView, item: Record<string, unknown>): boolean;
@@ -1,4 +1,5 @@
1
1
  import { FormulaContext } from './derivedFormula';
2
+ import { Where } from './where';
2
3
  /** Minimal field shape the derive loop needs — accepts both the client
3
4
  * FieldSpec and the server CollectionFieldSpec. */
4
5
  export interface DerivableFieldSpec {
@@ -7,6 +8,8 @@ export interface DerivableFieldSpec {
7
8
  to?: string;
8
9
  /** When type === "derived": formula evaluated against the record. */
9
10
  formula?: string;
11
+ /** When type === "flag": predicate matched against the record. */
12
+ where?: Where;
10
13
  }
11
14
  /** Minimal schema shape: just the ordered field map. */
12
15
  export interface DerivableSchema {
@@ -21,17 +24,19 @@ export type DeriveRefRecords = Record<string, Record<string, DerivableRecord>>;
21
24
  * null when dangling / not loaded), keyed by the LOCAL field name —
22
25
  * the shape `evaluateDerived` reads for `<field>.<col>` derefs. */
23
26
  export declare function resolveRowRefs(schema: DerivableSchema, record: DerivableRecord, refRecords: DeriveRefRecords): NonNullable<FormulaContext["refs"]>;
24
- /** Evaluate every `derived` field against `base`, saturating so a
25
- * derived field can read another derived field computed in an earlier
26
- * pass (`subtotal → tax → total` converges in ≤ field-count passes).
27
- * Cycles can't loop forever passes are bounded by the number of
28
- * derived fields and the loop breaks as soon as a pass changes
29
- * nothing. Failed formulas stay ABSENT (the UI renders them as
30
- * em-dash). Returns a copy; `base` is never mutated.
27
+ /** Evaluate every `derived` and `flag` field against `base`, saturating
28
+ * so a computed field can read another one computed in an earlier pass
29
+ * (`subtotal → tax → total` converges in ≤ field-count passes; a flag
30
+ * may read a derived value, or another flag via its stringified
31
+ * boolean). Cycles can't loop forever passes are bounded by the
32
+ * number of computed fields and the loop breaks as soon as a pass
33
+ * changes nothing. Failed formulas stay ABSENT (the UI renders them as
34
+ * em-dash); flags are total (always true/false). Returns a copy;
35
+ * `base` is never mutated.
31
36
  *
32
- * Derived keys already present in `base` are stripped before
37
+ * Computed keys already present in `base` are stripped before
33
38
  * evaluation: computed output is host-truth, never persisted-input
34
- * fallback. A record JSON can carry a stale (or forged) derived value
39
+ * fallback. A record JSON can carry a stale (or forged) computed value
35
40
  * — raw Write/Edit, legacy data — and without the strip, a failing
36
41
  * formula would silently surface that value as if the host computed
37
42
  * it. */
@@ -52,10 +52,15 @@ export type CollectionFieldSpec = z.infer<typeof FieldSpecZ>;
52
52
  * `derived` / display-only types (see `SubFieldSpecZ`). */
53
53
  export type CollectionSubFieldSpec = z.infer<typeof SubFieldSpecZ>;
54
54
  export type CollectionFieldType = CollectionFieldSpec["type"];
55
- /** derived/embed/backlinks/rollup/toggle are host-computed or projected
56
- * never written to the record JSON, so required / value checks and
57
- * edit-draft slots must not apply to them. THE single source for
58
- * "computed" — lives here (zod-free at runtime) so browser code
55
+ /** The computed-boolean variant a `where` predicate bound to a field
56
+ * name; see `FlagFieldZ`. */
57
+ export type CollectionFlagField = Extract<CollectionFieldSpec, {
58
+ type: "flag";
59
+ }>;
60
+ /** derived/embed/backlinks/rollup/toggle/flag are host-computed or
61
+ * projected — never written to the record JSON, so required / value
62
+ * checks and edit-draft slots must not apply to them. THE single source
63
+ * for "computed" — lives here (zod-free at runtime) so browser code
59
64
  * (`./draft`) and the zod record compiler (`./recordZ`, which re-exports
60
65
  * it) share one set instead of drifting copies. */
61
66
  export declare const COMPUTED_TYPES: ReadonlySet<CollectionFieldType>;
@@ -12,6 +12,46 @@ export declare const WhenZ: z.ZodObject<{
12
12
  field: z.ZodString;
13
13
  in: z.ZodArray<z.ZodString>;
14
14
  }, z.core.$strip>;
15
+ export declare const ValueRefZ: z.ZodObject<{
16
+ record: z.ZodOptional<z.ZodString>;
17
+ field: z.ZodString;
18
+ }, z.core.$strip>;
19
+ export declare const WhereCondZ: z.ZodObject<{
20
+ field: z.ZodString;
21
+ op: z.ZodEnum<{
22
+ in: "in";
23
+ eq: "eq";
24
+ ne: "ne";
25
+ gt: "gt";
26
+ gte: "gte";
27
+ lt: "lt";
28
+ lte: "lte";
29
+ contains: "contains";
30
+ }>;
31
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
32
+ valueFrom: z.ZodOptional<z.ZodObject<{
33
+ record: z.ZodOptional<z.ZodString>;
34
+ field: z.ZodString;
35
+ }, z.core.$strip>>;
36
+ }, z.core.$strip>;
37
+ export declare const WhereZ: z.ZodArray<z.ZodObject<{
38
+ field: z.ZodString;
39
+ op: z.ZodEnum<{
40
+ in: "in";
41
+ eq: "eq";
42
+ ne: "ne";
43
+ gt: "gt";
44
+ gte: "gte";
45
+ lt: "lt";
46
+ lte: "lte";
47
+ contains: "contains";
48
+ }>;
49
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
50
+ valueFrom: z.ZodOptional<z.ZodObject<{
51
+ record: z.ZodOptional<z.ZodString>;
52
+ field: z.ZodString;
53
+ }, z.core.$strip>>;
54
+ }, z.core.$strip>>;
15
55
  export declare const SubFieldSpecZ: z.ZodDiscriminatedUnion<[z.ZodObject<{
16
56
  label: z.ZodString;
17
57
  required: z.ZodOptional<z.ZodBoolean>;
@@ -210,6 +250,33 @@ export declare const FieldSpecZ: z.ZodDiscriminatedUnion<[z.ZodObject<{
210
250
  in: z.ZodArray<z.ZodString>;
211
251
  }, z.core.$strip>>;
212
252
  type: z.ZodLiteral<"toggle">;
253
+ }, z.core.$strip>, z.ZodObject<{
254
+ where: z.ZodArray<z.ZodObject<{
255
+ field: z.ZodString;
256
+ op: z.ZodEnum<{
257
+ in: "in";
258
+ eq: "eq";
259
+ ne: "ne";
260
+ gt: "gt";
261
+ gte: "gte";
262
+ lt: "lt";
263
+ lte: "lte";
264
+ contains: "contains";
265
+ }>;
266
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
267
+ valueFrom: z.ZodOptional<z.ZodObject<{
268
+ record: z.ZodOptional<z.ZodString>;
269
+ field: z.ZodString;
270
+ }, z.core.$strip>>;
271
+ }, z.core.$strip>>;
272
+ label: z.ZodString;
273
+ primary: z.ZodOptional<z.ZodBoolean>;
274
+ required: z.ZodOptional<z.ZodBoolean>;
275
+ when: z.ZodOptional<z.ZodObject<{
276
+ field: z.ZodString;
277
+ in: z.ZodArray<z.ZodString>;
278
+ }, z.core.$strip>>;
279
+ type: z.ZodLiteral<"flag">;
213
280
  }, z.core.$strip>], "type">;
214
281
  /** A schema-declared record action, rendered as a button in the read-only
215
282
  * detail view. Domain-free: the host validates the shape; the meaning
@@ -434,6 +501,24 @@ export declare const AgentIngestZ: z.ZodObject<{
434
501
  role: z.ZodString;
435
502
  template: z.ZodString;
436
503
  }, z.core.$strip>;
504
+ /** The Google event fields a collection may pull from. `id` is absent on
505
+ * purpose — it always lands in the primary field, since upsert-by-event-id
506
+ * is what makes the sync idempotent. */
507
+ export declare const GOOGLE_CALENDAR_SOURCE_FIELDS: readonly ["summary", "start", "end", "htmlLink", "colorId", "status"];
508
+ /** Marks a collection as the destination of the LLM-free Google Calendar
509
+ * sync (#2095). `map` is collectionField → Google event field, so the user's
510
+ * collection keeps whatever field names it already uses. */
511
+ export declare const GoogleCalendarSyncZ: z.ZodObject<{
512
+ calendarId: z.ZodOptional<z.ZodString>;
513
+ map: z.ZodRecord<z.ZodString, z.ZodEnum<{
514
+ summary: "summary";
515
+ start: "start";
516
+ end: "end";
517
+ htmlLink: "htmlLink";
518
+ colorId: "colorId";
519
+ status: "status";
520
+ }>>;
521
+ }, z.core.$strip>;
437
522
  /** `ingest` is a discriminated union on `kind`: the three declarative
438
523
  * retrievers fetch-and-map; `agent` dispatches a hidden worker. Optional on
439
524
  * every schema — skill-backed collections usually omit it; only feeds
@@ -469,46 +554,6 @@ export declare const IngestZ: z.ZodDiscriminatedUnion<[z.ZodObject<{
469
554
  role: z.ZodString;
470
555
  template: z.ZodString;
471
556
  }, z.core.$strip>], "kind">;
472
- export declare const ValueRefZ: z.ZodObject<{
473
- record: z.ZodOptional<z.ZodString>;
474
- field: z.ZodString;
475
- }, z.core.$strip>;
476
- export declare const WhereCondZ: z.ZodObject<{
477
- field: z.ZodString;
478
- op: z.ZodEnum<{
479
- in: "in";
480
- eq: "eq";
481
- ne: "ne";
482
- gt: "gt";
483
- gte: "gte";
484
- lt: "lt";
485
- lte: "lte";
486
- contains: "contains";
487
- }>;
488
- value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
489
- valueFrom: z.ZodOptional<z.ZodObject<{
490
- record: z.ZodOptional<z.ZodString>;
491
- field: z.ZodString;
492
- }, z.core.$strip>>;
493
- }, z.core.$strip>;
494
- export declare const WhereZ: z.ZodArray<z.ZodObject<{
495
- field: z.ZodString;
496
- op: z.ZodEnum<{
497
- in: "in";
498
- eq: "eq";
499
- ne: "ne";
500
- gt: "gt";
501
- gte: "gte";
502
- lt: "lt";
503
- lte: "lte";
504
- contains: "contains";
505
- }>;
506
- value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
507
- valueFrom: z.ZodOptional<z.ZodObject<{
508
- record: z.ZodOptional<z.ZodString>;
509
- field: z.ZodString;
510
- }, z.core.$strip>>;
511
- }, z.core.$strip>>;
512
557
  export declare const DynamicIconSourceZ: z.ZodObject<{
513
558
  collection: z.ZodString;
514
559
  from: z.ZodOptional<z.ZodEnum<{
@@ -616,12 +661,12 @@ export declare const DynamicIconSpecZ: z.ZodObject<{
616
661
  * workspace-relative and containment-checked exactly like `dataPath`. The
617
662
  * row-id column is the schema's existing `primaryKey` — there is
618
663
  * deliberately no second key concept here.
619
- * See plans/feat-collection-csv-duckdb-source.md. */
664
+ * See plans/done/feat-collection-csv-duckdb-source.md. */
620
665
  export declare const DataSourceZ: z.ZodObject<{
621
666
  type: z.ZodLiteral<"csv">;
622
667
  path: z.ZodString;
623
668
  }, z.core.$strip>;
624
- export declare const CollectionSchemaZ: z.ZodObject<{
669
+ export declare const CollectionSchemaZ: z.ZodPreprocess<z.ZodObject<{
625
670
  title: z.ZodString;
626
671
  icon: z.ZodString;
627
672
  dataPath: z.ZodOptional<z.ZodString>;
@@ -799,6 +844,33 @@ export declare const CollectionSchemaZ: z.ZodObject<{
799
844
  in: z.ZodArray<z.ZodString>;
800
845
  }, z.core.$strip>>;
801
846
  type: z.ZodLiteral<"toggle">;
847
+ }, z.core.$strip>, z.ZodObject<{
848
+ where: z.ZodArray<z.ZodObject<{
849
+ field: z.ZodString;
850
+ op: z.ZodEnum<{
851
+ in: "in";
852
+ eq: "eq";
853
+ ne: "ne";
854
+ gt: "gt";
855
+ gte: "gte";
856
+ lt: "lt";
857
+ lte: "lte";
858
+ contains: "contains";
859
+ }>;
860
+ value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
861
+ valueFrom: z.ZodOptional<z.ZodObject<{
862
+ record: z.ZodOptional<z.ZodString>;
863
+ field: z.ZodString;
864
+ }, z.core.$strip>>;
865
+ }, z.core.$strip>>;
866
+ label: z.ZodString;
867
+ primary: z.ZodOptional<z.ZodBoolean>;
868
+ required: z.ZodOptional<z.ZodBoolean>;
869
+ when: z.ZodOptional<z.ZodObject<{
870
+ field: z.ZodString;
871
+ in: z.ZodArray<z.ZodString>;
872
+ }, z.core.$strip>>;
873
+ type: z.ZodLiteral<"flag">;
802
874
  }, z.core.$strip>], "type">>;
803
875
  actions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
804
876
  role: z.ZodString;
@@ -1002,6 +1074,17 @@ export declare const CollectionSchemaZ: z.ZodObject<{
1002
1074
  role: z.ZodString;
1003
1075
  template: z.ZodString;
1004
1076
  }, z.core.$strip>], "kind">>;
1077
+ googleCalendar: z.ZodOptional<z.ZodObject<{
1078
+ calendarId: z.ZodOptional<z.ZodString>;
1079
+ map: z.ZodRecord<z.ZodString, z.ZodEnum<{
1080
+ summary: "summary";
1081
+ start: "start";
1082
+ end: "end";
1083
+ htmlLink: "htmlLink";
1084
+ colorId: "colorId";
1085
+ status: "status";
1086
+ }>>;
1087
+ }, z.core.$strip>>;
1005
1088
  dynamicIcon: z.ZodOptional<z.ZodObject<{
1006
1089
  source: z.ZodObject<{
1007
1090
  collection: z.ZodString;
@@ -1053,4 +1136,4 @@ export declare const CollectionSchemaZ: z.ZodObject<{
1053
1136
  }, z.core.$strip>>;
1054
1137
  fallback: z.ZodOptional<z.ZodString>;
1055
1138
  }, z.core.$strip>>;
1056
- }, z.core.$strip>;
1139
+ }, z.core.$strip>>;
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_promptSafety = require("../promptSafety-DRd15gQ6.cjs");
2
+ const require_ids = require("../ids-BR4U1dJD.cjs");
3
+ const require_promptSafety = require("../promptSafety-Co4cYQ_Z.cjs");
3
4
  const require_errorMessage = require("../errorMessage--LvXe_Jx.cjs");
4
5
  //#region src/collection/core/presentCollection.ts
5
6
  var TOOL_NAME = "presentCollection";
@@ -225,7 +226,7 @@ function rowDraftToRecord(rowDraft, subFields) {
225
226
  function draftToRecord(state, schema) {
226
227
  const record = {};
227
228
  for (const [key, field] of Object.entries(schema.fields)) {
228
- if (require_promptSafety.COMPUTED_TYPES.has(field.type)) continue;
229
+ if (require_ids.COMPUTED_TYPES.has(field.type)) continue;
229
230
  if (field.type === "boolean") {
230
231
  if (shouldEmitBoolean(state, key, field)) record[key] = state.bool[key] === true;
231
232
  continue;
@@ -281,7 +282,7 @@ function firstMissingTableSubField(field, rows) {
281
282
  * value, and the computed/projected kinds (COMPUTED_TYPES — the shared
282
283
  * set, so a future computed type can't drift past this check) have no
283
284
  * draft input at all. */
284
- var NEVER_MISSING_TYPES = /* @__PURE__ */ new Set(["boolean", ...require_promptSafety.COMPUTED_TYPES]);
285
+ var NEVER_MISSING_TYPES = /* @__PURE__ */ new Set(["boolean", ...require_ids.COMPUTED_TYPES]);
285
286
  function validateOneField(key, field, draft, record) {
286
287
  if (!require_promptSafety.fieldVisible(field, record)) return null;
287
288
  if (field.type === "table" && field.of) {
@@ -436,16 +437,16 @@ function shortHexId() {
436
437
  return crypto.randomUUID().replace(/-/g, "").slice(0, 8);
437
438
  }
438
439
  //#endregion
439
- exports.AGENT_INGEST_KIND = require_promptSafety.AGENT_INGEST_KIND;
440
- exports.COMPUTED_TYPES = require_promptSafety.COMPUTED_TYPES;
440
+ exports.AGENT_INGEST_KIND = require_ids.AGENT_INGEST_KIND;
441
+ exports.COMPUTED_TYPES = require_ids.COMPUTED_TYPES;
441
442
  exports.ENUM_ALERT = ENUM_ALERT;
442
443
  exports.ENUM_NEUTRAL = ENUM_NEUTRAL;
443
444
  exports.ENUM_NUDGE = ENUM_NUDGE;
444
- exports.FEED_SCHEDULES = require_promptSafety.FEED_SCHEDULES;
445
- exports.INGEST_KINDS = require_promptSafety.INGEST_KINDS;
445
+ exports.FEED_SCHEDULES = require_ids.FEED_SCHEDULES;
446
+ exports.INGEST_KINDS = require_ids.INGEST_KINDS;
446
447
  exports.MINUTES_PER_DAY = require_promptSafety.MINUTES_PER_DAY;
447
- exports.SAFE_RECORD_ID_PATTERN = require_promptSafety.SAFE_RECORD_ID_PATTERN;
448
- exports.SAFE_SLUG_PATTERN = require_promptSafety.SAFE_SLUG_PATTERN;
448
+ exports.SAFE_RECORD_ID_PATTERN = require_ids.SAFE_RECORD_ID_PATTERN;
449
+ exports.SAFE_SLUG_PATTERN = require_ids.SAFE_SLUG_PATTERN;
449
450
  exports.TOOL_DEFINITION = TOOL_DEFINITION;
450
451
  exports.TOOL_NAME = TOOL_NAME;
451
452
  exports.actionVisible = require_promptSafety.actionVisible;
@@ -466,7 +467,7 @@ exports.daySlice = require_promptSafety.daySlice;
466
467
  exports.defangForPrompt = require_promptSafety.defangForPrompt;
467
468
  exports.deriveAll = require_promptSafety.deriveAll;
468
469
  exports.draftToRecord = draftToRecord;
469
- exports.embedTargetId = require_promptSafety.embedTargetId;
470
+ exports.embedTargetId = require_ids.embedTargetId;
470
471
  exports.emptyRow = emptyRow;
471
472
  exports.enumColorClasses = enumColorClasses;
472
473
  exports.enumSortValue = enumSortValue;
@@ -477,12 +478,13 @@ exports.executePresentCollection = executePresentCollection;
477
478
  exports.fieldVisible = require_promptSafety.fieldVisible;
478
479
  exports.firstDateField = require_promptSafety.firstDateField;
479
480
  exports.firstMissingRequiredField = firstMissingRequiredField;
480
- exports.isFieldDrivenEvery = require_promptSafety.isFieldDrivenEvery;
481
- exports.isReadOnlySchema = require_promptSafety.isReadOnlySchema;
482
- exports.isSafeRecordId = require_promptSafety.isSafeRecordId;
483
- exports.isSafeSlug = require_promptSafety.isSafeSlug;
481
+ exports.isFieldDrivenEvery = require_ids.isFieldDrivenEvery;
482
+ exports.isReadOnlySchema = require_ids.isReadOnlySchema;
483
+ exports.isSafeRecordId = require_ids.isSafeRecordId;
484
+ exports.isSafeSlug = require_ids.isSafeSlug;
484
485
  exports.isSortableField = isSortableField;
485
486
  exports.itemIdOf = itemIdOf;
487
+ exports.itemIsDone = require_promptSafety.itemIsDone;
486
488
  exports.itemLabelOf = itemLabelOf;
487
489
  exports.labelFieldFor = labelFieldFor;
488
490
  exports.matchesWhere = require_promptSafety.matchesWhere;