chapterhouse 0.8.1 → 0.9.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.
@@ -0,0 +1,618 @@
1
+ /**
2
+ * Shared API contract schemas — single source of truth for every server↔client
3
+ * response type in Chapterhouse.
4
+ *
5
+ * Both the backend (`src/api/server.ts`) and the frontend (`web/src/api-schemas.ts`)
6
+ * import from here. When a server response shape changes, the TypeScript compiler
7
+ * immediately surfaces any mismatch on the import side — schema drift becomes a
8
+ * compile error rather than a runtime bug.
9
+ *
10
+ * Excluded from this file:
11
+ * - Parts model (TextPart, ToolCallPart, etc.) — client-only; synthesised from SSE
12
+ * frames in the frontend store. Lives in `web/src/api-schemas.ts`.
13
+ * - TurnEventSchema — depends on the client-side PartSchema (which includes the
14
+ * client-only ActivityHeartbeatPart). Lives in `web/src/api-schemas.ts`.
15
+ * See `src/copilot/turn-events.ts` for the daemon-side TypeScript mirror.
16
+ *
17
+ * Migration status: initial extraction from web/src/api-schemas.ts (issue #369).
18
+ */
19
+ import { z } from "zod";
20
+ // ---------------------------------------------------------------------------
21
+ // /api/config/public
22
+ // ---------------------------------------------------------------------------
23
+ const StandaloneConfigSchema = z.object({
24
+ appName: z.literal("Chapterhouse"),
25
+ entraAuthEnabled: z.literal(false),
26
+ standalone: z.literal(true),
27
+ chatSseEnabled: z.boolean().optional(),
28
+ });
29
+ const NonStandaloneConfigSchema = z.object({
30
+ appName: z.literal("Chapterhouse"),
31
+ entraAuthEnabled: z.literal(false),
32
+ standalone: z.literal(false),
33
+ chatSseEnabled: z.boolean().optional(),
34
+ });
35
+ const EntraRuntimeConfigSchema = z.object({
36
+ appName: z.literal("Chapterhouse"),
37
+ entraAuthEnabled: z.literal(true),
38
+ entraClientId: z.string(),
39
+ entraTenantId: z.string(),
40
+ chatSseEnabled: z.boolean().optional(),
41
+ });
42
+ export const PublicConfigResponseSchema = z.union([
43
+ StandaloneConfigSchema,
44
+ NonStandaloneConfigSchema,
45
+ EntraRuntimeConfigSchema,
46
+ ]);
47
+ // ---------------------------------------------------------------------------
48
+ // /api/bootstrap
49
+ // ---------------------------------------------------------------------------
50
+ export const BootstrapResponseSchema = z.union([
51
+ z.object({ authMode: z.literal("legacy"), token: z.string().nullable() }),
52
+ z.object({ authMode: z.literal("entra") }),
53
+ z.object({ authMode: z.literal("standalone") }),
54
+ ]);
55
+ // ---------------------------------------------------------------------------
56
+ // /api/message /api/restart
57
+ // ---------------------------------------------------------------------------
58
+ export const StatusResponseSchema = z.object({ status: z.string() });
59
+ // ---------------------------------------------------------------------------
60
+ // /api/cancel
61
+ // ---------------------------------------------------------------------------
62
+ export const CancelResponseSchema = z.object({
63
+ status: z.string(),
64
+ cancelled: z.boolean(),
65
+ });
66
+ // ---------------------------------------------------------------------------
67
+ // /api/agents
68
+ // ---------------------------------------------------------------------------
69
+ export const AgentSummarySchema = z.object({
70
+ name: z.string(),
71
+ slug: z.string(),
72
+ description: z.string(),
73
+ model: z.string(),
74
+ scope: z.string().nullable(),
75
+ type: z.enum(["builtin", "custom"]),
76
+ lastEdited: z.string().nullable(),
77
+ });
78
+ export const AgentListSchema = z.array(AgentSummarySchema);
79
+ export const AgentDetailSchema = z.object({
80
+ name: z.string(),
81
+ slug: z.string(),
82
+ description: z.string(),
83
+ model: z.string(),
84
+ scope: z.string().nullable(),
85
+ persistent: z.boolean(),
86
+ skills: z.array(z.string()),
87
+ type: z.enum(["builtin", "custom"]),
88
+ editable: z.boolean(),
89
+ systemPrompt: z.string(),
90
+ });
91
+ export const AgentPatchRequestSchema = z.object({
92
+ name: z.string().optional(),
93
+ description: z.string().optional(),
94
+ model: z.string().optional(),
95
+ systemPrompt: z.string().optional(),
96
+ }).strict();
97
+ // ---------------------------------------------------------------------------
98
+ // /api/projects
99
+ // ---------------------------------------------------------------------------
100
+ export const ProjectSummarySchema = z.object({
101
+ slug: z.string(),
102
+ cwd: z.string(),
103
+ hardRuleCount: z.number().int().nullable(),
104
+ softRuleCount: z.number().int().nullable(),
105
+ });
106
+ export const ProjectListSchema = z.array(ProjectSummarySchema);
107
+ export const ProjectHardRulesSchema = z.object({
108
+ auto_pr: z.boolean(),
109
+ require_worktree: z.boolean(),
110
+ pr_draft_default: z.boolean(),
111
+ default_branch: z.string(),
112
+ commit_co_author: z.string(),
113
+ test_command: z.string(),
114
+ build_command: z.string(),
115
+ lint_command: z.string(),
116
+ require_clean_worktree: z.boolean(),
117
+ });
118
+ export const ProjectHardRulesUpdateRequestSchema = z.object({
119
+ hardRules: ProjectHardRulesSchema,
120
+ });
121
+ export const ProjectSoftRulesUpdateRequestSchema = z.object({
122
+ softRules: z.array(z.string()),
123
+ });
124
+ export const ProjectDetailSchema = z.discriminatedUnion("rulesFound", [
125
+ z.object({
126
+ slug: z.string(),
127
+ cwd: z.string(),
128
+ rulesFound: z.literal(true),
129
+ hardRules: ProjectHardRulesSchema,
130
+ softRules: z.array(z.string()),
131
+ }),
132
+ z.object({
133
+ slug: z.string(),
134
+ cwd: z.string(),
135
+ rulesFound: z.literal(false),
136
+ hardRules: z.null(),
137
+ softRules: z.array(z.string()),
138
+ }),
139
+ ]);
140
+ export const ProjectDeleteResponseSchema = z.object({
141
+ ok: z.literal(true),
142
+ slug: z.string(),
143
+ });
144
+ export const AgentSaveResponseSchema = z.object({
145
+ ok: z.literal(true),
146
+ slug: z.string(),
147
+ });
148
+ // ---------------------------------------------------------------------------
149
+ // /api/workers (list)
150
+ // ---------------------------------------------------------------------------
151
+ export const WorkerRowSchema = z.object({
152
+ slug: z.string(),
153
+ name: z.string(),
154
+ model: z.string(),
155
+ taskId: z.string(),
156
+ description: z.string(),
157
+ status: z.string(),
158
+ startedAt: z.string(),
159
+ completedAt: z.string().nullable(),
160
+ });
161
+ export const WorkerListSchema = z.array(WorkerRowSchema);
162
+ // ---------------------------------------------------------------------------
163
+ // /api/workers/:taskId (detail)
164
+ // ---------------------------------------------------------------------------
165
+ export const WorkerDetailSchema = z.object({
166
+ taskId: z.string(),
167
+ agentSlug: z.string(),
168
+ name: z.string(),
169
+ description: z.string(),
170
+ prompt: z.string().nullable(),
171
+ status: z.string(),
172
+ result: z.string().nullable(),
173
+ startedAt: z.string(),
174
+ completedAt: z.string().nullable(),
175
+ });
176
+ // ---------------------------------------------------------------------------
177
+ // /api/workers/:taskId/events (historical event log)
178
+ // ---------------------------------------------------------------------------
179
+ export const TaskEventSchema = z.object({
180
+ id: z.number(),
181
+ taskId: z.string(),
182
+ seq: z.number(),
183
+ ts: z.number(),
184
+ kind: z.enum(["tool_start", "tool_complete"]),
185
+ toolName: z.string().nullable(),
186
+ summary: z.string().nullable(),
187
+ });
188
+ export const TaskEventsResponseSchema = z.object({
189
+ taskId: z.string(),
190
+ events: z.array(TaskEventSchema),
191
+ });
192
+ // ---------------------------------------------------------------------------
193
+ // /api/model (get / set)
194
+ // ---------------------------------------------------------------------------
195
+ export const ModelResponseSchema = z.object({ model: z.string() });
196
+ export const SetModelResponseSchema = z.object({ previous: z.string(), current: z.string() });
197
+ export const ListModelsResponseSchema = z.object({
198
+ models: z.array(z.string()),
199
+ current: z.string(),
200
+ });
201
+ // ---------------------------------------------------------------------------
202
+ // /api/auto
203
+ // ---------------------------------------------------------------------------
204
+ export const AutoConfigSchema = z.object({
205
+ enabled: z.boolean(),
206
+ tierModels: z.record(z.string(), z.string()),
207
+ cooldownMessages: z.number(),
208
+ currentModel: z.string(),
209
+ lastRoute: z.unknown(),
210
+ });
211
+ // ---------------------------------------------------------------------------
212
+ // /api/wiki/pages
213
+ // ---------------------------------------------------------------------------
214
+ export const WikiPageMetaSchema = z.object({
215
+ path: z.string(),
216
+ title: z.string(),
217
+ summary: z.string(),
218
+ section: z.string(),
219
+ tags: z.array(z.string()),
220
+ updated: z.string(),
221
+ scope: z.enum(["personal", "team"]),
222
+ });
223
+ export const WikiPageListSchema = z.array(WikiPageMetaSchema);
224
+ // ---------------------------------------------------------------------------
225
+ // /api/wiki/page (read / write / delete)
226
+ // ---------------------------------------------------------------------------
227
+ const WikiFrontmatterValueSchema = z.union([z.string(), z.array(z.string()), z.boolean()]);
228
+ export const WikiPageFrontmatterSchema = z.object({
229
+ title: z.string().optional(),
230
+ summary: z.string().optional(),
231
+ updated: z.string().optional(),
232
+ tags: z.array(z.string()).optional(),
233
+ autostub: z.boolean().optional(),
234
+ confidence: z.enum(["high", "medium", "low"]).optional(),
235
+ contested: z.boolean().optional(),
236
+ contradictions: z.array(z.string()).optional(),
237
+ related: z.array(z.string()).optional(),
238
+ auto_pr: z.boolean().optional(),
239
+ require_worktree: z.boolean().optional(),
240
+ pr_draft_default: z.boolean().optional(),
241
+ default_branch: z.string().optional(),
242
+ commit_co_author: z.string().optional(),
243
+ test_command: z.string().optional(),
244
+ build_command: z.string().optional(),
245
+ lint_command: z.string().optional(),
246
+ require_clean_worktree: z.boolean().optional(),
247
+ metadata: z.record(z.string(), WikiFrontmatterValueSchema),
248
+ });
249
+ export const WikiPageContentSchema = z.object({
250
+ path: z.string(),
251
+ content: z.string(),
252
+ renderedContent: z.string(),
253
+ frontmatter: WikiPageFrontmatterSchema,
254
+ });
255
+ export const WikiWriteResponseSchema = z.object({
256
+ ok: z.boolean(),
257
+ created: z.boolean(),
258
+ path: z.string(),
259
+ });
260
+ export const WikiDeleteResponseSchema = z.object({
261
+ ok: z.boolean(),
262
+ path: z.string(),
263
+ });
264
+ export const WikiBrowserPageSchema = z.object({
265
+ slug: z.string(),
266
+ title: z.string().default(""),
267
+ summary: z.string().default(""),
268
+ type: z.string().default("topics"),
269
+ last_updated: z.string().default(""),
270
+ pinned: z.boolean().optional(),
271
+ }).passthrough();
272
+ export const WikiBrowserPageListSchema = z.object({
273
+ pages: z.array(WikiBrowserPageSchema),
274
+ });
275
+ export const WikiLinkSchema = z.object({
276
+ source_slug: z.string(),
277
+ target_slug: z.string(),
278
+ link_type: z.string(),
279
+ }).passthrough();
280
+ export const WikiLinksResponseSchema = z.object({
281
+ links: z.array(WikiLinkSchema),
282
+ });
283
+ export const WikiSourceSchema = z.object({
284
+ source_url: z.string().optional(),
285
+ path: z.string().optional(),
286
+ kind: z.string(),
287
+ status: z.string(),
288
+ session_id: z.string().nullable().optional(),
289
+ ingested_at: z.string().nullable().optional(),
290
+ }).passthrough();
291
+ export const WikiSourcesResponseSchema = z.object({
292
+ sources: z.array(WikiSourceSchema),
293
+ });
294
+ export const WikiResearchSessionSchema = z.object({
295
+ id: z.string().optional(),
296
+ name: z.string(),
297
+ source_count: z.number().int().default(0),
298
+ open_questions_count: z.number().int().default(0),
299
+ last_activity_at: z.string().default(""),
300
+ }).passthrough();
301
+ export const WikiResearchSessionsResponseSchema = z.object({
302
+ sessions: z.array(WikiResearchSessionSchema),
303
+ });
304
+ export const WikiPageDetailSchema = z.object({
305
+ page: WikiBrowserPageSchema.extend({
306
+ compiled_truth: z.string().default(""),
307
+ pinned: z.boolean().default(false),
308
+ frontmatter: z.record(z.string(), z.unknown()).optional(),
309
+ links: z.array(WikiLinkSchema).optional(),
310
+ }).passthrough(),
311
+ });
312
+ export const WikiPageUpdateResponseSchema = z.object({
313
+ ok: z.boolean(),
314
+ page: WikiPageDetailSchema.shape.page.optional(),
315
+ pinned: z.boolean().optional(),
316
+ }).passthrough();
317
+ export const WikiKorgResponseSchema = z.object({
318
+ reply: z.string(),
319
+ }).passthrough();
320
+ export const WikiReingestResponseSchema = z.object({
321
+ ok: z.boolean(),
322
+ }).passthrough();
323
+ // ---------------------------------------------------------------------------
324
+ // /api/skills
325
+ // ---------------------------------------------------------------------------
326
+ export const SkillSchema = z.object({
327
+ slug: z.string(),
328
+ name: z.string(),
329
+ description: z.string(),
330
+ directory: z.string(),
331
+ source: z.enum(["bundled", "local", "global"]),
332
+ });
333
+ export const SkillListSchema = z.array(SkillSchema);
334
+ export const RemoveSkillResponseSchema = z.object({
335
+ ok: z.boolean(),
336
+ message: z.string(),
337
+ });
338
+ // ---------------------------------------------------------------------------
339
+ // /api/channels
340
+ // ---------------------------------------------------------------------------
341
+ export const ChannelSchema = z.object({
342
+ key: z.string().min(1),
343
+ label: z.string().min(1),
344
+ slug: z.string().optional(),
345
+ name: z.string().optional(),
346
+ description: z.string().optional(),
347
+ scope: z.string().optional(),
348
+ });
349
+ export const ChannelListSchema = z.array(ChannelSchema);
350
+ // ---------------------------------------------------------------------------
351
+ // /api/session/:key/messages
352
+ // ---------------------------------------------------------------------------
353
+ export const SessionMessageSchema = z.object({
354
+ id: z.number().int(),
355
+ role: z.enum(["user", "assistant"]),
356
+ content: z.string(),
357
+ ts: z.string(),
358
+ turn_id: z.string().nullable(),
359
+ turnId: z.string().optional(),
360
+ agentSlug: z.string().optional(),
361
+ agentDisplayName: z.string().optional(),
362
+ });
363
+ export const SessionMessagesResponseSchema = z.object({
364
+ sessionKey: z.string(),
365
+ messages: z.array(SessionMessageSchema),
366
+ });
367
+ // ---------------------------------------------------------------------------
368
+ // SSE stream events (/stream)
369
+ // ---------------------------------------------------------------------------
370
+ const RouteInfoSchema = z.object({
371
+ model: z.string(),
372
+ routerMode: z.string().optional(),
373
+ tier: z.string().nullable().optional(),
374
+ overrideName: z.string().optional(),
375
+ });
376
+ const ThinkingDeltaFrameSchema = z.object({
377
+ kind: z.literal("thinking_delta"),
378
+ reasoningId: z.string(),
379
+ deltaContent: z.string(),
380
+ agentSlug: z.string().optional(),
381
+ });
382
+ const ToolStartFrameSchema = z.object({
383
+ kind: z.literal("tool_start"),
384
+ toolCallId: z.string(),
385
+ toolName: z.string(),
386
+ mcpServerName: z.string().optional(),
387
+ arguments: z.record(z.string(), z.unknown()).optional(),
388
+ agentSlug: z.string().optional(),
389
+ });
390
+ const ToolCompleteFrameSchema = z.object({
391
+ kind: z.literal("tool_complete"),
392
+ toolCallId: z.string(),
393
+ success: z.boolean(),
394
+ resultPreview: z.string().optional(),
395
+ detailedContent: z.string().optional(),
396
+ agentSlug: z.string().optional(),
397
+ });
398
+ const SubagentStartedFrameSchema = z.object({
399
+ kind: z.literal("subagent_started"),
400
+ toolCallId: z.string(),
401
+ agentName: z.string(),
402
+ agentDisplayName: z.string(),
403
+ agentDescription: z.string(),
404
+ agentSlug: z.string().optional(),
405
+ });
406
+ const SubagentCompletedFrameSchema = z.object({
407
+ kind: z.literal("subagent_completed"),
408
+ toolCallId: z.string(),
409
+ agentName: z.string(),
410
+ agentDisplayName: z.string(),
411
+ durationMs: z.number().optional(),
412
+ agentSlug: z.string().optional(),
413
+ });
414
+ const SubagentFailedFrameSchema = z.object({
415
+ kind: z.literal("subagent_failed"),
416
+ toolCallId: z.string(),
417
+ agentName: z.string(),
418
+ agentDisplayName: z.string(),
419
+ error: z.string().optional(),
420
+ agentSlug: z.string().optional(),
421
+ });
422
+ export const ActivityFrameSchema = z.discriminatedUnion("kind", [
423
+ ThinkingDeltaFrameSchema,
424
+ ToolStartFrameSchema,
425
+ ToolCompleteFrameSchema,
426
+ SubagentStartedFrameSchema,
427
+ SubagentCompletedFrameSchema,
428
+ SubagentFailedFrameSchema,
429
+ ]);
430
+ export const StreamEventSchema = z.union([
431
+ z.object({ type: z.literal("connected"), connectionId: z.string() }),
432
+ z.object({ type: z.literal("delta"), content: z.string(), sessionKey: z.string().optional(), turnId: z.string().optional() }),
433
+ z.object({
434
+ type: z.literal("message"),
435
+ content: z.string(),
436
+ sessionKey: z.string().optional(),
437
+ turnId: z.string().optional(),
438
+ route: RouteInfoSchema.optional(),
439
+ }),
440
+ z.object({ type: z.literal("cancelled"), sessionKey: z.string().optional() }),
441
+ z.object({
442
+ type: z.literal("status"),
443
+ status: z.enum(["idle", "dreaming"]),
444
+ message: z.string(),
445
+ }),
446
+ z.object({
447
+ type: z.literal("agent_reload_pending"),
448
+ slug: z.string(),
449
+ reason: z.literal("in_flight"),
450
+ }),
451
+ z.object({
452
+ type: z.literal("agent_reloaded"),
453
+ slug: z.string(),
454
+ reason: z.enum(["session_restart", "confirmed_restart"]),
455
+ }),
456
+ z.object({
457
+ type: z.literal("queued"),
458
+ position: z.number(),
459
+ sessionKey: z.string().optional(),
460
+ turnId: z.string().optional(),
461
+ msgId: z.string().optional(),
462
+ }),
463
+ // Activity events — each ActivityFrame kind merged inline with the activity wrapper.
464
+ // turnId is included for future use (e.g., the activity sidecar in #85) but is NOT
465
+ // used by App.tsx for bubble-boundary detection — activity events don't drive bubble
466
+ // boundaries; only delta/message events (which carry the authoritative turnId) do.
467
+ z.object({
468
+ type: z.literal("activity"),
469
+ sessionKey: z.string().optional(),
470
+ turnId: z.string().optional(),
471
+ kind: z.literal("thinking_delta"),
472
+ reasoningId: z.string(),
473
+ deltaContent: z.string(),
474
+ agentSlug: z.string().optional(),
475
+ }),
476
+ z.object({
477
+ type: z.literal("activity"),
478
+ sessionKey: z.string().optional(),
479
+ turnId: z.string().optional(),
480
+ kind: z.literal("tool_start"),
481
+ toolCallId: z.string(),
482
+ toolName: z.string(),
483
+ mcpServerName: z.string().optional(),
484
+ arguments: z.record(z.string(), z.unknown()).optional(),
485
+ agentSlug: z.string().optional(),
486
+ }),
487
+ z.object({
488
+ type: z.literal("activity"),
489
+ sessionKey: z.string().optional(),
490
+ turnId: z.string().optional(),
491
+ kind: z.literal("tool_complete"),
492
+ toolCallId: z.string(),
493
+ success: z.boolean(),
494
+ resultPreview: z.string().optional(),
495
+ detailedContent: z.string().optional(),
496
+ agentSlug: z.string().optional(),
497
+ }),
498
+ z.object({
499
+ type: z.literal("activity"),
500
+ sessionKey: z.string().optional(),
501
+ turnId: z.string().optional(),
502
+ kind: z.literal("subagent_started"),
503
+ toolCallId: z.string(),
504
+ agentName: z.string(),
505
+ agentDisplayName: z.string(),
506
+ agentDescription: z.string(),
507
+ agentSlug: z.string().optional(),
508
+ }),
509
+ z.object({
510
+ type: z.literal("activity"),
511
+ sessionKey: z.string().optional(),
512
+ turnId: z.string().optional(),
513
+ kind: z.literal("subagent_completed"),
514
+ toolCallId: z.string(),
515
+ agentName: z.string(),
516
+ agentDisplayName: z.string(),
517
+ durationMs: z.number().optional(),
518
+ agentSlug: z.string().optional(),
519
+ }),
520
+ z.object({
521
+ type: z.literal("activity"),
522
+ sessionKey: z.string().optional(),
523
+ turnId: z.string().optional(),
524
+ kind: z.literal("subagent_failed"),
525
+ toolCallId: z.string(),
526
+ agentName: z.string(),
527
+ agentDisplayName: z.string(),
528
+ error: z.string().optional(),
529
+ agentSlug: z.string().optional(),
530
+ }),
531
+ // Emitted by the backend when a queued turn starts processing, with the number
532
+ // of messages still waiting behind it. Frontend renumbers "N ahead" indicators.
533
+ z.object({
534
+ type: z.literal("queue-advance"),
535
+ length: z.number(),
536
+ sessionKey: z.string().optional(),
537
+ }),
538
+ // Emitted by the backend when an active turn is aborted by the user via the
539
+ // interrupt endpoint. The frontend should drop the partial in-flight bubble
540
+ // (identified by abortedTurnId) so the replacement turn renders fresh.
541
+ z.object({
542
+ type: z.literal("turn-interrupted"),
543
+ abortedTurnId: z.string(),
544
+ sessionKey: z.string().optional(),
545
+ }),
546
+ ]);
547
+ // ---------------------------------------------------------------------------
548
+ // /api/sessions/:key/turn (POST — fire-and-forget submit, #131)
549
+ // ---------------------------------------------------------------------------
550
+ export const SubmitTurnResponseSchema = z.object({
551
+ turnId: z.string(),
552
+ });
553
+ // ---------------------------------------------------------------------------
554
+ // /api/memory/active-scope (GET + POST)
555
+ // ---------------------------------------------------------------------------
556
+ export const ActiveMemoryScopeSchema = z.object({
557
+ slug: z.string(),
558
+ title: z.string(),
559
+ }).nullable();
560
+ export const SetActiveScopeResponseSchema = z.object({
561
+ ok: z.literal(true),
562
+ scope: z.string().nullable(),
563
+ });
564
+ // ---------------------------------------------------------------------------
565
+ // /api/memory/scopes
566
+ // ---------------------------------------------------------------------------
567
+ const MemoryScopeCountsSchema = z.object({
568
+ observations: z.number(),
569
+ decisions: z.number(),
570
+ entities: z.number(),
571
+ action_items: z.number(),
572
+ });
573
+ const MemoryScopeItemSchema = z.object({
574
+ slug: z.string(),
575
+ title: z.string(),
576
+ description: z.string(),
577
+ active: z.boolean(),
578
+ counts: MemoryScopeCountsSchema,
579
+ });
580
+ export const MemoryScopeListSchema = z.object({
581
+ scopes: z.array(MemoryScopeItemSchema),
582
+ });
583
+ // ---------------------------------------------------------------------------
584
+ // /api/memory/:scope (entries)
585
+ // ---------------------------------------------------------------------------
586
+ export const MemoryEntriesSchema = z.object({
587
+ entries: z.array(z.record(z.string(), z.unknown())),
588
+ total: z.number(),
589
+ });
590
+ // ---------------------------------------------------------------------------
591
+ // /api/memory/:scope/remember
592
+ // ---------------------------------------------------------------------------
593
+ export const MemoryRememberResponseSchema = z.object({
594
+ ok: z.literal(true),
595
+ id: z.string(),
596
+ });
597
+ // ---------------------------------------------------------------------------
598
+ // /api/memory/inbox
599
+ // ---------------------------------------------------------------------------
600
+ const InboxItemSchema = z.object({
601
+ id: z.number(),
602
+ scope_slug: z.string().nullable(),
603
+ kind: z.string(),
604
+ payload: z.string(),
605
+ source_agent: z.string(),
606
+ created_at: z.string(),
607
+ });
608
+ export const MemoryInboxSchema = z.object({
609
+ items: z.array(InboxItemSchema),
610
+ total: z.number(),
611
+ });
612
+ // ---------------------------------------------------------------------------
613
+ // /api/memory/inbox/:id/route
614
+ // ---------------------------------------------------------------------------
615
+ export const InboxRouteResponseSchema = z.object({
616
+ ok: z.literal(true),
617
+ });
618
+ //# sourceMappingURL=api-schemas.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chapterhouse",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Chapterhouse — a team-level AI assistant for engineering teams, built on the GitHub Copilot SDK. Web UI only.",
5
5
  "bin": {
6
6
  "chapterhouse": "dist/cli.js"