@company-semantics/contracts 9.0.0 → 9.2.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 (141) hide show
  1. package/package.json +6 -3
  2. package/src/__tests__/resource-keys.test.ts +30 -23
  3. package/src/admin/authz-simulate.ts +4 -4
  4. package/src/admin/direct-grants.ts +2 -2
  5. package/src/api/generated-spec-hash.ts +2 -2
  6. package/src/api/generated.ts +97 -0
  7. package/src/api/http/routes/ai-chat.ts +3 -3
  8. package/src/api/http/utils/resource-response.ts +5 -2
  9. package/src/api/index.ts +4 -4
  10. package/src/api/primitives.ts +6 -2
  11. package/src/auth/README.md +1 -0
  12. package/src/auth/index.ts +12 -5
  13. package/src/autotune.ts +5 -1
  14. package/src/billing/index.ts +1 -1
  15. package/src/billing/types.ts +1 -1
  16. package/src/chat/README.md +3 -0
  17. package/src/chat/__tests__/runtime-profile.test.ts +68 -48
  18. package/src/chat/index.ts +10 -4
  19. package/src/chat/runtime-profile.ts +25 -10
  20. package/src/chat/schemas.ts +49 -41
  21. package/src/chat/types.ts +48 -42
  22. package/src/ci-envelope/README.md +2 -0
  23. package/src/ci-envelope/__tests__/transitions.test.ts +56 -56
  24. package/src/ci-envelope/index.ts +2 -2
  25. package/src/ci-envelope/types.ts +20 -20
  26. package/src/ci-results/index.ts +2 -2
  27. package/src/ci-results/repo-ci-result.ts +15 -12
  28. package/src/compatibility.ts +6 -6
  29. package/src/content/index.ts +10 -4
  30. package/src/content/schemas.ts +42 -24
  31. package/src/dispatch/index.ts +18 -15
  32. package/src/email/__tests__/registry.test.ts +81 -77
  33. package/src/email/index.ts +3 -3
  34. package/src/email/registry.ts +25 -25
  35. package/src/email/types.ts +43 -43
  36. package/src/errors/index.ts +8 -8
  37. package/src/execution/__tests__/events.test.ts +42 -42
  38. package/src/execution/__tests__/lifecycle.test.ts +192 -190
  39. package/src/execution/__tests__/registry.test.ts +114 -114
  40. package/src/execution/audit-export.ts +4 -4
  41. package/src/execution/errors.ts +7 -7
  42. package/src/execution/event-metadata.ts +4 -4
  43. package/src/execution/events.ts +23 -21
  44. package/src/execution/expiry.ts +5 -5
  45. package/src/execution/hash-chain.ts +2 -2
  46. package/src/execution/index.ts +19 -28
  47. package/src/execution/kinds.ts +7 -7
  48. package/src/execution/lifecycle.ts +33 -33
  49. package/src/execution/registry.ts +63 -63
  50. package/src/execution/schemas.ts +31 -23
  51. package/src/execution/status.ts +45 -26
  52. package/src/execution/summary.ts +16 -17
  53. package/src/execution/timeline-ui.ts +9 -9
  54. package/src/execution/types.ts +31 -25
  55. package/src/generated/openapi-routes.ts +2 -0
  56. package/src/guards/config.ts +22 -18
  57. package/src/guards/index.ts +4 -4
  58. package/src/guards/types.ts +32 -24
  59. package/src/identity/__tests__/avatar.test.ts +68 -59
  60. package/src/identity/avatar.ts +8 -8
  61. package/src/identity/display-name.ts +3 -3
  62. package/src/identity/index.ts +8 -8
  63. package/src/identity/people-org-chart.ts +8 -4
  64. package/src/identity/schemas.ts +28 -18
  65. package/src/identity/types.ts +5 -5
  66. package/src/impersonation/index.ts +5 -5
  67. package/src/impersonation/schemas.ts +15 -9
  68. package/src/impersonation-events.ts +21 -21
  69. package/src/impersonation.ts +25 -24
  70. package/src/index.ts +118 -90
  71. package/src/interfaces/mcp/tools/help.ts +19 -19
  72. package/src/internal-admin.ts +6 -6
  73. package/src/mcp/README.md +2 -0
  74. package/src/mcp/__tests__/capability-graph.test.ts +290 -290
  75. package/src/mcp/capability-graph.ts +42 -40
  76. package/src/mcp/failure-context.ts +1 -3
  77. package/src/mcp/index.ts +69 -56
  78. package/src/mcp/resources.ts +9 -9
  79. package/src/meetings/index.ts +2 -2
  80. package/src/meetings/schemas.ts +51 -34
  81. package/src/message-parts/README.md +2 -0
  82. package/src/message-parts/__tests__/builder.test.ts +142 -142
  83. package/src/message-parts/__tests__/confirmation.test.ts +100 -86
  84. package/src/message-parts/__tests__/preview.test.ts +63 -63
  85. package/src/message-parts/__tests__/wire.test.ts +130 -124
  86. package/src/message-parts/builder.ts +23 -23
  87. package/src/message-parts/confirmation.ts +17 -14
  88. package/src/message-parts/execution.ts +7 -7
  89. package/src/message-parts/index.ts +10 -10
  90. package/src/message-parts/lifecycle.ts +25 -25
  91. package/src/message-parts/preview.ts +30 -30
  92. package/src/message-parts/types.ts +27 -27
  93. package/src/message-parts/wire.ts +24 -24
  94. package/src/mutations.ts +2 -2
  95. package/src/observability.ts +23 -11
  96. package/src/org/__tests__/org-units.test.ts +131 -96
  97. package/src/org/__tests__/tree-ordering.test.ts +57 -37
  98. package/src/org/__tests__/view-scopes.test.ts +40 -40
  99. package/src/org/domain.ts +9 -9
  100. package/src/org/index.ts +31 -21
  101. package/src/org/org-units.ts +34 -20
  102. package/src/org/schemas.ts +261 -124
  103. package/src/org/sharing.ts +17 -13
  104. package/src/org/tree-ordering.ts +3 -1
  105. package/src/org/types.ts +54 -47
  106. package/src/org/view-scopes.ts +9 -9
  107. package/src/permissions/access-levels.ts +7 -2
  108. package/src/permissions/access-source.ts +6 -6
  109. package/src/permissions/index.ts +5 -5
  110. package/src/permissions/orgchart-roles.ts +7 -7
  111. package/src/permissions/permission-introspection.ts +7 -5
  112. package/src/permissions/share-api.ts +19 -9
  113. package/src/pressure.ts +4 -4
  114. package/src/queryIntent.ts +21 -21
  115. package/src/ralph/__tests__/prd-groups.test.ts +159 -159
  116. package/src/ralph/__tests__/prd.test.ts +30 -30
  117. package/src/ralph/index.ts +3 -8
  118. package/src/ralph/prd.ts +33 -33
  119. package/src/ralph/progress.ts +1 -1
  120. package/src/rate-limit/README.md +4 -4
  121. package/src/rate-limit/index.ts +3 -3
  122. package/src/requests.ts +36 -8
  123. package/src/resource-keys.ts +207 -124
  124. package/src/resource-registry.ts +5 -5
  125. package/src/route-builder.ts +3 -3
  126. package/src/safe-mode.ts +2 -2
  127. package/src/security/index.ts +4 -4
  128. package/src/security/org-secrets.ts +13 -9
  129. package/src/security/secret.ts +3 -3
  130. package/src/sse.ts +3 -1
  131. package/src/system/README.md +3 -0
  132. package/src/system/capabilities.ts +22 -23
  133. package/src/system/diagram.ts +45 -45
  134. package/src/system/index.ts +14 -14
  135. package/src/tiers.ts +1 -1
  136. package/src/timeouts.ts +1 -1
  137. package/src/tracing.ts +30 -30
  138. package/src/types/analytics.ts +2 -2
  139. package/src/usage/README.md +3 -0
  140. package/src/usage/execution-types.ts +69 -69
  141. package/src/usage/types.ts +7 -3
