@ebowwa/coder 0.7.64 → 0.7.66

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 (101) hide show
  1. package/dist/index.js +36233 -32
  2. package/dist/interfaces/ui/terminal/cli/index.js +34318 -158
  3. package/dist/interfaces/ui/terminal/native/README.md +53 -0
  4. package/dist/interfaces/ui/terminal/native/claude_code_native.darwin-x64.node +0 -0
  5. package/dist/interfaces/ui/terminal/native/claude_code_native.dylib +0 -0
  6. package/dist/interfaces/ui/terminal/native/index.d.ts +0 -0
  7. package/dist/interfaces/ui/terminal/native/index.darwin-arm64.node +0 -0
  8. package/dist/interfaces/ui/terminal/native/index.js +43 -0
  9. package/dist/interfaces/ui/terminal/native/index.node +0 -0
  10. package/dist/interfaces/ui/terminal/native/package.json +34 -0
  11. package/dist/native/README.md +53 -0
  12. package/dist/native/claude_code_native.darwin-x64.node +0 -0
  13. package/dist/native/claude_code_native.dylib +0 -0
  14. package/dist/native/index.d.ts +0 -480
  15. package/dist/native/index.darwin-arm64.node +0 -0
  16. package/dist/native/index.js +43 -1625
  17. package/dist/native/index.node +0 -0
  18. package/dist/native/package.json +34 -0
  19. package/native/index.darwin-arm64.node +0 -0
  20. package/native/index.js +33 -19
  21. package/package.json +3 -2
  22. package/packages/src/core/agent-loop/__tests__/compaction.test.ts +17 -14
  23. package/packages/src/core/agent-loop/compaction.ts +6 -2
  24. package/packages/src/core/agent-loop/index.ts +2 -0
  25. package/packages/src/core/agent-loop/loop-state.ts +1 -1
  26. package/packages/src/core/agent-loop/turn-executor.ts +4 -0
  27. package/packages/src/core/agent-loop/types.ts +4 -0
  28. package/packages/src/core/api-client-impl.ts +377 -176
  29. package/packages/src/core/cognitive-security/hooks.ts +2 -1
  30. package/packages/src/core/config/todo +7 -0
  31. package/packages/src/core/context/__tests__/integration.test.ts +334 -0
  32. package/packages/src/core/context/compaction.ts +170 -0
  33. package/packages/src/core/context/constants.ts +58 -0
  34. package/packages/src/core/context/extraction.ts +85 -0
  35. package/packages/src/core/context/index.ts +66 -0
  36. package/packages/src/core/context/summarization.ts +251 -0
  37. package/packages/src/core/context/token-estimation.ts +98 -0
  38. package/packages/src/core/context/types.ts +59 -0
  39. package/packages/src/core/models.ts +81 -4
  40. package/packages/src/core/normalizers/todo +5 -1
  41. package/packages/src/core/providers/README.md +230 -0
  42. package/packages/src/core/providers/__tests__/providers.test.ts +135 -0
  43. package/packages/src/core/providers/index.ts +419 -0
  44. package/packages/src/core/providers/types.ts +132 -0
  45. package/packages/src/core/retry.ts +10 -0
  46. package/packages/src/ecosystem/tools/index.ts +174 -0
  47. package/packages/src/index.ts +23 -2
  48. package/packages/src/interfaces/ui/index.ts +17 -20
  49. package/packages/src/interfaces/ui/spinner.ts +2 -2
  50. package/packages/src/interfaces/ui/terminal/bridge/index.ts +370 -0
  51. package/packages/src/interfaces/ui/terminal/bridge/ipc.ts +829 -0
  52. package/packages/src/interfaces/ui/terminal/bridge/screen-export.ts +968 -0
  53. package/packages/src/interfaces/ui/terminal/bridge/types.ts +226 -0
  54. package/packages/src/interfaces/ui/terminal/bridge/useBridge.ts +210 -0
  55. package/packages/src/interfaces/ui/terminal/cli/bootstrap.ts +132 -0
  56. package/packages/src/interfaces/ui/terminal/cli/index.ts +200 -13
  57. package/packages/src/interfaces/ui/terminal/cli/interactive/index.ts +110 -0
  58. package/packages/src/interfaces/ui/terminal/cli/interactive/input-handler.ts +402 -0
  59. package/packages/src/interfaces/ui/terminal/cli/interactive/interactive-runner.ts +820 -0
  60. package/packages/src/interfaces/ui/terminal/cli/interactive/message-store.ts +299 -0
  61. package/packages/src/interfaces/ui/terminal/cli/interactive/types.ts +274 -0
  62. package/packages/src/interfaces/ui/terminal/shared/index.ts +13 -0
  63. package/packages/src/interfaces/ui/terminal/shared/query.ts +9 -3
  64. package/packages/src/interfaces/ui/terminal/shared/setup.ts +5 -1
  65. package/packages/src/interfaces/ui/terminal/shared/spinner-frames.ts +73 -0
  66. package/packages/src/interfaces/ui/terminal/shared/status-line.ts +10 -2
  67. package/packages/src/native/index.ts +404 -27
  68. package/packages/src/native/tui_v2_types.ts +39 -0
  69. package/packages/src/teammates/coordination.test.ts +279 -0
  70. package/packages/src/teammates/coordination.ts +646 -0
  71. package/packages/src/teammates/index.ts +95 -25
  72. package/packages/src/teammates/integration.test.ts +272 -0
  73. package/packages/src/teammates/runner.test.ts +235 -0
  74. package/packages/src/teammates/runner.ts +750 -0
  75. package/packages/src/teammates/schemas.ts +673 -0
  76. package/packages/src/types/index.ts +1 -0
  77. package/packages/src/core/context-compaction.ts +0 -578
  78. package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
  79. package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
  80. package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +0 -262
  81. package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +0 -232
  82. package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +0 -62
  83. package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +0 -537
  84. package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +0 -107
  85. package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +0 -240
  86. package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +0 -54
  87. package/packages/src/interfaces/ui/terminal/tui/commands.ts +0 -438
  88. package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +0 -584
  89. package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +0 -614
  90. package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +0 -333
  91. package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +0 -604
  92. package/packages/src/interfaces/ui/terminal/tui/components/index.ts +0 -118
  93. package/packages/src/interfaces/ui/terminal/tui/console.ts +0 -49
  94. package/packages/src/interfaces/ui/terminal/tui/index.ts +0 -90
  95. package/packages/src/interfaces/ui/terminal/tui/run.tsx +0 -42
  96. package/packages/src/interfaces/ui/terminal/tui/spinner.ts +0 -69
  97. package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +0 -390
  98. package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +0 -422
  99. package/packages/src/interfaces/ui/terminal/tui/types.ts +0 -186
  100. package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +0 -104
  101. 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
+ }
@@ -362,6 +362,7 @@ export interface Teammate {
362
362
  prompt: string;
363
363
  planModeRequired: boolean;
364
364
  paneId?: string;
365
+ insideTmux?: boolean;
365
366
  status: TeammateStatus;
366
367
  }
367
368