Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions Scripts/Fixtures/test-suite-contract-ledger.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -1184,13 +1184,15 @@ root/RepoPromptTests.ContextBuilderFollowUpFinalizationMonitorTests/testStalledF
root/RepoPromptTests.ContextBuilderFollowUpFinalizationMonitorTests/testTimeoutOutcomeWinsWhenCancellationAlsoCompletesFinalization root Tests/RepoPromptTests/ContextBuilder/ContextBuilderFollowUpFinalizationMonitorTests.swift RepoPromptTests.ContextBuilderFollowUpFinalizationMonitorTests testTimeoutOutcomeWinsWhenCancellationAlsoCompletesFinalization ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.000000 unreviewed retain_pending_review 0 initial census source line 94
root/RepoPromptTests.ContextBuilderMCPProgressTimelineTests/testCommitAndClearTabContextReportsPersistenceSubphasesInOrder root Tests/RepoPromptTests/ContextBuilder/ContextBuilderMCPProgressTimelineTests.swift RepoPromptTests.ContextBuilderMCPProgressTimelineTests testCommitAndClearTabContextReportsPersistenceSubphasesInOrder ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.190000 unreviewed retain_pending_review 0 initial census source line 371
root/RepoPromptTests.ContextBuilderMCPProgressTimelineTests/testContextBuilderToolProviderReportsDiscoveryAndGenerationStageEnvelope root Tests/RepoPromptTests/ContextBuilder/ContextBuilderMCPProgressTimelineTests.swift RepoPromptTests.ContextBuilderMCPProgressTimelineTests testContextBuilderToolProviderReportsDiscoveryAndGenerationStageEnvelope ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.154000 unreviewed retain_pending_review 0 initial census source line 266
root/RepoPromptTests.ContextBuilderMCPProgressTimelineTests/testContextBuilderToolProviderUsesCallerPromptWithoutMutatingDiscoveryFallback root Tests/RepoPromptTests/ContextBuilder/ContextBuilderMCPProgressTimelineTests.swift RepoPromptTests.ContextBuilderMCPProgressTimelineTests testContextBuilderToolProviderUsesCallerPromptWithoutMutatingDiscoveryFallback ContextBuilder context_builder.typed_prompt.request_local_handoff prompt_authority,state_preservation,hidden_guidance integration_regression root_swiftpm routine 1 Injected follow-up and returned prompt contain caller task/context without discovery guidance or discovery output while the canonical tab retains its committed fallback. Typed MCP responses from empty tabs could fail, leak discovery-only guidance, execute discovery prose, or overwrite canonical tab state. 0.524000 singleton_window_registry window_state retain 0 request-local typed prompt handoff across provider execution and result packaging
root/RepoPromptTests.ContextBuilderMCPProgressTimelineTests/testDeferredRunMappingSurvivesCommitUntilCallerCleanup root Tests/RepoPromptTests/ContextBuilder/ContextBuilderMCPProgressTimelineTests.swift RepoPromptTests.ContextBuilderMCPProgressTimelineTests testDeferredRunMappingSurvivesCommitUntilCallerCleanup ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.048000 unreviewed retain_pending_review 0 initial census source line 425
root/RepoPromptTests.ContextBuilderMCPProgressTimelineTests/testPhaseCatalogCoversExpectedDiscoveryAndGenerationSequence root Tests/RepoPromptTests/ContextBuilder/ContextBuilderMCPProgressTimelineTests.swift RepoPromptTests.ContextBuilderMCPProgressTimelineTests testPhaseCatalogCoversExpectedDiscoveryAndGenerationSequence ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.000000 unreviewed retain_pending_review 0 initial census source line 6
root/RepoPromptTests.ContextBuilderMCPProgressTimelineTests/testResponseDispositionRoutesRequestedModesAndFailsClosedOnMissingFollowUpState root Tests/RepoPromptTests/ContextBuilder/ContextBuilderMCPProgressTimelineTests.swift RepoPromptTests.ContextBuilderMCPProgressTimelineTests testResponseDispositionRoutesRequestedModesAndFailsClosedOnMissingFollowUpState ContextBuilder completion_routing focused root_swiftpm routine 9 regression retain 0.000000 stable retain 0 committed snapshot response routing and fail-closed follow-up state
root/RepoPromptTests.ContextBuilderMCPProgressTimelineTests/testRunMCPPlanOrQuestionReportsProductionPhaseSequenceThroughFinalization root Tests/RepoPromptTests/ContextBuilder/ContextBuilderMCPProgressTimelineTests.swift RepoPromptTests.ContextBuilderMCPProgressTimelineTests testRunMCPPlanOrQuestionReportsProductionPhaseSequenceThroughFinalization ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.229000 unreviewed retain_pending_review 0 initial census source line 166
root/RepoPromptTests.ContextBuilderMCPProgressTimelineTests/testSoftStageBoundEmitsOnceWithoutFailingTimeline root Tests/RepoPromptTests/ContextBuilder/ContextBuilderMCPProgressTimelineTests.swift RepoPromptTests.ContextBuilderMCPProgressTimelineTests testSoftStageBoundEmitsOnceWithoutFailingTimeline ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.001500 unreviewed retain_pending_review 0 initial census source line 139
root/RepoPromptTests.ContextBuilderMCPProgressTimelineTests/testSuspendingSinkPreservesEveryTimedTransitionWhenItReentersTimeline root Tests/RepoPromptTests/ContextBuilder/ContextBuilderMCPProgressTimelineTests.swift RepoPromptTests.ContextBuilderMCPProgressTimelineTests testSuspendingSinkPreservesEveryTimedTransitionWhenItReentersTimeline ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.000500 unreviewed retain_pending_review 0 initial census source line 66
root/RepoPromptTests.ContextBuilderMCPProgressTimelineTests/testTimelineEmitsTimedTransitionsAndUsesCurrentSubphaseForHeartbeat root Tests/RepoPromptTests/ContextBuilder/ContextBuilderMCPProgressTimelineTests.swift RepoPromptTests.ContextBuilderMCPProgressTimelineTests testTimelineEmitsTimedTransitionsAndUsesCurrentSubphaseForHeartbeat ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.000500 unreviewed retain_pending_review 0 initial census source line 28
root/RepoPromptTests.ContextBuilderMCPProgressTimelineTests/testTypedPromptResolverEnforcesPrecedenceAndReservedMarkupGrammar root Tests/RepoPromptTests/ContextBuilder/ContextBuilderMCPProgressTimelineTests.swift RepoPromptTests.ContextBuilderMCPProgressTimelineTests testTypedPromptResolverEnforcesPrecedenceAndReservedMarkupGrammar ContextBuilder context_builder.typed_prompt.resolution prompt_precedence,lazy_sanitization,reserved_markup parser_boundary root_swiftpm routine 16 Table assertions prove committed-prompt precedence, exact lowercase sibling removal, quote-aware attribute rejection, malformed reserved-markup rejection, and caller fallback into typed generation. Malformed or hidden discovery guidance could reach typed generation, or valid committed prompts could be rejected. 0.001000 test_case retain 0 request-local prompt authority and fail-closed reserved-markup grammar
root/RepoPromptTests.ContextBuilderModelStartupSelectionTests/testCachedCLIFlagIsNotReadyUntilCurrentProcessVerification root Tests/RepoPromptTests/ContextBuilder/ContextBuilderModelStartupSelectionTests.swift RepoPromptTests.ContextBuilderModelStartupSelectionTests testCachedCLIFlagIsNotReadyUntilCurrentProcessVerification ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.001000 unreviewed retain_pending_review 0 initial census source line 307
root/RepoPromptTests.ContextBuilderModelStartupSelectionTests/testCursorStartupReadinessJoinsRunningPollWithoutDynamicMetadata root Tests/RepoPromptTests/ContextBuilder/ContextBuilderModelStartupSelectionTests.swift RepoPromptTests.ContextBuilderModelStartupSelectionTests testCursorStartupReadinessJoinsRunningPollWithoutDynamicMetadata ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.002000 unreviewed retain_pending_review 0 initial census source line 259
root/RepoPromptTests.ContextBuilderModelStartupSelectionTests/testDynamicPersistedSelectionSurvivesAfterACPDiscovery root Tests/RepoPromptTests/ContextBuilder/ContextBuilderModelStartupSelectionTests.swift RepoPromptTests.ContextBuilderModelStartupSelectionTests testDynamicPersistedSelectionSurvivesAfterACPDiscovery ContextBuilder unreviewed unreviewed root_swiftpm routine 1 unreviewed unreviewed 0.001000 unreviewed retain_pending_review 0 initial census source line 145
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,102 @@ private struct ContextBuilderToolResult: Codable {

enum ContextBuilderResponseDisposition {
case contextOnly
case generate(HeadlessMode)
case generate(HeadlessMode, prompt: String)
case failed(String)
}

enum ContextBuilderTypedPromptResolver {
private static let openingTag = "<discovery_agent-guidelines>"
private static let closingTag = "</discovery_agent-guidelines>"
private static let reservedName = "discovery_agent-guidelines"

static func resolve(
effectivePrompt: String,
usedAgentOutputAsPrompt: Bool,
callerInstructions: String
) -> String? {
if !usedAgentOutputAsPrompt,
!effectivePrompt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
{
return effectivePrompt
}
return sanitizeCallerInstructions(callerInstructions)
}

private static func sanitizeCallerInstructions(_ instructions: String) -> String? {
var sanitized = ""
var cursor = instructions.startIndex
var insideGuidelines = false

while cursor < instructions.endIndex {
guard instructions[cursor] == "<" else {
if !insideGuidelines {
sanitized.append(instructions[cursor])
}
cursor = instructions.index(after: cursor)
continue
}

guard let closingAngle = closingAngleIndex(in: instructions, after: cursor) else {
let remainder = instructions[cursor...]
guard remainder.range(of: reservedName, options: .caseInsensitive) == nil else {
return nil
}
if !insideGuidelines {
sanitized.append(contentsOf: remainder)
}
break
}

let afterTag = instructions.index(after: closingAngle)
let tag = instructions[cursor ..< afterTag]
if tag == openingTag {
guard !insideGuidelines else { return nil }
insideGuidelines = true
} else if tag == closingTag {
guard insideGuidelines else { return nil }
insideGuidelines = false
} else {
// Quote-aware tokenization keeps reserved markup inside attributes from becoming removable blocks.
guard tag.range(of: reservedName, options: .caseInsensitive) == nil else {
return nil
}
if !insideGuidelines {
sanitized.append(contentsOf: tag)
}
}
cursor = afterTag
}

guard !insideGuidelines else { return nil }
let result = sanitized.trimmingCharacters(in: .whitespacesAndNewlines)
return result.isEmpty ? nil : result
}

private static func closingAngleIndex(
in instructions: String,
after openingAngle: String.Index
) -> String.Index? {
var cursor = instructions.index(after: openingAngle)
var activeQuote: Character?

while cursor < instructions.endIndex {
let character = instructions[cursor]
if let quote = activeQuote {
if character == quote {
activeQuote = nil
}
} else if character == "\"" || character == "'" {
activeQuote = character
} else if character == ">" {
return cursor
}
cursor = instructions.index(after: cursor)
}
return nil
}
}

@MainActor
final class MCPContextBuilderToolProvider: MCPWindowToolProviding {
let group: MCPWindowToolGroup = .contextBuilder
Expand Down Expand Up @@ -444,15 +536,18 @@ final class MCPContextBuilderToolProvider: MCPWindowToolProviding {
responseType: responseType,
terminalDisposition: snapshot.terminalDisposition,
usedAgentOutputAsPrompt: snapshot.usedAgentOutputAsPrompt,
effectivePrompt: effectivePrompt
effectivePrompt: effectivePrompt,
callerInstructions: instructions
)
var resultPrompt = effectivePrompt

switch responseDisposition {
case .contextOnly:
break
case let .failed(message):
throw MCPError.internalError(message)
case let .generate(mode):
case let .generate(mode, prompt):
resultPrompt = prompt
try Task.checkCancellation()
let finalReviewAuthorization: ContextBuilderFinalReviewAuthorization?
if mode == .review, let workspaceContext {
Expand Down Expand Up @@ -543,7 +638,7 @@ final class MCPContextBuilderToolProvider: MCPWindowToolProviding {
tabResolution.agentModeSessionID,
tabResolution.agentModeRunID,
mode,
effectivePrompt,
prompt,
sel,
lookupContext,
tabResolution.reviewGitContext,
Expand Down Expand Up @@ -586,7 +681,7 @@ final class MCPContextBuilderToolProvider: MCPWindowToolProviding {
ContextBuilderToolResult(
tabID: resultTab.id.uuidString,
status: status,
prompt: effectivePrompt,
prompt: resultPrompt,
fileCount: fileCount,
totalTokens: normalizedTokens,
userTotalTokens: userTotalTokens,
Expand Down Expand Up @@ -652,28 +747,28 @@ final class MCPContextBuilderToolProvider: MCPWindowToolProviding {
responseType: ContextBuilderResponseType?,
terminalDisposition: ContextBuilderRunTerminalOutcome,
usedAgentOutputAsPrompt: Bool,
effectivePrompt: String
effectivePrompt: String,
callerInstructions: String
) -> ContextBuilderResponseDisposition {
guard responseType?.wantsResponse == true else { return .contextOnly }

switch terminalDisposition {
case .cancelled, .failed:
return .contextOnly
case .completed:
guard !usedAgentOutputAsPrompt else {
return .failed(
"Context Builder completed without a typed direct response for the requested \(responseType?.rawValue ?? "response")"
)
}
guard !effectivePrompt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
guard let prompt = ContextBuilderTypedPromptResolver.resolve(
effectivePrompt: effectivePrompt,
usedAgentOutputAsPrompt: usedAgentOutputAsPrompt,
callerInstructions: callerInstructions
) else {
return .failed(
"Context Builder completed without a prompt for the requested \(responseType?.rawValue ?? "response")"
)
}
guard let mode = responseType?.headlessMode else {
return .failed("Context Builder requested response mode is unavailable")
}
return .generate(mode)
return .generate(mode, prompt: prompt)
}
}

Expand Down
Loading
Loading