import type { DaemonSession } from './types.js'; import type { Session, StreamStatus } from '../types.js'; import type { CliId } from '../adapters/cli/types.js'; import { type Brand } from '../im/lark/lark-hosts.js'; import { type SessionTokenUsage } from './cost-calculator.js'; import { type SessionPreviewTarget } from './session-preview.js'; import { type SessionMessagePreview } from './session-message-preview.js'; export interface SessionRow extends SessionMessagePreview { sessionId: string; larkAppId: string; botName: string; cliId: CliId | 'unknown'; /** Concrete distribution identity frozen on the session. Older path-only * sessions expose a basename-derived legacy identity. */ runtimeId?: string; runtimeDisplayName?: string; status: StreamStatus | 'closed' | 'dormant'; adopt: boolean; spawnedAt: number; lastMessageAt: number; closedAt?: number; workingDir?: string; chatId: string; chatType?: 'group' | 'p2p'; chatDisplayName?: string; rootMessageId: string; threadId?: string; /** Whether the most recent inbound turn was authored by another Bot. * This is deliberately latest-turn provenance, not durable collaboration * ancestry: the persisted quote target is overwritten on every inbound * message. Dashboard labels derived from it must therefore say inferred. */ lastInputFromBot?: boolean; /** Conversation unit ('thread' = topic-anchored, 'chat' = plain chat scope). * Drives the board's locate button: chat-scope sessions have no topic to * locate, so the dashboard offers "open chat" (feishuChatLink) instead. * Absent on rows from older daemons → callers keep the locate behavior. */ scope?: 'thread' | 'chat'; title?: string; titleUpdatedAt?: string; /** Informational only; callers must not treat it as authenticated identity. */ titleSource?: Session['titleSource']; /** Backend stamped at spawn time. Exposed so external surfaces can filter zmx/tmux/etc. */ backendType?: Session['backendType']; /** Persisted backing multiplexer host name, not proof the target is currently live. */ backendSessionName?: string; /** 看板视图的手动放置(列 id / 列内排序位置),用户拖拽后持久化在 Session 上。 * 未设置时前端按运行状态推导默认列。 */ kanbanColumn?: string; kanbanPosition?: number; /** Locked sessions are protected from dashboard idle cleanup. */ locked?: boolean; ownerOpenId?: string; webPort: number | null; /** Owning daemon's advertised reverse-proxy port — WEB_EXTERNAL_PORT + botIndex * when configured, else the bound proxy port (0/undefined if the proxy isn't * up). When set, the terminal is reachable at {host}:{proxyPort}/s/{sessionId}. * Mirrors the port buildTerminalUrl puts in card links so both agree. */ proxyPort?: number; /** Internal daemon→dashboard routing data. The central browser projection * removes it and emits only `preview: { path, registeredAt }`. */ previewTarget?: SessionPreviewTarget; cliVersion?: string; hasHistory?: boolean; feishuChatLink: string; /** Direct AppLink to the session topic. It exists only when the native * `omt_...` id is known; the `om_...` routing root is not interchangeable. */ feishuThreadLink?: string; /** Repo-selection card is waiting for a click — the CLI has not spawned yet. * Feeds the board view's needs-you column. */ pendingRepo?: boolean; /** Dashboard「创建会话」入待办池:会话已建但 CLI 未起(parked),等激活才开跑。 * 前端据此在卡片上显示「待开始 / 开始」入口、并把卡片钉在待办池列。 */ queued?: boolean; /** A TUI prompt card is open and waiting for the user's choice. * Feeds the board view's needs-you column. */ tuiPromptActive?: boolean; /** The agent raised a hand (`botmux send --attention`) — it hit a blocker * needing human intervention. Carries the human-readable reason so the * board/overview can show *why* at a glance, plus `at` (epoch ms when it * was raised) so the UI shows a true "waiting since" time — NOT lastMessageAt, * which a silent raise never bumps. Feeds the needs-you column. */ agentAttention?: { kind: string; reason: string; at: number; }; /** 任务态(会话状态重设计 P2):从 CLI transcript 读到的当前 TODO 完成度。 * 与运行态正交——「运行态=空闲 + 有未完成 todo」正是要抓的「机器停了活没干完」。 * 仅 Claude / Codex 家族能提取;其它 CLI、无 transcript、从未建 todo 时为 undefined * (= 未知/不支持,前端不据此判「已交付」)。 */ openTodos?: { total: number; done: number; remaining: number; hasInProgress: boolean; items: Array<{ status: string; text: string; }>; }; /** Native Agent CLI token usage for this session. Null means unavailable. */ tokenUsage?: SessionTokenUsage | null; /** Worker process PID, active rows only. Used by dashboard resource attribution. */ workerPid?: number; /** Persisted row intentionally kept active but detached from daemon routing * because exact backing teardown could not be proven. It is rendered as * dormant (never closed) and remains explicitly closeable for a retry. */ quarantined?: boolean; /** Adopted external CLI PID, active rows only when the source backend exposed it. */ adoptCliPid?: number; /** Riff AIO Sandbox web terminal link. When set, the dashboard "Web终端" * button opens this URL directly instead of building a local port link. */ riffAccessUrl?: string; /** Presentation enrichment stamped by the central dashboard read-model: * bot avatar URL from the live daemon descriptor. * Absent on older daemons — consumers must fall back. */ botAvatarUrl?: string; /** Repo top-level dir name of workingDir, when it is a git repo. */ repoName?: string; /** Current branch of workingDir; absent for detached HEAD / non-repo. */ gitBranch?: string; } export declare function feishuChatLink(chatId: string, brand?: Brand): string; export declare function setBotName(name: string): void; export declare function getBotName(): string; export declare function sessionLastActivityAtMs(s: Session): number; export declare function composeRowFromActive(ds: DaemonSession, opts?: { fresh?: boolean; }): SessionRow; export declare function composeRowFromClosed(s: Session): SessionRow; /** * Project a persisted active row that currently has no DaemonSession runtime. * * This is deliberately not composeRowFromClosed(): `status='active'` remains * the ownership truth when a persistent-backend kill was inconclusive. The * dashboard presents it as dormant, with no terminal port, so operators can * see and explicitly retry closing it without an unsafe resume affordance. */ export declare function composeRowFromPersistedActive(s: Session): SessionRow; //# sourceMappingURL=dashboard-rows.d.ts.map