@shadowob/shared 1.1.7 → 1.1.9

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.
@@ -8,24 +8,21 @@ var playTemplate = (id) => ({
8
8
  },
9
9
  materials: { cover: playCover(id) }
10
10
  });
11
- var communityAction = (channelName, greeting) => ({
11
+ var communityAction = (channelName) => ({
12
12
  kind: "public_channel",
13
13
  channelName,
14
- buddyTemplateSlug: channelName,
15
- ...greeting ? { greeting } : {}
14
+ buddyTemplateSlug: channelName
16
15
  });
17
- var roomAction = (namePrefix, greeting) => ({
16
+ var roomAction = (namePrefix) => ({
18
17
  kind: "private_room",
19
18
  namePrefix,
20
- buddyTemplateSlug: namePrefix,
21
- ...greeting ? { greeting } : {}
19
+ buddyTemplateSlug: namePrefix
22
20
  });
23
- var cloudAction = (templateSlug, resourceTier = "lightweight", defaultChannelName) => ({
21
+ var cloudAction = (templateSlug, resourceTier = "lightweight") => ({
24
22
  kind: "cloud_deploy",
25
23
  templateSlug,
26
24
  buddyTemplateSlug: templateSlug,
27
- resourceTier,
28
- ...defaultChannelName ? { defaultChannelName } : {}
25
+ resourceTier
29
26
  });
30
27
  function getPlayBuddyUsername(templateSlug) {
31
28
  const normalized = templateSlug.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 27) || "play";
@@ -34,55 +31,15 @@ function getPlayBuddyUsername(templateSlug) {
34
31
  function getPlayBuddyEmail(templateSlug) {
35
32
  return `${getPlayBuddyUsername(templateSlug)}@shadowob.bot`;
36
33
  }
37
- var SHADOW_PLAY_SERVER_TEMPLATE = {
38
- slug: "shadow-plays",
39
- name: "Shadow Plays",
40
- description: "Default public community space for launchable homepage plays.",
41
- channels: [
42
- {
43
- name: "general",
44
- topic: "General discussion for new players and Shadow community members."
45
- },
46
- {
47
- name: "world-pulse",
48
- topic: "A public room for real-time global events and daily signal."
49
- },
50
- {
51
- name: "financial-freedom",
52
- topic: "A public room for lightweight financial freedom simulations and planning prompts."
53
- },
54
- {
55
- name: "ai-werewolf",
56
- topic: "A public room for AI-hosted social deduction sessions."
57
- },
58
- {
59
- name: "code-arena",
60
- topic: "A public room for coding challenges and real-time battles."
61
- },
62
- {
63
- name: "brain-fix",
64
- topic: "A calm public room for one-minute focus resets and reflection."
65
- },
66
- {
67
- name: "gitstory",
68
- topic: "A public room for turning software history into stories and retrospectives."
69
- },
70
- {
71
- name: "gstack",
72
- topic: "A public room for founder strategy, product stress tests, and launch planning."
73
- }
74
- ]
75
- };
76
34
  function cloudPlay(id, input) {
77
- const { defaultChannelName, ...content } = input;
78
35
  return {
79
36
  id,
80
37
  image: playCover(id),
81
38
  status: "gated",
82
- action: cloudAction(id, "lightweight", defaultChannelName),
39
+ action: cloudAction(id, "lightweight"),
83
40
  gates: { auth: "required", membership: "required", profile: "optional" },
84
41
  ...playTemplate(id),
85
- ...content
42
+ ...input
86
43
  };
87
44
  }
88
45
  var DEFAULT_HOMEPLAY_CATALOG = [
@@ -246,7 +203,7 @@ var DEFAULT_HOMEPLAY_CATALOG = [
246
203
  accentColor: "#f59e0b",
247
204
  hot: true,
248
205
  status: "gated",
249
- action: cloudAction("little-match-girl", "lightweight", "match-street"),
206
+ action: cloudAction("little-match-girl", "lightweight"),
250
207
  gates: { auth: "required", membership: "required", profile: "optional" },
251
208
  ...playTemplate("little-match-girl"),
252
209
  materials: { cover: "/home-assets/topics/night-radio.jpg" }
@@ -260,8 +217,7 @@ var DEFAULT_HOMEPLAY_CATALOG = [
260
217
  categoryEn: "Buddy Teams",
261
218
  starts: "16.4k",
262
219
  accentColor: "#22d3ee",
263
- hot: true,
264
- defaultChannelName: "choose"
220
+ hot: true
265
221
  }),
266
222
  cloudPlay("bmad-method-buddy", {
267
223
  title: "BMAD \u65B9\u6CD5 Buddy",
@@ -271,8 +227,7 @@ var DEFAULT_HOMEPLAY_CATALOG = [
271
227
  category: "Buddy \u56E2\u961F",
272
228
  categoryEn: "Buddy Teams",
273
229
  starts: "13.7k",
274
- accentColor: "#60a5fa",
275
- defaultChannelName: "delivery"
230
+ accentColor: "#60a5fa"
276
231
  }),
277
232
  cloudPlay("claude-ads-buddy", {
278
233
  title: "Claude Ads Buddy",
@@ -335,8 +290,7 @@ var DEFAULT_HOMEPLAY_CATALOG = [
335
290
  categoryEn: "Hacker & Painter",
336
291
  starts: "15.1k",
337
292
  accentColor: "#fb923c",
338
- hot: true,
339
- defaultChannelName: "office-hours"
293
+ hot: true
340
294
  }),
341
295
  cloudPlay("marketingskills-buddy", {
342
296
  title: "\u8425\u9500\u6280\u80FD Buddy",
@@ -423,7 +377,6 @@ function getDefaultHomePlay(playId) {
423
377
  export {
424
378
  getPlayBuddyUsername,
425
379
  getPlayBuddyEmail,
426
- SHADOW_PLAY_SERVER_TEMPLATE,
427
380
  DEFAULT_HOMEPLAY_CATALOG,
428
381
  getDefaultHomePlay
429
382
  };
@@ -0,0 +1,175 @@
1
+ // src/types/inbox.types.ts
2
+ var BUDDY_INBOX_TOPIC_PREFIX = "shadow:buddy-inbox:";
3
+ function buddyInboxTopic(agentId) {
4
+ return `${BUDDY_INBOX_TOPIC_PREFIX}${agentId}`;
5
+ }
6
+ function parseBuddyInboxAgentId(topic) {
7
+ if (!topic?.startsWith(BUDDY_INBOX_TOPIC_PREFIX)) return null;
8
+ const agentId = topic.slice(BUDDY_INBOX_TOPIC_PREFIX.length).trim();
9
+ return agentId || null;
10
+ }
11
+ function isBuddyInboxTopic(topic) {
12
+ return parseBuddyInboxAgentId(topic) !== null;
13
+ }
14
+ var TASK_MESSAGE_CARD_STATUSES = [
15
+ "queued",
16
+ "claimed",
17
+ "running",
18
+ "completed",
19
+ "failed",
20
+ "canceled",
21
+ "transferred"
22
+ ];
23
+ var TERMINAL_TASK_MESSAGE_CARD_STATUSES = [
24
+ "completed",
25
+ "failed",
26
+ "canceled",
27
+ "transferred"
28
+ ];
29
+ var TASK_MESSAGE_CARD_STATUS_TRANSITIONS = {
30
+ queued: ["queued", "claimed", "running", "completed", "failed", "canceled"],
31
+ claimed: ["claimed", "running", "completed", "failed", "canceled"],
32
+ running: ["running", "completed", "failed", "canceled"],
33
+ completed: ["completed"],
34
+ failed: ["failed", "transferred"],
35
+ canceled: ["canceled"],
36
+ transferred: ["transferred"]
37
+ };
38
+ function isTerminalTaskMessageCardStatus(status) {
39
+ return TERMINAL_TASK_MESSAGE_CARD_STATUSES.includes(
40
+ status
41
+ );
42
+ }
43
+ function canTransitionTaskMessageCardStatus(from, to) {
44
+ const allowed = TASK_MESSAGE_CARD_STATUS_TRANSITIONS[from];
45
+ return allowed.includes(to);
46
+ }
47
+ var DEFAULT_BUDDY_INBOX_ADMISSION_POLICY = {
48
+ defaultMode: "allow",
49
+ rules: []
50
+ };
51
+ function isRecord(value) {
52
+ return !!value && typeof value === "object" && !Array.isArray(value);
53
+ }
54
+ function parseAdmissionMode(value, fallback) {
55
+ if (value === "allow" || value === "deny" || value === "first_time" || value === "every_time") {
56
+ return value;
57
+ }
58
+ if (value === void 0 || value === null) return fallback;
59
+ throw new Error("Invalid Buddy Inbox admission mode");
60
+ }
61
+ function parseSubjectKind(value) {
62
+ if (value === "user" || value === "agent" || value === "server_app" || value === "system") {
63
+ return value;
64
+ }
65
+ throw new Error("Invalid Buddy Inbox admission subject kind");
66
+ }
67
+ function parseOptionalString(value, field, maxLength) {
68
+ if (value === void 0 || value === null || value === "") return void 0;
69
+ if (typeof value !== "string" || value.length > maxLength) {
70
+ throw new Error(`Invalid Buddy Inbox admission ${field}`);
71
+ }
72
+ return value;
73
+ }
74
+ function normalizeBuddyInboxAdmissionPolicy(value) {
75
+ if (value === void 0 || value === null) return { ...DEFAULT_BUDDY_INBOX_ADMISSION_POLICY };
76
+ if (!isRecord(value)) throw new Error("Invalid Buddy Inbox admission policy");
77
+ const defaultMode = parseAdmissionMode(value.defaultMode, "allow");
78
+ const rawRules = value.rules;
79
+ if (rawRules !== void 0 && !Array.isArray(rawRules)) {
80
+ throw new Error("Invalid Buddy Inbox admission rules");
81
+ }
82
+ const rules = (rawRules ?? []).slice(0, 100).map((entry) => {
83
+ if (!isRecord(entry)) throw new Error("Invalid Buddy Inbox admission rule");
84
+ return {
85
+ subjectKind: parseSubjectKind(entry.subjectKind),
86
+ subjectId: parseOptionalString(entry.subjectId, "subjectId", 160),
87
+ appKey: parseOptionalString(entry.appKey, "appKey", 120),
88
+ mode: parseAdmissionMode(entry.mode, defaultMode),
89
+ ...entry.approved === true ? { approved: true } : {},
90
+ note: parseOptionalString(entry.note, "note", 500),
91
+ createdAt: parseOptionalString(entry.createdAt, "createdAt", 64),
92
+ updatedAt: parseOptionalString(entry.updatedAt, "updatedAt", 64)
93
+ };
94
+ });
95
+ return { defaultMode, rules };
96
+ }
97
+ function buddyInboxAdmissionRuleKey(rule) {
98
+ return [rule.subjectKind, rule.subjectId ?? "", rule.appKey ?? ""].join(":");
99
+ }
100
+ function parsePendingTask(value) {
101
+ if (!isRecord(value)) throw new Error("Invalid Buddy Inbox pending task");
102
+ const title = parseOptionalString(value.title, "task.title", 180);
103
+ if (!title) throw new Error("Invalid Buddy Inbox pending task title");
104
+ const body = parseOptionalString(value.body, "task.body", 8e3);
105
+ const priority = value.priority;
106
+ if (priority !== void 0 && priority !== "low" && priority !== "normal" && priority !== "high" && priority !== "urgent") {
107
+ throw new Error("Invalid Buddy Inbox pending task priority");
108
+ }
109
+ const idempotencyKey = parseOptionalString(value.idempotencyKey, "task.idempotencyKey", 240);
110
+ const source = isRecord(value.source) ? value.source : void 0;
111
+ const data = isRecord(value.data) ? value.data : void 0;
112
+ return {
113
+ title,
114
+ ...body ? { body } : {},
115
+ ...priority ? { priority } : {},
116
+ ...idempotencyKey ? { idempotencyKey } : {},
117
+ ...source ? { source } : {},
118
+ ...data ? { data } : {}
119
+ };
120
+ }
121
+ function normalizeBuddyInboxAdmissionPendingDeliveries(value) {
122
+ if (value === void 0 || value === null) return [];
123
+ if (!Array.isArray(value)) throw new Error("Invalid Buddy Inbox pending deliveries");
124
+ return value.slice(0, 100).map((entry) => {
125
+ if (!isRecord(entry)) throw new Error("Invalid Buddy Inbox pending delivery");
126
+ const id = parseOptionalString(entry.id, "pending.id", 80);
127
+ const serverId = parseOptionalString(entry.serverId, "pending.serverId", 160);
128
+ const channelId = parseOptionalString(entry.channelId, "pending.channelId", 160);
129
+ const agentId = parseOptionalString(entry.agentId, "pending.agentId", 160);
130
+ const mode = parseAdmissionMode(entry.mode, "first_time");
131
+ if (mode !== "first_time" && mode !== "every_time") {
132
+ throw new Error("Invalid Buddy Inbox pending mode");
133
+ }
134
+ if (!isRecord(entry.subject)) throw new Error("Invalid Buddy Inbox pending subject");
135
+ if (!isRecord(entry.requestedBy)) throw new Error("Invalid Buddy Inbox pending requester");
136
+ if (!id || !serverId || !channelId || !agentId) {
137
+ throw new Error("Invalid Buddy Inbox pending delivery identifiers");
138
+ }
139
+ const requestedAt = parseOptionalString(entry.requestedAt, "pending.requestedAt", 64);
140
+ if (!requestedAt) throw new Error("Invalid Buddy Inbox pending requestedAt");
141
+ return {
142
+ id,
143
+ serverId,
144
+ channelId,
145
+ agentId,
146
+ mode,
147
+ subject: {
148
+ kind: parseSubjectKind(entry.subject.kind),
149
+ id: parseOptionalString(entry.subject.id, "subject.id", 160),
150
+ appKey: parseOptionalString(entry.subject.appKey, "subject.appKey", 120),
151
+ label: parseOptionalString(entry.subject.label, "subject.label", 160)
152
+ },
153
+ task: parsePendingTask(entry.task),
154
+ requestedBy: entry.requestedBy,
155
+ requestedAt,
156
+ updatedAt: parseOptionalString(entry.updatedAt, "pending.updatedAt", 64)
157
+ };
158
+ });
159
+ }
160
+
161
+ export {
162
+ BUDDY_INBOX_TOPIC_PREFIX,
163
+ buddyInboxTopic,
164
+ parseBuddyInboxAgentId,
165
+ isBuddyInboxTopic,
166
+ TASK_MESSAGE_CARD_STATUSES,
167
+ TERMINAL_TASK_MESSAGE_CARD_STATUSES,
168
+ TASK_MESSAGE_CARD_STATUS_TRANSITIONS,
169
+ isTerminalTaskMessageCardStatus,
170
+ canTransitionTaskMessageCardStatus,
171
+ DEFAULT_BUDDY_INBOX_ADMISSION_POLICY,
172
+ normalizeBuddyInboxAdmissionPolicy,
173
+ buddyInboxAdmissionRuleKey,
174
+ normalizeBuddyInboxAdmissionPendingDeliveries
175
+ };
package/dist/index.cjs CHANGED
@@ -20,14 +20,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ BUDDY_INBOX_TOPIC_PREFIX: () => BUDDY_INBOX_TOPIC_PREFIX,
23
24
  CAT_AVATAR_COUNT: () => CAT_AVATAR_COUNT,
24
25
  CLIENT_EVENTS: () => CLIENT_EVENTS,
26
+ DEFAULT_BUDDY_INBOX_ADMISSION_POLICY: () => DEFAULT_BUDDY_INBOX_ADMISSION_POLICY,
25
27
  DEFAULT_HOMEPLAY_CATALOG: () => DEFAULT_HOMEPLAY_CATALOG,
26
28
  LIMITS: () => LIMITS,
27
29
  SERVER_EVENTS: () => SERVER_EVENTS,
28
- SHADOW_PLAY_SERVER_TEMPLATE: () => SHADOW_PLAY_SERVER_TEMPLATE,
30
+ TASK_MESSAGE_CARD_STATUSES: () => TASK_MESSAGE_CARD_STATUSES,
31
+ TASK_MESSAGE_CARD_STATUS_TRANSITIONS: () => TASK_MESSAGE_CARD_STATUS_TRANSITIONS,
32
+ TERMINAL_TASK_MESSAGE_CARD_STATUSES: () => TERMINAL_TASK_MESSAGE_CARD_STATUSES,
29
33
  assignMentionRanges: () => assignMentionRanges,
34
+ buddyInboxAdmissionRuleKey: () => buddyInboxAdmissionRuleKey,
35
+ buddyInboxTopic: () => buddyInboxTopic,
30
36
  buildMentionMarkdownLinks: () => buildMentionMarkdownLinks,
37
+ canTransitionTaskMessageCardStatus: () => canTransitionTaskMessageCardStatus,
31
38
  canonicalMentionToken: () => canonicalMentionToken,
32
39
  canonicalizeMentionContent: () => canonicalizeMentionContent,
33
40
  escapeMarkdownLinkLabel: () => escapeMarkdownLinkLabel,
@@ -41,9 +48,14 @@ __export(index_exports, {
41
48
  getDefaultHomePlay: () => getDefaultHomePlay,
42
49
  getPlayBuddyEmail: () => getPlayBuddyEmail,
43
50
  getPlayBuddyUsername: () => getPlayBuddyUsername,
51
+ isBuddyInboxTopic: () => isBuddyInboxTopic,
44
52
  isCanonicalMentionToken: () => isCanonicalMentionToken,
53
+ isTerminalTaskMessageCardStatus: () => isTerminalTaskMessageCardStatus,
45
54
  isValidEmail: () => isValidEmail,
46
55
  mentionDisplayText: () => mentionDisplayText,
56
+ normalizeBuddyInboxAdmissionPendingDeliveries: () => normalizeBuddyInboxAdmissionPendingDeliveries,
57
+ normalizeBuddyInboxAdmissionPolicy: () => normalizeBuddyInboxAdmissionPolicy,
58
+ parseBuddyInboxAgentId: () => parseBuddyInboxAgentId,
47
59
  parseCanonicalMentionToken: () => parseCanonicalMentionToken,
48
60
  renderCatSvg: () => renderCatSvg,
49
61
  segmentTextByMentions: () => segmentTextByMentions,
@@ -124,24 +136,21 @@ var playTemplate = (id) => ({
124
136
  },
125
137
  materials: { cover: playCover(id) }
126
138
  });
127
- var communityAction = (channelName, greeting) => ({
139
+ var communityAction = (channelName) => ({
128
140
  kind: "public_channel",
129
141
  channelName,
130
- buddyTemplateSlug: channelName,
131
- ...greeting ? { greeting } : {}
142
+ buddyTemplateSlug: channelName
132
143
  });
133
- var roomAction = (namePrefix, greeting) => ({
144
+ var roomAction = (namePrefix) => ({
134
145
  kind: "private_room",
135
146
  namePrefix,
136
- buddyTemplateSlug: namePrefix,
137
- ...greeting ? { greeting } : {}
147
+ buddyTemplateSlug: namePrefix
138
148
  });
139
- var cloudAction = (templateSlug, resourceTier = "lightweight", defaultChannelName) => ({
149
+ var cloudAction = (templateSlug, resourceTier = "lightweight") => ({
140
150
  kind: "cloud_deploy",
141
151
  templateSlug,
142
152
  buddyTemplateSlug: templateSlug,
143
- resourceTier,
144
- ...defaultChannelName ? { defaultChannelName } : {}
153
+ resourceTier
145
154
  });
146
155
  function getPlayBuddyUsername(templateSlug) {
147
156
  const normalized = templateSlug.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 27) || "play";
@@ -150,55 +159,15 @@ function getPlayBuddyUsername(templateSlug) {
150
159
  function getPlayBuddyEmail(templateSlug) {
151
160
  return `${getPlayBuddyUsername(templateSlug)}@shadowob.bot`;
152
161
  }
153
- var SHADOW_PLAY_SERVER_TEMPLATE = {
154
- slug: "shadow-plays",
155
- name: "Shadow Plays",
156
- description: "Default public community space for launchable homepage plays.",
157
- channels: [
158
- {
159
- name: "general",
160
- topic: "General discussion for new players and Shadow community members."
161
- },
162
- {
163
- name: "world-pulse",
164
- topic: "A public room for real-time global events and daily signal."
165
- },
166
- {
167
- name: "financial-freedom",
168
- topic: "A public room for lightweight financial freedom simulations and planning prompts."
169
- },
170
- {
171
- name: "ai-werewolf",
172
- topic: "A public room for AI-hosted social deduction sessions."
173
- },
174
- {
175
- name: "code-arena",
176
- topic: "A public room for coding challenges and real-time battles."
177
- },
178
- {
179
- name: "brain-fix",
180
- topic: "A calm public room for one-minute focus resets and reflection."
181
- },
182
- {
183
- name: "gitstory",
184
- topic: "A public room for turning software history into stories and retrospectives."
185
- },
186
- {
187
- name: "gstack",
188
- topic: "A public room for founder strategy, product stress tests, and launch planning."
189
- }
190
- ]
191
- };
192
162
  function cloudPlay(id, input) {
193
- const { defaultChannelName, ...content } = input;
194
163
  return {
195
164
  id,
196
165
  image: playCover(id),
197
166
  status: "gated",
198
- action: cloudAction(id, "lightweight", defaultChannelName),
167
+ action: cloudAction(id, "lightweight"),
199
168
  gates: { auth: "required", membership: "required", profile: "optional" },
200
169
  ...playTemplate(id),
201
- ...content
170
+ ...input
202
171
  };
203
172
  }
204
173
  var DEFAULT_HOMEPLAY_CATALOG = [
@@ -362,7 +331,7 @@ var DEFAULT_HOMEPLAY_CATALOG = [
362
331
  accentColor: "#f59e0b",
363
332
  hot: true,
364
333
  status: "gated",
365
- action: cloudAction("little-match-girl", "lightweight", "match-street"),
334
+ action: cloudAction("little-match-girl", "lightweight"),
366
335
  gates: { auth: "required", membership: "required", profile: "optional" },
367
336
  ...playTemplate("little-match-girl"),
368
337
  materials: { cover: "/home-assets/topics/night-radio.jpg" }
@@ -376,8 +345,7 @@ var DEFAULT_HOMEPLAY_CATALOG = [
376
345
  categoryEn: "Buddy Teams",
377
346
  starts: "16.4k",
378
347
  accentColor: "#22d3ee",
379
- hot: true,
380
- defaultChannelName: "choose"
348
+ hot: true
381
349
  }),
382
350
  cloudPlay("bmad-method-buddy", {
383
351
  title: "BMAD \u65B9\u6CD5 Buddy",
@@ -387,8 +355,7 @@ var DEFAULT_HOMEPLAY_CATALOG = [
387
355
  category: "Buddy \u56E2\u961F",
388
356
  categoryEn: "Buddy Teams",
389
357
  starts: "13.7k",
390
- accentColor: "#60a5fa",
391
- defaultChannelName: "delivery"
358
+ accentColor: "#60a5fa"
392
359
  }),
393
360
  cloudPlay("claude-ads-buddy", {
394
361
  title: "Claude Ads Buddy",
@@ -451,8 +418,7 @@ var DEFAULT_HOMEPLAY_CATALOG = [
451
418
  categoryEn: "Hacker & Painter",
452
419
  starts: "15.1k",
453
420
  accentColor: "#fb923c",
454
- hot: true,
455
- defaultChannelName: "office-hours"
421
+ hot: true
456
422
  }),
457
423
  cloudPlay("marketingskills-buddy", {
458
424
  title: "\u8425\u9500\u6280\u80FD Buddy",
@@ -536,6 +502,166 @@ function getDefaultHomePlay(playId) {
536
502
  return DEFAULT_HOMEPLAY_CATALOG.find((play) => play.id === playId) ?? null;
537
503
  }
538
504
 
505
+ // src/types/inbox.types.ts
506
+ var BUDDY_INBOX_TOPIC_PREFIX = "shadow:buddy-inbox:";
507
+ function buddyInboxTopic(agentId) {
508
+ return `${BUDDY_INBOX_TOPIC_PREFIX}${agentId}`;
509
+ }
510
+ function parseBuddyInboxAgentId(topic) {
511
+ if (!topic?.startsWith(BUDDY_INBOX_TOPIC_PREFIX)) return null;
512
+ const agentId = topic.slice(BUDDY_INBOX_TOPIC_PREFIX.length).trim();
513
+ return agentId || null;
514
+ }
515
+ function isBuddyInboxTopic(topic) {
516
+ return parseBuddyInboxAgentId(topic) !== null;
517
+ }
518
+ var TASK_MESSAGE_CARD_STATUSES = [
519
+ "queued",
520
+ "claimed",
521
+ "running",
522
+ "completed",
523
+ "failed",
524
+ "canceled",
525
+ "transferred"
526
+ ];
527
+ var TERMINAL_TASK_MESSAGE_CARD_STATUSES = [
528
+ "completed",
529
+ "failed",
530
+ "canceled",
531
+ "transferred"
532
+ ];
533
+ var TASK_MESSAGE_CARD_STATUS_TRANSITIONS = {
534
+ queued: ["queued", "claimed", "running", "completed", "failed", "canceled"],
535
+ claimed: ["claimed", "running", "completed", "failed", "canceled"],
536
+ running: ["running", "completed", "failed", "canceled"],
537
+ completed: ["completed"],
538
+ failed: ["failed", "transferred"],
539
+ canceled: ["canceled"],
540
+ transferred: ["transferred"]
541
+ };
542
+ function isTerminalTaskMessageCardStatus(status) {
543
+ return TERMINAL_TASK_MESSAGE_CARD_STATUSES.includes(
544
+ status
545
+ );
546
+ }
547
+ function canTransitionTaskMessageCardStatus(from, to) {
548
+ const allowed = TASK_MESSAGE_CARD_STATUS_TRANSITIONS[from];
549
+ return allowed.includes(to);
550
+ }
551
+ var DEFAULT_BUDDY_INBOX_ADMISSION_POLICY = {
552
+ defaultMode: "allow",
553
+ rules: []
554
+ };
555
+ function isRecord(value) {
556
+ return !!value && typeof value === "object" && !Array.isArray(value);
557
+ }
558
+ function parseAdmissionMode(value, fallback) {
559
+ if (value === "allow" || value === "deny" || value === "first_time" || value === "every_time") {
560
+ return value;
561
+ }
562
+ if (value === void 0 || value === null) return fallback;
563
+ throw new Error("Invalid Buddy Inbox admission mode");
564
+ }
565
+ function parseSubjectKind(value) {
566
+ if (value === "user" || value === "agent" || value === "server_app" || value === "system") {
567
+ return value;
568
+ }
569
+ throw new Error("Invalid Buddy Inbox admission subject kind");
570
+ }
571
+ function parseOptionalString(value, field, maxLength) {
572
+ if (value === void 0 || value === null || value === "") return void 0;
573
+ if (typeof value !== "string" || value.length > maxLength) {
574
+ throw new Error(`Invalid Buddy Inbox admission ${field}`);
575
+ }
576
+ return value;
577
+ }
578
+ function normalizeBuddyInboxAdmissionPolicy(value) {
579
+ if (value === void 0 || value === null) return { ...DEFAULT_BUDDY_INBOX_ADMISSION_POLICY };
580
+ if (!isRecord(value)) throw new Error("Invalid Buddy Inbox admission policy");
581
+ const defaultMode = parseAdmissionMode(value.defaultMode, "allow");
582
+ const rawRules = value.rules;
583
+ if (rawRules !== void 0 && !Array.isArray(rawRules)) {
584
+ throw new Error("Invalid Buddy Inbox admission rules");
585
+ }
586
+ const rules = (rawRules ?? []).slice(0, 100).map((entry) => {
587
+ if (!isRecord(entry)) throw new Error("Invalid Buddy Inbox admission rule");
588
+ return {
589
+ subjectKind: parseSubjectKind(entry.subjectKind),
590
+ subjectId: parseOptionalString(entry.subjectId, "subjectId", 160),
591
+ appKey: parseOptionalString(entry.appKey, "appKey", 120),
592
+ mode: parseAdmissionMode(entry.mode, defaultMode),
593
+ ...entry.approved === true ? { approved: true } : {},
594
+ note: parseOptionalString(entry.note, "note", 500),
595
+ createdAt: parseOptionalString(entry.createdAt, "createdAt", 64),
596
+ updatedAt: parseOptionalString(entry.updatedAt, "updatedAt", 64)
597
+ };
598
+ });
599
+ return { defaultMode, rules };
600
+ }
601
+ function buddyInboxAdmissionRuleKey(rule) {
602
+ return [rule.subjectKind, rule.subjectId ?? "", rule.appKey ?? ""].join(":");
603
+ }
604
+ function parsePendingTask(value) {
605
+ if (!isRecord(value)) throw new Error("Invalid Buddy Inbox pending task");
606
+ const title = parseOptionalString(value.title, "task.title", 180);
607
+ if (!title) throw new Error("Invalid Buddy Inbox pending task title");
608
+ const body = parseOptionalString(value.body, "task.body", 8e3);
609
+ const priority = value.priority;
610
+ if (priority !== void 0 && priority !== "low" && priority !== "normal" && priority !== "high" && priority !== "urgent") {
611
+ throw new Error("Invalid Buddy Inbox pending task priority");
612
+ }
613
+ const idempotencyKey = parseOptionalString(value.idempotencyKey, "task.idempotencyKey", 240);
614
+ const source = isRecord(value.source) ? value.source : void 0;
615
+ const data = isRecord(value.data) ? value.data : void 0;
616
+ return {
617
+ title,
618
+ ...body ? { body } : {},
619
+ ...priority ? { priority } : {},
620
+ ...idempotencyKey ? { idempotencyKey } : {},
621
+ ...source ? { source } : {},
622
+ ...data ? { data } : {}
623
+ };
624
+ }
625
+ function normalizeBuddyInboxAdmissionPendingDeliveries(value) {
626
+ if (value === void 0 || value === null) return [];
627
+ if (!Array.isArray(value)) throw new Error("Invalid Buddy Inbox pending deliveries");
628
+ return value.slice(0, 100).map((entry) => {
629
+ if (!isRecord(entry)) throw new Error("Invalid Buddy Inbox pending delivery");
630
+ const id = parseOptionalString(entry.id, "pending.id", 80);
631
+ const serverId = parseOptionalString(entry.serverId, "pending.serverId", 160);
632
+ const channelId = parseOptionalString(entry.channelId, "pending.channelId", 160);
633
+ const agentId = parseOptionalString(entry.agentId, "pending.agentId", 160);
634
+ const mode = parseAdmissionMode(entry.mode, "first_time");
635
+ if (mode !== "first_time" && mode !== "every_time") {
636
+ throw new Error("Invalid Buddy Inbox pending mode");
637
+ }
638
+ if (!isRecord(entry.subject)) throw new Error("Invalid Buddy Inbox pending subject");
639
+ if (!isRecord(entry.requestedBy)) throw new Error("Invalid Buddy Inbox pending requester");
640
+ if (!id || !serverId || !channelId || !agentId) {
641
+ throw new Error("Invalid Buddy Inbox pending delivery identifiers");
642
+ }
643
+ const requestedAt = parseOptionalString(entry.requestedAt, "pending.requestedAt", 64);
644
+ if (!requestedAt) throw new Error("Invalid Buddy Inbox pending requestedAt");
645
+ return {
646
+ id,
647
+ serverId,
648
+ channelId,
649
+ agentId,
650
+ mode,
651
+ subject: {
652
+ kind: parseSubjectKind(entry.subject.kind),
653
+ id: parseOptionalString(entry.subject.id, "subject.id", 160),
654
+ appKey: parseOptionalString(entry.subject.appKey, "subject.appKey", 120),
655
+ label: parseOptionalString(entry.subject.label, "subject.label", 160)
656
+ },
657
+ task: parsePendingTask(entry.task),
658
+ requestedBy: entry.requestedBy,
659
+ requestedAt,
660
+ updatedAt: parseOptionalString(entry.updatedAt, "pending.updatedAt", 64)
661
+ };
662
+ });
663
+ }
664
+
539
665
  // src/utils/index.ts
540
666
  var import_nanoid = require("nanoid");
541
667
 
@@ -1039,14 +1165,21 @@ function slugify(text) {
1039
1165
  }
1040
1166
  // Annotate the CommonJS export names for ESM import in node:
1041
1167
  0 && (module.exports = {
1168
+ BUDDY_INBOX_TOPIC_PREFIX,
1042
1169
  CAT_AVATAR_COUNT,
1043
1170
  CLIENT_EVENTS,
1171
+ DEFAULT_BUDDY_INBOX_ADMISSION_POLICY,
1044
1172
  DEFAULT_HOMEPLAY_CATALOG,
1045
1173
  LIMITS,
1046
1174
  SERVER_EVENTS,
1047
- SHADOW_PLAY_SERVER_TEMPLATE,
1175
+ TASK_MESSAGE_CARD_STATUSES,
1176
+ TASK_MESSAGE_CARD_STATUS_TRANSITIONS,
1177
+ TERMINAL_TASK_MESSAGE_CARD_STATUSES,
1048
1178
  assignMentionRanges,
1179
+ buddyInboxAdmissionRuleKey,
1180
+ buddyInboxTopic,
1049
1181
  buildMentionMarkdownLinks,
1182
+ canTransitionTaskMessageCardStatus,
1050
1183
  canonicalMentionToken,
1051
1184
  canonicalizeMentionContent,
1052
1185
  escapeMarkdownLinkLabel,
@@ -1060,9 +1193,14 @@ function slugify(text) {
1060
1193
  getDefaultHomePlay,
1061
1194
  getPlayBuddyEmail,
1062
1195
  getPlayBuddyUsername,
1196
+ isBuddyInboxTopic,
1063
1197
  isCanonicalMentionToken,
1198
+ isTerminalTaskMessageCardStatus,
1064
1199
  isValidEmail,
1065
1200
  mentionDisplayText,
1201
+ normalizeBuddyInboxAdmissionPendingDeliveries,
1202
+ normalizeBuddyInboxAdmissionPolicy,
1203
+ parseBuddyInboxAgentId,
1066
1204
  parseCanonicalMentionToken,
1067
1205
  renderCatSvg,
1068
1206
  segmentTextByMentions,