@@ -1,121 +1,125 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { EMAIL_KINDS, getEmailKindDefinition, isValidEmailKind } from '../registry.js'
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ EMAIL_KINDS,
4
+ getEmailKindDefinition,
5
+ isValidEmailKind,
6
+ } from "../registry.js";
3
7
 
4
- describe('EMAIL_KINDS golden snapshot', () => {
5
- it('exact values are frozen', () => {
8
+ describe("EMAIL_KINDS golden snapshot", () => {
9
+ it("exact values are frozen", () => {
6
10
  expect(EMAIL_KINDS).toStrictEqual({
7
- 'auth.otp': {
8
- kind: 'auth.otp',
9
- subject: 'Your login code',
11
+ "auth.otp": {
12
+ kind: "auth.otp",
13
+ subject: "Your login code",
10
14
  plainTextRequired: true,
11
15
  htmlSupported: false,
12
16
  },
13
- 'auth.magic_link': {
14
- kind: 'auth.magic_link',
15
- subject: 'Your login link',
17
+ "auth.magic_link": {
18
+ kind: "auth.magic_link",
19
+ subject: "Your login link",
16
20
  plainTextRequired: true,
17
21
  htmlSupported: false,
18
22
  },
19
- 'org.invite': {
20
- kind: 'org.invite',
21
- subject: 'You have been invited to join a workspace',
23
+ "org.invite": {
24
+ kind: "org.invite",
25
+ subject: "You have been invited to join a workspace",
22
26
  plainTextRequired: true,
23
27
  htmlSupported: true,
24
28
  },
25
- 'org.leadership_granted': {
26
- kind: 'org.leadership_granted',
29
+ "org.leadership_granted": {
30
+ kind: "org.leadership_granted",
27
31
  subject: "You've been added to a team",
28
32
  plainTextRequired: true,
29
33
  htmlSupported: true,
30
34
  },
31
- 'security.alert': {
32
- kind: 'security.alert',
33
- subject: 'Security alert for your account',
35
+ "security.alert": {
36
+ kind: "security.alert",
37
+ subject: "Security alert for your account",
34
38
  plainTextRequired: true,
35
39
  htmlSupported: false,
36
40
  },
37
- 'chat.shared': {
38
- kind: 'chat.shared',
39
- subject: 'A chat has been shared with you',
41
+ "chat.shared": {
42
+ kind: "chat.shared",
43
+ subject: "A chat has been shared with you",
40
44
  plainTextRequired: true,
41
45
  htmlSupported: true,
42
46
  },
43
- })
44
- })
45
- })
47
+ });
48
+ });
49
+ });
46
50
 
47
- describe('EMAIL_KINDS registry invariants', () => {
48
- it('every registry key matches its definition.kind field', () => {
51
+ describe("EMAIL_KINDS registry invariants", () => {
52
+ it("every registry key matches its definition.kind field", () => {
49
53
  for (const [key, def] of Object.entries(EMAIL_KINDS)) {
50
- expect(def.kind).toBe(key)
54
+ expect(def.kind).toBe(key);
51
55
  }
52
- })
56
+ });
53
57
 
54
- it('all subjects are non-empty strings', () => {
58
+ it("all subjects are non-empty strings", () => {
55
59
  for (const def of Object.values(EMAIL_KINDS)) {
56
- expect(typeof def.subject).toBe('string')
57
- expect(def.subject.length).toBeGreaterThan(0)
60
+ expect(typeof def.subject).toBe("string");
61
+ expect(def.subject.length).toBeGreaterThan(0);
58
62
  }
59
- })
63
+ });
60
64
 
61
- it('all entries have required boolean fields plainTextRequired and htmlSupported', () => {
65
+ it("all entries have required boolean fields plainTextRequired and htmlSupported", () => {
62
66
  for (const def of Object.values(EMAIL_KINDS)) {
63
- expect(typeof def.plainTextRequired).toBe('boolean')
64
- expect(typeof def.htmlSupported).toBe('boolean')
67
+ expect(typeof def.plainTextRequired).toBe("boolean");
68
+ expect(typeof def.htmlSupported).toBe("boolean");
65
69
  }
66
- })
67
- })
70
+ });
71
+ });
68
72
 
69
- describe('getEmailKindDefinition', () => {
70
- it('returns correct definition for auth.otp', () => {
71
- const def = getEmailKindDefinition('auth.otp')
72
- expect(def).toStrictEqual(EMAIL_KINDS['auth.otp'])
73
- })
73
+ describe("getEmailKindDefinition", () => {
74
+ it("returns correct definition for auth.otp", () => {
75
+ const def = getEmailKindDefinition("auth.otp");
76
+ expect(def).toStrictEqual(EMAIL_KINDS["auth.otp"]);
77
+ });
74
78
 
75
- it('returns correct definition for auth.magic_link', () => {
76
- const def = getEmailKindDefinition('auth.magic_link')
77
- expect(def).toStrictEqual(EMAIL_KINDS['auth.magic_link'])
78
- })
79
+ it("returns correct definition for auth.magic_link", () => {
80
+ const def = getEmailKindDefinition("auth.magic_link");
81
+ expect(def).toStrictEqual(EMAIL_KINDS["auth.magic_link"]);
82
+ });
79
83
 
80
- it('returns correct definition for org.invite', () => {
81
- const def = getEmailKindDefinition('org.invite')
82
- expect(def).toStrictEqual(EMAIL_KINDS['org.invite'])
83
- })
84
+ it("returns correct definition for org.invite", () => {
85
+ const def = getEmailKindDefinition("org.invite");
86
+ expect(def).toStrictEqual(EMAIL_KINDS["org.invite"]);
87
+ });
84
88
 
85
- it('returns correct definition for security.alert', () => {
86
- const def = getEmailKindDefinition('security.alert')
87
- expect(def).toStrictEqual(EMAIL_KINDS['security.alert'])
88
- })
89
+ it("returns correct definition for security.alert", () => {
90
+ const def = getEmailKindDefinition("security.alert");
91
+ expect(def).toStrictEqual(EMAIL_KINDS["security.alert"]);
92
+ });
89
93
 
90
- it('returns correct definition for chat.shared', () => {
91
- const def = getEmailKindDefinition('chat.shared')
92
- expect(def).toStrictEqual(EMAIL_KINDS['chat.shared'])
93
- })
94
+ it("returns correct definition for chat.shared", () => {
95
+ const def = getEmailKindDefinition("chat.shared");
96
+ expect(def).toStrictEqual(EMAIL_KINDS["chat.shared"]);
97
+ });
94
98
 
95
- it('return value matches registry entry exactly', () => {
99
+ it("return value matches registry entry exactly", () => {
96
100
  for (const [key, expected] of Object.entries(EMAIL_KINDS)) {
97
- const def = getEmailKindDefinition(key as keyof typeof EMAIL_KINDS)
98
- expect(def).toBe(expected)
101
+ const def = getEmailKindDefinition(key as keyof typeof EMAIL_KINDS);
102
+ expect(def).toBe(expected);
99
103
  }
100
- })
101
- })
104
+ });
105
+ });
102
106
 
103
- describe('isValidEmailKind', () => {
104
- it('returns true for all valid email kinds', () => {
107
+ describe("isValidEmailKind", () => {
108
+ it("returns true for all valid email kinds", () => {
105
109
  for (const kind of Object.keys(EMAIL_KINDS)) {
106
- expect(isValidEmailKind(kind)).toBe(true)
110
+ expect(isValidEmailKind(kind)).toBe(true);
107
111
  }
108
- })
112
+ });
109
113
 
110
- it('returns false for unknown string', () => {
111
- expect(isValidEmailKind('unknown.kind')).toBe(false)
112
- })
114
+ it("returns false for unknown string", () => {
115
+ expect(isValidEmailKind("unknown.kind")).toBe(false);
116
+ });
113
117
 
114
- it('returns false for empty string', () => {
115
- expect(isValidEmailKind('')).toBe(false)
116
- })
118
+ it("returns false for empty string", () => {
119
+ expect(isValidEmailKind("")).toBe(false);
120
+ });
117
121
 
118
- it('returns false for partial match', () => {
119
- expect(isValidEmailKind('auth')).toBe(false)
120
- })
121
- })
122
+ it("returns false for partial match", () => {
123
+ expect(isValidEmailKind("auth")).toBe(false);
124
+ });
125
+ });
@@ -11,13 +11,13 @@
11
11
  // Kind Types
12
12
  // =============================================================================
13
13
 
14
- export type { EmailKind, EmailPayloads, SendEmailInput } from './types'
14
+ export type { EmailKind, EmailPayloads, SendEmailInput } from "./types";
15
15
 
16
16
  // =============================================================================
17
17
  // Definition Types
18
18
  // =============================================================================
19
19
 
20
- export type { EmailKindDefinition } from './registry'
20
+ export type { EmailKindDefinition } from "./registry";
21
21
 
22
22
  // =============================================================================
23
23
  // Registry
@@ -27,4 +27,4 @@ export {
27
27
  EMAIL_KINDS,
28
28
  getEmailKindDefinition,
29
29
  isValidEmailKind,
30
- } from './registry'
30
+ } from "./registry";
@@ -13,7 +13,7 @@
13
13
  * @see ADR-CONT-034 for design rationale
14
14
  */
15
15
 
16
- import type { EmailKind } from './types'
16
+ import type { EmailKind } from "./types";
17
17
 
18
18
  // =============================================================================
19
19
  // Email Kind Definition
@@ -31,13 +31,13 @@ import type { EmailKind } from './types'
31
31
  */
32
32
  export interface EmailKindDefinition {
33
33
  /** The email kind this definition describes */
34
- kind: EmailKind
34
+ kind: EmailKind;
35
35
  /** Email subject line (single source of truth) */
36
- subject: string
36
+ subject: string;
37
37
  /** Whether plain text body is required */
38
- plainTextRequired: boolean
38
+ plainTextRequired: boolean;
39
39
  /** Whether HTML body is supported */
40
- htmlSupported: boolean
40
+ htmlSupported: boolean;
41
41
  }
42
42
 
43
43
  // =============================================================================
@@ -58,43 +58,43 @@ export interface EmailKindDefinition {
58
58
  * 4. Implement template in backend
59
59
  */
60
60
  export const EMAIL_KINDS = {
61
- 'auth.otp': {
62
- kind: 'auth.otp',
63
- subject: 'Your login code',
61
+ "auth.otp": {
62
+ kind: "auth.otp",
63
+ subject: "Your login code",
64
64
  plainTextRequired: true,
65
65
  htmlSupported: false,
66
66
  },
67
- 'auth.magic_link': {
68
- kind: 'auth.magic_link',
69
- subject: 'Your login link',
67
+ "auth.magic_link": {
68
+ kind: "auth.magic_link",
69
+ subject: "Your login link",
70
70
  plainTextRequired: true,
71
71
  htmlSupported: false,
72
72
  },
73
- 'org.invite': {
74
- kind: 'org.invite',
75
- subject: 'You have been invited to join a workspace',
73
+ "org.invite": {
74
+ kind: "org.invite",
75
+ subject: "You have been invited to join a workspace",
76
76
  plainTextRequired: true,
77
77
  htmlSupported: true,
78
78
  },
79
- 'org.leadership_granted': {
80
- kind: 'org.leadership_granted',
79
+ "org.leadership_granted": {
80
+ kind: "org.leadership_granted",
81
81
  subject: "You've been added to a team",
82
82
  plainTextRequired: true,
83
83
  htmlSupported: true,
84
84
  },
85
- 'security.alert': {
86
- kind: 'security.alert',
87
- subject: 'Security alert for your account',
85
+ "security.alert": {
86
+ kind: "security.alert",
87
+ subject: "Security alert for your account",
88
88
  plainTextRequired: true,
89
89
  htmlSupported: false,
90
90
  },
91
- 'chat.shared': {
92
- kind: 'chat.shared',
93
- subject: 'A chat has been shared with you',
91
+ "chat.shared": {
92
+ kind: "chat.shared",
93
+ subject: "A chat has been shared with you",
94
94
  plainTextRequired: true,
95
95
  htmlSupported: true,
96
96
  },
97
- } as const satisfies Record<EmailKind, EmailKindDefinition>
97
+ } as const satisfies Record<EmailKind, EmailKindDefinition>;
98
98
 
99
99
  // =============================================================================
100
100
  // Registry Helpers
@@ -105,7 +105,7 @@ export const EMAIL_KINDS = {
105
105
  * Returns the definition for a given email kind.
106
106
  */
107
107
  export function getEmailKindDefinition(kind: EmailKind): EmailKindDefinition {
108
- return EMAIL_KINDS[kind]
108
+ return EMAIL_KINDS[kind];
109
109
  }
110
110
 
111
111
  /**
@@ -113,5 +113,5 @@ export function getEmailKindDefinition(kind: EmailKind): EmailKindDefinition {
113
113
  * Use at API boundaries to reject unknown kinds.
114
114
  */
115
115
  export function isValidEmailKind(kind: string): kind is EmailKind {
116
- return kind in EMAIL_KINDS
116
+ return kind in EMAIL_KINDS;
117
117
  }
@@ -24,12 +24,12 @@
24
24
  * 3. EmailPayloads interface (if kind has payload)
25
25
  */
26
26
  export type EmailKind =
27
- | 'auth.otp'
28
- | 'auth.magic_link' // future
29
- | 'org.invite' // future
30
- | 'org.leadership_granted'
31
- | 'security.alert' // future
32
- | 'chat.shared'
27
+ | "auth.otp"
28
+ | "auth.magic_link" // future
29
+ | "org.invite" // future
30
+ | "org.leadership_granted"
31
+ | "security.alert" // future
32
+ | "chat.shared";
33
33
 
34
34
  // =============================================================================
35
35
  // Email Payloads
@@ -42,57 +42,57 @@ export type EmailKind =
42
42
  * Kinds without entries here have empty payloads.
43
43
  */
44
44
  export interface EmailPayloads {
45
- 'auth.otp': {
45
+ "auth.otp": {
46
46
  /** The 6-digit OTP code */
47
- otp: string
47
+ otp: string;
48
48
  /** How long until the code expires */
49
- expiresInMinutes: number
49
+ expiresInMinutes: number;
50
50
  /** IP address of the request (for security context) */
51
- requestIp?: string
51
+ requestIp?: string;
52
52
  /** User agent of the request (for security context) */
53
- userAgent?: string
54
- }
55
- 'org.invite': {
56
- inviterName: string
57
- orgName: string
58
- role: 'admin' | 'member'
59
- acceptUrl: string
60
- expiresInDays: number
61
- }
62
- 'org.leadership_granted': {
53
+ userAgent?: string;
54
+ };
55
+ "org.invite": {
56
+ inviterName: string;
57
+ orgName: string;
58
+ role: "admin" | "member";
59
+ acceptUrl: string;
60
+ expiresInDays: number;
61
+ };
62
+ "org.leadership_granted": {
63
63
  /** Display name of the person who granted access */
64
- granterName: string
64
+ granterName: string;
65
65
  /** Display name of the recipient (optional; falls back to a neutral greeting) */
66
- recipientName?: string
66
+ recipientName?: string;
67
67
  /** Name of the org unit / team the access applies to */
68
- unitName: string
68
+ unitName: string;
69
69
  /** Human-readable role label the recipient was given */
70
- roleLabel: 'Leader' | 'Delegate'
70
+ roleLabel: "Leader" | "Delegate";
71
71
  /** Full URL to view the org unit in the app */
72
- ctaUrl: string
72
+ ctaUrl: string;
73
73
  /** Optional message from the granter, shown in the email and recorded with the grant */
74
- message?: string
75
- }
76
- 'security.alert': {
74
+ message?: string;
75
+ };
76
+ "security.alert": {
77
77
  /** Type of security alert */
78
- alertType: 'excessive_otp_requests' | 'unusual_login_location'
78
+ alertType: "excessive_otp_requests" | "unusual_login_location";
79
79
  /** Human-readable details about the alert */
80
- details: string
80
+ details: string;
81
81
  /** ISO timestamp of when the alert was triggered */
82
- timestamp: string
83
- }
84
- 'chat.shared': {
82
+ timestamp: string;
83
+ };
84
+ "chat.shared": {
85
85
  /** Display name of the person who shared */
86
- sharedByName: string
86
+ sharedByName: string;
87
87
  /** Title of the chat being shared */
88
- chatTitle: string
88
+ chatTitle: string;
89
89
  /** Full URL to view the shared chat */
90
- shareUrl: string
90
+ shareUrl: string;
91
91
  /** Share visibility mode */
92
- visibility: 'private' | 'public'
92
+ visibility: "private" | "public";
93
93
  /** First ~200 chars of chat content for email preview. Generated server-side from snapshot. */
94
- previewText?: string
95
- }
94
+ previewText?: string;
95
+ };
96
96
  }
97
97
 
98
98
  // =============================================================================
@@ -107,11 +107,11 @@ export interface EmailPayloads {
107
107
  */
108
108
  export interface SendEmailInput<K extends EmailKind> {
109
109
  /** The type of email to send */
110
- kind: K
110
+ kind: K;
111
111
  /** Recipient email address (will be normalized) */
112
- to: string
112
+ to: string;
113
113
  /** Type-safe payload for this email kind */
114
- payload: K extends keyof EmailPayloads ? EmailPayloads[K] : never
114
+ payload: K extends keyof EmailPayloads ? EmailPayloads[K] : never;
115
115
  /** Idempotency key to prevent duplicate sends */
116
- idempotencyKey: string
116
+ idempotencyKey: string;
117
117
  }
@@ -10,28 +10,28 @@
10
10
  /** Field-level validation detail for VALIDATION_ERROR responses. */
11
11
  export interface ValidationDetail {
12
12
  /** Field path (e.g. 'email', 'settings.name'). */
13
- field: string
13
+ field: string;
14
14
  /** Human-readable error message for this field. */
15
- message: string
15
+ message: string;
16
16
  /** Machine-readable sub-code (e.g. 'too_short', 'invalid_format') for frontend logic. */
17
- code?: string
17
+ code?: string;
18
18
  }
19
19
 
20
20
  /** Canonical error response shape returned by all API surfaces. */
21
21
  export interface ErrorResponse {
22
22
  /** Domain-specific error code (e.g. INVITE_EXPIRED, SSO_REQUIRED). */
23
- error: string
23
+ error: string;
24
24
  /** HTTP status code — lives in payload for transport portability across HTTP, streaming, agents, execution system. */
25
- status: number
25
+ status: number;
26
26
  /** Optional human-readable message — safe default applied by handler when absent. */
27
- message?: string
27
+ message?: string;
28
28
  /** Validation field-level details — only present for VALIDATION_ERROR. */
29
- details?: ValidationDetail[]
29
+ details?: ValidationDetail[];
30
30
  /**
31
31
  * Structured extension fields (e.g. ssoUrl, orgId).
32
32
  *
33
33
  * Conventions: camelCase keys, domain-scoped, shallow values,
34
34
  * serializable, stable keys, no duplication of top-level fields.
35
35
  */
36
- meta?: Record<string, unknown>
36
+ meta?: Record<string, unknown>;
37
37
  }
@@ -1,55 +1,55 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { EXECUTION_EVENT_TYPES, isExecutionEventType } from '../events.js'
1
+ import { describe, it, expect } from "vitest";
2
+ import { EXECUTION_EVENT_TYPES, isExecutionEventType } from "../events.js";
3
3
 
4
- describe('EXECUTION_EVENT_TYPES', () => {
5
- it('contains exactly 10 entries', () => {
6
- expect(EXECUTION_EVENT_TYPES).toHaveLength(10)
7
- })
4
+ describe("EXECUTION_EVENT_TYPES", () => {
5
+ it("contains exactly 10 entries", () => {
6
+ expect(EXECUTION_EVENT_TYPES).toHaveLength(10);
7
+ });
8
8
 
9
- it('contains all expected event types', () => {
9
+ it("contains all expected event types", () => {
10
10
  const expected = [
11
- 'execution_created',
12
- 'execution_started',
13
- 'confirmation_approved',
14
- 'confirmation_rejected',
15
- 'confirmation_expired',
16
- 'approval_requested',
17
- 'execution_completed',
18
- 'execution_failed',
19
- 'undo_completed',
20
- 'cancelled',
21
- ]
11
+ "execution_created",
12
+ "execution_started",
13
+ "confirmation_approved",
14
+ "confirmation_rejected",
15
+ "confirmation_expired",
16
+ "approval_requested",
17
+ "execution_completed",
18
+ "execution_failed",
19
+ "undo_completed",
20
+ "cancelled",
21
+ ];
22
22
  for (const evt of expected) {
23
- expect(EXECUTION_EVENT_TYPES).toContain(evt)
23
+ expect(EXECUTION_EVENT_TYPES).toContain(evt);
24
24
  }
25
- })
25
+ });
26
26
 
27
- it('has no duplicates', () => {
28
- const unique = new Set(EXECUTION_EVENT_TYPES)
29
- expect(unique.size).toBe(EXECUTION_EVENT_TYPES.length)
30
- })
31
- })
27
+ it("has no duplicates", () => {
28
+ const unique = new Set(EXECUTION_EVENT_TYPES);
29
+ expect(unique.size).toBe(EXECUTION_EVENT_TYPES.length);
30
+ });
31
+ });
32
32
 
33
- describe('isExecutionEventType', () => {
34
- it('returns true for every member of EXECUTION_EVENT_TYPES', () => {
33
+ describe("isExecutionEventType", () => {
34
+ it("returns true for every member of EXECUTION_EVENT_TYPES", () => {
35
35
  for (const evt of EXECUTION_EVENT_TYPES) {
36
- expect(isExecutionEventType(evt)).toBe(true)
36
+ expect(isExecutionEventType(evt)).toBe(true);
37
37
  }
38
- })
38
+ });
39
39
 
40
- it('returns false for empty string', () => {
41
- expect(isExecutionEventType('')).toBe(false)
42
- })
40
+ it("returns false for empty string", () => {
41
+ expect(isExecutionEventType("")).toBe(false);
42
+ });
43
43
 
44
- it('returns false for unknown string', () => {
45
- expect(isExecutionEventType('not_an_event')).toBe(false)
46
- })
44
+ it("returns false for unknown string", () => {
45
+ expect(isExecutionEventType("not_an_event")).toBe(false);
46
+ });
47
47
 
48
- it('returns false for partial match', () => {
49
- expect(isExecutionEventType('execution')).toBe(false)
50
- })
48
+ it("returns false for partial match", () => {
49
+ expect(isExecutionEventType("execution")).toBe(false);
50
+ });
51
51
 
52
- it('returns false for similar but wrong string', () => {
53
- expect(isExecutionEventType('execution_create')).toBe(false)
54
- })
55
- })
52
+ it("returns false for similar but wrong string", () => {
53
+ expect(isExecutionEventType("execution_create")).toBe(false);
54
+ });
55
+ });