@north-light/crouter 0.3.276 → 0.3.277

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.
@@ -821,9 +821,9 @@ describe('typed validation issues', () => {
821
821
  describe('plugin help addenda', () => {
822
822
  const gate = (paths) => ({
823
823
  reservedCoreNames: RESERVED,
824
- coreCommandPaths: new Set(paths),
824
+ coreCommandPaths: () => Promise.resolve(new Set(paths)),
825
825
  });
826
- test('a helpAddenda entry survives validation and is collected attributed to its plugin', () => {
826
+ test('a helpAddenda entry survives validation and is collected attributed to its plugin', async () => {
827
827
  const m = commandsJson();
828
828
  m['helpAddenda'] = { cron: 'Gate on the device: exit 75 parks the occurrence.' };
829
829
  installPlugin(userRoot, 'northlight-fixture', { manifest: m });
@@ -833,12 +833,12 @@ describe('plugin help addenda', () => {
833
833
  assert.equal(v.issues.length, 0, JSON.stringify(v.issues));
834
834
  assert.deepEqual(v.manifest?.helpAddenda, { cron: 'Gate on the device: exit 75 parks the occurrence.' });
835
835
  // The help-render lookup: attributed, and empty for an unaddended path.
836
- assert.deepEqual(collectHelpAddenda('cron', gate(['cron', 'node']), emptyStart), [
836
+ assert.deepEqual(await collectHelpAddenda('cron', gate(['cron', 'node']), emptyStart), [
837
837
  { plugin: 'northlight-fixture', text: 'Gate on the device: exit 75 parks the occurrence.' },
838
838
  ]);
839
- assert.deepEqual(collectHelpAddenda('node', gate(['cron', 'node']), emptyStart), []);
839
+ assert.deepEqual(await collectHelpAddenda('node', gate(['cron', 'node']), emptyStart), []);
840
840
  });
841
- test('a key naming no core command path fails the strict gate and renders nothing, even beside a valid key', () => {
841
+ test('a key naming no core command path fails the strict gate and renders nothing, even beside a valid key', async () => {
842
842
  const m = commandsJson();
843
843
  m['helpAddenda'] = {
844
844
  cron: 'a valid target beside the typo',
@@ -860,7 +860,7 @@ describe('plugin help addenda', () => {
860
860
  // The render lookup reproduces the GATE verdict, not the compose one: the
861
861
  // rejected map contributes nothing — including its valid key — so render
862
862
  // can never show what the install report said was rejected.
863
- assert.deepEqual(collectHelpAddenda('cron', gate(['cron', 'cron add']), emptyStart), []);
863
+ assert.deepEqual(await collectHelpAddenda('cron', gate(['cron', 'cron add']), emptyStart), []);
864
864
  });
865
865
  test('an installed addendum renders through the real help path as an attributed block', async () => {
866
866
  const m = commandsJson();
@@ -29,8 +29,10 @@ export declare function validateCommandManifest(raw: unknown, options: {
29
29
  * (install, bundle parse, doctor/inspect reports) so a helpAddenda key
30
30
  * naming no core command fails there — and by the help-render lookup
31
31
  * (`collectHelpAddenda`), which reproduces the gate's verdict so render
32
- * and ingress report can never disagree. Omitted only on the tolerant
33
- * compose path, where mounting ignores addenda entirely. */
32
+ * and ingress report can never disagree. Omitted on the tolerant compose
33
+ * path, where mounting ignores addenda entirely, and on that lookup's
34
+ * claimant pass, which only needs to know whether any addendum targets the
35
+ * rendered path at all. */
34
36
  coreCommandPaths?: ReadonlySet<string>;
35
37
  /** A repository fragment attaches roots below an existing extensible branch. */
36
38
  extensionFragment?: boolean;
@@ -5,8 +5,17 @@ export interface HelpAddendum {
5
5
  /** Collect every enabled plugin's addendum targeting `commandPath` (the
6
6
  * space-joined walked path, e.g. "cron add"), in plugin-name order. `gate`
7
7
  * carries the strict-validation inputs the install/bundle gates use; the
8
- * help renderer already holds both (build-root exports them). */
8
+ * help renderer already holds both (build-root exports them).
9
+ *
10
+ * `coreCommandPaths` arrives as a thunk because producing that set walks EVERY
11
+ * core subtree — the whole command tree loaded on a path that renders one
12
+ * leaf's help. So the claimant pass below runs the SAME validator without it:
13
+ * the core path set can only reject an addendum, never introduce one, so a
14
+ * commandPath no manifest claims tolerantly claims none under the gate either,
15
+ * and the expensive input is never forced. Claimants then re-validate strictly,
16
+ * reproducing the gate's exact verdict — a manifest the gate rejects renders
17
+ * nothing, including its valid keys. */
9
18
  export declare function collectHelpAddenda(commandPath: string, gate: {
10
19
  reservedCoreNames: ReadonlySet<string>;
11
- coreCommandPaths: ReadonlySet<string>;
12
- }, startDir?: string, profileId?: string | null): HelpAddendum[];
20
+ coreCommandPaths: () => Promise<ReadonlySet<string>>;
21
+ }, startDir?: string, profileId?: string | null): Promise<HelpAddendum[]>;
@@ -15,13 +15,26 @@ import { effectiveCommandPlugins, validatePluginCommands } from './discovery.js'
15
15
  /** Collect every enabled plugin's addendum targeting `commandPath` (the
16
16
  * space-joined walked path, e.g. "cron add"), in plugin-name order. `gate`
17
17
  * carries the strict-validation inputs the install/bundle gates use; the
18
- * help renderer already holds both (build-root exports them). */
19
- export function collectHelpAddenda(commandPath, gate, startDir, profileId) {
18
+ * help renderer already holds both (build-root exports them).
19
+ *
20
+ * `coreCommandPaths` arrives as a thunk because producing that set walks EVERY
21
+ * core subtree — the whole command tree loaded on a path that renders one
22
+ * leaf's help. So the claimant pass below runs the SAME validator without it:
23
+ * the core path set can only reject an addendum, never introduce one, so a
24
+ * commandPath no manifest claims tolerantly claims none under the gate either,
25
+ * and the expensive input is never forced. Claimants then re-validate strictly,
26
+ * reproducing the gate's exact verdict — a manifest the gate rejects renders
27
+ * nothing, including its valid keys. */
28
+ export async function collectHelpAddenda(commandPath, gate, startDir, profileId) {
20
29
  if (commandPath.length === 0)
21
30
  return [];
31
+ const claimants = effectiveCommandPlugins(startDir, profileId).filter((plugin) => validatePluginCommands(plugin, gate.reservedCoreNames).manifest?.helpAddenda?.[commandPath] !== undefined);
32
+ if (claimants.length === 0)
33
+ return [];
34
+ const coreCommandPaths = await gate.coreCommandPaths();
22
35
  const out = [];
23
- for (const plugin of effectiveCommandPlugins(startDir, profileId)) {
24
- const text = validatePluginCommands(plugin, gate.reservedCoreNames, gate.coreCommandPaths).manifest?.helpAddenda?.[commandPath];
36
+ for (const plugin of claimants) {
37
+ const text = validatePluginCommands(plugin, gate.reservedCoreNames, coreCommandPaths).manifest?.helpAddenda?.[commandPath];
25
38
  if (text !== undefined)
26
39
  out.push({ plugin: plugin.name, text });
27
40
  }
@@ -209,9 +209,9 @@ async function renderNodeWithAddenda(node, path, focusedFlag) {
209
209
  import('./command-plugins/help-addenda.js'),
210
210
  import('../build-root.js'),
211
211
  ]);
212
- const addenda = collectHelpAddenda(path.join(' '), {
212
+ const addenda = await collectHelpAddenda(path.join(' '), {
213
213
  reservedCoreNames: new Set(SUBTREE_NAMES),
214
- coreCommandPaths: await coreCommandPaths(),
214
+ coreCommandPaths,
215
215
  });
216
216
  return body + addenda.map((a) => `\n\n<plugin-help plugin="${a.plugin}">\n${a.text}\n</plugin-help>`).join('');
217
217
  }
@@ -20,8 +20,9 @@ export declare function hasHumanViewer(nodeId: string): boolean;
20
20
  /** What parking an unattended node actually amounts to — the one rule both the
21
21
  * live-broker park clock and the lifecycle tick apply.
22
22
  *
23
- * `release` — something can still wake it, or a person is watching it, so
24
- * parking is a pause.
23
+ * `release` — something can still wake it, a person is watching it, or a
24
+ * fatal provider fault prevents its engine from completing the
25
+ * summary turn; parking is a pause.
25
26
  * `park` — a resident with nothing left to wake it has completed its
26
27
  * conversation, and the row becomes prunable history. The live
27
28
  * clock asks it for a summary turn first; this verdict is what
@@ -6,6 +6,7 @@ import { readOutputRequest } from '../../core/runtime/structured-output.js';
6
6
  import { focusOf } from '../../core/runtime/placement.js';
7
7
  import { hasAttachedBrokerViewers } from '../../core/runtime/broker/client-registry.js';
8
8
  import { fanDoctrineWake } from '../../core/runtime/close.js';
9
+ import { readFault } from '../../core/runtime/fault.js';
9
10
  import { transition } from '../../core/runtime/lifecycle.js';
10
11
  /** Durable work that requires a node to remain automatically wakeable. */
11
12
  export function hasLiveObligation(nodeId) {
@@ -50,6 +51,9 @@ export function parkEvent(nodeId, lifecycle) {
50
51
  return 'release';
51
52
  if (hasLiveObligation(nodeId))
52
53
  return 'release';
54
+ const fault = readFault(nodeId);
55
+ if (fault?.link === 'pi→provider' && fault.retry.disposition === 'fatal')
56
+ return 'release';
53
57
  if (lifecycle === 'resident')
54
58
  return hasUnseenWakeMail(nodeId) ? 'release' : 'park';
55
59
  if (readInboxSince(nodeId, readCursor(nodeId)).length > 0)
package/dist/types.d.ts CHANGED
@@ -311,11 +311,11 @@ export interface ScopeConfig {
311
311
  /** Fold every SETTLED tool call in the attach viewer down to its single call
312
312
  * line (`/fold-tools`, Alt+C → z → f). A tool still running is never folded —
313
313
  * it folds itself once it settles. Toggling it in a viewer writes back here,
314
- * so the choice carries into later chats and later viewers. Default false. */
314
+ * so the choice carries into later chats and later viewers. Default true. */
315
315
  fold_finished_tools: boolean;
316
316
  /** Replace completed folded tool-call groups with a broker-generated one-line
317
317
  * summary when one is available. Pending and failed groups stay as calls.
318
- * Default false. */
318
+ * Default true. */
319
319
  summarize_tool_calls: boolean;
320
320
  /** Show individual memory reads and edited files in a folded tool recap.
321
321
  * Off shows the compact activity-count line instead. Default true. */
package/dist/types.js CHANGED
@@ -97,8 +97,8 @@ export function defaultScopeConfig() {
97
97
  live_cycles: DEFAULT_LIVE_CYCLES,
98
98
  condensed_history: DEFAULT_CONDENSED_HISTORY,
99
99
  bash_tool_purpose: true,
100
- fold_finished_tools: false,
101
- summarize_tool_calls: false,
100
+ fold_finished_tools: true,
101
+ summarize_tool_calls: true,
102
102
  detailed_tool_recaps: true,
103
103
  keybindings: {},
104
104
  tmux_passthrough: [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/crouter",
3
- "version": "0.3.276",
3
+ "version": "0.3.277",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -70,6 +70,8 @@
70
70
  ],
71
71
  "scripts": {
72
72
  "build": "node scripts/build.mjs",
73
+ "activate": "npm run build && npm run install-runtime && crtr sys daemon restart",
74
+ "activate:pull": "git pull && npm install && npm run activate",
73
75
  "build:api": "tsc -p packages/crouter-api/tsconfig.json",
74
76
  "typecheck:pi-packages": "npx tsc -p src/builtin-pi-packages/pi-crtr-extensions/tsconfig.json",
75
77
  "test:build-output": "node scripts/build-concurrency-test.mjs",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/crouter",
3
- "version": "0.3.276",
3
+ "version": "0.3.277",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/crouter",
9
- "version": "0.3.276",
9
+ "version": "0.3.277",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "dependencies": {