sentinelayer-cli 0.36.0 → 0.36.1
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 +1 -1
- package/src/commands/session.js +68 -4
- package/src/legacy-cli.js +14 -14
- package/src/scan/generator.js +10 -9
- package/src/session/listener.js +2 -2
- package/src/session/recap.js +25 -1
package/package.json
CHANGED
package/src/commands/session.js
CHANGED
|
@@ -64,7 +64,7 @@ import {
|
|
|
64
64
|
refreshSessionCacheForRemoteActivity,
|
|
65
65
|
updateSessionTitle,
|
|
66
66
|
} from "../session/store.js";
|
|
67
|
-
import { fetchSessionListeners, formatListenerLine } from "../session/listeners.js";
|
|
67
|
+
import { fetchSessionListeners, formatListenerLine, summarizeListeners } from "../session/listeners.js";
|
|
68
68
|
import {
|
|
69
69
|
getListenerProcessStatus,
|
|
70
70
|
removeListenerPidRecord,
|
|
@@ -1082,6 +1082,25 @@ function compareIsoDesc(left = "", right = "") {
|
|
|
1082
1082
|
return normalizeString(right).localeCompare(normalizeString(left));
|
|
1083
1083
|
}
|
|
1084
1084
|
|
|
1085
|
+
function latestIsoTimestamp(...values) {
|
|
1086
|
+
let selected = "";
|
|
1087
|
+
let selectedEpoch = Number.NEGATIVE_INFINITY;
|
|
1088
|
+
for (const value of values) {
|
|
1089
|
+
const normalized = normalizeString(value);
|
|
1090
|
+
if (!normalized) continue;
|
|
1091
|
+
const epoch = Date.parse(normalized);
|
|
1092
|
+
if (!Number.isFinite(epoch)) {
|
|
1093
|
+
if (!selected) selected = normalized;
|
|
1094
|
+
continue;
|
|
1095
|
+
}
|
|
1096
|
+
if (epoch >= selectedEpoch) {
|
|
1097
|
+
selected = new Date(epoch).toISOString();
|
|
1098
|
+
selectedEpoch = epoch;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
return selected;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1085
1104
|
function buildSessionParticipants({ statsAgents = [], registeredAgents = [] } = {}) {
|
|
1086
1105
|
const byAgentId = new Map();
|
|
1087
1106
|
for (const agent of Array.isArray(statsAgents) ? statsAgents : []) {
|
|
@@ -1100,6 +1119,12 @@ function buildSessionParticipants({ statsAgents = [], registeredAgents = [] } =
|
|
|
1100
1119
|
if (!agentId) continue;
|
|
1101
1120
|
const existing = byAgentId.get(agentId);
|
|
1102
1121
|
if (existing) {
|
|
1122
|
+
const lastActivityAt = latestIsoTimestamp(
|
|
1123
|
+
existing.lastActivityAt,
|
|
1124
|
+
existing.lastSeen,
|
|
1125
|
+
agent.lastActivityAt,
|
|
1126
|
+
agent.joinedAt,
|
|
1127
|
+
);
|
|
1103
1128
|
byAgentId.set(agentId, {
|
|
1104
1129
|
...existing,
|
|
1105
1130
|
model: existing.model || normalizeString(agent.model),
|
|
@@ -1108,7 +1133,8 @@ function buildSessionParticipants({ statsAgents = [], registeredAgents = [] } =
|
|
|
1108
1133
|
registered: true,
|
|
1109
1134
|
source: "events+registry",
|
|
1110
1135
|
joinedAt: normalizeString(agent.joinedAt) || existing.joinedAt,
|
|
1111
|
-
|
|
1136
|
+
lastSeen: latestIsoTimestamp(existing.lastSeen, lastActivityAt) || existing.lastSeen,
|
|
1137
|
+
lastActivityAt: lastActivityAt || null,
|
|
1112
1138
|
active: agent.active,
|
|
1113
1139
|
});
|
|
1114
1140
|
continue;
|
|
@@ -6285,13 +6311,51 @@ export function registerSessionCommand(program) {
|
|
|
6285
6311
|
]);
|
|
6286
6312
|
|
|
6287
6313
|
const staleAgents = detectStaleAgents(agents, {});
|
|
6314
|
+
const staleAgentIds = new Set(staleAgents.map((agent) => normalizeString(agent.agentId)));
|
|
6315
|
+
const activeAgents = agents.filter((agent) => !staleAgentIds.has(normalizeString(agent.agentId)));
|
|
6316
|
+
let listenerRows = summarizeListeners(recentEvents);
|
|
6317
|
+
let listenerPresence = {
|
|
6318
|
+
source: "local_recent_events",
|
|
6319
|
+
ok: true,
|
|
6320
|
+
count: listenerRows.length,
|
|
6321
|
+
liveCount: listenerRows.filter((row) => row.status === "active" || row.status === "idle").length,
|
|
6322
|
+
listeners: listenerRows,
|
|
6323
|
+
};
|
|
6324
|
+
try {
|
|
6325
|
+
const remoteListeners = await fetchSessionListeners(normalizedSessionId, {
|
|
6326
|
+
targetPath,
|
|
6327
|
+
limit: 200,
|
|
6328
|
+
maxPages: 5,
|
|
6329
|
+
});
|
|
6330
|
+
if (remoteListeners?.ok && Array.isArray(remoteListeners.listeners) && remoteListeners.listeners.length > 0) {
|
|
6331
|
+
listenerRows = remoteListeners.listeners;
|
|
6332
|
+
listenerPresence = {
|
|
6333
|
+
source: "remote_events",
|
|
6334
|
+
ok: true,
|
|
6335
|
+
count: listenerRows.length,
|
|
6336
|
+
liveCount: listenerRows.filter((row) => row.status === "active" || row.status === "idle").length,
|
|
6337
|
+
pageCount: Number(remoteListeners.pageCount || 0),
|
|
6338
|
+
scannedEventCount: Number(remoteListeners.scannedEventCount || 0),
|
|
6339
|
+
listenerEventCount: Number(remoteListeners.listenerEventCount || 0),
|
|
6340
|
+
partial: Boolean(remoteListeners.partial),
|
|
6341
|
+
listeners: listenerRows,
|
|
6342
|
+
};
|
|
6343
|
+
} else if (remoteListeners && remoteListeners.ok === false) {
|
|
6344
|
+
listenerPresence.remoteReason = normalizeString(remoteListeners.reason) || "remote_unavailable";
|
|
6345
|
+
}
|
|
6346
|
+
} catch (error) {
|
|
6347
|
+
listenerPresence.remoteReason = normalizeString(error?.message) || "remote_unavailable";
|
|
6348
|
+
}
|
|
6349
|
+
const liveListenerCount = listenerPresence.liveCount;
|
|
6288
6350
|
const payload = {
|
|
6289
6351
|
command: "session status",
|
|
6290
6352
|
targetPath,
|
|
6291
6353
|
sessionId: normalizedSessionId,
|
|
6292
6354
|
session: sessionPayload,
|
|
6293
|
-
|
|
6355
|
+
registeredAgents: agents,
|
|
6356
|
+
activeAgents,
|
|
6294
6357
|
staleAgents,
|
|
6358
|
+
listenerPresence,
|
|
6295
6359
|
runtimeRuns,
|
|
6296
6360
|
activeLeases: leases.assignments,
|
|
6297
6361
|
activeFileLocks: fileLocks,
|
|
@@ -6306,7 +6370,7 @@ export function registerSessionCommand(program) {
|
|
|
6306
6370
|
console.log(pc.bold(`Session ${normalizedSessionId}`));
|
|
6307
6371
|
console.log(
|
|
6308
6372
|
pc.gray(
|
|
6309
|
-
`status=${sessionPayload.status}
|
|
6373
|
+
`status=${sessionPayload.status} active=${activeAgents.length} registered=${agents.length} stale=${staleAgents.length} listeners=${liveListenerCount}/${listenerRows.length} runs=${runtimeRuns.length} leases=${leases.assignments.length} locks=${fileLocks.length} tasks=${activeTasks.tasks.length}`
|
|
6310
6374
|
)
|
|
6311
6375
|
);
|
|
6312
6376
|
for (const event of recentEvents) {
|
package/src/legacy-cli.js
CHANGED
|
@@ -2517,20 +2517,20 @@ jobs:
|
|
|
2517
2517
|
fi
|
|
2518
2518
|
- name: Run Omar Gate
|
|
2519
2519
|
id: omar
|
|
2520
|
-
uses: mrrCarter/sentinelayer-v1-action@
|
|
2520
|
+
uses: mrrCarter/sentinelayer-v1-action@5c4d8c175eb117ea5256452e50e01249ab126998
|
|
2521
2521
|
with:
|
|
2522
2522
|
github_token: \${{ github.token }}
|
|
2523
2523
|
openai_api_key: \${{ secrets.OPENAI_API_KEY }}
|
|
2524
|
-
google_api_key: \${{ secrets.GOOGLE_API_KEY }}
|
|
2525
|
-
llm_provider: \${{ secrets.GOOGLE_API_KEY != '' && 'google' || 'openai' }}
|
|
2524
|
+
google_api_key: \${{ secrets.GOOGLE_GEMINI_API_KEY != '' && secrets.GOOGLE_GEMINI_API_KEY || secrets.GOOGLE_API_KEY }}
|
|
2525
|
+
llm_provider: \${{ secrets.OPENAI_API_KEY != '' && 'openai' || ((secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'google' || 'openai') }}
|
|
2526
2526
|
sentinelayer_token: \${{ secrets.${normalizedSecret} }}${specIdBindingLine}
|
|
2527
|
-
sentinelayer_managed_llm: \${{ secrets
|
|
2527
|
+
sentinelayer_managed_llm: \${{ secrets.${normalizedSecret} != '' }}
|
|
2528
2528
|
scan_mode: \${{ github.event_name == 'workflow_dispatch' && inputs.scan_mode || 'deep' }}
|
|
2529
2529
|
severity_gate: \${{ github.event_name == 'workflow_dispatch' && inputs.severity_gate || 'P1' }}
|
|
2530
|
-
model: \${{ secrets.GOOGLE_API_KEY != '' && 'gemini-
|
|
2530
|
+
model: \${{ secrets.OPENAI_API_KEY != '' && 'gpt-5.3-codex' || ((secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'gemini-3.1-flash-lite' || 'gpt-5.3-codex') }}
|
|
2531
2531
|
codex_model: gpt-5.3-codex
|
|
2532
|
-
model_fallback: \${{ secrets.GOOGLE_API_KEY != '' && 'gemini-
|
|
2533
|
-
use_codex: \${{ secrets.GOOGLE_API_KEY == '' }}
|
|
2532
|
+
model_fallback: \${{ (secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'gemini-3.1-flash-lite' || 'gpt-4.1-mini' }}
|
|
2533
|
+
use_codex: \${{ secrets.OPENAI_API_KEY != '' || (secrets.GOOGLE_GEMINI_API_KEY == '' && secrets.GOOGLE_API_KEY == '') }}
|
|
2534
2534
|
codex_only: "false"
|
|
2535
2535
|
llm_failure_policy: block
|
|
2536
2536
|
max_daily_scans: \${{ vars.OMAR_MAX_DAILY_SCANS || '200' }}
|
|
@@ -2582,11 +2582,11 @@ jobs:
|
|
|
2582
2582
|
OMAR_SEVERITY_GATE: \${{ github.event_name == 'workflow_dispatch' && inputs.severity_gate || 'P1' }}
|
|
2583
2583
|
OMAR_P2_MAX_ALLOWED: \${{ github.event_name == 'workflow_dispatch' && inputs.p2_max_allowed || '5' }}
|
|
2584
2584
|
OMAR_OPENAI_KEY_PRESENT: \${{ secrets.OPENAI_API_KEY != '' }}
|
|
2585
|
-
OMAR_GOOGLE_KEY_PRESENT: \${{ secrets.GOOGLE_API_KEY != '' }}
|
|
2586
|
-
OMAR_MANAGED_LLM: \${{ secrets
|
|
2587
|
-
OMAR_LLM_PROVIDER: \${{ secrets.GOOGLE_API_KEY != '' && 'google' || 'openai' }}
|
|
2588
|
-
OMAR_MODEL: \${{ secrets.GOOGLE_API_KEY != '' && 'gemini-
|
|
2589
|
-
OMAR_MODEL_FALLBACK: \${{ secrets.GOOGLE_API_KEY != '' && 'gemini-
|
|
2585
|
+
OMAR_GOOGLE_KEY_PRESENT: \${{ secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '' }}
|
|
2586
|
+
OMAR_MANAGED_LLM: \${{ secrets.${normalizedSecret} != '' }}
|
|
2587
|
+
OMAR_LLM_PROVIDER: \${{ secrets.OPENAI_API_KEY != '' && 'openai' || ((secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'google' || 'openai') }}
|
|
2588
|
+
OMAR_MODEL: \${{ secrets.OPENAI_API_KEY != '' && 'gpt-5.3-codex' || ((secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'gemini-3.1-flash-lite' || 'gpt-5.3-codex') }}
|
|
2589
|
+
OMAR_MODEL_FALLBACK: \${{ (secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'gemini-3.1-flash-lite' || 'gpt-4.1-mini' }}
|
|
2590
2590
|
run: |
|
|
2591
2591
|
set -euo pipefail
|
|
2592
2592
|
mkdir -p omar-artifacts
|
|
@@ -2626,11 +2626,11 @@ jobs:
|
|
|
2626
2626
|
},
|
|
2627
2627
|
"scan": {
|
|
2628
2628
|
"mode": env("OMAR_SCAN_MODE", "deep"),
|
|
2629
|
-
"action_ref": "mrrCarter/sentinelayer-v1-action@
|
|
2629
|
+
"action_ref": "mrrCarter/sentinelayer-v1-action@5c4d8c175eb117ea5256452e50e01249ab126998",
|
|
2630
2630
|
"llm_provider": env("OMAR_LLM_PROVIDER", "openai"),
|
|
2631
2631
|
"model": env("OMAR_MODEL", "gpt-5.3-codex"),
|
|
2632
2632
|
"model_fallback": env("OMAR_MODEL_FALLBACK", "gpt-4.1-mini"),
|
|
2633
|
-
"llm_route": "
|
|
2633
|
+
"llm_route": "openai_api_key" if bool_env("OMAR_OPENAI_KEY_PRESENT") else ("google_api_key" if bool_env("OMAR_GOOGLE_KEY_PRESENT") else "sentinelayer_managed"),
|
|
2634
2634
|
"google_key_present": bool_env("OMAR_GOOGLE_KEY_PRESENT"),
|
|
2635
2635
|
"openai_key_present": bool_env("OMAR_OPENAI_KEY_PRESENT"),
|
|
2636
2636
|
"managed_llm": bool_env("OMAR_MANAGED_LLM"),
|
package/src/scan/generator.js
CHANGED
|
@@ -2,7 +2,7 @@ import YAML from "yaml";
|
|
|
2
2
|
|
|
3
3
|
export const DEFAULT_SCAN_WORKFLOW_PATH = ".github/workflows/omar-gate.yml";
|
|
4
4
|
export const DEFAULT_SCAN_SECRET_NAME = "SENTINELAYER_TOKEN";
|
|
5
|
-
export const SENTINELAYER_ACTION_REF = "mrrCarter/sentinelayer-v1-action@
|
|
5
|
+
export const SENTINELAYER_ACTION_REF = "mrrCarter/sentinelayer-v1-action@5c4d8c175eb117ea5256452e50e01249ab126998";
|
|
6
6
|
export const SUPPORTED_E2E_HINTS = Object.freeze(["auto", "yes", "no"]);
|
|
7
7
|
export const SUPPORTED_PLAYWRIGHT_MODES = Object.freeze(["auto", "off", "baseline", "audit"]);
|
|
8
8
|
|
|
@@ -230,17 +230,18 @@ export function buildSecurityReviewWorkflow({ secretName = DEFAULT_SCAN_SECRET_N
|
|
|
230
230
|
google_api_key:
|
|
231
231
|
"${{ secrets.GOOGLE_GEMINI_API_KEY != '' && secrets.GOOGLE_GEMINI_API_KEY || secrets.GOOGLE_API_KEY }}",
|
|
232
232
|
llm_provider:
|
|
233
|
-
"${{ (secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'google' || 'openai' }}",
|
|
233
|
+
"${{ secrets.OPENAI_API_KEY != '' && 'openai' || ((secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'google' || 'openai') }}",
|
|
234
234
|
sentinelayer_token: `\${{ secrets.${normalizedSecret} }}`,
|
|
235
|
-
sentinelayer_managed_llm: `\${{ secrets
|
|
235
|
+
sentinelayer_managed_llm: `\${{ secrets.${normalizedSecret} != '' }}`,
|
|
236
236
|
scan_mode: profile.scanMode || "deep",
|
|
237
237
|
severity_gate: profile.severityGate || "P1",
|
|
238
238
|
model:
|
|
239
|
-
"${{ (secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'gemini-3.1-flash-lite' || 'gpt-5.3-codex' }}",
|
|
239
|
+
"${{ secrets.OPENAI_API_KEY != '' && 'gpt-5.3-codex' || ((secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'gemini-3.1-flash-lite' || 'gpt-5.3-codex') }}",
|
|
240
240
|
codex_model: "gpt-5.3-codex",
|
|
241
241
|
model_fallback:
|
|
242
242
|
"${{ (secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'gemini-3.1-flash-lite' || 'gpt-4.1-mini' }}",
|
|
243
|
-
use_codex:
|
|
243
|
+
use_codex:
|
|
244
|
+
"${{ secrets.OPENAI_API_KEY != '' || (secrets.GOOGLE_GEMINI_API_KEY == '' && secrets.GOOGLE_API_KEY == '') }}",
|
|
244
245
|
codex_only: "false",
|
|
245
246
|
llm_failure_policy: "block",
|
|
246
247
|
max_daily_scans: "${{ vars.OMAR_MAX_DAILY_SCANS || '200' }}",
|
|
@@ -301,11 +302,11 @@ export function buildSecurityReviewWorkflow({ secretName = DEFAULT_SCAN_SECRET_N
|
|
|
301
302
|
OMAR_OPENAI_KEY_PRESENT: "${{ secrets.OPENAI_API_KEY != '' }}",
|
|
302
303
|
OMAR_GOOGLE_KEY_PRESENT:
|
|
303
304
|
"${{ secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '' }}",
|
|
304
|
-
OMAR_MANAGED_LLM: `\${{ secrets
|
|
305
|
+
OMAR_MANAGED_LLM: `\${{ secrets.${normalizedSecret} != '' }}`,
|
|
305
306
|
OMAR_LLM_PROVIDER:
|
|
306
|
-
"${{ (secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'google' || 'openai' }}",
|
|
307
|
+
"${{ secrets.OPENAI_API_KEY != '' && 'openai' || ((secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'google' || 'openai') }}",
|
|
307
308
|
OMAR_MODEL:
|
|
308
|
-
"${{ (secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'gemini-3.1-flash-lite' || 'gpt-5.3-codex' }}",
|
|
309
|
+
"${{ secrets.OPENAI_API_KEY != '' && 'gpt-5.3-codex' || ((secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'gemini-3.1-flash-lite' || 'gpt-5.3-codex') }}",
|
|
309
310
|
OMAR_MODEL_FALLBACK:
|
|
310
311
|
"${{ (secrets.GOOGLE_GEMINI_API_KEY != '' || secrets.GOOGLE_API_KEY != '') && 'gemini-3.1-flash-lite' || 'gpt-4.1-mini' }}",
|
|
311
312
|
},
|
|
@@ -352,7 +353,7 @@ export function buildSecurityReviewWorkflow({ secretName = DEFAULT_SCAN_SECRET_N
|
|
|
352
353
|
" 'llm_provider': env('OMAR_LLM_PROVIDER', 'openai'),",
|
|
353
354
|
" 'model': env('OMAR_MODEL', 'gpt-5.3-codex'),",
|
|
354
355
|
" 'model_fallback': env('OMAR_MODEL_FALLBACK', 'gpt-4.1-mini'),",
|
|
355
|
-
" 'llm_route': '
|
|
356
|
+
" 'llm_route': 'openai_api_key' if bool_env('OMAR_OPENAI_KEY_PRESENT') else ('google_api_key' if bool_env('OMAR_GOOGLE_KEY_PRESENT') else 'sentinelayer_managed'),",
|
|
356
357
|
" 'google_key_present': bool_env('OMAR_GOOGLE_KEY_PRESENT'),",
|
|
357
358
|
" 'openai_key_present': bool_env('OMAR_OPENAI_KEY_PRESENT'),",
|
|
358
359
|
" 'managed_llm': bool_env('OMAR_MANAGED_LLM'),",
|
package/src/session/listener.js
CHANGED
|
@@ -507,7 +507,7 @@ export async function listenSessionEvents({
|
|
|
507
507
|
if (signal?.aborted || streamHeartbeatInFlight) return;
|
|
508
508
|
streamHeartbeatInFlight = true;
|
|
509
509
|
try {
|
|
510
|
-
await notifyHeartbeat({ nextPollMs:
|
|
510
|
+
await notifyHeartbeat({ nextPollMs: streamHeartbeatMs });
|
|
511
511
|
} finally {
|
|
512
512
|
streamHeartbeatInFlight = false;
|
|
513
513
|
}
|
|
@@ -543,7 +543,7 @@ export async function listenSessionEvents({
|
|
|
543
543
|
});
|
|
544
544
|
},
|
|
545
545
|
onHeartbeat: async () => {
|
|
546
|
-
await notifyHeartbeat({ nextPollMs:
|
|
546
|
+
await notifyHeartbeat({ nextPollMs: streamHeartbeatMs });
|
|
547
547
|
},
|
|
548
548
|
}).finally(() => {
|
|
549
549
|
stopStreamHeartbeatTimer();
|
package/src/session/recap.js
CHANGED
|
@@ -20,6 +20,7 @@ const DEFAULT_RECAP_INACTIVITY_MS = 600_000;
|
|
|
20
20
|
const DEFAULT_RECAP_ACTIVITY_THRESHOLD = 5;
|
|
21
21
|
const DEFAULT_TASK_SUMMARY_LIMIT = 3;
|
|
22
22
|
const DEFAULT_WORK_PLAN_SUMMARY_LIMIT = 5;
|
|
23
|
+
const DEFAULT_ACTIVITY_SNIPPET_MAX_CHARS = 120;
|
|
23
24
|
const MAX_WORK_PLAN_BYTES = 128_000;
|
|
24
25
|
const WORK_PLAN_RELATIVE_PATH = "tasks/todo.md";
|
|
25
26
|
const HISTORICAL_WORK_PLAN_MIN_COMPLETED = 25;
|
|
@@ -130,6 +131,11 @@ function eventSequenceId(event = {}) {
|
|
|
130
131
|
return Math.floor(parsed);
|
|
131
132
|
}
|
|
132
133
|
|
|
134
|
+
function formatSequenceMarker(event = {}) {
|
|
135
|
+
const sequence = eventSequenceId(event);
|
|
136
|
+
return sequence === null ? "" : ` #${sequence}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
133
139
|
function eventCursor(event = {}) {
|
|
134
140
|
return normalizeString(event.cursor || event.eventId || event.idempotencyToken || event.ts);
|
|
135
141
|
}
|
|
@@ -373,7 +379,9 @@ function summarizeRecentActivity(events = [], { forAgentId = "", limit = 2 } = {
|
|
|
373
379
|
if (!message) {
|
|
374
380
|
continue;
|
|
375
381
|
}
|
|
376
|
-
snippets.push(
|
|
382
|
+
snippets.push(
|
|
383
|
+
`${agentId}${formatSequenceMarker(event)}: ${shortActivitySnippetText(message)}`,
|
|
384
|
+
);
|
|
377
385
|
if (snippets.length >= Math.max(1, limit)) {
|
|
378
386
|
break;
|
|
379
387
|
}
|
|
@@ -381,6 +389,22 @@ function summarizeRecentActivity(events = [], { forAgentId = "", limit = 2 } = {
|
|
|
381
389
|
return snippets.reverse();
|
|
382
390
|
}
|
|
383
391
|
|
|
392
|
+
function shortActivitySnippetText(value, maxLength = DEFAULT_ACTIVITY_SNIPPET_MAX_CHARS) {
|
|
393
|
+
const text = normalizeString(value).replace(/\s+/g, " ");
|
|
394
|
+
const normalizedMaxLength = normalizePositiveInteger(maxLength, DEFAULT_ACTIVITY_SNIPPET_MAX_CHARS);
|
|
395
|
+
if (text.length <= normalizedMaxLength) {
|
|
396
|
+
return text;
|
|
397
|
+
}
|
|
398
|
+
const suffix = "...";
|
|
399
|
+
const cutLimit = Math.max(1, normalizedMaxLength - suffix.length);
|
|
400
|
+
const preferredBoundary = text.lastIndexOf(" ", cutLimit);
|
|
401
|
+
const minimumBoundary = Math.floor(normalizedMaxLength * 0.6);
|
|
402
|
+
const cutIndex =
|
|
403
|
+
preferredBoundary >= minimumBoundary ? preferredBoundary : cutLimit;
|
|
404
|
+
const prefix = text.slice(0, cutIndex).replace(/[\s,;:.-]+$/g, "");
|
|
405
|
+
return `${prefix || text.slice(0, cutLimit)}${suffix}`;
|
|
406
|
+
}
|
|
407
|
+
|
|
384
408
|
async function readPendingTasks(sessionId, { forAgentId = "", targetPath = process.cwd() } = {}) {
|
|
385
409
|
const normalizedAgentId = normalizeString(forAgentId).toLowerCase();
|
|
386
410
|
if (!normalizedAgentId) {
|