@kelpie/schemas 0.1.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 (158) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +33 -0
  3. package/dist/account.d.ts +79 -0
  4. package/dist/account.d.ts.map +1 -0
  5. package/dist/account.js +66 -0
  6. package/dist/account.js.map +1 -0
  7. package/dist/activity.d.ts +23 -0
  8. package/dist/activity.d.ts.map +1 -0
  9. package/dist/activity.js +27 -0
  10. package/dist/activity.js.map +1 -0
  11. package/dist/agentTask.d.ts +97 -0
  12. package/dist/agentTask.d.ts.map +1 -0
  13. package/dist/agentTask.js +127 -0
  14. package/dist/agentTask.js.map +1 -0
  15. package/dist/candidate.d.ts +39 -0
  16. package/dist/candidate.d.ts.map +1 -0
  17. package/dist/candidate.js +40 -0
  18. package/dist/candidate.js.map +1 -0
  19. package/dist/company.d.ts +38 -0
  20. package/dist/company.d.ts.map +1 -0
  21. package/dist/company.js +57 -0
  22. package/dist/company.js.map +1 -0
  23. package/dist/dashboard.d.ts +112 -0
  24. package/dist/dashboard.d.ts.map +1 -0
  25. package/dist/dashboard.js +165 -0
  26. package/dist/dashboard.js.map +1 -0
  27. package/dist/deal.d.ts +40 -0
  28. package/dist/deal.d.ts.map +1 -0
  29. package/dist/deal.js +59 -0
  30. package/dist/deal.js.map +1 -0
  31. package/dist/decision.d.ts +46 -0
  32. package/dist/decision.d.ts.map +1 -0
  33. package/dist/decision.js +48 -0
  34. package/dist/decision.js.map +1 -0
  35. package/dist/form.d.ts +97 -0
  36. package/dist/form.d.ts.map +1 -0
  37. package/dist/form.js +98 -0
  38. package/dist/form.js.map +1 -0
  39. package/dist/formSubmission.d.ts +47 -0
  40. package/dist/formSubmission.d.ts.map +1 -0
  41. package/dist/formSubmission.js +47 -0
  42. package/dist/formSubmission.js.map +1 -0
  43. package/dist/handbookPage.d.ts +52 -0
  44. package/dist/handbookPage.d.ts.map +1 -0
  45. package/dist/handbookPage.js +56 -0
  46. package/dist/handbookPage.js.map +1 -0
  47. package/dist/importExport.d.ts +143 -0
  48. package/dist/importExport.d.ts.map +1 -0
  49. package/dist/importExport.js +239 -0
  50. package/dist/importExport.js.map +1 -0
  51. package/dist/index.d.ts +75 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +45 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/invite.d.ts +35 -0
  56. package/dist/invite.d.ts.map +1 -0
  57. package/dist/invite.js +31 -0
  58. package/dist/invite.js.map +1 -0
  59. package/dist/mcpTool.d.ts +26 -0
  60. package/dist/mcpTool.d.ts.map +1 -0
  61. package/dist/mcpTool.js +13 -0
  62. package/dist/mcpTool.js.map +1 -0
  63. package/dist/member.d.ts +26 -0
  64. package/dist/member.d.ts.map +1 -0
  65. package/dist/member.js +24 -0
  66. package/dist/member.js.map +1 -0
  67. package/dist/moduleSetting.d.ts +19 -0
  68. package/dist/moduleSetting.d.ts.map +1 -0
  69. package/dist/moduleSetting.js +16 -0
  70. package/dist/moduleSetting.js.map +1 -0
  71. package/dist/note.d.ts +34 -0
  72. package/dist/note.d.ts.map +1 -0
  73. package/dist/note.js +35 -0
  74. package/dist/note.js.map +1 -0
  75. package/dist/opportunity.d.ts +30 -0
  76. package/dist/opportunity.d.ts.map +1 -0
  77. package/dist/opportunity.js +41 -0
  78. package/dist/opportunity.js.map +1 -0
  79. package/dist/partnership.d.ts +36 -0
  80. package/dist/partnership.d.ts.map +1 -0
  81. package/dist/partnership.js +50 -0
  82. package/dist/partnership.js.map +1 -0
  83. package/dist/person.d.ts +45 -0
  84. package/dist/person.d.ts.map +1 -0
  85. package/dist/person.js +60 -0
  86. package/dist/person.js.map +1 -0
  87. package/dist/pipelineStage.d.ts +29 -0
  88. package/dist/pipelineStage.d.ts.map +1 -0
  89. package/dist/pipelineStage.js +38 -0
  90. package/dist/pipelineStage.js.map +1 -0
  91. package/dist/planItem.d.ts +43 -0
  92. package/dist/planItem.d.ts.map +1 -0
  93. package/dist/planItem.js +44 -0
  94. package/dist/planItem.js.map +1 -0
  95. package/dist/position.d.ts +22 -0
  96. package/dist/position.d.ts.map +1 -0
  97. package/dist/position.js +25 -0
  98. package/dist/position.js.map +1 -0
  99. package/dist/raise.d.ts +38 -0
  100. package/dist/raise.d.ts.map +1 -0
  101. package/dist/raise.js +53 -0
  102. package/dist/raise.js.map +1 -0
  103. package/dist/role.d.ts +21 -0
  104. package/dist/role.d.ts.map +1 -0
  105. package/dist/role.js +21 -0
  106. package/dist/role.js.map +1 -0
  107. package/dist/search.d.ts +41 -0
  108. package/dist/search.d.ts.map +1 -0
  109. package/dist/search.js +41 -0
  110. package/dist/search.js.map +1 -0
  111. package/dist/session.d.ts +92 -0
  112. package/dist/session.d.ts.map +1 -0
  113. package/dist/session.js +67 -0
  114. package/dist/session.js.map +1 -0
  115. package/dist/values.d.ts +216 -0
  116. package/dist/values.d.ts.map +1 -0
  117. package/dist/values.js +329 -0
  118. package/dist/values.js.map +1 -0
  119. package/dist/webhook.d.ts +85 -0
  120. package/dist/webhook.d.ts.map +1 -0
  121. package/dist/webhook.js +75 -0
  122. package/dist/webhook.js.map +1 -0
  123. package/dist/wire.d.ts +40 -0
  124. package/dist/wire.d.ts.map +1 -0
  125. package/dist/wire.js +40 -0
  126. package/dist/wire.js.map +1 -0
  127. package/package.json +51 -0
  128. package/src/account.ts +149 -0
  129. package/src/activity.ts +52 -0
  130. package/src/agentTask.ts +254 -0
  131. package/src/candidate.ts +83 -0
  132. package/src/company.ts +98 -0
  133. package/src/dashboard.ts +307 -0
  134. package/src/deal.ts +102 -0
  135. package/src/decision.ts +106 -0
  136. package/src/form.ts +217 -0
  137. package/src/formSubmission.ts +99 -0
  138. package/src/handbookPage.ts +99 -0
  139. package/src/importExport.ts +339 -0
  140. package/src/index.ts +324 -0
  141. package/src/invite.ts +69 -0
  142. package/src/mcpTool.ts +32 -0
  143. package/src/member.ts +53 -0
  144. package/src/moduleSetting.ts +37 -0
  145. package/src/note.ts +73 -0
  146. package/src/opportunity.ts +74 -0
  147. package/src/partnership.ts +89 -0
  148. package/src/person.ts +116 -0
  149. package/src/pipelineStage.ts +71 -0
  150. package/src/planItem.ts +91 -0
  151. package/src/position.ts +50 -0
  152. package/src/raise.ts +94 -0
  153. package/src/role.ts +45 -0
  154. package/src/search.ts +86 -0
  155. package/src/session.ts +177 -0
  156. package/src/values.ts +427 -0
  157. package/src/webhook.ts +164 -0
  158. package/src/wire.ts +51 -0
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@kelpie/schemas",
3
+ "version": "0.1.0",
4
+ "description": "Wire contract for the Kelpie API, as Zod schemas. Shared by the service, the UI, and any agent decoding /v1 responses.",
5
+ "keywords": [
6
+ "kelpie",
7
+ "crm",
8
+ "zod",
9
+ "schema",
10
+ "api"
11
+ ],
12
+ "license": "AGPL-3.0-only",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/velvet-tiger/kelpie.git",
16
+ "directory": "packages/schemas"
17
+ },
18
+ "homepage": "https://github.com/velvet-tiger/kelpie/tree/main/packages/schemas#readme",
19
+ "bugs": "https://github.com/velvet-tiger/kelpie/issues",
20
+ "type": "module",
21
+ "engines": {
22
+ "node": ">=24"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "src",
30
+ "!src/**/*.test.ts"
31
+ ],
32
+ "exports": {
33
+ ".": {
34
+ "kelpie-source": "./src/index.ts",
35
+ "types": "./dist/index.d.ts",
36
+ "default": "./dist/index.js"
37
+ }
38
+ },
39
+ "scripts": {
40
+ "build": "tsc --project tsconfig.build.json",
41
+ "typecheck": "tsc --project tsconfig.json",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest"
44
+ },
45
+ "dependencies": {
46
+ "zod": "^4.4.3"
47
+ },
48
+ "devDependencies": {
49
+ "vitest": "^4.1.10"
50
+ }
51
+ }
package/src/account.ts ADDED
@@ -0,0 +1,149 @@
1
+ import { z } from 'zod'
2
+
3
+ import { THEME_PREFERENCES } from './values.ts'
4
+ import type { ThemePreference } from './values.ts'
5
+ import { definedFields, idSchema, timestampSchema } from './wire.ts'
6
+
7
+ /**
8
+ * Wire shapes for `/v1/account`: the signed-in human, rather than the workspace
9
+ * they are looking at or the session they arrived on.
10
+ *
11
+ * An account is global. Changing a name here changes it in every workspace the
12
+ * person belongs to, which is why this is not part of the member resource.
13
+ */
14
+
15
+ export interface Account {
16
+ readonly id: string
17
+ readonly email: string
18
+ readonly name: string
19
+ }
20
+
21
+ export const accountSchema: z.ZodType<Account, unknown> = z.object({
22
+ id: idSchema,
23
+ email: z.string(),
24
+ name: z.string(),
25
+ })
26
+
27
+ export interface UpdateAccountInput {
28
+ readonly name?: string
29
+ readonly email?: string
30
+ /** Required by the service whenever `email` is present. */
31
+ readonly currentPassword?: string
32
+ }
33
+
34
+ export function updateAccountBody(input: UpdateAccountInput): Record<string, unknown> {
35
+ return definedFields({
36
+ name: input.name,
37
+ email: input.email,
38
+ current_password: input.currentPassword,
39
+ })
40
+ }
41
+
42
+ /**
43
+ * One of the caller's sessions, as the Security page lists them.
44
+ *
45
+ * Distinct from `Session` in `session.ts`, which is the single identity behind
46
+ * `GET /v1/auth/me`. This is a row in "where am I signed in", and `current`
47
+ * marks the one asking.
48
+ *
49
+ * `device` and `location` are nullable because the service records what the
50
+ * request actually carried. A `User-Agent` is usually present; nothing derives
51
+ * a location today, so it is always `null`.
52
+ */
53
+ export interface AccountSession {
54
+ readonly id: string
55
+ readonly device: string | null
56
+ readonly location: string | null
57
+ readonly lastActiveAt: Date
58
+ readonly current: boolean
59
+ }
60
+
61
+ export const accountSessionSchema: z.ZodType<AccountSession, unknown> = z
62
+ .object({
63
+ id: idSchema,
64
+ device: z.string().nullable(),
65
+ location: z.string().nullable(),
66
+ last_active_at: timestampSchema,
67
+ current: z.boolean(),
68
+ })
69
+ .transform(
70
+ (wire): AccountSession => ({
71
+ id: wire.id,
72
+ device: wire.device,
73
+ location: wire.location,
74
+ lastActiveAt: wire.last_active_at,
75
+ current: wire.current,
76
+ }),
77
+ )
78
+
79
+ /**
80
+ * The shortest password the service will hash.
81
+ *
82
+ * Part of the contract rather than a server detail: signup, reset, and the
83
+ * change-password form all have to state the rule before a caller submits, and
84
+ * a second copy in the browser is a copy that can drift below the real one.
85
+ */
86
+ export const MINIMUM_PASSWORD_LENGTH = 12
87
+
88
+ export interface ChangePasswordInput {
89
+ readonly currentPassword: string
90
+ readonly newPassword: string
91
+ }
92
+
93
+ export function changePasswordBody(input: ChangePasswordInput): Record<string, unknown> {
94
+ return { current_password: input.currentPassword, new_password: input.newPassword }
95
+ }
96
+
97
+ /**
98
+ * Per-account settings that follow the person between browsers.
99
+ *
100
+ * The three notification fields are stored choices, not switches over a running
101
+ * mailer: Kelpie sends no digest, mention, or product email yet. They record
102
+ * what to do when it does, and the Preferences page says so on screen rather
103
+ * than leaving a reader to infer a capability from a toggle.
104
+ */
105
+ export interface AccountPreferences {
106
+ readonly timezone: string
107
+ readonly theme: ThemePreference
108
+ readonly emailDigest: boolean
109
+ readonly mentionEmails: boolean
110
+ readonly productUpdates: boolean
111
+ }
112
+
113
+ export const accountPreferencesSchema: z.ZodType<AccountPreferences, unknown> = z
114
+ .object({
115
+ timezone: z.string(),
116
+ theme: z.enum(THEME_PREFERENCES),
117
+ email_digest: z.boolean(),
118
+ mention_emails: z.boolean(),
119
+ product_updates: z.boolean(),
120
+ })
121
+ .transform(
122
+ (wire): AccountPreferences => ({
123
+ timezone: wire.timezone,
124
+ theme: wire.theme,
125
+ emailDigest: wire.email_digest,
126
+ mentionEmails: wire.mention_emails,
127
+ productUpdates: wire.product_updates,
128
+ }),
129
+ )
130
+
131
+ export interface UpdateAccountPreferencesInput {
132
+ readonly timezone?: string
133
+ readonly theme?: ThemePreference
134
+ readonly emailDigest?: boolean
135
+ readonly mentionEmails?: boolean
136
+ readonly productUpdates?: boolean
137
+ }
138
+
139
+ export function updateAccountPreferencesBody(
140
+ input: UpdateAccountPreferencesInput,
141
+ ): Record<string, unknown> {
142
+ return definedFields({
143
+ timezone: input.timezone,
144
+ theme: input.theme,
145
+ email_digest: input.emailDigest,
146
+ mention_emails: input.mentionEmails,
147
+ product_updates: input.productUpdates,
148
+ })
149
+ }
@@ -0,0 +1,52 @@
1
+ import { z } from 'zod'
2
+
3
+ import { ACTIVITY_KINDS, RECORD_TARGET_TYPES } from './values.ts'
4
+ import type { ActivityKind, RecordTargetType } from './values.ts'
5
+ import { idSchema, timestampSchema } from './wire.ts'
6
+
7
+ /**
8
+ * Wire shape for `/v1/activities`. Read-only, so there is no body builder here.
9
+ *
10
+ * No `updatedAt`: the table is append-only and carries no such column.
11
+ *
12
+ * `actorLabel` is the display name to use when `actorMemberId` is null, e.g.
13
+ * "Form", "Gmail", "API key". Exactly one of the two is set.
14
+ */
15
+
16
+ export interface Activity {
17
+ readonly id: string
18
+ readonly targetType: RecordTargetType
19
+ readonly targetId: string
20
+ readonly kind: ActivityKind
21
+ readonly actorMemberId: string | null
22
+ readonly actorLabel: string | null
23
+ readonly action: string
24
+ readonly detail: string | null
25
+ readonly createdAt: Date
26
+ }
27
+
28
+ export const activitySchema: z.ZodType<Activity, unknown> = z
29
+ .object({
30
+ id: idSchema,
31
+ target_type: z.enum(RECORD_TARGET_TYPES),
32
+ target_id: idSchema,
33
+ kind: z.enum(ACTIVITY_KINDS),
34
+ actor_member_id: idSchema.nullable(),
35
+ actor_label: z.string().nullable(),
36
+ action: z.string(),
37
+ detail: z.string().nullable(),
38
+ created_at: timestampSchema,
39
+ })
40
+ .transform(
41
+ (wire): Activity => ({
42
+ id: wire.id,
43
+ targetType: wire.target_type,
44
+ targetId: wire.target_id,
45
+ kind: wire.kind,
46
+ actorMemberId: wire.actor_member_id,
47
+ actorLabel: wire.actor_label,
48
+ action: wire.action,
49
+ detail: wire.detail,
50
+ createdAt: wire.created_at,
51
+ }),
52
+ )
@@ -0,0 +1,254 @@
1
+ import { z } from 'zod'
2
+
3
+ import {
4
+ AGENT_RUN_STATUSES,
5
+ AGENT_TASK_PLACEMENTS,
6
+ AGENT_TASK_TARGET_TYPES,
7
+ } from './values.ts'
8
+ import type { AgentRunStatus, AgentTaskPlacement, AgentTaskTargetType } from './values.ts'
9
+ import {
10
+ definedFields,
11
+ idSchema,
12
+ nullableTimestampSchema,
13
+ recordTimestamps,
14
+ } from './wire.ts'
15
+ import type { RecordTimestamps } from './wire.ts'
16
+
17
+ /**
18
+ * Agent tasks (`agent-tasks.md`): prompt recipes over the workspace's own data.
19
+ *
20
+ * The task catalog ships in code, so a TaskDefinition has no timestamps and no
21
+ * body builder — it is read-only on the wire. A ResolvedTask is the one payload
22
+ * both triggers share: Copy puts `prompt` on the clipboard, Run POSTs the whole
23
+ * thing to a registered agent.
24
+ */
25
+
26
+ /** A catalog entry. `id` is a stable string like `company.enrich`, not a ULID. */
27
+ export interface AgentTaskDefinition {
28
+ readonly id: string
29
+ readonly label: string
30
+ readonly description: string
31
+ readonly targetTypes: readonly AgentTaskTargetType[]
32
+ readonly placement: AgentTaskPlacement
33
+ readonly handbookSlugs: readonly string[]
34
+ readonly instructions: string
35
+ readonly writePolicy: string
36
+ }
37
+
38
+ export const agentTaskDefinitionSchema: z.ZodType<AgentTaskDefinition, unknown> = z
39
+ .object({
40
+ id: idSchema,
41
+ label: z.string(),
42
+ description: z.string(),
43
+ target_types: z.array(z.enum(AGENT_TASK_TARGET_TYPES)),
44
+ placement: z.enum(AGENT_TASK_PLACEMENTS),
45
+ handbook_slugs: z.array(z.string()),
46
+ instructions: z.string(),
47
+ write_policy: z.string(),
48
+ })
49
+ .transform(
50
+ (wire): AgentTaskDefinition => ({
51
+ id: wire.id,
52
+ label: wire.label,
53
+ description: wire.description,
54
+ targetTypes: wire.target_types,
55
+ placement: wire.placement,
56
+ handbookSlugs: wire.handbook_slugs,
57
+ instructions: wire.instructions,
58
+ writePolicy: wire.write_policy,
59
+ }),
60
+ )
61
+
62
+ /**
63
+ * The structured snapshot resolve assembled. `related` keys are id-list names
64
+ * such as `person_ids`; which keys appear depends on the target type.
65
+ */
66
+ export interface AgentTaskContextPack {
67
+ readonly targetLabel: string
68
+ readonly deepLink: string
69
+ readonly handbookSlugs: readonly string[]
70
+ readonly pinnedNoteIds: readonly string[]
71
+ readonly openPlanIds: readonly string[]
72
+ readonly openDecisionIds: readonly string[]
73
+ readonly related: Readonly<Record<string, readonly string[]>>
74
+ }
75
+
76
+ const contextPackSchema: z.ZodType<AgentTaskContextPack, unknown> = z
77
+ .object({
78
+ target_label: z.string(),
79
+ deep_link: z.string(),
80
+ handbook_slugs: z.array(z.string()),
81
+ pinned_note_ids: z.array(idSchema),
82
+ open_plan_ids: z.array(idSchema),
83
+ open_decision_ids: z.array(idSchema),
84
+ related: z.record(z.string(), z.array(idSchema)),
85
+ })
86
+ .transform(
87
+ (wire): AgentTaskContextPack => ({
88
+ targetLabel: wire.target_label,
89
+ deepLink: wire.deep_link,
90
+ handbookSlugs: wire.handbook_slugs,
91
+ pinnedNoteIds: wire.pinned_note_ids,
92
+ openPlanIds: wire.open_plan_ids,
93
+ openDecisionIds: wire.open_decision_ids,
94
+ related: wire.related,
95
+ }),
96
+ )
97
+
98
+ /** What resolve answers, and what a run dispatches. Identical for copy and run. */
99
+ export interface ResolvedAgentTask {
100
+ readonly taskId: string
101
+ readonly targetType: AgentTaskTargetType
102
+ readonly targetId: string
103
+ readonly prompt: string
104
+ readonly context: AgentTaskContextPack
105
+ }
106
+
107
+ export const resolvedAgentTaskSchema: z.ZodType<ResolvedAgentTask, unknown> = z
108
+ .object({
109
+ task_id: idSchema,
110
+ target_type: z.enum(AGENT_TASK_TARGET_TYPES),
111
+ target_id: idSchema,
112
+ prompt: z.string(),
113
+ context: contextPackSchema,
114
+ })
115
+ .transform(
116
+ (wire): ResolvedAgentTask => ({
117
+ taskId: wire.task_id,
118
+ targetType: wire.target_type,
119
+ targetId: wire.target_id,
120
+ prompt: wire.prompt,
121
+ context: wire.context,
122
+ }),
123
+ )
124
+
125
+ export interface ResolveAgentTaskInput {
126
+ readonly targetType: AgentTaskTargetType
127
+ readonly targetId: string
128
+ }
129
+
130
+ export function resolveAgentTaskBody(input: ResolveAgentTaskInput): Record<string, unknown> {
131
+ return {
132
+ target_type: input.targetType,
133
+ target_id: input.targetId,
134
+ }
135
+ }
136
+
137
+ export interface RunAgentTaskInput extends ResolveAgentTaskInput {
138
+ readonly agentId: string
139
+ }
140
+
141
+ export function runAgentTaskBody(input: RunAgentTaskInput): Record<string, unknown> {
142
+ return {
143
+ ...resolveAgentTaskBody(input),
144
+ agent_id: input.agentId,
145
+ }
146
+ }
147
+
148
+ /**
149
+ * A bring-your-own agent endpoint. The auth header is sealed server-side and
150
+ * never returned; `hasAuthHeader` is all a reader learns about it.
151
+ */
152
+ export interface RegisteredAgent extends RecordTimestamps {
153
+ readonly id: string
154
+ readonly name: string
155
+ readonly endpoint: string
156
+ readonly hasAuthHeader: boolean
157
+ readonly lastRunAt: Date | null
158
+ }
159
+
160
+ export const registeredAgentSchema: z.ZodType<RegisteredAgent, unknown> = z
161
+ .object({
162
+ id: idSchema,
163
+ name: z.string(),
164
+ endpoint: z.string(),
165
+ has_auth_header: z.boolean(),
166
+ last_run_at: nullableTimestampSchema,
167
+ ...recordTimestamps,
168
+ })
169
+ .transform(
170
+ (wire): RegisteredAgent => ({
171
+ id: wire.id,
172
+ name: wire.name,
173
+ endpoint: wire.endpoint,
174
+ hasAuthHeader: wire.has_auth_header,
175
+ lastRunAt: wire.last_run_at,
176
+ createdAt: wire.created_at,
177
+ updatedAt: wire.updated_at,
178
+ }),
179
+ )
180
+
181
+ export interface CreateRegisteredAgentInput {
182
+ readonly name: string
183
+ readonly endpoint: string
184
+ /** Sent as the `Authorization` header on every dispatch to this agent. */
185
+ readonly authHeader?: string
186
+ }
187
+
188
+ export function createRegisteredAgentBody(
189
+ input: CreateRegisteredAgentInput,
190
+ ): Record<string, unknown> {
191
+ return definedFields({
192
+ name: input.name,
193
+ endpoint: input.endpoint,
194
+ auth_header: input.authHeader,
195
+ })
196
+ }
197
+
198
+ export interface RegisteredAgentInput {
199
+ readonly name?: string
200
+ readonly endpoint?: string
201
+ /** A string replaces the stored header; `null` clears it. */
202
+ readonly authHeader?: string | null
203
+ }
204
+
205
+ export function registeredAgentBody(input: RegisteredAgentInput): Record<string, unknown> {
206
+ return definedFields({
207
+ name: input.name,
208
+ endpoint: input.endpoint,
209
+ auth_header: input.authHeader,
210
+ })
211
+ }
212
+
213
+ /**
214
+ * One dispatch of a ResolvedTask to a registered agent. `failureReason` is set
215
+ * only on `failed`, and says what the dispatch hit — a status, a timeout, a
216
+ * refused connection.
217
+ */
218
+ export interface AgentRun extends RecordTimestamps {
219
+ readonly id: string
220
+ readonly agentId: string
221
+ readonly taskId: string
222
+ readonly targetType: AgentTaskTargetType
223
+ readonly targetId: string
224
+ readonly status: AgentRunStatus
225
+ readonly prompt: string
226
+ readonly failureReason: string | null
227
+ }
228
+
229
+ export const agentRunSchema: z.ZodType<AgentRun, unknown> = z
230
+ .object({
231
+ id: idSchema,
232
+ agent_id: idSchema,
233
+ task_id: idSchema,
234
+ target_type: z.enum(AGENT_TASK_TARGET_TYPES),
235
+ target_id: idSchema,
236
+ status: z.enum(AGENT_RUN_STATUSES),
237
+ prompt: z.string(),
238
+ failure_reason: z.string().nullable(),
239
+ ...recordTimestamps,
240
+ })
241
+ .transform(
242
+ (wire): AgentRun => ({
243
+ id: wire.id,
244
+ agentId: wire.agent_id,
245
+ taskId: wire.task_id,
246
+ targetType: wire.target_type,
247
+ targetId: wire.target_id,
248
+ status: wire.status,
249
+ prompt: wire.prompt,
250
+ failureReason: wire.failure_reason,
251
+ createdAt: wire.created_at,
252
+ updatedAt: wire.updated_at,
253
+ }),
254
+ )
@@ -0,0 +1,83 @@
1
+ import { z } from 'zod'
2
+
3
+ import { CANDIDATE_STATUSES, INTERVIEW_STAGES } from './values.ts'
4
+ import type { CandidateStatus, InterviewStage } from './values.ts'
5
+ import { definedFields, idSchema, recordTimestamps } from './wire.ts'
6
+ import type { RecordTimestamps } from './wire.ts'
7
+
8
+ /**
9
+ * Wire and write shapes for `/v1/candidates`, the Person↔Role link.
10
+ *
11
+ * Hiring pipeline state sits here rather than on Person, so the same person can
12
+ * be interviewing for one role and in the nurture pile for another. Interview
13
+ * notes attach to the candidate, not to the person or the role.
14
+ *
15
+ * Neither end is updatable: moving a candidacy to a different role or person is
16
+ * a delete and a create, the rule Position already follows.
17
+ */
18
+
19
+ export interface Candidate extends RecordTimestamps {
20
+ readonly id: string
21
+ readonly roleId: string
22
+ readonly personId: string
23
+ readonly status: CandidateStatus
24
+ /** Null unless the candidate is in process. The API keeps that true. */
25
+ readonly interviewStage: InterviewStage | null
26
+ readonly referrerPersonId: string | null
27
+ }
28
+
29
+ export const candidateSchema: z.ZodType<Candidate, unknown> = z
30
+ .object({
31
+ id: idSchema,
32
+ role_id: idSchema,
33
+ person_id: idSchema,
34
+ status: z.enum(CANDIDATE_STATUSES),
35
+ interview_stage: z.enum(INTERVIEW_STAGES).nullable(),
36
+ referrer_person_id: idSchema.nullable(),
37
+ ...recordTimestamps,
38
+ })
39
+ .transform(
40
+ (wire): Candidate => ({
41
+ id: wire.id,
42
+ roleId: wire.role_id,
43
+ personId: wire.person_id,
44
+ status: wire.status,
45
+ interviewStage: wire.interview_stage,
46
+ referrerPersonId: wire.referrer_person_id,
47
+ createdAt: wire.created_at,
48
+ updatedAt: wire.updated_at,
49
+ }),
50
+ )
51
+
52
+ export interface CreateCandidateInput {
53
+ readonly roleId: string
54
+ readonly personId: string
55
+ /** Absent means in process, where the mockup's "Add candidate" starts one. */
56
+ readonly status?: CandidateStatus
57
+ readonly interviewStage?: InterviewStage | null
58
+ readonly referrerPersonId?: string | null
59
+ }
60
+
61
+ export function createCandidateBody(input: CreateCandidateInput): Record<string, unknown> {
62
+ return definedFields({
63
+ role_id: input.roleId,
64
+ person_id: input.personId,
65
+ status: input.status,
66
+ interview_stage: input.interviewStage,
67
+ referrer_person_id: input.referrerPersonId,
68
+ })
69
+ }
70
+
71
+ export interface CandidateInput {
72
+ readonly status?: CandidateStatus
73
+ readonly interviewStage?: InterviewStage | null
74
+ readonly referrerPersonId?: string | null
75
+ }
76
+
77
+ export function candidateBody(input: CandidateInput): Record<string, unknown> {
78
+ return definedFields({
79
+ status: input.status,
80
+ interview_stage: input.interviewStage,
81
+ referrer_person_id: input.referrerPersonId,
82
+ })
83
+ }
package/src/company.ts ADDED
@@ -0,0 +1,98 @@
1
+ import { z } from 'zod'
2
+
3
+ import { ACCOUNT_TYPES, COMPANY_STAGES, ICP_FITS, SIZE_BANDS } from './values.ts'
4
+ import type { AccountType, CompanyStage, IcpFit, SizeBand } from './values.ts'
5
+ import { definedFields, idSchema, recordTimestamps } from './wire.ts'
6
+ import type { RecordTimestamps } from './wire.ts'
7
+
8
+ /** Wire and write shapes for `/v1/companies`. */
9
+
10
+ export interface Company extends RecordTimestamps {
11
+ readonly id: string
12
+ readonly name: string
13
+ readonly domain: string | null
14
+ readonly industry: string | null
15
+ readonly description: string
16
+ readonly stage: CompanyStage
17
+ readonly sizeBand: SizeBand
18
+ readonly hq: string | null
19
+ readonly website: string | null
20
+ readonly accountType: AccountType
21
+ readonly icpFit: IcpFit
22
+ readonly techStack: readonly string[]
23
+ readonly summary: string
24
+ readonly tags: readonly string[]
25
+ }
26
+
27
+ export const companySchema: z.ZodType<Company, unknown> = z
28
+ .object({
29
+ id: idSchema,
30
+ name: z.string(),
31
+ domain: z.string().nullable(),
32
+ industry: z.string().nullable(),
33
+ description: z.string(),
34
+ stage: z.enum(COMPANY_STAGES),
35
+ size_band: z.enum(SIZE_BANDS),
36
+ hq: z.string().nullable(),
37
+ website: z.string().nullable(),
38
+ account_type: z.enum(ACCOUNT_TYPES),
39
+ icp_fit: z.enum(ICP_FITS),
40
+ tech_stack: z.array(z.string()),
41
+ summary: z.string(),
42
+ tags: z.array(z.string()),
43
+ ...recordTimestamps,
44
+ })
45
+ .transform(
46
+ (wire): Company => ({
47
+ id: wire.id,
48
+ name: wire.name,
49
+ domain: wire.domain,
50
+ industry: wire.industry,
51
+ description: wire.description,
52
+ stage: wire.stage,
53
+ sizeBand: wire.size_band,
54
+ hq: wire.hq,
55
+ website: wire.website,
56
+ accountType: wire.account_type,
57
+ icpFit: wire.icp_fit,
58
+ techStack: wire.tech_stack,
59
+ summary: wire.summary,
60
+ tags: wire.tags,
61
+ createdAt: wire.created_at,
62
+ updatedAt: wire.updated_at,
63
+ }),
64
+ )
65
+
66
+ export interface CompanyInput {
67
+ readonly name?: string
68
+ readonly domain?: string | null
69
+ readonly industry?: string | null
70
+ readonly description?: string
71
+ readonly stage?: CompanyStage
72
+ readonly sizeBand?: SizeBand
73
+ readonly hq?: string | null
74
+ readonly website?: string | null
75
+ readonly accountType?: AccountType
76
+ readonly icpFit?: IcpFit
77
+ readonly techStack?: readonly string[]
78
+ readonly summary?: string
79
+ readonly tags?: readonly string[]
80
+ }
81
+
82
+ export function companyBody(input: CompanyInput): Record<string, unknown> {
83
+ return definedFields({
84
+ name: input.name,
85
+ domain: input.domain,
86
+ industry: input.industry,
87
+ description: input.description,
88
+ stage: input.stage,
89
+ size_band: input.sizeBand,
90
+ hq: input.hq,
91
+ website: input.website,
92
+ account_type: input.accountType,
93
+ icp_fit: input.icpFit,
94
+ tech_stack: input.techStack,
95
+ summary: input.summary,
96
+ tags: input.tags,
97
+ })
98
+ }