@promptbook/node 0.112.0-56 → 0.112.0-57

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 (37) hide show
  1. package/esm/index.es.js +990 -1005
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/ParsedAgentSourceWithCommitments.d.ts +7 -0
  4. package/esm/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/applyCommitmentsToAgentModelRequirements.d.ts +14 -0
  5. package/esm/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/augmentAgentModelRequirementsFromSource.d.ts +14 -0
  6. package/esm/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/filterCommitmentsForAgentModelRequirements.d.ts +10 -0
  7. package/esm/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/materializeInlineKnowledgeSources.d.ts +12 -0
  8. package/esm/src/book-2.0/agent-source/parseAgentSource/ParseAgentSourceState.d.ts +10 -0
  9. package/esm/src/book-2.0/agent-source/parseAgentSource/ParsedAgentProfile.d.ts +7 -0
  10. package/esm/src/book-2.0/agent-source/parseAgentSource/applyMetaCommitment.d.ts +8 -0
  11. package/esm/src/book-2.0/agent-source/parseAgentSource/consumeConversationSampleCommitment.d.ts +8 -0
  12. package/esm/src/book-2.0/agent-source/parseAgentSource/createCapabilitiesFromCommitment.d.ts +9 -0
  13. package/esm/src/book-2.0/agent-source/parseAgentSource/ensureMetaFullname.d.ts +7 -0
  14. package/esm/src/book-2.0/agent-source/parseAgentSource/extractAgentProfileText.d.ts +8 -0
  15. package/esm/src/book-2.0/agent-source/parseAgentSource/extractInitialMessage.d.ts +7 -0
  16. package/esm/src/book-2.0/agent-source/parseAgentSource/extractParsedAgentProfile.d.ts +8 -0
  17. package/esm/src/types/LlmToolDefinition.d.ts +17 -7
  18. package/esm/src/version.d.ts +1 -1
  19. package/package.json +2 -2
  20. package/umd/index.umd.js +990 -1005
  21. package/umd/index.umd.js.map +1 -1
  22. package/umd/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/ParsedAgentSourceWithCommitments.d.ts +7 -0
  23. package/umd/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/applyCommitmentsToAgentModelRequirements.d.ts +14 -0
  24. package/umd/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/augmentAgentModelRequirementsFromSource.d.ts +14 -0
  25. package/umd/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/filterCommitmentsForAgentModelRequirements.d.ts +10 -0
  26. package/umd/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/materializeInlineKnowledgeSources.d.ts +12 -0
  27. package/umd/src/book-2.0/agent-source/parseAgentSource/ParseAgentSourceState.d.ts +10 -0
  28. package/umd/src/book-2.0/agent-source/parseAgentSource/ParsedAgentProfile.d.ts +7 -0
  29. package/umd/src/book-2.0/agent-source/parseAgentSource/applyMetaCommitment.d.ts +8 -0
  30. package/umd/src/book-2.0/agent-source/parseAgentSource/consumeConversationSampleCommitment.d.ts +8 -0
  31. package/umd/src/book-2.0/agent-source/parseAgentSource/createCapabilitiesFromCommitment.d.ts +9 -0
  32. package/umd/src/book-2.0/agent-source/parseAgentSource/ensureMetaFullname.d.ts +7 -0
  33. package/umd/src/book-2.0/agent-source/parseAgentSource/extractAgentProfileText.d.ts +8 -0
  34. package/umd/src/book-2.0/agent-source/parseAgentSource/extractInitialMessage.d.ts +7 -0
  35. package/umd/src/book-2.0/agent-source/parseAgentSource/extractParsedAgentProfile.d.ts +8 -0
  36. package/umd/src/types/LlmToolDefinition.d.ts +17 -7
  37. package/umd/src/version.d.ts +1 -1
package/umd/index.umd.js CHANGED
@@ -48,7 +48,7 @@
48
48
  * @generated
49
49
  * @see https://github.com/webgptorg/promptbook
50
50
  */
51
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-56';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-57';
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
@@ -24171,6 +24171,44 @@
24171
24171
  * @private constant of createUseCalendarTools
24172
24172
  */
24173
24173
  const CALENDAR_URL_PARAMETER_DESCRIPTION = 'Google Calendar URL configured by USE CALENDAR (for example "https://calendar.google.com/...").';
