@north-light/crouter 0.3.162 → 0.3.163

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 (95) hide show
  1. package/dist/builtin-memory/00-runtime-base.md +1 -0
  2. package/dist/builtin-memory/04-orchestration-kernel.md +1 -0
  3. package/dist/builtin-memory/init.md +38 -0
  4. package/dist/builtin-memory/wedged-child-on-runaway-bash.md +2 -2
  5. package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +0 -1
  6. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +34 -2
  7. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +14 -3
  8. package/dist/clients/attach/__tests__/crtr-output.test.js +14 -9
  9. package/dist/clients/attach/__tests__/edit-diff.test.d.ts +1 -0
  10. package/dist/clients/attach/__tests__/edit-diff.test.js +34 -0
  11. package/dist/clients/attach/__tests__/editor-frame-title.test.d.ts +1 -0
  12. package/dist/clients/attach/__tests__/editor-frame-title.test.js +19 -0
  13. package/dist/clients/attach/render/chat-view.js +8 -3
  14. package/dist/clients/attach/render/crtr-output.js +31 -2
  15. package/dist/clients/attach/render/edit-diff.js +26 -2
  16. package/dist/clients/attach/render/tool-calls.d.ts +7 -2
  17. package/dist/clients/attach/render/tool-calls.js +25 -4
  18. package/dist/clients/attach/session/connection.d.ts +1 -1
  19. package/dist/clients/attach/session/editor-frame.d.ts +5 -1
  20. package/dist/clients/attach/session/editor-frame.js +21 -15
  21. package/dist/clients/attach/session/identity.d.ts +1 -1
  22. package/dist/clients/attach/viewer.js +621 -619
  23. package/dist/commands/__tests__/api-canvas-source.test.d.ts +1 -0
  24. package/dist/commands/__tests__/api-canvas-source.test.js +37 -0
  25. package/dist/commands/__tests__/human.test.js +2 -0
  26. package/dist/commands/__tests__/search-contents.test.d.ts +1 -0
  27. package/dist/commands/__tests__/search-contents.test.js +52 -0
  28. package/dist/commands/api-client.js +1 -0
  29. package/dist/commands/human/prompts.js +2 -2
  30. package/dist/commands/human/shared.d.ts +1 -0
  31. package/dist/commands/human/shared.js +7 -4
  32. package/dist/commands/memory/lint.d.ts +6 -0
  33. package/dist/commands/memory/lint.js +177 -35
  34. package/dist/commands/memory/read.js +22 -1
  35. package/dist/commands/memory/write.js +18 -4
  36. package/dist/commands/memory.js +1 -1
  37. package/dist/commands/search/contents.js +2 -2
  38. package/dist/commands/sys/__tests__/sync-deps.test.js +12 -21
  39. package/dist/commands/sys/__tests__/sync-import.test.js +30 -29
  40. package/dist/commands/sys/setup-wizard.d.ts +8 -1
  41. package/dist/commands/sys/setup-wizard.js +209 -58
  42. package/dist/commands/sys/sync-deps.js +14 -18
  43. package/dist/commands/sys/sync-project-guidance.js +16 -19
  44. package/dist/core/__tests__/migration.test.js +8 -3
  45. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +7 -10
  46. package/dist/core/__tests__/on-read-dedup-resume.test.js +13 -25
  47. package/dist/core/__tests__/on-read-identity.test.js +8 -15
  48. package/dist/core/__tests__/revive.test.js +2 -2
  49. package/dist/core/__tests__/tmux-surface.test.js +10 -4
  50. package/dist/core/__tests__/worktree.test.js +3 -3
  51. package/dist/core/canvas/extensions.d.ts +1 -1
  52. package/dist/core/canvas/extensions.js +18 -11
  53. package/dist/core/canvas/labels.d.ts +4 -5
  54. package/dist/core/canvas/labels.js +9 -9
  55. package/dist/core/command-manifests/schema.js +18 -5
  56. package/dist/core/command.js +17 -7
  57. package/dist/core/configured-clis/invoker.js +2 -0
  58. package/dist/core/help.d.ts +3 -1
  59. package/dist/core/help.js +5 -2
  60. package/dist/core/keybindings/__tests__/resolve.test.js +5 -2
  61. package/dist/core/keybindings/catalog.d.ts +3 -2
  62. package/dist/core/keybindings/catalog.js +34 -25
  63. package/dist/core/keybindings/index.d.ts +1 -1
  64. package/dist/core/keybindings/resolve.js +8 -0
  65. package/dist/core/keybindings/types.d.ts +6 -1
  66. package/dist/core/memory/doc-link-grammar.d.ts +20 -0
  67. package/dist/core/memory/doc-link-grammar.js +110 -0
  68. package/dist/core/memory-resolver.d.ts +5 -0
  69. package/dist/core/memory-resolver.js +12 -1
  70. package/dist/core/runtime/bearings.d.ts +8 -8
  71. package/dist/core/runtime/bearings.js +20 -16
  72. package/dist/core/runtime/broker.js +3 -13
  73. package/dist/core/runtime/canvas-extensions.d.ts +1 -0
  74. package/dist/core/runtime/canvas-extensions.js +2 -0
  75. package/dist/core/runtime/launch.d.ts +1 -1
  76. package/dist/core/runtime/launch.js +1 -1
  77. package/dist/core/runtime/tmux.js +136 -100
  78. package/dist/core/scope.js +4 -5
  79. package/dist/core/substrate/ceiling.d.ts +3 -6
  80. package/dist/core/substrate/ceiling.js +13 -15
  81. package/dist/core/substrate/index.d.ts +2 -3
  82. package/dist/core/substrate/index.js +2 -2
  83. package/dist/core/substrate/on-read.d.ts +4 -13
  84. package/dist/core/substrate/on-read.js +142 -262
  85. package/dist/core/substrate/render.js +4 -4
  86. package/dist/core/substrate/schema.d.ts +3 -2
  87. package/dist/core/substrate/schema.js +5 -5
  88. package/dist/pi-extensions/__tests__/canvas-tool-guide.test.d.ts +1 -0
  89. package/dist/pi-extensions/__tests__/canvas-tool-guide.test.js +96 -0
  90. package/dist/pi-extensions/canvas-doc-substrate.js +7 -8
  91. package/dist/pi-extensions/canvas-tool-guide.d.ts +14 -0
  92. package/dist/pi-extensions/canvas-tool-guide.js +74 -0
  93. package/package.json +1 -1
  94. package/runtime.lock.json +2 -2
  95. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crouter-help.ts +0 -95
