sentinelayer-cli 0.30.11 → 0.30.13
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 +209 -0
- package/src/session/daemon.js +1 -0
- package/src/session/listener.js +44 -2
- package/src/session/preview.js +2 -0
- package/src/session/transcript.js +46 -0
package/package.json
CHANGED
package/src/commands/session.js
CHANGED
|
@@ -1712,6 +1712,47 @@ function canPublishListenerPresence(agentId) {
|
|
|
1712
1712
|
);
|
|
1713
1713
|
}
|
|
1714
1714
|
|
|
1715
|
+
const SESSION_OBSERVATION_SEVERITIES = new Set(["info", "p3", "p2", "p1", "p0"]);
|
|
1716
|
+
const SESSION_OBSERVATION_KINDS = new Set([
|
|
1717
|
+
"ux",
|
|
1718
|
+
"process",
|
|
1719
|
+
"reliability",
|
|
1720
|
+
"security",
|
|
1721
|
+
"billing",
|
|
1722
|
+
"coordination",
|
|
1723
|
+
"architecture",
|
|
1724
|
+
"testing",
|
|
1725
|
+
"release",
|
|
1726
|
+
"other",
|
|
1727
|
+
]);
|
|
1728
|
+
const SESSION_OBSERVATION_SUMMARY_MAX_LENGTH = 4_000;
|
|
1729
|
+
const SESSION_OBSERVATION_OPTION_MAX_LENGTH = 512;
|
|
1730
|
+
const SESSION_OBSERVATION_PROPOSAL_MAX_LENGTH = 2_000;
|
|
1731
|
+
|
|
1732
|
+
function requireGrantedNonHumanAgentId(rawAgentId, commandName) {
|
|
1733
|
+
const agentId = normalizeAgentId(rawAgentId, "");
|
|
1734
|
+
if (!agentId || !canPublishListenerPresence(agentId)) {
|
|
1735
|
+
throw new Error(`${commandName} requires a granted non-human agent id.`);
|
|
1736
|
+
}
|
|
1737
|
+
return agentId;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
function normalizeSessionObservationChoice(rawValue, allowedValues, fallbackValue, fieldName) {
|
|
1741
|
+
const value = normalizeString(rawValue || fallbackValue).toLowerCase();
|
|
1742
|
+
if (!allowedValues.has(value)) {
|
|
1743
|
+
throw new Error(`${fieldName} must be one of: ${[...allowedValues].join(", ")}.`);
|
|
1744
|
+
}
|
|
1745
|
+
return value;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
function normalizeSessionObservationText(rawValue, fieldName, maxLength) {
|
|
1749
|
+
const value = normalizeString(rawValue);
|
|
1750
|
+
if (value.length > maxLength) {
|
|
1751
|
+
throw new Error(`${fieldName} must be ${maxLength} characters or fewer.`);
|
|
1752
|
+
}
|
|
1753
|
+
return value;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1715
1756
|
function listenerLifecycleEventName(type = "") {
|
|
1716
1757
|
const normalized = normalizeString(type).toLowerCase();
|
|
1717
1758
|
if (normalized === "started") return "session_listener_started";
|
|
@@ -3660,6 +3701,174 @@ export function registerSessionCommand(program) {
|
|
|
3660
3701
|
console.log(formatEventLine(persisted));
|
|
3661
3702
|
});
|
|
3662
3703
|
|
|
3704
|
+
session
|
|
3705
|
+
.command("observe <sessionId> [summary...]")
|
|
3706
|
+
.description("Record a durable product/process observation in the session")
|
|
3707
|
+
.requiredOption("--agent <id>", "Granted non-human agent id to record the observation as")
|
|
3708
|
+
.option("--model <model>", "Agent model/provider hint", "cli")
|
|
3709
|
+
.option("--display-name <name>", "Human-readable agent display name")
|
|
3710
|
+
.option("--role <role>", "Agent role metadata", "observer")
|
|
3711
|
+
.option(
|
|
3712
|
+
"--kind <kind>",
|
|
3713
|
+
`Observation kind: ${[...SESSION_OBSERVATION_KINDS].join(", ")}`,
|
|
3714
|
+
"process",
|
|
3715
|
+
)
|
|
3716
|
+
.option(
|
|
3717
|
+
"--severity <level>",
|
|
3718
|
+
`Observation severity: ${[...SESSION_OBSERVATION_SEVERITIES].join(", ")}`,
|
|
3719
|
+
"info",
|
|
3720
|
+
)
|
|
3721
|
+
.option("--owner <owner>", "Suggested owner/team for follow-up")
|
|
3722
|
+
.option("--batch <batch>", "Suggested PR batch or backlog lane")
|
|
3723
|
+
.option("--target-sequence <n>", "Anchor the observation to a session sequence id")
|
|
3724
|
+
.option("--target-cursor <cursor>", "Anchor the observation to a session event cursor")
|
|
3725
|
+
.option("--proposal <text>", "Suggested remediation or next step")
|
|
3726
|
+
.option("--message-file <path>", "Read the observation summary from a UTF-8 file")
|
|
3727
|
+
.option("--stdin", "Read the observation summary from stdin")
|
|
3728
|
+
.option("--path <path>", "Workspace path for the session", ".")
|
|
3729
|
+
.option("--json", "Emit machine-readable output")
|
|
3730
|
+
.action(async (sessionId, summaryParts, options, command) => {
|
|
3731
|
+
const normalizedSessionId = normalizeString(sessionId);
|
|
3732
|
+
if (!normalizedSessionId) {
|
|
3733
|
+
throw new Error("session id is required.");
|
|
3734
|
+
}
|
|
3735
|
+
const summary = normalizeSessionObservationText(
|
|
3736
|
+
await resolveSessionSayMessageInput(summaryParts, options),
|
|
3737
|
+
"summary",
|
|
3738
|
+
SESSION_OBSERVATION_SUMMARY_MAX_LENGTH,
|
|
3739
|
+
);
|
|
3740
|
+
if (!summary) {
|
|
3741
|
+
throw new Error("summary is required.");
|
|
3742
|
+
}
|
|
3743
|
+
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
3744
|
+
const agentId = requireGrantedNonHumanAgentId(options.agent, "observe");
|
|
3745
|
+
const kind = normalizeSessionObservationChoice(
|
|
3746
|
+
options.kind,
|
|
3747
|
+
SESSION_OBSERVATION_KINDS,
|
|
3748
|
+
"process",
|
|
3749
|
+
"kind",
|
|
3750
|
+
);
|
|
3751
|
+
const severity = normalizeSessionObservationChoice(
|
|
3752
|
+
options.severity,
|
|
3753
|
+
SESSION_OBSERVATION_SEVERITIES,
|
|
3754
|
+
"info",
|
|
3755
|
+
"severity",
|
|
3756
|
+
);
|
|
3757
|
+
const owner = normalizeSessionObservationText(
|
|
3758
|
+
options.owner,
|
|
3759
|
+
"owner",
|
|
3760
|
+
SESSION_OBSERVATION_OPTION_MAX_LENGTH,
|
|
3761
|
+
);
|
|
3762
|
+
const proposedBatch = normalizeSessionObservationText(
|
|
3763
|
+
options.batch,
|
|
3764
|
+
"batch",
|
|
3765
|
+
SESSION_OBSERVATION_OPTION_MAX_LENGTH,
|
|
3766
|
+
);
|
|
3767
|
+
const proposal = normalizeSessionObservationText(
|
|
3768
|
+
options.proposal,
|
|
3769
|
+
"proposal",
|
|
3770
|
+
SESSION_OBSERVATION_PROPOSAL_MAX_LENGTH,
|
|
3771
|
+
);
|
|
3772
|
+
const targetSequenceId = parseOptionalPositiveInteger(options.targetSequence, "target-sequence");
|
|
3773
|
+
const targetCursor = normalizeSessionObservationText(
|
|
3774
|
+
options.targetCursor,
|
|
3775
|
+
"target-cursor",
|
|
3776
|
+
SESSION_OBSERVATION_OPTION_MAX_LENGTH,
|
|
3777
|
+
);
|
|
3778
|
+
const localSession = await ensureLocalSessionForRemoteCommand(normalizedSessionId, {
|
|
3779
|
+
targetPath,
|
|
3780
|
+
});
|
|
3781
|
+
const clientMessageId = `cli-observation-${randomUUID()}`;
|
|
3782
|
+
const eventPayload = {
|
|
3783
|
+
schema: "session_observation/v1",
|
|
3784
|
+
summary,
|
|
3785
|
+
message: summary,
|
|
3786
|
+
kind,
|
|
3787
|
+
severity,
|
|
3788
|
+
channel: "session",
|
|
3789
|
+
source: "session_observe",
|
|
3790
|
+
clientKind: "cli",
|
|
3791
|
+
clientMessageId,
|
|
3792
|
+
};
|
|
3793
|
+
if (owner) eventPayload.owner = owner;
|
|
3794
|
+
if (proposedBatch) eventPayload.proposedBatch = proposedBatch;
|
|
3795
|
+
if (proposal) eventPayload.proposal = proposal;
|
|
3796
|
+
if (targetSequenceId) eventPayload.targetSequenceId = targetSequenceId;
|
|
3797
|
+
if (targetCursor) eventPayload.targetCursor = targetCursor;
|
|
3798
|
+
|
|
3799
|
+
const agent = await resolveSessionAgentEnvelope(normalizedSessionId, agentId, {
|
|
3800
|
+
targetPath,
|
|
3801
|
+
model: options.model,
|
|
3802
|
+
role: normalizeString(options.role) || "observer",
|
|
3803
|
+
displayName: options.displayName,
|
|
3804
|
+
});
|
|
3805
|
+
agent.role = normalizeString(agent.role) || "observer";
|
|
3806
|
+
agent.model = normalizeString(agent.model) || "cli";
|
|
3807
|
+
agent.clientKind = normalizeString(agent.clientKind) || "cli";
|
|
3808
|
+
|
|
3809
|
+
const event = createAgentEvent({
|
|
3810
|
+
event: "session_observation",
|
|
3811
|
+
agent,
|
|
3812
|
+
sessionId: normalizedSessionId,
|
|
3813
|
+
payload: eventPayload,
|
|
3814
|
+
});
|
|
3815
|
+
event.eventId = clientMessageId;
|
|
3816
|
+
event.idempotencyToken = clientMessageId;
|
|
3817
|
+
|
|
3818
|
+
const remoteConfirmationAnchor = await readSessionConfirmationAnchor(normalizedSessionId, {
|
|
3819
|
+
targetPath,
|
|
3820
|
+
});
|
|
3821
|
+
if (!remoteConfirmationAnchor?.ok) {
|
|
3822
|
+
if (remoteConfirmationAnchor?.reason === "api_403") {
|
|
3823
|
+
throw new Error(
|
|
3824
|
+
`Observation failed (api_403). Ensure this user has an active grant for '${agentId}'.`,
|
|
3825
|
+
);
|
|
3826
|
+
}
|
|
3827
|
+
throw new Error(
|
|
3828
|
+
`Observation confirmation anchor failed (${remoteConfirmationAnchor?.reason || "unknown"}); local cache was not updated.`,
|
|
3829
|
+
);
|
|
3830
|
+
}
|
|
3831
|
+
const remoteSync = await syncSessionEventToApi(normalizedSessionId, event, {
|
|
3832
|
+
targetPath,
|
|
3833
|
+
});
|
|
3834
|
+
if (!remoteSync?.synced) {
|
|
3835
|
+
throw new Error(
|
|
3836
|
+
`Observation failed (${remoteSync?.reason || "unknown"}). Ensure this user has an active grant for '${agentId}'.`,
|
|
3837
|
+
);
|
|
3838
|
+
}
|
|
3839
|
+
const remoteConfirmation = await confirmSessionEventVisible(normalizedSessionId, clientMessageId, {
|
|
3840
|
+
targetPath,
|
|
3841
|
+
anchorCursor: remoteConfirmationAnchor?.cursor,
|
|
3842
|
+
});
|
|
3843
|
+
if (!remoteConfirmation?.confirmed) {
|
|
3844
|
+
throw new Error(
|
|
3845
|
+
`Observation was accepted but not visible in canonical session events (${remoteConfirmation?.reason || "not_visible"}); local cache was not updated.`,
|
|
3846
|
+
);
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3849
|
+
const persisted = await appendToStream(normalizedSessionId, event, {
|
|
3850
|
+
targetPath,
|
|
3851
|
+
syncRemote: false,
|
|
3852
|
+
});
|
|
3853
|
+
const payload = {
|
|
3854
|
+
command: "session observe",
|
|
3855
|
+
targetPath,
|
|
3856
|
+
sessionId: normalizedSessionId,
|
|
3857
|
+
agentId,
|
|
3858
|
+
event: persisted,
|
|
3859
|
+
materializedLocalSession: localSession.materialized,
|
|
3860
|
+
refreshedLocalSession: Boolean(localSession.refreshed),
|
|
3861
|
+
remoteSync,
|
|
3862
|
+
remoteConfirmationAnchor,
|
|
3863
|
+
remoteConfirmation,
|
|
3864
|
+
};
|
|
3865
|
+
if (shouldEmitJson(options, command)) {
|
|
3866
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
3867
|
+
return;
|
|
3868
|
+
}
|
|
3869
|
+
console.log(formatEventLine(persisted));
|
|
3870
|
+
});
|
|
3871
|
+
|
|
3663
3872
|
async function runMessageActionCommand({
|
|
3664
3873
|
sessionId,
|
|
3665
3874
|
actionType,
|
package/src/session/daemon.js
CHANGED
package/src/session/listener.js
CHANGED
|
@@ -38,6 +38,10 @@ function normalizePositiveInteger(value, fallbackValue) {
|
|
|
38
38
|
return Math.floor(normalized);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
function isListenerStopDirective(event = {}) {
|
|
42
|
+
return normalizeString(event?.event) === "listener_stop";
|
|
43
|
+
}
|
|
44
|
+
|
|
41
45
|
function isPlainObject(value) {
|
|
42
46
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
43
47
|
}
|
|
@@ -255,6 +259,8 @@ export async function listenSessionEvents({
|
|
|
255
259
|
_readCursor = readSyncCursor,
|
|
256
260
|
_writeCursor = writeSyncCursor,
|
|
257
261
|
_sleep = defaultSleep,
|
|
262
|
+
_setInterval = setInterval,
|
|
263
|
+
_clearInterval = clearInterval,
|
|
258
264
|
_nowMs = Date.now,
|
|
259
265
|
} = {}) {
|
|
260
266
|
const normalizedSessionId = normalizeString(sessionId);
|
|
@@ -394,6 +400,7 @@ export async function listenSessionEvents({
|
|
|
394
400
|
const visibleEvents = [];
|
|
395
401
|
let preStartEventCount = 0;
|
|
396
402
|
for (const event of events) {
|
|
403
|
+
if (!isPlainObject(event)) continue;
|
|
397
404
|
const timestampMs = eventTimestampMs(event);
|
|
398
405
|
if (!timestampMs || timestampMs < startedAtMs) {
|
|
399
406
|
preStartEventCount += 1;
|
|
@@ -402,6 +409,7 @@ export async function listenSessionEvents({
|
|
|
402
409
|
if (isRecentActivity(activityMs, observedAtMs, activeWindowMs)) {
|
|
403
410
|
lastHumanActivityMs = Math.max(lastHumanActivityMs, activityMs);
|
|
404
411
|
}
|
|
412
|
+
if (timestampMs && timestampMs < startedAtMs && isListenerStopDirective(event)) continue;
|
|
405
413
|
if (isSessionListenerLifecycleEvent(event)) continue;
|
|
406
414
|
if (!eventMatchesAgent(event, normalizedAgentId)) continue;
|
|
407
415
|
visibleEvents.push(event);
|
|
@@ -441,9 +449,14 @@ export async function listenSessionEvents({
|
|
|
441
449
|
if (emittedKeys.has(key)) continue;
|
|
442
450
|
matched += 1;
|
|
443
451
|
if (!shouldEmitBatch && eventTimestampMs(event) < startedAtMs) continue;
|
|
444
|
-
await onEvent(event);
|
|
445
452
|
emittedKeys.add(key);
|
|
446
|
-
|
|
453
|
+
try {
|
|
454
|
+
await onEvent(event);
|
|
455
|
+
emitted += 1;
|
|
456
|
+
} catch (error) {
|
|
457
|
+
emittedKeys.delete(key);
|
|
458
|
+
throw error;
|
|
459
|
+
}
|
|
447
460
|
}
|
|
448
461
|
|
|
449
462
|
if (nextCursor && nextCursor !== cursor) {
|
|
@@ -473,6 +486,32 @@ export async function listenSessionEvents({
|
|
|
473
486
|
return humanActive;
|
|
474
487
|
}
|
|
475
488
|
|
|
489
|
+
let streamHeartbeatTimer = null;
|
|
490
|
+
let streamHeartbeatInFlight = false;
|
|
491
|
+
const streamHeartbeatMs = Math.max(1_000, Math.min(idleSleepMs, 60_000));
|
|
492
|
+
|
|
493
|
+
function stopStreamHeartbeatTimer() {
|
|
494
|
+
if (!streamHeartbeatTimer) return;
|
|
495
|
+
_clearInterval(streamHeartbeatTimer);
|
|
496
|
+
streamHeartbeatTimer = null;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function startStreamHeartbeatTimer() {
|
|
500
|
+
stopStreamHeartbeatTimer();
|
|
501
|
+
streamHeartbeatTimer = _setInterval(async () => {
|
|
502
|
+
if (signal?.aborted || streamHeartbeatInFlight) return;
|
|
503
|
+
streamHeartbeatInFlight = true;
|
|
504
|
+
try {
|
|
505
|
+
await notifyHeartbeat({ nextPollMs: null });
|
|
506
|
+
} finally {
|
|
507
|
+
streamHeartbeatInFlight = false;
|
|
508
|
+
}
|
|
509
|
+
}, streamHeartbeatMs);
|
|
510
|
+
if (streamHeartbeatTimer && typeof streamHeartbeatTimer.unref === "function") {
|
|
511
|
+
streamHeartbeatTimer.unref();
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
476
515
|
await notifyLifecycle(
|
|
477
516
|
lifecycleSnapshot("started", {
|
|
478
517
|
startedAt: new Date(startedAtMs).toISOString(),
|
|
@@ -484,6 +523,7 @@ export async function listenSessionEvents({
|
|
|
484
523
|
if (normalizedTransport !== "poll") {
|
|
485
524
|
streamAttempted = true;
|
|
486
525
|
activeTransport = "stream";
|
|
526
|
+
startStreamHeartbeatTimer();
|
|
487
527
|
const streamResult = await _stream(normalizedSessionId, {
|
|
488
528
|
targetPath,
|
|
489
529
|
since: cursor,
|
|
@@ -502,6 +542,8 @@ export async function listenSessionEvents({
|
|
|
502
542
|
onHeartbeat: async () => {
|
|
503
543
|
await notifyHeartbeat({ nextPollMs: null });
|
|
504
544
|
},
|
|
545
|
+
}).finally(() => {
|
|
546
|
+
stopStreamHeartbeatTimer();
|
|
505
547
|
});
|
|
506
548
|
if (!streamResult?.ok) {
|
|
507
549
|
streamFallbackReason = normalizeString(streamResult?.reason) || lastReason || "stream_failed";
|
package/src/session/preview.js
CHANGED
|
@@ -12,6 +12,7 @@ const PREVIEW_EVENTS = new Set([
|
|
|
12
12
|
"session_message",
|
|
13
13
|
"session_say",
|
|
14
14
|
"agent_response",
|
|
15
|
+
"session_observation",
|
|
15
16
|
"human_relay",
|
|
16
17
|
"daemon_alert",
|
|
17
18
|
"session_admin_kill",
|
|
@@ -45,6 +46,7 @@ export function pickLatestPreview(events = []) {
|
|
|
45
46
|
if (!kind || !PREVIEW_EVENTS.has(kind)) continue;
|
|
46
47
|
const payload = event.payload && typeof event.payload === "object" ? event.payload : {};
|
|
47
48
|
const text =
|
|
49
|
+
payload.summary ||
|
|
48
50
|
payload.message ||
|
|
49
51
|
payload.response ||
|
|
50
52
|
payload.alert ||
|
|
@@ -47,6 +47,7 @@ const TRANSCRIPT_EVENT_KINDS = new Set([
|
|
|
47
47
|
"session_reply",
|
|
48
48
|
"session_reaction",
|
|
49
49
|
"session_usage",
|
|
50
|
+
"session_observation",
|
|
50
51
|
"human_relay",
|
|
51
52
|
"agent_join",
|
|
52
53
|
"agent_left",
|
|
@@ -209,11 +210,56 @@ function actionBody(event) {
|
|
|
209
210
|
return "";
|
|
210
211
|
}
|
|
211
212
|
|
|
213
|
+
function boundedMarkdownText(value, { maxLength = 2_000 } = {}) {
|
|
214
|
+
const text = normalize(value).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
215
|
+
if (!maxLength || text.length <= maxLength) return text;
|
|
216
|
+
const head = Math.max(120, Math.floor((maxLength - 3) * 0.75));
|
|
217
|
+
const tail = Math.max(40, maxLength - 3 - head);
|
|
218
|
+
return `${text.slice(0, head)}...${text.slice(-tail)}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function observationTargetLabel(payload = {}) {
|
|
222
|
+
const targetSequence = Number(payload.targetSequenceId || payload.target_sequence_id || 0);
|
|
223
|
+
if (Number.isFinite(targetSequence) && targetSequence > 0) {
|
|
224
|
+
return `#${Math.floor(targetSequence)}`;
|
|
225
|
+
}
|
|
226
|
+
const targetCursor = tableText(payload.targetCursor || payload.target_cursor, { maxLength: 96 });
|
|
227
|
+
return targetCursor ? `cursor ${targetCursor}` : "";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function observationBody(event) {
|
|
231
|
+
const payload = event && typeof event.payload === "object" ? event.payload : {};
|
|
232
|
+
const severity = tableText(payload.severity || "info", { maxLength: 16 }).toLowerCase();
|
|
233
|
+
const kind = tableText(payload.kind || "process", { maxLength: 32 }).toLowerCase();
|
|
234
|
+
const summary = boundedMarkdownText(payload.summary || payload.message || payload.text, {
|
|
235
|
+
maxLength: 4_000,
|
|
236
|
+
});
|
|
237
|
+
const proposal = boundedMarkdownText(payload.proposal || payload.recommendation, {
|
|
238
|
+
maxLength: 2_000,
|
|
239
|
+
});
|
|
240
|
+
const owner = tableText(payload.owner || payload.assignee, { maxLength: 96 });
|
|
241
|
+
const proposedBatch = tableText(payload.proposedBatch || payload.batch, { maxLength: 96 });
|
|
242
|
+
const target = observationTargetLabel(payload);
|
|
243
|
+
const metadata = [];
|
|
244
|
+
if (owner) metadata.push(`Owner: \`${owner}\``);
|
|
245
|
+
if (proposedBatch) metadata.push(`Batch: \`${proposedBatch}\``);
|
|
246
|
+
if (target) metadata.push(`Target: \`${target}\``);
|
|
247
|
+
|
|
248
|
+
const lines = [`**Observation:** \`${severity || "info"}\` · \`${kind || "process"}\``];
|
|
249
|
+
if (metadata.length) lines.push(metadata.join(" · "));
|
|
250
|
+
if (summary) lines.push("", summary);
|
|
251
|
+
if (proposal) lines.push("", "**Proposal:**", proposal);
|
|
252
|
+
return lines.join("\n");
|
|
253
|
+
}
|
|
254
|
+
|
|
212
255
|
function eventBody(event) {
|
|
213
256
|
const kind = normalize(event?.event || event?.type);
|
|
214
257
|
if (kind === "session_action" || kind === "session_reply" || kind === "session_reaction") {
|
|
215
258
|
return actionBody(event);
|
|
216
259
|
}
|
|
260
|
+
if (kind === "session_observation") {
|
|
261
|
+
return observationBody(event);
|
|
262
|
+
}
|
|
217
263
|
const payload = event && typeof event.payload === "object" ? event.payload : {};
|
|
218
264
|
// session_usage carries the response inside payload.response.text
|
|
219
265
|
const responseText =
|