24174
+ /**
24175
+ * Shared schema for string arrays used by USE CALENDAR tools.
24176
+ *
24177
+ * @private constant of createUseCalendarTools
24178
+ */
24179
+ const STRING_ARRAY_ITEMS_SCHEMA = {
24180
+ type: 'string',
24181
+ };
24182
+ /**
24183
+ * Shared schema for integer arrays used by USE CALENDAR tools.
24184
+ *
24185
+ * @private constant of createUseCalendarTools
24186
+ */
24187
+ const INTEGER_ARRAY_ITEMS_SCHEMA = {
24188
+ type: 'integer',
24189
+ };
24190
+ /**
24191
+ * Shared `sendUpdates` schema used by USE CALENDAR tools.
24192
+ *
24193
+ * @private constant of createUseCalendarTools
24194
+ */
24195
+ const SEND_UPDATES_PARAMETER_SCHEMA = {
24196
+ type: 'string',
24197
+ description: 'Guest update policy ("all", "externalOnly", "none").',
24198
+ enum: ['all', 'externalOnly', 'none'],
24199
+ };
24200
+ /**
24201
+ * Creates an array parameter schema with explicit item definition so OpenAI accepts it.
24202
+ *
24203
+ * @private function of createUseCalendarTools
24204
+ */
24205
+ function createArrayParameterSchema(description, items) {
24206
+ return {
24207
+ type: 'array',
24208
+ description,
24209
+ items,
24210
+ };
24211
+ }
24174
24212
  /**
24175
24213
  * Adds USE CALENDAR tool definitions while keeping already registered tools untouched.
24176
24214
  *
@@ -24277,18 +24315,9 @@
24277
24315
  type: 'string',
24278
24316
  description: 'Optional timezone for datetime values.',
24279
24317
  },
24280
- attendees: {
24281
- type: 'array',
24282
- description: 'Optional guest email list.',
24283
- },
24284
- reminderMinutes: {
24285
- type: 'array',
24286
- description: 'Optional popup reminder minute offsets.',
24287
- },
24288
- sendUpdates: {
24289
- type: 'string',
24290
- description: 'Guest update policy ("all", "externalOnly", "none").',
24291
- },
24318
+ attendees: createArrayParameterSchema('Optional guest email list.', STRING_ARRAY_ITEMS_SCHEMA),
24319
+ reminderMinutes: createArrayParameterSchema('Optional popup reminder minute offsets.', INTEGER_ARRAY_ITEMS_SCHEMA),
24320
+ sendUpdates: SEND_UPDATES_PARAMETER_SCHEMA,
24292
24321
  },
24293
24322
  required: ['summary', 'start', 'end'],
24294
24323
  },
@@ -24331,18 +24360,9 @@
24331
24360
  type: 'string',
24332
24361
  description: 'Optional timezone for datetime values.',
24333
24362
  },
24334
- attendees: {
24335
- type: 'array',
24336
- description: 'Optional replacement guest email list.',
24337
- },
24338
- reminderMinutes: {
24339
- type: 'array',
24340
- description: 'Optional replacement popup reminder minute offsets.',
24341
- },
24342
- sendUpdates: {
24343
- type: 'string',
24344
- description: 'Guest update policy ("all", "externalOnly", "none").',
24345
- },
24363
+ attendees: createArrayParameterSchema('Optional replacement guest email list.', STRING_ARRAY_ITEMS_SCHEMA),
24364
+ reminderMinutes: createArrayParameterSchema('Optional replacement popup reminder minute offsets.', INTEGER_ARRAY_ITEMS_SCHEMA),
24365
+ sendUpdates: SEND_UPDATES_PARAMETER_SCHEMA,
24346
24366
  },
24347
24367
  required: ['eventId'],
24348
24368
  },
@@ -24361,10 +24381,7 @@
24361
24381
  type: 'string',
24362
24382
  description: 'Google Calendar event id.',
24363
24383
  },
24364
- sendUpdates: {
24365
- type: 'string',
24366
- description: 'Guest update policy ("all", "externalOnly", "none").',
24367
- },
24384
+ sendUpdates: SEND_UPDATES_PARAMETER_SCHEMA,
24368
24385
  },
24369
24386
  required: ['eventId'],
24370
24387
  },
@@ -24383,14 +24400,8 @@
24383
24400
  type: 'string',
24384
24401
  description: 'Google Calendar event id.',
24385
24402
  },
24386
- guests: {
24387
- type: 'array',
24388
- description: 'Guest email list to add to the event.',
24389
- },
24390
- sendUpdates: {
24391
- type: 'string',
24392
- description: 'Guest update policy ("all", "externalOnly", "none").',
24393
- },
24403
+ guests: createArrayParameterSchema('Guest email list to add to the event.', STRING_ARRAY_ITEMS_SCHEMA),
24404
+ sendUpdates: SEND_UPDATES_PARAMETER_SCHEMA,
24394
24405
  },
24395
24406
  required: ['eventId', 'guests'],
24396
24407
  },
@@ -31343,6 +31354,297 @@
31343
31354
  return normalizeAgentName(`Agent ${agentHash.substring(0, LIMITS.SHORT_NAME_LENGTH)}`);
31344
31355
  }
31345
31356
 
31357
+ /**
31358
+ * Consumes the agent-name prelude from raw source.
31359
+ *
31360
+ * Leading whitespace-only lines are skipped. The first subsequent line that contains any
31361
+ * non-whitespace characters is always treated as plain-text agent name, even when it starts
31362
+ * with a commitment keyword or other book syntax.
31363
+ *
31364
+ * @param agentSource - Raw agent source.
31365
+ * @returns Parsed prelude with shared line metadata.
31366
+ *
31367
+ * @private internal utility of book agent-source parsing
31368
+ */
31369
+ function parseAgentSourcePrelude(agentSource) {
31370
+ const lines = agentSource.split(/\r?\n/);
31371
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
31372
+ const line = lines[lineIndex];
31373
+ if (line === undefined) {
31374
+ continue;
31375
+ }
31376
+ const trimmedLine = line.trim();
31377
+ if (!trimmedLine) {
31378
+ continue;
31379
+ }
31380
+ return {
31381
+ lines,
31382
+ agentName: trimmedLine,
31383
+ agentNameLineIndex: lineIndex,
31384
+ agentNameLineNumber: lineIndex + 1,
31385
+ };
31386
+ }
31387
+ return {
31388
+ lines,
31389
+ agentName: null,
31390
+ agentNameLineIndex: -1,
31391
+ };
31392
+ }
31393
+
31394
+ /**
31395
+ * Regex pattern to match horizontal lines (markdown thematic breaks)
31396
+ * Matches 3 or more hyphens, underscores, or asterisks (with optional spaces between)
31397
+ */
31398
+ const HORIZONTAL_LINE_PATTERN$1 = /^[\s]*[-_*][\s]*[-_*][\s]*[-_*][\s]*[-_*]*[\s]*$/;
31399
+ /**
31400
+ * Parses agent source using the new commitment system with multiline support
31401
+ * This function replaces the hardcoded commitment parsing in the original parseAgentSource
31402
+ *
31403
+ * The first non-empty line is always consumed as plain-text agent name. Commitment parsing
31404
+ * starts only after that title line has been fixed.
31405
+ *
31406
+ * @private internal utility of `parseAgentSource`
31407
+ */
31408
+ function parseAgentSourceWithCommitments(agentSource) {
31409
+ var _a, _b;
31410
+ if (!agentSource || !agentSource.trim()) {
31411
+ return {
31412
+ agentName: null,
31413
+ commitments: [],
31414
+ nonCommitmentLines: [],
31415
+ };
31416
+ }
31417
+ const { lines, agentName, agentNameLineIndex, agentNameLineNumber } = parseAgentSourcePrelude(agentSource);
31418
+ const commitments = [];
31419
+ const nonCommitmentLines = agentNameLineIndex >= 0 ? [lines[agentNameLineIndex]] : [];
31420
+ // Parse commitments with multiline support
31421
+ let currentCommitment = null;
31422
+ // Process lines starting from after the agent name line
31423
+ const startIndex = agentNameLineIndex >= 0 ? agentNameLineIndex + 1 : 0;
31424
+ let isInsideCodeBlock = false;
31425
+ for (let i = startIndex; i < lines.length; i++) {
31426
+ const line = lines[i];
31427
+ if (line === undefined) {
31428
+ continue;
31429
+ }
31430
+ const trimmedLine = line.trim();
31431
+ // Check if this line starts or ends a code block
31432
+ if (trimmedLine.startsWith('```')) {
31433
+ isInsideCodeBlock = !isInsideCodeBlock;
31434
+ if (currentCommitment) {
31435
+ // If we are inside a commitment, the code block is part of it
31436
+ currentCommitment.contentLines.push(line);
31437
+ }
31438
+ else {
31439
+ // If we are not inside a commitment, the code block is non-commitment
31440
+ nonCommitmentLines.push(line);
31441
+ }
31442
+ continue;
31443
+ }
31444
+ if (isInsideCodeBlock) {
31445
+ if (currentCommitment) {
31446
+ // If we are inside a commitment and a code block, the line is part of the commitment
31447
+ currentCommitment.contentLines.push(line);
31448
+ }
31449
+ else {
31450
+ // If we are inside a code block but not a commitment, the line is non-commitment
31451
+ nonCommitmentLines.push(line);
31452
+ }
31453
+ continue;
31454
+ }
31455
+ // Check if this line starts a new commitment
31456
+ let foundNewCommitment = false;
31457
+ for (const definition of COMMITMENT_REGISTRY) {
31458
+ const typeRegex = definition.createTypeRegex();
31459
+ const match = typeRegex.exec(line.trim());
31460
+ if (match && ((_a = match.groups) === null || _a === void 0 ? void 0 : _a.type)) {
31461
+ // Save the previous commitment if it exists
31462
+ if (currentCommitment) {
31463
+ const fullContent = currentCommitment.contentLines.join('\n');
31464
+ commitments.push({
31465
+ type: currentCommitment.type,
31466
+ content: _spaceTrim.spaceTrim(fullContent),
31467
+ originalLine: currentCommitment.originalStartLine,
31468
+ lineNumber: currentCommitment.startLineNumber,
31469
+ });
31470
+ }
31471
+ // Extract the initial content from the commitment line
31472
+ const fullRegex = definition.createRegex();
31473
+ const fullMatch = fullRegex.exec(line.trim());
31474
+ const initialContent = ((_b = fullMatch === null || fullMatch === void 0 ? void 0 : fullMatch.groups) === null || _b === void 0 ? void 0 : _b.contents) || '';
31475
+ // Start a new commitment
31476
+ currentCommitment = {
31477
+ type: definition.type,
31478
+ startLineNumber: i + 1,
31479
+ originalStartLine: line,
31480
+ contentLines: initialContent ? [initialContent] : [],
31481
+ };
31482
+ foundNewCommitment = true;
31483
+ break;
31484
+ }
31485
+ }
31486
+ // Check if this is a horizontal line (ends any current commitment)
31487
+ const isHorizontalLine = HORIZONTAL_LINE_PATTERN$1.test(line);
31488
+ if (isHorizontalLine) {
31489
+ // Save the current commitment if it exists
31490
+ if (currentCommitment) {
31491
+ const fullContent = currentCommitment.contentLines.join('\n');
31492
+ commitments.push({
31493
+ type: currentCommitment.type,
31494
+ content: _spaceTrim.spaceTrim(fullContent),
31495
+ originalLine: currentCommitment.originalStartLine,
31496
+ lineNumber: currentCommitment.startLineNumber,
31497
+ });
31498
+ currentCommitment = null;
31499
+ }
31500
+ // Add horizontal line to non-commitment lines
31501
+ nonCommitmentLines.push(line);
31502
+ continue;
31503
+ }
31504
+ if (!foundNewCommitment) {
31505
+ if (currentCommitment) {
31506
+ // This line belongs to the current commitment
31507
+ currentCommitment.contentLines.push(line);
31508
+ }
31509
+ else {
31510
+ // This line is not part of any commitment
31511
+ nonCommitmentLines.push(line);
31512
+ }
31513
+ }
31514
+ }
31515
+ // Don't forget to save the last commitment if it exists
31516
+ if (currentCommitment) {
31517
+ const fullContent = currentCommitment.contentLines.join('\n');
31518
+ commitments.push({
31519
+ type: currentCommitment.type,
31520
+ content: _spaceTrim.spaceTrim(fullContent),
31521
+ originalLine: currentCommitment.originalStartLine,
31522
+ lineNumber: currentCommitment.startLineNumber,
31523
+ });
31524
+ }
31525
+ return {
31526
+ agentName,
31527
+ agentNameLineNumber,
31528
+ commitments,
31529
+ nonCommitmentLines,
31530
+ };
31531
+ }
31532
+
31533
+ /**
31534
+ * Parses parameters from text using both supported notations:
31535
+ * 1. @Parameter - single word parameter starting with @
31536
+ * 2. {parameterName} or {parameter with multiple words} or {parameterName: description text}
31537
+ *
31538
+ * Both notations represent the same syntax feature - parameters
31539
+ *
31540
+ * @param text - Text to extract parameters from
31541
+ * @returns Array of parsed parameters with unified representation
31542
+ *
31543
+ * @public exported from `@promptbook/core`
31544
+ */
31545
+ function parseParameters(text) {
31546
+ const parameters = [];
31547
+ // [🧠] Parameter syntax parsing - unified approach for two different notations of the same syntax feature
31548
+ // The Book language supports parameters in two different notations but they represent the same concept
31549
+ // Extract @Parameter notation (single word parameters starting with @)
31550
+ const atParameterRegex = /@[\w\u00C0-\u017F\u0100-\u024F\u1E00-\u1EFF]+/gim;
31551
+ text.replace(atParameterRegex, (match) => {
31552
+ const parameterName = match.slice(1); // Remove the @ symbol
31553
+ parameters.push({
31554
+ text: match,
31555
+ notation: 'at',
31556
+ name: parameterName,
31557
+ });
31558
+ return match;
31559
+ });
31560
+ // Extract {parameter} notation (parameters in braces)
31561
+ const braceParameterRegex = /\{([^}]+)\}/gim;
31562
+ text.replace(braceParameterRegex, (match, content) => {
31563
+ // Check if the parameter has a description (parameterName: description)
31564
+ const colonIndex = content.indexOf(':');
31565
+ if (colonIndex !== -1) {
31566
+ const name = content.substring(0, colonIndex).trim();
31567
+ const description = content.substring(colonIndex + 1).trim();
31568
+ parameters.push({
31569
+ text: match,
31570
+ notation: 'brace',
31571
+ name,
31572
+ description,
31573
+ });
31574
+ }
31575
+ else {
31576
+ // Simple parameter without description
31577
+ parameters.push({
31578
+ text: match,
31579
+ notation: 'brace',
31580
+ name: content.trim(),
31581
+ });
31582
+ }
31583
+ return match;
31584
+ });
31585
+ // Remove duplicates based on name (keep the first occurrence)
31586
+ const uniqueParameters = parameters.filter((parameter, index, array) => {
31587
+ return array.findIndex((parameterItem) => parameterItem.name === parameter.name) === index;
31588
+ });
31589
+ return uniqueParameters;
31590
+ }
31591
+
31592
+ /**
31593
+ * Ensures the parsed profile always exposes a fullname value.
31594
+ *
31595
+ * @private internal utility of `parseAgentSource`
31596
+ */
31597
+ function ensureMetaFullname(meta, fallbackFullname) {
31598
+ if (!meta.fullname) {
31599
+ meta.fullname = fallbackFullname;
31600
+ }
31601
+ }
31602
+
31603
+ /**
31604
+ * Resolves the public agent profile text from the last GOAL/GOALS commitment,
31605
+ * falling back to the deprecated PERSONA/PERSONAE commitments when no goal exists.
31606
+ *
31607
+ * @private internal utility of `parseAgentSource`
31608
+ */
31609
+ function extractAgentProfileText(commitments) {
31610
+ let goalDescription = '';
31611
+ let hasGoalDescription = false;
31612
+ let personaDescription = '';
31613
+ let hasPersonaDescription = false;
31614
+ for (const commitment of commitments) {
31615
+ if (commitment.type === 'GOAL' || commitment.type === 'GOALS') {
31616
+ goalDescription = commitment.content;
31617
+ hasGoalDescription = true;
31618
+ }
31619
+ if (commitment.type === 'PERSONA' || commitment.type === 'PERSONAE') {
31620
+ personaDescription = commitment.content;
31621
+ hasPersonaDescription = true;
31622
+ }
31623
+ }
31624
+ if (hasGoalDescription) {
31625
+ return goalDescription;
31626
+ }
31627
+ if (hasPersonaDescription) {
31628
+ return personaDescription;
31629
+ }
31630
+ return null;
31631
+ }
31632
+
31633
+ /**
31634
+ * Resolves the last INITIAL MESSAGE commitment, which is the public initial-message value.
31635
+ *
31636
+ * @private internal utility of `parseAgentSource`
31637
+ */
31638
+ function extractInitialMessage(commitments) {
31639
+ let initialMessage = null;
31640
+ for (const commitment of commitments) {
31641
+ if (commitment.type === 'INITIAL MESSAGE') {
31642
+ initialMessage = commitment.content;
31643
+ }
31644
+ }
31645
+ return initialMessage;
31646
+ }
31647
+
31346
31648
  /**
31347
31649
  * Normalizes a domain-like string into a comparable hostname form.
31348
31650
  *
@@ -31382,274 +31684,178 @@
31382
31684
  }
31383
31685
 
31384
31686
  /**
31385
- * Consumes the agent-name prelude from raw source.
31386
- *
31387
- * Leading whitespace-only lines are skipped. The first subsequent line that contains any
31388
- * non-whitespace characters is always treated as plain-text agent name, even when it starts
31389
- * with a commitment keyword or other book syntax.
31390
- *
31391
- * @param agentSource - Raw agent source.
31392
- * @returns Parsed prelude with shared line metadata.
31687
+ * Dedicated handlers for META-style commitments that directly map onto parsed meta fields.
31688
+ */
31689
+ const META_COMMITMENT_APPLIERS = {
31690
+ 'META AVATAR': applyMetaAvatarContent,
31691
+ 'META LINK': applyMetaLinkContent,
31692
+ 'META DOMAIN': applyMetaDomainContent,
31693
+ 'META IMAGE': applyMetaImageContent,
31694
+ 'META DESCRIPTION': applyMetaDescriptionContent,
31695
+ 'META DISCLAIMER': applyMetaDisclaimerContent,
31696
+ 'META INPUT PLACEHOLDER': applyMetaInputPlaceholderContent,
31697
+ 'MESSAGE SUFFIX': applyMessageSuffixContent,
31698
+ 'META COLOR': applyMetaColorContent,
31699
+ 'META FONT': applyMetaFontContent,
31700
+ 'META VOICE': applyMetaVoiceContent,
31701
+ };
31702
+ /**
31703
+ * Applies META-style commitments that mutate parsed profile metadata.
31393
31704
  *
31394
- * @private internal utility of book agent-source parsing
31705
+ * @private internal utility of `parseAgentSource`
31395
31706
  */
