@elevasis/core 0.21.0 → 0.22.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.
Files changed (59) hide show
  1. package/dist/index.d.ts +416 -6
  2. package/dist/index.js +240 -15
  3. package/dist/knowledge/index.d.ts +97 -1
  4. package/dist/organization-model/index.d.ts +416 -6
  5. package/dist/organization-model/index.js +240 -15
  6. package/dist/test-utils/index.d.ts +216 -1
  7. package/dist/test-utils/index.js +230 -14
  8. package/package.json +3 -3
  9. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +495 -302
  10. package/src/auth/multi-tenancy/permissions.ts +20 -8
  11. package/src/business/README.md +2 -2
  12. package/src/business/acquisition/api-schemas.test.ts +173 -0
  13. package/src/business/acquisition/api-schemas.ts +125 -7
  14. package/src/business/acquisition/index.ts +12 -0
  15. package/src/business/clients/api-schemas.test.ts +115 -0
  16. package/src/business/clients/api-schemas.ts +158 -0
  17. package/src/business/clients/index.ts +1 -0
  18. package/src/business/deals/api-schemas.ts +8 -0
  19. package/src/business/index.ts +5 -2
  20. package/src/business/projects/types.ts +19 -0
  21. package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -8
  22. package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -12
  23. package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -3
  24. package/src/execution/engine/agent/core/types.ts +25 -15
  25. package/src/execution/engine/agent/index.ts +6 -4
  26. package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -18
  27. package/src/execution/engine/index.ts +3 -0
  28. package/src/execution/engine/workflow/types.ts +7 -0
  29. package/src/organization-model/README.md +10 -3
  30. package/src/organization-model/__tests__/defaults.test.ts +6 -0
  31. package/src/organization-model/__tests__/domains/resources.test.ts +188 -0
  32. package/src/organization-model/__tests__/domains/roles.test.ts +402 -347
  33. package/src/organization-model/__tests__/domains/systems.test.ts +193 -0
  34. package/src/organization-model/__tests__/knowledge.test.ts +39 -0
  35. package/src/organization-model/__tests__/resolve.test.ts +1 -1
  36. package/src/organization-model/defaults.ts +24 -3
  37. package/src/organization-model/domains/knowledge.ts +3 -2
  38. package/src/organization-model/domains/resources.ts +88 -0
  39. package/src/organization-model/domains/roles.ts +93 -55
  40. package/src/organization-model/domains/systems.ts +46 -0
  41. package/src/organization-model/icons.ts +1 -0
  42. package/src/organization-model/index.ts +2 -0
  43. package/src/organization-model/organization-model.mdx +33 -14
  44. package/src/organization-model/published.ts +52 -1
  45. package/src/organization-model/schema.ts +121 -0
  46. package/src/organization-model/types.ts +46 -1
  47. package/src/platform/api/types.ts +38 -35
  48. package/src/platform/registry/__tests__/resource-registry.test.ts +2051 -2005
  49. package/src/platform/registry/__tests__/validation.test.ts +1343 -1086
  50. package/src/platform/registry/index.ts +14 -0
  51. package/src/platform/registry/resource-registry.ts +40 -2
  52. package/src/platform/registry/serialization.ts +241 -202
  53. package/src/platform/registry/serialized-types.ts +1 -0
  54. package/src/platform/registry/types.ts +411 -361
  55. package/src/platform/registry/validation.ts +743 -513
  56. package/src/projects/api-schemas.ts +290 -267
  57. package/src/reference/_generated/contracts.md +495 -302
  58. package/src/reference/glossary.md +8 -3
  59. package/src/supabase/database.types.ts +121 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Organization Model
3
- description: Organization OS Model layer documentation for the flat feature hierarchy, semantic domains, resource graph links, and curated @elevasis/core public API.
3
+ description: Organization OS Model layer documentation for the flat feature hierarchy, semantic domains, resource descriptors, and curated @elevasis/core public API.
4
4
  ---
