import { existsSync, readdirSync, unlinkSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import { join } from 'path';

// src/chat/types.ts
function createChatId() {
  return crypto.randomUUID();
}

// src/errors.ts
var AgentSDKError = class extends Error {
  /** @internal Marker for cross-bundle identity checks */
  _agentSDKError = true;
  /** Machine-readable error code. Prefer values from the ErrorCode enum. */
  code;
  /** Whether this error is safe to retry */
  retryable;
  /** HTTP status code hint for error classification */
  httpStatus;
  constructor(message, options) {
    super(message, options);
    this.name = "AgentSDKError";
    this.code = options?.code;
    this.retryable = options?.retryable ?? false;
    this.httpStatus = options?.httpStatus;
  }
  /** Check if an error is an AgentSDKError (works across bundled copies) */
  static is(error) {
    return error instanceof Error && "_agentSDKError" in error && error._agentSDKError === true;
  }
};

// src/chat/storage.ts
var StorageError = class extends AgentSDKError {
  /** Machine-readable error code from the unified ErrorCode enum */
  code;
  constructor(message, code) {
    super(message);
    this.name = "StorageError";
    this.code = code;
  }
};
var InMemoryStorage = class {
  data = /* @__PURE__ */ new Map();
  /** @inheritdoc */
  async get(key) {
    const item = this.data.get(key);
    return item !== void 0 ? structuredClone(item) : null;
  }
  /** @inheritdoc */
  async list(options) {
    let items = Array.from(this.data.values()).map((item) => structuredClone(item));
    if (options?.filter) {
      items = items.filter(options.filter);
    }
    if (options?.sort) {
      items.sort(options.sort);
    }
    if (options?.offset !== void 0) {
      items = items.slice(options.offset);
    }
    if (options?.limit !== void 0) {
      items = items.slice(0, options.limit);
    }
    return items;
  }
  /** @inheritdoc */
  async create(key, item) {
    if (this.data.has(key)) {
      throw new StorageError(
        `Item with key "${key}" already exists`,
        "STORAGE_DUPLICATE_KEY" /* STORAGE_DUPLICATE_KEY */
      );
    }
    this.data.set(key, structuredClone(item));
  }
  /** @inheritdoc */
  async update(key, item) {
    if (!this.data.has(key)) {
      throw new StorageError(
        `Item with key "${key}" not found`,
        "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
      );
    }
    this.data.set(key, structuredClone(item));
  }
  /** @inheritdoc */
  async delete(key) {
    if (!this.data.has(key)) {
      throw new StorageError(
        `Item with key "${key}" not found`,
        "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
      );
    }
    this.data.delete(key);
  }
  /** @inheritdoc */
  async has(key) {
    return this.data.has(key);
  }
  /** @inheritdoc */
  async count() {
    return this.data.size;
  }
  /** @inheritdoc */
  async clear() {
    this.data.clear();
  }
};
var FileStorage = class {
  directory;
  extension;
  constructor(options) {
    this.directory = options.directory;
    this.extension = options.extension ?? ".json";
    this.ensureDirectory();
  }
  /** @inheritdoc */
  async get(key) {
    const filePath = this.keyToPath(key);
    if (!existsSync(filePath)) {
      return null;
    }
    return this.readFile(filePath);
  }
  /** @inheritdoc */
  async list(options) {
    this.ensureDirectory();
    const files = readdirSync(this.directory).filter(
      (f) => f.endsWith(this.extension)
    );
    let items = [];
    for (const file of files) {
      const item = this.readFile(join(this.directory, file));
      items.push(item);
    }
    if (options?.filter) {
      items = items.filter(options.filter);
    }
    if (options?.sort) {
      items.sort(options.sort);
    }
    if (options?.offset !== void 0) {
      items = items.slice(options.offset);
    }
    if (options?.limit !== void 0) {
      items = items.slice(0, options.limit);
    }
    return items;
  }
  /** @inheritdoc */
  async create(key, item) {
    const filePath = this.keyToPath(key);
    if (existsSync(filePath)) {
      throw new StorageError(
        `Item with key "${key}" already exists`,
        "STORAGE_DUPLICATE_KEY" /* STORAGE_DUPLICATE_KEY */
      );
    }
    this.writeFile(filePath, item);
  }
  /** @inheritdoc */
  async update(key, item) {
    const filePath = this.keyToPath(key);
    if (!existsSync(filePath)) {
      throw new StorageError(
        `Item with key "${key}" not found`,
        "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
      );
    }
    this.writeFile(filePath, item);
  }
  /** @inheritdoc */
  async delete(key) {
    const filePath = this.keyToPath(key);
    if (!existsSync(filePath)) {
      throw new StorageError(
        `Item with key "${key}" not found`,
        "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
      );
    }
    unlinkSync(filePath);
  }
  /** @inheritdoc */
  async has(key) {
    return existsSync(this.keyToPath(key));
  }
  /** @inheritdoc */
  async count() {
    this.ensureDirectory();
    return readdirSync(this.directory).filter(
      (f) => f.endsWith(this.extension)
    ).length;
  }
  /** @inheritdoc */
  async clear() {
    this.ensureDirectory();
    const files = readdirSync(this.directory).filter(
      (f) => f.endsWith(this.extension)
    );
    for (const file of files) {
      unlinkSync(join(this.directory, file));
    }
  }
  keyToPath(key) {
    const safeKey = key.replace(
      /[^a-zA-Z0-9_-]/g,
      (c) => "%" + c.charCodeAt(0).toString(16).padStart(2, "0")
    );
    return join(this.directory, `${safeKey}${this.extension}`);
  }
  ensureDirectory() {
    if (!existsSync(this.directory)) {
      mkdirSync(this.directory, { recursive: true });
    }
  }
  readFile(filePath) {
    try {
      const content = readFileSync(filePath, "utf-8");
      return JSON.parse(content);
    } catch (error) {
      if (error instanceof SyntaxError) {
        throw new StorageError(
          `Failed to parse file: ${filePath}`,
          "STORAGE_SERIALIZATION_ERROR" /* STORAGE_SERIALIZATION_ERROR */
        );
      }
      throw new StorageError(
        `Failed to read file: ${filePath}`,
        "STORAGE_IO_ERROR" /* STORAGE_IO_ERROR */
      );
    }
  }
  writeFile(filePath, item) {
    try {
      const content = JSON.stringify(item, null, 2);
      writeFileSync(filePath, content, "utf-8");
    } catch {
      throw new StorageError(
        `Failed to write file: ${filePath}`,
        "STORAGE_IO_ERROR" /* STORAGE_IO_ERROR */
      );
    }
  }
};

// src/chat/sessions.ts
var BaseSessionStore = class {
  constructor(adapter) {
    this.adapter = adapter;
  }
  async createSession(options) {
    const now = (/* @__PURE__ */ new Date()).toISOString();
    const id = createChatId();
    const session = {
      id,
      title: options.title ?? "Untitled",
      messages: [],
      config: {
        model: options.config?.model ?? "",
        backend: options.config?.backend ?? "",
        ...options.config
      },
      metadata: {
        messageCount: 0,
        totalTokens: 0,
        tags: options.tags ? [...options.tags] : void 0,
        custom: options.custom ? { ...options.custom } : void 0
      },
      status: "active",
      createdAt: now,
      updatedAt: now
    };
    await this.adapter.create(id, session);
    return structuredClone(session);
  }
  async getSession(id) {
    return this.adapter.get(id);
  }
  async listSessions(options) {
    return this.adapter.list(options);
  }
  async updateTitle(id, title) {
    const session = await this.adapter.get(id);
    if (!session) {
      throw new StorageError(`Session "${id}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
    }
    session.title = title;
    session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
    await this.adapter.update(id, session);
  }
  async updateConfig(id, config) {
    const session = await this.adapter.get(id);
    if (!session) {
      throw new StorageError(`Session "${id}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
    }
    session.config = { ...session.config, ...config };
    session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
    await this.adapter.update(id, session);
  }
  async deleteSession(id) {
    await this.adapter.delete(id);
  }
  async appendMessage(sessionId, message) {
    const session = await this.adapter.get(sessionId);
    if (!session) {
      throw new StorageError(`Session "${sessionId}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
    }
    session.messages.push(structuredClone(message));
    session.metadata.messageCount = session.messages.length;
    session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
    await this.adapter.update(sessionId, session);
  }
  async saveMessages(sessionId, messages) {
    if (messages.length === 0) return;
    const session = await this.adapter.get(sessionId);
    if (!session) {
      throw new StorageError(`Session "${sessionId}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
    }
    for (const msg of messages) {
      session.messages.push(structuredClone(msg));
    }
    session.metadata.messageCount = session.messages.length;
    session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
    await this.adapter.update(sessionId, session);
  }
  async loadMessages(sessionId, options) {
    const session = await this.adapter.get(sessionId);
    if (!session) {
      throw new StorageError(`Session "${sessionId}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
    }
    const total = session.messages.length;
    const offset = options?.offset ?? 0;
    const limit = options?.limit ?? total;
    const messages = session.messages.slice(offset, offset + limit);
    return {
      messages: structuredClone(messages),
      total,
      hasMore: offset + limit < total
    };
  }
  async searchSessions(options) {
    const query = options.query.toLowerCase();
    const limit = options.limit ?? 20;
    return this.adapter.list({
      filter: (session) => {
        if (session.title?.toLowerCase().includes(query)) return true;
        return session.messages.some((msg) => {
          return msg.parts.some(
            (part) => part.type === "text" && part.text.toLowerCase().includes(query)
          );
        });
      },
      limit
    });
  }
  async count() {
    return this.adapter.count();
  }
  async clear() {
    return this.adapter.clear();
  }
};
var InMemorySessionStore = class extends BaseSessionStore {
  constructor() {
    super(new InMemoryStorage());
  }
};
var FileSessionStore = class extends BaseSessionStore {
  constructor(options) {
    super(new FileStorage({ directory: options.directory }));
  }
};

export { FileSessionStore, InMemorySessionStore, StorageError };
//# sourceMappingURL=sessions.js.map
//# sourceMappingURL=sessions.js.map