@mulmoclaude/core 0.2.0 → 0.2.5

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 (88) hide show
  1. package/assets/helps/collection-skills.md +52 -4
  2. package/assets/helps/custom-view.md +32 -11
  3. package/assets/helps/feeds.md +16 -7
  4. package/assets/skills-preset/mc-manage-automations/SKILL.md +36 -2
  5. package/dist/collection/core/schema.d.ts +37 -10
  6. package/dist/collection/index.cjs +4 -3
  7. package/dist/collection/index.cjs.map +1 -1
  8. package/dist/collection/index.js +4 -4
  9. package/dist/collection/index.js.map +1 -1
  10. package/dist/collection/paths.cjs.map +1 -1
  11. package/dist/collection/paths.js.map +1 -1
  12. package/dist/collection/server/discovery.d.ts +14 -2
  13. package/dist/collection/server/index.cjs +1 -1
  14. package/dist/collection/server/index.js +1 -1
  15. package/dist/collection/server/io.d.ts +12 -6
  16. package/dist/collection-watchers/index.cjs +2 -3
  17. package/dist/collection-watchers/index.cjs.map +1 -1
  18. package/dist/collection-watchers/index.js +2 -2
  19. package/dist/collection-watchers/index.js.map +1 -1
  20. package/dist/{deriveAll-C15OpM3K.cjs → deriveAll-VRWrs3SF.cjs} +19 -5
  21. package/dist/deriveAll-VRWrs3SF.cjs.map +1 -0
  22. package/dist/{deriveAll-C6BYnpBL.js → deriveAll-vzIhhKBK.js} +14 -6
  23. package/dist/deriveAll-vzIhhKBK.js.map +1 -0
  24. package/dist/feeds/index.cjs +17 -0
  25. package/dist/feeds/index.d.ts +2 -0
  26. package/dist/feeds/index.js +4 -0
  27. package/dist/feeds/ingestTypes.d.ts +57 -0
  28. package/dist/feeds/paths.cjs +48 -0
  29. package/dist/feeds/paths.cjs.map +1 -0
  30. package/dist/feeds/paths.d.ts +22 -0
  31. package/dist/feeds/paths.js +38 -0
  32. package/dist/feeds/paths.js.map +1 -0
  33. package/dist/feeds/server/agentIngest.d.ts +13 -0
  34. package/dist/feeds/server/engine.d.ts +12 -0
  35. package/dist/feeds/server/fetch/httpClient.d.ts +8 -0
  36. package/dist/feeds/server/fetch/rssParser.d.ts +12 -0
  37. package/dist/feeds/server/host.d.ts +51 -0
  38. package/dist/feeds/server/index.cjs +804 -0
  39. package/dist/feeds/server/index.cjs.map +1 -0
  40. package/dist/feeds/server/index.d.ts +7 -0
  41. package/dist/feeds/server/index.js +782 -0
  42. package/dist/feeds/server/index.js.map +1 -0
  43. package/dist/feeds/server/pathResolver.d.ts +6 -0
  44. package/dist/feeds/server/projectItem.d.ts +8 -0
  45. package/dist/feeds/server/refreshResult.d.ts +17 -0
  46. package/dist/feeds/server/registry.d.ts +14 -0
  47. package/dist/feeds/server/retrievers/httpJson.d.ts +3 -0
  48. package/dist/feeds/server/retrievers/index.d.ts +12 -0
  49. package/dist/feeds/server/retrievers/registerAll.d.ts +0 -0
  50. package/dist/feeds/server/retrievers/rss.d.ts +3 -0
  51. package/dist/feeds/server/state.d.ts +28 -0
  52. package/dist/file-change/index.cjs +2 -2
  53. package/dist/file-change/index.cjs.map +1 -1
  54. package/dist/ingestTypes-B8DaQOTy.js +15 -0
  55. package/dist/ingestTypes-B8DaQOTy.js.map +1 -0
  56. package/dist/ingestTypes-DDOVhOYC.cjs +32 -0
  57. package/dist/ingestTypes-DDOVhOYC.cjs.map +1 -0
  58. package/dist/notifier/index.cjs +1 -1
  59. package/dist/notifier/index.js +1 -1
  60. package/dist/{notifier-6PjsLxLm.js → notifier-ChpY0XrY.js} +1 -1
  61. package/dist/{notifier-6PjsLxLm.js.map → notifier-ChpY0XrY.js.map} +1 -1
  62. package/dist/{notifier-lJ4v2Y6B.cjs → notifier-bS8IEeLA.cjs} +1 -2
  63. package/dist/{notifier-lJ4v2Y6B.cjs.map → notifier-bS8IEeLA.cjs.map} +1 -1
  64. package/dist/scheduler/index.cjs +2 -2
  65. package/dist/scheduler/index.cjs.map +1 -1
  66. package/dist/scheduler/index.js.map +1 -1
  67. package/dist/{server-BhIdZgqu.js → server-B2YkcCwl.js} +76 -33
  68. package/dist/server-B2YkcCwl.js.map +1 -0
  69. package/dist/{server-BjoKk2tR.cjs → server-D27UzXug.cjs} +78 -35
  70. package/dist/server-D27UzXug.cjs.map +1 -0
  71. package/dist/skill-bridge/index.cjs +2 -2
  72. package/dist/skill-bridge/index.cjs.map +1 -1
  73. package/dist/whisper/client.cjs +1 -1
  74. package/dist/whisper/client.cjs.map +1 -1
  75. package/dist/whisper/client.js +1 -1
  76. package/dist/whisper/client.js.map +1 -1
  77. package/dist/whisper/index.cjs +4 -4
  78. package/dist/whisper/index.cjs.map +1 -1
  79. package/dist/whisper/index.js +2 -2
  80. package/dist/whisper/index.js.map +1 -1
  81. package/dist/workspace-setup/index.js +5 -1
  82. package/dist/workspace-setup/index.js.map +1 -1
  83. package/package.json +22 -3
  84. package/dist/deriveAll-C15OpM3K.cjs.map +0 -1
  85. package/dist/deriveAll-C6BYnpBL.js.map +0 -1
  86. package/dist/server-BhIdZgqu.js.map +0 -1
  87. package/dist/server-BjoKk2tR.cjs.map +0 -1
  88. /package/dist/{chunk-CKQMccvm.cjs → rolldown-runtime-D6vf50IK.cjs} +0 -0
