@songsid/agend 0.0.8 → 0.0.10

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.
@@ -2192,9 +2192,9 @@ When instances disagree, collect both viewpoints, make a decision, and record it
2192
2192
 
2193
2193
  -----
2194
2194
 
2195
- ## Context Rotation Bootstrap
2195
+ ## After Restart
2196
2196
 
2197
- After your context rotates, run this sequence BEFORE processing any new messages:
2197
+ After a restart, run this sequence BEFORE processing any new messages:
2198
2198
  1. list_instances() → rebuild fleet awareness
2199
2199
  2. list_teams() → restore team structure
2200
2200
  3. list_decisions() → reload policies and conventions
@@ -2527,7 +2527,8 @@ When users create specialized instances, suggest these configurations:
2527
2527
  }
2528
2528
  /** Forward a message to a classic channel instance with chat log context */
2529
2529
  async forwardToClassicInstance(instanceName, text, msg, extraMeta) {
2530
- const logContext = this.getRecentChatLog(instanceName);
2530
+ const contextLines = this.classicChannels?.getContextLines(msg.chatId) ?? 10;
2531
+ const logContext = this.getRecentChatLog(instanceName, contextLines);
2531
2532
  const fullText = logContext
2532
2533
  ? `[Chat log for context]\n${logContext}\n\n[User message]\n${text}`
2533
2534
  : text;
@@ -2565,8 +2566,8 @@ When users create specialized instances, suggest these configurations:
2565
2566
  ipc.send({ type: "raw_paste", content: text });
2566
2567
  this.logger.info({ instanceName, text: text.slice(0, 100) }, "Raw paste sent to classic instance");
2567
2568
  }
2568
- /** Read recent chat log (last ~50 lines) for agent context */
2569
- getRecentChatLog(instanceName) {
2569
+ /** Read recent chat log for agent context */
2570
+ getRecentChatLog(instanceName, maxLines = 10) {
2570
2571
  const logDir = ClassicChannelManager.chatLogDir(instanceName);
2571
2572
  const today = new Date().toISOString().slice(0, 10);
2572
2573
  const logFile = join(logDir, `${today}.log`);
@@ -2574,14 +2575,14 @@ When users create specialized instances, suggest these configurations:
2574
2575
  if (!existsSync(logFile))
2575
2576
  return undefined;
2576
2577
  const lines = readFileSync(logFile, "utf-8").trim().split("\n");
2577
- return lines.slice(-10).join("\n");
2578
+ return lines.slice(-maxLines).join("\n");
2578
2579
  }
2579
2580
  catch {
2580
2581
  return undefined;
2581
2582
  }
2582
2583
  }
2583
2584
  /** Start a classic channel instance with lightweight config */
2584
- async startClassicInstance(instanceName, backend, preTaskCommand) {
2585
+ async startClassicInstance(instanceName, backend, preTaskCommand, model) {
2585
2586
  if (this.daemons.has(instanceName))
2586
2587
  return;
2587
2588
  const config = {
@@ -2590,6 +2591,7 @@ When users create specialized instances, suggest these configurations:
2590
2591
  working_directory: join(getAgendHome(), "workspaces", instanceName),
2591
2592
  lightweight: true,
2592
2593
  ...(backend ? { backend } : {}),
2594
+ ...(model ? { model } : {}),
2593
2595
  ...(preTaskCommand ? { pre_task_command: preTaskCommand } : {}),
2594
2596
  };
2595
2597
  const topicMode = this.fleetConfig?.channel?.mode === "topic";
@@ -2608,7 +2610,7 @@ When users create specialized instances, suggest these configurations:
2608
2610
  const instanceName = classicInstanceName(sanitizeInstanceName(channelName || channelId), channelId);
2609
2611
  this.classicChannels.register(channelId, instanceName, channelName || channelId, userId);
2610
2612
  this.routing.register(channelId, { kind: "classic", name: instanceName });
2611
- await this.startClassicInstance(instanceName, this.classicChannels.getBackend(channelId, this.fleetConfig?.defaults?.backend), this.classicChannels.getPreTaskCommand(channelId));
2613
+ await this.startClassicInstance(instanceName, this.classicChannels.getBackend(channelId, this.fleetConfig?.defaults?.backend), this.classicChannels.getPreTaskCommand(channelId), this.classicChannels.getModel(channelId, this.fleetConfig?.defaults?.model));
2612
2614
  this.reregisterClassicChannels();
2613
2615
  this.logger.info({ channelId, instanceName, userId }, "Classic channel started");
2614
2616
  return `✅ Agent started in this channel. Use \`/chat <message>\` to talk.`;
@@ -2649,17 +2651,21 @@ When users create specialized instances, suggest these configurations:
2649
2651
  }
2650
2652
  this.topicArchiver.stop();
2651
2653
  this.scheduler?.shutdown();
2652
- // Stop instances sequentially to avoid tmux send-keys race conditions.
2653
- // Each stop sends quit + Enter via separate tmux commands; parallel stops
2654
- // can cause the Enter to arrive before the quit text is processed.
2655
- for (const [name, daemon] of this.daemons) {
2656
- try {
2657
- await daemon.stop();
2658
- }
2659
- catch (err) {
2660
- this.logger.warn({ name, err }, "Stop failed");
2661
- }
2662
- this.daemons.delete(name);
2654
+ // Stop instances in parallel batches to avoid long sequential waits.
2655
+ // Concurrency limited to avoid overwhelming the tmux server.
2656
+ const STOP_CONCURRENCY = 5;
2657
+ const entries = [...this.daemons.entries()];
2658
+ for (let i = 0; i < entries.length; i += STOP_CONCURRENCY) {
2659
+ const batch = entries.slice(i, i + STOP_CONCURRENCY);
2660
+ await Promise.all(batch.map(async ([name, daemon]) => {
2661
+ try {
2662
+ await daemon.stop();
2663
+ }
2664
+ catch (err) {
2665
+ this.logger.warn({ name, err }, "Stop failed");
2666
+ }
2667
+ this.daemons.delete(name);
2668
+ }));
2663
2669
  }
2664
2670
  for (const [, ipc] of this.instanceIpcClients) {
2665
2671
  await ipc.close();