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
1 change: 0 additions & 1 deletion apps/mobile/src/lib/threadActivity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,6 @@ describe("buildThreadFeed", () => {
id: MessageId.make("feedback-local"),
role: "assistant",
text: "Feedback sent to OpenAI.\n\nThread ID: `codex-thread-1`",
turnId: null,
streaming: false,
createdAt: "2026-08-29T00:00:00.000Z",
updatedAt: "2026-08-29T00:00:00.000Z",
Expand Down
22 changes: 17 additions & 5 deletions apps/mobile/src/lib/threadActivity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import type {
OrchestrationV2Actor,
OrchestrationV2CreationSource,
OrchestrationV2ExecutionNode,
OrchestrationMessage,
OrchestrationMessageContext,
OrchestrationV2ProjectedTurnItem,
OrchestrationV2RunAttempt,
OrchestrationV2RunStatus,
Expand Down Expand Up @@ -124,6 +124,18 @@ export interface ThreadFeedMessage {
readonly projectedItem?: OrchestrationV2ProjectedTurnItem;
}

/** A message shown before the server has it: a pending creation or local feedback. */
export interface LocalThreadMessage {
readonly id: MessageId;
readonly role: string;
readonly text: string;
readonly context?: OrchestrationMessageContext | undefined;
readonly attachments?: ReadonlyArray<ChatAttachment> | undefined;
readonly streaming: boolean;
readonly createdAt: string;
readonly updatedAt: string;
}

type RawThreadFeedEntry =
| {
readonly type: "message";
Expand Down Expand Up @@ -259,7 +271,7 @@ const projectedEntriesCache = new WeakMap<
}
>();
const localMessageEntriesCache = new WeakMap<
OrchestrationMessage,
LocalThreadMessage,
Extract<RawThreadFeedEntry, { readonly type: "message" }>
>();
const activityGroupsCache = new WeakMap<ThreadFeedActivity, ThreadFeedActivityGroup>();
Expand Down Expand Up @@ -1565,8 +1577,8 @@ export function buildPendingUserInputAnswers(
export function buildThreadFeed(
visibleTurnItems: ReadonlyArray<OrchestrationV2ProjectedTurnItem>,
options?: {
readonly localMessages?: ReadonlyArray<OrchestrationMessage>;
readonly anchoredMessages?: ReadonlyArray<OrchestrationMessage>;
readonly localMessages?: ReadonlyArray<LocalThreadMessage>;
readonly anchoredMessages?: ReadonlyArray<LocalThreadMessage>;
readonly attempts?: ReadonlyArray<OrchestrationV2RunAttempt>;
readonly nodes?: ReadonlyArray<OrchestrationV2ExecutionNode>;
},
Expand Down Expand Up @@ -1665,7 +1677,7 @@ export function buildThreadFeed(
...foldedAnswerMessageIds,
...entries.flatMap((entry) => (entry.type === "message" ? [entry.id] : [])),
]);
const appendLocalMessage = (message: OrchestrationMessage): RawThreadFeedEntry => {
const appendLocalMessage = (message: LocalThreadMessage): RawThreadFeedEntry => {
const cached = localMessageEntriesCache.get(message);
if (cached) return cached;
const entry: Extract<RawThreadFeedEntry, { readonly type: "message" }> = {
Expand Down
1 change: 0 additions & 1 deletion apps/mobile/src/state/pending-thread-creation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ describe("pendingThreadCreationMessage", () => {
id: creation.messageId,
role: "user",
text: creation.text,
turnId: null,
streaming: false,
createdAt: creation.createdAt,
updatedAt: creation.createdAt,
Expand Down
7 changes: 2 additions & 5 deletions apps/mobile/src/state/pending-thread-creation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
presentThreadShell,
type EnvironmentThreadShell,
} from "@t3tools/client-runtime/state/shell";
import type { OrchestrationThread } from "@t3tools/contracts";
import type { LocalThreadMessage } from "../lib/threadActivity";
import { DEFAULT_PROVIDER_INTERACTION_MODE, DEFAULT_RUNTIME_MODE } from "@t3tools/contracts";
import { Atom } from "effect/unstable/reactivity";
import * as DateTime from "effect/DateTime";
Expand Down Expand Up @@ -115,9 +115,7 @@ export function isPendingThreadCreationVisible(input: {
return !input.loadedMessageIds?.includes(input.creationMessageId);
}

export function pendingThreadCreationMessage(
message: QueuedThreadMessage,
): OrchestrationThread["messages"][number] {
export function pendingThreadCreationMessage(message: QueuedThreadMessage): LocalThreadMessage {
return {
id: message.messageId,
role: "user",
Expand All @@ -127,7 +125,6 @@ export function pendingThreadCreationMessage(
// cannot resolve, so the feed's attachment rows would sit on a spinner
// that only ends when the real message arrives — and never, if the
// creation is rejected. The delivered message renders them moments later.
turnId: null,
streaming: false,
createdAt: message.createdAt,
updatedAt: message.createdAt,
Expand Down
15 changes: 6 additions & 9 deletions apps/server/src/git/linkCreatedPullRequest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
type GitRunStackedActionResult,
type OrchestrationV2ServerCommand as OrchestrationCommand,
type OrchestrationProjectShell,
type OrchestrationThreadShell,
} from "@t3tools/contracts";
import { describe, expect, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
Expand All @@ -18,7 +17,10 @@ import {
type OrchestratorV2Shape,
OrchestratorDispatchError,
} from "../orchestration-v2/Orchestrator.ts";
import { v2PullRequestThread } from "../orchestration-v2/testkit/pullRequestFixtures.ts";
import {
type PullRequestTestThread,
v2PullRequestThread,
} from "../orchestration-v2/testkit/pullRequestFixtures.ts";
import * as ProjectStore from "../orchestration-v2/ProjectStore.ts";
import { ProjectService } from "../project/ProjectService.ts";
import { refreshPushedPullRequests } from "./refreshPushedPullRequests.ts";
Expand Down Expand Up @@ -51,7 +53,7 @@ const project: OrchestrationProjectShell = {
updatedAt: "2026-08-01T00:00:00.000Z",
};

const thread: OrchestrationThreadShell = {
const thread: PullRequestTestThread = {
id: THREAD_ID,
projectId: PROJECT_ID,
title: "Thread",
Expand All @@ -61,17 +63,12 @@ const thread: OrchestrationThreadShell = {
branch: null,
worktreePath: null,
pullRequests: [],
latestTurn: null,
createdAt: "2026-08-01T00:00:00.000Z",
updatedAt: "2026-08-20T00:00:00.000Z",
archivedAt: null,
settledOverride: null,
settledAt: null,
session: null,
latestUserMessageAt: "2026-08-20T00:00:00.000Z",
hasPendingApprovals: false,
hasPendingUserInput: false,
hasActionableProposedPlan: false,
};

function prResult(pr: GitRunStackedActionResult["pr"]): Pick<GitRunStackedActionResult, "pr"> {
Expand All @@ -80,7 +77,7 @@ function prResult(pr: GitRunStackedActionResult["pr"]): Pick<GitRunStackedAction

const makeDependencies = (
dispatch: OrchestratorV2Shape["dispatch"],
threadShell: OrchestrationThreadShell | null = thread,
threadShell: PullRequestTestThread | null = thread,
) =>
Layer.mergeAll(
Layer.mock(ProjectService)({
Expand Down
15 changes: 6 additions & 9 deletions apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
ThreadId,
type OrchestrationV2ServerCommand as OrchestrationCommand,
type OrchestrationProjectShell,
type OrchestrationThreadShell,
type ThreadPullRequestLink,
} from "@t3tools/contracts";
import { describe, expect, it } from "@effect/vitest";
Expand All @@ -22,7 +21,10 @@ import {
type OrchestratorV2Shape,
OrchestratorDispatchError,
} from "../../../orchestration-v2/Orchestrator.ts";
import { v2PullRequestThread } from "../../../orchestration-v2/testkit/pullRequestFixtures.ts";
import {
type PullRequestTestThread,
v2PullRequestThread,
} from "../../../orchestration-v2/testkit/pullRequestFixtures.ts";
import { ProjectService } from "../../../project/ProjectService.ts";
import * as McpInvocationContext from "../../McpInvocationContext.ts";
import { listThreadPullRequests, PullRequestsToolkitHandlersLive } from "./handlers.ts";
Expand Down Expand Up @@ -73,7 +75,7 @@ function makeProject(
};
}

function makeThread(pullRequests: ReadonlyArray<ThreadPullRequestLink>): OrchestrationThreadShell {
function makeThread(pullRequests: ReadonlyArray<ThreadPullRequestLink>): PullRequestTestThread {
return {
id: THREAD_ID,
projectId: PROJECT_ID,
Expand All @@ -84,17 +86,12 @@ function makeThread(pullRequests: ReadonlyArray<ThreadPullRequestLink>): Orchest
branch: null,
worktreePath: null,
pullRequests,
latestTurn: null,
createdAt: "2026-08-01T00:00:00.000Z",
updatedAt: "2026-08-20T00:00:00.000Z",
archivedAt: null,
settledOverride: null,
settledAt: null,
session: null,
latestUserMessageAt: "2026-08-20T00:00:00.000Z",
hasPendingApprovals: false,
hasPendingUserInput: false,
hasActionableProposedPlan: false,
};
}

Expand Down Expand Up @@ -131,7 +128,7 @@ function makeLink(
}

interface HarnessOptions {
readonly thread?: OrchestrationThreadShell | null;
readonly thread?: PullRequestTestThread | null;
readonly project?: OrchestrationProjectShell | null;
/** A rejection the orchestrator reports as the dispatch error's cause. */
readonly reject?: (command: OrchestrationCommand) => string | null;
Expand Down
30 changes: 16 additions & 14 deletions apps/server/src/orchestration-v2/PullRequestSyncReactor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import {
ThreadId,
type OrchestrationV2Command as OrchestrationCommand,
type OrchestrationProjectShell,
type OrchestrationShellSnapshot,
type OrchestrationThreadShell,
type PullRequestRef,
type PullRequestStack,
type PullRequestSummary,
Expand All @@ -29,6 +27,7 @@ import { ServerActivation } from "../serverActivation.ts";
import { OrchestratorV2, type OrchestratorV2Shape } from "./Orchestrator.ts";
import { ProjectionStoreV2 } from "./ProjectionStore.ts";
import * as PullRequestSyncReactor from "./PullRequestSyncReactor.ts";
import type { PullRequestTestThread } from "./testkit/pullRequestFixtures.ts";

const NOW = "2026-08-28T12:00:00.000Z";
const PROJECT_ID = ProjectId.make("sync-project");
Expand Down Expand Up @@ -58,8 +57,8 @@ function makeProject(id: ProjectId = PROJECT_ID): OrchestrationProjectShell {

function makeThread(
id: string,
overrides: Partial<OrchestrationThreadShell> = {},
): OrchestrationThreadShell {
overrides: Partial<PullRequestTestThread> = {},
): PullRequestTestThread {
return {
id: ThreadId.make(id),
projectId: PROJECT_ID,
Expand All @@ -73,17 +72,12 @@ function makeThread(
branch: null,
worktreePath: null,
pullRequests: [],
latestTurn: null,
createdAt: "2026-08-01T00:00:00.000Z",
updatedAt: "2026-08-20T00:00:00.000Z",
archivedAt: null,
settledOverride: null,
settledAt: null,
session: null,
latestUserMessageAt: "2026-08-20T00:00:00.000Z",
hasPendingApprovals: false,
hasPendingUserInput: false,
hasActionableProposedPlan: false,
...overrides,
};
}
Expand Down Expand Up @@ -118,10 +112,18 @@ function makeLink(
};
}

/** The shell the fake projection store serves: one project and its threads. */
interface TestShellSnapshot {
readonly snapshotSequence: number;
readonly projects: ReadonlyArray<OrchestrationProjectShell>;
readonly threads: ReadonlyArray<PullRequestTestThread>;
readonly updatedAt: string;
}

function makeSnapshot(
threads: ReadonlyArray<OrchestrationThreadShell>,
threads: ReadonlyArray<PullRequestTestThread>,
snapshotSequence = 1,
): OrchestrationShellSnapshot {
): TestShellSnapshot {
return {
snapshotSequence,
projects: [makeProject()],
Expand Down Expand Up @@ -151,7 +153,7 @@ function makeSummary(

interface HarnessOptions {
readonly invalidate?: PullRequestService["Service"]["invalidate"];
readonly snapshot: OrchestrationShellSnapshot;
readonly snapshot: TestShellSnapshot;
readonly summary?: (
input: PullRequestRef,
) => Effect.Effect<PullRequestSummary, PullRequestOperationError>;
Expand Down Expand Up @@ -269,9 +271,9 @@ const sweepAgain = Effect.fn("sweepPullRequestSyncHarness")(function* (

/** What the reactor would have persisted, so the next sweep sees its own writes. */
function applySync(
snapshot: OrchestrationShellSnapshot,
snapshot: TestShellSnapshot,
commands: ReadonlyArray<SyncCommand>,
): OrchestrationShellSnapshot {
): TestShellSnapshot {
return {
...snapshot,
snapshotSequence: snapshot.snapshotSequence + 1,
Expand Down
34 changes: 32 additions & 2 deletions apps/server/src/orchestration-v2/testkit/pullRequestFixtures.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,36 @@
import type { OrchestrationThreadShell, OrchestrationV2ThreadShell } from "@t3tools/contracts";
import type {
ModelSelection,
OrchestrationV2ThreadShell,
ProjectId,
RuntimeMode,
ThreadId,
ThreadLinkedPullRequest,
ThreadPullRequestLink,
} from "@t3tools/contracts";
import * as DateTime from "effect/DateTime";
export function v2PullRequestThread(thread: OrchestrationThreadShell): OrchestrationV2ThreadShell {

/** The thread fields pull request tests set; timestamps are ISO strings. */
export interface PullRequestTestThread {
readonly id: ThreadId;
readonly projectId: ProjectId;
readonly title: string;
readonly modelSelection: ModelSelection;
readonly runtimeMode: RuntimeMode;
readonly interactionMode: OrchestrationV2ThreadShell["interactionMode"];
readonly branch: string | null;
readonly worktreePath: string | null;
readonly pullRequests: ReadonlyArray<ThreadPullRequestLink>;
readonly linkedPullRequest?: ThreadLinkedPullRequest | null;
readonly branchPullRequest?: ThreadLinkedPullRequest | null;
readonly latestUserMessageAt: string | null;
readonly createdAt: string;
readonly updatedAt: string;
readonly archivedAt: string | null;
readonly settledOverride: OrchestrationV2ThreadShell["settledOverride"];
readonly settledAt: string | null;
}

export function v2PullRequestThread(thread: PullRequestTestThread): OrchestrationV2ThreadShell {
return {
id: thread.id,
projectId: thread.projectId,
Expand Down
3 changes: 2 additions & 1 deletion apps/server/src/persistence/ProviderSessionRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
RuntimeMode,
ThreadId,
} from "@t3tools/contracts";
import { ProviderSessionRuntimeStatus } from "@t3tools/contracts/legacy-orchestration";

import {
PersistenceDecodeError,
Expand All @@ -33,6 +32,8 @@ import {
* @module ProviderSessionRuntimeRepository
*/

const ProviderSessionRuntimeStatus = Schema.Literals(["starting", "running", "stopped", "error"]);

export const ProviderSessionRuntime = Schema.Struct({
threadId: ThreadId,
providerName: Schema.String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
* @module OrchestrationCommandReceiptRepository
*/
import { CommandId, IsoDateTime, NonNegativeInt, ProjectId, ThreadId } from "@t3tools/contracts";
import {
OrchestrationAggregateKind,
OrchestrationCommandReceiptStatus,
} from "@t3tools/contracts/legacy-orchestration";
import * as Option from "effect/Option";
import * as Schema from "effect/Schema";
import * as Context from "effect/Context";
Expand All @@ -20,12 +16,12 @@ import type { OrchestrationCommandReceiptRepositoryError } from "../Errors.ts";

export const OrchestrationCommandReceipt = Schema.Struct({
commandId: CommandId,
aggregateKind: OrchestrationAggregateKind,
aggregateKind: Schema.Literals(["project", "thread"]),
aggregateId: Schema.Union([ProjectId, ThreadId]),
commandType: Schema.String,
acceptedAt: IsoDateTime,
resultSequence: NonNegativeInt,
status: OrchestrationCommandReceiptStatus,
status: Schema.Literals(["accepted", "rejected"]),
error: Schema.NullOr(Schema.String),
});
export type OrchestrationCommandReceipt = typeof OrchestrationCommandReceipt.Type;
Expand Down
Loading
Loading