@promptbook/remote-server 0.114.0-35 → 0.114.0-38

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.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Removes duplicated instructions from a generated system message
3
+ *
4
+ * Commitments are applied one by one, so a commitment used multiple times in one book repeats its whole
5
+ * section, including the shared guidance preamble. For example two `WRITING RULES` commitments emit the
6
+ * `## Writing rules` heading and its guidance paragraph twice. This function merges all sections sharing
7
+ * one heading into the position of their first occurrence and keeps every identical block only once.
8
+ *
9
+ * @param systemMessage The assembled system message which may contain repeated sections and blocks
10
+ * @returns The system message where each section heading and each identical block appears exactly once
11
+ *
12
+ * @private internal utility of `createAgentModelRequirementsWithCommitments`
13
+ */
14
+ export declare function deduplicateSystemMessage(systemMessage: string): string;
@@ -0,0 +1,8 @@
1
+ import type { Command } from 'commander';
2
+ /**
3
+ * Registers the CLI commands on a fresh Commander program without parsing arguments or running an action.
4
+ * Tests can configure output and exit handling before registration, so subcommands inherit those settings.
5
+ *
6
+ * @private internal utility of `promptbookCli`
7
+ */
8
+ export declare function $initializePromptbookCliProgram(program: Command): void;
@@ -15,7 +15,7 @@ export declare const BOOK_LANGUAGE_VERSION: string_semantic_version;
15
15
  export declare const PROMPTBOOK_ENGINE_VERSION: string_promptbook_version;
16
16
  /**
17
17
  * Represents the version string of the Promptbook engine.
18
- * It follows semantic versioning (e.g., `0.114.0-34`).
18
+ * It follows semantic versioning (e.g., `0.114.0-37`).
19
19
  *
20
20
  * @generated
21
21
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptbook/remote-server",
3
- "version": "0.114.0-35",
3
+ "version": "0.114.0-38",
4
4
  "description": "Promptbook: Create persistent AI agents that turn your company's scattered knowledge into action",
5
5
  "private": false,
6
6
  "sideEffects": false,
@@ -96,7 +96,7 @@
96
96
  "types": "./esm/src/_packages/remote-server.index.d.ts",
97
97
  "typings": "./esm/src/_packages/remote-server.index.d.ts",
98
98
  "peerDependencies": {
99
- "@promptbook/core": "0.114.0-35"
99
+ "@promptbook/core": "0.114.0-38"
100
100
  },
101
101
  "dependencies": {
102
102
  "@openai/agents": "0.4.15",
package/umd/index.umd.js CHANGED
@@ -50,7 +50,7 @@
50
50
  * @generated
51
51
  * @see https://github.com/webgptorg/promptbook
52
52
  */
