@promptbook/cli 0.114.0-38 → 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 (57) hide show
  1. package/agents/default/.core/adam.book +33 -0
  2. package/apps/agents-server/src/utils/agentReferenceResolver/AgentReferenceResolutionIssue.ts +1 -69
  3. package/apps/agents-server/src/utils/agentReferenceResolver/extractAgentReferenceTokens.ts +1 -61
  4. package/apps/agents-server/src/utils/explicitFromCommitment.ts +1 -130
  5. package/apps/agents-server/src/utils/resolveInheritedAgentSource.ts +32 -817
  6. package/esm/index.es.js +1069 -31
  7. package/esm/index.es.js.map +1 -1
  8. package/esm/scripts/run-agent-messages/messages/createAgentRunnerSystemMessage.d.ts +2 -1
  9. package/esm/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +4 -0
  10. package/esm/scripts/run-codex-prompts/git/commitInitializedAgentBooks.d.ts +5 -0
  11. package/esm/src/book-2.0/agent-source/AgentReferenceResolutionIssue.d.ts +45 -0
  12. package/esm/src/book-2.0/agent-source/explicitFromCommitment.d.ts +62 -0
  13. package/esm/src/book-2.0/agent-source/extractAgentReferenceTokens.d.ts +32 -0
  14. package/esm/src/book-2.0/agent-source/resolveInheritedAgentSource.d.ts +73 -0
  15. package/esm/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +1 -0
  16. package/esm/src/cli/cli-commands/common/LocalAgentBookCollection.d.ts +46 -0
  17. package/esm/src/cli/cli-commands/common/createLocalAgentReferenceResolver.d.ts +8 -0
  18. package/esm/src/cli/cli-commands/common/ensureAdamAgentBook.d.ts +12 -0
  19. package/esm/src/cli/cli-commands/common/resolveBundledAgentBookPath.d.ts +6 -0
  20. package/esm/src/cli/cli-commands/common/resolveLocalAgentSource.d.ts +19 -0
  21. package/esm/src/version.d.ts +1 -1
  22. package/package.json +1 -1
  23. package/src/book-2.0/agent-source/AgentReferenceResolutionIssue.ts +75 -0
  24. package/src/book-2.0/agent-source/explicitFromCommitment.ts +132 -0
  25. package/src/book-2.0/agent-source/extractAgentReferenceTokens.ts +65 -0
  26. package/src/book-2.0/agent-source/resolveInheritedAgentSource.ts +841 -0
  27. package/src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.ts +2 -28
  28. package/src/cli/cli-commands/coder/init.ts +3 -1
  29. package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +5 -0
  30. package/src/cli/cli-commands/coder/list.ts +5 -3
  31. package/src/cli/cli-commands/coder/printInitializationSummary.ts +6 -0
  32. package/src/cli/cli-commands/coder/run.ts +4 -1
  33. package/src/cli/cli-commands/coder/server.ts +2 -1
  34. package/src/cli/cli-commands/common/LocalAgentBookCollection.ts +220 -0
  35. package/src/cli/cli-commands/common/createLocalAgentReferenceResolver.ts +62 -0
  36. package/src/cli/cli-commands/common/ensureAdamAgentBook.ts +40 -0
  37. package/src/cli/cli-commands/common/resolveBundledAgentBookPath.ts +38 -0
  38. package/src/cli/cli-commands/common/resolveLocalAgentSource.ts +127 -0
  39. package/src/other/templates/getTemplatesPipelineCollection.ts +650 -928
  40. package/src/version.ts +2 -2
  41. package/src/versions.txt +1 -0
  42. package/umd/index.umd.js +1069 -31
  43. package/umd/index.umd.js.map +1 -1
  44. package/umd/scripts/run-agent-messages/messages/createAgentRunnerSystemMessage.d.ts +2 -1
  45. package/umd/scripts/run-codex-prompts/common/resolveCoderAgent.d.ts +4 -0
  46. package/umd/scripts/run-codex-prompts/git/commitInitializedAgentBooks.d.ts +5 -0
  47. package/umd/src/book-2.0/agent-source/AgentReferenceResolutionIssue.d.ts +45 -0
  48. package/umd/src/book-2.0/agent-source/explicitFromCommitment.d.ts +62 -0
  49. package/umd/src/book-2.0/agent-source/extractAgentReferenceTokens.d.ts +32 -0
  50. package/umd/src/book-2.0/agent-source/resolveInheritedAgentSource.d.ts +73 -0
  51. package/umd/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +1 -0
  52. package/umd/src/cli/cli-commands/common/LocalAgentBookCollection.d.ts +46 -0
  53. package/umd/src/cli/cli-commands/common/createLocalAgentReferenceResolver.d.ts +8 -0
  54. package/umd/src/cli/cli-commands/common/ensureAdamAgentBook.d.ts +12 -0
  55. package/umd/src/cli/cli-commands/common/resolveBundledAgentBookPath.d.ts +6 -0
  56. package/umd/src/cli/cli-commands/common/resolveLocalAgentSource.d.ts +19 -0
  57. 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,7 +48,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
