@useatlas/types
Advanced tools
| /** | ||
| * Wire format for `executeSQL` tool results. | ||
| * | ||
| * Single-environment executions emit a 1-element `envContributions` array | ||
| * (just the one connection that ran). Cross-environment fanouts (PRD #2515, | ||
| * `scope: "all"` or picker mode `all`) emit one entry per member of the | ||
| * active connection group, so SDK consumers can render side-by-side panes | ||
| * keyed by `connectionId`. | ||
| * | ||
| * The runtime shape produced by `mergeMemberResults` in | ||
| * `@atlas/api/lib/multi-env-merger` mirrors {@link ConnectionContribution} | ||
| * exactly. The merger's interface IS the wire shape — both sides converged | ||
| * during slice 4 (#2519). | ||
| * | ||
| * @see PRD #2515 — agent-routed cross-environment querying | ||
| * @see issue #2519 — slice 4 acceptance criteria | ||
| */ | ||
| /** | ||
| * Per-environment execution metadata surfaced alongside merged rows. | ||
| * | ||
| * `error` is explicitly `null` (not omitted) on success so the JSON wire | ||
| * shape is stable — clients can render `c.error ?? "OK"` without an | ||
| * `in` check. `durationMs` is wall-clock for that one member's | ||
| * execution; the overall tool-call `executionMs` is the max across | ||
| * members (parallel fanout). | ||
| */ | ||
| export interface ConnectionContribution { | ||
| /** Connection id that this member executed against. */ | ||
| readonly connectionId: string; | ||
| /** Rows produced by this member. `0` when the member errored or returned no rows. */ | ||
| readonly rowCount: number; | ||
| /** Error message if this member failed; `null` on success. */ | ||
| readonly error: string | null; | ||
| /** Wall-clock duration of this member's execution in milliseconds. */ | ||
| readonly durationMs: number; | ||
| } | ||
| /** | ||
| * Successful `executeSQL` tool-call payload. | ||
| * | ||
| * `envContributions` is always present and has at least one entry — even | ||
| * for single-environment executions, where it carries a 1-element array | ||
| * describing the lone connection. This keeps the consumer code path | ||
| * uniform between single-env and fanout responses (slice 4 invariant). | ||
| * | ||
| * `executionMs` is the *overall* tool-call duration. For fanouts that's | ||
| * the max member duration (Promise.allSettled runs in parallel); for | ||
| * single-env it equals the member's `envContributions[0].durationMs`. | ||
| */ | ||
| export interface ExecuteSqlSuccessResult { | ||
| readonly success: true; | ||
| readonly explanation?: string; | ||
| readonly row_count: number; | ||
| readonly columns: readonly string[]; | ||
| readonly rows: readonly Record<string, unknown>[]; | ||
| readonly truncated?: boolean; | ||
| readonly cached?: boolean; | ||
| readonly maskingApplied?: boolean; | ||
| readonly executionMs?: number; | ||
| readonly envContributions?: readonly ConnectionContribution[]; | ||
| } | ||
| /** | ||
| * Failed `executeSQL` tool-call payload. | ||
| * | ||
| * `envContributions` is present on cross-environment "all members failed" | ||
| * outcomes so consumers can attribute the failure per connection. Other | ||
| * failure shapes (validation errors, approval required, etc.) omit the | ||
| * field entirely — the success/failure decision happened before any | ||
| * member executed. | ||
| */ | ||
| export interface ExecuteSqlFailureResult { | ||
| readonly success: false; | ||
| readonly explanation?: string; | ||
| readonly error?: string; | ||
| readonly executionMs?: number; | ||
| readonly envContributions?: readonly ConnectionContribution[]; | ||
| /** Other fields (approval_required, approval_request_id, etc.) flow through as `unknown`. */ | ||
| readonly [key: string]: unknown; | ||
| } | ||
| /** | ||
| * Wire format for the `executeSQL` tool-call result, as emitted in the | ||
| * `tool-output-available` SSE event and persisted in conversation | ||
| * messages. | ||
| */ | ||
| export type ExecuteSqlResult = ExecuteSqlSuccessResult | ExecuteSqlFailureResult; |
| /** | ||
| * Shared wire types for the proactive chat layer (PRD #2291, 1.5.0). | ||
| * | ||
| * Before this module, `plugins/chat/src/proactive/types.ts` and | ||
| * `packages/api/src/lib/proactive/*` declared structurally-identical | ||
| * mirror types ("shape-by-shape — declared here so the plugin doesn't | ||
| * import `@atlas/api`"). Those mirrors are the exact drift CLAUDE.md | ||
| * "shared types live in `@useatlas/types`" was written to prevent — | ||
| * any change to one side that misses the other silently breaks the | ||
| * wire. | ||
| * | ||
| * This module hosts the canonical shapes both sides import. Type-only: | ||
| * no runtime exports (per the @useatlas/types scaffold gotcha — adding | ||
| * VALUE exports breaks scaffold CI until the package is republished). | ||
| * Branded identifiers + constructor functions live in the API package | ||
| * because they need a runtime mint helper; the plugin can still cast | ||
| * at its boundaries when those land in a follow-up. | ||
| * | ||
| * What landed here in the 1.5.0 polish: | ||
| * - `AnnouncementOutcome.reason` is a tagged union — the metrics | ||
| * rollup no longer has to string-parse `reason: string` into | ||
| * `announcer_threw:${message}` shaped values. | ||
| * - `AllowDecision` (public-dataset gate) is a tagged union over | ||
| * refusal reasons — replaces the `metric-denied:${metric}` packed | ||
| * string with a structured `{ kind: "metric-denied"; metric }` | ||
| * so audit consumers can pluck `metric` without re-parsing. | ||
| * | ||
| * Kept flat (deferred to a follow-up architecture-wins PR — each | ||
| * cascades through 50+ callsites + test assertions and is best done | ||
| * as a standalone narrowing-migration PR): | ||
| * - `PauseDecision` (boolean-blind `layer?: PauseLayer`) | ||
| * - `ClassificationResult.isQuestion` (boolean blindness) | ||
| * - `ProactiveMeterEvent` (eventType-conditional field shape) | ||
| * - Branded `WorkspaceId` / `ChannelId` / `UserId` / `Confidence` / | ||
| * `MicroUSD` / `Millis` / `EpochMs` (primitive obsession) | ||
| */ | ||
| /** | ||
| * Channel-scoped pause layers (`channel_id IS NOT NULL`). | ||
| * | ||
| * Split from `PauseLayer` so the type system makes a row that says | ||
| * it's `channel-24h` carry a non-null channel id at the type level. | ||
| */ | ||
| export type ChannelPauseLayer = "channel-24h" | "admin-channel"; | ||
| /** | ||
| * The four pause shapes recognised by the registry. | ||
| * | ||
| * - `channel-24h` — in-channel `@atlas pause` (channel-scoped, 24h) | ||
| * - `admin-channel` — per-channel admin deny (channel-scoped, indefinite) | ||
| * - `workspace-kill` — admin "pause all proactive" (workspace-wide, indefinite) | ||
| * - `user-optout` — DM `unsubscribe` (per-user, workspace-wide, indefinite) | ||
| */ | ||
| export type PauseLayer = ChannelPauseLayer | "workspace-kill" | "user-optout"; | ||
| /** | ||
| * Outcome of a pause-registry lookup. | ||
| * | ||
| * Kept flat (`{ paused: boolean; layer?; until? }`) rather than | ||
| * discriminated by `paused` because every existing test assertion | ||
| * (`expect(decision.layer).toBe("workspace-kill")`) and the admin | ||
| * pauses route's `decision.layer === "workspace-kill"` check expect | ||
| * `layer` to be accessible regardless of TS narrowing. Discriminating | ||
| * this type is a future architecture-wins refactor — see this module | ||
| * header for rationale. | ||
| * | ||
| * `until` is epoch ms; absent on indefinite pauses (workspace-kill, | ||
| * admin-channel, user-optout). `channel-24h` always has `until`. | ||
| * `layer` is absent only when `paused: false`. | ||
| */ | ||
| export interface PauseDecision { | ||
| paused: boolean; | ||
| layer?: PauseLayer; | ||
| /** Epoch ms when the pause expires; absent on indefinite pauses. */ | ||
| until?: number; | ||
| } | ||
| /** Three-tier sensitivity preset. Maps to a confidence threshold in policy. */ | ||
| export type SensitivityPreset = "cautious" | "balanced" | "eager"; | ||
| /** Result of running a message through the question classifier. */ | ||
| export interface ClassificationResult { | ||
| /** Whether the message looks like an answerable data question. */ | ||
| isQuestion: boolean; | ||
| /** Confidence in [0, 1] — 1.0 = certain, 0.0 = certainly not. */ | ||
| confidence: number; | ||
| /** Optional short reason from the LLM, useful for audit + tuning. */ | ||
| reasoning?: string; | ||
| } | ||
| /** Lifecycle stages tracked by the proactive answer meter. */ | ||
| export type ProactiveMeterEventType = "classify" | "react" | "offer" | "accept" | "feedback" | "public_refused"; | ||
| /** Outcome values captured on `feedback` events. */ | ||
| export type ProactiveMeterOutcome = "helpful" | "not-helpful" | "wrong-data" | "no-feedback"; | ||
| /** | ||
| * One row of the proactive meter. Plugin emits these via the host's | ||
| * `onMeterEvent` callback; API writes them to `proactive_meter_events`. | ||
| * | ||
| * Field shape is deliberately flat — discriminating per `eventType` | ||
| * would force every emitter to switch on the type before constructing | ||
| * the payload. Deferred to a follow-up; see module header. | ||
| */ | ||
| export interface ProactiveMeterEvent { | ||
| workspaceId: string; | ||
| channelId: string; | ||
| messageId?: string | null; | ||
| eventType: ProactiveMeterEventType; | ||
| outcome?: ProactiveMeterOutcome | null; | ||
| tokens?: number; | ||
| costMicroUsd?: number; | ||
| confidence?: number | null; | ||
| actorUserId?: string | null; | ||
| metadata?: Record<string, unknown>; | ||
| } | ||
| /** One entry on a workspace's curated public-dataset allowlist. */ | ||
| export interface PublicDatasetEntry { | ||
| /** Fully-qualified entity name (e.g. `marketing.users`). */ | ||
| entityName: string; | ||
| /** Column / measure names denied within this entity. May be empty. */ | ||
| denyMetrics: string[]; | ||
| } | ||
| /** | ||
| * Allowlist verdict for one entity touch. | ||
| * | ||
| * Tagged union: refusal reason is a structured `kind` rather than the | ||
| * pre-polish `deniedReason: \`metric-denied:${metric}\`` packed string. | ||
| * Audit consumers pluck `kind` / `metric` directly instead of parsing | ||
| * a packed string. | ||
| */ | ||
| export type AllowDecision = { | ||
| allowed: true; | ||
| } | { | ||
| allowed: false; | ||
| kind: "entity-not-in-allowlist"; | ||
| } | { | ||
| allowed: false; | ||
| kind: "metric-denied"; | ||
| /** The denied metric the query touched. */ | ||
| metric: string; | ||
| }; | ||
| /** Quota snapshot returned by the host quota-status reader. */ | ||
| export interface ProactiveQuotaStatus { | ||
| /** Cap value persisted on the workspace config. Null = unlimited. */ | ||
| monthlyClassifierCap: number | null; | ||
| /** Distinct classify rows since the start of the current UTC month. */ | ||
| classifyCountThisMonth: number; | ||
| /** True when `classifyCountThisMonth >= monthlyClassifierCap`. */ | ||
| capReached: boolean; | ||
| /** | ||
| * True when the underlying DB read failed and the snapshot is the | ||
| * fail-open default. Listener emits a `classify` meter row tagged | ||
| * `skipped: "quota-read-failed"` so the bypass surfaces in the | ||
| * analytics rollup. | ||
| */ | ||
| readFailed?: boolean; | ||
| } | ||
| /** | ||
| * Outcome of a `announceActivation` call. Tagged union on `reason` so | ||
| * metrics consumers can pivot on the rejection class without parsing | ||
| * the message — replaces the pre-polish `reason: string` shape. | ||
| */ | ||
| export type AnnouncementOutcome = { | ||
| posted: true; | ||
| messageId?: string; | ||
| } | { | ||
| posted: false; | ||
| reason: "no_internal_db"; | ||
| } | { | ||
| posted: false; | ||
| reason: "no_config_row"; | ||
| } | { | ||
| posted: false; | ||
| reason: "already_posted"; | ||
| } | { | ||
| posted: false; | ||
| reason: "no_announcer_configured"; | ||
| } | { | ||
| posted: false; | ||
| reason: "claim_update_failed"; | ||
| message: string; | ||
| } | { | ||
| posted: false; | ||
| reason: "announcer_threw"; | ||
| message: string; | ||
| } | { | ||
| posted: false; | ||
| reason: "announcer_rejected"; | ||
| message: string; | ||
| }; |
+67
-0
@@ -44,2 +44,26 @@ /** Known database types for UI dropdowns and wire format validation. Plugins may register additional dbType values not listed here. */ | ||
| health?: ConnectionHealth; | ||
| /** | ||
| * Connection group membership. Three states are meaningful: | ||
| * - `undefined` — older serializer / client predating the field. | ||
| * - `null` — explicitly unassigned (no group, or moved out via admin UI). | ||
| * - `string` — current membership. | ||
| * Schema + code use `group_id`; UI copy renders this as "environment". | ||
| */ | ||
| groupId?: string | null; | ||
| /** | ||
| * Display name of the group, denormalized so list responses can render | ||
| * a badge without a second round-trip to `/admin/connections/groups`. | ||
| * Same three-state semantics as {@link groupId}. | ||
| */ | ||
| groupName?: string | null; | ||
| /** | ||
| * Mirrors the `/admin/billing` usage-panel predicate | ||
| * (`connections WHERE org_id = $1 AND status != 'archived'`). | ||
| * | ||
| * Optional for wire compatibility: consumers must treat `undefined` | ||
| * as "count it" so a mixed-version deploy preserves pre-#2490 | ||
| * behavior. The `isBillable()` helper in `packages/web/src/ui/lib/types.ts` | ||
| * encodes this convention — prefer it over reading the field directly. | ||
| */ | ||
| billable?: boolean; | ||
| } | ||
@@ -97,2 +121,8 @@ /** Real-time pool size counters (only available for core adapters with pool access). */ | ||
| groupId?: string | null; | ||
| /** | ||
| * Display name of the group, denormalized so the detail view can render | ||
| * a badge without a second round-trip. Same three-state semantics as | ||
| * {@link groupId}. | ||
| */ | ||
| groupName?: string | null; | ||
| } | ||
@@ -110,5 +140,27 @@ /** | ||
| */ | ||
| /** | ||
| * Group lifecycle. | ||
| * | ||
| * - `active` — default. Group accepts new members, content writes, | ||
| * and chat routing. | ||
| * - `archived` — read-only tombstone. The group's content was | ||
| * cascade-archived; renames, member assignments, and | ||
| * re-archives are refused server-side. | ||
| * | ||
| * Type-only export deliberately — a value tuple here would block | ||
| * scaffold smoke tests until the next `@useatlas/types` publish, | ||
| * because the scaffolded template resolves the published version of | ||
| * the package and a new value export isn't visible there yet. Schemas | ||
| * inline `z.enum(["active", "archived"])` or | ||
| * `as const satisfies readonly ConnectionGroupStatus[]` instead. | ||
| * See `feedback_useatlas_types_scaffold_gotcha`. | ||
| */ | ||
| export type ConnectionGroupStatus = "active" | "archived"; | ||
| export interface ConnectionGroup { | ||
| id: string; | ||
| name: string; | ||
| /** Lifecycle. Optional so a consumer pinned to a pre-status wire | ||
| * type still typechecks; consumers should treat `undefined` as | ||
| * `active`. */ | ||
| status?: ConnectionGroupStatus; | ||
| /** Number of connections currently assigned to this group. */ | ||
@@ -120,2 +172,17 @@ memberCount: number; | ||
| /** | ||
| * Counts surfaced by the group-archive cascade. The HTTP status encodes | ||
| * the win/lose discriminant (200 = this caller archived; 409 = a | ||
| * concurrent admin won, or the group was already archived). The body | ||
| * just reports cascade scope. | ||
| * | ||
| * Heterogeneous terminal states: entities flip to `archived`, tasks to | ||
| * `enabled=false`, approvals to `expired`. The counts are uniform; the | ||
| * underlying lifecycle vocabulary is not. | ||
| */ | ||
| export interface GroupArchiveCounts { | ||
| entities: number; | ||
| tasks: number; | ||
| approvals: number; | ||
| } | ||
| /** | ||
| * One connection's membership in a {@link ConnectionGroup}. Returned by | ||
@@ -122,0 +189,0 @@ * the group detail endpoint so the admin UI can render member chips |
| /** Conversation persistence types — wire format for conversations and messages. */ | ||
| export type MessageRole = "user" | "assistant" | "system" | "tool"; | ||
| export type Surface = "web" | "api" | "mcp" | "slack" | "notebook"; | ||
| /** | ||
| * Three-state cross-environment routing picker for a conversation (#2518): | ||
| * | ||
| * - `"auto"` — agent's `scope` decides per turn. Default for new | ||
| * conversations created via the picker's Auto mode. | ||
| * - `"pin"` — force single-env execution against the conversation's | ||
| * stored `connectionId`; the agent's `scope` override is ignored. | ||
| * - `"all"` — force fanout across every member of the active group; | ||
| * the agent's `scope` override is ignored. | ||
| * | ||
| * `null` on a persisted row is read as `"pin"` (back-compat — pre-#2518 | ||
| * rows carry a non-null `connectionId` and the safest interpretation | ||
| * is "stay pinned to that member"). | ||
| */ | ||
| export type ConversationRoutingMode = "auto" | "pin" | "all"; | ||
| export interface Conversation { | ||
@@ -24,2 +39,15 @@ id: string; | ||
| connectionGroupId: string | null; | ||
| /** | ||
| * Three-state Auto/Pin/All picker state (#2518). `null` on existing | ||
| * rows is read as `"pin"` by the runtime — pre-#2518 conversations | ||
| * carry a single `connectionId` and the safest interpretation is | ||
| * "stay pinned to that member". New conversations created via the | ||
| * Auto picker mode persist `"auto"`; explicit Pin / All selections | ||
| * persist their literal values. | ||
| * | ||
| * Optional in the type so pre-#2518 test fixtures and external | ||
| * SDK consumers can construct a `Conversation` without supplying | ||
| * the field; the runtime treats missing the same as `null`. | ||
| */ | ||
| routingMode?: ConversationRoutingMode | null; | ||
| starred: boolean; | ||
@@ -26,0 +54,0 @@ createdAt: string; |
+32
-0
@@ -97,1 +97,33 @@ import type { ShareMode } from "./share"; | ||
| } | ||
| /** | ||
| * Per-user destructive-op staging (#2365, PRD #2362). | ||
| * | ||
| * The bound chat agent's `removeCard` and `updateCardSql` tools enqueue | ||
| * a `StagedChange` row instead of mutating immediately. The dashboard | ||
| * renders staged cards with a ghost overlay (strikethrough for removal, | ||
| * side-by-side SQL diff for edits) until the user accepts or discards | ||
| * the stage inline. | ||
| */ | ||
| export type StageKind = "remove_card" | "edit_sql"; | ||
| export type StageStatus = "pending" | "applied" | "discarded"; | ||
| export type StagePayload = { | ||
| kind: "remove_card"; | ||
| cardId: string; | ||
| } | { | ||
| kind: "edit_sql"; | ||
| cardId: string; | ||
| newSql: string; | ||
| currentSql: string; | ||
| }; | ||
| export interface StagedChange { | ||
| id: string; | ||
| dashboardId: string; | ||
| userId: string; | ||
| kind: StageKind; | ||
| payload: StagePayload; | ||
| status: StageStatus; | ||
| createdAt: string; | ||
| updatedAt: string; | ||
| appliedAt: string | null; | ||
| discardedAt: string | null; | ||
| } |
+2
-0
@@ -38,1 +38,3 @@ export * from "./auth"; | ||
| export * from "./preferences"; | ||
| export * from "./execute-sql"; | ||
| export * from "./proactive"; |
+13
-6
@@ -49,9 +49,16 @@ /** Migration bundle types — wire format for atlas export/import. */ | ||
| /** | ||
| * Group scope (multi-environment semantic layer, #2340). One entity | ||
| * row per group — multi-member groups share the same definition. | ||
| * Nullable for global / unscoped entities and for bundles exported | ||
| * by pre-1.4.4 instances whose legacy `connectionId` no longer | ||
| * resolves to a live group. | ||
| * Group scope (multi-environment semantic layer, #2340). Three accepted | ||
| * shapes: | ||
| * - **omitted** — producer with no group concept (pre-1.4.4 bundle). | ||
| * Importers coalesce this to `null`. | ||
| * - **explicit `null`** — 1.4.4+ unscoped row (global / no binding), or a | ||
| * bundle whose legacy `connectionId` no longer resolves to a live group. | ||
| * - **explicit string** — group id. One entity row per group; multi-member | ||
| * groups share the same definition. | ||
| * | ||
| * Optional because strict shape validation on import would otherwise reject | ||
| * producers that have no concept of the column. Value-nullability alone | ||
| * wasn't enough — optionality is what makes the field additive on the wire. | ||
| */ | ||
| connectionGroupId: string | null; | ||
| connectionGroupId?: string | null; | ||
| } | ||
@@ -58,0 +65,0 @@ /** Exported learned pattern. */ |
+30
-0
@@ -79,2 +79,32 @@ /** | ||
| } | ||
| /** | ||
| * Per-plugin health snapshot included in `PlatformOverview.pluginHealth`. | ||
| * Mirrors the in-memory registry's `describe()` shape, narrowed to what | ||
| * the dashboard needs. | ||
| */ | ||
| export interface PlatformPluginHealth { | ||
| id: string; | ||
| name: string; | ||
| types: string[]; | ||
| status: string; | ||
| } | ||
| /** | ||
| * Wire shape for `GET /api/v1/platform/overview` (#2489). Deployment-wide | ||
| * scaffold the workspace `/admin` Overview must NOT surface: | ||
| * disk-bundled entities, plugin registry, plugin health, and the pool | ||
| * capacity warnings string. | ||
| */ | ||
| export interface PlatformOverview { | ||
| /** Deployment-scaffold entity count from `discoverEntities(root)`. */ | ||
| entities: number; | ||
| /** Plugin registry size. */ | ||
| plugins: number; | ||
| pluginHealth: PlatformPluginHealth[]; | ||
| /** Disk-scan warnings (per-file YAML parse failures). */ | ||
| warnings?: string[]; | ||
| /** Pool capacity warnings — deployment-wide config; never on /admin. */ | ||
| poolWarnings?: string[]; | ||
| /** Echoed from the request for log correlation. */ | ||
| requestId: string; | ||
| } | ||
| export interface NoisyNeighbor { | ||
@@ -81,0 +111,0 @@ workspaceId: string; |
+12
-2
| { | ||
| "name": "@useatlas/types", | ||
| "version": "0.1.0", | ||
| "version": "0.1.3", | ||
| "description": "Shared types for the Atlas text-to-SQL agent", | ||
| "type": "module", | ||
| "scripts": { | ||
| "build": "rm -rf dist && bun build src/index.ts src/auth.ts src/conversation.ts src/connection.ts src/action.ts src/scheduled-task.ts src/errors.ts src/semantic.ts src/share.ts src/billing.ts src/dashboard.ts src/mode.ts src/starter-prompt.ts src/integrations.ts src/email-provider.ts src/mcp.ts src/preferences.ts --outdir dist --root src --target node --packages external && bun x tsc -p tsconfig.build.json", | ||
| "build": "rm -rf dist && bun build src/index.ts src/auth.ts src/conversation.ts src/connection.ts src/action.ts src/scheduled-task.ts src/errors.ts src/semantic.ts src/share.ts src/billing.ts src/dashboard.ts src/mode.ts src/starter-prompt.ts src/integrations.ts src/email-provider.ts src/mcp.ts src/preferences.ts src/execute-sql.ts src/proactive.ts --outdir dist --root src --target node --packages external && bun x tsc -p tsconfig.build.json", | ||
| "prepare": "bun run build" | ||
@@ -95,2 +95,12 @@ }, | ||
| "default": "./dist/preferences.js" | ||
| }, | ||
| "./execute-sql": { | ||
| "types": "./dist/execute-sql.d.ts", | ||
| "import": "./dist/execute-sql.js", | ||
| "default": "./dist/execute-sql.js" | ||
| }, | ||
| "./proactive": { | ||
| "types": "./dist/proactive.d.ts", | ||
| "import": "./dist/proactive.js", | ||
| "default": "./dist/proactive.js" | ||
| } | ||
@@ -97,0 +107,0 @@ }, |
202440
9.8%62
6.9%5136
9.21%