@clanker-code/pi-subagents 0.10.7 → 0.11.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/src/wait.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type WaitOutcome = "completed" | "timeout" | "aborted";
1
+ export type WaitOutcome = "completed" | "timeout" | "aborted" | "pending_message";
2
2
 
3
3
  /** Human-readable "Xm Ys" for a duration in seconds. */
4
4
  export function formatWaitTimeout(seconds: number): string {
@@ -8,13 +8,19 @@ export function formatWaitTimeout(seconds: number): string {
8
8
  }
9
9
 
10
10
  /**
11
- * Race an agent completion promise against the configured wait timeout and the
12
- * parent abort signal. The subagent is never aborted here.
11
+ * Race an agent completion promise against the configured wait timeout, the
12
+ * parent abort signal, and an optional pending-message check. The subagent is
13
+ * never aborted here.
14
+ *
15
+ * @param pendingCheck - Optional promise that resolves when the parent session
16
+ * has queued user messages waiting to be delivered. When it resolves, the
17
+ * wait ends early so the parent turn can process the incoming message.
13
18
  */
14
19
  export function raceWait(
15
20
  promise: Promise<string>,
16
21
  signal: AbortSignal | undefined,
17
22
  timeoutSeconds: number,
23
+ pendingCheck?: Promise<void>,
18
24
  ): Promise<WaitOutcome> {
19
25
  return new Promise((resolve) => {
20
26
  let settled = false;
@@ -29,6 +35,7 @@ export function raceWait(
29
35
  const onAbort = () => finish("aborted");
30
36
  signal?.addEventListener("abort", onAbort, { once: true });
31
37
  promise.then(() => finish("completed"));
38
+ pendingCheck?.then(() => finish("pending_message"));
32
39
  });
33
40
  }
34
41
 
@@ -40,5 +47,50 @@ export function waitTimeoutMessage(outcome: WaitOutcome, timeoutSeconds: number)
40
47
  if (outcome === "aborted") {
41
48
  return `Agent is still running. The wait was cancelled by the user (parent turn aborted). The subagent was NOT stopped — it continues in the background.\nCall get_subagent_result with wait: true again to keep waiting, use peek to check progress, or omit wait to check status.`;
42
49
  }
50
+ if (outcome === "pending_message") {
51
+ return `Agent is still running. The wait was interrupted by an incoming user message. The subagent was NOT stopped — it continues in the background.\nThe queued message will be delivered after this tool returns.\nCall get_subagent_result with wait: true again to keep waiting, use peek to check progress, or omit wait to check status.`;
52
+ }
43
53
  return "Agent is still running. Use peek to check recent progress, wait: true to block until it finishes, or check back later.";
44
54
  }
55
+
56
+ /**
57
+ * Create a promise that resolves when the parent session has queued user
58
+ * messages. Polls at the given interval until `hasPendingMessages()` returns
59
+ * true. The caller should race this against the agent completion / timeout.
60
+ */
61
+ export function pollPendingMessages(
62
+ hasPendingMessages: () => boolean,
63
+ intervalMs = 1000,
64
+ ): { promise: Promise<void>; cancel: () => void } {
65
+ let settled = false;
66
+ let resolve!: () => void;
67
+ const promise = new Promise<void>((r) => { resolve = r; });
68
+
69
+ // Check immediately in case a message arrived between the tool call
70
+ // start and this poll setup.
71
+ if (hasPendingMessages()) {
72
+ settled = true;
73
+ resolve();
74
+ return { promise, cancel: () => {} };
75
+ }
76
+
77
+ const timer = setInterval(() => {
78
+ if (settled) return;
79
+ if (hasPendingMessages()) {
80
+ settled = true;
81
+ clearInterval(timer);
82
+ resolve();
83
+ }
84
+ }, intervalMs);
85
+
86
+ return {
87
+ promise,
88
+ cancel: () => {
89
+ if (!settled) {
90
+ settled = true;
91
+ clearInterval(timer);
92
+ resolve();
93
+ }
94
+ },
95
+ };
96
+ }