@north-light/crouter 0.3.272 → 0.3.273

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.
@@ -64,7 +64,7 @@ export const browseLeaf = defineLeaf({
64
64
  profileId,
65
65
  source,
66
66
  viewedNodeIds: async () => new Set((await cliClient().listFocuses()).map((focus) => focus.node_id)),
67
- closeNode: (id) => cliClient().closeNode(id, { finish: true, caller_pid: process.ppid }).then((r) => ({ closed: r.closed })),
67
+ closeNode: (id) => cliClient().closeNode(id, process.ppid > 0 ? { finish: true, caller_pid: process.ppid } : { finish: true }).then((r) => ({ closed: r.closed })),
68
68
  });
69
69
  },
70
70
  });
@@ -63,12 +63,11 @@ function parseRunTimeout(raw) {
63
63
  }
64
64
  return seconds;
65
65
  }
66
- /** The calling agent's profile, which is the scope every cron verb speaks in:
67
- * a profile-scoped cron lists and cancels within its profile, a global one is
68
- * canvas-home-wide. Outside a profile there is no scope to narrow to, so the
69
- * caller sees the whole canvas home. */
70
- function scopeQuery() {
71
- const p = envProfileId();
66
+ /** The profile scope for a cron route: an explicit selector overrides the
67
+ * calling agent's profile. Outside a profile there is no scope to narrow to,
68
+ * so the caller sees the whole canvas home. */
69
+ function scopeQuery(profile) {
70
+ const p = typeof profile === 'string' && profile.trim() !== '' ? canonicalProfileId(profile) : envProfileId();
72
71
  return p !== undefined && p.trim() !== '' ? { profile: p } : undefined;
73
72
  }
74
73
  /** Resolve the chosen-else-inherited cwd, and refuse one that is not a
@@ -270,7 +269,7 @@ const addLeaf = defineLeaf({
270
269
  render: (r) => {
271
270
  const recur = String(r['recur']);
272
271
  const cadence = recur === 'none' ? 'one-shot (row deleted after the run)' : recur;
273
- return `Cron armed — ${String(r['cron_id'])}.\n\n- next fire: ${String(r['fire_at'])}\n- cadence: ${cadence}\n\nIt runs daemon-side; nothing more to do here. Inspect it with \`crtr cron show ${String(r['cron_id'])}\`; cancel any time with \`crtr cron cancel ${String(r['cron_id'])}\`.`;
272
+ return `Cron armed — ${String(r['cron_id'])}.\n\n- next fire: ${String(r['fire_at'])}\n- cadence: ${cadence}\n\nIt runs daemon-side; nothing more to do here. Inspect it with \`crtr cron show ${String(r['cron_id'])}\`; cancel with \`crtr cron cancel ${String(r['cron_id'])}\` from its profile, or pass \`--profile <profile>\` from another profile.`;
274
273
  },
275
274
  });
276
275
  // cron list
@@ -494,9 +493,11 @@ const cancelLeaf = defineLeaf({
494
493
  summary: 'remove one cron',
495
494
  params: [
496
495
  { kind: 'positional', name: 'cron-id', required: true, constraint: 'The cron to remove (from `cron list`, or $CRTR_CRON_ID inside a run).' },
496
+ { kind: 'flag', name: 'profile', type: 'string', required: false, constraint: 'Profile scope to cancel within. Defaults to the calling node\'s profile; pass the profile id or manifest name for a cron armed under another profile.' },
497
497
  ],
498
498
  output: [],
499
499
  outputKind: 'object',
500
+ dynamicState: () => profilesStateBlock(),
500
501
  effects: ['Deletes the cron row and its run history; kills any in-flight run. A no-op if already gone.'],
501
502
  },
502
503
  run: async (input) => {
@@ -504,7 +505,7 @@ const cancelLeaf = defineLeaf({
504
505
  const runId = process.env['CRTR_RUN_ID'];
505
506
  await cliClient()
506
507
  .cancelCron(id, {
507
- ...scopeQuery(),
508
+ ...scopeQuery(input['profile']),
508
509
  ...(runId === undefined || runId === '' ? {} : { run_id: runId }),
509
510
  })
510
511
  .catch((err) => rethrowAsCliError(err, 'Check the id against `crtr cron list`.'));
@@ -100,7 +100,7 @@ const nodeClose = defineLeaf({
100
100
  throw new InputError({ error: 'no_node', message: 'no node found in this pane to close', next: 'Pass --node <id>, or run from inside the agent\'s pane.' });
101
101
  }
102
102
  const res = await cliClient()
103
- .closeNode(id, { caller_pid: process.ppid })
103
+ .closeNode(id, process.ppid > 0 ? { caller_pid: process.ppid } : {})
104
104
  .catch((err) => rethrowAsCliError(err, 'List nodes with `crtr node inspect list`.'));
105
105
  return { node_id: res.root, count: res.closed.length, closed_ids: res.closed, spared: res.spared };
106
106
  },
@@ -21,7 +21,7 @@ const NEXT = 'Run `crtr pkg plugin show` or `crtr sys doctor` to inspect the plu
21
21
  * Returns null if valid; otherwise returns a { field, receivedType } error.
22
22
  * Transport-neutral: used by exec and HTTP transport adapters. */
23
23
  export function validateDeclaredResult(output, result) {
24
- return validateFields(output, result, false);
24
+ return validateFields(output, result, true);
25
25
  }
26
26
  /** Core leaf declarations may explicitly require a nullable field. */
27
27
  export function validateCoreDeclaredResult(output, result) {
@@ -88,7 +88,10 @@ export function buildContextBearingsFromState(state, projectContext, situational
88
88
  }
89
89
  export function buildForkBearingsFromState(state, situational = '') {
90
90
  const { node, fork_source: source } = state;
91
- const bearings = ['<crtr-bearings>', identity(state), '', 'The source node’s earlier task, graph position, lifecycle, children, schedules, worktree, situational context, and context-directory path do not carry over. Shared conversation, project, and operating context remain reference material.', '', '## Your node now', `Lifecycle: ${node.lifecycle}.`];
91
+ const sourceMandate = source?.status === 'active' || source?.status === 'idle'
92
+ ? `Source node ${source.node_id} ("${fullName(source)}") is still live and still holds the mandate from the copied conversation; do not resume that work here.`
93
+ : 'The source node’s earlier graph position, lifecycle, children, schedules, worktree, situational context, and context-directory path do not carry over. Shared conversation, project, and operating context remain reference material.';
94
+ const bearings = ['<crtr-bearings>', identity(state), '', sourceMandate, '', '## Your node now', `Lifecycle: ${node.lifecycle}.`];
92
95
  const map = graphMap(state);
93
96
  bearings.push(...(map === '' ? ['Canvas: independent root with no parent or inherited graph edges.'] : ['', map]));
94
97
  bearings.push(`Context directory: \`${contextDir(node.node_id)}\` (\`$CRTR_CONTEXT_DIR\`). Use this path in place of every source-node context path above.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/crouter",
3
- "version": "0.3.272",
3
+ "version": "0.3.273",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/crouter",
3
- "version": "0.3.272",
3
+ "version": "0.3.273",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/crouter",
9
- "version": "0.3.272",
9
+ "version": "0.3.273",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "dependencies": {