Skip to content

Commit 8119814

Browse files
chore(v2): merge main into the V2 branch (second batch, 18 commits)
Merges origin/main (95030dc), the 18 commits after a21b42c. Conflict resolutions that change V2 code: - AgentAwarenessRelay: keep V2's publish worker and drain, add main's unlinked backoff and requestCatchUp (cloud/http now wakes it). Main's catch-up tests are rewritten against V2's relay harness. - server.ts: keep V2's MCP route wiring, add main's untracedRequestsLayer last. - Sidebar: V2's sortSettledThreadsForSidebar is dropped for main's shared sortSettledThreads (client-runtime, same resolver); its tests moved there and use V2's latestRun. - #13767 cache encode shortcut: not taken. It relies on V1 shells being in encoded form; V2 shells hold DateTime values and V2 already encodes cooperatively (mobile shell-cache-encoding, #12117). Main's IndexedDB "abort" listener fix is kept. - #13764 queued sends: main's client-side QueuedMessageSender and queuedMessageStore stay deleted; V2 queues runs on the server (Orchestrator startNextQueuedRun), which already drains unopened threads. - #13765 / #13756: V1 ProjectionSnapshotQuery and ProjectionPipeline keep V2's versions except main's skip of empty attachment-cleanup spans. - Docs: composer.md keeps V2's queue text; keybindings.md adds usagePageOpen to V2's list. - Deleted-in-V2 V1 files that main modified stay deleted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2 parents d07c035 + 95030dc commit 8119814

57 files changed

Lines changed: 1992 additions & 697 deletions

