// src/stream/stream-normalizer.ts
function asRecord(value) {
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
}
function asString(value) {
  return typeof value === "string" && value.length > 0 ? value : void 0;
}
function resolveToolId(part) {
  return String(
    part.id ?? part.callID ?? part.callId ?? part.toolUseId ?? part.toolCallId ?? part.tool ?? part.name ?? `tool-${Date.now()}`
  );
}
function resolveToolName(part) {
  return String(part.tool ?? part.name ?? "tool");
}
function normalizeTime(value) {
  const record = asRecord(value);
  if (!record) return void 0;
  const start = Number(record.start ?? record.startedAt ?? record.started_at);
  const end = Number(record.end ?? record.completedAt ?? record.completed_at);
  if (!Number.isFinite(start) && !Number.isFinite(end)) return void 0;
  return {
    start: Number.isFinite(start) ? start : void 0,
    end: Number.isFinite(end) ? end : void 0
  };
}
function normalizeToolEvent(event) {
  if (event.type === "tool_call" || event.type === "tool.call") {
    const data = event.data ?? {};
    return {
      type: "message.part.updated",
      data: {
        part: {
          type: "tool",
          id: data.id ?? data.callId ?? data.callID ?? data.name,
          tool: data.name ?? data.tool ?? "tool",
          input: data.arguments ?? data.input,
          status: "running"
        }
      }
    };
  }
  if (event.type === "tool_result" || event.type === "tool.result") {
    const data = event.data ?? {};
    const error = asString(data.error);
    return {
      type: "message.part.updated",
      data: {
        part: {
          type: "tool",
          id: data.id ?? data.callId ?? data.callID ?? data.name,
          tool: data.name ?? data.tool ?? "tool",
          output: data.output,
          error,
          status: error ? "error" : "completed"
        }
      }
    };
  }
  return event;
}
function normalizePersistedPart(rawPart) {
  const type = String(rawPart.type ?? "");
  if (type === "text") {
    return {
      type: "text",
      text: asString(rawPart.text) ?? asString(rawPart.content) ?? ""
    };
  }
  if (type === "reasoning") {
    return {
      type: "reasoning",
      text: asString(rawPart.text) ?? asString(rawPart.content) ?? "",
      time: normalizeTime(rawPart.time)
    };
  }
  if (type === "tool") {
    const state = asRecord(rawPart.state);
    const output = state?.output ?? rawPart.output;
    const error = asString(state?.error ?? rawPart.error);
    const status = state?.status === "completed" || rawPart.status === "completed" ? "completed" : state?.status === "error" || rawPart.status === "error" || error ? "error" : output !== void 0 ? "completed" : "running";
    return {
      type: "tool",
      id: resolveToolId(rawPart),
      tool: resolveToolName(rawPart),
      callID: rawPart.callID != null || rawPart.callId != null ? String(rawPart.callID ?? rawPart.callId) : void 0,
      state: {
        status,
        input: state?.input ?? rawPart.input,
        output,
        error,
        metadata: asRecord(state?.metadata) ?? asRecord(rawPart.metadata),
        time: normalizeTime(state?.time ?? rawPart.time)
      }
    };
  }
  return null;
}
function getPartKey(part) {
  const type = String(part.type ?? "unknown");
  if (type === "tool") {
    return `tool:${resolveToolId(part)}`;
  }
  if (type === "reasoning") {
    return `reasoning:${String(part.id ?? part.partId ?? part.index ?? "current")}`;
  }
  return `text:${String(part.id ?? part.partId ?? part.index ?? "current")}`;
}
function mergePersistedPart(existing, incoming, delta) {
  const type = String(incoming.type ?? "");
  if (!existing) {
    if (type === "text" && delta) {
      return { type: "text", text: delta };
    }
    return incoming;
  }
  if (type === "text" && String(existing.type ?? "") === "text") {
    return {
      ...existing,
      ...incoming,
      text: delta ? `${String(existing.text ?? "")}${delta}` : String(incoming.text ?? "")
    };
  }
  if (type === "reasoning" && String(existing.type ?? "") === "reasoning") {
    const existingText = String(existing.text ?? "");
    const incomingText = String(incoming.text ?? "");
    return {
      ...existing,
      ...incoming,
      text: delta && incomingText === existingText ? `${existingText}${delta}` : incomingText || existingText,
      time: incoming.time ?? existing.time
    };
  }
  if (type === "tool" && String(existing.type ?? "") === "tool") {
    return {
      ...existing,
      ...incoming,
      state: {
        ...asRecord(existing.state) ?? {},
        ...asRecord(incoming.state) ?? {},
        time: asRecord(incoming.state)?.time ?? asRecord(existing.state)?.time
      }
    };
  }
  return incoming;
}
function finalizeAssistantParts(partOrder, partMap, finalText) {
  const parts = partOrder.map((key) => partMap.get(key)).filter((part) => Boolean(part));
  if (!parts.some((part) => String(part.type ?? "") === "text")) {
    if (finalText.trim()) {
      parts.push({ type: "text", text: finalText });
    }
    return parts;
  }
  return parts.map((part) => {
    if (String(part.type ?? "") !== "text") return part;
    return {
      ...part,
      text: finalText || String(part.text ?? "")
    };
  });
}
function encodeEvent(encoder, event) {
  return encoder.encode(`${JSON.stringify(event)}
`);
}

