(
workspace: { id: string; displayName: string },
threads: SessionThread[],
)
| 172 | * Returns `null` when `threads` is empty. |
| 173 | */ |
| 174 | export function buildSessionExperienceContext( |
| 175 | workspace: { id: string; displayName: string }, |
| 176 | threads: SessionThread[], |
| 177 | ): BuildSessionResult | null { |
| 178 | if (!threads.length) return null; |
| 179 | |
| 180 | const { trimmedThreads, notes } = trimToBudget(threads, SESSION_EVENT_BUDGET); |
| 181 | |
| 182 | const payload: SessionExperienceContext = { |
| 183 | context_id: workspace.id, |
| 184 | workspace_id: workspace.id, |
| 185 | workspace_name: workspace.displayName, |
| 186 | threads: trimmedThreads.map(t => ({ |
| 187 | thread_id: t.thread_id, |
| 188 | events: t.events, |
| 189 | })), |
| 190 | ...(notes.length ? { payload_notes: notes } : {}), |
| 191 | }; |
| 192 | |
| 193 | const trimmedStepCount = trimmedThreads.reduce( |
| 194 | (acc, t) => acc + t.events.filter(e => e.type === 'create_table').length, |
| 195 | 0, |
| 196 | ); |
| 197 | |
| 198 | return { |
| 199 | payload, |
| 200 | threads: trimmedThreads, |
| 201 | stats: { threadCount: trimmedThreads.length, stepCount: trimmedStepCount }, |
| 202 | notes, |
| 203 | }; |
| 204 | } |
| 205 | |
| 206 | /** JSON-byte size of an events array (used for budgeting). */ |
| 207 | function eventsByteSize(events: Array<Record<string, any>>): number { |
no test coverage detected