@promptbook/cli 0.112.0-115 → 0.112.0-118

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 (82) hide show
  1. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatSidebarDefault.tsx +5 -6
  2. package/apps/agents-server/src/utils/externalChatRunner/processExternalUserChatJob.ts +17 -7
  3. package/apps/agents-server/src/utils/localChatRunner/processLocalUserChatJob.ts +17 -7
  4. package/apps/agents-server/src/utils/userChat/createImmediateUserChatAnswerModelRequirements.ts +11 -0
  5. package/apps/agents-server/src/utils/userChat/listUserChats.ts +5 -7
  6. package/esm/index.es.js +451 -95
  7. package/esm/index.es.js.map +1 -1
  8. package/esm/scripts/run-codex-prompts/common/parseDuration.d.ts +19 -0
  9. package/esm/src/_packages/components.index.d.ts +2 -0
  10. package/esm/src/_packages/node.index.d.ts +20 -0
  11. package/esm/src/book-3.0/BookNodeAgentSource.d.ts +1 -1
  12. package/esm/src/book-3.0/CliAgent.d.ts +15 -17
  13. package/esm/src/book-3.0/agentFolderPaths.d.ts +30 -0
  14. package/esm/src/book-3.0/cliAgentEnv.d.ts +33 -0
  15. package/esm/src/book-components/BookEditor/BookEditorBrowserConfig.d.ts +2 -0
  16. package/esm/src/book-components/BookEditor/BookEditorForClient.d.ts +7 -0
  17. package/esm/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +9 -0
  18. package/esm/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +2 -30
  19. package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +2 -18
  20. package/esm/src/scrapers/website/utils/createShowdownConverter.d.ts +2 -2
  21. package/esm/src/version.d.ts +1 -1
  22. package/package.json +1 -1
  23. package/src/_packages/components.index.ts +2 -0
  24. package/src/_packages/node.index.ts +20 -0
  25. package/src/avatars/avatarAnimationScheduler.ts +33 -2
  26. package/src/avatars/visuals/fractalAvatarVisual.ts +5 -4
  27. package/src/avatars/visuals/minecraft2AvatarVisual.ts +16 -11
  28. package/src/avatars/visuals/minecraftAvatarVisual.ts +21 -7
  29. package/src/avatars/visuals/octopus3d2AvatarVisual.ts +69 -17
  30. package/src/avatars/visuals/octopus3d3AvatarVisual.ts +81 -18
  31. package/src/avatars/visuals/octopus3dAvatarVisual.ts +69 -17
  32. package/src/book-3.0/Book.ts +3 -1
  33. package/src/book-3.0/BookNodeAgentSource.ts +2 -2
  34. package/src/book-3.0/CliAgent.ts +87 -71
  35. package/src/book-3.0/agentFolderPaths.ts +38 -0
  36. package/src/book-3.0/cliAgentEnv.ts +46 -0
  37. package/src/book-components/BookEditor/BookEditor.tsx +1 -1
  38. package/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.tsx +2 -4
  39. package/src/book-components/BookEditor/BookEditorActionbar.tsx +32 -2
  40. package/src/book-components/BookEditor/BookEditorBrowserConfig.ts +11 -0
  41. package/src/book-components/BookEditor/BookEditorForClient.tsx +33 -0
  42. package/src/book-components/BookEditor/BookEditorMonaco.tsx +1 -1
  43. package/src/book-components/BookEditor/BookEditorMonacoTokenization.ts +83 -15
  44. package/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.ts +11 -0
  45. package/src/book-components/BookEditor/useBookEditorMonacoLanguage.ts +32 -46
  46. package/src/book-components/BookEditor/useBookEditorMonacoStyles.ts +1 -1
  47. package/src/book-components/BookEditor/useBookEditorMonacoUploads.ts +1 -1
  48. package/src/book-components/Chat/utils/renderMarkdown.ts +3 -2
  49. package/src/cli/cli-commands/agent-folder/agentProjectPaths.ts +15 -35
  50. package/src/cli/cli-commands/coder/run.ts +28 -3
  51. package/src/cli/cli-commands/common/promptRunnerCliOptions.ts +9 -29
  52. package/src/commands/KNOWLEDGE/utils/knowledgeSourceContentToName.ts +2 -2
  53. package/src/commitments/_common/teamInternalAgentAccess.ts +2 -2
  54. package/src/formats/csv/CsvFormatParser.ts +4 -4
  55. package/src/formats/csv/utils/csvParse.ts +2 -2
  56. package/src/llm-providers/agent/AgentLlmExecutionTools.ts +2 -2
  57. package/src/llm-providers/agent/AgentLlmExecutionToolsAgentKitRunner.ts +2 -2
  58. package/src/llm-providers/agent/AgentLlmExecutionToolsOpenAiAssistantRunner.ts +2 -2
  59. package/src/other/templates/getTemplatesPipelineCollection.ts +712 -807
  60. package/src/scrapers/_common/utils/getScraperIntermediateSource.ts +2 -2
  61. package/src/scrapers/website/WebsiteScraper.ts +1 -1
  62. package/src/scrapers/website/utils/createShowdownConverter.ts +2 -2
  63. package/src/utils/misc/computeHash.ts +2 -2
  64. package/src/utils/random/$randomToken.ts +2 -2
  65. package/src/version.ts +2 -2
  66. package/src/versions.txt +3 -0
  67. package/umd/index.umd.js +453 -94
  68. package/umd/index.umd.js.map +1 -1
  69. package/umd/scripts/run-codex-prompts/common/parseDuration.d.ts +19 -0
  70. package/umd/src/_packages/components.index.d.ts +2 -0
  71. package/umd/src/_packages/node.index.d.ts +20 -0
  72. package/umd/src/book-3.0/BookNodeAgentSource.d.ts +1 -1
  73. package/umd/src/book-3.0/CliAgent.d.ts +15 -17
  74. package/umd/src/book-3.0/agentFolderPaths.d.ts +30 -0
  75. package/umd/src/book-3.0/cliAgentEnv.d.ts +33 -0
  76. package/umd/src/book-components/BookEditor/BookEditorBrowserConfig.d.ts +2 -0
  77. package/umd/src/book-components/BookEditor/BookEditorForClient.d.ts +7 -0
  78. package/umd/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +9 -0
  79. package/umd/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +2 -30
  80. package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +2 -18
  81. package/umd/src/scrapers/website/utils/createShowdownConverter.d.ts +2 -2
  82. package/umd/src/version.d.ts +1 -1
