Repository navigation
fix(marketing): send Android visitors to Google Play #24720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
75c8774
a2975ad
7502fd9
31bfffb
53bb0c5
d08e7ed
0384e19
9cf1f10
d57b784
3789f8a
fd1f44a
f34a7f3
3f004d7
dd69556
cef86d7
76a5e04
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ import { | |
| latestProviderTurnForAttempt, | ||
| type OrchestrationV2DomainEvent, | ||
| type OrchestrationV2ExecutionNode, | ||
| type OrchestrationV2ContextHandoff, | ||
| type OrchestrationV2ProviderThread, | ||
| type OrchestrationV2Run, | ||
| type OrchestrationV2RunAttempt, | ||
|
|
@@ -282,14 +283,7 @@ export const layer: Layer.Layer< | |
| source.id === handoff.targetRunId && | ||
| (source.status === "failed" || | ||
| source.status === "interrupted" || | ||
| (source.status === "completed" && | ||
| handoff.delivery === undefined && | ||
| projection.messages.some( | ||
| (message) => | ||
| message.id === source.userMessageId && | ||
| message.attachments.length === 0 && | ||
| message.text.trim().toLowerCase() === "/compact", | ||
| ))), | ||
| (source.status === "completed" && handoff.delivery === undefined)), | ||
| ))), | ||
| ); | ||
| const nativeForkTransfer = projection.contextTransfers.find( | ||
|
|
@@ -1107,10 +1101,15 @@ export const layer: Layer.Layer< | |
| ) | ||
| .map((source) => source.id), | ||
| ); | ||
| let turnStartHistory: ReadonlyArray<OrchestrationV2TurnItem> | undefined; | ||
| const loadTurnStartHistory = Effect.gen(function* () { | ||
| turnStartHistory ??= yield* projectionStore.getTurnStartHistory(input.threadId); | ||
| return turnStartHistory; | ||
| }); | ||
| // Legacy attempts lack native ids; exclude recovered attachments absent from the replacement thread. | ||
| const nativeContextEstimate = Effect.gen(function* () { | ||
| return sameNativeThread | ||
| ? (yield* projectionStore.getTurnStartHistory(input.threadId)).reduce((sum, item) => { | ||
| ? (yield* loadTurnStartHistory).reduce((sum, item) => { | ||
| if ( | ||
| item.runId === run.id || | ||
| (item.runId !== null && | ||
|
|
@@ -1145,6 +1144,40 @@ export const layer: Layer.Layer< | |
| ...runningProviderThread, | ||
| contextUsage: handoffUsage, | ||
| }; | ||
| const nativeThreadHasTurns = | ||
| nativeInputRunIds.size > 0 || | ||
| (legacyInputRunIds.size > 0 && | ||
| sameNativeThread && | ||
| !acceptedAttempts.some((source) => source.nativeThreadId !== undefined)); | ||
| // A turn in this native thread is not enough: another provider's handoff | ||
| // may contain work the native transcript has never seen. | ||
| const itemIsInNativeThread = (item: { | ||
| readonly threadId: string; | ||
| readonly providerThreadId: string | null; | ||
| readonly runId: string | null; | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
| }) => | ||
| item.threadId === projection.thread.id && | ||
| item.providerThreadId === providerThread.id && | ||
| item.runId !== null && | ||
| nativeInputRunIds.has(item.runId); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔎 Supported by static analysis
Exclude
🐛 Suggested fix
With this change, a handoff that contains an interrupt result is no longer deferrable. On an over-budget turn, it fails with
|
||
| const handoffIsAlreadyInNativeThread = ( | ||
| handoff: OrchestrationV2ContextHandoff, | ||
| sourceItemsById: ReadonlyMap<string, OrchestrationV2TurnItem>, | ||
| ) => { | ||
| const history = handoff.history; | ||
| if (!handoff.fromProviderThreadIds.includes(providerThread.id) || history === undefined) | ||
| return false; | ||
| const omittedItemIds = history.omittedItemIds ?? []; | ||
| return ( | ||
| (history.messages.length > 0 || omittedItemIds.length > 0) && | ||
| history.omittedItems === omittedItemIds.length && | ||
| history.messages.every(itemIsInNativeThread) && | ||
| omittedItemIds.every((itemId) => { | ||
| const item = sourceItemsById.get(itemId); | ||
| return item !== undefined && itemIsInNativeThread(item); | ||
| }) | ||
| ); | ||
| }; | ||
| const missedRuns = projection.runs.filter( | ||
| (source) => | ||
| source.ordinal < run.ordinal && | ||
|
|
@@ -1187,9 +1220,25 @@ export const layer: Layer.Layer< | |
| createdAt: yield* DateTime.now, | ||
| }), | ||
| ]; | ||
| const handoffsToDeliver = [...effectiveHandoffs, ...retryHandoff]; | ||
| const canDeferHandoff = | ||
| sameNativeThread && | ||
| nativeThreadHasTurns && | ||
| missedItems.length === 0 && | ||
| handoffsToDeliver.length > 0; | ||
| const sourceItemsById = | ||
| canDeferHandoff && | ||
| handoffsToDeliver.some((handoff) => (handoff.history?.omittedItems ?? 0) > 0) | ||
| ? new Map((yield* loadTurnStartHistory).map((item) => [item.id, item] as const)) | ||
| : new Map<string, OrchestrationV2TurnItem>(); | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
| const delivery = yield* deliverContextHandoffs({ | ||
| handoffs: [...effectiveHandoffs, ...retryHandoff], | ||
| handoffs: handoffsToDeliver, | ||
| deferInline: compact, | ||
| deferIfOverBudget: | ||
| canDeferHandoff && | ||
| handoffsToDeliver.every((handoff) => | ||
| handoffIsAlreadyInNativeThread(handoff, sourceItemsById), | ||
| ), | ||
| providerThread: runningProviderThread, | ||
| budget: Effect.gen(function* () { | ||
| return handoffBudget({ | ||
|
|
@@ -1313,11 +1362,7 @@ export const layer: Layer.Layer< | |
| .filter((turn) => turn.providerThreadId === providerThread.id) | ||
| .map((turn) => turn.ordinal), | ||
| ) + 1, | ||
| nativeThreadHasTurns: | ||
| nativeInputRunIds.size > 0 || | ||
| (legacyInputRunIds.size > 0 && | ||
| sameNativeThread && | ||
| !acceptedAttempts.some((source) => source.nativeThreadId !== undefined)), | ||
| nativeThreadHasTurns, | ||
| shouldStartProviderTurn: runControls.shouldStartProviderTurn, | ||
| shouldFinalizeRun: runControls.shouldFinalizeRun, | ||
| hasUnpairedRunInterruptRequest: runControls.hasUnpairedRunInterruptRequest, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Show the Google Play icon on both Android buttons.
When this branch runs,
data-platformbecomesandroid. The icon rules at lines 674–677 do not match Android, so both buttons show text without an icon. Add a Google Play icon to each button and a matching Android display rule to produce the intended CTA.