Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/sim/app/api/knowledge/search/route.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -381,7 +381,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
results,
})
if (!resultProvenanceSnapshot.imported) {
resultSecretRegistry.markIncomplete()
resultSecretRegistry.markIncomplete('knowledge-result-provenance-unavailable')
if (useReranker) {
return NextResponse.json(
{ error: 'Knowledge result secret provenance is unavailable' },
Expand DownExpand Up@@ -608,7 +608,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
'knowledge'
))
) {
resultSecretRegistry.markIncomplete()
resultSecretRegistry.markIncomplete('knowledge-result-provenance-unavailable')
}
}

Expand Down
2 changes: 1 addition & 1 deletion apps/sim/app/api/knowledge/secret-provenance.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -154,7 +154,7 @@ export async function createKnowledgeProvenanceResponse(options: {
})
for (const provenance of options.provenances) {
if (provenance.status === 'unknown') {
registry.markIncomplete()
registry.markIncomplete('durable-provenance-unknown')
break
}
const sourceRegistry = await createDurableSecretProvenanceRegistry(provenance, {
Expand Down
4 changes: 2 additions & 2 deletions apps/sim/app/api/memory/secret-provenance.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,7 +98,7 @@ export async function createMemoryResponse(options: {
workspaceId: options.workspaceId,
})
if (options.memories.length > MAX_PRIVATE_MEMORY_CROSSINGS) {
registry.markIncomplete()
registry.markIncomplete('memory-crossing-capacity-exceeded')
} else {
const ids = [...new Set(options.memories.map((record) => record.id))]
const memoriesById = new Map<string, MemoryCrossing[]>()
Expand All@@ -123,7 +123,7 @@ export async function createMemoryResponse(options: {
provenanceEntryCount > MAX_PRIVATE_MEMORY_PROVENANCE_ENTRIES ||
provenanceBytes > MAX_PRIVATE_MEMORY_PROVENANCE_BYTES
) {
registry.markIncomplete()
registry.markIncomplete('memory-crossing-capacity-exceeded')
break
}
const sidecarById = new Map(sidecars.map((sidecar) => [sidecar.memoryId, sidecar]))
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/app/api/workflows/[id]/log/route.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -142,7 +142,7 @@ export const POST = withRouteHandler(
: undefined
const trustedProvenance = trustedExecutionState?.resolvedSecretTraceProvenance
if (trustedProvenance === undefined) {
resolvedSecretTraceRegistry.markIncomplete()
resolvedSecretTraceRegistry.markIncomplete('restored-provenance-untrusted')
} else {
await resolvedSecretTraceRegistry.importProvenance(trustedProvenance, {
trusted: true,
Expand Down
4 changes: 2 additions & 2 deletions apps/sim/executor/handlers/agent/memory.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -278,7 +278,7 @@ describe('Memory', () => {

it('persists raw memory with unknown lineage when provenance is unavailable', async () => {
const registry = new ResolvedSecretTraceRegistry()
registry.markIncomplete()
registry.markIncomplete('unspecified')
const appendMessage = vi
.spyOn(memoryService as any, 'appendMessage')
.mockResolvedValue(undefined)
Expand All@@ -293,7 +293,7 @@ describe('Memory', () => {

it('seeds raw memory with unknown lineage when provenance is unavailable', async () => {
const registry = new ResolvedSecretTraceRegistry()
registry.markIncomplete()
registry.markIncomplete('unspecified')
const seedMemoryRecord = vi
.spyOn(memoryService as any, 'seedMemoryRecord')
.mockResolvedValue(undefined)
Expand Down
5 changes: 3 additions & 2 deletions apps/sim/executor/handlers/generic/generic-handler.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -199,7 +199,8 @@ export class GenericBlockHandler implements BlockHandler {
boundary && boundary.paths.length > 0 && registry?.hasResolvedInputProjections()
? registry.projectResolvedInputSelections(inputs)
: undefined
if (projectedInputs?.complete === false) registry?.markIncomplete()
if (projectedInputs?.complete === false)
registry?.markIncomplete('structural-input-projection-incomplete')

if (projectedInputs?.complete && boundary && tool && registry) {
for (const projection of projectedInputs.values) {
Expand DownExpand Up@@ -233,7 +234,7 @@ export class GenericBlockHandler implements BlockHandler {
continue
}
if (boundary.requiredProjectionRoots.has(projection.path[0])) {
registry.markIncomplete()
registry.markIncomplete('structural-input-root-unprojected')
}
continue
}
Expand Down
12 changes: 6 additions & 6 deletions apps/sim/executor/handlers/mothership/mothership-handler.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -372,7 +372,7 @@ async function consumeMothershipProvenance(
return false
}
if (inspection.status === 'invalid') {
registry?.markIncomplete()
registry?.markIncomplete('mothership-provenance-invalid')
throw new Error('Mothership response provenance metadata is invalid')
}

Expand All@@ -399,7 +399,7 @@ function inspectMothershipResponseCapability(
return false
}

registry?.markIncomplete()
registry?.markIncomplete('mothership-provenance-invalid')
throw new Error('Mothership response provenance metadata is invalid')
}

Expand DownExpand Up@@ -464,7 +464,7 @@ async function readMothershipExecuteResponse(
result = (await response.json()) as MothershipExecuteResult
} catch (error) {
if (expectsProvenance) {
registry?.markIncomplete()
registry?.markIncomplete('mothership-response-unreadable')
throw new Error('Mothership response provenance metadata is invalid')
}
throw error
Expand DownExpand Up@@ -528,7 +528,7 @@ async function readMothershipExecuteResponse(
return finalResult
} finally {
if (expectsProvenance && !finalResult && !receivedTerminalProvenance) {
registry?.markIncomplete()
registry?.markIncomplete('mothership-provenance-missing')
}
reader.releaseLock()
}
Expand DownExpand Up@@ -630,7 +630,7 @@ function createMothershipStreamingExecution(
}
} finally {
if (expectsProvenance && !sawFinal && !receivedTerminalProvenance) {
options.registry?.markIncomplete()
options.registry?.markIncomplete('mothership-provenance-missing')
}
cleanup()
reader?.releaseLock()
Expand DownExpand Up@@ -948,7 +948,7 @@ export class MothershipBlockHandler implements BlockHandler {
try {
payload = (await response.clone().json()) as MothershipExecuteResult
} catch {
resultRegistry?.markIncomplete()
resultRegistry?.markIncomplete('mothership-response-unreadable')
throw new Error('Mothership response provenance metadata is invalid')
}
await consumeMothershipProvenance(payload, response, resultRegistry)
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/executor/handlers/pi/local/sim-tools.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -320,7 +320,7 @@ describe('buildSimToolSpecs', () => {
output: { result: 'untrusted output' },
})
const registry = new ResolvedSecretTraceRegistry()
registry.markIncomplete()
registry.markIncomplete('unspecified')
const [spec] = await buildSimToolSpecs(executionContext(registry), toolInput)

const result = await spec.execute({})
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/executor/handlers/pi/pi-handler.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,7 +227,7 @@ describe('PiBlockHandler', () => {

it('fails closed when task provenance is incomplete', async () => {
const registry = new ResolvedSecretTraceRegistry()
registry.markIncomplete()
registry.markIncomplete('unspecified')

await expect(
handler.execute(
Expand Down
4 changes: 2 additions & 2 deletions apps/sim/executor/handlers/pi/search/tool.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -288,7 +288,7 @@ describe('buildPiSearchToolSpec', () => {

it('fails closed before search when provenance is incomplete', async () => {
const registry = new ResolvedSecretTraceRegistry()
registry.markIncomplete()
registry.markIncomplete('unspecified')

const result = await buildTool('exa', executionContext(registry)).execute({ query: 'pi' })

Expand All@@ -303,7 +303,7 @@ describe('buildPiSearchToolSpec', () => {
const registry = new ResolvedSecretTraceRegistry()
const mergeSpy = vi.spyOn(registry, 'mergeToolCallRegistry')
mockExecuteTool.mockImplementation(async (_toolId, _params, options) => {
options.resolvedSecretTraceRegistry.markIncomplete()
options.resolvedSecretTraceRegistry.markIncomplete('unspecified')
return {
success: true,
output: {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -70,7 +70,7 @@ describe('projectResolvedSecretModelContent', () => {
value: '{{TOKEN}}',
})

registry.markIncomplete()
registry.markIncomplete('unspecified')
expect(projectResolvedSecretModelContent('secret-value', registry)).toEqual({ safe: false })
expect(projectResolvedSecretModelContent('secret-value', undefined)).toEqual({ safe: false })
})
Expand DownExpand Up@@ -368,7 +368,7 @@ describe('projectResolvedSecretModelJsonContent', () => {

it('does not invoke JSON serialization when provenance is incomplete', () => {
const registry = new ResolvedSecretTraceRegistry()
registry.markIncomplete()
registry.markIncomplete('unspecified')
const toJSON = vi.fn(() => ({ value: 'untrusted' }))

expect(projectResolvedSecretModelJsonContent({ toJSON }, registry)).toEqual({ safe: false })
Expand DownExpand Up@@ -474,7 +474,7 @@ describe('projectResolvedSecretDiagnosticError', () => {
it('falls back to text-free structure when provenance is missing or incomplete', () => {
const error = new Error('secret __var_API_KEY')
const registry = new ResolvedSecretTraceRegistry()
registry.markIncomplete()
registry.markIncomplete('unspecified')

expect(projectResolvedSecretDiagnosticError(error, undefined)).toEqual({
errorType: 'error',
Expand Down
79 changes: 77 additions & 2 deletions apps/sim/executor/utils/resolved-secret-trace-registry.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -141,7 +141,7 @@ describe('ResolvedSecretTraceProvenanceAccumulator', () => {
entries: [{ name: 'TOKEN', encryptedValue: 'encrypted-value' }],
scope,
})
accumulator.markIncomplete()
accumulator.markIncomplete('unspecified')
expect(accumulator.exportProvenance().entries).toEqual([])
})
})
Expand DownExpand Up@@ -1443,6 +1443,81 @@ describe('incompleteness diagnostics', () => {
expect(reasons).not.toContain('value-provenance-filter-incomplete')
})

it.each([
'tool-input-not-enumerable',
'tool-params-transform-failed',
'structural-input-projection-incomplete',
'mothership-provenance-invalid',
'client-tool-seal-failed',
'knowledge-row-missing',
'knowledge-row-content-mismatch',
'mothership-response-unreadable',
'structural-input-root-unprojected',
'backfill-checkpoint-unusable',
] as const)('reports %s at error, since it cannot trip on a healthy run', (reason) => {
new ResolvedSecretTraceRegistry([], scope).markIncomplete(reason)

expect(mockLogger.error).toHaveBeenCalledWith(
'Resolved secret registry marked incomplete',
expect.objectContaining({ reason })
)
expect(mockLogger.warn).not.toHaveBeenCalled()
})

it.each([
'mothership-provenance-missing',
'client-tool-completion-missing',
'client-tool-completion-deferred',
'client-tool-completion-unidentified',
'client-tool-execution-untrusted',
'client-tool-content-unavailable',
'knowledge-result-provenance-unavailable',
'knowledge-response-capacity-exceeded',
'memory-crossing-capacity-exceeded',
'workspace-scope-missing',
'mounted-file-provenance-unavailable',
'table-snapshot-unsafe-for-mount',
'restored-provenance-untrusted',
'backfill-checkpoint-absent',
'client-tool-seal-absent',
] as const)('reports %s at warn, since it is reachable without a fault', (reason) => {
new ResolvedSecretTraceRegistry([], scope).markIncomplete(reason)

expect(mockLogger.warn).toHaveBeenCalledWith(
'Resolved secret registry marked incomplete',
expect.objectContaining({ reason })
)
expect(mockLogger.error).not.toHaveBeenCalled()
})

/**
* The taxonomy's rule is that an error reason cannot trip on a healthy run. A backfill walking
* historical rows hits the no-checkpoint case on essentially every legacy row, so classifying it
* as a fault would put one error line per row into the stream this split exists to protect.
*/
it('separates an absent checkpoint from an unusable one, so a backfill cannot flood errors', () => {
new ResolvedSecretTraceRegistry([], scope).markIncomplete('backfill-checkpoint-absent')
expect(mockLogger.error).not.toHaveBeenCalled()
expect(mockLogger.warn).toHaveBeenCalledWith(
'Resolved secret registry marked incomplete',
expect.objectContaining({ reason: 'backfill-checkpoint-absent' })
)

vi.clearAllMocks()
new ResolvedSecretTraceRegistry([], scope).markIncomplete('backfill-checkpoint-unusable')
expect(mockLogger.error).toHaveBeenCalledWith(
'Resolved secret registry marked incomplete',
expect.objectContaining({ reason: 'backfill-checkpoint-unusable' })
)
})

it('does not report a log-less session at all, since it fires on every such run', () => {
new ResolvedSecretTraceRegistry([], scope).markIncomplete('log-creation-skipped')

expect(mockLogger.error).not.toHaveBeenCalled()
expect(mockLogger.warn).not.toHaveBeenCalled()
})

it('reports an incoming incomplete bundle at warn, since no catalog was ever on offer', () => {
const registry = new ResolvedSecretTraceRegistry([], scope)

Expand All@@ -1465,7 +1540,7 @@ describe('incompleteness diagnostics', () => {
it('keeps an unaudited caller taking the default reason out of the error stream', () => {
const registry = new ResolvedSecretTraceRegistry([], scope)

registry.markIncomplete()
registry.markIncomplete('unspecified')

expect(mockLogger.error).not.toHaveBeenCalled()
expect(mockLogger.warn).toHaveBeenCalledWith(
Expand Down
Loading
Loading