sparkecoder 0.1.125 → 0.1.127

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 (113) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +52 -3
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +118 -17
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +2 -2
  7. package/dist/{index-DczYH89U.d.ts → index-Bcz0aCAR.d.ts} +104 -104
  8. package/dist/index.d.ts +5 -5
  9. package/dist/index.js +118 -17
  10. package/dist/index.js.map +1 -1
  11. package/dist/{schema-DxrKyetI.d.ts → schema-BWbWmfDQ.d.ts} +3 -3
  12. package/dist/{search-CVVfuBPZ.d.ts → search-DOzC4ojH.d.ts} +4 -4
  13. package/dist/server/index.js +118 -17
  14. package/dist/server/index.js.map +1 -1
  15. package/dist/skills/default/slack-messaging.md +268 -0
  16. package/dist/tools/index.d.ts +3 -3
  17. package/dist/tools/index.js +3 -1
  18. package/dist/tools/index.js.map +1 -1
  19. package/package.json +1 -1
  20. package/src/skills/default/slack-messaging.md +268 -0
  21. package/web/.next/BUILD_ID +1 -1
  22. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  23. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  24. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  25. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  26. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
  41. package/web/.next/standalone/web/.next/server/app/agents.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +1 -1
  49. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  50. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
  60. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  67. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  68. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
  69. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
  70. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  76. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  77. package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
  78. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
  79. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  83. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  84. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  85. package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
  86. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  87. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  88. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
  89. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  90. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  91. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  92. package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
  93. package/web/.next/standalone/web/.next/server/app/settings.rsc +1 -1
  94. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +1 -1
  95. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
  96. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
  97. package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +1 -1
  98. package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  99. package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  100. package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  101. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  102. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  103. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  104. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  105. /package/web/.next/standalone/web/.next/static/{qtMOCCjmqN22PUb49g4j- → l63ZK5juWEj8lzzuVVSpx}/_buildManifest.js +0 -0
  106. /package/web/.next/standalone/web/.next/static/{qtMOCCjmqN22PUb49g4j- → l63ZK5juWEj8lzzuVVSpx}/_clientMiddlewareManifest.json +0 -0
  107. /package/web/.next/standalone/web/.next/static/{qtMOCCjmqN22PUb49g4j- → l63ZK5juWEj8lzzuVVSpx}/_ssgManifest.js +0 -0
  108. /package/web/.next/standalone/web/.next/static/static/{qtMOCCjmqN22PUb49g4j- → l63ZK5juWEj8lzzuVVSpx}/_buildManifest.js +0 -0
  109. /package/web/.next/standalone/web/.next/static/static/{qtMOCCjmqN22PUb49g4j- → l63ZK5juWEj8lzzuVVSpx}/_clientMiddlewareManifest.json +0 -0
  110. /package/web/.next/standalone/web/.next/static/static/{qtMOCCjmqN22PUb49g4j- → l63ZK5juWEj8lzzuVVSpx}/_ssgManifest.js +0 -0
  111. /package/web/.next/static/{qtMOCCjmqN22PUb49g4j- → l63ZK5juWEj8lzzuVVSpx}/_buildManifest.js +0 -0
  112. /package/web/.next/static/{qtMOCCjmqN22PUb49g4j- → l63ZK5juWEj8lzzuVVSpx}/_clientMiddlewareManifest.json +0 -0
  113. /package/web/.next/static/{qtMOCCjmqN22PUb49g4j- → l63ZK5juWEj8lzzuVVSpx}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -4240,8 +4240,10 @@ async function loadAgentsMd(agentsMdPath) {
4240
4240
  }
