Skip to content

Commit 9ced079

Browse files
shivamhwpclaude
andcommitted
chore: merge main into narrow window fix
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2 parents ab34a06 + e21347b commit 9ced079

158 files changed

Lines changed: 6484 additions & 901 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,41 @@ jobs:
192192
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
193193
run: node apps/desktop/scripts/verify-preload-bundle.mjs
194194

195+
# Browser tests run source modules; this boots the production chunk graph. The
196+
# desktop build above already produced apps/web/dist, and the client waits about
197+
# 15s for a session that never comes, so this runs here instead of in Test Web,
198+
# which gates the whole run. Chromium comes from the cache Test Web shares.
199+
- name: Resolve Playwright version
200+
id: playwright_version
201+
if: ${{ !cancelled() && steps.build.outcome == 'success' && needs.changes.outputs.test_web != 'false' }}
202+
run: echo "version=$(node -p "require('./apps/web/node_modules/playwright/package.json').version")" >> "$GITHUB_OUTPUT"
203+
204+
- name: Cache Chromium
205+
id: chromium_cache
206+
if: ${{ !cancelled() && steps.build.outcome == 'success' && needs.changes.outputs.test_web != 'false' }}
207+
uses: actions/cache@v6
208+
with:
209+
path: ~/.cache/ms-playwright
210+
key: playwright-chromium-shell-${{ runner.os }}-${{ runner.arch }}-${{ steps.playwright_version.outputs.version }}
211+
212+
- name: Install Chromium for the built web client boot check
213+
if: ${{ !cancelled() && steps.build.outcome == 'success' && needs.changes.outputs.test_web != 'false' && steps.chromium_cache.outputs.cache-hit != 'true' }}
214+
run: node apps/web/node_modules/playwright/cli.js install --only-shell chromium
215+
216+
- name: Install missing Chromium system dependencies
217+
if: ${{ !cancelled() && steps.build.outcome == 'success' && needs.changes.outputs.test_web != 'false' }}
218+
run: |
219+
browser=$(find "$HOME/.cache/ms-playwright" -name chrome-headless-shell -type f -print -quit)
220+
test -n "$browser"
221+
if ldd "$browser" | grep '=> not found'; then
222+
node apps/web/node_modules/playwright/cli.js install-deps chromium
223+
fi
224+
225+
- name: Boot the built web client
226+
if: ${{ !cancelled() && steps.build.outcome == 'success' && needs.changes.outputs.test_web != 'false' }}
227+
timeout-minutes: 3
228+
run: node apps/web/scripts/verify-built-boot.ts
229+
195230
- name: Typecheck
196231
if: ${{ !cancelled() && steps.typecheck.outcome == 'success' }}
197232
timeout-minutes: 5
@@ -340,12 +375,6 @@ jobs:
340375
- name: Test
341376
run: vp run --filter @t3tools/web test
342377

343-
# Browser tests run source modules; this boots the production chunk graph.
344-
- name: Boot the built web client
345-
run: |
346-
vp run --filter @t3tools/web build
347-
node apps/web/scripts/verify-built-boot.ts
348-
349378
# apps/server sets `fileParallelism: false`, so its test files run strictly
350379
# one at a time. Sharding spreads them over separate runners instead of
351380
# separate workers, so no two server test files ever share a machine and the

‎apps/desktop/src/app/DesktopApp.test.ts‎

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { assert, it } from "@effect/vitest";
22
import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
33
import * as Deferred from "effect/Deferred";
44
import * as Effect from "effect/Effect";
5+
import * as Exit from "effect/Exit";
56
import * as Layer from "effect/Layer";
67
import * as FileSystem from "effect/FileSystem";
78
import * as Path from "effect/Path";
@@ -69,14 +70,19 @@ import * as ElectronWindow from "../electron/ElectronWindow.ts";
6970
import * as MacPermissions from "../permissions/MacPermissions.ts";
7071
import * as PreviewManager from "../preview/Manager.ts";
7172

