@mulmoclaude/core 4.1.1 → 4.3.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.
- package/assets/helps/collection-skills.md +17 -6
- package/assets/helps/error-recovery.md +53 -0
- package/dist/collection/core/calendarGrid.d.ts +2 -2
- package/dist/collection/core/serverTime.d.ts +93 -0
- package/dist/collection/index.cjs +9 -2
- package/dist/collection/index.d.ts +1 -0
- package/dist/collection/index.js +3 -3
- package/dist/collection/registry/server/index.cjs +2 -2
- package/dist/collection/registry/server/index.js +2 -2
- package/dist/collection/server/index.cjs +4 -3
- package/dist/collection/server/index.js +4 -4
- package/dist/collection/server/manageTool.d.ts +24 -0
- package/dist/collection-watchers/index.cjs +4 -4
- package/dist/collection-watchers/index.js +4 -4
- package/dist/{discovery-B9EESjHP.cjs → discovery-Dl9nRa-m.cjs} +38 -20
- package/dist/discovery-Dl9nRa-m.cjs.map +1 -0
- package/dist/{discovery-D88x2DmW.js → discovery-P64fgtuD.js} +38 -20
- package/dist/discovery-P64fgtuD.js.map +1 -0
- package/dist/feeds/index.cjs +2 -2
- package/dist/feeds/index.js +2 -2
- package/dist/feeds/server/index.cjs +4 -4
- package/dist/feeds/server/index.js +4 -4
- package/dist/google/index.cjs +2 -2
- package/dist/google/index.js +2 -2
- package/dist/{ingestTypes-B312BwXg.js → ingestTypes-CIy1lHjf.js} +2 -2
- package/dist/{ingestTypes-B312BwXg.js.map → ingestTypes-CIy1lHjf.js.map} +1 -1
- package/dist/{ingestTypes-B4XfrUoq.cjs → ingestTypes-CaX7_s-K.cjs} +2 -2
- package/dist/{ingestTypes-B4XfrUoq.cjs.map → ingestTypes-CaX7_s-K.cjs.map} +1 -1
- package/dist/{itemId-DfTm08jm.js → itemId-BY2YPBkT.js} +200 -6
- package/dist/itemId-BY2YPBkT.js.map +1 -0
- package/dist/{itemId-Dzrr3vs5.cjs → itemId-CEQdK01z.cjs} +241 -5
- package/dist/itemId-CEQdK01z.cjs.map +1 -0
- package/dist/{promptSafety-CJ-sC1pX.js → promptSafety-Bp780ezk.js} +2 -2
- package/dist/{promptSafety-CJ-sC1pX.js.map → promptSafety-Bp780ezk.js.map} +1 -1
- package/dist/{promptSafety-CuczAN9d.cjs → promptSafety-C0BWDsfT.cjs} +2 -2
- package/dist/{promptSafety-CuczAN9d.cjs.map → promptSafety-C0BWDsfT.cjs.map} +1 -1
- package/dist/{server-L12n8We3.js → server-BvXO49Vj.js} +87 -17
- package/dist/server-BvXO49Vj.js.map +1 -0
- package/dist/{server-meS6kjBN.cjs → server-CF1qllGV.cjs} +92 -16
- package/dist/server-CF1qllGV.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/discovery-B9EESjHP.cjs.map +0 -1
- package/dist/discovery-D88x2DmW.js.map +0 -1
- package/dist/itemId-DfTm08jm.js.map +0 -1
- package/dist/itemId-Dzrr3vs5.cjs.map +0 -1
- package/dist/server-L12n8We3.js.map +0 -1
- package/dist/server-meS6kjBN.cjs.map +0 -1
|
@@ -2,9 +2,9 @@ const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
|
|
|
2
2
|
const require_dist = require("./dist-CsgSfWwR.cjs");
|
|
3
3
|
const require_root = require("./root-rPH6FGDT.cjs");
|
|
4
4
|
const require_relPath = require("./relPath-CTAjGdCL.cjs");
|
|
5
|
-
const require_itemId = require("./itemId-
|
|
6
|
-
const require_promptSafety = require("./promptSafety-
|
|
7
|
-
const require_discovery = require("./discovery-
|
|
5
|
+
const require_itemId = require("./itemId-CEQdK01z.cjs");
|
|
6
|
+
const require_promptSafety = require("./promptSafety-C0BWDsfT.cjs");
|
|
7
|
+
const require_discovery = require("./discovery-Dl9nRa-m.cjs");
|
|
8
8
|
const require_feeds_paths = require("./feeds/paths.cjs");
|
|
9
9
|
const require_skill_bridge_index = require("./skill-bridge/index.cjs");
|
|
10
10
|
let node_fs = require("node:fs");
|
|
@@ -395,13 +395,16 @@ function enforcedProblem(key, spec, value) {
|
|
|
395
395
|
* outside the canonical `YYYY-MM-DDTHH:MM[:SS]` shape (e.g. a `Z` suffix,
|
|
396
396
|
* which the day view can't place). `string`-backed types accept anything
|
|
397
397
|
* stringifiable; `ref` existence is out of scope. */
|
|
398
|
+
/** Named in the lint's own message, so the shape is shown rather than
|
|
399
|
+
* described. */
|
|
400
|
+
var CANONICAL_SERVER_TIME_EXAMPLE = "2026-08-15T01:45:54.605987654Z";
|
|
398
401
|
function strictTypeProblem(key, spec, value) {
|
|
399
402
|
switch (spec.type) {
|
|
400
403
|
case "number":
|
|
401
404
|
case "money": return Number.isFinite(require_promptSafety.coerceNumeric(value)) ? null : `'${key}' = '${String(value)}' is not numeric (a '${spec.type}' field stores a plain number)`;
|
|
402
405
|
case "boolean": return value === true || value === false ? null : `'${key}' = '${String(value)}' is not a boolean (store true or false, unquoted)`;
|
|
403
406
|
case "date": return require_itemId.parseIsoDate(value) !== null ? null : `'${key}' = '${String(value)}' is not a real YYYY-MM-DD date`;
|
|
404
|
-
case "datetime": return require_itemId.parseIsoDateTime(value) !== null ? null : `'${key}' = '${String(value)}' is not a YYYY-MM-DDTHH:MM datetime (seconds optional, no timezone suffix — the shape the calendar parses)`;
|
|
407
|
+
case "datetime": return require_itemId.parseIsoDateTime(value) !== null || require_itemId.isCanonicalServerTime(value) ? null : `'${key}' = '${String(value)}' is not a YYYY-MM-DDTHH:MM datetime (seconds optional, no timezone suffix — the shape the calendar parses), nor a server-stamped instant (${CANONICAL_SERVER_TIME_EXAMPLE})`;
|
|
405
408
|
default: return null;
|
|
406
409
|
}
|
|
407
410
|
}
|
|
@@ -1565,6 +1568,22 @@ var MAX_SCHEMA_ISSUES = 20;
|
|
|
1565
1568
|
* write that reported success would leave a half-filled collection nobody
|
|
1566
1569
|
* knows is half-filled. */
|
|
1567
1570
|
var MAX_PUT_ITEMS = 1e3;
|
|
1571
|
+
/** Cap the strict-tier findings one putItems call reports back. The rows were
|
|
1572
|
+
* WRITTEN, so this is a report and not a refusal — and a generated batch is
|
|
1573
|
+
* wrong the same way in every row, which means ten samples plus the total say
|
|
1574
|
+
* everything a thousand would. */
|
|
1575
|
+
var MAX_PUT_LINT = 10;
|
|
1576
|
+
/** What the `lint` block SAYS, once per call. A bare list of findings on rows the
|
|
1577
|
+
* call also reports as `written` reads as noise; the reason to act is that the
|
|
1578
|
+
* next reader of these rows is stricter than the write was.
|
|
1579
|
+
*
|
|
1580
|
+
* Like everything else the tool says about validation, this describes the tool
|
|
1581
|
+
* with its gate ON. Under `ablateValidation` nothing is checked — `rejected`
|
|
1582
|
+
* stays empty too — and the caveat is deliberately NOT written into the
|
|
1583
|
+
* agent-facing text: ablation exists to measure what a model does without the
|
|
1584
|
+
* guardrails, and a model told "unless ablation is on" is no longer without
|
|
1585
|
+
* them (CodeRabbit on #2925). */
|
|
1586
|
+
var PUT_LINT_NOTE = "These rows WERE written. The write gate refuses what would make a record unopenable — a missing required field, a value outside an enum, a mismatched primaryKey, a computed key, a colliding id under `create` — but it does not check the SHAPE of a value, so a wrong-shaped one is reported here rather than rejected. `getItems` surfaces the same finding as a `warning` (on a full listing, or when a requested id is missing), and publishing a shared app REFUSES the row outright. Fix the generator and rewrite them before writing the rest of the set.";
|
|
1568
1587
|
/** Refuse an `itemsFile` larger than this, from `stat` and before any read.
|
|
1569
1588
|
* The row cap alone cannot bound the work: the file has to be read and parsed
|
|
1570
1589
|
* WHOLE before there are rows to count, so a huge blob is paid for in full
|
|
@@ -1733,6 +1752,26 @@ async function mergeWithExisting(collection, store, record, itemId) {
|
|
|
1733
1752
|
...record
|
|
1734
1753
|
};
|
|
1735
1754
|
}
|
|
1755
|
+
/** The strict-tier finding on a row the write gate LET THROUGH, or nothing.
|
|
1756
|
+
*
|
|
1757
|
+
* The gate stays exactly where it was — `lint-not-lock` is why the strict tier
|
|
1758
|
+
* exists (see `../core/recordZ`), and turning these into rejections would make
|
|
1759
|
+
* a collection whose legacy rows predate the typed rules unwritable. What was
|
|
1760
|
+
* missing is that the row's author never HEARD about it: `getItems` reports the
|
|
1761
|
+
* same finding and publishing a shared app refuses exactly these rows, so the
|
|
1762
|
+
* one surface that stayed silent was the one that could still fix the generator
|
|
1763
|
+
* before it wrote the other 719 rows (mulmoterminal#1763).
|
|
1764
|
+
*
|
|
1765
|
+
* Run against the record as WRITTEN (`toWrite`, post-merge, post-defaults) —
|
|
1766
|
+
* what a later read will lint is what landed, not what the caller sent. */
|
|
1767
|
+
function lintOf(record, itemId, schema, deps) {
|
|
1768
|
+
if (deps.ablateValidation) return {};
|
|
1769
|
+
const problem = validateRecordObject(record, itemId, schema, "strict");
|
|
1770
|
+
return problem ? { lint: {
|
|
1771
|
+
id: require_promptSafety.defangForPrompt(itemId),
|
|
1772
|
+
problem: require_promptSafety.defangForPrompt(problem)
|
|
1773
|
+
} } : {};
|
|
1774
|
+
}
|
|
1736
1775
|
async function putOneItem(collection, store, write, record, mode, deps) {
|
|
1737
1776
|
const { schema } = collection;
|
|
1738
1777
|
const created = mode === "create" ? {
|
|
@@ -1758,7 +1797,10 @@ async function putOneItem(collection, store, write, record, mode, deps) {
|
|
|
1758
1797
|
if (invalid) return reject(itemId, invalid);
|
|
1759
1798
|
}
|
|
1760
1799
|
const result = await write(itemId, toWrite, { refuseOverwrite: mode === "create" });
|
|
1761
|
-
if (result.kind === "ok") return {
|
|
1800
|
+
if (result.kind === "ok") return {
|
|
1801
|
+
written: result.itemId,
|
|
1802
|
+
...lintOf(toWrite, result.itemId, schema, deps)
|
|
1803
|
+
};
|
|
1762
1804
|
if (result.kind === "invalid-id") return reject(itemId, `'${itemId}' is not a valid record id (letters/digits at the ends; -, _, or . inside; no '..' or path characters)`);
|
|
1763
1805
|
if (result.kind === "conflict") return reject(itemId, `'${itemId}' already exists — mode "create" refuses overwrite; use "upsert" to update it`);
|
|
1764
1806
|
return reject(itemId, "write refused: the collection's data dir escapes the workspace");
|
|
@@ -1931,23 +1973,51 @@ async function resolvePutRows(args, deps) {
|
|
|
1931
1973
|
if (rows.length > 1e3) return `manageCollection: refused — ${rows.length} rows is over the putItems limit of ${MAX_PUT_ITEMS}. Nothing was written; split them across several calls.`;
|
|
1932
1974
|
return rows;
|
|
1933
1975
|
}
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1976
|
+
/** The `lint` block of a putItems result, or nothing when every written row is
|
|
1977
|
+
* clean — absent rather than empty, so a clean call reads exactly as it always
|
|
1978
|
+
* did and the key's presence is itself the signal.
|
|
1979
|
+
*
|
|
1980
|
+
* `total` counts every flagged row and `rows` shows the first
|
|
1981
|
+
* `MAX_PUT_LINT` of them, so a capped report can never be read as a total. */
|
|
1982
|
+
function lintReport(lint) {
|
|
1983
|
+
if (lint.length === 0) return {};
|
|
1984
|
+
return { lint: {
|
|
1985
|
+
total: lint.length,
|
|
1986
|
+
note: PUT_LINT_NOTE,
|
|
1987
|
+
rows: lint.slice(0, 10)
|
|
1988
|
+
} };
|
|
1989
|
+
}
|
|
1990
|
+
/** Write the batch row by row, sorting each outcome into its own list. One row
|
|
1991
|
+
* at a time on purpose: a rejection is per row, so a bad row must not take the
|
|
1992
|
+
* batch with it. */
|
|
1993
|
+
async function putEachRow(collection, store, write, rows, mode, deps) {
|
|
1940
1994
|
const written = [];
|
|
1941
1995
|
const rejected = [];
|
|
1996
|
+
const lint = [];
|
|
1942
1997
|
for (const record of rows) {
|
|
1943
|
-
const outcome = await putOneItem(collection, store, write, record,
|
|
1998
|
+
const outcome = await putOneItem(collection, store, write, record, mode, deps);
|
|
1944
1999
|
if (outcome.written) written.push(outcome.written);
|
|
1945
2000
|
if (outcome.rejected) rejected.push(outcome.rejected);
|
|
2001
|
+
if (outcome.lint) lint.push(outcome.lint);
|
|
1946
2002
|
}
|
|
2003
|
+
return {
|
|
2004
|
+
written,
|
|
2005
|
+
rejected,
|
|
2006
|
+
lint
|
|
2007
|
+
};
|
|
2008
|
+
}
|
|
2009
|
+
async function handlePutItems(collection, args, deps) {
|
|
2010
|
+
const store = require_discovery.storeFor(collection, { workspaceRoot: deps.workspaceRoot });
|
|
2011
|
+
const { write } = store;
|
|
2012
|
+
if (!write) return `manageCollection: ${require_discovery.readOnlyRefusal(collection.slug)} (its records are the rows of '${collection.schema.dataSource?.path}'; edit that file to change the data).`;
|
|
2013
|
+
const rows = await resolvePutRows(args, deps);
|
|
2014
|
+
if (typeof rows === "string") return rows;
|
|
2015
|
+
const { written, rejected, lint } = await putEachRow(collection, store, write, rows, args.mode, deps);
|
|
1947
2016
|
return JSON.stringify({
|
|
1948
2017
|
collection: collection.slug,
|
|
1949
2018
|
written,
|
|
1950
|
-
rejected
|
|
2019
|
+
rejected,
|
|
2020
|
+
...lintReport(lint)
|
|
1951
2021
|
});
|
|
1952
2022
|
}
|
|
1953
2023
|
/** Delete records by id, through the store — so it works on any writable
|
|
@@ -2157,7 +2227,7 @@ async function handlePutSchema(slug, schemaArg, deps) {
|
|
|
2157
2227
|
written: true
|
|
2158
2228
|
});
|
|
2159
2229
|
}
|
|
2160
|
-
var MANAGE_COLLECTION_PROMPT = "Use `manageCollection` instead of raw Read/Write/Edit when working with a collection's records OR its schema (raw file I/O stays available as the escape hatch). Before authoring or changing a collection's `schema.json`, call `schemaDocs` to load the field/DSL reference — the default reply is the core authoring guide plus a table of contents; fetch advanced sections (actions, bells, calendar/kanban views, dataSource, storage) by passing their heading as `topic` rather than dumping `topic: \"all\"`. Then read with `getSchema` and write with `putSchema` — `putSchema` validates the whole schema before writing and returns actionable errors instead of silently failing discovery's validation. `getItems` is the only way to see computed values — `derived` fields (e.g. a portfolio's value), `toggle` projections, and `embed` records are host-computed and never present in the stored JSON files. On large collections pass `ids` and/or `fields` to keep the result small. For a question that spans collections (\"which clients have unpaid invoices?\"), start with `getOntology`: it lists every collection with its primaryKey, record count, and outbound `ref`/`embed` relations, so you know which collections to join before reading any records. `putItems`
|
|
2230
|
+
var MANAGE_COLLECTION_PROMPT = "Use `manageCollection` instead of raw Read/Write/Edit when working with a collection's records OR its schema (raw file I/O stays available as the escape hatch). Before authoring or changing a collection's `schema.json`, call `schemaDocs` to load the field/DSL reference — the default reply is the core authoring guide plus a table of contents; fetch advanced sections (actions, bells, calendar/kanban views, dataSource, storage) by passing their heading as `topic` rather than dumping `topic: \"all\"`. Then read with `getSchema` and write with `putSchema` — `putSchema` validates the whole schema before writing and returns actionable errors instead of silently failing discovery's validation. `getItems` is the only way to see computed values — `derived` fields (e.g. a portfolio's value), `toggle` projections, and `embed` records are host-computed and never present in the stored JSON files. On large collections pass `ids` and/or `fields` to keep the result small. For a question that spans collections (\"which clients have unpaid invoices?\"), start with `getOntology`: it lists every collection with its primaryKey, record count, and outbound `ref`/`embed` relations, so you know which collections to join before reading any records. `putItems` GATES every row on what would make the record unopenable — required fields, enum values, primaryKey = record id — and returns `{ written, rejected }`; fix each rejected row using its `problem` text and retry just those rows. Never include computed fields in a row you write. That gate does NOT check the SHAPE of a value: a `datetime` written as an instant (`2026-08-17T15:00:00.000Z`, what `toISOString()` produces) rather than as a wall clock, a `number` holding something that is not a number at all, a `date` that is not a real day are all WRITTEN, and come back in a `lint` block beside `written`. Read it — a full `getItems` listing warns about the same rows and publishing a shared app REFUSES them, so a silent `lint` is the only proof the values are right. Before generating a large set, read the exact stored form of each type in the `Field types` section of `schemaDocs` (`topic: \"Field types\"` fetches just that one), then write ONE batch and check that `lint` is absent. `datetime` in particular is a local wall clock (`YYYY-MM-DDTHH:MM`, no timezone suffix), so `new Date(...).toISOString()` is wrong twice over — the suffix, and the hours the conversion moved. The one `Z`-suffixed datetime the strict tier accepts is a shared app's server-stamped instant, written by the SERVER with nine fractional digits; you never produce that value. When the rows come from a script rather than from you (a generated schedule, an imported set, anything past a few dozen records), write them to a JSON file UNDER THE WORKSPACE and pass its absolute path as `itemsFile` instead of `items` — the host reads the file, so the rows never pass through your context. Do NOT hand-transcribe a generated file into `items`, and never drive the collection by spawning the MCP bridge yourself. To update a few fields of an existing record, use `mode: \"merge\"` with a partial row ({ id, <changed fields> }) — the default upsert replaces the WHOLE record, so a partial upsert would silently erase every optional field it omits. `deleteItems` removes records by id and returns `{ deleted, rejected }`; an id that doesn't exist comes back rejected rather than counted as deleted, so check `rejected` before reporting a deletion as done. Answer aggregation questions (counts, sums, averages, group-bys) with `queryItems` on ANY collection — on a dataSource (CSV) collection it scans the whole file (getItems is row-capped, so aggregates computed from its output can be silently wrong on large files); on a file-backed collection it aggregates the enriched records, so computed fields (derived/rollup/toggle) are queryable columns.";
|
|
2161
2231
|
/** Validate getItems' optional `ids`/`fields` args, then delegate. */
|
|
2162
2232
|
async function dispatchGetItems(collection, args, deps) {
|
|
2163
2233
|
const ids = optionalStringArray(args.ids, "ids");
|
|
@@ -2213,7 +2283,7 @@ async function dispatchManageCollection(deps, args) {
|
|
|
2213
2283
|
}
|
|
2214
2284
|
var MANAGE_COLLECTION_DEFINITION = {
|
|
2215
2285
|
name: "manageCollection",
|
|
2216
|
-
description: "Read and write a schema-driven collection through the host — both its records and its structure. getItems returns records WITH computed values (derived formulas, toggles, embeds) the stored JSON files don't contain; putItems
|
|
2286
|
+
description: "Read and write a schema-driven collection through the host — both its records and its structure. getItems returns records WITH computed values (derived formulas, toggles, embeds) the stored JSON files don't contain; putItems gates each row on required/enum/primaryKey before writing and lints the value shapes it does not gate; deleteItems removes records by id. getOntology maps the whole workspace: every collection with its record count and outbound ref/embed relations — call it first for cross-collection questions. schemaDocs returns the collection-authoring reference — the core guide plus a table of contents by default; pass `topic` for a specific section. getSchema/putSchema read and validate-then-write the collection's schema.json. Prefer it over raw file I/O on collections.",
|
|
2217
2287
|
inputSchema: {
|
|
2218
2288
|
type: "object",
|
|
2219
2289
|
properties: {
|
|
@@ -2300,6 +2370,12 @@ Object.defineProperty(exports, "MAX_PUT_ITEMS", {
|
|
|
2300
2370
|
return MAX_PUT_ITEMS;
|
|
2301
2371
|
}
|
|
2302
2372
|
});
|
|
2373
|
+
Object.defineProperty(exports, "MAX_PUT_LINT", {
|
|
2374
|
+
enumerable: true,
|
|
2375
|
+
get: function() {
|
|
2376
|
+
return MAX_PUT_LINT;
|
|
2377
|
+
}
|
|
2378
|
+
});
|
|
2303
2379
|
Object.defineProperty(exports, "MAX_RECORD_ISSUES", {
|
|
2304
2380
|
enumerable: true,
|
|
2305
2381
|
get: function() {
|
|
@@ -2517,4 +2593,4 @@ Object.defineProperty(exports, "validateRecordObject", {
|
|
|
2517
2593
|
}
|
|
2518
2594
|
});
|
|
2519
2595
|
|
|
2520
|
-
//# sourceMappingURL=server-
|
|
2596
|
+
//# sourceMappingURL=server-CF1qllGV.cjs.map
|