@crouton-kit/crouter 0.3.65 → 0.3.67

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.
Files changed (83) hide show
  1. package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -1
  2. package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +2 -0
  3. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +24 -12
  4. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +41 -16
  5. package/dist/clients/attach/__tests__/attach-chrome-remote.test.d.ts +1 -0
  6. package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +371 -0
  7. package/dist/clients/attach/__tests__/attach-remote-readonly.test.d.ts +1 -0
  8. package/dist/clients/attach/__tests__/attach-remote-readonly.test.js +191 -0
  9. package/dist/clients/attach/__tests__/transport-relay.test.js +38 -1
  10. package/dist/clients/attach/attach-cmd.d.ts +30 -2
  11. package/dist/clients/attach/attach-cmd.js +632 -632
  12. package/dist/clients/attach/canvas-panels.js +9 -3
  13. package/dist/clients/attach/graph-overlay.d.ts +5 -0
  14. package/dist/clients/attach/graph-overlay.js +65 -11
  15. package/dist/clients/attach/input-controller.d.ts +9 -0
  16. package/dist/clients/attach/input-controller.js +47 -8
  17. package/dist/clients/attach/slash-commands.d.ts +11 -4
  18. package/dist/clients/attach/slash-commands.js +44 -9
  19. package/dist/clients/attach/transport-relay.d.ts +5 -5
  20. package/dist/clients/attach/transport-relay.js +3 -30
  21. package/dist/commands/__tests__/canvas-config.test.d.ts +1 -0
  22. package/dist/commands/__tests__/canvas-config.test.js +107 -0
  23. package/dist/commands/canvas-browse.js +26 -1
  24. package/dist/commands/canvas-config.d.ts +2 -0
  25. package/dist/commands/canvas-config.js +210 -0
  26. package/dist/commands/canvas-use.d.ts +1 -0
  27. package/dist/commands/canvas-use.js +63 -0
  28. package/dist/commands/canvas.js +4 -2
  29. package/dist/commands/node-lifecycle-revive.js +9 -2
  30. package/dist/commands/node.js +41 -11
  31. package/dist/core/__tests__/on-read-crouter-home-fence.test.d.ts +1 -0
  32. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +67 -0
  33. package/dist/core/__tests__/remote-canvas-target.test.d.ts +1 -0
  34. package/dist/core/__tests__/remote-canvas-target.test.js +87 -0
  35. package/dist/core/__tests__/secrets.test.d.ts +1 -0
  36. package/dist/core/__tests__/secrets.test.js +55 -0
  37. package/dist/core/canvas/__tests__/remote-canvas-source.test.d.ts +1 -0
  38. package/dist/core/canvas/__tests__/remote-canvas-source.test.js +371 -0
  39. package/dist/core/canvas/__tests__/remote-event-stream.test.d.ts +1 -0
  40. package/dist/core/canvas/__tests__/remote-event-stream.test.js +144 -0
  41. package/dist/core/canvas/__tests__/render-remote.test.d.ts +1 -0
  42. package/dist/core/canvas/__tests__/render-remote.test.js +221 -0
  43. package/dist/core/canvas/__tests__/source-resolve.test.d.ts +1 -0
  44. package/dist/core/canvas/__tests__/source-resolve.test.js +77 -0
  45. package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.d.ts +1 -0
  46. package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.js +117 -0
  47. package/dist/core/canvas/browse/app.d.ts +14 -0
  48. package/dist/core/canvas/browse/app.js +97 -15
  49. package/dist/core/canvas/browse/rebuild-coalescer.d.ts +7 -0
  50. package/dist/core/canvas/browse/rebuild-coalescer.js +61 -0
  51. package/dist/core/canvas/browse/render.d.ts +4 -0
  52. package/dist/core/canvas/browse/render.js +3 -1
  53. package/dist/core/canvas/nav-model.d.ts +19 -10
  54. package/dist/core/canvas/nav-model.js +30 -12
  55. package/dist/core/canvas/pid.d.ts +71 -13
  56. package/dist/core/canvas/pid.js +73 -29
  57. package/dist/core/canvas/remote-canvas-source.d.ts +62 -0
  58. package/dist/core/canvas/remote-canvas-source.js +222 -0
  59. package/dist/core/canvas/remote-event-stream.d.ts +24 -0
  60. package/dist/core/canvas/remote-event-stream.js +94 -0
  61. package/dist/core/canvas/render.d.ts +13 -1
  62. package/dist/core/canvas/render.js +56 -37
  63. package/dist/core/canvas/source.d.ts +9 -0
  64. package/dist/core/canvas/source.js +15 -0
  65. package/dist/core/config.js +4 -3
  66. package/dist/core/runtime/__tests__/node-env.test.d.ts +1 -0
  67. package/dist/core/runtime/__tests__/node-env.test.js +91 -0
  68. package/dist/core/runtime/nodes.js +10 -0
  69. package/dist/core/runtime/revive-all.js +8 -2
  70. package/dist/core/runtime/revive.js +18 -8
  71. package/dist/core/secrets.d.ts +25 -0
  72. package/dist/core/secrets.js +55 -0
  73. package/dist/core/substrate/on-read.js +13 -2
  74. package/dist/core/view/__tests__/transport-remote.test.d.ts +1 -0
  75. package/dist/core/view/__tests__/transport-remote.test.js +95 -0
  76. package/dist/core/view/remote-canvas-target.d.ts +2 -1
  77. package/dist/core/view/remote-canvas-target.js +12 -7
  78. package/dist/core/view/transport-remote.d.ts +2 -0
  79. package/dist/core/view/transport-remote.js +53 -0
  80. package/dist/daemon/crtrd.js +18 -8
  81. package/dist/types.d.ts +19 -11
  82. package/dist/types.js +1 -1
  83. package/package.json +1 -1