Some content is hidden

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

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -749,10 +749,12 @@ export const ThreadDetailScreen = memo(function ThreadDetailScreen(props: Thread
749749
(model) => model.slug === props.selectedThread.modelSelection.model,
750750
);
751751
const workspaceContentWidth = useWorkspaceContentWidth();
752+
// Clearing animated width can retain the unfolded width after Android resumes folded.
753+
// Assign both layouts explicitly so the dock always follows its current parent.
752754
const composerWidthStyle = useAnimatedStyle(() =>
753755
isSplitLayout && workspaceContentWidth !== null
754-
? { width: workspaceContentWidth.value, right: undefined }
755-
: { width: undefined, right: 0 },
756+
? { width: workspaceContentWidth.value }
757+
: { width: "100%" },
756758
);
757759
const selectedInstanceId = props.selectedThread.modelSelection.instanceId;
758760
useStreamingHaptics(props.selectedThread.id, props.selectedThreadFeed);
@@ -1095,7 +1097,7 @@ export const ThreadDetailScreen = memo(function ThreadDetailScreen(props: Thread
10951097
<Animated.View
10961098
layout={COMPOSER_LAYOUT_TRANSITION}
10971099
pointerEvents="box-none"
1098-
style={[{ position: "absolute", bottom: 0, left: 0, right: 0 }, composerWidthStyle]}
1100+
style={[{ position: "absolute", bottom: 0, left: 0 }, composerWidthStyle]}
10991101
>
11001102
{/* No paddingTop here: the overlay's measured height becomes the
11011103
list's bottom inset, so any padding above the pill/composer

‎apps/mobile/src/features/threads/threadListV2.ts‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
sortActiveThreadsByOrderKey,
1616
resolveSettledThreadTimestamp,
1717
sortPinnedThreadsByOrderKey,
18+
sortSettledThreads,
1819
} from "@t3tools/client-runtime/state/thread-sort";
1920
import type { EnvironmentId, ProjectId } from "@t3tools/contracts";
2021

@@ -671,11 +672,7 @@ export function buildThreadListV2Items(input: {
671672
: orderedSnoozed.filter(
672673
(thread) => `${thread.environmentId}:${thread.id}` === selectedThreadKey,
673674
);
674-
const orderedSettled = [...settled].sort(
675-
(left, right) =>
676-
parseTimestampMs(resolveSettledThreadTimestamp(right) ?? "") -
677-
parseTimestampMs(resolveSettledThreadTimestamp(left) ?? ""),
678-
);
675+
const orderedSettled = sortSettledThreads(settled);
679676
const settledLimit = input.settledLimit ?? Number.POSITIVE_INFINITY;
680677
const pagedSettled =
681678
orderedSettled.length > settledLimit ? orderedSettled.slice(0, settledLimit) : orderedSettled;

‎apps/server/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"@opencode-ai/sdk": "^1.3.15",
3838
"diff": "8.0.3",
3939
"effect": "catalog:",
40-
"node-pty": "^1.1.0",
40+
"node-pty": "^1.2.0-beta.15",
4141
"stream-chain": "^4.2.5",
4242
"stream-json": "3.6.0",
4343
"yaml": "catalog:",

‎apps/server/src/cli/trace.ts‎

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ import * as Effect from "effect/Effect";
1313
import * as FileSystem from "effect/FileSystem";
1414
import * as Option from "effect/Option";
1515
import * as Schema from "effect/Schema";
16-
import * as Stream from "effect/Stream";
1716
import { Command, Flag } from "effect/unstable/cli";
1817

1918
import * as ServerConfig from "../config.ts";
20-
import { toRotatedTracePaths, TraceFileReadError } from "../diagnostics/TraceDiagnostics.ts";
19+
import { streamTraceFileLines, toRotatedTracePaths } from "../diagnostics/TraceDiagnostics.ts";
2120
import { resolveBaseDir } from "../os-jank.ts";
2221
import { baseDirFlag, DurationFromString, traceFileConfig, traceMaxFilesConfig } from "./config.ts";
2322

@@ -187,27 +186,9 @@ const traceSummaryCommand = Command.make("summary", {
187186
? (yield* Clock.currentTimeMillis) - Duration.toMillis(flags.since.value)
188187
: undefined;
189188
const summarizer = makeTraceSpanSummary(sinceMs);
190-
// Stream each file so only one chunk of text is in memory at a time.
191189
yield* Effect.forEach(
192190
toRotatedTracePaths(traceFilePath, yield* traceMaxFilesConfig),
193-
(path) =>
194-
fs.stream(path).pipe(
195-
Stream.decodeText,
196-
Stream.splitLines,
197-
Stream.runForEachArray((lines) => Effect.sync(() => lines.forEach(summarizer.addLine))),
198-
Effect.catchTags({
199-
PlatformError: (cause) =>
200-
cause.reason._tag === "NotFound"
201-
? Effect.void
202-
: Effect.fail(
203-
new TraceFileReadError({
204-
traceFilePath: path,
205-
causeTag: cause.reason._tag,
206-
cause,
207-
}),
208-
),
209-
}),
210-
),
191+
(path) => streamTraceFileLines(fs, path, summarizer.addLine),
211192
{ discard: true },
212193
);
213194
const summary = summarizer.finish();

‎apps/server/src/cloud/http.test.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
type ServiceUpdateRecord,
3434
} from "./serviceProtocol.ts";
3535
import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
36+
import * as AgentAwarenessRelay from "../relay/AgentAwarenessRelay.ts";
3637
import { CLOUD_CLI_DESIRED_LINK_SECRET } from "./CliState.ts";
3738
import * as CliTokenManager from "./CliTokenManager.ts";
3839
import {
@@ -81,6 +82,13 @@ const storeFailure = (tag: "AlreadyExists" | "PermissionDenied") =>
8182
});
8283

8384
const unusedSecretStoreOperation = () => Effect.die("unused secret-store operation");
85+
// Linking wakes the awareness relay; these tests do not run it.
86+
const idleAwarenessRelay = AgentAwarenessRelay.AgentAwarenessRelay.of({
87+
publishThread: () => Effect.void,
88+
drain: Effect.void,
89+
requestCatchUp: () => Effect.void,
90+
start: () => Effect.void,
91+
});
8492
const decodeManagedTunnelRecoveryRegistration = Schema.decodeUnknownEffect(
8593
Schema.fromJsonString(RelayManagedEndpointRecoveryRegistrationRequest),
8694
);
@@ -262,6 +270,7 @@ describe("reconcileDesiredCloudLink", () => {
262270
HttpClient.HttpClient,
263271
HttpClient.make(() => unusedSecretStoreOperation()),
264272
),
273+
Effect.provideService(AgentAwarenessRelay.AgentAwarenessRelay, idleAwarenessRelay),
265274
Effect.provide(NodeServices.layer),
266275
),
267276
);
@@ -375,6 +384,7 @@ describe("releaseManagedTunnelOnShutdown", () => {
375384
<A, E, R>(effect: Effect.Effect<A, E, R>) =>
376385
effect.pipe(
377386
Effect.provideService(ServerSecretStore.ServerSecretStore, harness.store),
387+
Effect.provideService(AgentAwarenessRelay.AgentAwarenessRelay, idleAwarenessRelay),
378388
Effect.provideService(
379389
ServerEnvironment.ServerEnvironment,
380390
ServerEnvironment.ServerEnvironment.of({

‎apps/server/src/cloud/http.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
6969
import { requireEnvironmentScope } from "../auth/http.ts";
7070
import * as ServerConfig from "../config.ts";
7171
import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
72+
import * as AgentAwarenessRelay from "../relay/AgentAwarenessRelay.ts";
7273
import * as ManagedEndpointRuntime from "./ManagedEndpointRuntime.ts";
7374
import {
7475
SERVICE_STATE_FILE,
@@ -411,6 +412,7 @@ interface CloudHttpDependencies {
411412
readonly environmentAuth: EnvironmentAuth.EnvironmentAuth["Service"];
412413
readonly cliTokenManager: CliTokenManager.CloudCliTokenManager["Service"];
413414
readonly httpClient: HttpClient.HttpClient;
415+
readonly awarenessRelay: AgentAwarenessRelay.AgentAwarenessRelay["Service"];
414416
}
415417

416418
const cloudHttpDependencies = Effect.gen(function* () {
@@ -421,6 +423,7 @@ const cloudHttpDependencies = Effect.gen(function* () {
421423
environmentAuth: yield* EnvironmentAuth.EnvironmentAuth,
422424
cliTokenManager: yield* CliTokenManager.CloudCliTokenManager,
423425
httpClient: yield* HttpClient.HttpClient,
426+
awarenessRelay: yield* AgentAwarenessRelay.AgentAwarenessRelay,
424427
} satisfies CloudHttpDependencies;
425428
});
426429

@@ -668,6 +671,7 @@ const applyCloudRelayConfig = Effect.fn("environment.cloud.applyRelayConfig")(fu
668671
CLOUD_MINT_PUBLIC_KEY,
669672
stringToBytes(payload.cloudMintPublicKey),
670673
);
674+
yield* dependencies.awarenessRelay.requestCatchUp();
671675
if (payload.endpointRuntime) {
672676
const endpointRuntimeJson = yield* encodeEndpointRuntimeConfigJson(payload.endpointRuntime);
673677
yield* dependencies.secrets.set(
@@ -1349,6 +1353,7 @@ const cloudPreferencesHandler = Effect.fn("environment.cloud.preferences")(
13491353
PUBLISH_AGENT_ACTIVITY_SECRET,
13501354
stringToBytes(String(payload.publishAgentActivity)),
13511355
);
1356+
yield* dependencies.awarenessRelay.requestCatchUp();
13521357
return yield* readCloudLinkState(dependencies);
13531358
},
13541359
Effect.catchIf(

0 commit comments

Comments
 (0)