@promptbook/cli 0.114.0-6 → 0.114.0-7

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.
@@ -33,9 +33,9 @@ type AgentProjectMarkdownReferenceInfo = Pick<
33
33
  /**
34
34
  * Creates markdown references rendering every mention of one project as a project chip.
35
35
  *
36
- * One project is mentioned either by its `[[project-name]]` token, by a link into its project page
37
- * or files, or by its public project URL. All of them render the same chip showing the project name
38
- * together with its running state.
36
+ * One project is mentioned either by its display name in chat prose, its `[[project-name]]` token,
37
+ * a link into its project page or files, or its public project URL. All of them render the same chip
38
+ * showing the project name together with its running state.
39
39
  *
40
40
  * @param options - Agent and project metadata.
41
41
  * @returns Inline markdown references for the shared chat renderer.
@@ -50,6 +50,7 @@ export function createAgentProjectMarkdownReferences(
50
50
 
51
51
  return {
52
52
  reference: project.projectName,
53
+ sourceTextAliases: [label],
53
54
  label,
54
55
  href: projectProfileHref,
55
56
  sourceHrefPrefixes: [projectProfileHref, ...(projectUrl ? [projectUrl] : [])],
package/esm/index.es.js CHANGED
@@ -48,7 +48,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
48
48
  * @generated
49
49
  * @see https://github.com/webgptorg/promptbook
50
50
  */
51
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-6';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-7';
52
52
  /**
53
53
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
54
54
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -17,6 +17,13 @@ export type MarkdownInlineReference = {
17
17
  * Raw reference text between `[[` and `]]`.
18
18
  */
19
19
  readonly reference: string;
20
+ /**
21
+ * Optional text aliases which should render as this reference when an agent mentions them in markdown.
22
+ *
23
+ * They are also accepted inside a `[[reference]]` token. This lets application code use a stable
24
+ * technical reference while recognizing a human-facing name in older messages.
25
+ */
26
+ readonly sourceTextAliases?: ReadonlyArray<string>;
20
27
  /**
21
28
  * Human-readable label rendered inside the link chip.
22
29
  */
