u-foo 2.3.25 → 2.3.26
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/package.json
CHANGED
|
@@ -349,6 +349,27 @@ async function handleEvent(
|
|
|
349
349
|
await busSender.flush();
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
+
function compactToolDetail(value = "", maxLength = 120) {
|
|
353
|
+
const text = String(value || "").replace(/\s+/g, " ").trim();
|
|
354
|
+
if (!text) return "";
|
|
355
|
+
if (text.length <= maxLength) return text;
|
|
356
|
+
return `${text.slice(0, Math.max(0, maxLength - 3))}...`;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function summarizeThreadToolCall(event = {}) {
|
|
360
|
+
const name = String(event.name || event.tool || event.tool_name || "tool").trim() || "tool";
|
|
361
|
+
const args = event.args && typeof event.args === "object" ? event.args : {};
|
|
362
|
+
const detail = args.command
|
|
363
|
+
|| args.cmd
|
|
364
|
+
|| args.code
|
|
365
|
+
|| args.path
|
|
366
|
+
|| args.file
|
|
367
|
+
|| args.target
|
|
368
|
+
|| args.query
|
|
369
|
+
|| "";
|
|
370
|
+
return [name, compactToolDetail(detail)].filter(Boolean).join(" · ");
|
|
371
|
+
}
|
|
372
|
+
|
|
352
373
|
async function handleThreadedEvent({
|
|
353
374
|
agentType,
|
|
354
375
|
provider,
|
|
@@ -372,6 +393,11 @@ async function handleThreadedEvent({
|
|
|
372
393
|
} else {
|
|
373
394
|
plainReplyParts.push(String(event.delta));
|
|
374
395
|
}
|
|
396
|
+
} else if (event.type === "tool_call") {
|
|
397
|
+
const summary = summarizeThreadToolCall(event);
|
|
398
|
+
if (streamToPublisher && summary) {
|
|
399
|
+
emitStreamDelta(`\nTool: ${summary}\n`);
|
|
400
|
+
}
|
|
375
401
|
} else if (event.type === "turn_failed") {
|
|
376
402
|
throw new Error(event.error || `thread turn failed for ${agentType}`);
|
|
377
403
|
}
|
|
@@ -3,6 +3,12 @@ const { version: packageVersion } = require("../../package.json");
|
|
|
3
3
|
|
|
4
4
|
const ANSI_RESET = "\x1b[0m";
|
|
5
5
|
const CLAUDE_ORANGE = "\x1b[38;2;217;119;87m";
|
|
6
|
+
const BUS_STATUS_INDICATORS = {
|
|
7
|
+
working: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
8
|
+
starting: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
9
|
+
waiting_input: ["∙", "∙∙", "∙∙∙", "∙∙", "∙"],
|
|
10
|
+
blocked: ["!"],
|
|
11
|
+
};
|
|
6
12
|
|
|
7
13
|
function createAgentViewController(options = {}) {
|
|
8
14
|
const {
|
|
@@ -11,6 +17,8 @@ function createAgentViewController(options = {}) {
|
|
|
11
17
|
processStdout = process.stdout,
|
|
12
18
|
now = () => Date.now(),
|
|
13
19
|
setTimeoutFn = setTimeout,
|
|
20
|
+
setIntervalFn = setInterval,
|
|
21
|
+
clearIntervalFn = clearInterval,
|
|
14
22
|
computeAgentBar = () => ({ bar: "", windowStart: 0 }),
|
|
15
23
|
agentBarHints = { normal: "", dashboard: "" },
|
|
16
24
|
maxAgentWindow = 4,
|
|
@@ -23,6 +31,7 @@ function createAgentViewController(options = {}) {
|
|
|
23
31
|
setAgentListWindowStart = () => {},
|
|
24
32
|
getAgentLabel = (id) => id,
|
|
25
33
|
getAgentStates = () => ({}),
|
|
34
|
+
getAgentActivityMeta = () => ({}),
|
|
26
35
|
getProjectRoot = () => process.cwd(),
|
|
27
36
|
setDashboardView = () => {},
|
|
28
37
|
setScreenGrabKeys = (value) => {
|
|
@@ -62,6 +71,10 @@ function createAgentViewController(options = {}) {
|
|
|
62
71
|
let busStartupAgentId = "";
|
|
63
72
|
let busStartupLineCount = 0;
|
|
64
73
|
let busAgentReplyActive = false;
|
|
74
|
+
let busStatusInterval = null;
|
|
75
|
+
let busStatusIndex = 0;
|
|
76
|
+
let busStatusKey = "";
|
|
77
|
+
let busStatusLocalStartedAt = 0;
|
|
65
78
|
const originalRender = screen.render.bind(screen);
|
|
66
79
|
let renderFrozen = false;
|
|
67
80
|
|
|
@@ -202,6 +215,130 @@ function createAgentViewController(options = {}) {
|
|
|
202
215
|
return hasAnsi(text) ? fitAnsiText(text, normalizedWidth) : plainLine(text, normalizedWidth);
|
|
203
216
|
}
|
|
204
217
|
|
|
218
|
+
function parseTimeMs(value) {
|
|
219
|
+
if (Number.isFinite(value)) return Number(value);
|
|
220
|
+
const text = String(value || "").trim();
|
|
221
|
+
if (!text) return NaN;
|
|
222
|
+
const parsed = Date.parse(text);
|
|
223
|
+
return Number.isFinite(parsed) ? parsed : NaN;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function formatElapsed(ms = 0) {
|
|
227
|
+
const totalSeconds = Math.max(0, Math.floor(Number(ms) / 1000));
|
|
228
|
+
return `${totalSeconds} s`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function normalizeActivityState(value = "") {
|
|
232
|
+
const state = String(value || "").trim().toLowerCase();
|
|
233
|
+
if (state === "waiting") return "waiting_input";
|
|
234
|
+
if (state === "busy" || state === "processing") return "working";
|
|
235
|
+
return state;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function getActivityLabel(state = "") {
|
|
239
|
+
if (state === "working") return "working";
|
|
240
|
+
if (state === "waiting_input") return "waiting";
|
|
241
|
+
if (state === "blocked") return "blocked";
|
|
242
|
+
if (state === "starting") return "starting";
|
|
243
|
+
if (state === "idle" || state === "ready") return "ready";
|
|
244
|
+
return state || "ready";
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function isTimedActivityState(state = "") {
|
|
248
|
+
return state === "working"
|
|
249
|
+
|| state === "waiting_input"
|
|
250
|
+
|| state === "blocked"
|
|
251
|
+
|| state === "starting";
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function asActivityObject(value) {
|
|
255
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function pickActivityDetail(meta = {}) {
|
|
259
|
+
const candidates = [
|
|
260
|
+
meta.activity_detail,
|
|
261
|
+
meta.detail,
|
|
262
|
+
meta.status_text,
|
|
263
|
+
meta.command,
|
|
264
|
+
meta.tool_name,
|
|
265
|
+
meta.tool,
|
|
266
|
+
];
|
|
267
|
+
return String(candidates.find((item) => String(item || "").trim()) || "").trim();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function getViewingAgentActivity() {
|
|
271
|
+
const states = getAgentStates() || {};
|
|
272
|
+
const stateEntry = viewingAgent && states ? states[viewingAgent] : "";
|
|
273
|
+
const stateObject = asActivityObject(stateEntry);
|
|
274
|
+
const meta = {
|
|
275
|
+
...(stateObject || {}),
|
|
276
|
+
...(asActivityObject(getAgentActivityMeta(viewingAgent)) || {}),
|
|
277
|
+
};
|
|
278
|
+
const state = normalizeActivityState(meta.activity_state || meta.state || (stateObject ? "" : stateEntry) || "");
|
|
279
|
+
const detail = pickActivityDetail(meta);
|
|
280
|
+
const sinceMs = parseTimeMs(meta.activity_since || meta.since || meta.updated_at || meta.updatedAt);
|
|
281
|
+
return { state: state || "ready", detail, sinceMs };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function resolveBusStatus() {
|
|
285
|
+
const activity = getViewingAgentActivity();
|
|
286
|
+
const state = activity.state || "ready";
|
|
287
|
+
const timed = isTimedActivityState(state);
|
|
288
|
+
const key = `${viewingAgent || ""}:${state}:${activity.detail || ""}`;
|
|
289
|
+
if (key !== busStatusKey) {
|
|
290
|
+
busStatusKey = key;
|
|
291
|
+
busStatusIndex = 0;
|
|
292
|
+
busStatusLocalStartedAt = now();
|
|
293
|
+
}
|
|
294
|
+
const startedAt = timed && Number.isFinite(activity.sinceMs)
|
|
295
|
+
? activity.sinceMs
|
|
296
|
+
: busStatusLocalStartedAt;
|
|
297
|
+
return {
|
|
298
|
+
...activity,
|
|
299
|
+
state,
|
|
300
|
+
label: getActivityLabel(state),
|
|
301
|
+
timed,
|
|
302
|
+
startedAt,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function buildBusStatusLine(width = 80, status = resolveBusStatus()) {
|
|
307
|
+
const normalizedWidth = Math.max(1, width);
|
|
308
|
+
const detail = status.detail ? ` · ${status.detail}` : "";
|
|
309
|
+
if (status.timed) {
|
|
310
|
+
const indicators = BUS_STATUS_INDICATORS[status.state] || BUS_STATUS_INDICATORS.working;
|
|
311
|
+
const indicator = indicators[busStatusIndex % indicators.length] || "";
|
|
312
|
+
const elapsed = formatElapsed(now() - status.startedAt);
|
|
313
|
+
return fitText(`${indicator} ${status.label} · ${elapsed}${detail}`, normalizedWidth);
|
|
314
|
+
}
|
|
315
|
+
if (normalizedWidth < 32) return fitText(`ufoo · ${status.label}`, normalizedWidth);
|
|
316
|
+
if (normalizedWidth < 48) return fitText(`ufoo · ${status.label} · Enter send`, normalizedWidth);
|
|
317
|
+
return fitText(`ufoo · ${status.label} · Enter send · Esc back${detail}`, normalizedWidth);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function stopBusStatusTimer() {
|
|
321
|
+
if (!busStatusInterval) return;
|
|
322
|
+
clearIntervalFn(busStatusInterval);
|
|
323
|
+
busStatusInterval = null;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function syncBusStatusTimer(status) {
|
|
327
|
+
const shouldTick = currentView === "agent" && agentViewUsesBus && status && status.timed;
|
|
328
|
+
if (!shouldTick) {
|
|
329
|
+
stopBusStatusTimer();
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (busStatusInterval) return;
|
|
333
|
+
busStatusInterval = setIntervalFn(() => {
|
|
334
|
+
busStatusIndex += 1;
|
|
335
|
+
renderBusView();
|
|
336
|
+
}, 1000);
|
|
337
|
+
if (busStatusInterval && typeof busStatusInterval.unref === "function") {
|
|
338
|
+
busStatusInterval.unref();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
205
342
|
function sliceDisplayCells(text = "", startCell = 0, maxCells = 1) {
|
|
206
343
|
const targetStart = Math.max(0, startCell);
|
|
207
344
|
const targetWidth = Math.max(1, maxCells);
|
|
@@ -456,12 +593,16 @@ function createAgentViewController(options = {}) {
|
|
|
456
593
|
const logContentTop = 1;
|
|
457
594
|
const logContentBottom = Math.max(logContentTop, inputTop - 1);
|
|
458
595
|
const logContentHeight = Math.max(1, logContentBottom - logContentTop + 1);
|
|
596
|
+
const status = resolveBusStatus();
|
|
597
|
+
const logRows = Math.max(0, logContentHeight - 1);
|
|
598
|
+
const statusRow = logContentTop + logRows;
|
|
459
599
|
|
|
460
600
|
processStdout.write("\x1b[?25l");
|
|
461
|
-
const visibleLines = getWrappedBusLogLines(width).slice(-
|
|
462
|
-
for (let i = 0; i <
|
|
601
|
+
const visibleLines = getWrappedBusLogLines(width).slice(-logRows);
|
|
602
|
+
for (let i = 0; i < logRows; i += 1) {
|
|
463
603
|
writeAt(logContentTop + i, logLine(visibleLines[i] || "", width));
|
|
464
604
|
}
|
|
605
|
+
writeAt(statusRow, logLine(buildBusStatusLine(width, status), width));
|
|
465
606
|
|
|
466
607
|
writeAt(inputTop, horizontalLine(width));
|
|
467
608
|
const viewport = getBusInputViewport(width);
|
|
@@ -471,6 +612,7 @@ function createAgentViewController(options = {}) {
|
|
|
471
612
|
renderAgentDashboard();
|
|
472
613
|
const cursorCol = clamp(3 + viewport.cursorCol, 1, width);
|
|
473
614
|
processStdout.write(`\x1b[${inputTop + 1};${cursorCol}H\x1b[?25h`);
|
|
615
|
+
syncBusStatusTimer(status);
|
|
474
616
|
}
|
|
475
617
|
|
|
476
618
|
function renderAgentDashboard() {
|
|
@@ -566,6 +708,7 @@ function createAgentViewController(options = {}) {
|
|
|
566
708
|
agentViewUsesBus = false;
|
|
567
709
|
agentOutputSuppressed = false;
|
|
568
710
|
agentBarVisible = false;
|
|
711
|
+
stopBusStatusTimer();
|
|
569
712
|
busInputValue = "";
|
|
570
713
|
busInputCursor = 0;
|
|
571
714
|
busLogLines = [];
|
|
@@ -871,6 +1014,16 @@ function createAgentViewController(options = {}) {
|
|
|
871
1014
|
}
|
|
872
1015
|
}
|
|
873
1016
|
|
|
1017
|
+
function refreshAgentView() {
|
|
1018
|
+
if (currentView !== "agent") return false;
|
|
1019
|
+
if (agentViewUsesBus) {
|
|
1020
|
+
renderBusView();
|
|
1021
|
+
} else {
|
|
1022
|
+
renderAgentDashboard();
|
|
1023
|
+
}
|
|
1024
|
+
return true;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
874
1027
|
function isAgentBarVisible() {
|
|
875
1028
|
return agentBarVisible;
|
|
876
1029
|
}
|
|
@@ -882,6 +1035,7 @@ function createAgentViewController(options = {}) {
|
|
|
882
1035
|
getAgentInputSuppressUntil,
|
|
883
1036
|
getAgentOutputSuppressed,
|
|
884
1037
|
setAgentOutputSuppressed,
|
|
1038
|
+
refreshAgentView,
|
|
885
1039
|
isAgentBarVisible,
|
|
886
1040
|
renderAgentDashboard,
|
|
887
1041
|
setAgentBarVisible,
|
|
@@ -76,7 +76,7 @@ function createDaemonMessageRouter(options = {}) {
|
|
|
76
76
|
const key = typeof data.key === "string" ? data.key : "";
|
|
77
77
|
if (isLikelySubscriberId(key)) {
|
|
78
78
|
if (data.phase === BUS_STATUS_PHASES.START) {
|
|
79
|
-
setTransientAgentState(key, "working");
|
|
79
|
+
setTransientAgentState(key, "working", { detail: text });
|
|
80
80
|
} else if (data.phase === BUS_STATUS_PHASES.DONE || data.phase === BUS_STATUS_PHASES.ERROR) {
|
|
81
81
|
clearTransientAgentState(key);
|
|
82
82
|
}
|
|
@@ -354,6 +354,18 @@ function createDaemonMessageRouter(options = {}) {
|
|
|
354
354
|
function handleBusMessage(msg) {
|
|
355
355
|
const data = msg.data || {};
|
|
356
356
|
if (data.event === "activity_state_changed") {
|
|
357
|
+
const agentId = String(data.subscriber || data.publisher || "").trim();
|
|
358
|
+
const state = String(data.state || data.activity_state || "").trim();
|
|
359
|
+
const detailSource = data.detail || (data.data && data.data.detail) || data.message || "";
|
|
360
|
+
if (agentId && state) {
|
|
361
|
+
const normalized = state.toLowerCase();
|
|
362
|
+
if (normalized === "idle" || normalized === "ready") {
|
|
363
|
+
clearTransientAgentState(agentId);
|
|
364
|
+
} else {
|
|
365
|
+
setTransientAgentState(agentId, state, { detail: detailSource });
|
|
366
|
+
}
|
|
367
|
+
refreshDashboard();
|
|
368
|
+
}
|
|
357
369
|
requestStatus();
|
|
358
370
|
return true;
|
|
359
371
|
}
|
package/src/chat/index.js
CHANGED
|
@@ -61,6 +61,7 @@ const { loadPromptProfileRegistry } = require("../group/promptProfiles");
|
|
|
61
61
|
const {
|
|
62
62
|
DEFAULT_TRANSIENT_AGENT_STATE_TTL_MS,
|
|
63
63
|
setTransientAgentState: setTransientAgentStateValue,
|
|
64
|
+
getTransientAgentStateEntry,
|
|
64
65
|
getTransientAgentState,
|
|
65
66
|
pruneTransientAgentStates,
|
|
66
67
|
} = require("./transientAgentState");
|
|
@@ -1340,7 +1341,11 @@ async function runChat(projectRoot, options = {}) {
|
|
|
1340
1341
|
selectedAgentIndex = 0;
|
|
1341
1342
|
}
|
|
1342
1343
|
}
|
|
1343
|
-
|
|
1344
|
+
if (agentViewController && typeof agentViewController.refreshAgentView === "function") {
|
|
1345
|
+
agentViewController.refreshAgentView();
|
|
1346
|
+
} else {
|
|
1347
|
+
renderAgentDashboard();
|
|
1348
|
+
}
|
|
1344
1349
|
return;
|
|
1345
1350
|
}
|
|
1346
1351
|
if (focusMode === "dashboard") {
|
|
@@ -1537,13 +1542,39 @@ async function runChat(projectRoot, options = {}) {
|
|
|
1537
1542
|
getAgentLabel,
|
|
1538
1543
|
getAgentStates: () => {
|
|
1539
1544
|
const states = {};
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1545
|
+
for (const id of activeAgents) {
|
|
1546
|
+
let state = "";
|
|
1547
|
+
if (activeAgentMetaMap) {
|
|
1548
|
+
const meta = activeAgentMetaMap.get(id);
|
|
1549
|
+
if (meta && meta.activity_state) state = meta.activity_state;
|
|
1550
|
+
}
|
|
1551
|
+
if (!state) {
|
|
1552
|
+
state = getTransientAgentState(transientAgentStateMap, id, {
|
|
1553
|
+
ttlMs: DEFAULT_TRANSIENT_AGENT_STATE_TTL_MS,
|
|
1554
|
+
});
|
|
1543
1555
|
}
|
|
1556
|
+
if (state) states[id] = state;
|
|
1544
1557
|
}
|
|
1545
1558
|
return states;
|
|
1546
1559
|
},
|
|
1560
|
+
getAgentActivityMeta: (agentId) => {
|
|
1561
|
+
const id = String(agentId || "").trim();
|
|
1562
|
+
const meta = activeAgentMetaMap && activeAgentMetaMap.get(id)
|
|
1563
|
+
? { ...activeAgentMetaMap.get(id) }
|
|
1564
|
+
: {};
|
|
1565
|
+
const transient = getTransientAgentStateEntry(transientAgentStateMap, id, {
|
|
1566
|
+
ttlMs: DEFAULT_TRANSIENT_AGENT_STATE_TTL_MS,
|
|
1567
|
+
});
|
|
1568
|
+
if (transient) {
|
|
1569
|
+
const previousState = meta.activity_state;
|
|
1570
|
+
meta.activity_state = transient.state;
|
|
1571
|
+
if ((!meta.activity_since || previousState !== transient.state) && Number.isFinite(transient.updatedAt)) {
|
|
1572
|
+
meta.activity_since = new Date(transient.updatedAt).toISOString();
|
|
1573
|
+
}
|
|
1574
|
+
if (transient.detail) meta.activity_detail = transient.detail;
|
|
1575
|
+
}
|
|
1576
|
+
return meta;
|
|
1577
|
+
},
|
|
1547
1578
|
getProjectRoot: () => activeProjectRoot,
|
|
1548
1579
|
setDashboardView: (value) => {
|
|
1549
1580
|
dashboardView = value;
|
|
@@ -1643,9 +1674,9 @@ async function runChat(projectRoot, options = {}) {
|
|
|
1643
1674
|
appendStreamDelta,
|
|
1644
1675
|
finalizeStream,
|
|
1645
1676
|
hasStream: (publisher) => streamTracker.hasStream(publisher),
|
|
1646
|
-
setTransientAgentState: (agentId, state) => {
|
|
1677
|
+
setTransientAgentState: (agentId, state, options) => {
|
|
1647
1678
|
if (!agentId || !state) return;
|
|
1648
|
-
setTransientAgentStateValue(transientAgentStateMap, agentId, state);
|
|
1679
|
+
setTransientAgentStateValue(transientAgentStateMap, agentId, state, options);
|
|
1649
1680
|
},
|
|
1650
1681
|
clearTransientAgentState: (agentId) => {
|
|
1651
1682
|
if (!agentId) return;
|
|
@@ -1653,7 +1684,11 @@ async function runChat(projectRoot, options = {}) {
|
|
|
1653
1684
|
},
|
|
1654
1685
|
refreshDashboard: () => {
|
|
1655
1686
|
if (getCurrentView() === "agent") {
|
|
1656
|
-
|
|
1687
|
+
if (agentViewController && typeof agentViewController.refreshAgentView === "function") {
|
|
1688
|
+
agentViewController.refreshAgentView();
|
|
1689
|
+
} else {
|
|
1690
|
+
renderAgentDashboard();
|
|
1691
|
+
}
|
|
1657
1692
|
return;
|
|
1658
1693
|
}
|
|
1659
1694
|
renderDashboard();
|
|
@@ -6,23 +6,38 @@ function normalizeNow(now) {
|
|
|
6
6
|
return Number.isFinite(now) ? now : Date.now();
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
function
|
|
9
|
+
function normalizeSetOptions(nowOrOptions, detailArg = "") {
|
|
10
|
+
if (nowOrOptions && typeof nowOrOptions === "object") {
|
|
11
|
+
return {
|
|
12
|
+
now: normalizeNow(nowOrOptions.now),
|
|
13
|
+
detail: String(nowOrOptions.detail || "").trim(),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
now: normalizeNow(nowOrOptions),
|
|
18
|
+
detail: String(detailArg || "").trim(),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function setTransientAgentState(store, agentId, state, nowOrOptions = Date.now(), detailArg = "") {
|
|
10
23
|
if (!(store instanceof Map)) return;
|
|
11
24
|
const id = String(agentId || "").trim();
|
|
12
25
|
const nextState = String(state || "").trim();
|
|
13
26
|
if (!id || !nextState) return;
|
|
27
|
+
const options = normalizeSetOptions(nowOrOptions, detailArg);
|
|
14
28
|
store.set(id, {
|
|
15
29
|
state: nextState,
|
|
16
|
-
updatedAt:
|
|
30
|
+
updatedAt: options.now,
|
|
31
|
+
detail: options.detail,
|
|
17
32
|
});
|
|
18
33
|
}
|
|
19
34
|
|
|
20
|
-
function
|
|
21
|
-
if (!(store instanceof Map)) return
|
|
35
|
+
function getTransientAgentStateEntry(store, agentId, options = {}) {
|
|
36
|
+
if (!(store instanceof Map)) return null;
|
|
22
37
|
const id = String(agentId || "").trim();
|
|
23
|
-
if (!id) return
|
|
38
|
+
if (!id) return null;
|
|
24
39
|
const entry = store.get(id);
|
|
25
|
-
if (!entry) return
|
|
40
|
+
if (!entry) return null;
|
|
26
41
|
|
|
27
42
|
const ttlMs = Number.isFinite(options.ttlMs)
|
|
28
43
|
? Math.max(0, Math.trunc(options.ttlMs))
|
|
@@ -32,16 +47,23 @@ function getTransientAgentState(store, agentId, options = {}) {
|
|
|
32
47
|
const updatedAt = typeof entry === "object" && Number.isFinite(entry.updatedAt)
|
|
33
48
|
? entry.updatedAt
|
|
34
49
|
: now;
|
|
50
|
+
const detail = typeof entry === "object" ? String(entry.detail || "").trim() : "";
|
|
35
51
|
|
|
36
52
|
if (!state) {
|
|
37
53
|
store.delete(id);
|
|
38
|
-
return
|
|
54
|
+
return null;
|
|
39
55
|
}
|
|
40
56
|
if (ttlMs > 0 && now - updatedAt > ttlMs) {
|
|
41
57
|
store.delete(id);
|
|
42
|
-
return
|
|
58
|
+
return null;
|
|
43
59
|
}
|
|
44
|
-
return state;
|
|
60
|
+
return { state, updatedAt, detail };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getTransientAgentState(store, agentId, options = {}) {
|
|
64
|
+
const entry = getTransientAgentStateEntry(store, agentId, options);
|
|
65
|
+
if (!entry) return "";
|
|
66
|
+
return entry.state;
|
|
45
67
|
}
|
|
46
68
|
|
|
47
69
|
function pruneTransientAgentStates(store, activeAgentIds = [], options = {}) {
|
|
@@ -59,6 +81,7 @@ function pruneTransientAgentStates(store, activeAgentIds = [], options = {}) {
|
|
|
59
81
|
module.exports = {
|
|
60
82
|
DEFAULT_TRANSIENT_AGENT_STATE_TTL_MS,
|
|
61
83
|
setTransientAgentState,
|
|
84
|
+
getTransientAgentStateEntry,
|
|
62
85
|
getTransientAgentState,
|
|
63
86
|
pruneTransientAgentStates,
|
|
64
87
|
};
|