@promptbook/node 0.114.0-11 → 0.114.0-12

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 (41) hide show
  1. package/esm/index.es.js +306 -84
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/scripts/run-codex-prompts/common/runGoScript/$spawnLoggedBashScript.d.ts +25 -0
  4. package/esm/scripts/run-codex-prompts/common/runGoScript/$terminateLoggedBashProcessTree.d.ts +11 -0
  5. package/esm/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +4 -0
  6. package/esm/src/book-2.0/agent-source/AgentSourceParseResult.d.ts +5 -0
  7. package/esm/src/book-2.0/agent-source/ParsedUnknownCommitment.d.ts +26 -0
  8. package/esm/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.unknown.test.d.ts +1 -0
  9. package/esm/src/book-2.0/agent-source/getBookCommitmentLineType.d.ts +19 -0
  10. package/esm/src/book-2.0/agent-source/parseAgentSourceWithCommitments.unknown.test.d.ts +1 -0
  11. package/esm/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.d.ts +28 -0
  12. package/esm/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.test.d.ts +1 -0
  13. package/esm/src/cli/cli-commands/common/harness/$checkHarnessInstallation.d.ts +3 -3
  14. package/esm/src/cli/cli-commands/common/harness/$checkHarnessInstallation.test.d.ts +1 -0
  15. package/esm/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -3
  16. package/esm/src/cli/cli-commands/common/harness/HarnessInstallationStatus.d.ts +3 -2
  17. package/esm/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +29 -0
  18. package/esm/src/cli/cli-commands/common/harnessUpdateCliOptions.test.d.ts +1 -0
  19. package/esm/src/executables/platforms/locateAppOnWindows.d.ts +1 -1
  20. package/esm/src/executables/platforms/locateAppOnWindows.test.d.ts +1 -0
  21. package/package.json +2 -2
  22. package/umd/index.umd.js +304 -82
  23. package/umd/index.umd.js.map +1 -1
  24. package/umd/scripts/run-codex-prompts/common/runGoScript/$spawnLoggedBashScript.d.ts +25 -0
  25. package/umd/scripts/run-codex-prompts/common/runGoScript/$terminateLoggedBashProcessTree.d.ts +11 -0
  26. package/umd/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +4 -0
  27. package/umd/src/book-2.0/agent-source/AgentSourceParseResult.d.ts +5 -0
  28. package/umd/src/book-2.0/agent-source/ParsedUnknownCommitment.d.ts +26 -0
  29. package/umd/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.unknown.test.d.ts +1 -0
  30. package/umd/src/book-2.0/agent-source/getBookCommitmentLineType.d.ts +19 -0
  31. package/umd/src/book-2.0/agent-source/parseAgentSourceWithCommitments.unknown.test.d.ts +1 -0
  32. package/umd/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.d.ts +28 -0
  33. package/umd/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.test.d.ts +1 -0
  34. package/umd/src/cli/cli-commands/common/harness/$checkHarnessInstallation.d.ts +3 -3
  35. package/umd/src/cli/cli-commands/common/harness/$checkHarnessInstallation.test.d.ts +1 -0
  36. package/umd/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -3
  37. package/umd/src/cli/cli-commands/common/harness/HarnessInstallationStatus.d.ts +3 -2
  38. package/umd/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +29 -0
  39. package/umd/src/cli/cli-commands/common/harnessUpdateCliOptions.test.d.ts +1 -0
  40. package/umd/src/executables/platforms/locateAppOnWindows.d.ts +1 -1
  41. package/umd/src/executables/platforms/locateAppOnWindows.test.d.ts +1 -0
package/esm/index.es.js CHANGED
@@ -1,8 +1,8 @@
1
- import { join, dirname, basename, isAbsolute, resolve, relative } from 'path';
1
+ import { join, dirname, basename, isAbsolute, resolve, delimiter, relative } from 'path';
2
2
  import { unlink, mkdir, appendFile, writeFile, readFile, stat, access, constants, readdir, watch } from 'fs/promises';
3
3
  import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
4
  import colors from 'colors';
5
- import { spawn } from 'child_process';
5
+ import { spawn, spawnSync } from 'child_process';
6
6
  import { fileURLToPath } from 'url';
7
7
  import CryptoJS from 'crypto-js';