72-
it.effect(
73-
"starts the backend and window while Linux registration is stalled and cancels it on shutdown",
74-
() =>
73+
it.effect.each([false, true])(
74+
"stops backends together before flushing history and cancels Linux registration, stop defect=%s",
75+
(stopDefect) =>
7576
Effect.gen(function* () {
7677
const registrationStarted = yield* Deferred.make<void>();
7778
const releaseRegistration = yield* Deferred.make<void>();
7879
const registrationStopped = yield* Deferred.make<void>();
7980
const windowStarted = yield* Deferred.make<void>();
81+
const firstStopStarted = yield* Deferred.make<void>();
82+
const secondStopStarted = yield* Deferred.make<void>();
83+
const releaseStops = yield* Deferred.make<void>();
84+
const historyStarted = yield* Deferred.make<void>();
85+
const releaseHistory = yield* Deferred.make<void>();
8086
const quitting = yield* Ref.make(false);
8187
const events: string[] = [];
8288
const record = (event: string) =>
@@ -169,7 +175,21 @@ it.effect(
169175
start: record("backend"),
170176
currentConfig: Effect.succeedSome({ preflightFailure: Option.none() }),
171177
}),
172-
list: Effect.succeed([]),
178+
list: Effect.succeed([
179+
{
180+
stop: () =>
181+
Deferred.succeed(firstStopStarted, undefined).pipe(
182+
Effect.andThen(Deferred.await(releaseStops)),
183+
Effect.andThen(stopDefect ? Effect.die("stop defect") : Effect.void),
184+
),
185+
},
186+
{
187+
stop: () =>
188+
Deferred.succeed(secondStopStarted, undefined).pipe(
189+
Effect.andThen(Deferred.await(releaseStops)),
190+
),
191+
},
192+
]),
173193
} as unknown as DesktopBackendPool.DesktopBackendPool["Service"]),
174194
Effect.provideService(DesktopServerExposure.DesktopServerExposure, {
175195
configureFromSettings: () => Effect.succeed({ mode: "local-only" }),
@@ -182,7 +202,10 @@ it.effect(
182202
start: Effect.void,
183203
} as unknown as DesktopAppActivation.DesktopAppActivation["Service"]),
184204
Effect.provideService(DesktopRendererHistory.DesktopRendererHistory, {
185-
shutdown: Effect.void,
205+
shutdown: Deferred.succeed(historyStarted, undefined).pipe(
206+
Effect.andThen(Deferred.await(releaseHistory)),
207+
Effect.asVoid,
208+
),
186209
} as DesktopRendererHistory.DesktopRendererHistory["Service"]),
187210
);
188211
const app = yield* runApp.pipe(
@@ -226,8 +249,19 @@ it.effect(
226249
yield* Deferred.await(windowStarted);
227250
assert.deepEqual(events, ["identity", "ready", "identity", "backend", "window"]);
228251
assert.isFalse(yield* Deferred.isDone(registrationStopped));
229-
yield* (yield* DesktopShutdown.DesktopShutdown).request;
230-
yield* Fiber.join(app);
252+
const shutdown = yield* DesktopShutdown.DesktopShutdown;
253+
yield* shutdown.request;
254+
yield* Deferred.await(firstStopStarted);
255+
yield* Deferred.await(secondStopStarted);
256+
assert.isFalse(yield* Deferred.isDone(historyStarted));
257+
assert.isFalse(yield* shutdown.isComplete);
258+
yield* Deferred.succeed(releaseStops, undefined);
259+
yield* Deferred.await(historyStarted);
260+
assert.isFalse(yield* shutdown.isComplete);
261+
yield* Deferred.succeed(releaseHistory, undefined);
262+
const result = yield* Fiber.await(app);
263+
assert.equal(Exit.isFailure(result), stopDefect);
264+
assert.isTrue(yield* shutdown.isComplete);
231265
assert.isTrue(yield* Deferred.isDone(registrationStopped));
232266
}).pipe(Effect.provide(DesktopShutdown.layer)),
233267
);

‎apps/desktop/src/app/DesktopApp.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ const scopedProgram = Effect.scoped(
363363
stopAllPoolInstances().pipe(
364364
Effect.ensuring(rendererHistory.shutdown),
365365
Effect.ensuring(shutdown.markComplete),
366+
Effect.withSpan("desktop.app.shutdown"),
366367
),
367368
);
368369