@@ -49,7 +49,7 @@ data/<name>/items/ ← the records (separate from the skill dir)
49
49
  discovery enforces and reports the exact problem, where a hand-edit can
50
50
  silently fail validation and make the collection vanish from the UI. It writes
51
51
  the canonical `data/skills/<slug>/schema.json` and mirrors it for you — same
52
- destination as authoring, just validated. (Creating a *new* collection still
52
+ destination as authoring, just validated. (Creating a _new_ collection still
53
53
  means writing `SKILL.md` + `schema.json` under `data/skills/<slug>/`, since
54
54
  there's nothing to `getSchema` yet.)
55
55
  - **Do NOT use the `mc-` prefix** for skills you create. `mc-*` is reserved for
@@ -127,7 +127,7 @@ skipped, never crashes the host):
127
127
  | `title` | Human name shown in the sidebar / header. Required. |
128
128
  | `icon` | A **Material Symbols** name (`receipt_long`, `people`, `schedule`, `menu_book`). Required. |
129
129
  | `dataPath` | Workspace-relative records folder, e.g. `data/recipes/items`. Must stay under the workspace. Required. |
130
- | `primaryKey` | The field name whose value is the filename. That field MUST set `primary: true`. The value must be a valid record id (see the **Records** section's id-charset rule). Required. |
130
+ | `primaryKey` | The field name whose value is the filename. That field MUST set `primary: true`. The value must be a valid record id (see the **Records** section's id-charset rule). Required. |
131
131
  | `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. |
132
132
  | `fields` | Ordered map of field-name → field spec. **Insertion order = column order** in the table. Required. |
133
133
  | `actions` | Optional array of per-record buttons (see below). |
@@ -492,7 +492,7 @@ month's rent `paid`, and next month's pending record appears on its own.
492
492
  - **Field-driven interval** (one list, mixed cadences): instead of a single
493
493
  `{ unit, interval }`, `every` may select the interval **per record** from
494
494
  an `enum` field. Use `{ "fromField": "<enum field>", "map": { <value>:
495
- { unit, interval, … } } }` — the host reads the record's value and
495
+ { unit, interval, … } } }` — the host reads the record's value and
496
496
  advances by the matching entry. This lets one collection carry daily,
497
497
  weekly, and monthly obligations together:
498
498
 
@@ -513,6 +513,7 @@ month's rent `paid`, and next month's pending record appears on its own.
513
513
  by `set`) so the successor keeps its frequency and the chain keeps
514
514
  recurring. Each `map` value is a literal `every` (same `unit` / `interval`
515
515
  / `dayOfMonth` rules as above).
516
+
516
517
  - **`carry`** — record fields copied verbatim onto the successor (must name
517
518
  real fields). Fields not in `carry` / `set` / the trigger+primary keys start
518
519
  blank.
@@ -538,6 +539,53 @@ This covers _periodic_ obligations. It does **not** do escalating, multi-stage
538
539
  reminders over a long prep window (info → warning → urgent) — that is
539
540
  intentionally out of scope for collections.
540
541
 
542
+ ### Scheduled agent refresh (`ingest.kind: "agent"`)
543
+
544
+ When a collection's records need to be **refreshed on a schedule by judgment**
545
+ — fetch today's stock quotes for every ticker, re-check each watched URL, pull
546
+ fresh figures that a static feed URL can't express — add an `ingest` block with
547
+ `kind: "agent"`. On schedule (and on the **Refresh** button, which appears on
548
+ any collection with an `ingest` block), the host launches a **hidden background
549
+ worker** in `role`, seeded with your `template` plus the same compact
550
+ all-records summary a collection-level action gets. The worker edits the records
551
+ itself via its collection tools, then finishes silently — nothing shows in the
552
+ chat list. Host stays generic: all the domain logic lives in the template prose.
553
+
554
+ ```json
555
+ "ingest": {
556
+ "kind": "agent",
557
+ "schedule": "daily",
558
+ "atHour": 22,
559
+ "role": "investor",
560
+ "template": "templates/refresh.md"
561
+ }
562
+ ```
563
+
564
+ - **`schedule`**: `hourly` | `daily` | `weekly` | `on-demand`. `on-demand` never
565
+ auto-runs (Refresh button only). Cadence is elapsed-based ("≥24 h since the
566
+ last run, checked hourly") unless you anchor it (below).
567
+ - **`atHour`** (optional, `daily` only): the hour (0–23) to run around. The host
568
+ ticks hourly, so the run lands within that hour.
569
+ **⚠ `atHour` is UTC — NOT the user's local time.** A bare `"atHour": 9` fires
570
+ at 09:00 UTC (= 18:00 JST, = 04:00 ET), which is almost never what "9am" means
571
+ to the user. So when the user says a local time, **always convert to UTC
572
+ first**: 07:00 JST → `"atHour": 22`; 9am ET ≈ `"atHour": 13`; 9am PT ≈
573
+ `"atHour": 17`. (UTC is used for an unambiguous, DST-free comparison, matching
574
+ the rest of the scheduler.)
575
+ - **`role`**: the role the worker runs in — it must own the tools the refresh
576
+ needs (e.g. `investor` for the Yahoo Finance endpoints).
577
+ - **`template`**: a path-safe `templates/…md` file (same rule as action
578
+ templates) whose prose tells the worker exactly what to do. End it with "edit
579
+ the records and stop — do not present anything" (no one is watching its
580
+ canvas).
581
+ - No `url`/`map` — the agent owns retrieval and record shape; its writes are
582
+ still schema-validated. A failed run raises a single bell ("Collection refresh
583
+ failed: `<slug>`") that clears on the next success.
584
+
585
+ Reach for `ingest.kind: "agent"` (not a `manageAutomations` task) whenever the
586
+ schedule belongs to one collection: it travels with the schema, dies with the
587
+ collection, and needs no separate setup.
588
+
541
589
  ### Calendar view
542
590
 
543
591
  Any collection that has at least one `date` (or `datetime`) field gains a
@@ -713,7 +761,7 @@ single source of truth and the "done" checkbox is a `toggle` field projecting it
713
761
  separators, **no** leading/trailing dot, and **no** `..` substring. If your
714
762
  natural key contains anything else (a space, `/`, `:`, a leading dot), sanitise
715
763
  it first — e.g. replace each illegal run with `_`. Note `manageCollection`
716
- enforces this on every targeted read/write, so an id that only *looks* fine in
764
+ enforces this on every targeted read/write, so an id that only _looks_ fine in
717
765
  a full `getItems` listing but violates the rule can't be updated or deleted by
718
766
  id — fix the id, don't work around it with raw file I/O.
719
767
  - **The file MUST be valid JSON.** A malformed record is **silently skipped** at
@@ -69,19 +69,36 @@ window.__MC_VIEW = {
69
69
 
70
70
  ### Reading records
71
71
 
72
+ > **Always project `fields`.** The host refuses any read of more than 200
73
+ > records that does not pass `?fields=…` or `?ids=…`, because an unscoped
74
+ > fetch wastes work and bandwidth on columns your view never reads. Once a
75
+ > user's collection grows past the threshold an unprojected view starts
76
+ > returning `400` instead of records — write the projection from day one so
77
+ > the same view keeps working forever.
78
+
72
79
  ```js
73
80
  const { token, dataUrl } = window.__MC_VIEW;
74
- const res = await fetch(dataUrl, { headers: { Authorization: "Bearer " + token } });
75
- if (!res.ok) throw new Error("load failed: " + res.status);
81
+ // List the columns this view actually reads never send `dataUrl` raw.
82
+ const url = dataUrl + "?fields=" + encodeURIComponent("title,start,end,status");
83
+ const res = await fetch(url, { headers: { Authorization: "Bearer " + token } });
84
+ if (!res.ok) {
85
+ // Surface the server's reason (the 400 carries `{ error: "<message>" }`),
86
+ // not just the status code — that text is what tells the user / Claude
87
+ // how to fix the call (e.g. "pass `fields` to project only the columns").
88
+ const detail = await res.text();
89
+ throw new Error("load failed (" + res.status + "): " + detail);
90
+ }
76
91
  const { items } = await res.json(); // { collection, count, items: [...] }
77
92
  ```
78
93
 
79
94
  Records come back **with computed fields already resolved** (derived formulas,
80
95
  toggles, embeds) — the same numbers the user sees elsewhere.
81
96
 
82
- - Narrow large reads: `dataUrl + "?fields=title,start,end"` or `?ids=a,b,c`
83
- (comma-separated). A read of **more than 200 records** without `ids`/`fields`
84
- is refusedalways project `fields` for big collections.
97
+ - **`?fields=a,b,c`** only these columns per record (the primary key is always
98
+ included). Use this on every read; it is mandatory above 200 records.
99
+ - **`?ids=x,y,z`**only these specific record ids. Use it when the view edits
100
+ / shows one record at a time. Combinable with `fields`.
101
+ - The primary key is always returned regardless of `fields`.
85
102
 
86
103
  ### Writing records (only with the `write` capability)
87
104
 
@@ -111,9 +128,11 @@ By default a view paints once, on load. To keep it fresh, register a callback
111
128
  it runs whenever the collection's data changes on the server:
112
129
 
113
130
  ```js
131
+ // Same projection rule as the initial read — list the columns this view uses.
132
+ const url = dataUrl + "?fields=" + encodeURIComponent("title,start,end,status");
114
133
  async function render() {
115
- const res = await fetch(dataUrl, { headers: { Authorization: "Bearer " + token } });
116
- if (!res.ok) return; // handle the error per your UI
134
+ const res = await fetch(url, { headers: { Authorization: "Bearer " + token } });
135
+ if (!res.ok) return; // surface the body if you want a richer error UI
117
136
  const { items } = await res.json();
118
137
  // …draw items…
119
138
  }
@@ -303,8 +322,9 @@ fields `start` / `end` and a `title`. `capabilities: ["read"]`.
303
322
  const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
304
323
  async function main() {
305
324
  const { token, dataUrl } = window.__MC_VIEW;
306
- const res = await fetch(dataUrl, { headers: { Authorization: "Bearer " + token } });
307
- if (!res.ok) throw new Error("HTTP " + res.status);
325
+ const url = dataUrl + "?fields=" + encodeURIComponent("title,start");
326
+ const res = await fetch(url, { headers: { Authorization: "Bearer " + token } });
327
+ if (!res.ok) throw new Error("HTTP " + res.status + ": " + (await res.text()));
308
328
  const { items } = await res.json();
309
329
  const buckets = MONTHS.map(() => []);
310
330
  for (const it of items) {
@@ -382,10 +402,11 @@ writes it back. `capabilities: ["read","write"]`.
382
402
  const DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
383
403
  const { token, dataUrl } = window.__MC_VIEW;
384
404
  const auth = { Authorization: "Bearer " + token, "Content-Type": "application/json" };
405
+ const readUrl = dataUrl + "?fields=" + encodeURIComponent("title,start");
385
406
 
386
407
  async function read() {
387
- const res = await fetch(dataUrl, { headers: { Authorization: "Bearer " + token } });
388
- if (!res.ok) throw new Error("HTTP " + res.status);
408
+ const res = await fetch(readUrl, { headers: { Authorization: "Bearer " + token } });
409
+ if (!res.ok) throw new Error("HTTP " + res.status + ": " + (await res.text()));
389
410
  return (await res.json()).items;
390
411
  }
391
412
  async function bump(item) {
@@ -16,8 +16,8 @@ engine does that automatically: the first time the feed's view is opened, on an
16
16
  hourly schedule thereafter, and when the user clicks **Refresh feed**. Records
17
17
  render in the standard collection view at `/feeds/<slug>`.
18
18
 
19
- This is the project philosophy: *the workspace is the database; you are the
20
- intelligent interface.* Adding a feed = **fetch the URL, look at its real
19
+ This is the project philosophy: _the workspace is the database; you are the
20
+ intelligent interface._ Adding a feed = **fetch the URL, look at its real
21
21
  fields, and write one `schema.json`.**
22
22
 
23
23
  ## Workflow to add a feed
@@ -45,11 +45,11 @@ lists all registered feeds, and its delete button does the same.
45
45
  "primaryKey": "id",
46
46
  "displayField": "headline",
47
47
  "fields": {
48
- "id": { "type": "string", "label": "ID", "primary": true },
49
- "headline": { "type": "string", "label": "Headline" },
50
- "url": { "type": "string", "label": "URL" },
51
- "published": { "type": "date", "label": "Published" },
52
- "summary": { "type": "markdown", "label": "Summary" }
48
+ "id": { "type": "string", "label": "ID", "primary": true },
49
+ "headline": { "type": "string", "label": "Headline" },
50
+ "url": { "type": "string", "label": "URL" },
51
+ "published": { "type": "date", "label": "Published" },
52
+ "summary": { "type": "markdown", "label": "Summary" }
53
53
  },
54
54
  "ingest": {
55
55
  "kind": "rss",
@@ -87,6 +87,10 @@ lists all registered feeds, and its delete button does the same.
87
87
  - `url`: the feed / API endpoint (must be public http/https; the host refuses
88
88
  private/loopback addresses).
89
89
  - `schedule`: `hourly` | `daily` | `weekly` | `on-demand`.
90
+ - `atHour` (optional, `daily` only): the hour (0–23) to anchor a daily feed to
91
+ (the host ticks hourly, so the run lands within that hour). **⚠ UTC, NOT local
92
+ time** — `"atHour": 9` is 09:00 UTC (= 18:00 JST). Always convert the user's
93
+ local time to UTC before writing.
90
94
  - `map`: `{ <yourFieldName>: <sourcePath> }` — a dot/bracket path into each
91
95
  fetched item. **Map the fields you actually saw when you inspected the feed.**
92
96
  - rss/atom: each item is the parsed XML element. Tags are keys (`title`,
@@ -106,6 +110,11 @@ lists all registered feeds, and its delete button does the same.
106
110
  - http-json needs an array of objects. Columnar/parallel-array APIs (e.g.
107
111
  Open-Meteo weather: `hourly.time[]` + `hourly.temperature_2m[]`) are not
108
112
  supported yet.
113
+ - When a refresh needs **judgment** the declarative `map` can't express — auth
114
+ headers, per-record requests, picking which symbols to fetch, computing a
115
+ value — use `ingest.kind: "agent"` instead (a scheduled hidden worker; see
116
+ `config/helps/collection-skills.md` → "Scheduled agent refresh"). It works on
117
+ any collection, not just feeds.
109
118
  - A malformed `schema.json` is skipped at load time (with a diagnostic on the
110
119
  notification bell), so double-check the shape above before writing.
111
120
  - A feed can carry **custom views** just like any collection — author the HTML at
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: mc-manage-automations
3
- description: Schedule, list, edit, or remove a recurring agent task (cron / interval). Use when the user wants the agent to run a prompt on a schedule ("毎朝7時に天気", "every weekday 8am check email", "schedule a weekly cleanup"), list active automations, or stop one. Edits `config/scheduler/tasks.json` (cwd-relative the agent already runs with cwd = workspace); the auto-refresh hook re-registers tasks on save.
3
+ description: Schedule, list, edit, or remove a recurring agent task (cron / interval) in `config/scheduler/tasks.json`. Use for cross-collection digests, notifications, or any standalone recurring prompt ("毎朝7時に天気", "every weekday 8am check email", "schedule a weekly cleanup"), or to list/stop automations. EXCEPTION do NOT create a task here to keep ONE collection's records fresh (e.g. "update the stock-quotes daily", "refresh these quotes every morning", "re-poll these URLs"): instead add an `ingest` block with `kind: "agent"` to THAT collection's own `schema.json` (it self-refreshes on schedule and from its Refresh button; syntax in `config/helps/collection-skills.md` → "Scheduled agent refresh"). Read this skill body in full before writing `tasks.json` the entry format is structured, not free-form.
4
4
  ---
5
5
 
6
6
  # Automations manager
@@ -18,6 +18,40 @@ take effect immediately.
18
18
  End with a one-line confirmation ("Scheduled weather-morning, daily 07:00
19
19
  UTC." / "Stopped weekly-cleanup.") so the user can verify without scrolling.
20
20
 
21
+ ## First: is this just refreshing ONE collection?
22
+
23
+ Before writing a `tasks.json` automation, check what the recurring work
24
+ actually is. If it's **"keep one collection's records up to date"** — refresh
25
+ stock quotes, re-poll a set of watched URLs, pull fresh figures for every row —
26
+ do **NOT** create an automation here. Add an `ingest` block to _that
27
+ collection's own_ `schema.json` instead:
28
+
29
+ ```json
30
+ "ingest": {
31
+ "kind": "agent",
32
+ "schedule": "daily",
33
+ "atHour": 21,
34
+ "role": "<a role that owns the tools the refresh needs>",
35
+ "template": "templates/refresh.md"
36
+ }
37
+ ```
38
+
39
+ and write `templates/refresh.md` telling the worker how to refresh the records.
40
+ The full contract is in `config/helps/collection-skills.md` → "Scheduled agent
41
+ refresh". On schedule (and on the collection's **Refresh** button) the host runs
42
+ a hidden background worker that edits the records.
43
+
44
+ Prefer this over a `tasks.json` automation whenever the work is one collection's
45
+ refresh: the schedule lives **with** the collection — it travels when the skill
46
+ is copied and is deleted when the collection is, instead of leaving an orphan
47
+ task here whose prompt points at records that may no longer exist. (Editing
48
+ `data/stock-quotes/items/*.json` daily? That's `ingest.kind: "agent"` on the
49
+ stock-quotes schema, not an automation.)
50
+
51
+ Use the `tasks.json` automation below only for work that **isn't** one
52
+ collection's refresh: cross-collection digests, notifications, or an arbitrary
53
+ recurring prompt.
54
+
21
55
  ## Workflow 1: schedule a new automation
22
56
 
23
57
  **Triggers**: "毎朝 7 時に天気を", "every weekday 8am check email",
@@ -26,7 +60,7 @@ UTC." / "Stopped weekly-cleanup.") so the user can verify without scrolling.
26
60
  **Step 1 — convert local time to UTC.** Schedules are stored / compared in
27
61
  UTC (the task-manager calls `Date.getUTCHours/Minutes`). When the user gives
28
62
  a local time, convert before writing. E.g. "07:00 JST" → `"16:00"` of the
29
- *previous* UTC day? No — `07:00 JST` is `22:00 UTC` of the *previous* day.
63
+ _previous_ UTC day? No — `07:00 JST` is `22:00 UTC` of the _previous_ day.
30
64
  JST is UTC+9, so `07:00 JST = 22:00 UTC (yesterday)`. (Run the math, don't
31
65
  trust your reflex — UTC offsets are a classic typo source.)
32
66
 
@@ -7,15 +7,39 @@
7
7
  * these three + the presence check. */
8
8
  export interface CollectionIngest {
9
9
  kind: string;
10
- url: string;
11
10
  schedule: string;
11
+ /** Optional time-of-day anchor for `schedule: "daily"` — the hour (0–23) to
12
+ * refresh around (the host ticks hourly, so the run lands within that hour).
13
+ * Ignored for non-daily schedules. Absent ⇒ elapsed-based daily ("≥24 h since
14
+ * the last run"). NOTE: **UTC**, not local — compared via `getUTCHours()` for
15
+ * an unambiguous, DST-free check (matching the rest of the scheduler), so
16
+ * convert local times before writing (e.g. 07:00 JST → `atHour: 22`). */
17
+ atHour?: number;
18
+ /** Declarative retrievers (`rss`/`atom`/`http-json`) only — the host fetches
19
+ * this URL on the schedule. Absent for `kind: "agent"`, where the agent owns
20
+ * retrieval. */
21
+ url?: string;
22
+ /** `kind: "agent"` only: role id the scheduled hidden worker runs in. */
23
+ role?: string;
24
+ /** `kind: "agent"` only: skill-relative template path (under `templates/`)
25
+ * whose prose tells the worker how to refresh the records. */
26
+ template?: string;
12
27
  }
13
- /** Retriever kinds a Feed's `ingest.kind` may declare. The host's feeds engine
14
- * dispatches on these; they live here (with the schema contract) so the schema
15
- * validator can enforce them. The host re-exports these from
28
+ /** Declarative retriever kinds a Feed's `ingest.kind` may declare. The host's
29
+ * feeds engine dispatches on these; they live here (with the schema contract)
30
+ * so the schema validator can enforce them. The host re-exports these from
16
31
  * `server/workspace/feeds/ingestTypes.ts`. */
17
32
  export declare const INGEST_KINDS: readonly ["rss", "atom", "http-json"];
18
33
  export type IngestKind = (typeof INGEST_KINDS)[number];
34
+ /** The agent-performed ingest kind. Instead of a declarative fetch, the host
35
+ * dispatches a hidden background chat (origin `system`) in `ingest.role`,
36
+ * seeded with `ingest.template` + a summary of every record, on the
37
+ * `ingest.schedule` cadence; the worker edits records via the collections io
38
+ * layer. Kept separate from {@link INGEST_KINDS} (which the declarative
39
+ * retriever registry keys on) so the schema validator can model `ingest` as a
40
+ * discriminated union without the feeds engine gaining an "agent" retriever. */
41
+ export declare const AGENT_INGEST_KIND: "agent";
42
+ export type AgentIngestKind = typeof AGENT_INGEST_KIND;
19
43
  /** Refresh cadences a Feed's `ingest.schedule` may declare. */
20
44
  export declare const FEED_SCHEDULES: readonly ["hourly", "daily", "weekly", "on-demand"];
21
45
  export type FeedSchedule = (typeof FEED_SCHEDULES)[number];
@@ -335,12 +359,15 @@ export interface CollectionSchema {
335
359
  * notify for every open record (the prior behaviour). `notifyWhen.field`
336
360
  * must name a real top-level field. */
337
361
  notifyWhen?: CollectionWhen;
338
- /** Optional declarative retrieval config. When present, this collection
339
- * is a "Feed": the host periodically fetches `ingest.url`, maps the
340
- * response into records, and upserts them by `primaryKey`. Only feeds
341
- * discovered from the `<workspace>/feeds/` registry carry this; skill
342
- * collections omit it. The host's feeds subsystem narrows this to its
343
- * richer `IngestSpec` (which `extends CollectionIngest`). */
362
+ /** Optional scheduled-retrieval config. When present, the host refreshes
363
+ * this collection on `ingest.schedule`. Two flavours: a declarative Feed
364
+ * (`kind: rss/atom/http-json`) periodically fetches `ingest.url`, maps the
365
+ * response into records, and upserts them by `primaryKey` — only feeds
366
+ * discovered from the `<workspace>/feeds/` registry carry this. Or
367
+ * `kind: "agent"`, valid on any (incl. skill-backed) collection: the host
368
+ * dispatches a hidden background worker in `ingest.role` seeded with
369
+ * `ingest.template`, and the worker edits the records itself. The host's
370
+ * feeds subsystem narrows this to its richer `IngestSpec`. */
344
371
  ingest?: CollectionIngest;
345
372
  }
346
373
  export interface CollectionSummary {
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_deriveAll = require("../deriveAll-C15OpM3K.cjs");
2
+ const require_deriveAll = require("../deriveAll-VRWrs3SF.cjs");
3
3
  //#region src/collection/core/presentCollection.ts
4
4
  var TOOL_NAME = "presentCollection";
5
5
  var TOOL_DEFINITION = {
@@ -328,7 +328,7 @@ function firstMissingRequiredField(draft, schema) {
328
328
  //#region src/collection/core/sortItems.ts
329
329
  var EMPTY = { empty: true };
330
330
  /** Field types that render no value text in the table, so offer no sort. */
331
- var NON_SORTABLE = new Set([
331
+ var NON_SORTABLE = /* @__PURE__ */ new Set([
332
332
  "markdown",
333
333
  "table",
334
334
  "image",
@@ -410,7 +410,7 @@ function sortItems(items, direction, valueOf) {
410
410
  }
411
411
  //#endregion
412
412
  //#region src/collection/core/itemLabel.ts
413
- var LABEL_FIELD_TYPES = new Set([
413
+ var LABEL_FIELD_TYPES = /* @__PURE__ */ new Set([
414
414
  "string",
415
415
  "text",
416
416
  "markdown",
@@ -736,6 +736,7 @@ function defangForPrompt(value) {
736
736
  return value.replace(/[<>]/g, "").replace(/`/g, "'").replace(/\$\{/g, "$ {").replace(/\s+/g, " ").slice(0, DEFANG_MAX_LEN);
737
737
  }
738
738
  //#endregion
739
+ exports.AGENT_INGEST_KIND = require_deriveAll.AGENT_INGEST_KIND;
739
740
  exports.ENUM_ALERT = ENUM_ALERT;
740
741
  exports.ENUM_NEUTRAL = ENUM_NEUTRAL;
741
742
  exports.ENUM_NUDGE = ENUM_NUDGE;