// src/stream/turn-identity.ts
function normalizeClientTurnId(value) {
  if (value === void 0 || value === null) return void 0;
  if (typeof value !== "string") throw new Error("turnId must be a string");
  const trimmed = value.trim();
  if (!trimmed) throw new Error("turnId must not be blank");
  if (trimmed.length > 160) throw new Error("turnId is too long");
  if (!/^[A-Za-z0-9:_-]+$/.test(trimmed)) {
    throw new Error("turnId contains unsupported characters");
  }
  return trimmed;
}
function buildUserTextParts(text, turnId) {
  const part = { type: "text", text };
  if (turnId) part.turnId = turnId;
  return [part];
}
function messageHasTurnId(message, turnId) {
  for (const part of message.parts ?? []) {
    if (part && typeof part === "object" && String(part.turnId ?? "") === turnId) {
      return true;
    }
  }
  return false;
}
function resolveChatTurn(input) {
  const { existingMessages, userContent, turnId } = input;
  const reusableIndex = findReusableUserMessageIndex(existingMessages, userContent, turnId);
  if (reusableIndex >= 0) {
    return {
      turnIndex: countUserMessages(existingMessages.slice(0, reusableIndex)),
      shouldInsertUserMessage: false,
      priorMessages: existingMessages.slice(0, reusableIndex),
      userParts: buildUserTextParts(userContent, turnId)
    };
  }
  return {
    turnIndex: countUserMessages(existingMessages),
    shouldInsertUserMessage: true,
    priorMessages: existingMessages,
    userParts: buildUserTextParts(userContent, turnId)
  };
}
function findReusableUserMessageIndex(messages, userContent, turnId) {
  if (turnId) {
    for (let index = messages.length - 1; index >= 0; index -= 1) {
      const message = messages[index];
      if (message?.role === "user" && messageHasTurnId(message, turnId)) return index;
    }
  }
  const latest = messages.at(-1);
  if (latest?.role === "user" && latest.content === userContent) {
    return messages.length - 1;
  }
  return -1;
}
function countUserMessages(messages) {
  return messages.filter((message) => message.role === "user").length;
}

