Donne à l'utilisateur la surface pour activer le serveur depuis l'app. - `features/settings/` : `SettingsView`, `DeploymentSettings`, `useDeployment`. - `adapters/desktopServer.ts` + port `DesktopServerGateway` et DTO associés ; adapters mock et http/unsupported alignés (le mode web n'expose pas le contrôle du serveur qui l'héberge). - `ProjectsView` : le `showSettings: boolean` devient une navigation interne `AI Profiles` / `Deployment`. Le libellé alternant « Close AI Profiles » disparaît — Settings existait déjà dans cette vue, la surface évolue au lieu d'ajouter un `PanelId`. CORRECTION D'UN BRIEF FAUX, remontée spontanément par DevFrontend et qui mérite de survivre : le cadrage décrivait le mode `remoteProxyOtherMachine` avec deux champs. `validate_settings` en exige un troisième, `lanBindAddress`, et rejette loopback comme unspecified. Construit selon la spec, chaque save et chaque start en mode 3 aurait échoué — le lot serait parti vert et cassé. Le panneau expose donc un select alimenté par `candidateLanAddresses` fourni par le backend : la règle « le frontend n'invente jamais une IP » tient. QA ré-exécutée par Git avant merge : `npm run typecheck` exit 0 · `npx vitest run` 87 files / 789 passed, 0 échec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1324 lines
44 KiB
TypeScript
1324 lines
44 KiB
TypeScript
/**
|
|
* Pure UI-domain types — TS mirrors of the backend DTOs (camelCase wire format,
|
|
* ARCHITECTURE §1.3). No React, no Tauri here: these are plain data shapes the
|
|
* ports speak in, so view logic and gateways stay testable in isolation.
|
|
*/
|
|
|
|
/** Health report returned by the `health` gateway/command. */
|
|
export interface HealthReport {
|
|
version: string;
|
|
alive: boolean;
|
|
timeMillis: number;
|
|
correlationId: string;
|
|
note: string | null;
|
|
}
|
|
|
|
/**
|
|
* Lifecycle status of a local model server during an agent launch (F35, mirror
|
|
* of the backend `ModelServerStatusDto`, tagged on `state`, camelCase wire).
|
|
*
|
|
* Contract note — the backend event carries the `serverId` on the *envelope*
|
|
* ({@link DomainEvent} `modelServerStatusChanged`), not inside the status, and it
|
|
* does NOT emit `baseURL`/`model` in the status payload: the UI reads what the
|
|
* backend actually sends. `reused` (on `ready`) tells apart a freshly-started
|
|
* server from a reused running one.
|
|
*/
|
|
export type ModelServerStatus =
|
|
| { state: "notConfigured" }
|
|
| { state: "probing" }
|
|
| { state: "starting" }
|
|
| {
|
|
/**
|
|
* The server is downloading/preparing a remote model. Progress fields are
|
|
* optional (all `null` in the F1 MVP; the bar/% display is the F2 stretch):
|
|
* the UI only needs `downloading` to raise the cell overlay.
|
|
*/
|
|
state: "downloading";
|
|
downloadedBytes: number | null;
|
|
totalBytes: number | null;
|
|
percent: number | null;
|
|
source: string | null;
|
|
}
|
|
| { state: "ready"; reused: boolean }
|
|
| { state: "failed"; code: string; message: string };
|
|
|
|
/**
|
|
* Stop policy of a managed local model server (mirror of the backend
|
|
* `StopPolicyDto`, camelCase wire):
|
|
* - `keepAlive`: leave the process running,
|
|
* - `stopOnAppExit`: stop when IdeA exits (the effective default),
|
|
* - `stopWhenUnused`: stop when no profile references it.
|
|
*/
|
|
export type StopPolicy = "keepAlive" | "stopOnAppExit" | "stopWhenUnused";
|
|
|
|
/**
|
|
* Where the served `.gguf` model comes from (F35 V2, mirror of the backend
|
|
* `ModelSourceDto`, camelCase wire, tagged on `type`):
|
|
* - `localPath`: an absolute `.gguf` path already present on disk (`--model`),
|
|
* - `huggingFace`: a `namespace/repo[:quant]` reference llama.cpp downloads and
|
|
* caches automatically (`-hf`).
|
|
*/
|
|
export type ModelSource =
|
|
| { type: "localPath"; path: string }
|
|
| { type: "huggingFace"; repo: string };
|
|
|
|
/**
|
|
* A declared local model server (F35, mirror of the backend flat
|
|
* `LocalModelServerConfigDto`, camelCase wire). Global to IdeA (not project
|
|
* scoped). Referenced by an OpenCode profile through
|
|
* `OpenCodeConfig.localModelServerId` (= this `id`).
|
|
*
|
|
* Contract notes:
|
|
* - `id` is a UUID **minted client-side** on create (the backend requires a valid
|
|
* UUID; it does not generate the server id, only an internal model id it hides).
|
|
* - `servedModelName` is what the user configures and what is sent to OpenCode as
|
|
* the `model` — the backend's internal `model.id`/`model.label` are not exposed.
|
|
* - `modelSource` (V2) replaces the former `modelPath`; it is required by the
|
|
* backend when `autoStart` is `true`.
|
|
*/
|
|
export interface LocalModelServerConfig {
|
|
id: string;
|
|
/** Only `llamaCpp` is supported today (camelCase, not "llamacpp"). */
|
|
kind: "llamaCpp";
|
|
name: string;
|
|
/** OpenAI-compatible base URL served by `llama-server` (normalised to `/v1`). */
|
|
baseURL: string;
|
|
port: number;
|
|
/** Explicit model source (`.gguf` path or Hugging Face ref). Required for auto-start. */
|
|
modelSource?: ModelSource;
|
|
/** Model name exposed by the server and sent to OpenCode as `model`. */
|
|
servedModelName: string;
|
|
/** Explicit `llama-server` executable path/command (optional). */
|
|
binaryPath?: string;
|
|
/** llama.cpp `--host` (default `127.0.0.1`). */
|
|
host: string;
|
|
/** llama.cpp `-ngl` GPU layers (optional; `0` = CPU only). */
|
|
gpuLayers?: number;
|
|
/** llama.cpp `-c` context size (optional). */
|
|
contextSize?: number;
|
|
/** Whether to pass `--jinja` (Jinja chat template). */
|
|
jinja: boolean;
|
|
args: string[];
|
|
autoStart: boolean;
|
|
stopPolicy: StopPolicy;
|
|
}
|
|
|
|
/**
|
|
* A previewed `llama-server` command line (F35 V2, mirror of the backend
|
|
* `PreviewModelServerCommandDto`). Built by the backend from a draft config —
|
|
* never reconstructed client-side.
|
|
*/
|
|
export interface ModelServerCommandPreview {
|
|
/** Resolved executable (e.g. `llama-server` or an absolute path). */
|
|
command: string;
|
|
/** Argument vector, unescaped. */
|
|
args: string[];
|
|
/** Human-readable, shell-escaped command line. */
|
|
display: string;
|
|
}
|
|
|
|
/**
|
|
* How the embedded web server is exposed (ticket #68, mirror of the backend
|
|
* `ServerExposureMode`).
|
|
*
|
|
* - `localOnly` — binds loopback, no remote access.
|
|
* - `remoteProxyLocal` — binds loopback; an HTTPS reverse proxy runs on *this*
|
|
* machine and reaches IdeA over loopback.
|
|
* - `remoteProxyOtherMachine` — binds a concrete LAN address so a proxy on
|
|
* *another* host can reach it; that proxy must be declared in
|
|
* `trustedProxies`.
|
|
*/
|
|
export type ServerExposureMode =
|
|
| "localOnly"
|
|
| "remoteProxyLocal"
|
|
| "remoteProxyOtherMachine";
|
|
|
|
/**
|
|
* Persisted embedded-server exposure settings (mirror of the backend
|
|
* `ServerExposureSettingsDto`).
|
|
*
|
|
* The backend validates every field on save/start; the UI never derives network
|
|
* facts (LAN addresses, upstream URL) from these — it asks `previewExposure`.
|
|
*/
|
|
export interface ServerExposureSettings {
|
|
mode: ServerExposureMode;
|
|
/** TCP port to bind. `0` asks the OS for an ephemeral port. */
|
|
port: number;
|
|
/** Public HTTPS origin, required by both remote modes. */
|
|
publicOrigin?: string;
|
|
/**
|
|
* Peers allowed to contact IdeA, as IPs or CIDRs. **Not** a listen address.
|
|
* Required (non-empty) by `remoteProxyOtherMachine`.
|
|
*/
|
|
trustedProxies: string[];
|
|
/**
|
|
* Concrete LAN address to bind, required by `remoteProxyOtherMachine`. Must
|
|
* come from {@link ServerExposurePreview.candidateLanAddresses} — the UI is
|
|
* never allowed to invent one.
|
|
*/
|
|
lanBindAddress?: string;
|
|
}
|
|
|
|
/** A non-fatal diagnostic from the exposure preview (never blocks a start). */
|
|
export interface DiagnosticWarning {
|
|
/** Stable warning code (e.g. `missingTrustedProxy`). */
|
|
code: string;
|
|
/** Human-readable, actionable message. */
|
|
message: string;
|
|
}
|
|
|
|
/**
|
|
* Backend-derived preview of a draft exposure config (mirror of
|
|
* `ServerExposurePreviewDto`). The backend is the sole authority on LAN
|
|
* addresses and the proxy upstream URL.
|
|
*/
|
|
export interface ServerExposurePreview {
|
|
/** LAN addresses the backend discovered on this host. */
|
|
candidateLanAddresses: string[];
|
|
/** URL the reverse proxy should target; absent in `localOnly`. */
|
|
upstreamUrl?: string;
|
|
/** Non-fatal diagnostics to surface alongside the form. */
|
|
warnings: DiagnosticWarning[];
|
|
}
|
|
|
|
/** Embedded-server lifecycle state (mirror of `EmbeddedServerStatusStateDto`). */
|
|
export type EmbeddedServerState =
|
|
| "stopped"
|
|
| "starting"
|
|
| "running"
|
|
| "stopping"
|
|
| "failed";
|
|
|
|
/**
|
|
* Embedded-server status (mirror of `EmbeddedServerStatusDto`). `pairingCode`
|
|
* is a runtime-only secret: it exists while the server runs, is never
|
|
* persisted, and must never be written into a settings field.
|
|
*/
|
|
export interface EmbeddedServerStatus {
|
|
state: EmbeddedServerState;
|
|
/** Local URL, when running. */
|
|
localUrl?: string;
|
|
/** Public URL, when a remote mode is configured. */
|
|
publicUrl?: string;
|
|
/** Upstream URL to hand to the reverse proxy. */
|
|
upstreamUrl?: string;
|
|
/** Runtime pairing code — present only while running. */
|
|
pairingCode?: string;
|
|
/** Last failure, when `state` is `failed`. */
|
|
error?: GatewayError;
|
|
}
|
|
|
|
/** A domain event relayed from the backend (tagged union on `type`). */
|
|
export type DomainEvent =
|
|
| { type: "projectCreated"; projectId: string }
|
|
| { type: "agentLaunched"; agentId: string; sessionId: string }
|
|
| { type: "agentExited"; agentId: string; code: number }
|
|
| {
|
|
/**
|
|
* A local model server changed lifecycle state while (re)launching an
|
|
* agent whose OpenCode profile binds `localModelServerId` (F35). Keyed by
|
|
* `serverId`; the frontend correlates it back to the agent through the
|
|
* profile's `opencode.localModelServerId`.
|
|
*/
|
|
type: "modelServerStatusChanged";
|
|
serverId: string;
|
|
status: ModelServerStatus;
|
|
}
|
|
| {
|
|
/**
|
|
* An agent launch failed before a runtime session was created (F35). Unlike
|
|
* a generic "agent failed", this carries an actionable `message` and a
|
|
* stable `code`; `cause` namespaces the failure (e.g. `"model_server"`).
|
|
*/
|
|
type: "agentLaunchFailed";
|
|
agentId: string;
|
|
cause: string;
|
|
code: string;
|
|
message: string;
|
|
}
|
|
| { type: "agentProfileChanged"; agentId: string; profileId: string }
|
|
| { type: "agentBusyChanged"; agentId: string; busy: boolean }
|
|
| {
|
|
type: "backgroundTaskChanged";
|
|
projectId: string;
|
|
taskId: string;
|
|
agentId: string;
|
|
state: string;
|
|
}
|
|
| {
|
|
type: "agentInboxChanged";
|
|
agentId: string;
|
|
depth: number;
|
|
action: string;
|
|
}
|
|
| {
|
|
type: "agentWakeChanged";
|
|
projectId: string;
|
|
agentId: string;
|
|
action: string;
|
|
reason?: string;
|
|
}
|
|
| {
|
|
/**
|
|
* A delegation is ready to be injected into the agent's native terminal
|
|
* (ARCHITECTURE §20). The backend is the queue/busy authority but no longer
|
|
* PTY-writes the turn: the frontend write-portal runs the handshake (b→e)
|
|
* and writes `text` + `submitSequence`. `submitSequence`/`submitDelayMs`
|
|
* come from the target's profile; absent ⇒ the portal applies its defaults
|
|
* (`"\r"`, ~350 ms).
|
|
*/
|
|
type: "delegationReady";
|
|
agentId: string;
|
|
ticket: string;
|
|
text: string;
|
|
submitSequence?: string;
|
|
submitDelayMs?: number;
|
|
}
|
|
| {
|
|
/**
|
|
* An agent entered a session/rate limit (ARCHITECTURE §21). Low-frequency,
|
|
* model-agnostic. `resetsAtMs` is the reset instant in epoch-milliseconds;
|
|
* absent ⇒ unknown (no reliable time → no auto-resume). The frontend badges
|
|
* "limité jusqu'à HH:MM" when known, "limité" otherwise.
|
|
*/
|
|
type: "agentRateLimited";
|
|
agentId: string;
|
|
resetsAtMs?: number;
|
|
}
|
|
| {
|
|
/**
|
|
* An auto-resume was armed for a rate-limited agent (ARCHITECTURE §21).
|
|
* `fireAtMs` is the wake-up deadline in epoch-milliseconds. The frontend
|
|
* shows the countdown + the "Annuler la reprise" button (cancellable window).
|
|
*/
|
|
type: "agentResumeScheduled";
|
|
agentId: string;
|
|
fireAtMs: number;
|
|
}
|
|
| {
|
|
/**
|
|
* An agent's auto-resume was cancelled (ARCHITECTURE §21): the user clicked
|
|
* "Annuler la reprise". The frontend removes the countdown but keeps the
|
|
* "limité" state (no auto-resume will fire).
|
|
*/
|
|
type: "agentResumeCancelled";
|
|
agentId: string;
|
|
}
|
|
| {
|
|
/**
|
|
* An agent was effectively resumed after a limit (ARCHITECTURE §21): the
|
|
* wake-up fired (or an immediate resume). The frontend clears all limit state.
|
|
*/
|
|
type: "agentResumed";
|
|
agentId: string;
|
|
}
|
|
| {
|
|
/**
|
|
* Human net (level 3): a session limit is suspected without any reliable
|
|
* reset time (ARCHITECTURE §21.1). IdeA never resumes blind: this asks the
|
|
* frontend to surface an "heure inconnue" state and prompt the user.
|
|
* `resetsAtMs` carries an estimate when one exists, else absent.
|
|
*/
|
|
type: "agentRateLimitSuspected";
|
|
agentId: string;
|
|
resetsAtMs?: number;
|
|
}
|
|
| { type: "templateUpdated"; templateId: string; version: number }
|
|
| { type: "agentDriftDetected"; agentId: string; from: number; to: number }
|
|
| { type: "agentSynced"; agentId: string; to: number }
|
|
| { type: "layoutChanged"; projectId: string }
|
|
| { type: "remoteConnected"; projectId: string }
|
|
| { type: "gitStateChanged"; projectId: string }
|
|
| { type: "memorySaved"; slug: string }
|
|
| {
|
|
type: "orchestratorRequestProcessed";
|
|
requesterId: string;
|
|
action: string;
|
|
ok: boolean;
|
|
/**
|
|
* Which entry door the delegation arrived through: `"mcp"` (MCP server) or
|
|
* `"file"` (`.ideai/requests` watcher). Optional for backward-compatibility:
|
|
* an event relayed without it simply yields no source badge.
|
|
*/
|
|
source?: "mcp" | "file";
|
|
}
|
|
| { type: "issueCreated"; issueId: string; issueRef: string }
|
|
| { type: "issueUpdated"; issueRef: string; version: number }
|
|
| {
|
|
/**
|
|
* A ticket was deleted (ticket #6). Mirror of the backend
|
|
* `DomainEventDto::IssueDeleted`. `issueRef` is the deleted `#N`;
|
|
* `freedSprint` is the sprint id it was released from, when any. Starts with
|
|
* `issue`, so {@link isTicketEvent} matches it and ticket lists refresh
|
|
* automatically; an open detail on the same `issueRef` closes.
|
|
*/
|
|
type: "issueDeleted";
|
|
projectId: string;
|
|
issueRef: string;
|
|
freedSprint: string | null;
|
|
}
|
|
| {
|
|
type: "issueStatusChanged";
|
|
issueRef: string;
|
|
status: TicketStatus;
|
|
version: number;
|
|
}
|
|
| {
|
|
type: "issuePriorityChanged";
|
|
issueRef: string;
|
|
priority: TicketPriority;
|
|
version: number;
|
|
}
|
|
| { type: "issueCarnetUpdated"; issueRef: string; version: number }
|
|
| {
|
|
type: "issueLinked";
|
|
issueRef: string;
|
|
target: string;
|
|
kind: TicketLinkKind;
|
|
version: number;
|
|
}
|
|
| {
|
|
type: "issueUnlinked";
|
|
issueRef: string;
|
|
target: string;
|
|
kind: TicketLinkKind;
|
|
version: number;
|
|
}
|
|
| {
|
|
type: "issueAgentAssigned";
|
|
issueRef: string;
|
|
agentId: string;
|
|
version: number;
|
|
}
|
|
| {
|
|
type: "issueAgentUnassigned";
|
|
issueRef: string;
|
|
agentId: string;
|
|
version: number;
|
|
}
|
|
| {
|
|
/**
|
|
* A ticket changed sprint membership (ticket #10). Mirror of the backend
|
|
* `DomainEventDto::IssueSprintChanged`. `from`/`to` are sprint ids (`null`
|
|
* ⇒ no sprint). Starts with `issue`, so {@link isTicketEvent} matches it
|
|
* and ticket lists refresh automatically.
|
|
*/
|
|
type: "issueSprintChanged";
|
|
issueRef: string;
|
|
from: string | null;
|
|
to: string | null;
|
|
version: number;
|
|
}
|
|
| { type: "sprintCreated"; sprintId: string; order: number }
|
|
| { type: "sprintRenamed"; sprintId: string; name: string; version: number }
|
|
| {
|
|
type: "sprintReordered";
|
|
sprintId: string;
|
|
order: number;
|
|
version: number;
|
|
}
|
|
| { type: "sprintDeleted"; sprintId: string }
|
|
| {
|
|
/**
|
|
* A ticket AI assistant chat was opened (ticket #8). Mirror of the backend
|
|
* `DomainEventDto::TicketAssistantOpened`. `issueRef` is the bound ticket,
|
|
* `profileId` the runtime AI profile driving the assistant.
|
|
*/
|
|
type: "ticketAssistantOpened";
|
|
issueRef: string;
|
|
profileId: string;
|
|
}
|
|
| {
|
|
/** A ticket AI assistant chat was closed/disposed (ticket #8). */
|
|
type: "ticketAssistantClosed";
|
|
issueRef: string;
|
|
}
|
|
| {
|
|
/**
|
|
* An intermediate assistant announcement emitted **during** an inter-agent
|
|
* structured turn (ticket #4, "annonces inter-agent"). Mirror of the backend
|
|
* `DomainEventDto::AgentAnnouncement`. Ephemeral (never persisted): the front
|
|
* folds these into a live, bounded, per-`(target, ticketId)` index that feeds
|
|
* the target-cell overlay (F3) and the requester-cell preview (F2).
|
|
*
|
|
* `requester` is `"user"` or the requesting agent's id; `target` is the agent
|
|
* being contacted; `text` is the human-readable réflexion/annonce.
|
|
*/
|
|
type: "agentAnnouncement";
|
|
projectId: string;
|
|
requester: string;
|
|
target: string;
|
|
ticketId: string;
|
|
text: string;
|
|
atMs: number;
|
|
}
|
|
| { type: "ptyOutput"; sessionId: string; bytes: number[] };
|
|
|
|
/**
|
|
* Whether a domain event is one of the ticket (`Issue*`) events. A small helper
|
|
* so ticket view-models refresh on any ticket mutation without re-enumerating
|
|
* every variant at each call site.
|
|
*/
|
|
export function isTicketEvent(
|
|
event: DomainEvent,
|
|
): event is Extract<DomainEvent, { type: `issue${string}` }> {
|
|
return event.type.startsWith("issue");
|
|
}
|
|
|
|
/**
|
|
* Whether a domain event is a sprint lifecycle event (`sprint*`). Lets the
|
|
* ticket view-model re-fetch the sprint list when sprints are created, renamed,
|
|
* reordered or deleted (ticket #10).
|
|
*/
|
|
export function isSprintEvent(
|
|
event: DomainEvent,
|
|
): event is Extract<DomainEvent, { type: `sprint${string}` }> {
|
|
return event.type.startsWith("sprint");
|
|
}
|
|
|
|
/** Where a project physically lives (mirror of the backend `RemoteRef`). */
|
|
export type RemoteRef =
|
|
| { kind: "local" }
|
|
| {
|
|
kind: "ssh";
|
|
host: string;
|
|
port: number;
|
|
user: string;
|
|
auth: unknown;
|
|
remoteRoot: string;
|
|
}
|
|
| { kind: "wsl"; distro: string };
|
|
|
|
/** A project as returned by the `project` gateway (mirror of `ProjectDto`). */
|
|
export interface Project {
|
|
id: string;
|
|
name: string;
|
|
root: string;
|
|
remote: RemoteRef;
|
|
createdAt: number;
|
|
}
|
|
|
|
/** Stable error shape mirrored from the backend `ErrorDto`. */
|
|
export interface GatewayError {
|
|
code: string;
|
|
message: string;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Work state (UX conversations/delegations live read-model)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Live session currently associated with an agent in the work-state read-model. */
|
|
export interface LiveWorkSession {
|
|
nodeId: string;
|
|
sessionId: string;
|
|
kind: "pty" | "structured";
|
|
}
|
|
|
|
/** Busy/idle status for an agent in the work-state read-model. */
|
|
export type WorkBusyState =
|
|
| { state: "idle" }
|
|
| { state: "busy"; ticket: string; sinceMs: number };
|
|
|
|
/** FIFO status for an input/ticket currently tracked by the backend queue. */
|
|
export type TicketWorkStatus = "inProgress" | "queued";
|
|
|
|
/** Who produced an input/ticket in the work-state read-model. */
|
|
export type TicketWorkSource =
|
|
| { kind: "human" }
|
|
| { kind: "agent"; agentId: string };
|
|
|
|
/** One queued or in-progress input/ticket for an agent. */
|
|
export interface AgentTicketState {
|
|
ticketId: string;
|
|
conversationId: string;
|
|
position: number;
|
|
status: TicketWorkStatus;
|
|
source: TicketWorkSource;
|
|
requesterLabel: string;
|
|
taskPreview: string;
|
|
taskLen: number;
|
|
}
|
|
|
|
/** One first-class background task visible in the work-state read-model. */
|
|
export interface BackgroundCompletion {
|
|
taskId: string;
|
|
ownerAgentId: string;
|
|
projectId: string;
|
|
kind: string;
|
|
status: "running" | "completed" | "failed" | "cancelled" | "pending" | "delivered";
|
|
exitCode: number | null;
|
|
/** Human-readable summary / error / reason of a terminal result (ticket #5). */
|
|
summary: string | null;
|
|
stdoutTail: string | null;
|
|
stderrTail: string | null;
|
|
/** Last-update timestamp (epoch ms); chronological ordering key. */
|
|
updatedAtMs: number;
|
|
}
|
|
|
|
/** One item currently pending in an agent's inbox. */
|
|
export interface InboxItem {
|
|
id: string;
|
|
agentId: string;
|
|
source: string;
|
|
kind: string;
|
|
body: string;
|
|
createdAtMs: number;
|
|
correlationId?: string;
|
|
priority: number;
|
|
}
|
|
|
|
/** Availability of a compact conversation summary in the work-state read-model. */
|
|
export type ConversationPreviewStatus =
|
|
| "ready"
|
|
| "missing"
|
|
| "partial"
|
|
| "unavailable";
|
|
|
|
/** Recent turn preview attached to a conversation summary. */
|
|
export interface ConversationTurnWorkPreview {
|
|
role: "prompt" | "response" | "toolActivity";
|
|
source: TicketWorkSource;
|
|
atMs: number;
|
|
textPreview: string;
|
|
textLen: number;
|
|
}
|
|
|
|
/** Compact thread context joined to tickets by conversation id. */
|
|
export interface ConversationWorkSummary {
|
|
conversationId: string;
|
|
status: ConversationPreviewStatus;
|
|
objectivePreview: string | null;
|
|
summaryPreview: string | null;
|
|
summaryLen: number;
|
|
upTo: string | null;
|
|
recentTurns: ConversationTurnWorkPreview[];
|
|
}
|
|
|
|
/** One agent row in the project work-state read-model. */
|
|
export interface AgentWorkState {
|
|
agentId: string;
|
|
name: string;
|
|
profileId: string;
|
|
live?: LiveWorkSession;
|
|
busy: WorkBusyState;
|
|
tickets: AgentTicketState[];
|
|
inboxDepth?: number;
|
|
inbox?: InboxItem[];
|
|
backgroundTasks?: BackgroundCompletion[];
|
|
}
|
|
|
|
/** Minimal read-only live-state surface for a project. */
|
|
export interface ProjectWorkState {
|
|
agents: AgentWorkState[];
|
|
conversations: ConversationWorkSummary[];
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Conversation transcript (LS7 — human thread-per-pair viewer, mirror of LS6 DTO)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Nature of a transcript turn (mirror of the backend `TurnRole`). */
|
|
export type TurnRole = "prompt" | "response" | "toolActivity";
|
|
|
|
/** Origin of a transcript turn (mirror of the backend `TurnSource`). */
|
|
export type TurnSource = { kind: "human" } | { kind: "agent"; agentId: string };
|
|
|
|
/** One turn of the human transcript — full text, never truncated. */
|
|
export interface TurnView {
|
|
id: string;
|
|
atMs: number;
|
|
role: TurnRole;
|
|
source: TurnSource;
|
|
text: string;
|
|
textLen: number;
|
|
}
|
|
|
|
/** Pagination travel direction (`"backward"` = towards older turns). */
|
|
export type PageDirection = "forward" | "backward";
|
|
|
|
/** A page of the human transcript, oldest-to-newest. */
|
|
export interface TurnPage {
|
|
turns: TurnView[];
|
|
hasMore: boolean;
|
|
nextAnchor?: string;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Permissions (LP1)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Agent capability governed by IdeA permissions. */
|
|
export type Capability = "read" | "write" | "delete" | "executeBash";
|
|
|
|
/** Permission rule verdict. */
|
|
export type PermissionEffect = "allow" | "deny";
|
|
|
|
/** Fallback stance when no permission rule matches. */
|
|
export type PermissionPosture = "ask" | "allow" | "deny";
|
|
|
|
/** Glob path scope, relative to the project root. */
|
|
export type PathScope = string[];
|
|
|
|
/** Shell command matcher. */
|
|
export type CommandMatcher =
|
|
| { kind: "exact"; value: string }
|
|
| { kind: "prefix"; value: string }
|
|
| { kind: "glob"; value: string };
|
|
|
|
/** Per-command bash verdict. */
|
|
export interface CommandRule {
|
|
matcher: CommandMatcher;
|
|
effect: PermissionEffect;
|
|
}
|
|
|
|
/** One permission rule. */
|
|
export interface PermissionRule {
|
|
capability: Capability;
|
|
effect: PermissionEffect;
|
|
paths?: PathScope;
|
|
commands?: CommandRule[];
|
|
}
|
|
|
|
/** Permission policy bundle. */
|
|
export interface PermissionSet {
|
|
rules: PermissionRule[];
|
|
fallback: PermissionPosture;
|
|
}
|
|
|
|
/** One sparse agent override in `.ideai/permissions.json`. */
|
|
export interface AgentPermissionOverride {
|
|
agentId: string;
|
|
permissions: PermissionSet;
|
|
}
|
|
|
|
/** Full project permission document. */
|
|
export interface ProjectPermissions {
|
|
version: number;
|
|
projectDefaults?: PermissionSet;
|
|
agents?: AgentPermissionOverride[];
|
|
}
|
|
|
|
/** Effective project+agent policy. */
|
|
export interface EffectivePermissions {
|
|
rules: PermissionRule[];
|
|
fallback: PermissionPosture;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Layout (L4) — mirror of the domain `LayoutTree` (ARCHITECTURE §3, §7).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Split direction: `row` = columns (left→right), `column` = rows (top→bottom). */
|
|
export type Direction = "row" | "column";
|
|
|
|
/** A terminal-hosting leaf cell. `session` is the hosted SessionId, if any.
|
|
* `agent` is the agent id if an agent is pinned to this cell (absent = plain terminal).
|
|
* `conversationId` is the persistent CLI conversation id (survives PTY close/reopen,
|
|
* lets the agent resume); omitted when absent (mirrors the backend `skip_serializing_if`).
|
|
* `agentWasRunning` records whether the cell's agent process was running at close time;
|
|
* omitted when `false`. */
|
|
export interface LeafCell {
|
|
id: string;
|
|
session?: string | null;
|
|
agent?: string;
|
|
conversationId?: string;
|
|
agentWasRunning?: boolean;
|
|
}
|
|
|
|
/** A weighted child of a split. `weight` is a relative (`> 0`) share. */
|
|
export interface WeightedChild {
|
|
node: LayoutNode;
|
|
weight: number;
|
|
}
|
|
|
|
/** A weighted n-ary split (rows or columns). */
|
|
export interface SplitContainer {
|
|
id: string;
|
|
direction: Direction;
|
|
children: WeightedChild[];
|
|
}
|
|
|
|
/** A spreadsheet grid cell placement with spans. */
|
|
export interface GridCell {
|
|
node: LayoutNode;
|
|
row: number;
|
|
col: number;
|
|
rowSpan: number;
|
|
colSpan: number;
|
|
}
|
|
|
|
/** A spreadsheet-style grid with per-row/col weights and span-based merging. */
|
|
export interface GridContainer {
|
|
id: string;
|
|
colWeights: number[];
|
|
rowWeights: number[];
|
|
cells: GridCell[];
|
|
}
|
|
|
|
/**
|
|
* A node in the layout tree. Tagged on `type` with the payload under `node`,
|
|
* matching the backend `#[serde(tag = "type", content = "node")]`.
|
|
*/
|
|
export type LayoutNode =
|
|
| { type: "leaf"; node: LeafCell }
|
|
| { type: "split"; node: SplitContainer }
|
|
| { type: "grid"; node: GridContainer };
|
|
|
|
/** The root of a tab's terminal layout. */
|
|
export interface LayoutTree {
|
|
root: LayoutNode;
|
|
}
|
|
|
|
/**
|
|
* A layout mutation (mirror of the backend `LayoutOperationDto`, tagged on
|
|
* `type`). Node/session ids are UUID strings.
|
|
*/
|
|
export type LayoutOperation =
|
|
| {
|
|
type: "split";
|
|
target: string;
|
|
direction: Direction;
|
|
newLeaf: string;
|
|
container: string;
|
|
}
|
|
| { type: "merge"; container: string; keepIndex: number }
|
|
| { type: "resize"; container: string; weights: number[] }
|
|
| { type: "move"; from: string; to: string }
|
|
| { type: "setSession"; target: string; session?: string | null }
|
|
| { type: "setCellAgent"; target: string; agent: string | null }
|
|
| { type: "setCellConversation"; target: string; conversationId: string | null }
|
|
| { type: "setAgentRunning"; target: string; running: boolean };
|
|
|
|
/** The kind of a named layout. */
|
|
export type LayoutKind = "terminal" | "gitGraph";
|
|
|
|
/** Named layout entry returned by `listLayouts`. */
|
|
export interface LayoutInfo {
|
|
id: string;
|
|
name: string;
|
|
kind: LayoutKind;
|
|
}
|
|
|
|
/**
|
|
* A commit node in the full git graph (DAG). `refs` carries short branch/tag
|
|
* names (e.g. `"main"`, `"tag: v1.0"`). `timestamp` is seconds since Unix epoch.
|
|
*/
|
|
export interface GraphCommit {
|
|
hash: string;
|
|
summary: string;
|
|
parents: string[];
|
|
refs: string[];
|
|
author: string;
|
|
timestamp: number;
|
|
}
|
|
|
|
/** Response of `listLayouts`. */
|
|
export interface LayoutList {
|
|
layouts: LayoutInfo[];
|
|
activeId: string;
|
|
}
|
|
|
|
/** Unsubscribe handle returned by event subscriptions. */
|
|
export type Unsubscribe = () => void;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// AI profiles & first-run (L5) — mirror of the domain `AgentProfile` /
|
|
// `ContextInjection` (CONTEXT §9, ARCHITECTURE §3).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Context-injection strategy (tagged on `strategy`, mirror of the backend
|
|
* `ContextInjection`):
|
|
* - `conventionFile`: write the `.md` to a conventional file (e.g. `CLAUDE.md`),
|
|
* - `flag`: pass the context path through a CLI flag,
|
|
* - `stdin`: pipe the content on stdin,
|
|
* - `env`: pass the context via an environment variable.
|
|
*/
|
|
export type ContextInjection =
|
|
| { strategy: "conventionFile"; target: string }
|
|
| { strategy: "flag"; flag: string }
|
|
| { strategy: "stdin" }
|
|
| { strategy: "env"; var: string };
|
|
|
|
/** The four injection strategy discriminants. */
|
|
export type InjectionStrategy = ContextInjection["strategy"];
|
|
|
|
/**
|
|
* Structured-execution adapter of a profile (mirror of the backend
|
|
* `StructuredAdapter`, camelCase wire format). Absent (`structuredAdapter`
|
|
* omitted) ⇒ the profile is a plain TUI/PTY agent (historical behaviour);
|
|
* present ⇒ the profile is selectable as a structured AI agent:
|
|
* - `claude` / `codex` / `openCode`: driven by a spawned CLI binary,
|
|
* - `openAiCompatible`: driven by IdeA's native HTTP adapter against an
|
|
* OpenAI-compatible chat server (Ollama, llama.cpp, a LAN runtime) — see
|
|
* {@link HttpChatConfig}.
|
|
*/
|
|
export type StructuredAdapter = "claude" | "codex" | "openCode" | "openAiCompatible";
|
|
|
|
/**
|
|
* HTTP configuration of an OpenAI-compatible chat server (mirror of the backend
|
|
* `HttpChatConfig`, camelCase wire format). Carried by a profile whose
|
|
* {@link StructuredAdapter} is `openAiCompatible`.
|
|
*
|
|
* Transparent by design: secrets are never stored here — `apiKeyEnv` is the
|
|
* *name* of an environment variable, never the key itself (the backend resolves
|
|
* it at call time).
|
|
*/
|
|
export interface HttpChatConfig {
|
|
/** Root or `/chat/completions` endpoint of the HTTP server (http/https). */
|
|
endpoint: string;
|
|
/** Model name sent in the `/chat/completions` payload. */
|
|
model: string;
|
|
/** Name of the env var holding the API key (optional). Never the key. */
|
|
apiKeyEnv?: string;
|
|
/** Per-turn request timeout, in milliseconds. */
|
|
requestTimeoutMs?: number;
|
|
/** Connection timeout, in milliseconds. */
|
|
connectTimeoutMs?: number;
|
|
/** Tool-calling re-loop guard for one turn (defaults to ~16 backend-side). */
|
|
maxToolIterations?: number;
|
|
}
|
|
|
|
/**
|
|
* Configuration for an OpenCode process-backed profile pointed at its own
|
|
* llama.cpp (OpenAI-compatible) endpoint. Mirror of the backend `OpenCodeConfig`
|
|
* (camelCase wire format: `baseURL`, `apiKey?`, `model`, `reasoning?`,
|
|
* `attachment?`). OpenCode is a locally-piloted CLI, distinct from the in-process
|
|
* {@link HttpChatConfig} adapter.
|
|
*/
|
|
export interface OpenCodeConfig {
|
|
/**
|
|
* OpenAI-compatible base URL served by `llama-server` (http/https), for
|
|
* example `http://localhost:8080/v1`.
|
|
*/
|
|
baseURL: string;
|
|
/**
|
|
* Optional API key forwarded to the provider — the key *itself*, not an env
|
|
* var name (a local llama.cpp typically needs none / a placeholder). Omitted
|
|
* when blank (mirrors the backend `skip_serializing_if`).
|
|
*/
|
|
apiKey?: string;
|
|
/** Model name served by `llama-server`, for example `qwen3-coder-30b`. */
|
|
model: string;
|
|
/** Enables model reasoning. Effective default: `true`. */
|
|
reasoning?: boolean;
|
|
/** Enables attachments. Effective default: `false`. */
|
|
attachment?: boolean;
|
|
/**
|
|
* Optional id of a managed local model server (F35) this profile binds to.
|
|
* When set, IdeA can start/stop the referenced `llama-server` for the profile;
|
|
* omitted when the profile points at an externally-managed endpoint (mirrors
|
|
* the backend `skip_serializing_if`). Opaque string id on the wire.
|
|
*/
|
|
localModelServerId?: string;
|
|
}
|
|
|
|
/**
|
|
* A declarative AI-CLI profile (mirror of the backend `AgentProfile`). `id` is a
|
|
* UUID string; `detect` is the optional detection command line.
|
|
*/
|
|
export interface AgentProfile {
|
|
id: string;
|
|
name: string;
|
|
command: string;
|
|
args: string[];
|
|
contextInjection: ContextInjection;
|
|
detect: string | null;
|
|
cwdTemplate: string;
|
|
/** Optional CLI flags for agent-session continuity: `assignFlag` to bind a
|
|
* conversation id at launch, `resumeFlag` to resume an existing conversation. */
|
|
session?: { assignFlag?: string; resumeFlag: string };
|
|
/**
|
|
* Structured-execution adapter (mirror of the backend `structured_adapter`,
|
|
* `skip_serializing_if = Option::is_none`). Absent ⇒ plain TUI/PTY profile;
|
|
* present ⇒ selectable structured AI agent. Additive: existing Claude/Codex
|
|
* profiles are unaffected.
|
|
*/
|
|
structuredAdapter?: StructuredAdapter;
|
|
/**
|
|
* HTTP config for a `openAiCompatible` structured adapter (mirror of the
|
|
* backend `chat_http`, `skip_serializing_if = Option::is_none`). Absent for
|
|
* every historical profile and every non-HTTP adapter.
|
|
*/
|
|
chatHttp?: HttpChatConfig;
|
|
/** OpenCode process-backed config. Present for `structuredAdapter: "openCode"`. */
|
|
opencode?: OpenCodeConfig;
|
|
}
|
|
|
|
/** Availability of a candidate profile after detection (mirror of the DTO). */
|
|
export interface ProfileAvailability {
|
|
profile: AgentProfile;
|
|
available: boolean;
|
|
}
|
|
|
|
/** First-run state (mirror of `FirstRunStateDto`). */
|
|
export interface FirstRunState {
|
|
isFirstRun: boolean;
|
|
referenceProfiles: AgentProfile[];
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Agents (L6) — mirror of the domain `Agent` / `AgentOrigin` (ARCHITECTURE §6).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Origin of an agent (tagged on `type`, mirror of the backend `AgentOrigin`):
|
|
* - `scratch`: created from scratch, no template link,
|
|
* - `fromTemplate`: derived from a template, tracking the last synced version.
|
|
*/
|
|
export type AgentOrigin =
|
|
| { type: "scratch" }
|
|
| { type: "fromTemplate"; templateId: string; syncedTemplateVersion: number };
|
|
|
|
/**
|
|
* A project-scoped agent (mirror of the backend `Agent` DTO, camelCase wire
|
|
* format). `contextPath` is the relative path of the agent's `.md` within
|
|
* `.ideai/` (e.g. `agents/foo.md`).
|
|
*/
|
|
export interface Agent {
|
|
id: string;
|
|
name: string;
|
|
contextPath: string;
|
|
profileId: string;
|
|
origin: AgentOrigin;
|
|
synchronized: boolean;
|
|
/** Skills assigned to this agent (injected into its convention file). */
|
|
skills: SkillRef[];
|
|
}
|
|
|
|
/**
|
|
* A terminal/PTY session as returned by the backend (mirror of
|
|
* `TerminalSessionDto`, camelCase wire format). Surfaced by `changeAgentProfile`
|
|
* as the freshly relaunched session when a live agent was hot-swapped.
|
|
*/
|
|
export interface TerminalSession {
|
|
/** Stable session id (UUID) — used for write/resize/close + the output channel. */
|
|
sessionId: string;
|
|
/** Working directory the shell runs in. */
|
|
cwd: string;
|
|
/** Current rows. */
|
|
rows: number;
|
|
/** Current cols. */
|
|
cols: number;
|
|
/**
|
|
* Conversation id assigned by this (re)launch, when the profile supports
|
|
* session assignment and the hosting cell had none yet; absent otherwise.
|
|
*/
|
|
assignedConversationId?: string;
|
|
}
|
|
|
|
/**
|
|
* An agent that can be resumed when its project is (re)opened (mirror of the
|
|
* backend `ResumableAgentDto`, camelCase wire format; ARCHITECTURE §15.2). It is
|
|
* a read-only inventory entry computed at open time — never a spawn:
|
|
* - `nodeId` is the layout leaf hosting the agent (where to relaunch),
|
|
* - `conversationId` is the persisted CLI conversation id; absent ⇒ relaunch fresh,
|
|
* - `wasRunning` is `agentWasRunning` frozen at close time (drives the status label),
|
|
* - `resumeSupported` reflects whether the agent's profile exposes a usable
|
|
* session strategy; when `false`, "Reprendre" relaunches fresh ("relance à neuf").
|
|
*/
|
|
export interface ResumableAgent {
|
|
agentId: string;
|
|
name: string;
|
|
nodeId: string;
|
|
conversationId?: string;
|
|
wasRunning: boolean;
|
|
resumeSupported: boolean;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Skills (L12) — mirror of the domain `Skill` / `SkillRef`.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Where a skill lives (selects its backing store): `global` skills are reusable
|
|
* across projects; `project` skills are specific to one project's `.ideai/`.
|
|
*/
|
|
export type SkillScope = "global" | "project";
|
|
|
|
/**
|
|
* A reusable, model-agnostic workflow assignable to agents (mirror of the
|
|
* backend `Skill` DTO, camelCase wire format).
|
|
*/
|
|
export interface Skill {
|
|
id: string;
|
|
name: string;
|
|
contentMd: string;
|
|
scope: SkillScope;
|
|
}
|
|
|
|
/** A reference from an agent to one assigned skill (mirror of `SkillRef`). */
|
|
export interface SkillRef {
|
|
skillId: string;
|
|
scope: SkillScope;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Memory (L14) — mirror of the backend `MemoryDto` / `MemoryIndexEntryDto`.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* The category of a memory note (selects how it is recalled/injected). Mirrors
|
|
* the backend `type` field; identity of a note is its **slug** (kebab-case).
|
|
*/
|
|
export type MemoryType = "user" | "feedback" | "project" | "reference";
|
|
|
|
/**
|
|
* A memory note (mirror of the backend `Memory` DTO, camelCase wire format).
|
|
* `name` doubles as the human title and the source of the slug identity.
|
|
*/
|
|
export interface Memory {
|
|
name: string;
|
|
description: string;
|
|
type: MemoryType;
|
|
content: string;
|
|
}
|
|
|
|
/**
|
|
* One entry of the memory index (mirror of the backend `MemoryIndexEntry`):
|
|
* a lightweight, recall-oriented projection of a note.
|
|
*/
|
|
export interface MemoryIndexEntry {
|
|
slug: string;
|
|
title: string;
|
|
hook: string;
|
|
type: MemoryType;
|
|
}
|
|
|
|
/** A resolved `[[wikilink]]` target — the slug of another note. */
|
|
export type MemoryLink = string;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Embedder (L14 / lot C2) — mirror of the backend `EmbedderProfileDto` and
|
|
// `EmbedderEnginesDto`. Drives the memory/embedder settings panel. Identity of
|
|
// a profile is its `id`; changing the active embedder takes effect at the next
|
|
// app start (the UI says so explicitly).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Embedding strategy of an embedder profile (mirror of the backend `strategy`).
|
|
* `none` ⇒ no vector tier (naïve recall); the other strategies select where the
|
|
* embeddings come from.
|
|
*/
|
|
export type EmbedderStrategy = "localOnnx" | "localServer" | "api" | "none";
|
|
|
|
/**
|
|
* A declarative embedder profile (mirror of the backend `EmbedderProfile` DTO,
|
|
* camelCase wire format). Transparent by design: secrets are never stored here —
|
|
* `apiKeyEnv` is the *name* of an environment variable, never the key itself.
|
|
*/
|
|
export interface EmbedderProfile {
|
|
id: string;
|
|
name: string;
|
|
strategy: EmbedderStrategy;
|
|
/** Model id/name (ONNX model, server/api model). Omitted for `none`. */
|
|
model?: string;
|
|
/** Server/API endpoint URL. Omitted for `localOnnx` / `none`. */
|
|
endpoint?: string;
|
|
/** Name of the env var holding the API key (api strategy). Never the key. */
|
|
apiKeyEnv?: string;
|
|
/** Embedding vector dimension. */
|
|
dimension: number;
|
|
}
|
|
|
|
/**
|
|
* A recommended local ONNX engine (mirror of the backend `recommendedOnnx`
|
|
* entry). `e5-small` is the curated default (`recommended: true`).
|
|
*/
|
|
export interface RecommendedOnnxEngine {
|
|
id: string;
|
|
displayName: string;
|
|
dimension: number;
|
|
approxSizeMb: number;
|
|
recommended: boolean;
|
|
}
|
|
|
|
/**
|
|
* Capabilities/availability snapshot of the embedder engines on this build
|
|
* (mirror of the backend `describe_embedder_engines`). The `vector*Enabled`
|
|
* flags reflect Cargo feature flags compiled into the build: a strategy whose
|
|
* flag is `false` is shown disabled ("not available in this build").
|
|
*/
|
|
export interface EmbedderEngines {
|
|
recommendedOnnx: RecommendedOnnxEngine[];
|
|
ollamaDetected: boolean;
|
|
onnxCachedModels: string[];
|
|
vectorHttpEnabled: boolean;
|
|
vectorOnnxEnabled: boolean;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Templates (L7) — mirror of the domain `Template` / `AgentDrift`.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* A reusable agent template (mirror of the backend `Template` DTO).
|
|
* `version` is incremented on each `updateTemplate` call.
|
|
*/
|
|
export interface Template {
|
|
id: string;
|
|
name: string;
|
|
contentMd: string;
|
|
version: number;
|
|
defaultProfileId: string;
|
|
}
|
|
|
|
/**
|
|
* Drift between a synchronized agent's last-synced template version and the
|
|
* template's current version (mirror of `AgentDrift` backend DTO).
|
|
*/
|
|
export interface AgentDrift {
|
|
agentId: string;
|
|
from: number;
|
|
to: number;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Git (L8) — UI-domain types for the git feature.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** A file in the working tree with its staging state. */
|
|
export interface GitFileStatus {
|
|
path: string;
|
|
staged: boolean;
|
|
}
|
|
|
|
/** A git commit summary (short representation). */
|
|
export interface GitCommit {
|
|
hash: string;
|
|
summary: string;
|
|
}
|
|
|
|
/** The list of branches and the currently checked-out branch. */
|
|
export interface GitBranches {
|
|
branches: string[];
|
|
current: string | null;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Tickets (public wire name for the backend `Issue` model — "ticket" is the
|
|
// user-visible term; all commands/DTOs/events speak `ticket`/`issue`). Mirrors
|
|
// the `Ticket*Dto` shapes in `crates/app-tauri/src/tickets.rs` (camelCase).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** A ticket reference on the wire, always `#<n>` (e.g. `"#42"`). */
|
|
export type TicketRef = string;
|
|
|
|
/** Lifecycle status of a ticket. */
|
|
export type TicketStatus = "open" | "inProgress" | "QA" | "closed";
|
|
|
|
/** Priority of a ticket. */
|
|
export type TicketPriority = "low" | "medium" | "high" | "critical";
|
|
|
|
/** The kind of relationship between two tickets. */
|
|
export type TicketLinkKind =
|
|
| "relatesTo"
|
|
| "blocks"
|
|
| "blockedBy"
|
|
| "duplicates"
|
|
| "dependsOn";
|
|
|
|
/** Who performed a ticket mutation (mirror of `TicketActorDto`, tagged `kind`). */
|
|
export type TicketActor =
|
|
| { kind: "user" }
|
|
| { kind: "agent"; agentId: string }
|
|
| { kind: "system" };
|
|
|
|
/** One directed link from a ticket to another (mirror of `TicketLinkDto`). */
|
|
export interface TicketLink {
|
|
targetRef: TicketRef;
|
|
kind: TicketLinkKind;
|
|
}
|
|
|
|
/** Lifecycle status of a sprint (mirror of the backend `SprintStatus`). */
|
|
export type SprintStatus = "planned" | "active" | "done";
|
|
|
|
/** A sprint bucket tickets can belong to (mirror of `SprintDto`). */
|
|
export interface Sprint {
|
|
id: string;
|
|
/** Reorderable position; sections are displayed by ascending `order`. */
|
|
order: number;
|
|
name: string;
|
|
status: SprintStatus;
|
|
/** How many tickets currently belong to this sprint. */
|
|
ticketCount: number;
|
|
/** Optimistic-concurrency version. */
|
|
version: number;
|
|
}
|
|
|
|
/** The full ticket, as returned by `ticket_read`/`ticket_create`/… . */
|
|
export interface Ticket {
|
|
id: string;
|
|
ref: TicketRef;
|
|
number: number;
|
|
title: string;
|
|
description: string;
|
|
status: TicketStatus;
|
|
priority: TicketPriority;
|
|
/** Sprint membership (`null` ⇒ no sprint). Additive (ticket #10). */
|
|
sprintId?: string | null;
|
|
/** Present only when the ticket was read with `includeCarnet`. */
|
|
carnet?: string;
|
|
links: TicketLink[];
|
|
assignedAgentIds: string[];
|
|
createdBy: TicketActor;
|
|
updatedBy: TicketActor;
|
|
createdAt: number;
|
|
updatedAt: number;
|
|
/** Optimistic-concurrency version; echoed back as `expectedVersion` on writes. */
|
|
version: number;
|
|
}
|
|
|
|
/** A ticket summary row from `ticket_list` (mirror of `TicketSummaryDto`). */
|
|
export interface TicketSummary {
|
|
ref: TicketRef;
|
|
path: string;
|
|
title: string;
|
|
status: TicketStatus;
|
|
priority: TicketPriority;
|
|
/** Sprint membership (`null` ⇒ no sprint). Additive (ticket #10). */
|
|
sprintId?: string | null;
|
|
assignedAgentIds: string[];
|
|
updatedAt: number;
|
|
}
|
|
|
|
/** One page of ticket summaries (mirror of `TicketListDto`). */
|
|
export interface TicketList {
|
|
items: TicketSummary[];
|
|
/** Opaque cursor for the next page, absent when the list is exhausted. */
|
|
nextCursor?: string;
|
|
}
|
|
|
|
/** The scoped-to-a-ticket Markdown carnet (mirror of `TicketCarnetDto`). */
|
|
export interface TicketCarnet {
|
|
ref: TicketRef;
|
|
carnet: string;
|
|
version: number;
|
|
}
|
|
|
|
/**
|
|
* A ticket AI assistant chat session (mirror of the backend `TicketChatDto`,
|
|
* ticket #8). `sessionId` is the live structured session driving the assistant;
|
|
* `requester` is the assistant agent id; `issueRef` the bound ticket.
|
|
*/
|
|
export interface TicketChat {
|
|
sessionId: string;
|
|
requester: string;
|
|
issueRef: TicketRef;
|
|
}
|
|
|
|
/**
|
|
* One streamed chunk of an assistant turn (mirror of the backend `ReplyChunk`,
|
|
* tagged on `kind`). `textDelta` is an incremental text fragment; `toolActivity`
|
|
* a best-effort activity badge; `final` the deterministic end-of-turn chunk
|
|
* carrying the aggregated content — after it the turn is frozen. `error` is a
|
|
* visible terminal fallback (ticket #60): the model produced no usable answer,
|
|
* so the turn ends with a human-readable explanation instead of a silent hang.
|
|
*/
|
|
export type ReplyChunk =
|
|
| { kind: "textDelta"; text: string }
|
|
| { kind: "toolActivity"; label: string }
|
|
| { kind: "final"; content: string }
|
|
| { kind: "error"; message: string };
|