pi-usereq 0.76.0 → 0.78.0

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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # PI-useReq/pi-usereq (0.76.0)
1
+ # PI-useReq/pi-usereq (0.78.0)
2
2
 
3
3
  <p align="center">
4
4
  <img src="https://img.shields.io/badge/python-3.11%2B-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python 3.11+">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-usereq",
3
- "version": "0.76.0",
3
+ "version": "0.78.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Ogekuri/PI-useReq.git"
@@ -32,7 +32,6 @@ import {
32
32
  normalizePiNotifyTemplateValue,
33
33
  } from "./pi-notify.js";
34
34
  import {
35
- DEFAULT_DEBUG_ENABLED,
36
35
  DEFAULT_DEBUG_LOG_FILE,
37
36
  DEFAULT_DEBUG_LOG_ON_STATUS,
38
37
  DEFAULT_DEBUG_PROMPTS_ENABLED,
@@ -40,7 +39,7 @@ import {
40
39
  DEFAULT_DEBUG_STATUS_CHANGES,
41
40
  DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED,
42
41
  DEFAULT_DEBUG_WORKFLOW_EVENTS,
43
- normalizeDebugEnabled,
42
+ getRuntimeDebugEnabled,
44
43
  normalizeDebugEnabledPrompts,
45
44
  normalizeDebugEnabledTools,
46
45
  normalizeDebugLogFile,
@@ -149,7 +148,8 @@ interface GlobalStaticCheckLanguageConfig {
149
148
 
150
149
  /**
151
150
  * @brief Defines the persisted local pi-usereq configuration schema.
152
- * @details Captures project-scoped directory, debug, and static-check enablement fields written to `<base-path>/.pi-usereq.json` while excluding global notification, tool, git, and checker-command settings. The interface is compile-time only and introduces no runtime side effects.
151
+ * @details Captures project-scoped directory, debug, and static-check enablement fields written to `<base-path>/.pi-usereq.json` while excluding the runtime-only `DEBUG_ENABLED` flag and global notification, tool, git, and checker-command settings. The interface is compile-time only and introduces no runtime side effects.
152
+ * @satisfies REQ-426
153
153
  */
154
154
  interface UseReqLocalConfig {
155
155
  "docs-dir": string;
@@ -159,7 +159,6 @@ interface UseReqLocalConfig {
159
159
  "context-files-references": boolean;
160
160
  "context-files-workflow": boolean;
161
161
  "static-check": Record<string, LocalStaticCheckLanguageConfig>;
162
- DEBUG_ENABLED: "enable" | "disable";
163
162
  DEBUG_LOG_FILE: string;
164
163
  DEBUG_PROMPTS_ENABLED: DebugPromptsEnabled;
165
164
  DEBUG_PROMPTS_LOG_PATH: string;
@@ -587,7 +586,7 @@ export function getGlobalConfigPath(): string {
587
586
  * @details Populates canonical docs/test/source directories, derives local static-check enable defaults from the supplied global checker definitions, and seeds documented debug defaults including tool-wrapper command registration without any cross-project fields. Runtime is O(l). No filesystem side effects occur.
588
587
  * @param[in] globalStaticCheckConfig {Record<string, GlobalStaticCheckLanguageConfig>} Global checker definitions used to derive local enable defaults.
589
588
  * @return {UseReqLocalConfig} Fresh default local configuration object.
590
- * @satisfies CTN-019, REQ-328, REQ-413, REQ-414
589
+ * @satisfies CTN-013, CTN-019, REQ-328, REQ-413, REQ-414, REQ-426
591
590
  */
592
591
  function getDefaultLocalConfig(
593
592
  globalStaticCheckConfig: Record<string, GlobalStaticCheckLanguageConfig>,
@@ -600,7 +599,6 @@ function getDefaultLocalConfig(
600
599
  "context-files-references": DEFAULT_CONTEXT_FILES_FLAG,
601
600
  "context-files-workflow": DEFAULT_CONTEXT_FILES_FLAG,
602
601
  "static-check": getDefaultLocalStaticCheckConfig(globalStaticCheckConfig),
603
- DEBUG_ENABLED: DEFAULT_DEBUG_ENABLED,
604
602
  DEBUG_LOG_FILE: DEFAULT_DEBUG_LOG_FILE,
605
603
  DEBUG_PROMPTS_ENABLED: DEFAULT_DEBUG_PROMPTS_ENABLED,
606
604
  DEBUG_PROMPTS_LOG_PATH: DEFAULT_DEBUG_PROMPTS_LOG_PATH,
@@ -652,11 +650,11 @@ function getDefaultGlobalConfig(): UseReqGlobalConfig {
652
650
 
653
651
  /**
654
652
  * @brief Merges persisted local and global configuration scopes into the effective runtime config.
655
- * @details Normalizes local directories, combines local static-check enable flags with global checker arrays, resolves effective worktree disablement when automatic git commit is off, normalizes debug and notification fields including tool-wrapper command registration, and disables Pushover until both credentials are populated. Runtime is O(l + c + p). No external state is mutated.
653
+ * @details Normalizes local directories, combines local static-check enable flags with global checker arrays, resolves effective worktree disablement when automatic git commit is off, normalizes debug and notification fields including tool-wrapper command registration, sources the runtime-only `DEBUG_ENABLED` flag from the process-scoped debug store, and disables Pushover until both credentials are populated. Runtime is O(l + c + p). No external state is mutated.
656
654
  * @param[in] localConfig {UseReqLocalConfig} Persisted local configuration.
657
655
  * @param[in] globalConfig {UseReqGlobalConfig} Persisted global configuration.
658
656
  * @return {UseReqConfig} Effective merged configuration.
659
- * @satisfies REQ-328, REQ-413, REQ-414
657
+ * @satisfies REQ-236, REQ-328, REQ-413, REQ-414, REQ-426
660
658
  */
661
659
  function mergeConfigScopes(
662
660
  localConfig: UseReqLocalConfig,
@@ -691,7 +689,7 @@ function mergeConfigScopes(
691
689
  AUTO_GIT_COMMIT: autoGitCommit,
692
690
  GIT_WORKTREE_ENABLED: gitWorktreeEnabled,
693
691
  GIT_WORKTREE_PREFIX: normalizeGitWorktreePrefix(globalConfig.GIT_WORKTREE_PREFIX),
694
- DEBUG_ENABLED: normalizeDebugEnabled(localConfig.DEBUG_ENABLED),
692
+ DEBUG_ENABLED: getRuntimeDebugEnabled(),
695
693
  DEBUG_LOG_FILE: normalizeDebugLogFile(localConfig.DEBUG_LOG_FILE),
696
694
  DEBUG_PROMPTS_ENABLED: normalizeDebugPromptsEnabled(localConfig.DEBUG_PROMPTS_ENABLED),
697
695
  DEBUG_PROMPTS_LOG_PATH: normalizeDebugPromptsLogPath(localConfig.DEBUG_PROMPTS_LOG_PATH),
@@ -862,11 +860,11 @@ function normalizeGlobalStaticCheckConfig(
862
860
 
863
861
  /**
864
862
  * @brief Loads and sanitizes the persisted local configuration.
865
- * @details Returns defaults when `<base-path>/.pi-usereq.json` is absent. Otherwise parses the local JSON payload, normalizes project-scoped directory, debug, tool-wrapper command registration, and static-check enable fields, and ignores misplaced global keys without migration. Runtime is O(n) in file size. Side effects are limited to filesystem reads.
863
+ * @details Returns defaults when `<base-path>/.pi-usereq.json` is absent. Otherwise parses the local JSON payload, normalizes project-scoped directory, debug, tool-wrapper command registration, and static-check enable fields, ignores any persisted `DEBUG_ENABLED` payload, and ignores misplaced global keys without migration. Runtime is O(n) in file size. Side effects are limited to filesystem reads.
866
864
  * @param[in] projectBase {string} Absolute project root path.
867
865
  * @param[in] defaultStaticCheckConfig {Record<string, LocalStaticCheckLanguageConfig>} Local static-check enable defaults derived from the current global checker map.
868
866
  * @return {UseReqLocalConfig} Sanitized local configuration.
869
- * @satisfies CTN-019, REQ-328, REQ-413, REQ-414
867
+ * @satisfies CTN-019, REQ-328, REQ-413, REQ-414, REQ-426
870
868
  */
871
869
  function loadLocalConfig(
872
870
  projectBase: string,
@@ -883,7 +881,6 @@ function loadLocalConfig(
883
881
  "context-files-references": DEFAULT_CONTEXT_FILES_FLAG,
884
882
  "context-files-workflow": DEFAULT_CONTEXT_FILES_FLAG,
885
883
  "static-check": normalizeLocalStaticCheckConfig(undefined, defaultStaticCheckConfig),
886
- DEBUG_ENABLED: DEFAULT_DEBUG_ENABLED,
887
884
  DEBUG_LOG_FILE: DEFAULT_DEBUG_LOG_FILE,
888
885
  DEBUG_PROMPTS_ENABLED: DEFAULT_DEBUG_PROMPTS_ENABLED,
889
886
  DEBUG_PROMPTS_LOG_PATH: DEFAULT_DEBUG_PROMPTS_LOG_PATH,
@@ -915,7 +912,6 @@ function loadLocalConfig(
915
912
  "context-files-references": normalizeContextFilesFlag(data["context-files-references"]),
916
913
  "context-files-workflow": normalizeContextFilesFlag(data["context-files-workflow"]),
917
914
  "static-check": normalizeLocalStaticCheckConfig(data["static-check"], defaultStaticCheckConfig),
918
- DEBUG_ENABLED: normalizeDebugEnabled(data.DEBUG_ENABLED),
919
915
  DEBUG_LOG_FILE: normalizeDebugLogFile(data.DEBUG_LOG_FILE),
920
916
  DEBUG_PROMPTS_ENABLED: normalizeDebugPromptsEnabled(data.DEBUG_PROMPTS_ENABLED),
921
917
  DEBUG_PROMPTS_LOG_PATH: normalizeDebugPromptsLogPath(data.DEBUG_PROMPTS_LOG_PATH),
@@ -995,10 +991,10 @@ export function loadConfig(projectBase: string): UseReqConfig {
995
991
 
996
992
  /**
997
993
  * @brief Builds the persisted local configuration payload.
998
- * @details Copies only project-scoped keys into a fresh object so runtime-derived metadata plus global checker, tool, git, and notification fields never reach `.pi-usereq.json`, while preserving the debug tool-wrapper command flag beside other local debug settings. Runtime is O(n) in config size. No external state is mutated.
994
+ * @details Copies only project-scoped keys into a fresh object so runtime-derived metadata, the runtime-only `DEBUG_ENABLED` flag, and global checker, tool, git, and notification fields never reach `.pi-usereq.json`, while preserving the debug tool-wrapper command flag beside other persisted local debug settings. Runtime is O(n) in config size. No external state is mutated.
999
995
  * @param[in] config {UseReqConfig} Effective configuration object.
1000
996
  * @return {UseReqLocalConfig} Persistable local configuration payload.
1001
- * @satisfies CTN-012, CTN-013, CTN-019, REQ-104, REQ-146, REQ-249, REQ-316, REQ-277, REQ-328, REQ-413, REQ-414
997
+ * @satisfies CTN-012, CTN-013, CTN-019, REQ-104, REQ-146, REQ-249, REQ-316, REQ-277, REQ-328, REQ-413, REQ-414, REQ-426
1002
998
  */
1003
999
  function buildPersistedLocalConfig(config: UseReqConfig): UseReqLocalConfig {
1004
1000
  const normalizedSrcDir = config["src-dir"]
@@ -1022,7 +1018,6 @@ function buildPersistedLocalConfig(config: UseReqConfig): UseReqLocalConfig {
1022
1018
  ),
1023
1019
  ]),
1024
1020
  ),
1025
- DEBUG_ENABLED: normalizeDebugEnabled(config.DEBUG_ENABLED),
1026
1021
  DEBUG_LOG_FILE: normalizeDebugLogFile(config.DEBUG_LOG_FILE),
1027
1022
  DEBUG_PROMPTS_ENABLED: normalizeDebugPromptsEnabled(config.DEBUG_PROMPTS_ENABLED),
1028
1023
  DEBUG_PROMPTS_LOG_PATH: normalizeDebugPromptsLogPath(config.DEBUG_PROMPTS_LOG_PATH),
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @file
3
3
  * @brief Declares debug inventories, normalizers, and JSON log persistence helpers.
4
- * @details Centralizes debug-menu selector inventories, config-field normalization, workflow-status gating, append-only JSON log writing for tool, prompt, and dedicated workflow debug events, process-scoped capture of the exact `before_provider_request` provider payload for active prompt runs, and error-stage provider payload dumps gated by `after_provider_response` error statuses. Runtime is dominated by JSON serialization plus filesystem I/O during log writes. Side effects include directory creation and file overwrite when debug entries are appended.
4
+ * @details Centralizes debug-menu selector inventories, config-field normalization, the process-scoped runtime debug-enable state, workflow-status gating, append-only JSON log writing for tool, prompt, and dedicated workflow debug events, process-scoped capture of the exact `before_provider_request` provider payload for active prompt runs, and error-stage provider payload dumps gated by `after_provider_response` error statuses. Runtime is dominated by JSON serialization plus filesystem I/O during log writes. Side effects include directory creation and file overwrite when debug entries are appended.
5
5
  */
6
6
 
7
7
  import fs from "node:fs";
@@ -24,6 +24,49 @@ import {
24
24
  */
25
25
  export const DEFAULT_DEBUG_ENABLED = "disable" as const;
26
26
 
27
+ /**
28
+ * @brief Describes the process-scoped runtime debug-enable state persisted across session rebinds.
29
+ * @details Stores the user-toggled runtime `Debug` flag on `globalThis` because pi rebinds extension modules for `/new`, `/resume`, `/fork`, and `/reload` while the hosting process persists across those operations; the flag therefore survives ctrl+N session forks and `req-reset` but resets to `disable` when a fresh pi CLI process starts. The interface is compile-time only and introduces no runtime cost.
30
+ * @satisfies REQ-236, REQ-426
31
+ */
32
+ interface ProcessScopedDebugEnabledStore {
33
+ runtimeDebugEnabled: "enable" | "disable" | undefined;
34
+ }
35
+
36
+ /**
37
+ * @brief Returns the process-scoped runtime debug-enable store.
38
+ * @details Lazily initializes one `globalThis` record so session rebinds reuse the toggled runtime debug mode instead of resetting it to the persisted default. Runtime is O(1). Side effect: initializes process-scoped state on first access.
39
+ * @return {ProcessScopedDebugEnabledStore} Mutable process-scoped runtime debug store.
40
+ */
41
+ function getProcessScopedDebugEnabledStore(): ProcessScopedDebugEnabledStore {
42
+ const globalScope = globalThis as typeof globalThis & { __piUsereqDebugEnabledStore?: ProcessScopedDebugEnabledStore };
43
+ if (!globalScope.__piUsereqDebugEnabledStore) {
44
+ globalScope.__piUsereqDebugEnabledStore = { runtimeDebugEnabled: undefined };
45
+ }
46
+ return globalScope.__piUsereqDebugEnabledStore;
47
+ }
48
+
49
+ /**
50
+ * @brief Resolves the runtime debug-enable mode for the current pi host process.
51
+ * @details Returns the process-scoped toggled value when present and the documented `disable` default otherwise, so every fresh pi CLI process starts with debug logging disabled regardless of any stale persisted payload. Runtime is O(1). No external state is mutated.
52
+ * @return {"enable" | "disable"} Effective runtime debug-enable mode.
53
+ * @satisfies REQ-236
54
+ */
55
+ export function getRuntimeDebugEnabled(): "enable" | "disable" {
56
+ return getProcessScopedDebugEnabledStore().runtimeDebugEnabled ?? DEFAULT_DEBUG_ENABLED;
57
+ }
58
+
59
+ /**
60
+ * @brief Stores one user-toggled runtime debug-enable mode for the current pi host process.
61
+ * @details Mutates only the process-scoped runtime store so `Debug` menu toggles survive session rebinds without reaching persisted local or global configuration files. Runtime is O(1). Side effect: mutates process-scoped runtime state.
62
+ * @param[in] runtimeDebugEnabled {"enable" | "disable"} Next runtime debug-enable mode.
63
+ * @return {void} No return value.
64
+ * @satisfies REQ-236, REQ-426
65
+ */
66
+ export function setRuntimeDebugEnabled(runtimeDebugEnabled: "enable" | "disable"): void {
67
+ getProcessScopedDebugEnabledStore().runtimeDebugEnabled = runtimeDebugEnabled;
68
+ }
69
+
27
70
  /**
28
71
  * @brief Defines the default debug log file value.
29
72
  * @details New configurations write debug JSON entries to `/tmp/PI-useReq.json` unless the user overrides the path. Access complexity is O(1).
@@ -442,10 +485,10 @@ export function shouldLogDebugPromptWorkflowEvent(
442
485
 
443
486
  /**
444
487
  * @brief Tests whether one dispatched prompt content file should be written.
445
- * @details Requires global debug enablement, explicit prompt-content debug enablement, and a matching workflow-state filter before any filesystem work occurs; prompt-content logging is therefore fully subordinate to the global `Debug` flag. Runtime is O(1). No external state is mutated.
488
+ * @details Requires global debug enablement plus explicit prompt-content debug enablement before any filesystem work occurs; prompt-content logging is fully subordinate to the global `Debug` flag and exempt from the `DEBUG_LOG_ON_STATUS` workflow-state filter so enabled runs always save their debug files. Runtime is O(1). No external state is mutated.
446
489
  * @param[in] config {UseReqConfig} Effective project configuration.
447
- * @param[in] workflowState {DebugWorkflowState} Current workflow state.
448
- * @param[in] promptName {PromptCommandName} Bundled prompt name.
490
+ * @param[in] workflowState {DebugWorkflowState} Current workflow state; retained for signature compatibility and excluded from gating per REQ-416.
491
+ * @param[in] promptName {PromptCommandName} Bundled prompt name; retained for signature compatibility.
449
492
  * @return {boolean} `true` when the prompt content file should be written.
450
493
  * @satisfies REQ-413, REQ-416
451
494
  */
@@ -455,8 +498,7 @@ export function shouldLogDebugPromptContent(
455
498
  promptName: PromptCommandName,
456
499
  ): boolean {
457
500
  return normalizeDebugEnabled(config.DEBUG_ENABLED) === "enable"
458
- && normalizeDebugPromptsEnabled(config.DEBUG_PROMPTS_ENABLED) === "enable"
459
- && matchesDebugWorkflowState(normalizeDebugLogOnStatus(config.DEBUG_LOG_ON_STATUS), workflowState);
501
+ && normalizeDebugPromptsEnabled(config.DEBUG_PROMPTS_ENABLED) === "enable";
460
502
  }
461
503
 
462
504
  /**
@@ -681,21 +723,53 @@ export function formatPromptDebugFileName(
681
723
  }
682
724
 
683
725
  /**
684
- * @brief Writes one dispatched prompt content file when prompt debug logging is enabled.
685
- * @details Applies global-debug, prompt-debug, and workflow-state gating before creating the configured prompt log directory and writing the exact delivered prompt content into one `<timestamp>-<req-command>` file so the send can be replayed from the pi CLI without pi-usereq installed. Runtime is dominated by one directory creation plus one file write when enabled and O(1) otherwise. Side effects include directory creation and file creation only for enabled matching entries.
726
+ * @brief Builds one command-invocation summary debug filename for one bundled prompt.
727
+ * @details Appends the `-request` suffix to the shared `<timestamp>-<req-command>` base so every saved command invocation summary stays attributable to the originating `/req-*` prompt. Runtime is O(1). No external state is mutated.
728
+ * @param[in] promptName {PromptCommandName} Bundled prompt name.
729
+ * @param[in] date {Date} Write-time timestamp. Defaults to the current wall-clock time.
730
+ * @return {string} Prompt debug filename in the `<timestamp>-<req-command>-request` shape.
731
+ * @satisfies REQ-417, REQ-427
732
+ */
733
+ export function formatPromptDebugRequestFileName(
734
+ promptName: PromptCommandName,
735
+ date = new Date(),
736
+ ): string {
737
+ return `${formatPromptDebugFileName(promptName, date)}-request`;
738
+ }
739
+
740
+ /**
741
+ * @brief Builds one dispatched-prompt content debug filename for one bundled prompt.
742
+ * @details Appends the `-prompt` suffix to the shared `<timestamp>-<req-command>` base so every saved initial LLM prompt stays attributable to the originating `/req-*` prompt. Runtime is O(1). No external state is mutated.
743
+ * @param[in] promptName {PromptCommandName} Bundled prompt name.
744
+ * @param[in] date {Date} Write-time timestamp. Defaults to the current wall-clock time.
745
+ * @return {string} Prompt debug filename in the `<timestamp>-<req-command>-prompt` shape.
746
+ * @satisfies REQ-417, REQ-418
747
+ */
748
+ export function formatPromptDebugPromptFileName(
749
+ promptName: PromptCommandName,
750
+ date = new Date(),
751
+ ): string {
752
+ return `${formatPromptDebugFileName(promptName, date)}-prompt`;
753
+ }
754
+
755
+ /**
756
+ * @brief Writes one gated prompt debug file under the configured prompt log directory.
757
+ * @details Applies the global-debug plus prompt-debug gating, creates the configured prompt log directory, and writes the supplied text into the requested filename so dispatched-prompt artifacts can be replayed from the pi CLI without pi-usereq installed. Runtime is dominated by one directory creation plus one file write when enabled and O(1) otherwise. Side effects include directory creation and file creation only for enabled matching entries.
686
758
  * @param[in] projectBase {string} Absolute original project base path.
687
759
  * @param[in] config {UseReqConfig} Effective project configuration.
688
- * @param[in] workflowState {DebugWorkflowState} Current workflow state.
760
+ * @param[in] workflowState {DebugWorkflowState} Current workflow state; excluded from gating per REQ-416.
689
761
  * @param[in] promptName {PromptCommandName} Bundled prompt name.
690
- * @param[in] content {string} Exact rendered prompt content delivered through `sendMessage` or `sendUserMessage`.
691
- * @return {boolean} `true` when the prompt content file is written; otherwise `false`.
692
- * @satisfies REQ-416, REQ-417, REQ-418
762
+ * @param[in] fileName {string} Target filename under the resolved prompt log directory.
763
+ * @param[in] content {string} Exact text to persist.
764
+ * @return {boolean} `true` when the file is written; otherwise `false`.
765
+ * @satisfies REQ-416
693
766
  */
694
- export function logDebugPromptContent(
767
+ function writePromptDebugFile(
695
768
  projectBase: string,
696
769
  config: UseReqConfig,
697
770
  workflowState: DebugWorkflowState,
698
771
  promptName: PromptCommandName,
772
+ fileName: string,
699
773
  content: string,
700
774
  ): boolean {
701
775
  if (!shouldLogDebugPromptContent(config, workflowState, promptName)) {
@@ -704,20 +778,79 @@ export function logDebugPromptContent(
704
778
  const logDirectory = resolveDebugPromptsLogPath(projectBase, config);
705
779
  try {
706
780
  fs.mkdirSync(logDirectory, { recursive: true });
707
- fs.writeFileSync(path.join(logDirectory, formatPromptDebugFileName(promptName)), content, "utf8");
781
+ fs.writeFileSync(path.join(logDirectory, fileName), content, "utf8");
708
782
  return true;
709
783
  } catch {
710
784
  return false;
711
785
  }
712
786
  }
713
787
 
788
+ /**
789
+ * @brief Writes one command invocation summary debug file when prompt debug logging is enabled.
790
+ * @details Persists the exact summary text displayed on screen into one `<timestamp>-<req-command>-request` file so the `/req-*` invocation headers survive beside the dispatched prompt. Runtime is dominated by one directory creation plus one file write when enabled and O(1) otherwise. Side effects include directory creation and file creation only for enabled matching entries.
791
+ * @param[in] projectBase {string} Absolute original project base path.
792
+ * @param[in] config {UseReqConfig} Effective project configuration.
793
+ * @param[in] workflowState {DebugWorkflowState} Current workflow state; excluded from gating per REQ-416.
794
+ * @param[in] promptName {PromptCommandName} Bundled prompt name.
795
+ * @param[in] requestText {string} Exact command invocation summary text displayed on screen.
796
+ * @return {boolean} `true` when the request file is written; otherwise `false`.
797
+ * @satisfies REQ-416, REQ-417, REQ-427, REQ-428
798
+ */
799
+ export function logDebugPromptRequest(
800
+ projectBase: string,
801
+ config: UseReqConfig,
802
+ workflowState: DebugWorkflowState,
803
+ promptName: PromptCommandName,
804
+ requestText: string,
805
+ ): boolean {
806
+ return writePromptDebugFile(
807
+ projectBase,
808
+ config,
809
+ workflowState,
810
+ promptName,
811
+ formatPromptDebugRequestFileName(promptName),
812
+ requestText,
813
+ );
814
+ }
815
+
816
+ /**
817
+ * @brief Writes one dispatched prompt content file when prompt debug logging is enabled.
818
+ * @details Persists the exact rendered prompt content delivered through `sendMessage` or `sendUserMessage` into one `<timestamp>-<req-command>-prompt` file so the send can be replayed from the pi CLI without pi-usereq installed. Runtime is dominated by one directory creation plus one file write when enabled and O(1) otherwise. Side effects include directory creation and file creation only for enabled matching entries.
819
+ * @param[in] projectBase {string} Absolute original project base path.
820
+ * @param[in] config {UseReqConfig} Effective project configuration.
821
+ * @param[in] workflowState {DebugWorkflowState} Current workflow state; excluded from gating per REQ-416.
822
+ * @param[in] promptName {PromptCommandName} Bundled prompt name.
823
+ * @param[in] content {string} Exact rendered prompt content delivered through `sendMessage` or `sendUserMessage`.
824
+ * @return {boolean} `true` when the prompt content file is written; otherwise `false`.
825
+ * @satisfies REQ-416, REQ-417, REQ-418, REQ-428
826
+ */
827
+ export function logDebugPromptContent(
828
+ projectBase: string,
829
+ config: UseReqConfig,
830
+ workflowState: DebugWorkflowState,
831
+ promptName: PromptCommandName,
832
+ content: string,
833
+ ): boolean {
834
+ return writePromptDebugFile(
835
+ projectBase,
836
+ config,
837
+ workflowState,
838
+ promptName,
839
+ formatPromptDebugPromptFileName(promptName),
840
+ content,
841
+ );
842
+ }
843
+
714
844
  /**
715
845
  * @brief Describes one process-scoped captured provider request payload bound to an active prompt run.
716
- * @details Pairs the bundled prompt name with the JSON serialization of the exact `before_provider_request` payload so a later `after_provider_response` error status can dump the failing request verbatim without retaining live provider objects. The interface is compile-time only and introduces no runtime cost.
846
+ * @details Pairs the bundled prompt name, the serialization of the exact `before_provider_request` payload, the original project base, and the resolved prompt log directory so a later `after_provider_response` error status can dump the failing request verbatim without live controller state, cached configuration, or live provider objects. The interface is compile-time only and introduces no runtime cost.
847
+ * @satisfies REQ-421
717
848
  */
718
849
  interface CapturedPromptErrorPayload {
719
850
  promptName: PromptCommandName;
720
851
  payloadText: string;
852
+ projectBase: string;
853
+ logDirectory: string;
721
854
  }
722
855
 
723
856
  /**
@@ -758,66 +891,105 @@ export function formatPromptDebugErrorFileName(
758
891
  return `${formatPromptDebugTimestamp(date)}-${getDebugPromptName(promptName)}-error-${errorCode}`;
759
892
  }
760
893
 
894
+ /**
895
+ * @brief Resolves the failing HTTP status carried by one finalized agent message.
896
+ * @details Extracts the leading provider error status from one `message_end` assistant message whose `stopReason` equals `error`, accepting both pi `formatProviderError` display shapes (`"<status>: <body>"` and `"<prefix> (<status>): <body>"`) so provider APIs whose SDK clients throw before emitting `after_provider_response` for non-2xx statuses (the OpenAI-compatible family, including `zai`) still flush the captured provider request payload through the message lifecycle channel. Runtime is O(n) in the inspected error-message prefix length. No external state is mutated.
897
+ * @param[in] message {unknown} Finalized agent message forwarded by one `message_end` lifecycle event.
898
+ * @return {number | undefined} Failing HTTP status greater than or equal to `400`, or `undefined` when the message carries no provider error status.
899
+ * @satisfies REQ-422, REQ-423, REQ-425
900
+ */
901
+ export function resolveProviderErrorFlushStatusFromMessage(message: unknown): number | undefined {
902
+ if (typeof message !== "object" || message === null) {
903
+ return undefined;
904
+ }
905
+ const candidateMessage = message as { role?: unknown; stopReason?: unknown; errorMessage?: unknown };
906
+ if (candidateMessage.role !== "assistant" || candidateMessage.stopReason !== "error") {
907
+ return undefined;
908
+ }
909
+ if (typeof candidateMessage.errorMessage !== "string") {
910
+ return undefined;
911
+ }
912
+ const unprefixedStatus = /^(\d{3}):/.exec(candidateMessage.errorMessage)?.[1];
913
+ const prefixedStatus = /^[^(]+\((\d{3})\): /.exec(candidateMessage.errorMessage)?.[1];
914
+ const statusText = unprefixedStatus ?? prefixedStatus;
915
+ if (statusText === undefined) {
916
+ return undefined;
917
+ }
918
+ const status = Number.parseInt(statusText, 10);
919
+ return Number.isFinite(status) && status >= 400 ? status : undefined;
920
+ }
921
+
922
+ /**
923
+ * @brief Serializes one provider request payload into deterministic text.
924
+ * @details Pretty-prints the payload as JSON while degrading circular references into one `[Circular]` token and bigint values into decimal strings so serialization failures never discard the capture. Runtime is O(n) in serialized payload size. No external state is mutated.
925
+ * @param[in] payload {unknown} Exact `before_provider_request` provider request payload.
926
+ * @return {string} Serialized payload text; the empty string when the payload serializes to nothing.
927
+ */
928
+ function serializePromptProviderPayload(payload: unknown): string {
929
+ const seenObjects = new WeakSet<object>();
930
+ return JSON.stringify(
931
+ payload,
932
+ (_key: string, value: unknown) => {
933
+ if (typeof value === "bigint") {
934
+ return value.toString();
935
+ }
936
+ if (typeof value === "object" && value !== null) {
937
+ if (seenObjects.has(value)) {
938
+ return "[Circular]";
939
+ }
940
+ seenObjects.add(value);
941
+ }
942
+ return value;
943
+ },
944
+ 2,
945
+ ) ?? "";
946
+ }
947
+
761
948
  /**
762
949
  * @brief Captures one exact provider request payload for the active prompt run when prompt debug logging is enabled.
763
- * @details Applies the identical global-debug, prompt-debug, and workflow-state gating used by dispatched prompt content files, serializes the exact `before_provider_request` payload as pretty-printed JSON, and stores it in the process-scoped capture store keyed by the bundled prompt name so the payload survives session replacement until an error response flushes it. Runtime is O(n) in serialized payload size when enabled and O(1) otherwise. Side effect: mutates the process-scoped capture store for enabled matching requests.
950
+ * @details Applies the global-debug plus prompt-debug gating, serializes the exact `before_provider_request` payload through the cycle-safe serializer, and stores the prompt name, payload text, original project base, and resolved prompt log directory in the process-scoped capture store so the payload survives session replacement and later error-stage writes require no live workflow state or cached configuration. Runtime is O(n) in serialized payload size when enabled and O(1) otherwise. Side effect: mutates the process-scoped capture store for enabled matching requests.
764
951
  * @param[in] config {UseReqConfig} Effective project configuration.
765
- * @param[in] workflowState {DebugWorkflowState} Current workflow state.
766
952
  * @param[in] promptName {PromptCommandName} Active bundled prompt name.
953
+ * @param[in] projectBase {string} Absolute original project base path used to resolve the prompt log directory.
767
954
  * @param[in] payload {unknown} Exact `before_provider_request` provider request payload.
768
955
  * @return {void} No return value.
769
956
  * @satisfies REQ-416, REQ-421
770
957
  */
771
958
  export function capturePromptProviderRequestForDebug(
772
959
  config: UseReqConfig,
773
- workflowState: DebugWorkflowState,
774
960
  promptName: PromptCommandName,
961
+ projectBase: string,
775
962
  payload: unknown,
776
963
  ): void {
777
- if (!shouldLogDebugPromptContent(config, workflowState, promptName)) {
964
+ if (!shouldLogDebugPromptContent(config, "unknown", promptName)) {
778
965
  return;
779
966
  }
780
- let payloadText: string;
781
- try {
782
- payloadText = JSON.stringify(payload, null, 2) ?? "";
783
- } catch {
784
- return;
785
- }
786
- getProcessScopedPromptErrorCaptureStore().capturedPromptErrorPayload = { promptName, payloadText };
967
+ getProcessScopedPromptErrorCaptureStore().capturedPromptErrorPayload = {
968
+ promptName,
969
+ payloadText: serializePromptProviderPayload(payload),
970
+ projectBase,
971
+ logDirectory: resolveDebugPromptsLogPath(projectBase, config),
972
+ };
787
973
  }
788
974
 
789
975
  /**
790
976
  * @brief Writes the captured provider request payload as one error-stage prompt debug file for the failing prompt.
791
- * @details Consumes the process-scoped capture for the supplied prompt, discards it after every write attempt so stale payloads never reach later runs, applies the identical global-debug, prompt-debug, and workflow-state gating used by dispatched prompt content files, and writes the exact captured payload into one `<timestamp>-<req-command>-error-<status>` file beside the initial dispatched prompt file under the configured prompt log path. Runtime is dominated by one directory creation plus one file write when a matching capture exists and O(1) otherwise. Side effects include directory creation and file creation only for enabled matching failures.
792
- * @param[in] projectBase {string} Absolute original project base path.
793
- * @param[in] config {UseReqConfig} Effective project configuration.
794
- * @param[in] workflowState {DebugWorkflowState} Current workflow state.
795
- * @param[in] promptName {PromptCommandName} Active bundled prompt name.
977
+ * @details Consumes the process-scoped capture, discards it after every write attempt so stale payloads never reach later runs, rejects failing statuses below 400, and writes the exact captured payload into one `<timestamp>-<req-command>-error-<status>` file inside the captured log directory beside the initial dispatched prompt artifacts. All write inputs come from the capture itself, so the flush requires no live workflow state, cached configuration, or active controller prompt request. Runtime is dominated by one directory creation plus one file write when a capture exists and O(1) otherwise. Side effects include directory creation and file creation only when a capture exists and the status is an error status.
796
978
  * @param[in] errorCode {number} Failing provider response status, such as `400`.
797
979
  * @return {boolean} `true` when the provider error payload file is written; otherwise `false`.
798
980
  * @satisfies REQ-422, REQ-423, REQ-424, REQ-425
799
981
  */
800
- export function flushCapturedPromptErrorPayload(
801
- projectBase: string,
802
- config: UseReqConfig,
803
- workflowState: DebugWorkflowState,
804
- promptName: PromptCommandName,
805
- errorCode: number,
806
- ): boolean {
982
+ export function flushCapturedPromptErrorPayload(errorCode: number): boolean {
807
983
  const captureStore = getProcessScopedPromptErrorCaptureStore();
808
984
  const captured = captureStore.capturedPromptErrorPayload;
809
985
  captureStore.capturedPromptErrorPayload = undefined;
810
- if (!captured || captured.promptName !== promptName) {
811
- return false;
812
- }
813
- if (!shouldLogDebugPromptContent(config, workflowState, promptName)) {
986
+ if (!captured || !Number.isFinite(errorCode) || errorCode < 400) {
814
987
  return false;
815
988
  }
816
- const logDirectory = resolveDebugPromptsLogPath(projectBase, config);
817
989
  try {
818
- fs.mkdirSync(logDirectory, { recursive: true });
990
+ fs.mkdirSync(captured.logDirectory, { recursive: true });
819
991
  fs.writeFileSync(
820
- path.join(logDirectory, formatPromptDebugErrorFileName(promptName, errorCode)),
992
+ path.join(captured.logDirectory, formatPromptDebugErrorFileName(captured.promptName, errorCode)),
821
993
  captured.payloadText,
822
994
  "utf8",
823
995
  );
@@ -826,3 +998,13 @@ export function flushCapturedPromptErrorPayload(
826
998
  return false;
827
999
  }
828
1000
  }
1001
+
1002
+ /**
1003
+ * @brief Discards any captured provider request payload for the ended prompt run.
1004
+ * @details Clears the process-scoped capture unconditionally so leftover payloads from a finished `req-*` prompt run never flush into later provider errors raised outside extension-owned prompt orchestration. Runtime is O(1). Side effect: mutates the process-scoped capture store.
1005
+ * @return {void} No return value.
1006
+ * @satisfies REQ-421, REQ-425
1007
+ */
1008
+ export function discardCapturedPromptErrorPayload(): void {
1009
+ getProcessScopedPromptErrorCaptureStore().capturedPromptErrorPayload = undefined;
1010
+ }
package/src/index.ts CHANGED
@@ -134,9 +134,11 @@ import {
134
134
  DEFAULT_DEBUG_TOOL_COMMANDS_ENABLED,
135
135
  DEFAULT_DEBUG_WORKFLOW_EVENTS,
136
136
  capturePromptProviderRequestForDebug,
137
+ discardCapturedPromptErrorPayload,
137
138
  flushCapturedPromptErrorPayload,
138
139
  logDebugPromptContent,
139
140
  logDebugPromptEvent,
141
+ logDebugPromptRequest,
140
142
  logDebugPromptWorkflowEvent,
141
143
  logDebugToolExecution,
142
144
  normalizeDebugEnabledPrompts,
@@ -148,6 +150,8 @@ import {
148
150
  normalizeDebugStatusChanges,
149
151
  normalizeDebugToolCommandsEnabled,
150
152
  normalizeDebugWorkflowEvents,
153
+ resolveProviderErrorFlushStatusFromMessage,
154
+ setRuntimeDebugEnabled,
151
155
  shouldLogDebugPromptWorkflowState,
152
156
  type DebugLogOnStatus,
153
157
  type DebugWorkflowState,
@@ -1445,14 +1449,14 @@ async function finalizeMatchedPromptSuccess(
1445
1449
 
1446
1450
  /**
1447
1451
  * @brief Handles one intercepted pi lifecycle hook for pi-usereq status updates.
1448
- * @details Applies session-start-specific resource validation, project-config refresh, idle-time shared-tokenizer and context-file measurement pre-warm, startup-tool enablement, and selected debug-tool logging before forwarding the originating hook name and payload into the shared `updateExtensionStatus(...)` pipeline. Before `agent_start`, re-verifies any prepared prompt execution session switch. On `agent_end`, dispatches configured command-notify, sound, and prompt-specific Pushover effects, logs dedicated workflow-closure diagnostics, classifies the prompt outcome, and for every matched successful worktree-backed completion defers the restore switch, stash-assisted merge, and worktree deletion to `agent_settled` when the running pi host supports that 0.80.4+ event (because its `switchSession` awaits the active agent run to become idle and would deadlock inside `agent_end`), or executes the finalization directly at `agent_end` when the host does not emit `agent_settled`. On `agent_settled`, reuses persisted replacement-session command contexts when event contexts omit `switchSession()`, executes the deferred stash-assisted merge-and-delete finalization path, emits a warning-only notification when restored `base-path` changes are reapplied after merge, tolerates stale replacement-session notification contexts after session replacement, retains the worktree plus notifies closure failure for interrupted or failed outcomes, logs selected prompt workflow transitions, and transitions workflow state through `merging`, `error`, and `idle` as required. On `session_shutdown`, captures pre-update prompt snapshots so workflow-shutdown diagnostics and same-runtime command continuation preserve the active prompt workflow state across switch-triggered rebinding, then disposes the shared controller. Runtime is dominated by configuration loading during `session_start` and git finalization during matched successful closure handling; all other hooks are O(1). Side effects include resource checks, active-tool mutation, active-session replacement, status updates, live-ticker disposal on shutdown, optional child-process spawning, outbound HTTPS requests, branch merges, worktree deletion, and optional debug-log writes.
1452
+ * @details Applies session-start-specific resource validation, project-config refresh, idle-time shared-tokenizer and context-file measurement pre-warm, startup-tool enablement, and selected debug-tool logging before forwarding the originating hook name and payload into the shared `updateExtensionStatus(...)` pipeline. After the status update, captures the exact `before_provider_request` provider payload for the live active or pending prompt run with a fresh config fallback when the controller has no cached config, flushes the process-scoped captured payload as one `<timestamp>-<req-command>-error-<status>` file whenever `after_provider_response` reports a status >= 400 or whenever one finalized `message_end` assistant error message carries a leading HTTP status >= 400 (the provider-independent fallback for provider APIs whose SDK clients throw before emitting `after_provider_response` for non-2xx statuses) without requiring live workflow state, and discards the capture at prompt-run end so stale payloads never reach later runs. Before `agent_start`, re-verifies any prepared prompt execution session switch. On `agent_end`, dispatches configured command-notify, sound, and prompt-specific Pushover effects, logs dedicated workflow-closure diagnostics, classifies the prompt outcome, and for every matched successful worktree-backed completion defers the restore switch, stash-assisted merge, and worktree deletion to `agent_settled` when the running pi host supports that 0.80.4+ event (because its `switchSession` awaits the active agent run to become idle and would deadlock inside `agent_end`), or executes the finalization directly at `agent_end` when the host does not emit `agent_settled`. On `agent_settled`, reuses persisted replacement-session command contexts when event contexts omit `switchSession()`, executes the deferred stash-assisted merge-and-delete finalization path, emits a warning-only notification when restored `base-path` changes are reapplied after merge, tolerates stale replacement-session notification contexts after session replacement, retains the worktree plus notifies closure failure for interrupted or failed outcomes, logs selected prompt workflow transitions, and transitions workflow state through `merging`, `error`, and `idle` as required. On `session_shutdown`, captures pre-update prompt snapshots so workflow-shutdown diagnostics and same-runtime command continuation preserve the active prompt workflow state across switch-triggered rebinding, then disposes the shared controller. Runtime is dominated by configuration loading during `session_start` and git finalization during matched successful closure handling; all other hooks are O(1). Side effects include resource checks, active-tool mutation, active-session replacement, status updates, live-ticker disposal on shutdown, optional child-process spawning, outbound HTTPS requests, branch merges, worktree deletion, and optional debug-log writes.
1449
1453
  * @param[in] pi {ExtensionAPI} Active extension API instance.
1450
1454
  * @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
1451
1455
  * @param[in] hookName {PiUsereqStatusHookName} Intercepted hook name.
1452
1456
  * @param[in] event {unknown} Hook payload forwarded by pi.
1453
1457
  * @param[in] ctx {ExtensionContext} Active extension context.
1454
1458
  * @return {Promise<void>} Promise resolved when hook processing completes.
1455
- * @satisfies REQ-117, REQ-118, REQ-119, REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-176, REQ-178, REQ-184, REQ-185, REQ-186, REQ-187, REQ-208, REQ-209, REQ-221, REQ-228, REQ-229, REQ-230, REQ-244, REQ-245, REQ-246, REQ-247, REQ-276, REQ-277, REQ-278, REQ-279, REQ-280, REQ-291, REQ-292
1459
+ * @satisfies REQ-117, REQ-118, REQ-119, REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-176, REQ-178, REQ-184, REQ-185, REQ-186, REQ-187, REQ-208, REQ-209, REQ-221, REQ-228, REQ-229, REQ-230, REQ-244, REQ-245, REQ-246, REQ-247, REQ-276, REQ-277, REQ-278, REQ-279, REQ-280, REQ-291, REQ-292, REQ-421, REQ-422, REQ-425
1456
1460
  */
1457
1461
  async function handleExtensionStatusEvent(
1458
1462
  pi: ExtensionAPI,
@@ -1511,27 +1515,45 @@ async function handleExtensionStatusEvent(
1511
1515
  }
1512
1516
  : undefined;
1513
1517
  updateExtensionStatus(statusController, hookName, event, ctx);
1514
- if (hookName === "before_provider_request" && activePromptRequest !== undefined && statusController.config) {
1518
+ // Provider payload capture and error flush read the LIVE post-update controller state and fall
1519
+ // back to a fresh config load: after switchSession rebinds the extension, the entry-read
1520
+ // `activePromptRequest` snapshot can be stale, `agent_start` promotion may lag one hook, and the
1521
+ // replacement-session instance may not have cached config yet. The process-scoped capture store
1522
+ // carries everything the error-stage write needs, so the flush itself is state-independent.
1523
+ const livePromptRequest = statusController.state.activePromptRequest
1524
+ ?? statusController.state.pendingPromptRequest;
1525
+ if (hookName === "before_provider_request" && livePromptRequest !== undefined) {
1515
1526
  const providerRequestEvent = event as { payload?: unknown };
1516
1527
  capturePromptProviderRequestForDebug(
1517
- statusController.config,
1518
- statusController.state.workflowState,
1519
- activePromptRequest.promptName,
1528
+ statusController.config ?? loadProjectConfig(resolveLiveBootstrapCwd(ctx.cwd)),
1529
+ livePromptRequest.promptName,
1530
+ livePromptRequest.basePath,
1520
1531
  providerRequestEvent.payload,
1521
1532
  );
1522
1533
  }
1523
- if (hookName === "after_provider_response" && activePromptRequest !== undefined && statusController.config) {
1534
+ if (hookName === "after_provider_response") {
1524
1535
  const providerResponseEvent = event as { status?: unknown };
1525
1536
  if (typeof providerResponseEvent.status === "number" && providerResponseEvent.status >= 400) {
1526
- flushCapturedPromptErrorPayload(
1527
- activePromptRequest.basePath,
1528
- statusController.config,
1529
- statusController.state.workflowState,
1530
- activePromptRequest.promptName,
1531
- providerResponseEvent.status,
1532
- );
1537
+ flushCapturedPromptErrorPayload(providerResponseEvent.status);
1538
+ }
1539
+ }
1540
+ // Provider APIs whose SDK clients throw on non-2xx statuses (the OpenAI-compatible family,
1541
+ // including `zai`) never reach their `onResponse` hook for failing responses, so
1542
+ // `after_provider_response` stays silent exactly when REQ-422 must fire. The finalized
1543
+ // error assistant message is the provider-independent signal: the pi agent loop emits
1544
+ // `message_end` for every failed provider request with the HTTP status embedded in
1545
+ // `errorMessage` by `formatProviderError`.
1546
+ if (hookName === "message_end") {
1547
+ const providerErrorStatus = resolveProviderErrorFlushStatusFromMessage(
1548
+ (event as { message?: unknown }).message,
1549
+ );
1550
+ if (providerErrorStatus !== undefined) {
1551
+ flushCapturedPromptErrorPayload(providerErrorStatus);
1533
1552
  }
1534
1553
  }
1554
+ if (hookName === "agent_end" || hookName === "agent_settled") {
1555
+ discardCapturedPromptErrorPayload();
1556
+ }
1535
1557
  if (hookName === "tool_result" && statusController.config) {
1536
1558
  const toolEvent = event as {
1537
1559
  toolName?: string;
@@ -1806,13 +1828,14 @@ function getDebugToolToggleNames(): PiUsereqStartupToolName[] {
1806
1828
 
1807
1829
  /**
1808
1830
  * @brief Restores the debug configuration subtree to its documented defaults.
1809
- * @details Resets global debug enablement, log path, prompt-content debug enablement plus prompt log path, tool-wrapper command registration, workflow-state filter, dedicated workflow-event logging, and selected tool plus prompt debug toggles without mutating unrelated settings. Runtime is O(1). Side effect: mutates `config`.
1831
+ * @details Resets runtime debug enablement in both the effective config object and the process-scoped runtime debug store, then resets log path, prompt-content debug enablement plus prompt log path, tool-wrapper command registration, workflow-state filter, dedicated workflow-event logging, and selected tool plus prompt debug toggles without mutating unrelated settings. Runtime is O(1). Side effect: mutates `config` and process-scoped runtime debug state.
1810
1832
  * @param[in,out] config {UseReqConfig} Mutable configuration object.
1811
1833
  * @return {void} No return value.
1812
- * @satisfies REQ-236, REQ-237, REQ-238, REQ-239, REQ-195, REQ-277, REQ-322, REQ-413, REQ-414, REQ-420
1834
+ * @satisfies REQ-236, REQ-237, REQ-238, REQ-239, REQ-195, REQ-277, REQ-322, REQ-413, REQ-414, REQ-420, REQ-426
1813
1835
  */
1814
1836
  function resetDebugConfigToDefaults(config: UseReqConfig): void {
1815
1837
  config.DEBUG_ENABLED = "disable";
1838
+ setRuntimeDebugEnabled("disable");
1816
1839
  config.DEBUG_LOG_FILE = DEFAULT_DEBUG_LOG_FILE;
1817
1840
  config.DEBUG_PROMPTS_ENABLED = DEFAULT_DEBUG_PROMPTS_ENABLED;
1818
1841
  config.DEBUG_PROMPTS_LOG_PATH = DEFAULT_DEBUG_PROMPTS_LOG_PATH;
@@ -2014,12 +2037,12 @@ function buildDebugMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice
2014
2037
 
2015
2038
  /**
2016
2039
  * @brief Runs the interactive Debug submenu.
2017
- * @details Lets the user toggle global debug enablement, prompt-content debug enablement plus prompt log path, tool-wrapper command registration, debug file and workflow filters, dedicated workflow-event logging, per-tool selectors, and per-prompt selectors while preserving row focus across re-renders. Runtime depends on user interaction count. Side effects include UI updates, config mutation, and optional debug command registration.
2040
+ * @details Lets the user toggle runtime global debug enablement (stored only in the process-scoped runtime debug store and never persisted), prompt-content debug enablement plus prompt log path, tool-wrapper command registration, debug file and workflow filters, dedicated workflow-event logging, per-tool selectors, and per-prompt selectors while preserving row focus across re-renders. Runtime depends on user interaction count. Side effects include UI updates, config mutation, process-scoped runtime debug state mutation, and optional debug command registration.
2018
2041
  * @param[in] pi {ExtensionAPI} Active extension API instance.
2019
2042
  * @param[in] ctx {ExtensionCommandContext} Active command context.
2020
2043
  * @param[in,out] config {UseReqConfig} Mutable configuration object.
2021
2044
  * @return {Promise<void>} Promise resolved when the submenu closes.
2022
- * @satisfies REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243, REQ-192, REQ-193, REQ-195, REQ-277, REQ-321, REQ-322, REQ-323, REQ-413, REQ-414, REQ-415, REQ-420
2045
+ * @satisfies REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243, REQ-192, REQ-193, REQ-195, REQ-277, REQ-321, REQ-322, REQ-323, REQ-413, REQ-414, REQ-415, REQ-420, REQ-426
2023
2046
  */
2024
2047
  async function configureDebugMenu(
2025
2048
  pi: ExtensionAPI,
@@ -2044,6 +2067,7 @@ async function configureDebugMenu(
2044
2067
  onChange: (choiceId, newValue) => {
2045
2068
  if (choiceId === "debug-enabled") {
2046
2069
  config.DEBUG_ENABLED = newValue === "enable" ? "enable" : "disable";
2070
+ setRuntimeDebugEnabled(config.DEBUG_ENABLED);
2047
2071
  onConfigChange();
2048
2072
  ctx.ui.notify(`Debug ${config.DEBUG_ENABLED}`, "info");
2049
2073
  return;
@@ -2110,6 +2134,7 @@ async function configureDebugMenu(
2110
2134
 
2111
2135
  if (choice === "debug-enabled") {
2112
2136
  config.DEBUG_ENABLED = config.DEBUG_ENABLED === "enable" ? "disable" : "enable";
2137
+ setRuntimeDebugEnabled(config.DEBUG_ENABLED);
2113
2138
  onConfigChange();
2114
2139
  ctx.ui.notify(`Debug ${config.DEBUG_ENABLED}`, "info");
2115
2140
  continue;
@@ -3453,11 +3478,11 @@ function registerReqReferencesCommand(
3453
3478
 
3454
3479
  /**
3455
3480
  * @brief Registers bundled prompt-backed commands with the extension.
3456
- * @details Creates one prompt-template-backed `req-<prompt>` command per bundled prompt name. Each handler rejects non-`idle` workflow state by transitioning the shared workflow state to `error` before command-side preflight, otherwise transitions the shared workflow state through `checking`, `error`, and `running`, runs dedicated prompt-command git and required-doc preflight checks, optionally prepares a dedicated worktree execution plan using the active session directory, persists the prompt metadata needed for switch-triggered rebinding, switches the active session to the verified execution cwd before prompt handoff, logs dedicated workflow-activation diagnostics, renders the prompt, starts prompt delivery into the forked active session, records `running` immediately after delivery handoff begins, saves the exact delivered prompt content into the debug prompt log path when prompt debug logging is enabled, and then awaits the wrapped prompt-delivery promise whose stale post-restore rejections are suppressed. Runtime is O(p) for registration; handler cost depends on prompt preflight, worktree preparation, session switching, prompt rendering, prompt dispatch, and optional debug logging. Side effects include command registration, status-controller mutation, worktree creation, active-session replacement, optional worktree rollback, optional prompt-content debug file creation, user-message delivery during execution, and optional debug-log writes.
3481
+ * @details Creates one prompt-template-backed `req-<prompt>` command per bundled prompt name. Each handler rejects non-`idle` workflow state by transitioning the shared workflow state to `error` before command-side preflight, otherwise transitions the shared workflow state through `checking`, `error`, and `running`, runs dedicated prompt-command git and required-doc preflight checks, optionally prepares a dedicated worktree execution plan using the active session directory, persists the prompt metadata needed for switch-triggered rebinding, switches the active session to the verified execution cwd before prompt handoff, logs dedicated workflow-activation diagnostics, renders the prompt, starts prompt delivery into the forked active session, records `running` immediately after delivery handoff begins, saves the exact command invocation summary plus the delivered prompt content into the debug prompt log path as separate `-request` and `-prompt` files when prompt debug logging is enabled, and then awaits the wrapped prompt-delivery promise whose stale post-restore rejections are suppressed. Runtime is O(p) for registration; handler cost depends on prompt preflight, worktree preparation, session switching, prompt rendering, prompt dispatch, and optional debug logging. Side effects include command registration, status-controller mutation, worktree creation, active-session replacement, optional worktree rollback, optional prompt-debug file creation, user-message delivery during execution, and optional debug-log writes.
3457
3482
  * @param[in] pi {ExtensionAPI} Active extension API instance.
3458
3483
  * @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
3459
3484
  * @return {void} No return value.
3460
- * @satisfies REQ-004, REQ-067, REQ-068, REQ-169, REQ-200, REQ-201, REQ-202, REQ-203, REQ-206, REQ-207, REQ-219, REQ-220, REQ-221, REQ-224, REQ-225, REQ-226, REQ-227, REQ-245, REQ-246, REQ-247, REQ-277, REQ-281, REQ-377, REQ-416, REQ-417, REQ-418
3485
+ * @satisfies REQ-004, REQ-067, REQ-068, REQ-169, REQ-200, REQ-201, REQ-202, REQ-203, REQ-206, REQ-207, REQ-219, REQ-220, REQ-221, REQ-224, REQ-225, REQ-226, REQ-227, REQ-245, REQ-246, REQ-247, REQ-277, REQ-281, REQ-377, REQ-416, REQ-417, REQ-418, REQ-427, REQ-428
3461
3486
  */
3462
3487
  function registerPromptCommands(
3463
3488
  pi: ExtensionAPI,
@@ -3563,6 +3588,13 @@ function registerPromptCommands(
3563
3588
  promptName,
3564
3589
  "running",
3565
3590
  );
3591
+ logDebugPromptRequest(
3592
+ projectBase,
3593
+ config,
3594
+ statusController.state.workflowState,
3595
+ promptName,
3596
+ commandSummary,
3597
+ );
3566
3598
  logDebugPromptContent(
3567
3599
  projectBase,
3568
3600
  config,