Skip to content

Commit 6582f51

Browse files
committed
fix(logs): label live display projection warnings and tighten the sources dialog test
1 parent 550fe32 commit 6582f51

4 files changed

Lines changed: 52 additions & 11 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/message-sources/message-sources.test.tsx‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ describe('creating a Sim link from the sources popover', () => {
6868
const form = [...document.querySelectorAll('[role="dialog"]')].find((dialog) =>
6969
dialog.textContent?.includes('New link')
7070
)
71-
expect(form).toBeDefined()
72-
expect(document.querySelector<HTMLInputElement>('input[type="url"]')?.value).toBe(source.url)
71+
expect(form?.querySelector<HTMLInputElement>('input[type="url"]')?.value).toBe(source.url)
7372
expect(row()).toBeNull()
7473
})
7574
})

‎apps/sim/lib/logs/execution/logging-session.test.ts‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
} from '@sim/testing/mocks/billing-usage-reservation.mock'
77
import { encryptionMock, encryptionMockFns } from '@sim/testing/mocks/encryption.mock'
88
import { executionPayloadStoreMock } from '@sim/testing/mocks/execution-payload-store.mock'
9+
import { getMockLogger } from '@sim/testing/mocks/logger.mock'
910
import {
1011
getMockPlatformEvent,
1112
telemetryMock,
@@ -774,6 +775,35 @@ describe('LoggingSession completion retries', () => {
774775
expect(rawLogs[128].output.value).toBe('row-128:raw-secret')
775776
})
776777

778+
it('labels a live block-log projection warning with the live phase', async () => {
779+
const session = new LoggingSession('workflow-1', 'execution-live-phase', 'manual', 'req-1')
780+
session.setResolvedSecretTraceRegistry(
781+
createSecretRegistry([{ plaintext: 'raw-secret', replacement: '{{TOKEN}}' }])
782+
)
783+
784+
await session.projectBlockLogsForDisplay([
785+
{
786+
blockId: 'function-1',
787+
blockName: 'Function 1',
788+
blockType: 'function',
789+
startedAt: '2026-07-01T00:00:00.000Z',
790+
endedAt: '2026-07-01T00:00:00.001Z',
791+
durationMs: 1,
792+
success: true,
793+
executionOrder: 1,
794+
output: { rows: Array.from({ length: 100_001 }, (_, index) => index) },
795+
displayResolvedSecretTraceProvenance: createDisplayProvenance([
796+
{ plaintext: 'raw-secret', replacement: '{{TOKEN}}' },
797+
]),
798+
},
799+
])
800+
801+
expect(getMockLogger('TraceSecretProjection').warn).toHaveBeenCalledWith(
802+
'Omitting trace content that could not be sanitized',
803+
expect.objectContaining({ executionId: 'execution-live-phase', phase: 'live' })
804+
)
805+
})
806+
777807
it('projects a numeric Function result produced by a resolved numeric secret', async () => {
778808
const session = new LoggingSession('workflow-1', 'execution-numeric-secret', 'manual', 'req-1')
779809
session.setResolvedSecretTraceRegistry(

‎apps/sim/lib/logs/execution/logging-session.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,13 @@ export class LoggingSession {
309309

310310
private async projectRawTraceSpans(
311311
traceSpans: TraceSpan[],
312-
registry = this.resolvedSecretTraceRegistry
312+
registry = this.resolvedSecretTraceRegistry,
313+
phase?: 'live'
313314
): Promise<TraceSpan[]> {
314315
return projectTraceSpansForSecrets(traceSpans, {
315316
registry,
316317
store: this.getSecretProjectionStore(),
318+
phase,
317319
})
318320
}
319321

@@ -365,7 +367,8 @@ export class LoggingSession {
365367
output: envelope,
366368
},
367369
],
368-
registry
370+
registry,
371+
'live'
369372
)
370373
const projected = this.readProjectedDisplayContent(projectedSpan?.output)
371374
return this.shouldClearLiveDisplay(registry)
@@ -426,7 +429,8 @@ export class LoggingSession {
426429
...(log.childTraceSpans ? { children: log.childTraceSpans } : {}),
427430
},
428431
],
429-
registry
432+
registry,
433+
'live'
430434
)
431435
const display = this.readProjectedDisplayContent(projectedLog?.output)
432436
displayLogs.push({
@@ -1503,7 +1507,7 @@ export class LoggingSession {
15031507
* structure with no content rather than unprojected values.
15041508
*/
15051509
async projectTraceSpansForLiveDisplay(traceSpans: TraceSpan[]): Promise<TraceSpan[]> {
1506-
return this.projectRawTraceSpans(traceSpans)
1510+
return this.projectRawTraceSpans(traceSpans, undefined, 'live')
15071511
}
15081512

15091513
async safeComplete(params: SessionCompleteParams = {}): Promise<void> {

‎apps/sim/lib/logs/execution/trace-secret-projection.ts‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ export interface ProjectTraceSpansForSecretsOptions {
125125
store: LargeValueStoreContext
126126
/** Read-only display projections omit ref-backed content instead of writing another ref. */
127127
allowLargeValueWrites?: boolean
128+
/** Labels warnings; defaults to `write`, or `read` when large-value writes are disabled. */
129+
phase?: ProjectionPhase
128130
/**
129131
* Synthetic spans that each carry one display-envelope value, keyed by span id. Warnings name
130132
* the envelope field instead of the synthetic span and count it apart from real spans.
@@ -146,19 +148,22 @@ class TraceSecretProjectionError extends Error {
146148
class TraceContentBudgetError extends TraceSecretProjectionError {}
147149

148150
/**
149-
* Identifies the run behind a projection warning. `phase` says whether the run was being written
150-
* to the log or a stored log was being read for display.
151+
* Why a projection ran: `write` prepares the run's stored log, `read` displays a stored log, and
152+
* `live` streams a running execution to a viewer without storing it.
151153
*/
154+
type ProjectionPhase = 'write' | 'read' | 'live'
155+
156+
/** Identifies the run behind a projection warning. */
152157
interface ProjectionLogDetails {
153158
executionId?: string
154159
workflowId?: string
155160
workspaceId?: string
156-
phase: 'read' | 'write'
161+
phase: ProjectionPhase
157162
}
158163

159164
function projectionLogDetails(
160165
store: LargeValueStoreContext,
161-
phase: ProjectionLogDetails['phase']
166+
phase: ProjectionPhase
162167
): ProjectionLogDetails {
163168
return {
164169
...filterUndefined({
@@ -1713,7 +1718,10 @@ export async function projectTraceSpansForSecrets(
17131718
options: ProjectTraceSpansForSecretsOptions
17141719
): Promise<TraceSpan[]> {
17151720
const allowLargeValueWrites = options.allowLargeValueWrites !== false
1716-
const logDetails = projectionLogDetails(options.store, allowLargeValueWrites ? 'write' : 'read')
1721+
const logDetails = projectionLogDetails(
1722+
options.store,
1723+
options.phase ?? (allowLargeValueWrites ? 'write' : 'read')
1724+
)
17171725
if (!options.registry?.isComplete()) {
17181726
return structuralOnlyTraceSpans(traceSpans, logDetails)
17191727
}

0 commit comments

Comments
 (0)