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
6 changes: 6 additions & 0 deletions .changeset/built-transition-published-5799.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@object-ui/plugin-chatbot': patch
'@object-ui/app-shell': patch
---

The built-moment transition (#5799) now fires on auto-publish environments too: `detectBuiltAppPackage` reads the raw build envelope (`status:'drafted'` OR `'published'`, packageId + an `app` item), because an auto-publish posture rewrites apply_blueprint's envelope to `published` and the drafted-only `draftReview` lift never fired there — measured live on staging, where reopening a built conversation stayed on the full page.
7 changes: 7 additions & 0 deletions packages/app-shell/src/console/ai/AiChatPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import {
detectBuilderHandoff,
detectProposedChanges,
detectReplayOutcome,
detectBuiltAppPackage,
buildProgressFromDraftReview,
// The authoring/honest -> runtime message seam (objectui#4399 / PR #4416),
// consumed here one hop up from the plugin's own renderers (objectui#4437).
Expand Down Expand Up @@ -1886,6 +1887,12 @@ export function ChatPane({
const builtPackageId = useMemo(() => {
for (let i = messages.length - 1; i >= 0; i--) {
for (const tool of messages[i].toolInvocations ?? []) {
// Posture-independent: read the raw envelope, not draftReview — an
// auto-publish env rewrites the build envelope to status:'published'
// and the drafted-only lift never fires there (measured live on
// staging: reopening a built conversation stayed on the full page).
const pkg = detectBuiltAppPackage(tool.result);
if (pkg) return pkg;
const dr = tool.draftReview;
if (dr?.packageId && dr.items?.some((it) => it.type === 'app')) return dr.packageId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,16 @@ describe('AiChatPage — built-moment transition (objectui#5799)', () => {
});
});

it("an AUTO-PUBLISH environment's published envelope transitions too (the staging posture)", async () => {
serverTurns = JSON.parse(
JSON.stringify(BUILT_TURNS).replace('"status":"drafted"', '"status":"published"'),
);
renderPage();
await waitFor(() => expect(screen.getByTestId('studio-page')).toBeInTheDocument(), {
timeout: 4000,
});
});

it('a conversation with no whole-app build stays on the full page', async () => {
serverTurns = UNBUILT_TURNS;
renderPage();
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-chatbot/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ export {
detectProposedChanges,
detectReplayOutcome,
detectAuthoringVerdict,
detectBuiltAppPackage,
buildProgressFromDraftReview,
} from './mapMessages';
export type {
Expand Down
23 changes: 23 additions & 0 deletions packages/plugin-chatbot/src/mapMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,29 @@ export function detectAuthoringVerdict(
return { kind: 'drafted', ...packageId };
}

/**
* objectui#5799 — did this tool result finish a WHOLE-APP build, and for which
* package? Posture-independent on purpose: an auto-publish environment
* rewrites the apply_blueprint envelope to `status:'published'` (keeping
* `drafted[]` + `packageId`), so keying the built-moment transition on
* `draftReview` (drafted-only) missed every staging/cloud build — measured
* live: reopening a built conversation stayed on the full page.
*/
export function detectBuiltAppPackage(result: unknown): string | undefined {
const obj = parseResultEnvelope(result);
if (!obj) return undefined;
if (obj.status !== 'drafted' && obj.status !== 'published') return undefined;
const pkg = (obj as { packageId?: unknown }).packageId;
if (typeof pkg !== 'string' || !pkg) return undefined;
const drafted = (obj as { drafted?: unknown }).drafted;
const hasApp =
Array.isArray(drafted) &&
drafted.some(
(d) => d && typeof d === 'object' && (d as { type?: unknown }).type === 'app',
);
return hasApp ? pkg : undefined;
}

export function detectDraftResult(result: unknown): DraftReview | undefined {
const obj = parseResultEnvelope(result);
if (!obj || obj.status !== 'drafted') return undefined;
Expand Down
Loading