‎apps/desktop/src/backend/DesktopBackendManager.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,10 @@ const closeRun = (
312312
onNone: () => Effect.void,
313313
onSome: (fiber) => Fiber.await(fiber).pipe(Effect.asVoid),
314314
});
315-
const close = Scope.close(run.scope, Exit.void).pipe(Effect.andThen(waitForFiber));
315+
const close = Scope.close(run.scope, Exit.void).pipe(
316+
Effect.withSpan("desktop.backendInstance.closeScope"),
317+
Effect.andThen(waitForFiber.pipe(Effect.withSpan("desktop.backendInstance.awaitRun"))),
318+
);
316319
const timeout = options?.timeout;
317320

318321
if (!timeout) {
@@ -573,6 +576,7 @@ export const runBackendProcess = Effect.fn("runBackendProcess")(function* (
573576
yield* probeReadiness().pipe(Effect.repeat({ while: (ready) => !ready }), Effect.forkScoped);
574577

575578
const exit = yield* handle.exitCode.pipe(
579+
Effect.withSpan("desktop.backendProcess.awaitExit"),
576580
Effect.map((exitCode) => ({
577581
code: Option.some(exitCode),
578582
reason: `code=${exitCode}`,
@@ -608,6 +612,7 @@ export const runBackendProcess = Effect.fn("runBackendProcess")(function* (
608612
}).pipe(
609613
Effect.timeout(options.outputDrainTimeout ?? DEFAULT_BACKEND_OUTPUT_DRAIN_TIMEOUT),
610614
Effect.ignore,
615+
Effect.withSpan("desktop.backendProcess.drainOutput"),
611616
);
612617
if (Exit.isFailure(exit)) {
613618
return yield* Effect.failCause(exit.cause);

‎apps/desktop/src/telemetry/DesktopRendererHistory.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,11 @@ const make = Effect.fn("DesktopRendererHistory.make")(function* () {
273273
accepting = false;
274274
for (const renderer of active.values()) renderer.removeListeners();
275275
active.clear();
276-
}).pipe(Effect.andThen(Fiber.interrupt(writerFiber)), Effect.asVoid);
276+
}).pipe(
277+
Effect.andThen(Fiber.interrupt(writerFiber)),
278+
Effect.asVoid,
279+
Effect.withSpan("desktop.rendererHistory.shutdown"),
280+
);
277281
yield* Effect.addFinalizer(() => shutdown);
278282

279283
return DesktopRendererHistory.of({ register, recordMetrics, shutdown });

‎apps/mobile/src/components/ui/Dialog.android.tsx‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
Text,
1111
useNativeState,
1212
} from "@expo/ui/jetpack-compose";
13+
import { verticalScroll } from "@expo/ui/jetpack-compose/modifiers";
1314

1415
import { useAppearancePreferences } from "../../features/settings/appearance/AppearancePreferencesProvider";
1516
import { useScaledTextRole } from "../../features/settings/appearance/useScaledTextRole";
@@ -252,7 +253,9 @@ function DialogPresentation(props: DialogPresentationProps) {
252253
</AlertDialog.Text>
253254
) : props.request.message ? (
254255
<AlertDialog.Text>
255-
<Text style={bodyTypography}>{props.request.message}</Text>
256+
<Text modifiers={[verticalScroll()]} style={bodyTypography}>
257+
{props.request.message}
258+
</Text>
256259
</AlertDialog.Text>
257260
) : null}
258261
{actions.length > 1 ? (

‎apps/mobile/src/features/settings/ThreadSettingsSections.tsx‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ function BetaSettingsSection() {
268268
</SettingsSection>
269269
<Text className="px-2 text-sm text-foreground-muted">
270270
Let agents on a machine work on your other machines, and theirs on it. Turn it on for each
271-
machine. While it is off, nothing new starts over a link; tasks already running finish.
271+
machine. While off, nothing new starts over a link; running tasks finish.
272272
</Text>
273273
</View>
274274
);
@@ -306,9 +306,8 @@ function ToolCallVisibilitySection() {
306306
))}
307307
</SettingsSection>
308308
<Text className="px-2 text-sm text-foreground-muted">
309-
Hide the commands, edits, and searches agents run, so a thread reads like a conversation.
310-
Hide successful keeps calls that fail. Zen also hides the agent&apos;s in-between messages
311-
and shows only final answers. Approvals, questions, and errors always show.
309+
Hide the commands, edits, and searches agents run. Hide successful keeps failures. Zen shows
310+
only final answers. Approvals, questions, and errors always show.
312311
</Text>
313312
</View>
314313
);

‎apps/mobile/src/features/settings/components/DatabaseStorageNotice.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function DatabaseStorageNotice({
3131
return (
3232
<SettingsSection
3333
title="Reclaim database space"
34-
footer="Old history is cleaned up automatically, but these databases still hold the freed space. Run the command on each machine to return it to disk; it pauses and restarts the background service. Quit the desktop app first if it hosts the environment."
34+
footer="These databases still hold space freed by history cleanup. Run the command on each machine to reclaim it. It restarts the background service. Quit the desktop app first if it hosts the environment."
3535
>
3636
{visible.map(({ environmentId, status }) => (
3737
<SettingsActionRow

‎apps/mobile/src/features/threads/ThreadDetailScreen.tsx‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ import type { ThreadContentPresentation } from "./threadContentPresentation";
208208
import { resolveThreadFeedSubmissionAnchor } from "./thread-feed-live-follow";
209209
import { useOpenLinkedThread } from "./useOpenLinkedThread";
210210
import { useGlobalVoiceInput } from "../voice-input/VoiceInputProvider";
211+
import type { OrchestrationV2ProviderSession } from "@t3tools/contracts";
211212

212213
export interface ThreadDetailScreenProps {
213214
readonly header?: React.ReactNode;
@@ -240,6 +241,7 @@ export interface ThreadDetailScreenProps {
240241
readonly activeWorkStartedAt: string | null;
241242
readonly runlessWorkActive?: boolean;
242243
readonly providerSubagentStatus?: ProviderSubagentStatus | null;
244+
readonly extensionStatuses?: NonNullable<OrchestrationV2ProviderSession["extensionStatuses"]>;
243245
readonly isCompacting: boolean;
244246
readonly creationState:
245247
| {
@@ -735,6 +737,13 @@ function ThreadDetailBody(
735737
const pendingBackgroundWork = presentPendingBackgroundWork(
736738
props.selectedThread.pendingBackgroundTasks,
737739
);
740+
const extensionStatus = props.extensionStatuses?.length
741+
? {
742+
label: `Pi · ${props.extensionStatuses.map((status) => status.text).join(" · ")}`,
743+
onPress: () =>
744+
Dialog.alert("Pi", props.extensionStatuses?.map((status) => status.text).join("\n\n")),
745+
}
746+
: null;
738747
const floatingStatus = ((): FloatingWorkingStatus | null => {
739748
const connectionStatus = connectionFloatingStatus({
740749
connectionError: connectionRetrying ? retryConnectionError : props.connectionError,
@@ -864,6 +873,9 @@ function ThreadDetailBody(
864873
accessibilityLabel: `${goal.title}: ${goal.objective}`,
865874
};
866875
}
876+
if (extensionStatus !== null && contentPresentationKind === "ready") {
877+
return { kind: "extension", ...extensionStatus };
878+
}
867879
return null;
868880
})();
869881
const showWorkingControl = floatingStatus !== null;
@@ -1602,6 +1614,16 @@ function ThreadDetailBody(
16021614
<FloatingWorkingControl
16031615
colorScheme={isDarkMode ? "dark" : "light"}
16041616
status={floatingStatus}
1617+
extensionStatus={
1618+
floatingStatus !== null &&
1619+
(floatingStatus.kind === "working" ||
1620+
floatingStatus.kind === "compacting" ||
1621+
floatingStatus.kind === "background" ||
1622+
floatingStatus.kind === "goal" ||
1623+
floatingStatus.kind === "lifecycle")
1624+
? extensionStatus
1625+
: null
1626+
}
16051627
lift={floatingControlLift}
16061628
devicePreview={
16071629
devicePreviews.length > 0

‎apps/mobile/src/features/threads/ThreadRouteScreen.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,7 @@ function ThreadRouteContent(
991991
}
992992
isCompacting={composer.isCompacting}
993993
runlessWorkActive={composer.runlessWorkActive}
994+
extensionStatuses={composer.extensionStatuses}
994995
providerSubagentStatus={composer.providerSubagentStatus}
995996
creationState={creationState}
996997
setupWorkingStartedAt={

0 commit comments

Comments
 (0)