@hk_net/pi-timestamp 0.1.4 → 0.1.5

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 (2) hide show
  1. package/package.json +3 -3
  2. package/timestamp.ts +27 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hk_net/pi-timestamp",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Pi extension that shows timestamps for user input and agent completion timing",
5
5
  "type": "module",
6
6
  "private": false,
@@ -26,8 +26,8 @@
26
26
  "README.md"
27
27
  ],
28
28
  "peerDependencies": {
29
- "@earendil-works/pi-coding-agent": ">=0.80.1",
30
- "@earendil-works/pi-tui": ">=0.80.1"
29
+ "@earendil-works/pi-coding-agent": ">=0.80.2",
30
+ "@earendil-works/pi-tui": ">=0.80.2"
31
31
  },
32
32
  "pi": {
33
33
  "extensions": [
package/timestamp.ts CHANGED
@@ -32,11 +32,18 @@ function formatDuration(ms: number): string {
32
32
  return `${Math.floor(hrs)}h ${Math.floor(totalMins % 60)}m`;
33
33
  }
34
34
 
35
+ function isTimeoutErrorMessage(message: string | undefined): boolean {
36
+ return /timed? out|timeout/i.test(message ?? "");
37
+ }
38
+
35
39
  export default function (pi: ExtensionAPI) {
36
40
  let taskStartTime: number | undefined;
41
+ let waitingForRetryAfterTimeout = false;
37
42
 
38
- // Track when the agent starts processing
43
+ // Track when the agent starts processing. If Pi is auto-retrying after a timeout,
44
+ // keep the original start time so the eventual completion covers the full task.
39
45
  pi.on("agent_start", async () => {
46
+ if (waitingForRetryAfterTimeout) return;
40
47
  taskStartTime = Date.now();
41
48
  });
42
49
 
@@ -46,16 +53,33 @@ export default function (pi: ExtensionAPI) {
46
53
  pi.on("message_end", async (event, ctx) => {
47
54
  if (event.message.role !== "user") return;
48
55
 
56
+ if (waitingForRetryAfterTimeout) {
57
+ taskStartTime = undefined;
58
+ waitingForRetryAfterTimeout = false;
59
+ }
60
+
49
61
  const ts = event.message.timestamp;
50
62
  if (!ts) return;
51
63
 
52
64
  ctx.ui.notify(`Sent ${formatTime(ts)}`, "info");
53
65
  });
54
66
 
55
- // Show completion timing after agent finishes
56
- pi.on("agent_end", async (_event, ctx) => {
67
+ // Show completion timing after the whole task finishes. If Pi ended this agent loop
68
+ // with a timeout error and will likely auto-retry, wait for the later completion.
69
+ pi.on("agent_end", async (event, ctx) => {
70
+ const lastAssistantMessage = [...event.messages].reverse().find((message) => message.role === "assistant");
71
+ if (
72
+ lastAssistantMessage?.role === "assistant" &&
73
+ lastAssistantMessage.stopReason === "error" &&
74
+ isTimeoutErrorMessage(lastAssistantMessage.errorMessage)
75
+ ) {
76
+ waitingForRetryAfterTimeout = true;
77
+ return;
78
+ }
79
+
57
80
  const startTime = taskStartTime;
58
81
  taskStartTime = undefined;
82
+ waitingForRetryAfterTimeout = false;
59
83
 
60
84
  if (startTime === undefined) return;
61
85