@ebowwa/coder 0.7.64 → 0.7.65
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/dist/index.js +36168 -32
- package/dist/interfaces/ui/terminal/cli/index.js +34253 -158
- package/dist/interfaces/ui/terminal/native/README.md +53 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.dylib +0 -0
- package/dist/interfaces/ui/terminal/native/index.d.ts +0 -0
- package/dist/interfaces/ui/terminal/native/index.darwin-arm64.node +0 -0
- package/dist/interfaces/ui/terminal/native/index.js +43 -0
- package/dist/interfaces/ui/terminal/native/index.node +0 -0
- package/dist/interfaces/ui/terminal/native/package.json +34 -0
- package/dist/native/README.md +53 -0
- package/dist/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/native/claude_code_native.dylib +0 -0
- package/dist/native/index.d.ts +0 -480
- package/dist/native/index.darwin-arm64.node +0 -0
- package/dist/native/index.js +43 -1625
- package/dist/native/index.node +0 -0
- package/dist/native/package.json +34 -0
- package/native/index.darwin-arm64.node +0 -0
- package/native/index.js +33 -19
- package/package.json +3 -2
- package/packages/src/core/agent-loop/__tests__/compaction.test.ts +17 -14
- package/packages/src/core/agent-loop/compaction.ts +6 -2
- package/packages/src/core/agent-loop/index.ts +2 -0
- package/packages/src/core/agent-loop/loop-state.ts +1 -1
- package/packages/src/core/agent-loop/turn-executor.ts +4 -0
- package/packages/src/core/agent-loop/types.ts +4 -0
- package/packages/src/core/api-client-impl.ts +283 -173
- package/packages/src/core/cognitive-security/hooks.ts +2 -1
- package/packages/src/core/config/todo +7 -0
- package/packages/src/core/context/__tests__/integration.test.ts +334 -0
- package/packages/src/core/context/compaction.ts +170 -0
- package/packages/src/core/context/constants.ts +58 -0
- package/packages/src/core/context/extraction.ts +85 -0
- package/packages/src/core/context/index.ts +66 -0
- package/packages/src/core/context/summarization.ts +251 -0
- package/packages/src/core/context/token-estimation.ts +98 -0
- package/packages/src/core/context/types.ts +59 -0
- package/packages/src/core/models.ts +81 -4
- package/packages/src/core/normalizers/todo +5 -1
- package/packages/src/core/providers/README.md +230 -0
- package/packages/src/core/providers/__tests__/providers.test.ts +135 -0
- package/packages/src/core/providers/index.ts +419 -0
- package/packages/src/core/providers/types.ts +132 -0
- package/packages/src/core/retry.ts +10 -0
- package/packages/src/ecosystem/tools/index.ts +174 -0
- package/packages/src/index.ts +23 -2
- package/packages/src/interfaces/ui/index.ts +17 -20
- package/packages/src/interfaces/ui/spinner.ts +2 -2
- package/packages/src/interfaces/ui/terminal/bridge/index.ts +370 -0
- package/packages/src/interfaces/ui/terminal/bridge/ipc.ts +829 -0
- package/packages/src/interfaces/ui/terminal/bridge/screen-export.ts +968 -0
- package/packages/src/interfaces/ui/terminal/bridge/types.ts +226 -0
- package/packages/src/interfaces/ui/terminal/bridge/useBridge.ts +210 -0
- package/packages/src/interfaces/ui/terminal/cli/bootstrap.ts +132 -0
- package/packages/src/interfaces/ui/terminal/cli/index.ts +200 -13
- package/packages/src/interfaces/ui/terminal/cli/interactive/index.ts +110 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/input-handler.ts +393 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/interactive-runner.ts +820 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/message-store.ts +299 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/types.ts +274 -0
- package/packages/src/interfaces/ui/terminal/shared/index.ts +13 -0
- package/packages/src/interfaces/ui/terminal/shared/query.ts +9 -3
- package/packages/src/interfaces/ui/terminal/shared/setup.ts +5 -1
- package/packages/src/interfaces/ui/terminal/shared/spinner-frames.ts +73 -0
- package/packages/src/interfaces/ui/terminal/shared/status-line.ts +10 -2
- package/packages/src/native/index.ts +404 -27
- package/packages/src/native/tui_v2_types.ts +39 -0
- package/packages/src/teammates/coordination.test.ts +279 -0
- package/packages/src/teammates/coordination.ts +646 -0
- package/packages/src/teammates/index.ts +95 -25
- package/packages/src/teammates/integration.test.ts +272 -0
- package/packages/src/teammates/runner.test.ts +235 -0
- package/packages/src/teammates/runner.ts +750 -0
- package/packages/src/teammates/schemas.ts +673 -0
- package/packages/src/types/index.ts +1 -0
- package/packages/src/core/context-compaction.ts +0 -578
- package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
- package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
- package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +0 -262
- package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +0 -232
- package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +0 -62
- package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +0 -537
- package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +0 -107
- package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +0 -240
- package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +0 -54
- package/packages/src/interfaces/ui/terminal/tui/commands.ts +0 -438
- package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +0 -584
- package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +0 -614
- package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +0 -333
- package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +0 -604
- package/packages/src/interfaces/ui/terminal/tui/components/index.ts +0 -118
- package/packages/src/interfaces/ui/terminal/tui/console.ts +0 -49
- package/packages/src/interfaces/ui/terminal/tui/index.ts +0 -90
- package/packages/src/interfaces/ui/terminal/tui/run.tsx +0 -42
- package/packages/src/interfaces/ui/terminal/tui/spinner.ts +0 -69
- package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +0 -390
- package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +0 -422
- package/packages/src/interfaces/ui/terminal/tui/types.ts +0 -186
- package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +0 -104
- package/packages/src/interfaces/ui/terminal/tui/useNativeInput.ts +0 -239
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod Validation Schemas for Teammate System
|
|
3
|
+
*
|
|
4
|
+
* Provides runtime type validation and parsing for all teammate-related types.
|
|
5
|
+
* Includes safe parse functions with proper error handling.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
// ============================================
|
|
11
|
+
// CONSTANTS FOR FILE-SAFE PATHS
|
|
12
|
+
// ============================================
|
|
13
|
+
|
|
14
|
+
// Maximum lengths for file-safe paths (filesystem limit is 255 chars per component)
|
|
15
|
+
export const MAX_TEAM_NAME_LENGTH = 64;
|
|
16
|
+
export const MAX_TEAMMATE_NAME_LENGTH = 64;
|
|
17
|
+
export const MAX_TEAMMATE_ID_LENGTH = 128;
|
|
18
|
+
export const MAX_DESCRIPTION_LENGTH = 500;
|
|
19
|
+
|
|
20
|
+
// Re-export for use in other modules
|
|
21
|
+
export const FILENAME_MAX_LENGTH = 64;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Sanitize a string for use in file paths
|
|
25
|
+
* Removes/replaces characters that could cause filesystem issues
|
|
26
|
+
*/
|
|
27
|
+
export function sanitizeForFilePath(name: string): string {
|
|
28
|
+
return name
|
|
29
|
+
.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") // Replace invalid chars
|
|
30
|
+
.replace(/\s+/g, "_") // Replace whitespace with underscore
|
|
31
|
+
.replace(/_{2,}/g, "_") // Collapse multiple underscores
|
|
32
|
+
.replace(/^_|_$/g, "") // Remove leading/trailing underscores
|
|
33
|
+
.slice(0, FILENAME_MAX_LENGTH); // Truncate to max length
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ============================================
|
|
37
|
+
// BASE SCHEMAS
|
|
38
|
+
// ============================================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* TeammateStatus validation
|
|
42
|
+
*/
|
|
43
|
+
export const TeammateStatusSchema = z.enum([
|
|
44
|
+
"pending",
|
|
45
|
+
"in_progress",
|
|
46
|
+
"idle",
|
|
47
|
+
"completed",
|
|
48
|
+
"failed",
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
export type TeammateStatus = z.infer<typeof TeammateStatusSchema>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Teammate validation
|
|
55
|
+
*/
|
|
56
|
+
export const TeammateSchema = z.object({
|
|
57
|
+
teammateId: z.string()
|
|
58
|
+
.min(1, "teammateId cannot be empty")
|
|
59
|
+
.max(MAX_TEAMMATE_ID_LENGTH, `teammateId cannot exceed ${MAX_TEAMMATE_ID_LENGTH} characters`),
|
|
60
|
+
name: z.string()
|
|
61
|
+
.min(1, "name cannot be empty")
|
|
62
|
+
.max(MAX_TEAMMATE_NAME_LENGTH, `name cannot exceed ${MAX_TEAMMATE_NAME_LENGTH} characters`),
|
|
63
|
+
teamName: z.string()
|
|
64
|
+
.min(1, "teamName cannot be empty")
|
|
65
|
+
.max(MAX_TEAM_NAME_LENGTH, `teamName cannot exceed ${MAX_TEAM_NAME_LENGTH} characters`),
|
|
66
|
+
color: z.string().default("blue"),
|
|
67
|
+
prompt: z.string().default(""), // Allow empty prompts for backward compatibility
|
|
68
|
+
planModeRequired: z.boolean().default(false),
|
|
69
|
+
paneId: z.string().optional(),
|
|
70
|
+
insideTmux: z.boolean().optional(),
|
|
71
|
+
status: TeammateStatusSchema,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
export type Teammate = z.infer<typeof TeammateSchema>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* TeammateStatus validation
|
|
78
|
+
*/
|
|
79
|
+
export const TeamStatusSchema = z.enum([
|
|
80
|
+
"active",
|
|
81
|
+
"paused",
|
|
82
|
+
"completed",
|
|
83
|
+
"archived",
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
export type TeamStatus = z.infer<typeof TeamStatusSchema>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* CoordinationSettings validation
|
|
90
|
+
*/
|
|
91
|
+
export const CoordinationSettingsSchema = z.object({
|
|
92
|
+
dependencyOrder: z.array(z.string()).default([]),
|
|
93
|
+
communicationProtocol: z.enum(["broadcast", "direct", "mixed"]).default("broadcast"),
|
|
94
|
+
taskAssignmentStrategy: z.enum(["manual", "auto", "priority"]).default("manual"),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export type CoordinationSettings = z.infer<typeof CoordinationSettingsSchema>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Team validation
|
|
101
|
+
*/
|
|
102
|
+
export const TeamSchema = z.object({
|
|
103
|
+
name: z.string()
|
|
104
|
+
.min(1, "name cannot be empty")
|
|
105
|
+
.max(MAX_TEAM_NAME_LENGTH, `name cannot exceed ${MAX_TEAM_NAME_LENGTH} characters`)
|
|
106
|
+
.refine(
|
|
107
|
+
(name) => /^[a-zA-Z0-9_-]+$/.test(name),
|
|
108
|
+
"name can only contain alphanumeric characters, hyphens, and underscores"
|
|
109
|
+
),
|
|
110
|
+
description: z.string()
|
|
111
|
+
.max(MAX_DESCRIPTION_LENGTH, `description cannot exceed ${MAX_DESCRIPTION_LENGTH} characters`)
|
|
112
|
+
.default(""),
|
|
113
|
+
teammates: z.array(TeammateSchema).default([]),
|
|
114
|
+
taskListId: z.string().default(""),
|
|
115
|
+
status: TeamStatusSchema.default("active"),
|
|
116
|
+
coordination: CoordinationSettingsSchema.default({
|
|
117
|
+
dependencyOrder: [],
|
|
118
|
+
communicationProtocol: "broadcast",
|
|
119
|
+
taskAssignmentStrategy: "manual",
|
|
120
|
+
}),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
export type Team = z.infer<typeof TeamSchema>;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* TeammateMessage validation
|
|
127
|
+
*/
|
|
128
|
+
export const TeammateMessageSchema = z.object({
|
|
129
|
+
type: z.enum(["broadcast", "direct", "notification"]),
|
|
130
|
+
from: z.string().min(1, "from cannot be empty"),
|
|
131
|
+
to: z.string().optional(),
|
|
132
|
+
content: z.string(),
|
|
133
|
+
timestamp: z.number().int().positive(),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
export type TeammateMessage = z.infer<typeof TeammateMessageSchema>;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* StoredMessage validation (includes persistence fields)
|
|
140
|
+
*/
|
|
141
|
+
export const StoredMessageSchema = TeammateMessageSchema.extend({
|
|
142
|
+
id: z.string().min(1, "id cannot be empty"),
|
|
143
|
+
teamName: z.string().min(1, "teamName cannot be empty"),
|
|
144
|
+
createdAt: z.number().int().positive(),
|
|
145
|
+
readAt: z.number().int().positive().optional(),
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
export type StoredMessage = z.infer<typeof StoredMessageSchema>;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* TeamConfig validation (for team creation)
|
|
152
|
+
*/
|
|
153
|
+
export const TeamConfigSchema = z.object({
|
|
154
|
+
name: z.string().min(1, "name cannot be empty"),
|
|
155
|
+
description: z.string().min(1, "description cannot be empty"),
|
|
156
|
+
teammates: z.array(
|
|
157
|
+
TeammateSchema.omit({ status: true })
|
|
158
|
+
).min(1, "team must have at least one teammate"),
|
|
159
|
+
taskListId: z.string().optional(),
|
|
160
|
+
coordination: CoordinationSettingsSchema.optional(),
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
export type TeamConfig = z.infer<typeof TeamConfigSchema>;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* TeammateConfig validation (for teammate creation)
|
|
167
|
+
*/
|
|
168
|
+
export const TeammateConfigSchema = TeammateSchema.partial({
|
|
169
|
+
teammateId: true,
|
|
170
|
+
status: true,
|
|
171
|
+
paneId: true,
|
|
172
|
+
insideTmux: true,
|
|
173
|
+
}).required({
|
|
174
|
+
name: true,
|
|
175
|
+
teamName: true,
|
|
176
|
+
color: true,
|
|
177
|
+
prompt: true,
|
|
178
|
+
planModeRequired: true,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
export type TeammateConfig = z.infer<typeof TeammateConfigSchema>;
|
|
182
|
+
|
|
183
|
+
// ============================================
|
|
184
|
+
// INBOX STATS SCHEMA
|
|
185
|
+
// ============================================
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* InboxStats validation
|
|
189
|
+
*/
|
|
190
|
+
export const InboxStatsSchema = z.object({
|
|
191
|
+
pending: z.number().int().nonnegative(),
|
|
192
|
+
processed: z.number().int().nonnegative(),
|
|
193
|
+
oldestPending: z.number().int().positive().optional(),
|
|
194
|
+
newestPending: z.number().int().positive().optional(),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
export type InboxStats = z.infer<typeof InboxStatsSchema>;
|
|
198
|
+
|
|
199
|
+
// ============================================
|
|
200
|
+
// WAIT RESULT SCHEMA
|
|
201
|
+
// ============================================
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* WaitResult validation (for waitForTeammatesToBecomeIdle)
|
|
205
|
+
*/
|
|
206
|
+
export const WaitResultSchema = z.object({
|
|
207
|
+
success: z.boolean(),
|
|
208
|
+
timedOut: z.boolean(),
|
|
209
|
+
statuses: z.record(TeammateStatusSchema),
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
export type WaitResult = z.infer<typeof WaitResultSchema>;
|
|
213
|
+
|
|
214
|
+
// ============================================
|
|
215
|
+
// TEMPLATE SCHEMAS
|
|
216
|
+
// ============================================
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Template types for teammate creation
|
|
220
|
+
*/
|
|
221
|
+
export const TemplateTypeSchema = z.enum([
|
|
222
|
+
"architect",
|
|
223
|
+
"implementer",
|
|
224
|
+
"reviewer",
|
|
225
|
+
"tester",
|
|
226
|
+
]);
|
|
227
|
+
|
|
228
|
+
export type TemplateType = z.infer<typeof TemplateTypeSchema>;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Valid color options for teammates
|
|
232
|
+
*/
|
|
233
|
+
export const TeammateColorSchema = z.enum([
|
|
234
|
+
"black",
|
|
235
|
+
"red",
|
|
236
|
+
"green",
|
|
237
|
+
"yellow",
|
|
238
|
+
"blue",
|
|
239
|
+
"magenta",
|
|
240
|
+
"cyan",
|
|
241
|
+
"white",
|
|
242
|
+
"brightBlack",
|
|
243
|
+
"brightRed",
|
|
244
|
+
"brightGreen",
|
|
245
|
+
"brightYellow",
|
|
246
|
+
"brightBlue",
|
|
247
|
+
"brightMagenta",
|
|
248
|
+
"brightCyan",
|
|
249
|
+
"brightWhite",
|
|
250
|
+
"gray",
|
|
251
|
+
"orange",
|
|
252
|
+
"purple",
|
|
253
|
+
"pink",
|
|
254
|
+
]);
|
|
255
|
+
|
|
256
|
+
export type TeammateColor = z.infer<typeof TeammateColorSchema>;
|
|
257
|
+
|
|
258
|
+
// ============================================
|
|
259
|
+
// SAFE PARSE FUNCTIONS
|
|
260
|
+
// ============================================
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Validation error class
|
|
264
|
+
*/
|
|
265
|
+
export class ValidationError extends Error {
|
|
266
|
+
public readonly issues: z.ZodIssue[];
|
|
267
|
+
|
|
268
|
+
constructor(message: string, issues: z.ZodIssue[]) {
|
|
269
|
+
super(message);
|
|
270
|
+
this.name = "ValidationError";
|
|
271
|
+
this.issues = issues;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Format validation issues as a readable string
|
|
276
|
+
*/
|
|
277
|
+
formatIssues(): string {
|
|
278
|
+
return this.issues
|
|
279
|
+
.map((issue) => {
|
|
280
|
+
const path = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
281
|
+
return ` - ${path}: ${issue.message}`;
|
|
282
|
+
})
|
|
283
|
+
.join("\n");
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Safely parse a Teammate
|
|
289
|
+
* @throws ValidationError if validation fails
|
|
290
|
+
*/
|
|
291
|
+
export function parseTeammate(data: unknown): Teammate {
|
|
292
|
+
const result = TeammateSchema.safeParse(data);
|
|
293
|
+
|
|
294
|
+
if (!result.success) {
|
|
295
|
+
throw new ValidationError(
|
|
296
|
+
"Invalid Teammate data",
|
|
297
|
+
result.error.issues
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return result.data;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Safely parse a Team
|
|
306
|
+
* @throws ValidationError if validation fails
|
|
307
|
+
*/
|
|
308
|
+
export function parseTeam(data: unknown): Team {
|
|
309
|
+
const result = TeamSchema.safeParse(data);
|
|
310
|
+
|
|
311
|
+
if (!result.success) {
|
|
312
|
+
throw new ValidationError(
|
|
313
|
+
"Invalid Team data",
|
|
314
|
+
result.error.issues
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return result.data;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Safely parse a TeammateMessage
|
|
323
|
+
* @throws ValidationError if validation fails
|
|
324
|
+
*/
|
|
325
|
+
export function parseTeammateMessage(data: unknown): TeammateMessage {
|
|
326
|
+
const result = TeammateMessageSchema.safeParse(data);
|
|
327
|
+
|
|
328
|
+
if (!result.success) {
|
|
329
|
+
throw new ValidationError(
|
|
330
|
+
"Invalid TeammateMessage data",
|
|
331
|
+
result.error.issues
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return result.data;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Safely parse a StoredMessage
|
|
340
|
+
* @throws ValidationError if validation fails
|
|
341
|
+
*/
|
|
342
|
+
export function parseStoredMessage(data: unknown): StoredMessage {
|
|
343
|
+
const result = StoredMessageSchema.safeParse(data);
|
|
344
|
+
|
|
345
|
+
if (!result.success) {
|
|
346
|
+
throw new ValidationError(
|
|
347
|
+
"Invalid StoredMessage data",
|
|
348
|
+
result.error.issues
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return result.data;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Safely parse a TeamConfig
|
|
357
|
+
* @throws ValidationError if validation fails
|
|
358
|
+
*/
|
|
359
|
+
export function parseTeamConfig(data: unknown): TeamConfig {
|
|
360
|
+
const result = TeamConfigSchema.safeParse(data);
|
|
361
|
+
|
|
362
|
+
if (!result.success) {
|
|
363
|
+
throw new ValidationError(
|
|
364
|
+
"Invalid TeamConfig data",
|
|
365
|
+
result.error.issues
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return result.data;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Safely parse a TeammateConfig
|
|
374
|
+
* @throws ValidationError if validation fails
|
|
375
|
+
*/
|
|
376
|
+
export function parseTeammateConfig(data: unknown): TeammateConfig {
|
|
377
|
+
const result = TeammateConfigSchema.safeParse(data);
|
|
378
|
+
|
|
379
|
+
if (!result.success) {
|
|
380
|
+
throw new ValidationError(
|
|
381
|
+
"Invalid TeammateConfig data",
|
|
382
|
+
result.error.issues
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return result.data;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Safely parse InboxStats
|
|
391
|
+
* @throws ValidationError if validation fails
|
|
392
|
+
*/
|
|
393
|
+
export function parseInboxStats(data: unknown): InboxStats {
|
|
394
|
+
const result = InboxStatsSchema.safeParse(data);
|
|
395
|
+
|
|
396
|
+
if (!result.success) {
|
|
397
|
+
throw new ValidationError(
|
|
398
|
+
"Invalid InboxStats data",
|
|
399
|
+
result.error.issues
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return result.data;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Safely parse WaitResult
|
|
408
|
+
* @throws ValidationError if validation fails
|
|
409
|
+
*/
|
|
410
|
+
export function parseWaitResult(data: unknown): WaitResult {
|
|
411
|
+
const result = WaitResultSchema.safeParse(data);
|
|
412
|
+
|
|
413
|
+
if (!result.success) {
|
|
414
|
+
throw new ValidationError(
|
|
415
|
+
"Invalid WaitResult data",
|
|
416
|
+
result.error.issues
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return result.data;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ============================================
|
|
424
|
+
// SAFE PARSE WITH DEFAULTS
|
|
425
|
+
// ============================================
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Safely parse with validation error return (no throw)
|
|
429
|
+
*/
|
|
430
|
+
export function safeParseTeammate(data: unknown): {
|
|
431
|
+
success: boolean;
|
|
432
|
+
data?: Teammate;
|
|
433
|
+
error?: string;
|
|
434
|
+
} {
|
|
435
|
+
const result = TeammateSchema.safeParse(data);
|
|
436
|
+
|
|
437
|
+
if (!result.success) {
|
|
438
|
+
return {
|
|
439
|
+
success: false,
|
|
440
|
+
error: new ValidationError(
|
|
441
|
+
"Invalid Teammate data",
|
|
442
|
+
result.error.issues
|
|
443
|
+
).formatIssues(),
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return {
|
|
448
|
+
success: true,
|
|
449
|
+
data: result.data,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Safely parse team with validation error return (no throw)
|
|
455
|
+
*/
|
|
456
|
+
export function safeParseTeam(data: unknown): {
|
|
457
|
+
success: boolean;
|
|
458
|
+
data?: Team;
|
|
459
|
+
error?: string;
|
|
460
|
+
} {
|
|
461
|
+
const result = TeamSchema.safeParse(data);
|
|
462
|
+
|
|
463
|
+
if (!result.success) {
|
|
464
|
+
return {
|
|
465
|
+
success: false,
|
|
466
|
+
error: new ValidationError(
|
|
467
|
+
"Invalid Team data",
|
|
468
|
+
result.error.issues
|
|
469
|
+
).formatIssues(),
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
success: true,
|
|
475
|
+
data: result.data,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Safely parse message with validation error return (no throw)
|
|
481
|
+
*/
|
|
482
|
+
export function safeParseTeammateMessage(data: unknown): {
|
|
483
|
+
success: boolean;
|
|
484
|
+
data?: TeammateMessage;
|
|
485
|
+
error?: string;
|
|
486
|
+
} {
|
|
487
|
+
const result = TeammateMessageSchema.safeParse(data);
|
|
488
|
+
|
|
489
|
+
if (!result.success) {
|
|
490
|
+
return {
|
|
491
|
+
success: false,
|
|
492
|
+
error: new ValidationError(
|
|
493
|
+
"Invalid TeammateMessage data",
|
|
494
|
+
result.error.issues
|
|
495
|
+
).formatIssues(),
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return {
|
|
500
|
+
success: true,
|
|
501
|
+
data: result.data,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Safely parse stored message with validation error return (no throw)
|
|
507
|
+
*/
|
|
508
|
+
export function safeParseStoredMessage(data: unknown): {
|
|
509
|
+
success: boolean;
|
|
510
|
+
data?: StoredMessage;
|
|
511
|
+
error?: string;
|
|
512
|
+
} {
|
|
513
|
+
const result = StoredMessageSchema.safeParse(data);
|
|
514
|
+
|
|
515
|
+
if (!result.success) {
|
|
516
|
+
return {
|
|
517
|
+
success: false,
|
|
518
|
+
error: new ValidationError(
|
|
519
|
+
"Invalid StoredMessage data",
|
|
520
|
+
result.error.issues
|
|
521
|
+
).formatIssues(),
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return {
|
|
526
|
+
success: true,
|
|
527
|
+
data: result.data,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// ============================================
|
|
532
|
+
// UTILITY FUNCTIONS
|
|
533
|
+
// ============================================
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Validate a teammate ID format
|
|
537
|
+
*/
|
|
538
|
+
export function isValidTeammateId(id: string): boolean {
|
|
539
|
+
// Format: teammate_<timestamp>_<random>
|
|
540
|
+
const pattern = /^teammate_\d+_[a-z0-9]{6}$/;
|
|
541
|
+
return pattern.test(id);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Validate a message ID format
|
|
546
|
+
*/
|
|
547
|
+
export function isValidMessageId(id: string): boolean {
|
|
548
|
+
// Format: msg_<timestamp>_<random>
|
|
549
|
+
const pattern = /^msg_\d+_[a-z0-9]{6}$/;
|
|
550
|
+
return pattern.test(id);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Validate a team name (alphanumeric, hyphens, underscores)
|
|
555
|
+
*/
|
|
556
|
+
export function isValidTeamName(name: string): boolean {
|
|
557
|
+
const pattern = /^[a-zA-Z0-9_-]+$/;
|
|
558
|
+
return pattern.test(name) && name.length > 0 && name.length <= 50;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Create a teammate with automatic validation
|
|
563
|
+
*/
|
|
564
|
+
export function createValidatedTeammate(
|
|
565
|
+
config: TeammateConfig
|
|
566
|
+
): Teammate {
|
|
567
|
+
const parsed = parseTeammateConfig(config);
|
|
568
|
+
|
|
569
|
+
return {
|
|
570
|
+
...parsed,
|
|
571
|
+
teammateId: parsed.teammateId || generateTeammateId(),
|
|
572
|
+
status: parsed.status || "pending",
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Create a team with automatic validation
|
|
578
|
+
*/
|
|
579
|
+
export function createValidatedTeam(config: TeamConfig): Team {
|
|
580
|
+
const parsed = parseTeamConfig(config);
|
|
581
|
+
|
|
582
|
+
return {
|
|
583
|
+
...parsed,
|
|
584
|
+
status: "active" as const,
|
|
585
|
+
teammates: parsed.teammates.map((t) => ({
|
|
586
|
+
...t,
|
|
587
|
+
teammateId: t.teammateId || generateTeammateId(),
|
|
588
|
+
status: "pending" as const,
|
|
589
|
+
})),
|
|
590
|
+
taskListId: parsed.taskListId || "",
|
|
591
|
+
coordination: parsed.coordination || {
|
|
592
|
+
dependencyOrder: [],
|
|
593
|
+
communicationProtocol: "broadcast" as const,
|
|
594
|
+
taskAssignmentStrategy: "manual" as const,
|
|
595
|
+
},
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Generate a valid teammate ID
|
|
601
|
+
*/
|
|
602
|
+
export function generateTeammateId(): string {
|
|
603
|
+
return `teammate_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Generate a valid message ID
|
|
608
|
+
*/
|
|
609
|
+
export function generateMessageId(): string {
|
|
610
|
+
return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// ============================================
|
|
614
|
+
// BATCH VALIDATION
|
|
615
|
+
// ============================================
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Validate multiple teammates
|
|
619
|
+
* @returns Object with valid teammates and array of errors
|
|
620
|
+
*/
|
|
621
|
+
export function validateTeammatesBatch(
|
|
622
|
+
teammates: unknown[]
|
|
623
|
+
): {
|
|
624
|
+
valid: Teammate[];
|
|
625
|
+
invalid: Array<{ index: number; error: string }>;
|
|
626
|
+
} {
|
|
627
|
+
const valid: Teammate[] = [];
|
|
628
|
+
const invalid: Array<{ index: number; error: string }> = [];
|
|
629
|
+
|
|
630
|
+
teammates.forEach((teammate, index) => {
|
|
631
|
+
const result = safeParseTeammate(teammate);
|
|
632
|
+
|
|
633
|
+
if (result.success && result.data) {
|
|
634
|
+
valid.push(result.data);
|
|
635
|
+
} else {
|
|
636
|
+
invalid.push({
|
|
637
|
+
index,
|
|
638
|
+
error: result.error || "Unknown validation error",
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
return { valid, invalid };
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Validate multiple messages
|
|
648
|
+
* @returns Object with valid messages and array of errors
|
|
649
|
+
*/
|
|
650
|
+
export function validateMessagesBatch(
|
|
651
|
+
messages: unknown[]
|
|
652
|
+
): {
|
|
653
|
+
valid: TeammateMessage[];
|
|
654
|
+
invalid: Array<{ index: number; error: string }>;
|
|
655
|
+
} {
|
|
656
|
+
const valid: TeammateMessage[] = [];
|
|
657
|
+
const invalid: Array<{ index: number; error: string }> = [];
|
|
658
|
+
|
|
659
|
+
messages.forEach((message, index) => {
|
|
660
|
+
const result = safeParseTeammateMessage(message);
|
|
661
|
+
|
|
662
|
+
if (result.success && result.data) {
|
|
663
|
+
valid.push(result.data);
|
|
664
|
+
} else {
|
|
665
|
+
invalid.push({
|
|
666
|
+
index,
|
|
667
|
+
error: result.error || "Unknown validation error",
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
return { valid, invalid };
|
|
673
|
+
}
|