sentinelayer-cli 0.30.5 → 0.30.7

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sentinelayer-cli",
3
- "version": "0.30.5",
3
+ "version": "0.30.7",
4
4
  "description": "Scaffold Sentinelayer spec/prompt/guide artifacts with secure browser auth and token bootstrap.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -5867,10 +5867,11 @@ export function registerSessionCommand(program) {
5867
5867
  ? buildSessionActionEvents(normalizedSessionId, remoteActions.actions)
5868
5868
  : [];
5869
5869
  const includeControlEvents = Boolean(options.includeControlEvents);
5870
+ const dedupedEvents = dedupeSessionEvents(events);
5870
5871
  const transcriptBaseEvents = includeControlEvents
5871
- ? events
5872
- : filterSessionMaterialEvents(events);
5873
- const hiddenControlEventCount = events.length - transcriptBaseEvents.length;
5872
+ ? dedupedEvents
5873
+ : filterSessionMaterialEvents(dedupedEvents);
5874
+ const hiddenControlEventCount = dedupedEvents.length - transcriptBaseEvents.length;
5874
5875
  const exportEvents = mergeSessionActionEvents(transcriptBaseEvents, actionEvents);
5875
5876
  const stats = computeTranscriptStats({
5876
5877
  sessionMeta: sessionPayload,
@@ -6021,10 +6022,11 @@ export function registerSessionCommand(program) {
6021
6022
  ? buildSessionActionEvents(normalizedSessionId, remoteActions.actions)
6022
6023
  : [];
6023
6024
  const includeControlEvents = Boolean(options.includeControlEvents);
6025
+ const dedupedEvents = dedupeSessionEvents(events);
6024
6026
  const transcriptBaseEvents = includeControlEvents
6025
- ? events
6026
- : filterSessionMaterialEvents(events);
6027
- const hiddenControlEventCount = events.length - transcriptBaseEvents.length;
6027
+ ? dedupedEvents
6028
+ : filterSessionMaterialEvents(dedupedEvents);
6029
+ const hiddenControlEventCount = dedupedEvents.length - transcriptBaseEvents.length;
6028
6030
  const transcriptEvents = mergeSessionActionEvents(transcriptBaseEvents, actionEvents);
6029
6031
 
6030
6032
  // Pull GitHub/Google avatar + display name from the active auth
@@ -52,9 +52,61 @@ function stableStringify(value) {
52
52
  return JSON.stringify(stableJsonValue(value));
53
53
  }
54
54
 
55
+ function messageEventCanUseRelaxedContentKey(eventKind) {
56
+ return [
57
+ "agent_response",
58
+ "human_relay",
59
+ "session_message",
60
+ "session_say",
61
+ ].includes(keyString(eventKind));
62
+ }
63
+
64
+ function eventDurabilityScore(event = {}) {
65
+ let score = 0;
66
+ if (readSessionEventSequence(event) > 0) score += 2;
67
+ if (sessionEventHasDurableCursor(event)) score += 1;
68
+ return score;
69
+ }
70
+
71
+ function mergeObjectPreferringPrimary(primaryValue, secondaryValue) {
72
+ if (
73
+ primaryValue &&
74
+ typeof primaryValue === "object" &&
75
+ !Array.isArray(primaryValue) &&
76
+ secondaryValue &&
77
+ typeof secondaryValue === "object" &&
78
+ !Array.isArray(secondaryValue)
79
+ ) {
80
+ return { ...secondaryValue, ...primaryValue };
81
+ }
82
+ return primaryValue === undefined ? secondaryValue : primaryValue;
83
+ }
84
+
85
+ function mergeDuplicateEvent(existingEvent = {}, candidateEvent = {}) {
86
+ const existingScore = eventDurabilityScore(existingEvent);
87
+ const candidateScore = eventDurabilityScore(candidateEvent);
88
+ const candidateIsPrimary = candidateScore > existingScore || (
89
+ candidateScore === existingScore &&
90
+ sessionEventUpgradesExisting(existingEvent, candidateEvent)
91
+ ) || (
92
+ candidateScore === existingScore &&
93
+ !sessionEventUpgradesExisting(candidateEvent, existingEvent)
94
+ );
95
+ const primaryEvent = candidateIsPrimary ? candidateEvent : existingEvent;
96
+ const secondaryEvent = candidateIsPrimary ? existingEvent : candidateEvent;
97
+
98
+ return {
99
+ ...secondaryEvent,
100
+ ...primaryEvent,
101
+ agent: mergeObjectPreferringPrimary(primaryEvent.agent, secondaryEvent.agent),
102
+ payload: mergeObjectPreferringPrimary(primaryEvent.payload, secondaryEvent.payload),
103
+ };
104
+ }
105
+
55
106
  export function sessionEventIdentityKeys(event = {}) {
56
107
  if (!event || typeof event !== "object") return [];
57
108
  const keys = [];
109
+ const eventKind = keyString(event.event || event.type);
58
110
  const id = keyString(event.id);
59
111
  if (id) {
60
112
  keys.push(`id:${id}`);
@@ -109,7 +161,7 @@ export function sessionEventIdentityKeys(event = {}) {
109
161
  if (message) {
110
162
  try {
111
163
  keys.push(`content:${stableStringify({
112
- event: keyString(event.event || event.type),
164
+ event: eventKind,
113
165
  agent: keyString(event.agent?.id || event.agentId || payload.agentId || payload.authorId),
114
166
  payload: {
115
167
  channel: keyString(payload.channel),
@@ -123,6 +175,22 @@ export function sessionEventIdentityKeys(event = {}) {
123
175
  } catch {
124
176
  // Best-effort duplicate suppression only.
125
177
  }
178
+ if (messageEventCanUseRelaxedContentKey(eventKind)) {
179
+ try {
180
+ keys.push(`content-relaxed:${stableStringify({
181
+ event: eventKind,
182
+ agent: keyString(event.agent?.id || event.agentId || payload.agentId || payload.authorId),
183
+ payload: {
184
+ channel: keyString(payload.channel),
185
+ message,
186
+ source: keyString(payload.source),
187
+ },
188
+ ts: timestampKey(event.ts, event.timestamp, event.at),
189
+ })}`);
190
+ } catch {
191
+ // Best-effort duplicate suppression only.
192
+ }
193
+ }
126
194
  }
127
195
  return keys;
128
196
  }
@@ -151,7 +219,7 @@ export function dedupeSessionEvents(events = []) {
151
219
  const existingIndex = existingIndexes.length > 0 ? Math.min(...existingIndexes) : -1;
152
220
 
153
221
  if (existingIndex >= 0) {
154
- deduped[existingIndex] = event;
222
+ deduped[existingIndex] = mergeDuplicateEvent(deduped[existingIndex], event);
155
223
  for (const key of keys) {
156
224
  indexByKey.set(key, existingIndex);
157
225
  }