5
5
 
6
6
  ## Overview
@@ -35,10 +35,13 @@ Top-level fields on `OrganizationModel`:
35
35
  - `offerings`
36
36
  - `roles`
37
37
  - `goals`
38
+ - `systems`
39
+ - `resources`
38
40
  - `statuses`
39
41
  - `operations`
42
+ - `knowledge`
40
43
 
41
- Resources are not authored inside `OrganizationModel`. Deployable workflows, agents, triggers, integrations, external resources, and human checkpoints declare graph links on their resource metadata.
44
+ Resource identity is authored inside `OrganizationModel.resources.entries`. Runtime workflows, agents, and integrations import those descriptors, derive `resourceId` and kind from them, and attach executable behavior in operations code.
42
45
 
43
46
  ## Feature Shape
44
47
 
@@ -71,7 +74,7 @@ Development-only features remain defined and enabled with `devOnly: true`; shell
71
74
 
72
75
  Navigation surfaces may also carry `devOnly` for compatibility with the legacy/domain navigation model. `knowledge.command-view` is intentionally development-only while its graph visualization modes continue to mature.
73
76
 
74
- ## Graph IDs and Resource Links
77
+ ## Graph IDs and Resource Descriptors
75
78
 
76
79
  Cross-collection links use kind-prefixed graph IDs:
77
80
 
@@ -80,18 +83,30 @@ Cross-collection links use kind-prefixed graph IDs:
80
83
  - `resource:lead-import`
81
84
  - `capability:operations.queue.review`
82
85
 
83
- Example resource metadata:
86
+ Resource identity is authored in the OM Resources domain. Operations imports descriptors from `organizationModel.resources.entries` and derives runtime `resourceId` / `type` while attaching executable behavior.
84
87
 
85
88
  ```ts
86
- config: {
87
- resourceId: 'lead-import',
88
- name: 'Lead Import',
89
- type: 'workflow',
90
- version: '1.0.0',
91
- status: 'prod',
92
- links: [{ nodeId: 'feature:sales.lead-gen', kind: 'operates-on' }],
93
- category: 'production'
94
- }
89
+ import { defineResources } from '@elevasis/core/organization-model'
90
+
91
+ export const resourceDescriptors = defineResources({
92
+ leadImport: {
93
+ id: 'lead-import',
94
+ kind: 'workflow',
95
+ systemId: 'sys.prospecting',
96
+ ownerRoleId: 'role-ops-lead',
97
+ status: 'active',
98
+ capabilityKey: 'prospecting.lead-import'
99
+ }
100
+ })
101
+
102
+ export const resourceGovernanceModel = {
103
+ systems: {
104
+ systems
105
+ },
106
+ resources: {
107
+ entries: Object.values(resourceDescriptors)
108
+ }
109
+ } as const
95
110
  ```
96
111
 
97
112
  ## Domain Semantics
@@ -102,7 +117,10 @@ The model keeps business semantics in named domains:
102
117
  - `prospecting` -- company/contact lifecycle stages
103
118
  - `projects` -- project, milestone, and task statuses
104
119
  - `identity`, `customers`, `offerings`, `roles`, `goals` -- organizational reality
120
+ - `systems` -- bounded contexts that group governed operational resources
121
+ - `resources` -- governance-only workflow, agent, and integration descriptors
105
122
  - `statuses` and `operations` -- runtime/vibe-layer semantic registries
123
+ - `knowledge` -- playbooks, strategies, references, and graph-governing links
106
124
 
107
125
  ## Authoring and Resolution
108
126
 
@@ -121,8 +139,9 @@ The model keeps business semantics in named domains:
121
139
  - container/leaf path rules
122
140
  - valid sidebar positions
123
141
  - reality-domain cross references such as offering target segments and role reporting lines
142
+ - system, resource, role, knowledge, and goal cross references used by resource governance
124
143
 