4241
4241
  async function loadSkillContent(skillName, directories) {
4242
4242
  const allSkills = await loadAllSkills(directories);
4243
+ const normalize2 = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
4244
+ const wanted = normalize2(skillName);
4243
4245
  const skill = allSkills.find(
4244
- (s) => s.name.toLowerCase() === skillName.toLowerCase()
4246
+ (s) => normalize2(s.name) === wanted || normalize2(basename(s.filePath, extname4(s.filePath))) === wanted
4245
4247
  );
4246
4248
  if (!skill) {
4247
4249
  return null;
@@ -7781,7 +7783,7 @@ Every user-message you see is tagged at the front with a channel pill describing
7781
7783
  Pill formats:
7782
7784
 
7783
7785
  - \`[WEB] ...\` \u2014 typed in the web dashboard. Your assistant text streams back automatically. No tool call needed.
7784
- - \`[SLACK channel=C0123 thread=1700000000.001 user=U0123] ...\` \u2014 a Slack mention or DM. To reply: \`messenger({action:'post', channel:'slack', to:'C0123', threadTs:'1700000000.001', text:'...'})\`. **Always set \`threadTs\`** so the reply lands in the same thread.
7786
+ - \`[SLACK channel=C0123 thread=1700000000.001 user=Ryan Trattner <@U0123> (ryan@example.com)] ...\` \u2014 a Slack mention or DM. To reply: \`messenger({action:'post', channel:'slack', to:'C0123', threadTs:'1700000000.001', text:'...'})\`. **Always set \`threadTs\`** so the reply lands in the same thread. The \`user=\` field is enriched in-place: when the bot has the right scopes you get \`<Name> <@U_ID> (email)\`; if scopes are missing you may see just the raw id \u2014 that's not an error, just less context. Any \`<@U\u2026>\` mentions inside the message text are similarly normalized to \`<Name> <@U_ID>\` so you can immediately tell who's being addressed.
7785
7787
  - \`[SYSTEM worker.completed worker-name] ...\` \u2014 a worker you spawned finished. Look back at the conversation: where did the request originate?
7786
7788
  - If the original request was \`[WEB]\`, a normal text reply is enough (the user is watching).
7787
7789
  - If it was \`[SLACK ...]\`, post the result to that Slack thread via \`messenger\`.
@@ -7801,6 +7803,48 @@ If \`messenger({action:'post', ...})\` returns \`{ok:false, error:'...'}\` (e.g.
7801
7803
  **Never silently swallow a delivery failure.**
7802
7804
  </delivery_failures>
7803
7805
 
7806
+ <reaching_specific_people>
7807
+
7808
+ When a request needs a *specific human's attention* (e.g. "ask Ryan to review this", "let Sarah know the deploy is done", "wait for Charlie's OK before merging") \u2014 don't just reply into thin air, actually surface it to that person. Two mechanisms, in order of preference:
7809
+
7810
+ **1. @-ping them in a Slack channel they're already in (the cheap path)**
7811
+
7812
+ If you know the person's Slack user id and the conversation has a relevant channel/thread, include a literal mention token in the message text. Slack renders \`<@U0123>\` as a clickable @-mention and pushes a notification to that user.
7813
+
7814
+ \`\`\`
7815
+ messenger({
7816
+ action: 'post', channel: 'slack',
7817
+ to: 'C0B5CESSBGD', threadTs: '1700000000.001',
7818
+ text: '<@U05C6RWBDPC> heads up \u2014 the deploy hit a flaky test, can you check?'
7819
+ })
7820
+ \`\`\`
7821
+
7822
+ You already have the user id from inbound \`[SLACK ... user=U0123]\` pills. If multiple people are involved in a thread, you've seen their ids in the recent messages \u2014 use those. **The mention must be wrapped in \`<@...>\` \u2014 bare \`@username\` does NOT notify.**
7823
+
7824
+ **2. DM them directly (when channel context is missing OR you only know their email)**
7825
+
7826
+ For "DM Alice" / "shoot Bob a note" / pinging someone not in the current thread, use the **\`slack-messaging\` skill** \u2014 load it first with \`load_skill slack-messaging\`. It documents the lookup recipe (\`users.lookupByEmail\` \u2192 user id \u2192 \`messenger\` with the user id as \`to\`). Slack auto-opens the DM; no extra step.
7827
+
7828
+ Quick form (when you've already loaded the skill earlier in the turn): look up the user id by email via the Slack API, then \`messenger({action:'post', channel:'slack', to:'<U_ID>', text:'...'})\`. No \`threadTs\` for DMs \u2014 they're standalone.
7829
+
7830
+ **When to use which**
7831
+
7832
+ | Situation | Use |
7833
+ |---|---|
7834
+ | Inbound Slack thread + you know the user is in it | ping with \`<@U\u2026>\` in the existing thread |
7835
+ | Need them but they're not in this channel/thread | DM (skill) |
7836
+ | Have only their email, not their id | DM (skill \u2014 it looks up the id) |
7837
+ | Need a broadcast-style heads-up | ping in their team's channel |
7838
+ | Sensitive / 1:1 conversation | always DM, never ping in a shared channel |
7839
+
7840
+ **Don'ts**
7841
+
7842
+ - Don't @-mention everyone in a channel ("FYI" pings \u2014 leave that to humans).
7843
+ - Don't ping AND DM the same person for the same thing \u2014 pick one.
7844
+ - Don't fabricate user ids or emails. If you don't know how to reach them, ask the requester ("which Sarah?") instead of guessing.
7845
+ - Don't ping in a thread the person hasn't shown up in unless the asker explicitly named them \u2014 surprise pings are annoying.
7846
+ </reaching_specific_people>
7847
+
7804
7848
  <hard_rules>
7805
7849
 
7806
7850
  - Avoid direct workspace work. Do not directly edit product code, run builds, or perform substantive implementation yourself; spawn workers for that.
@@ -8721,7 +8765,12 @@ function load() {
8721
8765
  const now = Date.now();
8722
8766
  for (const [id, e] of Object.entries(parsed.users || {})) {
8723
8767
  if (e && typeof e.expiresAt === "number" && e.expiresAt > now) {
8724
- userMap.set(id, { name: e.name ?? null, expiresAt: e.expiresAt });
8768
+ userMap.set(id, {
8769
+ name: e.name ?? null,
8770
+ realName: e.realName ?? null,
8771
+ email: e.email ?? null,
8772
+ expiresAt: e.expiresAt
8773
+ });
8725
8774
  }
8726
8775
  }
8727
8776
  for (const [key2, e] of Object.entries(parsed.threads || {})) {
@@ -8916,7 +8965,7 @@ function getSlackAllowlistPolicy() {
8916
8965
  return { allowedUsers: [], allowedChannels: [], allowDmsFromAnyone: true };
8917
8966
  }
8918
8967
  }
8919
- async function fetchSlackUserName(userId) {
8968
+ async function fetchSlackUserInfo(userId) {
8920
8969
  const token = getSlackBotToken();
8921
8970
  if (!token) return null;
8922
8971
  try {
@@ -8930,31 +8979,45 @@ async function fetchSlackUserName(userId) {
8930
8979
  }
8931
8980
  const profile = data.user?.profile || {};
8932
8981
  const name = profile.display_name_normalized || profile.display_name || profile.real_name_normalized || profile.real_name || data.user?.real_name || data.user?.name || null;
8933
- return name ? String(name) : null;
8982
+ const realName = profile.real_name_normalized || profile.real_name || data.user?.real_name || null;
8983
+ const email = profile.email || null;
8984
+ return {
8985
+ name: name ? String(name) : null,
8986
+ realName: realName ? String(realName) : null,
8987
+ email: email ? String(email) : null
8988
+ };
8934
8989
  } catch (err) {
8935
8990
  console.warn(`[slack] users.info(${userId}) error:`, err?.message || err);
8936
8991
  return null;
8937
8992
  }
8938
8993
  }
8939
- async function resolveSlackUserName(userId) {
8994
+ async function resolveSlackUserInfo(userId) {
8940
8995
  if (!userId) return null;
8941
8996
  const now = Date.now();
8942
8997
  const hit = getCachedUserName(userId);
8943
- if (hit && hit.expiresAt > now) return hit.name;
8998
+ if (hit && hit.expiresAt > now) {
8999
+ return { name: hit.name, realName: hit.realName ?? null, email: hit.email ?? null };
9000
+ }
8944
9001
  const inflight = userInflight.get(userId);
8945
9002
  if (inflight) return inflight;
8946
9003
  const p = (async () => {
8947
- const name = await fetchSlackUserName(userId);
9004
+ const info = await fetchSlackUserInfo(userId);
8948
9005
  setCachedUserName(userId, {
8949
- name,
8950
- expiresAt: now + (name ? USER_TTL_MS : USER_FAIL_TTL_MS)
9006
+ name: info?.name ?? null,
9007
+ realName: info?.realName ?? null,
9008
+ email: info?.email ?? null,
9009
+ expiresAt: now + (info?.name ? USER_TTL_MS : USER_FAIL_TTL_MS)
8951
9010
  });
8952
9011
  userInflight.delete(userId);
8953
- return name;
9012
+ return info;
8954
9013
  })();
8955
9014
  userInflight.set(userId, p);
8956
9015
  return p;
8957
9016
  }
9017
+ async function resolveSlackUserName(userId) {
9018
+ const info = await resolveSlackUserInfo(userId);
9019
+ return info?.name ?? null;
9020
+ }
8958
9021
  async function normalizeSlackMentions(text) {
8959
9022
  if (!text) return text;
8960
9023
  const userMentionRe = /<@([UW][A-Z0-9]+)(?:\|([^>]+))?>/g;
@@ -9187,6 +9250,13 @@ var init_slack = __esm({
9187
9250
  });
9188
9251
 
9189
9252
  // src/integrations/channels/system.ts
9253
+ var system_exports = {};
9254
+ __export(system_exports, {
9255
+ systemChannel: () => systemChannel,
9256
+ workerCompletedEvent: () => workerCompletedEvent,
9257
+ workerFailedEvent: () => workerFailedEvent,
9258
+ workerQuestionEvent: () => workerQuestionEvent
9259
+ });
9190
9260
  function workerCompletedEvent(workerId, workerName, summary) {
9191
9261
  const ref = { channel: "system", kind: "worker.completed", workerId, workerName };
9192
9262
  return {
@@ -10174,6 +10244,14 @@ var init_pending_input = __esm({
10174
10244
  });
10175
10245
 
10176
10246
  // src/orchestrator/inbox.ts
10247
+ var inbox_exports = {};
10248
+ __export(inbox_exports, {
10249
+ clearInbox: () => clearInbox,
10250
+ flush: () => flush,
10251
+ peekInbox: () => peekInbox,
10252
+ pushToInbox: () => pushToInbox,
10253
+ setFlushHandler: () => setFlushHandler
10254
+ });
10177
10255
  function setFlushHandler(fn) {
10178
10256
  flushHandler = fn;
10179
10257
  }
@@ -10219,6 +10297,18 @@ async function flush(sessionId) {
10219
10297
  console.error("[orchestrator-inbox] flush handler threw:", err?.message || err);
10220
10298
  }
10221
10299
  }
10300
+ function peekInbox(sessionId) {
10301
+ return inboxes.get(sessionId)?.pending.slice() ?? [];
10302
+ }
10303
+ function clearInbox(sessionId) {
10304
+ const e = inboxes.get(sessionId);
10305
+ if (!e) return;
10306
+ if (e.timer) {
10307
+ clearTimeout(e.timer);
10308
+ e.timer = void 0;
10309
+ }
10310
+ e.pending.length = 0;
10311
+ }
10222
10312
  var inboxes, FLUSH_DEBOUNCE_MS, flushHandler;
10223
10313
  var init_inbox = __esm({
10224
10314
  "src/orchestrator/inbox.ts"() {
@@ -15090,6 +15180,18 @@ tasks.post(
15090
15180
  data: { status: "failed", error: errorMsg }
15091
15181
  });
15092
15182
  }
15183
+ if (body.orchestratorSessionId) {
15184
+ try {
15185
+ const { pushToInbox: pushToInbox2 } = await Promise.resolve().then(() => (init_inbox(), inbox_exports));
15186
+ const { workerFailedEvent: workerFailedEvent2 } = await Promise.resolve().then(() => (init_system(), system_exports));
15187
+ pushToInbox2(
15188
+ body.orchestratorSessionId,
15189
+ workerFailedEvent2(taskId, body.name || "worker", `(uncaught) ${errorMsg}`)
15190
+ );
15191
+ } catch (notifyErr) {
15192
+ console.error(`[TASK] failed to notify orchestrator of crash:`, notifyErr?.message || notifyErr);
15193
+ }
15194
+ }
15093
15195
  }
15094
15196
  } finally {
15095
15197
  await writeSSE("[DONE]");
@@ -15339,12 +15441,11 @@ slack.post("/events", async (c) => {
15339
15441
  if (orchestratorId) {
15340
15442
  inbound.content = await normalizeSlackMentions(inbound.content);
15341
15443
  if (ev.user) {
15342
- const speakerName = await resolveSlackUserName(ev.user);
15343
- if (speakerName) {
15344
- inbound.content = inbound.content.replace(
15345
- `user=${ev.user}`,
15346
- `user=${speakerName} <@${ev.user}>`
15347
- );
15444
+ const info = await resolveSlackUserInfo(ev.user);
15445
+ if (info?.name) {
15446
+ const emailSuffix = info.email ? ` (${info.email})` : "";
15447
+ const enriched = `user=${info.name} <@${ev.user}>${emailSuffix}`;
15448
+ inbound.content = inbound.content.replace(`user=${ev.user}`, () => enriched);
15348
15449
  }
15349
15450
  }
15350
15451
  pushToInbox(orchestratorId, inbound);