@@ -1,24 +1,32 @@
1
1
  import { mkdir, writeFile } from 'fs/promises';
2
2
  import { dirname } from 'path';
3
+ import { executeAgentChatTurn } from '../../scripts/run-agent-chat/executeAgentChatTurn';
3
4
  import { NotAllowed } from '../errors/NotAllowed';
4
5
  import { resolvePromptbookTemporaryPath } from '../utils/filesystem/promptbookTemporaryPath';
5
- import { $execCommand } from '../utils/execCommand/$execCommand';
6
+ import { spaceTrim } from '../utils/organization/spaceTrim';
6
7
  import type { BookNodeAgentSourceOptions, ResolvedBookNodeAgentSource } from './BookNodeAgentSource';
7
8
  import { resolveBookNodeAgentSource } from './BookNodeAgentSource';
9
+ import {
10
+ CLI_AGENT_HARNESS_NAMES,
11
+ CLI_AGENT_THINKING_LEVEL_VALUES,
12
+ PTBK_HARNESS_ENV,
13
+ PTBK_MODEL_ENV,
14
+ PTBK_THINKING_LEVEL_ENV,
15
+ } from './cliAgentEnv';
8
16
 
9
17
  /**
10
18
  * CLI harness names supported by `ptbk agent exec`.
11
19
  *
12
20
  * @public exported from `@promptbook/node`
13
21
  */
14
- export type CliAgentHarness = 'openai-codex' | 'github-copilot' | 'cline' | 'claude-code' | 'opencode' | 'gemini';
22
+ export type CliAgentHarness = (typeof CLI_AGENT_HARNESS_NAMES)[number];
15
23
 
16
24
  /**
17
25
  * Thinking levels supported by CLI coding harnesses.
18
26
  *
19
27
  * @public exported from `@promptbook/node`
20
28
  */
21
- export type CliAgentThinkingLevel = 'low' | 'medium' | 'high' | 'xhigh';
29
+ export type CliAgentThinkingLevel = (typeof CLI_AGENT_THINKING_LEVEL_VALUES)[number];
22
30
 
23
31
  /**
24
32
  * Per-run CLI options exposed by `CliAgent`.
@@ -31,6 +39,7 @@ export type CliAgentRunOptions = {
31
39
  readonly allowCredits?: boolean;
32
40
  readonly context?: string;
33
41
  readonly harness?: CliAgentHarness;
42
+ readonly isVerbose?: boolean;
34
43
  readonly model?: string;
35
44
  readonly noUi?: boolean;
36
45
  readonly thinkingLevel?: CliAgentThinkingLevel;
@@ -41,22 +50,7 @@ export type CliAgentRunOptions = {
41
50
  *
42
51
  * @public exported from `@promptbook/node`
43
52
  */