8
8
  import hexEncoder from 'crypto-js/enc-hex';
@@ -29,7 +29,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
29
29
  * @generated
30
30
  * @see https://github.com/webgptorg/promptbook
31
31
  */
32
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-11';
32
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-12';
33
33
  /**
34
34
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
35
35
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -2936,14 +2936,90 @@ const PTBK_CODER_LOG_FILE_ENV_NAME = 'PTBK_CODER_LOG_FILE';
2936
2936
  * without the generated script and the prompt it embeds.
2937
2937
  */
2938
2938
  const SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER = '--- raw output ---';
2939
+ /**
2940
+ * Command which terminates the Bash process tree rooted at the running harness.
2941
+ *
2942
+ * Bash's POSIX process IDs differ from Windows process IDs in Git Bash, so the Windows branch resolves the native PID
2943
+ * before delegating to `taskkill`. Unix harnesses run in a dedicated Bash job process group, which can be terminated
2944
+ * with its negative process ID.
2945
+ */
2946
+ const TERMINATE_BASH_PROCESS_TREE_COMMAND = process.platform === 'win32'
2947
+ ? spaceTrim$1(`
2948
+ HARNESS_WINDOWS_PROCESS_ID="$(ps -l -p "$HARNESS_PROCESS_ID" | awk 'NR == 2 { print $4 }')"
2949
+ if [ -n "$HARNESS_WINDOWS_PROCESS_ID" ]; then
2950
+ MSYS_NO_PATHCONV=1 taskkill.exe /PID "$HARNESS_WINDOWS_PROCESS_ID" /T /F > /dev/null 2>&1 || true
2951
+ fi
2952
+ `)
2953
+ : 'kill -TERM -- "-$HARNESS_PROCESS_ID" 2>/dev/null || true';
2954
+ /**
2955
+ * Shell condition that detects whether the Node process which owns the harness is still running.
2956
+ *
2957
+ * Git Bash translates process IDs and command switches, so querying the native Windows PID needs both `tasklist` and
2958
+ * disabled MSYS path conversion there. Unix can use the native `kill -0` process existence check.
2959
+ */
2960
+ const IS_PARENT_PROCESS_RUNNING_CONDITION = process.platform === 'win32'
2961
+ ? 'MSYS_NO_PATHCONV=1 tasklist.exe /FI "PID eq $PARENT_CODER_PROCESS_ID" /NH | awk -v processId="$PARENT_CODER_PROCESS_ID" \'$2 == processId { isFound = 1 } END { exit isFound ? 0 : 1 }\''
2962
+ : 'kill -0 "$PARENT_CODER_PROCESS_ID" 2>/dev/null';
2963
+ /**
2964
+ * Environment variable that identifies the Node process responsible for a temporary harness shell.
2965
+ */
2966
+ const PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME = 'PTBK_CODER_PARENT_PROCESS_ID';
2939
2967
  /**
2940
2968
  * Small bash wrapper that preserves stdout/stderr streams while teeing both into the runtime log file.
2969
+ *
2970
+ * A watcher polls the owning Node process by its PID. If that process exits abruptly, the watcher stops the whole
2971
+ * Bash and harness process tree instead of letting it continue as an orphan.
2941
2972
  */
