@@ -6,7 +6,7 @@ import type { Editor, WorkspaceLeaf } from 'obsidian';
66import { addIcon , MarkdownView , Notice , Plugin } from 'obsidian' ;
77
88import { QoderianStorage } from './app/storage/app-storage' ;
9- import { beginRestoreReport } from './core/diagnostics/restore-report' ;
9+ import { beginRestoreReport , reportRestoreIssue } from './core/diagnostics/restore-report' ;
1010import { buildCursorContext } from './core/editor/editor-context' ;
1111import { getVaultPath } from './core/fs/path' ;
1212import type {
@@ -25,9 +25,11 @@ import { type InlineEditContext, InlineEditModal } from './features/inline-edit/
2525import { QoderianSettingTab } from './features/settings/settings-tab' ;
2626import { setLocale , t } from './i18n/i18n' ;
2727import type { Locale } from './i18n/types' ;
28- import { setActiveQoderCliEdition } from './qoder/config/cli-edition' ;
28+ import { getActiveQoderCliEdition , setActiveQoderCliEdition } from './qoder/config/cli-edition' ;
2929import { normalizeQoderSettings } from './qoder/config/qoder-settings-reconciler' ;
3030import { getQoderSettings } from './qoder/config/settings' ;
31+ import { sdkSessionExistsForEdition } from './qoder/history/sdk-session-paths' ;
32+ import { resolveLegacySessionEdition , selectMetadataForEdition } from './qoder/history/session-edition-filter' ;
3133import { extractUserDisplayContent } from './qoder/prompt/context/prompt-context' ;
3234import {
3335 createQoderServices ,
@@ -395,6 +397,7 @@ export default class QoderianPlugin extends Plugin {
395397 enabledMcpServers : conversation . enabledMcpServers ,
396398 usage : conversation . usage ,
397399 resumeAtMessageId : conversation . resumeAtMessageId ,
400+ edition : conversation . edition ?? getActiveQoderCliEdition ( ) ,
398401 } ;
399402 }
400403
@@ -415,7 +418,45 @@ export default class QoderianPlugin extends Plugin {
415418 const didNormalizeModelVariants = this . normalizeModelVariantSettings ( ) ;
416419
417420 const allMetadata = await this . storage . sessions . listMetadata ( ) ;
418- this . conversations = allMetadata . map ( meta => {
421+ await this . migrateLegacySessionEditions ( allMetadata ) ;
422+ this . conversations = await this . buildConversationIndex ( allMetadata ) ;
423+ setLocale ( this . settings . locale as Locale ) ;
424+
425+ if ( didNormalizeModelVariants ) {
426+ await this . saveSettings ( ) ;
427+ }
428+ }
429+
430+ normalizeModelVariantSettings ( ) : boolean {
431+ return normalizeQoderSettings ( this . settings ) ;
432+ }
433+
434+ async saveSettings ( ) {
435+ this . syncActiveQoderCliEdition ( ) ;
436+ await this . storage . saveQoderianSettings ( this . settings ) ;
437+ }
438+
439+ /** Keeps the edition-aware path helpers aligned with the persisted settings. */
440+ private syncActiveQoderCliEdition ( ) {
441+ setActiveQoderCliEdition ( getQoderSettings ( this . settings ) . edition ) ;
442+ }
443+
444+ /**
445+ * Builds the in-memory conversation index from session metadata, keeping
446+ * only the sessions owned by the active CLI edition.
447+ */
448+ private async buildConversationIndex ( allMetadata : SessionMetadata [ ] ) : Promise < Conversation [ ] > {
449+ const edition = getActiveQoderCliEdition ( ) ;
450+ const otherEdition = edition === 'cn' ? 'global' : 'cn' ;
451+ const vaultPath = getVaultPath ( this . app ) ;
452+ const visible = selectMetadataForEdition (
453+ allMetadata ,
454+ edition ,
455+ ( sessionId ) => vaultPath !== null
456+ && sdkSessionExistsForEdition ( vaultPath , sessionId , otherEdition ) ,
457+ ) ;
458+
459+ return visible . map ( meta => {
419460 const resumeSessionId = meta . sessionId !== undefined ? meta . sessionId : meta . id ;
420461
421462 return {
@@ -433,29 +474,58 @@ export default class QoderianPlugin extends Plugin {
433474 usage : meta . usage ,
434475 titleGenerationStatus : meta . titleGenerationStatus ,
435476 resumeAtMessageId : meta . resumeAtMessageId ,
477+ edition : meta . edition ?? edition ,
436478 } ;
437479 } ) . sort (
438480 ( a , b ) => ( b . lastResponseAt ?? b . updatedAt ) - ( a . lastResponseAt ?? a . updatedAt )
439481 ) ;
440- setLocale ( this . settings . locale as Locale ) ;
441-
442- if ( didNormalizeModelVariants ) {
443- await this . saveSettings ( ) ;
444- }
445482 }
446483
447- normalizeModelVariantSettings ( ) : boolean {
448- return normalizeQoderSettings ( this . settings ) ;
484+ /** Rebuilds the conversation index after the CLI edition changes. */
485+ async reloadConversationIndex ( ) : Promise < void > {
486+ const allMetadata = await this . storage . sessions . listMetadata ( ) ;
487+ this . conversations = await this . buildConversationIndex ( allMetadata ) ;
449488 }
450489
451- async saveSettings ( ) {
452- this . syncActiveQoderCliEdition ( ) ;
453- await this . storage . saveQoderianSettings ( this . settings ) ;
454- }
490+ /**
491+ * One-shot upgrade for metadata written before the `edition` field existed:
492+ * stamps each legacy session with the edition whose config root holds its
493+ * history file, so future loads no longer depend on filesystem probing.
494+ * Sessions whose files are missing everywhere stay unstamped.
495+ */
496+ private async migrateLegacySessionEditions ( allMetadata : SessionMetadata [ ] ) : Promise < void > {
497+ const edition = getActiveQoderCliEdition ( ) ;
498+ const otherEdition = edition === 'cn' ? 'global' : 'cn' ;
499+ const vaultPath = getVaultPath ( this . app ) ;
500+ if ( vaultPath === null ) {
501+ return ;
502+ }
455503
456- /** Keeps the edition-aware path helpers aligned with the persisted settings. */
457- private syncActiveQoderCliEdition ( ) {
458- setActiveQoderCliEdition ( getQoderSettings ( this . settings ) . edition ) ;
504+ for ( const meta of allMetadata ) {
505+ if ( meta . edition !== undefined ) {
506+ continue ;
507+ }
508+ const resolved = resolveLegacySessionEdition ( meta , edition , ( sessionId ) => {
509+ if ( sdkSessionExistsForEdition ( vaultPath , sessionId , edition ) ) {
510+ return 'active' ;
511+ }
512+ return sdkSessionExistsForEdition ( vaultPath , sessionId , otherEdition )
513+ ? 'other'
514+ : 'unknown' ;
515+ } ) ;
516+ if ( resolved === undefined ) {
517+ continue ;
518+ }
519+ try {
520+ await this . storage . sessions . saveMetadata ( { ...meta , edition : resolved } ) ;
521+ meta . edition = resolved ;
522+ } catch {
523+ reportRestoreIssue (
524+ 'metadata' ,
525+ `Failed to stamp edition on session metadata "${ meta . id } "; it will be re-attempted on next load.` ,
526+ ) ;
527+ }
528+ }
459529 }
460530
461531 getResolvedQoderCliPath ( ) : string | null {
@@ -511,6 +581,7 @@ export default class QoderianPlugin extends Plugin {
511581 updatedAt : Date . now ( ) ,
512582 sessionId : sessionId ?? null ,
513583 messages : [ ] ,
584+ edition : getActiveQoderCliEdition ( ) ,
514585 } ;
515586
516587 this . conversations . unshift ( conversation ) ;
0 commit comments