@@ -0,0 +1,95 @@
1
+ // transport-remote.test.ts — `createRemoteExec` (`../transport-remote.ts`), the
2
+ // Node-side POST transport `RemoteCanvasSource` shells every remote read
3
+ // through. Covers the one thing a bare `fetch(...)` with no
4
+ // AbortController/timeout doesn't give you for free: a hung endpoint fails
5
+ // CLOSED within a bound instead of wedging the caller forever. WITHOUT the
6
+ // timeout, the "stalled endpoint" test below would hang past its own
7
+ // assertion deadline (the promise the transport returns never settles).
8
+ import { test } from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import { createServer } from 'node:http';
11
+ import { createRemoteExec } from '../transport-remote.js';
12
+ function startStalledServer() {
13
+ return new Promise((resolve) => {
14
+ // Accept the connection but never write a response — the request just
15
+ // hangs, exactly what a wedged in-guest `/__crtr/source` handler looks
16
+ // like from the caller's side.
17
+ const server = createServer(() => { });
18
+ server.listen(0, '127.0.0.1', () => {
19
+ const addr = server.address();
20
+ const port = typeof addr === 'object' && addr !== null ? addr.port : 0;
21
+ resolve({
22
+ url: `http://127.0.0.1:${port}`,
23
+ close: () => new Promise((res) => server.close(() => res())),
24
+ });
25
+ });
26
+ });
27
+ }
28
+ test('a stalled endpoint returns a timeout failure within the configured bound instead of hanging forever', async () => {
29
+ const server = await startStalledServer();
30
+ const exec = createRemoteExec(server.url, 'tok', 100); // small timeout for the test
31
+ const started = Date.now();
32
+ // Race against a generous outer deadline: if the transport doesn't
33
+ // self-bound, this whole test times out instead of asserting cleanly —
34
+ // still proves the point, but assert explicitly for a clear failure message.
35
+ const result = await Promise.race([
36
+ exec('crtr', ['--json', 'node', 'inspect', 'list']),
37
+ new Promise((_resolve, reject) => setTimeout(() => reject(new Error('transport did not resolve within the outer deadline — no timeout is wired')), 2000)),
38
+ ]);
39
+ const elapsed = Date.now() - started;
40
+ assert.equal(result.ok, false);
41
+ assert.match(result.stderr, /timeout/i);
42
+ assert.ok(elapsed < 1000, `expected the timeout to fire near the configured 100ms bound, took ${elapsed}ms`);
43
+ await server.close();
44
+ });
45
+ test('a normal fast response is unaffected by the timeout wiring', async () => {
46
+ const server = createServer((req, res) => {
47
+ const chunks = [];
48
+ req.on('data', (c) => chunks.push(c));
49
+ req.on('end', () => {
50
+ res.writeHead(200, { 'content-type': 'application/json' });
51
+ res.end(JSON.stringify({ ok: true, exitCode: 0, stdout: JSON.stringify({ nodes: [] }), stderr: '' }));
52
+ });
53
+ });
54
+ await new Promise((r) => server.listen(0, '127.0.0.1', r));
55
+ const addr = server.address();
56
+ const port = typeof addr === 'object' && addr !== null ? addr.port : 0;
57
+ const exec = createRemoteExec(`http://127.0.0.1:${port}`, 'tok', 5000);
58
+ const result = await exec('crtr', ['--json', 'node', 'inspect', 'list']);
59
+ assert.equal(result.ok, true);
60
+ assert.equal(result.stdout, JSON.stringify({ nodes: [] }));
61
+ await new Promise((r) => server.close(() => r()));
62
+ });
63
+ // --- Phase 3 review Minor 1: no token bytes may ever reach returned stderr ---
64
+ test('an unreachable endpoint returns a flat generic network-error message, never the raw exception text', async () => {
65
+ const exec = createRemoteExec('http://127.0.0.1:1', 'valid-token-123');
66
+ const result = await exec('crtr', ['--json', 'node', 'inspect', 'list']);
67
+ assert.equal(result.ok, false);
68
+ assert.equal(result.stderr, 'remote source: network error');
69
+ });
70
+ test('a malformed token (contains a newline) is rejected up front with a generic message, never reaching fetch/Headers', async () => {
71
+ const exec = createRemoteExec('http://127.0.0.1:1', 'bad\ntoken');
72
+ const result = await exec('crtr', ['--json', 'node', 'inspect', 'list']);
73
+ assert.equal(result.ok, false);
74
+ assert.equal(result.stderr, 'remote source: invalid stored relay token');
75
+ assert.ok(!result.stderr.includes('bad\ntoken'));
76
+ assert.ok(!result.stderr.includes('bad'));
77
+ });
78
+ test('a valid token containing typical bearer-token punctuation (-, _, .) still round-trips against a real server', async () => {
79
+ const server = createServer((req, res) => {
80
+ const chunks = [];
81
+ req.on('data', (c) => chunks.push(c));
82
+ req.on('end', () => {
83
+ res.writeHead(200, { 'content-type': 'application/json' });
84
+ res.end(JSON.stringify({ ok: true, exitCode: 0, stdout: JSON.stringify({ nodes: [] }), stderr: '' }));
85
+ });
86
+ });
87
+ await new Promise((r) => server.listen(0, '127.0.0.1', r));
88
+ const addr = server.address();
89
+ const port = typeof addr === 'object' && addr !== null ? addr.port : 0;
90
+ const exec = createRemoteExec(`http://127.0.0.1:${port}`, 'a1b2c3-real-token_ABC.789', 5000);
91
+ const result = await exec('crtr', ['--json', 'node', 'inspect', 'list']);
92
+ assert.equal(result.ok, true);
93
+ assert.equal(result.stdout, JSON.stringify({ nodes: [] }));
94
+ await new Promise((r) => server.close(() => r()));
95
+ });
@@ -1,3 +1,4 @@
1
- import type { Scope } from '../../types.js';
1
+ import type { Scope, RemoteCanvasTarget } from '../../types.js';
2
2
  import type { RelayTarget } from '../../clients/attach/transport-relay.js';
