@north-light/crouter 0.3.163 → 0.3.164

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.
@@ -70,7 +70,7 @@ export function lintBodyLength(fm, body, docName) {
70
70
  const sys = fm['system-prompt-visibility'];
71
71
  const file = fm['file-read-visibility'];
72
72
  const words = countBodyWords(body);
73
- const remedy = 'Keep the load-bearing core here and split the depth into [[linked]] reference docs saved at `none` visibility on both axes (the link is how they are found, so they cost nothing until followed). Keep it whole — `lint-ignore: length` in the frontmatter — only when every reader who surfaces this doc genuinely benefits from reading 100% of it, or it is one indivisible body of knowledge; then splitting just adds hops.';
73
+ const remedy = 'Keep the load-bearing core here and split the depth into [[linked]] reference docs saved at `none` visibility on both axes (the link is how they are found, so they cost nothing until followed). Split by subject: each leaf covers a different subject a task might need on its own; never split off “further evidence”, examples, or references — a references leaf is never followed, so supporting material stays next to the point it supports or gets cut. Keep it whole — `lint-ignore: length` in the frontmatter — only when every reader who surfaces this doc genuinely benefits from reading 100% of it, or it is one indivisible body of knowledge; then splitting just adds hops.';
74
74
  if (sys === 'content' && words > SYSTEM_CONTENT_MAX_WORDS) {
75
75
  return `body is ${words} words but system-prompt-visibility: content inlines every word into every agent's system prompt at boot — capped at ${SYSTEM_CONTENT_MAX_WORDS} words (system-prompt preview gets ${SYSTEM_PREVIEW_MAX_WORDS}; name/none are never capped on that axis). ${remedy}`;
76
76
  }
@@ -23,7 +23,7 @@ export const writeLeaf = defineLeaf({
23
23
  'Write the routing line (--when-and-why-to-read) first, before storing anything: "When <circumstance>, this <kind> should be read because <broader downstream payoff>." WHY is the READER’s payoff — the consequence they secure for the task in front of them by spending the read — never what the document says, its rule, or why it should be obeyed. The trap most authors fall into is the DISGUISED restatement: a because-clause that reads like a benefit but is just the doc’s own thesis reworded as an outcome. It still fails. The test: if you could derive the because-clause by paraphrasing the doc’s advice, it is a restatement, not a payoff — a real payoff names a consequence in the reader’s world that the document itself never asserts. Bad (naked restatement): "because only genuine first principles belong in taste memory." Bad (restatement in benefit’s clothing): "because keeping the test loop fast and free of speculative tests protects the development pace" — that is the doc’s rule in outcome costume, derivable straight from its advice. Good: "because identifying throughlines in user taste lets future decisions be made faster and with less re-litigation." The stranger test: someone mid-task who has NOT read the doc must be able to decide from this one line alone whether the read is worth it. If you cannot name the concrete situation that triggers it, you do not yet understand the memory — ask the user one sharp question instead of improvising.\n\n' +
24
24
  'Gate and read-when share the same predicate language: a field map is AND-ed across fields; dotted fields resolve nested values; field matchers may be scalar, array, or object. Scalar matchers do exact equality, with arrays matching any element. Array matchers do membership or intersection. Object matchers accept `eq`, `ne`, `in`, `nin`, `exists`, `contains`, `containsAll`, `containsAny`, `matches`, `imatches`, `gt`, `gte`, `lt`, and `lte`. Combinators are `all`, `any`, and `not`; sibling field matchers next to them are AND-ed in. An empty condition is inert. An unknown op never matches.\n\n' +
25
25
  'Link related docs with `[[canonical/name]]`. A doc link in any body is a first-class cross-reference to another memory document by its exact canonical name — the same identifier `crtr memory read` takes (a directory INDEX is linked by its bare directory name). Links are pointers, never transclusion: the linked body is loaded only when a reader follows it, so a link costs nothing until needed. `crtr memory lint` fails on a link that resolves to no document, and `crtr memory read` lists a doc’s resolvable links alongside its body. There is no alias or label form.\n\n' +
26
- 'When a doc grows long or information-rich, nest it into a graph instead of letting it become a scroll. The main doc at the topic’s path keeps the high-level, most load-bearing information, most important first; depth splits into reference docs under the topic’s directory (`area/topic/...`), each pointed at with a `[[link]]` as further reading. The main doc is the entry point a reader can act from alone; a reference leaf is loaded only when the task needs that depth. Save reference leaves at `none` visibility on both axes — the link from the main doc is how they are found, so any higher rung just double-charges every boot or read for depth the graph already routes. This is the same layering the visibility rungs apply to boot cost, applied to the body itself. Keep every doc as short as its job allows; `crtr memory lint` caps body length by rung and its findings carry the split guidance.\n\n' +
26
+ 'When a doc grows long or information-rich, nest it into a graph instead of letting it become a scroll. The main doc at the topic’s path keeps the high-level, most load-bearing information, most important first; depth splits into reference docs under the topic’s directory (`area/topic/...`), each pointed at with a `[[link]]`. Split by subject: a leaf earns its link by covering a different subject a task might need on its own; a leaf of offloaded “further evidence”, examples, or references is never followed, so supporting material either sits in the main doc next to the point it supports or gets cut. The main doc is the entry point a reader can act from alone; a reference leaf is loaded only when the task needs that depth. Save reference leaves at `none` visibility on both axes — the link from the main doc is how they are found, so any higher rung just double-charges every boot or read for depth the graph already routes. This is the same layering the visibility rungs apply to boot cost, applied to the body itself. Keep every doc as short as its job allows; `crtr memory lint` caps body length by rung and its findings carry the split guidance.\n\n' +
27
27
  'Find before write. Prefer slightly expanding an existing document, nesting genuinely separate depth under its topic, and updating the existing `when-and-why-to-read` (plus its INDEX router when present) over creating another similar memory. A new document earns its own identity only when it has a distinct read trigger and a coherent body that would make the existing document harder to route or use. Group related docs with path names (area/topic). Do not store what is already recorded or what only matters to this conversation. Body is for current truth, not history. Provenance is automatic on create and preserved on update. Run `crtr memory lint` — the frontmatter validator — after authoring.\n\n' +
28
28
  '--rationale is the gap this doc exists to close — the observed agent failure that prompted it, captured from user signal (a correction, a mistake you watched happen) rather than inferred from the doc’s own content. The bar: if the rationale is guessable from reading the doc, it is not the real one — a guessable gap is one agents do not actually fall into, so a merely-plausible-sounding reason is not worth recording. It is maintainer-facing only and NEVER ships in a delivered surface (boot render, on-read injection, `memory read` content) — it lives in frontmatter, visible only via `memory read --frontmatter`. Omit it when you have no such gap to record; omitting the flag on an update always preserves whatever rationale already exists.',
29
29
  params: [
@@ -57,8 +57,11 @@ export function registerCanvasToolGuide(pi) {
57
57
  // The guide remains available in this broker even when its cache cannot persist.
58
58
  }
59
59
  }
60
- catch {
60
+ catch (err) {
61
61
  // Keep the last successful guide and never delay a turn for a failed capture.
62
+ // Say so once in the broker log: a node silently missing its whole tool guide
63
+ // is otherwise indistinguishable from one that has it.
64
+ console.error('[canvas-tool-guide] crtr -h capture failed:', err);
62
65
  }
63
66
  }
64
67
  // Node startup is the only point a capture may wait. Warm spares pay it before
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/crouter",
3
- "version": "0.3.163",
3
+ "version": "0.3.164",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -107,7 +107,7 @@
107
107
  },
108
108
  "license": "MIT",
109
109
  "dependencies": {
110
- "@crouton-kit/humanloop": "0.4.20",
110
+ "@crouton-kit/humanloop": "0.4.21",
111
111
  "@earendil-works/pi-agent-core": "0.83.0",
112
112
  "@earendil-works/pi-ai": "0.83.0",
113
113
  "@earendil-works/pi-coding-agent": "0.83.0",
package/runtime.lock.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@north-light/crouter",
3
- "version": "0.3.163",
3
+ "version": "0.3.164",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/crouter",
9
- "version": "0.3.163",
9
+ "version": "0.3.164",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "dependencies": {
13
- "@crouton-kit/humanloop": "0.4.20",
13
+ "@crouton-kit/humanloop": "0.4.21",
14
14
  "@earendil-works/pi-agent-core": "0.83.0",
15
15
  "@earendil-works/pi-ai": "0.83.0",
16
16
  "@earendil-works/pi-coding-agent": "0.83.0",
@@ -2073,9 +2073,9 @@
2073
2073
  }
2074
2074
  },
2075
2075
  "node_modules/@crouton-kit/humanloop": {
2076
- "version": "0.4.20",
2077
- "resolved": "https://registry.npmjs.org/@crouton-kit/humanloop/-/humanloop-0.4.20.tgz",
2078
- "integrity": "sha512-4MBEVzQegNQQVCUktgsCyk4civYvhpCYJfY6nPTuOjNMpz+prT0sXfjNntcZS14cG0Hj/Igzzc010VpgVnN5WQ==",
2076
+ "version": "0.4.21",
2077
+ "resolved": "https://registry.npmjs.org/@crouton-kit/humanloop/-/humanloop-0.4.21.tgz",
2078
+ "integrity": "sha512-j/8dVAhl2R4dWvykSjC+yCEEb/n7P+P1FZLEFWD1pVB24tubYVmA3vJSr359nOO8esmJ+h/+S8Oef8eGZ6n6tA==",
2079
2079
  "hasInstallScript": true,
2080
2080
  "workspaces": [
2081
2081
  "web"