pi-usereq 0.81.0 → 0.82.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.81.0)
1
+ # PI-useReq/pi-usereq (0.82.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+">
@@ -335,7 +335,7 @@ The extension renders a status field in the pi status line showing: extension id
335
335
  pi -p "Rewrite this function" --append-system-prompt /tmp/PI-useReq/20261009072235123-req-implement
336
336
  ```
337
337
 
338
- - **Provider error logging** — when a dispatched `req-*` run receives a provider response with status >= 400, pi-usereq writes the exact failing request payload into `<timestamp>-<command>-error-<status>` (raw provider-request JSON), the provider error message text into `<timestamp>-<command>-error-<status>-response`, and replay metadata into `<timestamp>-<command>-error-<status>-replay.json`, all beside the run's `-request` and `-prompt` files. The `-replay.json` sidecar carries the provider, base URL, model id, thinking level, context window, max output tokens, the retained execution-session file, and ready-to-use replay commands. You can re-generate the failure for immediate pi CLI debugging:
338
+ - **Provider error logging** — when a dispatched `req-*` run receives a provider response with status >= 400, pi-usereq writes the exact failing request payload into `<timestamp>-<command>-error-<status>` (raw provider-request JSON), the provider error message text into `<timestamp>-<command>-error-<status>-response`, a byte-for-byte snapshot of the retained execution session into `<timestamp>-<command>-error-<status>-session.jsonl`, and replay metadata into `<timestamp>-<command>-error-<status>-replay.json`, all beside the run's `-request` and `-prompt` files. The `-replay.json` sidecar carries the provider, base URL, model id, thinking level, context window, max output tokens, the retained execution-session file, and ready-to-use replay commands; `replayCommands.piFork` targets the log-local `-session.jsonl` snapshot, which freezes the transcript exactly at the failing provider request (before any context edit or compaction). You can re-generate the failure for immediate pi CLI debugging:
339
339
 
340
340
  ```bash
341
341
  # Re-send the exact failing provider request (the payload file is raw request JSON)
@@ -351,8 +351,11 @@ The extension renders a status field in the pi status line showing: extension id
351
351
  pi --no-session --model '~deepseek/deepseek-flash-latest' --thinking high \
352
352
  -p "$(cat /tmp/PI-useReq/20261012070647580-req-analyze-prompt)"
353
353
 
