agenr 0.8.27 → 0.8.28
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/CHANGELOG.md +20 -0
- package/dist/openclaw-plugin/index.d.ts +17 -0
- package/dist/openclaw-plugin/index.js +244 -76
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.8.28] - 2026-02-24
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- command hook fires before_reset handoff logic for RPC-triggered /new (closes #210)
|
|
7
|
+
- before_reset hook only fires in the in-process auto-reply path; sessions.reset RPC
|
|
8
|
+
path only fires the command hook
|
|
9
|
+
- new command hook handler reads and parses the session JSONL directly, then runs
|
|
10
|
+
the same Phase 1 fallback store + Phase 2 LLM upgrade logic
|
|
11
|
+
- dedup guard (Set<sessionId>) prevents double-writes when both hooks fire in
|
|
12
|
+
auto-reply path
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- [AGENR-PROBE] debug logging throughout command hook path for observability
|
|
16
|
+
(to be removed in a future cleanup pass)
|
|
17
|
+
- readAndParseSessionJsonl() helper to parse JSONL session files line by line
|
|
18
|
+
- runHandoffForSession() shared helper extracted from before_reset for reuse
|
|
19
|
+
|
|
20
|
+
### Tests
|
|
21
|
+
- 5 new tests for command hook handoff behavior in index.test.ts
|
|
22
|
+
|
|
3
23
|
## [0.8.27] - 2026-02-24
|
|
4
24
|
|
|
5
25
|
### Changed
|
|
@@ -78,6 +78,7 @@ type HandoffMessage = {
|
|
|
78
78
|
};
|
|
79
79
|
declare function getBaseSessionPath(filePath: string): string;
|
|
80
80
|
declare function readSessionsJson(sessionsDir: string): Promise<Record<string, unknown>>;
|
|
81
|
+
declare function readAndParseSessionJsonl(sessionFile: string): Promise<unknown[]>;
|
|
81
82
|
declare function getSurfaceForSessionFile(sessionFilePath: string, sessionsJson: Record<string, unknown>): string;
|
|
82
83
|
declare function readMessagesFromJsonl(filePath: string): Promise<HandoffMessage[]>;
|
|
83
84
|
declare function findPriorResetFile(sessionsDir: string, currentSessionFile: string): Promise<string | null>;
|
|
@@ -90,6 +91,20 @@ declare function capTranscriptLength(params: {
|
|
|
90
91
|
maxChars: number;
|
|
91
92
|
}): string;
|
|
92
93
|
declare function summarizeSessionForHandoff(currentRawMessages: BeforeResetEvent["messages"], sessionsDir: string, currentSessionFile: string, logger: PluginApi["logger"], streamSimpleImpl?: StreamSimpleFn): Promise<string | null>;
|
|
94
|
+
declare function runHandoffForSession(opts: {
|
|
95
|
+
messages: unknown[];
|
|
96
|
+
sessionFile: string | null;
|
|
97
|
+
sessionId: string;
|
|
98
|
+
sessionKey: string;
|
|
99
|
+
agentId: string;
|
|
100
|
+
agenrPath: string;
|
|
101
|
+
budget: number;
|
|
102
|
+
defaultProject: string | undefined;
|
|
103
|
+
storeConfig: Record<string, unknown>;
|
|
104
|
+
sessionsDir: string;
|
|
105
|
+
logger: PluginLogger | undefined;
|
|
106
|
+
source: "before_reset" | "command";
|
|
107
|
+
}): Promise<void>;
|
|
93
108
|
declare const plugin: {
|
|
94
109
|
id: string;
|
|
95
110
|
name: string;
|
|
@@ -99,6 +114,7 @@ declare const plugin: {
|
|
|
99
114
|
declare const __testing: {
|
|
100
115
|
clearState(): void;
|
|
101
116
|
readSessionsJson: typeof readSessionsJson;
|
|
117
|
+
readAndParseSessionJsonl: typeof readAndParseSessionJsonl;
|
|
102
118
|
getBaseSessionPath: typeof getBaseSessionPath;
|
|
103
119
|
getSurfaceForSessionFile: typeof getSurfaceForSessionFile;
|
|
104
120
|
readMessagesFromJsonl: typeof readMessagesFromJsonl;
|
|
@@ -106,6 +122,7 @@ declare const __testing: {
|
|
|
106
122
|
buildMergedTranscript: typeof buildMergedTranscript;
|
|
107
123
|
capTranscriptLength: typeof capTranscriptLength;
|
|
108
124
|
summarizeSessionForHandoff: typeof summarizeSessionForHandoff;
|
|
125
|
+
runHandoffForSession: typeof runHandoffForSession;
|
|
109
126
|
};
|
|
110
127
|
|
|
111
128
|
export { __testing, plugin as default };
|
|
@@ -917,6 +917,7 @@ var SKIP_SESSION_PATTERNS = [":subagent:", ":cron:"];
|
|
|
917
917
|
var DEFAULT_MAX_SEEN_SESSIONS = 1e3;
|
|
918
918
|
var seenSessions = /* @__PURE__ */ new Map();
|
|
919
919
|
var sessionSignalState = /* @__PURE__ */ new Map();
|
|
920
|
+
var handoffSeenSessionIds = /* @__PURE__ */ new Set();
|
|
920
921
|
var pluginDb = null;
|
|
921
922
|
var pluginDbInit = null;
|
|
922
923
|
var didRegisterDbShutdown = false;
|
|
@@ -1072,6 +1073,26 @@ async function readSessionsJson(sessionsDir) {
|
|
|
1072
1073
|
return {};
|
|
1073
1074
|
}
|
|
1074
1075
|
}
|
|
1076
|
+
async function readAndParseSessionJsonl(sessionFile) {
|
|
1077
|
+
try {
|
|
1078
|
+
const raw = await fs.promises.readFile(sessionFile, "utf8");
|
|
1079
|
+
const messages = [];
|
|
1080
|
+
for (const line of raw.split("\n")) {
|
|
1081
|
+
const trimmed = line.trim();
|
|
1082
|
+
if (!trimmed) {
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
1085
|
+
try {
|
|
1086
|
+
const parsed = JSON.parse(trimmed);
|
|
1087
|
+
messages.push(parsed);
|
|
1088
|
+
} catch {
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
return messages;
|
|
1092
|
+
} catch {
|
|
1093
|
+
return [];
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1075
1096
|
function getSurfaceForSessionFile(sessionFilePath, sessionsJson) {
|
|
1076
1097
|
try {
|
|
1077
1098
|
const normalizedTarget = path3.resolve(sessionFilePath);
|
|
@@ -1407,19 +1428,144 @@ async function retireFallbackHandoffEntries(params) {
|
|
|
1407
1428
|
);
|
|
1408
1429
|
}
|
|
1409
1430
|
}
|
|
1431
|
+
function normalizeHandoffMessages(messages) {
|
|
1432
|
+
return messages.map((message) => {
|
|
1433
|
+
if (!isRecord2(message)) {
|
|
1434
|
+
return message;
|
|
1435
|
+
}
|
|
1436
|
+
if (message["role"] === "user" || message["role"] === "assistant") {
|
|
1437
|
+
return message;
|
|
1438
|
+
}
|
|
1439
|
+
if (message["type"] !== "message" || !isRecord2(message["message"])) {
|
|
1440
|
+
return message;
|
|
1441
|
+
}
|
|
1442
|
+
const parsedMessage = { ...message["message"] };
|
|
1443
|
+
if (typeof parsedMessage["timestamp"] !== "string" && typeof message["timestamp"] === "string") {
|
|
1444
|
+
parsedMessage["timestamp"] = message["timestamp"];
|
|
1445
|
+
}
|
|
1446
|
+
return parsedMessage;
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
async function runHandoffForSession(opts) {
|
|
1450
|
+
const sessionId = opts.sessionId.trim() || opts.sessionKey;
|
|
1451
|
+
if (handoffSeenSessionIds.has(sessionId)) {
|
|
1452
|
+
process.stderr.write(
|
|
1453
|
+
`[AGENR-PROBE] ${opts.source} hook: dedup skip sessionId=${sessionId} source=${opts.source}
|
|
1454
|
+
`
|
|
1455
|
+
);
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
handoffSeenSessionIds.add(sessionId);
|
|
1459
|
+
const evictionTimer = setTimeout(() => {
|
|
1460
|
+
handoffSeenSessionIds.delete(sessionId);
|
|
1461
|
+
}, 6e4);
|
|
1462
|
+
if (typeof evictionTimer.unref === "function") {
|
|
1463
|
+
evictionTimer.unref();
|
|
1464
|
+
}
|
|
1465
|
+
const normalizedMessages = normalizeHandoffMessages(opts.messages);
|
|
1466
|
+
if (normalizedMessages.length === 0) {
|
|
1467
|
+
process.stderr.write(`[AGENR-PROBE] ${opts.source} hook: no messages after normalization source=${opts.source}
|
|
1468
|
+
`);
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
if (!opts.sessionFile) {
|
|
1472
|
+
opts.logger?.debug?.(`[agenr] ${opts.source}: no sessionFile in event, using fallback`);
|
|
1473
|
+
}
|
|
1474
|
+
const fallbackText = extractLastExchangeText(normalizedMessages);
|
|
1475
|
+
let fallbackEntrySubject = null;
|
|
1476
|
+
if (fallbackText) {
|
|
1477
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace("T", " ");
|
|
1478
|
+
fallbackEntrySubject = `session handoff ${timestamp}`;
|
|
1479
|
+
const fallbackEntry = {
|
|
1480
|
+
entries: [
|
|
1481
|
+
{
|
|
1482
|
+
type: "event",
|
|
1483
|
+
importance: 9,
|
|
1484
|
+
subject: fallbackEntrySubject,
|
|
1485
|
+
content: fallbackText,
|
|
1486
|
+
tags: ["handoff", "session"]
|
|
1487
|
+
}
|
|
1488
|
+
]
|
|
1489
|
+
};
|
|
1490
|
+
try {
|
|
1491
|
+
await runStoreTool(opts.agenrPath, fallbackEntry, opts.storeConfig, opts.defaultProject);
|
|
1492
|
+
opts.logger?.debug?.(`[agenr] ${opts.source}: fallback handoff stored`);
|
|
1493
|
+
} catch (err) {
|
|
1494
|
+
opts.logger?.debug?.(
|
|
1495
|
+
`[agenr] ${opts.source}: fallback store failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1496
|
+
);
|
|
1497
|
+
fallbackEntrySubject = null;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
if (opts.sessionFile) {
|
|
1501
|
+
void testingApi.summarizeSessionForHandoff(
|
|
1502
|
+
normalizedMessages,
|
|
1503
|
+
opts.sessionsDir,
|
|
1504
|
+
opts.sessionFile,
|
|
1505
|
+
opts.logger ?? {
|
|
1506
|
+
warn: () => void 0,
|
|
1507
|
+
error: () => void 0
|
|
1508
|
+
}
|
|
1509
|
+
).then(async (summary) => {
|
|
1510
|
+
if (!summary) {
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
if (fallbackEntrySubject && fallbackText) {
|
|
1514
|
+
await retireFallbackHandoffEntries({
|
|
1515
|
+
agenrPath: opts.agenrPath,
|
|
1516
|
+
budget: opts.budget,
|
|
1517
|
+
defaultProject: opts.defaultProject,
|
|
1518
|
+
fallbackSubject: fallbackEntrySubject,
|
|
1519
|
+
fallbackText,
|
|
1520
|
+
logger: opts.logger ?? {
|
|
1521
|
+
warn: () => void 0,
|
|
1522
|
+
error: () => void 0
|
|
1523
|
+
}
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace("T", " ");
|
|
1527
|
+
const llmEntry = {
|
|
1528
|
+
entries: [
|
|
1529
|
+
{
|
|
1530
|
+
type: "event",
|
|
1531
|
+
importance: 10,
|
|
1532
|
+
subject: `session handoff ${timestamp}`,
|
|
1533
|
+
content: summary,
|
|
1534
|
+
tags: ["handoff", "session"]
|
|
1535
|
+
}
|
|
1536
|
+
]
|
|
1537
|
+
};
|
|
1538
|
+
try {
|
|
1539
|
+
await runStoreTool(opts.agenrPath, llmEntry, opts.storeConfig, opts.defaultProject);
|
|
1540
|
+
opts.logger?.debug?.(`[agenr] ${opts.source}: LLM handoff stored`);
|
|
1541
|
+
} catch (err) {
|
|
1542
|
+
opts.logger?.debug?.(
|
|
1543
|
+
`[agenr] ${opts.source}: LLM handoff store failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1544
|
+
);
|
|
1545
|
+
}
|
|
1546
|
+
}).catch((err) => {
|
|
1547
|
+
opts.logger?.debug?.(
|
|
1548
|
+
`[agenr] ${opts.source}: LLM handoff rejected: ${err instanceof Error ? err.message : String(err)}`
|
|
1549
|
+
);
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1410
1553
|
var testingApi = {
|
|
1411
1554
|
clearState() {
|
|
1412
1555
|
seenSessions.clear();
|
|
1413
1556
|
sessionSignalState.clear();
|
|
1557
|
+
handoffSeenSessionIds.clear();
|
|
1414
1558
|
},
|
|
1415
1559
|
readSessionsJson,
|
|
1560
|
+
readAndParseSessionJsonl,
|
|
1416
1561
|
getBaseSessionPath,
|
|
1417
1562
|
getSurfaceForSessionFile,
|
|
1418
1563
|
readMessagesFromJsonl,
|
|
1419
1564
|
findPriorResetFile,
|
|
1420
1565
|
buildMergedTranscript,
|
|
1421
1566
|
capTranscriptLength,
|
|
1422
|
-
summarizeSessionForHandoff
|
|
1567
|
+
summarizeSessionForHandoff,
|
|
1568
|
+
runHandoffForSession
|
|
1423
1569
|
};
|
|
1424
1570
|
var plugin = {
|
|
1425
1571
|
id: "agenr",
|
|
@@ -1578,26 +1724,25 @@ ${formatted.trim()}`);
|
|
|
1578
1724
|
api.on("before_reset", async (event, ctx) => {
|
|
1579
1725
|
try {
|
|
1580
1726
|
process.stderr.write(
|
|
1581
|
-
`[AGENR-PROBE] before_reset FIRED sessionKey=${ctx.sessionKey ?? "none"} msgs=${Array.isArray(event.messages) ? event.messages.length : "non-array"}
|
|
1727
|
+
`[AGENR-PROBE] before_reset FIRED sessionKey=${ctx.sessionKey ?? "none"} msgs=${Array.isArray(event.messages) ? event.messages.length : "non-array"} source=before_reset
|
|
1582
1728
|
`
|
|
1583
1729
|
);
|
|
1584
|
-
api.logger.info?.(`[agenr] before_reset: fired sessionKey=${ctx.sessionKey ?? "none"} agentId=${ctx.agentId ?? "none"} msgs=${Array.isArray(event.messages) ? event.messages.length : "non-array"} sessionFile=${event.sessionFile ?? "none"}`);
|
|
1730
|
+
api.logger.info?.(`[agenr] before_reset: fired sessionKey=${ctx.sessionKey ?? "none"} agentId=${ctx.agentId ?? "none"} msgs=${Array.isArray(event.messages) ? event.messages.length : "non-array"} sessionFile=${event.sessionFile ?? "none"} source=before_reset`);
|
|
1585
1731
|
const sessionKey = ctx.sessionKey;
|
|
1586
1732
|
if (!sessionKey) {
|
|
1587
1733
|
return;
|
|
1588
1734
|
}
|
|
1589
|
-
process.stderr.write(`[AGENR-PROBE] before_reset: sessionKey ok
|
|
1735
|
+
process.stderr.write(`[AGENR-PROBE] before_reset: sessionKey ok source=before_reset
|
|
1590
1736
|
`);
|
|
1591
1737
|
const messages = event.messages;
|
|
1592
1738
|
if (!Array.isArray(messages) || messages.length === 0) {
|
|
1593
1739
|
return;
|
|
1594
1740
|
}
|
|
1595
|
-
process.stderr.write(
|
|
1596
|
-
`
|
|
1741
|
+
process.stderr.write(
|
|
1742
|
+
`[AGENR-PROBE] before_reset: messages ok count=${messages.length} source=before_reset
|
|
1743
|
+
`
|
|
1744
|
+
);
|
|
1597
1745
|
const currentSessionFile = typeof event.sessionFile === "string" && event.sessionFile.trim() ? event.sessionFile.trim() : null;
|
|
1598
|
-
if (!currentSessionFile) {
|
|
1599
|
-
api.logger.debug?.("[agenr] before_reset: no sessionFile in event, using fallback");
|
|
1600
|
-
}
|
|
1601
1746
|
const agentId = ctx.agentId?.trim() || "main";
|
|
1602
1747
|
const sessionsDir = config?.sessionsDir ?? path3.join(os.homedir(), `.openclaw/agents/${agentId}/sessions`);
|
|
1603
1748
|
const agenrPath = resolveAgenrPath(config);
|
|
@@ -1607,73 +1752,21 @@ ${formatted.trim()}`);
|
|
|
1607
1752
|
...config,
|
|
1608
1753
|
logger: api.logger
|
|
1609
1754
|
};
|
|
1610
|
-
const
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
};
|
|
1626
|
-
try {
|
|
1627
|
-
await runStoreTool(agenrPath, fallbackEntry, storeConfig, defaultProject);
|
|
1628
|
-
api.logger.debug?.("[agenr] before_reset: fallback handoff stored");
|
|
1629
|
-
} catch (err) {
|
|
1630
|
-
api.logger.debug?.(
|
|
1631
|
-
`[agenr] before_reset: fallback store failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1632
|
-
);
|
|
1633
|
-
fallbackEntrySubject = null;
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
if (currentSessionFile) {
|
|
1637
|
-
void testingApi.summarizeSessionForHandoff(messages, sessionsDir, currentSessionFile, api.logger).then(async (summary) => {
|
|
1638
|
-
if (!summary) {
|
|
1639
|
-
return;
|
|
1640
|
-
}
|
|
1641
|
-
if (fallbackEntrySubject && fallbackText) {
|
|
1642
|
-
await retireFallbackHandoffEntries({
|
|
1643
|
-
agenrPath,
|
|
1644
|
-
budget,
|
|
1645
|
-
defaultProject,
|
|
1646
|
-
fallbackSubject: fallbackEntrySubject,
|
|
1647
|
-
fallbackText,
|
|
1648
|
-
logger: api.logger
|
|
1649
|
-
});
|
|
1650
|
-
}
|
|
1651
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace("T", " ");
|
|
1652
|
-
const llmEntry = {
|
|
1653
|
-
entries: [
|
|
1654
|
-
{
|
|
1655
|
-
type: "event",
|
|
1656
|
-
importance: 10,
|
|
1657
|
-
subject: `session handoff ${timestamp}`,
|
|
1658
|
-
content: summary,
|
|
1659
|
-
tags: ["handoff", "session"]
|
|
1660
|
-
}
|
|
1661
|
-
]
|
|
1662
|
-
};
|
|
1663
|
-
try {
|
|
1664
|
-
await runStoreTool(agenrPath, llmEntry, storeConfig, defaultProject);
|
|
1665
|
-
api.logger.debug?.("[agenr] before_reset: LLM handoff stored");
|
|
1666
|
-
} catch (err) {
|
|
1667
|
-
api.logger.debug?.(
|
|
1668
|
-
`[agenr] before_reset: LLM handoff store failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1669
|
-
);
|
|
1670
|
-
}
|
|
1671
|
-
}).catch((err) => {
|
|
1672
|
-
api.logger.debug?.(
|
|
1673
|
-
`[agenr] before_reset: LLM handoff rejected: ${err instanceof Error ? err.message : String(err)}`
|
|
1674
|
-
);
|
|
1675
|
-
});
|
|
1676
|
-
}
|
|
1755
|
+
const sessionId = ctx.sessionId ?? ctx.sessionKey ?? sessionKey;
|
|
1756
|
+
await runHandoffForSession({
|
|
1757
|
+
messages,
|
|
1758
|
+
sessionFile: currentSessionFile,
|
|
1759
|
+
sessionId,
|
|
1760
|
+
sessionKey,
|
|
1761
|
+
agentId,
|
|
1762
|
+
agenrPath,
|
|
1763
|
+
budget,
|
|
1764
|
+
defaultProject,
|
|
1765
|
+
storeConfig,
|
|
1766
|
+
sessionsDir,
|
|
1767
|
+
logger: api.logger,
|
|
1768
|
+
source: "before_reset"
|
|
1769
|
+
});
|
|
1677
1770
|
} catch (err) {
|
|
1678
1771
|
api.logger.warn(
|
|
1679
1772
|
`agenr plugin before_reset failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -1682,6 +1775,81 @@ ${formatted.trim()}`);
|
|
|
1682
1775
|
});
|
|
1683
1776
|
process.stderr.write(`[AGENR-PROBE] before_reset hook registered
|
|
1684
1777
|
`);
|
|
1778
|
+
api.on(
|
|
1779
|
+
"command",
|
|
1780
|
+
async (event, ctx) => {
|
|
1781
|
+
try {
|
|
1782
|
+
process.stderr.write(
|
|
1783
|
+
`[AGENR-PROBE] command hook FIRED action=${event.action ?? "none"} sessionKey=${event.sessionKey ?? "none"} source=${String(event.context?.commandSource ?? "unknown")}
|
|
1784
|
+
`
|
|
1785
|
+
);
|
|
1786
|
+
if (event.action !== "new" && event.action !== "reset") {
|
|
1787
|
+
process.stderr.write(
|
|
1788
|
+
`[AGENR-PROBE] command hook: skipping action=${event.action ?? "none"} source=command
|
|
1789
|
+
`
|
|
1790
|
+
);
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
const sessionKey = event.sessionKey;
|
|
1794
|
+
if (!sessionKey) {
|
|
1795
|
+
process.stderr.write("[AGENR-PROBE] command hook: no sessionKey, skipping source=command\n");
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
const sessionFile = event.context?.sessionEntry?.sessionFile ?? null;
|
|
1799
|
+
const sessionId = event.context?.sessionEntry?.sessionId ?? sessionKey;
|
|
1800
|
+
process.stderr.write(
|
|
1801
|
+
`[AGENR-PROBE] command hook: new/reset detected sessionKey=${sessionKey} sessionFile=${sessionFile ?? "none"} source=command
|
|
1802
|
+
`
|
|
1803
|
+
);
|
|
1804
|
+
api.logger.info?.(
|
|
1805
|
+
`[agenr] command hook: fired action=${event.action} sessionKey=${sessionKey} sessionFile=${sessionFile ?? "none"} source=command`
|
|
1806
|
+
);
|
|
1807
|
+
let messages = [];
|
|
1808
|
+
if (sessionFile) {
|
|
1809
|
+
messages = await testingApi.readAndParseSessionJsonl(sessionFile);
|
|
1810
|
+
process.stderr.write(
|
|
1811
|
+
`[AGENR-PROBE] command hook: parsed ${messages.length} messages from JSONL source=command
|
|
1812
|
+
`
|
|
1813
|
+
);
|
|
1814
|
+
} else {
|
|
1815
|
+
process.stderr.write("[AGENR-PROBE] command hook: no sessionFile available source=command\n");
|
|
1816
|
+
}
|
|
1817
|
+
if (messages.length === 0) {
|
|
1818
|
+
process.stderr.write("[AGENR-PROBE] command hook: no messages, skipping handoff source=command\n");
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
const agentId = ctx.agentId?.trim() || "main";
|
|
1822
|
+
const sessionsDir = config?.sessionsDir ?? path3.join(os.homedir(), `.openclaw/agents/${agentId}/sessions`);
|
|
1823
|
+
const agenrPath = resolveAgenrPath(config);
|
|
1824
|
+
const defaultProject = config?.project?.trim() || void 0;
|
|
1825
|
+
const budget = resolveBudget(config);
|
|
1826
|
+
const storeConfig = {
|
|
1827
|
+
...config,
|
|
1828
|
+
logger: api.logger
|
|
1829
|
+
};
|
|
1830
|
+
await runHandoffForSession({
|
|
1831
|
+
messages,
|
|
1832
|
+
sessionFile,
|
|
1833
|
+
sessionId,
|
|
1834
|
+
sessionKey,
|
|
1835
|
+
agentId,
|
|
1836
|
+
agenrPath,
|
|
1837
|
+
budget,
|
|
1838
|
+
defaultProject,
|
|
1839
|
+
storeConfig,
|
|
1840
|
+
sessionsDir,
|
|
1841
|
+
logger: api.logger,
|
|
1842
|
+
source: "command"
|
|
1843
|
+
});
|
|
1844
|
+
process.stderr.write("[AGENR-PROBE] command hook: handoff complete source=command\n");
|
|
1845
|
+
} catch (err) {
|
|
1846
|
+
api.logger.warn(
|
|
1847
|
+
`agenr plugin command hook failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1848
|
+
);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
);
|
|
1852
|
+
process.stderr.write("[AGENR-PROBE] command hook registered\n");
|
|
1685
1853
|
if (api.registerTool) {
|
|
1686
1854
|
if (config?.enabled === false) {
|
|
1687
1855
|
return;
|