125
- Resource graph links are validated during resource registry registration because resources live in deployment specs.
144
+ `DeploymentSpec` remains the runtime/deploy assembly around these descriptors. It is not the source of resource identity.
126
145
 
127
146
  ## Provider Integration
128
147
 
@@ -48,8 +48,40 @@ export {
48
48
  ProductSchema,
49
49
  PricingModelSchema
50
50
  } from './domains/offerings'
51
- export { DEFAULT_ORGANIZATION_MODEL_ROLES, RolesDomainSchema, RoleSchema } from './domains/roles'
51
+ export {
52
+ AgentRoleHolderSchema,
53
+ DEFAULT_ORGANIZATION_MODEL_ROLES,
54
+ HumanRoleHolderSchema,
55
+ RoleHolderSchema,
56
+ RoleHoldersSchema,
57
+ RoleIdSchema,
58
+ RolesDomainSchema,
59
+ RoleSchema,
60
+ TeamRoleHolderSchema
61
+ } from './domains/roles'
52
62
  export { DEFAULT_ORGANIZATION_MODEL_GOALS, GoalsDomainSchema, ObjectiveSchema, KeyResultSchema } from './domains/goals'
63
+ export {
64
+ DEFAULT_ORGANIZATION_MODEL_SYSTEMS,
65
+ SystemEntrySchema,
66
+ SystemIdSchema,
67
+ SystemKindSchema,
68
+ SystemsDomainSchema,
69
+ SystemStatusSchema
70
+ } from './domains/systems'
71
+ export {
72
+ AgentKindSchema,
73
+ AgentResourceEntrySchema,
74
+ DEFAULT_ORGANIZATION_MODEL_RESOURCES,
75
+ defineResource,
76
+ defineResources,
77
+ IntegrationResourceEntrySchema,
78
+ ResourceEntrySchema,
79
+ ResourceGovernanceStatusSchema,
80
+ ResourceIdSchema,
81
+ ResourceKindSchema,
82
+ ResourcesDomainSchema,
83
+ WorkflowResourceEntrySchema
84
+ } from './domains/resources'
53
85
  export {
54
86
  KnowledgeDomainSchema,
55
87
  KnowledgeLinkSchema,
@@ -73,6 +105,20 @@ export type {
73
105
  OrganizationModelGoals,
74
106
  OrganizationModelKeyResult,
75
107
  OrganizationModelObjective,
108
+ OrganizationModelSystemEntry,
109
+ OrganizationModelSystemId,
110
+ OrganizationModelSystemKind,
111
+ OrganizationModelSystems,
112
+ OrganizationModelSystemStatus,
113
+ OrganizationModelAgentKind,
114
+ OrganizationModelAgentResourceEntry,
115
+ OrganizationModelIntegrationResourceEntry,
116
+ OrganizationModelResourceEntry,
117
+ OrganizationModelResourceGovernanceStatus,
118
+ OrganizationModelResourceId,
119
+ OrganizationModelResourceKind,
120
+ OrganizationModelResources,
121
+ OrganizationModelWorkflowResourceEntry,
76
122
  OrganizationModelOfferings,
77
123
  OrganizationModelOperationEntry,
78
124
  OrganizationModelOperationSemanticClass,
@@ -80,7 +126,12 @@ export type {
80
126
  OrganizationModelPricingModel,
81
127
  OrganizationModelProduct,
82
128
  OrganizationModelRole,
129
+ OrganizationModelAgentRoleHolder,
130
+ OrganizationModelHumanRoleHolder,
131
+ OrganizationModelRoleHolder,
132
+ OrganizationModelRoleId,
83
133
  OrganizationModelTechStackEntry,
134
+ OrganizationModelTeamRoleHolder,
84
135
  OrganizationModelRoles,
85
136
  OrganizationModelStatuses,
86
137
  OrganizationModelStatusEntry,
@@ -13,6 +13,8 @@ import { GoalsDomainSchema, DEFAULT_ORGANIZATION_MODEL_GOALS } from './domains/g
13
13
  import { OperationsDomainSchema } from './domains/operations'
14
14
  import { StatusesDomainSchema } from './domains/statuses'
15
15
  import { KnowledgeDomainSchema } from './domains/knowledge'
16
+ import { SystemsDomainSchema, DEFAULT_ORGANIZATION_MODEL_SYSTEMS } from './domains/systems'
17
+ import { ResourcesDomainSchema, DEFAULT_ORGANIZATION_MODEL_RESOURCES } from './domains/resources'
16
18
 
17
19
  const OrganizationModelSchemaBase = z.object({
18
20
  version: z.literal(1).default(1),
@@ -27,6 +29,8 @@ const OrganizationModelSchemaBase = z.object({
27
29
  offerings: OfferingsDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_OFFERINGS),
28
30
  roles: RolesDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_ROLES),
29
31
  goals: GoalsDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_GOALS),
32
+ systems: SystemsDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_SYSTEMS),
33
+ resources: ResourcesDomainSchema.default(DEFAULT_ORGANIZATION_MODEL_RESOURCES),
30
34
  statuses: StatusesDomainSchema.default({ entries: [] }),
31
35
  operations: OperationsDomainSchema.default({ entries: [] }),
32
36
  knowledge: KnowledgeDomainSchema.default({ nodes: [] })
@@ -74,6 +78,12 @@ function defaultFeaturePathFor(id: string): string {
74
78
  return `/${id.replaceAll('.', '/')}`
75
79
  }
76
80
 
81
+ function asRoleHolderArray(
82
+ heldBy: NonNullable<z.infer<typeof OrganizationModelSchemaBase>['roles']['roles'][number]['heldBy']>
83
+ ) {
84
+ return Array.isArray(heldBy) ? heldBy : [heldBy]
85
+ }
86
+
77
87
  export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((model, ctx) => {
78
88
  const featuresById = collectIds(model.features, ctx, ['features'], 'Feature')
79
89
  const featureIdsByEffectivePath = new Map<string, string>()
@@ -199,6 +209,9 @@ export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((
199
209
  }
200
210
  })
201
211
 
212
+ const goalsById = new Map(model.goals.objectives.map((objective) => [objective.id, objective]))
213
+ const knowledgeById = new Map(model.knowledge.nodes.map((node) => [node.id, node]))
214
+
202
215
  // Roles -> reportsToId cross-ref: each reportsToId must resolve to another role in the same collection
203
216
  const rolesById = new Map(model.roles.roles.map((role) => [role.id, role]))
204
217
  model.roles.roles.forEach((role, roleIndex) => {
@@ -210,4 +223,112 @@ export const OrganizationModelSchema = OrganizationModelSchemaBase.superRefine((
210
223
  )
211
224
  }
212
225
  })
226
+
227
+ const systemsById = collectIds(model.systems.systems, ctx, ['systems', 'systems'], 'System')
228
+ model.roles.roles.forEach((role, roleIndex) => {
229
+ role.responsibleFor?.forEach((systemId, systemIndex) => {
230
+ if (!systemsById.has(systemId)) {
231
+ addIssue(
232
+ ctx,
233
+ ['roles', 'roles', roleIndex, 'responsibleFor', systemIndex],
234
+ `Role "${role.id}" references unknown responsibleFor system "${systemId}"`
235
+ )
236
+ }
237
+ })
238
+ })
239
+
240
+ model.systems.systems.forEach((system, systemIndex) => {
241
+ if (system.responsibleRoleId !== undefined && !rolesById.has(system.responsibleRoleId)) {
242
+ addIssue(
243
+ ctx,
244
+ ['systems', 'systems', systemIndex, 'responsibleRoleId'],
245
+ `System "${system.id}" references unknown responsibleRoleId "${system.responsibleRoleId}"`
246
+ )
247
+ }
248
+
249
+ system.governedByKnowledge.forEach((nodeId, nodeIndex) => {
250
+ if (!knowledgeById.has(nodeId)) {
251
+ addIssue(
252
+ ctx,
253
+ ['systems', 'systems', systemIndex, 'governedByKnowledge', nodeIndex],
254
+ `System "${system.id}" references unknown knowledge node "${nodeId}"`
255
+ )
256
+ }
257
+ })
258
+
259
+ system.drivesGoals.forEach((goalId, goalIndex) => {
260
+ if (!goalsById.has(goalId)) {
261
+ addIssue(
262
+ ctx,
263
+ ['systems', 'systems', systemIndex, 'drivesGoals', goalIndex],
264
+ `System "${system.id}" references unknown goal "${goalId}"`
265
+ )
266
+ }
267
+ })
268
+ })
269
+
270
+ const resourcesById = collectIds(model.resources.entries, ctx, ['resources', 'entries'], 'Resource')
271
+ model.resources.entries.forEach((resource, resourceIndex) => {
272
+ if (!systemsById.has(resource.systemId)) {
273
+ addIssue(
274
+ ctx,
275
+ ['resources', 'entries', resourceIndex, 'systemId'],
276
+ `Resource "${resource.id}" references unknown systemId "${resource.systemId}"`
277
+ )
278
+ }
279
+
280
+ if (resource.ownerRoleId !== undefined && !rolesById.has(resource.ownerRoleId)) {
281
+ addIssue(
282
+ ctx,
283
+ ['resources', 'entries', resourceIndex, 'ownerRoleId'],
284
+ `Resource "${resource.id}" references unknown ownerRoleId "${resource.ownerRoleId}"`
285
+ )
286
+ }
287
+
288
+ if (resource.kind === 'agent' && resource.actsAsRoleId !== undefined && !rolesById.has(resource.actsAsRoleId)) {
289
+ addIssue(
290
+ ctx,
291
+ ['resources', 'entries', resourceIndex, 'actsAsRoleId'],
292
+ `Agent resource "${resource.id}" references unknown actsAsRoleId "${resource.actsAsRoleId}"`
293
+ )
294
+ }
295
+ })
296
+
297
+ model.roles.roles.forEach((role, roleIndex) => {
298
+ if (role.heldBy === undefined) return
299
+
300
+ asRoleHolderArray(role.heldBy).forEach((holder, holderIndex) => {
301
+ if (holder.kind !== 'agent') return
302
+
303
+ const resource = resourcesById.get(holder.agentId)
304
+ if (resource === undefined) {
305
+ addIssue(
306
+ ctx,
307
+ ['roles', 'roles', roleIndex, 'heldBy', Array.isArray(role.heldBy) ? holderIndex : 'agentId'],
308
+ `Role "${role.id}" references unknown agent holder resource "${holder.agentId}"`
309
+ )
310
+ return
311
+ }
312
+
313
+ if (resource.kind !== 'agent') {
314
+ addIssue(
315
+ ctx,
316
+ ['roles', 'roles', roleIndex, 'heldBy', Array.isArray(role.heldBy) ? holderIndex : 'agentId'],
317
+ `Role "${role.id}" agent holder "${holder.agentId}" must reference an agent resource`
318
+ )
319
+ }
320
+ })
321
+ })
322
+
323
+ model.knowledge.nodes.forEach((node, nodeIndex) => {
324
+ node.ownerIds.forEach((roleId, ownerIndex) => {
325
+ if (!rolesById.has(roleId)) {
326
+ addIssue(
327
+ ctx,
328
+ ['knowledge', 'nodes', nodeIndex, 'ownerIds', ownerIndex],
329
+ `Knowledge node "${node.id}" references unknown owner role "${roleId}"`
330
+ )
331
+ }
332
+ })
333
+ })
213
334
  })
@@ -10,7 +10,15 @@ import { OperationsDomainSchema, OperationEntrySchema, OperationSemanticClassSch
10
10
  import { StatusesDomainSchema, StatusEntrySchema, StatusSemanticClassSchema } from './domains/statuses'
11
11
  import { CustomersDomainSchema, CustomerSegmentSchema, FirmographicsSchema } from './domains/customers'
12
12
  import { OfferingsDomainSchema, ProductSchema, PricingModelSchema } from './domains/offerings'
13
- import { RolesDomainSchema, RoleSchema } from './domains/roles'
13
+ import {
14
+ AgentRoleHolderSchema,
15
+ HumanRoleHolderSchema,
16
+ RoleHolderSchema,
17
+ RoleIdSchema,
18
+ RolesDomainSchema,
19
+ RoleSchema,
20
+ TeamRoleHolderSchema
21
+ } from './domains/roles'
14
22
  import { GoalsDomainSchema, ObjectiveSchema, KeyResultSchema } from './domains/goals'
15
23
  import {
16
24
  KnowledgeDomainBindingSchema,
@@ -19,6 +27,24 @@ import {
19
27
  OrgKnowledgeKindSchema,
20
28
  OrgKnowledgeNodeSchema
21
29
  } from './domains/knowledge'
30
+ import {
31
+ SystemEntrySchema,
32
+ SystemIdSchema,
33
+ SystemKindSchema,
34
+ SystemsDomainSchema,
35
+ SystemStatusSchema
36
+ } from './domains/systems'
37
+ import {
38
+ AgentKindSchema,
39
+ AgentResourceEntrySchema,
40
+ IntegrationResourceEntrySchema,
41
+ ResourceEntrySchema,
42
+ ResourceGovernanceStatusSchema,
43
+ ResourceIdSchema,
44
+ ResourceKindSchema,
45
+ ResourcesDomainSchema,
46
+ WorkflowResourceEntrySchema
47
+ } from './domains/resources'
22
48
  import { OrganizationModelIconTokenSchema, OrganizationModelBuiltinIconTokenSchema } from './icons'
23
49
  import { OrganizationModelSchema } from './schema'
24
50
 
@@ -47,9 +73,28 @@ export type OrganizationModelProduct = z.infer<typeof ProductSchema>
47
73
  export type OrganizationModelPricingModel = z.infer<typeof PricingModelSchema>
48
74
  export type OrganizationModelRoles = z.infer<typeof RolesDomainSchema>
49
75
  export type OrganizationModelRole = z.infer<typeof RoleSchema>
76
+ export type OrganizationModelRoleId = z.infer<typeof RoleIdSchema>
77
+ export type OrganizationModelRoleHolder = z.infer<typeof RoleHolderSchema>
78
+ export type OrganizationModelHumanRoleHolder = z.infer<typeof HumanRoleHolderSchema>
79
+ export type OrganizationModelAgentRoleHolder = z.infer<typeof AgentRoleHolderSchema>
80
+ export type OrganizationModelTeamRoleHolder = z.infer<typeof TeamRoleHolderSchema>
50
81
  export type OrganizationModelGoals = z.infer<typeof GoalsDomainSchema>
51
82
  export type OrganizationModelObjective = z.infer<typeof ObjectiveSchema>
52
83
  export type OrganizationModelKeyResult = z.infer<typeof KeyResultSchema>
84
+ export type OrganizationModelSystems = z.infer<typeof SystemsDomainSchema>
85
+ export type OrganizationModelSystemEntry = z.infer<typeof SystemEntrySchema>
86
+ export type OrganizationModelSystemId = z.infer<typeof SystemIdSchema>
87
+ export type OrganizationModelSystemKind = z.infer<typeof SystemKindSchema>
88
+ export type OrganizationModelSystemStatus = z.infer<typeof SystemStatusSchema>
89
+ export type OrganizationModelResources = z.infer<typeof ResourcesDomainSchema>
90
+ export type OrganizationModelResourceEntry = z.infer<typeof ResourceEntrySchema>
91
+ export type OrganizationModelResourceId = z.infer<typeof ResourceIdSchema>
92
+ export type OrganizationModelResourceKind = z.infer<typeof ResourceKindSchema>
93
+ export type OrganizationModelResourceGovernanceStatus = z.infer<typeof ResourceGovernanceStatusSchema>
94
+ export type OrganizationModelAgentKind = z.infer<typeof AgentKindSchema>
95
+ export type OrganizationModelWorkflowResourceEntry = z.infer<typeof WorkflowResourceEntrySchema>
96
+ export type OrganizationModelAgentResourceEntry = z.infer<typeof AgentResourceEntrySchema>
97
+ export type OrganizationModelIntegrationResourceEntry = z.infer<typeof IntegrationResourceEntrySchema>
53
98
  export type OrganizationModelKnowledge = z.infer<typeof KnowledgeDomainSchema>
54
99
  export type OrgKnowledgeNode = z.infer<typeof OrgKnowledgeNodeSchema>
55
100
  export type OrgKnowledgeKind = z.infer<typeof OrgKnowledgeKindSchema>
@@ -1,35 +1,38 @@
1
- /**
2
- * Standard API error response format
3
- * Returned by APIError.toJSON() in apps/api
4
- * Consumed by Command Center UI for type-safe error handling
5
- */
6
- export interface APIErrorResponse {
7
- /** Human-readable error message */
8
- error: string
9
-
10
- /** Machine-readable error code */
11
- code: APIErrorCode
12
-
13
- /** Request ID for debugging (optional) */
14
- requestId?: string
15
-
16
- /** Field-level validation errors (only present for VALIDATION_ERROR code) */
17
- fields?: Record<string, string[]>
18
-
19
- /** Seconds until the rate limit resets (only present for RATE_LIMIT_EXCEEDED code) */
20
- retryAfter?: number
21
- }
22
-
23
- /**
24
- * Type-safe error codes
25
- * MUST match API_ERROR_CODES in apps/api/src/errors/error-codes.ts
26
- */
27
- export type APIErrorCode =
28
- | 'VALIDATION_ERROR'
29
- | 'AUTHENTICATION_FAILED'
30
- | 'FORBIDDEN'
31
- | 'NOT_FOUND'
32
- | 'CONFLICT'
33
- | 'RATE_LIMIT_EXCEEDED'
34
- | 'INTERNAL_SERVER_ERROR'
35
- | 'SERVICE_UNAVAILABLE'
1
+ /**
2
+ * Standard API error response format
3
+ * Returned by APIError.toJSON() in apps/api
4
+ * Consumed by Command Center UI for type-safe error handling
5
+ */
6
+ export interface APIErrorResponse {
7
+ /** Human-readable error message */
8
+ error: string
9
+
10
+ /** Machine-readable error code */
11
+ code: APIErrorCode
12
+
13
+ /** Request ID for debugging (optional) */
14
+ requestId?: string
15
+
16
+ /** Field-level validation errors (only present for VALIDATION_ERROR code) */
17
+ fields?: Record<string, string[]>
18
+
19
+ /** Seconds until the rate limit resets (only present for RATE_LIMIT_EXCEEDED code) */
20
+ retryAfter?: number
21
+
22
+ /** Structured detail payload (e.g. linked-row counts on CONFLICT errors) */
23
+ details?: Record<string, unknown>
24
+ }
25
+
26
+ /**
27
+ * Type-safe error codes
28
+ * MUST match API_ERROR_CODES in apps/api/src/errors/error-codes.ts
29
+ */
30
+ export type APIErrorCode =
31
+ | 'VALIDATION_ERROR'
32
+ | 'AUTHENTICATION_FAILED'
33
+ | 'FORBIDDEN'
34
+ | 'NOT_FOUND'
35
+ | 'CONFLICT'
36
+ | 'RATE_LIMIT_EXCEEDED'
37
+ | 'INTERNAL_SERVER_ERROR'
38
+ | 'SERVICE_UNAVAILABLE'