2942
2973
  const LOGGED_BASH_WRAPPER_COMMAND = spaceTrim$1(`
2974
+ PARENT_CODER_PROCESS_ID="\${${PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME}}"
2975
+ unset ${PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME}
2976
+
2977
+ terminate_harness_process_tree() {
2978
+ # Keep the EXIT cleanup intact so the wrapper can also stop its parent-process watcher.
2979
+ trap - HUP INT TERM
2980
+ ${TERMINATE_BASH_PROCESS_TREE_COMMAND}
2981
+ }
2982
+
2983
+ is_parent_process_running() {
2984
+ ${IS_PARENT_PROCESS_RUNNING_CONDITION}
2985
+ }
2986
+
2943
2987
  if [ -n "\${${PTBK_CODER_LOG_FILE_ENV_NAME}:-}" ]; then
2944
- exec > >(tee -a "\$${PTBK_CODER_LOG_FILE_ENV_NAME}") 2> >(tee -a "\$${PTBK_CODER_LOG_FILE_ENV_NAME}" >&2)
2988
+ exec > >(tee -a "$${PTBK_CODER_LOG_FILE_ENV_NAME}") 2> >(tee -a "$${PTBK_CODER_LOG_FILE_ENV_NAME}" >&2)
2989
+ fi
2990
+
2991
+ watch_parent_process() {
2992
+ trap 'exit 0' HUP INT TERM
2993
+
2994
+ while is_parent_process_running; do
2995
+ sleep 1
2996
+ done
2997
+
2998
+ terminate_harness_process_tree
2999
+ }
3000
+
3001
+ # Background harness jobs need their own process group on Unix so termination cannot reach the parent coder.
3002
+ set -m
3003
+ if ! is_parent_process_running; then
3004
+ exit 1
2945
3005
  fi
2946
- bash "$1"
3006
+ bash "$1" &
3007
+ HARNESS_PROCESS_ID=$!
3008
+
3009
+ cleanup_parent_process_watcher() {
3010
+ kill "$PARENT_PROCESS_WATCHER_PID" 2>/dev/null || true
3011
+ wait "$PARENT_PROCESS_WATCHER_PID" 2>/dev/null || true
3012
+ }
3013
+
3014
+ watch_parent_process &
3015
+ PARENT_PROCESS_WATCHER_PID=$!
3016
+
3017
+ trap cleanup_parent_process_watcher EXIT
3018
+ trap terminate_harness_process_tree HUP INT TERM
3019
+
3020
+ wait "$HARNESS_PROCESS_ID"
3021
+ SCRIPT_EXIT_CODE=$?
3022
+ exit "$SCRIPT_EXIT_CODE"
2947
3023
  `);
2948
3024
  /**
2949
3025
  * Shapes one bash invocation that optionally mirrors live script output into a temporary log file.
@@ -2999,6 +3075,38 @@ function describeTempScriptKind(scriptPath) {
2999
3075
  return scriptPath.toLowerCase().endsWith('.test.sh') ? 'test shell' : 'runner shell';
3000
3076
  }
3001
3077
 
3078
+ /**
3079
+ * Standard streams used by every temporary Bash runner.
3080
+ */
3081
+ const BASH_PROCESS_STDIO = ['pipe', 'pipe', 'pipe'];
3082
+ /**
3083
+ * Whether the current Node process is running on Windows.
3084
+ */
3085
+ const IS_WINDOWS$1 = process.platform === 'win32';
3086
+ /**
3087
+ * Starts one temporary Bash script in a process tree owned by the current Node process.
3088
+ *
3089
+ * The wrapper watches the supplied owning process ID. When that process exits abruptly, the wrapper terminates every
3090
+ * nested shell and harness process instead of leaving it orphaned.
3091
+ *
3092
+ * @private internal utility of the coding prompt runner
3093
+ */
3094
+ function $spawnLoggedBashScript(options) {
3095
+ var _a;
3096
+ const bashExecution = buildLoggedBashExecution(options.scriptPath, options.logPath);
3097
+ const parentProcessId = (_a = options.parentProcessId) !== null && _a !== void 0 ? _a : process.pid;
3098
+ return spawn('bash', bashExecution.args, {
3099
+ detached: !IS_WINDOWS$1,
3100
+ env: {
3101
+ ...process.env,
3102
+ ...bashExecution.env,
3103
+ [PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME]: parentProcessId.toString(),
3104
+ },
3105
+ stdio: BASH_PROCESS_STDIO,
3106
+ windowsHide: true,
3107
+ });
3108
+ }
3109
+
3002
3110
  /**
3003
3111
  * Prints one live shell output chunk to the terminal when console mirroring is enabled.
3004
3112
  */