3
+ export declare function getRemoteCanvasTarget(name: string, scope?: Scope): RemoteCanvasTarget | undefined;
3
4
  export declare function resolveRemoteCanvasTarget(name: string, scope?: Scope): RelayTarget;
@@ -2,12 +2,17 @@
2
2
  // config entry (see `RemoteCanvasTarget`, `src/types.ts`) into a `RelayTarget`
3
3
  // ready for `RelayTransport` (`src/clients/attach/transport-relay.ts`).
4
4
  //
5
- // The raw bearer token NEVER lives in config or argv — only the name of an
6
- // environment variable holding it (`relayTokenEnv`), read here at connect
7
- // time. This is the Phase 1 interim contract; Phase 3 owns the real
8
- // secret-store-backed `crtr canvas config`/`crtr canvas use` CLI.
5
+ // The raw bearer token NEVER lives in config.json — only a ref
6
+ // (`relayTokenRef`) into the 0600 secrets store (`src/core/secrets.ts`),
7
+ // resolved here at connect time via `getRelayToken`. Targets are registered
8
+ // and the token stored via `crtr canvas config add` (`--relay-token -` reads
9
+ // it from stdin, the preferred form to keep it out of shell history).
9
10
  import { usage } from '../errors.js';
10
11
  import { readConfig } from '../config.js';
