@promptbook/cli 0.114.0-35 → 0.114.0-39

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 (86) hide show
  1. package/agents/default/.core/adam.book +33 -0
  2. package/apps/agents-server/package.json +1 -1
  3. package/apps/agents-server/scripts/build-agents-server.js +4 -0
  4. package/apps/agents-server/scripts/build-e2e.js +1 -1
  5. package/apps/agents-server/src/app/swagger/SwaggerPageClient.tsx +35 -19
  6. package/apps/agents-server/src/types/swagger-ui-dist.d.ts +5 -0
  7. package/apps/agents-server/src/utils/agentReferenceResolver/AgentReferenceResolutionIssue.ts +1 -69
  8. package/apps/agents-server/src/utils/agentReferenceResolver/extractAgentReferenceTokens.ts +1 -61
  9. package/apps/agents-server/src/utils/explicitFromCommitment.ts +1 -130
  10. package/apps/agents-server/src/utils/resolveInheritedAgentSource.ts +32 -817
  11. package/esm/index.es.js +1431 -128
  12. package/esm/index.es.js.map +1 -1
  13. package/esm/scripts/run-agent-messages/messages/createAgentRunnerSystemMessage.d.ts +2 -1
  14. package/esm/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +11 -0
  15. package/esm/scripts/run-codex-prompts/git/commitInitializedAgentBooks.d.ts +5 -0
  16. package/esm/scripts/run-codex-prompts/main/resolvePromptRunner.d.ts +5 -8
  17. package/esm/scripts/run-codex-prompts/main/runPromptRound.d.ts +2 -4
  18. package/esm/scripts/run-codex-prompts/prompts/buildPromptRunTraceContent.d.ts +5 -17
  19. package/esm/scripts/run-codex-prompts/prompts/buildPromptStatusDetails.d.ts +3 -15
  20. package/esm/scripts/run-codex-prompts/prompts/formatRunnerSignature.d.ts +14 -1
  21. package/esm/scripts/run-codex-prompts/prompts/markPromptFailed.d.ts +2 -9
  22. package/esm/src/book-2.0/agent-source/AgentReferenceResolutionIssue.d.ts +45 -0
  23. package/esm/src/book-2.0/agent-source/createAgentModelRequirements.deduplication.test.d.ts +1 -0
  24. package/esm/src/book-2.0/agent-source/deduplicateSystemMessage.d.ts +14 -0
  25. package/esm/src/book-2.0/agent-source/deduplicateSystemMessage.test.d.ts +1 -0
  26. package/esm/src/book-2.0/agent-source/explicitFromCommitment.d.ts +62 -0
  27. package/esm/src/book-2.0/agent-source/extractAgentReferenceTokens.d.ts +32 -0
  28. package/esm/src/book-2.0/agent-source/resolveInheritedAgentSource.d.ts +73 -0
  29. package/esm/src/cli/$initializePromptbookCliProgram.d.ts +8 -0
  30. package/esm/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +1 -0
  31. package/esm/src/cli/cli-commands/common/LocalAgentBookCollection.d.ts +46 -0
  32. package/esm/src/cli/cli-commands/common/createLocalAgentReferenceResolver.d.ts +8 -0
  33. package/esm/src/cli/cli-commands/common/ensureAdamAgentBook.d.ts +12 -0
  34. package/esm/src/cli/cli-commands/common/resolveBundledAgentBookPath.d.ts +6 -0
  35. package/esm/src/cli/cli-commands/common/resolveLocalAgentSource.d.ts +19 -0
  36. package/esm/src/version.d.ts +1 -1
  37. package/package.json +2 -3
  38. package/src/book-2.0/agent-source/AgentReferenceResolutionIssue.ts +75 -0
  39. package/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.ts +3 -2
  40. package/src/book-2.0/agent-source/deduplicateSystemMessage.ts +327 -0
  41. package/src/book-2.0/agent-source/explicitFromCommitment.ts +132 -0
  42. package/src/book-2.0/agent-source/extractAgentReferenceTokens.ts +65 -0
  43. package/src/book-2.0/agent-source/resolveInheritedAgentSource.ts +841 -0
  44. package/src/cli/$initializePromptbookCliProgram.ts +87 -0
  45. package/src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.ts +2 -28
  46. package/src/cli/cli-commands/coder/init.ts +3 -1
  47. package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +5 -0
  48. package/src/cli/cli-commands/coder/list.ts +5 -3
  49. package/src/cli/cli-commands/coder/printInitializationSummary.ts +6 -0
  50. package/src/cli/cli-commands/coder/run.ts +4 -1
  51. package/src/cli/cli-commands/coder/server.ts +2 -1
  52. package/src/cli/cli-commands/common/LocalAgentBookCollection.ts +220 -0
  53. package/src/cli/cli-commands/common/createLocalAgentReferenceResolver.ts +62 -0
  54. package/src/cli/cli-commands/common/ensureAdamAgentBook.ts +40 -0
  55. package/src/cli/cli-commands/common/resolveBundledAgentBookPath.ts +38 -0
  56. package/src/cli/cli-commands/common/resolveLocalAgentSource.ts +127 -0
  57. package/src/cli/promptbookCli.ts +2 -87
  58. package/src/other/templates/getTemplatesPipelineCollection.ts +612 -944
  59. package/src/version.ts +2 -2
  60. package/src/versions.txt +4 -0
  61. package/umd/index.umd.js +1431 -128
  62. package/umd/index.umd.js.map +1 -1
  63. package/umd/scripts/run-agent-messages/messages/createAgentRunnerSystemMessage.d.ts +2 -1
  64. package/umd/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +11 -0
  65. package/umd/scripts/run-codex-prompts/git/commitInitializedAgentBooks.d.ts +5 -0
  66. package/umd/scripts/run-codex-prompts/main/resolvePromptRunner.d.ts +5 -8
  67. package/umd/scripts/run-codex-prompts/main/runPromptRound.d.ts +2 -4
  68. package/umd/scripts/run-codex-prompts/prompts/buildPromptRunTraceContent.d.ts +5 -17
  69. package/umd/scripts/run-codex-prompts/prompts/buildPromptStatusDetails.d.ts +3 -15
  70. package/umd/scripts/run-codex-prompts/prompts/formatRunnerSignature.d.ts +14 -1
  71. package/umd/scripts/run-codex-prompts/prompts/markPromptFailed.d.ts +2 -9
  72. package/umd/src/book-2.0/agent-source/AgentReferenceResolutionIssue.d.ts +45 -0
  73. package/umd/src/book-2.0/agent-source/createAgentModelRequirements.deduplication.test.d.ts +1 -0
  74. package/umd/src/book-2.0/agent-source/deduplicateSystemMessage.d.ts +14 -0
  75. package/umd/src/book-2.0/agent-source/deduplicateSystemMessage.test.d.ts +1 -0
  76. package/umd/src/book-2.0/agent-source/explicitFromCommitment.d.ts +62 -0
  77. package/umd/src/book-2.0/agent-source/extractAgentReferenceTokens.d.ts +32 -0
  78. package/umd/src/book-2.0/agent-source/resolveInheritedAgentSource.d.ts +73 -0
  79. package/umd/src/cli/$initializePromptbookCliProgram.d.ts +8 -0
  80. package/umd/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +1 -0
  81. package/umd/src/cli/cli-commands/common/LocalAgentBookCollection.d.ts +46 -0
  82. package/umd/src/cli/cli-commands/common/createLocalAgentReferenceResolver.d.ts +8 -0
  83. package/umd/src/cli/cli-commands/common/ensureAdamAgentBook.d.ts +12 -0
  84. package/umd/src/cli/cli-commands/common/resolveBundledAgentBookPath.d.ts +6 -0
  85. package/umd/src/cli/cli-commands/common/resolveLocalAgentSource.d.ts +19 -0
  86. package/umd/src/version.d.ts +1 -1
package/esm/index.es.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import colors from 'colors';
2
2
  import commander, { Option } from 'commander';
3
3
  import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
- import { writeFile, stat, readdir, mkdir, readFile, rm, cp, lstat, symlink, rename, unlink, appendFile, realpath, copyFile, statfs, access, constants, watch, rmdir } from 'fs/promises';
4
+ import { writeFile, stat, readdir, mkdir, readFile, rm, cp, lstat, symlink, rename, unlink, appendFile, realpath, copyFile, statfs, access, constants as constants$1, watch, rmdir } from 'fs/promises';
5
5
  import { join, delimiter, relative, basename, resolve, dirname, isAbsolute, extname } from 'path';
6
6
  import { createHash, randomBytes } from 'crypto';
7
7
  import { spawn, spawnSync, execFile } from 'child_process';
8
8
  import moment from 'moment';
9
9
  import * as fs from 'fs';
10
- import { createWriteStream, mkdirSync, writeFileSync, statSync, readFileSync, existsSync, promises } from 'fs';
10
+ import { createWriteStream, mkdirSync, writeFileSync, constants, statSync, readFileSync, existsSync, promises } from 'fs';
11
11
  import * as dotenv from 'dotenv';
12
12
  import * as readline from 'readline';
13
13
  import { createInterface, emitKeypressEvents, cursorTo, clearLine } from 'readline';
@@ -30,9 +30,9 @@ import ignore from 'ignore';
30
30
  import { EventEmitter } from 'events';
31
31
  import { tmpdir } from 'os';
32
32
  import { lookup, extension } from 'mime-types';
33
+ import { pathToFileURL } from 'url';
33
34
  import papaparse from 'papaparse';
34
35
  import '@supabase/supabase-js';
35
- import { pathToFileURL } from 'url';
36
36
 
37
37
  // ⚠️ WARNING: This code has been generated so that any manual changes will be overwritten
38
38
  /**
@@ -48,12 +48,42 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
48
48
  * @generated
49
49
  * @see https://github.com/webgptorg/promptbook
50
50
  */
51
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-35';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-39';
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
55
55
  */
56
56
 
