@rallycry/conveyor-agent 10.13.72 → 11.0.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/dist/{boot-ZNL7X5LQ.js → boot-PKQ2I66D.js} +49 -2
- package/dist/{chunk-WMMBAKPE.js → chunk-GL2DIQEQ.js} +121 -8
- package/dist/{chunk-2L5THOWD.js → chunk-JQVAWRVL.js} +2 -2
- package/dist/{chunk-LSZ2KLJY.js → chunk-N4WSUTGV.js} +40 -3
- package/dist/{chunk-2SN32LM6.js → chunk-PEEGCZAR.js} +2055 -1035
- package/dist/cli.js +257 -100
- package/dist/index.d.ts +170 -3
- package/dist/index.js +3 -3
- package/dist/{serve-boot-4N3FRXVQ.js → serve-boot-YPAUKENG.js} +3 -3
- package/package.json +5 -4
- package/skills/conveyor-build/SKILL.md +262 -0
- package/skills/conveyor-build/references/pack-path.md +224 -0
- package/skills/conveyor-build/references/task-path.md +67 -0
- package/skills/conveyor-consensus/SKILL.md +99 -0
- package/skills/conveyor-consensus/references/doc-template.html +204 -0
- package/skills/conveyor-local-loop/SKILL.md +258 -0
- package/skills/conveyor-meeting-review/SKILL.md +72 -0
- package/skills/conveyor-plan/SKILL.md +177 -0
- package/skills/conveyor-plan/references/plan-format.md +134 -0
- package/skills/conveyor-review/SKILL.md +161 -0
- package/skills/conveyor-start/SKILL.md +106 -0
- package/skills/conveyor-triage/SKILL.md +174 -0
- package/skills/conveyor-workflows/SKILL.md +195 -0
- package/skills/conveyor-workflows/references/mcp-setup.md +43 -0
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
ensureDir,
|
|
12
12
|
redactToken,
|
|
13
13
|
reportBootMilestone
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-GL2DIQEQ.js";
|
|
15
15
|
import {
|
|
16
16
|
workbenchPort
|
|
17
17
|
} from "./chunk-KMB3BU4S.js";
|
|
@@ -101,6 +101,7 @@ function decodeJwtClaims(jwt) {
|
|
|
101
101
|
function resolveModeEnv(claims) {
|
|
102
102
|
if (claims.mode === "review") return { CONVEYOR_MODE: "code-review" };
|
|
103
103
|
if (claims.mode === "pack") return { CONVEYOR_MODE: "pack" };
|
|
104
|
+
if (claims.mode === "plan") return { CONVEYOR_MODE: "plan" };
|
|
104
105
|
if (claims.mode === "serving") return { CONVEYOR_MODE: "serving" };
|
|
105
106
|
if (claims.mode === "adhoc") return { CONVEYOR_MODE: "adhoc" };
|
|
106
107
|
if (!claims.taskId && claims.projectId) return { CONVEYOR_MODE: "pm" };
|
|
@@ -853,6 +854,7 @@ async function startWorkspaceSshd(exec, log, port = process.env.CONVEYOR_WORKSPA
|
|
|
853
854
|
// src/boot/workspace-extras.ts
|
|
854
855
|
import {
|
|
855
856
|
appendFileSync as appendFileSync3,
|
|
857
|
+
cpSync,
|
|
856
858
|
existsSync as existsSync3,
|
|
857
859
|
lstatSync as lstatSync2,
|
|
858
860
|
mkdirSync as mkdirSync3,
|
|
@@ -863,7 +865,8 @@ import {
|
|
|
863
865
|
statSync as statSync3,
|
|
864
866
|
symlinkSync as symlinkSync2
|
|
865
867
|
} from "fs";
|
|
866
|
-
import { join as join5 } from "path";
|
|
868
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
869
|
+
import { fileURLToPath } from "url";
|
|
867
870
|
function errText(err) {
|
|
868
871
|
return redactToken(err instanceof Error ? err.message : String(err));
|
|
869
872
|
}
|
|
@@ -1017,6 +1020,49 @@ function linkGrimoireSkills(workspace, log) {
|
|
|
1017
1020
|
excludeLinkedSkills(workspace, linkedNames, log);
|
|
1018
1021
|
log.info(`[boot] Linked ${linkedNames.length} grimoire skills into ${targetDir}`);
|
|
1019
1022
|
}
|
|
1023
|
+
function bundledSkillsDir(moduleUrl = import.meta.url) {
|
|
1024
|
+
try {
|
|
1025
|
+
const dir = join5(dirname2(fileURLToPath(moduleUrl)), "..", "skills");
|
|
1026
|
+
return isDir2(dir) ? dir : null;
|
|
1027
|
+
} catch {
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
function materializeBundledSkills(workspace, log, sourceDirOverride) {
|
|
1032
|
+
const sourceDir = sourceDirOverride === void 0 ? bundledSkillsDir() : sourceDirOverride;
|
|
1033
|
+
if (!sourceDir) {
|
|
1034
|
+
log.warn(
|
|
1035
|
+
"[boot] WARN: no bundled skills payload in this agent build. Cloud modes that invoke a skill will have no workflow to follow. This is a packaging fault (tsup copySkillsPayload did not run), not a workspace problem."
|
|
1036
|
+
);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
const targetDir = join5(workspace, ".claude", "skills");
|
|
1040
|
+
try {
|
|
1041
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
1042
|
+
} catch {
|
|
1043
|
+
log.warn(`[boot] WARN: unable to create ${targetDir}; skipping bundled skills`);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
const copied = [];
|
|
1047
|
+
let names = [];
|
|
1048
|
+
try {
|
|
1049
|
+
names = readdirSync2(sourceDir);
|
|
1050
|
+
} catch {
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
for (const name of names) {
|
|
1054
|
+
if (!isDir2(join5(sourceDir, name))) continue;
|
|
1055
|
+
const target = join5(targetDir, name);
|
|
1056
|
+
if (existsSync3(target) || isSymlink(target)) continue;
|
|
1057
|
+
try {
|
|
1058
|
+
cpSync(join5(sourceDir, name), target, { recursive: true, dereference: true });
|
|
1059
|
+
copied.push(name);
|
|
1060
|
+
} catch {
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
excludeLinkedSkills(workspace, copied, log);
|
|
1064
|
+
log.info(`[boot] Materialized ${copied.length} bundled skills into ${targetDir}`);
|
|
1065
|
+
}
|
|
1020
1066
|
async function neutralizeGrimoirePointer(workspace, git, log) {
|
|
1021
1067
|
if (!existsSync3(join5(workspace, ".claude", "grimoire"))) return;
|
|
1022
1068
|
try {
|
|
@@ -1058,6 +1104,7 @@ async function runPreReadyBinds(workspace, bundle, git, log) {
|
|
|
1058
1104
|
bindGraphifyBundle(workspace, graphifyEnv, log);
|
|
1059
1105
|
await ensureGrimoireSubmodule(workspace, bundle.githubToken ?? "", git, log);
|
|
1060
1106
|
linkGrimoireSkills(workspace, log);
|
|
1107
|
+
materializeBundledSkills(workspace, log);
|
|
1061
1108
|
await neutralizeGrimoirePointer(workspace, git, log);
|
|
1062
1109
|
}
|
|
1063
1110
|
async function cloneReferenceRepos(refsJson, git, log, referencesDir = "/workspaces/references") {
|
|
@@ -6,15 +6,112 @@ import {
|
|
|
6
6
|
|
|
7
7
|
// src/boot/git-prep.ts
|
|
8
8
|
import { execFile } from "child_process";
|
|
9
|
-
import { existsSync, mkdirSync } from "fs";
|
|
10
|
-
import { join } from "path";
|
|
9
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
10
|
+
import { join as join2 } from "path";
|
|
11
11
|
import { promisify } from "util";
|
|
12
|
+
|
|
13
|
+
// src/boot/commit-trailers.ts
|
|
14
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
15
|
+
import { dirname, isAbsolute, join } from "path";
|
|
16
|
+
var HOOK_MARKER = "conveyor-commit-trailers";
|
|
17
|
+
var realFs = {
|
|
18
|
+
exists: (path) => existsSync(path),
|
|
19
|
+
read: (path) => readFileSync(path, "utf8"),
|
|
20
|
+
write: (path, content) => writeFileSync(path, content, "utf8"),
|
|
21
|
+
mkdirp: (path) => mkdirSync(path, { recursive: true }),
|
|
22
|
+
chmodExec: (path) => chmodSync(path, 493)
|
|
23
|
+
};
|
|
24
|
+
function buildHookScript() {
|
|
25
|
+
return `#!/bin/sh
|
|
26
|
+
# ${HOOK_MARKER} \u2014 stamps Conveyor card/driver/session provenance on each commit.
|
|
27
|
+
# Generated by @rallycry/conveyor-agent (boot/commit-trailers.ts); edits are
|
|
28
|
+
# overwritten on the next pod boot.
|
|
29
|
+
#
|
|
30
|
+
# Every path exits 0 on purpose: losing a commit to attribution bookkeeping
|
|
31
|
+
# would be a far worse outcome than losing the attribution.
|
|
32
|
+
MSG="$1"
|
|
33
|
+
[ -n "$MSG" ] || exit 0
|
|
34
|
+
|
|
35
|
+
stamp() {
|
|
36
|
+
[ -n "$2" ] || return 0
|
|
37
|
+
git interpret-trailers --in-place --if-exists doNothing --trailer "$1: $2" "$MSG" 2>/dev/null || true
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
stamp "Conveyor-Card" "$CONVEYOR_CARD_SLUG"
|
|
41
|
+
stamp "Conveyor-Driver" "$CONVEYOR_DRIVER_ID"
|
|
42
|
+
stamp "Conveyor-Session" "$CONVEYOR_SESSION_ID"
|
|
43
|
+
exit 0
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
function resolveHookTargets(repoDir, hooksPath, hasHusky) {
|
|
47
|
+
const huskyTarget = { hookPath: join(repoDir, ".husky", "commit-msg"), huskyManaged: true };
|
|
48
|
+
if (hooksPath?.replace(/\/+$/, "").endsWith(".husky/_")) return [huskyTarget];
|
|
49
|
+
const dir = hooksPath ? isAbsolute(hooksPath) ? hooksPath : join(repoDir, hooksPath) : join(repoDir, ".git", "hooks");
|
|
50
|
+
const resolved = { hookPath: join(dir, "commit-msg"), huskyManaged: false };
|
|
51
|
+
return hasHusky ? [resolved, huskyTarget] : [resolved];
|
|
52
|
+
}
|
|
53
|
+
function excludeFromRepo(repoDir, relPath, fs) {
|
|
54
|
+
const excludePath = join(repoDir, ".git", "info", "exclude");
|
|
55
|
+
const existing = fs.exists(excludePath) ? fs.read(excludePath) : "";
|
|
56
|
+
if (existing.split("\n").some((line) => line.trim() === relPath)) return;
|
|
57
|
+
const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
58
|
+
fs.write(excludePath, `${existing}${separator}${relPath}
|
|
59
|
+
`);
|
|
60
|
+
}
|
|
61
|
+
function declaresHusky(repoDir, fs) {
|
|
62
|
+
if (fs.exists(join(repoDir, ".husky"))) return true;
|
|
63
|
+
const pkgPath = join(repoDir, "package.json");
|
|
64
|
+
if (!fs.exists(pkgPath)) return false;
|
|
65
|
+
try {
|
|
66
|
+
const pkg = JSON.parse(fs.read(pkgPath));
|
|
67
|
+
if (pkg.devDependencies?.husky ?? pkg.dependencies?.husky) return true;
|
|
68
|
+
return Boolean(pkg.scripts?.prepare?.includes("husky"));
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function installCommitTrailerHook(deps) {
|
|
74
|
+
const { git, repoDir, log } = deps;
|
|
75
|
+
const env = deps.env ?? process.env;
|
|
76
|
+
const fs = deps.fs ?? realFs;
|
|
77
|
+
if (!env.CONVEYOR_CARD_SLUG) return { state: "skipped", reason: "no-card" };
|
|
78
|
+
try {
|
|
79
|
+
let hooksPath = null;
|
|
80
|
+
try {
|
|
81
|
+
const { stdout } = await git(["config", "--get", "core.hooksPath"], { cwd: repoDir });
|
|
82
|
+
hooksPath = stdout.trim() || null;
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
const targets = resolveHookTargets(repoDir, hooksPath, declaresHusky(repoDir, fs));
|
|
86
|
+
const written = [];
|
|
87
|
+
for (const { hookPath, huskyManaged } of targets) {
|
|
88
|
+
if (fs.exists(hookPath) && !fs.read(hookPath).includes(HOOK_MARKER)) {
|
|
89
|
+
log.warn(`[boot] WARN: commit-msg hook already present at ${hookPath}; leaving it alone`);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
fs.mkdirp(dirname(hookPath));
|
|
93
|
+
fs.write(hookPath, buildHookScript());
|
|
94
|
+
fs.chmodExec(hookPath);
|
|
95
|
+
if (huskyManaged) excludeFromRepo(repoDir, ".husky/commit-msg", fs);
|
|
96
|
+
written.push(hookPath);
|
|
97
|
+
}
|
|
98
|
+
if (written.length === 0) return { state: "skipped", reason: "project-hook" };
|
|
99
|
+
log.info(`[boot] Commit trailers installed (${written.join(", ")})`);
|
|
100
|
+
return { state: "installed", paths: written };
|
|
101
|
+
} catch (err) {
|
|
102
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
103
|
+
log.warn(`[boot] WARN: commit trailer hook not installed: ${reason}`);
|
|
104
|
+
return { state: "failed", reason };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/boot/git-prep.ts
|
|
12
109
|
var QUICK_GIT_TIMEOUT_MS = 6e4;
|
|
13
110
|
var FETCH_TIMEOUT_MS = 3e5;
|
|
14
111
|
var CLONE_TIMEOUT_MS = 6e5;
|
|
15
112
|
var execFileAsync = promisify(execFile);
|
|
16
113
|
function ensureDir(dir) {
|
|
17
|
-
|
|
114
|
+
mkdirSync2(dir, { recursive: true });
|
|
18
115
|
}
|
|
19
116
|
function defaultGit(args, opts = {}) {
|
|
20
117
|
return execFileAsync("git", args, {
|
|
@@ -143,10 +240,26 @@ async function clonePostAssignment(deps, paths) {
|
|
|
143
240
|
}
|
|
144
241
|
return syncTaskBranchToRepo(deps, paths);
|
|
145
242
|
}
|
|
243
|
+
async function withCommitTrailers(deps, paths, state) {
|
|
244
|
+
if (state.state !== "ready") return state;
|
|
245
|
+
await installCommitTrailerHook({
|
|
246
|
+
git: deps.git,
|
|
247
|
+
repoDir: paths.repoDir,
|
|
248
|
+
log: deps.log,
|
|
249
|
+
// The bundle, NOT process.env: git prep runs inside the workbench sidecar,
|
|
250
|
+
// whose own env holds only CONVEYOR_API_URL and POD_BOOTSTRAP_TOKEN.
|
|
251
|
+
// `bundle.envVars` (where CONVEYOR_CARD_SLUG rides) is merged into
|
|
252
|
+
// `ctx.childEnv` for the separate agent container and is never written back
|
|
253
|
+
// onto this process — so reading process.env here saw nothing and skipped
|
|
254
|
+
// the install on every real pod.
|
|
255
|
+
env: { ...process.env, ...deps.bundle.envVars }
|
|
256
|
+
});
|
|
257
|
+
return state;
|
|
258
|
+
}
|
|
146
259
|
async function prepareWorkspaceGit(deps) {
|
|
147
260
|
const { bundle, log } = deps;
|
|
148
261
|
const workspacesDir = deps.workspacesDir ?? "/workspaces";
|
|
149
|
-
const repoDir =
|
|
262
|
+
const repoDir = join2(workspacesDir, "repo");
|
|
150
263
|
const { branch, cloneUrl, repoOwner, repoName } = bundle.gitPlan;
|
|
151
264
|
const credential = bundle.gitCredential;
|
|
152
265
|
const paths = {
|
|
@@ -165,17 +278,17 @@ async function prepareWorkspaceGit(deps) {
|
|
|
165
278
|
if (bundle.gitPlan.provider === "github") {
|
|
166
279
|
syncGithubTokenFiles(bundle.githubToken ?? credential.secret);
|
|
167
280
|
}
|
|
168
|
-
if (
|
|
281
|
+
if (existsSync2(join2(repoDir, ".git"))) {
|
|
169
282
|
log.info(
|
|
170
283
|
deps.podImage ? `[boot] Pod image \u2014 updating repo to latest (branch=${branch})...` : `[boot] Repo present (non-pod-image) \u2014 updating repo to latest (branch=${branch})...`
|
|
171
284
|
);
|
|
172
|
-
return await syncTaskBranchToRepo(deps, paths);
|
|
285
|
+
return await withCommitTrailers(deps, paths, await syncTaskBranchToRepo(deps, paths));
|
|
173
286
|
}
|
|
174
287
|
try {
|
|
175
|
-
|
|
288
|
+
mkdirSync2(workspacesDir, { recursive: true });
|
|
176
289
|
} catch {
|
|
177
290
|
}
|
|
178
|
-
return await clonePostAssignment(deps, paths);
|
|
291
|
+
return await withCommitTrailers(deps, paths, await clonePostAssignment(deps, paths));
|
|
179
292
|
} catch (err) {
|
|
180
293
|
log.error(`[boot] ERROR: git prep failed unexpectedly: ${errText(err)}`);
|
|
181
294
|
return { state: "failed", reason: "git prep failed unexpectedly" };
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
readWorkspaceBytes,
|
|
6
6
|
statWorkspacePath,
|
|
7
7
|
workspacePathExists
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-N4WSUTGV.js";
|
|
9
9
|
import {
|
|
10
10
|
reportBootMilestone
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-GL2DIQEQ.js";
|
|
12
12
|
import {
|
|
13
13
|
getWorkbenchClient
|
|
14
14
|
} from "./chunk-EXQ6AHOY.js";
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
DEFAULT_RETRY_DELAY_MS,
|
|
4
4
|
FETCH_TIMEOUT_MS,
|
|
5
5
|
GIT_PREP_MAX_RETRIES
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-GL2DIQEQ.js";
|
|
7
7
|
import {
|
|
8
8
|
buildConveyorSocketOptions,
|
|
9
9
|
callWithAck,
|
|
@@ -278,6 +278,8 @@ var AgentConnection = class _AgentConnection {
|
|
|
278
278
|
earlyPullBranches = [];
|
|
279
279
|
spawnReviewCallback = null;
|
|
280
280
|
earlySpawnReviews = [];
|
|
281
|
+
spawnBuilderCallback = null;
|
|
282
|
+
earlySpawnBuilders = [];
|
|
281
283
|
spawnTuiCallback = null;
|
|
282
284
|
earlySpawnTuis = [];
|
|
283
285
|
probeUsageCallback = null;
|
|
@@ -455,6 +457,10 @@ var AgentConnection = class _AgentConnection {
|
|
|
455
457
|
if (this.spawnReviewCallback) this.spawnReviewCallback(data);
|
|
456
458
|
else this.earlySpawnReviews.push(data);
|
|
457
459
|
});
|
|
460
|
+
this.socket.on("session:spawnBuilder", (data) => {
|
|
461
|
+
if (this.spawnBuilderCallback) this.spawnBuilderCallback(data);
|
|
462
|
+
else this.earlySpawnBuilders.push(data);
|
|
463
|
+
});
|
|
458
464
|
this.socket.on("session:spawnTui", (data) => {
|
|
459
465
|
if (this.spawnTuiCallback) this.spawnTuiCallback(data);
|
|
460
466
|
else this.earlySpawnTuis.push(data);
|
|
@@ -752,6 +758,13 @@ var AgentConnection = class _AgentConnection {
|
|
|
752
758
|
for (const data of this.earlySpawnReviews) callback(data);
|
|
753
759
|
this.earlySpawnReviews = [];
|
|
754
760
|
}
|
|
761
|
+
/** Mirror of onSpawnReview for the Builder handoff. Drains the early buffer
|
|
762
|
+
* so a Build pressed during boot is not lost. */
|
|
763
|
+
onSpawnBuilder(callback) {
|
|
764
|
+
this.spawnBuilderCallback = callback;
|
|
765
|
+
for (const data of this.earlySpawnBuilders) callback(data);
|
|
766
|
+
this.earlySpawnBuilders = [];
|
|
767
|
+
}
|
|
755
768
|
/**
|
|
756
769
|
* Report that a same-pod review child failed to spawn (fire-and-forget).
|
|
757
770
|
* The server Ends the orphaned review session and falls back to a dedicated
|
|
@@ -767,6 +780,23 @@ var AgentConnection = class _AgentConnection {
|
|
|
767
780
|
}).catch(() => {
|
|
768
781
|
});
|
|
769
782
|
}
|
|
783
|
+
/**
|
|
784
|
+
* Report that this (planner) pod could not spawn the Builder child, so the
|
|
785
|
+
* server can End the orphaned build session and reopen the card instead of
|
|
786
|
+
* leaving it InProgress with a Builder tab that never appears.
|
|
787
|
+
*
|
|
788
|
+
* sessionId is OUR (planner) session — the task-identity guard runs on it;
|
|
789
|
+
* the build session is identified separately.
|
|
790
|
+
*/
|
|
791
|
+
reportBuilderSpawnFailure(buildSessionId, error) {
|
|
792
|
+
if (!this.socket) return;
|
|
793
|
+
void this.call("reportBuilderSpawnFailure", {
|
|
794
|
+
sessionId: this.config.sessionId,
|
|
795
|
+
buildSessionId,
|
|
796
|
+
...error ? { error: error.slice(0, 2e3) } : {}
|
|
797
|
+
}).catch(() => {
|
|
798
|
+
});
|
|
799
|
+
}
|
|
770
800
|
/**
|
|
771
801
|
* Report that this pod's git credential is dead and refreshing did not fix
|
|
772
802
|
* it (fire-and-forget).
|
|
@@ -2056,11 +2086,18 @@ var Lifecycle = class {
|
|
|
2056
2086
|
}
|
|
2057
2087
|
}
|
|
2058
2088
|
// ── Idle timer ─────────────────────────────────────────────────────
|
|
2059
|
-
|
|
2089
|
+
/** Start (or restart) the idle timer.
|
|
2090
|
+
* @param overrideMs Optional custom delay in ms, mirroring
|
|
2091
|
+
* `startDormantTimer`. SessionRunner passes a short delay when it DEFERS a
|
|
2092
|
+
* shutdown because a spawned child is still working: the pod must re-check
|
|
2093
|
+
* soon after that child exits, rather than granting itself a fresh full idle
|
|
2094
|
+
* window every time it defers. */
|
|
2095
|
+
startIdleTimer(overrideMs) {
|
|
2060
2096
|
this.clearIdleTimers();
|
|
2097
|
+
const delay2 = Math.max(0, overrideMs ?? this.config.idleTimeoutMs);
|
|
2061
2098
|
this.idleTimer = setTimeout(() => {
|
|
2062
2099
|
this.callbacks.onIdleTimeout();
|
|
2063
|
-
},
|
|
2100
|
+
}, delay2);
|
|
2064
2101
|
}
|
|
2065
2102
|
cancelIdleTimer() {
|
|
2066
2103
|
this.clearIdleTimers();
|