Unified · Split
| @@ -2602,6 +2602,9 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 2601 unmodified lines | |||
| 2602 | cwd: "/tmp/repo", | 2602 | cwd: "/tmp/repo", |
| 2603 | }, | 2603 | }, |
| 2604 | layers: { | 2604 | layers: { |
| 2605 | vcsDriver: { | ||
| 2606 | isInsideWorkTree: () => Effect.succeed(true), | ||
| 2607 | }, | ||
| 2605 | gitManager: { | 2608 | gitManager: { |
| 2606 | invalidateLocalStatus: () => Effect.void, | 2609 | invalidateLocalStatus: () => Effect.void, |
| 2607 | invalidateRemoteStatus: () => Effect.void, | 2610 | invalidateRemoteStatus: () => Effect.void, |
| @@ -2738,6 +2741,21 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 130 unmodified lines | |||
| 2738 | createRef: (input) => Effect.succeed({ refName: input.refName }), | 2741 | createRef: (input) => Effect.succeed({ refName: input.refName }), |
| 2739 | switchRef: (input) => Effect.succeed({ refName: input.refName }), | 2742 | switchRef: (input) => Effect.succeed({ refName: input.refName }), |
| 2740 | }, | 2743 | }, |
| 2744 | vcsStatusBroadcaster: { | ||
| 2745 | refreshStatus: () => | ||
| 2746 | Effect.succeed({ | ||
| 2747 | isRepo: true, | ||
| 2748 | hasPrimaryRemote: true, | ||
| 2749 | isDefaultRef: true, | ||
| 2750 | refName: "main", | ||
| 2751 | hasWorkingTreeChanges: false, | ||
| 2752 | workingTree: { files: [], insertions: 0, deletions: 0 }, | ||
| 2753 | hasUpstream: true, | ||
| 2754 | aheadCount: 0, | ||
| 2755 | behindCount: 0, | ||
| 2756 | pr: null, | ||
| 2757 | }), | ||
| 2758 | }, | ||
| 2741 | reviewService: { | 2759 | reviewService: { |
| 2742 | getDiffPreview: (input) => | 2760 | getDiffPreview: (input) => |
| 2743 | Effect.succeed({ | 2761 | Effect.succeed({ |
| @@ -16,9 +16,15 @@ import { · 15 unmodified lines | |||
| 16 | type ReactNode, | 16 | type ReactNode, |
| 17 | } from "react"; | 17 | } from "react"; |
| 18 | import { LegendList, type LegendListRef } from "@legendapp/list/react"; | 18 | import { LegendList, type LegendListRef } from "@legendapp/list/react"; |
| 19 | import { FileDiff } from "@pierre/diffs/react"; | ||
| 19 | import { deriveTimelineEntries, formatElapsed } from "../../session-logic"; | 20 | import { deriveTimelineEntries, formatElapsed } from "../../session-logic"; |
| 20 | import { type TurnDiffSummary } from "../../types"; | 21 | import { type TurnDiffSummary } from "../../types"; |
| 21 | import { summarizeTurnDiffStats } from "../../lib/turnDiffTree"; | 22 | import { summarizeTurnDiffStats } from "../../lib/turnDiffTree"; |
| 23 | import { | ||
| 24 | getRenderablePatch, | ||
| 25 | resolveDiffThemeName, | ||
| 26 | resolveFileDiffPath, | ||
| 27 | } from "../../lib/diffRendering"; | ||
| 22 | import ChatMarkdown from "../ChatMarkdown"; | 28 | import ChatMarkdown from "../ChatMarkdown"; |
| 23 | import { | 29 | import { |
| 24 | BotIcon, | 30 | BotIcon, |
| @@ -67,6 +73,11 @@ import { · 42 unmodified lines | |||
| 67 | } from "./userMessageTerminalContexts"; | 73 | } from "./userMessageTerminalContexts"; |
| 68 | import { SkillInlineText } from "./SkillInlineText"; | 74 | import { SkillInlineText } from "./SkillInlineText"; |
| 69 | import { formatWorkspaceRelativePath } from "../../filePathDisplay"; | 75 | import { formatWorkspaceRelativePath } from "../../filePathDisplay"; |
| 76 | import { | ||
| 77 | buildReviewCommentRenderablePatch, | ||
| 78 | parseReviewCommentMessageSegments, | ||
| 79 | type ReviewCommentContext, | ||
| 80 | } from "../../reviewCommentContext"; | ||
| 70 | 81 | ||
| 71 | // --------------------------------------------------------------------------- | 82 | // --------------------------------------------------------------------------- |
| 72 | // Context — shared state consumed by every row component via Context. | 83 | // Context — shared state consumed by every row component via Context. |
| @@ -837,6 +848,25 @@ const UserMessageBody = memo(function UserMessageBody(props: { · 764 unmodified lines | |||
| 837 | terminalContexts: ParsedTerminalContextEntry[]; | 848 | terminalContexts: ParsedTerminalContextEntry[]; |
| 838 | skills: ReadonlyArray<Pick<ServerProviderSkill, "name" | "displayName">>; | 849 | skills: ReadonlyArray<Pick<ServerProviderSkill, "name" | "displayName">>; |
| 839 | }) { | 850 | }) { |
| 851 | const reviewCommentSegments = parseReviewCommentMessageSegments(props.text); | ||
| 852 | if (reviewCommentSegments.some((segment) => segment.kind === "review-comment")) { | ||
| 853 | return ( | ||
| 854 | <div className="space-y-3 text-sm leading-relaxed text-foreground"> | ||
| 855 | {reviewCommentSegments.map((segment) => | ||
| 856 | segment.kind === "text" ? ( | ||
| 857 | segment.text.trim().length > 0 ? ( | ||
| 858 | <div key={segment.id} className="whitespace-pre-wrap wrap-break-word"> | ||
| 859 | <SkillInlineText text={segment.text.trim()} skills={props.skills} /> | ||
| 860 | </div> | ||
| 861 | ) : null | ||
| 862 | ) : ( | ||
| 863 | <UserMessageReviewCommentCard key={segment.comment.id} comment={segment.comment} /> | ||
| 864 | ), | ||
| 865 | )} | ||
| 866 | </div> | ||
| 867 | ); | ||
| 868 | } | ||
| 869 | |||
| 840 | if (props.terminalContexts.length > 0) { | 870 | if (props.terminalContexts.length > 0) { |
| 841 | const hasEmbeddedInlineLabels = textContainsInlineTerminalContextLabels( | 871 | const hasEmbeddedInlineLabels = textContainsInlineTerminalContextLabels( |
| 842 | props.text, | 872 | props.text, |
| @@ -930,6 +960,49 @@ const UserMessageBody = memo(function UserMessageBody(props: { · 87 unmodified lines | |||
| 930 | ); | 960 | ); |
| 931 | }); | 961 | }); |
| 932 | 962 | ||
| 963 | function UserMessageReviewCommentCard({ comment }: { comment: ReviewCommentContext }) { | ||
| 964 | const ctx = use(TimelineRowCtx); | ||
| 965 | const renderablePatch = getRenderablePatch( | ||
| 966 | buildReviewCommentRenderablePatch(comment), | ||
| 967 | `review-comment:${comment.id}`, | ||
| 968 | ); | ||
| 969 | |||
| 970 | return ( | ||
| 971 | <div className="space-y-2 rounded-lg border border-border/70 bg-background/70 p-3"> | ||
| 972 | <div className="space-y-1"> | ||
| 973 | <div className="text-xs font-medium text-foreground"> | ||
| 974 | {formatWorkspaceRelativePath(comment.filePath, ctx.workspaceRoot)} | ||
| 975 | </div> | ||
| 976 | <div className="text-[11px] text-muted-foreground"> | ||
| 977 | {comment.sectionTitle} · {comment.rangeLabel} | ||
| 978 | </div> | ||
| 979 | </div> | ||
| 980 | {comment.text.length > 0 && ( | ||
| 981 | <div className="whitespace-pre-wrap wrap-break-word text-sm"> | ||
| 982 | <SkillInlineText text={comment.text} skills={ctx.skills} /> | ||
| 983 | </div> | ||
| 984 | )} | ||
| 985 | {renderablePatch?.kind === "files" && | ||
| 986 | renderablePatch.files.map((fileDiff) => ( | ||
| 987 | <FileDiff | ||
| 988 | key={resolveFileDiffPath(fileDiff)} | ||
| 989 | fileDiff={fileDiff} | ||
| 990 | options={{ | ||
| 991 | collapsed: false, | ||
| 992 | diffStyle: "unified", | ||
| 993 | theme: resolveDiffThemeName(ctx.resolvedTheme), | ||
| 994 | }} | ||
| 995 | /> | ||
| 996 | ))} | ||
| 997 | {renderablePatch?.kind === "raw" && ( | ||
| 998 | <pre className="overflow-x-auto rounded-md bg-muted/40 p-2 text-xs"> | ||
| 999 | {renderablePatch.text} | ||
| 1000 | </pre> | ||
| 1001 | )} | ||
| 1002 | </div> | ||
| 1003 | ); | ||
| 1004 | } | ||
| 1005 | |||
| 933 | // --------------------------------------------------------------------------- | 1006 | // --------------------------------------------------------------------------- |
| 934 | // Structural sharing — reuse old row references when data hasn't changed | 1007 | // Structural sharing — reuse old row references when data hasn't changed |
| 935 | // so LegendList (and React) can skip re-rendering unchanged items. | 1008 | // so LegendList (and React) can skip re-rendering unchanged items. |
| @@ -239,7 +239,28 @@ describe("remote environment api", () => { · 238 unmodified lines | |||
| 239 | httpBaseUrl: "https://remote.example.com/", | 239 | httpBaseUrl: "https://remote.example.com/", |
| 240 | bearerToken: "bearer-token", | 240 | bearerToken: "bearer-token", |
| 241 | }), | 241 | }), |
| 242 | ).resolves.toBe("wss://remote.example.com/ws?wsToken=ws-token"); | 242 | ).resolves.toBe("wss://remote.example.com/?wsToken=ws-token"); |
| 243 | }); | ||
| 244 | |||
| 245 | it("preserves the caller-provided websocket path when minting a token", async () => { | ||
| 246 | const fetchMock = vi.fn().mockResolvedValue( | ||
| 247 | new Response( | ||
| 248 | JSON.stringify({ | ||
| 249 | token: "ws-token", | ||
| 250 | expiresAt: "2026-05-01T12:05:00.000Z", | ||
| 251 | }), | ||
| 252 | { status: 200 }, | ||
| 253 | ), | ||
| 254 | ); | ||
| 255 | globalThis.fetch = fetchMock as typeof fetch; | ||
| 256 | |||
| 257 | await expect( | ||
| 258 | resolveRemoteWebSocketConnectionUrl({ | ||
| 259 | wsBaseUrl: "wss://remote.example.com/custom/socket?existing=1", | ||
| 260 | httpBaseUrl: "https://remote.example.com/", | ||
| 261 | bearerToken: "bearer-token", | ||
| 262 | }), | ||
| 263 | ).resolves.toBe("wss://remote.example.com/custom/socket?existing=1&wsToken=ws-token"); | ||
| 243 | }); | 264 | }); |
| 244 | }); | 265 | }); |
| 245 | 266 | ||
| @@ -72,6 +72,7 @@ function createTestClient() { · 71 unmodified lines | |||
| 72 | clear: vi.fn(async () => undefined), | 72 | clear: vi.fn(async () => undefined), |
| 73 | restart: vi.fn(async () => undefined), | 73 | restart: vi.fn(async () => undefined), |
| 74 | close: vi.fn(async () => undefined), | 74 | close: vi.fn(async () => undefined), |
| 75 | onEvent: vi.fn(() => () => undefined), | ||
| 75 | onMetadata: vi.fn(() => () => undefined), | 76 | onMetadata: vi.fn(() => () => undefined), |
| 76 | }, | 77 | }, |
| 77 | projects: { | 78 | projects: { |
| @@ -269,6 +269,17 @@ describe("retainThreadDetailSubscription", () => { · 268 unmodified lines | |||
| 269 | mockCreateEnvironmentConnection.mockImplementation((input) => { | 269 | mockCreateEnvironmentConnection.mockImplementation((input) => { |
| 270 | const reconnect = vi.fn(async () => undefined); | 270 | const reconnect = vi.fn(async () => undefined); |
| 271 | mockConnectionReconnects.push(reconnect); | 271 | mockConnectionReconnects.push(reconnect); |
| 272 | queueMicrotask(() => { | ||
| 273 | input.onConfigSnapshot?.({ | ||
| 274 | environment: { | ||
| 275 | environmentId: input.knownEnvironment.environmentId, | ||
| 276 | label: input.knownEnvironment.label, | ||
| 277 | platform: { os: "darwin", arch: "arm64" }, | ||
| 278 | serverVersion: "0.0.0-test", | ||
| 279 | capabilities: { repositoryIdentity: true }, | ||
| 280 | }, | ||
| 281 | }); | ||
| 282 | }); | ||
| 272 | return { | 283 | return { |
| 273 | kind: input.kind, | 284 | kind: input.kind, |
| 274 | environmentId: input.knownEnvironment.environmentId, | 285 | environmentId: input.knownEnvironment.environmentId, |
| @@ -438,13 +449,13 @@ describe("retainThreadDetailSubscription", () => { · 163 unmodified lines | |||
| 438 | const stop = startEnvironmentConnectionService(new QueryClient()); | 449 | const stop = startEnvironmentConnectionService(new QueryClient()); |
| 439 | savedEnvironmentRegistryListener?.(); | 450 | savedEnvironmentRegistryListener?.(); |
| 440 | await vi.waitFor(() => { | 451 | await vi.waitFor(() => { |
| 441 | expect(mockCreateEnvironmentConnection).toHaveBeenCalledTimes(2); | ||
| 442 | expect( | 452 | expect( |
| 443 | listEnvironmentConnections().some( | 453 | listEnvironmentConnections().some( |
| 444 | (connection) => connection.environmentId === environmentId, | 454 | (connection) => connection.environmentId === environmentId, |
| 445 | ), | 455 | ), |
| 446 | ).toBe(true); | 456 | ).toBe(true); |
| 447 | }); | 457 | }); |
| 458 | const createConnectionCallsBeforeReconnect = mockCreateEnvironmentConnection.mock.calls.length; | ||
| 448 | 459 | ||
| 449 | const release = retainThreadDetailSubscription(environmentId, threadId); | 460 | const release = retainThreadDetailSubscription(environmentId, threadId); |
| 450 | expect(mockSubscribeThread).toHaveBeenCalledTimes(1); | 461 | expect(mockSubscribeThread).toHaveBeenCalledTimes(1); |
| @@ -459,7 +470,9 @@ describe("retainThreadDetailSubscription", () => { · 8 unmodified lines | |||
| 459 | await vi.advanceTimersByTimeAsync(200); | 470 | await vi.advanceTimersByTimeAsync(200); |
| 460 | await reconnectPromise; | 471 | await reconnectPromise; |
| 461 | await vi.waitFor(() => { | 472 | await vi.waitFor(() => { |
| 462 | expect(mockCreateEnvironmentConnection).toHaveBeenCalledTimes(3); | 473 | expect(mockCreateEnvironmentConnection).toHaveBeenCalledTimes( |
| 474 | createConnectionCallsBeforeReconnect + 1, | ||
| 475 | ); | ||
| 463 | expect(mockSubscribeThread).toHaveBeenCalledTimes(2); | 476 | expect(mockSubscribeThread).toHaveBeenCalledTimes(2); |
| 464 | }); | 477 | }); |
| 465 | 478 | ||
| @@ -486,6 +499,33 @@ describe("retainThreadDetailSubscription", () => { · 20 unmodified lines | |||
| 486 | 499 | ||
| 487 | const { resetEnvironmentServiceForTests, startEnvironmentConnectionService } = | 500 | const { resetEnvironmentServiceForTests, startEnvironmentConnectionService } = |
| 488 | await import("./service"); | 501 | await import("./service"); |
| 502 | mockCreateEnvironmentConnection.mockImplementation((input) => { | ||
| 503 | const reconnect = vi.fn(async () => undefined); | ||
| 504 | mockConnectionReconnects.push(reconnect); | ||
| 505 | queueMicrotask(() => { | ||
| 506 | input.onConfigSnapshot?.({ | ||
| 507 | environment: { | ||
| 508 | environmentId: input.knownEnvironment.environmentId, | ||
| 509 | label: input.knownEnvironment.label, | ||
| 510 | platform: { os: "darwin", arch: "arm64" }, | ||
| 511 | serverVersion: "0.0.0-test", | ||
| 512 | capabilities: { repositoryIdentity: true }, | ||
| 513 | }, | ||
| 514 | }); | ||
| 515 | }); | ||
| 516 | return { | ||
| 517 | kind: input.kind, | ||
| 518 | environmentId: input.knownEnvironment.environmentId, | ||
| 519 | knownEnvironment: input.knownEnvironment, | ||
| 520 | client: { | ||
| 521 | ...input.client, | ||
| 522 | isHeartbeatFresh: vi.fn(() => true), | ||
| 523 | }, | ||
| 524 | ensureBootstrapped: vi.fn(async () => undefined), | ||
| 525 | reconnect, | ||
| 526 | dispose: vi.fn(async () => undefined), | ||
| 527 | }; | ||
| 528 | }); | ||
| 489 | 529 | ||
| 490 | const stop = startEnvironmentConnectionService(new QueryClient()); | 530 | const stop = startEnvironmentConnectionService(new QueryClient()); |
| 491 | expect(mockConnectionReconnects).toHaveLength(1); | 531 | expect(mockConnectionReconnects).toHaveLength(1); |
| @@ -242,15 +242,10 @@ describe("WsTransport (web instrumentation)", () => { · 241 unmodified lines | |||
| 242 | socket.close(1012, "service restart"); | 242 | socket.close(1012, "service restart"); |
| 243 | 243 | ||
| 244 | await waitFor(() => { | 244 | await waitFor(() => { |
| 245 | expect(onClose).toHaveBeenCalledWith( | 245 | expect(onClose).toHaveBeenCalledWith({ |
| 246 | { | 246 | code: 1012, |
| 247 | code: 1012, | 247 | reason: "service restart", |
| 248 | reason: "service restart", | 248 | }); |
| 249 | }, | ||
| 250 | { | ||
| 251 | intentional: false, | ||
| 252 | }, | ||
| 253 | ); | ||
| 254 | expect(getWsConnectionStatus()).toMatchObject({ | 249 | expect(getWsConnectionStatus()).toMatchObject({ |
| 255 | attemptCount: 2, | 250 | attemptCount: 2, |
| 256 | closeReason: "service restart", | 251 | closeReason: "service restart", |
| @@ -645,8 +645,8 @@ describe("WsTransport", () => { · 644 unmodified lines | |||
| 645 | }); | 645 | }); |
| 646 | 646 | ||
| 647 | it("does not retry stream subscriptions after application-level failures", async () => { | 647 | it("does not retry stream subscriptions after application-level failures", async () => { |
| 648 | const transport = createTransport("ws://localhost:3020"); | 648 | const warnSpy = vi.fn(); |
| 649 | const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined); | 649 | const transport = createTransport("ws://localhost:3020", undefined, { logWarning: warnSpy }); |
| 650 | let attempts = 0; | 650 | let attempts = 0; |
| 651 | 651 | ||
| 652 | const unsubscribe = transport.subscribe( | 652 | const unsubscribe = transport.subscribe( |
| @@ -684,8 +684,8 @@ describe("WsTransport", () => { · 31 unmodified lines | |||
| 684 | }); | 684 | }); |
| 685 | 685 | ||
| 686 | it("keeps retrying stream subscriptions after transport failures", async () => { | 686 | it("keeps retrying stream subscriptions after transport failures", async () => { |
| 687 | const transport = createTransport("ws://localhost:3020"); | 687 | const warnSpy = vi.fn(); |
| 688 | const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined); | 688 | const transport = createTransport("ws://localhost:3020", undefined, { logWarning: warnSpy }); |
| 689 | let attempts = 0; | 689 | let attempts = 0; |
| 690 | 690 | ||
| 691 | const unsubscribe = transport.subscribe( | 691 | const unsubscribe = transport.subscribe( |
| @@ -717,8 +717,8 @@ describe("WsTransport", () => { · 25 unmodified lines | |||
| 717 | }); | 717 | }); |
| 718 | 718 | ||
| 719 | it("logs a transport disconnect once even when multiple subscriptions fail together", async () => { | 719 | it("logs a transport disconnect once even when multiple subscriptions fail together", async () => { |
| 720 | const transport = createTransport("ws://localhost:3020"); | 720 | const warnSpy = vi.fn(); |
| 721 | const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined); | 721 | const transport = createTransport("ws://localhost:3020", undefined, { logWarning: warnSpy }); |
| 722 | 722 | ||
| 723 | const unsubscribeA = transport.subscribe( | 723 | const unsubscribeA = transport.subscribe( |
| 724 | () => Stream.fail(new Error("SocketCloseError: 1006")), | 724 | () => Stream.fail(new Error("SocketCloseError: 1006")), |
| @@ -31,6 +31,7 @@ export interface WsTransportOptions { · 30 unmodified lines | |||
| 31 | url: WsRpcProtocolSocketUrlProvider, | 31 | url: WsRpcProtocolSocketUrlProvider, |
| 32 | lifecycleHandlers?: WsProtocolLifecycleHandlers, | 32 | lifecycleHandlers?: WsProtocolLifecycleHandlers, |
| 33 | ) => Layer.Layer<RpcClient.Protocol, never, never>; | 33 | ) => Layer.Layer<RpcClient.Protocol, never, never>; |
| 34 | readonly logWarning?: (message: string, metadata: { readonly error: string }) => void; | ||
| 34 | /** | 35 | /** |
| 35 | * Invoked at the start of {@link WsTransport.reconnect} before the session is replaced. | 36 | * Invoked at the start of {@link WsTransport.reconnect} before the session is replaced. |
| 36 | */ | 37 | */ |
| @@ -172,18 +173,14 @@ export class WsTransport { · 135 unmodified lines | |||
| 172 | 173 | ||
| 173 | const formattedError = formatErrorMessage(error); | 174 | const formattedError = formatErrorMessage(error); |
| 174 | if (!isTransportConnectionErrorMessage(formattedError)) { | 175 | if (!isTransportConnectionErrorMessage(formattedError)) { |
| 175 | Effect.runSync( | 176 | this.logWarning("WebSocket RPC subscription failed", { error: formattedError }); |
| 176 | Effect.logWarning("WebSocket RPC subscription failed", { error: formattedError }), | ||
| 177 | ); | ||
| 178 | return; | 177 | return; |
| 179 | } | 178 | } |
| 180 | 179 | ||
| 181 | if (!this.hasReportedTransportDisconnect) { | 180 | if (!this.hasReportedTransportDisconnect) { |
| 182 | Effect.runSync( | 181 | this.logWarning("WebSocket RPC subscription disconnected", { |
| 183 | Effect.logWarning("WebSocket RPC subscription disconnected", { | 182 | error: formattedError, |
| 184 | error: formattedError, | 183 | }); |
| 185 | }), | ||
| 186 | ); | ||
| 187 | } | 184 | } |
| 188 | this.hasReportedTransportDisconnect = true; | 185 | this.hasReportedTransportDisconnect = true; |
| 189 | await sleep(retryDelayMs); | 186 | await sleep(retryDelayMs); |
| @@ -252,6 +249,15 @@ export class WsTransport { · 62 unmodified lines | |||
| 252 | }; | 249 | }; |
| 253 | } | 250 | } |
| 254 | 251 | ||
| 252 | private logWarning(message: string, metadata: { readonly error: string }) { | ||
| 253 | const logWarning = this.options?.logWarning; | ||
| 254 | if (logWarning) { | ||
| 255 | logWarning(message, metadata); | ||
| 256 | } else { | ||
| 257 | Effect.runSync(Effect.logWarning(message, metadata)); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 255 | private runStreamOnSession<TValue>( | 261 | private runStreamOnSession<TValue>( |
| 256 | session: TransportSession, | 262 | session: TransportSession, |
| 257 | connect: (client: WsRpcProtocolClient) => Stream.Stream<TValue, Error, never>, | 263 | connect: (client: WsRpcProtocolClient) => Stream.Stream<TValue, Error, never>, |
| @@ -44,6 +44,7 @@ describe("TerminalOpenInput", () => { · 43 unmodified lines | |||
| 44 | expect( | 44 | expect( |
| 45 | decodes(TerminalOpenInput, { | 45 | decodes(TerminalOpenInput, { |
| 46 | threadId: "thread-1", | 46 | threadId: "thread-1", |
| 47 | terminalId: DEFAULT_TERMINAL_ID, | ||
| 47 | cwd: "/tmp/project", | 48 | cwd: "/tmp/project", |
| 48 | cols: 423, | 49 | cols: 423, |
| 49 | rows: 40, | 50 | rows: 40, |