@kammcs/buildit-mcp 0.10.0 → 0.12.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.
@@ -2,7 +2,7 @@
2
2
  * Exact schemas for the agent API: closed objects and enums, patterns and bounds. The test fake uses them to check requests and its own responses against the contract; the server parses responses with the tolerant schemas.ts.
3
3
  *
4
4
  * GENERATED by scripts/generate.ts from openapi/openapi.json (agent API
5
- * 1.11.0). Do not edit: change the contract, then run
5
+ * 1.14.0). Do not edit: change the contract, then run
6
6
  * `npm run generate`.
7
7
  */
8
8
  import { z } from 'zod';
@@ -94,6 +94,7 @@ export const EntityKindSchema = z.enum([
94
94
  'approval',
95
95
  'handoff',
96
96
  'upload',
97
+ 'request',
97
98
  ]);
98
99
  export const EstimateSchema = z.number().min(0).max(9999.99);
99
100
  export const EstimateScaleSchema = z.enum(['none', 'points', 'tshirt']);
@@ -1065,6 +1066,36 @@ export const ErrorSchema = z.strictObject({
1065
1066
  .regex(new RegExp('^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$', 'u')),
1066
1067
  }),
1067
1068
  }),
1069
+ z.strictObject({
1070
+ code: z.literal('request_not_found'),
1071
+ message: z.string().min(1),
1072
+ hint: z.string().optional(),
1073
+ details: z.strictObject({
1074
+ request_id: z
1075
+ .string()
1076
+ .regex(new RegExp('^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$', 'u')),
1077
+ }),
1078
+ }),
1079
+ z.strictObject({
1080
+ code: z.literal('request_closed'),
1081
+ message: z.string().min(1),
1082
+ hint: z.string().optional(),
1083
+ details: z.strictObject({
1084
+ request_id: z
1085
+ .string()
1086
+ .regex(new RegExp('^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$', 'u')),
1087
+ }),
1088
+ }),
1089
+ z.strictObject({
1090
+ code: z.literal('request_result_private'),
1091
+ message: z.string().min(1),
1092
+ hint: z.string().optional(),
1093
+ details: z.strictObject({
1094
+ request_id: z
1095
+ .string()
1096
+ .regex(new RegExp('^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$', 'u')),
1097
+ }),
1098
+ }),
1068
1099
  ]),
1069
1100
  });