48
48
  * @generated
49
49
  * @see https://github.com/webgptorg/promptbook
50
50
  */
51
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-38';
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
@@ -1319,6 +1319,12 @@ const DEFAULT_INTERMEDIATE_FILES_STRATEGY = 'HIDE_AND_KEEP';
1319
1319
  * @public exported from `@promptbook/core`
1320
1320
  */
1321
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;
1322
1328
  /**
1323
1329
  * The maximum number of attempts to execute LLM task before giving up
1324
1330
  *
@@ -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
  /**
@@ -39682,8 +39734,8 @@ function extractMcpServers(agentSource) {
39682
39734
  /**
39683
39735
  * Compiles agent source into the behavior text passed to CLI harness runners.
39684
39736
  */
39685
- async function createAgentRunnerSystemMessage(agentSource) {
39686
- return formatAgentModelRequirementsForRunner(await createAgentModelRequirements(agentSource));
39737
+ async function createAgentRunnerSystemMessage(agentSource, options) {
39738
+ return formatAgentModelRequirementsForRunner(await createAgentModelRequirements(agentSource, undefined, undefined, undefined, options));
39687
39739
  }
39688
39740
  /**
39689
39741
  * Formats prepared model requirements for a text-only CLI harness prompt.
@@ -43679,6 +43731,59 @@ function buildPromptSlug$1(templateSlugPrefix, title) {
43679
43731
  // Note: [🟡] Code for CLI command [generate-boilerplates](src/cli/cli-commands/coder/generate-boilerplates.ts) should never be published outside of `@promptbook/cli`
43680
43732
  // Note: [💞] Ignore a discrepancy between file name and entity name
43681
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
+
43682
43787
  /**
43683
43788
  * Asks the user in the terminal to confirm one optional change.
43684
43789
  *
@@ -44482,29 +44587,9 @@ async function ensureCoderDeveloperAgentFile(projectPath) {
44482
44587
  if (await isExistingFile$2(absoluteFilePath)) {
44483
44588
  return 'unchanged';
44484
44589
  }
44485
- await copyFile(await resolveDefaultCoderDeveloperAgentFilePath(), absoluteFilePath);
44590
+ await copyFile(await resolveBundledAgentBookPath(DEFAULT_CODER_DEVELOPER_AGENT_SOURCE_FILE_PATH), absoluteFilePath);
44486
44591
  return 'created';
44487
44592
  }
44488
- /**
44489
- * Resolves the bundled developer agent from a source checkout or generated CLI package.
44490
- */
44491
- async function resolveDefaultCoderDeveloperAgentFilePath() {
44492
- const candidates = [
44493
- join(__dirname, '..', DEFAULT_CODER_DEVELOPER_AGENT_SOURCE_FILE_PATH),
44494
- join(__dirname, '..', '..', '..', '..', DEFAULT_CODER_DEVELOPER_AGENT_SOURCE_FILE_PATH),
44495
- ];
44496
- for (const candidate of candidates) {
44497
- if (await isExistingFile$2(candidate)) {
44498
- return candidate;
44499
- }
44500
- }
44501
- throw new NotAllowed(spaceTrim$1(`
44502
- Cannot find the bundled Promptbook developer agent.
44503
-
44504
- Checked:
44505
- ${candidates.map((candidate) => `- \`${candidate}\``).join('\n')}
44506
- `));
44507
- }
44508
44593
  /**
44509
44594
  * Checks whether a path exists and is a file.
44510
44595
  */
@@ -44987,6 +45072,7 @@ async function initializeCoderProjectConfiguration(projectPath) {
44987
45072
  const promptsDoneDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_DONE_DIRECTORY_PATH);
44988
45073
  const promptsTemplatesDirectoryStatus = await ensureDirectory(projectPath, PROMPTS_TEMPLATES_DIRECTORY_PATH);
44989
45074
  const agentsDirectoryStatus = await ensureDirectory(projectPath, CODER_AGENTS_DIRECTORY_PATH);
45075
+ const adamAgentFileStatus = await ensureAdamAgentBook(join(projectPath, CODER_AGENTS_DIRECTORY_PATH));
44990
45076
  const { envFileStatus, initializedEnvVariableNames } = await ensureCoderEnvFile(projectPath);
44991
45077
  const gitignoreFileStatus = await ensureCoderGitignoreFile(projectPath);
44992
45078
  const { status: packageJsonFileStatus, addedEntryKeys: addedPackageJsonScriptNames } = await ensureCoderPackageJsonFile(projectPath);
@@ -44997,6 +45083,7 @@ async function initializeCoderProjectConfiguration(projectPath) {
44997
45083
  promptsDoneDirectoryStatus,
44998
45084
  promptsTemplatesDirectoryStatus,
44999
45085
  agentsDirectoryStatus,
45086
+ adamAgentFileStatus,
45000
45087
  envFileStatus,
45001
45088
  gitignoreFileStatus,
45002
45089
  packageJsonFileStatus,
@@ -45019,6 +45106,7 @@ function printInitializationSummary(summary) {
45019
45106
  printInitializationStatusLine('prompts/done/', summary.promptsDoneDirectoryStatus);
45020
45107
  printInitializationStatusLine('prompts/templates/', summary.promptsTemplatesDirectoryStatus);
45021
45108
  printInitializationStatusLine('agents/', summary.agentsDirectoryStatus);
45109
+ printInitializationStatusLine(`${CODER_AGENTS_DIRECTORY_PATH}/${ADAM_AGENT_BOOK_RELATIVE_PATH}`, summary.adamAgentFileStatus);
45022
45110
  printInitializationStatusLine('.env', summary.envFileStatus);
45023
45111
  printInitializationStatusLine('.gitignore', summary.gitignoreFileStatus);
45024
45112
  printInitializationStatusLine('package.json', summary.packageJsonFileStatus);
@@ -45093,6 +45181,7 @@ function $initializeCoderInitCommand(program) {
45093
45181
  - prompts/done/
45094
45182
  ${block(listDefaultCoderProjectPromptTemplateDisplayPaths())}
45095
45183
  - ${CODER_DEVELOPER_AGENT_FILE_PATH}
45184
+ - ${CODER_AGENTS_DIRECTORY_PATH}/${ADAM_AGENT_BOOK_RELATIVE_PATH}
45096
45185
  - ${AGENTS_FILE_PATH}
45097
45186
  - .gitignore with local artifacts from every supported harness
45098
45187
  - package.json
@@ -45237,8 +45326,7 @@ function $initializeCoderListCommand(program) {
45237
45326
  const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: false });
45238
45327
  const resolvedCoderAgentBook = agent === undefined
45239
45328
  ? undefined
45240
- : await (await Promise.resolve().then(function () { return resolveCoderAgent$1; }))
45241
- .resolveCoderAgentBook(agent, process.cwd());
45329
+ : await (await Promise.resolve().then(function () { return resolveCoderAgent$1; })).resolveCoderAgentBook(agent, process.cwd());
45242
45330
  const promptRunnerIdentity = runnerOptions.agentName === undefined &&
45243
45331
  runnerOptions.model === undefined &&
45244
45332
  resolvedCoderAgentBook === undefined
@@ -45995,6 +46083,8 @@ function $initializeCoderRunCommand(program) {
45995
46083
  ${block(QUESTIONS_DESCRIPTION)}
45996
46084
 
45997
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
45998
46088
  - Automatically stages and commits changes with agent identity unless --no-commit is used
45999
46089
  - Commits only the prompt file and the files the coding agent has changed, leaving unrelated changes alone
46000
46090
  - Refuses to start on a dirty working tree unless --git-changes says what should happen with the changes
@@ -46968,7 +47058,7 @@ function $provideFilesystemForNode(options) {
46968
47058
  return {
46969
47059
  stat,
46970
47060
  access,
46971
- constants,
47061
+ constants: constants$1,
46972
47062
  readFile,
46973
47063
  writeFile,
46974
47064
  readdir,
@@ -75829,6 +75919,929 @@ var findUnwrittenPrompts$1 = /*#__PURE__*/Object.freeze({
75829
75919
  findUnwrittenPrompts: findUnwrittenPrompts
75830
75920
  });
75831
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
+
75832
76845
  /**
75833
76846
  * Reads an optional agent `.book` file and prepares the references that route prompts to it.
75834
76847
  *
@@ -75873,9 +76886,14 @@ async function resolveCoderAgent(agentBookReference, currentWorkingDirectory) {
75873
76886
  if (resolvedAgentBook === undefined) {
75874
76887
  return undefined;
75875
76888
  }
76889
+ const resolvedSource = await resolveLocalAgentSource(resolve(currentWorkingDirectory, agentBookReference.trim()), currentWorkingDirectory);
75876
76890
  return {
75877
76891
  ...resolvedAgentBook,
75878
- systemMessage: await createAgentRunnerSystemMessage(resolvedAgentBook.agentSource),
76892
+ agentSource: resolvedSource.agentSource,
76893
+ systemMessage: await createAgentRunnerSystemMessage(resolvedSource.agentSource, {
76894
+ agentReferenceResolver: resolvedSource.agentReferenceResolver,
76895
+ }),
76896
+ createdAgentBookPaths: resolvedSource.createdAgentBookPaths,
75879
76897
  };
75880
76898
  }
75881
76899
  /**
@@ -76996,6 +78014,23 @@ function listenForCoderRunControls() {
76996
78014
  });
76997
78015
  }
76998
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
+
76999
78034
  /**
77000
78035
  * Checks working tree clean.
77001
78036
  */
@@ -80365,6 +81400,9 @@ async function runCodexPrompts(providedOptions) {
80365
81400
  if (await runDryRunIfRequested(options)) {
80366
81401
  return;
80367
81402
  }
81403
+ if (!options.noCommit && resolvedCoderAgent) {
81404
+ await commitInitializedAgentBooks(process.cwd(), resolvedCoderAgent.createdAgentBookPaths);
81405
+ }
80368
81406
  const { runner, actualRunnerModel, runnerMetadata: harnessRunnerMetadata } = resolvePromptRunner(options);
80369
81407
  // Note: The harness only knows itself, so the Book agent it runs as is joined here - this is the single
80370
81408
  // place where the whole run report of prompt status lines and run traces is put together