31396
- function parseAgentSourcePrelude(agentSource) {
31397
- const lines = agentSource.split(/\r?\n/);
31398
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
31399
- const line = lines[lineIndex];
31400
- if (line === undefined) {
31401
- continue;
31402
- }
31403
- const trimmedLine = line.trim();
31404
- if (!trimmedLine) {
31405
- continue;
31406
- }
31407
- return {
31408
- lines,
31409
- agentName: trimmedLine,
31410
- agentNameLineIndex: lineIndex,
31411
- agentNameLineNumber: lineIndex + 1,
31412
- };
31707
+ function applyMetaCommitment(state, commitment) {
31708
+ const applyMetaContent = META_COMMITMENT_APPLIERS[commitment.type];
31709
+ if (applyMetaContent) {
31710
+ applyMetaContent(state, commitment.content);
31711
+ return;
31712
+ }
31713
+ if (commitment.type === 'META') {
31714
+ applyGenericMetaCommitment(state, commitment.content);
31413
31715
  }
31414
- return {
31415
- lines,
31416
- agentName: null,
31417
- agentNameLineIndex: -1,
31418
- };
31419
31716
  }
31420
-
31421
- /**
31422
- * Regex pattern to match horizontal lines (markdown thematic breaks)
31423
- * Matches 3 or more hyphens, underscores, or asterisks (with optional spaces between)
31424
- */
31425
- const HORIZONTAL_LINE_PATTERN$1 = /^[\s]*[-_*][\s]*[-_*][\s]*[-_*][\s]*[-_*]*[\s]*$/;
31426
31717
  /**
31427
- * Parses agent source using the new commitment system with multiline support
31428
- * This function replaces the hardcoded commitment parsing in the original parseAgentSource
31429
- *
31430
- * The first non-empty line is always consumed as plain-text agent name. Commitment parsing
31431
- * starts only after that title line has been fixed.
31432
- *
31433
- * @private internal utility of `parseAgentSource`
31718
+ * Applies the generic META commitment form (`META TYPE value`).
31434
31719
  */
31435
- function parseAgentSourceWithCommitments(agentSource) {
31436
- var _a, _b;
31437
- if (!agentSource || !agentSource.trim()) {
31438
- return {
31439
- agentName: null,
31440
- commitments: [],
31441
- nonCommitmentLines: [],
31442
- };
31720
+ function applyGenericMetaCommitment(state, content) {
31721
+ const metaTypeRaw = content.split(' ')[0] || 'NONE';
31722
+ const metaValue = _spaceTrim.spaceTrim(content.substring(metaTypeRaw.length));
31723
+ if (metaTypeRaw === 'LINK') {
31724
+ state.links.push(metaValue);
31443
31725
  }
31444
- const { lines, agentName, agentNameLineIndex, agentNameLineNumber } = parseAgentSourcePrelude(agentSource);
31445
- const commitments = [];
31446
- const nonCommitmentLines = agentNameLineIndex >= 0 ? [lines[agentNameLineIndex]] : [];
31447
- // Parse commitments with multiline support
31448
- let currentCommitment = null;
31449
- // Process lines starting from after the agent name line
31450
- const startIndex = agentNameLineIndex >= 0 ? agentNameLineIndex + 1 : 0;
31451
- let isInsideCodeBlock = false;
31452
- for (let i = startIndex; i < lines.length; i++) {
31453
- const line = lines[i];
31454
- if (line === undefined) {
31455
- continue;
31456
- }
31457
- const trimmedLine = line.trim();
31458
- // Check if this line starts or ends a code block
31459
- if (trimmedLine.startsWith('```')) {
31460
- isInsideCodeBlock = !isInsideCodeBlock;
31461
- if (currentCommitment) {
31462
- // If we are inside a commitment, the code block is part of it
31463
- currentCommitment.contentLines.push(line);
31464
- }
31465
- else {
31466
- // If we are not inside a commitment, the code block is non-commitment
31467
- nonCommitmentLines.push(line);
31468
- }
31469
- continue;
31470
- }
31471
- if (isInsideCodeBlock) {
31472
- if (currentCommitment) {
31473
- // If we are inside a commitment and a code block, the line is part of the commitment
31474
- currentCommitment.contentLines.push(line);
31475
- }
31476
- else {
31477
- // If we are inside a code block but not a commitment, the line is non-commitment
31478
- nonCommitmentLines.push(line);
31479
- }
31480
- continue;
31481
- }
31482
- // Check if this line starts a new commitment
31483
- let foundNewCommitment = false;
31484
- for (const definition of COMMITMENT_REGISTRY) {
31485
- const typeRegex = definition.createTypeRegex();
31486
- const match = typeRegex.exec(line.trim());
31487
- if (match && ((_a = match.groups) === null || _a === void 0 ? void 0 : _a.type)) {
31488
- // Save the previous commitment if it exists
31489
- if (currentCommitment) {
31490
- const fullContent = currentCommitment.contentLines.join('\n');
31491
- commitments.push({
31492
- type: currentCommitment.type,
31493
- content: _spaceTrim.spaceTrim(fullContent),
31494
- originalLine: currentCommitment.originalStartLine,
31495
- lineNumber: currentCommitment.startLineNumber,
31496
- });
31497
- }
31498
- // Extract the initial content from the commitment line
31499
- const fullRegex = definition.createRegex();
31500
- const fullMatch = fullRegex.exec(line.trim());
31501
- const initialContent = ((_b = fullMatch === null || fullMatch === void 0 ? void 0 : fullMatch.groups) === null || _b === void 0 ? void 0 : _b.contents) || '';
31502
- // Start a new commitment
31503
- currentCommitment = {
31504
- type: definition.type,
31505
- startLineNumber: i + 1,
31506
- originalStartLine: line,
31507
- contentLines: initialContent ? [initialContent] : [],
31508
- };
31509
- foundNewCommitment = true;
31510
- break;
31511
- }
31512
- }
31513
- // Check if this is a horizontal line (ends any current commitment)
31514
- const isHorizontalLine = HORIZONTAL_LINE_PATTERN$1.test(line);
31515
- if (isHorizontalLine) {
31516
- // Save the current commitment if it exists
31517
- if (currentCommitment) {
31518
- const fullContent = currentCommitment.contentLines.join('\n');
31519
- commitments.push({
31520
- type: currentCommitment.type,
31521
- content: _spaceTrim.spaceTrim(fullContent),
31522
- originalLine: currentCommitment.originalStartLine,
31523
- lineNumber: currentCommitment.startLineNumber,
31524
- });
31525
- currentCommitment = null;
31526
- }
31527
- // Add horizontal line to non-commitment lines
31528
- nonCommitmentLines.push(line);
31529
- continue;
31530
- }
31531
- if (!foundNewCommitment) {
31532
- if (currentCommitment) {
31533
- // This line belongs to the current commitment
31534
- currentCommitment.contentLines.push(line);
31535
- }
31536
- else {
31537
- // This line is not part of any commitment
31538
- nonCommitmentLines.push(line);
31539
- }
31540
- }
31726
+ if (metaTypeRaw.toUpperCase() === 'AVATAR') {
31727
+ applyMetaAvatarContent(state, metaValue);
31728
+ return;
31541
31729
  }
31542
- // Don't forget to save the last commitment if it exists
31543
- if (currentCommitment) {
31544
- const fullContent = currentCommitment.contentLines.join('\n');
31545
- commitments.push({
31546
- type: currentCommitment.type,
31547
- content: _spaceTrim.spaceTrim(fullContent),
31548
- originalLine: currentCommitment.originalStartLine,
31549
- lineNumber: currentCommitment.startLineNumber,
31550
- });
31730
+ const metaType = normalizeTo_camelCase(metaTypeRaw);
31731
+ state.meta[metaType] = metaValue;
31732
+ }
31733
+ /**
31734
+ * Applies META AVATAR content into the canonical `meta.avatar` field.
31735
+ */
31736
+ function applyMetaAvatarContent(state, content) {
31737
+ const avatarVisualId = resolveAvatarVisualId(content);
31738
+ if (avatarVisualId) {
31739
+ state.meta.avatar = avatarVisualId;
31740
+ return;
31551
31741
  }
31552
- return {
31553
- agentName,
31554
- agentNameLineNumber,
31555
- commitments,
31556
- nonCommitmentLines,
31557
- };
31742
+ delete state.meta.avatar;
31558
31743
  }
31559
-
31560
31744
  /**
31561
- * Parses parameters from text using both supported notations:
31562
- * 1. @Parameter - single word parameter starting with @
31563
- * 2. {parameterName} or {parameter with multiple words} or {parameterName: description text}
31564
- *
31565
- * Both notations represent the same syntax feature - parameters
31745
+ * Applies META LINK content into links and the canonical `meta.link` field.
31746
+ */
31747
+ function applyMetaLinkContent(state, content) {
31748
+ const linkValue = _spaceTrim.spaceTrim(content);
31749
+ state.links.push(linkValue);
31750
+ state.meta.link = linkValue;
31751
+ }
31752
+ /**
31753
+ * Applies META DOMAIN content into the normalized `meta.domain` field.
31754
+ */
31755
+ function applyMetaDomainContent(state, content) {
31756
+ state.meta.domain = normalizeMetaDomain(content);
31757
+ }
31758
+ /**
31759
+ * Applies META IMAGE content into the canonical `meta.image` field.
31760
+ */
31761
+ function applyMetaImageContent(state, content) {
31762
+ state.meta.image = _spaceTrim.spaceTrim(content);
31763
+ }
31764
+ /**
31765
+ * Applies META DESCRIPTION content into the canonical `meta.description` field.
31766
+ */
31767
+ function applyMetaDescriptionContent(state, content) {
31768
+ state.meta.description = _spaceTrim.spaceTrim(content);
31769
+ }
31770
+ /**
31771
+ * Applies META DISCLAIMER content into the canonical `meta.disclaimer` field.
31772
+ */
31773
+ function applyMetaDisclaimerContent(state, content) {
31774
+ state.meta.disclaimer = content;
31775
+ }
31776
+ /**
31777
+ * Applies META INPUT PLACEHOLDER content into the canonical `meta.inputPlaceholder` field.
31778
+ */
31779
+ function applyMetaInputPlaceholderContent(state, content) {
31780
+ state.meta.inputPlaceholder = _spaceTrim.spaceTrim(content);
31781
+ }
31782
+ /**
31783
+ * Applies MESSAGE SUFFIX content into the canonical `meta.messageSuffix` field.
31784
+ */
31785
+ function applyMessageSuffixContent(state, content) {
31786
+ state.meta.messageSuffix = content;
31787
+ }
31788
+ /**
31789
+ * Applies META COLOR content into the canonical `meta.color` field.
31790
+ */
31791
+ function applyMetaColorContent(state, content) {
31792
+ state.meta.color = normalizeSeparator(content);
31793
+ }
31794
+ /**
31795
+ * Applies META FONT content into the canonical `meta.font` field.
31796
+ */
31797
+ function applyMetaFontContent(state, content) {
31798
+ state.meta.font = normalizeSeparator(content);
31799
+ }
31800
+ /**
31801
+ * Applies META VOICE content into the canonical `meta.voice` field.
31802
+ */
31803
+ function applyMetaVoiceContent(state, content) {
31804
+ state.meta.voice = _spaceTrim.spaceTrim(content);
31805
+ }
31806
+ /**
31807
+ * Normalizes the separator in the content
31566
31808
  *
31567
- * @param text - Text to extract parameters from
31568
- * @returns Array of parsed parameters with unified representation
31809
+ * @param content - The content to normalize
31810
+ * @returns The content with normalized separators
31811
+ */
31812
+ function normalizeSeparator(content) {
31813
+ const trimmed = _spaceTrim.spaceTrim(content);
31814
+ if (trimmed.includes(',')) {
31815
+ return trimmed;
31816
+ }
31817
+ return trimmed.split(/\s+/).join(', ');
31818
+ }
31819
+ /**
31820
+ * Normalizes META DOMAIN content to a hostname-like value when possible.
31569
31821
  *
31570
- * @public exported from `@promptbook/core`
31822
+ * @param content - Raw META DOMAIN content.
31823
+ * @returns Normalized domain or a trimmed fallback.
31571
31824
  */
31572
- function parseParameters(text) {
31573
- const parameters = [];
31574
- // [🧠] Parameter syntax parsing - unified approach for two different notations of the same syntax feature
31575
- // The Book language supports parameters in two different notations but they represent the same concept
31576
- // Extract @Parameter notation (single word parameters starting with @)
31577
- const atParameterRegex = /@[\w\u00C0-\u017F\u0100-\u024F\u1E00-\u1EFF]+/gim;
31578
- text.replace(atParameterRegex, (match) => {
31579
- const parameterName = match.slice(1); // Remove the @ symbol
31580
- parameters.push({
31581
- text: match,
31582
- notation: 'at',
31583
- name: parameterName,
31584
- });
31585
- return match;
31586
- });
31587
- // Extract {parameter} notation (parameters in braces)
31588
- const braceParameterRegex = /\{([^}]+)\}/gim;
31589
- text.replace(braceParameterRegex, (match, content) => {
31590
- // Check if the parameter has a description (parameterName: description)
31591
- const colonIndex = content.indexOf(':');
31592
- if (colonIndex !== -1) {
31593
- const name = content.substring(0, colonIndex).trim();
31594
- const description = content.substring(colonIndex + 1).trim();
31595
- parameters.push({
31596
- text: match,
31597
- notation: 'brace',
31598
- name,
31599
- description,
31600
- });
31601
- }
31602
- else {
31603
- // Simple parameter without description
31604
- parameters.push({
31605
- text: match,
31606
- notation: 'brace',
31607
- name: content.trim(),
31608
- });
31609
- }
31610
- return match;
31611
- });
31612
- // Remove duplicates based on name (keep the first occurrence)
31613
- const uniqueParameters = parameters.filter((parameter, index, array) => {
31614
- return array.findIndex((parameterItem) => parameterItem.name === parameter.name) === index;
31615
- });
31616
- return uniqueParameters;
31825
+ function normalizeMetaDomain(content) {
31826
+ const trimmed = _spaceTrim.spaceTrim(content);
31827
+ return normalizeDomainForMatching(trimmed) || trimmed.toLowerCase();
31617
31828
  }
31618
31829
 
31619
31830
  /**
31620
- * Parses basic information from agent source
31621
- *
31622
- * There are 2 similar functions:
31623
- * - `parseAgentSource` which is a lightweight parser for agent source, it parses basic information and its purpose is to be quick and synchronous. The commitments there are hardcoded.
31624
- * - `createAgentModelRequirements` which is an asynchronous function that creates model requirements it applies each commitment one by one and works asynchronously.
31831
+ * Updates sample-conversation state for communication commitments.
31625
31832
  *
31626
- * @public exported from `@promptbook/core`
31833
+ * @private internal utility of `parseAgentSource`
31627
31834
  */
31628
- function parseAgentSource(agentSource) {
31629
- const parseResult = parseAgentSourceWithCommitments(agentSource);
31630
- const resolvedAgentName = parseResult.agentName || createDefaultAgentName(agentSource);
31631
- const personaDescription = extractAgentProfileText(parseResult.commitments);
31632
- const initialMessage = extractInitialMessage(parseResult.commitments);
31633
- const parsedProfile = extractParsedAgentProfile(parseResult.commitments);
31634
- ensureMetaFullname(parsedProfile.meta, resolvedAgentName);
31635
- return {
31636
- agentName: normalizeAgentName(resolvedAgentName),
31637
- agentHash: computeAgentHash(agentSource),
31638
- permanentId: parsedProfile.meta.id,
31639
- personaDescription,
31640
- initialMessage,
31641
- meta: parsedProfile.meta,
31642
- links: parsedProfile.links,
31643
- parameters: parseParameters(agentSource),
31644
- capabilities: parsedProfile.capabilities,
31645
- samples: parsedProfile.samples,
31646
- knowledgeSources: parsedProfile.knowledgeSources,
31647
- };
31835
+ function consumeConversationSampleCommitment(state, commitment) {
31836
+ switch (commitment.type) {
31837
+ case 'INITIAL MESSAGE':
31838
+ state.samples.push({ question: null, answer: commitment.content });
31839
+ return true;
31840
+ case 'USER MESSAGE':
31841
+ state.pendingUserMessage = commitment.content;
31842
+ return true;
31843
+ case 'INTERNAL MESSAGE':
31844
+ // INTERNAL MESSAGE stores trace payloads and is intentionally ignored in basic profile samples.
31845
+ return true;
31846
+ case 'AGENT MESSAGE':
31847
+ if (state.pendingUserMessage !== null) {
31848
+ state.samples.push({ question: state.pendingUserMessage, answer: commitment.content });
31849
+ state.pendingUserMessage = null;
31850
+ }
31851
+ return true;
31852
+ default:
31853
+ return false;
31854
+ }
31648
31855
  }
31856
+
31649
31857
  /**
31650
31858
  * Static capability descriptors for commitments that map one-to-one to a visible capability.
31651
- *
31652
- * @private internal utility of `parseAgentSource`
31653
31859
  */
31654
31860
  const SIMPLE_CAPABILITY_BY_COMMITMENT_TYPE = {
31655
31861
  'USE BROWSER': {
@@ -31712,142 +31918,11 @@
31712
31918
  label: 'Calendar',
31713
31919
  iconName: 'Calendar',
31714
31920
  },
31715
- };
31716
- /**
31717
- * Dedicated handlers for META-style commitments that directly map onto parsed meta fields.
31718
- *
31719
- * @private internal utility of `parseAgentSource`
31720
- */
31721
- const META_COMMITMENT_APPLIERS = {
31722
- 'META AVATAR': applyMetaAvatarContent,
31723
- 'META LINK': applyMetaLinkContent,
31724
- 'META DOMAIN': applyMetaDomainContent,
31725
- 'META IMAGE': applyMetaImageContent,
31726
- 'META DESCRIPTION': applyMetaDescriptionContent,
31727
- 'META DISCLAIMER': applyMetaDisclaimerContent,
31728
- 'META INPUT PLACEHOLDER': applyMetaInputPlaceholderContent,
31729
- 'MESSAGE SUFFIX': applyMessageSuffixContent,
31730
- 'META COLOR': applyMetaColorContent,
31731
- 'META FONT': applyMetaFontContent,
31732
- 'META VOICE': applyMetaVoiceContent,
31733
- };
31734
- /**
31735
- * Detects local slash-based references used by FROM and IMPORT commitments.
31736
- *
31737
- * @private internal utility of `parseAgentSource`
31738
- */
31739
- const LOCAL_AGENT_REFERENCE_PREFIXES = ['./', '../', '/'];
31740
- /**
31741
- * Resolves the public agent profile text from the last GOAL/GOALS commitment,
31742
- * falling back to the deprecated PERSONA/PERSONAE commitments when no goal exists.
31743
- *
31744
- * @private internal utility of `parseAgentSource`
31745
- */
31746
- function extractAgentProfileText(commitments) {
31747
- let goalDescription = '';
31748
- let hasGoalDescription = false;
31749
- let personaDescription = '';
31750
- let hasPersonaDescription = false;
31751
- for (const commitment of commitments) {
31752
- if (commitment.type === 'GOAL' || commitment.type === 'GOALS') {
31753
- goalDescription = commitment.content;
31754
- hasGoalDescription = true;
31755
- }
31756
- if (commitment.type === 'PERSONA' || commitment.type === 'PERSONAE') {
31757
- personaDescription = commitment.content;
31758
- hasPersonaDescription = true;
31759
- }
31760
- }
31761
- if (hasGoalDescription) {
31762
- return goalDescription;
31763
- }
31764
- if (hasPersonaDescription) {
31765
- return personaDescription;
31766
- }
31767
- return null;
31768
- }
31769
- /**
31770
- * Resolves the last INITIAL MESSAGE commitment, which is the public initial-message value.
31771
- *
31772
- * @private internal utility of `parseAgentSource`
31773
- */
31774
- function extractInitialMessage(commitments) {
31775
- let initialMessage = null;
31776
- for (const commitment of commitments) {
31777
- if (commitment.type === 'INITIAL MESSAGE') {
31778
- initialMessage = commitment.content;
31779
- }
31780
- }
31781
- return initialMessage;
31782
- }
31783
- /**
31784
- * Collects capability, sample, meta, link, and knowledge-source data from commitments.
31785
- *
31786
- * @private internal utility of `parseAgentSource`
31787
- */
31788
- function extractParsedAgentProfile(commitments) {
31789
- const state = {
31790
- meta: {},
31791
- links: [],
31792
- capabilities: [],
31793
- samples: [],
31794
- knowledgeSources: [],
31795
- pendingUserMessage: null,
31796
- knownKnowledgeSourceUrls: new Set(),
31797
- };
31798
- for (const commitment of commitments) {
31799
- processParsedCommitment(state, commitment);
31800
- }
31801
- return {
31802
- meta: state.meta,
31803
- links: state.links,
31804
- capabilities: state.capabilities,
31805
- samples: state.samples,
31806
- knowledgeSources: state.knowledgeSources,
31807
- };
31808
- }
31809
- /**
31810
- * Processes one parsed commitment through the sample, capability, and meta stages.
31811
- *
31812
- * @private internal utility of `parseAgentSource`
31813
- */
31814
- function processParsedCommitment(state, commitment) {
31815
- if (consumeConversationSampleCommitment(state, commitment)) {
31816
- return;
31817
- }
31818
- const capabilities = createCapabilitiesFromCommitment(state, commitment);
31819
- if (capabilities.length > 0) {
31820
- state.capabilities.push(...capabilities);
31821
- return;
31822
- }
31823
- applyMetaCommitment(state, commitment);
31824
- }
31825
- /**
31826
- * Updates sample-conversation state for communication commitments.
31827
- *
31828
- * @private internal utility of `parseAgentSource`
31829
- */
31830
- function consumeConversationSampleCommitment(state, commitment) {
31831
- switch (commitment.type) {
31832
- case 'INITIAL MESSAGE':
31833
- state.samples.push({ question: null, answer: commitment.content });
31834
- return true;
31835
- case 'USER MESSAGE':
31836
- state.pendingUserMessage = commitment.content;
31837
- return true;
31838
- case 'INTERNAL MESSAGE':
31839
- // INTERNAL MESSAGE stores trace payloads and is intentionally ignored in basic profile samples.
31840
- return true;
31841
- case 'AGENT MESSAGE':
31842
- if (state.pendingUserMessage !== null) {
31843
- state.samples.push({ question: state.pendingUserMessage, answer: commitment.content });
31844
- state.pendingUserMessage = null;
31845
- }
31846
- return true;
31847
- default:
31848
- return false;
31849
- }
31850
- }
31921
+ };
31922
+ /**
31923
+ * Detects local slash-based references used by FROM and IMPORT commitments.
31924
+ */
31925
+ const LOCAL_AGENT_REFERENCE_PREFIXES = ['./', '../', '/'];
31851
31926
  /**
31852
31927
  * Creates the visible capabilities produced by one parsed commitment.
31853
31928
  *
@@ -31877,8 +31952,6 @@
31877
31952
  }
31878
31953
  /**
31879
31954
  * Clones one static capability descriptor for a simple capability commitment.
31880
- *
31881
- * @private internal utility of `parseAgentSource`
31882
31955
  */
31883
31956
  function createSimpleCapability(commitmentType) {
31884
31957
  const capability = SIMPLE_CAPABILITY_BY_COMMITMENT_TYPE[commitmentType];
@@ -31886,8 +31959,6 @@
31886
31959
  }
31887
31960
  /**
31888
31961
  * Creates the USE PROJECT capability badge.
31889
- *
31890
- * @private internal utility of `parseAgentSource`
31891
31962
  */
31892
31963
  function createProjectCapability(content) {
31893
31964
  var _a;
@@ -31901,8 +31972,6 @@
31901
31972
  }
31902
31973
  /**
31903
31974
  * Creates the FROM inheritance capability when the reference should stay visible in the profile.
31904
- *
31905
- * @private internal utility of `parseAgentSource`
31906
31975
  */
31907
31976
  function createInheritanceCapability(content) {
31908
31977
  const reference = extractFirstCommitmentLine(content);
@@ -31929,8 +31998,6 @@
31929
31998
  }
31930
31999
  /**
31931
32000
  * Creates the IMPORT capability badge.
31932
- *
31933
- * @private internal utility of `parseAgentSource`
31934
32001
  */
31935
32002
  function createImportCapability(content) {
31936
32003
  const reference = extractFirstCommitmentLine(content);
@@ -31958,8 +32025,6 @@
31958
32025
  }
31959
32026
  /**
31960
32027
  * Creates TEAM capability badges for all parsed teammates.
31961
- *
31962
- * @private internal utility of `parseAgentSource`
31963
32028
  */
31964
32029
  function createTeamCapabilities(content) {
31965
32030
  const teammates = parseTeamCommitmentContent(content);
@@ -31972,8 +32037,6 @@
31972
32037
  }
31973
32038
  /**
31974
32039
  * Creates the KNOWLEDGE capability badge and records URL-based knowledge sources.
31975
- *
31976
- * @private internal utility of `parseAgentSource`
31977
32040
  */
31978
32041
  function createKnowledgeCapability(state, content) {
31979
32042
  const trimmedContent = _spaceTrim.spaceTrim(content);
@@ -31988,8 +32051,6 @@
31988
32051
  }
31989
32052
  /**
31990
32053
  * Stores unique URL-based knowledge sources for later citation resolution.
31991
- *
31992
- * @private internal utility of `parseAgentSource`
31993
32054
  */
31994
32055
  function rememberKnowledgeSources(state, extractedUrls) {
31995
32056
  for (const extractedUrl of extractedUrls) {
@@ -32013,8 +32074,6 @@
32013
32074
  }
32014
32075
  /**
32015
32076
  * Derives the visible KNOWLEDGE badge label and icon from commitment content.
32016
- *
32017
- * @private internal utility of `parseAgentSource`
32018
32077
  */
32019
32078
  function createKnowledgeCapabilityPresentation(content, extractedUrls) {
32020
32079
  let label = content;
@@ -32047,8 +32106,6 @@
32047
32106
  }
32048
32107
  /**
32049
32108
  * Shortens text-only KNOWLEDGE commitments into the same preview label as before.
32050
- *
32051
- * @private internal utility of `parseAgentSource`
32052
32109
  */
32053
32110
  function createKnowledgeTextLabel(content) {
32054
32111
  const words = content.split(/\s+/);
@@ -32057,592 +32114,196 @@
32057
32114
  }
32058
32115
  return content;
32059
32116
  }
32060
- /**
32061
- * Applies META-style commitments that mutate parsed profile metadata.
32062
- *
32063
- * @private internal utility of `parseAgentSource`
32064
- */
32065
- function applyMetaCommitment(state, commitment) {
32066
- const applyMetaContent = META_COMMITMENT_APPLIERS[commitment.type];
32067
- if (applyMetaContent) {
32068
- applyMetaContent(state, commitment.content);
32069
- return;
32070
- }
32071
- if (commitment.type === 'META') {
32072
- applyGenericMetaCommitment(state, commitment.content);
32073
- }
32074
- }
32075
- /**
32076
- * Applies the generic META commitment form (`META TYPE value`).
32077
- *
32078
- * @private internal utility of `parseAgentSource`
32079
- */
32080
- function applyGenericMetaCommitment(state, content) {
32081
- const metaTypeRaw = content.split(' ')[0] || 'NONE';
32082
- const metaValue = _spaceTrim.spaceTrim(content.substring(metaTypeRaw.length));
32083
- if (metaTypeRaw === 'LINK') {
32084
- state.links.push(metaValue);
32085
- }
32086
- if (metaTypeRaw.toUpperCase() === 'AVATAR') {
32087
- applyMetaAvatarContent(state, metaValue);
32088
- return;
32089
- }
32090
- const metaType = normalizeTo_camelCase(metaTypeRaw);
32091
- state.meta[metaType] = metaValue;
32092
- }
32093
- /**
32094
- * Applies META AVATAR content into the canonical `meta.avatar` field.
32095
- *
32096
- * @private internal utility of `parseAgentSource`
32097
- */
32098
- function applyMetaAvatarContent(state, content) {
32099
- const avatarVisualId = resolveAvatarVisualId(content);
32100
- if (avatarVisualId) {
32101
- state.meta.avatar = avatarVisualId;
32102
- return;
32103
- }
32104
- delete state.meta.avatar;
32105
- }
32106
- /**
32107
- * Applies META LINK content into links and the canonical `meta.link` field.
32108
- *
32109
- * @private internal utility of `parseAgentSource`
32110
- */
32111
- function applyMetaLinkContent(state, content) {
32112
- const linkValue = _spaceTrim.spaceTrim(content);
32113
- state.links.push(linkValue);
32114
- state.meta.link = linkValue;
32115
- }
32116
- /**
32117
- * Applies META DOMAIN content into the normalized `meta.domain` field.
32118
- *
32119
- * @private internal utility of `parseAgentSource`
32120
- */
32121
- function applyMetaDomainContent(state, content) {
32122
- state.meta.domain = normalizeMetaDomain(content);
32123
- }
32124
- /**
32125
- * Applies META IMAGE content into the canonical `meta.image` field.
32126
- *
32127
- * @private internal utility of `parseAgentSource`
32128
- */
32129
- function applyMetaImageContent(state, content) {
32130
- state.meta.image = _spaceTrim.spaceTrim(content);
32131
- }
32132
- /**
32133
- * Applies META DESCRIPTION content into the canonical `meta.description` field.
32134
- *
32135
- * @private internal utility of `parseAgentSource`
32136
- */
32137
- function applyMetaDescriptionContent(state, content) {
32138
- state.meta.description = _spaceTrim.spaceTrim(content);
32139
- }
32140
- /**
32141
- * Applies META DISCLAIMER content into the canonical `meta.disclaimer` field.
32142
- *
32143
- * @private internal utility of `parseAgentSource`
32144
- */
32145
- function applyMetaDisclaimerContent(state, content) {
32146
- state.meta.disclaimer = content;
32147
- }
32148
- /**
32149
- * Applies META INPUT PLACEHOLDER content into the canonical `meta.inputPlaceholder` field.
32150
- *
32151
- * @private internal utility of `parseAgentSource`
32152
- */
32153
- function applyMetaInputPlaceholderContent(state, content) {
32154
- state.meta.inputPlaceholder = _spaceTrim.spaceTrim(content);
32155
- }
32156
- /**
32157
- * Applies MESSAGE SUFFIX content into the canonical `meta.messageSuffix` field.
32158
- *
32159
- * @private internal utility of `parseAgentSource`
32160
- */
32161
- function applyMessageSuffixContent(state, content) {
32162
- state.meta.messageSuffix = content;
32163
- }
32164
- /**
32165
- * Applies META COLOR content into the canonical `meta.color` field.
32166
- *
32167
- * @private internal utility of `parseAgentSource`
32168
- */
32169
- function applyMetaColorContent(state, content) {
32170
- state.meta.color = normalizeSeparator(content);
32171
- }
32172
- /**
32173
- * Applies META FONT content into the canonical `meta.font` field.
32174
- *
32175
- * @private internal utility of `parseAgentSource`
32176
- */
32177
- function applyMetaFontContent(state, content) {
32178
- state.meta.font = normalizeSeparator(content);
32179
- }
32180
- /**
32181
- * Applies META VOICE content into the canonical `meta.voice` field.
32182
- *
32183
- * @private internal utility of `parseAgentSource`
32184
- */
32185
- function applyMetaVoiceContent(state, content) {
32186
- state.meta.voice = _spaceTrim.spaceTrim(content);
32187
- }
32188
- /**
32189
- * Ensures the parsed profile always exposes a fullname value.
32190
- *
32191
- * @private internal utility of `parseAgentSource`
32192
- */
32193
- function ensureMetaFullname(meta, fallbackFullname) {
32194
- if (!meta.fullname) {
32195
- meta.fullname = fallbackFullname;
32196
- }
32197
- }
32198
32117
  /**
32199
32118
  * Extracts the first logical line from multiline commitment content.
32200
- *
32201
- * @private internal utility of `parseAgentSource`
32202
32119
  */
32203
32120
  function extractFirstCommitmentLine(content) {
32204
32121
  return _spaceTrim.spaceTrim(content).split(/\r?\n/)[0] || '';
32205
32122
  }
32206
32123
  /**
32207
32124
  * Detects local FROM/IMPORT references that should use local-link labels and icons.
32208
- *
32209
- * @private internal utility of `parseAgentSource`
32210
32125
  */
32211
32126
  function isLocalAgentReference(reference) {
32212
32127
  return LOCAL_AGENT_REFERENCE_PREFIXES.some((prefix) => reference.startsWith(prefix));
32213
32128
  }
32214
- /**
32215
- * Normalizes the separator in the content
32216
- *
32217
- * @param content - The content to normalize
32218
- * @returns The content with normalized separators
32219
- *
32220
- * @private internal utility of `parseAgentSource`
32221
- */
32222
- function normalizeSeparator(content) {
32223
- const trimmed = _spaceTrim.spaceTrim(content);
32224
- if (trimmed.includes(',')) {
32225
- return trimmed;
32226
- }
32227
- return trimmed.split(/\s+/).join(', ');
32228
- }
32229
- /**
32230
- * Normalizes META DOMAIN content to a hostname-like value when possible.
32231
- *
32232
- * @param content - Raw META DOMAIN content.
32233
- * @returns Normalized domain or a trimmed fallback.
32234
- *
32235
- * @private internal utility of `parseAgentSource`
32236
- */
32237
- function normalizeMetaDomain(content) {
32238
- const trimmed = _spaceTrim.spaceTrim(content);
32239
- return normalizeDomainForMatching(trimmed) || trimmed.toLowerCase();
32240
- }
32241
- // TODO: [🕛] Unite `AgentBasicInformation`, `ChatParticipant`, `LlmExecutionTools` + `LlmToolsMetadata`
32242
32129
 
32243
32130
  /**
32244
- * Gets all tool titles provided by all commitments
32131
+ * Collects capability, sample, meta, link, and knowledge-source data from commitments.
32245
32132
  *
32246
- * @public exported from `@promptbook/core`
32133
+ * @private internal utility of `parseAgentSource`
32247
32134
  */
32248
- function getAllCommitmentsToolTitles() {
32249
- const allToolTitles = {};
32250
- for (const commitmentDefinition of getAllCommitmentDefinitions()) {
32251
- const toolTitles = commitmentDefinition.getToolTitles();
32252
- for (const [funcName, title] of Object.entries(toolTitles)) {
32253
- if (allToolTitles[funcName] !== undefined &&
32254
- just(false) /* <- Note: [⛹️] How to deal with commitment aliases */) {
32255
- throw new UnexpectedError(`Duplicate tool function name detected: \`${funcName}\` provided by commitment \`${commitmentDefinition.type}\``);
32256
- }
32257
- allToolTitles[funcName] = title;
32258
- }
32135
+ function extractParsedAgentProfile(commitments) {
32136
+ const state = {
32137
+ meta: {},
32138
+ links: [],
32139
+ capabilities: [],
32140
+ samples: [],
32141
+ knowledgeSources: [],
32142
+ pendingUserMessage: null,
32143
+ knownKnowledgeSourceUrls: new Set(),
32144
+ };
32145
+ for (const commitment of commitments) {
32146
+ processParsedCommitment(state, commitment);
32259
32147
  }
32260
- return allToolTitles;
32148
+ return {
32149
+ meta: state.meta,
32150
+ links: state.links,
32151
+ capabilities: state.capabilities,
32152
+ samples: state.samples,
32153
+ knowledgeSources: state.knowledgeSources,
32154
+ };
32261
32155
  }
32262
-
32263
32156
  /**
32264
- * Restricts an Updatable to a (2) BehaviorSubject variant
32265
- *
32266
- * @see Updatable
32267
- *
32268
- * @private internal utility <- TODO: [🧠] Maybe export from `@promptbook/types`
32157
+ * Processes one parsed commitment through the sample, capability, and meta stages.
32269
32158
  */
32270
- function asUpdatableSubject(value) {
32271
- if (value instanceof rxjs.BehaviorSubject) {
32272
- return value;
32273
- }
32274
- else if (Array.isArray(value)) {
32275
- if (value.length !== 2) {
32276
- throw new TypeError('`asUpdatableSubject`: Invalid tuple length, expected 2 elements');
32277
- }
32278
- if (typeof value[1] !== 'function') {
32279
- throw new TypeError('`asUpdatableSubject`: Invalid tuple, expected second element to be a function');
32280
- }
32281
- const [theValue, setValue] = value;
32282
- const subject = new rxjs.BehaviorSubject(theValue);
32283
- subject.subscribe((newValue) => {
32284
- setValue(newValue);
32285
- });
32286
- return subject;
32159
+ function processParsedCommitment(state, commitment) {
32160
+ if (consumeConversationSampleCommitment(state, commitment)) {
32161
+ return;
32287
32162
  }
32288
- else {
32289
- return new rxjs.BehaviorSubject(value);
32163
+ const capabilities = createCapabilitiesFromCommitment(state, commitment);
32164
+ if (capabilities.length > 0) {
32165
+ state.capabilities.push(...capabilities);
32166
+ return;
32290
32167
  }
32168
+ applyMetaCommitment(state, commitment);
32291
32169
  }
32292
- // TODO: [🧠] Maybe `BehaviorSubject` is too heavy for this use case, maybe just tuple `[value,setValue]` is enough
32293
32170
 
32294
32171
  /**
32295
- * Creates an empty/basic agent model requirements object
32296
- * This serves as the starting point for the reduce-like pattern
32297
- * where each commitment applies its changes to build the final requirements
32172
+ * Parses basic information from agent source
32298
32173
  *
32299
- * @public exported from `@promptbook/core`
32300
- */
32301
- function createEmptyAgentModelRequirements() {
32302
- return {
32303
- systemMessage: '',
32304
- promptSuffix: '',
32305
- // modelName: 'gpt-5',
32306
- modelName: 'gpt-5.4-mini',
32307
- temperature: 0.7,
32308
- topP: 0.9,
32309
- topK: 50,
32310
- parentAgentUrl: null,
32311
- isClosed: false,
32312
- };
32313
- }
32314
- /**
32315
- * Creates a basic agent model requirements with just the agent name
32316
- * This is used when we have an agent name but no commitments
32174
+ * There are 2 similar functions:
32175
+ * - `parseAgentSource` which is a lightweight parser for agent source, it parses basic information and its purpose is to be quick and synchronous. The commitments there are hardcoded.
32176
+ * - `createAgentModelRequirements` which is an asynchronous function that creates model requirements it applies each commitment one by one and works asynchronously.
32317
32177
  *
32318
32178
  * @public exported from `@promptbook/core`
32319
32179
  */
32320
- function createBasicAgentModelRequirements(agentName) {
32321
- const empty = createEmptyAgentModelRequirements();
32180
+ function parseAgentSource(agentSource) {
32181
+ const parseResult = parseAgentSourceWithCommitments(agentSource);
32182
+ const resolvedAgentName = parseResult.agentName || createDefaultAgentName(agentSource);
32183
+ const personaDescription = extractAgentProfileText(parseResult.commitments);
32184
+ const initialMessage = extractInitialMessage(parseResult.commitments);
32185
+ const parsedProfile = extractParsedAgentProfile(parseResult.commitments);
32186
+ ensureMetaFullname(parsedProfile.meta, resolvedAgentName);
32322
32187
  return {
32323
- ...empty,
32324
- systemMessage: `You are ${agentName || 'AI Agent'}`,
32188
+ agentName: normalizeAgentName(resolvedAgentName),
32189
+ agentHash: computeAgentHash(agentSource),
32190
+ permanentId: parsedProfile.meta.id,
32191
+ personaDescription,
32192
+ initialMessage,
32193
+ meta: parsedProfile.meta,
32194
+ links: parsedProfile.links,
32195
+ parameters: parseParameters(agentSource),
32196
+ capabilities: parsedProfile.capabilities,
32197
+ samples: parsedProfile.samples,
32198
+ knowledgeSources: parsedProfile.knowledgeSources,
32325
32199
  };
32326
32200
  }
32327
- // TODO: [🐤] Deduplicate `AgentModelRequirements` and `ModelRequirements` model requirements
32328
-
32329
- /**
32330
- * Gets a commitment definition by its type
32331
- *
32332
- * @param type The commitment type to look up
32333
- * @returns The commitment definition or null if not found
32334
- *
32335
- * @public exported from `@promptbook/core`
32336
- */
32337
- function getCommitmentDefinition(type) {
32338
- return COMMITMENT_REGISTRY.find((commitmentDefinition) => commitmentDefinition.type === type) || null;
32339
- }
32340
-
32341
- /**
32342
- * Plugin for importing agent books *(`.book` files)*
32343
- *
32344
- * @private [🥝] Maybe export the import plugins through some package
32345
- */
32346
- const AgentFileImportPlugin = {
32347
- name: 'agent-file-import-plugin',
32348
- canImport(mimeType) {
32349
- // [🧠] Should we have a specific MIME type for agent books?
32350
- // For now, let's assume it's identified by .book extension or certain MIME types if provided
32351
- return mimeType === 'text/x-promptbook' || mimeType === 'application/x-promptbook';
32352
- },
32353
- import(content) {
32354
- const parseResult = parseAgentSourceWithCommitments(content);
32355
- // Bring only the agent corpus (non-commitment lines and relevant commitments)
32356
- // Stripping the agent name (which is usually the first line)
32357
- const corpus = parseResult.nonCommitmentLines
32358
- .filter((line, index) => index > 0 || !parseResult.agentName)
32359
- .join('\n')
32360
- .trim();
32361
- // Also include relevant commitments that make up the "corpus" of the agent
32362
- // For example PERSONA, RULE, KNOWLEDGE
32363
- const relevantCommitments = parseResult.commitments
32364
- .filter((c) => ['PERSONA', 'RULE', 'KNOWLEDGE'].includes(c.type))
32365
- .map((c) => `${c.type} ${c.content}`)
32366
- .join('\n\n');
32367
- return _spaceTrim.spaceTrim((block) => `
32368
- ${block(relevantCommitments)}
32369
-
32370
- ${block(corpus)}
32371
- `).trim();
32372
- },
32373
- };
32374
-
32375
- /**
32376
- * Plugin for importing JSON files
32377
- *
32378
- * @private [🥝] Maybe export the import plugins through some package
32379
- */
32380
- const JsonFileImportPlugin = {
32381
- name: 'json-file-import-plugin',
32382
- canImport(mimeType) {
32383
- return mimeType === 'application/json' || mimeType.endsWith('+json');
32384
- },
32385
- import(content) {
32386
- try {
32387
- const json = JSON.parse(content);
32388
- const formattedJson = JSON.stringify(json, null, 4);
32389
- return `\`\`\`json\n${formattedJson}\n\`\`\``;
32390
- }
32391
- catch (error) {
32392
- // If JSON is invalid, still import it but maybe not as pretty JSON
32393
- return `\`\`\`json\n${content}\n\`\`\``;
32394
- }
32395
- },
32396
- };
32397
-
32398
- /**
32399
- * Plugin for importing generic text files
32400
- *
32401
- * @private [🥝] Maybe export the import plugins through some package
32402
- */
32403
- const TextFileImportPlugin = {
32404
- name: 'text-file-import-plugin',
32405
- canImport(mimeType) {
32406
- return (mimeType === 'text/plain' ||
32407
- mimeType === 'text/markdown' ||
32408
- mimeType === 'text/x-typescript' ||
32409
- mimeType === 'text/javascript' ||
32410
- mimeType === 'text/css' ||
32411
- mimeType === 'text/html' ||
32412
- mimeType.startsWith('text/'));
32413
- },
32414
- import(content, mimeType) {
32415
- const extension = mimeTypeToExtension(mimeType);
32416
- const codeBlockType = extension || 'txt';
32417
- return `\`\`\`${codeBlockType}\n${content}\n\`\`\``;
32418
- },
32419
- };
32420
-
32421
- /**
32422
- * All available file import plugins
32423
- *
32424
- * @private [🥝] Maybe export the import plugins through some package
32425
- */
32426
- const $fileImportPlugins = [
32427
- AgentFileImportPlugin,
32428
- JsonFileImportPlugin,
32429
- TextFileImportPlugin,
32430
- ];
32431
-
32432
- /**
32433
- * Removes single-hash comment lines (`# Comment`) from a system message
32434
- * This is used to clean up the final system message before sending it to the AI model
32435
- * while preserving the original content with comments in metadata
32436
- *
32437
- * @param systemMessage The system message that may contain comment lines
32438
- * @returns The system message with single-hash comment lines removed
32439
- *
32440
- * @private - TODO: [🧠] Maybe should be public?
32441
- */
32442
- function removeCommentsFromSystemMessage(systemMessage) {
32443
- if (!systemMessage) {
32444
- return systemMessage;
32445
- }
32446
- const lines = systemMessage.split(/\r?\n/);
32447
- const filteredLines = lines.filter((line) => {
32448
- const trimmedLine = line.trim();
32449
- // Remove only single-hash comment markers (`# Comment`) and keep markdown headings (`## Heading`).
32450
- return !/^#(?!#)\s/.test(trimmedLine);
32451
- });
32452
- return filteredLines.join('\n').trim();
32453
- }
32454
-
32455
- /**
32456
- * Commitment types whose content may contain compact agent references that must be resolved before applying the commitment.
32457
- *
32458
- * @private internal constant of `createAgentModelRequirementsWithCommitments`
32459
- */
32460
- const COMMITMENTS_WITH_AGENT_REFERENCES = new Set(['FROM', 'IMPORT', 'IMPORTS', 'TEAM']);
32461
- /**
32462
- * DELETE-like commitment types that invalidate earlier tagged commitments.
32463
- *
32464
- * @private internal constant of `createAgentModelRequirementsWithCommitments`
32465
- */
32466
- const DELETE_COMMITMENT_TYPES = new Set(['DELETE', 'CANCEL', 'DISCARD', 'REMOVE']);
32467
- /**
32468
- * Commitments whose earlier occurrences are overwritten by the last occurrence in source order.
32469
- *
32470
- * @private internal constant of `createAgentModelRequirementsWithCommitments`
32471
- */
32472
- const OVERWRITTEN_COMMITMENT_GROUP_BY_TYPE = new Map([
32473
- ['GOAL', 'GOAL'],
32474
- ['GOALS', 'GOAL'],
32475
- ]);
32476
- /**
32477
- * Regex pattern matching markdown horizontal lines that should not be copied into the final system message.
32478
- *
32479
- * @private internal constant of `createAgentModelRequirementsWithCommitments`
32480
- */
32481
- const HORIZONTAL_LINE_PATTERN = /^[\s]*[-_*][\s]*[-_*][\s]*[-_*][\s]*[-_*]*[\s]*$/;
32482
- /**
32483
- * MIME type prefixes treated as binary and therefore not eligible for text import plugins.
32484
- *
32485
- * @private internal constant of `createAgentModelRequirementsWithCommitments`
32486
- */
32487
- const BINARY_MIME_TYPE_PREFIXES = [
32488
- 'image/',
32489
- 'video/',
32490
- 'audio/',
32491
- 'application/octet-stream',
32492
- 'application/pdf',
32493
- 'application/zip',
32494
- ];
32495
- /**
32496
- * Returns a safe fallback content when a resolver fails to transform a reference commitment.
32497
- *
32498
- * @param commitmentType - Commitment being resolved.
32499
- * @param originalContent - Original unresolved commitment content.
32500
- * @returns Fallback content that keeps requirement creation resilient.
32501
- *
32502
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32503
- */
32504
- function getSafeReferenceCommitmentFallback(commitmentType, originalContent) {
32505
- if (commitmentType === 'FROM') {
32506
- return 'VOID';
32507
- }
32508
- if (commitmentType === 'IMPORT' || commitmentType === 'IMPORTS' || commitmentType === 'TEAM') {
32509
- return '';
32510
- }
32511
- return originalContent;
32512
- }
32513
- /**
32514
- * Creates agent model requirements by parsing commitments, applying them in source order,
32515
- * and finalizing derived sections such as imports, example interactions, and inline knowledge uploads.
32516
- *
32517
- * @param agentSource - Agent source book to parse.
32518
- * @param modelName - Optional override for the agent model name.
32519
- * @param options - Additional options such as reference and teammate resolvers.
32520
- * @returns Fully prepared model requirements for the parsed agent source.
32521
- *
32522
- * @private internal utility of `createAgentModelRequirements`
32523
- */
32524
- async function createAgentModelRequirementsWithCommitments(agentSource, modelName, options) {
32525
- const parseResult = parseAgentSourceWithCommitments(agentSource);
32526
- const filteredCommitments = filterOverwrittenCommitments(filterDeletedCommitments(parseResult.commitments));
32527
- let requirements = createInitialAgentModelRequirements(parseResult.agentName, modelName);
32528
- requirements = await applyCommitmentsToRequirements(requirements, filteredCommitments, options);
32529
- requirements = aggregateUseCommitmentSystemMessages(requirements, filteredCommitments);
32530
- requirements = await importReferencedFiles(requirements);
32531
- requirements = appendMcpServers(requirements, agentSource);
32532
- requirements = appendNonCommitmentContent(requirements, parseResult);
32533
- requirements = appendExampleInteractions(requirements, parseResult);
32534
- requirements = await applyPendingInlineKnowledgeSources(requirements, options === null || options === void 0 ? void 0 : options.inlineKnowledgeSourceUploader);
32535
- return finalizeRequirements(requirements);
32536
- }
32201
+ // TODO: [🕛] Unite `AgentBasicInformation`, `ChatParticipant`, `LlmExecutionTools` + `LlmToolsMetadata`
32202
+
32537
32203
  /**
32538
- * Removes earlier commitments that are overwritten by later commitments of the same semantic group.
32204
+ * Gets all tool titles provided by all commitments
32539
32205
  *
32540
- * This currently keeps only the last `GOAL` / `GOALS` commitment so inheritance rewrites
32541
- * and multi-goal sources expose one effective goal to the runtime.
32206
+ * @public exported from `@promptbook/core`
32207
+ */
32208
+ function getAllCommitmentsToolTitles() {
32209
+ const allToolTitles = {};
32210
+ for (const commitmentDefinition of getAllCommitmentDefinitions()) {
32211
+ const toolTitles = commitmentDefinition.getToolTitles();
32212
+ for (const [funcName, title] of Object.entries(toolTitles)) {
32213
+ if (allToolTitles[funcName] !== undefined &&
32214
+ just(false) /* <- Note: [⛹️] How to deal with commitment aliases */) {
32215
+ throw new UnexpectedError(`Duplicate tool function name detected: \`${funcName}\` provided by commitment \`${commitmentDefinition.type}\``);
32216
+ }
32217
+ allToolTitles[funcName] = title;
32218
+ }
32219
+ }
32220
+ return allToolTitles;
32221
+ }
32222
+
32223
+ /**
32224
+ * Restricts an Updatable to a (2) BehaviorSubject variant
32542
32225
  *
32543
- * @param commitments - Parsed commitments after DELETE-like filtering.
32544
- * @returns Commitments with overwritten entries removed while preserving source order.
32226
+ * @see Updatable
32545
32227
  *
32546
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32228
+ * @private internal utility <- TODO: [🧠] Maybe export from `@promptbook/types`
32547
32229
  */
32548
- function filterOverwrittenCommitments(commitments) {
32549
- const seenOverwriteGroups = new Set();
32550
- const keptCommitments = [];
32551
- for (let index = commitments.length - 1; index >= 0; index--) {
32552
- const commitment = commitments[index];
32553
- const overwriteGroup = OVERWRITTEN_COMMITMENT_GROUP_BY_TYPE.get(commitment.type);
32554
- if (!overwriteGroup) {
32555
- keptCommitments.push(commitment);
32556
- continue;
32230
+ function asUpdatableSubject(value) {
32231
+ if (value instanceof rxjs.BehaviorSubject) {
32232
+ return value;
32233
+ }
32234
+ else if (Array.isArray(value)) {
32235
+ if (value.length !== 2) {
32236
+ throw new TypeError('`asUpdatableSubject`: Invalid tuple length, expected 2 elements');
32557
32237
  }
32558
- if (seenOverwriteGroups.has(overwriteGroup)) {
32559
- continue;
32238
+ if (typeof value[1] !== 'function') {
32239
+ throw new TypeError('`asUpdatableSubject`: Invalid tuple, expected second element to be a function');
32560
32240
  }
32561
- seenOverwriteGroups.add(overwriteGroup);
32562
- keptCommitments.push(commitment);
32241
+ const [theValue, setValue] = value;
32242
+ const subject = new rxjs.BehaviorSubject(theValue);
32243
+ subject.subscribe((newValue) => {
32244
+ setValue(newValue);
32245
+ });
32246
+ return subject;
32247
+ }
32248
+ else {
32249
+ return new rxjs.BehaviorSubject(value);
32563
32250
  }
32564
- return keptCommitments.reverse();
32565
32251
  }
32252
+ // TODO: [🧠] Maybe `BehaviorSubject` is too heavy for this use case, maybe just tuple `[value,setValue]` is enough
32253
+
32566
32254
  /**
32567
- * Creates the initial requirements object with the parsed agent name stored in metadata and an optional model override.
32568
- *
32569
- * @param agentName - Parsed agent name from the source prelude.
32570
- * @param modelName - Optional explicit model name override.
32571
- * @returns Initial requirements before any commitment is applied.
32255
+ * Creates an empty/basic agent model requirements object
32256
+ * This serves as the starting point for the reduce-like pattern
32257
+ * where each commitment applies its changes to build the final requirements
32572
32258
  *
32573
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32259
+ * @public exported from `@promptbook/core`
32574
32260
  */
32575
- function createInitialAgentModelRequirements(agentName, modelName) {
32576
- const initialRequirements = createBasicAgentModelRequirements(agentName);
32577
- const requirementsWithMetadata = {
32578
- ...initialRequirements,
32579
- _metadata: {
32580
- ...initialRequirements._metadata,
32581
- agentName,
32582
- },
32583
- };
32584
- if (!modelName) {
32585
- return requirementsWithMetadata;
32586
- }
32261
+ function createEmptyAgentModelRequirements() {
32587
32262
  return {
32588
- ...requirementsWithMetadata,
32589
- modelName,
32263
+ systemMessage: '',
32264
+ promptSuffix: '',
32265
+ // modelName: 'gpt-5',
32266
+ modelName: 'gpt-5.4-mini',
32267
+ temperature: 0.7,
32268
+ topP: 0.9,
32269
+ topK: 50,
32270
+ parentAgentUrl: null,
32271
+ isClosed: false,
32590
32272
  };
32591
32273
  }
32592
32274
  /**
32593
- * Applies DELETE-like invalidation commitments and returns only commitments that should continue through the pipeline.
32594
- *
32595
- * @param commitments - Parsed commitments in original source order.
32596
- * @returns Filtered commitments with earlier deleted items removed.
32275
+ * Creates a basic agent model requirements with just the agent name
32276
+ * This is used when we have an agent name but no commitments
32597
32277
  *
32598
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32278
+ * @public exported from `@promptbook/core`
32599
32279
  */
32600
- function filterDeletedCommitments(commitments) {
32601
- const filteredCommitments = [];
32602
- for (const commitment of commitments) {
32603
- if (!isDeleteCommitmentType(commitment.type)) {
32604
- filteredCommitments.push(commitment);
32605
- continue;
32606
- }
32607
- const targetParameterNames = getCommitmentParameterNames(commitment.content);
32608
- if (targetParameterNames.length === 0) {
32609
- continue;
32610
- }
32611
- for (let index = filteredCommitments.length - 1; index >= 0; index--) {
32612
- const previousCommitment = filteredCommitments[index];
32613
- const previousParameterNames = getCommitmentParameterNames(previousCommitment.content);
32614
- const isTargeted = previousParameterNames.some((parameterName) => targetParameterNames.includes(parameterName));
32615
- if (isTargeted) {
32616
- filteredCommitments.splice(index, 1);
32617
- }
32618
- }
32619
- }
32620
- return filteredCommitments;
32280
+ function createBasicAgentModelRequirements(agentName) {
32281
+ const empty = createEmptyAgentModelRequirements();
32282
+ return {
32283
+ ...empty,
32284
+ systemMessage: `You are ${agentName || 'AI Agent'}`,
32285
+ };
32621
32286
  }
32287
+ // TODO: [🐤] Deduplicate `AgentModelRequirements` and `ModelRequirements` model requirements
32288
+
32622
32289
  /**
32623
- * Checks whether a commitment type behaves like DELETE and therefore invalidates earlier tagged commitments.
32290
+ * Gets a commitment definition by its type
32624
32291
  *
32625
- * @param commitmentType - Commitment type to check.
32626
- * @returns `true` when the commitment removes prior tagged commitments.
32292
+ * @param type The commitment type to look up
32293
+ * @returns The commitment definition or null if not found
32627
32294
  *
32628
- * @private internal utility of `filterDeletedCommitments`
32295
+ * @public exported from `@promptbook/core`
32629
32296
  */
32630
- function isDeleteCommitmentType(commitmentType) {
32631
- return DELETE_COMMITMENT_TYPES.has(commitmentType);
32297
+ function getCommitmentDefinition(type) {
32298
+ return COMMITMENT_REGISTRY.find((commitmentDefinition) => commitmentDefinition.type === type) || null;
32632
32299
  }
32300
+
32633
32301
  /**
32634
- * Extracts normalized parameter names used for DELETE-like invalidation matching.
32635
- *
32636
- * @param content - Commitment content to parse.
32637
- * @returns Lower-cased non-empty parameter names.
32302
+ * Commitment types whose content may contain compact agent references that must be resolved before applying the commitment.
32638
32303
  *
32639
- * @private internal utility of `filterDeletedCommitments`
32304
+ * @private internal constant of `applyCommitmentsToAgentModelRequirements`
32640
32305
  */
32641
- function getCommitmentParameterNames(content) {
32642
- return parseParameters(content)
32643
- .map((parameter) => parameter.name.trim().toLowerCase())
32644
- .filter(Boolean);
32645
- }
32306
+ const COMMITMENTS_WITH_AGENT_REFERENCES = new Set(['FROM', 'IMPORT', 'IMPORTS', 'TEAM']);
32646
32307
  /**
32647
32308
  * Applies parsed commitments one by one while keeping the per-commitment steps focused and easy to follow.
32648
32309
  *
@@ -32651,9 +32312,9 @@
32651
32312
  * @param options - Optional reference and teammate resolvers.
32652
32313
  * @returns Requirements after all applicable commitments are processed.
32653
32314
  *
32654
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32315
+ * @private function of `createAgentModelRequirementsWithCommitments`
32655
32316
  */
32656
- async function applyCommitmentsToRequirements(requirements, commitments, options) {
32317
+ async function applyCommitmentsToAgentModelRequirements(requirements, commitments, options) {
32657
32318
  for (const [index, commitment] of commitments.entries()) {
32658
32319
  if (shouldSkipCommitmentApplication(commitment, index, commitments.length)) {
32659
32320
  continue;
@@ -32671,7 +32332,7 @@
32671
32332
  * @param agentReferenceResolver - Optional resolver for compact agent references.
32672
32333
  * @returns Original or resolved commitment content.
32673
32334
  *
32674
- * @private internal utility of `applyCommitmentsToRequirements`
32335
+ * @private internal utility of `applyCommitmentsToAgentModelRequirements`
32675
32336
  */
32676
32337
  async function resolveCommitmentContent(commitment, agentReferenceResolver) {
32677
32338
  if (!agentReferenceResolver || !isAgentReferenceCommitment(commitment.type)) {
@@ -32685,6 +32346,24 @@
32685
32346
  return getSafeReferenceCommitmentFallback(commitment.type, commitment.content);
32686
32347
  }
32687
32348
  }
32349
+ /**
32350
+ * Returns a safe fallback content when a resolver fails to transform a reference commitment.
32351
+ *
32352
+ * @param commitmentType - Commitment being resolved.
32353
+ * @param originalContent - Original unresolved commitment content.
32354
+ * @returns Fallback content that keeps requirement creation resilient.
32355
+ *
32356
+ * @private internal utility of `applyCommitmentsToAgentModelRequirements`
32357
+ */
32358
+ function getSafeReferenceCommitmentFallback(commitmentType, originalContent) {
32359
+ if (commitmentType === 'FROM') {
32360
+ return 'VOID';
32361
+ }
32362
+ if (commitmentType === 'IMPORT' || commitmentType === 'IMPORTS' || commitmentType === 'TEAM') {
32363
+ return '';
32364
+ }
32365
+ return originalContent;
32366
+ }
32688
32367
  /**
32689
32368
  * Checks whether the commitment content may need agent-reference resolution before application.
32690
32369
  *
@@ -32704,7 +32383,7 @@
32704
32383
  * @param commitmentCount - Total number of filtered commitments.
32705
32384
  * @returns `true` when the commitment should not be applied.
32706
32385
  *
32707
- * @private internal utility of `applyCommitmentsToRequirements`
32386
+ * @private internal utility of `applyCommitmentsToAgentModelRequirements`
32708
32387
  */
32709
32388
  function shouldSkipCommitmentApplication(commitment, commitmentIndex, commitmentCount) {
32710
32389
  return commitment.type === 'CLOSED' && commitmentIndex !== commitmentCount - 1;
@@ -32718,7 +32397,7 @@
32718
32397
  * @param options - Optional teammate profile resolvers.
32719
32398
  * @returns Requirements with pre-resolved teammate profiles stored in metadata when possible.
32720
32399
  *
32721
- * @private internal utility of `applyCommitmentsToRequirements`
32400
+ * @private internal utility of `applyCommitmentsToAgentModelRequirements`
32722
32401
  */
32723
32402
  async function preResolveTeammateProfilesForTeamCommitment(requirements, commitment, commitmentContent, options) {
32724
32403
  var _a;
@@ -32773,7 +32452,7 @@
32773
32452
  * @param commitmentContent - Final content passed into the definition.
32774
32453
  * @returns Updated requirements, or the original requirements when the commitment fails.
32775
32454
  *
32776
- * @private internal utility of `applyCommitmentsToRequirements`
32455
+ * @private internal utility of `applyCommitmentsToAgentModelRequirements`
32777
32456
  */
32778
32457
  function applyCommitmentDefinitionSafely(requirements, commitment, commitmentContent) {
32779
32458
  const definition = getCommitmentDefinition(commitment.type);
@@ -32788,13 +32467,140 @@
32788
32467
  return requirements;
32789
32468
  }
32790
32469
  }
32470
+
32471
+ /**
32472
+ * Plugin for importing agent books *(`.book` files)*
32473
+ *
32474
+ * @private [🥝] Maybe export the import plugins through some package
32475
+ */
32476
+ const AgentFileImportPlugin = {
32477
+ name: 'agent-file-import-plugin',
32478
+ canImport(mimeType) {
32479
+ // [🧠] Should we have a specific MIME type for agent books?
32480
+ // For now, let's assume it's identified by .book extension or certain MIME types if provided
32481
+ return mimeType === 'text/x-promptbook' || mimeType === 'application/x-promptbook';
32482
+ },
32483
+ import(content) {
32484
+ const parseResult = parseAgentSourceWithCommitments(content);
32485
+ // Bring only the agent corpus (non-commitment lines and relevant commitments)
32486
+ // Stripping the agent name (which is usually the first line)
32487
+ const corpus = parseResult.nonCommitmentLines
32488
+ .filter((line, index) => index > 0 || !parseResult.agentName)
32489
+ .join('\n')
32490
+ .trim();
32491
+ // Also include relevant commitments that make up the "corpus" of the agent
32492
+ // For example PERSONA, RULE, KNOWLEDGE
32493
+ const relevantCommitments = parseResult.commitments
32494
+ .filter((c) => ['PERSONA', 'RULE', 'KNOWLEDGE'].includes(c.type))
32495
+ .map((c) => `${c.type} ${c.content}`)
32496
+ .join('\n\n');
32497
+ return _spaceTrim.spaceTrim((block) => `
32498
+ ${block(relevantCommitments)}
32499
+
32500
+ ${block(corpus)}
32501
+ `).trim();
32502
+ },
32503
+ };
32504
+
32505
+ /**
32506
+ * Plugin for importing JSON files
32507
+ *
32508
+ * @private [🥝] Maybe export the import plugins through some package
32509
+ */
32510
+ const JsonFileImportPlugin = {
32511
+ name: 'json-file-import-plugin',
32512
+ canImport(mimeType) {
32513
+ return mimeType === 'application/json' || mimeType.endsWith('+json');
32514
+ },
32515
+ import(content) {
32516
+ try {
32517
+ const json = JSON.parse(content);
32518
+ const formattedJson = JSON.stringify(json, null, 4);
32519
+ return `\`\`\`json\n${formattedJson}\n\`\`\``;
32520
+ }
32521
+ catch (error) {
32522
+ // If JSON is invalid, still import it but maybe not as pretty JSON
32523
+ return `\`\`\`json\n${content}\n\`\`\``;
32524
+ }
32525
+ },
32526
+ };
32527
+
32528
+ /**
32529
+ * Plugin for importing generic text files
32530
+ *
32531
+ * @private [🥝] Maybe export the import plugins through some package
32532
+ */
32533
+ const TextFileImportPlugin = {
32534
+ name: 'text-file-import-plugin',
32535
+ canImport(mimeType) {
32536
+ return (mimeType === 'text/plain' ||
32537
+ mimeType === 'text/markdown' ||
32538
+ mimeType === 'text/x-typescript' ||
32539
+ mimeType === 'text/javascript' ||
32540
+ mimeType === 'text/css' ||
32541
+ mimeType === 'text/html' ||
32542
+ mimeType.startsWith('text/'));
32543
+ },
32544
+ import(content, mimeType) {
32545
+ const extension = mimeTypeToExtension(mimeType);
32546
+ const codeBlockType = extension || 'txt';
32547
+ return `\`\`\`${codeBlockType}\n${content}\n\`\`\``;
32548
+ },
32549
+ };
32550
+
32551
+ /**
32552
+ * All available file import plugins
32553
+ *
32554
+ * @private [🥝] Maybe export the import plugins through some package
32555
+ */
32556
+ const $fileImportPlugins = [
32557
+ AgentFileImportPlugin,
32558
+ JsonFileImportPlugin,
32559
+ TextFileImportPlugin,
32560
+ ];
32561
+
32562
+ /**
32563
+ * Regex pattern matching markdown horizontal lines that should not be copied into the final system message.
32564
+ *
32565
+ * @private internal constant of `augmentAgentModelRequirementsFromSource`
32566
+ */
32567
+ const HORIZONTAL_LINE_PATTERN = /^[\s]*[-_*][\s]*[-_*][\s]*[-_*][\s]*[-_*]*[\s]*$/;
32568
+ /**
32569
+ * MIME type prefixes treated as binary and therefore not eligible for text import plugins.
32570
+ *
32571
+ * @private internal constant of `augmentAgentModelRequirementsFromSource`
32572
+ */
32573
+ const BINARY_MIME_TYPE_PREFIXES = [
32574
+ 'image/',
32575
+ 'video/',
32576
+ 'audio/',
32577
+ 'application/octet-stream',
32578
+ 'application/pdf',
32579
+ 'application/zip',
32580
+ ];
32581
+ /**
32582
+ * Adds source-derived sections after commitments have been applied.
32583
+ *
32584
+ * @param requirements - Requirements after commitment application and USE aggregation.
32585
+ * @param parseResult - Parsed source used to recover non-commitment prose and examples.
32586
+ * @param agentSource - Original source used to recover MCP server declarations.
32587
+ * @returns Requirements with source-derived sections appended.
32588
+ *
32589
+ * @private function of `createAgentModelRequirementsWithCommitments`
32590
+ */
32591
+ async function augmentAgentModelRequirementsFromSource(requirements, parseResult, agentSource) {
32592
+ requirements = await importReferencedFiles(requirements);
32593
+ requirements = appendMcpServers(requirements, agentSource);
32594
+ requirements = appendNonCommitmentContent(requirements, parseResult);
32595
+ return appendExampleInteractions(requirements, parseResult);
32596
+ }
32791
32597
  /**
32792
32598
  * Imports text files referenced by IMPORT commitments and appends their transformed content to the system message.
32793
32599
  *
32794
32600
  * @param requirements - Requirements possibly containing `importedFileUrls`.
32795
32601
  * @returns Requirements with imported file content appended to the system message.
32796
32602
  *
32797
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32603
+ * @private internal utility of `augmentAgentModelRequirementsFromSource`
32798
32604
  */
32799
32605
  async function importReferencedFiles(requirements) {
32800
32606
  const importedFileUrls = requirements.importedFileUrls;
@@ -32880,7 +32686,7 @@
32880
32686
  * @param agentSource - Original agent source used for MCP extraction.
32881
32687
  * @returns Requirements with `mcpServers` set when MCP commitments are present.
32882
32688
  *
32883
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32689
+ * @private internal utility of `augmentAgentModelRequirementsFromSource`
32884
32690
  */
32885
32691
  function appendMcpServers(requirements, agentSource) {
32886
32692
  const mcpServers = extractMcpServers(agentSource);
@@ -32899,7 +32705,7 @@
32899
32705
  * @param parseResult - Parsed source including non-commitment lines.
32900
32706
  * @returns Requirements with the remaining prose appended to the system message.
32901
32707
  *
32902
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32708
+ * @private internal utility of `augmentAgentModelRequirementsFromSource`
32903
32709
  */
32904
32710
  function appendNonCommitmentContent(requirements, parseResult) {
32905
32711
  const nonCommitmentContent = getNonCommitmentContent(parseResult);
@@ -32942,7 +32748,7 @@
32942
32748
  * @param parseResult - Parsed source used to recover initial message content.
32943
32749
  * @returns Requirements with the example interaction block appended when examples exist.
32944
32750
  *
32945
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32751
+ * @private internal utility of `augmentAgentModelRequirementsFromSource`
32946
32752
  */
32947
32753
  function appendExampleInteractions(requirements, parseResult) {
32948
32754
  const exampleInteractionsContent = createExampleInteractionsContent(parseResult, requirements.samples);
@@ -32997,7 +32803,7 @@
32997
32803
  * @param section - Section content to append.
32998
32804
  * @returns Requirements with the additional system-message block appended.
32999
32805
  *
33000
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32806
+ * @private internal utility of `augmentAgentModelRequirementsFromSource`
33001
32807
  */
33002
32808
  function appendSystemMessageSection(requirements, section) {
33003
32809
  return {
@@ -33006,29 +32812,149 @@
33006
32812
  };
33007
32813
  }
33008
32814
  /**
33009
- * Performs the final system-message cleanup pass after all other augmentation steps are complete.
32815
+ * Mocked security check for imported files.
33010
32816
  *
33011
- * @param requirements - Fully built requirements before final cleanup.
33012
- * @returns Requirements with comment lines removed from the final system message.
32817
+ * @param urlOrPath - The URL or local path of the file to check.
32818
+ * @returns A promise that resolves if the file is considered safe.
33013
32819
  *
33014
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32820
+ * @private internal utility of `createImportedFileSystemMessage`
33015
32821
  */
33016
- function finalizeRequirements(requirements) {
33017
- return {
33018
- ...requirements,
33019
- systemMessage: removeCommentsFromSystemMessage(requirements.systemMessage),
33020
- };
32822
+ async function mockedSecurityCheck(urlOrPath) {
32823
+ // TODO: Implement proper security checks
32824
+ await new Promise((resolve) => setTimeout(resolve, 10));
32825
+ if (urlOrPath.includes('malicious')) {
32826
+ throw new Error(`Security check failed for: ${urlOrPath}`);
32827
+ }
32828
+ }
32829
+ /**
32830
+ * Checks whether the given MIME type belongs to a binary file.
32831
+ *
32832
+ * @param mimeType - The MIME type to check.
32833
+ * @returns `true` when the MIME type is treated as binary.
32834
+ *
32835
+ * @private internal utility of `createImportedFileSystemMessage`
32836
+ */
32837
+ function isBinaryMimeType(mimeType) {
32838
+ return BINARY_MIME_TYPE_PREFIXES.some((prefix) => mimeType.startsWith(prefix));
32839
+ }
32840
+
32841
+ /**
32842
+ * DELETE-like commitment types that invalidate earlier tagged commitments.
32843
+ *
32844
+ * @private internal constant of `filterCommitmentsForAgentModelRequirements`
32845
+ */
32846
+ const DELETE_COMMITMENT_TYPES = new Set(['DELETE', 'CANCEL', 'DISCARD', 'REMOVE']);
32847
+ /**
32848
+ * Commitments whose earlier occurrences are overwritten by the last occurrence in source order.
32849
+ *
32850
+ * @private internal constant of `filterCommitmentsForAgentModelRequirements`
32851
+ */
32852
+ const OVERWRITTEN_COMMITMENT_GROUP_BY_TYPE = new Map([
32853
+ ['GOAL', 'GOAL'],
32854
+ ['GOALS', 'GOAL'],
32855
+ ]);
32856
+ /**
32857
+ * Applies the commitment filtering rules used before commitment definitions are executed.
32858
+ *
32859
+ * @param commitments - Parsed commitments in original source order.
32860
+ * @returns Commitments after DELETE-like invalidation and overwritten-goal filtering.
32861
+ *
32862
+ * @private function of `createAgentModelRequirementsWithCommitments`
32863
+ */
32864
+ function filterCommitmentsForAgentModelRequirements(commitments) {
32865
+ return filterOverwrittenCommitments(filterDeletedCommitments(commitments));
32866
+ }
32867
+ /**
32868
+ * Removes earlier commitments that are overwritten by later commitments of the same semantic group.
32869
+ *
32870
+ * @param commitments - Parsed commitments after DELETE-like filtering.
32871
+ * @returns Commitments with overwritten entries removed while preserving source order.
32872
+ *
32873
+ * @private internal utility of `filterCommitmentsForAgentModelRequirements`
32874
+ */
32875
+ function filterOverwrittenCommitments(commitments) {
32876
+ const seenOverwriteGroups = new Set();
32877
+ const keptCommitments = [];
32878
+ for (let index = commitments.length - 1; index >= 0; index--) {
32879
+ const commitment = commitments[index];
32880
+ const overwriteGroup = OVERWRITTEN_COMMITMENT_GROUP_BY_TYPE.get(commitment.type);
32881
+ if (!overwriteGroup) {
32882
+ keptCommitments.push(commitment);
32883
+ continue;
32884
+ }
32885
+ if (seenOverwriteGroups.has(overwriteGroup)) {
32886
+ continue;
32887
+ }
32888
+ seenOverwriteGroups.add(overwriteGroup);
32889
+ keptCommitments.push(commitment);
32890
+ }
32891
+ return keptCommitments.reverse();
32892
+ }
32893
+ /**
32894
+ * Applies DELETE-like invalidation commitments and returns only commitments that should continue through the pipeline.
32895
+ *
32896
+ * @param commitments - Parsed commitments in original source order.
32897
+ * @returns Filtered commitments with earlier deleted items removed.
32898
+ *
32899
+ * @private internal utility of `filterCommitmentsForAgentModelRequirements`
32900
+ */
32901
+ function filterDeletedCommitments(commitments) {
32902
+ const filteredCommitments = [];
32903
+ for (const commitment of commitments) {
32904
+ if (!isDeleteCommitmentType(commitment.type)) {
32905
+ filteredCommitments.push(commitment);
32906
+ continue;
32907
+ }
32908
+ const targetParameterNames = getCommitmentParameterNames(commitment.content);
32909
+ if (targetParameterNames.length === 0) {
32910
+ continue;
32911
+ }
32912
+ for (let index = filteredCommitments.length - 1; index >= 0; index--) {
32913
+ const previousCommitment = filteredCommitments[index];
32914
+ const previousParameterNames = getCommitmentParameterNames(previousCommitment.content);
32915
+ const isTargeted = previousParameterNames.some((parameterName) => targetParameterNames.includes(parameterName));
32916
+ if (isTargeted) {
32917
+ filteredCommitments.splice(index, 1);
32918
+ }
32919
+ }
32920
+ }
32921
+ return filteredCommitments;
32922
+ }
32923
+ /**
32924
+ * Checks whether a commitment type behaves like DELETE and therefore invalidates earlier tagged commitments.
32925
+ *
32926
+ * @param commitmentType - Commitment type to check.
32927
+ * @returns `true` when the commitment removes prior tagged commitments.
32928
+ *
32929
+ * @private internal utility of `filterDeletedCommitments`
32930
+ */
32931
+ function isDeleteCommitmentType(commitmentType) {
32932
+ return DELETE_COMMITMENT_TYPES.has(commitmentType);
32933
+ }
32934
+ /**
32935
+ * Extracts normalized parameter names used for DELETE-like invalidation matching.
32936
+ *
32937
+ * @param content - Commitment content to parse.
32938
+ * @returns Lower-cased non-empty parameter names.
32939
+ *
32940
+ * @private internal utility of `filterDeletedCommitments`
32941
+ */
32942
+ function getCommitmentParameterNames(content) {
32943
+ return parseParameters(content)
32944
+ .map((parameter) => parameter.name.trim().toLowerCase())
32945
+ .filter(Boolean);
33021
32946
  }
32947
+
33022
32948
  /**
33023
- * Attempts to upload inline knowledge entries, falling back to legacy data URLs when the upload fails or is not configured.
32949
+ * Converts staged inline knowledge files into the final knowledge source URLs stored on requirements.
33024
32950
  *
33025
32951
  * @param requirements - Current requirements snapshot.
33026
32952
  * @param uploader - Optional uploader for inline knowledge files.
33027
32953
  * @returns Requirements with inline knowledge converted into upload URLs or data URLs.
33028
32954
  *
33029
- * @private internal utility of `createAgentModelRequirementsWithCommitments`
32955
+ * @private function of `createAgentModelRequirementsWithCommitments`
33030
32956
  */
33031
- async function applyPendingInlineKnowledgeSources(requirements, uploader) {
32957
+ async function materializeInlineKnowledgeSources(requirements, uploader) {
33032
32958
  var _a;
33033
32959
  const inlineSources = extractInlineKnowledgeSources(requirements._metadata);
33034
32960
  if (inlineSources.length === 0) {
@@ -33054,7 +32980,7 @@
33054
32980
  * @param uploader - Upload implementation provided by the caller.
33055
32981
  * @returns Uploaded knowledge URL or a legacy data URL fallback.
33056
32982
  *
33057
- * @private internal utility of `applyPendingInlineKnowledgeSources`
32983
+ * @private internal utility of `materializeInlineKnowledgeSources`
33058
32984
  */
33059
32985
  async function uploadInlineKnowledgeSourceWithFallback(inlineSource, uploader) {
33060
32986
  try {
@@ -33074,7 +33000,7 @@
33074
33000
  * @param metadata - Current requirements metadata.
33075
33001
  * @returns Inline knowledge files collected during commitment application.
33076
33002
  *
33077
- * @private internal utility of `applyPendingInlineKnowledgeSources`
33003
+ * @private internal utility of `materializeInlineKnowledgeSources`
33078
33004
  */
33079
33005
  function extractInlineKnowledgeSources(metadata) {
33080
33006
  if (!metadata) {
@@ -33089,7 +33015,7 @@
33089
33015
  * @param metadata - Current requirements metadata.
33090
33016
  * @returns Metadata without the temporary inline knowledge staging field.
33091
33017
  *
33092
- * @private internal utility of `applyPendingInlineKnowledgeSources`
33018
+ * @private internal utility of `materializeInlineKnowledgeSources`
33093
33019
  */
33094
33020
  function stripInlineKnowledgeMetadata(metadata) {
33095
33021
  if (!metadata || !Object.prototype.hasOwnProperty.call(metadata, 'inlineKnowledgeSources')) {
@@ -33098,31 +33024,90 @@
33098
33024
  const { inlineKnowledgeSources: _unusedInlineKnowledgeSources, ...rest } = metadata;
33099
33025
  return Object.keys(rest).length > 0 ? rest : undefined;
33100
33026
  }
33027
+
33101
33028
  /**
33102
- * Mocked security check for imported files.
33029
+ * Removes single-hash comment lines (`# Comment`) from a system message
33030
+ * This is used to clean up the final system message before sending it to the AI model
33031
+ * while preserving the original content with comments in metadata
33103
33032
  *
33104
- * @param urlOrPath - The URL or local path of the file to check.
33105
- * @returns A promise that resolves if the file is considered safe.
33033
+ * @param systemMessage The system message that may contain comment lines
33034
+ * @returns The system message with single-hash comment lines removed
33106
33035
  *
33107
- * @private internal utility of `createImportedFileSystemMessage`
33036
+ * @private - TODO: [🧠] Maybe should be public?
33108
33037
  */
33109
- async function mockedSecurityCheck(urlOrPath) {
33110
- // TODO: Implement proper security checks
33111
- await new Promise((resolve) => setTimeout(resolve, 10));
33112
- if (urlOrPath.includes('malicious')) {
33113
- throw new Error(`Security check failed for: ${urlOrPath}`);
33038
+ function removeCommentsFromSystemMessage(systemMessage) {
33039
+ if (!systemMessage) {
33040
+ return systemMessage;
33041
+ }
33042
+ const lines = systemMessage.split(/\r?\n/);
33043
+ const filteredLines = lines.filter((line) => {
33044
+ const trimmedLine = line.trim();
33045
+ // Remove only single-hash comment markers (`# Comment`) and keep markdown headings (`## Heading`).
33046
+ return !/^#(?!#)\s/.test(trimmedLine);
33047
+ });
33048
+ return filteredLines.join('\n').trim();
33049
+ }
33050
+
33051
+ /**
33052
+ * Creates agent model requirements by parsing commitments, applying them in source order,
33053
+ * and finalizing derived sections such as imports, example interactions, and inline knowledge uploads.
33054
+ *
33055
+ * @param agentSource - Agent source book to parse.
33056
+ * @param modelName - Optional override for the agent model name.
33057
+ * @param options - Additional options such as reference and teammate resolvers.
33058
+ * @returns Fully prepared model requirements for the parsed agent source.
33059
+ *
33060
+ * @private internal utility of `createAgentModelRequirements`
33061
+ */
33062
+ async function createAgentModelRequirementsWithCommitments(agentSource, modelName, options) {
33063
+ const parseResult = parseAgentSourceWithCommitments(agentSource);
33064
+ const filteredCommitments = filterCommitmentsForAgentModelRequirements(parseResult.commitments);
33065
+ let requirements = createInitialAgentModelRequirements(parseResult.agentName, modelName);
33066
+ requirements = await applyCommitmentsToAgentModelRequirements(requirements, filteredCommitments, options);
33067
+ requirements = aggregateUseCommitmentSystemMessages(requirements, filteredCommitments);
33068
+ requirements = await augmentAgentModelRequirementsFromSource(requirements, parseResult, agentSource);
33069
+ requirements = await materializeInlineKnowledgeSources(requirements, options === null || options === void 0 ? void 0 : options.inlineKnowledgeSourceUploader);
33070
+ return finalizeRequirements(requirements);
33071
+ }
33072
+ /**
33073
+ * Creates the initial requirements object with the parsed agent name stored in metadata and an optional model override.
33074
+ *
33075
+ * @param agentName - Parsed agent name from the source prelude.
33076
+ * @param modelName - Optional explicit model name override.
33077
+ * @returns Initial requirements before any commitment is applied.
33078
+ *
33079
+ * @private internal utility of `createAgentModelRequirementsWithCommitments`
33080
+ */
33081
+ function createInitialAgentModelRequirements(agentName, modelName) {
33082
+ const initialRequirements = createBasicAgentModelRequirements(agentName);
33083
+ const requirementsWithMetadata = {
33084
+ ...initialRequirements,
33085
+ _metadata: {
33086
+ ...initialRequirements._metadata,
33087
+ agentName,
33088
+ },
33089
+ };
33090
+ if (!modelName) {
33091
+ return requirementsWithMetadata;
33114
33092
  }
33093
+ return {
33094
+ ...requirementsWithMetadata,
33095
+ modelName,
33096
+ };
33115
33097
  }
33116
33098
  /**
33117
- * Checks whether the given MIME type belongs to a binary file.
33099
+ * Performs the final system-message cleanup pass after all other augmentation steps are complete.
33118
33100
  *
33119
- * @param mimeType - The MIME type to check.
33120
- * @returns `true` when the MIME type is treated as binary.
33101
+ * @param requirements - Fully built requirements before final cleanup.
33102
+ * @returns Requirements with comment lines removed from the final system message.
33121
33103
  *
33122
- * @private internal utility of `createImportedFileSystemMessage`
33104
+ * @private internal utility of `createAgentModelRequirementsWithCommitments`
33123
33105
  */
33124
- function isBinaryMimeType(mimeType) {
33125
- return BINARY_MIME_TYPE_PREFIXES.some((prefix) => mimeType.startsWith(prefix));
33106
+ function finalizeRequirements(requirements) {
33107
+ return {
33108
+ ...requirements,
33109
+ systemMessage: removeCommentsFromSystemMessage(requirements.systemMessage),
33110
+ };
33126
33111
  }
33127
33112
 
33128
33113
  /**