57
+ /**
58
+ * This error type indicates that you try to use a feature that is not available in the current environment
59
+ *
60
+ * @public exported from `@promptbook/core`
61
+ */
62
+ class EnvironmentMismatchError extends Error {
63
+ constructor(message) {
64
+ super(message);
65
+ this.name = 'EnvironmentMismatchError';
66
+ Object.setPrototypeOf(this, EnvironmentMismatchError.prototype);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Detects if the code is running in a Node.js environment
72
+ *
73
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
74
+ *
75
+ * @public exported from `@promptbook/utils`
76
+ */
77
+ function $isRunningInNode() {
78
+ try {
79
+ return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
80
+ }
81
+ catch (e) {
82
+ return false;
83
+ }
84
+ }
85
+ // TODO: [🎺]
86
+
57
87
  /**
58
88
  * Trims string from all 4 sides
59
89
  *
@@ -1289,6 +1319,12 @@ const DEFAULT_INTERMEDIATE_FILES_STRATEGY = 'HIDE_AND_KEEP';
1289
1319
  * @public exported from `@promptbook/core`
1290
1320
  */
1291
1321
  const DEFAULT_MAX_PARALLEL_COUNT = 5; // <- TODO: [🤹‍♂️]
1322
+ /**
1323
+ * The maximum depth to which recursion can occur
1324
+ *
1325
+ * @public exported from `@promptbook/core`
1326
+ */
1327
+ const DEFAULT_MAX_RECURSION = 10;
1292
1328
  /**
1293
1329
  * The maximum number of attempts to execute LLM task before giving up
1294
1330
  *
@@ -1438,36 +1474,6 @@ true);
1438
1474
  * TODO: [🧠][🧜‍♂️] Maybe join remoteServerUrl and path into single value
1439
1475
  */
1440
1476
 
1441
- /**
1442
- * This error type indicates that you try to use a feature that is not available in the current environment
1443
- *
1444
- * @public exported from `@promptbook/core`
1445
- */
1446
- class EnvironmentMismatchError extends Error {
1447
- constructor(message) {
1448
- super(message);
1449
- this.name = 'EnvironmentMismatchError';
1450
- Object.setPrototypeOf(this, EnvironmentMismatchError.prototype);
1451
- }
1452
- }
1453
-
1454
- /**
1455
- * Detects if the code is running in a Node.js environment
1456
- *
1457
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
1458
- *
1459
- * @public exported from `@promptbook/utils`
1460
- */
1461
- function $isRunningInNode() {
1462
- try {
1463
- return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
1464
- }
1465
- catch (e) {
1466
- return false;
1467
- }
1468
- }
1469
- // TODO: [🎺]
1470
-
1471
1477
  /**
1472
1478
  * Detects if the code is running in a browser environment in main thread (Not in a web worker)
1473
1479
  *
@@ -5655,6 +5661,13 @@ const PSEUDO_AGENT_REFERENCE_KIND_MAP = {
5655
5661
  user: 'USER',
5656
5662
  ...Object.fromEntries(VOID_ALIAS_ENTRIES),
5657
5663
  };
5664
+ /**
5665
+ * Maps pseudo-agent kinds to canonical pseudo-agent URLs.
5666
+ */
5667
+ const PSEUDO_AGENT_URL_BY_KIND = {
5668
+ USER: PSEUDO_AGENT_USER_URL,
5669
+ VOID: PSEUDO_AGENT_VOID_URL,
5670
+ };
5658
5671
  /**
5659
5672
  * Unwraps a compact reference token (`{Name}` or `@Name`) to plain reference payload.
5660
5673
  *
@@ -5720,6 +5733,28 @@ function resolvePseudoAgentKindFromUrl(agentUrl) {
5720
5733
  }
5721
5734
  return null;
5722
5735
  }
5736
+ /**
5737
+ * Checks whether a URL points to a pseudo-agent.
5738
+ *
5739
+ * @param agentUrl - URL to inspect.
5740
+ * @returns True when the URL identifies a pseudo-agent.
5741
+ *
5742
+ * @private internal utility of pseudo-agent resolution
5743
+ */
5744
+ function isPseudoAgentUrl(agentUrl) {
5745
+ return resolvePseudoAgentKindFromUrl(agentUrl) !== null;
5746
+ }
5747
+ /**
5748
+ * Creates canonical pseudo-agent URL by pseudo-agent kind.
5749
+ *
5750
+ * @param pseudoAgentKind - Pseudo-agent kind.
5751
+ * @returns Canonical pseudo-agent URL.
5752
+ *
5753
+ * @private internal utility of pseudo-agent resolution
5754
+ */
5755
+ function createPseudoAgentUrl(pseudoAgentKind) {
5756
+ return PSEUDO_AGENT_URL_BY_KIND[pseudoAgentKind];
5757
+ }
5723
5758
  /**
5724
5759
  * Creates a deterministic human-readable pseudonym for the `{User}` pseudo-agent.
5725
5760
  *
@@ -5758,6 +5793,23 @@ function isVoidPseudoAgentReference(rawReference) {
5758
5793
  const normalizedReference = normalizePseudoReferenceKey(rawReference);
5759
5794
  return LEGACY_VOID_ALIASES.has(normalizedReference);
5760
5795
  }
5796
+ /**
5797
+ * Checks whether a pseudo-agent can be used inside the specified commitment.
5798
+ *
5799
+ * `USER` is intentionally disallowed in FROM/IMPORT commitments.
5800
+ *
5801
+ * @param pseudoAgentKind - Pseudo-agent kind to validate.
5802
+ * @param commitmentType - Commitment where it is used.
5803
+ * @returns True when the pseudo-agent is allowed in the commitment.
5804
+ *
5805
+ * @private internal utility of pseudo-agent resolution
5806
+ */
5807
+ function isPseudoAgentAllowedInCommitment(pseudoAgentKind, commitmentType) {
5808
+ if (pseudoAgentKind === 'USER') {
5809
+ return commitmentType === 'TEAM';
5810
+ }
5811
+ return true;
5812
+ }
5761
5813
  // Note: [💞] Ignore a discrepancy between file name and entity name
5762
5814
 
5763
5815
  /**
@@ -39274,6 +39326,245 @@ function stripInlineKnowledgeMetadata(metadata) {
39274
39326
  return Object.keys(rest).length > 0 ? rest : undefined;
39275
39327
  }
39276
39328
 
39329
+ /**
39330
+ * Pattern matching a markdown section heading (`## Title`) that separates system-message sections
39331
+ *
39332
+ * @private internal constant of `deduplicateSystemMessage`
39333
+ */
39334
+ const SYSTEM_MESSAGE_SECTION_HEADING_PATTERN = /^##(?!#)\s*(.+?)\s*$/;
39335
+ /**
39336
+ * Pattern matching a fenced code block delimiter which suspends section and block splitting
39337
+ *
39338
+ * @private internal constant of `deduplicateSystemMessage`
39339
+ */
39340
+ const CODE_FENCE_PATTERN = /^\s*(?:```|~~~)/;
39341
+ /**
39342
+ * Pattern matching a markdown list item which keeps merged bullet lists visually compact
39343
+ *
39344
+ * @private internal constant of `deduplicateSystemMessage`
39345
+ */
39346
+ const LIST_ITEM_PATTERN = /^\s*(?:[-*+]|\d+[.)])\s/;
39347
+ /**
39348
+ * Removes duplicated instructions from a generated system message
39349
+ *
39350
+ * Commitments are applied one by one, so a commitment used multiple times in one book repeats its whole
39351
+ * section, including the shared guidance preamble. For example two `WRITING RULES` commitments emit the
39352
+ * `## Writing rules` heading and its guidance paragraph twice. This function merges all sections sharing
39353
+ * one heading into the position of their first occurrence and keeps every identical block only once.
39354
+ *
39355
+ * @param systemMessage The assembled system message which may contain repeated sections and blocks
39356
+ * @returns The system message where each section heading and each identical block appears exactly once
39357
+ *
39358
+ * @private internal utility of `createAgentModelRequirementsWithCommitments`
39359
+ */
39360
+ function deduplicateSystemMessage(systemMessage) {
39361
+ if (!systemMessage.trim()) {
39362
+ return systemMessage;
39363
+ }
39364
+ return splitSystemMessageIntoRegions(systemMessage)
39365
+ .reduce(mergeRegionSharingHeading, [])
39366
+ .map(removeDuplicateBlocksFromRegion)
39367
+ .map(renderRegion)
39368
+ .filter((renderedRegion) => renderedRegion !== '')
39369
+ .join('\n\n');
39370
+ }
39371
+ /**
39372
+ * Splits the system message into the intro region and one region per `## Title` section
39373
+ *
39374
+ * @param systemMessage The system message to split
39375
+ * @returns Regions in their original order, without empty ones
39376
+ *
39377
+ * @private internal utility of `deduplicateSystemMessage`
39378
+ */
39379
+ function splitSystemMessageIntoRegions(systemMessage) {
39380
+ const regions = [];
39381
+ let headingLine = null;
39382
+ let bodyLines = [];
39383
+ let isInsideCodeFence = false;
39384
+ for (const line of systemMessage.split(/\r?\n/)) {
39385
+ if (CODE_FENCE_PATTERN.test(line)) {
39386
+ isInsideCodeFence = !isInsideCodeFence;
39387
+ }
39388
+ if (isInsideCodeFence || !SYSTEM_MESSAGE_SECTION_HEADING_PATTERN.test(line)) {
39389
+ bodyLines.push(line);
39390
+ continue;
39391
+ }
39392
+ regions.push(createRegion(headingLine, bodyLines));
39393
+ headingLine = line;
39394
+ bodyLines = [];
39395
+ }
39396
+ regions.push(createRegion(headingLine, bodyLines));
39397
+ return regions.filter((region) => region.headingLine !== null || region.blocks.length > 0);
39398
+ }
39399
+ /**
39400
+ * Creates one region from its heading line and its raw body lines
39401
+ *
39402
+ * @param headingLine The original `## Title` line, or `null` for the intro before the first heading
39403
+ * @param bodyLines The raw lines following the heading line
39404
+ * @returns The region with its body already split into blocks
39405
+ *
39406
+ * @private internal utility of `splitSystemMessageIntoRegions`
39407
+ */
39408
+ function createRegion(headingLine, bodyLines) {
39409
+ return {
39410
+ titleKey: headingLine === null ? null : normalizeSectionTitle(headingLine),
39411
+ headingLine,
39412
+ isBodySeparatedByBlankLine: bodyLines[0] !== undefined && bodyLines[0].trim() === '',
39413
+ blocks: splitBodyIntoBlocks(bodyLines),
39414
+ };
39415
+ }
39416
+ /**
39417
+ * Normalizes a heading line into a key usable for matching sections which belong together
39418
+ *
39419
+ * @param headingLine The original `## Title` line
39420
+ * @returns Lowercased title without the markdown heading prefix
39421
+ *
39422
+ * @private internal utility of `createRegion`
39423
+ */
39424
+ function normalizeSectionTitle(headingLine) {
39425
+ var _a, _b;
39426
+ return ((_b = (_a = SYSTEM_MESSAGE_SECTION_HEADING_PATTERN.exec(headingLine)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : headingLine).toLowerCase();
39427
+ }
39428
+ /**
39429
+ * Splits raw body lines into blocks separated by blank lines while keeping fenced code blocks intact
39430
+ *
39431
+ * @param bodyLines The raw lines of one region body
39432
+ * @returns Non-empty blocks in their original order
39433
+ *
39434
+ * @private internal utility of `createRegion`
39435
+ */
39436
+ function splitBodyIntoBlocks(bodyLines) {
39437
+ const blockTexts = [];
39438
+ let blockLines = [];
39439
+ let isInsideCodeFence = false;
39440
+ for (const line of bodyLines) {
39441
+ if (CODE_FENCE_PATTERN.test(line)) {
39442
+ isInsideCodeFence = !isInsideCodeFence;
39443
+ }
39444
+ if (!isInsideCodeFence && line.trim() === '') {
39445
+ blockTexts.push(blockLines.join('\n'));
39446
+ blockLines = [];
39447
+ continue;
39448
+ }
39449
+ blockLines.push(line);
39450
+ }
39451
+ blockTexts.push(blockLines.join('\n'));
39452
+ return blockTexts
39453
+ .filter((blockText) => blockText.trim() !== '')
39454
+ .map((blockText) => ({ text: blockText, isOpeningMergedSection: false }));
39455
+ }
39456
+ /**
39457
+ * Appends one region to the already merged regions, merging it into an earlier region with the same heading
39458
+ *
39459
+ * @param mergedRegions Regions merged so far, used as the reducer accumulator
39460
+ * @param region The next region in original order
39461
+ * @returns The accumulator with the region either appended or merged into its earlier twin
39462
+ *
39463
+ * @private internal utility of `deduplicateSystemMessage`
39464
+ */
39465
+ function mergeRegionSharingHeading(mergedRegions, region) {
39466
+ const existingRegionIndex = mergedRegions.findIndex((mergedRegion) => region.titleKey !== null && mergedRegion.titleKey === region.titleKey);
39467
+ if (existingRegionIndex === -1) {
39468
+ return [...mergedRegions, region];
39469
+ }
39470
+ return mergedRegions.map((mergedRegion, index) => index !== existingRegionIndex
39471
+ ? mergedRegion
39472
+ : {
39473
+ ...mergedRegion,
39474
+ blocks: [...mergedRegion.blocks, ...markFirstBlockAsOpeningMergedSection(region.blocks)],
39475
+ });
39476
+ }
39477
+ /**
39478
+ * Marks the first block of a merged section occurrence so the merge seam can be rendered as one list
39479
+ *
39480
+ * @param blocks The blocks of the section occurrence being merged into an earlier section
39481
+ * @returns The same blocks with the first one marked as opening a merged section
39482
+ *
39483
+ * @private internal utility of `mergeRegionSharingHeading`
39484
+ */
39485
+ function markFirstBlockAsOpeningMergedSection(blocks) {
39486
+ return blocks.map((block, index) => (index === 0 ? { ...block, isOpeningMergedSection: true } : block));
39487
+ }
39488
+ /**
39489
+ * Keeps only the first occurrence of every identical block inside one region
39490
+ *
39491
+ * @param region The region whose blocks may repeat
39492
+ * @returns The region without repeated blocks
39493
+ *
39494
+ * @private internal utility of `deduplicateSystemMessage`
39495
+ */
39496
+ function removeDuplicateBlocksFromRegion(region) {
39497
+ const alreadyRenderedTexts = new Set();
39498
+ const uniqueBlocks = [];
39499
+ let isMergeSeamPending = false;
39500
+ for (const block of region.blocks) {
39501
+ if (alreadyRenderedTexts.has(block.text)) {
39502
+ // Note: A removed block must still hand its merge seam over to the next block which survives
39503
+ isMergeSeamPending = isMergeSeamPending || block.isOpeningMergedSection;
39504
+ continue;
39505
+ }
39506
+ alreadyRenderedTexts.add(block.text);
39507
+ uniqueBlocks.push({
39508
+ text: block.text,
39509
+ isOpeningMergedSection: block.isOpeningMergedSection || isMergeSeamPending,
39510
+ });
39511
+ isMergeSeamPending = false;
39512
+ }
39513
+ return { ...region, blocks: uniqueBlocks };
39514
+ }
39515
+ /**
39516
+ * Renders one region back into its markdown representation
39517
+ *
39518
+ * @param region The region to render
39519
+ * @returns The heading line together with its joined blocks
39520
+ *
39521
+ * @private internal utility of `deduplicateSystemMessage`
39522
+ */
39523
+ function renderRegion(region) {
39524
+ const body = joinBlocks(region.blocks);
39525
+ if (region.headingLine === null) {
39526
+ return body;
39527
+ }
39528
+ if (body === '') {
39529
+ return region.headingLine;
39530
+ }
39531
+ return `${region.headingLine}${region.isBodySeparatedByBlankLine ? '\n\n' : '\n'}${body}`;
39532
+ }
39533
+ /**
39534
+ * Joins blocks of one region back together
39535
+ *
39536
+ * @param blocks The blocks to join in their original order
39537
+ * @returns The joined body of one region
39538
+ *
39539
+ * @private internal utility of `renderRegion`
39540
+ */
39541
+ function joinBlocks(blocks) {
39542
+ return blocks
39543
+ .map((block, index) => index === 0 ? block.text : `${createBlockSeparator(blocks[index - 1], block)}${block.text}`)
39544
+ .join('');
39545
+ }
39546
+ /**
39547
+ * Chooses the separator between two neighboring blocks
39548
+ *
39549
+ * Blocks are normally separated by a blank line, exactly as they were written. Only where two sections were
39550
+ * merged together their lists are joined into one compact list instead of two separate ones.
39551
+ *
39552
+ * @param previousBlock The block rendered before the separator
39553
+ * @param nextBlock The block rendered after the separator
39554
+ * @returns Either a single or a double newline
39555
+ *
39556
+ * @private internal utility of `joinBlocks`
39557
+ */
39558
+ function createBlockSeparator(previousBlock, nextBlock) {
39559
+ if (!nextBlock.isOpeningMergedSection) {
39560
+ return '\n\n';
39561
+ }
39562
+ const previousBlockLines = previousBlock.text.split('\n');
39563
+ const isListContinuing = LIST_ITEM_PATTERN.test(previousBlockLines[previousBlockLines.length - 1]) &&
39564
+ LIST_ITEM_PATTERN.test(nextBlock.text.split('\n')[0]);
39565
+ return isListContinuing ? '\n' : '\n\n';
39566
+ }
39567
+
39277
39568
  /**
39278
39569
  * Removes single-hash comment lines (`# Comment`) from a system message
39279
39570
  * This is used to clean up the final system message before sending it to the AI model
@@ -39347,14 +39638,14 @@ function createInitialAgentModelRequirements(agentName, modelName) {
39347
39638
  * Performs the final system-message cleanup pass after all other augmentation steps are complete.
39348
39639
  *
39349
39640
  * @param requirements - Fully built requirements before final cleanup.
39350
- * @returns Requirements with comment lines removed from the final system message.
39641
+ * @returns Requirements with comment lines removed and repeated instructions deduplicated in the final system message.
39351
39642
  *
39352
39643
  * @private internal utility of `createAgentModelRequirementsWithCommitments`
39353
39644
  */
39354
39645
  function finalizeRequirements(requirements) {
39355
39646
  return {
39356
39647
  ...requirements,
39357
- systemMessage: removeCommentsFromSystemMessage(requirements.systemMessage),
39648
+ systemMessage: deduplicateSystemMessage(removeCommentsFromSystemMessage(requirements.systemMessage)),
39358
39649
  };
39359
39650
  }
39360
39651
 
@@ -39443,8 +39734,8 @@ function extractMcpServers(agentSource) {
39443
39734
  /**
39444
39735
  * Compiles agent source into the behavior text passed to CLI harness runners.
39445
39736
  */
39446
- async function createAgentRunnerSystemMessage(agentSource) {
39447
- return formatAgentModelRequirementsForRunner(await createAgentModelRequirements(agentSource));
39737
+ async function createAgentRunnerSystemMessage(agentSource, options) {
39738
+ return formatAgentModelRequirementsForRunner(await createAgentModelRequirements(agentSource, undefined, undefined, undefined, options));
39448
39739
  }
39449
39740
  /**
39450
39741
  * Formats prepared model requirements for a text-only CLI harness prompt.
@@ -43440,6 +43731,59 @@ function buildPromptSlug$1(templateSlugPrefix, title) {
43440
43731
  // Note: [🟡] Code for CLI command [generate-boilerplates](src/cli/cli-commands/coder/generate-boilerplates.ts) should never be published outside of `@promptbook/cli`
43441
43732
  // Note: [💞] Ignore a discrepancy between file name and entity name
43442
43733
 
43734
+ /**
43735
+ * Locates a bundled agent in either the source checkout or the generated CLI package.
43736
+ *
43737
+ * @private internal utility of CLI agent initialization
43738
+ */
43739
+ async function resolveBundledAgentBookPath(relativeFilePath) {
43740
+ const candidates = [
43741
+ join(__dirname, '..', relativeFilePath),
43742
+ join(__dirname, '..', '..', '..', '..', relativeFilePath),
43743
+ ];
43744
+ for (const candidate of candidates) {
43745
+ if (await stat(candidate).then((entry) => entry.isFile(), () => false)) {
43746
+ return candidate;
43747
+ }
43748
+ }
43749
+ throw new NotFoundError(spaceTrim$1((block) => `
43750
+ Cannot find the bundled agent book \`${relativeFilePath}\`.
43751
+
43752
+ Checked:
43753
+ ${block(candidates.map((candidate) => `- \`${candidate}\``).join('\n'))}
43754
+ `));
43755
+ }
43756
+
43757
+ /**
43758
+ * Adam's location relative to a directory of local agents.
43759
+ *
43760
+ * @private internal constant of CLI agent initialization
43761
+ */
43762
+ const ADAM_AGENT_BOOK_RELATIVE_PATH = '.core/adam.book';
43763
+ /**
43764
+ * Creates the same Adam book used by the Agent Server without overwriting a project-owned book.
43765
+ *
43766
+ * @private internal utility of CLI agent initialization
43767
+ */
43768
+ async function ensureAdamAgentBook(agentDirectoryPath) {
43769
+ const adamBookPath = join(agentDirectoryPath, ADAM_AGENT_BOOK_RELATIVE_PATH);
43770
+ if (await stat(adamBookPath).then((entry) => entry.isFile(), () => false)) {
43771
+ return 'unchanged';
43772
+ }
43773
+ const bundledAdamBookPath = await resolveBundledAgentBookPath('agents/default/.core/adam.book');
43774
+ await mkdir(dirname(adamBookPath), { recursive: true });
43775
+ try {
43776
+ await copyFile(bundledAdamBookPath, adamBookPath, constants.COPYFILE_EXCL);
43777
+ return 'created';
43778
+ }
43779
+ catch (error) {
43780
+ if (error.code === 'EEXIST') {
43781
+ return 'unchanged';
43782
+ }
43783
+ throw error;
43784
+ }
43785
+ }
43786
+
43443
43787
  /**
43444
43788
  * Asks the user in the terminal to confirm one optional change.
43445
43789
  *
@@ -44243,29 +44587,9 @@ async function ensureCoderDeveloperAgentFile(projectPath) {
44243
44587
  if (await isExistingFile$2(absoluteFilePath)) {
44244
44588
  return 'unchanged';
44245
44589
  }
44246
- await copyFile(await resolveDefaultCoderDeveloperAgentFilePath(), absoluteFilePath);
44590
+ await copyFile(await resolveBundledAgentBookPath(DEFAULT_CODER_DEVELOPER_AGENT_SOURCE_FILE_PATH), absoluteFilePath);
44247
44591
  return 'created';
44248
44592
  }
44249
- /**
44250
- * Resolves the bundled developer agent from a source checkout or generated CLI package.
44251
- */
44252
- async function resolveDefaultCoderDeveloperAgentFilePath() {
44253
- const candidates = [
44254
- join(__dirname, '..', DEFAULT_CODER_DEVELOPER_AGENT_SOURCE_FILE_PATH),
44255
- join(__dirname, '..', '..', '..', '..', DEFAULT_CODER_DEVELOPER_AGENT_SOURCE_FILE_PATH),
44256
- ];
44257
- for (const candidate of candidates) {
44258
- if (await isExistingFile$2(candidate)) {
44259
- return candidate;
44260
- }
44261
- }
44262
- throw new NotAllowed(spaceTrim$1(`
44263
- Cannot find the bundled Promptbook developer agent.
44264
-
44265
- Checked:
44266
- ${candidates.map((candidate) => `- \`${candidate}\``).join('\n')}
44267
- `));
44268
- }
44269
44593
  /**
44270
44594
  * Checks whether a path exists and is a file.
44271
44595
  */
@@ -44748,6 +45072,7 @@ async function initializeCoderProjectConfiguration(projectPath) {
44748
45072
  const promptsDoneDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_DONE_DIRECTORY_PATH);
44749
45073
  const promptsTemplatesDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_TEMPLATES_DIRECTORY_PATH);
44750
45074
  const agentsDirectoryStatus = await ensureDirectory(projectPath, CODER_AGENTS_DIRECTORY_PATH);
45075
+ const adamAgentFileStatus = await ensureAdamAgentBook(join(projectPath, CODER_AGENTS_DIRECTORY_PATH));
44751
45076
  const { envFileStatus, initializedEnvVariableNames } = await ensureCoderEnvFile(projectPath);
44752
45077
  const gitignoreFileStatus = await ensureCoderGitignoreFile(projectPath);
44753
45078
  const { status: packageJsonFileStatus, addedEntryKeys: addedPackageJsonScriptNames } = await ensureCoderPackageJsonFile(projectPath);
@@ -44758,6 +45083,7 @@ async function initializeCoderProjectConfiguration(projectPath) {
44758
45083
  promptsDoneDirectoryStatus,
44759
45084
  promptsTemplatesDirectoryStatus,
44760
45085
  agentsDirectoryStatus,
45086
+ adamAgentFileStatus,
44761
45087
  envFileStatus,
44762
45088
  gitignoreFileStatus,
44763
45089
  packageJsonFileStatus,
@@ -44780,6 +45106,7 @@ function printInitializationSummary(summary) {
44780
45106
  printInitializationStatusLine('prompts/done/', summary.promptsDoneDirectoryStatus);
44781
45107
  printInitializationStatusLine('prompts/templates/', summary.promptsTemplatesDirectoryStatus);
44782
45108
  printInitializationStatusLine('agents/', summary.agentsDirectoryStatus);
45109
+ printInitializationStatusLine(`${CODER_AGENTS_DIRECTORY_PATH}/${ADAM_AGENT_BOOK_RELATIVE_PATH}`, summary.adamAgentFileStatus);
44783
45110
  printInitializationStatusLine('.env', summary.envFileStatus);
44784
45111
  printInitializationStatusLine('.gitignore', summary.gitignoreFileStatus);
44785
45112
  printInitializationStatusLine('package.json', summary.packageJsonFileStatus);
@@ -44854,6 +45181,7 @@ function $initializeCoderInitCommand(program) {
44854
45181
  - prompts/done/
44855
45182
  ${block(listDefaultCoderProjectPromptTemplateDisplayPaths())}
44856
45183
  - ${CODER_DEVELOPER_AGENT_FILE_PATH}
45184
+ - ${CODER_AGENTS_DIRECTORY_PATH}/${ADAM_AGENT_BOOK_RELATIVE_PATH}
44857
45185
  - ${AGENTS_FILE_PATH}
44858
45186
  - .gitignore with local artifacts from every supported harness
44859
45187
  - package.json
@@ -44998,8 +45326,7 @@ function $initializeCoderListCommand(program) {
44998
45326
  const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: false });
44999
45327
  const resolvedCoderAgentBook = agent === undefined
45000
45328
  ? undefined
45001
- : await (await Promise.resolve().then(function () { return resolveCoderAgent$1; }))
45002
- .resolveCoderAgentBook(agent, process.cwd());
45329
+ : await (await Promise.resolve().then(function () { return resolveCoderAgent$1; })).resolveCoderAgentBook(agent, process.cwd());
45003
45330
  const promptRunnerIdentity = runnerOptions.agentName === undefined &&
45004
45331
  runnerOptions.model === undefined &&
45005
45332
  resolvedCoderAgentBook === undefined
@@ -45756,6 +46083,8 @@ function $initializeCoderRunCommand(program) {
45756
46083
  ${block(QUESTIONS_DESCRIPTION)}
45757
46084
 
45758
46085
  Features:
46086
+ - Resolves FROM, IMPORT and TEAM by agent name, book-relative path, project path or URL
46087
+ - Creates a missing .core/adam.book beside the selected agent for default inheritance
45759
46088
  - Automatically stages and commits changes with agent identity unless --no-commit is used
45760
46089
  - Commits only the prompt file and the files the coding agent has changed, leaving unrelated changes alone
45761
46090
  - Refuses to start on a dirty working tree unless --git-changes says what should happen with the changes
@@ -46729,7 +47058,7 @@ function $provideFilesystemForNode(options) {
46729
47058
  return {
46730
47059
  stat,
46731
47060
  access,
46732
- constants,
47061
+ constants: constants$1,
46733
47062
  readFile,
46734
47063
  writeFile,
46735
47064
  readdir,
@@ -58701,6 +59030,43 @@ const DEPRECATED_TOP_LEVEL_COMMAND_MESSAGES = {
58701
59030
  'start-agents-server': 'Use `ptbk agents-server start` instead.',
58702
59031
  'start-pipelines-server': OLD_PIPELINE_SYSTEM_DEPRECATION_MESSAGE,
58703
59032
  };
59033
+ /**
59034
+ * Registers the CLI commands on a fresh Commander program without parsing arguments or running an action.
59035
+ * Tests can configure output and exit handling before registration, so subcommands inherit those settings.
59036
+ *
59037
+ * @private internal utility of `promptbookCli`
59038
+ */
59039
+ function $initializePromptbookCliProgram(program) {
59040
+ program.name('promptbook');
59041
+ program.alias('ptbk');
59042
+ program.version(PROMPTBOOK_ENGINE_VERSION);
59043
+ program.description(CLAIM);
59044
+ // Commands are listed in registration order. Keep `coder` first and hide deprecated commands below.
59045
+ $initializeCoderCommand(program);
59046
+ $initializeAgentCommand(program);
59047
+ $initializeAgentFolderCommand(program);
59048
+ $initializeAgentsServerCommand(program);
59049
+ $initializeAboutCommand(program);
59050
+ $initializeHelloCommand(program);
59051
+ $initializeRunCommand(program);
59052
+ $initializeLoginCommand(program);
59053
+ $initializeMakeCommand(program);
59054
+ $initializePrettifyCommand(program);
59055
+ $initializeTestCommand(program);
59056
+ $initializeListModelsCommand(program);
59057
+ $initializeListScrapersCommand(program);
59058
+ $initializeStartAgentsServerCommand(program);
59059
+ $initializeStartPipelinesServerCommand(program);
59060
+ for (const command of program.commands) {
59061
+ const deprecationMessage = DEPRECATED_TOP_LEVEL_COMMAND_MESSAGES[command.name()];
59062
+ if (deprecationMessage !== undefined) {
59063
+ $deprecateCliCommand(command, deprecationMessage);
59064
+ }
59065
+ }
59066
+ program.commands.forEach($addGlobalOptionsToCommand);
59067
+ }
59068
+ // Note: [🟡] Code for CLI command registration [$initializePromptbookCliProgram](src/cli/$initializePromptbookCliProgram.ts) should never be published outside of `@promptbook/cli`
59069
+
58704
59070
  /**
58705
59071
  * Raw top-level CLI arguments that print the Promptbook version.
58706
59072
  *
@@ -58733,32 +59099,7 @@ async function promptbookCli() {
58733
59099
  console.info(colors.gray(`Promptbook CLI version ${PROMPTBOOK_ENGINE_VERSION} in ${__filename.split('\\').join('/')}`));
58734
59100
  }
58735
59101
  const program = new commander.Command();
58736
- program.name('promptbook');
58737
- program.alias('ptbk');
58738
- program.version(PROMPTBOOK_ENGINE_VERSION);
58739
- program.description(CLAIM);
58740
- // Note: These options are valid for all commands
58741
- // Note: Commands are listed in the help in the order they are registered,
58742
- // `coder` is the most used one so it goes first and the deprecated ones,
58743
- // which `$deprecateTopLevelCommands` hides from the help entirely, go last
58744
- $initializeCoderCommand(program);
58745
- $initializeAgentCommand(program);
58746
- $initializeAgentFolderCommand(program);
58747
- $initializeAgentsServerCommand(program);
58748
- $initializeAboutCommand(program);
58749
- $initializeHelloCommand(program);
58750
- $initializeRunCommand(program);
58751
- $initializeLoginCommand(program);
58752
- $initializeMakeCommand(program);
58753
- $initializePrettifyCommand(program);
58754
- $initializeTestCommand(program);
58755
- $initializeListModelsCommand(program);
58756
- $initializeListScrapersCommand(program);
58757
- $initializeStartAgentsServerCommand(program);
58758
- $initializeStartPipelinesServerCommand(program);
58759
- $deprecateTopLevelCommands(program);
58760
- // TODO: [🧠] Should it be here or not> $addGlobalOptionsToCommand(program);
58761
- program.commands.forEach($addGlobalOptionsToCommand);
59102
+ $initializePromptbookCliProgram(program);
58762
59103
  // Note: There is no default command, so `ptbk` without a subcommand asks for one
58763
59104
  // instead of running the deprecated `ptbk run`
58764
59105
  if (commandLineArguments.length === 0) {
@@ -58775,17 +59116,6 @@ function isTopLevelVersionRequested(commandLineArguments) {
58775
59116
  const firstCommandLineArgument = commandLineArguments[0];
58776
59117
  return firstCommandLineArgument !== undefined && VERSION_OPTION_ARGUMENTS.has(firstCommandLineArgument);
58777
59118
  }
58778
- /**
58779
- * Marks each configured top-level legacy command as deprecated, which keeps it usable but takes it out of the help.
58780
- */
58781
- function $deprecateTopLevelCommands(program) {
58782
- for (const command of program.commands) {
58783
- const deprecationMessage = DEPRECATED_TOP_LEVEL_COMMAND_MESSAGES[command.name()];
58784
- if (deprecationMessage !== undefined) {
58785
- $deprecateCliCommand(command, deprecationMessage);
58786
- }
58787
- }
58788
- }
58789
59119
  // Note: [🟡] Code for CLI program [promptbookCli](src/cli/promptbookCli.ts) should never be published outside of `@promptbook/cli`
58790
59120
  // TODO: [🥠] Do not export, its just for CLI script
58791
59121
  // TODO: [🕌] When more functionalities, rename
@@ -75589,6 +75919,929 @@ var findUnwrittenPrompts$1 = /*#__PURE__*/Object.freeze({
75589
75919
  findUnwrittenPrompts: findUnwrittenPrompts
75590
75920
  });
75591
75921
 
75922
+ /**
75923
+ * Commitment keyword that declares the parent agent of one book.
75924
+ *
75925
+ * @private utility of Agents Server inheritance resolution
75926
+ */
75927
+ const FROM_COMMITMENT_TYPE = 'FROM';
75928
+ /**
75929
+ * Matches one standalone `FROM` commitment line and captures its inline content.
75930
+ *
75931
+ * The Book parser accepts horizontal whitespace between a commitment keyword and its value, so inheritance resolution
75932
+ * must do the same. Keeping this pattern here makes every server-side consumer agree on which `FROM` lines are real.
75933
+ *
75934
+ * @private utility of Agents Server inheritance resolution
75935
+ */
75936
+ const FROM_COMMITMENT_LINE_PATTERN = new RegExp(`^${FROM_COMMITMENT_TYPE}(?:\\s+(.*))?$`);
75937
+ /**
75938
+ * Collects every explicit single-line `FROM` commitment of one book.
75939
+ *
75940
+ * This lightweight parser is intentionally limited to the subset needed by inheritance
75941
+ * resolution so it stays safe to bundle into the Next.js proxy path.
75942
+ *
75943
+ * @param sourceLines - Book source already split into lines.
75944
+ * @returns Found `FROM` commitments in source order, empty when the book declares no parent.
75945
+ *
75946
+ * @private utility of Agents Server inheritance resolution
75947
+ */
75948
+ function collectExplicitFromCommitments(sourceLines) {
75949
+ const explicitFromCommitments = [];
75950
+ let hasSeenTitle = false;
75951
+ let isInsideCodeBlock = false;
75952
+ for (let lineIndex = 0; lineIndex < sourceLines.length; lineIndex++) {
75953
+ const trimmedLine = (sourceLines[lineIndex] || '').trim();
75954
+ if (!hasSeenTitle) {
75955
+ if (!trimmedLine) {
75956
+ continue;
75957
+ }
75958
+ hasSeenTitle = true;
75959
+ continue;
75960
+ }
75961
+ if (trimmedLine.startsWith('```')) {
75962
+ isInsideCodeBlock = !isInsideCodeBlock;
75963
+ continue;
75964
+ }
75965
+ if (isInsideCodeBlock) {
75966
+ continue;
75967
+ }
75968
+ const fromCommitmentMatch = FROM_COMMITMENT_LINE_PATTERN.exec(trimmedLine);
75969
+ if (fromCommitmentMatch) {
75970
+ explicitFromCommitments.push({
75971
+ lineIndex,
75972
+ content: (fromCommitmentMatch[1] || '').trim(),
75973
+ });
75974
+ }
75975
+ }
75976
+ return explicitFromCommitments;
75977
+ }
75978
+ /**
75979
+ * Returns the effective explicit `FROM` commitment of one book.
75980
+ *
75981
+ * A book may repeat `FROM`, in which case the last one wins and overrides every earlier one.
75982
+ *
75983
+ * @param agentSource - Raw book source.
75984
+ * @returns The last explicit `FROM` commitment, or `undefined` when the book declares no parent.
75985
+ *
75986
+ * @private utility of Agents Server inheritance resolution
75987
+ */
75988
+ function getEffectiveExplicitFromCommitment(agentSource) {
75989
+ const explicitFromCommitments = collectExplicitFromCommitments(agentSource.split(/\r?\n/));
75990
+ return explicitFromCommitments[explicitFromCommitments.length - 1];
75991
+ }
75992
+ /**
75993
+ * Returns the effective explicit `FROM` commitment content of one book.
75994
+ *
75995
+ * A book may repeat `FROM`, in which case the last one wins and overrides every earlier one.
75996
+ *
75997
+ * @param agentSource - Raw book source.
75998
+ * @returns Trimmed commitment content, empty string for a blank explicit `FROM`, or `undefined` when `FROM` is absent.
75999
+ *
76000
+ * @private utility of Agents Server inheritance resolution
76001
+ */
76002
+ function getExplicitFromCommitmentContent(agentSource) {
76003
+ var _a;
76004
+ return (_a = getEffectiveExplicitFromCommitment(agentSource)) === null || _a === void 0 ? void 0 : _a.content;
76005
+ }
76006
+ // Note: [💞] Ignore a discrepancy between file name and exported helper names.
76007
+
76008
+ /**
76009
+ * Type guard for issue-tracking resolver implementations.
76010
+ *
76011
+ * @param resolver - Resolver instance to check.
76012
+ * @returns True when the resolver provides `consumeResolutionIssues`.
76013
+ */
76014
+ function isIssueTrackingAgentReferenceResolver(resolver) {
76015
+ return typeof resolver.consumeResolutionIssues === 'function';
76016
+ }
76017
+ /**
76018
+ * Drains unresolved compact-reference issues from a resolver when supported.
76019
+ *
76020
+ * @param resolver - Resolver instance that may implement issue tracking.
76021
+ * @returns Collected issues or an empty list.
76022
+ *
76023
+ * @private internal utility of agent reference resolution
76024
+ */
76025
+ function consumeAgentReferenceResolutionIssues(resolver) {
76026
+ if (!resolver) {
76027
+ return [];
76028
+ }
76029
+ if (!isIssueTrackingAgentReferenceResolver(resolver)) {
76030
+ return [];
76031
+ }
76032
+ return resolver.consumeResolutionIssues();
76033
+ }
76034
+
76035
+ /**
76036
+ * Gets the corpus of an agent source (removes title and trailing status)
76037
+ *
76038
+ * @param agentSource The agent source
76039
+ * @returns The agent source corpus
76040
+ */
76041
+ function getAgentSourceCorpus(agentSource) {
76042
+ // Remove trailing OPEN or CLOSED if present
76043
+ const agentSourceWithoutStatus = agentSource.replace(/\n?(OPEN|CLOSED)\s*$/i, '');
76044
+ // <- TODO: [🈲] Simple and encapsulated way to get book corpus
76045
+ // Remove the first line (title) from agent source
76046
+ const agentSourceCorpus = spaceTrim(agentSourceWithoutStatus.replace(/^.*$/m, ''));
76047
+ // <- TODO: [🈲] Simple and encapsulated way to get book corpus
76048
+ return agentSourceCorpus;
76049
+ }
76050
+ /**
76051
+ * Formats a resolver issue into a visible NOTE line in resolved agent source.
76052
+ *
76053
+ * @param issue - Tracked missing-reference issue.
76054
+ * @returns Single-line NOTE statement.
76055
+ */
76056
+ function formatResolutionIssueAsNote(issue) {
76057
+ const commitmentType = issue.commitmentType === 'IMPORTS' ? 'IMPORT' : issue.commitmentType;
76058
+ if (commitmentType === 'FROM') {
76059
+ return `NOTE Referenced agent "${issue.reference}" in FROM commitment was not found. Inheritance skipped.`;
76060
+ }
76061
+ if (commitmentType === 'IMPORT') {
76062
+ return `NOTE Referenced agent "${issue.reference}" in IMPORT commitment was not found. Import skipped.`;
76063
+ }
76064
+ if (commitmentType === 'TEAM') {
76065
+ return `NOTE Referenced agent "${issue.reference}" in TEAM commitment was not found. Teammate disabled.`;
76066
+ }
76067
+ return `NOTE Referenced agent "${issue.reference}" in ${commitmentType} commitment was not found.`;
76068
+ }
76069
+ /**
76070
+ * Appends NOTE lines for unresolved references while avoiding duplicates.
76071
+ *
76072
+ * @param targetChunks - Output chunks being assembled for the resolved book.
76073
+ * @param issues - Missing-reference issues to materialize into NOTE lines.
76074
+ */
76075
+ function appendResolutionIssueNotes(targetChunks, issues) {
76076
+ const seenIssueKeys = new Set();
76077
+ for (const issue of issues) {
76078
+ const key = `${issue.commitmentType}:${issue.reference}`.toLowerCase();
76079
+ if (seenIssueKeys.has(key)) {
76080
+ continue;
76081
+ }
76082
+ seenIssueKeys.add(key);
76083
+ targetChunks.push(formatResolutionIssueAsNote(issue), '');
76084
+ }
76085
+ }
76086
+ /**
76087
+ * Inserts NOTE lines right after the title line in a book.
76088
+ *
76089
+ * @param agentSource - Original agent source.
76090
+ * @param notes - NOTE lines to insert.
76091
+ * @returns Updated book with notes placed after the title.
76092
+ */
76093
+ function insertNotesAfterTitle(agentSource, notes) {
76094
+ if (notes.length === 0) {
76095
+ return agentSource;
76096
+ }
76097
+ const sourceLines = spaceTrim(agentSource).split(/\r?\n/);
76098
+ const titleLine = sourceLines[0] || '';
76099
+ const restLines = sourceLines.slice(1);
76100
+ const nextLines = [titleLine, '', ...notes, '', ...restLines];
76101
+ return padBook(validateBook(nextLines.join('\n')));
76102
+ }
76103
+ /**
76104
+ * Resolves the effective `FROM` parent URL using only lightweight commitment parsing.
76105
+ *
76106
+ * @param rawAgentSource - Original source used for diagnostics.
76107
+ * @param agentReferenceResolver - Optional compact-reference resolver.
76108
+ * @returns Valid parent URL, explicit `null` for `FROM VOID`/blank `FROM`, or `undefined` when `FROM` is absent.
76109
+ */
76110
+ async function resolveParentAgentUrlFromCommitments(rawAgentSource, agentReferenceResolver) {
76111
+ const explicitFromContent = getExplicitFromCommitmentContent(rawAgentSource);
76112
+ if (explicitFromContent === undefined) {
76113
+ return undefined;
76114
+ }
76115
+ let resolvedParentReference = explicitFromContent.trim();
76116
+ if (agentReferenceResolver && resolvedParentReference) {
76117
+ resolvedParentReference = (await agentReferenceResolver.resolveCommitmentContent('FROM', resolvedParentReference)).trim();
76118
+ }
76119
+ if (!resolvedParentReference || isVoidPseudoAgentReference(resolvedParentReference)) {
76120
+ return null;
76121
+ }
76122
+ if (!isValidAgentUrl(resolvedParentReference)) {
76123
+ throw new ParseError(spaceTrim((block) => `
76124
+ Invalid parent agent URL in FROM "${resolvedParentReference}" commitment:
76125
+
76126
+ \`\`\`book
76127
+ ${block(rawAgentSource)}
76128
+ \`\`\`
76129
+
76130
+ `));
76131
+ }
76132
+ return resolvedParentReference;
76133
+ }
76134
+ /**
76135
+ * Normalizes agent URLs used for cycle detection and lineage reporting.
76136
+ *
76137
+ * @param agentUrl - Raw agent URL.
76138
+ * @returns URL without trailing slashes.
76139
+ */
76140
+ function normalizeAgentUrl(agentUrl) {
76141
+ return agentUrl.replace(/\/+$/g, '');
76142
+ }
76143
+ /**
76144
+ * Builds the current resolution lineage ending with the agent being resolved now.
76145
+ *
76146
+ * @param options - Resolution options with current agent metadata.
76147
+ * @returns Ordered lineage without empty values.
76148
+ */
76149
+ function createResolutionLineage(options) {
76150
+ const lineage = [...((options === null || options === void 0 ? void 0 : options.inheritancePath) || [])];
76151
+ if (options === null || options === void 0 ? void 0 : options.currentAgentUrl) {
76152
+ lineage.push(options.currentAgentUrl);
76153
+ }
76154
+ for (const alias of (options === null || options === void 0 ? void 0 : options.currentAgentAliases) || []) {
76155
+ lineage.push(alias);
76156
+ }
76157
+ return [...new Set(lineage.map(normalizeAgentUrl))];
76158
+ }
76159
+ /**
76160
+ * Finds a cycle that would be closed by resolving one more agent reference.
76161
+ *
76162
+ * @param referenceUrl - Next agent reference to resolve.
76163
+ * @param options - Current resolution options.
76164
+ * @returns Cycle details, or `null` when the reference is not in the current lineage.
76165
+ *
76166
+ * @private internal helper for Agents Server inherited/imported agent resolution
76167
+ */
76168
+ function findResolutionCycle(referenceUrl, options) {
76169
+ const normalizedReferenceUrl = normalizeAgentUrl(referenceUrl);
76170
+ const lineage = createResolutionLineage(options);
76171
+ const cycleStartIndex = lineage.findIndex((visitedUrl) => visitedUrl === normalizedReferenceUrl);
76172
+ if (cycleStartIndex === -1) {
76173
+ return null;
76174
+ }
76175
+ return { normalizedReferenceUrl, lineage, cycleStartIndex };
76176
+ }
76177
+ /**
76178
+ * Creates the shared import context used throughout one resolution pass.
76179
+ *
76180
+ * @param options - Resolution options passed to `resolveInheritedAgentSource`.
76181
+ * @returns Normalized shared import context.
76182
+ */
76183
+ function createAgentImportContext(options) {
76184
+ return {
76185
+ adamAgentUrl: (options === null || options === void 0 ? void 0 : options.adamAgentUrl) || 'https://core.ptbk.io/agents/adam',
76186
+ agentReferenceResolver: options === null || options === void 0 ? void 0 : options.agentReferenceResolver,
76187
+ importAgentOptions: {
76188
+ recursionLevel: (options === null || options === void 0 ? void 0 : options.recursionLevel) || 0,
76189
+ inheritancePath: createResolutionLineage(options),
76190
+ },
76191
+ agentSourceImporter: options.agentSourceImporter,
76192
+ resolutionOptions: options,
76193
+ };
76194
+ }
76195
+ /**
76196
+ * Computes the effective parent URL after applying explicit `FROM` and Adam fallback rules.
76197
+ *
76198
+ * @param resolvedParentAgentUrl - Parent URL derived from lightweight commitment parsing.
76199
+ * @param hasExplicitFromCommitment - Whether the source explicitly declared `FROM`.
76200
+ * @param adamAgentUrl - Default Adam ancestor URL.
76201
+ * @param currentAgentUrl - Canonical URL of the source being resolved.
76202
+ * @param currentAgentAliases - Additional URLs that identify the source being resolved.
76203
+ * @param agentSource - Raw source used for unexpected diagnostics.
76204
+ * @returns Effective parent URL or `null` when inheritance is disabled.
76205
+ */
76206
+ function determineParentAgentUrl(resolvedParentAgentUrl, hasExplicitFromCommitment, adamAgentUrl, currentAgentUrl, currentAgentAliases, agentSource) {
76207
+ if (isValidAgentUrl(resolvedParentAgentUrl)) {
76208
+ return resolvedParentAgentUrl;
76209
+ }
76210
+ if (resolvedParentAgentUrl === null && hasExplicitFromCommitment) {
76211
+ return null;
76212
+ }
76213
+ if (resolvedParentAgentUrl === undefined || resolvedParentAgentUrl === null) {
76214
+ const currentAgentUrls = [currentAgentUrl, ...(currentAgentAliases || [])].filter((agentUrl) => Boolean(agentUrl));
76215
+ const isCurrentAgentAdam = currentAgentUrls.some((agentUrl) => normalizeAgentUrl(agentUrl) === normalizeAgentUrl(adamAgentUrl));
76216
+ return isCurrentAgentAdam ? null : adamAgentUrl;
76217
+ }
76218
+ throw new ParseError(spaceTrim((block) => `
76219
+ Invalid parent agent URL in FROM "${resolvedParentAgentUrl}" commitment:
76220
+
76221
+ \`\`\`book
76222
+ ${block(agentSource || '')}
76223
+ \`\`\`
76224
+ `));
76225
+ }
76226
+ /**
76227
+ * Imports one referenced agent and returns only its body content ready to be embedded as a NOTE block.
76228
+ *
76229
+ * @param agentUrl - Canonical referenced agent URL.
76230
+ * @param commitmentType - Commitment that introduced the reference.
76231
+ * @param context - Shared import context for the current resolution pass.
76232
+ * @returns Imported source body without title/status.
76233
+ */
76234
+ async function importAgentCorpus(agentUrl, commitmentType, context) {
76235
+ assertNoResolutionCycle(agentUrl, commitmentType, context.resolutionOptions);
76236
+ const importedAgentSource = await context.agentSourceImporter(agentUrl, {
76237
+ commitmentType,
76238
+ importAgentOptions: context.importAgentOptions,
76239
+ });
76240
+ return getAgentSourceCorpus(importedAgentSource);
76241
+ }
76242
+ /**
76243
+ * Resolves the effective parent state before the source body is rewritten.
76244
+ *
76245
+ * @param agentSource - Raw child agent source.
76246
+ * @param context - Shared import context for the current resolution pass.
76247
+ * @returns Parent URL, imported parent body, and unresolved `FROM` issues.
76248
+ */
76249
+ async function resolveParentAgentContext(agentSource, context) {
76250
+ var _a, _b;
76251
+ const explicitFromContent = getExplicitFromCommitmentContent(agentSource);
76252
+ const hasExplicitFromCommitment = explicitFromContent !== undefined;
76253
+ const resolvedParentAgentUrl = await resolveParentAgentUrlFromCommitments(agentSource, context.agentReferenceResolver);
76254
+ const fromResolutionIssues = consumeAgentReferenceResolutionIssues(context.agentReferenceResolver).filter((issue) => issue.commitmentType === 'FROM');
76255
+ const parentAgentUrl = determineParentAgentUrl(resolvedParentAgentUrl, hasExplicitFromCommitment, context.adamAgentUrl, (_a = context.resolutionOptions) === null || _a === void 0 ? void 0 : _a.currentAgentUrl, (_b = context.resolutionOptions) === null || _b === void 0 ? void 0 : _b.currentAgentAliases, agentSource);
76256
+ const isCyclicFrom = Boolean(parentAgentUrl && findResolutionCycle(parentAgentUrl, context.resolutionOptions));
76257
+ const effectiveParentAgentUrl = isCyclicFrom ? null : parentAgentUrl;
76258
+ const parentAgentSourceCorpus = effectiveParentAgentUrl
76259
+ ? await importAgentCorpus(effectiveParentAgentUrl, 'FROM', context)
76260
+ : null;
76261
+ return {
76262
+ // A parent that closes the current lineage has the same runtime effect as `FROM @Null`.
76263
+ parentAgentUrl: effectiveParentAgentUrl,
76264
+ parentAgentSourceCorpus,
76265
+ fromResolutionIssues,
76266
+ isCyclicFrom,
76267
+ };
76268
+ }
76269
+ /**
76270
+ * Formats one embedded agent body into the NOTE block used by inheritance/import resolution.
76271
+ *
76272
+ * @param noteLine - First NOTE line describing the embedded source.
76273
+ * @param sourceCorpus - Imported source body to embed.
76274
+ * @returns Multi-line NOTE block.
76275
+ */
76276
+ function createEmbeddedAgentSourceNote(noteLine, sourceCorpus) {
76277
+ return spaceTrim((block) => `
76278
+
76279
+ ${noteLine}
76280
+ ${block(sourceCorpus)}
76281
+
76282
+ NOTE ===========
76283
+ `);
76284
+ }
76285
+ /**
76286
+ * Resolves one `IMPORT ...` line into embedded source or leaves non-agent imports untouched.
76287
+ *
76288
+ * @param line - Current source line.
76289
+ * @param context - Shared import context for the current resolution pass.
76290
+ * @returns Output lines replacing the original line.
76291
+ */
76292
+ async function resolveImportCommitmentLine(line, context) {
76293
+ const rawImportedUrlOrPath = line
76294
+ .trim()
76295
+ .replace(/^IMPORTS?\s+/, '')
76296
+ .trim();
76297
+ let importedUrlOrPath = rawImportedUrlOrPath;
76298
+ let importResolutionIssues = [];
76299
+ if (context.agentReferenceResolver && rawImportedUrlOrPath) {
76300
+ try {
76301
+ importedUrlOrPath = await context.agentReferenceResolver.resolveCommitmentContent('IMPORT', rawImportedUrlOrPath);
76302
+ }
76303
+ catch (error) {
76304
+ console.warn('[AgentReferenceResolver] Failed to resolve IMPORT commitment references:', error);
76305
+ }
76306
+ finally {
76307
+ importResolutionIssues = consumeAgentReferenceResolutionIssues(context.agentReferenceResolver).filter((issue) => issue.commitmentType === 'IMPORT' || issue.commitmentType === 'IMPORTS');
76308
+ }
76309
+ }
76310
+ const resolvedChunks = [];
76311
+ appendResolutionIssueNotes(resolvedChunks, importResolutionIssues);
76312
+ if (!importedUrlOrPath) {
76313
+ return resolvedChunks;
76314
+ }
76315
+ if (!isValidAgentUrl(importedUrlOrPath)) {
76316
+ resolvedChunks.push(line);
76317
+ return resolvedChunks;
76318
+ }
76319
+ const importedAgentUrl = importedUrlOrPath;
76320
+ const importedAgentSourceCorpus = await importAgentCorpus(importedAgentUrl, 'IMPORT', context);
76321
+ resolvedChunks.push(createEmbeddedAgentSourceNote(`NOTE Imported from ${importedAgentUrl}`, importedAgentSourceCorpus), '');
76322
+ return resolvedChunks;
76323
+ }
76324
+ /**
76325
+ * Resolves the effective `FROM ...` line into inherited content or fallback NOTE lines.
76326
+ *
76327
+ * @param line - Current source line.
76328
+ * @param parentContext - Effective parent state computed earlier in the resolution.
76329
+ * @returns Output lines and remaining `FROM` issues.
76330
+ */
76331
+ function resolveFromCommitmentLine(line, parentContext) {
76332
+ if (parentContext.parentAgentUrl === null) {
76333
+ const agentSourceChunks = [parentContext.isCyclicFrom ? 'FROM @Null' : line];
76334
+ if (parentContext.fromResolutionIssues.length > 0) {
76335
+ appendResolutionIssueNotes(agentSourceChunks, parentContext.fromResolutionIssues);
76336
+ }
76337
+ return {
76338
+ agentSourceChunks,
76339
+ fromResolutionIssues: [],
76340
+ };
76341
+ }
76342
+ if (parentContext.parentAgentSourceCorpus) {
76343
+ return {
76344
+ agentSourceChunks: [
76345
+ createEmbeddedAgentSourceNote(`NOTE Inherited FROM ${parentContext.parentAgentUrl}`, parentContext.parentAgentSourceCorpus),
76346
+ '', // <- Note: Add an extra newline for separation
76347
+ ],
76348
+ fromResolutionIssues: parentContext.fromResolutionIssues,
76349
+ };
76350
+ }
76351
+ return {
76352
+ agentSourceChunks: [
76353
+ `NOTE Parent agent "${parentContext.parentAgentUrl}" was not found or could not be loaded. Inheritance skipped.`,
76354
+ '',
76355
+ ],
76356
+ fromResolutionIssues: parentContext.fromResolutionIssues,
76357
+ };
76358
+ }
76359
+ /**
76360
+ * Finds the `FROM` lines that a later `FROM` takes precedence over.
76361
+ *
76362
+ * A book is allowed to repeat `FROM`; the last one wins, so every earlier one is dropped instead of being resolved.
76363
+ * The Book editor warns about this while the author is still writing the book.
76364
+ *
76365
+ * @param explicitFromCommitments - Explicit parent declarations found in source order.
76366
+ * @returns Zero-based indexes of every overridden `FROM` line, empty when at most one `FROM` is present.
76367
+ */
76368
+ function collectOverriddenFromLineIndexes(explicitFromCommitments) {
76369
+ return new Set(explicitFromCommitments.slice(0, -1).map((explicitFromCommitment) => explicitFromCommitment.lineIndex));
76370
+ }
76371
+ /**
76372
+ * Rewrites the source body line by line while delegating each branching step to a focused helper.
76373
+ *
76374
+ * @param agentSource - Raw child agent source.
76375
+ * @param context - Shared import context for the current resolution pass.
76376
+ * @param parentContext - Effective parent state computed earlier in the resolution.
76377
+ * @returns Rewritten chunks plus final `FROM` bookkeeping.
76378
+ */
76379
+ async function resolveAgentSourceBuild(agentSource, context, parentContext) {
76380
+ const agentSourceChunks = spaceTrim(agentSource).split(/\r?\n/);
76381
+ const resolvedAgentSourceChunks = [];
76382
+ const explicitFromCommitments = collectExplicitFromCommitments(agentSourceChunks);
76383
+ const effectiveFromCommitment = explicitFromCommitments[explicitFromCommitments.length - 1];
76384
+ const overriddenFromLineIndexes = collectOverriddenFromLineIndexes(explicitFromCommitments);
76385
+ let isFromResolved = false;
76386
+ let isInsideCodeBlock = false;
76387
+ let fromResolutionIssues = parentContext.fromResolutionIssues;
76388
+ // <- TODO: [🈲] Simple and encapsulated way to split book into commitments
76389
+ for (let lineIndex = 0; lineIndex < agentSourceChunks.length; lineIndex++) {
76390
+ const line = agentSourceChunks[lineIndex] || '';
76391
+ if (line.trim().startsWith('```')) {
76392
+ isInsideCodeBlock = !isInsideCodeBlock;
76393
+ }
76394
+ if (isInsideCodeBlock || line.trim().startsWith('```')) {
76395
+ resolvedAgentSourceChunks.push(line);
76396
+ continue;
76397
+ }
76398
+ // Note: A repeated `FROM` is overridden by the last one, so the outdated line is dropped from the resolved source.
76399
+ if (overriddenFromLineIndexes.has(lineIndex)) {
76400
+ continue;
76401
+ }
76402
+ if (lineIndex > 0 && /^IMPORTS?\s+/.test(line.trim())) {
76403
+ resolvedAgentSourceChunks.push(...(await resolveImportCommitmentLine(line, context)));
76404
+ continue;
76405
+ }
76406
+ if ((effectiveFromCommitment === null || effectiveFromCommitment === void 0 ? void 0 : effectiveFromCommitment.lineIndex) === lineIndex) {
76407
+ const resolvedFromCommitment = resolveFromCommitmentLine(line, {
76408
+ ...parentContext,
76409
+ fromResolutionIssues,
76410
+ });
76411
+ resolvedAgentSourceChunks.push(...resolvedFromCommitment.agentSourceChunks);
76412
+ fromResolutionIssues = resolvedFromCommitment.fromResolutionIssues;
76413
+ isFromResolved = true;
76414
+ continue;
76415
+ }
76416
+ resolvedAgentSourceChunks.push(line);
76417
+ }
76418
+ // <- TODO: [🈲] Simple and encapsulated way to split book into commitments
76419
+ return {
76420
+ agentSourceChunks: resolvedAgentSourceChunks,
76421
+ isFromResolved,
76422
+ fromResolutionIssues,
76423
+ };
76424
+ }
76425
+ /**
76426
+ * Materializes the implicit Adam inheritance block when no explicit `FROM ...` was present.
76427
+ *
76428
+ * @param build - Current rewritten source build.
76429
+ * @param parentContext - Effective parent state computed earlier in the resolution.
76430
+ * @param adamAgentUrl - Default Adam ancestor URL.
76431
+ * @returns Updated source build with implicit Adam inheritance applied when needed.
76432
+ */
76433
+ function applyImplicitAdamInheritance(build, parentContext, adamAgentUrl) {
76434
+ if (build.isFromResolved || parentContext.parentAgentUrl !== adamAgentUrl) {
76435
+ return build;
76436
+ }
76437
+ const titleLine = build.agentSourceChunks[0] || '';
76438
+ const restLines = build.agentSourceChunks.slice(1);
76439
+ const agentSourceChunks = [titleLine, ''];
76440
+ if (parentContext.parentAgentSourceCorpus) {
76441
+ agentSourceChunks.push(createEmbeddedAgentSourceNote(`NOTE Inherited Adam FROM ${parentContext.parentAgentUrl}`, parentContext.parentAgentSourceCorpus), ...restLines);
76442
+ }
76443
+ else {
76444
+ agentSourceChunks.push(`NOTE Default parent agent "${parentContext.parentAgentUrl}" was not found or could not be loaded. Inheritance skipped.`, '', ...restLines);
76445
+ }
76446
+ return {
76447
+ ...build,
76448
+ agentSourceChunks,
76449
+ };
76450
+ }
76451
+ /**
76452
+ * Validates and pads the resolved source, then inserts any deferred unresolved-`FROM` notes.
76453
+ *
76454
+ * @param build - Final rewritten source build.
76455
+ * @returns Valid resolved book.
76456
+ */
76457
+ function finalizeResolvedAgentSource(build) {
76458
+ const resolvedAgentSource = padBook(validateBook(build.agentSourceChunks.join('\n')));
76459
+ if (build.fromResolutionIssues.length === 0) {
76460
+ return resolvedAgentSource;
76461
+ }
76462
+ const unresolvedFromNotes = build.fromResolutionIssues.map(formatResolutionIssueAsNote);
76463
+ return insertNotesAfterTitle(resolvedAgentSource, unresolvedFromNotes);
76464
+ }
76465
+ /**
76466
+ * Throws when a `FROM`/`IMPORT` edge would create an inheritance cycle.
76467
+ *
76468
+ * @param referenceUrl - Next referenced agent URL.
76469
+ * @param commitmentType - Commitment that introduced the reference.
76470
+ * @param options - Current resolution options.
76471
+ */
76472
+ function assertNoResolutionCycle(referenceUrl, commitmentType, options) {
76473
+ const resolutionCycle = findResolutionCycle(referenceUrl, options);
76474
+ if (!resolutionCycle) {
76475
+ return;
76476
+ }
76477
+ const cycleChain = [
76478
+ ...resolutionCycle.lineage.slice(resolutionCycle.cycleStartIndex),
76479
+ resolutionCycle.normalizedReferenceUrl,
76480
+ ]
76481
+ .map((visitedUrl) => `- \`${visitedUrl}\``)
76482
+ .join('\n');
76483
+ throw new ParseError(spaceTrim((block) => `
76484
+ Cyclic \`${commitmentType}\` reference detected while resolving agent source.
76485
+
76486
+ Resolution chain:
76487
+ ${block(cycleChain)}
76488
+ `));
76489
+ }
76490
+ /**
76491
+ * Resolves agent source with inheritance (FROM commitment)
76492
+ *
76493
+ * It recursively fetches the parent agent source and merges it with the current source.
76494
+ *
76495
+ * @param agentSource The initial agent source
76496
+ * @returns The resolved agent source with inheritance applied
76497
+ *
76498
+ * @private internal utility of agent source resolution
76499
+ */
76500
+ async function resolveInheritedAgentSource(agentSource, options) {
76501
+ const context = createAgentImportContext(options);
76502
+ const parentContext = await resolveParentAgentContext(agentSource, context);
76503
+ const resolvedBuild = await resolveAgentSourceBuild(agentSource, context, parentContext);
76504
+ const finalizedBuild = applyImplicitAdamInheritance(resolvedBuild, parentContext, context.adamAgentUrl);
76505
+ return finalizeResolvedAgentSource(finalizedBuild);
76506
+ }
76507
+ // TODO: [🈲] Create a function that can manipulate books by modifying commitments, splitting the book up into commitments or syntactic tokens, and editing or deleting these via object methods.
76508
+
76509
+ /**
76510
+ * Matches supported compact reference token syntaxes in commitment content.
76511
+ */
76512
+ const AGENT_REFERENCE_TOKEN_REGEX = /(\{([^}]+)\}|@([A-Za-z0-9_-]+))/g;
76513
+ /**
76514
+ * Extracts compact agent-reference tokens from commitment content.
76515
+ *
76516
+ * @param content - Commitment content to inspect.
76517
+ * @returns Matched compact reference tokens.
76518
+ *
76519
+ * @private internal utility of agent reference resolution
76520
+ */
76521
+ function extractAgentReferenceTokens(content) {
76522
+ var _a, _b;
76523
+ const matches = [];
76524
+ let match;
76525
+ AGENT_REFERENCE_TOKEN_REGEX.lastIndex = 0;
76526
+ while ((match = AGENT_REFERENCE_TOKEN_REGEX.exec(content)) !== null) {
76527
+ const token = match[1];
76528
+ const reference = ((_b = (_a = match[2]) !== null && _a !== void 0 ? _a : match[3]) !== null && _b !== void 0 ? _b : '').trim();
76529
+ const index = match.index;
76530
+ if (!token || index === undefined) {
76531
+ continue;
76532
+ }
76533
+ matches.push({
76534
+ token,
76535
+ reference,
76536
+ index,
76537
+ length: token.length,
76538
+ });
76539
+ }
76540
+ return matches;
76541
+ }
76542
+
76543
+ /**
76544
+ * Creates a resolver scoped to the book which declares each reference.
76545
+ *
76546
+ * @private internal utility of CLI agent resolution
76547
+ */
76548
+ function createLocalAgentReferenceResolver(collection, declaringBook) {
76549
+ /** Resolves pseudo-agents using the same aliases and restrictions as the Agent Server. */
76550
+ async function resolveReference(commitmentType, reference) {
76551
+ const pseudoAgentKind = resolvePseudoAgentKindFromReference(reference);
76552
+ if (!pseudoAgentKind) {
76553
+ return collection.resolveReference(reference, declaringBook);
76554
+ }
76555
+ if (!isPseudoAgentAllowedInCommitment(pseudoAgentKind, commitmentType)) {
76556
+ throw new ParseError(spaceTrim$1(`Pseudo-agent \`${reference}\` cannot be used in \`${commitmentType}\`.`));
76557
+ }
76558
+ if (pseudoAgentKind === 'VOID' && commitmentType === 'FROM') {
76559
+ return VOID_PSEUDO_AGENT_REFERENCE;
76560
+ }
76561
+ if (pseudoAgentKind === 'VOID' && (commitmentType === 'IMPORT' || commitmentType === 'IMPORTS')) {
76562
+ return '';
76563
+ }
76564
+ return createPseudoAgentUrl(pseudoAgentKind);
76565
+ }
76566
+ return {
76567
+ resolveCommitmentContent: async (commitmentType, content) => {
76568
+ const tokens = extractAgentReferenceTokens(content);
76569
+ if (tokens.length === 0) {
76570
+ if (commitmentType !== 'TEAM' && content.trim()) {
76571
+ return resolveReference(commitmentType, content.trim());
76572
+ }
76573
+ return content;
76574
+ }
76575
+ const parts = [];
76576
+ let previousEnd = 0;
76577
+ for (const token of tokens) {
76578
+ parts.push(content.slice(previousEnd, token.index));
76579
+ parts.push(await resolveReference(commitmentType, token.reference));
76580
+ previousEnd = token.index + token.length;
76581
+ }
76582
+ parts.push(content.slice(previousEnd));
76583
+ return parts.join('');
76584
+ },
76585
+ };
76586
+ }
76587
+
76588
+ /**
76589
+ * Internal URLs allow the shared Book commitments to identify local books without an HTTP server.
76590
+ * These identifiers are always intercepted by this collection and are never fetched.
76591
+ */
76592
+ const LOCAL_AGENT_URL_PREFIX = 'https://local-agent.promptbook/';
76593
+ /**
76594
+ * Directories which cannot contain project-owned agent definitions.
76595
+ */
76596
+ const IGNORED_DIRECTORY_NAMES = new Set(['.git', 'node_modules']);
76597
+ /**
76598
+ * Reads repository books and indexes their first-line names for CLI reference resolution.
76599
+ *
76600
+ * @private internal utility of CLI agent resolution
76601
+ */
76602
+ class LocalAgentBookCollection {
76603
+ /** Creates a collection rooted at the primary book's directory. */
76604
+ constructor(agentDirectoryPath, currentWorkingDirectory) {
76605
+ this.agentDirectoryPath = agentDirectoryPath;
76606
+ this.currentWorkingDirectory = currentWorkingDirectory;
76607
+ this.booksByUrl = new Map();
76608
+ this.booksByName = new Map();
76609
+ /** Files initialized by this collection, for the coder's normal commit handling. */
76610
+ this.createdAgentBookPaths = [];
76611
+ }
76612
+ /** Discovers nested books, including the hidden `.core` directory. */
76613
+ async initialize() {
76614
+ await this.readDirectory(this.agentDirectoryPath);
76615
+ }
76616
+ /** Identifies the default ancestor without creating an unused book for FROM null/void. */
76617
+ getAdamAgentUrl() {
76618
+ var _a;
76619
+ (_a = this.adamAgentUrl) !== null && _a !== void 0 ? _a : (this.adamAgentUrl = this.booksByName.has(normalizeAgentName('Adam'))
76620
+ ? this.findByName('Adam').url
76621
+ : createLocalAgentBookUrl(join(this.agentDirectoryPath, ADAM_AGENT_BOOK_RELATIVE_PATH)));
76622
+ return this.adamAgentUrl;
76623
+ }
76624
+ /** Resolves a unique first-line name, refusing ambiguous matches. */
76625
+ findByName(name) {
76626
+ const matches = this.booksByName.get(normalizeAgentName(name)) || [];
76627
+ if (matches.length === 1) {
76628
+ return matches[0];
76629
+ }
76630
+ if (matches.length > 1) {
76631
+ throw new ParseError(spaceTrim$1((block) => `
76632
+ Agent reference \`${name}\` is ambiguous. Use an explicit book path.
76633
+
76634
+ Matching books:
76635
+ ${block(matches.map((book) => `- \`${book.filePath || book.url}\``).join('\n'))}
76636
+ `));
76637
+ }
76638
+ throw new NotFoundError(spaceTrim$1(`
76639
+ Agent \`${name}\` was not found in \`${this.agentDirectoryPath}\` or its subdirectories.
76640
+
76641
+ References use the agent's **first-line name**. You can also use an explicit \`.book\` path or URL.
76642
+ `));
76643
+ }
76644
+ /** Resolves a name, a path relative to its declaring book, or a path relative to the CLI cwd. */
76645
+ async resolveReference(reference, declaringBook) {
76646
+ const isRemoteReference = Boolean(isValidAgentUrl(reference));
76647
+ if (isRemoteReference) {
76648
+ return reference;
76649
+ }
76650
+ const isBookPath = /\.book$/i.test(reference) || isAbsolute(reference);
76651
+ if (!isBookPath) {
76652
+ if (normalizeAgentName(reference) === normalizeAgentName('Adam')) {
76653
+ return this.getAdamAgentUrl();
76654
+ }
76655
+ return this.findByName(reference).url;
76656
+ }
76657
+ const isRelativeToBook = /^\.{1,2}[\\/]/.test(reference);
76658
+ if (!declaringBook.filePath && isRelativeToBook) {
76659
+ return new URL(reference.replaceAll('\\', '/'), declaringBook.url).href;
76660
+ }
76661
+ const directoryPath = isRelativeToBook && declaringBook.filePath ? dirname(declaringBook.filePath) : this.currentWorkingDirectory;
76662
+ return (await this.readBook(resolve(directoryPath, reference))).url;
76663
+ }
76664
+ /** Reads a local book once, canonicalizing symlinks so aliases cannot bypass cycle detection. */
76665
+ async readBook(filePath) {
76666
+ try {
76667
+ const canonicalFilePath = await realpath(filePath);
76668
+ const url = createLocalAgentBookUrl(canonicalFilePath);
76669
+ const existingBook = this.booksByUrl.get(url);
76670
+ if (existingBook) {
76671
+ return existingBook;
76672
+ }
76673
+ return this.registerBook(url, await readFile(canonicalFilePath, 'utf-8'), canonicalFilePath);
76674
+ }
76675
+ catch (error) {
76676
+ if (['ENOENT', 'EISDIR', 'ENOTDIR'].includes(error.code || '')) {
76677
+ throw new NotFoundError(spaceTrim$1(`Agent book \`${filePath}\` was not found or is not a file.`));
76678
+ }
76679
+ throw error;
76680
+ }
76681
+ }
76682
+ /** Loads a referenced remote book, while keeping local identifiers entirely inside the collection. */
76683
+ async getBook(url) {
76684
+ var _a;
76685
+ const existingBook = this.booksByUrl.get(url);
76686
+ if (existingBook) {
76687
+ return existingBook;
76688
+ }
76689
+ if (url === this.getAdamAgentUrl()) {
76690
+ const adamBookPath = join(this.agentDirectoryPath, ADAM_AGENT_BOOK_RELATIVE_PATH);
76691
+ if ((await ensureAdamAgentBook(this.agentDirectoryPath)) === 'created') {
76692
+ this.createdAgentBookPaths.push(adamBookPath);
76693
+ }
76694
+ const adamBook = await this.readBook(adamBookPath);
76695
+ this.booksByUrl.set(url, adamBook);
76696
+ return adamBook;
76697
+ }
76698
+ if (url.startsWith(LOCAL_AGENT_URL_PREFIX)) {
76699
+ throw new NotFoundError(spaceTrim$1(`Local agent book \`${url}\` is not registered.`));
76700
+ }
76701
+ const bookUrl = new URL(url);
76702
+ if (!/\.(book|md)$/i.test(bookUrl.pathname) && !bookUrl.pathname.endsWith('/api/book')) {
76703
+ bookUrl.pathname = bookUrl.pathname.replace(/\/$/, '') + '/api/book';
76704
+ }
76705
+ const response = await fetch(bookUrl.href);
76706
+ if (!response.ok) {
76707
+ throw new NotFoundError(spaceTrim$1(`Cannot load agent book \`${url}\`: **${response.status} ${response.statusText}**.`));
76708
+ }
76709
+ let source;
76710
+ if ((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('application/json')) {
76711
+ const payload = await response.json();
76712
+ source = typeof payload === 'string' ? payload : payload === null || payload === void 0 ? void 0 : payload.source;
76713
+ }
76714
+ else {
76715
+ source = await response.text();
76716
+ }
76717
+ if (typeof source !== 'string') {
76718
+ throw new ParseError(spaceTrim$1(`Agent book \`${url}\` did not return a text source.`));
76719
+ }
76720
+ return this.registerBook(url, source);
76721
+ }
76722
+ /** Adds a book under its normalized first-line name and preserves the display name for TEAM. */
76723
+ registerBook(url, source, filePath) {
76724
+ const agentSource = source;
76725
+ const parsedSource = parseAgentSource(agentSource);
76726
+ const book = {
76727
+ url,
76728
+ filePath,
76729
+ source: agentSource,
76730
+ profile: {
76731
+ agentName: parsedSource.meta.fullname || parsedSource.agentName,
76732
+ personaDescription: parsedSource.personaDescription,
76733
+ },
76734
+ };
76735
+ this.booksByUrl.set(url, book);
76736
+ // Remote titles must not shadow repository-local names.
76737
+ if (filePath) {
76738
+ const agentName = normalizeAgentName(parsedSource.agentName);
76739
+ this.booksByName.set(agentName, [...(this.booksByName.get(agentName) || []), book]);
76740
+ }
76741
+ return book;
76742
+ }
76743
+ /** Walks real directories in deterministic order without following directory symlink loops. */
76744
+ async readDirectory(directoryPath) {
76745
+ const entries = await readdir(directoryPath, { withFileTypes: true });
76746
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
76747
+ const entryPath = join(directoryPath, entry.name);
76748
+ if (entry.isDirectory() && !IGNORED_DIRECTORY_NAMES.has(entry.name)) {
76749
+ await this.readDirectory(entryPath);
76750
+ }
76751
+ else if (entry.isFile() && entry.name.toLowerCase().endsWith('.book')) {
76752
+ await this.readBook(entryPath);
76753
+ }
76754
+ }
76755
+ }
76756
+ }
76757
+ /** Creates an internal URL for a local path without requiring that the file already exists. */
76758
+ function createLocalAgentBookUrl(filePath) {
76759
+ return LOCAL_AGENT_URL_PREFIX + encodeURIComponent(pathToFileURL(filePath).href);
76760
+ }
76761
+
76762
+ /**
76763
+ * Applies the shared Agent Server inheritance rules to a repository's local and remote books.
76764
+ *
76765
+ * @private internal utility of CLI agent resolution
76766
+ */
76767
+ async function resolveLocalAgentSource(agentBookPath, currentWorkingDirectory) {
76768
+ const collection = new LocalAgentBookCollection(dirname(agentBookPath), currentWorkingDirectory);
76769
+ await collection.initialize();
76770
+ const primaryBook = await collection.readBook(agentBookPath);
76771
+ const adamAgentUrl = collection.getAdamAgentUrl();
76772
+ /** Resolves each dependency with its own location and the current inheritance stack. */
76773
+ async function resolveBook(book, inheritancePath = []) {
76774
+ if (inheritancePath.length > DEFAULT_MAX_RECURSION) {
76775
+ throw new ParseError(spaceTrim$1(`Agent inheritance exceeds **${DEFAULT_MAX_RECURSION}** levels at \`${book.filePath || book.url}\`.`));
76776
+ }
76777
+ const agentReferenceResolver = createLocalAgentReferenceResolver(collection, book);
76778
+ const source = await resolveBookCommitmentReferences(book.source, agentReferenceResolver);
76779
+ return resolveInheritedAgentSource(source, {
76780
+ adamAgentUrl,
76781
+ currentAgentUrl: book.url,
76782
+ inheritancePath,
76783
+ agentSourceImporter: async (url, context) => resolveBook(await collection.getBook(url), context.importAgentOptions.inheritancePath),
76784
+ });
76785
+ }
76786
+ return {
76787
+ agentSource: await resolveBook(primaryBook),
76788
+ agentReferenceResolver: {
76789
+ ...createLocalAgentReferenceResolver(collection, primaryBook),
76790
+ resolveTeammateProfile: async (url) => {
76791
+ if (isPseudoAgentUrl(url)) {
76792
+ return null;
76793
+ }
76794
+ const book = await collection.getBook(url);
76795
+ const parsedSource = parseAgentSource(await resolveBook(book));
76796
+ return {
76797
+ agentName: book.profile.agentName,
76798
+ personaDescription: parsedSource.personaDescription,
76799
+ };
76800
+ },
76801
+ },
76802
+ createdAgentBookPaths: collection.createdAgentBookPaths,
76803
+ };
76804
+ }
76805
+ /**
76806
+ * Resolves reference blocks before embedding dependencies so inherited TEAM paths keep their original directory.
76807
+ * Parser-provided locations keep examples, unknown commitments and ordinary prose untouched.
76808
+ */
76809
+ async function resolveBookCommitmentReferences(source, resolver) {
76810
+ var _a;
76811
+ const parsedSource = parseAgentSourceWithCommitments(source);
76812
+ const lines = source.split(/\r?\n/);
76813
+ const commitmentStartLines = [...parsedSource.commitments, ...(parsedSource.unknownCommitments || [])]
76814
+ .map((commitment) => commitment.lineNumber - 1)
76815
+ .sort((left, right) => left - right);
76816
+ const effectiveFrom = getEffectiveExplicitFromCommitment(source);
76817
+ for (const commitment of [...parsedSource.commitments].reverse()) {
76818
+ if (!['FROM', 'IMPORT', 'IMPORTS', 'TEAM'].includes(commitment.type)) {
76819
+ continue;
76820
+ }
76821
+ const startLine = commitment.lineNumber - 1;
76822
+ if (commitment.type === 'FROM' && (effectiveFrom === null || effectiveFrom === void 0 ? void 0 : effectiveFrom.lineIndex) !== startLine) {
76823
+ continue;
76824
+ }
76825
+ let endLine = (_a = commitmentStartLines.find((lineNumber) => lineNumber > startLine)) !== null && _a !== void 0 ? _a : lines.length;
76826
+ // A horizontal rule also terminates a commitment before the next registered keyword.
76827
+ for (let lineIndex = startLine + 1; lineIndex < endLine; lineIndex++) {
76828
+ if (/^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/.test(lines[lineIndex] || '')) {
76829
+ endLine = lineIndex;
76830
+ break;
76831
+ }
76832
+ }
76833
+ const content = await resolver.resolveCommitmentContent(commitment.type, commitment.content);
76834
+ const commitmentType = commitment.type === 'IMPORTS' ? 'IMPORT' : commitment.type;
76835
+ const replacement = content.trim()
76836
+ ? `${commitmentType} ${content}\n`
76837
+ : commitmentType === 'FROM'
76838
+ ? 'FROM @Null\n'
76839
+ : '';
76840
+ lines.splice(startLine, endLine - startLine, ...replacement.split('\n'));
76841
+ }
76842
+ return lines.join('\n');
76843
+ }
76844
+
75592
76845
  /**
75593
76846
  * Reads an optional agent `.book` file and prepares the references that route prompts to it.
75594
76847
  *
@@ -75610,13 +76863,16 @@ async function resolveCoderAgentBook(agentBookReference, currentWorkingDirectory
75610
76863
  }
75611
76864
  throw error;
75612
76865
  }));
76866
+ // Note: Parsed once here, because both the display name of the agent and its routing references need it
76867
+ const parsedAgentSource = parseAgentSource(agentSource);
75613
76868
  return {
75614
76869
  agentSource,
76870
+ agentName: parsedAgentSource.meta.fullname || parsedAgentSource.agentName,
75615
76871
  agentReferences: createCoderAgentReferences({
75616
76872
  agentBookReference: normalizedAgentBookReference,
75617
76873
  resolvedAgentBookPath,
75618
76874
  currentWorkingDirectory,
75619
- agentSource,
76875
+ normalizedAgentNameFromBook: parsedAgentSource.agentName,
75620
76876
  }),
75621
76877
  };
75622
76878
  }
@@ -75630,9 +76886,14 @@ async function resolveCoderAgent(agentBookReference, currentWorkingDirectory) {
75630
76886
  if (resolvedAgentBook === undefined) {
75631
76887
  return undefined;
75632
76888
  }
76889
+ const resolvedSource = await resolveLocalAgentSource(resolve(currentWorkingDirectory, agentBookReference.trim()), currentWorkingDirectory);
75633
76890
  return {
75634
76891
  ...resolvedAgentBook,
75635
- systemMessage: await createAgentRunnerSystemMessage(resolvedAgentBook.agentSource),
76892
+ agentSource: resolvedSource.agentSource,
76893
+ systemMessage: await createAgentRunnerSystemMessage(resolvedSource.agentSource, {
76894
+ agentReferenceResolver: resolvedSource.agentReferenceResolver,
76895
+ }),
76896
+ createdAgentBookPaths: resolvedSource.createdAgentBookPaths,
75636
76897
  };
75637
76898
  }
75638
76899
  /**
@@ -75644,13 +76905,12 @@ function createCoderAgentReferences(options) {
75644
76905
  const relativeAgentBookPath = relative(options.currentWorkingDirectory, options.resolvedAgentBookPath).replaceAll('\\', '/');
75645
76906
  const agentBookFileName = basename(options.resolvedAgentBookPath);
75646
76907
  const agentBookName = basename(options.resolvedAgentBookPath, extname(options.resolvedAgentBookPath));
75647
- const agentNameFromBook = parseAgentSource(options.agentSource).agentName;
75648
76908
  return Array.from(new Set([
75649
76909
  options.agentBookReference,
75650
76910
  relativeAgentBookPath,
75651
76911
  agentBookFileName,
75652
76912
  agentBookName,
75653
- agentNameFromBook,
76913
+ options.normalizedAgentNameFromBook,
75654
76914
  ].filter((agentReference) => agentReference.trim() !== '')));
75655
76915
  }
75656
76916
 
@@ -75913,11 +77173,27 @@ function formatUsagePrice(usage) {
75913
77173
 
75914
77174
  /**
75915
77175
  * Formats runner details for prompt status lines.
77176
+ *
77177
+ * Produces `` OpenAI Codex `gpt-5.6-luna` thinking `max` `` for a plain run and
77178
+ * `` Developer on OpenAI Codex `gpt-5.6-luna` thinking `max` `` for a run personalized with `--agent`.
75916
77179
  */
75917
- function formatRunnerSignature(runnerName, modelName, thinkingLevel) {
75918
- const normalizedRunner = runnerName === null || runnerName === void 0 ? void 0 : runnerName.trim();
75919
- const normalizedModel = modelName === null || modelName === void 0 ? void 0 : modelName.trim();
75920
- const thinkingLevelSuffix = thinkingLevel ? ` thinking \`${thinkingLevel}\`` : '';
77180
+ function formatRunnerSignature(options) {
77181
+ var _a;
77182
+ const normalizedAgentName = (_a = options.agentName) === null || _a === void 0 ? void 0 : _a.trim();
77183
+ const harnessSignature = formatHarnessSignature(options);
77184
+ if (!normalizedAgentName) {
77185
+ return harnessSignature;
77186
+ }
77187
+ return `${normalizedAgentName} on ${harnessSignature}`;
77188
+ }
77189
+ /**
77190
+ * Formats the harness part of one runner signature, which names the harness, its model and its thinking level.
77191
+ */
77192
+ function formatHarnessSignature(options) {
77193
+ var _a, _b;
77194
+ const normalizedRunner = (_a = options.runnerName) === null || _a === void 0 ? void 0 : _a.trim();
77195
+ const normalizedModel = (_b = options.modelName) === null || _b === void 0 ? void 0 : _b.trim();
77196
+ const thinkingLevelSuffix = options.thinkingLevel ? ` thinking \`${options.thinkingLevel}\`` : '';
75921
77197
  if (!normalizedRunner && !normalizedModel) {
75922
77198
  return 'unknown';
75923
77199
  }
@@ -75932,7 +77208,12 @@ function formatRunnerSignature(runnerName, modelName, thinkingLevel) {
75932
77208
  * Prints the compact summary of one finished `ptbk coder ping`.
75933
77209
  */
75934
77210
  function printCoderPingResult(result) {
75935
- const runnerSignature = formatRunnerSignature(result.runnerName, result.modelName, result.thinkingLevel);
77211
+ // Note: `ptbk coder ping` measures one harness alone, it never runs the prompts of a Book agent
77212
+ const runnerSignature = formatRunnerSignature({
77213
+ runnerName: result.runnerName,
77214
+ modelName: result.modelName,
77215
+ thinkingLevel: result.thinkingLevel,
77216
+ });
75936
77217
  const loginMethodLabel = formatCodexLoginMethod(result.loginMethod);
75937
77218
  const loginMethodSuffix = loginMethodLabel === undefined ? '' : ` (${loginMethodLabel})`;
75938
77219
  console.info(colors.green(`🏓 ${runnerSignature}${loginMethodSuffix} answered in ${formatCoderPingResponseTime(result.durationMs)}`));
@@ -76733,6 +78014,23 @@ function listenForCoderRunControls() {
76733
78014
  });
76734
78015
  }
76735
78016
 
78017
+ /**
78018
+ * Commits newly initialized, non-ignored agent books before the coder's clean-tree checkpoint.
78019
+ * Only books created by this run are eligible; existing books and unrelated changes remain outside the commit.
78020
+ */
78021
+ async function commitInitializedAgentBooks(projectPath, createdAgentBookPaths) {
78022
+ if (createdAgentBookPaths.length === 0) {
78023
+ return;
78024
+ }
78025
+ const createdPaths = new Set(createdAgentBookPaths.map((filePath) => resolve(filePath)));
78026
+ const changedPaths = await listWorkingTreeChangedFiles(projectPath);
78027
+ const relevantPaths = changedPaths.filter((filePath) => createdPaths.has(resolve(projectPath, filePath)));
78028
+ if (relevantPaths.length === 0) {
78029
+ return;
78030
+ }
78031
+ await commitChanges('Initialize Adam agent', { projectPath, relevantPaths });
78032
+ }
78033
+
76736
78034
  /**
76737
78035
  * Checks working tree clean.
76738
78036
  */
@@ -78420,11 +79718,11 @@ function toPromptRunnerAttribution(runnerSignatures) {
78420
79718
  * Builds the shared body of a prompt status line, used by both the in-progress `[^]` and the done `[x]` status.
78421
79719
  *
78422
79720
  * Produces details such as
78423
- * ``by OpenAI Codex `gpt-5.6-luna` thinking `max` (ChatGPT account) - Implementation ~$0.2036 10 minutes``.
79721
+ * ``by Developer on OpenAI Codex `gpt-5.6-luna` thinking `max` (ChatGPT account) - Implementation ~$0.2036 10 minutes``.
78424
79722
  */
78425
79723
  function buildPromptStatusDetails(options) {
78426
- const { steps, inProgressStepKind, runnerName, modelName, previousRunnerSignatures, attemptCount, loginMethod, thinkingLevel, } = options;
78427
- const runnerSignature = formatRunnerSignature(runnerName, modelName, thinkingLevel);
79724
+ const { steps, inProgressStepKind, previousRunnerSignatures, attemptCount, loginMethod } = options;
79725
+ const runnerSignature = formatRunnerSignature(options);
78428
79726
  const attemptMetadata = formatPromptAttemptMetadata('done', attemptCount);
78429
79727
  const loginMethodLabel = formatCodexLoginMethod(loginMethod);
78430
79728
  const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
@@ -78536,9 +79834,9 @@ function markPromptDone(options) {
78536
79834
  * Marks a prompt section as failed and records runner details.
78537
79835
  */
78538
79836
  function markPromptFailed(options) {
78539
- const { file, section, runnerName, modelName, previousRunnerSignatures, promptExecutionStartedDate, attemptCount } = options;
79837
+ const { file, section, previousRunnerSignatures, promptExecutionStartedDate, attemptCount } = options;
78540
79838
  const attribution = formatPromptRunnerAttribution({
78541
- currentRunnerSignature: formatRunnerSignature(runnerName, modelName),
79839
+ currentRunnerSignature: formatRunnerSignature(options),
78542
79840
  previousRunnerSignatures,
78543
79841
  });
78544
79842
  const attemptMetadata = formatPromptAttemptMetadata('failed', attemptCount);
@@ -78624,9 +79922,9 @@ function getSafeCodeBlock(content, language = 'markdown') {
78624
79922
  /**
78625
79923
  * Renders the markdown run trace of one finished or failed prompt round.
78626
79924
  *
78627
- * The trace pairs the metadata of the round - which harness, model and thinking level ran it, how long each of
78628
- * its steps took and what it cost - with the untouched runtime log, so a finished round can still be analyzed
78629
- * after its temporary artifacts are cleaned up.
79925
+ * The trace pairs the metadata of the round - which agent, harness, model and thinking level ran it, how long
79926
+ * each of its steps took and what it cost - with the untouched runtime log, so a finished round can still be
79927
+ * analyzed after its temporary artifacts are cleaned up.
78630
79928
  */
78631
79929
  function buildPromptRunTraceContent(options) {
78632
79930
  const sections = [
@@ -78641,7 +79939,7 @@ function buildPromptRunTraceContent(options) {
78641
79939
  */
78642
79940
  function buildPromptRunTraceSummarySection(options) {
78643
79941
  const { file, section, outcome } = options;
78644
- const runnerSignature = formatRunnerSignature(options.runnerName, options.modelName, options.thinkingLevel);
79942
+ const runnerSignature = formatRunnerSignature(options);
78645
79943
  const loginMethodLabel = outcome.kind === 'succeeded' ? formatCodexLoginMethod(outcome.loginMethod) : undefined;
78646
79944
  const loginMethodSuffix = loginMethodLabel ? ` (${loginMethodLabel})` : '';
78647
79945
  const detailLines = [
@@ -78951,8 +80249,7 @@ async function recordPromptRoundInProgress(options) {
78951
80249
  section: nextPrompt.section,
78952
80250
  steps: progress.finishedSteps,
78953
80251
  inProgressStepKind: progress.startedStepKind,
78954
- runnerName: runnerMetadata.runnerName,
78955
- modelName: runnerMetadata.modelName,
80252
+ ...runnerMetadata,
78956
80253
  previousRunnerSignatures,
78957
80254
  attemptCount,
78958
80255
  loginMethod: progress.loginMethod,
@@ -79022,8 +80319,7 @@ async function finalizeSuccessfulPromptRound(options) {
79022
80319
  file: nextPrompt.file,
79023
80320
  section: nextPrompt.section,
79024
80321
  steps: result.steps,
79025
- runnerName: runnerMetadata.runnerName,
79026
- modelName: runnerMetadata.modelName,
80322
+ ...runnerMetadata,
79027
80323
  previousRunnerSignatures,
79028
80324
  attemptCount: result.attemptCount,
79029
80325
  loginMethod: result.loginMethod,
@@ -79102,8 +80398,7 @@ async function finalizeFailedPromptRound(options) {
79102
80398
  markPromptFailed({
79103
80399
  file: nextPrompt.file,
79104
80400
  section: nextPrompt.section,
79105
- runnerName: runnerMetadata.runnerName,
79106
- modelName: runnerMetadata.modelName,
80401
+ ...runnerMetadata,
79107
80402
  previousRunnerSignatures,
79108
80403
  promptExecutionStartedDate,
79109
80404
  attemptCount,
@@ -79133,16 +80428,15 @@ async function finalizeFailedPromptRound(options) {
79133
80428
  * Persists the run trace of one prompt round, so the round can still be analyzed after its temporary
79134
80429
  * artifacts are gone.
79135
80430
  *
79136
- * This is the single place where both the successful and the failed round record what they did, which harness,
79137
- * model and thinking level did it and everything that harness has written while doing it.
80431
+ * This is the single place where both the successful and the failed round record what they did, which agent,
80432
+ * harness, model and thinking level did it and everything that harness has written while doing it.
79138
80433
  */
79139
80434
  async function recordPromptRoundTrace(options) {
79140
80435
  const { options: runOptions, nextPrompt, runnerMetadata, promptExecutionStartedDate, attemptCount, logPath, outcome, } = options;
79141
80436
  await writePromptRunTrace({
79142
80437
  file: nextPrompt.file,
79143
80438
  section: nextPrompt.section,
79144
- runnerName: runnerMetadata.runnerName,
79145
- modelName: runnerMetadata.modelName,
80439
+ ...runnerMetadata,
79146
80440
  thinkingLevel: runOptions.thinkingLevel,
79147
80441
  testCommand: runOptions.testCommand,
79148
80442
  attemptCount,
@@ -80106,7 +81400,16 @@ async function runCodexPrompts(providedOptions) {
80106
81400
  if (await runDryRunIfRequested(options)) {
80107
81401
  return;
80108
81402
  }
80109
- const { runner, actualRunnerModel, runnerMetadata } = resolvePromptRunner(options);
81403
+ if (!options.noCommit && resolvedCoderAgent) {
81404
+ await commitInitializedAgentBooks(process.cwd(), resolvedCoderAgent.createdAgentBookPaths);
81405
+ }
81406
+ const { runner, actualRunnerModel, runnerMetadata: harnessRunnerMetadata } = resolvePromptRunner(options);
81407
+ // Note: The harness only knows itself, so the Book agent it runs as is joined here - this is the single
81408
+ // place where the whole run report of prompt status lines and run traces is put together
81409
+ const runnerMetadata = {
81410
+ ...harnessRunnerMetadata,
81411
+ agentName: resolvedCoderAgent === null || resolvedCoderAgent === void 0 ? void 0 : resolvedCoderAgent.agentName,
81412
+ };
80110
81413
  const promptRunnerIdentity = {
80111
81414
  harnessName: options.agentName,
80112
81415
  modelName: actualRunnerModel,