12
+ import { getRelayToken } from '../secrets.js';
13
+ export function getRemoteCanvasTarget(name, scope = 'user') {
14
+ return readConfig(scope).remoteCanvas.targets[name];
15
+ }
11
16
  export function resolveRemoteCanvasTarget(name, scope = 'user') {
12
17
  const cfg = readConfig(scope);
13
18
  const target = cfg.remoteCanvas.targets[name];
@@ -15,11 +20,11 @@ export function resolveRemoteCanvasTarget(name, scope = 'user') {
15
20
  const known = Object.keys(cfg.remoteCanvas.targets);
16
21
  throw usage(`no remote canvas target named '${name}'` +
17
22
  (known.length > 0 ? ` — configured: ${known.join(', ')}` : ' — none configured yet') +
18
- `. Add remoteCanvas.targets.${name} to config.json (run \`crtr sys config path\`): {"previewEndpoint": "https://<hash>.preview.bl.run", "relayTokenEnv": "<ENV_VAR_NAME>"}.`);
23
+ `. Add one with \`crtr canvas config add ${name} --preview <url> --relay-token -\`.`);
19
24
  }
20
- const relayToken = process.env[target.relayTokenEnv];
25
+ const relayToken = getRelayToken(target.relayTokenRef, scope);
21
26
  if (relayToken === undefined || relayToken === '') {
22
- throw usage(`remote canvas target '${name}' needs its relay token in $${target.relayTokenEnv} that environment variable is unset. Export it before running this command; the token is never read from config or argv.`);
27
+ throw usage(`target '${name}' has no stored relay token — add it with \`crtr canvas config add ${name} --relay-token -\`.`);
23
28
  }
24
29
  return { previewEndpoint: target.previewEndpoint, relayToken };
25
30
  }
@@ -0,0 +1,2 @@
1
+ import type { RawResponse } from './contract.js';
2
+ export declare function createRemoteExec(previewEndpoint: string, relayToken: string, timeoutMs?: number): (bin: string, args: string[]) => Promise<RawResponse>;
@@ -0,0 +1,53 @@
1
+ // transport-remote.ts — the Node-side remote exec transport for a
2
+ // RemoteCanvasSource. POSTs a SourceRequest to a Blaxel preview endpoint's
3
+ // `/__crtr/source` route (the SAME in-guest source-fulfillment endpoint the
4
+ // browser/web transport already speaks), bearer-authed with the relay token.
5
+ //
6
+ // This is the ONLY way RemoteCanvasSource reaches the remote canvas: it never
7
+ // reads a remote SQLite file, and it never invokes an in-guest command that
8
+ // doesn't already exist. Every argv it sends is `crtr --json <existing
9
+ // subcommand>` — read-only.
10
+ /** The remote read plane crosses a network boundary — a stalled TCP connection
11
+ * or a hung `/__crtr/source` handler must fail closed, not wedge `node
12
+ * inspect`/`canvas browse`/attach forever. Low enough that a genuinely hung
13
+ * endpoint surfaces as a failed read within one interactive frame, not a
14
+ * silent hang. */
15
+ const DEFAULT_TIMEOUT_MS = 5_000;
16
+ export function createRemoteExec(previewEndpoint, relayToken, timeoutMs = DEFAULT_TIMEOUT_MS) {
17
+ // A malformed stored token (control chars / non-Latin1 bytes) would throw
18
+ // from the Headers constructor with the invalid value IN the thrown
19
+ // message — validate up front so that never happens, and fail with a
20
+ // GENERIC message instead (Phase 3 review Minor 1: no token bytes may ever
21
+ // reach returned stderr). HTTP header values are ISO-8859-1; reject
22
+ // anything outside `/^[\x20-\x7e]+$/` (printable ASCII, no CR/LF/control).
23
+ const tokenValid = /^[\x20-\x7e]+$/.test(relayToken);
24
+ return async function exec(bin, args) {
25
+ if (!tokenValid) {
26
+ return { ok: false, stdout: '', stderr: 'remote source: invalid stored relay token' };
27
+ }
28
+ const req = { kind: 'exec', bin, args };
29
+ const controller = new AbortController();
30
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
31
+ let res;
32
+ try {
33
+ res = await fetch(new URL('/__crtr/source', previewEndpoint), {
34
+ method: 'POST',
35
+ headers: { 'content-type': 'application/json', authorization: `Bearer ${relayToken}` },
36
+ body: JSON.stringify(req),
37
+ signal: controller.signal,
38
+ });
39
+ }
40
+ catch (e) {
41
+ if (controller.signal.aborted) {
42
+ return { ok: false, stdout: '', stderr: `remote source: timeout after ${timeoutMs}ms` };
43
+ }
44
+ return { ok: false, stdout: '', stderr: 'remote source: network error' };
45
+ }
46
+ finally {
47
+ clearTimeout(timer);
48
+ }
49
+ if (!res.ok)
50
+ return { ok: false, stdout: '', stderr: `remote source: HTTP ${res.status} ${res.statusText}` };
51
+ return (await res.json());
52
+ };
53
+ }
@@ -64,7 +64,7 @@ import { FAULT_QUIET_MS, readFault, recordFault } from '../core/runtime/fault.js
64
64
  import { fanDoctrineWake } from '../core/runtime/close.js';
