@hasna/assistants 1.1.17 → 1.1.19

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.
package/dist/index.js CHANGED
@@ -25784,6 +25784,60 @@ var init_executor3 = __esm(async () => {
25784
25784
  MAX_SHELL_OUTPUT_LENGTH = 64 * 1024;
25785
25785
  });
25786
25786
 
25787
+ // packages/core/src/channels/mentions.ts
25788
+ function parseMentions(content) {
25789
+ const mentions = [];
25790
+ const quotedRegex = /@"([^"]+)"/g;
25791
+ let match;
25792
+ while ((match = quotedRegex.exec(content)) !== null) {
25793
+ const name = match[1].trim();
25794
+ if (name && !mentions.includes(name)) {
25795
+ mentions.push(name);
25796
+ }
25797
+ }
25798
+ const simpleRegex = /@([a-zA-Z0-9_-]+)/g;
25799
+ while ((match = simpleRegex.exec(content)) !== null) {
25800
+ const name = match[1];
25801
+ if (!mentions.includes(name)) {
25802
+ mentions.push(name);
25803
+ }
25804
+ }
25805
+ return mentions;
25806
+ }
25807
+ function resolveNameToKnown(mentionName, knownNames) {
25808
+ const lower = mentionName.toLowerCase();
25809
+ const exact = knownNames.find((k) => k.name.toLowerCase() === lower);
25810
+ if (exact)
25811
+ return exact;
25812
+ const prefix = knownNames.find((k) => k.name.toLowerCase().startsWith(lower));
25813
+ if (prefix)
25814
+ return prefix;
25815
+ const word = knownNames.find((k) => k.name.toLowerCase().split(/\s+/).some((w) => w === lower));
25816
+ if (word)
25817
+ return word;
25818
+ return null;
25819
+ }
25820
+ function resolveMentions(names, members) {
25821
+ const resolved = [];
25822
+ for (const name of names) {
25823
+ const lower = name.toLowerCase();
25824
+ const member = members.find((m) => m.assistantName.toLowerCase() === lower);
25825
+ if (member) {
25826
+ resolved.push({
25827
+ name,
25828
+ memberId: member.assistantId,
25829
+ memberType: member.memberType
25830
+ });
25831
+ }
25832
+ }
25833
+ return resolved;
25834
+ }
25835
+ function getMentionedMemberIds(content, members) {
25836
+ const names = parseMentions(content);
25837
+ const resolved = resolveMentions(names, members);
25838
+ return resolved.map((r) => r.memberId);
25839
+ }
25840
+
25787
25841
  // packages/core/src/scheduler/format.ts
