Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
cad5d7b
feat(planreview): native versioned plan review in the right panel (#56)
tusharbhardwaj-bk Aug 7, 2026
1aa4264
fix(planreview): render the plan as a document, and open reviews for …
tusharbhardwaj-bk Aug 7, 2026
a7ed7c2
fix(planreview): responsiveness, false conflict warnings, and panel s…
tusharbhardwaj-bk Aug 8, 2026
f18eff8
feat(sidebar): nest agent-spawned sessions as trees in the exp sidebar
tusharbhardwaj-bk Aug 8, 2026
b4a732c
fix(web): filter session trees after nesting, not before
tusharbhardwaj-bk Aug 8, 2026
c97d79b
Merge remote-tracking branch 'origin/expbkmain' into t3code/f86f2cef
tusharbhardwaj-bk Aug 8, 2026
5aa04ac
perf(web): keep the sidebar row memo alive across session-tree renders
tusharbhardwaj-bk Aug 8, 2026
dce80f7
feat(server): reclaim archived sessions' worktrees, keeping their his…
tusharbhardwaj-bk Aug 8, 2026
6b0e42e
test(server): drive the lineage invariant through it.effect
tusharbhardwaj-bk Aug 8, 2026
8e9cc4c
Merge expbkmain into the archived-worktree reclaim branch
tusharbhardwaj-bk Aug 8, 2026
e088877
style(web): use BEGIN/END fork markers on the archived-panel seam
tusharbhardwaj-bk Aug 8, 2026
34be495
Merge pull request #60 from beknown-work/t3code/f86f2cef
tusharbhardwaj-bk Aug 8, 2026
223dee1
test(server): count the three archive RPCs in the web UI catalog
tusharbhardwaj-bk Aug 8, 2026
2634291
test(server): count the three session-archive RPCs in the web UI catalog
tusharbhardwaj-bk Aug 8, 2026
f322baf
Merge pull request #61 from beknown-work/t3code/fix-web-ui-rpc-count
tusharbhardwaj-bk Aug 8, 2026
c5ece33
fix(server): carry session lineage through the bootstrap create path
tusharbhardwaj-bk Aug 8, 2026
896bb57
feat(mcp): let agents link and unlink session parents
tusharbhardwaj-bk Aug 8, 2026
679b946
Merge pull request #62 from beknown-work/t3code/fix-session-lineage-b…
tusharbhardwaj-bk Aug 8, 2026
83ad576
fix(server): carry session lineage through the atomic first turn
tusharbhardwaj-bk Aug 8, 2026
eff5cb2
Merge pull request #63 from beknown-work/t3code/fix-lineage-atomic-tu…
tusharbhardwaj-bk Aug 8, 2026
d2d191b
feat(web): filter the sidebar by ownership and by who else is on a se…
tusharbhardwaj-bk Aug 8, 2026
5d8d8af
fix(web): actually forward the session-tree props to the sidebar row
tusharbhardwaj-bk Aug 8, 2026
1e85e43
Merge pull request #64 from beknown-work/t3code/fix-sidebar-tree-props
tusharbhardwaj-bk Aug 8, 2026
6755cdb
fix(web): stop the tree disclosure shoving parent titles right
tusharbhardwaj-bk Aug 8, 2026
5a2e863
feat(web): hoist a parent when any child needs a human
tusharbhardwaj-bk Aug 8, 2026
395a80c
feat(web): bulk selection and forced removal in the worktree reclaim …
tusharbhardwaj-bk Aug 8, 2026
8c186e9
Merge remote-tracking branch 'origin/expbkmain' into t3code/401beb01
tusharbhardwaj-bk Aug 8, 2026
3e275fc
Merge pull request #65 from beknown-work/t3code/tighten-tree-disclosure
tusharbhardwaj-bk Aug 8, 2026
390d642
feat(planreview): full-height document, mermaid diagrams, HTML plans …
tusharbhardwaj-bk Aug 8, 2026
3855663
feat(sessions): bulk session manager with AI work summaries
tusharbhardwaj-bk Aug 8, 2026
4313f6c
Merge remote-tracking branch 'origin/expbkmain' into t3code/56e26545
tusharbhardwaj-bk Aug 8, 2026
b2892ef
Merge pull request #67 from beknown-work/t3code/56e26545
tusharbhardwaj-bk Aug 8, 2026
b29be81
fix(sessions): resolve the summarize spinner on archived sessions
tusharbhardwaj-bk Aug 8, 2026
72dd421
Merge remote-tracking branch 'origin/expbkmain' into t3code/56e26545
tusharbhardwaj-bk Aug 8, 2026
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: 1 addition & 0 deletions apps/desktop/src/settings/DesktopClientSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const clientSettings: ClientSettings = {
glassOpacity: 80,
phaseGroupedSidebarEnabled: true,
planModeEnabled: false,
nativePlanReviewEnabled: true,
providerModelPreferences: {},
providerRateLimitsEnabled: true,
resourceMonitorEnabled: false,
Expand Down
12 changes: 12 additions & 0 deletions apps/server/integration/OrchestrationEngineHarness.integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ import {
} from "../src/orchestration/Services/OrchestrationEngine.ts";
import { ThreadDeletionReactor } from "../src/orchestration/Services/ThreadDeletionReactor.ts";
import { OrchestrationReactor } from "../src/orchestration/Services/OrchestrationReactor.ts";
// T3-CUSTOM(expbkt3): BEGIN — bulk session manager work summaries.
import { WorkSummaryReactor } from "../src/orchestration/Services/WorkSummaryReactor.ts";
// T3-CUSTOM(expbkt3): END
import { ProjectionSnapshotQuery } from "../src/orchestration/Services/ProjectionSnapshotQuery.ts";
import {
RuntimeReceiptBus,
Expand Down Expand Up @@ -377,6 +380,15 @@ export const makeOrchestrationIntegrationHarness = (
Layer.provideMerge(providerCommandReactorLayer),
Layer.provideMerge(checkpointReactorLayer),
Layer.provideMerge(catchupSummaryReactorLayer),
// T3-CUSTOM(expbkt3): BEGIN — bulk session manager work summaries are exercised by
// their own reactor test; this harness only needs the dependency satisfied.
Layer.provideMerge(
Layer.succeed(WorkSummaryReactor, {
start: () => Effect.void,
drain: Effect.void,
}),
),
// T3-CUSTOM(expbkt3): END
Layer.provideMerge(
Layer.succeed(ThreadDeletionReactor, {
start: () => Effect.void,
Expand Down
14 changes: 14 additions & 0 deletions apps/server/src/auth/rpcForkScopes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,18 @@ export const FORK_RPC_REQUIRED_SCOPES = {
[WS_FORK_METHODS.usersRevokeSessions]: AuthOrchestrationOperateScope,
[WS_FORK_METHODS.usersSourceControlProfileSet]: AuthOrchestrationOperateScope,
[WS_FORK_METHODS.linearIssuesResolve]: AuthOrchestrationReadScope,
[WS_FORK_METHODS.planReviewGet]: AuthOrchestrationReadScope,
[WS_FORK_METHODS.planReviewList]: AuthOrchestrationReadScope,
[WS_FORK_METHODS.planReviewVersionDiff]: AuthOrchestrationReadScope,
[WS_FORK_METHODS.subscribePlanReview]: AuthOrchestrationReadScope,
[WS_FORK_METHODS.planReviewSaveDraft]: AuthOrchestrationOperateScope,
[WS_FORK_METHODS.planReviewCutVersion]: AuthOrchestrationOperateScope,
[WS_FORK_METHODS.planReviewUpsertDiscussion]: AuthOrchestrationOperateScope,
[WS_FORK_METHODS.planReviewResolveDiscussion]: AuthOrchestrationOperateScope,
[WS_FORK_METHODS.planReviewSubmit]: AuthOrchestrationOperateScope,
// T3-CUSTOM(expbkt3): archived-session worktree reclaim. Scanning is a read;
// exporting writes files and reclaiming deletes them, so both need operate.
[WS_FORK_METHODS.sessionArchiveScan]: AuthOrchestrationReadScope,
[WS_FORK_METHODS.sessionArchiveExport]: AuthOrchestrationOperateScope,
[WS_FORK_METHODS.sessionArchiveReclaim]: AuthOrchestrationOperateScope,
} as const;
3 changes: 3 additions & 0 deletions apps/server/src/git/GitManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ function createTextGeneration(
Effect.die("updateRollingSummary is not exercised by GitManager tests"),
generateCatchupSummary: () =>
Effect.die("generateCatchupSummary is not exercised by GitManager tests"),
generateWorkSummary: () =>
Effect.die("generateWorkSummary is not exercised by GitManager tests"),
...overrides,
};

Expand Down Expand Up @@ -353,6 +355,7 @@ function createTextGeneration(
Effect.die("updateRollingSummary is not used by git text generation"),
generateCatchupSummary: () =>
Effect.die("generateCatchupSummary is not used by git text generation"),
generateWorkSummary: () => Effect.die("generateWorkSummary is not used by git text generation"),
};
}

Expand Down
138 changes: 138 additions & 0 deletions apps/server/src/mcp/toolkits/control/handlers.lineage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// T3-CUSTOM(expbkt3): session lineage on t3_create_session.
//
// Covers the full createAsChild matrix: nesting is the agent's call, and the
// implicit default must never fire for a conductor-scoped external-user token.
import { expect, it } from "@effect/vitest";
import { EnvironmentId, ProjectId, ProviderInstanceId, ThreadId, UserId } from "@t3tools/contracts";
import * as Effect from "effect/Effect";
import * as Option from "effect/Option";

import { OrchestrationAccessControl } from "../../../orchestration/Services/AccessControl.ts";
import * as McpInvocationContext from "../../McpInvocationContext.ts";
import { T3ControlToolError } from "./tools.ts";
import { __testing } from "./handlers.ts";

const actorUserId = UserId.make("user-lineage");
const callerThreadId = ThreadId.make("thread-caller");
const openProjectId = ProjectId.make("project-open");
const closedProjectId = ProjectId.make("project-closed");
const openParentId = ThreadId.make("thread-open-parent");
const closedParentId = ThreadId.make("thread-closed-parent");

const threads = [
{ id: callerThreadId, projectId: openProjectId },
{ id: openParentId, projectId: openProjectId },
{ id: closedParentId, projectId: closedProjectId },
];

function makeScope(
overrides: Partial<McpInvocationContext.McpInvocationScope> = {},
): McpInvocationContext.McpInvocationScope {
return {
principal: "provider-session",
actorUserId,
environmentId: EnvironmentId.make("environment-lineage"),
threadId: callerThreadId,
providerSessionId: "provider-session-lineage",
providerInstanceId: ProviderInstanceId.make("codex"),
capabilities: new Set(["t3.read", "t3.control", "t3.session.create"]),
issuedAt: 1,
...overrides,
};
}

const accessControl = OrchestrationAccessControl.of({
actorFor: () => Option.some(actorUserId),
canAccessThread: () => Effect.succeed(true),
canAccessProject: (_userId, projectId) => Effect.succeed(projectId === openProjectId),
canTransferThreadOwnership: () => Effect.succeed(false),
canTransferProjectOwnership: () => Effect.succeed(false),
});

const resolve = (input: {
readonly scope?: McpInvocationContext.McpInvocationScope;
readonly createAsChild?: boolean;
readonly parentSessionId?: string;
}) =>
__testing
.resolveCreatedSessionParent({
operation: "create-session",
scope: input.scope ?? makeScope(),
threads,
createAsChild: input.createAsChild,
parentSessionId: input.parentSessionId,
})
.pipe(Effect.provideService(OrchestrationAccessControl, accessControl));

it.effect("nests under the calling session by default", () =>
Effect.gen(function* () {
expect(yield* resolve({})).toBe(callerThreadId);
}),
);

it.effect("nests when createAsChild is explicitly true", () =>
Effect.gen(function* () {
expect(yield* resolve({ createAsChild: true })).toBe(callerThreadId);
}),
);

it.effect("creates a top-level session when createAsChild is false", () =>
Effect.gen(function* () {
expect(yield* resolve({ createAsChild: false })).toBe(null);
}),
);

it.effect("never parents an external-user token to its conductor thread", () =>
Effect.gen(function* () {
// scope.threadId here is the user's conductor thread, not a session they
// are working in — defaulting to it would build one giant bogus tree.
const scope = makeScope({ principal: "external-user" });
expect(yield* resolve({ scope })).toBe(null);
}),
);

it.effect("never parents an external operator implicitly", () =>
Effect.gen(function* () {
const scope = makeScope({ principal: "external-operator" });
expect(yield* resolve({ scope })).toBe(null);
}),
);

it.effect("honours an explicit accessible parent", () =>
Effect.gen(function* () {
expect(yield* resolve({ parentSessionId: openParentId })).toBe(openParentId);
}),
);

it.effect("hides a parent whose project the caller cannot access", () =>
Effect.gen(function* () {
const error = yield* resolve({ parentSessionId: closedParentId }).pipe(Effect.flip);
expect(error).toBeInstanceOf(T3ControlToolError);
expect(error.message).toBe(`T3 session ${closedParentId} was not found.`);
}),
);

it.effect("reports an unknown parent as absent", () =>
Effect.gen(function* () {
const error = yield* resolve({ parentSessionId: "thread-does-not-exist" }).pipe(Effect.flip);
expect(error).toBeInstanceOf(T3ControlToolError);
}),
);

it.effect("refuses a contradictory createAsChild:false plus parentSessionId", () =>
Effect.gen(function* () {
const error = yield* resolve({
createAsChild: false,
parentSessionId: openParentId,
}).pipe(Effect.flip);
expect(error).toBeInstanceOf(T3ControlToolError);
expect(error.message).toContain("cannot be combined with parentSessionId");
}),
);

it.effect("lets an external operator name a parent without an access check", () =>
Effect.gen(function* () {
const scope = makeScope({ principal: "external-operator" });
expect(yield* resolve({ scope, parentSessionId: closedParentId })).toBe(closedParentId);
}),
);
Loading
Loading