1070
1101
  export const FeedbackSchema = z.strictObject({
@@ -1204,6 +1235,7 @@ export const PostSessionMessageRequestSchema = z.strictObject({
1204
1235
  conversation_id: UuidSchema.optional(),
1205
1236
  card: QuestionCardSchema.optional(),
1206
1237
  files: z.array(UuidSchema).min(1).max(10).optional(),
1238
+ request: UuidSchema.optional(),
1207
1239
  });
1208
1240
  export const PresignedPutSchema = z.strictObject({
1209
1241
  url: z.string(),
@@ -1372,6 +1404,17 @@ export const SessionLinkFileSchema = z.strictObject({
1372
1404
  duration_ms: z.int().min(1).optional(),
1373
1405
  });
1374
1406
  export const SessionOwnerSchema = z.strictObject({ id: UuidSchema, name: z.string() });
1407
+ export const SessionRequestChannelContextSchema = z.strictObject({
1408
+ type: z.literal('channel'),
1409
+ channel: z.strictObject({ id: UuidSchema, name: z.string().nullable() }),
1410
+ from: TimestampSchema,
1411
+ to: TimestampSchema,
1412
+ });
1413
+ export const SessionRequestThreadContextSchema = z.strictObject({
1414
+ type: z.literal('thread'),
1415
+ channel: z.strictObject({ id: UuidSchema, name: z.string().nullable(), dm: z.boolean() }),
1416
+ thread_id: UuidSchema,
1417
+ });
1375
1418
  export const SessionRuleSchema = z.union([
1376
1419
  z.strictObject({
1377
1420
  type: z.literal('addRules'),
@@ -1776,39 +1819,11 @@ export const MeResponseSchema = z.strictObject({
1776
1819
  projects: z.array(ProjectBriefSchema),
1777
1820
  rate_limits: RateLimitsSchema,
1778
1821
  });
1779
- export const MessageSchema = z.strictObject({
1822
+ export const MessageItemSchema = z.strictObject({
1780
1823
  id: UuidSchema,
1781
- channel_id: UuidSchema,
1782
- author: MessageAuthorSchema.nullable(),
1783
- kind: MessageKindSchema,
1784
- body: z.string(),
1785
- code: z.string().nullable(),
1786
- code_language: z.string().nullable(),
1787
- file: z
1788
- .strictObject({
1789
- id: UuidSchema,
1790
- name: z.string(),
1791
- type: z.string(),
1792
- duration_ms: z.int().min(1).nullable(),
1793
- })
1794
- .nullable(),
1795
- photos: z.array(z.strictObject({
1796
- id: UuidSchema,
1797
- name: z.string(),
1798
- type: z.string(),
1799
- alt: z.string().nullable(),
1800
- duration_ms: z.int().min(1).nullable(),
1801
- })),
1802
- mentions: z.array(UserSchema),
1803
- reply_count: z.int(),
1804
- last_reply_at: TimestampSchema.nullable(),
1805
- created_at: TimestampSchema,
1806
- edited_at: TimestampSchema.nullable(),
1807
- deleted: z.boolean(),
1808
- via_agent: z.string().nullable(),
1809
- pinned: z.boolean(),
1810
- pinned_at: TimestampSchema.nullable(),
1811
- pinned_by: MessageAuthorSchema.nullable(),
1824
+ key: ItemKeySchema,
1825
+ title: z.string(),
1826
+ status: StatusBriefSchema,
1812
1827
  });
1813
1828
  export const PageSchema = z.strictObject({
1814
1829
  id: UuidSchema,
@@ -2012,6 +2027,21 @@ export const SessionMessageSchema = z.strictObject({
2012
2027
  created_at: TimestampSchema,
2013
2028
  session: SessionBriefSchema,
2014
2029
  });
2030
+ export const SessionRequestBriefContextSchema = z.strictObject({
2031
+ type: z.literal('brief'),
2032
+ project: SessionEventProjectSchema,
2033
+ channel: z.strictObject({ id: UuidSchema, name: z.string().nullable() }),
2034
+ from: TimestampSchema,
2035
+ to: TimestampSchema,
2036
+ });
2037
+ export const SessionRequestItemContextSchema = z.strictObject({
2038
+ type: z.literal('item'),
2039
+ item: ItemIdKeySchema,
2040
+ });
2041
+ export const SessionRequestItemsContextSchema = z.strictObject({
2042
+ type: z.literal('items'),
2043
+ items: z.array(ItemIdKeySchema).min(1).max(20),
2044
+ });
2015
2045
  export const SessionResponseSchema = z.strictObject({
2016
2046
  session: z.strictObject({
2017
2047
  id: UuidSchema,
@@ -2235,10 +2265,6 @@ export const LinkSchema = z.strictObject({
2235
2265
  kind: LinkKindSchema,
2236
2266
  item: LinkedItemSchema.nullable(),
2237
2267
  });
2238
- export const ListChannelMessagesResponseSchema = z.strictObject({
2239
- items: z.array(MessageSchema),
2240
- next_cursor: z.string().nullable(),
2241
- });
2242
2268
  export const ListChannelsResponseSchema = z.strictObject({
2243
2269
  items: z.array(ChannelSchema),
2244
2270
  next_cursor: z.string().nullable(),
@@ -2259,12 +2285,42 @@ export const ListSessionThreadsResponseSchema = z.strictObject({
2259
2285
  threads: z.array(SessionThreadSchema).max(50),
2260
2286
  });
2261
2287
  export const ListWorkTypesResponseSchema = z.strictObject({ items: z.array(WorkTypeSchema) });
2262
- export const PageResponseSchema = z.strictObject({ page: PageSchema });
2263
- export const PinnedMessageSchema = z.strictObject({
2264
- pinned_at: TimestampSchema,
2288
+ export const MessageSchema = z.strictObject({
2289
+ id: UuidSchema,
2290
+ channel_id: UuidSchema,
2291
+ author: MessageAuthorSchema.nullable(),
2292
+ kind: MessageKindSchema,
2293
+ body: z.string(),
2294
+ code: z.string().nullable(),
2295
+ code_language: z.string().nullable(),
2296
+ file: z
2297
+ .strictObject({
2298
+ id: UuidSchema,
2299
+ name: z.string(),
2300
+ type: z.string(),
2301
+ duration_ms: z.int().min(1).nullable(),
2302
+ })
2303
+ .nullable(),
2304
+ photos: z.array(z.strictObject({
2305
+ id: UuidSchema,
2306
+ name: z.string(),
2307
+ type: z.string(),
2308
+ alt: z.string().nullable(),
2309
+ duration_ms: z.int().min(1).nullable(),
2310
+ })),
2311
+ mentions: z.array(UserSchema),
2312
+ reply_count: z.int(),
2313
+ last_reply_at: TimestampSchema.nullable(),
2314
+ created_at: TimestampSchema,
2315
+ edited_at: TimestampSchema.nullable(),
2316
+ deleted: z.boolean(),
2317
+ via_agent: z.string().nullable(),
2318
+ pinned: z.boolean(),
2319
+ pinned_at: TimestampSchema.nullable(),
2265
2320
  pinned_by: MessageAuthorSchema.nullable(),
2266
- message: MessageSchema,
2321
+ item: MessageItemSchema.optional(),
2267
2322
  });
2323
+ export const PageResponseSchema = z.strictObject({ page: PageSchema });
2268
2324
  export const PlanResultSchema = z.union([
2269
2325
  z.strictObject({ action: z.literal('delete_item'), deleted: z.array(ItemBriefSchema) }),
2270
2326
  z.strictObject({
@@ -2431,11 +2487,13 @@ export const SessionItemUnassignedDataSchema = z.strictObject({
2431
2487
  ]),
2432
2488
  user: SessionEventUserSchema.nullable(),
2433
2489
  });
2434
- export const ThreadResponseSchema = z.strictObject({
2435
- root: MessageSchema,
2436
- replies: z.array(MessageSchema),
2437
- next_cursor: z.string().nullable(),
2438
- });
2490
+ export const SessionRequestContextSchema = z.union([
2491
+ SessionRequestChannelContextSchema,
2492
+ SessionRequestThreadContextSchema,
2493
+ SessionRequestItemContextSchema,
2494
+ SessionRequestItemsContextSchema,
2495
+ SessionRequestBriefContextSchema,
2496
+ ]);
2439
2497
  export const TransitionItemRequestSchema = z.strictObject({
2440
2498
  status: NameRefSchema,
2441
2499
  comment: CommentBodySchema.optional(),
@@ -2539,10 +2597,15 @@ export const ItemSearchResultSchema = z.strictObject({
2539
2597
  description: z.string().optional(),
2540
2598
  custom: z.array(CustomFieldValueSchema).optional(),
2541
2599
  });
2542
- export const ListPinsResponseSchema = z.strictObject({
2543
- items: z.array(PinnedMessageSchema),
2600
+ export const ListChannelMessagesResponseSchema = z.strictObject({
2601
+ items: z.array(MessageSchema),
2544
2602
  next_cursor: z.string().nullable(),
2545
2603
  });
2604
+ export const PinnedMessageSchema = z.strictObject({
2605
+ pinned_at: TimestampSchema,
2606
+ pinned_by: MessageAuthorSchema.nullable(),
2607
+ message: MessageSchema,
2608
+ });
2546
2609
  export const ProposeSessionHandoffResponseSchema = z.strictObject({
2547
2610
  handoff: SessionHandoffSchema,
2548
2611
  created: z.boolean(),
@@ -2605,6 +2668,37 @@ export const SessionPlanOutcomeSchema = z.strictObject({
2605
2668
  error: z.strictObject({ code: z.string(), message: z.string().optional() }).optional(),
2606
2669
  changed: z.array(z.strictObject({ kind: z.string(), ref: z.string() })).optional(),
2607
2670
  });
2671
+ export const SessionRequestDataSchema = z.strictObject({
2672
+ request: z.strictObject({
2673
+ id: UuidSchema,
2674
+ kind: z.enum([
2675
+ 'catch_up',
2676
+ 'thread_to_items',
2677
+ 'triage',
2678
+ 'item_helper.spec',
2679
+ 'item_helper.subtasks',
2680
+ 'item_helper.estimate',
2681
+ 'item_helper.related',
2682
+ 'item_helper.stalled',
2683
+ ]),
2684
+ source: z.enum(['button', 'subscription']),
2685
+ subscription_id: UuidSchema.nullable(),
2686
+ created_at: TimestampSchema,
2687
+ expires_at: TimestampSchema,
2688
+ }),
2689
+ requester: SessionEventUserSchema,
2690
+ locale: z.string(),
2691
+ time_zone: z.string(),
2692
+ project: SessionEventProjectSchema.nullable(),
2693
+ context: SessionRequestContextSchema,
2694
+ result: z.strictObject({ place: z.enum(['dm', 'thread', 'item_thread']) }),
2695
+ handed: z.strictObject({ from_session: SessionBriefSchema }).nullable(),
2696
+ });
2697
+ export const ThreadResponseSchema = z.strictObject({
2698
+ root: MessageSchema,
2699
+ replies: z.array(MessageSchema),
2700
+ next_cursor: z.string().nullable(),
2701
+ });
2608
2702
  export const CreateItemResponseSchema = z.strictObject({
2609
2703
  item: ItemDetailSchema,
2610
2704
  created: z.boolean(),
@@ -2620,6 +2714,10 @@ export const GetItemResponseSchema = z.strictObject({
2620
2714
  feedback: FeedbackSchema.optional(),
2621
2715
  });
2622
2716
  export const ItemWriteResponseSchema = z.strictObject({ item: ItemDetailSchema });
2717
+ export const ListPinsResponseSchema = z.strictObject({
2718
+ items: z.array(PinnedMessageSchema),
2719
+ next_cursor: z.string().nullable(),
2720
+ });
2623
2721
  export const SearchItemsResponseSchema = z.strictObject({
2624
2722
  items: z.array(ItemSearchResultSchema),
2625
2723
  next_cursor: z.string().nullable(),
@@ -2679,6 +2777,17 @@ export const SessionMessageEventSchema = z.strictObject({
2679
2777
  session: SessionBriefSchema,
2680
2778
  data: SessionEventDataSchema,
2681
2779
  });
2780
+ export const SessionRequestEventSchema = z.strictObject({
2781
+ v: z.literal(1),
2782
+ id: UuidSchema,
2783
+ type: z.literal('agent.request'),
2784
+ created_at: TimestampSchema,
2785
+ org: z.strictObject({ id: UuidSchema }),
2786
+ app: z.strictObject({ id: UuidSchema, installation_id: UuidSchema }),
2787
+ attempt: z.int().min(1),
2788
+ session: SessionBriefSchema,
2789
+ data: SessionRequestDataSchema,
2790
+ });
2682
2791
  export const SessionCardClosedEventSchema = z.strictObject({
2683
2792
  v: z.literal(1),
2684
2793
  id: UuidSchema,
@@ -2698,6 +2807,7 @@ export const SessionEventSchema = z.union([
2698
2807
  SessionItemAssignedEventSchema,
2699
2808
  SessionItemUnassignedEventSchema,
2700
2809
  SessionChildDoneEventSchema,
2810
+ SessionRequestEventSchema,
2701
2811
  ]);
2702
2812
  export const SessionEventsResponseSchema = z.strictObject({
2703
2813
  events: z.array(SessionEventSchema),
@@ -2758,6 +2868,7 @@ export const QUERY_SCHEMAS = {
2758
2868
  get_workflow: z.strictObject({}),
2759
2869
  list_channel_messages: z.strictObject({
2760
2870
  since: TimestampSchema.optional(),
2871
+ active_since: TimestampSchema.optional(),
2761
2872
  cursor: CursorSchema.optional(),
2762
2873
  limit: z.coerce.number().int().min(1).max(100).optional(),
2763
2874
  }),
@@ -2768,6 +2879,7 @@ export const QUERY_SCHEMAS = {
2768
2879
  }),
2769
2880
  list_comments: z.strictObject({
2770
2881
  order: z.enum(['asc', 'desc']).optional(),
2882
+ since: TimestampSchema.optional(),
2771
2883
  cursor: CursorSchema.optional(),
2772
2884
  limit: z.coerce.number().int().min(1).max(100).optional(),
2773
2885
  }),
@@ -13,6 +13,11 @@
13
13
  * `mention` (the owner mentioned it in the item's thread), `message` (the
14
14
  * owner's other replies in a thread it works in) and `item_unassigned`
15
15
  * (stop: it also stops "is writing" and the waiters of that thread).
16
+ * A request (`agent.request`, requests.ts) becomes `event="request"`, with
17
+ * its kind, context ids and the owner's language and time zone as
18
+ * attributes and a lead line written here from structured fields; only
19
+ * this session's, only the owner's, and once per request (a hand-on to
20
+ * another session writes a new event for the same request).
16
21
  * Session handoffs (handoffs.ts): an assignment that came by handoff is an
17
22
  * `item_assigned` with `handoff="true"`; `work_item.child_done` becomes
18
23
  * `child_done` (a subtask this session handed on is in review or done, was
@@ -51,6 +56,7 @@ import { ackSessionEvents, getSession, listSessionEvents, listSessionThreads, SE
51
56
  import { apiApprovalCloser } from './approvals.js';
52
57
  import { attachmentsBlock, forwardedFilesLead } from './attachment-blocks.js';
53
58
  import { AttachmentStore, filesServed } from './attachments.js';
59
+ import { REQUEST_EVENT_TYPE, requestContent, } from './requests.js';
54
60
  import { ToolRing } from './tool-ring.js';
55
61
  import { CardWaiters, eventMessageId } from './waiters.js';
56
62
  /** The notification Claude Code reads as a channel event. */
@@ -63,6 +69,8 @@ const ENDED_NOTICES = {
63
69
  };
64
70
  /** How many item keys are remembered (key to id). */
65
71
  const ITEMS_MAX = 500;
72
+ /** How many requests are remembered (id to kind and place), for reply's check. */
73
+ const REQUESTS_MAX = 200;
66
74
  /**
67
75
  * What the loop and the channel tools (reply, ask, the plan cards) share:
68
76
  * the session, the thread of the latest message, the items seen in events,
@@ -129,6 +137,19 @@ export class ChannelState {
129
137
  : [{ thread_id: this.lastDmThread, item: undefined }]),
130
138
  ];
131
139
  }
140
+ /** The requests this session was sent, by id (the oldest forgotten past REQUESTS_MAX). */
141
+ requests = new Map();
142
+ /** Remembers a request's kind and where its result goes. */
143
+ rememberRequest(id, request) {
144
+ const k = id.toLowerCase();
145
+ this.requests.delete(k);
146
+ this.requests.set(k, request);
147
+ if (this.requests.size > REQUESTS_MAX) {
148
+ const oldest = this.requests.keys().next().value;
149
+ if (oldest !== undefined)
150
+ this.requests.delete(oldest);
151
+ }
152
+ }
132
153
  /** The owner's locale, from their latest event (the cards' language). */
133
154
  ownerLocale;
134
155
  /**
@@ -533,6 +554,10 @@ export class ChannelLoop {
533
554
  return;
534
555
  }
535
556
  this.seen.add(event.id);
557
+ if (event.type === REQUEST_EVENT_TYPE) {
558
+ await this.deliverRequest(event, info);
559
+ return;
560
+ }
536
561
  if (event.type === 'card.response' || event.type === 'card.closed') {
537
562
  await this.deliverCard(event, info);
538
563
  return;
@@ -614,6 +639,51 @@ export class ChannelLoop {
614
639
  this.options.state.typing?.start(threadId);
615
640
  logger.info('channel message delivered', { event_id: event.id, message_id: message.id });
616
641
  }
642
+ /**
643
+ * A request (agent.request): the owner's tap on a "... with my agent"
644
+ * button, or a subscription they set up. Dropped when it is another
645
+ * session's (other_session), not the owner's (not_owner: the server sends
646
+ * only the owner's, and this checks again), a request already handed on
647
+ * (repeat: by request id, since a hand-on writes a new event), or a shape
648
+ * this version can't read (shape).
649
+ */
650
+ async deliverRequest(event, info) {
651
+ const { logger, state } = this.options;
652
+ if (event.session.id !== info.session.id) {
653
+ logger.warn('channel event dropped', { event_id: event.id, reason: 'other_session' });
654
+ return;
655
+ }
656
+ const { requester, request } = event.data;
657
+ if (requester.id !== info.owner.id || requester.is_guest) {
658
+ logger.warn('channel event dropped', { event_id: event.id, reason: 'not_owner' });
659
+ return;
660
+ }
661
+ const seenKey = `r:${request.id.toLowerCase()}`;
662
+ if (this.seen.has(seenKey)) {
663
+ logger.debug('channel event dropped', { event_id: event.id, reason: 'repeat' });
664
+ return;
665
+ }
666
+ const built = requestContent(event.data, {
667
+ owner: info.owner.name,
668
+ session: event.session.label,
669
+ });
670
+ if (!built.ok) {
671
+ logger.warn('channel event dropped', { event_id: event.id, reason: built.reason });
672
+ return;
673
+ }
674
+ this.seen.add(seenKey);
675
+ this.noteLocale({ locale: built.meta.locale });
676
+ await this.send({ content: built.content, meta: built.meta });
677
+ state.rememberRequest(built.requestId, {
678
+ kind: request.kind,
679
+ place: built.meta.result,
680
+ });
681
+ logger.info('request delivered', {
682
+ event_id: event.id,
683
+ request_id: built.requestId,
684
+ kind: request.kind,
685
+ });
686
+ }
617
687
  /** Sends one notification; a failure stops the loop (the page is not acknowledged). */
618
688
  async send(params) {
619
689
  try {