25788
25842
  function formatRelativeTime(timestamp, now2 = Date.now()) {
25789
25843
  if (!timestamp)
@@ -67267,6 +67321,12 @@ class SwarmCoordinator {
67267
67321
  }
67268
67322
  const config = { ...this.config, ...input.config };
67269
67323
  this.config = config;
67324
+ if (!Number.isFinite(config.maxConcurrent) || config.maxConcurrent < 1) {
67325
+ this.streamText(`
67326
+ \u26A0\uFE0F Invalid swarm config: maxConcurrent=${config.maxConcurrent}. Using 1.
67327
+ `);
67328
+ this.config.maxConcurrent = 1;
67329
+ }
67270
67330
  this.validateToolLists(config);
67271
67331
  const swarmId = generateId();
67272
67332
  this.state = {
@@ -68234,7 +68294,12 @@ class TaskGraphScheduler {
68234
68294
  stopped = false;
68235
68295
  constructor(graph, options = {}) {
68236
68296
  this.graph = graph;
68237
- this.options = { ...DEFAULT_SCHEDULER_OPTIONS, ...options };
68297
+ const merged = { ...DEFAULT_SCHEDULER_OPTIONS, ...options };
68298
+ const maxConcurrent = Number.isFinite(merged.maxConcurrent) ? merged.maxConcurrent : DEFAULT_SCHEDULER_OPTIONS.maxConcurrent;
68299
+ this.options = {
68300
+ ...merged,
68301
+ maxConcurrent: Math.max(1, maxConcurrent)
68302
+ };
68238
68303
  }
68239
68304
  async execute(executor2) {
68240
68305
  const results = new Map;
@@ -74461,15 +74526,28 @@ Configure the external source with the URL and secret above.
74461
74526
  if (activePerson && result.success) {
74462
74527
  const agentPool = context.getChannelAgentPool?.();
74463
74528
  const members = manager.getMembers(channel);
74529
+ const currentAssistantId = context.getAssistantManager?.()?.getActive?.()?.id;
74464
74530
  if (agentPool && members.length > 0) {
74465
- const currentAssistantId = context.getAssistantManager?.()?.getActive?.()?.id;
74466
74531
  agentPool.triggerResponses(channel, activePerson.name, message, members, currentAssistantId || undefined);
74467
74532
  }
74533
+ const mentions = parseMentions(message);
74534
+ if (mentions.length > 0) {
74535
+ const assistantMembers = members.filter((m) => m.memberType === "assistant");
74536
+ const knownNames = assistantMembers.map((m) => ({ id: m.assistantId, name: m.assistantName }));
74537
+ const resolved = mentions.map((m) => resolveNameToKnown(m, knownNames)).filter(Boolean);
74538
+ if (resolved.length > 0) {
74539
+ if (!resolved.some((r) => r.id === currentAssistantId)) {
74540
+ return { handled: true };
74541
+ }
74542
+ } else {
74543
+ return { handled: true };
74544
+ }
74545
+ }
74468
74546
  return {
74469
74547
  handled: false,
74470
74548
  prompt: `[Channel Message] ${activePerson.name} posted in #${channel}: "${message}"
74471
74549
 
74472
- Respond in #${channel} using channel_send. Be helpful and conversational.`
74550
+ You are in a group channel with other assistants and people. Respond in #${channel} using channel_send. Be helpful and conversational. You may reference or build on what other assistants have said.`
74473
74551
  };
74474
74552
  }
74475
74553
  return { handled: true };
@@ -79324,7 +79402,7 @@ ${repoUrl}/issues/new
79324
79402
  context.setProjectContext(projectContext);
79325
79403
  }
79326
79404
  }
79327
- var VERSION = "1.1.17";
79405
+ var VERSION = "1.1.19";
79328
79406
  var init_builtin = __esm(async () => {
79329
79407
  init_src2();
79330
79408
  init_store();
@@ -172747,11 +172825,12 @@ class ChannelStore {
172747
172825
  sendMessage(channelId, senderId, senderName, content) {
172748
172826
  const id = generateMessageId2();
172749
172827
  const now2 = new Date().toISOString();
172750
- const stmt = this.db.prepare(`INSERT INTO channel_messages (id, channel_id, sender_id, sender_name, content, created_at)
172751
- VALUES (?, ?, ?, ?, ?, ?)`);
172752
- stmt.run(id, channelId, senderId, senderName, content, now2);
172753
- this.db.prepare("UPDATE channels SET updated_at = ? WHERE id = ?").run(now2, channelId);
172754
- this.db.prepare("UPDATE channel_members SET last_read_at = ? WHERE channel_id = ? AND assistant_id = ?").run(now2, channelId, senderId);
172828
+ this.db.transaction(() => {
172829
+ this.db.prepare(`INSERT INTO channel_messages (id, channel_id, sender_id, sender_name, content, created_at)
172830
+ VALUES (?, ?, ?, ?, ?, ?)`).run(id, channelId, senderId, senderName, content, now2);
172831
+ this.db.prepare("UPDATE channels SET updated_at = ? WHERE id = ?").run(now2, channelId);
172832
+ this.db.prepare("UPDATE channel_members SET last_read_at = ? WHERE channel_id = ? AND assistant_id = ?").run(now2, channelId, senderId);
172833
+ });
172755
172834
  return id;
172756
172835
  }
172757
172836
  getMessages(channelId, options) {
@@ -172898,6 +172977,9 @@ class ChannelsManager {
172898
172977
  this.config = options.config;
172899
172978
  this.store = new ChannelStore;
172900
172979
  }
172980
+ getStore() {
172981
+ return this.store;
172982
+ }
172901
172983
  createChannel(name2, description) {
172902
172984
  return this.store.createChannel(name2, description || null, this.assistantId, this.assistantName);
172903
172985
  }
@@ -173527,101 +173609,99 @@ var init_client4 = __esm(async () => {
173527
173609
  ]);
173528
173610
  });
173529
173611
 
173530
- // packages/core/src/channels/mentions.ts
173531
- function parseMentions(content) {
173532
- const mentions = [];
173533
- const quotedRegex = /@"([^"]+)"/g;
173534
- let match;
173535
- while ((match = quotedRegex.exec(content)) !== null) {
173536
- const name2 = match[1].trim();
173537
- if (name2 && !mentions.includes(name2)) {
173538
- mentions.push(name2);
173539
- }
173540
- }
173541
- const simpleRegex = /@([a-zA-Z0-9_-]+)/g;
173542
- while ((match = simpleRegex.exec(content)) !== null) {
173543
- const name2 = match[1];
173544
- if (!mentions.includes(name2)) {
173545
- mentions.push(name2);
173546
- }
173547
- }
173548
- return mentions;
173549
- }
173550
- function resolveNameToKnown(mentionName, knownNames) {
173551
- const lower = mentionName.toLowerCase();
173552
- const exact = knownNames.find((k7) => k7.name.toLowerCase() === lower);
173553
- if (exact)
173554
- return exact;
173555
- const prefix2 = knownNames.find((k7) => k7.name.toLowerCase().startsWith(lower));
173556
- if (prefix2)
173557
- return prefix2;
173558
- const word = knownNames.find((k7) => k7.name.toLowerCase().split(/\s+/).some((w6) => w6 === lower));
173559
- if (word)
173560
- return word;
173561
- return null;
173562
- }
173563
- function resolveMentions(names, members) {
173564
- const resolved = [];
173565
- for (const name2 of names) {
173566
- const lower = name2.toLowerCase();
173567
- const member = members.find((m5) => m5.assistantName.toLowerCase() === lower);
173568
- if (member) {
173569
- resolved.push({
173570
- name: name2,
173571
- memberId: member.assistantId,
173572
- memberType: member.memberType
173573
- });
173574
- }
173575
- }
173576
- return resolved;
173577
- }
173578
- function getMentionedMemberIds(content, members) {
173579
- const names = parseMentions(content);
173580
- const resolved = resolveMentions(names, members);
173581
- return resolved.map((r6) => r6.memberId);
173582
- }
173583
-
173584
173612
  // packages/core/src/channels/agent-pool.ts
173585
173613
  class ChannelAgentPool {
173586
173614
  agents = new Map;
173587
173615
  cwd;
173588
- constructor(cwd) {
173616
+ getChannelsManager;
173617
+ responding = false;
173618
+ maxRounds = 2;
173619
+ constructor(cwd, getChannelsManager) {
173589
173620
  this.cwd = cwd;
173621
+ this.getChannelsManager = getChannelsManager;
173590
173622
  }
173591
173623
  async triggerResponses(channelName, personName, message, channelMembers, excludeAssistantId) {
173592
- const assistantMembers = channelMembers.filter((m5) => m5.memberType === "assistant");
173593
- if (assistantMembers.length === 0)
173594
- return;
173595
- let targetMembers = assistantMembers;
173596
- const mentions = parseMentions(message);
173597
- if (mentions.length > 0) {
173598
- const knownNames = assistantMembers.map((m5) => ({
173599
- id: m5.assistantId,
173600
- name: m5.assistantName
173601
- }));
173602
- const resolved = mentions.map((m5) => resolveNameToKnown(m5, knownNames)).filter(Boolean);
173603
- if (resolved.length > 0) {
173604
- const resolvedIds = new Set(resolved.map((r6) => r6.id));
173605
- targetMembers = assistantMembers.filter((m5) => resolvedIds.has(m5.assistantId));
173624
+ if (this.responding)
173625
+ return;
173626
+ this.responding = true;
173627
+ try {
173628
+ const assistantMembers = channelMembers.filter((m5) => m5.memberType === "assistant");
173629
+ if (assistantMembers.length === 0)
173630
+ return;
173631
+ let targetMembers = assistantMembers;
173632
+ const mentions = parseMentions(message);
173633
+ if (mentions.length > 0) {
173634
+ const knownNames = assistantMembers.map((m5) => ({
173635
+ id: m5.assistantId,
173636
+ name: m5.assistantName
173637
+ }));
173638
+ const resolved = mentions.map((m5) => resolveNameToKnown(m5, knownNames)).filter(Boolean);
173639
+ if (resolved.length > 0) {
173640
+ const resolvedIds = new Set(resolved.map((r6) => r6.id));
173641
+ targetMembers = assistantMembers.filter((m5) => resolvedIds.has(m5.assistantId));
173642
+ } else {
173643
+ return;
173644
+ }
173606
173645
  }
173646
+ if (excludeAssistantId) {
173647
+ targetMembers = targetMembers.filter((m5) => m5.assistantId !== excludeAssistantId);
173648
+ }
173649
+ if (targetMembers.length === 0)
173650
+ return;
173651
+ const prompt = `[Channel Message] ${personName} posted in #${channelName}: "${message}"
173652
+
173653
+ You are in a group channel with other assistants and people. Respond in #${channelName} using channel_send. Be helpful and conversational. You may reference or build on what other assistants have said.`;
173654
+ await this.executeRound(channelName, targetMembers, prompt, excludeAssistantId);
173655
+ for (let round = 2;round <= this.maxRounds; round++) {
173656
+ await new Promise((r6) => setTimeout(r6, 1000));
173657
+ const followUpMembers = this.getAgentsWithUnread(channelName, targetMembers, excludeAssistantId);
173658
+ if (followUpMembers.length === 0)
173659
+ break;
173660
+ const followUpPrompt = `[Channel Update] New messages appeared in #${channelName}. Read them with channel_read and respond if you have something valuable to add. If the conversation is complete or you have nothing meaningful to contribute, simply say nothing (do not use channel_send).`;
173661
+ await this.executeRound(channelName, followUpMembers, followUpPrompt, excludeAssistantId);
173662
+ }
173663
+ } finally {
173664
+ this.responding = false;
173607
173665
  }
173608
- if (excludeAssistantId) {
173609
- targetMembers = targetMembers.filter((m5) => m5.assistantId !== excludeAssistantId);
173610
- }
173611
- if (targetMembers.length === 0)
173666
+ }
173667
+ async executeRound(channelName, members, prompt, excludeAssistantId) {
173668
+ let roundMembers = excludeAssistantId ? members.filter((m5) => m5.assistantId !== excludeAssistantId) : members;
173669
+ if (roundMembers.length === 0)
173612
173670
  return;
173613
- const prompt = `[Channel Message] ${personName} posted in #${channelName}: "${message}"
173614
-
173615
- Respond in #${channelName} using channel_send. Be helpful and conversational.`;
173616
- const sends = targetMembers.map(async (member) => {
173671
+ const shuffled = this.shuffleArray([...roundMembers]);
173672
+ for (let i5 = 0;i5 < shuffled.length; i5++) {
173673
+ const member = shuffled[i5];
173617
173674
  try {
173618
173675
  const client = await this.getOrCreateClient(member.assistantId);
173619
173676
  await client.send(prompt);
173620
173677
  } catch (error3) {
173621
- console.error(`ChannelAgentPool: Failed to trigger response for ${member.assistantName}:`, error3);
173678
+ console.error(`ChannelAgentPool: Failed for ${member.assistantName}:`, error3);
173679
+ }
173680
+ if (i5 < shuffled.length - 1) {
173681
+ await new Promise((r6) => setTimeout(r6, 500 + Math.floor(Math.random() * 1500)));
173622
173682
  }
173683
+ }
173684
+ }
173685
+ getAgentsWithUnread(channelName, members, excludeId) {
173686
+ const manager = this.getChannelsManager?.();
173687
+ if (!manager)
173688
+ return [];
173689
+ const channel = manager.getChannel(channelName);
173690
+ if (!channel)
173691
+ return [];
173692
+ return members.filter((m5) => {
173693
+ if (m5.assistantId === excludeId)
173694
+ return false;
173695
+ const unread = manager.getStore().getUnreadMessages(channel.id, m5.assistantId);
173696
+ return unread.length > 0;
173623
173697
  });
173624
- await Promise.allSettled(sends);
173698
+ }
173699
+ shuffleArray(array) {
173700
+ for (let i5 = array.length - 1;i5 > 0; i5--) {
173701
+ const j7 = Math.floor(Math.random() * (i5 + 1));
173702
+ [array[i5], array[j7]] = [array[j7], array[i5]];
173703
+ }
173704
+ return array;
173625
173705
  }
173626
173706
  async getOrCreateClient(assistantId) {
173627
173707
  const existing = this.agents.get(assistantId);
@@ -182645,6 +182725,7 @@ class AssistantLoop {
182645
182725
  isRunning = false;
182646
182726
  shouldStop = false;
182647
182727
  cumulativeTurns = 0;
182728
+ emittedTerminalChunk = false;
182648
182729
  toolAbortController = null;
182649
182730
  systemPrompt = null;
182650
182731
  connectorDiscovery = null;
@@ -182907,7 +182988,7 @@ class AssistantLoop {
182907
182988
  const assistantName = assistant?.name || "assistant";
182908
182989
  this.channelsManager = createChannelsManager(assistantId, assistantName, this.config.channels);
182909
182990
  registerChannelTools(this.toolRegistry, () => this.channelsManager);
182910
- this.channelAgentPool = new ChannelAgentPool(this.cwd);
182991
+ this.channelAgentPool = new ChannelAgentPool(this.cwd, () => this.channelsManager);
182911
182992
  }
182912
182993
  try {
182913
182994
  this.peopleManager = await createPeopleManager();
@@ -183265,6 +183346,7 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
183265
183346
  throw new Error("Assistant not initialized. Call initialize() first.");
183266
183347
  }
183267
183348
  this.isRunning = true;
183349
+ this.emittedTerminalChunk = false;
183268
183350
  this.setHeartbeatState("processing");
183269
183351
  this.shouldStop = false;
183270
183352
  this.cumulativeTurns = 0;
@@ -183289,6 +183371,7 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
183289
183371
  if (explicitToolResult) {
183290
183372
  this.pendingMemoryContext = null;
183291
183373
  this.pendingContextInjection = null;
183374
+ this.ensureTerminalChunk();
183292
183375
  return explicitToolResult;
183293
183376
  }
183294
183377
  if (userMessage.startsWith("/")) {
@@ -183326,6 +183409,7 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
183326
183409
  panelValue: `session:${payload}`
183327
183410
  });
183328
183411
  }
183412
+ this.ensureTerminalChunk();
183329
183413
  return { ok: true, summary: `Handled ${userMessage}` };
183330
183414
  }
183331
183415
  if (commandResult.prompt) {
@@ -183336,6 +183420,7 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
183336
183420
  if (handled) {
183337
183421
  this.pendingMemoryContext = null;
183338
183422
  this.pendingContextInjection = null;
183423
+ this.ensureTerminalChunk();
183339
183424
  return { ok: true, summary: `Executed ${userMessage}` };
183340
183425
  }
183341
183426
  } else {
@@ -183350,6 +183435,7 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
183350
183435
  panelValue: commandResult.panelValue
183351
183436
  });
183352
183437
  }
183438
+ this.ensureTerminalChunk();
183353
183439
  return { ok: true, summary: `Handled ${userMessage}` };
183354
183440
  }
183355
183441
  if (commandResult.prompt) {
@@ -184196,7 +184282,15 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
184196
184282
  }
184197
184283
  return true;
184198
184284
  }
184285
+ ensureTerminalChunk() {
184286
+ if (!this.emittedTerminalChunk) {
184287
+ this.emit({ type: "done" });
184288
+ }
184289
+ }
184199
184290
  emit(chunk) {
184291
+ if (chunk.type === "done" || chunk.type === "error") {
184292
+ this.emittedTerminalChunk = true;
184293
+ }
184200
184294
  this.onChunk?.(chunk);
184201
184295
  }
184202
184296
  async emitNotification(params) {
@@ -206275,6 +206369,8 @@ await init_src3();
206275
206369
 
206276
206370
  // packages/terminal/src/hooks/useSafeInput.ts
206277
206371
  var import_react27 = __toESM(require_react(), 1);
206372
+ var rawModeUsers = 0;
206373
+ var rawModeEnabled = false;
206278
206374
  function parseKeypress2(s6) {
206279
206375
  const key = { name: "", ctrl: false, shift: false, meta: false, option: false, sequence: s6 };
206280
206376
  if (s6 === "\r" || s6 === `
@@ -206321,9 +206417,17 @@ function useSafeInput(handler, options = {}) {
206321
206417
  import_react27.useEffect(() => {
206322
206418
  if (!isRawModeSupported || !setRawMode)
206323
206419
  return;
206324
- setRawMode(true);
206420
+ rawModeUsers += 1;
206421
+ if (!rawModeEnabled) {
206422
+ setRawMode(true);
206423
+ rawModeEnabled = true;
206424
+ }
206325
206425
  return () => {
206326
- setRawMode(false);
206426
+ rawModeUsers = Math.max(0, rawModeUsers - 1);
206427
+ if (rawModeEnabled && rawModeUsers === 0) {
206428
+ setRawMode(false);
206429
+ rawModeEnabled = false;
206430
+ }
206327
206431
  };
206328
206432
  }, [isRawModeSupported, setRawMode]);
206329
206433
  import_react27.useEffect(() => {
@@ -219183,6 +219287,11 @@ function ChannelsPanel({ manager, onClose, activePersonId, activePersonName, onP
219183
219287
  const [createName, setCreateName] = import_react46.useState("");
219184
219288
  const [createDesc, setCreateDesc] = import_react46.useState("");
219185
219289
  const [chatInput, setChatInput] = import_react46.useState("");
219290
+ const lastSubmitTimeRef = import_react46.useRef(0);
219291
+ const [mentionActive, setMentionActive] = import_react46.useState(false);
219292
+ const [mentionQuery, setMentionQuery] = import_react46.useState("");
219293
+ const [mentionIndex, setMentionIndex] = import_react46.useState(0);
219294
+ const [chatMembers, setChatMembers] = import_react46.useState([]);
219186
219295
  const [inviteName, setInviteName] = import_react46.useState("");
219187
219296
  const loadChannels = () => {
219188
219297
  try {
@@ -219202,6 +219311,7 @@ function ChannelsPanel({ manager, onClose, activePersonId, activePersonName, onP
219202
219311
  setSelectedChannel(ch2);
219203
219312
  const result = manager.readMessages(ch2.id, 50);
219204
219313
  setMessages(result?.messages || []);
219314
+ setChatMembers(manager.getMembers(ch2.id));
219205
219315
  setMode("chat");
219206
219316
  }
219207
219317
  };
@@ -219213,7 +219323,68 @@ function ChannelsPanel({ manager, onClose, activePersonId, activePersonName, onP
219213
219323
  setMode("members");
219214
219324
  }
219215
219325
  };
219326
+ const mentionCandidates = import_react46.useMemo(() => {
219327
+ if (!mentionActive)
219328
+ return [];
219329
+ const q7 = mentionQuery.toLowerCase();
219330
+ return chatMembers.filter((m5) => m5.assistantName.toLowerCase().includes(q7));
219331
+ }, [mentionActive, mentionQuery, chatMembers]);
219332
+ const handleChatInputChange = (value) => {
219333
+ const prev = chatInput;
219334
+ setChatInput(value);
219335
+ if (value.length === prev.length + 1 && value[value.length - 1] === "@" && !mentionActive) {
219336
+ setMentionActive(true);
219337
+ setMentionQuery("");
219338
+ setMentionIndex(0);
219339
+ return;
219340
+ }
219341
+ if (mentionActive) {
219342
+ const lastAt = value.lastIndexOf("@");
219343
+ if (lastAt >= 0) {
219344
+ const afterAt = value.slice(lastAt + 1);
219345
+ if (lastAt > prev.lastIndexOf("@") + prev.slice(prev.lastIndexOf("@") + 1).length + 1) {
219346
+ setMentionActive(false);
219347
+ } else {
219348
+ setMentionQuery(afterAt);
219349
+ setMentionIndex(0);
219350
+ }
219351
+ } else {
219352
+ setMentionActive(false);
219353
+ }
219354
+ }
219355
+ };
219356
+ const insertMention = (memberName) => {
219357
+ const lastAt = chatInput.lastIndexOf("@");
219358
+ if (lastAt >= 0) {
219359
+ const before = chatInput.slice(0, lastAt);
219360
+ const needsQuotes = memberName.includes(" ");
219361
+ const mention = needsQuotes ? `@"${memberName}" ` : `@${memberName} `;
219362
+ setChatInput(before + mention);
219363
+ }
219364
+ setMentionActive(false);
219365
+ setMentionQuery("");
219366
+ setMentionIndex(0);
219367
+ };
219216
219368
  useSafeInput((input, key) => {
219369
+ if (mentionActive && mode === "chat") {
219370
+ if (key.escape) {
219371
+ setMentionActive(false);
219372
+ setMentionQuery("");
219373
+ return;
219374
+ }
219375
+ if (key.upArrow) {
219376
+ setMentionIndex((prev) => Math.max(0, prev - 1));
219377
+ return;
219378
+ }
219379
+ if (key.downArrow) {
219380
+ setMentionIndex((prev) => Math.min(mentionCandidates.length - 1, prev + 1));
219381
+ return;
219382
+ }
219383
+ if (key.tab && mentionCandidates.length > 0) {
219384
+ insertMention(mentionCandidates[mentionIndex].assistantName);
219385
+ return;
219386
+ }
219387
+ }
219217
219388
  const isTextEntry = mode === "create-name" || mode === "create-desc" || mode === "invite" || mode === "chat";
219218
219389
  if (key.escape || input === "q" && !isTextEntry) {
219219
219390
  if (key.escape && mode === "list" || input === "q" && mode === "list") {
@@ -219222,6 +219393,7 @@ function ChannelsPanel({ manager, onClose, activePersonId, activePersonName, onP
219222
219393
  setMode("list");
219223
219394
  setSelectedChannel(null);
219224
219395
  setStatusMessage(null);
219396
+ setMentionActive(false);
219225
219397
  }
219226
219398
  return;
219227
219399
  }
@@ -219307,7 +219479,7 @@ function ChannelsPanel({ manager, onClose, activePersonId, activePersonName, onP
219307
219479
  }, undefined, false, undefined, this),
219308
219480
  /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text3, {
219309
219481
  color: "gray",
219310
- children: mode === "list" ? "q:close c:create enter:open m:members i:invite l:leave d:delete r:refresh" : mode === "chat" ? "esc:back (type to chat)" : mode === "members" ? "esc:back" : mode === "delete-confirm" ? "y:confirm n:cancel" : mode === "create-confirm" ? "y:confirm n:cancel" : "Enter to continue"
219482
+ children: mode === "list" ? "q:close c:create enter:open m:members i:invite l:leave d:delete r:refresh" : mode === "chat" ? "esc:back (type to chat, @ to mention)" : mode === "members" ? "esc:back" : mode === "delete-confirm" ? "y:confirm n:cancel" : mode === "create-confirm" ? "y:confirm n:cancel" : "Enter to continue"
219311
219483
  }, undefined, false, undefined, this)
219312
219484
  ]
219313
219485
  }, undefined, true, undefined, this);
@@ -219449,13 +219621,18 @@ function ChannelsPanel({ manager, onClose, activePersonId, activePersonName, onP
219449
219621
  }, undefined, false, undefined, this),
219450
219622
  /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(build_default2, {
219451
219623
  value: chatInput,
219452
- onChange: setChatInput,
219624
+ onChange: handleChatInputChange,
219453
219625
  onSubmit: () => {
219454
219626
  if (chatInput.trim()) {
219627
+ const now2 = Date.now();
219628
+ if (now2 - lastSubmitTimeRef.current < 500)
219629
+ return;
219630
+ lastSubmitTimeRef.current = now2;
219455
219631
  const msg = chatInput.trim();
219456
219632
  const result = activePersonId && activePersonName ? manager.sendAs(selectedChannel.id, msg, activePersonId, activePersonName) : manager.send(selectedChannel.id, msg);
219457
219633
  if (result.success) {
219458
219634
  setChatInput("");
219635
+ setMentionActive(false);
219459
219636
  const updated = manager.readMessages(selectedChannel.id, 50);
219460
219637
  setMessages(updated?.messages || []);
219461
219638
  if (activePersonId && activePersonName && onPersonMessage && selectedChannel) {
@@ -219485,9 +219662,40 @@ function ChannelsPanel({ manager, onClose, activePersonId, activePersonName, onP
219485
219662
  }
219486
219663
  }
219487
219664
  },
219488
- placeholder: "Type a message..."
219665
+ placeholder: "Type a message... (@ to mention)"
219489
219666
  }, undefined, false, undefined, this)
219490
219667
  ]
219668
+ }, undefined, true, undefined, this),
219669
+ mentionActive && mentionCandidates.length > 0 && /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
219670
+ flexDirection: "column",
219671
+ paddingX: 1,
219672
+ borderStyle: "single",
219673
+ borderColor: "yellow",
219674
+ marginTop: 0,
219675
+ children: [
219676
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text3, {
219677
+ color: "yellow",
219678
+ bold: true,
219679
+ children: "Members (Tab to select, Esc to dismiss)"
219680
+ }, undefined, false, undefined, this),
219681
+ mentionCandidates.slice(0, 8).map((m5, i5) => /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Box_default, {
219682
+ children: [
219683
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text3, {
219684
+ color: i5 === mentionIndex ? "blue" : undefined,
219685
+ children: i5 === mentionIndex ? "\u25B8 " : " "
219686
+ }, undefined, false, undefined, this),
219687
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text3, {
219688
+ bold: i5 === mentionIndex,
219689
+ color: i5 === mentionIndex ? "blue" : undefined,
219690
+ children: m5.assistantName
219691
+ }, undefined, false, undefined, this),
219692
+ /* @__PURE__ */ jsx_dev_runtime23.jsxDEV(Text3, {
219693
+ color: "gray",
219694
+ children: m5.memberType === "person" ? " [person]" : " [assistant]"
219695
+ }, undefined, false, undefined, this)
219696
+ ]
219697
+ }, m5.assistantId, true, undefined, this))
219698
+ ]
219491
219699
  }, undefined, true, undefined, this)
219492
219700
  ]
219493
219701
  }, undefined, true, undefined, this);
@@ -233699,18 +233907,20 @@ When done, report the result.`);
233699
233907
  const isActiveMember = activeAssistantId && members.some((m5) => m5.assistantId === activeAssistantId && m5.memberType === "assistant");
233700
233908
  const mentions = parseMentions(message);
233701
233909
  let activeAssistantTargeted = true;
233702
- if (mentions.length > 0 && isActiveMember) {
233910
+ if (mentions.length > 0) {
233703
233911
  const assistantMembers = members.filter((m5) => m5.memberType === "assistant");
233704
233912
  const knownNames = assistantMembers.map((m5) => ({ id: m5.assistantId, name: m5.assistantName }));
233705
233913
  const resolved = mentions.map((m5) => resolveNameToKnown(m5, knownNames)).filter(Boolean);
233706
233914
  if (resolved.length > 0) {
233707
233915
  activeAssistantTargeted = resolved.some((r6) => r6.id === activeAssistantId);
233916
+ } else {
233917
+ activeAssistantTargeted = false;
233708
233918
  }
233709
233919
  }
233710
233920
  if (isActiveMember && activeAssistantTargeted) {
233711
233921
  const prompt = `[Channel Message] ${personName} posted in #${channelName}: "${message}"
233712
233922
 
233713
- Respond in #${channelName} using channel_send. Be helpful and conversational.`;
233923
+ You are in a group channel with other assistants and people. Respond in #${channelName} using channel_send. Be helpful and conversational. You may reference or build on what other assistants have said.`;
233714
233924
  activeSession?.client.send(prompt);
233715
233925
  }
233716
233926
  }
@@ -234504,7 +234714,7 @@ Interactive Mode:
234504
234714
  // packages/terminal/src/index.tsx
234505
234715
  var jsx_dev_runtime44 = __toESM(require_jsx_dev_runtime(), 1);
234506
234716
  setRuntime(bunRuntime);
234507
- var VERSION4 = "1.1.17";
234717
+ var VERSION4 = "1.1.19";
234508
234718
  var SYNC_START = "\x1B[?2026h";
234509
234719
  var SYNC_END = "\x1B[?2026l";
234510
234720
  function enableSynchronizedOutput() {
@@ -234644,4 +234854,4 @@ export {
234644
234854
  main
234645
234855
  };
234646
234856
 
234647
- //# debugId=06504F1621F0D4C464756E2164756E21
234857
+ //# debugId=BA4928B11FA139C964756E2164756E21