@askexenow/exe-os 0.9.350 → 0.9.352
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/bin/cli.js +1 -1
- package/dist/bin/deferred-daemon-restart.js +5 -5
- package/dist/bin/exe-forget.js +1 -1
- package/dist/bin/exe-search.js +1 -1
- package/dist/bin/install.js +5 -5
- package/dist/{catchup-brief-HPB5CL7R.js → catchup-brief-7WCQGC2Z.js} +1 -1
- package/dist/{chunk-EGYDW57S.js → chunk-H2BE7ZPC.js} +3 -3
- package/dist/{chunk-FUOXMZUZ.js → chunk-HKXNF72F.js} +7 -5
- package/dist/{chunk-EDEVSNAF.js → chunk-P5C7JCIZ.js} +130 -0
- package/dist/{chunk-RYKHPC3Y.js → chunk-XKA343SE.js} +1 -1
- package/dist/{daemon-orchestration-WHGMXCZW.js → daemon-orchestration-X7UPV6VU.js} +5 -1
- package/dist/{exe-key-GYNPBQDF.js → exe-key-5R6XAPHS.js} +5 -5
- package/dist/hooks/error-recall.js +1 -1
- package/dist/hooks/manifest.json +4 -4
- package/dist/hooks/prompt-submit.js +12 -1
- package/dist/hooks/session-start.js +1 -1
- package/dist/lib/exe-daemon.js +183 -18
- package/dist/lib/hybrid-search.js +1 -1
- package/dist/mcp/register-tools.js +4 -4
- package/dist/mcp/server.js +4 -4
- package/dist/{reranker-GJEC7PVT.js → reranker-N4QIAVEZ.js} +1 -1
- package/package.json +1 -1
- package/release-notes.json +32 -57
- package/src/commands/exe/intercom.md +11 -3
- /package/dist/{chunk-4NPP7ASA.js → chunk-AD42TL2C.js} +0 -0
package/dist/bin/cli.js
CHANGED
|
@@ -201,7 +201,7 @@ if (args.includes("--global")) {
|
|
|
201
201
|
process.exit(1);
|
|
202
202
|
}
|
|
203
203
|
} else if (args[0] === "key") {
|
|
204
|
-
const { main: runKey } = await import("../exe-key-
|
|
204
|
+
const { main: runKey } = await import("../exe-key-5R6XAPHS.js");
|
|
205
205
|
await runKey(args.slice(1));
|
|
206
206
|
} else if (args[0] === "grant") {
|
|
207
207
|
const { main: runGrant } = await import("./exe-grant.js");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
computeMcpSurfaceHash
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-XKA343SE.js";
|
|
5
5
|
import "../chunk-WCGNCH3Q.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-HKXNF72F.js";
|
|
7
7
|
import "../chunk-557C2IGL.js";
|
|
8
8
|
import "../chunk-PELEK2IT.js";
|
|
9
9
|
import "../chunk-L3DKZC4R.js";
|
|
@@ -15,9 +15,9 @@ import "../chunk-MBULOZJF.js";
|
|
|
15
15
|
import "../chunk-KBFHODXV.js";
|
|
16
16
|
import "../chunk-OPDZ62TW.js";
|
|
17
17
|
import "../chunk-YJBBW6OT.js";
|
|
18
|
-
import "../chunk-
|
|
18
|
+
import "../chunk-AD42TL2C.js";
|
|
19
19
|
import "../chunk-CRCB5S7I.js";
|
|
20
|
-
import "../chunk-
|
|
20
|
+
import "../chunk-P5C7JCIZ.js";
|
|
21
21
|
import "../chunk-KES3SNOH.js";
|
|
22
22
|
import "../chunk-E6ORBQHP.js";
|
|
23
23
|
import "../chunk-WAVZDYXS.js";
|
|
@@ -75,7 +75,7 @@ import "../chunk-S3THSOWV.js";
|
|
|
75
75
|
import "../chunk-IOEUAQWP.js";
|
|
76
76
|
import "../chunk-I5AVZOL2.js";
|
|
77
77
|
import "../chunk-APRVAUOR.js";
|
|
78
|
-
import "../chunk-
|
|
78
|
+
import "../chunk-H2BE7ZPC.js";
|
|
79
79
|
import "../chunk-AB4PAQYK.js";
|
|
80
80
|
import "../chunk-LVG5ATTU.js";
|
|
81
81
|
import "../chunk-PENPH5AG.js";
|
package/dist/bin/exe-forget.js
CHANGED
package/dist/bin/exe-search.js
CHANGED
package/dist/bin/install.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import "../chunk-XBOLFVMD.js";
|
|
13
13
|
import {
|
|
14
14
|
computeMcpSurfaceHash
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-XKA343SE.js";
|
|
16
16
|
import {
|
|
17
17
|
buildDaemonLaunchdPlist,
|
|
18
18
|
buildWatchdogLaunchdPlist,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
generateSessionWrappers
|
|
25
25
|
} from "../chunk-CVTWQZOR.js";
|
|
26
26
|
import "../chunk-WCGNCH3Q.js";
|
|
27
|
-
import "../chunk-
|
|
27
|
+
import "../chunk-HKXNF72F.js";
|
|
28
28
|
import "../chunk-557C2IGL.js";
|
|
29
29
|
import "../chunk-PELEK2IT.js";
|
|
30
30
|
import "../chunk-L3DKZC4R.js";
|
|
@@ -36,9 +36,9 @@ import "../chunk-MBULOZJF.js";
|
|
|
36
36
|
import "../chunk-KBFHODXV.js";
|
|
37
37
|
import "../chunk-OPDZ62TW.js";
|
|
38
38
|
import "../chunk-YJBBW6OT.js";
|
|
39
|
-
import "../chunk-
|
|
39
|
+
import "../chunk-AD42TL2C.js";
|
|
40
40
|
import "../chunk-CRCB5S7I.js";
|
|
41
|
-
import "../chunk-
|
|
41
|
+
import "../chunk-P5C7JCIZ.js";
|
|
42
42
|
import "../chunk-KES3SNOH.js";
|
|
43
43
|
import "../chunk-E6ORBQHP.js";
|
|
44
44
|
import "../chunk-WAVZDYXS.js";
|
|
@@ -99,7 +99,7 @@ import "../chunk-APRVAUOR.js";
|
|
|
99
99
|
import {
|
|
100
100
|
resolveEmbeddingsEnabled
|
|
101
101
|
} from "../chunk-KWOAM7TA.js";
|
|
102
|
-
import "../chunk-
|
|
102
|
+
import "../chunk-H2BE7ZPC.js";
|
|
103
103
|
import "../chunk-AB4PAQYK.js";
|
|
104
104
|
import "../chunk-LVG5ATTU.js";
|
|
105
105
|
import "../chunk-PENPH5AG.js";
|
|
@@ -564,7 +564,7 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
564
564
|
let rerankerAvailable = false;
|
|
565
565
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
566
566
|
try {
|
|
567
|
-
const { isRerankerAvailable } = await import("./reranker-
|
|
567
|
+
const { isRerankerAvailable } = await import("./reranker-N4QIAVEZ.js");
|
|
568
568
|
rerankerAvailable = isRerankerAvailable();
|
|
569
569
|
} catch {
|
|
570
570
|
}
|
|
@@ -767,7 +767,7 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
767
767
|
try {
|
|
768
768
|
let rerankedRecords;
|
|
769
769
|
if (graphContextMap.size > 0) {
|
|
770
|
-
const { rerankWithContext } = await import("./reranker-
|
|
770
|
+
const { rerankWithContext } = await import("./reranker-N4QIAVEZ.js");
|
|
771
771
|
const candidates = merged.map((m) => ({
|
|
772
772
|
text: m.raw_text,
|
|
773
773
|
context: graphContextMap.get(m.id)
|
|
@@ -775,7 +775,7 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
775
775
|
const scored = await rerankWithContext(effectiveQuery, candidates, rerankReturnLimit);
|
|
776
776
|
rerankedRecords = scored.map((s) => merged[s.index]);
|
|
777
777
|
} else {
|
|
778
|
-
const { rerank } = await import("./reranker-
|
|
778
|
+
const { rerank } = await import("./reranker-N4QIAVEZ.js");
|
|
779
779
|
rerankedRecords = await rerank(effectiveQuery, merged, rerankReturnLimit);
|
|
780
780
|
}
|
|
781
781
|
if (rerankedRecords.length > 0) {
|
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
import {
|
|
60
60
|
isRerankerAvailable,
|
|
61
61
|
rerankWithScores
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-AD42TL2C.js";
|
|
63
63
|
import {
|
|
64
64
|
getEntityByName,
|
|
65
65
|
getEntityNeighbors,
|
|
@@ -70,7 +70,7 @@ import {
|
|
|
70
70
|
} from "./chunk-CRCB5S7I.js";
|
|
71
71
|
import {
|
|
72
72
|
AUTO_WAKE_MAX_RETRIES
|
|
73
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-P5C7JCIZ.js";
|
|
74
74
|
import {
|
|
75
75
|
listRecentSessionEvents
|
|
76
76
|
} from "./chunk-KES3SNOH.js";
|
|
@@ -242,7 +242,7 @@ import {
|
|
|
242
242
|
import {
|
|
243
243
|
hybridSearch,
|
|
244
244
|
recentRecords
|
|
245
|
-
} from "./chunk-
|
|
245
|
+
} from "./chunk-H2BE7ZPC.js";
|
|
246
246
|
import {
|
|
247
247
|
buildFtsMatch,
|
|
248
248
|
tokenizeForFts
|
|
@@ -2837,11 +2837,13 @@ function registerResumeEmployee(server) {
|
|
|
2837
2837
|
}
|
|
2838
2838
|
},
|
|
2839
2839
|
async ({ agent_id, project_name }) => {
|
|
2840
|
+
let client;
|
|
2840
2841
|
if (!isInitialized()) {
|
|
2841
2842
|
const { fastDbInit: fastDbInit2 } = await import("./fast-db-init-5HF3TLAF.js");
|
|
2842
|
-
await fastDbInit2();
|
|
2843
|
+
client = await fastDbInit2();
|
|
2844
|
+
} else {
|
|
2845
|
+
client = getClient();
|
|
2843
2846
|
}
|
|
2844
|
-
const client = getClient();
|
|
2845
2847
|
const reScope = sessionScopeFilter();
|
|
2846
2848
|
const openTasks = await client.execute({
|
|
2847
2849
|
sql: `SELECT id, title, priority, task_file, status, assigned_tmux,
|
|
@@ -1982,6 +1982,134 @@ function createStuckTaskRealDeps(getClient) {
|
|
|
1982
1982
|
}
|
|
1983
1983
|
};
|
|
1984
1984
|
}
|
|
1985
|
+
async function releaseStaleClaims(deps, nowMs = Date.now()) {
|
|
1986
|
+
let liveSessions;
|
|
1987
|
+
try {
|
|
1988
|
+
liveSessions = deps.listTmuxSessionsAsync ? await deps.listTmuxSessionsAsync() : deps.listTmuxSessions();
|
|
1989
|
+
} catch (e) {
|
|
1990
|
+
process.stderr.write("[stale-claim] list tmux sessions: " + (e instanceof Error ? e.message : String(e)) + "\n");
|
|
1991
|
+
return [];
|
|
1992
|
+
}
|
|
1993
|
+
if (liveSessions.length === 0 && deps.tmuxSessionsVerified?.() === false) {
|
|
1994
|
+
process.stderr.write("[stale-claim] Skipping claim release: tmux session list was not verified (probe failed/timeout). Preserving claims.\n");
|
|
1995
|
+
return [];
|
|
1996
|
+
}
|
|
1997
|
+
const liveSessionSet = new Set(liveSessions);
|
|
1998
|
+
let tasks;
|
|
1999
|
+
try {
|
|
2000
|
+
tasks = await deps.queryClaimedOpenBlockedTasks();
|
|
2001
|
+
} catch (e) {
|
|
2002
|
+
process.stderr.write("[stale-claim] query claimed tasks: " + (e instanceof Error ? e.message : String(e)) + "\n");
|
|
2003
|
+
return [];
|
|
2004
|
+
}
|
|
2005
|
+
const released = [];
|
|
2006
|
+
for (const t of tasks) {
|
|
2007
|
+
if (!t.claimedSession || t.claimedSession === "unknown") continue;
|
|
2008
|
+
if (liveSessionSet.has(t.claimedSession)) continue;
|
|
2009
|
+
const updatedMs = new Date(t.updatedAt).getTime();
|
|
2010
|
+
if (!Number.isNaN(updatedMs) && nowMs - updatedMs < STUCK_TASK_GRACE_MS) continue;
|
|
2011
|
+
try {
|
|
2012
|
+
await deps.releaseClaim(t.taskId, t.claimedSession);
|
|
2013
|
+
released.push(t.taskId);
|
|
2014
|
+
process.stderr.write(
|
|
2015
|
+
`[stale-claim] Task ${t.taskId.slice(0, 8)} (${t.agentId}) \u2014 claim holder session ${t.claimedSession}` + (t.claimInstanceId ? ` (${t.claimInstanceId})` : "") + ` is gone; released claim so the owner can re-claim.
|
|
2016
|
+
`
|
|
2017
|
+
);
|
|
2018
|
+
} catch (err) {
|
|
2019
|
+
process.stderr.write(`[stale-claim] Failed to release claim on ${t.taskId}: ${err instanceof Error ? err.message : String(err)}
|
|
2020
|
+
`);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
return released;
|
|
2024
|
+
}
|
|
2025
|
+
function createStaleClaimRealDeps(getClient) {
|
|
2026
|
+
let lastTmuxListVerified = false;
|
|
2027
|
+
const listSync = () => {
|
|
2028
|
+
try {
|
|
2029
|
+
const socket = process.env.EXE_TMUX_SOCKET;
|
|
2030
|
+
const socketArgs = socket ? ["-L", socket] : [];
|
|
2031
|
+
const out = execFileSync("tmux", [...socketArgs, "list-sessions", "-F", "#{session_name}"], {
|
|
2032
|
+
encoding: "utf8",
|
|
2033
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
2034
|
+
timeout: 3e3
|
|
2035
|
+
});
|
|
2036
|
+
lastTmuxListVerified = true;
|
|
2037
|
+
return out.trim().split("\n").filter(Boolean);
|
|
2038
|
+
} catch {
|
|
2039
|
+
lastTmuxListVerified = false;
|
|
2040
|
+
return [];
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
return {
|
|
2044
|
+
listTmuxSessions: listSync,
|
|
2045
|
+
listTmuxSessionsAsync: async () => {
|
|
2046
|
+
try {
|
|
2047
|
+
const socket = process.env.EXE_TMUX_SOCKET;
|
|
2048
|
+
const socketArgs = socket ? ["-L", socket] : [];
|
|
2049
|
+
const { stdout } = await execFileAsync("tmux", [...socketArgs, "list-sessions", "-F", "#{session_name}"], {
|
|
2050
|
+
encoding: "utf8",
|
|
2051
|
+
timeout: 3e3
|
|
2052
|
+
});
|
|
2053
|
+
lastTmuxListVerified = true;
|
|
2054
|
+
return (stdout ?? "").trim().split("\n").filter(Boolean);
|
|
2055
|
+
} catch {
|
|
2056
|
+
lastTmuxListVerified = false;
|
|
2057
|
+
return [];
|
|
2058
|
+
}
|
|
2059
|
+
},
|
|
2060
|
+
tmuxSessionsVerified: () => lastTmuxListVerified,
|
|
2061
|
+
queryClaimedOpenBlockedTasks: async () => {
|
|
2062
|
+
const { loadDeviceId } = await import("./lib/license.js");
|
|
2063
|
+
const deviceId = loadDeviceId();
|
|
2064
|
+
const client = getClient();
|
|
2065
|
+
const rows = await client.execute({
|
|
2066
|
+
sql: `SELECT id, assigned_to, assigned_tmux, claim_session_instance_id, updated_at
|
|
2067
|
+
FROM tasks
|
|
2068
|
+
WHERE status IN ('open', 'blocked')
|
|
2069
|
+
AND assigned_tmux IS NOT NULL
|
|
2070
|
+
AND assigned_tmux != 'unknown'
|
|
2071
|
+
AND (device_id = ? OR device_id IS NULL)
|
|
2072
|
+
ORDER BY updated_at ASC
|
|
2073
|
+
LIMIT 1000`,
|
|
2074
|
+
args: [deviceId]
|
|
2075
|
+
});
|
|
2076
|
+
return rows.rows.map((r) => ({
|
|
2077
|
+
taskId: String(r.id),
|
|
2078
|
+
agentId: String(r.assigned_to ?? "unknown"),
|
|
2079
|
+
claimedSession: r.assigned_tmux ? String(r.assigned_tmux) : "",
|
|
2080
|
+
claimInstanceId: r.claim_session_instance_id ? String(r.claim_session_instance_id) : null,
|
|
2081
|
+
updatedAt: String(r.updated_at)
|
|
2082
|
+
}));
|
|
2083
|
+
},
|
|
2084
|
+
releaseClaim: async (taskId, holderSession) => {
|
|
2085
|
+
const client = getClient();
|
|
2086
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
2087
|
+
await client.execute({
|
|
2088
|
+
sql: `UPDATE tasks
|
|
2089
|
+
SET assigned_tmux = NULL,
|
|
2090
|
+
instance_id = NULL,
|
|
2091
|
+
claimed_by = NULL,
|
|
2092
|
+
claimed_at = NULL,
|
|
2093
|
+
last_heartbeat_at = NULL,
|
|
2094
|
+
claim_session_instance_id = NULL,
|
|
2095
|
+
lease_expires_at = NULL,
|
|
2096
|
+
updated_at = ?
|
|
2097
|
+
WHERE id = ?
|
|
2098
|
+
AND status IN ('open', 'blocked')
|
|
2099
|
+
AND assigned_tmux = ?`,
|
|
2100
|
+
args: [nowIso, taskId, holderSession]
|
|
2101
|
+
});
|
|
2102
|
+
recordOrchestrationEventBestEffort({
|
|
2103
|
+
eventType: "task.claim_released",
|
|
2104
|
+
source: "daemon-orchestration.releaseStaleClaims",
|
|
2105
|
+
severity: "warn",
|
|
2106
|
+
taskId,
|
|
2107
|
+
result: "dead_holder_session",
|
|
2108
|
+
payload: { holderSession }
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
};
|
|
2112
|
+
}
|
|
1985
2113
|
var DEFAULT_STALE_REVIEW_DAYS = 7;
|
|
1986
2114
|
async function cleanupStaleReviews(deps) {
|
|
1987
2115
|
const thresholdDays = deps.getStaleReviewThreshold();
|
|
@@ -3411,6 +3539,8 @@ export {
|
|
|
3411
3539
|
releaseStuckTasks,
|
|
3412
3540
|
paneShowsBackgroundSubagentWait,
|
|
3413
3541
|
createStuckTaskRealDeps,
|
|
3542
|
+
releaseStaleClaims,
|
|
3543
|
+
createStaleClaimRealDeps,
|
|
3414
3544
|
cleanupStaleReviews,
|
|
3415
3545
|
createStaleReviewRealDeps,
|
|
3416
3546
|
ESCALATE_AFTER_MS,
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
createOrphanReaperRealDepsAsync,
|
|
42
42
|
createReviewNudgeRealDeps,
|
|
43
43
|
createSessionTTLRealDeps,
|
|
44
|
+
createStaleClaimRealDeps,
|
|
44
45
|
createStaleReviewRealDeps,
|
|
45
46
|
createStuckBlockedRealDeps,
|
|
46
47
|
createStuckTaskRealDeps,
|
|
@@ -66,6 +67,7 @@ import {
|
|
|
66
67
|
reapOrphanedMcpProcesses,
|
|
67
68
|
reapOrphanedWorktrees,
|
|
68
69
|
reapZombieAgentProcesses,
|
|
70
|
+
releaseStaleClaims,
|
|
69
71
|
releaseStuckTasks,
|
|
70
72
|
saveNudgeState,
|
|
71
73
|
saveStuckBlockedState,
|
|
@@ -73,7 +75,7 @@ import {
|
|
|
73
75
|
shouldKillIdleSession,
|
|
74
76
|
shouldKillSession,
|
|
75
77
|
shouldNudgeEmployee
|
|
76
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-P5C7JCIZ.js";
|
|
77
79
|
import "./chunk-WAVZDYXS.js";
|
|
78
80
|
import "./chunk-MFX2RWJZ.js";
|
|
79
81
|
import "./chunk-APRVAUOR.js";
|
|
@@ -166,6 +168,7 @@ export {
|
|
|
166
168
|
createOrphanReaperRealDepsAsync,
|
|
167
169
|
createReviewNudgeRealDeps,
|
|
168
170
|
createSessionTTLRealDeps,
|
|
171
|
+
createStaleClaimRealDeps,
|
|
169
172
|
createStaleReviewRealDeps,
|
|
170
173
|
createStuckBlockedRealDeps,
|
|
171
174
|
createStuckTaskRealDeps,
|
|
@@ -191,6 +194,7 @@ export {
|
|
|
191
194
|
reapOrphanedMcpProcesses,
|
|
192
195
|
reapOrphanedWorktrees,
|
|
193
196
|
reapZombieAgentProcesses,
|
|
197
|
+
releaseStaleClaims,
|
|
194
198
|
releaseStuckTasks,
|
|
195
199
|
saveNudgeState,
|
|
196
200
|
saveStuckBlockedState,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
computeMcpSurfaceHash
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XKA343SE.js";
|
|
5
5
|
import "./chunk-WCGNCH3Q.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-HKXNF72F.js";
|
|
7
7
|
import "./chunk-557C2IGL.js";
|
|
8
8
|
import "./chunk-PELEK2IT.js";
|
|
9
9
|
import "./chunk-L3DKZC4R.js";
|
|
@@ -15,9 +15,9 @@ import "./chunk-MBULOZJF.js";
|
|
|
15
15
|
import "./chunk-KBFHODXV.js";
|
|
16
16
|
import "./chunk-OPDZ62TW.js";
|
|
17
17
|
import "./chunk-YJBBW6OT.js";
|
|
18
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-AD42TL2C.js";
|
|
19
19
|
import "./chunk-CRCB5S7I.js";
|
|
20
|
-
import "./chunk-
|
|
20
|
+
import "./chunk-P5C7JCIZ.js";
|
|
21
21
|
import "./chunk-KES3SNOH.js";
|
|
22
22
|
import "./chunk-E6ORBQHP.js";
|
|
23
23
|
import "./chunk-WAVZDYXS.js";
|
|
@@ -75,7 +75,7 @@ import "./chunk-S3THSOWV.js";
|
|
|
75
75
|
import "./chunk-IOEUAQWP.js";
|
|
76
76
|
import "./chunk-I5AVZOL2.js";
|
|
77
77
|
import "./chunk-APRVAUOR.js";
|
|
78
|
-
import "./chunk-
|
|
78
|
+
import "./chunk-H2BE7ZPC.js";
|
|
79
79
|
import "./chunk-AB4PAQYK.js";
|
|
80
80
|
import "./chunk-LVG5ATTU.js";
|
|
81
81
|
import "./chunk-PENPH5AG.js";
|
package/dist/hooks/manifest.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"generatedAt": "2026-07-
|
|
3
|
+
"generatedAt": "2026-07-25T04:38:45.093Z",
|
|
4
4
|
"hashes": {
|
|
5
5
|
"bug-report-worker.js": "b9bcde949c60c3d57ba3f6a66f0fd7c0c24dcbd6314422a0544e38e0687067a9",
|
|
6
6
|
"codex-stop-task-finalizer.js": "6f80ca6d60a73244bbe2d0e347cf1c7eb59f8296388f932d2fe34ca69dc5c0a5",
|
|
7
7
|
"commit-complete.js": "744cd477da753aeb82063c2f8219a3fe9af93681e66e41d5f96d0779027b3aeb",
|
|
8
|
-
"error-recall.js": "
|
|
8
|
+
"error-recall.js": "3f27b6841fd86675b604485a548e5d97d7dbbfdb06afa18701708e0782dae6e0",
|
|
9
9
|
"exe-heartbeat-hook.js": "f8f18927a4397e7c497ec4e4fc79b571b87b73eec5809ee3a7425cffa49b3112",
|
|
10
10
|
"ingest-worker.js": "e194c2237bf431941c27cb213bc793d136b74b04a3e7bf9262371784c999e4b0",
|
|
11
11
|
"ingest.js": "955a231c1dd63fb75843531d2b58dff5b84d73770b9c3ebf5d5e35d6989e9284",
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"post-tool-combined.js": "ef91564fcfb1560e39a612eb91dcda1b2e4ab3cc9338c27ab7f92b390b03b7fa",
|
|
16
16
|
"pre-compact.js": "933f9817f66f4e7d28d903ab40b86478ecfc6f9972e9e447838772d5176ccabf",
|
|
17
17
|
"pre-tool-use.js": "1da521b7fa580ea0a8b3f7722d48ead1fde758e24fd628019f24ee56842dfe46",
|
|
18
|
-
"prompt-submit.js": "
|
|
18
|
+
"prompt-submit.js": "ce4c63dcebe9bde6f9034576da54f1917882a920f74f539907641b2c0f2f7f23",
|
|
19
19
|
"session-end.js": "0eb2bdedb37806e8fa352aad5cb006aa2af12c213169f1cad4157ee6e1737cf5",
|
|
20
|
-
"session-start.js": "
|
|
20
|
+
"session-start.js": "6931cfa741ad0932c75b15752a7aa941aac58c6d8153dd254becee7cd48a4a70",
|
|
21
21
|
"stop.js": "901df128a49816c84208badd1f6a564bb2094d39180212e304b8610fbd3fd85f",
|
|
22
22
|
"subagent-stop.js": "8483c06cfd5c0441cee1b5f1156a9a9ad22862735aa5579010735fce3ed32c0c",
|
|
23
23
|
"summary-worker.js": "3c4ea227f43b94b1c4ce01207dddf55d0c252ace2a53541399a7078519dc19e6"
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import {
|
|
21
21
|
hybridSearch,
|
|
22
22
|
lightweightSearch
|
|
23
|
-
} from "../chunk-
|
|
23
|
+
} from "../chunk-H2BE7ZPC.js";
|
|
24
24
|
import "../chunk-AB4PAQYK.js";
|
|
25
25
|
import "../chunk-LVG5ATTU.js";
|
|
26
26
|
import "../chunk-PENPH5AG.js";
|
|
@@ -35,6 +35,7 @@ import "../chunk-4QCFFW2H.js";
|
|
|
35
35
|
import "../chunk-76CJBM36.js";
|
|
36
36
|
import {
|
|
37
37
|
decideQueuedNudgeDrain,
|
|
38
|
+
getMySession,
|
|
38
39
|
getSessionState,
|
|
39
40
|
isExeSession
|
|
40
41
|
} from "../chunk-LOYK2BRY.js";
|
|
@@ -344,6 +345,16 @@ process.stdin.on("end", async () => {
|
|
|
344
345
|
let _cachedSessionName = null;
|
|
345
346
|
const getCurrentSessionName = () => {
|
|
346
347
|
if (_cachedSessionName !== null) return _cachedSessionName;
|
|
348
|
+
if (inTmux) {
|
|
349
|
+
try {
|
|
350
|
+
const live = getMySession();
|
|
351
|
+
if (live) {
|
|
352
|
+
_cachedSessionName = live;
|
|
353
|
+
return live;
|
|
354
|
+
}
|
|
355
|
+
} catch {
|
|
356
|
+
}
|
|
357
|
+
}
|
|
347
358
|
const ownSession = process.env.EXE_SESSION_NAME?.trim();
|
|
348
359
|
if (ownSession) {
|
|
349
360
|
_cachedSessionName = ownSession;
|
|
@@ -207,7 +207,7 @@ You are **${ag.agentId}**. The daemon was degraded at boot \u2014 memory context
|
|
|
207
207
|
query = `last actions on ${projectName}`;
|
|
208
208
|
header = "## Resuming Session\nHere's where you left off:";
|
|
209
209
|
try {
|
|
210
|
-
const { buildCatchupBrief } = await import("../catchup-brief-
|
|
210
|
+
const { buildCatchupBrief } = await import("../catchup-brief-7WCQGC2Z.js");
|
|
211
211
|
const brief = await buildCatchupBrief(
|
|
212
212
|
agentId,
|
|
213
213
|
projectName,
|
package/dist/lib/exe-daemon.js
CHANGED
|
@@ -544,28 +544,80 @@ function shouldWarnSlowTimer(durationMs, slept, thresholdMs) {
|
|
|
544
544
|
}
|
|
545
545
|
|
|
546
546
|
// src/lib/daemon-timer-watchdog.ts
|
|
547
|
+
var DEFAULT_LAG_PROBE_INTERVAL_MS = 200;
|
|
548
|
+
function defaultLagThresholdMs(timeoutMs) {
|
|
549
|
+
return Math.max(1e3, timeoutMs * 0.1);
|
|
550
|
+
}
|
|
551
|
+
function normalizeLagProbeIntervalMs(intervalMs) {
|
|
552
|
+
return Number.isFinite(intervalMs) && intervalMs !== void 0 && intervalMs > 0 ? intervalMs : DEFAULT_LAG_PROBE_INTERVAL_MS;
|
|
553
|
+
}
|
|
554
|
+
function validNonNegativeMs(value) {
|
|
555
|
+
return Number.isFinite(value) && value > 0 ? value : 0;
|
|
556
|
+
}
|
|
547
557
|
function startTimerTimeoutWatchdog(options) {
|
|
548
558
|
const now = options.now ?? Date.now;
|
|
549
559
|
const setTimer = options.setTimeoutFn ?? setTimeout;
|
|
550
560
|
const clearTimer = options.clearTimeoutFn ?? clearTimeout;
|
|
561
|
+
const setLagProbeTimer = options.setLagProbeTimeoutFn ?? setTimer;
|
|
562
|
+
const clearLagProbeTimer = options.clearLagProbeTimeoutFn ?? clearTimer;
|
|
563
|
+
const lagProbeIntervalMs = normalizeLagProbeIntervalMs(options.lagProbeIntervalMs);
|
|
564
|
+
const lagThresholdMs = options.lagThresholdMs ?? defaultLagThresholdMs(options.timeoutMs);
|
|
551
565
|
let stopped = false;
|
|
552
566
|
let handle = null;
|
|
567
|
+
let lagProbeHandle = null;
|
|
568
|
+
let nextLagProbeDueMs = now() + lagProbeIntervalMs;
|
|
569
|
+
let maxLagMs = 0;
|
|
570
|
+
const recordLag = (lagMs) => {
|
|
571
|
+
maxLagMs = Math.max(maxLagMs, validNonNegativeMs(lagMs));
|
|
572
|
+
};
|
|
573
|
+
const currentMaxLagMs = () => {
|
|
574
|
+
if (options.eventLoopLagSource) {
|
|
575
|
+
recordLag(options.eventLoopLagSource());
|
|
576
|
+
} else {
|
|
577
|
+
recordLag(now() - nextLagProbeDueMs);
|
|
578
|
+
}
|
|
579
|
+
return maxLagMs;
|
|
580
|
+
};
|
|
581
|
+
const armLagProbe = () => {
|
|
582
|
+
if (stopped || options.eventLoopLagSource) return;
|
|
583
|
+
nextLagProbeDueMs = now() + lagProbeIntervalMs;
|
|
584
|
+
lagProbeHandle = setLagProbeTimer(() => {
|
|
585
|
+
lagProbeHandle = null;
|
|
586
|
+
recordLag(now() - nextLagProbeDueMs);
|
|
587
|
+
armLagProbe();
|
|
588
|
+
}, lagProbeIntervalMs);
|
|
589
|
+
lagProbeHandle.unref?.();
|
|
590
|
+
};
|
|
553
591
|
const arm = () => {
|
|
554
592
|
if (stopped) return;
|
|
555
593
|
handle = setTimer(() => {
|
|
556
594
|
handle = null;
|
|
557
595
|
const pausedMs = Math.max(0, options.pauseTotalMs() - options.pauseSnapshotMs);
|
|
558
596
|
const busyMs = now() - options.startedAtMs - pausedMs;
|
|
559
|
-
if (busyMs >= options.timeoutMs)
|
|
597
|
+
if (busyMs >= options.timeoutMs) {
|
|
598
|
+
const windowMaxLagMs = currentMaxLagMs();
|
|
599
|
+
if (windowMaxLagMs >= lagThresholdMs) {
|
|
600
|
+
options.onTimeout(busyMs);
|
|
601
|
+
} else if (options.onSlowAwait) {
|
|
602
|
+
options.onSlowAwait(busyMs, windowMaxLagMs);
|
|
603
|
+
} else {
|
|
604
|
+
options.onTimeout(busyMs);
|
|
605
|
+
}
|
|
606
|
+
maxLagMs = 0;
|
|
607
|
+
nextLagProbeDueMs = now() + lagProbeIntervalMs;
|
|
608
|
+
}
|
|
560
609
|
arm();
|
|
561
610
|
}, options.timeoutMs);
|
|
562
611
|
handle.unref?.();
|
|
563
612
|
};
|
|
613
|
+
armLagProbe();
|
|
564
614
|
arm();
|
|
565
615
|
return () => {
|
|
566
616
|
stopped = true;
|
|
567
617
|
if (handle) clearTimer(handle);
|
|
618
|
+
if (lagProbeHandle) clearLagProbeTimer(lagProbeHandle);
|
|
568
619
|
handle = null;
|
|
620
|
+
lagProbeHandle = null;
|
|
569
621
|
};
|
|
570
622
|
}
|
|
571
623
|
|
|
@@ -1650,12 +1702,14 @@ async function traceDaemonTimer(name, fn) {
|
|
|
1650
1702
|
`);
|
|
1651
1703
|
}
|
|
1652
1704
|
const watchdogStart = Date.now();
|
|
1705
|
+
let timeoutWatchdogSawBlocking = false;
|
|
1653
1706
|
const stopTimeoutWatchdog = startTimerTimeoutWatchdog({
|
|
1654
1707
|
timeoutMs: TIMER_TIMEOUT_MS,
|
|
1655
1708
|
startedAtMs: watchdogStart,
|
|
1656
1709
|
pauseSnapshotMs: pauseSnapshot,
|
|
1657
1710
|
pauseTotalMs: () => sweepPauseTotalMs(name),
|
|
1658
1711
|
onTimeout: () => {
|
|
1712
|
+
timeoutWatchdogSawBlocking = true;
|
|
1659
1713
|
_lastBlockingTimer = { name, durationMs: TIMER_TIMEOUT_MS, ts: Date.now() };
|
|
1660
1714
|
process.stderr.write(
|
|
1661
1715
|
`[exed] Timer TIMEOUT WARNING: ${name} has been running for >${TIMER_TIMEOUT_MS / 1e3}s \u2014 likely blocking the event loop
|
|
@@ -1667,6 +1721,23 @@ async function traceDaemonTimer(name, fn) {
|
|
|
1667
1721
|
message: `Timer ${name} exceeded ${TIMER_TIMEOUT_MS / 1e3}s budget`,
|
|
1668
1722
|
data: { timer: name, timeoutMs: TIMER_TIMEOUT_MS }
|
|
1669
1723
|
});
|
|
1724
|
+
},
|
|
1725
|
+
onSlowAwait: (busyMs, maxLagMs) => {
|
|
1726
|
+
const msg = `[exed] Timer LONG (awaiting I/O, loop responsive): ${name} busy ${Math.round(busyMs)}ms (max event-loop lag ${Math.round(maxLagMs)}ms)`;
|
|
1727
|
+
process.stderr.write(`${msg}
|
|
1728
|
+
`);
|
|
1729
|
+
logDaemonHealth({
|
|
1730
|
+
event: "process_stats",
|
|
1731
|
+
pid: process.pid,
|
|
1732
|
+
message: msg,
|
|
1733
|
+
data: {
|
|
1734
|
+
type: "timer_long_await",
|
|
1735
|
+
timer: name,
|
|
1736
|
+
busyMs: Math.round(busyMs),
|
|
1737
|
+
maxEventLoopLagMs: Math.round(maxLagMs),
|
|
1738
|
+
timeoutMs: TIMER_TIMEOUT_MS
|
|
1739
|
+
}
|
|
1740
|
+
});
|
|
1670
1741
|
}
|
|
1671
1742
|
});
|
|
1672
1743
|
try {
|
|
@@ -1679,7 +1750,7 @@ async function traceDaemonTimer(name, fn) {
|
|
|
1679
1750
|
const slept = span.sleptMs > 1e3;
|
|
1680
1751
|
const sweepPausedMs = Math.max(0, sweepPauseTotalMs(name) - pauseSnapshot);
|
|
1681
1752
|
const busyMs = Math.max(0, durationMs - sweepPausedMs);
|
|
1682
|
-
if (!slept && busyMs >= TIMER_TIMEOUT_MS) {
|
|
1753
|
+
if (timeoutWatchdogSawBlocking && !slept && busyMs >= TIMER_TIMEOUT_MS) {
|
|
1683
1754
|
_lastBlockingTimer = { name, durationMs: busyMs, ts: Date.now() };
|
|
1684
1755
|
}
|
|
1685
1756
|
if (shouldWarnSlowTimer(busyMs, slept, TIMER_SLOW_WARN_MS)) {
|
|
@@ -2717,6 +2788,17 @@ async function handleHealthCheck(socket, requestId) {
|
|
|
2717
2788
|
}
|
|
2718
2789
|
}
|
|
2719
2790
|
var _dbRequestsServed = 0;
|
|
2791
|
+
async function runDaemonTimerRead(sql, args = []) {
|
|
2792
|
+
try {
|
|
2793
|
+
const { isDbReadWorkerReady, runReadQueryInWorker } = await import("./db-read-worker-client.js");
|
|
2794
|
+
if (isDbReadWorkerReady()) {
|
|
2795
|
+
return await runReadQueryInWorker(sql, args);
|
|
2796
|
+
}
|
|
2797
|
+
} catch {
|
|
2798
|
+
}
|
|
2799
|
+
const { getClient } = await import("./database.js");
|
|
2800
|
+
return getClient().execute({ sql, args });
|
|
2801
|
+
}
|
|
2720
2802
|
async function handleDbExecute(socket, requestId, sql, args) {
|
|
2721
2803
|
if (_drainingWrites) {
|
|
2722
2804
|
sendResponse(socket, { id: requestId, error: "Server shutting down" });
|
|
@@ -5113,7 +5195,7 @@ async function startReviewPolling() {
|
|
|
5113
5195
|
lastNudgeSent: /* @__PURE__ */ new Map(),
|
|
5114
5196
|
intervalMs: REVIEW_POLL_INTERVAL_MS
|
|
5115
5197
|
};
|
|
5116
|
-
const { pollReviewNudge, createReviewNudgeRealDeps, loadNudgeState } = await import("../daemon-orchestration-
|
|
5198
|
+
const { pollReviewNudge, createReviewNudgeRealDeps, loadNudgeState } = await import("../daemon-orchestration-X7UPV6VU.js");
|
|
5117
5199
|
const nudgeState = loadNudgeState();
|
|
5118
5200
|
const tick = async () => traceDaemonTimer("review_polling", async () => {
|
|
5119
5201
|
fired("review_polling");
|
|
@@ -5121,6 +5203,47 @@ async function startReviewPolling() {
|
|
|
5121
5203
|
try {
|
|
5122
5204
|
const { getClient } = await import("./database.js");
|
|
5123
5205
|
const deps = polling.createRealDeps(getClient);
|
|
5206
|
+
const { strictSessionScopeFilter } = await import("../task-scope-3F3MSGSX.js");
|
|
5207
|
+
const { getCoordinatorName, DEFAULT_COORDINATOR_TEMPLATE_NAME } = await import("./employees.js");
|
|
5208
|
+
deps.findOrphanedDoneTasks = async (sessionScope) => {
|
|
5209
|
+
const coordinatorName = getCoordinatorName();
|
|
5210
|
+
const odScope = strictSessionScopeFilter(sessionScope, "t");
|
|
5211
|
+
const result = await runDaemonTimerRead(
|
|
5212
|
+
`SELECT t.id, t.title, t.assigned_to, t.assigned_by,
|
|
5213
|
+
t.project_name, t.task_file, t.result, t.status
|
|
5214
|
+
FROM tasks t
|
|
5215
|
+
WHERE t.status = 'needs_review'
|
|
5216
|
+
AND t.assigned_to != ?
|
|
5217
|
+
AND t.assigned_to != ?${odScope.sql}
|
|
5218
|
+
AND (t.result IS NULL OR t.result NOT LIKE '%## Review notes%')`,
|
|
5219
|
+
[coordinatorName, DEFAULT_COORDINATOR_TEMPLATE_NAME, ...odScope.args]
|
|
5220
|
+
);
|
|
5221
|
+
return result.rows;
|
|
5222
|
+
};
|
|
5223
|
+
deps.findTieredReviewCandidates = async (sessionScope) => {
|
|
5224
|
+
let project = null;
|
|
5225
|
+
try {
|
|
5226
|
+
const { getProjectName } = await import("../project-name-EWG5KAZP.js");
|
|
5227
|
+
project = getProjectName() || null;
|
|
5228
|
+
} catch {
|
|
5229
|
+
project = null;
|
|
5230
|
+
}
|
|
5231
|
+
if (!project) return [];
|
|
5232
|
+
const trScope = strictSessionScopeFilter(sessionScope, "t");
|
|
5233
|
+
const result = await runDaemonTimerRead(
|
|
5234
|
+
`SELECT t.id, t.title, t.priority, t.complexity, t.context,
|
|
5235
|
+
t.project_name, t.session_scope, t.assigned_to, t.reviewer,
|
|
5236
|
+
t.verification_receipts
|
|
5237
|
+
FROM tasks t
|
|
5238
|
+
WHERE t.status = 'needs_review'
|
|
5239
|
+
AND t.project_name = ?
|
|
5240
|
+
AND COALESCE(t.review_sampled, 0) = 0${trScope.sql}
|
|
5241
|
+
ORDER BY t.updated_at ASC
|
|
5242
|
+
LIMIT 100`,
|
|
5243
|
+
[project, ...trScope.args]
|
|
5244
|
+
);
|
|
5245
|
+
return result.rows;
|
|
5246
|
+
};
|
|
5124
5247
|
const { sessions: sharedSessions } = await getSharedOrchestrationCaches();
|
|
5125
5248
|
deps.listTmuxSessions = () => sharedSessions;
|
|
5126
5249
|
const sent = await polling.pollPendingReviews(deps, state);
|
|
@@ -5159,7 +5282,7 @@ function startSessionTTL() {
|
|
|
5159
5282
|
if (!await ensureStoreForPolling()) return;
|
|
5160
5283
|
if (inPostRestartDestructiveGrace("session_ttl")) return;
|
|
5161
5284
|
const { getClient } = await import("./database.js");
|
|
5162
|
-
const { checkSessionTTL, createSessionTTLRealDeps } = await import("../daemon-orchestration-
|
|
5285
|
+
const { checkSessionTTL, createSessionTTLRealDeps } = await import("../daemon-orchestration-X7UPV6VU.js");
|
|
5163
5286
|
const deps = createSessionTTLRealDeps(getClient);
|
|
5164
5287
|
const killed = await checkSessionTTL(deps, _wedgeObservations);
|
|
5165
5288
|
if (killed.length > 0) acted("session_ttl");
|
|
@@ -5211,7 +5334,7 @@ function startIdleKill() {
|
|
|
5211
5334
|
const cfg = await getCachedConfig();
|
|
5212
5335
|
if (!cfg) return;
|
|
5213
5336
|
const { getClient } = await import("./database.js");
|
|
5214
|
-
const { pollIdleKill, createIdleKillRealDeps } = await import("../daemon-orchestration-
|
|
5337
|
+
const { pollIdleKill, createIdleKillRealDeps } = await import("../daemon-orchestration-X7UPV6VU.js");
|
|
5215
5338
|
const { sessions: liveSessions, paneCache, aliveSessions } = await getSharedOrchestrationCaches();
|
|
5216
5339
|
const realDeps = createIdleKillRealDeps(
|
|
5217
5340
|
getClient,
|
|
@@ -6040,8 +6163,7 @@ async function refreshAgentData() {
|
|
|
6040
6163
|
const started = Date.now();
|
|
6041
6164
|
try {
|
|
6042
6165
|
if (!await ensureStoreForPolling()) return;
|
|
6043
|
-
const
|
|
6044
|
-
const result = await offloadReadQuery(
|
|
6166
|
+
const result = await runDaemonTimerRead(
|
|
6045
6167
|
`SELECT agent_id,
|
|
6046
6168
|
COUNT(*) as total,
|
|
6047
6169
|
SUM(CASE WHEN timestamp > datetime('now', '-1 day') THEN 1 ELSE 0 END) as growth_24h,
|
|
@@ -6568,7 +6690,7 @@ function startOrphanReaper() {
|
|
|
6568
6690
|
const tick = async () => traceDaemonTimer("orphan_reaper", async () => {
|
|
6569
6691
|
fired("orphan_reaper");
|
|
6570
6692
|
try {
|
|
6571
|
-
const { reapOrphanedMcpProcesses, createOrphanReaperRealDepsAsync } = await import("../daemon-orchestration-
|
|
6693
|
+
const { reapOrphanedMcpProcesses, createOrphanReaperRealDepsAsync } = await import("../daemon-orchestration-X7UPV6VU.js");
|
|
6572
6694
|
const deps = await createOrphanReaperRealDepsAsync();
|
|
6573
6695
|
const reaped = await reapOrphanedMcpProcesses(deps);
|
|
6574
6696
|
if (reaped.length > 0) acted("orphan_reaper");
|
|
@@ -6593,7 +6715,7 @@ function startZombieAgentReaper() {
|
|
|
6593
6715
|
const tick = async () => traceDaemonTimer("zombie_agent_reaper", async () => {
|
|
6594
6716
|
fired("zombie_agent_reaper");
|
|
6595
6717
|
try {
|
|
6596
|
-
const { reapZombieAgentProcesses, createZombieAgentReaperRealDepsAsync } = await import("../daemon-orchestration-
|
|
6718
|
+
const { reapZombieAgentProcesses, createZombieAgentReaperRealDepsAsync } = await import("../daemon-orchestration-X7UPV6VU.js");
|
|
6597
6719
|
const deps = await createZombieAgentReaperRealDepsAsync();
|
|
6598
6720
|
const reaped = reapZombieAgentProcesses(deps);
|
|
6599
6721
|
if (reaped.length > 0) acted("zombie_agent_reaper");
|
|
@@ -6648,7 +6770,7 @@ function startWorktreeReaper() {
|
|
|
6648
6770
|
const tick = async () => traceDaemonTimer("worktree_reaper", async () => {
|
|
6649
6771
|
fired("worktree_reaper");
|
|
6650
6772
|
try {
|
|
6651
|
-
const { reapOrphanedWorktrees, createWorktreeReaperRealDeps } = await import("../daemon-orchestration-
|
|
6773
|
+
const { reapOrphanedWorktrees, createWorktreeReaperRealDeps } = await import("../daemon-orchestration-X7UPV6VU.js");
|
|
6652
6774
|
const deps = await createWorktreeReaperRealDeps();
|
|
6653
6775
|
const result = await reapOrphanedWorktrees(deps);
|
|
6654
6776
|
if (result.pruned.length > 0) {
|
|
@@ -6722,7 +6844,7 @@ function startAutoWake() {
|
|
|
6722
6844
|
if (!await ensureStoreForPolling()) return;
|
|
6723
6845
|
try {
|
|
6724
6846
|
const { getClient } = await import("./database.js");
|
|
6725
|
-
const { pollOrphanedTasks, createAutoWakeRealDeps } = await import("../daemon-orchestration-
|
|
6847
|
+
const { pollOrphanedTasks, createAutoWakeRealDeps } = await import("../daemon-orchestration-X7UPV6VU.js");
|
|
6726
6848
|
const deps = createAutoWakeRealDeps(getClient, process.cwd(), getTmuxSessionsAsync);
|
|
6727
6849
|
const woken = await pollOrphanedTasks(deps);
|
|
6728
6850
|
if (woken.length > 0) acted("auto_wake");
|
|
@@ -6917,14 +7039,22 @@ function startStuckTaskRelease() {
|
|
|
6917
7039
|
const {
|
|
6918
7040
|
releaseStuckTasks,
|
|
6919
7041
|
createStuckTaskRealDeps,
|
|
7042
|
+
releaseStaleClaims,
|
|
7043
|
+
createStaleClaimRealDeps,
|
|
6920
7044
|
escalateStuckBlockedTasks,
|
|
6921
7045
|
createStuckBlockedRealDeps
|
|
6922
|
-
} = await import("../daemon-orchestration-
|
|
7046
|
+
} = await import("../daemon-orchestration-X7UPV6VU.js");
|
|
6923
7047
|
const deps = createStuckTaskRealDeps(getClient);
|
|
6924
7048
|
const released = await releaseStuckTasks(deps);
|
|
6925
7049
|
if (released.length > 0) {
|
|
6926
7050
|
acted("stuck_task_release");
|
|
6927
7051
|
process.stderr.write(`[exed] Stuck-release: ${released.length} task(s) moved to blocked
|
|
7052
|
+
`);
|
|
7053
|
+
}
|
|
7054
|
+
const claimsReleased = await releaseStaleClaims(createStaleClaimRealDeps(getClient));
|
|
7055
|
+
if (claimsReleased.length > 0) {
|
|
7056
|
+
acted("stuck_task_release");
|
|
7057
|
+
process.stderr.write(`[exed] Stale-claim release: ${claimsReleased.length} dead-holder claim(s) cleared
|
|
6928
7058
|
`);
|
|
6929
7059
|
}
|
|
6930
7060
|
const blockedResult = await escalateStuckBlockedTasks(createStuckBlockedRealDeps(getClient));
|
|
@@ -7130,7 +7260,7 @@ function startTaskEnforcementScanner() {
|
|
|
7130
7260
|
sendKeysLiteral: transport.sendKeysLiteral?.bind(transport),
|
|
7131
7261
|
isAlive: (session) => aliveSessions.has(session)
|
|
7132
7262
|
};
|
|
7133
|
-
const { checkAutoWakeGates } = await import("../daemon-orchestration-
|
|
7263
|
+
const { checkAutoWakeGates } = await import("../daemon-orchestration-X7UPV6VU.js");
|
|
7134
7264
|
const { ensureEmployeeAsync, employeeSessionName, sendIntercomAsync } = await import("./tmux-routing.js");
|
|
7135
7265
|
const { shouldAutoInstance } = await import("./employees.js");
|
|
7136
7266
|
const { recordOrchestrationEventBestEffort } = await import("../orchestration-events-EXQRGVEJ.js");
|
|
@@ -7446,6 +7576,43 @@ function startBackgroundJobGuardrails() {
|
|
|
7446
7576
|
timer.unref();
|
|
7447
7577
|
process.stderr.write("[exed] Background job guardrails started (every 60s)\n");
|
|
7448
7578
|
}
|
|
7579
|
+
var TRACE_TTL_SWEEP_TOOLS = ["Bash", "Read", "Edit", "Grep", "Write", "Glob", "apply_patch"];
|
|
7580
|
+
async function sweepExpiredTracesFromDaemonTimer() {
|
|
7581
|
+
const placeholders = TRACE_TTL_SWEEP_TOOLS.map(() => "?").join(",");
|
|
7582
|
+
const idsResult = await runDaemonTimerRead(
|
|
7583
|
+
`SELECT id FROM memories
|
|
7584
|
+
WHERE tool_name IN (${placeholders})
|
|
7585
|
+
AND COALESCE(memory_type, 'raw') = 'raw'
|
|
7586
|
+
AND timestamp < datetime('now', '-7 days')
|
|
7587
|
+
AND COALESCE(importance, 0) < 7`,
|
|
7588
|
+
TRACE_TTL_SWEEP_TOOLS
|
|
7589
|
+
);
|
|
7590
|
+
const memoryIds = idsResult.rows.map((r) => String(r.id)).filter(Boolean);
|
|
7591
|
+
if (memoryIds.length === 0) return 0;
|
|
7592
|
+
const { getClient } = await import("./database.js");
|
|
7593
|
+
const { cleanupGraphLinksForMemories, cleanupCardsForMemories } = await import("../graph-rag-P3PSYW7P.js");
|
|
7594
|
+
const client = getClient();
|
|
7595
|
+
await cleanupGraphLinksForMemories(client, memoryIds);
|
|
7596
|
+
await cleanupCardsForMemories(client, memoryIds);
|
|
7597
|
+
let deleted = 0;
|
|
7598
|
+
const batchSize = 500;
|
|
7599
|
+
for (let i = 0; i < memoryIds.length; i += batchSize) {
|
|
7600
|
+
const batch = memoryIds.slice(i, i + batchSize);
|
|
7601
|
+
const idPlaceholders = batch.map(() => "?").join(",");
|
|
7602
|
+
const result = await client.execute({
|
|
7603
|
+
sql: `DELETE FROM memories
|
|
7604
|
+
WHERE id IN (${idPlaceholders})
|
|
7605
|
+
AND tool_name IN (${placeholders})
|
|
7606
|
+
AND COALESCE(memory_type, 'raw') = 'raw'
|
|
7607
|
+
AND timestamp < datetime('now', '-7 days')
|
|
7608
|
+
AND COALESCE(importance, 0) < 7`,
|
|
7609
|
+
args: [...batch, ...TRACE_TTL_SWEEP_TOOLS]
|
|
7610
|
+
});
|
|
7611
|
+
deleted += Number(result.rowsAffected ?? 0);
|
|
7612
|
+
await yieldToEventLoop();
|
|
7613
|
+
}
|
|
7614
|
+
return deleted;
|
|
7615
|
+
}
|
|
7449
7616
|
function handleSignalShutdown() {
|
|
7450
7617
|
if (_shuttingDown) return;
|
|
7451
7618
|
const { intent: restartIntent, reason } = consumeRestartIntentDetail();
|
|
@@ -8341,7 +8508,7 @@ function startTaskGroupBarriers() {
|
|
|
8341
8508
|
fired("task_group_barriers");
|
|
8342
8509
|
if (!await ensureStoreForPolling()) return;
|
|
8343
8510
|
try {
|
|
8344
|
-
const { pollTaskGroupBarriers } = await import("../daemon-orchestration-
|
|
8511
|
+
const { pollTaskGroupBarriers } = await import("../daemon-orchestration-X7UPV6VU.js");
|
|
8345
8512
|
const firedCount = await pollTaskGroupBarriers();
|
|
8346
8513
|
if (firedCount > 0) {
|
|
8347
8514
|
acted("task_group_barriers");
|
|
@@ -8363,7 +8530,7 @@ function startCheckpointGc() {
|
|
|
8363
8530
|
fired("checkpoint_gc");
|
|
8364
8531
|
if (!await ensureStoreForPolling()) return;
|
|
8365
8532
|
try {
|
|
8366
|
-
const { gcStaleCheckpoints } = await import("../daemon-orchestration-
|
|
8533
|
+
const { gcStaleCheckpoints } = await import("../daemon-orchestration-X7UPV6VU.js");
|
|
8367
8534
|
const cleared = await gcStaleCheckpoints();
|
|
8368
8535
|
if (cleared > 0) {
|
|
8369
8536
|
acted("checkpoint_gc");
|
|
@@ -8563,9 +8730,7 @@ function startLocalFirstTimers() {
|
|
|
8563
8730
|
fired("trace_ttl_sweep");
|
|
8564
8731
|
try {
|
|
8565
8732
|
if (!await ensureStoreForPolling()) return;
|
|
8566
|
-
const
|
|
8567
|
-
const { sweepExpiredTraces } = await import("./consolidation.js");
|
|
8568
|
-
const count = await sweepExpiredTraces(getClient());
|
|
8733
|
+
const count = await sweepExpiredTracesFromDaemonTimer();
|
|
8569
8734
|
if (count > 0) {
|
|
8570
8735
|
acted("trace_ttl_sweep");
|
|
8571
8736
|
process.stderr.write(`[exed] Trace TTL: deleted ${count} expired raw tool traces (>7d, importance<7)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
registerAllTools
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-HKXNF72F.js";
|
|
4
4
|
import "../chunk-557C2IGL.js";
|
|
5
5
|
import "../chunk-PELEK2IT.js";
|
|
6
6
|
import "../chunk-L3DKZC4R.js";
|
|
@@ -12,9 +12,9 @@ import "../chunk-MBULOZJF.js";
|
|
|
12
12
|
import "../chunk-KBFHODXV.js";
|
|
13
13
|
import "../chunk-OPDZ62TW.js";
|
|
14
14
|
import "../chunk-YJBBW6OT.js";
|
|
15
|
-
import "../chunk-
|
|
15
|
+
import "../chunk-AD42TL2C.js";
|
|
16
16
|
import "../chunk-CRCB5S7I.js";
|
|
17
|
-
import "../chunk-
|
|
17
|
+
import "../chunk-P5C7JCIZ.js";
|
|
18
18
|
import "../chunk-KES3SNOH.js";
|
|
19
19
|
import "../chunk-E6ORBQHP.js";
|
|
20
20
|
import "../chunk-WAVZDYXS.js";
|
|
@@ -72,7 +72,7 @@ import "../chunk-S3THSOWV.js";
|
|
|
72
72
|
import "../chunk-IOEUAQWP.js";
|
|
73
73
|
import "../chunk-I5AVZOL2.js";
|
|
74
74
|
import "../chunk-APRVAUOR.js";
|
|
75
|
-
import "../chunk-
|
|
75
|
+
import "../chunk-H2BE7ZPC.js";
|
|
76
76
|
import "../chunk-AB4PAQYK.js";
|
|
77
77
|
import "../chunk-LVG5ATTU.js";
|
|
78
78
|
import "../chunk-PENPH5AG.js";
|
package/dist/mcp/server.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "../chunk-WCGNCH3Q.js";
|
|
11
11
|
import {
|
|
12
12
|
registerAllTools
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-HKXNF72F.js";
|
|
14
14
|
import "../chunk-557C2IGL.js";
|
|
15
15
|
import "../chunk-PELEK2IT.js";
|
|
16
16
|
import "../chunk-L3DKZC4R.js";
|
|
@@ -22,9 +22,9 @@ import "../chunk-MBULOZJF.js";
|
|
|
22
22
|
import "../chunk-KBFHODXV.js";
|
|
23
23
|
import "../chunk-OPDZ62TW.js";
|
|
24
24
|
import "../chunk-YJBBW6OT.js";
|
|
25
|
-
import "../chunk-
|
|
25
|
+
import "../chunk-AD42TL2C.js";
|
|
26
26
|
import "../chunk-CRCB5S7I.js";
|
|
27
|
-
import "../chunk-
|
|
27
|
+
import "../chunk-P5C7JCIZ.js";
|
|
28
28
|
import "../chunk-KES3SNOH.js";
|
|
29
29
|
import "../chunk-E6ORBQHP.js";
|
|
30
30
|
import "../chunk-WAVZDYXS.js";
|
|
@@ -89,7 +89,7 @@ import {
|
|
|
89
89
|
disposeEmbedder
|
|
90
90
|
} from "../chunk-I5AVZOL2.js";
|
|
91
91
|
import "../chunk-APRVAUOR.js";
|
|
92
|
-
import "../chunk-
|
|
92
|
+
import "../chunk-H2BE7ZPC.js";
|
|
93
93
|
import "../chunk-AB4PAQYK.js";
|
|
94
94
|
import {
|
|
95
95
|
disposeStore,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askexenow/exe-os",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.352",
|
|
4
4
|
"description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
package/release-notes.json
CHANGED
|
@@ -1,15 +1,42 @@
|
|
|
1
1
|
{
|
|
2
|
-
"current": "0.9.
|
|
2
|
+
"current": "0.9.352",
|
|
3
3
|
"notes": {
|
|
4
|
-
"0.9.
|
|
5
|
-
"version": "0.9.
|
|
6
|
-
"date": "2026-07-
|
|
4
|
+
"0.9.352": {
|
|
5
|
+
"version": "0.9.352",
|
|
6
|
+
"date": "2026-07-25",
|
|
7
7
|
"features": [],
|
|
8
8
|
"fixes": [],
|
|
9
9
|
"security": [],
|
|
10
10
|
"other": [
|
|
11
|
-
"v0.9.
|
|
11
|
+
"v0.9.352 — multi-coordinator session-scope fix (7eec74b7/45a9f87e) (#477)"
|
|
12
|
+
],
|
|
13
|
+
"migration_notes": []
|
|
14
|
+
},
|
|
15
|
+
"0.9.351": {
|
|
16
|
+
"version": "0.9.351",
|
|
17
|
+
"date": "2026-07-25",
|
|
18
|
+
"features": [
|
|
19
|
+
"daemon offloads heavy timer reads (review polling, agent-data refresh, trace-TTL sweep) to the db-read-worker so long scans no longer block the MCP-serving event loop (d73b5014) (#454)"
|
|
12
20
|
],
|
|
21
|
+
"fixes": [
|
|
22
|
+
"task(resume)/resume_employee no longer throws \"Database client not initialized\" — the resume handler uses the client fastDbInit() returns instead of re-grabbing a global the degraded init paths never register, so reviving a dead cross-project employee works (ff3ef612) (#474)",
|
|
23
|
+
"daemon lag-aware timer watchdog stops false-flagging a timer that is merely awaiting slow I/O as a blocked event loop, killing bogus self-heal restarts (c5fdb821) (#455)",
|
|
24
|
+
"daemon auto-releases a stale task-claim whose holder tmux session is gone (dead-holder-session half of the claim-integrity cluster), so an owner locked out by a dead subagent claim can re-claim (784ebe98, 31a59ab2) (#473)"
|
|
25
|
+
],
|
|
26
|
+
"security": [],
|
|
27
|
+
"other": [],
|
|
28
|
+
"migration_notes": []
|
|
29
|
+
},
|
|
30
|
+
"0.9.350": {
|
|
31
|
+
"version": "0.9.350",
|
|
32
|
+
"date": "2026-07-24",
|
|
33
|
+
"features": [],
|
|
34
|
+
"fixes": [
|
|
35
|
+
"closeout no longer misreports a long dreaming/consolidation timeout as \"Daemon unavailable\" — request-timeouts are classified as timeouts (retryable) and the dreaming + final write are retried with bounded backoff (6b09c8bc) (#470)",
|
|
36
|
+
"create_task resolves the project ROOT when the coordinator cwd is a SUBFOLDER of the project — ancestor-walk to the enclosing git repo instead of refusing dispatch or returning the subfolder (3afb8eb9) (#471)"
|
|
37
|
+
],
|
|
38
|
+
"security": [],
|
|
39
|
+
"other": [],
|
|
13
40
|
"migration_notes": []
|
|
14
41
|
},
|
|
15
42
|
"0.9.349": {
|
|
@@ -49,58 +76,6 @@
|
|
|
49
76
|
"security": [],
|
|
50
77
|
"other": [],
|
|
51
78
|
"migration_notes": []
|
|
52
|
-
},
|
|
53
|
-
"0.9.347": {
|
|
54
|
-
"version": "0.9.347",
|
|
55
|
-
"date": "2026-07-22",
|
|
56
|
-
"features": [],
|
|
57
|
-
"fixes": [
|
|
58
|
-
"daemon: cloud_sync timer no longer blocks the event loop for minutes–hours — the root cause of recurring \"MCP connected but tools fail\" instability; drains in bounded per-tick slices with a durable resumable cursor (c5fdb821 p0) (#422)",
|
|
59
|
-
"stack-update: fail-loud + fail-safe on image auth/pull failure — never leave prod silently partial; pre-deploy pullability check aborts before any mutation (f1c36209 p0) (#432)",
|
|
60
|
-
"api-router: runtime self-heal ALTER for support_feature_requests.product — list_my_features no longer 500s on drifted D1 instances (5604f580) (#421)",
|
|
61
|
-
"orchestration: re-validate the dispatch claim before a stale auto-blocked task resumes — kills duplicate-fleet / wrong-work on pane un-wedge (e1d26a94) (#431)",
|
|
62
|
-
"codex: configurable working default model (EXE_CODEX_DEFAULT_MODEL, fallback gpt-5.5) instead of the ChatGPT-account-rejected gpt-5.3-codex (d691ab3a) (#430)",
|
|
63
|
-
"mcp: reconcile consolidated tool schemas with handlers — config.import_orchestration no longer throws; graph.merge_entities merges or errors loudly instead of silently no-opping (9f6216fe, b8773ad7) (#428)",
|
|
64
|
-
"task: assigned reviewer can now close_task (was mis-gated on caller identity) (b6543735) (#427)",
|
|
65
|
-
"task: completion \"What Changed\" derives from git diff, not scraped prose — no more fabricated changelogs (ae43d0b9) (#429)"
|
|
66
|
-
],
|
|
67
|
-
"security": [],
|
|
68
|
-
"other": [
|
|
69
|
-
"test: deflake ReDoS redaction timing bound (1000ms→5000ms) — was blocking releases; still catches real ReDoS (#426)",
|
|
70
|
-
"test: deflake mcp-proxy retry-budget (20ms→2000ms test config) — recurring release-blocker, no production change (#433)",
|
|
71
|
-
"ci: classified auto-retry on infra failures (e381fcb0) (#425)"
|
|
72
|
-
],
|
|
73
|
-
"migration_notes": []
|
|
74
|
-
},
|
|
75
|
-
"0.9.346": {
|
|
76
|
-
"version": "0.9.346",
|
|
77
|
-
"date": "2026-07-21",
|
|
78
|
-
"features": [
|
|
79
|
-
"exe-os mcp — control plane to list active/inactive MCP servers across Claude/Codex/OpenCode + one-command enable/disable (2aa87aba) (#405)",
|
|
80
|
-
"add Moonshot Kimi K3 to the opencode runtime model whitelist (777f9591) (#404)"
|
|
81
|
-
],
|
|
82
|
-
"fixes": [
|
|
83
|
-
"auto-reclaim stale db.lock left by a dead writer (dead-PID / PID-reuse / legacy) + clamp waitForDaemonReady probes to the caller budget — daemon no longer crash-loops with MCP down (caef9285 p0, 7a4aa0d5) (#399)",
|
|
84
|
-
"await resolveExeSession in the create dispatch path — an un-awaited Promise was reaching ensureEmployeeAsync as the session arg, poisoning dispatch session resolution; also ClickHouse restore profile + git-crypt worktree smudge retry + spawn_worktree preserved (95ca2cd5, d18606d9) (#418)",
|
|
85
|
-
"subagent-aware liveness; a reaped session no longer self-reports a false completion (routes to blocked, preserves result only as unverified evidence) (7de95f57, 879c354d) (#403)",
|
|
86
|
-
"session-instance claims + honest liveness — the duplicate-racer compound defect: bulk-kill circuit breaker, live-sibling checkpoint skipping, owner-scoped auto-wake (#400)",
|
|
87
|
-
"assigned-agent identity at spawn (no more coordinator-identity leak) + dead-spawn dispatch-claim release (8e1bb3de, 629b4c4d) (#408)",
|
|
88
|
-
"gate task nudges on real queue state + invalidate stale resume checkpoints — kills the phantom P0-nudge death spiral (22e464ad, 0f30e7a5) (#414)",
|
|
89
|
-
"re-arming timer watchdog, per-timer sweep-pause accounting, undici connection-loss detection (UND_ERR_SOCKET / other side closed / terminated) (35b72cd3, a705c2bf, 90ba3f38) (#407)",
|
|
90
|
-
"honor INSERT OR IGNORE rowsAffected so dropped duplicates skip ontology/graph/hygiene follow-up; align graph-rag content_hash with the governor (504f76fc, ae584290) (#401)",
|
|
91
|
-
"nested-repo-aware worktree guard + scoped-hash index self-heal with id tie-break (60cb17eb, 8d711f75) (#397)",
|
|
92
|
-
"support add_comment/get_comments now resolve feature-request ids, not just bug ids (64a42e0f) (#406)",
|
|
93
|
-
"align api-router support redaction tokens to the real [REDACTED:<class>] contract; bind cloud-sync verify stamp to file identity (237384bd, e0d1353a) (#417)",
|
|
94
|
-
"8 re-review leftovers — the last exe-os code-fixable batch (#413)"
|
|
95
|
-
],
|
|
96
|
-
"security": [
|
|
97
|
-
"redaction token contract aligned across client/server + api-router so license keys, API keys, tokens and recovery phrases redact consistently (237384bd) (#417)"
|
|
98
|
-
],
|
|
99
|
-
"other": [
|
|
100
|
-
"CI: eliminate false reds on self-hosted build-my — per-run NPM_CONFIG_CACHE removes node-20 setup-cancellation flake; smoke needs build-and-test to cut contention; publish green-CI gate no longer counts schedule-only runs (a80addb4) (#411)",
|
|
101
|
-
"test: deflake daemon-ready \"returns within budget when pings are slow\" with fake timers — real contract is 1..2 budget-bounded pings (#419)"
|
|
102
|
-
],
|
|
103
|
-
"migration_notes": []
|
|
104
79
|
}
|
|
105
80
|
}
|
|
106
81
|
}
|
|
@@ -12,13 +12,21 @@ Triggered by the coordinator via `tmux send-keys -t {window} '/exe-intercom' Ent
|
|
|
12
12
|
# Resolve the configured coordinator and current tmux session.
|
|
13
13
|
COORDINATOR=$(node -e "const fs=require('fs'), os=require('os'), path=require('path'); try { const r=JSON.parse(fs.readFileSync(path.join(os.homedir(),'.exe-os','exe-employees.json'),'utf8')); const c=r.find(e => String(e.role||'').toLowerCase()==='coo'); console.log(c?.name || 'exe'); } catch { console.log('exe'); }" 2>/dev/null)
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
# Bugs 7eec74b7 + 45a9f87e (multi-coordinator scope mis-resolution): an employee
|
|
16
|
+
# session MUST derive its identity from its OWN live tmux session name
|
|
17
|
+
# (davinci-yoda1), never from the EXE_SESSION_NAME env baked at spawn. With two
|
|
18
|
+
# coordinators live (yoda1 + yoda2) that env can carry the WRONG coordinator,
|
|
19
|
+
# making the worker refuse its own yoda1-scoped task as a "cross-session leak".
|
|
20
|
+
# Live tmux is authoritative; fall back to env only when not inside tmux.
|
|
21
|
+
if [ -n "$TMUX" ]; then
|
|
18
22
|
SESSION_NAME=$(tmux display-message -p '#{session_name}' 2>/dev/null)
|
|
23
|
+
[ -z "$SESSION_NAME" ] && SESSION_NAME="$EXE_SESSION_NAME"
|
|
24
|
+
elif [ -n "$EXE_SESSION_NAME" ]; then
|
|
25
|
+
SESSION_NAME="$EXE_SESSION_NAME"
|
|
19
26
|
else
|
|
20
27
|
SESSION_NAME="UNKNOWN"
|
|
21
28
|
fi
|
|
29
|
+
[ -z "$SESSION_NAME" ] && SESSION_NAME="UNKNOWN"
|
|
22
30
|
|
|
23
31
|
# Employee sessions are {employee}-{coordinatorSession}; coordinator sessions have no dash.
|
|
24
32
|
if [ "$SESSION_NAME" = "UNKNOWN" ]; then
|
|
File without changes
|