@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.
- package/assets/helps/collection-skills.md +52 -4
- package/assets/helps/custom-view.md +32 -11
- package/assets/helps/feeds.md +16 -7
- package/assets/skills-preset/mc-manage-automations/SKILL.md +36 -2
- package/dist/collection/core/schema.d.ts +37 -10
- package/dist/collection/index.cjs +4 -3
- package/dist/collection/index.cjs.map +1 -1
- package/dist/collection/index.js +4 -4
- package/dist/collection/index.js.map +1 -1
- package/dist/collection/paths.cjs.map +1 -1
- package/dist/collection/paths.js.map +1 -1
- package/dist/collection/server/discovery.d.ts +14 -2
- package/dist/collection/server/index.cjs +1 -1
- package/dist/collection/server/index.js +1 -1
- package/dist/collection/server/io.d.ts +12 -6
- package/dist/collection-watchers/index.cjs +2 -3
- package/dist/collection-watchers/index.cjs.map +1 -1
- package/dist/collection-watchers/index.js +2 -2
- package/dist/collection-watchers/index.js.map +1 -1
- package/dist/{deriveAll-C15OpM3K.cjs → deriveAll-VRWrs3SF.cjs} +19 -5
- package/dist/deriveAll-VRWrs3SF.cjs.map +1 -0
- package/dist/{deriveAll-C6BYnpBL.js → deriveAll-vzIhhKBK.js} +14 -6
- package/dist/deriveAll-vzIhhKBK.js.map +1 -0
- package/dist/feeds/index.cjs +17 -0
- package/dist/feeds/index.d.ts +2 -0
- package/dist/feeds/index.js +4 -0
- package/dist/feeds/ingestTypes.d.ts +57 -0
- package/dist/feeds/paths.cjs +48 -0
- package/dist/feeds/paths.cjs.map +1 -0
- package/dist/feeds/paths.d.ts +22 -0
- package/dist/feeds/paths.js +38 -0
- package/dist/feeds/paths.js.map +1 -0
- package/dist/feeds/server/agentIngest.d.ts +13 -0
- package/dist/feeds/server/engine.d.ts +12 -0
- package/dist/feeds/server/fetch/httpClient.d.ts +8 -0
- package/dist/feeds/server/fetch/rssParser.d.ts +12 -0
- package/dist/feeds/server/host.d.ts +51 -0
- package/dist/feeds/server/index.cjs +804 -0
- package/dist/feeds/server/index.cjs.map +1 -0
- package/dist/feeds/server/index.d.ts +7 -0
- package/dist/feeds/server/index.js +782 -0
- package/dist/feeds/server/index.js.map +1 -0
- package/dist/feeds/server/pathResolver.d.ts +6 -0
- package/dist/feeds/server/projectItem.d.ts +8 -0
- package/dist/feeds/server/refreshResult.d.ts +17 -0
- package/dist/feeds/server/registry.d.ts +14 -0
- package/dist/feeds/server/retrievers/httpJson.d.ts +3 -0
- package/dist/feeds/server/retrievers/index.d.ts +12 -0
- package/dist/feeds/server/retrievers/registerAll.d.ts +0 -0
- package/dist/feeds/server/retrievers/rss.d.ts +3 -0
- package/dist/feeds/server/state.d.ts +28 -0
- package/dist/file-change/index.cjs +2 -2
- package/dist/file-change/index.cjs.map +1 -1
- package/dist/ingestTypes-B8DaQOTy.js +15 -0
- package/dist/ingestTypes-B8DaQOTy.js.map +1 -0
- package/dist/ingestTypes-DDOVhOYC.cjs +32 -0
- package/dist/ingestTypes-DDOVhOYC.cjs.map +1 -0
- package/dist/notifier/index.cjs +1 -1
- package/dist/notifier/index.js +1 -1
- package/dist/{notifier-6PjsLxLm.js → notifier-ChpY0XrY.js} +1 -1
- package/dist/{notifier-6PjsLxLm.js.map → notifier-ChpY0XrY.js.map} +1 -1
- package/dist/{notifier-lJ4v2Y6B.cjs → notifier-bS8IEeLA.cjs} +1 -2
- package/dist/{notifier-lJ4v2Y6B.cjs.map → notifier-bS8IEeLA.cjs.map} +1 -1
- package/dist/scheduler/index.cjs +2 -2
- package/dist/scheduler/index.cjs.map +1 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/{server-BhIdZgqu.js → server-B2YkcCwl.js} +76 -33
- package/dist/server-B2YkcCwl.js.map +1 -0
- package/dist/{server-BjoKk2tR.cjs → server-D27UzXug.cjs} +78 -35
- package/dist/server-D27UzXug.cjs.map +1 -0
- package/dist/skill-bridge/index.cjs +2 -2
- package/dist/skill-bridge/index.cjs.map +1 -1
- package/dist/whisper/client.cjs +1 -1
- package/dist/whisper/client.cjs.map +1 -1
- package/dist/whisper/client.js +1 -1
- package/dist/whisper/client.js.map +1 -1
- package/dist/whisper/index.cjs +4 -4
- package/dist/whisper/index.cjs.map +1 -1
- package/dist/whisper/index.js +2 -2
- package/dist/whisper/index.js.map +1 -1
- package/dist/workspace-setup/index.js +5 -1
- package/dist/workspace-setup/index.js.map +1 -1
- package/package.json +22 -3
- package/dist/deriveAll-C15OpM3K.cjs.map +0 -1
- package/dist/deriveAll-C6BYnpBL.js.map +0 -1
- package/dist/server-BhIdZgqu.js.map +0 -1
- package/dist/server-BjoKk2tR.cjs.map +0 -1
- /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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
75
|
-
|
|
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
|
-
-
|
|
83
|
-
|
|
84
|
-
|
|
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(
|
|
116
|
-
if (!res.ok) return; //
|
|
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
|
|
307
|
-
|
|
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(
|
|
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) {
|
package/assets/helps/feeds.md
CHANGED
|
@@ -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:
|
|
20
|
-
intelligent interface
|
|
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":
|
|
49
|
-
"headline":
|
|
50
|
-
"url":
|
|
51
|
-
"published": { "type": "date",
|
|
52
|
-
"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
|
|
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
|
-
|
|
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
|
-
/**
|
|
14
|
-
* dispatches on these; they live here (with the schema contract)
|
|
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
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
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-
|
|
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;
|