@askexenow/exe-os 0.8.40 → 0.8.42
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/backfill-conversations.js +805 -642
- package/dist/bin/backfill-responses.js +804 -641
- package/dist/bin/backfill-vectors.js +791 -634
- package/dist/bin/cleanup-stale-review-tasks.js +788 -631
- package/dist/bin/cli.js +1376 -659
- package/dist/bin/exe-agent.js +20 -1
- package/dist/bin/exe-assign.js +1503 -1343
- package/dist/bin/exe-boot.js +2549 -1784
- package/dist/bin/exe-call.js +39 -1
- package/dist/bin/exe-cloud.js +12 -2
- package/dist/bin/exe-dispatch.js +39 -2
- package/dist/bin/exe-doctor.js +791 -634
- package/dist/bin/exe-export-behaviors.js +792 -637
- package/dist/bin/exe-forget.js +145 -0
- package/dist/bin/exe-gateway.js +2501 -1846
- package/dist/bin/exe-heartbeat.js +147 -1
- package/dist/bin/exe-kill.js +795 -640
- package/dist/bin/exe-launch-agent.js +2168 -2008
- package/dist/bin/exe-link.js +44 -12
- package/dist/bin/exe-new-employee.js +6 -2
- package/dist/bin/exe-pending-messages.js +146 -1
- package/dist/bin/exe-pending-notifications.js +788 -631
- package/dist/bin/exe-pending-reviews.js +176 -1
- package/dist/bin/exe-rename.js +23 -0
- package/dist/bin/exe-review.js +490 -327
- package/dist/bin/exe-search.js +157 -4
- package/dist/bin/exe-session-cleanup.js +2487 -403
- package/dist/bin/exe-settings.js +2 -1
- package/dist/bin/exe-status.js +474 -317
- package/dist/bin/exe-team.js +474 -317
- package/dist/bin/git-sweep.js +2691 -151
- package/dist/bin/graph-backfill.js +794 -637
- package/dist/bin/graph-export.js +798 -641
- package/dist/bin/scan-tasks.js +2951 -44
- package/dist/bin/setup.js +50 -26
- package/dist/bin/shard-migrate.js +792 -637
- package/dist/bin/wiki-sync.js +794 -637
- package/dist/gateway/index.js +2542 -1887
- package/dist/hooks/bug-report-worker.js +2118 -576
- package/dist/hooks/commit-complete.js +2690 -150
- package/dist/hooks/error-recall.js +157 -4
- package/dist/hooks/ingest-worker.js +1455 -803
- package/dist/hooks/instructions-loaded.js +151 -0
- package/dist/hooks/notification.js +153 -2
- package/dist/hooks/post-compact.js +164 -0
- package/dist/hooks/pre-compact.js +3073 -101
- package/dist/hooks/pre-tool-use.js +151 -0
- package/dist/hooks/prompt-ingest-worker.js +1670 -1509
- package/dist/hooks/prompt-submit.js +2650 -1074
- package/dist/hooks/response-ingest-worker.js +154 -6
- package/dist/hooks/session-end.js +153 -2
- package/dist/hooks/session-start.js +157 -4
- package/dist/hooks/stop.js +151 -0
- package/dist/hooks/subagent-stop.js +155 -2
- package/dist/hooks/summary-worker.js +190 -21
- package/dist/index.js +326 -102
- package/dist/lib/cloud-sync.js +31 -10
- package/dist/lib/config.js +2 -0
- package/dist/lib/consolidation.js +69 -2
- package/dist/lib/database.js +19 -0
- package/dist/lib/device-registry.js +19 -0
- package/dist/lib/embedder.js +3 -1
- package/dist/lib/employee-templates.js +20 -1
- package/dist/lib/exe-daemon.js +285 -18
- package/dist/lib/hybrid-search.js +157 -4
- package/dist/lib/messaging.js +39 -2
- package/dist/lib/schedules.js +792 -637
- package/dist/lib/store.js +796 -636
- package/dist/lib/tasks.js +1485 -918
- package/dist/lib/tmux-routing.js +194 -10
- package/dist/mcp/server.js +1643 -924
- package/dist/mcp/tools/create-task.js +2283 -829
- package/dist/mcp/tools/list-tasks.js +2788 -159
- package/dist/mcp/tools/send-message.js +39 -2
- package/dist/mcp/tools/update-task.js +79 -0
- package/dist/runtime/index.js +280 -68
- package/dist/tui/App.js +1485 -645
- package/package.json +3 -2
|
@@ -310,6 +310,13 @@ async function ensureSchema() {
|
|
|
310
310
|
});
|
|
311
311
|
} catch {
|
|
312
312
|
}
|
|
313
|
+
try {
|
|
314
|
+
await client.execute({
|
|
315
|
+
sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
|
|
316
|
+
args: []
|
|
317
|
+
});
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
313
320
|
try {
|
|
314
321
|
await client.execute({
|
|
315
322
|
sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
|
|
@@ -756,6 +763,18 @@ async function ensureSchema() {
|
|
|
756
763
|
CREATE INDEX IF NOT EXISTS idx_session_kills_agent
|
|
757
764
|
ON session_kills(agent_id);
|
|
758
765
|
`);
|
|
766
|
+
await client.execute(`
|
|
767
|
+
CREATE TABLE IF NOT EXISTS global_procedures (
|
|
768
|
+
id TEXT PRIMARY KEY,
|
|
769
|
+
title TEXT NOT NULL,
|
|
770
|
+
content TEXT NOT NULL,
|
|
771
|
+
priority TEXT NOT NULL DEFAULT 'p0',
|
|
772
|
+
domain TEXT,
|
|
773
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
774
|
+
created_at TEXT NOT NULL,
|
|
775
|
+
updated_at TEXT NOT NULL
|
|
776
|
+
)
|
|
777
|
+
`);
|
|
759
778
|
await client.executeMultiple(`
|
|
760
779
|
CREATE TABLE IF NOT EXISTS conversations (
|
|
761
780
|
id TEXT PRIMARY KEY,
|
|
@@ -968,6 +987,7 @@ var config_exports = {};
|
|
|
968
987
|
__export(config_exports, {
|
|
969
988
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
970
989
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
990
|
+
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
971
991
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
972
992
|
DB_PATH: () => DB_PATH,
|
|
973
993
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -1123,7 +1143,7 @@ async function loadConfigFrom(configPath) {
|
|
|
1123
1143
|
return { ...DEFAULT_CONFIG };
|
|
1124
1144
|
}
|
|
1125
1145
|
}
|
|
1126
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
1146
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
1127
1147
|
var init_config = __esm({
|
|
1128
1148
|
"src/lib/config.ts"() {
|
|
1129
1149
|
"use strict";
|
|
@@ -1131,6 +1151,7 @@ var init_config = __esm({
|
|
|
1131
1151
|
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
1132
1152
|
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
1133
1153
|
CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
|
|
1154
|
+
COO_AGENT_NAME = "exe";
|
|
1134
1155
|
LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
|
|
1135
1156
|
CURRENT_CONFIG_VERSION = 1;
|
|
1136
1157
|
DEFAULT_CONFIG = {
|
|
@@ -1209,6 +1230,61 @@ var init_config = __esm({
|
|
|
1209
1230
|
}
|
|
1210
1231
|
});
|
|
1211
1232
|
|
|
1233
|
+
// src/lib/state-bus.ts
|
|
1234
|
+
var StateBus, orgBus;
|
|
1235
|
+
var init_state_bus = __esm({
|
|
1236
|
+
"src/lib/state-bus.ts"() {
|
|
1237
|
+
"use strict";
|
|
1238
|
+
StateBus = class {
|
|
1239
|
+
handlers = /* @__PURE__ */ new Map();
|
|
1240
|
+
globalHandlers = /* @__PURE__ */ new Set();
|
|
1241
|
+
/** Emit an event to all subscribers */
|
|
1242
|
+
emit(event) {
|
|
1243
|
+
const typeHandlers = this.handlers.get(event.type);
|
|
1244
|
+
if (typeHandlers) {
|
|
1245
|
+
for (const handler of typeHandlers) {
|
|
1246
|
+
try {
|
|
1247
|
+
handler(event);
|
|
1248
|
+
} catch {
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
for (const handler of this.globalHandlers) {
|
|
1253
|
+
try {
|
|
1254
|
+
handler(event);
|
|
1255
|
+
} catch {
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
/** Subscribe to a specific event type */
|
|
1260
|
+
on(type, handler) {
|
|
1261
|
+
if (!this.handlers.has(type)) {
|
|
1262
|
+
this.handlers.set(type, /* @__PURE__ */ new Set());
|
|
1263
|
+
}
|
|
1264
|
+
this.handlers.get(type).add(handler);
|
|
1265
|
+
}
|
|
1266
|
+
/** Subscribe to ALL events */
|
|
1267
|
+
onAny(handler) {
|
|
1268
|
+
this.globalHandlers.add(handler);
|
|
1269
|
+
}
|
|
1270
|
+
/** Unsubscribe from a specific event type */
|
|
1271
|
+
off(type, handler) {
|
|
1272
|
+
this.handlers.get(type)?.delete(handler);
|
|
1273
|
+
}
|
|
1274
|
+
/** Unsubscribe from ALL events */
|
|
1275
|
+
offAny(handler) {
|
|
1276
|
+
this.globalHandlers.delete(handler);
|
|
1277
|
+
}
|
|
1278
|
+
/** Remove all listeners */
|
|
1279
|
+
clear() {
|
|
1280
|
+
this.handlers.clear();
|
|
1281
|
+
this.globalHandlers.clear();
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
orgBus = new StateBus();
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1212
1288
|
// src/lib/shard-manager.ts
|
|
1213
1289
|
var shard_manager_exports = {};
|
|
1214
1290
|
__export(shard_manager_exports, {
|
|
@@ -1450,6 +1526,71 @@ var init_shard_manager = __esm({
|
|
|
1450
1526
|
}
|
|
1451
1527
|
});
|
|
1452
1528
|
|
|
1529
|
+
// src/lib/global-procedures.ts
|
|
1530
|
+
var global_procedures_exports = {};
|
|
1531
|
+
__export(global_procedures_exports, {
|
|
1532
|
+
deactivateGlobalProcedure: () => deactivateGlobalProcedure,
|
|
1533
|
+
getGlobalProceduresBlock: () => getGlobalProceduresBlock,
|
|
1534
|
+
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
1535
|
+
storeGlobalProcedure: () => storeGlobalProcedure
|
|
1536
|
+
});
|
|
1537
|
+
import { randomUUID } from "crypto";
|
|
1538
|
+
async function loadGlobalProcedures() {
|
|
1539
|
+
const client = getClient();
|
|
1540
|
+
const result = await client.execute({
|
|
1541
|
+
sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
|
|
1542
|
+
args: []
|
|
1543
|
+
});
|
|
1544
|
+
const procedures = result.rows;
|
|
1545
|
+
if (procedures.length > 0) {
|
|
1546
|
+
_cache = procedures.map((p) => `### ${p.title}
|
|
1547
|
+
${p.content}`).join("\n\n");
|
|
1548
|
+
} else {
|
|
1549
|
+
_cache = "";
|
|
1550
|
+
}
|
|
1551
|
+
_cacheLoaded = true;
|
|
1552
|
+
return procedures;
|
|
1553
|
+
}
|
|
1554
|
+
function getGlobalProceduresBlock() {
|
|
1555
|
+
if (!_cacheLoaded) return "";
|
|
1556
|
+
if (!_cache) return "";
|
|
1557
|
+
return `## Organization-Wide Procedures (MANDATORY \u2014 supersedes all other rules)
|
|
1558
|
+
|
|
1559
|
+
${_cache}
|
|
1560
|
+
`;
|
|
1561
|
+
}
|
|
1562
|
+
async function storeGlobalProcedure(input) {
|
|
1563
|
+
const id = randomUUID();
|
|
1564
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1565
|
+
const client = getClient();
|
|
1566
|
+
await client.execute({
|
|
1567
|
+
sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
|
|
1568
|
+
VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
1569
|
+
args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
|
|
1570
|
+
});
|
|
1571
|
+
await loadGlobalProcedures();
|
|
1572
|
+
return id;
|
|
1573
|
+
}
|
|
1574
|
+
async function deactivateGlobalProcedure(id) {
|
|
1575
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1576
|
+
const client = getClient();
|
|
1577
|
+
const result = await client.execute({
|
|
1578
|
+
sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE id = ?",
|
|
1579
|
+
args: [now, id]
|
|
1580
|
+
});
|
|
1581
|
+
await loadGlobalProcedures();
|
|
1582
|
+
return result.rowsAffected > 0;
|
|
1583
|
+
}
|
|
1584
|
+
var _cache, _cacheLoaded;
|
|
1585
|
+
var init_global_procedures = __esm({
|
|
1586
|
+
"src/lib/global-procedures.ts"() {
|
|
1587
|
+
"use strict";
|
|
1588
|
+
init_database();
|
|
1589
|
+
_cache = "";
|
|
1590
|
+
_cacheLoaded = false;
|
|
1591
|
+
}
|
|
1592
|
+
});
|
|
1593
|
+
|
|
1453
1594
|
// src/lib/store.ts
|
|
1454
1595
|
var store_exports = {};
|
|
1455
1596
|
__export(store_exports, {
|
|
@@ -1529,6 +1670,11 @@ async function initStore(options) {
|
|
|
1529
1670
|
"version-query"
|
|
1530
1671
|
);
|
|
1531
1672
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1673
|
+
try {
|
|
1674
|
+
const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
|
|
1675
|
+
await loadGlobalProcedures2();
|
|
1676
|
+
} catch {
|
|
1677
|
+
}
|
|
1532
1678
|
}
|
|
1533
1679
|
function classifyTier(record) {
|
|
1534
1680
|
if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
|
|
@@ -1570,6 +1716,12 @@ async function writeMemory(record) {
|
|
|
1570
1716
|
supersedes_id: record.supersedes_id ?? null
|
|
1571
1717
|
};
|
|
1572
1718
|
_pendingRecords.push(dbRow);
|
|
1719
|
+
orgBus.emit({
|
|
1720
|
+
type: "memory_stored",
|
|
1721
|
+
agentId: record.agent_id,
|
|
1722
|
+
project: record.project_name,
|
|
1723
|
+
timestamp: record.timestamp
|
|
1724
|
+
});
|
|
1573
1725
|
const MAX_PENDING = 1e3;
|
|
1574
1726
|
if (_pendingRecords.length > MAX_PENDING) {
|
|
1575
1727
|
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
@@ -1915,6 +2067,7 @@ var init_store = __esm({
|
|
|
1915
2067
|
init_database();
|
|
1916
2068
|
init_keychain();
|
|
1917
2069
|
init_config();
|
|
2070
|
+
init_state_bus();
|
|
1918
2071
|
INIT_MAX_RETRIES = 3;
|
|
1919
2072
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
1920
2073
|
_pendingRecords = [];
|
|
@@ -2018,7 +2171,7 @@ var init_self_query_router = __esm({
|
|
|
2018
2171
|
// src/lib/exe-daemon-client.ts
|
|
2019
2172
|
import net from "net";
|
|
2020
2173
|
import { spawn } from "child_process";
|
|
2021
|
-
import { randomUUID } from "crypto";
|
|
2174
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2022
2175
|
import { existsSync as existsSync4, unlinkSync, readFileSync as readFileSync2, openSync, closeSync, statSync } from "fs";
|
|
2023
2176
|
import path4 from "path";
|
|
2024
2177
|
import { fileURLToPath } from "url";
|
|
@@ -2210,7 +2363,7 @@ function sendRequest(texts, priority) {
|
|
|
2210
2363
|
resolve({ error: "Not connected" });
|
|
2211
2364
|
return;
|
|
2212
2365
|
}
|
|
2213
|
-
const id =
|
|
2366
|
+
const id = randomUUID2();
|
|
2214
2367
|
const timer = setTimeout(() => {
|
|
2215
2368
|
_pending.delete(id);
|
|
2216
2369
|
resolve({ error: "Request timeout" });
|
|
@@ -2228,7 +2381,7 @@ function sendRequest(texts, priority) {
|
|
|
2228
2381
|
async function pingDaemon() {
|
|
2229
2382
|
if (!_socket || !_connected) return null;
|
|
2230
2383
|
return new Promise((resolve) => {
|
|
2231
|
-
const id =
|
|
2384
|
+
const id = randomUUID2();
|
|
2232
2385
|
const timer = setTimeout(() => {
|
|
2233
2386
|
_pending.delete(id);
|
|
2234
2387
|
resolve(null);
|
package/dist/lib/messaging.js
CHANGED
|
@@ -631,8 +631,28 @@ function getMySession() {
|
|
|
631
631
|
return getTransport().getMySession();
|
|
632
632
|
}
|
|
633
633
|
function employeeSessionName(employee, exeSession, instance) {
|
|
634
|
+
if (!/^exe\d+$/.test(exeSession)) {
|
|
635
|
+
const root = extractRootExe(exeSession);
|
|
636
|
+
if (root) {
|
|
637
|
+
process.stderr.write(
|
|
638
|
+
`[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
|
|
639
|
+
`
|
|
640
|
+
);
|
|
641
|
+
exeSession = root;
|
|
642
|
+
} else {
|
|
643
|
+
throw new Error(
|
|
644
|
+
`Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
634
648
|
const suffix = instance != null && instance > 0 ? String(instance) : "";
|
|
635
|
-
|
|
649
|
+
const name = `${employee}${suffix}-${exeSession}`;
|
|
650
|
+
if (!VALID_SESSION_NAME.test(name)) {
|
|
651
|
+
throw new Error(
|
|
652
|
+
`Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
return name;
|
|
636
656
|
}
|
|
637
657
|
function extractRootExe(name) {
|
|
638
658
|
const match = name.match(/(exe\d+)$/);
|
|
@@ -791,6 +811,22 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
791
811
|
error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
|
|
792
812
|
};
|
|
793
813
|
}
|
|
814
|
+
if (!/^exe\d+$/.test(exeSession)) {
|
|
815
|
+
const root = extractRootExe(exeSession);
|
|
816
|
+
if (root) {
|
|
817
|
+
process.stderr.write(
|
|
818
|
+
`[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
|
|
819
|
+
`
|
|
820
|
+
);
|
|
821
|
+
exeSession = root;
|
|
822
|
+
} else {
|
|
823
|
+
return {
|
|
824
|
+
status: "failed",
|
|
825
|
+
sessionName: "",
|
|
826
|
+
error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
}
|
|
794
830
|
let effectiveInstance = opts?.instance;
|
|
795
831
|
if (effectiveInstance === void 0 && opts?.autoInstance) {
|
|
796
832
|
const free = findFreeInstance(
|
|
@@ -1037,7 +1073,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1037
1073
|
releaseSpawnLock(sessionName);
|
|
1038
1074
|
return { sessionName };
|
|
1039
1075
|
}
|
|
1040
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
1076
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
1041
1077
|
var init_tmux_routing = __esm({
|
|
1042
1078
|
"src/lib/tmux-routing.ts"() {
|
|
1043
1079
|
"use strict";
|
|
@@ -1052,6 +1088,7 @@ var init_tmux_routing = __esm({
|
|
|
1052
1088
|
SPAWN_LOCK_DIR = path7.join(os4.homedir(), ".exe-os", "spawn-locks");
|
|
1053
1089
|
SESSION_CACHE = path7.join(os4.homedir(), ".exe-os", "session-cache");
|
|
1054
1090
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
1091
|
+
VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
|
|
1055
1092
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
1056
1093
|
INTERCOM_LOG2 = path7.join(os4.homedir(), ".exe-os", "intercom.log");
|
|
1057
1094
|
DEBOUNCE_FILE = path7.join(SESSION_CACHE, "intercom-debounce.json");
|