354
- # Fork the retained execution session (kept on failed runs) and continue it
355
- pi --fork /home/ogekuri/.pi/agent/sessions/--home-ogekuri-Git-PI-useReq--/2026-10-12T05-06-46-788Z_01a12e81-9584-72f4-92e3-19008d04cccf.jsonl
354
+ # Resume the exact failing state using only log files, then re-trigger the failing request:
355
+ # the -session.jsonl snapshot is the execution session frozen at the 400, so any
356
+ # continuation message re-sends the same oversized request and reproduces the error
357
+ pi --fork /tmp/PI-useReq/20261012091841114-req-analyze-error-400-session.jsonl \
358
+ --model '~deepseek/deepseek-flash-latest' --thinking high "continue"
356
359
  ```
357
360
 
358
361
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-usereq",
3
- "version": "0.81.0",
3
+ "version": "0.82.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Ogekuri/PI-useReq.git"
@@ -966,6 +966,23 @@ export function formatPromptDebugErrorReplayFileName(
966
966
  return `${formatPromptDebugErrorFileName(promptName, errorCode, date)}-replay.json`;
967
967
  }
968
968
 
969
+ /**
970
+ * @brief Builds one provider error session-snapshot debug filename for one bundled prompt.
971
+ * @details Appends the `-session.jsonl` suffix to the shared `<timestamp>-<req-command>-error-<status>` shape so the byte-for-byte execution-session copy stays attributable to the exact failing request payload file written with the identical write-time timestamp and remains directly consumable by `pi --fork` without files outside the prompt log directory. Runtime is O(1). No external state is mutated.
972
+ * @param[in] promptName {PromptCommandName} Bundled prompt name.
973
+ * @param[in] errorCode {number} Failing provider response status, such as `400`.
974
+ * @param[in] date {Date} Write-time timestamp shared with the payload file. Defaults to the current wall-clock time.
975
+ * @return {string} Provider error session-snapshot filename in the `<timestamp>-<req-command>-error-<status>-session.jsonl` shape.
976
+ * @satisfies REQ-423, REQ-432
977
+ */
978
+ export function formatPromptDebugErrorSessionFileName(
979
+ promptName: PromptCommandName,
980
+ errorCode: number,
981
+ date = new Date(),
982
+ ): string {
983
+ return `${formatPromptDebugErrorFileName(promptName, errorCode, date)}-session.jsonl`;
984
+ }
985
+
969
986
  /**
970
987
  * @brief Resolves the failing HTTP status carried by one finalized agent message.
971
988
  * @details Extracts the leading provider error status from one `message_end` assistant message whose `stopReason` equals `error`, accepting the pi `formatProviderError` display shapes (`"<status>: <body>"` and `"<prefix> (<status>): <body>"`) plus the raw SDK `error.message` shape (`"<status> <body>"`) surfaced unchanged by the agent run-failure path, 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.
@@ -1112,15 +1129,17 @@ function resolveProviderReplayEndpoint(facts: PromptErrorReplayFacts): string |
1112
1129
 
1113
1130
  /**
1114
1131
  * @brief Builds the replay sidecar JSON text for one flushed provider error payload.
1115
- * @details Serializes the captured provider, base URL, API, model identifier, thinking level, context-window and max-output-token facts, the execution-session file, the dispatched prompt debug file, the flushed request payload file, and ready-to-use replay command lines: one `curl --data @` provider replay of the exact failing request, one pi one-shot replay of the dispatched prompt file, and one `pi --fork` replay of the retained execution session. Command lines are emitted only when their supporting facts exist so every emitted command is directly executable. Runtime is O(n) in serialized sidecar size. No external state is mutated.
1132
+ * @details Serializes the captured provider, base URL, API, model identifier, thinking level, context-window and max-output-token facts, the execution-session file, the dispatched prompt debug file, the flushed request payload file, and ready-to-use replay command lines: one `curl --data @` provider replay of the exact failing request, one pi one-shot replay of the dispatched prompt file, and one `pi --fork` replay that targets the log-local `-session.jsonl` snapshot when supplied and falls back to the retained execution session otherwise. Command lines are emitted only when their supporting facts exist so every emitted command is directly executable. Runtime is O(n) in serialized sidecar size. No external state is mutated.
1116
1133
  * @param[in] captured {CapturedPromptErrorPayload} Consumed capture supplying replay facts and artifact paths.
1117
1134
  * @param[in] requestFileName {string} Payload filename written with the same write-time timestamp.
1135
+ * @param[in] sessionSnapshotFileName {string | undefined} Log-local `-session.jsonl` snapshot filename written with the same write-time timestamp; `undefined` falls back to the retained execution-session path.
1118
1136
  * @return {string} Newline-terminated replay sidecar JSON text.
1119
- * @satisfies REQ-430
1137
+ * @satisfies REQ-430, REQ-433
1120
1138
  */
1121
1139
  function buildPromptErrorReplaySidecarText(
1122
1140
  captured: CapturedPromptErrorPayload,
1123
1141
  requestFileName: string,
1142
+ sessionSnapshotFileName?: string,
1124
1143
  ): string {
1125
1144
  const facts = captured.replayFacts;
1126
1145
  const requestFilePath = path.join(captured.logDirectory, requestFileName);
@@ -1133,10 +1152,13 @@ function buildPromptErrorReplaySidecarText(
1133
1152
  if (captured.promptDebugFile !== undefined && facts.modelId !== undefined) {
1134
1153
  replayCommands.piOneShot = `pi --no-session --model '${facts.modelId}' --thinking ${facts.thinkingLevel ?? "off"} -p "$(cat '${captured.promptDebugFile}')"`;
1135
1154
  }
1136
- if (facts.executionSessionFile !== undefined) {
1155
+ const sessionForkFile = sessionSnapshotFileName !== undefined
1156
+ ? path.join(captured.logDirectory, sessionSnapshotFileName)
1157
+ : facts.executionSessionFile;
1158
+ if (sessionForkFile !== undefined) {
1137
1159
  const modelFlags = facts.modelId === undefined ? "" : ` --model '${facts.modelId}'`;
1138
1160
  const thinkingFlags = facts.thinkingLevel === undefined ? "" : ` --thinking ${facts.thinkingLevel}`;
1139
- replayCommands.piFork = `pi --fork '${facts.executionSessionFile}'${modelFlags}${thinkingFlags}`;
1161
+ replayCommands.piFork = `pi --fork '${sessionForkFile}'${modelFlags}${thinkingFlags}`;
1140
1162
  }
1141
1163
  const sidecar = {
1142
1164
  provider: facts.provider ?? null,
@@ -1156,11 +1178,11 @@ function buildPromptErrorReplaySidecarText(
1156
1178
 
1157
1179
  /**
1158
1180
  * @brief Writes the captured provider request payload plus its response text and replay sidecar as error-stage prompt debug files.
1159
- * @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 that stays raw provider-request JSON usable with `curl --data @`, the provider error text into one identically timestamped `-response` file when supplied, and the replay sidecar into one identically timestamped `-replay.json` file inside the captured log directory beside the initial dispatched prompt artifacts. Records the flushed location so a later finalized `message_end` error message can still append the `-response` file when `after_provider_response` fired first without error text. 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 at most three file writes 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.
1181
+ * @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 that stays raw provider-request JSON usable with `curl --data @`, the provider error text into one identically timestamped `-response` file when supplied, one identically timestamped byte-for-byte `-session.jsonl` copy of the retained execution session so `pi --fork` can resume the exact failing state from the prompt log directory alone, and the replay sidecar into one identically timestamped `-replay.json` file inside the captured log directory beside the initial dispatched prompt artifacts. Records the flushed location so a later finalized `message_end` error message can still append the `-response` file when `after_provider_response` fired first without error text. 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 at most four file writes 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.
1160
1182
  * @param[in] errorCode {number} Failing provider response status, such as `400`.
1161
1183
  * @param[in] errorResponseText {string | undefined} Finalized provider error message text when already available; the `-response` file is written later when omitted.
1162
1184
  * @return {boolean} `true` when the provider error payload file set is written; otherwise `false`.
1163
- * @satisfies REQ-422, REQ-423, REQ-424, REQ-425, REQ-429, REQ-430, REQ-431
1185
+ * @satisfies REQ-422, REQ-423, REQ-424, REQ-425, REQ-429, REQ-430, REQ-431, REQ-432, REQ-433
1164
1186
  */
1165
1187
  export function flushCapturedPromptErrorPayload(errorCode: number, errorResponseText?: string): boolean {
1166
1188
  const captureStore = getProcessScopedPromptErrorCaptureStore();
@@ -1185,9 +1207,13 @@ export function flushCapturedPromptErrorPayload(errorCode: number, errorResponse
1185
1207
  errorResponseText,
1186
1208
  );
1187
1209
  }
1210
+ const sessionSnapshotFileName = copyPromptErrorSessionSnapshotFile(
1211
+ captured,
1212
+ formatPromptDebugErrorSessionFileName(captured.promptName, errorCode, writeTime),
1213
+ );
1188
1214
  fs.writeFileSync(
1189
1215
  path.join(captured.logDirectory, formatPromptDebugErrorReplayFileName(captured.promptName, errorCode, writeTime)),
1190
- buildPromptErrorReplaySidecarText(captured, payloadFileName),
1216
+ buildPromptErrorReplaySidecarText(captured, payloadFileName, sessionSnapshotFileName),
1191
1217
  "utf8",
1192
1218
  );
1193
1219
  captureStore.lastFlushedPromptError = {
@@ -1220,6 +1246,31 @@ function writePromptProviderErrorResponseFile(
1220
1246
  fs.writeFileSync(path.join(logDirectory, fileName), `${errorResponseText}\n`, "utf8");
1221
1247
  }
1222
1248
 
1249
+ /**
1250
+ * @brief Copies the retained execution-session file beside one flushed provider error payload.
1251
+ * @details Duplicates the captured execution-session file byte-for-byte into the captured log directory under the requested `-session.jsonl` filename synchronously during the error-stage flush, so the snapshot freezes the transcript exactly at the state that produced the failing provider request before pi's `context_edit` or compaction appends further entries and stays directly consumable by `pi --fork` without any file outside the prompt log directory. Degrades to `undefined` on any copy failure so the replay sidecar falls back to the retained execution-session path. Runtime is dominated by one directory creation plus one file copy. Side effects include directory creation and file creation.
1252
+ * @param[in] captured {CapturedPromptErrorPayload} Consumed capture supplying the log directory and execution-session replay facts.
1253
+ * @param[in] snapshotFileName {string} Session-snapshot filename sharing the payload write-time timestamp.
1254
+ * @return {string | undefined} Written snapshot filename when the copy succeeds; otherwise `undefined`.
1255
+ * @satisfies REQ-432
1256
+ */
1257
+ function copyPromptErrorSessionSnapshotFile(
1258
+ captured: CapturedPromptErrorPayload,
1259
+ snapshotFileName: string,
1260
+ ): string | undefined {
1261
+ const sessionFile = captured.replayFacts.executionSessionFile;
1262
+ if (sessionFile === undefined || sessionFile.length === 0) {
1263
+ return undefined;
1264
+ }
1265
+ try {
1266
+ fs.mkdirSync(captured.logDirectory, { recursive: true });
1267
+ fs.copyFileSync(sessionFile, path.join(captured.logDirectory, snapshotFileName));
1268
+ return snapshotFileName;
1269
+ } catch {
1270
+ return undefined;
1271
+ }
1272
+ }
1273
+
1223
1274
  /**
1224
1275
  * @brief Appends the provider error response text beside one already flushed error payload file.
1225
1276
  * @details Providers that fire `after_provider_response` before surfacing their error text flush the payload without a `-response` file, so this helper writes the finalized `message_end` error message beside the recorded flush location when the failing status matches. Runtime is dominated by one directory creation plus one file write when a matching record exists and O(1) otherwise. Side effects include directory creation and file creation only for a matching record.