53
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-35';
53
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-38';
54
54
  /**
55
55
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
56
56
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -31129,6 +31129,245 @@
31129
31129
  return Object.keys(rest).length > 0 ? rest : undefined;
31130
31130
  }
31131
31131
 
31132
+ /**
31133
+ * Pattern matching a markdown section heading (`## Title`) that separates system-message sections
31134
+ *
31135
+ * @private internal constant of `deduplicateSystemMessage`
31136
+ */
31137
+ const SYSTEM_MESSAGE_SECTION_HEADING_PATTERN = /^##(?!#)\s*(.+?)\s*$/;
31138
+ /**
31139
+ * Pattern matching a fenced code block delimiter which suspends section and block splitting
31140
+ *
31141
+ * @private internal constant of `deduplicateSystemMessage`
31142
+ */
31143
+ const CODE_FENCE_PATTERN = /^\s*(?:```|~~~)/;
31144
+ /**
31145
+ * Pattern matching a markdown list item which keeps merged bullet lists visually compact
31146
+ *
31147
+ * @private internal constant of `deduplicateSystemMessage`
31148
+ */
31149
+ const LIST_ITEM_PATTERN = /^\s*(?:[-*+]|\d+[.)])\s/;
31150
+ /**
31151
+ * Removes duplicated instructions from a generated system message
31152
+ *
31153
+ * Commitments are applied one by one, so a commitment used multiple times in one book repeats its whole
31154
+ * section, including the shared guidance preamble. For example two `WRITING RULES` commitments emit the
31155
+ * `## Writing rules` heading and its guidance paragraph twice. This function merges all sections sharing
31156
+ * one heading into the position of their first occurrence and keeps every identical block only once.
31157
+ *
31158
+ * @param systemMessage The assembled system message which may contain repeated sections and blocks
31159
+ * @returns The system message where each section heading and each identical block appears exactly once
31160
+ *
31161
+ * @private internal utility of `createAgentModelRequirementsWithCommitments`
31162
+ */
31163
+ function deduplicateSystemMessage(systemMessage) {
31164
+ if (!systemMessage.trim()) {
31165
+ return systemMessage;
31166
+ }
31167
+ return splitSystemMessageIntoRegions(systemMessage)
31168
+ .reduce(mergeRegionSharingHeading, [])
31169
+ .map(removeDuplicateBlocksFromRegion)
31170
+ .map(renderRegion)
31171
+ .filter((renderedRegion) => renderedRegion !== '')
31172
+ .join('\n\n');
31173
+ }
31174
+ /**
31175
+ * Splits the system message into the intro region and one region per `## Title` section
31176
+ *
31177
+ * @param systemMessage The system message to split
31178
+ * @returns Regions in their original order, without empty ones
31179
+ *
31180
+ * @private internal utility of `deduplicateSystemMessage`
31181
+ */
31182
+ function splitSystemMessageIntoRegions(systemMessage) {
31183
+ const regions = [];
31184
+ let headingLine = null;
31185
+ let bodyLines = [];
31186
+ let isInsideCodeFence = false;
31187
+ for (const line of systemMessage.split(/\r?\n/)) {
31188
+ if (CODE_FENCE_PATTERN.test(line)) {
31189
+ isInsideCodeFence = !isInsideCodeFence;
31190
+ }
31191
+ if (isInsideCodeFence || !SYSTEM_MESSAGE_SECTION_HEADING_PATTERN.test(line)) {
31192
+ bodyLines.push(line);
31193
+ continue;
31194
+ }
31195
+ regions.push(createRegion(headingLine, bodyLines));
31196
+ headingLine = line;
31197
+ bodyLines = [];
31198
+ }
31199
+ regions.push(createRegion(headingLine, bodyLines));
31200
+ return regions.filter((region) => region.headingLine !== null || region.blocks.length > 0);
31201
+ }
31202
+ /**
31203
+ * Creates one region from its heading line and its raw body lines
31204
+ *
31205
+ * @param headingLine The original `## Title` line, or `null` for the intro before the first heading
31206
+ * @param bodyLines The raw lines following the heading line
31207
+ * @returns The region with its body already split into blocks
31208
+ *
31209
+ * @private internal utility of `splitSystemMessageIntoRegions`
31210
+ */
31211
+ function createRegion(headingLine, bodyLines) {
31212
+ return {
31213
+ titleKey: headingLine === null ? null : normalizeSectionTitle(headingLine),
31214
+ headingLine,
31215
+ isBodySeparatedByBlankLine: bodyLines[0] !== undefined && bodyLines[0].trim() === '',
31216
+ blocks: splitBodyIntoBlocks(bodyLines),
31217
+ };
31218
+ }
31219
+ /**
31220
+ * Normalizes a heading line into a key usable for matching sections which belong together
31221
+ *
31222
+ * @param headingLine The original `## Title` line
31223
+ * @returns Lowercased title without the markdown heading prefix
31224
+ *
31225
+ * @private internal utility of `createRegion`
31226
+ */
31227
+ function normalizeSectionTitle(headingLine) {
31228
+ var _a, _b;
31229
+ return ((_b = (_a = SYSTEM_MESSAGE_SECTION_HEADING_PATTERN.exec(headingLine)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : headingLine).toLowerCase();
31230
+ }
31231
+ /**
31232
+ * Splits raw body lines into blocks separated by blank lines while keeping fenced code blocks intact
31233
+ *
31234
+ * @param bodyLines The raw lines of one region body
31235
+ * @returns Non-empty blocks in their original order
31236
+ *
31237
+ * @private internal utility of `createRegion`
31238
+ */
31239
+ function splitBodyIntoBlocks(bodyLines) {
31240
+ const blockTexts = [];
31241
+ let blockLines = [];
31242
+ let isInsideCodeFence = false;
31243
+ for (const line of bodyLines) {
31244
+ if (CODE_FENCE_PATTERN.test(line)) {
31245
+ isInsideCodeFence = !isInsideCodeFence;
31246
+ }
31247
+ if (!isInsideCodeFence && line.trim() === '') {
31248
+ blockTexts.push(blockLines.join('\n'));
31249
+ blockLines = [];
31250
+ continue;
31251
+ }
31252
+ blockLines.push(line);
31253
+ }
31254
+ blockTexts.push(blockLines.join('\n'));
31255
+ return blockTexts
31256
+ .filter((blockText) => blockText.trim() !== '')
31257
+ .map((blockText) => ({ text: blockText, isOpeningMergedSection: false }));
31258
+ }
31259
+ /**
31260
+ * Appends one region to the already merged regions, merging it into an earlier region with the same heading
31261
+ *
31262
+ * @param mergedRegions Regions merged so far, used as the reducer accumulator
31263
+ * @param region The next region in original order
31264
+ * @returns The accumulator with the region either appended or merged into its earlier twin
31265
+ *
31266
+ * @private internal utility of `deduplicateSystemMessage`
31267
+ */
31268
+ function mergeRegionSharingHeading(mergedRegions, region) {
31269
+ const existingRegionIndex = mergedRegions.findIndex((mergedRegion) => region.titleKey !== null && mergedRegion.titleKey === region.titleKey);
31270
+ if (existingRegionIndex === -1) {
31271
+ return [...mergedRegions, region];
31272
+ }
31273
+ return mergedRegions.map((mergedRegion, index) => index !== existingRegionIndex
31274
+ ? mergedRegion
31275
+ : {
31276
+ ...mergedRegion,
31277
+ blocks: [...mergedRegion.blocks, ...markFirstBlockAsOpeningMergedSection(region.blocks)],
31278
+ });
31279
+ }
31280
+ /**
31281
+ * Marks the first block of a merged section occurrence so the merge seam can be rendered as one list
31282
+ *
31283
+ * @param blocks The blocks of the section occurrence being merged into an earlier section
31284
+ * @returns The same blocks with the first one marked as opening a merged section
31285
+ *
31286
+ * @private internal utility of `mergeRegionSharingHeading`
31287
+ */
31288
+ function markFirstBlockAsOpeningMergedSection(blocks) {
31289
+ return blocks.map((block, index) => (index === 0 ? { ...block, isOpeningMergedSection: true } : block));
31290
+ }
31291
+ /**
31292
+ * Keeps only the first occurrence of every identical block inside one region
31293
+ *
31294
+ * @param region The region whose blocks may repeat
31295
+ * @returns The region without repeated blocks
31296
+ *
31297
+ * @private internal utility of `deduplicateSystemMessage`
31298
+ */
31299
+ function removeDuplicateBlocksFromRegion(region) {
31300
+ const alreadyRenderedTexts = new Set();
31301
+ const uniqueBlocks = [];
31302
+ let isMergeSeamPending = false;
31303
+ for (const block of region.blocks) {
31304
+ if (alreadyRenderedTexts.has(block.text)) {
31305
+ // Note: A removed block must still hand its merge seam over to the next block which survives
31306
+ isMergeSeamPending = isMergeSeamPending || block.isOpeningMergedSection;
31307
+ continue;
31308
+ }
31309
+ alreadyRenderedTexts.add(block.text);
31310
+ uniqueBlocks.push({
31311
+ text: block.text,
31312
+ isOpeningMergedSection: block.isOpeningMergedSection || isMergeSeamPending,
31313
+ });
31314
+ isMergeSeamPending = false;
31315
+ }
31316
+ return { ...region, blocks: uniqueBlocks };
31317
+ }
31318
+ /**
31319
+ * Renders one region back into its markdown representation
31320
+ *
31321
+ * @param region The region to render
31322
+ * @returns The heading line together with its joined blocks
31323
+ *
31324
+ * @private internal utility of `deduplicateSystemMessage`
31325
+ */
31326
+ function renderRegion(region) {
31327
+ const body = joinBlocks(region.blocks);
31328
+ if (region.headingLine === null) {
31329
+ return body;
31330
+ }
31331
+ if (body === '') {
31332
+ return region.headingLine;
31333
+ }
31334
+ return `${region.headingLine}${region.isBodySeparatedByBlankLine ? '\n\n' : '\n'}${body}`;
31335
+ }
31336
+ /**
31337
+ * Joins blocks of one region back together
31338
+ *
31339
+ * @param blocks The blocks to join in their original order
31340
+ * @returns The joined body of one region
31341
+ *
31342
+ * @private internal utility of `renderRegion`
31343
+ */
31344
+ function joinBlocks(blocks) {
31345
+ return blocks
31346
+ .map((block, index) => index === 0 ? block.text : `${createBlockSeparator(blocks[index - 1], block)}${block.text}`)
31347
+ .join('');
31348
+ }
31349
+ /**
31350
+ * Chooses the separator between two neighboring blocks
31351
+ *
31352
+ * Blocks are normally separated by a blank line, exactly as they were written. Only where two sections were
31353
+ * merged together their lists are joined into one compact list instead of two separate ones.
31354
+ *
31355
+ * @param previousBlock The block rendered before the separator
31356
+ * @param nextBlock The block rendered after the separator
31357
+ * @returns Either a single or a double newline
31358
+ *
31359
+ * @private internal utility of `joinBlocks`
31360
+ */
31361
+ function createBlockSeparator(previousBlock, nextBlock) {
31362
+ if (!nextBlock.isOpeningMergedSection) {
31363
+ return '\n\n';
31364
+ }
31365
+ const previousBlockLines = previousBlock.text.split('\n');
31366
+ const isListContinuing = LIST_ITEM_PATTERN.test(previousBlockLines[previousBlockLines.length - 1]) &&
31367
+ LIST_ITEM_PATTERN.test(nextBlock.text.split('\n')[0]);
31368
+ return isListContinuing ? '\n' : '\n\n';
31369
+ }
31370
+
31132
31371
  /**
31133
31372
  * Removes single-hash comment lines (`# Comment`) from a system message
31134
31373
  * This is used to clean up the final system message before sending it to the AI model
@@ -31202,14 +31441,14 @@
31202
31441
  * Performs the final system-message cleanup pass after all other augmentation steps are complete.
31203
31442
  *
31204
31443
  * @param requirements - Fully built requirements before final cleanup.
31205
- * @returns Requirements with comment lines removed from the final system message.
31444
+ * @returns Requirements with comment lines removed and repeated instructions deduplicated in the final system message.
31206
31445
  *
31207
31446
  * @private internal utility of `createAgentModelRequirementsWithCommitments`
31208
31447
  */
31209
31448
  function finalizeRequirements(requirements) {
31210
31449
  return {
31211
31450
  ...requirements,
31212
- systemMessage: removeCommentsFromSystemMessage(requirements.systemMessage),
31451
+ systemMessage: deduplicateSystemMessage(removeCommentsFromSystemMessage(requirements.systemMessage)),
31213
31452
  };
31214
31453
  }
31215
31454