65
65
  import { logger } from '../core/log.js';
66
66
  export { FAULT_QUIET_MS };
67
- import { isPidAlive, isRecordedPidAlive } from '../core/canvas/pid.js';
67
+ import { isPidAlive, isRecordedPidAlive, recordedPidLiveness } from '../core/canvas/pid.js';
68
68
  import { reconcileBootLiveness } from '../core/canvas/boot.js';
69
69
  import { listLivePanes, tearDownNode } from '../core/runtime/placement.js';
70
70
  import { reviveNode } from '../core/runtime/revive.js';
@@ -705,14 +705,24 @@ async function handleNodeLiveness(row, now, revivedThisTick) {
705
705
  // its turn is over, and the engine never exited — past the grace it kills the
706
706
  // engine so the dead-pid refresh branch below revives it fresh.
707
707
  //
708
- // Liveness here is IDENTITY-aware, not a bare isPidAlive: on a heavily-
708
+ // Liveness here is IDENTITY-aware AND requires CONFIRMED alive, not a bare
709
+ // isPidAlive or the fail-open-to-alive boolean adapter: on a heavily-
709
710
  // forking host (esp. Linux, where low pids recycle fast) a just-dead
710
- // broker's pid can be reused by an unrelated process before this tick
711
- // runs, and a bare isPidAlive would report the STRANGER as "our broker
712
- // alive" clearing the grace clock forever and wedging revive (the
713
- // recorded-pid-reuse hang). isRecordedPidAlive guards against that with
714
- // the launch-time identity baseline (`row.pi_pid_identity`).
715
- if (pid != null && isRecordedPidAlive(pid, row.pi_pid_identity)) {
711
+ // broker's pid can be reused by an unrelated process within milliseconds,
712
+ // AND the `ps` probe confirming identity can itself intermittently fail.
713
+ // Either misread as "alive" here doesn't just misreport this one tick it
714
+ // clears `unhealthySince` (the grace clock), discarding the daemon's memory
715
+ // that the node had already been observed dead, so the VERY NEXT tick treats
716
+ // a dead-for-20s+ node as a brand-new first observation and restarts the
717
+ // whole grace window from scratch (the confirmed Round-2 crash-revive wedge:
718
+ // a single `probe-null` read on a Linux CI runner wedged
719
+ // broker-crash-teardown.test.ts's 'CRASH → grace-revive RESUME'). So only a
720
+ // POSITIVELY CONFIRMED-alive read (`recordedPidLiveness === 'alive'`) may
721
+ // clear the clock; `'dead'` AND `'indeterminate'` both fall through to the
722
+ // ordinary not-alive path below, which now CONTRIBUTES to the grace window
723
+ // instead of zeroing it — robust to a flicker, and still revives promptly
724
+ // once a node is truly gone for the full grace duration.
725
+ if (pid != null && recordedPidLiveness(pid, row.pi_pid_identity) === 'alive') {
716
726
  unhealthySince.delete(id);
717
727
  clearStrandedRelaunchState(id); // a live engine means the stranding resolved
718
728
  handleYieldStall(row, pid, now);
package/dist/types.d.ts CHANGED
@@ -138,10 +138,10 @@ export interface ScopeConfig {
138
138
  * entries at the same scope precedence as the rest of `ScopeConfig`. */
139
139
  kinds: Record<string, KindConfig>;
140
140
  /** Named remote-canvas targets for `crtr surface attach to <id> --canvas
141
- * <name>` (Phase 1 interim contract hand-edited in config.json, no CLI
142
- * verbs yet; Phase 3 owns the real secret-store-backed `crtr canvas
143
- * config`/`crtr canvas use`). Unset via `sys config set` — not in
144
- * `TOP_LEVEL_KEYS`. */
141
+ * <name>`, `crtr node inspect list/show --canvas <name>`, and `crtr canvas
142
+ * browse --canvas <name>` registered/managed via `crtr canvas config
143
+ * add/list/show/remove` and selected durably via `crtr canvas use`. Unset
144
+ * via `sys config set` — not in `TOP_LEVEL_KEYS`. */
145
145
  remoteCanvas: RemoteCanvasConfig;
146
146
  /** Extra env-var names/globs a scope explicitly admits across the broker
147
147
  * spawn-env boundary (`buildBrokerEnv`, `core/runtime/spawn-env.ts`) — the
@@ -154,14 +154,17 @@ export interface ScopeConfig {
154
154
  };
155
155
  }
156
156
  /** One remote canvas target: where to relay-attach and how to find its
157
- * bearer token. The token itself is NEVER stored here — only the name of an
158
- * environment variable holding it, resolved at connect time by
159
- * `resolveRemoteCanvasTarget` (`src/core/view/remote-canvas-target.ts`). */
157
+ * bearer token. The token itself is NEVER stored here — only a ref into the
158
+ * 0600 secrets store, resolved at connect time by `resolveRemoteCanvasTarget`
159
+ * (`src/core/view/remote-canvas-target.ts`) via `getRelayToken`
160
+ * (`src/core/secrets.ts`). */
160
161
  export interface RemoteCanvasTarget {
161
162
  previewEndpoint: string;
162
- /** Name of an environment variable holding the raw relay/bearer token
163
- * NEVER the token itself. Resolved from process.env at connect time. */
164
- relayTokenEnv: string;
163
+ /** Name/ref under which the raw token is stored in secrets.json NEVER the
164
+ * token itself. Resolved via `getRelayToken` (`core/secrets.ts`) at connect
165
+ * time. Defaults to the target name at creation but is a distinct field so
166
+ * a future rename doesn't orphan the secret. */
167
+ relayTokenRef: string;
165
168
  cpOrigin?: string;
166
169
  }
167
170
  export interface RemoteCanvasConfig {
@@ -176,6 +179,11 @@ export interface ScopeState {
176
179
  }>;
177
180
  last_self_check?: string;
178
181
  bootstrap_done?: boolean;
182
+ /** The name of the remote canvas target `crtr canvas use` last selected
183
+ * (see `RemoteCanvasConfig`), or `null`/omitted for local. Read by
184
+ * `resolveCanvasSource` (`core/canvas/source.ts`) as the durable fallback
185
+ * when a command's own `--canvas` flag is absent. */
186
+ activeCanvas?: string | null;
179
187
  }
180
188
  export interface SubagentFrontmatter {
181
189
  name: string;
@@ -244,7 +252,7 @@ export declare const PROFILE_DIR = "profiles";
244
252
  export declare const DEFAULT_MAX_PANES_PER_WINDOW = 3;
245
253
  export declare function defaultScopeConfig(): ScopeConfig;
246
254
  /** No remote canvas targets are configured out of the box — every one is
247
- * hand-added to config.json (Phase 1 interim contract, see `RemoteCanvasConfig`). */
255
+ * registered via `crtr canvas config add` (see `RemoteCanvasConfig`). */
248
256
  export declare function defaultRemoteCanvasConfig(): RemoteCanvasConfig;
249
257
  /** The builtin kind registry (spec §1.5): the built-in defaults for every
250
258
  * top-level kind and its sub-persona kinds. `whenToUse`/`model` are the
package/dist/types.js CHANGED
@@ -36,7 +36,7 @@ export function defaultScopeConfig() {
36
36
  };
37
37
  }
38
38
  /** No remote canvas targets are configured out of the box — every one is
39
- * hand-added to config.json (Phase 1 interim contract, see `RemoteCanvasConfig`). */
39
+ * registered via `crtr canvas config add` (see `RemoteCanvasConfig`). */
40
40
  export function defaultRemoteCanvasConfig() {
41
41
  return { targets: {} };
42
42
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crouton-kit/crouter",
3
- "version": "0.3.65",
3
+ "version": "0.3.67",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",