@@ -0,0 +1,96 @@
1
+ // Run with: node --import tsx/esm --test src/pi-extensions/__tests__/canvas-tool-guide.test.ts
2
+ //
3
+ // Regression: canvas brokers must load and inject the root crtr guide themselves;
4
+ // optional pi packages are not part of the canvas runtime contract.
5
+ import { test } from 'node:test';
6
+ import assert from 'node:assert/strict';
7
+ import { chmodSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
8
+ import { tmpdir } from 'node:os';
9
+ import { delimiter, join } from 'node:path';
10
+ import { CANVAS_EXTENSIONS, CANVAS_TOOL_GUIDE_PATH } from '../../core/runtime/canvas-extensions.js';
11
+ import registerCanvasToolGuide from '../canvas-tool-guide.js';
12
+ function fakePi() {
13
+ return {
14
+ on(event, handler) {
15
+ if (event === 'before_agent_start')
16
+ this.beforeAgentStart = handler;
17
+ if (event === 'session_start')
18
+ this.sessionStart = handler;
19
+ },
20
+ };
21
+ }
22
+ test('canvas nodes load the cwd-specific root guide, share its boot cache, and never capture during a turn', async () => {
23
+ assert.ok(CANVAS_EXTENSIONS.includes(CANVAS_TOOL_GUIDE_PATH), 'the guide is a mandatory canvas extension');
24
+ const root = mkdtempSync(join(tmpdir(), 'crtr-tool-guide-'));
25
+ const bin = join(root, 'bin');
26
+ const cwd = join(root, 'configured-project');
27
+ const countFile = join(root, 'capture-count');
28
+ const capturedCwdFile = join(root, 'capture-cwd');
29
+ const previous = {
30
+ cwd: process.cwd(),
31
+ home: process.env['HOME'],
32
+ nodeId: process.env['CRTR_NODE_ID'],
33
+ path: process.env['PATH'],
34
+ };
35
+ try {
36
+ mkdirSync(bin);
37
+ mkdirSync(cwd);
38
+ const command = join(bin, 'crtr');
39
+ writeFileSync(command, `#!/bin/sh
40
+ count=0
41
+ if [ -f "$CAPTURE_COUNT" ]; then count=$(cat "$CAPTURE_COUNT"); fi
42
+ printf '%s' "$((count + 1))" > "$CAPTURE_COUNT"
43
+ printf '%s' "$PWD" > "$CAPTURE_CWD"
44
+ printf '%s\\n' 'configured root guide'
45
+ `);
46
+ chmodSync(command, 0o755);
47
+ process.chdir(cwd);
48
+ process.env['HOME'] = root;
49
+ process.env['CRTR_NODE_ID'] = 'canvas-tool-guide-test';
50
+ process.env['PATH'] = `${bin}${delimiter}${previous.path ?? ''}`;
51
+ process.env['CAPTURE_COUNT'] = countFile;
52
+ process.env['CAPTURE_CWD'] = capturedCwdFile;
53
+ const first = fakePi();
54
+ registerCanvasToolGuide(first);
55
+ const firstSessionStart = first.sessionStart;
56
+ assert.ok(firstSessionStart, 'session-start capture is registered');
57
+ await firstSessionStart();
58
+ const second = fakePi();
59
+ registerCanvasToolGuide(second);
60
+ const secondSessionStart = second.sessionStart;
61
+ assert.ok(secondSessionStart, 'session-start capture is registered for every broker');
62
+ await secondSessionStart();
63
+ assert.equal(readFileSync(countFile, 'utf8'), '1', 'a sibling broker uses the 15-second disk cache');
64
+ assert.equal(readFileSync(capturedCwdFile, 'utf8'), realpathSync(cwd), 'root help discovers commands from the node cwd');
65
+ const beforeAgentStart = second.beforeAgentStart;
66
+ assert.ok(beforeAgentStart, 'turn-time guide injection is registered');
67
+ const prompt = 'Available tools\n\nGuidelines:\nUse the available tools.';
68
+ assert.equal(beforeAgentStart({ systemPrompt: prompt })?.systemPrompt, 'Available tools\n\n<crtr-tool-guide>\nconfigured root guide\n</crtr-tool-guide>\n\nGuidelines:\nUse the available tools.', 'the captured guide sits in the tool-selection frame');
69
+ rmSync(join(root, '.crouter'), { recursive: true, force: true });
70
+ const unbooted = fakePi();
71
+ registerCanvasToolGuide(unbooted);
72
+ const unbootedBeforeAgentStart = unbooted.beforeAgentStart;
73
+ assert.ok(unbootedBeforeAgentStart, 'turn-time guide injection is registered before boot capture');
74
+ assert.equal(unbootedBeforeAgentStart({ systemPrompt: prompt }), undefined, 'a node without a boot capture starts without a guide');
75
+ await new Promise((resolve) => setTimeout(resolve, 50));
76
+ assert.equal(readFileSync(countFile, 'utf8'), '1', 'a turn never starts a root-help subprocess');
77
+ }
78
+ finally {
79
+ process.chdir(previous.cwd);
80
+ if (previous.home === undefined)
81
+ delete process.env['HOME'];
82
+ else
83
+ process.env['HOME'] = previous.home;
84
+ if (previous.nodeId === undefined)
85
+ delete process.env['CRTR_NODE_ID'];
86
+ else
87
+ process.env['CRTR_NODE_ID'] = previous.nodeId;
88
+ if (previous.path === undefined)
89
+ delete process.env['PATH'];
90
+ else
91
+ process.env['PATH'] = previous.path;
92
+ delete process.env['CAPTURE_COUNT'];
93
+ delete process.env['CAPTURE_CWD'];
94
+ rmSync(root, { recursive: true, force: true });
95
+ }
96
+ });
@@ -12,16 +12,15 @@
12
12
  // into `event.systemPrompt`, right after pi's native "Available tools" list
13
13
  // (before the `\n\nGuidelines:` anchor), so the substrate's standing
14
14
  // directives sit in the tool-selection frame the agent reads while choosing
15
- // a capability — mirroring the personal crouter-help.ts anchor logic. Falls
16
- // back to appending when the anchor is absent. (The `<memory kind="knowledge">`
15
+ // a capability. Falls back to appending when the anchor is absent. (The `<memory kind="knowledge">`
17
16
  // catalog rides the bearings message instead — see core/substrate/render.ts.)
18
17
  //
19
18
  // 2. tool_result (gated on `read`) — the ON-READ half. For each non-error
20
- // `read`, surfaces the substrate docs that should appear alongside the file
21
- // (positional `.crouter/memory/` ancestors + `applies-to` globs) at their
22
- // file-read-visibility rung, prepended as a `<auto-loaded-context>` block
23
- // before the file contents. A per-session realpath set dedups so the same
24
- // doc is not re-injected on repeat reads.
19
+ // `read`, evaluates explicit `applies-to` globs (plus `read-when` metadata)
20
+ // across the resolved corpus and any project stores enclosing the read
21
+ // file. Matches render at their file-read rung in `<auto-loaded-context>`
22
+ // before the file contents. A transcript-scoped realpath set deduplicates
23
+ // repeated reads and resumed broker processes.
25
24
  //
26
25
  // Both handler bodies are wrapped in try/catch and degrade to INERT on error —
27
26
  // a substrate bug must never brick node boot or break a `read`, only quietly
@@ -42,7 +41,7 @@ import { loadInjectedDocs, saveInjectedDocs } from '../core/substrate/injected-s
42
41
  /** The stable seam that closes pi's native "Available tools" list. We splice the
43
42
  * substrate's boot sections right before it — the agent decides which capability
44
43
  * to reach for while reading the tools, so memory docs/preferences must sit
45
- * there, not far below. Mirrors crouter-help.ts. Falls back to appending if absent. */
44
+ * there, not far below. Falls back to appending if absent. */
46
45
  const TOOLS_ANCHOR = '\n\nGuidelines:';
47
46
  const AUTO_CONTEXT_RE = /<auto-loaded-context(?:\s[^>]*)?>\n([\s\S]*?)\n<\/auto-loaded-context>/;
48
47
  function mergeAutoLoadedContext(injected, content) {
@@ -0,0 +1,14 @@
1
+ declare function injectToolGuide(systemPrompt: string, helpText: string): string;
2
+ interface BeforeAgentStartEventLike {
3
+ systemPrompt: string;
4
+ }
5
+ interface PiLike {
6
+ on(event: 'session_start', handler: () => void | Promise<void>): void;
7
+ on(event: 'before_agent_start', handler: (event: BeforeAgentStartEventLike) => {
8
+ systemPrompt: string;
9
+ } | void): void;
10
+ }
11
+ /** Register the root crtr tool-guide injection for a canvas node. */
12
+ export declare function registerCanvasToolGuide(pi: PiLike): void;
13
+ export { injectToolGuide };
14
+ export default registerCanvasToolGuide;
@@ -0,0 +1,74 @@
1
+ // canvas-tool-guide.ts — injects the root crtr CLI guide into every canvas node.
2
+ //
3
+ // Capturing `crtr -h` builds the full configured command tree, including commands
4
+ // discovered from the node's cwd. It runs only at session start; before_agent_start
5
+ // injects the last successful capture without starting work or waiting.
6
+ import { execFile } from 'node:child_process';
7
+ import { createHash } from 'node:crypto';
8
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
9
+ import { homedir } from 'node:os';
10
+ import { dirname, join } from 'node:path';
11
+ import { promisify } from 'node:util';
12
+ const run = promisify(execFile);
13
+ const TTL_MS = 15_000;
14
+ const TOOLS_ANCHOR = '\n\nGuidelines:';
15
+ function helpCacheFile(cwd) {
16
+ const hash = createHash('sha1').update(cwd).digest('hex').slice(0, 16);
17
+ return join(homedir(), '.crouter', 'cache', `crtr-tool-guide-${hash}.json`);
18
+ }
19
+ function injectToolGuide(systemPrompt, helpText) {
20
+ const block = `<crtr-tool-guide>\n${helpText}\n</crtr-tool-guide>`;
21
+ const anchor = systemPrompt.indexOf(TOOLS_ANCHOR);
22
+ if (anchor === -1)
23
+ return `${systemPrompt}\n\n${block}`;
24
+ return `${systemPrompt.slice(0, anchor)}\n\n${block}${systemPrompt.slice(anchor)}`;
25
+ }
26
+ /** Register the root crtr tool-guide injection for a canvas node. */
27
+ export function registerCanvasToolGuide(pi) {
28
+ const nodeId = process.env['CRTR_NODE_ID'];
29
+ if (nodeId === undefined || nodeId.trim() === '')
30
+ return;
31
+ let helpText = '';
32
+ let capturedAt = 0;
33
+ async function refresh() {
34
+ if (helpText !== '' && Date.now() - capturedAt < TTL_MS)
35
+ return;
36
+ const file = helpCacheFile(process.cwd());
37
+ try {
38
+ const cached = JSON.parse(readFileSync(file, 'utf8'));
39
+ if (typeof cached.text === 'string' && cached.text !== '' && typeof cached.at === 'number' && Date.now() - cached.at < TTL_MS) {
40
+ helpText = cached.text;
41
+ capturedAt = cached.at;
42
+ return;
43
+ }
44
+ }
45
+ catch {
46
+ // A missing or invalid cache is refreshed below.
47
+ }
48
+ try {
49
+ const { stdout } = await run('crtr', ['-h'], { timeout: 10_000 });
50
+ helpText = stdout.trim();
51
+ capturedAt = Date.now();
52
+ try {
53
+ mkdirSync(dirname(file), { recursive: true });
54
+ writeFileSync(file, JSON.stringify({ text: helpText, at: capturedAt }), 'utf8');
55
+ }
56
+ catch {
57
+ // The guide remains available in this broker even when its cache cannot persist.
58
+ }
59
+ }
60
+ catch {
61
+ // Keep the last successful guide and never delay a turn for a failed capture.
62
+ }
63
+ }
64
+ // Node startup is the only point a capture may wait. Warm spares pay it before
65
+ // a user claims them; resumed nodes refresh before their next turn.
66
+ pi.on('session_start', refresh);
67
+ pi.on('before_agent_start', (event) => {
68
+ if (helpText === '')
69
+ return;
70
+ return { systemPrompt: injectToolGuide(event.systemPrompt, helpText) };
71
+ });
72
+ }
73
+ export { injectToolGuide };
74
+ export default registerCanvasToolGuide;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/crouter",
3
- "version": "0.3.162",
3
+ "version": "0.3.163",
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.162",
3
+ "version": "0.3.163",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/crouter",
9
- "version": "0.3.162",
9
+ "version": "0.3.163",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "dependencies": {
@@ -1,95 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import { execFile } from "node:child_process";
3
- import { promisify } from "node:util";
4
- import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
- import { homedir } from "node:os";
6
- import { createHash } from "node:crypto";
7
- import { dirname, join } from "node:path";
8
-
9
- const run = promisify(execFile);
10
-
11
- // `crtr -h` is the auto-loaded tool guide. Capturing it costs a real subprocess:
12
- // bare `crtr -h` is the one invocation that must build the FULL command tree, so
13
- // it loads every subtree's module graph — ~250ms on a laptop, ~320ms in the
14
- // guest container. The guide's content is static per crouter version + installed
15
- // plugins, so that cost must never sit in front of a turn. The TTL only exists
16
- // to pick a tree change (an upgrade, a newly installed plugin) up eventually.
17
- const TTL_MS = 15_000;
18
-
19
- // The TTL cache is also persisted to disk, keyed by cwd (skills resolve by
20
- // scope, so the guide is cwd-specific). Every broker/node is a FRESH process, so
21
- // the in-memory cache alone is always cold at session_start — each boot would
22
- // re-spawn `crtr -h`. The disk layer lets a cluster of node boots within one TTL
23
- // window (daemon revives, child spawns, rapid relaunches) share a single capture.
24
- function helpCacheFile(cwd: string): string {
25
- const h = createHash("sha1").update(cwd).digest("hex").slice(0, 16);
26
- return join(homedir(), ".crouter", "cache", `crouter-help-${h}.json`);
27
- }
28
-
29
- export default function (pi: ExtensionAPI) {
30
- let helpText = "";
31
- let capturedAt = 0;
32
-
33
- async function refresh(): Promise<void> {
34
- if (Date.now() - capturedAt < TTL_MS && helpText) return; // in-memory fresh
35
- const file = helpCacheFile(process.cwd());
36
-
37
- // Cross-process disk cache: reuse a recent capture from a sibling boot.
38
- try {
39
- const c = JSON.parse(readFileSync(file, "utf8")) as { text?: string; at?: number };
40
- if (typeof c.text === "string" && c.text && typeof c.at === "number" && Date.now() - c.at < TTL_MS) {
41
- helpText = c.text;
42
- capturedAt = c.at;
43
- return; // fresh on disk — skip the spawn
44
- }
45
- } catch {
46
- // miss / corrupt — fall through to re-probe
47
- }
48
-
49
- try {
50
- const { stdout } = await run("crouter", ["-h"], { timeout: 10_000 });
51
- helpText = stdout.trim();
52
- capturedAt = Date.now();
53
- try {
54
- mkdirSync(dirname(file), { recursive: true });
55
- writeFileSync(file, JSON.stringify({ text: helpText, at: capturedAt }), "utf8");
56
- } catch {
57
- // best-effort: a missing cache just means the next boot re-probes
58
- }
59
- } catch {
60
- // soft-fail: keep last-known text (or empty); never block the turn
61
- }
62
- }
63
-
64
- // Boot-time capture: the one place a refresh may block, because it happens at
65
- // node mint/revive, never in front of a user's turn. A warm-pool spare has
66
- // therefore already paid it before it is ever claimed.
67
- pi.on("session_start", refresh);
68
-
69
- pi.on("before_agent_start", (event) => {
70
- // NEVER await here. A stale-by-TTL capture used to re-spawn `crtr -h`
71
- // synchronously, putting its full subprocess cost between the delivered
72
- // message and agent_start — measured as the single largest term in a
73
- // Northlight first-message ack. Serve the last-known text and let the
74
- // refresh land for the next turn instead.
75
- if (Date.now() - capturedAt >= TTL_MS) void refresh();
76
- if (!helpText) return;
77
-
78
- const block = `<crtr-tool-guide>\n${helpText}\n</crtr-tool-guide>`;
79
-
80
- // Place the guide in the tool-selection frame, right after pi's native
81
- // "Available tools" list rather than at the very bottom — the agent decides
82
- // which capability to reach for while reading the tools, so the crtr
83
- // commands (agent/skill/job) must be present there, not 3k tokens later.
84
- // `\n\nGuidelines:` is the stable seam that closes the tools area. Fall back
85
- // to appending if pi ever changes that marker.
86
- const anchor = "\n\nGuidelines:";
87
- const idx = event.systemPrompt.indexOf(anchor);
88
- if (idx === -1) {
89
- return { systemPrompt: `${event.systemPrompt}\n\n${block}` };
90
- }
91
- return {
92
- systemPrompt: `${event.systemPrompt.slice(0, idx)}\n\n${block}${event.systemPrompt.slice(idx)}`,
93
- };
94
- });
95
- }