@@ -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-5`).
18
+ * It follows semantic versioning (e.g., `0.114.0-6`).
19
19
  *
20
20
  * @generated
21
21
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptbook/cli",
3
- "version": "0.114.0-6",
3
+ "version": "0.114.0-7",
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,
@@ -445,7 +445,7 @@
445
445
  opacity: 0.8;
446
446
  }
447
447
 
448
- .MarkdownContent details {
448
+ .MarkdownContent details:not(.inlineReferenceChip) {
449
449
  margin: 0.95em 0;
450
450
  border: 1px solid var(--md-table-border);
451
451
  border-radius: 16px;
@@ -455,13 +455,13 @@
455
455
  transition: border-color 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease;
456
456
  }
457
457
 
458
- .MarkdownContent details:hover,
459
- .MarkdownContent details:focus-within {
458
+ .MarkdownContent details:not(.inlineReferenceChip):hover,
459
+ .MarkdownContent details:not(.inlineReferenceChip):focus-within {
460
460
  border-color: color-mix(in srgb, var(--md-link-color) 24%, var(--md-table-border) 76%);
461
461
  box-shadow: 0 12px 28px rgba(15, 23, 42, 0.12);
462
462
  }
463
463
 
464
- .MarkdownContent details > summary {
464
+ .MarkdownContent details:not(.inlineReferenceChip) > summary {
465
465
  display: flex;
466
466
  align-items: center;
467
467
  gap: 0.72rem;
@@ -479,7 +479,7 @@
479
479
  transition: background 0.18s ease, color 0.18s ease;
480
480
  }
481
481
 
482
- .MarkdownContent details > summary::before {
482
+ .MarkdownContent details:not(.inlineReferenceChip) > summary::before {
483
483
  content: '';
484
484
  width: 0.62rem;
485
485
  height: 0.62rem;
@@ -492,8 +492,8 @@
492
492
  transition: transform 0.18s ease, opacity 0.18s ease;
493
493
  }
494
494
 
495
- .MarkdownContent details > summary:hover,
496
- .MarkdownContent details > summary:focus-visible {
495
+ .MarkdownContent details:not(.inlineReferenceChip) > summary:hover,
496
+ .MarkdownContent details:not(.inlineReferenceChip) > summary:focus-visible {
497
497
  background: linear-gradient(
498
498
  180deg,
499
499
  color-mix(in srgb, var(--md-table-head-bg) 76%, rgba(255, 255, 255, 0.95) 24%),
@@ -501,34 +501,34 @@
501
501
  );
502
502
  }
503
503
 
504
- .MarkdownContent details > summary:focus-visible {
504
+ .MarkdownContent details:not(.inlineReferenceChip) > summary:focus-visible {
505
505
  outline: 2px solid color-mix(in srgb, var(--md-link-color) 52%, transparent);
506
506
  outline-offset: -2px;
507
507
  }
508
508
 
509
- .MarkdownContent details > summary::-webkit-details-marker {
509
+ .MarkdownContent details:not(.inlineReferenceChip) > summary::-webkit-details-marker {
510
510
  display: none;
511
511
  }
512
512
 
513
- .MarkdownContent details[open] > summary {
513
+ .MarkdownContent details:not(.inlineReferenceChip)[open] > summary {
514
514
  border-bottom: 1px solid color-mix(in srgb, var(--md-table-border) 88%, transparent);
515
515
  }
516
516
 
517
- .MarkdownContent details[open] > summary::before {
517
+ .MarkdownContent details:not(.inlineReferenceChip)[open] > summary::before {
518
518
  transform: rotate(45deg);
519
519
  opacity: 1;
520
520
  }
521
521
 
522
- .MarkdownContent details > :not(summary) {
522
+ .MarkdownContent details:not(.inlineReferenceChip) > :not(summary) {
523
523
  margin-left: 1.05em;
524
524
  margin-right: 1.05em;
525
525
  }
526
526
 
527
- .MarkdownContent details > summary + * {
527
+ .MarkdownContent details:not(.inlineReferenceChip) > summary + * {
528
528
  margin-top: 0.95em;
529
529
  }
530
530
 
531
- .MarkdownContent details > :last-child {
531
+ .MarkdownContent details:not(.inlineReferenceChip) > :last-child {
532
532
  margin-bottom: 1.05em;
533
533
  }
534
534
 
@@ -280,6 +280,23 @@ const INLINE_REFERENCE_PLACEHOLDER_PREFIX = '@@PROMPTBOOK_INLINE_REFERENCE_PLACE
280
280
  */
281
281
  const INLINE_REFERENCE_PLACEHOLDER_REGEX = new RegExp(`${INLINE_REFERENCE_PLACEHOLDER_PREFIX}(\\d+)__`, 'g');
282
282
 
283
+ /**
284
+ * Prefix for markdown text-segment placeholders used while resolving plain-text inline-reference aliases.
285
+ *
286
+ * @private utility of `renderMarkdown`
287
+ */
288
+ const MARKDOWN_REFERENCE_TEXT_MASK_PLACEHOLDER_PREFIX = '@@PROMPTBOOK_MARKDOWN_REFERENCE_TEXT_MASK_PLACEHOLDER__';
289
+
290
+ /**
291
+ * Pattern matching markdown text-segment placeholders.
292
+ *
293
+ * @private utility of `renderMarkdown`
294
+ */
295
+ const MARKDOWN_REFERENCE_TEXT_MASK_PLACEHOLDER_REGEX = new RegExp(
296
+ `${MARKDOWN_REFERENCE_TEXT_MASK_PLACEHOLDER_PREFIX}(\\d+)__`,
297
+ 'g',
298
+ );
299
+
283
300
  /**
284
301
  * Pattern matching markdown links and images together with their raw target.
285
302
  *
@@ -365,6 +382,14 @@ export type MarkdownInlineReference = {
365
382
  */
366
383
  readonly reference: string;
367
384
 
385
+ /**
386
+ * Optional text aliases which should render as this reference when an agent mentions them in markdown.
387
+ *
388
+ * They are also accepted inside a `[[reference]]` token. This lets application code use a stable
389
+ * technical reference while recognizing a human-facing name in older messages.
390
+ */
391
+ readonly sourceTextAliases?: ReadonlyArray<string>;
392
+
368
393
  /**
369
394
  * Human-readable label rendered inside the link chip.
370
395
  */
@@ -460,6 +485,14 @@ type MaskedCodeSegmentsResult = {
460
485
  restore: (value: string_markdown) => string_markdown;
461
486
  };
462
487
 
488
+ /**
489
+ * Result of temporarily masked markdown links, images and bare URLs.
490
+ */
491
+ type MaskedMarkdownLinkAndUrlSegmentsResult = {
492
+ masked: string_markdown;
493
+ restore: (value: string_markdown) => string_markdown;
494
+ };
495
+
463
496
  /**
464
497
  * Result of masked details blocks.
465
498
  */
@@ -787,6 +820,44 @@ function maskMarkdownCodeSegments(markdown: string_markdown): MaskedCodeSegments
787
820
  };
788
821
  }
789
822
 
823
+ /**
824
+ * Masks markdown links, images and bare URLs while plain-text reference aliases are resolved.
825
+ *
826
+ * A matching display name inside an unrelated link label must keep that original link instead of
827
+ * producing a nested chip inside its anchor.
828
+ *
829
+ * @param markdown - Markdown without code segments.
830
+ * @returns Masked markdown and a restore helper.
831
+ *
832
+ * @private utility of `renderMarkdown`
833
+ */
834
+ function maskMarkdownLinksAndBareUrls(markdown: string_markdown): MaskedMarkdownLinkAndUrlSegmentsResult {
835
+ const segments: string[] = [];
836
+ const addPlaceholder = (segment: string): string => {
837
+ const placeholder = `${MARKDOWN_REFERENCE_TEXT_MASK_PLACEHOLDER_PREFIX}${segments.length}__`;
838
+ segments.push(segment);
839
+ return placeholder;
840
+ };
841
+
842
+ MARKDOWN_LINK_REGEX.lastIndex = 0;
843
+ let masked = markdown.replace(MARKDOWN_LINK_REGEX, (match) => addPlaceholder(match)) as string_markdown;
844
+
845
+ BARE_URL_REGEX.lastIndex = 0;
846
+ masked = masked.replace(BARE_URL_REGEX, (_match, leadingWhitespace: string, rawUrl: string) => {
847
+ return `${leadingWhitespace}${addPlaceholder(rawUrl)}`;
848
+ }) as string_markdown;
849
+
850
+ return {
851
+ masked,
852
+ restore(value: string_markdown): string_markdown {
853
+ return value.replace(
854
+ MARKDOWN_REFERENCE_TEXT_MASK_PLACEHOLDER_REGEX,
855
+ (_match, index) => segments[Number(index)] ?? '',
856
+ );
857
+ },
858
+ };
859
+ }
860
+
790
861
  /**
791
862
  * Masks `<details>...</details>` blocks in the markdown source so that Showdown never
792
863
  * processes their content.
@@ -891,6 +962,18 @@ function normalizeMarkdownInlineReferenceKey(value: string): string {
891
962
  return value.trim().toLowerCase();
892
963
  }
893
964
 
965
+ /**
966
+ * Lists all token keys which can identify one inline reference.
967
+ *
968
+ * @param reference - Inline reference with its stable key and optional display-name aliases.
969
+ * @returns Keys recognized inside `[[...]]` tokens.
970
+ *
971
+ * @private utility of `renderMarkdown`
972
+ */
973
+ function listMarkdownInlineReferenceKeys(reference: MarkdownInlineReference): ReadonlyArray<string> {
974
+ return [reference.reference, ...(reference.sourceTextAliases || [])];
975
+ }
976
+
894
977
  /**
895
978
  * Builds a lookup of inline markdown references keyed by the authored `[[reference]]` text.
896
979
  *
@@ -905,17 +988,148 @@ function createMarkdownInlineReferenceByKey(
905
988
  const referenceByKey = new Map<string, MarkdownInlineReference>();
906
989
 
907
990
  for (const reference of references) {
908
- const normalizedReference = normalizeMarkdownInlineReferenceKey(reference.reference);
909
- if (!normalizedReference || referenceByKey.has(normalizedReference)) {
910
- continue;
911
- }
991
+ for (const referenceKey of listMarkdownInlineReferenceKeys(reference)) {
992
+ const normalizedReference = normalizeMarkdownInlineReferenceKey(referenceKey);
993
+ if (!normalizedReference || referenceByKey.has(normalizedReference)) {
994
+ continue;
995
+ }
912
996
 
913
- referenceByKey.set(normalizedReference, reference);
997
+ referenceByKey.set(normalizedReference, reference);
998
+ }
914
999
  }
915
1000
 
916
1001
  return referenceByKey;
917
1002
  }
918
1003
 
1004
+ /**
1005
+ * One display-name alias and the inline reference it renders.
1006
+ *
1007
+ * @private utility of `renderMarkdown`
1008
+ */
1009
+ type MarkdownInlineReferenceTextMatcher = {
1010
+ /**
1011
+ * Reference rendered for the alias.
1012
+ */
1013
+ readonly reference: MarkdownInlineReference;
1014
+
1015
+ /**
1016
+ * Human-readable source text recognized in markdown.
1017
+ */
1018
+ readonly sourceTextAlias: string;
1019
+ };
1020
+
1021
+ /**
1022
+ * Escapes text before embedding it into a regular expression.
1023
+ *
1024
+ * @param value - Literal text to match.
1025
+ * @returns Regular-expression-safe text.
1026
+ *
1027
+ * @private utility of `renderMarkdown`
1028
+ */
1029
+ function escapeRegularExpression(value: string): string {
1030
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1031
+ }
1032
+
1033
+ /**
1034
+ * Creates non-ambiguous display-name matchers for inline references.
1035
+ *
1036
+ * Longer aliases are matched first so a project named `Prague Map` wins over a project named `Map`.
1037
+ * An alias shared by more than one reference is deliberately ignored instead of linking to an arbitrary project.
1038
+ *
1039
+ * @param references - References available to this markdown render.
1040
+ * @returns Plain-text matchers ordered from longest to shortest alias.
1041
+ *
1042
+ * @private utility of `renderMarkdown`
1043
+ */
1044
+ function createMarkdownInlineReferenceTextMatchers(
1045
+ references: ReadonlyArray<MarkdownInlineReference>,
1046
+ ): ReadonlyArray<MarkdownInlineReferenceTextMatcher> {
1047
+ const matcherByNormalizedAlias = new Map<string, MarkdownInlineReferenceTextMatcher | null>();
1048
+
1049
+ for (const reference of references) {
1050
+ for (const sourceTextAliasCandidate of reference.sourceTextAliases || []) {
1051
+ const sourceTextAlias = sourceTextAliasCandidate.trim();
1052
+ const normalizedAlias = normalizeMarkdownInlineReferenceKey(sourceTextAlias);
1053
+ if (!normalizedAlias) {
1054
+ continue;
1055
+ }
1056
+
1057
+ const existingMatcher = matcherByNormalizedAlias.get(normalizedAlias);
1058
+ if (existingMatcher === undefined) {
1059
+ matcherByNormalizedAlias.set(normalizedAlias, { reference, sourceTextAlias });
1060
+ } else if (existingMatcher !== null && existingMatcher.reference !== reference) {
1061
+ matcherByNormalizedAlias.set(normalizedAlias, null);
1062
+ }
1063
+ }
1064
+ }
1065
+
1066
+ return Array.from(matcherByNormalizedAlias.values())
1067
+ .filter((matcher): matcher is MarkdownInlineReferenceTextMatcher => matcher !== null)
1068
+ .sort(
1069
+ (firstMatcher, secondMatcher) =>
1070
+ secondMatcher.sourceTextAlias.length - firstMatcher.sourceTextAlias.length ||
1071
+ firstMatcher.sourceTextAlias.localeCompare(secondMatcher.sourceTextAlias),
1072
+ );
1073
+ }
1074
+
1075
+ /**
1076
+ * Creates a regular expression matching one plain-text alias with optional bold markdown around it.
1077
+ *
1078
+ * @param sourceTextAlias - Literal display name to match.
1079
+ * @param isMarkdownFormattingMatched - Whether matching `**alias**` and `__alias__` form.
1080
+ * @returns Unicode-aware whole-text regular expression.
1081
+ *
1082
+ * @private utility of `renderMarkdown`
1083
+ */
1084
+ function createMarkdownInlineReferenceTextAliasRegex(
1085
+ sourceTextAlias: string,
1086
+ isMarkdownFormattingMatched: boolean,
1087
+ ): RegExp {
1088
+ const escapedAlias = escapeRegularExpression(sourceTextAlias);
1089
+ const boundaryCharacterClass = '\\p{L}\\p{N}_-';
1090
+ const boundaryPattern = `[^${boundaryCharacterClass}]`;
1091
+ const aliasPattern = isMarkdownFormattingMatched
1092
+ ? `(?:\\*\\*|__)${escapedAlias}(?:\\*\\*|__)`
1093
+ : escapedAlias;
1094
+
1095
+ return new RegExp(`(^|${boundaryPattern})${aliasPattern}(?=$|${boundaryPattern})`, 'giu');
1096
+ }
1097
+
1098
+ /**
1099
+ * Replaces known project display names in agent markdown with inline-reference placeholders.
1100
+ *
1101
+ * Bold aliases are replaced first so an older message such as `**Prague Murders Map**` becomes one
1102
+ * chip rather than a chip nested inside formatting markup.
1103
+ *
1104
+ * @param markdown - Markdown with code and links already masked.
1105
+ * @param matchers - Non-ambiguous display-name aliases available to this render.
1106
+ * @param createPlaceholder - Renders a matched reference into a placeholder.
1107
+ * @returns Markdown containing inline-reference placeholders.
1108
+ *
1109
+ * @private utility of `renderMarkdown`
1110
+ */
1111
+ function replaceMarkdownInlineReferenceTextAliases(
1112
+ markdown: string_markdown,
1113
+ matchers: ReadonlyArray<MarkdownInlineReferenceTextMatcher>,
1114
+ createPlaceholder: CreateMarkdownInlineReferencePlaceholder,
1115
+ ): string_markdown {
1116
+ let referencedMarkdown = markdown;
1117
+
1118
+ for (const matcher of matchers) {
1119
+ for (const isMarkdownFormattingMatched of [true, false]) {
1120
+ const aliasRegex = createMarkdownInlineReferenceTextAliasRegex(
1121
+ matcher.sourceTextAlias,
1122
+ isMarkdownFormattingMatched,
1123
+ );
1124
+ referencedMarkdown = referencedMarkdown.replace(aliasRegex, (_match, leadingBoundary: string) => {
1125
+ return `${leadingBoundary || ''}${createPlaceholder(matcher.reference)}`;
1126
+ }) as string_markdown;
1127
+ }
1128
+ }
1129
+
1130
+ return referencedMarkdown;
1131
+ }
1132
+
919
1133
  /**
920
1134
  * Comparable parts of one URL used to recognize inline references written as links or bare URLs.
921
1135
  *
@@ -1303,6 +1517,19 @@ function applyMarkdownInlineReferences(markdown: string_markdown, options?: Rend
1303
1517
  );
1304
1518
  }
1305
1519
 
1520
+ const textMatchers = createMarkdownInlineReferenceTextMatchers(references);
1521
+ if (textMatchers.length !== 0) {
1522
+ const { masked: markdownWithMaskedLinksAndUrls, restore: restoreLinksAndUrls } =
1523
+ maskMarkdownLinksAndBareUrls(referencedMarkdown);
1524
+ referencedMarkdown = restoreLinksAndUrls(
1525
+ replaceMarkdownInlineReferenceTextAliases(
1526
+ markdownWithMaskedLinksAndUrls,
1527
+ textMatchers,
1528
+ createPlaceholder,
1529
+ ),
1530
+ );
1531
+ }
1532
+
1306
1533
  return restoreMarkdownInlineReferenceChips(referencedMarkdown, renderedChips);
1307
1534
  }
1308
1535