(r2Key: string)
| 41 | env: Env, |
| 42 | msg: IngestQueueMessage, |
| 43 | ctx: ExecutionContext |
| 44 | ): Promise<void> { |
| 45 | const sessionRow = await loadSessionOrCleanupStaging(env, msg); |
| 46 | if (!sessionRow) return; |
| 47 | |
| 48 | const body = await getStagingObjectBody(env, msg.r2Key); |
| 49 | const mergedChanges = new Map<string, string | null>(); |
| 50 | const mergedAttentionSignals: AttentionSignal[] = []; |
| 51 | |
| 52 | try { |
| 53 | const accepted = await ingestStagedSessionItems( |
| 54 | env, |
| 55 | msg, |
| 56 | body, |
| 57 | mergedChanges, |
| 58 | mergedAttentionSignals |
| 59 | ); |
| 60 | if (accepted) { |
| 61 | await applyMetadataChanges(env, msg.kiloUserId, msg.sessionId, mergedChanges, ctx); |
| 62 | } |
| 63 | } catch (err) { |
| 64 | // An earlier chunk may have committed to the DO before a later chunk (or the |
| 65 | // JSON parse) failed. The DO reports a metadata change only when its stored |
| 66 | // value differs, so on retry those already-persisted values won't be |
| 67 | // re-emitted — Postgres would never catch up. Flush what we have now so the |
| 68 | // two stores stay in sync. Best-effort: never mask the original error. |
| 69 | await flushPartialMetadataChanges(env, msg, mergedChanges, ctx); |
| 70 | // Same reasoning for attention signals: a committed status transition won't |
| 71 | // re-emit on retry, so dispatch what was collected before the failure. |
| 72 | scheduleAttentionSignalDispatch( |
| 73 | env, |
| 74 | msg, |
| 75 | sessionRow, |
| 76 | mergedChanges, |
| 77 | mergedAttentionSignals, |
| 78 | ctx |
| 79 | ); |
| 80 | throw err; |
| 81 | } |
| 82 | |
| 83 | scheduleAttentionSignalDispatch(env, msg, sessionRow, mergedChanges, mergedAttentionSignals, ctx); |
| 84 | await env.SESSION_INGEST_R2.delete(msg.r2Key); |
| 85 | } |
| 86 | |
| 87 | type IngestSessionRow = { |
| 88 | session_id: string; |
| 89 | parent_session_id: string | null; |
| 90 | created_on_platform: string | null; |
| 91 | }; |
| 92 | |
| 93 | /** |
| 94 | * Loads the session row for the queued message, or cleans up the staging object and returns |
| 95 | * null if the session has been deleted since the message was queued. The parent column feeds |
| 96 | * the attention-push eligibility check later in processing. |
| 97 | */ |
| 98 | async function loadSessionOrCleanupStaging( |
| 99 | env: Env, |
| 100 | msg: IngestQueueMessage |
no test coverage detected