@@ -2715,22 +2715,40 @@ describe("ClaudeAdapterV2 MCP credential channel", () => {
27152715});
27162716
27172717describe("ClaudeAdapterV2 native session identity", () => {
2718- const openTurnWithOrdinal = (providerTurnOrdinal: number, nativeThreadHasTurns?: boolean) =>
2718+ const openTurnWithOrdinal = (
2719+ providerTurnOrdinal: number,
2720+ nativeThreadHasTurns?: boolean,
2721+ importedNativeThreadId?: string,
2722+ ) =>
27192723 Effect.scoped(
27202724 Effect.gen(function* () {
27212725 const fileSystem = yield* FileSystem.FileSystem;
27222726 const idAllocator = yield* IdAllocator.IdAllocatorV2;
2727+ const path = yield* Path.Path;
27232728 const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({
27242729 prefix: "t3-claude-v2-session-identity-",
27252730 });
2731+ if (importedNativeThreadId !== undefined) {
2732+ // An unreadable copy in another project must not hide the readable one.
2733+ yield* fileSystem.makeDirectory(
2734+ path.join(attachmentsDir, "projects", "-other", `${importedNativeThreadId}.jsonl`),
2735+ { recursive: true },
2736+ );
2737+ const projectDir = path.join(attachmentsDir, "projects", "-workspace");
2738+ yield* fileSystem.makeDirectory(projectDir, { recursive: true });
2739+ yield* fileSystem.writeFileString(
2740+ path.join(projectDir, `${importedNativeThreadId}.jsonl`),
2741+ `{"type":"user","message":{"role":"user","content":"hello"},"sessionId":"${importedNativeThreadId}"}\n`,
2742+ );
2743+ }
27262744 const openedQueries: Array<ClaudeAdapterV2.ClaudeAgentSdkQueryOpenInput> = [];
27272745 const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({
27282746 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
27292747 settings: DEFAULT_CLAUDE_SETTINGS,
2730- environment: {},
2748+ environment: { CLAUDE_CONFIG_DIR: attachmentsDir },
27312749 attachmentsDir,
27322750 fileSystem,
2733- path: yield* Path.Path ,
2751+ path,
27342752 random: yield* SecureRandom.SecureRandom,
27352753 idAllocator,
27362754 queryRunner: {
@@ -2763,12 +2781,49 @@ describe("ClaudeAdapterV2 native session identity", () => {
27632781 modelSelection: CLAUDE_TEST_MODEL_SELECTION,
27642782 runtimePolicy: CLAUDE_TEST_RUNTIME_POLICY,
27652783 });
2766- const providerThread = yield* runtime.ensureThread({
2767- threadId,
2768- modelSelection: CLAUDE_TEST_MODEL_SELECTION,
2769- runtimePolicy: CLAUDE_TEST_RUNTIME_POLICY,
2770- });
27712784 const now = yield* DateTime.now;
2785+ // Mirrors the provider thread a session import records: a strong native
2786+ // ref with no conversation head and no provider turns.
2787+ const providerThread =
2788+ importedNativeThreadId === undefined
2789+ ? yield* runtime.ensureThread({
2790+ threadId,
2791+ modelSelection: CLAUDE_TEST_MODEL_SELECTION,
2792+ runtimePolicy: CLAUDE_TEST_RUNTIME_POLICY,
2793+ })
2794+ : yield* runtime.resumeThread({
2795+ threadId,
2796+ modelSelection: CLAUDE_TEST_MODEL_SELECTION,
2797+ runtimePolicy: CLAUDE_TEST_RUNTIME_POLICY,
2798+ providerThread: {
2799+ id: idAllocator.derive.providerThread({
2800+ driver: ClaudeAdapterV2.CLAUDE_PROVIDER,
2801+ providerInstanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
2802+ nativeThreadId: importedNativeThreadId,
2803+ }),
2804+ driver: ClaudeAdapterV2.CLAUDE_PROVIDER,
2805+ providerInstanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
2806+ providerSessionId: null,
2807+ appThreadId: threadId,
2808+ ownerNodeId: null,
2809+ nativeThreadRef: {
2810+ driver: ClaudeAdapterV2.CLAUDE_PROVIDER,
2811+ nativeId: importedNativeThreadId,
2812+ strength: "strong",
2813+ },
2814+ nativeConversationHeadRef: null,
2815+ status: "not_loaded",
2816+ firstRunOrdinal: null,
2817+ lastRunOrdinal: null,
2818+ handoffIds: [],
2819+ forkedFrom: null,
2820+ contextUsage: null,
2821+ nativeMetadata: null,
2822+ goal: null,
2823+ createdAt: now,
2824+ updatedAt: now,
2825+ },
2826+ });
27722827 yield* runtime.startTurn(
27732828 makeClaudeTestTurnInput({
27742829 threadId,
@@ -2814,6 +2869,15 @@ describe("ClaudeAdapterV2 native session identity", () => {
28142869 assert.equal(openedQueries[0]?.options.title, "Claude attachment test");
28152870 }),
28162871 );
2872+
2873+ it.effect("resumes an imported native session on its first T3 turn", () =>
2874+ Effect.gen(function* () {
2875+ const openedQueries = yield* openTurnWithOrdinal(1, false, "imported-native-session");
2876+ assert.equal(openedQueries.length, 1);
2877+ assert.equal(openedQueries[0]?.options.resume, "imported-native-session");
2878+ assert.equal(openedQueries[0]?.options.sessionId, undefined);
2879+ }),
2880+ );
28172881});
28182882
28192883const encodeJsonString = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));
0 commit comments