import * as Schema from "effect/Schema";import { IsoDateTime, NonNegativeInt, ProjectId, TrimmedNonEmptyString } from "./baseSchemas.ts";import { ProviderInstanceId } from "./providerInstance.ts"; /** Coding agent home directories the scanner knows how to read. */export const AgentSessionSource = Schema.Literals(["claudeAgent", "codex"]);export type AgentSessionSource = typeof AgentSessionSource.Type; /** File identity saved with an imported session so bounded retries can skip unchanged history. */export const AgentSessionImportSource = Schema.Struct({ provider: AgentSessionSource, providerInstanceId: ProviderInstanceId, providerSessionId: TrimmedNonEmptyString, filePath: TrimmedNonEmptyString, size: NonNegativeInt, mtimeMs: Schema.NullOr(Schema.Number), device: Schema.Number, inode: Schema.NullOr(Schema.Number), birthtimeMs: Schema.NullOr(Schema.Number),});export type AgentSessionImportSource = typeof AgentSessionImportSource.Type; /** Imported message ids retain their origin after event metadata is projected into SQLite. */export function isImportedAgentSessionMessageId(messageId: string): boolean { return messageId.startsWith("import:");} /** * Empty for now. Kept as a struct so future scan options (source filters, * explicit roots) can be added without a new method. */export const AgentSessionScanInput = Schema.Struct({});export type AgentSessionScanInput = typeof AgentSessionScanInput.Type; /** * A directory that at least one agent CLI has run in, suitable for import as a * T3 Code project. `alreadyImported` marks candidates that already have an * active project rooted at the same path. *//** * Git identity of a candidate directory, read from `.git/config` without * spawning git. `remoteKey` is the normalized origin URL, shared by every * clone of the same repository so the client can group them. `repository` * is the GitHub `owner/name` when the origin is on GitHub. */export const AgentSessionProjectGit = Schema.Struct({ remoteKey: Schema.NullOr(Schema.String), repository: Schema.NullOr(Schema.String),});export type AgentSessionProjectGit = typeof AgentSessionProjectGit.Type; export const AgentSessionProjectCandidate = Schema.Struct({ path: TrimmedNonEmptyString, title: TrimmedNonEmptyString, projectId: Schema.optional(ProjectId), sources: Schema.Array(AgentSessionSource), threadCount: NonNegativeInt, lastActiveAt: Schema.NullOr(IsoDateTime), alreadyImported: Schema.Boolean, /** * `null` when the directory is not the root of a git repository. Missing on * servers that predate the git scan, where the client cannot tell repositories * from plain folders and should treat every candidate as a standalone project. */ git: Schema.optionalKey(Schema.NullOr(AgentSessionProjectGit)),});export type AgentSessionProjectCandidate = typeof AgentSessionProjectCandidate.Type; export const AgentSessionScanResult = Schema.Struct({ candidates: Schema.Array(AgentSessionProjectCandidate), scannedAt: IsoDateTime, truncated: Schema.optional(Schema.Boolean),});export type AgentSessionScanResult = typeof AgentSessionScanResult.Type; export const AgentSessionImportInput = Schema.Struct({ projectId: ProjectId, expectedWorkspaceRoot: Schema.optional(TrimmedNonEmptyString),});export type AgentSessionImportInput = typeof AgentSessionImportInput.Type; export class AgentSessionImportProjectNotFoundError extends Schema.TaggedError<AgentSessionImportProjectNotFoundError>()( "AgentSessionImportProjectNotFoundError", { projectId: ProjectId },) { override get message(): string { return `Project '${this.projectId}' does not exist.`; }} export class AgentSessionImportProjectChangedError extends Schema.TaggedError<AgentSessionImportProjectChangedError>()( "AgentSessionImportProjectChangedError", { projectId: ProjectId },) { override get message(): string { return `Project '${this.projectId}' changed directories. Scan for projects again before importing history.`; }} export const AgentSessionImportResult = Schema.Struct({ importedCount: NonNegativeInt, skippedCount: NonNegativeInt,});export type AgentSessionImportResult = typeof AgentSessionImportResult.Type; export class AgentSessionScanError extends Schema.TaggedError<AgentSessionScanError>()( "AgentSessionScanError", { operation: Schema.Literals(["read-settings", "read-projects"]), cause: Schema.Defect(), },) { override get message(): string { return `Failed to scan agent sessions during ${this.operation}.`; }}
import * as Schema from "effect/Schema";
import { IsoDateTime, NonNegativeInt, ProjectId, TrimmedNonEmptyString } from "./baseSchemas.ts";
import { ProviderInstanceId } from "./providerInstance.ts";
/** Coding agent home directories the scanner knows how to read. */
export const AgentSessionSource = Schema.Literals(["claudeAgent", "codex"]);
export type AgentSessionSource = typeof AgentSessionSource.Type;
/** File identity saved with an imported session so bounded retries can skip unchanged history. */
export const AgentSessionImportSource = Schema.Struct({
provider: AgentSessionSource,
providerInstanceId: ProviderInstanceId,
providerSessionId: TrimmedNonEmptyString,
filePath: TrimmedNonEmptyString,
size: NonNegativeInt,
mtimeMs: Schema.NullOr(Schema.Number),
device: Schema.Number,
inode: Schema.NullOr(Schema.Number),
birthtimeMs: Schema.NullOr(Schema.Number),
});
export type AgentSessionImportSource = typeof AgentSessionImportSource.Type;
/** Imported message ids retain their origin after event metadata is projected into SQLite. */
export function isImportedAgentSessionMessageId(messageId: string): boolean {
return messageId.startsWith("import:");
}
/**
* Empty for now. Kept as a struct so future scan options (source filters,
* explicit roots) can be added without a new method.
*/
export const AgentSessionScanInput = Schema.Struct({});
export type AgentSessionScanInput = typeof AgentSessionScanInput.Type;
/**
* A directory that at least one agent CLI has run in, suitable for import as a
* T3 Code project. `alreadyImported` marks candidates that already have an
* active project rooted at the same path.
*/
/**
* Git identity of a candidate directory, read from `.git/config` without
* spawning git. `remoteKey` is the normalized origin URL, shared by every
* clone of the same repository so the client can group them. `repository`
* is the GitHub `owner/name` when the origin is on GitHub.
*/
export const AgentSessionProjectGit = Schema.Struct({
remoteKey: Schema.NullOr(Schema.String),
repository: Schema.NullOr(Schema.String),
});
export type AgentSessionProjectGit = typeof AgentSessionProjectGit.Type;
export const AgentSessionProjectCandidate = Schema.Struct({
path: TrimmedNonEmptyString,
title: TrimmedNonEmptyString,
projectId: Schema.optional(ProjectId),
sources: Schema.Array(AgentSessionSource),
threadCount: NonNegativeInt,
lastActiveAt: Schema.NullOr(IsoDateTime),
alreadyImported: Schema.Boolean,
/**
* `null` when the directory is not the root of a git repository. Missing on
* servers that predate the git scan, where the client cannot tell repositories
* from plain folders and should treat every candidate as a standalone project.
*/
git: Schema.optionalKey(Schema.NullOr(AgentSessionProjectGit)),
});
export type AgentSessionProjectCandidate = typeof AgentSessionProjectCandidate.Type;
export const AgentSessionScanResult = Schema.Struct({
candidates: Schema.Array(AgentSessionProjectCandidate),
scannedAt: IsoDateTime,
truncated: Schema.optional(Schema.Boolean),
});
export type AgentSessionScanResult = typeof AgentSessionScanResult.Type;
export const AgentSessionImportInput = Schema.Struct({
projectId: ProjectId,
expectedWorkspaceRoot: Schema.optional(TrimmedNonEmptyString),
});
export type AgentSessionImportInput = typeof AgentSessionImportInput.Type;
export class AgentSessionImportProjectNotFoundError extends Schema.TaggedError<AgentSessionImportProjectNotFoundError>()(
"AgentSessionImportProjectNotFoundError",
{ projectId: ProjectId },
) {
override get message(): string {
return `Project '${this.projectId}' does not exist.`;
}
}
export class AgentSessionImportProjectChangedError extends Schema.TaggedError<AgentSessionImportProjectChangedError>()(
"AgentSessionImportProjectChangedError",
{ projectId: ProjectId },
) {
override get message(): string {
return `Project '${this.projectId}' changed directories. Scan for projects again before importing history.`;
}
}
export const AgentSessionImportResult = Schema.Struct({
importedCount: NonNegativeInt,
skippedCount: NonNegativeInt,
});
export type AgentSessionImportResult = typeof AgentSessionImportResult.Type;
export class AgentSessionScanError extends Schema.TaggedError<AgentSessionScanError>()(
"AgentSessionScanError",
{
operation: Schema.Literals(["read-settings", "read-projects"]),
cause: Schema.Defect(),
},
) {
override get message(): string {
return `Failed to scan agent sessions during ${this.operation}.`;
}
}