/** * Remote session access through chi-sync: lazy per-repo session listing and * materialization into the native store for resume/import. chi-sync is * resolved dynamically so chi-commons stays loadable without it; callers get * a skip reason instead of an error. */ export interface RemoteSession { sessionId: string; title: string; ownerId: string; updatedAt: string; roundCount?: number; /** Estimated z=0 context cost supplied by the remote session index. */ approxTokens: number; } export interface MaterializedSession { path: string; head: "canonical" | "missing-head" | "no-head"; } export type RemoteAccess = | { ok: true; repoSlug: string; list(): Promise; materialize(sessionId: string): Promise; } | { ok: false; reason: string }; export async function remoteAccess(cwd: string): Promise { let sync: typeof import("@henkaku-center/chi-sync"); try { sync = await import("@henkaku-center/chi-sync"); } catch { return { ok: false, reason: "chi-sync-not-installed" }; } const context = sync.resolveSyncContext(cwd); if (!context.ok) return { ok: false, reason: context.reason }; const client = sync.createBackendClient({ headers: { authorization: `Bearer ${context.sessionToken}`, "x-chi-repo": context.repoSlug }, fetchImpl: sync.timeoutFetch(20_000), }); return { ok: true, repoSlug: context.repoSlug, async list() { const response = await client.listSessions(); return (response.sessions ?? []).map((session) => ({ sessionId: String(session.sessionId), title: String(session.title ?? `Session ${session.sessionId}`), ownerId: String(session.ownerId ?? ""), updatedAt: String(session.updatedAt ?? ""), roundCount: typeof session.roundCount === "number" ? session.roundCount : undefined, approxTokens: typeof session.tokenCount === "number" ? session.tokenCount : typeof session.approxTokens === "number" ? session.approxTokens : 0, })); }, async materialize(sessionId) { const result = await sync.materializeSession({ client, sessionId, cwd, stateRoot: context.gitRoot }); return result.ok ? { path: result.path, head: result.head } : { error: result.reason }; }, }; }