44
- export type CliAgentOptions = BookNodeAgentSourceOptions &
45
- CliAgentRunOptions & {
46
- /**
47
- * Executable used for the wrapper command.
48
- *
49
- * @default ptbk
50
- */
51
- readonly command?: string;
52
- };
53
-
54
- /**
55
- * Default executable used by `CliAgent`.
56
- *
57
- * @private internal constant of `CliAgent`
58
- */
59
- const DEFAULT_CLI_AGENT_COMMAND = 'ptbk';
53
+ export type CliAgentOptions = BookNodeAgentSourceOptions & CliAgentRunOptions;
60
54
 
61
55
  /**
62
56
  * Default non-interactive mode used by `CliAgent`.
@@ -66,10 +60,13 @@ const DEFAULT_CLI_AGENT_COMMAND = 'ptbk';
66
60
  const DEFAULT_CLI_AGENT_IS_NO_UI = true;
67
61
 
68
62
  /**
69
- * Lightweight JavaScript wrapper around `ptbk agent exec`.
63
+ * Lightweight JavaScript wrapper around the Promptbook agent execution pipeline.
64
+ *
65
+ * It uses the same harnesses and execution path as `ptbk agent exec`, running the runner
66
+ * in-process instead of spawning a separate CLI process.
70
67
  *
71
- * It uses the same CLI harnesses as Promptbook's agent command, making it the most faithful
72
- * way to run a local Book agent from Node.js when you want the CLI execution flow.
68
+ * When no `harness` is provided in the constructor or per-run options, `CliAgent` falls back
69
+ * to the `PTBK_HARNESS` environment variable, mirroring `ptbk agent exec` behavior.
73
70
  *
74
71
  * @public exported from `@promptbook/node`
75
72
  */