@@ -3021,12 +3129,12 @@ function printLiveScriptChunk(chunk, source, shouldPrintLiveOutput) {
3021
3129
  async function runBashScriptWithOutput(options) {
3022
3130
  var _a;
3023
3131
  await appendScriptExecutionLogStart(options);
3024
- const bashExecution = buildLoggedBashExecution(options.scriptPath, options.logPath);
3025
3132
  const scriptPathPosix = toPosixPath(options.scriptPath);
3026
3133
  const shouldPrintLiveOutput = (_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : true;
3027
3134
  return await new Promise((resolve, reject) => {
3028
- const commandProcess = spawn('bash', bashExecution.args, {
3029
- env: bashExecution.env ? { ...process.env, ...bashExecution.env } : process.env,
3135
+ const commandProcess = $spawnLoggedBashScript({
3136
+ scriptPath: options.scriptPath,
3137
+ logPath: options.logPath,
3030
3138
  });
3031
3139
  let output = '';
3032
3140
  let settled = false;
@@ -3085,8 +3193,9 @@ async function runBashScriptWithOutput(options) {
3085
3193
  const failure = new Error(spaceTrim$1(output) || `Command "bash ${scriptPathPosix}" exited with code ${code}`);
3086
3194
  settleWithLog(`failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}`, () => reject(failure), failure);
3087
3195
  };
3196
+ // Wait for `close`, not only `exit`, because the Bash wrapper can still be flushing its tee process
3197
+ // substitutions after the direct shell exits.
3088
3198
  commandProcess.on('close', handleExit);
3089
- commandProcess.on('exit', handleExit);
3090
3199
  commandProcess.on('disconnect', () => {
3091
3200
  const failure = new Error(`Command "bash ${scriptPathPosix}" disconnected`);
3092
3201
  settleWithLog('failed after disconnect', () => reject(failure), failure);
@@ -3953,6 +4062,35 @@ function isGitHubCopilotArgumentLimitError(error) {
3953
4062
  error.message.includes('bin/node'));
3954
4063
  }
3955
4064
 
4065
+ /**
4066
+ * Whether the current coder process runs on Windows.
4067
+ */
4068
+ const IS_WINDOWS = process.platform === 'win32';
4069
+ /**
4070
+ * Stops one active temporary Bash shell together with the harness process tree it owns.
4071
+ *
4072
+ * Unix sends `SIGTERM` to the wrapper so its shell trap terminates the separately grouped harness job. Windows does
4073
+ * not reliably deliver that signal to Bash, so the native `taskkill` command terminates the direct shell tree instead.
4074
+ *
4075
+ * @private internal utility of the coding prompt runner
4076
+ */
4077
+ function $terminateLoggedBashProcessTree(commandProcess) {
4078
+ if (commandProcess.exitCode !== null || commandProcess.signalCode !== null) {
4079
+ return;
4080
+ }
4081
+ if (!IS_WINDOWS) {
4082
+ commandProcess.kill('SIGTERM');
4083
+ return;
4084
+ }
4085
+ if (!commandProcess.pid) {
4086
+ return;
4087
+ }
4088
+ spawnSync('taskkill.exe', ['/PID', commandProcess.pid.toString(), '/T', '/F'], {
4089
+ stdio: 'ignore',
4090
+ windowsHide: true,
4091
+ });
4092
+ }
4093
+
3956
4094
  /**
3957
4095
  * Maximum number of output characters included in thrown error messages.
3958
4096
  */
@@ -3995,15 +4133,16 @@ async function runScriptUntilMarkerIdle(options) {
3995
4133
  const scriptPathPosix = toPosixPath(scriptPath);
3996
4134
  const shouldPrintLiveOutput = (_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : true;
3997
4135
  await appendScriptExecutionLogStart(options);
3998
- const bashExecution = buildLoggedBashExecution(scriptPath, options.logPath);
3999
4136
  return await new Promise((resolve, reject) => {
4000
- const commandProcess = spawn('bash', bashExecution.args, {
4001
- env: bashExecution.env ? { ...process.env, ...bashExecution.env } : process.env,
4137
+ const commandProcess = $spawnLoggedBashScript({
4138
+ scriptPath,
4139
+ logPath: options.logPath,
4002
4140
  });
4003
4141
  let stdoutBuffer = '';
4004
4142
  let stderrBuffer = '';
4005
4143
  let fullOutput = '';
4006
4144
  let markerSeen = false;
4145
+ let isCompletedAfterIdleTimeout = false;
4007
4146
  let idleTimer;
4008
4147
  let settled = false;
4009
4148
  let isSettling = false;
@@ -4045,15 +4184,16 @@ async function runScriptUntilMarkerIdle(options) {
4045
4184
  });
4046
4185
  };
4047
4186
  /**
4048
- * Finishes a marker-completed run and then asks bash to stop.
4187
+ * Stops a marker-completed process tree after its trailing output becomes idle.
4049
4188
  */
4050
4189
  const finishAfterIdleTimeout = () => {
4051
- settleWithLog('completed after idle timeout', () => resolve(fullOutput));
4190
+ isCompletedAfterIdleTimeout = true;
4191
+ idleTimer = undefined;
4052
4192
  try {
4053
- commandProcess.kill();
4193
+ $terminateLoggedBashProcessTree(commandProcess);
4054
4194
  }
4055
4195
  catch (_a) {
4056
- // Windows can report EPERM when bash exits before the idle timer fires.
4196
+ // The close handler below still settles normally when Bash already exited before the idle timer fired.
4057
4197
  }
4058
4198
  };
4059
4199
  /**
@@ -4112,7 +4252,11 @@ async function runScriptUntilMarkerIdle(options) {
4112
4252
  const failure = code === 0 || markerSeen
4113
4253
  ? undefined
4114
4254
  : new Error(buildCommandFailureMessage(scriptPathPosix, code, fullOutput));
4115
- const status = failure ? `failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}` : 'succeeded';
4255
+ const status = isCompletedAfterIdleTimeout
4256
+ ? 'completed after idle timeout'
4257
+ : failure
4258
+ ? `failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}`
4259
+ : 'succeeded';
4116
4260
  settleWithLog(status, () => {
4117
4261
  if (!failure) {
4118
4262
  resolve(fullOutput);
@@ -4121,8 +4265,9 @@ async function runScriptUntilMarkerIdle(options) {
4121
4265
  reject(failure);
4122
4266
  });
4123
4267
  };
4268
+ // Wait for `close`, not only `exit`, because the Bash wrapper can still be flushing its tee process
4269
+ // substitutions after the direct shell exits.
4124
4270
  commandProcess.on('close', handleExit);
4125
- commandProcess.on('exit', handleExit);
4126
4271
  commandProcess.on('disconnect', () => {
4127
4272
  const failure = new Error(buildCommandFailureMessage(scriptPathPosix, null, fullOutput));
4128
4273
  settleWithLog('failed after disconnect', () => reject(failure), failure);
@@ -27327,6 +27472,30 @@ function applyCommitmentDefinitionSafely(requirements, commitment, commitmentCon
27327
27472
  }
27328
27473
  }
27329
27474
 
27475
+ /**
27476
+ * Pattern shared by Book parsing and editor highlighting for a commitment-like line.
27477
+ *
27478
+ * A Book commitment keyword consists of one or more all-uppercase words with at least
27479
+ * two characters each. This deliberately mirrors the Book editor syntax rule so an
27480
+ * unknown keyword starts a new source block everywhere it is interpreted.
27481
+ *
27482
+ * @private internal constant of `getBookCommitmentLineType`
27483
+ */
27484
+ const BOOK_COMMITMENT_LINE_TYPE_REGEX = /^\s*(?<type>[A-Z][A-Z0-9]+(?:\s+[A-Z][A-Z0-9]+)*)(?=\s|$)/;
27485
+ /**
27486
+ * Extracts the uppercase commitment type from a Book line, including types not registered by Promptbook.
27487
+ *
27488
+ * @param line - One raw Book source line.
27489
+ * @returns Matched commitment type or `null` when the line does not begin with commitment syntax.
27490
+ *
27491
+ * @private internal utility of Book parsing and `<BookEditor/>`
27492
+ */
27493
+ function getBookCommitmentLineType(line) {
27494
+ var _a;
27495
+ const match = BOOK_COMMITMENT_LINE_TYPE_REGEX.exec(line);
27496
+ return ((_a = match === null || match === void 0 ? void 0 : match.groups) === null || _a === void 0 ? void 0 : _a.type) || null;
27497
+ }
27498
+
27330
27499
  /**
27331
27500
  * Consumes the agent-name prelude from raw source.
27332
27501
  *
@@ -27384,13 +27553,15 @@ function parseAgentSourceWithCommitments(agentSource) {
27384
27553
  return {
27385
27554
  agentName: null,
27386
27555
  commitments: [],
27556
+ unknownCommitments: [],
27387
27557
  nonCommitmentLines: [],
27388
27558
  };
27389
27559
  }
27390
27560
  const { lines, agentName, agentNameLineIndex, agentNameLineNumber } = parseAgentSourcePrelude(agentSource);
27391
27561
  const commitments = [];
27562
+ const unknownCommitments = [];
27392
27563
  const nonCommitmentLines = agentNameLineIndex >= 0 ? [lines[agentNameLineIndex]] : [];
27393
- // Parse commitments with multiline support
27564
+ // Parse commitments with multiline support.
27394
27565
  let currentCommitment = null;
27395
27566
  // Process lines starting from after the agent name line
27396
27567
  const startIndex = agentNameLineIndex >= 0 ? agentNameLineIndex + 1 : 0;
@@ -27404,25 +27575,11 @@ function parseAgentSourceWithCommitments(agentSource) {
27404
27575
  // Check if this line starts or ends a code block
27405
27576
  if (trimmedLine.startsWith('```')) {
27406
27577
  isInsideCodeBlock = !isInsideCodeBlock;
27407
- if (currentCommitment) {
27408
- // If we are inside a commitment, the code block is part of it
27409
- currentCommitment.contentLines.push(line);
27410
- }
27411
- else {
27412
- // If we are not inside a commitment, the code block is non-commitment
27413
- nonCommitmentLines.push(line);
27414
- }
27578
+ appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
27415
27579
  continue;
27416
27580
  }
27417
27581
  if (isInsideCodeBlock) {
27418
- if (currentCommitment) {
27419
- // If we are inside a commitment and a code block, the line is part of the commitment
27420
- currentCommitment.contentLines.push(line);
27421
- }
27422
- else {
27423
- // If we are inside a code block but not a commitment, the line is non-commitment
27424
- nonCommitmentLines.push(line);
27425
- }
27582
+ appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
27426
27583
  continue;
27427
27584
  }
27428
27585
  // Check if this line starts a new commitment
@@ -27431,22 +27588,14 @@ function parseAgentSourceWithCommitments(agentSource) {
27431
27588
  const typeRegex = definition.createTypeRegex();
27432
27589
  const match = typeRegex.exec(line.trim());
27433
27590
  if (match && ((_a = match.groups) === null || _a === void 0 ? void 0 : _a.type)) {
27434
- // Save the previous commitment if it exists
27435
- if (currentCommitment) {
27436
- const fullContent = currentCommitment.contentLines.join('\n');
27437
- commitments.push({
27438
- type: currentCommitment.type,
27439
- content: spaceTrim$1(fullContent),
27440
- originalLine: currentCommitment.originalStartLine,
27441
- lineNumber: currentCommitment.startLineNumber,
27442
- });
27443
- }
27591
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
27444
27592
  // Extract the initial content from the commitment line
27445
27593
  const fullRegex = definition.createRegex();
27446
27594
  const fullMatch = fullRegex.exec(line.trim());
27447
27595
  const initialContent = ((_b = fullMatch === null || fullMatch === void 0 ? void 0 : fullMatch.groups) === null || _b === void 0 ? void 0 : _b.contents) || '';
27448
27596
  // Start a new commitment
27449
27597
  currentCommitment = {
27598
+ kind: 'known',
27450
27599
  type: definition.type,
27451
27600
  startLineNumber: i + 1,
27452
27601
  originalStartLine: line,
@@ -27456,52 +27605,90 @@ function parseAgentSourceWithCommitments(agentSource) {
27456
27605
  break;
27457
27606
  }
27458
27607
  }
27608
+ if (foundNewCommitment) {
27609
+ continue;
27610
+ }
27611
+ const unknownCommitmentType = getBookCommitmentLineType(line);
27612
+ if (unknownCommitmentType) {
27613
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
27614
+ currentCommitment = {
27615
+ kind: 'unknown',
27616
+ type: unknownCommitmentType,
27617
+ startLineNumber: i + 1,
27618
+ originalStartLine: line,
27619
+ sourceLines: [line],
27620
+ };
27621
+ continue;
27622
+ }
27459
27623
  // Check if this is a horizontal line (ends any current commitment)
27460
27624
  const isHorizontalLine = HORIZONTAL_LINE_PATTERN$1.test(line);
27461
27625
  if (isHorizontalLine) {
27462
- // Save the current commitment if it exists
27463
- if (currentCommitment) {
27464
- const fullContent = currentCommitment.contentLines.join('\n');
27465
- commitments.push({
27466
- type: currentCommitment.type,
27467
- content: spaceTrim$1(fullContent),
27468
- originalLine: currentCommitment.originalStartLine,
27469
- lineNumber: currentCommitment.startLineNumber,
27470
- });
27471
- currentCommitment = null;
27472
- }
27626
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
27627
+ currentCommitment = null;
27473
27628
  // Add horizontal line to non-commitment lines
27474
27629
  nonCommitmentLines.push(line);
27475
27630
  continue;
27476
27631
  }
27477
- if (!foundNewCommitment) {
27478
- if (currentCommitment) {
27479
- // This line belongs to the current commitment
27480
- currentCommitment.contentLines.push(line);
27481
- }
27482
- else {
27483
- // This line is not part of any commitment
27484
- nonCommitmentLines.push(line);
27485
- }
27486
- }
27487
- }
27488
- // Don't forget to save the last commitment if it exists
27489
- if (currentCommitment) {
27490
- const fullContent = currentCommitment.contentLines.join('\n');
27491
- commitments.push({
27492
- type: currentCommitment.type,
27493
- content: spaceTrim$1(fullContent),
27494
- originalLine: currentCommitment.originalStartLine,
27495
- lineNumber: currentCommitment.startLineNumber,
27496
- });
27632
+ appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
27497
27633
  }
27634
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
27498
27635
  return {
27499
27636
  agentName,
27500
27637
  agentNameLineNumber,
27501
27638
  commitments,
27639
+ unknownCommitments,
27502
27640
  nonCommitmentLines,
27503
27641
  };
27504
27642
  }
27643
+ /**
27644
+ * Stores the current known or unknown commitment in its appropriate parsed collection.
27645
+ *
27646
+ * @param currentCommitment - Commitment-like block collected so far.
27647
+ * @param commitments - Parsed registered commitments to append to.
27648
+ * @param unknownCommitments - Parsed unregistered commitments to append to.
27649
+ *
27650
+ * @private internal utility of `parseAgentSourceWithCommitments`
27651
+ */
27652
+ function appendCurrentCommitment(currentCommitment, commitments, unknownCommitments) {
27653
+ if (!currentCommitment) {
27654
+ return;
27655
+ }
27656
+ if (currentCommitment.kind === 'unknown') {
27657
+ unknownCommitments.push({
27658
+ type: currentCommitment.type,
27659
+ originalLine: currentCommitment.originalStartLine,
27660
+ lineNumber: currentCommitment.startLineNumber,
27661
+ source: spaceTrim$1(currentCommitment.sourceLines.join('\n')),
27662
+ });
27663
+ return;
27664
+ }
27665
+ commitments.push({
27666
+ type: currentCommitment.type,
27667
+ content: spaceTrim$1(currentCommitment.contentLines.join('\n')),
27668
+ originalLine: currentCommitment.originalStartLine,
27669
+ lineNumber: currentCommitment.startLineNumber,
27670
+ });
27671
+ }
27672
+ /**
27673
+ * Adds one source line to the active commitment-like block, or to plain source content when none is active.
27674
+ *
27675
+ * @param currentCommitment - Commitment-like block currently being collected.
27676
+ * @param line - Source line to append.
27677
+ * @param nonCommitmentLines - Plain source lines to append to when no block is active.
27678
+ *
27679
+ * @private internal utility of `parseAgentSourceWithCommitments`
27680
+ */
27681
+ function appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines) {
27682
+ if (!currentCommitment) {
27683
+ nonCommitmentLines.push(line);
27684
+ return;
27685
+ }
27686
+ if (currentCommitment.kind === 'unknown') {
27687
+ currentCommitment.sourceLines.push(line);
27688
+ return;
27689
+ }
27690
+ currentCommitment.contentLines.push(line);
27691
+ }
27505
27692
 
27506
27693
  /**
27507
27694
  * Plugin for importing agent books *(`.book` files)*
@@ -27639,7 +27826,8 @@ async function augmentAgentModelRequirementsFromSource(requirements, parseResult
27639
27826
  requirements = await importReferencedFiles(requirements);
27640
27827
  requirements = appendMcpServers(requirements, agentSource);
27641
27828
  requirements = appendNonCommitmentContent(requirements, parseResult);
27642
- return appendExampleInteractions(requirements, parseResult);
27829
+ requirements = appendExampleInteractions(requirements, parseResult);
27830
+ return appendUnknownCommitmentContent(requirements, parseResult);
27643
27831
  }
27644
27832
  /**
27645
27833
  * Imports text files referenced by IMPORT commitments and appends their transformed content to the system message.
@@ -27761,6 +27949,36 @@ function appendNonCommitmentContent(requirements, parseResult) {
27761
27949
  }
27762
27950
  return appendSystemMessageSection(requirements, nonCommitmentContent);
27763
27951
  }
27952
+ /**
27953
+ * Appends unknown commitment blocks as final extra context without applying commitment side effects.
27954
+ *
27955
+ * @param requirements - Current requirements snapshot.
27956
+ * @param parseResult - Parsed source including unknown commitment blocks.
27957
+ * @returns Requirements with unknown commitment context appended at the end of the system message.
27958
+ *
27959
+ * @private internal utility of `augmentAgentModelRequirementsFromSource`
27960
+ */
27961
+ function appendUnknownCommitmentContent(requirements, parseResult) {
27962
+ const unknownCommitmentContent = getUnknownCommitmentContent(parseResult);
27963
+ if (!unknownCommitmentContent) {
27964
+ return requirements;
27965
+ }
27966
+ return appendSystemMessageSection(requirements, unknownCommitmentContent);
27967
+ }
27968
+ /**
27969
+ * Joins unknown commitment blocks into their final extra-context representation.
27970
+ *
27971
+ * @param parseResult - Parsed source including unknown commitment blocks.
27972
+ * @returns Unknown commitment source blocks in their original order.
27973
+ *
27974
+ * @private internal utility of `appendUnknownCommitmentContent`
27975
+ */
27976
+ function getUnknownCommitmentContent(parseResult) {
27977
+ return parseResult.unknownCommitments
27978
+ .map((commitment) => commitment.source)
27979
+ .filter(Boolean)
27980
+ .join('\n\n');
27981
+ }
27764
27982
  /**
27765
27983
  * Collects plain-text lines that were not parsed as commitments and should still become part of the final system message.
27766
27984
  *
@@ -39735,23 +39953,27 @@ async function locateAppOnMacOs({ macOsName, }) {
39735
39953
  // TODO: [🧠][♿] Maybe export through `@promptbook/node`
39736
39954
 
39737
39955
  /**
39738
- * Attempts to locate the specified application on a Windows system by searching common installation directories.
39956
+ * Attempts to locate the specified application on a Windows system by searching PATH and common installation directories.
39739
39957
  * Returns the path to the executable if found, or null otherwise.
39740
39958
  *
39741
39959
  * @private within the repository
39742
39960
  */
39743
39961
  async function locateAppOnWindows({ appName, windowsSuffix, }) {
39744
39962
  try {
39745
- const prefixes = [
39963
+ const EXECUTABLE_FILENAME = windowsSuffix.replace(/^.*[\\/]/, '');
39964
+ const PATH_EXECUTABLE_PATHS = (process.env.PATH || '')
39965
+ .split(delimiter)
39966
+ .filter(Boolean)
39967
+ .map((pathPrefix) => join(pathPrefix, EXECUTABLE_FILENAME));
39968
+ const INSTALLATION_EXECUTABLE_PATHS = [
39746
39969
  process.env.LOCALAPPDATA,
39747
39970
  join(process.env.LOCALAPPDATA || '', 'Programs'),
39748
39971
  process.env.PROGRAMFILES,
39749
39972
  process.env['PROGRAMFILES(X86)'],
39750
- ];
39751
- for (const prefix of prefixes) {
39752
- const path = prefix + windowsSuffix;
39753
- if (await isExecutable(path, $provideFilesystemForNode())) {
39754
- return path;
39973
+ ].map((pathPrefix) => pathPrefix + windowsSuffix);
39974
+ for (const executablePath of [...PATH_EXECUTABLE_PATHS, ...INSTALLATION_EXECUTABLE_PATHS]) {
39975
+ if (await isExecutable(executablePath, $provideFilesystemForNode())) {
39976
+ return executablePath;
39755
39977
  }
39756
39978
  }
39757
39979
  throw new Error(`Can not locate app ${appName} on Windows.`);