oira666_pi-subagent 0.3.15 → 0.3.16
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 +9 -7
- package/package.json +1 -1
- package/runner.ts +21 -4
package/README.md
CHANGED
|
@@ -171,12 +171,14 @@ semantics. Embedded hosts without a script entrypoint can use
|
|
|
171
171
|
## Subagent Liveness Timeouts
|
|
172
172
|
|
|
173
173
|
A delegated process cannot block its parents forever. The runner applies a
|
|
174
|
-
startup timeout before the first model turn and
|
|
175
|
-
after startup.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
174
|
+
startup timeout before the first model turn and an agent-inactivity timeout
|
|
175
|
+
after startup. The inactivity watchdog is paused while any tool call is in
|
|
176
|
+
progress, so tool executions can run for unlimited time; a fresh full idle
|
|
177
|
+
window starts after the last concurrent tool finishes. Agent/turn events and
|
|
178
|
+
changed nested-agent state reset the idle timer; repeated unchanged progress
|
|
179
|
+
heartbeats do not. On timeout or cancellation, the runner terminates the child
|
|
180
|
+
process tree and bounds cleanup; even if a wedged OS process never reports
|
|
181
|
+
`close`, the tool returns an error result so every waiting parent can settle.
|
|
180
182
|
|
|
181
183
|
RPC completion is based on Pi's `agent_settled` event—not `agent_end`.
|
|
182
184
|
`agent_end` is only a low-level run boundary and may be followed by Pi's normal
|
|
@@ -188,7 +190,7 @@ reported immediately as failures.
|
|
|
188
190
|
| --- | --- | --- |
|
|
189
191
|
| `PI_SUBAGENT_STARTUP_TIMEOUT` | `120000` | Milliseconds allowed to reach the first model turn; `0` disables |
|
|
190
192
|
| `PI_SUBAGENT_STARTUP_RETRIES` | `2` | Fresh retries after a startup timeout |
|
|
191
|
-
| `PI_SUBAGENT_IDLE_TIMEOUT` | `1200000` | Milliseconds without
|
|
193
|
+
| `PI_SUBAGENT_IDLE_TIMEOUT` | `1200000` | Milliseconds without agent activity after startup, excluding time spent in ongoing tool calls; `0` disables |
|
|
192
194
|
|
|
193
195
|
## Timestamps & Status Footer
|
|
194
196
|
|
package/package.json
CHANGED
package/runner.ts
CHANGED
|
@@ -45,8 +45,9 @@ const RETRY_WAIT_GRACE_MS = 60_000;
|
|
|
45
45
|
const DEFAULT_STARTUP_TIMEOUT_MS = 120_000; // only for startup (before first assistant turn)
|
|
46
46
|
const SUBAGENT_STARTUP_TIMEOUT_ENV = "PI_SUBAGENT_STARTUP_TIMEOUT";
|
|
47
47
|
// Once startup succeeds, a child can otherwise remain alive forever if Pi loses
|
|
48
|
-
// the next model/RPC turn after a tool result. Bound
|
|
49
|
-
// repeated progress heartbeats)
|
|
48
|
+
// the next model/RPC turn after a tool result. Bound agent inactivity (not
|
|
49
|
+
// repeated progress heartbeats), but never while one of its tools is executing:
|
|
50
|
+
// tool runtimes are intentionally unbounded.
|
|
50
51
|
const DEFAULT_IDLE_TIMEOUT_MS = 20 * 60_000;
|
|
51
52
|
const SUBAGENT_IDLE_TIMEOUT_ENV = "PI_SUBAGENT_IDLE_TIMEOUT";
|
|
52
53
|
// A startup timeout is almost always a transient cold-start stall (slow cli /
|
|
@@ -874,6 +875,10 @@ export async function runAgentSubprocess(opts: RunAgentOptions): Promise<SingleR
|
|
|
874
875
|
let agentSettled = false;
|
|
875
876
|
let forcedExitCode: number | undefined;
|
|
876
877
|
let lastNestedProgressSignature: string | undefined;
|
|
878
|
+
// Track only tool calls from this process attempt. Persisted live render
|
|
879
|
+
// state may contain a tool that was interrupted before a resume and must
|
|
880
|
+
// not disable the new process's watchdog forever.
|
|
881
|
+
const activeToolCallIds = new Set<string>();
|
|
877
882
|
let abortHandler: (() => void) | undefined;
|
|
878
883
|
const promptRequestId = `pi-subagent-${process.pid}-${Date.now()}-${attempt}`;
|
|
879
884
|
let steeringRequest = 0;
|
|
@@ -982,12 +987,18 @@ export async function runAgentSubprocess(opts: RunAgentOptions): Promise<SingleR
|
|
|
982
987
|
})();
|
|
983
988
|
|
|
984
989
|
const noteSemanticActivity = (minimumQuietPeriodMs = 0) => {
|
|
990
|
+
if (idleTimer) { clearTimeout(idleTimer); idleTimer = undefined; }
|
|
985
991
|
if (!receivedFirstEvent || idleTimeoutMs === 0 || resolved) return;
|
|
986
|
-
|
|
992
|
+
// The idle timeout measures the agent itself, not tools it has invoked.
|
|
993
|
+
// A tool may legitimately be silent for longer than the configured
|
|
994
|
+
// timeout, so leave the watchdog disarmed until every concurrent tool
|
|
995
|
+
// execution has ended. tool_execution_end will call this again and
|
|
996
|
+
// start a fresh full inactivity window.
|
|
997
|
+
if (activeToolCallIds.size > 0) return;
|
|
987
998
|
const quietPeriodMs = Math.max(idleTimeoutMs, minimumQuietPeriodMs);
|
|
988
999
|
idleTimer = setTimeout(() => {
|
|
989
1000
|
if (resolved) return;
|
|
990
|
-
const message = `Subagent inactivity timeout: no
|
|
1001
|
+
const message = `Subagent inactivity timeout: no agent activity for ${quietPeriodMs}ms.`;
|
|
991
1002
|
forcedExitCode = 1;
|
|
992
1003
|
result.stopReason = "error";
|
|
993
1004
|
result.errorMessage = message;
|
|
@@ -1001,6 +1012,12 @@ export async function runAgentSubprocess(opts: RunAgentOptions): Promise<SingleR
|
|
|
1001
1012
|
let event: any;
|
|
1002
1013
|
try { event = JSON.parse(line); } catch { event = null; }
|
|
1003
1014
|
|
|
1015
|
+
if (event?.type === "tool_execution_start" && typeof event.toolCallId === "string") {
|
|
1016
|
+
activeToolCallIds.add(event.toolCallId);
|
|
1017
|
+
} else if (event?.type === "tool_execution_end" && typeof event.toolCallId === "string") {
|
|
1018
|
+
activeToolCallIds.delete(event.toolCallId);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1004
1021
|
if (
|
|
1005
1022
|
event?.type === "response" &&
|
|
1006
1023
|
event.id === promptRequestId &&
|