@@ -79,11 +76,11 @@ export class CliAgent {
79
76
  public constructor(private readonly options: CliAgentOptions) {}
80
77
 
81
78
  /**
82
- * Runs one non-interactive agent turn through `ptbk agent exec`.
79
+ * Runs one non-interactive agent turn through the selected harness.
83
80
  *
84
81
  * @param message - User message sent to the agent.
85
- * @param options - Optional per-run CLI overrides.
86
- * @returns Raw stdout emitted by the CLI command.
82
+ * @param options - Optional per-run overrides.
83
+ * @returns Final agent answer.
87
84
  */
88
85
  public async run(message: string, options: CliAgentRunOptions = {}): Promise<string> {
89
86
  const normalizedMessage = message.trim();
@@ -96,27 +93,39 @@ export class CliAgent {
96
93
  const agentPath = await this.resolveExecutableAgentPath(resolvedSource);
97
94
  const mergedOptions = mergeCliAgentRunOptions(this.options, options);
98
95
 
99
- return $execCommand({
100
- command: this.options.command || DEFAULT_CLI_AGENT_COMMAND,
101
- args: createCliAgentExecArguments({
102
- agentPath,
103
- allowCredits: mergedOptions.allowCredits ?? false,
104
- context: mergedOptions.context,
105
- harness: mergedOptions.harness,
106
- message: normalizedMessage,
107
- model: mergedOptions.model,
108
- noUi: mergedOptions.noUi ?? DEFAULT_CLI_AGENT_IS_NO_UI,
109
- thinkingLevel: mergedOptions.thinkingLevel,
110
- }),
111
- cwd: resolvedSource.currentWorkingDirectory,
112
- crashOnError: true,
113
- timeout: Infinity,
114
- isVerbose: false,
96
+ const harness = mergedOptions.harness ?? resolveCliAgentHarnessFromEnv();
97
+
98
+ if (!harness) {
99
+ throw new NotAllowed(
100
+ spaceTrim(`
101
+ No harness specified for \`CliAgent\`. Pass \`harness\` in the constructor options or per-run options,
102
+ or set the \`${PTBK_HARNESS_ENV}\` environment variable.
103
+
104
+ Available harnesses: ${CLI_AGENT_HARNESS_NAMES.join(', ')}
105
+
106
+ Example: \`PTBK_HARNESS=claude-code\`
107
+ `),
108
+ );
109
+ }
110
+
111
+ const result = await executeAgentChatTurn({
112
+ agentPath,
113
+ messages: [{ sender: 'USER', content: normalizedMessage }],
114
+ agentName: harness,
115
+ model: mergedOptions.model ?? process.env[PTBK_MODEL_ENV],
116
+ noUi: mergedOptions.noUi ?? DEFAULT_CLI_AGENT_IS_NO_UI,
117
+ thinkingLevel: mergedOptions.thinkingLevel ?? resolveCliAgentThinkingLevelFromEnv(),
118
+ allowCredits: mergedOptions.allowCredits ?? false,
119
+ isVerbose: mergedOptions.isVerbose ?? false,
120
+ context: mergedOptions.context,
121
+ currentWorkingDirectory: resolvedSource.currentWorkingDirectory,
115
122
  });
123
+
124
+ return result.answer;
116
125
  }
117
126
 
118
127
  /**
119
- * Resolves the agent path passed to the CLI, materializing one temporary `.book` file when needed.
128
+ * Resolves the agent path passed to the runner, materializing one temporary `.book` file when needed.
120
129
  *
121
130
  * @private internal utility of `CliAgent`
122
131
  */
@@ -137,7 +146,7 @@ export class CliAgent {
137
146
  }
138
147
 
139
148
  /**
140
- * Merges constructor defaults with per-run CLI overrides.
149
+ * Merges constructor defaults with per-run overrides.
141
150
  *
142
151
  * @private internal utility of `CliAgent`
143
152
  */
@@ -146,6 +155,7 @@ function mergeCliAgentRunOptions(defaults: CliAgentRunOptions, overrides: CliAge
146
155
  allowCredits: overrides.allowCredits ?? defaults.allowCredits,
147
156
  context: overrides.context ?? defaults.context,
148
157
  harness: overrides.harness ?? defaults.harness,
158
+ isVerbose: overrides.isVerbose ?? defaults.isVerbose,
149
159
  model: overrides.model ?? defaults.model,
150
160
  noUi: overrides.noUi ?? defaults.noUi,
151
161
  thinkingLevel: overrides.thinkingLevel ?? defaults.thinkingLevel,
@@ -153,47 +163,53 @@ function mergeCliAgentRunOptions(defaults: CliAgentRunOptions, overrides: CliAge
153
163
  }
154
164
 
155
165
  /**
156
- * Builds CLI arguments for `ptbk agent exec`.
166
+ * Reads and validates the harness name from the `PTBK_HARNESS` environment variable.
157
167
  *
158
168
  * @private internal utility of `CliAgent`
159
169
  */
160
- function createCliAgentExecArguments(options: {
161
- readonly agentPath: string;
162
- readonly allowCredits: boolean;
163
- readonly context?: string;
164
- readonly harness?: CliAgentHarness;
165
- readonly message: string;
166
- readonly model?: string;
167
- readonly noUi: boolean;
168
- readonly thinkingLevel?: CliAgentThinkingLevel;
169
- }): Array<string> {
170
- const argumentsList = ['agent', 'exec', '--agent', options.agentPath, '--message', options.message];
170
+ function resolveCliAgentHarnessFromEnv(): CliAgentHarness | undefined {
171
+ const envValue = process.env[PTBK_HARNESS_ENV];
171
172
 
172
- if (options.harness) {
173
- argumentsList.push('--harness', options.harness);
173
+ if (!envValue) {
174
+ return undefined;
174
175
  }
175
176
 
176
- if (options.model) {
177
- argumentsList.push('--model', options.model);
178
- }
177
+ if (!(CLI_AGENT_HARNESS_NAMES as ReadonlyArray<string>).includes(envValue)) {
178
+ throw new NotAllowed(
179
+ spaceTrim(`
180
+ Invalid value for \`${PTBK_HARNESS_ENV}\` environment variable: \`${envValue}\`
179
181
 
180
- if (options.noUi) {
181
- argumentsList.push('--no-ui');
182
+ Must be one of: ${CLI_AGENT_HARNESS_NAMES.join(', ')}
183
+ `),
184
+ );
182
185
  }
183
186
 
184
- if (options.thinkingLevel) {
185
- argumentsList.push('--thinking-level', options.thinkingLevel);
186
- }
187
+ return envValue as CliAgentHarness;
188
+ }
187
189
 
188
- if (options.allowCredits) {
189
- argumentsList.push('--allow-credits');
190
+ /**
191
+ * Reads and validates the thinking level from the `PTBK_THINKING_LEVEL` environment variable.
192
+ *
193
+ * @private internal utility of `CliAgent`
194
+ */
195
+ function resolveCliAgentThinkingLevelFromEnv(): CliAgentThinkingLevel | undefined {
196
+ const envValue = process.env[PTBK_THINKING_LEVEL_ENV];
197
+
198
+ if (!envValue) {
199
+ return undefined;
190
200
  }
191
201
 
192
- if (options.context?.trim()) {
193
- argumentsList.push('--context', options.context.trim());
202
+ if (!(CLI_AGENT_THINKING_LEVEL_VALUES as ReadonlyArray<string>).includes(envValue)) {
203
+ throw new NotAllowed(
204
+ spaceTrim(`
205
+ Invalid value for \`${PTBK_THINKING_LEVEL_ENV}\` environment variable: \`${envValue}\`
206
+
207
+ Must be one of: ${CLI_AGENT_THINKING_LEVEL_VALUES.join(', ')}
208
+ `),
209
+ );
194
210
  }
195
211
 
196
- return argumentsList;
212
+ return envValue as CliAgentThinkingLevel;
197
213
  }
198
214
 
199
215
  /**
@@ -214,7 +230,7 @@ function createCliAgentTemporaryBookPath(resolvedSource: ResolvedBookNodeAgentSo
214
230
  }
215
231
 
216
232
  /**
217
- * Keeps in-memory Book source readable when persisted for the CLI wrapper.
233
+ * Keeps in-memory Book source readable when persisted for the runner.
218
234
  *
219
235
  * @private internal utility of `CliAgent`
220
236
  */
@@ -0,0 +1,38 @@
1
+ import { join } from 'path';
2
+
3
+ // Note: [💞] Ignore a discrepancy between file name and entity name
4
+
5
+ /**
6
+ * Relative path to the local agent source used by the agent folder convention.
7
+ *
8
+ * @public exported from `@promptbook/node`
9
+ */
10
+ export const AGENT_BOOK_FILE_PATH = 'agent.book';
11
+
12
+ /**
13
+ * Relative path to the message queue root used by the agent folder convention.
14
+ *
15
+ * @public exported from `@promptbook/node`
16
+ */
17
+ export const AGENT_MESSAGES_DIRECTORY_PATH = 'messages';
18
+
19
+ /**
20
+ * Relative path to queued user messages consumed by the agent runner.
21
+ *
22
+ * @public exported from `@promptbook/node`
23
+ */
24
+ export const AGENT_QUEUED_MESSAGES_DIRECTORY_PATH = join(AGENT_MESSAGES_DIRECTORY_PATH, 'queued');
25
+
26
+ /**
27
+ * Relative path to answered messages written by the agent runner.
28
+ *
29
+ * @public exported from `@promptbook/node`
30
+ */
31
+ export const AGENT_FINISHED_MESSAGES_DIRECTORY_PATH = join(AGENT_MESSAGES_DIRECTORY_PATH, 'finished');
32
+
33
+ /**
34
+ * Relative path to messages that the agent runner stopped retrying.
35
+ *
36
+ * @public exported from `@promptbook/node`
37
+ */
38
+ export const AGENT_FAILED_MESSAGES_DIRECTORY_PATH = join(AGENT_MESSAGES_DIRECTORY_PATH, 'failed');
@@ -0,0 +1,46 @@
1
+ // Note: [💞] Ignore a discrepancy between file name and entity name
2
+
3
+ /**
4
+ * All CLI harness names supported by `CliAgent` and `ptbk agent exec`.
5
+ *
6
+ * @public exported from `@promptbook/node`
7
+ */
8
+ export const CLI_AGENT_HARNESS_NAMES = [
9
+ 'openai-codex',
10
+ 'github-copilot',
11
+ 'cline',
12
+ 'claude-code',
13
+ 'opencode',
14
+ 'gemini',
15
+ ] as const;
16
+
17
+ /**
18
+ * All supported thinking-level values for CLI coding-agent runners.
19
+ *
20
+ * @public exported from `@promptbook/node`
21
+ */
22
+ export const CLI_AGENT_THINKING_LEVEL_VALUES = ['low', 'medium', 'high', 'xhigh'] as const;
23
+
24
+ /**
25
+ * Environment variable used as the default runner identifier when `--harness` is omitted or not set in `CliAgent`.
26
+ *
27
+ * Set this to one of the harness names (`openai-codex`, `github-copilot`, `cline`, `claude-code`, `opencode`, `gemini`)
28
+ * so that `CliAgent` and `ptbk agent exec` can run without an explicit `harness` option.
29
+ *
30
+ * @public exported from `@promptbook/node`
31
+ */
32
+ export const PTBK_HARNESS_ENV = 'PTBK_HARNESS';
33
+
34
+ /**
35
+ * Environment variable used as the default runner model when `--model` is omitted or not set in `CliAgent`.
36
+ *
37
+ * @public exported from `@promptbook/node`
38
+ */
39
+ export const PTBK_MODEL_ENV = 'PTBK_MODEL';
40
+
41
+ /**
42
+ * Environment variable used as the default thinking level when `--thinking-level` is omitted or not set in `CliAgent`.
43
+ *
44
+ * @public exported from `@promptbook/node`
45
+ */
46
+ export const PTBK_THINKING_LEVEL_ENV = 'PTBK_THINKING_LEVEL';
@@ -7,7 +7,6 @@ import { createPortal } from 'react-dom';
7
7
  import type { Promisable } from 'type-fest';
8
8
  import type { string_book } from '../../book-2.0/agent-source/string_book';
9
9
  import { DEFAULT_BOOK } from '../../book-2.0/agent-source/string_book';
10
- import { DEFAULT_IS_VERBOSE } from '../../config';
11
10
  import type { number_percent } from '../../types/number_percent';
12
11
  import type { number_positive } from '../../types/number_positive';
13
12
  import type { string_knowledge_source_content } from '../../types/string_knowledge_source_content';
@@ -15,6 +14,7 @@ import type { string_css_value } from '../../types/string_markdown';
15
14
  import { countLines } from '../../utils/expectation-counters/countLines';
16
15
  import type { HoistedMenuItem } from '../_common/MenuHoisting/MenuHoistingContext';
17
16
  import { classNames } from '../_common/react-utils/classNames';
17
+ import { DEFAULT_IS_VERBOSE } from './BookEditorBrowserConfig';
18
18
  import styles from './BookEditor.module.css';
19
19
  import { BookEditorMonaco } from './BookEditorMonaco';
20
20
  import type { BookEditorTheme } from './BookEditorTheme';
@@ -1,9 +1,7 @@
1
- import { PUBLIC_AGENTS_SERVERS } from '../../../servers';
2
- import { CLAIM, IS_COST_PREVENTED, NAME } from '../../config';
3
1
  import { BOOK_LANGUAGE_VERSION, PROMPTBOOK_ENGINE_VERSION } from '../../version';
4
2
  import { $detectRuntimeEnvironment } from '../../utils/environment/$detectRuntimeEnvironment';
5
3
  import type { AboutPromptbookInformationOptions } from '../../utils/misc/aboutPromptbookInformation';
6
- import { valueToString } from '../../utils/parameters/valueToString';
4
+ import { CLAIM, IS_COST_PREVENTED, NAME, PUBLIC_AGENTS_SERVERS } from './BookEditorBrowserConfig';
7
5
  import styles from './BookEditor.module.css';
8
6
 
9
7
  /**
@@ -63,7 +61,7 @@ export function BookEditorAboutPromptbookInformation(props: AboutPromptbookInfor
63
61
  <ul>
64
62
  {Object.entries(runtimeEnvironmentInfo).map(([key, value]) => (
65
63
  <li key={key}>
66
- <strong>{key}:</strong> {valueToString(value)}
64
+ <strong>{key}:</strong> {String(value)}
67
65
  </li>
68
66
  ))}
69
67
  </ul>
@@ -6,7 +6,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
6
  // Note: Do not import from `../../package.json` because it would be included in the bundle
7
7
  // TODO: [🧠] Is there a better way to get the version?
8
8
  import { DEFAULT_BOOK, validateBook } from '../../book-2.0/agent-source/string_book';
9
- import { $induceBookDownload } from '../../utils/files/$induceBookDownload';
10
9
  import { AboutIcon } from '../icons/AboutIcon';
11
10
  import { AttachmentIcon } from '../icons/AttachmentIcon';
12
11
  import { CameraIcon } from '../icons/CameraIcon';
@@ -37,6 +36,37 @@ type BookEditorActionbarProps = {
37
36
  hoistedMenuItems?: ReadonlyArray<HoistedMenuItem>;
38
37
  };
39
38
 
39
+ function induceBookDownloadInBrowser(book: string): void {
40
+ const filename = `${normalizeBookDownloadName(extractBookTitle(book) || 'AI Avatar')}.book`;
41
+ const objectUrl = URL.createObjectURL(new Blob([book], { type: 'application/json' }));
42
+ const link = window.document.createElement('a');
43
+
44
+ link.href = objectUrl;
45
+ link.download = filename;
46
+ link.click();
47
+ URL.revokeObjectURL(objectUrl);
48
+ }
49
+
50
+ function extractBookTitle(book: string): string | null {
51
+ return (
52
+ book
53
+ .split(/\r?\n/)
54
+ .map((line) => line.trim())
55
+ .find(Boolean) || null
56
+ );
57
+ }
58
+
59
+ function normalizeBookDownloadName(value: string): string {
60
+ return (
61
+ value
62
+ .normalize('NFD')
63
+ .replace(/[\u0300-\u036f]/g, '')
64
+ .replace(/[^\dA-Za-z]+/g, '-')
65
+ .replace(/^-+|-+$/g, '')
66
+ .toLowerCase() || 'ai-avatar'
67
+ );
68
+ }
69
+
40
70
  /**
41
71
  * Handles book editor actionbar.
42
72
  *
@@ -67,7 +97,7 @@ export function BookEditorActionbar(props: BookEditorActionbarProps) {
67
97
 
68
98
  const handleDownload = useCallback(() => {
69
99
  const book = validateBook(valueRef.current || DEFAULT_BOOK);
70
- /* not await */ $induceBookDownload(book);
100
+ induceBookDownloadInBrowser(book);
71
101
  }, []);
72
102
 
73
103
  const actions = useMemo(() => {
@@ -0,0 +1,11 @@
1
+ // Re-export browser-compatible constants from canonical sources to avoid duplicate entity names
2
+ export {
3
+ NAME,
4
+ CLAIM,
5
+ PROMPTBOOK_LEGAL_ENTITY,
6
+ IS_COST_PREVENTED,
7
+ DEFAULT_IS_VERBOSE,
8
+ DEFAULT_MAX_CONCURRENT_UPLOADS,
9
+ PROMPTBOOK_SYNTAX_COLORS,
10
+ } from '../../config';
11
+ export { PUBLIC_AGENTS_SERVERS } from '../../../servers';
@@ -0,0 +1,33 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { BookEditorProps } from './BookEditor';
3
+
4
+ type BookEditorComponent = typeof import('./BookEditor').BookEditor;
5
+
6
+ /**
7
+ * Renders a book editor
8
+ *
9
+ * @public exported from `@promptbook/components`
10
+ */
11
+ export function BookEditorForClient(props: BookEditorProps) {
12
+ const [BookEditorComponent, setBookEditorComponent] = useState<BookEditorComponent | null>(null);
13
+
14
+ useEffect(() => {
15
+ let isCancelled = false;
16
+
17
+ void import('./BookEditor').then(({ BookEditor }) => {
18
+ if (!isCancelled) {
19
+ setBookEditorComponent(() => BookEditor);
20
+ }
21
+ });
22
+
23
+ return () => {
24
+ isCancelled = true;
25
+ };
26
+ }, []);
27
+
28
+ if (!BookEditorComponent) {
29
+ return <div style={{ minHeight: 260 }} />;
30
+ }
31
+
32
+ return <BookEditorComponent {...props} />;
33
+ }
@@ -13,7 +13,7 @@ import styles from './BookEditor.module.css';
13
13
  import { BookEditorActionbar } from './BookEditorActionbar';
14
14
  import { BookEditorMonacoConstants } from './BookEditorMonacoConstants';
15
15
  import { BOOK_EDITOR_RENDER_THEME, resolveBookEditorRenderTheme } from './BookEditorTheme';
16
- import { createDeprecatedCommitmentDiagnostics } from './createDeprecatedCommitmentDiagnostics';
16
+ import { createDeprecatedCommitmentDiagnostics } from './createDeprecatedCommitmentDiagnostics.browser';
17
17
  import { useBookEditorMonacoDecorations } from './useBookEditorMonacoDecorations';
18
18
  import { useBookEditorMonacoDiagnostics } from './useBookEditorMonacoDiagnostics';
19
19
  import { useBookEditorMonacoInteractions } from './useBookEditorMonacoInteractions';
@@ -1,7 +1,3 @@
1
- import type { string_book } from '../../book-2.0/agent-source/string_book';
2
- import { parseAgentSourceWithCommitments } from '../../book-2.0/agent-source/parseAgentSourceWithCommitments';
3
- import type { BookCommitment } from '../../commitments/_base/BookCommitment';
4
-
5
1
  /**
6
2
  * Regex source for absolute URL references inside TEAM/FROM/IMPORT commitments.
7
3
  *
@@ -28,12 +24,41 @@ const AGENT_REFERENCE_BRACED_PATTERN = '\\{[^{}\\r\\n]+\\}';
28
24
  *
29
25
  * @private function of BookEditorMonaco
30
26
  */
31
- const AGENT_REFERENCE_COMMITMENT_TYPES: ReadonlySet<BookCommitment> = new Set<BookCommitment>([
27
+ const AGENT_REFERENCE_COMMITMENT_TYPES = ['FROM', 'IMPORT', 'IMPORTS', 'TEAM'] as const;
28
+
29
+ /**
30
+ * Commitment types known to the browser editor tokenizer.
31
+ *
32
+ * @private function of BookEditorMonaco
33
+ */
34
+ const BOOK_EDITOR_COMMITMENT_TYPES = [
35
+ 'PERSONA',
36
+ 'KNOWLEDGE',
37
+ 'TASK',
38
+ 'PROMPT',
39
+ 'EXPECT',
40
+ 'FORMAT',
41
+ 'MODEL',
42
+ 'SAMPLE',
32
43
  'FROM',
33
44
  'IMPORT',
34
45
  'IMPORTS',
35
46
  'TEAM',
36
- ]);
47
+ 'TODO',
48
+ 'NOTE',
49
+ 'NOTES',
50
+ 'NONCE',
51
+ ] as const;
52
+
53
+ /**
54
+ * Regex pattern to match horizontal lines.
55
+ *
56
+ * @private function of BookEditorMonaco
57
+ */
58
+ const HORIZONTAL_LINE_PATTERN = /^[\s]*[-_*][\s]*[-_*][\s]*[-_*][\s]*[-_*]*[\s]*$/;
59
+
60
+ const BOOK_EDITOR_COMMITMENT_LINE_REGEX = createCommitmentLineRegex(BOOK_EDITOR_COMMITMENT_TYPES);
61
+ const AGENT_REFERENCE_COMMITMENT_LINE_REGEX = createCommitmentLineRegex(AGENT_REFERENCE_COMMITMENT_TYPES);
37
62
 
38
63
  /**
39
64
  * Range descriptor for one commitment block in source coordinates.
@@ -249,31 +274,74 @@ function collectAgentReferenceCommitmentLineRanges(
249
274
  content: string,
250
275
  sourceLines: ReadonlyArray<string>,
251
276
  ): Array<CommitmentLineRange> {
252
- const parsed = parseAgentSourceWithCommitments(content as string_book);
253
277
  const ranges: Array<CommitmentLineRange> = [];
278
+ let activeRangeStartLineNumber: number | null = null;
279
+ let isInsideCodeBlock = false;
280
+ const startLineIndex = findBookBodyStartLineIndex(sourceLines);
281
+
282
+ for (let lineIndex = startLineIndex; lineIndex < sourceLines.length; lineIndex++) {
283
+ const line = sourceLines[lineIndex] || '';
284
+ const trimmedLine = line.trim();
254
285
 
255
- for (let index = 0; index < parsed.commitments.length; index++) {
256
- const commitment = parsed.commitments[index];
257
- if (!commitment || !AGENT_REFERENCE_COMMITMENT_TYPES.has(commitment.type)) {
286
+ if (trimmedLine.startsWith('```')) {
287
+ isInsideCodeBlock = !isInsideCodeBlock;
258
288
  continue;
259
289
  }
260
290
 
261
- const nextCommitmentStartLine = parsed.commitments[index + 1]?.lineNumber ?? sourceLines.length + 1;
262
- const endLineNumber = Math.min(nextCommitmentStartLine - 1, sourceLines.length);
291
+ if (isInsideCodeBlock) {
292
+ continue;
293
+ }
263
294
 
264
- if (commitment.lineNumber > endLineNumber) {
295
+ if (HORIZONTAL_LINE_PATTERN.test(line)) {
296
+ if (activeRangeStartLineNumber !== null) {
297
+ ranges.push({
298
+ startLineNumber: activeRangeStartLineNumber,
299
+ endLineNumber: lineIndex,
300
+ });
301
+ activeRangeStartLineNumber = null;
302
+ }
303
+ continue;
304
+ }
305
+
306
+ if (!BOOK_EDITOR_COMMITMENT_LINE_REGEX.test(trimmedLine)) {
265
307
  continue;
266
308
  }
267
309
 
310
+ if (activeRangeStartLineNumber !== null) {
311
+ ranges.push({
312
+ startLineNumber: activeRangeStartLineNumber,
313
+ endLineNumber: lineIndex,
314
+ });
315
+ }
316
+
317
+ activeRangeStartLineNumber = AGENT_REFERENCE_COMMITMENT_LINE_REGEX.test(trimmedLine) ? lineIndex + 1 : null;
318
+ }
319
+
320
+ if (activeRangeStartLineNumber !== null) {
268
321
  ranges.push({
269
- startLineNumber: commitment.lineNumber,
270
- endLineNumber,
322
+ startLineNumber: activeRangeStartLineNumber,
323
+ endLineNumber: sourceLines.length,
271
324
  });
272
325
  }
273
326
 
274
327
  return ranges;
275
328
  }
276
329
 
330
+ function createCommitmentLineRegex(commitmentTypes: ReadonlyArray<string>): RegExp {
331
+ const commitmentPattern = [...commitmentTypes]
332
+ .sort((a, b) => b.length - a.length)
333
+ .map((type) => type.replace(/\s+/, '\\s+'))
334
+ .join('|');
335
+
336
+ return new RegExp(`^\\s*(${commitmentPattern})(?=\\s|$)`, 'i');
337
+ }
338
+
339
+ function findBookBodyStartLineIndex(sourceLines: ReadonlyArray<string>): number {
340
+ const titleLineIndex = sourceLines.findIndex((line) => line.trim().length > 0);
341
+
342
+ return titleLineIndex === -1 ? 0 : titleLineIndex + 1;
343
+ }
344
+
277
345
  /**
278
346
  * Agent reference helpers for `BookEditorMonaco`.
279
347
  *
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Browser-compatible stub for `createDeprecatedCommitmentDiagnostics`.
3
+ *
4
+ * In the browser build the full Node.js implementation is not available,
5
+ * so this stub always returns an empty array to keep the editor functional.
6
+ *
7
+ * @private internal utility of `BookEditorMonaco`
8
+ */
9
+ export function createDeprecatedCommitmentDiagnostics(_agentSource?: string): [] {
10
+ return [];
11
+ }