pullfrog 0.1.19 → 0.1.20
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/dist/cli.mjs +118 -158
- package/dist/index.js +117 -157
- package/dist/utils/activity.d.ts +18 -33
- package/dist/utils/subprocess.d.ts +0 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -101235,6 +101235,111 @@ function formatMcpToolRef(agentId, toolName) {
|
|
|
101235
101235
|
|
|
101236
101236
|
// utils/activity.ts
|
|
101237
101237
|
import { performance as performance2 } from "node:perf_hooks";
|
|
101238
|
+
function isMonitorDebugEnabled() {
|
|
101239
|
+
return process.env.ACTIONS_STEP_DEBUG === "true" || process.env.RUNNER_DEBUG === "1" || process.env.LOG_LEVEL === "debug";
|
|
101240
|
+
}
|
|
101241
|
+
var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
|
|
101242
|
+
var AGENT_ACTIVITY_TIMEOUT_MS = 9e5;
|
|
101243
|
+
var DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5e3;
|
|
101244
|
+
var DEBUG_TS_PREFIX = /^(?:\[\d{4}-\d{2}-\d{2}T[^\]]+\]\s+)?/.source;
|
|
101245
|
+
var ACTIVITY_NOISE_PATTERNS = [
|
|
101246
|
+
new RegExp(`${DEBUG_TS_PREFIX}\\[mcp-proxy\\]`),
|
|
101247
|
+
new RegExp(`${DEBUG_TS_PREFIX}\xBB provider error detected`),
|
|
101248
|
+
new RegExp(`${DEBUG_TS_PREFIX}\\[DEBUG\\]\\s+(?:spawn|process) activity `),
|
|
101249
|
+
/^::debug::(?:spawn|process) activity /
|
|
101250
|
+
];
|
|
101251
|
+
function isActivityNoise(chunk) {
|
|
101252
|
+
const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
101253
|
+
if (!text.trim()) return true;
|
|
101254
|
+
return text.split("\n").every((line) => {
|
|
101255
|
+
const trimmed = line.trim();
|
|
101256
|
+
if (!trimmed) return true;
|
|
101257
|
+
return ACTIVITY_NOISE_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
101258
|
+
});
|
|
101259
|
+
}
|
|
101260
|
+
var _lastActivity = performance2.now();
|
|
101261
|
+
function markActivity() {
|
|
101262
|
+
_lastActivity = performance2.now();
|
|
101263
|
+
}
|
|
101264
|
+
function getIdleMs() {
|
|
101265
|
+
return Math.round(performance2.now() - _lastActivity);
|
|
101266
|
+
}
|
|
101267
|
+
function wrapWrite(original, onActivity) {
|
|
101268
|
+
const wrapped = (chunk, encodingOrCb, cb) => {
|
|
101269
|
+
if (!isActivityNoise(chunk)) {
|
|
101270
|
+
onActivity();
|
|
101271
|
+
}
|
|
101272
|
+
if (typeof encodingOrCb === "function") {
|
|
101273
|
+
return original(chunk, encodingOrCb);
|
|
101274
|
+
}
|
|
101275
|
+
return original(chunk, encodingOrCb, cb);
|
|
101276
|
+
};
|
|
101277
|
+
return wrapped;
|
|
101278
|
+
}
|
|
101279
|
+
function startProcessOutputMonitor(ctx) {
|
|
101280
|
+
let timedOut = false;
|
|
101281
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
101282
|
+
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
101283
|
+
process.stdout.write = wrapWrite(originalStdoutWrite, markActivity);
|
|
101284
|
+
process.stderr.write = wrapWrite(originalStderrWrite, markActivity);
|
|
101285
|
+
const debugBypass = (msg) => {
|
|
101286
|
+
if (!isMonitorDebugEnabled()) return;
|
|
101287
|
+
originalStdoutWrite(`[${(/* @__PURE__ */ new Date()).toISOString()}] [DEBUG] ${msg}
|
|
101288
|
+
`);
|
|
101289
|
+
};
|
|
101290
|
+
debugBypass(`process activity monitor started: timeout=${ctx.timeoutMs}ms`);
|
|
101291
|
+
const intervalId = setInterval(() => {
|
|
101292
|
+
const idleMs = getIdleMs();
|
|
101293
|
+
debugBypass(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
|
|
101294
|
+
if (timedOut || idleMs <= ctx.timeoutMs) return;
|
|
101295
|
+
timedOut = true;
|
|
101296
|
+
ctx.onTimeout(idleMs);
|
|
101297
|
+
}, ctx.checkIntervalMs);
|
|
101298
|
+
function stop() {
|
|
101299
|
+
clearInterval(intervalId);
|
|
101300
|
+
process.stdout.write = originalStdoutWrite;
|
|
101301
|
+
process.stderr.write = originalStderrWrite;
|
|
101302
|
+
}
|
|
101303
|
+
return { stop };
|
|
101304
|
+
}
|
|
101305
|
+
function createProcessOutputActivityTimeout(ctx) {
|
|
101306
|
+
markActivity();
|
|
101307
|
+
let rejectFn = null;
|
|
101308
|
+
const promise2 = new Promise((_2, reject) => {
|
|
101309
|
+
rejectFn = reject;
|
|
101310
|
+
});
|
|
101311
|
+
let monitor = null;
|
|
101312
|
+
monitor = startProcessOutputMonitor({
|
|
101313
|
+
timeoutMs: ctx.timeoutMs,
|
|
101314
|
+
checkIntervalMs: ctx.checkIntervalMs,
|
|
101315
|
+
onTimeout: (idleMs) => {
|
|
101316
|
+
if (!rejectFn) return;
|
|
101317
|
+
const idleSec = Math.round(idleMs / 1e3);
|
|
101318
|
+
if (monitor) {
|
|
101319
|
+
monitor.stop();
|
|
101320
|
+
}
|
|
101321
|
+
const reject = rejectFn;
|
|
101322
|
+
rejectFn = null;
|
|
101323
|
+
reject(new Error(`activity timeout: no output for ${idleSec}s`));
|
|
101324
|
+
}
|
|
101325
|
+
});
|
|
101326
|
+
return {
|
|
101327
|
+
promise: promise2,
|
|
101328
|
+
// stop() also disarms forceReject so a late safety-net fire can't reject
|
|
101329
|
+
// the promise after the run has already succeeded.
|
|
101330
|
+
stop: () => {
|
|
101331
|
+
monitor?.stop();
|
|
101332
|
+
rejectFn = null;
|
|
101333
|
+
},
|
|
101334
|
+
forceReject: (reason) => {
|
|
101335
|
+
if (!rejectFn) return;
|
|
101336
|
+
monitor?.stop();
|
|
101337
|
+
const reject = rejectFn;
|
|
101338
|
+
rejectFn = null;
|
|
101339
|
+
reject(new Error(reason));
|
|
101340
|
+
}
|
|
101341
|
+
};
|
|
101342
|
+
}
|
|
101238
101343
|
|
|
101239
101344
|
// utils/log.ts
|
|
101240
101345
|
var core = __toESM(require_core(), 1);
|
|
@@ -101550,137 +101655,6 @@ function formatUsageSummary(entries) {
|
|
|
101550
101655
|
].join("\n");
|
|
101551
101656
|
}
|
|
101552
101657
|
|
|
101553
|
-
// utils/activity.ts
|
|
101554
|
-
function isMonitorDebugEnabled() {
|
|
101555
|
-
return process.env.ACTIONS_STEP_DEBUG === "true" || process.env.RUNNER_DEBUG === "1" || process.env.LOG_LEVEL === "debug";
|
|
101556
|
-
}
|
|
101557
|
-
var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
|
|
101558
|
-
var DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5e3;
|
|
101559
|
-
var DEBUG_TS_PREFIX = /^(?:\[\d{4}-\d{2}-\d{2}T[^\]]+\]\s+)?/.source;
|
|
101560
|
-
var ACTIVITY_NOISE_PATTERNS = [
|
|
101561
|
-
new RegExp(`${DEBUG_TS_PREFIX}\\[mcp-proxy\\]`),
|
|
101562
|
-
new RegExp(`${DEBUG_TS_PREFIX}\xBB provider error detected`),
|
|
101563
|
-
new RegExp(`${DEBUG_TS_PREFIX}\\[DEBUG\\]\\s+(?:spawn|process) activity `),
|
|
101564
|
-
/^::debug::(?:spawn|process) activity /
|
|
101565
|
-
];
|
|
101566
|
-
function isActivityNoise(chunk) {
|
|
101567
|
-
const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
101568
|
-
if (!text.trim()) return true;
|
|
101569
|
-
return text.split("\n").every((line) => {
|
|
101570
|
-
const trimmed = line.trim();
|
|
101571
|
-
if (!trimmed) return true;
|
|
101572
|
-
return ACTIVITY_NOISE_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
101573
|
-
});
|
|
101574
|
-
}
|
|
101575
|
-
var _lastActivity = performance2.now();
|
|
101576
|
-
var MAX_TOOL_CALL_SUSPENSION_MS = 15 * 60 * 1e3;
|
|
101577
|
-
var _suspendedAt = null;
|
|
101578
|
-
var _suspensionTimer = null;
|
|
101579
|
-
function markActivity() {
|
|
101580
|
-
_lastActivity = performance2.now();
|
|
101581
|
-
}
|
|
101582
|
-
function getIdleMs() {
|
|
101583
|
-
if (_suspendedAt !== null) return 0;
|
|
101584
|
-
return Math.round(performance2.now() - _lastActivity);
|
|
101585
|
-
}
|
|
101586
|
-
function suspendActivity(maxMs = MAX_TOOL_CALL_SUSPENSION_MS) {
|
|
101587
|
-
if (_suspendedAt !== null) return;
|
|
101588
|
-
_suspendedAt = performance2.now();
|
|
101589
|
-
_suspensionTimer = setTimeout(() => {
|
|
101590
|
-
log.warning(`activity watchdog suspended >${Math.round(maxMs / 1e3)}s \u2014 auto-resuming`);
|
|
101591
|
-
resumeActivity();
|
|
101592
|
-
}, maxMs);
|
|
101593
|
-
_suspensionTimer.unref?.();
|
|
101594
|
-
}
|
|
101595
|
-
function resumeActivity() {
|
|
101596
|
-
if (_suspendedAt === null) return;
|
|
101597
|
-
_suspendedAt = null;
|
|
101598
|
-
if (_suspensionTimer) {
|
|
101599
|
-
clearTimeout(_suspensionTimer);
|
|
101600
|
-
_suspensionTimer = null;
|
|
101601
|
-
}
|
|
101602
|
-
_lastActivity = performance2.now();
|
|
101603
|
-
}
|
|
101604
|
-
function isActivitySuspended() {
|
|
101605
|
-
return _suspendedAt !== null;
|
|
101606
|
-
}
|
|
101607
|
-
function wrapWrite(original, onActivity) {
|
|
101608
|
-
const wrapped = (chunk, encodingOrCb, cb) => {
|
|
101609
|
-
if (!isActivityNoise(chunk)) {
|
|
101610
|
-
onActivity();
|
|
101611
|
-
}
|
|
101612
|
-
if (typeof encodingOrCb === "function") {
|
|
101613
|
-
return original(chunk, encodingOrCb);
|
|
101614
|
-
}
|
|
101615
|
-
return original(chunk, encodingOrCb, cb);
|
|
101616
|
-
};
|
|
101617
|
-
return wrapped;
|
|
101618
|
-
}
|
|
101619
|
-
function startProcessOutputMonitor(ctx) {
|
|
101620
|
-
let timedOut = false;
|
|
101621
|
-
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
101622
|
-
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
101623
|
-
process.stdout.write = wrapWrite(originalStdoutWrite, markActivity);
|
|
101624
|
-
process.stderr.write = wrapWrite(originalStderrWrite, markActivity);
|
|
101625
|
-
const debugBypass = (msg) => {
|
|
101626
|
-
if (!isMonitorDebugEnabled()) return;
|
|
101627
|
-
originalStdoutWrite(`[${(/* @__PURE__ */ new Date()).toISOString()}] [DEBUG] ${msg}
|
|
101628
|
-
`);
|
|
101629
|
-
};
|
|
101630
|
-
debugBypass(`process activity monitor started: timeout=${ctx.timeoutMs}ms`);
|
|
101631
|
-
const intervalId = setInterval(() => {
|
|
101632
|
-
const idleMs = getIdleMs();
|
|
101633
|
-
debugBypass(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
|
|
101634
|
-
if (timedOut || idleMs <= ctx.timeoutMs) return;
|
|
101635
|
-
timedOut = true;
|
|
101636
|
-
ctx.onTimeout(idleMs);
|
|
101637
|
-
}, ctx.checkIntervalMs);
|
|
101638
|
-
function stop() {
|
|
101639
|
-
clearInterval(intervalId);
|
|
101640
|
-
process.stdout.write = originalStdoutWrite;
|
|
101641
|
-
process.stderr.write = originalStderrWrite;
|
|
101642
|
-
}
|
|
101643
|
-
return { stop };
|
|
101644
|
-
}
|
|
101645
|
-
function createProcessOutputActivityTimeout(ctx) {
|
|
101646
|
-
markActivity();
|
|
101647
|
-
let rejectFn = null;
|
|
101648
|
-
const promise2 = new Promise((_2, reject) => {
|
|
101649
|
-
rejectFn = reject;
|
|
101650
|
-
});
|
|
101651
|
-
let monitor = null;
|
|
101652
|
-
monitor = startProcessOutputMonitor({
|
|
101653
|
-
timeoutMs: ctx.timeoutMs,
|
|
101654
|
-
checkIntervalMs: ctx.checkIntervalMs,
|
|
101655
|
-
onTimeout: (idleMs) => {
|
|
101656
|
-
if (!rejectFn) return;
|
|
101657
|
-
const idleSec = Math.round(idleMs / 1e3);
|
|
101658
|
-
if (monitor) {
|
|
101659
|
-
monitor.stop();
|
|
101660
|
-
}
|
|
101661
|
-
const reject = rejectFn;
|
|
101662
|
-
rejectFn = null;
|
|
101663
|
-
reject(new Error(`activity timeout: no output for ${idleSec}s`));
|
|
101664
|
-
}
|
|
101665
|
-
});
|
|
101666
|
-
return {
|
|
101667
|
-
promise: promise2,
|
|
101668
|
-
// stop() also disarms forceReject so a late safety-net fire can't reject
|
|
101669
|
-
// the promise after the run has already succeeded.
|
|
101670
|
-
stop: () => {
|
|
101671
|
-
monitor?.stop();
|
|
101672
|
-
rejectFn = null;
|
|
101673
|
-
},
|
|
101674
|
-
forceReject: (reason) => {
|
|
101675
|
-
if (!rejectFn) return;
|
|
101676
|
-
monitor?.stop();
|
|
101677
|
-
const reject = rejectFn;
|
|
101678
|
-
rejectFn = null;
|
|
101679
|
-
reject(new Error(reason));
|
|
101680
|
-
}
|
|
101681
|
-
};
|
|
101682
|
-
}
|
|
101683
|
-
|
|
101684
101658
|
// utils/install.ts
|
|
101685
101659
|
import { spawnSync } from "node:child_process";
|
|
101686
101660
|
import { chmodSync, createWriteStream, existsSync as existsSync2, mkdirSync } from "node:fs";
|
|
@@ -101880,7 +101854,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
101880
101854
|
// package.json
|
|
101881
101855
|
var package_default = {
|
|
101882
101856
|
name: "pullfrog",
|
|
101883
|
-
version: "0.1.
|
|
101857
|
+
version: "0.1.20",
|
|
101884
101858
|
type: "module",
|
|
101885
101859
|
bin: {
|
|
101886
101860
|
pullfrog: "dist/cli.mjs",
|
|
@@ -102218,11 +102192,6 @@ async function spawn3(options) {
|
|
|
102218
102192
|
`spawn activity timer: pid=${child.pid} cmd=${options.cmd} timeout=${activityTimeoutMs}ms`
|
|
102219
102193
|
);
|
|
102220
102194
|
activityCheckIntervalId = setInterval(() => {
|
|
102221
|
-
if (options.isPausedExternally?.()) {
|
|
102222
|
-
lastActivityTime = performance3.now();
|
|
102223
|
-
log.debug(`spawn activity check: pid=${child.pid} paused externally`);
|
|
102224
|
-
return;
|
|
102225
|
-
}
|
|
102226
102195
|
const idleMs = performance3.now() - lastActivityTime;
|
|
102227
102196
|
log.debug(
|
|
102228
102197
|
`spawn activity check: pid=${child.pid} idle=${Math.round(idleMs)}ms / ${activityTimeoutMs}ms`
|
|
@@ -103690,7 +103659,6 @@ async function runClaude(params) {
|
|
|
103690
103659
|
}
|
|
103691
103660
|
} else if (block.type === "tool_use") {
|
|
103692
103661
|
const toolName = block.name || "unknown";
|
|
103693
|
-
suspendActivity();
|
|
103694
103662
|
if (params.onToolUse) {
|
|
103695
103663
|
params.onToolUse({
|
|
103696
103664
|
toolName,
|
|
@@ -103735,7 +103703,6 @@ async function runClaude(params) {
|
|
|
103735
103703
|
for (const block of content) {
|
|
103736
103704
|
if (typeof block === "string") continue;
|
|
103737
103705
|
if (block.type === "tool_result") {
|
|
103738
|
-
resumeActivity();
|
|
103739
103706
|
timerFor(label).markToolResult();
|
|
103740
103707
|
const outputContent = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map(
|
|
103741
103708
|
(entry) => typeof entry === "string" ? entry : typeof entry === "object" && entry !== null && "text" in entry ? String(entry.text) : JSON.stringify(entry)
|
|
@@ -103822,9 +103789,10 @@ async function runClaude(params) {
|
|
|
103822
103789
|
args: params.args,
|
|
103823
103790
|
cwd: params.cwd,
|
|
103824
103791
|
env: params.env,
|
|
103825
|
-
|
|
103792
|
+
// flat agent idle budget — long synchronous MCP tool calls (issue #760)
|
|
103793
|
+
// sit well under it, so no per-toolcall suspend bracketing is needed.
|
|
103794
|
+
activityTimeout: AGENT_ACTIVITY_TIMEOUT_MS,
|
|
103826
103795
|
onActivityTimeout: params.onActivityTimeout,
|
|
103827
|
-
isPausedExternally: isActivitySuspended,
|
|
103828
103796
|
stdio: ["ignore", "pipe", "pipe"],
|
|
103829
103797
|
// run claude in its own process group so SIGKILL on activity timeout /
|
|
103830
103798
|
// outer cancellation reaches any subprocesses it spawns (rg, file
|
|
@@ -108179,13 +108147,6 @@ async function onToolPart(ctx, part, label, isOrchestrator) {
|
|
|
108179
108147
|
const status = part.state.status;
|
|
108180
108148
|
const toolName = part.tool;
|
|
108181
108149
|
const toolId = part.callID;
|
|
108182
|
-
if (toolName !== "task") {
|
|
108183
|
-
if (status === "completed" || status === "error") {
|
|
108184
|
-
resumeActivity();
|
|
108185
|
-
} else {
|
|
108186
|
-
suspendActivity();
|
|
108187
|
-
}
|
|
108188
|
-
}
|
|
108189
108150
|
if (toolName === "task" && status === "running" && isOrchestrator && !ctx.taskDispatchByCallID.has(toolId)) {
|
|
108190
108151
|
const input = part.state.input ?? {};
|
|
108191
108152
|
const dispatched = ctx.labeler.recordTaskDispatch(input);
|
|
@@ -108433,7 +108394,6 @@ function startInnerActivityWatchdog(params) {
|
|
|
108433
108394
|
let fired = false;
|
|
108434
108395
|
const id = setInterval(() => {
|
|
108435
108396
|
if (fired) return;
|
|
108436
|
-
if (isActivitySuspended()) return;
|
|
108437
108397
|
const idleMs = performance6.now() - params.ctx.lastEventAt;
|
|
108438
108398
|
if (idleMs <= params.timeoutMs) return;
|
|
108439
108399
|
fired = true;
|
|
@@ -108589,13 +108549,13 @@ var opencode = agent({
|
|
|
108589
108549
|
const watchdog = startInnerActivityWatchdog({
|
|
108590
108550
|
ctx: runnerCtx,
|
|
108591
108551
|
// model-stall budget: how long the orchestrator may stream NO progress
|
|
108592
|
-
// (no token/tool part.updated) before we tear the turn down.
|
|
108593
|
-
//
|
|
108594
|
-
//
|
|
108595
|
-
//
|
|
108596
|
-
//
|
|
108597
|
-
//
|
|
108598
|
-
timeoutMs:
|
|
108552
|
+
// (no token/tool part.updated) before we tear the turn down. opencode's
|
|
108553
|
+
// keepalive/lifecycle events keep the outer process-output monitor
|
|
108554
|
+
// alive even while the model is silent, so this inner timer is the only
|
|
108555
|
+
// stall detector for the v2 SSE path. it shares the flat idle budget so
|
|
108556
|
+
// a long synchronous tool call (no part.updated while it runs) can't
|
|
108557
|
+
// false-positive it.
|
|
108558
|
+
timeoutMs: AGENT_ACTIVITY_TIMEOUT_MS,
|
|
108599
108559
|
abortController
|
|
108600
108560
|
});
|
|
108601
108561
|
const sdkModel = parseModel2(model);
|
|
@@ -161974,7 +161934,7 @@ ${instructions.user}` : null,
|
|
|
161974
161934
|
}
|
|
161975
161935
|
}
|
|
161976
161936
|
activityTimeout = createProcessOutputActivityTimeout({
|
|
161977
|
-
timeoutMs:
|
|
161937
|
+
timeoutMs: AGENT_ACTIVITY_TIMEOUT_MS,
|
|
161978
161938
|
checkIntervalMs: DEFAULT_ACTIVITY_CHECK_INTERVAL_MS
|
|
161979
161939
|
});
|
|
161980
161940
|
activityTimeout.promise.catch(() => {
|
|
@@ -163019,7 +162979,7 @@ async function run2() {
|
|
|
163019
162979
|
}
|
|
163020
162980
|
|
|
163021
162981
|
// cli.ts
|
|
163022
|
-
var VERSION10 = "0.1.
|
|
162982
|
+
var VERSION10 = "0.1.20";
|
|
163023
162983
|
var bin = basename2(process.argv[1] || "");
|
|
163024
162984
|
var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
|
|
163025
162985
|
var rawArgs = process.argv.slice(2);
|
package/dist/index.js
CHANGED
|
@@ -99435,6 +99435,111 @@ function formatMcpToolRef(agentId, toolName) {
|
|
|
99435
99435
|
|
|
99436
99436
|
// utils/activity.ts
|
|
99437
99437
|
import { performance as performance2 } from "node:perf_hooks";
|
|
99438
|
+
function isMonitorDebugEnabled() {
|
|
99439
|
+
return process.env.ACTIONS_STEP_DEBUG === "true" || process.env.RUNNER_DEBUG === "1" || process.env.LOG_LEVEL === "debug";
|
|
99440
|
+
}
|
|
99441
|
+
var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
|
|
99442
|
+
var AGENT_ACTIVITY_TIMEOUT_MS = 9e5;
|
|
99443
|
+
var DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5e3;
|
|
99444
|
+
var DEBUG_TS_PREFIX = /^(?:\[\d{4}-\d{2}-\d{2}T[^\]]+\]\s+)?/.source;
|
|
99445
|
+
var ACTIVITY_NOISE_PATTERNS = [
|
|
99446
|
+
new RegExp(`${DEBUG_TS_PREFIX}\\[mcp-proxy\\]`),
|
|
99447
|
+
new RegExp(`${DEBUG_TS_PREFIX}\xBB provider error detected`),
|
|
99448
|
+
new RegExp(`${DEBUG_TS_PREFIX}\\[DEBUG\\]\\s+(?:spawn|process) activity `),
|
|
99449
|
+
/^::debug::(?:spawn|process) activity /
|
|
99450
|
+
];
|
|
99451
|
+
function isActivityNoise(chunk) {
|
|
99452
|
+
const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
99453
|
+
if (!text.trim()) return true;
|
|
99454
|
+
return text.split("\n").every((line) => {
|
|
99455
|
+
const trimmed = line.trim();
|
|
99456
|
+
if (!trimmed) return true;
|
|
99457
|
+
return ACTIVITY_NOISE_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
99458
|
+
});
|
|
99459
|
+
}
|
|
99460
|
+
var _lastActivity = performance2.now();
|
|
99461
|
+
function markActivity() {
|
|
99462
|
+
_lastActivity = performance2.now();
|
|
99463
|
+
}
|
|
99464
|
+
function getIdleMs() {
|
|
99465
|
+
return Math.round(performance2.now() - _lastActivity);
|
|
99466
|
+
}
|
|
99467
|
+
function wrapWrite(original, onActivity) {
|
|
99468
|
+
const wrapped = (chunk, encodingOrCb, cb) => {
|
|
99469
|
+
if (!isActivityNoise(chunk)) {
|
|
99470
|
+
onActivity();
|
|
99471
|
+
}
|
|
99472
|
+
if (typeof encodingOrCb === "function") {
|
|
99473
|
+
return original(chunk, encodingOrCb);
|
|
99474
|
+
}
|
|
99475
|
+
return original(chunk, encodingOrCb, cb);
|
|
99476
|
+
};
|
|
99477
|
+
return wrapped;
|
|
99478
|
+
}
|
|
99479
|
+
function startProcessOutputMonitor(ctx) {
|
|
99480
|
+
let timedOut = false;
|
|
99481
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
99482
|
+
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
99483
|
+
process.stdout.write = wrapWrite(originalStdoutWrite, markActivity);
|
|
99484
|
+
process.stderr.write = wrapWrite(originalStderrWrite, markActivity);
|
|
99485
|
+
const debugBypass = (msg) => {
|
|
99486
|
+
if (!isMonitorDebugEnabled()) return;
|
|
99487
|
+
originalStdoutWrite(`[${(/* @__PURE__ */ new Date()).toISOString()}] [DEBUG] ${msg}
|
|
99488
|
+
`);
|
|
99489
|
+
};
|
|
99490
|
+
debugBypass(`process activity monitor started: timeout=${ctx.timeoutMs}ms`);
|
|
99491
|
+
const intervalId = setInterval(() => {
|
|
99492
|
+
const idleMs = getIdleMs();
|
|
99493
|
+
debugBypass(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
|
|
99494
|
+
if (timedOut || idleMs <= ctx.timeoutMs) return;
|
|
99495
|
+
timedOut = true;
|
|
99496
|
+
ctx.onTimeout(idleMs);
|
|
99497
|
+
}, ctx.checkIntervalMs);
|
|
99498
|
+
function stop() {
|
|
99499
|
+
clearInterval(intervalId);
|
|
99500
|
+
process.stdout.write = originalStdoutWrite;
|
|
99501
|
+
process.stderr.write = originalStderrWrite;
|
|
99502
|
+
}
|
|
99503
|
+
return { stop };
|
|
99504
|
+
}
|
|
99505
|
+
function createProcessOutputActivityTimeout(ctx) {
|
|
99506
|
+
markActivity();
|
|
99507
|
+
let rejectFn = null;
|
|
99508
|
+
const promise2 = new Promise((_, reject) => {
|
|
99509
|
+
rejectFn = reject;
|
|
99510
|
+
});
|
|
99511
|
+
let monitor = null;
|
|
99512
|
+
monitor = startProcessOutputMonitor({
|
|
99513
|
+
timeoutMs: ctx.timeoutMs,
|
|
99514
|
+
checkIntervalMs: ctx.checkIntervalMs,
|
|
99515
|
+
onTimeout: (idleMs) => {
|
|
99516
|
+
if (!rejectFn) return;
|
|
99517
|
+
const idleSec = Math.round(idleMs / 1e3);
|
|
99518
|
+
if (monitor) {
|
|
99519
|
+
monitor.stop();
|
|
99520
|
+
}
|
|
99521
|
+
const reject = rejectFn;
|
|
99522
|
+
rejectFn = null;
|
|
99523
|
+
reject(new Error(`activity timeout: no output for ${idleSec}s`));
|
|
99524
|
+
}
|
|
99525
|
+
});
|
|
99526
|
+
return {
|
|
99527
|
+
promise: promise2,
|
|
99528
|
+
// stop() also disarms forceReject so a late safety-net fire can't reject
|
|
99529
|
+
// the promise after the run has already succeeded.
|
|
99530
|
+
stop: () => {
|
|
99531
|
+
monitor?.stop();
|
|
99532
|
+
rejectFn = null;
|
|
99533
|
+
},
|
|
99534
|
+
forceReject: (reason) => {
|
|
99535
|
+
if (!rejectFn) return;
|
|
99536
|
+
monitor?.stop();
|
|
99537
|
+
const reject = rejectFn;
|
|
99538
|
+
rejectFn = null;
|
|
99539
|
+
reject(new Error(reason));
|
|
99540
|
+
}
|
|
99541
|
+
};
|
|
99542
|
+
}
|
|
99438
99543
|
|
|
99439
99544
|
// utils/log.ts
|
|
99440
99545
|
var core = __toESM(require_core(), 1);
|
|
@@ -99750,137 +99855,6 @@ function formatUsageSummary(entries) {
|
|
|
99750
99855
|
].join("\n");
|
|
99751
99856
|
}
|
|
99752
99857
|
|
|
99753
|
-
// utils/activity.ts
|
|
99754
|
-
function isMonitorDebugEnabled() {
|
|
99755
|
-
return process.env.ACTIONS_STEP_DEBUG === "true" || process.env.RUNNER_DEBUG === "1" || process.env.LOG_LEVEL === "debug";
|
|
99756
|
-
}
|
|
99757
|
-
var DEFAULT_ACTIVITY_TIMEOUT_MS = 3e5;
|
|
99758
|
-
var DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5e3;
|
|
99759
|
-
var DEBUG_TS_PREFIX = /^(?:\[\d{4}-\d{2}-\d{2}T[^\]]+\]\s+)?/.source;
|
|
99760
|
-
var ACTIVITY_NOISE_PATTERNS = [
|
|
99761
|
-
new RegExp(`${DEBUG_TS_PREFIX}\\[mcp-proxy\\]`),
|
|
99762
|
-
new RegExp(`${DEBUG_TS_PREFIX}\xBB provider error detected`),
|
|
99763
|
-
new RegExp(`${DEBUG_TS_PREFIX}\\[DEBUG\\]\\s+(?:spawn|process) activity `),
|
|
99764
|
-
/^::debug::(?:spawn|process) activity /
|
|
99765
|
-
];
|
|
99766
|
-
function isActivityNoise(chunk) {
|
|
99767
|
-
const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
99768
|
-
if (!text.trim()) return true;
|
|
99769
|
-
return text.split("\n").every((line) => {
|
|
99770
|
-
const trimmed = line.trim();
|
|
99771
|
-
if (!trimmed) return true;
|
|
99772
|
-
return ACTIVITY_NOISE_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
99773
|
-
});
|
|
99774
|
-
}
|
|
99775
|
-
var _lastActivity = performance2.now();
|
|
99776
|
-
var MAX_TOOL_CALL_SUSPENSION_MS = 15 * 60 * 1e3;
|
|
99777
|
-
var _suspendedAt = null;
|
|
99778
|
-
var _suspensionTimer = null;
|
|
99779
|
-
function markActivity() {
|
|
99780
|
-
_lastActivity = performance2.now();
|
|
99781
|
-
}
|
|
99782
|
-
function getIdleMs() {
|
|
99783
|
-
if (_suspendedAt !== null) return 0;
|
|
99784
|
-
return Math.round(performance2.now() - _lastActivity);
|
|
99785
|
-
}
|
|
99786
|
-
function suspendActivity(maxMs = MAX_TOOL_CALL_SUSPENSION_MS) {
|
|
99787
|
-
if (_suspendedAt !== null) return;
|
|
99788
|
-
_suspendedAt = performance2.now();
|
|
99789
|
-
_suspensionTimer = setTimeout(() => {
|
|
99790
|
-
log.warning(`activity watchdog suspended >${Math.round(maxMs / 1e3)}s \u2014 auto-resuming`);
|
|
99791
|
-
resumeActivity();
|
|
99792
|
-
}, maxMs);
|
|
99793
|
-
_suspensionTimer.unref?.();
|
|
99794
|
-
}
|
|
99795
|
-
function resumeActivity() {
|
|
99796
|
-
if (_suspendedAt === null) return;
|
|
99797
|
-
_suspendedAt = null;
|
|
99798
|
-
if (_suspensionTimer) {
|
|
99799
|
-
clearTimeout(_suspensionTimer);
|
|
99800
|
-
_suspensionTimer = null;
|
|
99801
|
-
}
|
|
99802
|
-
_lastActivity = performance2.now();
|
|
99803
|
-
}
|
|
99804
|
-
function isActivitySuspended() {
|
|
99805
|
-
return _suspendedAt !== null;
|
|
99806
|
-
}
|
|
99807
|
-
function wrapWrite(original, onActivity) {
|
|
99808
|
-
const wrapped = (chunk, encodingOrCb, cb) => {
|
|
99809
|
-
if (!isActivityNoise(chunk)) {
|
|
99810
|
-
onActivity();
|
|
99811
|
-
}
|
|
99812
|
-
if (typeof encodingOrCb === "function") {
|
|
99813
|
-
return original(chunk, encodingOrCb);
|
|
99814
|
-
}
|
|
99815
|
-
return original(chunk, encodingOrCb, cb);
|
|
99816
|
-
};
|
|
99817
|
-
return wrapped;
|
|
99818
|
-
}
|
|
99819
|
-
function startProcessOutputMonitor(ctx) {
|
|
99820
|
-
let timedOut = false;
|
|
99821
|
-
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
99822
|
-
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
99823
|
-
process.stdout.write = wrapWrite(originalStdoutWrite, markActivity);
|
|
99824
|
-
process.stderr.write = wrapWrite(originalStderrWrite, markActivity);
|
|
99825
|
-
const debugBypass = (msg) => {
|
|
99826
|
-
if (!isMonitorDebugEnabled()) return;
|
|
99827
|
-
originalStdoutWrite(`[${(/* @__PURE__ */ new Date()).toISOString()}] [DEBUG] ${msg}
|
|
99828
|
-
`);
|
|
99829
|
-
};
|
|
99830
|
-
debugBypass(`process activity monitor started: timeout=${ctx.timeoutMs}ms`);
|
|
99831
|
-
const intervalId = setInterval(() => {
|
|
99832
|
-
const idleMs = getIdleMs();
|
|
99833
|
-
debugBypass(`process activity check: idle=${idleMs}ms / ${ctx.timeoutMs}ms`);
|
|
99834
|
-
if (timedOut || idleMs <= ctx.timeoutMs) return;
|
|
99835
|
-
timedOut = true;
|
|
99836
|
-
ctx.onTimeout(idleMs);
|
|
99837
|
-
}, ctx.checkIntervalMs);
|
|
99838
|
-
function stop() {
|
|
99839
|
-
clearInterval(intervalId);
|
|
99840
|
-
process.stdout.write = originalStdoutWrite;
|
|
99841
|
-
process.stderr.write = originalStderrWrite;
|
|
99842
|
-
}
|
|
99843
|
-
return { stop };
|
|
99844
|
-
}
|
|
99845
|
-
function createProcessOutputActivityTimeout(ctx) {
|
|
99846
|
-
markActivity();
|
|
99847
|
-
let rejectFn = null;
|
|
99848
|
-
const promise2 = new Promise((_, reject) => {
|
|
99849
|
-
rejectFn = reject;
|
|
99850
|
-
});
|
|
99851
|
-
let monitor = null;
|
|
99852
|
-
monitor = startProcessOutputMonitor({
|
|
99853
|
-
timeoutMs: ctx.timeoutMs,
|
|
99854
|
-
checkIntervalMs: ctx.checkIntervalMs,
|
|
99855
|
-
onTimeout: (idleMs) => {
|
|
99856
|
-
if (!rejectFn) return;
|
|
99857
|
-
const idleSec = Math.round(idleMs / 1e3);
|
|
99858
|
-
if (monitor) {
|
|
99859
|
-
monitor.stop();
|
|
99860
|
-
}
|
|
99861
|
-
const reject = rejectFn;
|
|
99862
|
-
rejectFn = null;
|
|
99863
|
-
reject(new Error(`activity timeout: no output for ${idleSec}s`));
|
|
99864
|
-
}
|
|
99865
|
-
});
|
|
99866
|
-
return {
|
|
99867
|
-
promise: promise2,
|
|
99868
|
-
// stop() also disarms forceReject so a late safety-net fire can't reject
|
|
99869
|
-
// the promise after the run has already succeeded.
|
|
99870
|
-
stop: () => {
|
|
99871
|
-
monitor?.stop();
|
|
99872
|
-
rejectFn = null;
|
|
99873
|
-
},
|
|
99874
|
-
forceReject: (reason) => {
|
|
99875
|
-
if (!rejectFn) return;
|
|
99876
|
-
monitor?.stop();
|
|
99877
|
-
const reject = rejectFn;
|
|
99878
|
-
rejectFn = null;
|
|
99879
|
-
reject(new Error(reason));
|
|
99880
|
-
}
|
|
99881
|
-
};
|
|
99882
|
-
}
|
|
99883
|
-
|
|
99884
99858
|
// utils/install.ts
|
|
99885
99859
|
import { spawnSync } from "node:child_process";
|
|
99886
99860
|
import { chmodSync, createWriteStream, existsSync as existsSync2, mkdirSync } from "node:fs";
|
|
@@ -100080,7 +100054,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
100080
100054
|
// package.json
|
|
100081
100055
|
var package_default = {
|
|
100082
100056
|
name: "pullfrog",
|
|
100083
|
-
version: "0.1.
|
|
100057
|
+
version: "0.1.20",
|
|
100084
100058
|
type: "module",
|
|
100085
100059
|
bin: {
|
|
100086
100060
|
pullfrog: "dist/cli.mjs",
|
|
@@ -100418,11 +100392,6 @@ async function spawn(options) {
|
|
|
100418
100392
|
`spawn activity timer: pid=${child.pid} cmd=${options.cmd} timeout=${activityTimeoutMs}ms`
|
|
100419
100393
|
);
|
|
100420
100394
|
activityCheckIntervalId = setInterval(() => {
|
|
100421
|
-
if (options.isPausedExternally?.()) {
|
|
100422
|
-
lastActivityTime = performance3.now();
|
|
100423
|
-
log.debug(`spawn activity check: pid=${child.pid} paused externally`);
|
|
100424
|
-
return;
|
|
100425
|
-
}
|
|
100426
100395
|
const idleMs = performance3.now() - lastActivityTime;
|
|
100427
100396
|
log.debug(
|
|
100428
100397
|
`spawn activity check: pid=${child.pid} idle=${Math.round(idleMs)}ms / ${activityTimeoutMs}ms`
|
|
@@ -101890,7 +101859,6 @@ async function runClaude(params) {
|
|
|
101890
101859
|
}
|
|
101891
101860
|
} else if (block.type === "tool_use") {
|
|
101892
101861
|
const toolName = block.name || "unknown";
|
|
101893
|
-
suspendActivity();
|
|
101894
101862
|
if (params.onToolUse) {
|
|
101895
101863
|
params.onToolUse({
|
|
101896
101864
|
toolName,
|
|
@@ -101935,7 +101903,6 @@ async function runClaude(params) {
|
|
|
101935
101903
|
for (const block of content) {
|
|
101936
101904
|
if (typeof block === "string") continue;
|
|
101937
101905
|
if (block.type === "tool_result") {
|
|
101938
|
-
resumeActivity();
|
|
101939
101906
|
timerFor(label).markToolResult();
|
|
101940
101907
|
const outputContent = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map(
|
|
101941
101908
|
(entry) => typeof entry === "string" ? entry : typeof entry === "object" && entry !== null && "text" in entry ? String(entry.text) : JSON.stringify(entry)
|
|
@@ -102022,9 +101989,10 @@ async function runClaude(params) {
|
|
|
102022
101989
|
args: params.args,
|
|
102023
101990
|
cwd: params.cwd,
|
|
102024
101991
|
env: params.env,
|
|
102025
|
-
|
|
101992
|
+
// flat agent idle budget — long synchronous MCP tool calls (issue #760)
|
|
101993
|
+
// sit well under it, so no per-toolcall suspend bracketing is needed.
|
|
101994
|
+
activityTimeout: AGENT_ACTIVITY_TIMEOUT_MS,
|
|
102026
101995
|
onActivityTimeout: params.onActivityTimeout,
|
|
102027
|
-
isPausedExternally: isActivitySuspended,
|
|
102028
101996
|
stdio: ["ignore", "pipe", "pipe"],
|
|
102029
101997
|
// run claude in its own process group so SIGKILL on activity timeout /
|
|
102030
101998
|
// outer cancellation reaches any subprocesses it spawns (rg, file
|
|
@@ -106421,13 +106389,6 @@ async function onToolPart(ctx, part, label, isOrchestrator) {
|
|
|
106421
106389
|
const status = part.state.status;
|
|
106422
106390
|
const toolName = part.tool;
|
|
106423
106391
|
const toolId = part.callID;
|
|
106424
|
-
if (toolName !== "task") {
|
|
106425
|
-
if (status === "completed" || status === "error") {
|
|
106426
|
-
resumeActivity();
|
|
106427
|
-
} else {
|
|
106428
|
-
suspendActivity();
|
|
106429
|
-
}
|
|
106430
|
-
}
|
|
106431
106392
|
if (toolName === "task" && status === "running" && isOrchestrator && !ctx.taskDispatchByCallID.has(toolId)) {
|
|
106432
106393
|
const input = part.state.input ?? {};
|
|
106433
106394
|
const dispatched = ctx.labeler.recordTaskDispatch(input);
|
|
@@ -106675,7 +106636,6 @@ function startInnerActivityWatchdog(params) {
|
|
|
106675
106636
|
let fired = false;
|
|
106676
106637
|
const id = setInterval(() => {
|
|
106677
106638
|
if (fired) return;
|
|
106678
|
-
if (isActivitySuspended()) return;
|
|
106679
106639
|
const idleMs = performance6.now() - params.ctx.lastEventAt;
|
|
106680
106640
|
if (idleMs <= params.timeoutMs) return;
|
|
106681
106641
|
fired = true;
|
|
@@ -106831,13 +106791,13 @@ var opencode = agent({
|
|
|
106831
106791
|
const watchdog = startInnerActivityWatchdog({
|
|
106832
106792
|
ctx: runnerCtx,
|
|
106833
106793
|
// model-stall budget: how long the orchestrator may stream NO progress
|
|
106834
|
-
// (no token/tool part.updated) before we tear the turn down.
|
|
106835
|
-
//
|
|
106836
|
-
//
|
|
106837
|
-
//
|
|
106838
|
-
//
|
|
106839
|
-
//
|
|
106840
|
-
timeoutMs:
|
|
106794
|
+
// (no token/tool part.updated) before we tear the turn down. opencode's
|
|
106795
|
+
// keepalive/lifecycle events keep the outer process-output monitor
|
|
106796
|
+
// alive even while the model is silent, so this inner timer is the only
|
|
106797
|
+
// stall detector for the v2 SSE path. it shares the flat idle budget so
|
|
106798
|
+
// a long synchronous tool call (no part.updated while it runs) can't
|
|
106799
|
+
// false-positive it.
|
|
106800
|
+
timeoutMs: AGENT_ACTIVITY_TIMEOUT_MS,
|
|
106841
106801
|
abortController
|
|
106842
106802
|
});
|
|
106843
106803
|
const sdkModel = parseModel2(model);
|
|
@@ -160216,7 +160176,7 @@ ${instructions.user}` : null,
|
|
|
160216
160176
|
}
|
|
160217
160177
|
}
|
|
160218
160178
|
activityTimeout = createProcessOutputActivityTimeout({
|
|
160219
|
-
timeoutMs:
|
|
160179
|
+
timeoutMs: AGENT_ACTIVITY_TIMEOUT_MS,
|
|
160220
160180
|
checkIntervalMs: DEFAULT_ACTIVITY_CHECK_INTERVAL_MS
|
|
160221
160181
|
});
|
|
160222
160182
|
activityTimeout.promise.catch(() => {
|
package/dist/utils/activity.d.ts
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* generic `spawn()` idle default for ordinary short-lived subprocesses (prep
|
|
3
|
+
* probes, package-manager invocations, dependency installs). these should be
|
|
4
|
+
* producing output steadily, so a comparatively tight budget catches a wedged
|
|
5
|
+
* command promptly. the long-silent-tool tolerance the agent harnesses need
|
|
6
|
+
* lives in AGENT_ACTIVITY_TIMEOUT_MS, applied explicitly at those sites.
|
|
7
|
+
*/
|
|
1
8
|
export declare const DEFAULT_ACTIVITY_TIMEOUT_MS = 300000;
|
|
9
|
+
/**
|
|
10
|
+
* flat idle budget for the agent activity watchdog (the outer process-output
|
|
11
|
+
* monitor, the v1 harness spawns, and the v2 inner event-silence watchdog).
|
|
12
|
+
* sized to exceed the worst-case legitimate silent tool window (issue #760:
|
|
13
|
+
* `checkout_pr` git fetch+deepen on a large monorepo, ~4-5min) with generous
|
|
14
|
+
* headroom, so no single in-flight tool call can be mistaken for a stall. a
|
|
15
|
+
* timeout this generous needs no suspend/resume bracketing — the cost of a
|
|
16
|
+
* genuinely hung run is only GitHub Actions minutes, not tokens.
|
|
17
|
+
*/
|
|
18
|
+
export declare const AGENT_ACTIVITY_TIMEOUT_MS = 900000;
|
|
2
19
|
export declare const DEFAULT_ACTIVITY_CHECK_INTERVAL_MS = 5000;
|
|
3
20
|
export declare const ACTIVITY_NOISE_PATTERNS: readonly RegExp[];
|
|
4
21
|
export declare function isActivityNoise(chunk: string | Uint8Array): boolean;
|
|
@@ -12,44 +29,12 @@ export type ActivityTimeout = {
|
|
|
12
29
|
/** force the timeout to reject immediately with a custom reason */
|
|
13
30
|
forceReject: (reason: string) => void;
|
|
14
31
|
};
|
|
15
|
-
/**
|
|
16
|
-
* upper bound on how long a single tool call can suspend the activity
|
|
17
|
-
* watchdog. matched against the typical worst-case `checkout_pr`
|
|
18
|
-
* fetch+deepen on a large monorepo (issue #760: 4-5min) plus generous
|
|
19
|
-
* headroom for slower MCP tools, while still bounding the worst case if
|
|
20
|
-
* a tool genuinely hangs and `tool_result` never arrives — auto-resume
|
|
21
|
-
* fires here and the normal idle clock takes over from a fresh baseline.
|
|
22
|
-
*/
|
|
23
|
-
export declare const MAX_TOOL_CALL_SUSPENSION_MS: number;
|
|
24
32
|
/**
|
|
25
33
|
* mark activity to reset the no-output timeout.
|
|
26
34
|
* call this whenever the agent emits any event, even if it isn't logged to stdout.
|
|
27
35
|
*/
|
|
28
36
|
export declare function markActivity(): void;
|
|
29
|
-
/**
|
|
30
|
-
* get the time since last activity in milliseconds.
|
|
31
|
-
* returns 0 while the watchdog is suspended (issue #760).
|
|
32
|
-
*/
|
|
37
|
+
/** get the time since last activity in milliseconds. */
|
|
33
38
|
export declare function getIdleMs(): number;
|
|
34
|
-
/**
|
|
35
|
-
* suspend the activity watchdog while a long-running, in-flight unit of
|
|
36
|
-
* work is happening (e.g. an MCP `tools/call` that synchronously awaits
|
|
37
|
-
* a multi-minute git fetch). bracket calls with `resumeActivity()` from
|
|
38
|
-
* the agent harness's `tool_use` / `tool_result` event handlers.
|
|
39
|
-
*
|
|
40
|
-
* - idempotent: nested suspends are no-ops; the first resume wins.
|
|
41
|
-
* - bounded: auto-resumes after `maxMs` so a buggy tool that never
|
|
42
|
-
* produces a `tool_result` can't pin the watchdog open forever.
|
|
43
|
-
* - safe: only the *agent harness* (claude.ts / opencode.ts) on explicit,
|
|
44
|
-
* paired CLI events should call this. NEVER blanket-suspend on internal
|
|
45
|
-
* noise — that would resurrect issue #12 zombie runs.
|
|
46
|
-
*/
|
|
47
|
-
export declare function suspendActivity(maxMs?: number): void;
|
|
48
|
-
/**
|
|
49
|
-
* resume the activity watchdog. resets the idle baseline so a stale
|
|
50
|
-
* idle window before the suspend can't immediately re-fire.
|
|
51
|
-
*/
|
|
52
|
-
export declare function resumeActivity(): void;
|
|
53
|
-
export declare function isActivitySuspended(): boolean;
|
|
54
39
|
export declare function createProcessOutputActivityTimeout(ctx: ActivityTimeoutContext): ActivityTimeout;
|
|
55
40
|
export {};
|
|
@@ -43,7 +43,6 @@ export interface SpawnOptions {
|
|
|
43
43
|
timeout?: number;
|
|
44
44
|
activityTimeout?: number;
|
|
45
45
|
onActivityTimeout?: (() => void) | undefined;
|
|
46
|
-
isPausedExternally?: () => boolean;
|
|
47
46
|
cwd?: string;
|
|
48
47
|
stdio?: ("pipe" | "ignore" | "inherit")[];
|
|
49
48
|
onStdout?: (chunk: string) => void;
|