@company-semantics/contracts 0.100.0 → 0.102.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/api/README.md +21 -0
- package/src/api/http/README.md +21 -0
- package/src/api/http/utils/resource-response.ts +11 -0
- package/src/auth/README.md +21 -0
- package/src/billing/README.md +22 -0
- package/src/chat/README.md +38 -0
- package/src/ci-envelope/README.md +34 -0
- package/src/index.ts +8 -0
- package/src/interfaces/README.md +23 -0
- package/src/mcp/README.md +36 -0
- package/src/message-parts/README.md +39 -0
- package/src/message-parts/builder.ts +1 -1
- package/src/message-parts/confirmation.ts +1 -1
- package/src/message-parts/execution.ts +1 -1
- package/src/message-parts/preview.ts +1 -1
- package/src/message-parts/types.ts +1 -1
- package/src/message-parts/wire.ts +1 -1
- package/src/resource-keys.ts +174 -0
- package/src/resource-response.ts +9 -0
- package/src/usage/README.md +34 -0
package/package.json
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# api/
|
|
2
|
+
|
|
3
|
+
Shared API route helpers and contracts for backend HTTP endpoints.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Provides reusable functions that encode API response shapes shared between the contracts package and backend route implementations. Currently focused on tool discovery response building.
|
|
8
|
+
|
|
9
|
+
## Invariants
|
|
10
|
+
|
|
11
|
+
- Functions in this domain are pure — no side effects, no database access
|
|
12
|
+
- Response shapes must match the types defined in `src/mcp/` (e.g., `ToolDiscoveryResponse`)
|
|
13
|
+
- Capability graph inclusion is opt-in via query parameter convention (`?include=graph`)
|
|
14
|
+
|
|
15
|
+
## Public API
|
|
16
|
+
|
|
17
|
+
- `buildToolDiscoveryResponse(tools, includeGraph)` — Constructs a `ToolDiscoveryResponse`, optionally including the capability graph derived from tool metadata
|
|
18
|
+
|
|
19
|
+
## Dependencies
|
|
20
|
+
|
|
21
|
+
- `src/mcp/` — `MCPToolDescriptor`, `ToolDiscoveryResponse`, `CapabilityGraph` types and `buildCapabilityGraph` function
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# api/http/
|
|
2
|
+
|
|
3
|
+
HTTP-specific API route contracts and helpers.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Contains route-level shared logic for HTTP API endpoints. Organizes helpers by route path under `routes/`. Currently provides the tool discovery response builder used by the backend's `/api/capabilities/tools` endpoint.
|
|
8
|
+
|
|
9
|
+
## Invariants
|
|
10
|
+
|
|
11
|
+
- Route helpers are pure functions — no HTTP framework coupling (no Fastify, no Express)
|
|
12
|
+
- Helpers encode response shape logic only; authentication, authorization, and request parsing belong in the backend
|
|
13
|
+
- File organization mirrors backend route paths (`routes/ai-chat.ts` → backend's `ai-chat.ts`)
|
|
14
|
+
|
|
15
|
+
## Public API
|
|
16
|
+
|
|
17
|
+
- `routes/ai-chat.ts` — `buildToolDiscoveryResponse(tools, includeGraph)` for the capabilities/tools endpoint
|
|
18
|
+
|
|
19
|
+
## Dependencies
|
|
20
|
+
|
|
21
|
+
- `src/mcp/` — Tool descriptor types and capability graph builder
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ResourceResponse } from '../../../resource-response';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wrap resource data with version for cache invalidation.
|
|
5
|
+
* @param data - The resource data
|
|
6
|
+
* @param version - MUST be entity.updatedAt or DB row version. NEVER Date.now().
|
|
7
|
+
* Using request time defeats invalidation — race conditions reappear.
|
|
8
|
+
*/
|
|
9
|
+
export function resourceResponse<T>(data: T, version: number): ResourceResponse<T> {
|
|
10
|
+
return { data, version };
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# auth/
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Shared types for authentication flows across Company Semantics codebases. Defines the OTP and SSO authentication start protocol.
|
|
6
|
+
|
|
7
|
+
## Invariants
|
|
8
|
+
|
|
9
|
+
- AuthStartResponse is a discriminated union on `mode` — consumers must handle all three modes
|
|
10
|
+
- OTPErrorCode values are stable strings used by both backend (error generation) and frontend (user messaging)
|
|
11
|
+
|
|
12
|
+
## Public API
|
|
13
|
+
|
|
14
|
+
**Types:**
|
|
15
|
+
- `OTPErrorCode` — enum: `InvalidCode`, `Expired`, `AlreadyUsed`, `NotFound`
|
|
16
|
+
- `AuthStartMode` — `'otp' | 'sso' | 'hybrid'`
|
|
17
|
+
- `AuthStartResponse` — discriminated union describing auth flow entry point
|
|
18
|
+
|
|
19
|
+
## Dependencies
|
|
20
|
+
|
|
21
|
+
- None (leaf domain in contracts)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# billing/
|
|
2
|
+
|
|
3
|
+
Read-only billing DTO types for the organization billing surface.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Defines the shared type vocabulary for organization billing status and plan information. These types are consumed by backend (billing routes) and app (billing UI) to display plan and seat information.
|
|
8
|
+
|
|
9
|
+
## Invariants
|
|
10
|
+
|
|
11
|
+
- All types are read-only DTOs — no mutation operations in v1
|
|
12
|
+
- `billingOwnerUserId` must equal `org.ownerUserId` — no separate billing owner concept in v1
|
|
13
|
+
- `OrgPlanStatus` is a closed union: `active`, `trialing`, `past_due`, `canceled`
|
|
14
|
+
|
|
15
|
+
## Public API
|
|
16
|
+
|
|
17
|
+
- `OrgPlanStatus` — Union type for organization plan states
|
|
18
|
+
- `OrgBillingInfo` — Read-only billing information (plan name, status, cadence, seats, billing owner)
|
|
19
|
+
|
|
20
|
+
## Dependencies
|
|
21
|
+
|
|
22
|
+
- None — leaf domain with no imports from other contracts modules
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# chat/
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Shared types for chat persistence, sharing, real-time events, and runtime profile selection.
|
|
6
|
+
|
|
7
|
+
## Invariants
|
|
8
|
+
|
|
9
|
+
- Chat share snapshot boundary: messages with sequenceNumber > messageCountAtShare are never exposed (SNAPSHOT-1)
|
|
10
|
+
- Domain events for a given entity are emitted in commit order; clients may assume monotonic updatedAt
|
|
11
|
+
- Clients must treat SSE events as idempotent — full payload replacement handles duplicates
|
|
12
|
+
- Invalidation events ensure convergence after SSE disconnects
|
|
13
|
+
- Runtime profile labels are vendor-agnostic (no model names in UI)
|
|
14
|
+
- Default runtime profile is `agentic`
|
|
15
|
+
|
|
16
|
+
## Public API
|
|
17
|
+
|
|
18
|
+
**Types:**
|
|
19
|
+
- `ChatVisibility` — `'private' | 'public'` share visibility
|
|
20
|
+
- `ChatSummary`, `ChatSummaryExtended` — list view models
|
|
21
|
+
- `ChatShareInfo`, `SharedChatView`, `SharedChatMessage` — sharing types
|
|
22
|
+
- `CreateShareRequest`, `UpdateShareRequest` — share mutation requests
|
|
23
|
+
- `TitleSource`, `ChatListFilters`, `TitleGenerationRequest/Response` — lifecycle types
|
|
24
|
+
- `ChatRuntimeProfile` — `'fast' | 'balanced' | 'agentic'` orchestration strategy
|
|
25
|
+
- `ChatRuntimeProfileInfo` — profile metadata for UI rendering
|
|
26
|
+
|
|
27
|
+
**SSE event types:**
|
|
28
|
+
- `ChatCreatedEvent`, `ChatUpdatedEvent`, `ChatDeletedEvent` — domain events
|
|
29
|
+
- `InvalidateChatEvent`, `InvalidateChatListEvent` — staleness signals
|
|
30
|
+
- `ChatSseEvent` — union of all SSE event types
|
|
31
|
+
|
|
32
|
+
**Runtime values:**
|
|
33
|
+
- `CHAT_RUNTIME_PROFILES` — ordered array of profile info for UI rendering
|
|
34
|
+
- `DEFAULT_CHAT_RUNTIME_PROFILE` — `'agentic'`
|
|
35
|
+
|
|
36
|
+
## Dependencies
|
|
37
|
+
|
|
38
|
+
- None (leaf domain in contracts)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# ci-envelope/
|
|
2
|
+
|
|
3
|
+
Type vocabulary for CI execution envelopes — the governance records that capture AI intent before mutations.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Defines the shared type vocabulary for CI execution envelopes. An envelope is an append-only record created before code mutations that captures intent, scope, authority, and outcome. This enables pre-execution gating and post-hoc audit without LLM guesswork.
|
|
8
|
+
|
|
9
|
+
## Invariants
|
|
10
|
+
|
|
11
|
+
- Types only — no validation logic, no policy mappings (those live in `company-semantics-ci/envelope/`)
|
|
12
|
+
- Envelopes are append-only: status transitions forward only, no field deletions
|
|
13
|
+
- `ENVELOPE_TRANSITIONS` documents the state machine but does not enforce it
|
|
14
|
+
- Terminal states: `blocked` and `finalized` have no outgoing transitions
|
|
15
|
+
|
|
16
|
+
## Public API
|
|
17
|
+
|
|
18
|
+
**Types:**
|
|
19
|
+
- `CIExecutionEnvelope` — Core envelope record (version, id, actor, session, intent, scope, authority, guardPlan, status, outcome)
|
|
20
|
+
- `CIEnvelopeStatus` — Lifecycle states: `proposed` → `validated` → `executing` → `finalized` (or `blocked`)
|
|
21
|
+
- `ExecutionIntent` — Semantic categories: `feature_extension`, `bug_fix`, `refactor_structural`, `guard_update`, `policy_update`, `schema_change`, `doc_update`, `cleanup`, `experimental`
|
|
22
|
+
- `CIIntentDeclaration` — Intent with description and optional ADR references
|
|
23
|
+
- `CIExecutionScope` — Affected repos, path globs, and change kinds
|
|
24
|
+
- `CIAuthorityGrant` — Permission basis (CLAUDE.md/AI_AUTONOMY.md references)
|
|
25
|
+
- `CIGuardPlan` — Required and advisory guard lists
|
|
26
|
+
- `CIActorInfo`, `CISessionInfo` — Actor and session metadata
|
|
27
|
+
- `CIGuardViolation`, `CIExecutionOutcome` — Post-execution audit data
|
|
28
|
+
|
|
29
|
+
**Constants:**
|
|
30
|
+
- `ENVELOPE_TRANSITIONS` — Valid status transition graph (documentation only)
|
|
31
|
+
|
|
32
|
+
## Dependencies
|
|
33
|
+
|
|
34
|
+
- None (leaf module — no imports from other contracts domains)
|
package/src/index.ts
CHANGED
|
@@ -531,3 +531,11 @@ export type {
|
|
|
531
531
|
} from './ci-envelope/index'
|
|
532
532
|
|
|
533
533
|
export { ENVELOPE_TRANSITIONS } from './ci-envelope/index'
|
|
534
|
+
|
|
535
|
+
// Resource key types (shared vocabulary for data layer)
|
|
536
|
+
// @see PRD-00321 for design rationale
|
|
537
|
+
export type { ResourceKey, Action } from './resource-keys'
|
|
538
|
+
export { resolveScope, toQueryKey, fromQueryKey, resourceRelationships, matchesResourceKey } from './resource-keys'
|
|
539
|
+
|
|
540
|
+
// Resource response wrapper (typed versioning for cache invalidation)
|
|
541
|
+
export type { ResourceResponse } from './resource-response'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# interfaces/
|
|
2
|
+
|
|
3
|
+
Shared logic for MCP tool interface presentation and formatting.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Contains pure formatting functions used by MCP tool interfaces. Currently provides help tool enrichment — functions that format workflow summaries and domain groupings from tool descriptors for the `cs_help` tool output.
|
|
8
|
+
|
|
9
|
+
## Invariants
|
|
10
|
+
|
|
11
|
+
- Functions are pure — no side effects, no service dependencies
|
|
12
|
+
- Input is always `MCPToolDescriptor[]` (the same shape backend tool handlers produce)
|
|
13
|
+
- Output is formatted text, not structured data — these are presentation-layer helpers
|
|
14
|
+
|
|
15
|
+
## Public API
|
|
16
|
+
|
|
17
|
+
- `mcp/tools/help.ts`:
|
|
18
|
+
- `formatWorkflowSummaries(descriptors)` — Derives workflows via capability graph and formats as text
|
|
19
|
+
- `formatDomainGroupings(descriptors)` — Groups tools by domain field and formats as text
|
|
20
|
+
|
|
21
|
+
## Dependencies
|
|
22
|
+
|
|
23
|
+
- `src/mcp/` — `MCPToolDescriptor` type and `buildCapabilityGraph` function
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# mcp/
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Type vocabulary for MCP (Model Context Protocol) tool discovery, invocation metadata, capability graph derivation, and failure recovery.
|
|
6
|
+
|
|
7
|
+
## Invariants
|
|
8
|
+
|
|
9
|
+
- Discovery is descriptive (never executes); invocation is authoritative (runtime is executor)
|
|
10
|
+
- Tool discovery output must be runtime-sourced, not streamed char-by-char
|
|
11
|
+
- Intent `mutate` + risk `high` requires `requiresConfirmation: true` — enforced at tool registration
|
|
12
|
+
- ResourceType is a closed union — new types require a contracts release
|
|
13
|
+
- FailureContext recoveryActions are exhaustive — the LLM cannot invent alternatives
|
|
14
|
+
- `cancel` is always implicitly available in failure recovery (not declared in recoveryActions)
|
|
15
|
+
- At most one FailureContext is active at a time (no stacking)
|
|
16
|
+
- Capability graph is a pure derivation from produces/consumes metadata
|
|
17
|
+
|
|
18
|
+
## Public API
|
|
19
|
+
|
|
20
|
+
**Types:**
|
|
21
|
+
- `MCPToolDescriptor` — complete tool descriptor for discovery and invocation
|
|
22
|
+
- `ToolDomain`, `ToolCategory` (deprecated), `ToolVisibility`, `ToolInvocationMode` — classification enums
|
|
23
|
+
- `ToolEffectClass`, `ToolIntent`, `ToolStability`, `ToolComplexity` — tool metadata types
|
|
24
|
+
- `ToolIntegration` — extensible integration provider tag
|
|
25
|
+
- `ResourceType` — closed union of tool resource flow types
|
|
26
|
+
- `CapabilityGraph`, `CapabilityGraphEdge`, `ToolWorkflow` — graph types
|
|
27
|
+
- `ToolDiscoveryResponse` — HTTP transport for GET /api/capabilities/tools
|
|
28
|
+
- `ToolListMessagePart`, `ToolListDataPart` — chat protocol and wire format parts
|
|
29
|
+
- `FailureContext`, `RecoveryAction` — structured failure recovery types
|
|
30
|
+
|
|
31
|
+
**Runtime values:**
|
|
32
|
+
- `buildCapabilityGraph(tools)` — pure function deriving graph from produces/consumes metadata
|
|
33
|
+
|
|
34
|
+
## Dependencies
|
|
35
|
+
|
|
36
|
+
- None (leaf domain in contracts; consumed by backend, app)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# message-parts/
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Canonical vocabulary for structured assistant message output. Defines the type system for all rich content parts that flow between backend (producer) and app (consumer) in the chat protocol.
|
|
6
|
+
|
|
7
|
+
## Invariants
|
|
8
|
+
|
|
9
|
+
- Narrative (text) parts MUST come before surface parts — enforced by PartBuilder state machine
|
|
10
|
+
- SurfacePart is an extensible union — add new surface kinds without protocol changes
|
|
11
|
+
- TextPart is the ONLY part type that may be streamed character-by-character
|
|
12
|
+
- Wire format uses AI SDK's `data-{name}` convention; frontend normalizes to semantic types
|
|
13
|
+
- At most one preview proposal is active at a time
|
|
14
|
+
- Confirmation is deterministic — no natural language inference ("yes" is invalid)
|
|
15
|
+
- Execution results carry `state` resolved from ExecutionState (single authority, no redundant status)
|
|
16
|
+
- Undo creates append-only audit rows — original execution is never mutated
|
|
17
|
+
|
|
18
|
+
## Public API
|
|
19
|
+
|
|
20
|
+
**Types:**
|
|
21
|
+
- `TextPart`, `SurfacePart`, `AssistantMessagePart` — core part unions
|
|
22
|
+
- `ToolListPart`, `StatusPanelPart`, `ChartPart`, `TablePart` — surface part variants
|
|
23
|
+
- `PreviewData`, `PreviewArtifact`, `PreviewPart` — action preview surfaces
|
|
24
|
+
- `ConfirmationData`, `ConfirmationRiskLevel`, `ConfirmationPart` — confirmation surfaces
|
|
25
|
+
- `ExecutionResultData`, `ExecutionArtifactStatus`, `UndoResultData` — execution results
|
|
26
|
+
- `StreamPhase`, `MessageLifecycleEvent` — message lifecycle events
|
|
27
|
+
- `IntegrationKey`, `IntegrationProvider` — integration identifiers
|
|
28
|
+
|
|
29
|
+
**Runtime values:**
|
|
30
|
+
- `CONFIRMATION_LABELS` — exhaustive map of ExecutionKind to human-readable labels
|
|
31
|
+
- `getConfirmationLabel(kind, fallback)` — label lookup with fallback
|
|
32
|
+
- `WireSurfaceBuilder` — factory for creating wire-format data parts
|
|
33
|
+
- `createPartBuilder()`, `addText()`, `addSurface()`, `buildParts()` — functional part builder
|
|
34
|
+
- `isTextPart()`, `isSurfacePart()` — type guards
|
|
35
|
+
|
|
36
|
+
## Dependencies
|
|
37
|
+
|
|
38
|
+
- `../execution/kinds` — ExecutionKind type for confirmation labels
|
|
39
|
+
- `../mcp/index` — MCPToolDescriptor for tool list parts
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* This prevents "post-surface commentary" features from sneaking in.
|
|
17
17
|
*
|
|
18
|
-
* @see
|
|
18
|
+
* @see decisions/ADR-CONT-026.md for design rationale
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import type { AssistantMessagePart, TextPart, SurfacePart } from './types'
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* - At most one action is confirmable at a time
|
|
15
15
|
* - Mismatched actionId fails closed and re-prompts
|
|
16
16
|
*
|
|
17
|
-
* @see
|
|
17
|
+
* @see decisions/ADR-CONT-037.md for design rationale
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import type { ExecutionKind } from '../execution/kinds'
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* INV-6: Undo race condition — 409 on expired window. Server rejects undo
|
|
25
25
|
* attempts after availableUntil deadline.
|
|
26
26
|
*
|
|
27
|
-
* @see
|
|
27
|
+
* @see decisions/ADR-CONT-037.md for design rationale
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import type { PreviewArtifactKind } from './preview';
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* - User messages during preview do not affect mode; only assistant output controls persistence
|
|
15
15
|
* - At most one preview proposal is active at a time (multiple previews are sequential, not concurrent)
|
|
16
16
|
*
|
|
17
|
-
* @see
|
|
17
|
+
* @see decisions/ADR-CONT-026.md for design rationale
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - TextPart is the ONLY part type that may be streamed char-by-char
|
|
11
11
|
* - Once a surface is added, the turn is in "surface phase" permanently
|
|
12
12
|
*
|
|
13
|
-
* @see
|
|
13
|
+
* @see decisions/ADR-CONT-026.md for design rationale
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import type { ToolListMessagePart } from '../mcp/index'
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Use these in backend code when writing to UIMessageStream.
|
|
8
8
|
* Frontend normalizes wire format to semantic types.
|
|
9
9
|
*
|
|
10
|
-
* @see
|
|
10
|
+
* @see decisions/ADR-CONT-026.md for design rationale
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import type { MCPToolDescriptor, ToolListDataPart } from '../mcp/index'
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ResourceKey — shared vocabulary for resources across the system.
|
|
3
|
+
* All keys MUST include scope (orgId or userId) to prevent cross-org cache leaks.
|
|
4
|
+
* Naming: camelCase nouns. Collections plural, identities singular.
|
|
5
|
+
*/
|
|
6
|
+
export type ResourceKey =
|
|
7
|
+
// Collections (plural) — lists of entities
|
|
8
|
+
| { type: 'members'; orgId: string }
|
|
9
|
+
| { type: 'departments'; orgId: string }
|
|
10
|
+
| { type: 'chats'; orgId: string }
|
|
11
|
+
| { type: 'teams'; orgId: string }
|
|
12
|
+
| { type: 'integrations'; orgId: string }
|
|
13
|
+
| { type: 'invites'; orgId: string }
|
|
14
|
+
| { type: 'auditEvents'; orgId: string }
|
|
15
|
+
// Identities (singular) — single entities
|
|
16
|
+
| { type: 'member'; orgId: string; memberId: string }
|
|
17
|
+
| { type: 'workspace'; orgId: string }
|
|
18
|
+
| { type: 'authSettings'; orgId: string }
|
|
19
|
+
| { type: 'billing'; orgId: string }
|
|
20
|
+
| { type: 'aiUsage'; orgId: string }
|
|
21
|
+
| { type: 'deletionEligibility'; orgId: string }
|
|
22
|
+
| { type: 'transferOwnership'; orgId: string }
|
|
23
|
+
| { type: 'goals'; orgId: string }
|
|
24
|
+
// User-scoped
|
|
25
|
+
| { type: 'dismissedBanners'; userId: string }
|
|
26
|
+
| { type: 'userOrgs'; userId: string }
|
|
27
|
+
| { type: 'sessions'; userId: string };
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Action — structured mutation key used across execution system, audit, permissions.
|
|
31
|
+
*/
|
|
32
|
+
export type Action = { resource: string; verb: string };
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolve scope for cache partitioning. Handles impersonation.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveScope(context: {
|
|
38
|
+
orgId: string;
|
|
39
|
+
impersonatedOrgId?: string;
|
|
40
|
+
}): { orgId: string } {
|
|
41
|
+
return { orgId: context.impersonatedOrgId ?? context.orgId };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** All ResourceKey type literals for exhaustive checking. */
|
|
45
|
+
const ORG_SCOPED_TYPES = [
|
|
46
|
+
'members', 'departments', 'chats', 'teams', 'integrations', 'invites',
|
|
47
|
+
'auditEvents', 'workspace', 'authSettings', 'billing', 'aiUsage',
|
|
48
|
+
'deletionEligibility', 'transferOwnership', 'goals',
|
|
49
|
+
] as const;
|
|
50
|
+
|
|
51
|
+
const USER_SCOPED_TYPES = ['dismissedBanners', 'userOrgs', 'sessions'] as const;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Canonical ResourceKey → query key conversion.
|
|
55
|
+
* This is the ONLY function that constructs query keys — no ad-hoc key
|
|
56
|
+
* construction anywhere in the codebase.
|
|
57
|
+
*
|
|
58
|
+
* Mapping is deterministic and stable:
|
|
59
|
+
* { type: 'members', orgId: 'abc' } → ['members', 'abc']
|
|
60
|
+
* { type: 'member', orgId: 'abc', memberId: '123' } → ['member', 'abc', '123']
|
|
61
|
+
*/
|
|
62
|
+
export function toQueryKey(key: ResourceKey): readonly string[] {
|
|
63
|
+
switch (key.type) {
|
|
64
|
+
// Identity with extra field
|
|
65
|
+
case 'member':
|
|
66
|
+
return [key.type, key.orgId, key.memberId] as const;
|
|
67
|
+
|
|
68
|
+
// User-scoped
|
|
69
|
+
case 'dismissedBanners':
|
|
70
|
+
case 'userOrgs':
|
|
71
|
+
case 'sessions':
|
|
72
|
+
return [key.type, key.userId] as const;
|
|
73
|
+
|
|
74
|
+
// Org-scoped collections and identities
|
|
75
|
+
case 'members':
|
|
76
|
+
case 'departments':
|
|
77
|
+
case 'chats':
|
|
78
|
+
case 'teams':
|
|
79
|
+
case 'integrations':
|
|
80
|
+
case 'invites':
|
|
81
|
+
case 'auditEvents':
|
|
82
|
+
case 'workspace':
|
|
83
|
+
case 'authSettings':
|
|
84
|
+
case 'billing':
|
|
85
|
+
case 'aiUsage':
|
|
86
|
+
case 'deletionEligibility':
|
|
87
|
+
case 'transferOwnership':
|
|
88
|
+
case 'goals':
|
|
89
|
+
return [key.type, key.orgId] as const;
|
|
90
|
+
|
|
91
|
+
default: {
|
|
92
|
+
const _exhaustive: never = key;
|
|
93
|
+
throw new Error(`Unknown resource type: ${JSON.stringify(_exhaustive)}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Inverse of toQueryKey — reconstructs a ResourceKey from a query key array.
|
|
100
|
+
* Throws if the array cannot be parsed into a valid ResourceKey.
|
|
101
|
+
*/
|
|
102
|
+
export function fromQueryKey(queryKey: readonly string[]): ResourceKey {
|
|
103
|
+
const [type, ...rest] = queryKey;
|
|
104
|
+
|
|
105
|
+
if (!type || rest.length === 0) {
|
|
106
|
+
throw new Error(`Invalid query key: expected at least [type, scope], got ${JSON.stringify(queryKey)}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Identity with extra field
|
|
110
|
+
if (type === 'member') {
|
|
111
|
+
if (rest.length !== 2) {
|
|
112
|
+
throw new Error(`Invalid query key for 'member': expected [type, orgId, memberId], got ${JSON.stringify(queryKey)}`);
|
|
113
|
+
}
|
|
114
|
+
return { type: 'member', orgId: rest[0], memberId: rest[1] };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// User-scoped types
|
|
118
|
+
if ((USER_SCOPED_TYPES as readonly string[]).includes(type)) {
|
|
119
|
+
return { type, userId: rest[0] } as ResourceKey;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Org-scoped types
|
|
123
|
+
if ((ORG_SCOPED_TYPES as readonly string[]).includes(type)) {
|
|
124
|
+
return { type, orgId: rest[0] } as ResourceKey;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
throw new Error(`Unknown resource type in query key: '${type}'`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Bidirectional resource relationships.
|
|
132
|
+
* TanStack does NOT support prefix matching across different key shapes.
|
|
133
|
+
* When invalidating a collection, we must also invalidate related identity entries (and vice versa).
|
|
134
|
+
*/
|
|
135
|
+
export const resourceRelationships: Record<string, string[]> = {
|
|
136
|
+
members: ['member'],
|
|
137
|
+
member: ['members'],
|
|
138
|
+
departments: ['department'],
|
|
139
|
+
department: ['departments'],
|
|
140
|
+
chats: ['chat'],
|
|
141
|
+
chat: ['chats'],
|
|
142
|
+
teams: ['team'],
|
|
143
|
+
team: ['teams'],
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Strict predicate for matching resource keys.
|
|
148
|
+
* Compares type + scope fields exactly. No partial matching. No loose comparisons.
|
|
149
|
+
* Used by invalidateResource with TanStack's predicate-based invalidation.
|
|
150
|
+
*/
|
|
151
|
+
export function matchesResourceKey(queryKey: readonly unknown[], targetKey: ResourceKey): boolean {
|
|
152
|
+
if (!Array.isArray(queryKey) && !isReadonlyArray(queryKey)) return false;
|
|
153
|
+
const stringKey = queryKey.filter((v): v is string => typeof v === 'string');
|
|
154
|
+
if (stringKey.length !== queryKey.length) return false;
|
|
155
|
+
|
|
156
|
+
let parsed: ResourceKey;
|
|
157
|
+
try {
|
|
158
|
+
parsed = fromQueryKey(stringKey);
|
|
159
|
+
} catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (parsed.type !== targetKey.type) return false;
|
|
164
|
+
|
|
165
|
+
if ('orgId' in parsed && 'orgId' in targetKey && parsed.orgId !== targetKey.orgId) return false;
|
|
166
|
+
if ('userId' in parsed && 'userId' in targetKey && parsed.userId !== targetKey.userId) return false;
|
|
167
|
+
if ('memberId' in parsed && 'memberId' in targetKey && parsed.memberId !== targetKey.memberId) return false;
|
|
168
|
+
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function isReadonlyArray(value: unknown): value is readonly unknown[] {
|
|
173
|
+
return Array.isArray(value);
|
|
174
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard response wrapper for resource endpoints.
|
|
3
|
+
* version MUST come from data source (entity.updatedAt), NEVER from request time (Date.now()).
|
|
4
|
+
* Using request time defeats invalidation — race conditions reappear.
|
|
5
|
+
*/
|
|
6
|
+
export type ResourceResponse<T> = {
|
|
7
|
+
data: T;
|
|
8
|
+
version: number;
|
|
9
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# usage/
|
|
2
|
+
|
|
3
|
+
Type vocabulary for AI usage tracking, execution telemetry, and budget governance.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Defines shared types for two usage tracking layers: (1) low-level AI usage events (token counts, costs per model/feature) and (2) higher-level runtime execution telemetry (per-request aggregates across tool loops). Also includes budget governance types for org-level spend controls.
|
|
8
|
+
|
|
9
|
+
## Invariants
|
|
10
|
+
|
|
11
|
+
- All cost fields are `string` (decimal USD with 8 decimal places) — never floating point
|
|
12
|
+
- Types only — no runtime logic, no database access
|
|
13
|
+
- `AiFeature` enum must stay in sync with the backend's `ai_feature` pgEnum
|
|
14
|
+
- `ChatRuntimeProfile` dependency flows from `src/chat/` — usage types reference it, not the reverse
|
|
15
|
+
|
|
16
|
+
## Public API
|
|
17
|
+
|
|
18
|
+
**AI Usage Types (`types.ts`):**
|
|
19
|
+
- `AiFeature` — Feature categories: `chat`, `chat_title`, `ingest_summarize`, `ingest_embedding`, `goal_extraction`
|
|
20
|
+
- `UsageSummary`, `DailyUsage`, `UsageByModel`, `UsageByFeature`, `UsageByUser` — Aggregation response shapes
|
|
21
|
+
- `UnifiedUsageResponse` and related interfaces — Combined usage dashboard response
|
|
22
|
+
|
|
23
|
+
**Execution Telemetry (`execution-types.ts`):**
|
|
24
|
+
- `RuntimeExecutionTelemetry` — Per-execution record (tokens, cost, duration, profile, success)
|
|
25
|
+
- `ExecutionFailureReason` — Failure categories: `tool_error`, `timeout`, `model_error`, `budget_exceeded`, `client_disconnect`, `unknown`
|
|
26
|
+
- `OrgExecutionSummary`, `DailyExecutionStats`, `ExecutionByUser`, `ExecutionDetail`, `ProfileStats` — Aggregation shapes
|
|
27
|
+
|
|
28
|
+
**Budget Governance (`execution-types.ts`):**
|
|
29
|
+
- `BudgetCheck` — Pre-execution budget gate result
|
|
30
|
+
- `OrgBudgetSettings` — Org budget configuration shape
|
|
31
|
+
|
|
32
|
+
## Dependencies
|
|
33
|
+
|
|
34
|
+
- `src/chat/` — `ChatRuntimeProfile` type (used by execution telemetry)
|