// src/stream/turn-buffer.ts
function deltaTypeOf(ev) {
  const e = ev;
  if (!e || typeof e !== "object") return null;
  const inner = e.kind === "event" ? e.event : e;
  if (!inner || typeof inner !== "object") return null;
  if ((inner.type === "text" || inner.type === "reasoning") && typeof inner.text === "string") {
    return inner.type;
  }
  return null;
}
function coalesceDeltas(events) {
  const out = [];
  for (const ev of events) {
    const type = deltaTypeOf(ev);
    const prev = out[out.length - 1];
    if (type && prev && deltaTypeOf(prev) === type) {
      const read = (x) => x.kind === "event" ? x.event : x;
      const merged = JSON.parse(JSON.stringify(prev));
      read(merged).text = String(read(prev).text) + String(read(ev).text);
      out[out.length - 1] = merged;
      continue;
    }
    out.push(ev);
  }
  return out;
}
async function pumpBufferedTurn(opts) {
  const flushIntervalMs = opts.flushIntervalMs ?? 400;
  const startedAt = Date.now();
  let seq = 0;
  let clientGone = false;
  let pending = [];
  let lastFlush = Date.now();
  async function flush() {
    if (pending.length === 0) return;
    const batch = coalesceDeltas(pending);
    pending = [];
    const rows = batch.map((ev) => ({ seq: ++seq, event: JSON.stringify(ev) }));
    await opts.store.append(opts.turnId, rows);
    lastFlush = Date.now();
  }
  await opts.store.setStatus(opts.turnId, "running");
  try {
    for await (const raw of opts.source) {
      const ev = raw && typeof raw === "object" ? { ...raw, _t: Date.now() - startedAt } : raw;
      pending.push(ev);
      if (!clientGone && opts.write) {
        try {
          await opts.write(JSON.stringify(ev));
        } catch {
          clientGone = true;
        }
      }
      if (Date.now() - lastFlush >= flushIntervalMs) await flush();
    }
    await flush();
    await opts.store.setStatus(opts.turnId, "complete");
  } catch (err) {
    await flush().catch(() => {
    });
    await opts.store.setStatus(opts.turnId, "error").catch(() => {
    });
    throw err;
  }
}
async function* replayTurnEvents(opts) {
  const pollMs = opts.pollMs ?? 500;
  const timeoutMs = opts.timeoutMs ?? 12e4;
  let cursor = opts.fromSeq ?? 0;
  const deadline = Date.now() + timeoutMs;
  for (; ; ) {
    const batch = await opts.store.read(opts.turnId, cursor);
    for (const row of batch) {
      cursor = Math.max(cursor, row.seq);
      yield row;
    }
    const status = await opts.store.getStatus(opts.turnId);
    if (status !== "running") {
      yield { seq: -1, event: JSON.stringify({ type: "turn_status", status: status ?? "unknown" }) };
      return;
    }
    if (Date.now() >= deadline) {
      yield { seq: -1, event: JSON.stringify({ type: "turn_status", status: "timeout" }) };
      return;
    }
    await new Promise((r) => setTimeout(r, pollMs));
  }
}
var TURN_EVENTS_MIGRATION_SQL = `
CREATE TABLE IF NOT EXISTS turn_events (
  turnId TEXT NOT NULL,
  seq INTEGER NOT NULL,
  event TEXT NOT NULL,
  PRIMARY KEY (turnId, seq)
);
CREATE TABLE IF NOT EXISTS turn_status (
  turnId TEXT PRIMARY KEY,
  status TEXT NOT NULL,
  updatedAt TEXT NOT NULL
);
`;
function createD1TurnEventStore(db) {
  return {
    async append(turnId, events) {
      if (!events.length) return;
      const placeholders = events.map(() => "(?, ?, ?)").join(", ");
      const values = events.flatMap((e) => [turnId, e.seq, e.event]);
      await db.prepare(`INSERT OR IGNORE INTO turn_events (turnId, seq, event) VALUES ${placeholders}`).bind(...values).run();
    },
    async read(turnId, fromSeq) {
      const { results } = await db.prepare("SELECT seq, event FROM turn_events WHERE turnId = ? AND seq > ? ORDER BY seq ASC").bind(turnId, fromSeq).all();
      return results;
    },
    async setStatus(turnId, status) {
      await db.prepare(
        "INSERT INTO turn_status (turnId, status, updatedAt) VALUES (?, ?, ?) ON CONFLICT(turnId) DO UPDATE SET status = excluded.status, updatedAt = excluded.updatedAt"
      ).bind(turnId, status, (/* @__PURE__ */ new Date()).toISOString()).run();
    },
    async getStatus(turnId) {
      const row = await db.prepare("SELECT status FROM turn_status WHERE turnId = ?").bind(turnId).first();
      return row?.status ?? null;
    }
  };
}
function createMemoryTurnEventStore() {
  const events = /* @__PURE__ */ new Map();
  const status = /* @__PURE__ */ new Map();
  return {
    async append(turnId, rows) {
      const list = events.get(turnId) ?? [];
      list.push(...rows);
      events.set(turnId, list);
    },
    async read(turnId, fromSeq) {
      return (events.get(turnId) ?? []).filter((e) => e.seq > fromSeq);
    },
    async setStatus(turnId, s) {
      status.set(turnId, s);
    },
    async getStatus(turnId) {
      return status.get(turnId) ?? null;
    }
  };
}

export {
  asRecord,
  asString,
  resolveToolId,
  resolveToolName,
  normalizeTime,
  normalizeToolEvent,
  normalizePersistedPart,
  getPartKey,
  mergePersistedPart,
  finalizeAssistantParts,
  encodeEvent,
  normalizeClientTurnId,
  buildUserTextParts,
  messageHasTurnId,
  resolveChatTurn,
  coalesceDeltas,
  pumpBufferedTurn,
  replayTurnEvents,
  TURN_EVENTS_MIGRATION_SQL,
  createD1TurnEventStore,
  createMemoryTurnEventStore
};
//# sourceMappingURL=chunk-CPI3RILI.js.map