agentix-cli 0.36.0 → 0.38.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.
Files changed (90) hide show
  1. package/apps/mac-voice/Sources/AgentXVoice/AgentClient.swift +6 -7
  2. package/apps/mac-voice/Sources/AgentXVoice/Config.swift +4 -0
  3. package/apps/mac-voice/Sources/AgentXVoice/Progress.swift +4 -4
  4. package/apps/mac-voice/Sources/AgentXVoice/Speech.swift +84 -13
  5. package/apps/mac-voice/Sources/AgentXVoice/main.swift +6 -6
  6. package/dist/{App-WY6GOC3O.js → App-HSRGG5VA.js} +2 -2
  7. package/dist/{ChatApp-H7W5V2QL.js → ChatApp-4FOU6Q3S.js} +2 -2
  8. package/dist/{agent-6MW3J5FN.js → agent-54NUP2Z3.js} +2 -2
  9. package/dist/{board-dashboard-FKT5GEYE.js → board-dashboard-R7FWWFFK.js} +4 -4
  10. package/dist/{chunk-COY76JUG.js → chunk-B32HVIDN.js} +2 -2
  11. package/dist/{chunk-2IK2SIFN.js → chunk-B5HKRYCU.js} +2 -2
  12. package/dist/{chunk-ZA7FEJKR.js → chunk-DIKZWTL5.js} +5 -5
  13. package/dist/{chunk-EXBIM6WZ.js → chunk-DWAVMTAV.js} +145 -137
  14. package/dist/chunk-DWAVMTAV.js.map +1 -0
  15. package/dist/{chunk-YAER4PD6.js → chunk-FAJEBVZE.js} +2 -2
  16. package/dist/{chunk-AXYREZLV.js → chunk-H4MIYENA.js} +2 -2
  17. package/dist/chunk-IAG6FI3Q.js +13 -0
  18. package/dist/chunk-IAG6FI3Q.js.map +1 -0
  19. package/dist/{chunk-3CGB43RZ.js → chunk-KZPHCJMW.js} +242 -192
  20. package/dist/chunk-KZPHCJMW.js.map +1 -0
  21. package/dist/{chunk-GM5PFIJU.js → chunk-NZ6AQIKJ.js} +2 -2
  22. package/dist/{chunk-AOVH27BF.js → chunk-P7LNYB53.js} +2 -2
  23. package/dist/{chunk-DPNBGANY.js → chunk-RCVGIKC2.js} +2 -2
  24. package/dist/{chunk-CHWSVTY7.js → chunk-REPZID5M.js} +2 -2
  25. package/dist/{chunk-4LDSMWKQ.js → chunk-RQKUQOFC.js} +2 -2
  26. package/dist/{chunk-CRKOJZVK.js → chunk-SJPLXXCI.js} +2 -2
  27. package/dist/{chunk-BTMNOTTO.js → chunk-WBGROLOX.js} +2 -2
  28. package/dist/{chunk-TRAVSDZN.js → chunk-X763UOES.js} +2 -2
  29. package/dist/cli.js +1 -1
  30. package/dist/client-6BGTH2TT.js +2 -0
  31. package/dist/config-SRTXHGSK.js +2 -0
  32. package/dist/config-mutator-KJ5GEMVV.js +2 -0
  33. package/dist/doctor-J4FLV45L.js +2 -0
  34. package/dist/index.d.ts +80 -1
  35. package/dist/index.js +1 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/program-P7LNISI7.js +2 -0
  38. package/dist/query-ZSUBNWOL.js +2 -0
  39. package/dist/{registry-LANHP7WG.js → registry-OMUDCBKM.js} +2 -2
  40. package/dist/{request-planner-S4YWZEOG.js → request-planner-326NOKPZ.js} +2 -2
  41. package/dist/{review-candidates-3E3GW4K7.js → review-candidates-LEOPEOEG.js} +2 -2
  42. package/dist/{routing-QT4Z2ORA.js → routing-AWIRBYGU.js} +2 -2
  43. package/dist/scheduler-PGGYW5AB.js +2 -0
  44. package/dist/{seat-VFYCLEMH.js → seat-XB4EGUKJ.js} +2 -2
  45. package/dist/{select-SIJHCD54.js → select-L4VIYSFJ.js} +2 -2
  46. package/dist/wiki-GBC6I7ZX.js +2 -0
  47. package/package.json +1 -1
  48. package/dist/chunk-3CGB43RZ.js.map +0 -1
  49. package/dist/chunk-EBWWNNJF.js +0 -13
  50. package/dist/chunk-EBWWNNJF.js.map +0 -1
  51. package/dist/chunk-EXBIM6WZ.js.map +0 -1
  52. package/dist/client-OC7COXM6.js +0 -2
  53. package/dist/config-KAMUYXO3.js +0 -2
  54. package/dist/config-mutator-SMGYCOYW.js +0 -2
  55. package/dist/doctor-RNQDJCCI.js +0 -2
  56. package/dist/program-Q3URXEEE.js +0 -2
  57. package/dist/query-PINMYFMF.js +0 -2
  58. package/dist/scheduler-JUYBJLLK.js +0 -2
  59. package/dist/wiki-NY6V6OAT.js +0 -2
  60. /package/dist/{App-WY6GOC3O.js.map → App-HSRGG5VA.js.map} +0 -0
  61. /package/dist/{ChatApp-H7W5V2QL.js.map → ChatApp-4FOU6Q3S.js.map} +0 -0
  62. /package/dist/{agent-6MW3J5FN.js.map → agent-54NUP2Z3.js.map} +0 -0
  63. /package/dist/{board-dashboard-FKT5GEYE.js.map → board-dashboard-R7FWWFFK.js.map} +0 -0
  64. /package/dist/{chunk-COY76JUG.js.map → chunk-B32HVIDN.js.map} +0 -0
  65. /package/dist/{chunk-2IK2SIFN.js.map → chunk-B5HKRYCU.js.map} +0 -0
  66. /package/dist/{chunk-ZA7FEJKR.js.map → chunk-DIKZWTL5.js.map} +0 -0
  67. /package/dist/{chunk-YAER4PD6.js.map → chunk-FAJEBVZE.js.map} +0 -0
  68. /package/dist/{chunk-AXYREZLV.js.map → chunk-H4MIYENA.js.map} +0 -0
  69. /package/dist/{chunk-GM5PFIJU.js.map → chunk-NZ6AQIKJ.js.map} +0 -0
  70. /package/dist/{chunk-AOVH27BF.js.map → chunk-P7LNYB53.js.map} +0 -0
  71. /package/dist/{chunk-DPNBGANY.js.map → chunk-RCVGIKC2.js.map} +0 -0
  72. /package/dist/{chunk-CHWSVTY7.js.map → chunk-REPZID5M.js.map} +0 -0
  73. /package/dist/{chunk-4LDSMWKQ.js.map → chunk-RQKUQOFC.js.map} +0 -0
  74. /package/dist/{chunk-CRKOJZVK.js.map → chunk-SJPLXXCI.js.map} +0 -0
  75. /package/dist/{chunk-BTMNOTTO.js.map → chunk-WBGROLOX.js.map} +0 -0
  76. /package/dist/{chunk-TRAVSDZN.js.map → chunk-X763UOES.js.map} +0 -0
  77. /package/dist/{client-OC7COXM6.js.map → client-6BGTH2TT.js.map} +0 -0
  78. /package/dist/{config-KAMUYXO3.js.map → config-SRTXHGSK.js.map} +0 -0
  79. /package/dist/{config-mutator-SMGYCOYW.js.map → config-mutator-KJ5GEMVV.js.map} +0 -0
  80. /package/dist/{doctor-RNQDJCCI.js.map → doctor-J4FLV45L.js.map} +0 -0
  81. /package/dist/{program-Q3URXEEE.js.map → program-P7LNISI7.js.map} +0 -0
  82. /package/dist/{query-PINMYFMF.js.map → query-ZSUBNWOL.js.map} +0 -0
  83. /package/dist/{registry-LANHP7WG.js.map → registry-OMUDCBKM.js.map} +0 -0
  84. /package/dist/{request-planner-S4YWZEOG.js.map → request-planner-326NOKPZ.js.map} +0 -0
  85. /package/dist/{review-candidates-3E3GW4K7.js.map → review-candidates-LEOPEOEG.js.map} +0 -0
  86. /package/dist/{routing-QT4Z2ORA.js.map → routing-AWIRBYGU.js.map} +0 -0
  87. /package/dist/{scheduler-JUYBJLLK.js.map → scheduler-PGGYW5AB.js.map} +0 -0
  88. /package/dist/{seat-VFYCLEMH.js.map → seat-XB4EGUKJ.js.map} +0 -0
  89. /package/dist/{select-SIJHCD54.js.map → select-L4VIYSFJ.js.map} +0 -0
  90. /package/dist/{wiki-NY6V6OAT.js.map → wiki-GBC6I7ZX.js.map} +0 -0
@@ -22,9 +22,9 @@ enum AgentClient {
22
22
  let buttons: [(String, String)]
23
23
  let imageURL: String?
24
24
  let durationMs: Int?
25
- /// The agent that answered and its ElevenLabs voice, when it has one.
25
+ /// The agent that answered and how it sounds.
26
26
  let agentID: String?
27
- let voiceID: String?
27
+ let voice: VoiceChoice?
28
28
  /// Presence mode the daemon chose for this turn (talk, teach, …).
29
29
  var presenceMode: String? = nil
30
30
  }
@@ -113,11 +113,10 @@ enum AgentClient {
113
113
  struct UiButton: Decodable { let label: String; let url: String }
114
114
  struct UiMedia: Decodable { let type: String; let url: String; let caption: String? }
115
115
  struct Ui: Decodable { let buttons: [UiButton]?; let media: UiMedia? }
116
- struct Voice: Decodable { let elevenlabsVoiceId: String? }
117
116
  struct Presence: Decodable { let mode: String? }
118
117
  struct Reply: Decodable {
119
118
  let agentId: String?
120
- let voice: Voice?
119
+ let voice: VoiceChoice?
121
120
  let presence: Presence?
122
121
  let text: String?
123
122
  let full: String?
@@ -126,7 +125,7 @@ enum AgentClient {
126
125
  let duration: Int?
127
126
  }
128
127
  let reply = try? JSONDecoder().decode(Reply.self, from: data)
129
- let voiceID = reply?.voice?.elevenlabsVoiceId
128
+ let voice = reply?.voice
130
129
 
131
130
  // 202 means accepted-but-busy: the daemon has already written a
132
131
  // speakable explanation into `text`. Falling through to the error
@@ -135,7 +134,7 @@ enum AgentClient {
135
134
  if let http = response as? HTTPURLResponse, http.statusCode == 202,
136
135
  let queuedText = reply?.text, !queuedText.isEmpty {
137
136
  return Answer(text: queuedText, written: nil, buttons: [], imageURL: nil,
138
- durationMs: reply?.duration, agentID: reply?.agentId, voiceID: voiceID)
137
+ durationMs: reply?.duration, agentID: reply?.agentId, voice: voice)
139
138
  }
140
139
 
141
140
  if let http = response as? HTTPURLResponse, !(200..<300).contains(http.statusCode) {
@@ -157,7 +156,7 @@ enum AgentClient {
157
156
 
158
157
  return Answer(text: text, written: reply?.full,
159
158
  buttons: buttons + extra, imageURL: image,
160
- durationMs: reply?.duration, agentID: reply?.agentId, voiceID: voiceID,
159
+ durationMs: reply?.duration, agentID: reply?.agentId, voice: voice,
161
160
  presenceMode: reply?.presence?.mode)
162
161
  }
163
162
  }
@@ -10,6 +10,10 @@ enum Config {
10
10
  static let daemonURL = env("AGENTX_DAEMON_URL") ?? "http://127.0.0.1:18800"
11
11
  static let agentID = env("AGENTX_VOICE_AGENT") ?? "secretary-agent"
12
12
 
13
+ /// Who speaks before the daemon has said which voice an agent uses:
14
+ /// "system" (free macOS voices, the default) or "elevenlabs".
15
+ static let voiceProvider = env("AGENTX_VOICE_PROVIDER") ?? "system"
16
+
13
17
  /// ElevenLabs default voice, used when the answering agent has none
14
18
  /// configured. Override with AGENTX_VOICE_ID.
15
19
  static let voiceID = env("AGENTX_VOICE_ID") ?? "21m00Tcm4TlvDq8ikWAM"
@@ -17,7 +17,7 @@ final class Progress: NSObject, URLSessionDataDelegate {
17
17
  private var buffer = Data()
18
18
  private let agentID: String
19
19
  /// The step label, and the working agent's voice id when it has one.
20
- private let onStep: (String, String?) -> Void
20
+ private let onStep: (String, VoiceChoice?) -> Void
21
21
 
22
22
  /// Every touch of `buffer`, `task` and `session` happens here.
23
23
  ///
@@ -31,7 +31,7 @@ final class Progress: NSObject, URLSessionDataDelegate {
31
31
  private let queue = DispatchQueue(label: "tn.acme.agentx.voice.progress")
32
32
  private var stopped = false
33
33
 
34
- init(agentID: String, onStep: @escaping (String, String?) -> Void) {
34
+ init(agentID: String, onStep: @escaping (String, VoiceChoice?) -> Void) {
35
35
  self.agentID = agentID
36
36
  self.onStep = onStep
37
37
  }
@@ -113,8 +113,8 @@ final class Progress: NSObject, URLSessionDataDelegate {
113
113
  // and narrating theirs would be both confusing and a privacy leak.
114
114
  guard (obj["agentId"] as? String) == agentID else { return }
115
115
  guard let label = summarise(obj) else { return }
116
- let voiceID = (obj["voice"] as? [String: Any])?["elevenlabsVoiceId"] as? String
117
- DispatchQueue.main.async { self.onStep(label, voiceID) }
116
+ let voice = VoiceChoice(json: obj["voice"])
117
+ DispatchQueue.main.async { self.onStep(label, voice) }
118
118
  }
119
119
 
120
120
  /// Turn a step event into something worth showing in 180 points of
@@ -1,13 +1,39 @@
1
1
  import AVFoundation
2
2
  import Foundation
3
3
 
4
- /// Speech in and out, ElevenLabs first with a local fallback on each side.
4
+ /// How the daemon says an agent should sound (the `voice` of /ask and of
5
+ /// step events). Missing fields mean the free system voice.
6
+ struct VoiceChoice: Decodable {
7
+ var provider: String?
8
+ var elevenlabsVoiceId: String?
9
+ /// A macOS voice identifier for `say -v`, or a Siri voice id; nil for
10
+ /// the OS default.
11
+ var systemVoice: String?
12
+ /// When ElevenLabs cannot speak, use the system voice instead.
13
+ var fallback: Bool?
14
+
15
+ init(provider: String? = nil, elevenlabsVoiceId: String? = nil, systemVoice: String? = nil, fallback: Bool? = nil) {
16
+ self.provider = provider; self.elevenlabsVoiceId = elevenlabsVoiceId
17
+ self.systemVoice = systemVoice; self.fallback = fallback
18
+ }
19
+
20
+ /// From a step event, which arrives as untyped JSON.
21
+ init?(json: Any?) {
22
+ guard let o = json as? [String: Any] else { return nil }
23
+ self.init(provider: o["provider"] as? String, elevenlabsVoiceId: o["elevenlabsVoiceId"] as? String,
24
+ systemVoice: o["systemVoice"] as? String, fallback: o["fallback"] as? Bool)
25
+ }
26
+ }
27
+
28
+ /// Speech in and out. Speech out uses the free macOS voices unless the
29
+ /// daemon says an agent speaks through ElevenLabs; speech in uses
30
+ /// ElevenLabs when there is a key, with a local fallback.
5
31
  ///
6
32
  /// The fallbacks are not decoration. This widget is push-to-talk on a
7
33
  /// laptop: it will be used on hotel wifi, on a plane, and on the day the
8
34
  /// key expires. A voice assistant that answers "network error" out loud is
9
35
  /// worse than one that answers in a robot voice, so STT degrades to
10
- /// on-device mlx-whisper and TTS degrades to `say`.
36
+ /// on-device mlx-whisper and ElevenLabs TTS degrades to `say`.
11
37
  enum Speech {
12
38
  // MARK: Speech to text
13
39
 
@@ -75,19 +101,25 @@ enum Speech {
75
101
  /// Silence whatever this app is saying (an answer or a step line).
76
102
  static func stop() { Player.shared.stop() }
77
103
 
78
- /// `voiceID` is the answering agent's voice; nil means the global default.
79
- static func speak(_ text: String, voiceID: String? = nil) async {
104
+ /// `voice` is the answering agent's, as the daemon resolved it; nil
105
+ /// (no answer yet) means the provider this app is configured with.
106
+ static func speak(_ text: String, voice: VoiceChoice? = nil) async {
80
107
  guard !text.isEmpty else { return }
81
- if let key = Config.elevenLabsKey {
82
- do {
83
- let mp3 = try await elevenLabsTTS(text: text, voiceID: voiceID ?? Config.voiceID, key: key)
84
- try await Player.shared.play(mp3)
85
- return
86
- } catch {
87
- Log.warn("ElevenLabs TTS failed (\(error.localizedDescription)); falling back to say")
108
+ if (voice?.provider ?? Config.voiceProvider) == "elevenlabs" {
109
+ if let key = Config.elevenLabsKey {
110
+ do {
111
+ let mp3 = try await elevenLabsTTS(text: text, voiceID: voice?.elevenlabsVoiceId ?? Config.voiceID, key: key)
112
+ try await Player.shared.play(mp3)
113
+ return
114
+ } catch {
115
+ Log.warn("ElevenLabs TTS failed (\(error.localizedDescription))")
116
+ }
117
+ } else {
118
+ Log.warn("ElevenLabs is the voice provider but there is no key")
88
119
  }
120
+ guard voice?.fallback ?? true else { return }
89
121
  }
90
- _ = try? run("/usr/bin/say", [text])
122
+ await Player.shared.say(text, voice: voice?.systemVoice)
91
123
  }
92
124
 
93
125
  private static func elevenLabsTTS(text: String, voiceID: String, key: String) async throws -> Data {
@@ -138,11 +170,49 @@ enum VoiceError: LocalizedError {
138
170
  }
139
171
 
140
172
  /// Holds a strong reference to the player for the life of playback —
141
- /// an AVAudioPlayer that goes out of scope stops mid-sentence.
173
+ /// an AVAudioPlayer that goes out of scope stops mid-sentence — and to the
174
+ /// `say` process, so `stop()` silences either.
142
175
  final class Player: NSObject, AVAudioPlayerDelegate {
143
176
  static let shared = Player()
144
177
  private var player: AVAudioPlayer?
145
178
  private var finished: CheckedContinuation<Void, Never>?
179
+ private var sayProcess: Process?
180
+
181
+ /// Speak with a system voice; returns when done or stopped. The text
182
+ /// goes in on stdin, so a line that starts with "-" is not an option.
183
+ ///
184
+ /// `say -v` cannot use a Siri voice. A Siri line, and any line in the
185
+ /// OS default voice, goes through the daemon's script, which switches
186
+ /// the default for the line and locks it against the daemon's own lines.
187
+ func say(_ text: String, voice: String?) async {
188
+ let p = Process()
189
+ let script = NSHomeDirectory() + "/.agentx/voice/siri-say.sh"
190
+ let siri = voice?.hasPrefix("com.apple.ttsbundle.gryphon-neural_") ?? false
191
+ if (voice == nil || siri) && FileManager.default.fileExists(atPath: script) {
192
+ p.executableURL = URL(fileURLWithPath: "/bin/sh")
193
+ p.arguments = [script] + (siri ? [voice!] : [])
194
+ } else {
195
+ p.executableURL = URL(fileURLWithPath: "/usr/bin/say")
196
+ p.arguments = voice.flatMap { siri ? nil : ["-v", $0] } ?? []
197
+ }
198
+ let input = Pipe()
199
+ p.standardInput = input
200
+ p.standardOutput = FileHandle.nullDevice
201
+ p.standardError = FileHandle.nullDevice
202
+ await withCheckedContinuation { (c: CheckedContinuation<Void, Never>) in
203
+ p.terminationHandler = { _ in c.resume() }
204
+ do { try p.run() } catch {
205
+ p.terminationHandler = nil
206
+ Log.warn("say failed to start (\(error.localizedDescription))")
207
+ c.resume()
208
+ return
209
+ }
210
+ sayProcess = p
211
+ input.fileHandleForWriting.write(Data(text.utf8))
212
+ try? input.fileHandleForWriting.close()
213
+ }
214
+ if sayProcess === p { sayProcess = nil }
215
+ }
146
216
 
147
217
  func play(_ mp3: Data) async throws {
148
218
  let p = try AVAudioPlayer(data: mp3)
@@ -160,6 +230,7 @@ final class Player: NSObject, AVAudioPlayerDelegate {
160
230
 
161
231
  /// Cut the line off now; the `play` awaiting it returns.
162
232
  func stop() {
233
+ sayProcess?.terminate(); sayProcess = nil
163
234
  player?.stop()
164
235
  finished?.resume(); finished = nil; player = nil
165
236
  }
@@ -30,7 +30,7 @@ final class App: NSObject, NSApplicationDelegate {
30
30
  private var lastSpokeAt = Date.distantPast
31
31
  /// The answering agent's voice, learned from the daemon. Nil until
32
32
  /// then, which speaks in the global default.
33
- private var voiceID: String?
33
+ private var voice: VoiceChoice?
34
34
  /// Set when the door opens: what was speaking (and is now hushed), so
35
35
  /// the words spoken go to that activity rather than to /ask.
36
36
  private var talkCheck: Task<AgentClient.Hushed, Never>?
@@ -315,7 +315,7 @@ final class App: NSObject, NSApplicationDelegate {
315
315
  panel.render(.thinking)
316
316
  return await runTurn(next)
317
317
  }
318
- if let v = answer.voiceID { voiceID = v }
318
+ if let v = answer.voice { voice = v }
319
319
  Log.info("answer (\(answer.agentID ?? "?")): \(answer.text)")
320
320
  // Show BEFORE speaking, but only when there is something
321
321
  // the speech cannot deliver — a link, an image, or more
@@ -331,7 +331,7 @@ final class App: NSObject, NSApplicationDelegate {
331
331
  // Scroll the sentence being spoken, so it can be read as
332
332
  // well as heard — and re-read after, which speech cannot do.
333
333
  panel.render(.saying(answer.text))
334
- await Speech.speak(answer.text, voiceID: voiceID)
334
+ await Speech.speak(answer.text, voice: voice)
335
335
  // Cut off by the door: the new words are being recorded.
336
336
  if recorder.isRecording { busy = false; return }
337
337
  if let next = followUp { followUp = nil; return await runTurn(next) }
@@ -352,7 +352,7 @@ final class App: NSObject, NSApplicationDelegate {
352
352
  panel.render(.error(short(error.localizedDescription)))
353
353
  // Say it aloud too — a voice assistant that fails only in
354
354
  // a 230px label has failed silently for anyone not looking.
355
- await Speech.speak("Sorry, that didn't work.", voiceID: voiceID)
355
+ await Speech.speak("Sorry, that didn't work.", voice: voice)
356
356
  resetSoon()
357
357
  }
358
358
  busy = false
@@ -376,7 +376,7 @@ final class App: NSObject, NSApplicationDelegate {
376
376
  progress = Progress(agentID: Config.agentID) { [weak self] step, voice in
377
377
  Task { @MainActor in
378
378
  guard let self, self.busy else { return }
379
- if let voice { self.voiceID = voice }
379
+ if let voice { self.voice = voice }
380
380
  self.lastStep = step
381
381
  Log.info("step: \(step)")
382
382
  self.panel.render(.working(step, Int(Date().timeIntervalSince(self.startedAt))))
@@ -427,7 +427,7 @@ final class App: NSObject, NSApplicationDelegate {
427
427
  tool: tool, detail: detail, elapsed: Int(elapsed)) else { return }
428
428
  guard !self.spokenSteps.contains(phrase) else { return }
429
429
  self.spokenSteps.insert(phrase)
430
- await Speech.speak(phrase.prefix(1).capitalized + phrase.dropFirst() + ".", voiceID: self.voiceID)
430
+ await Speech.speak(phrase.prefix(1).capitalized + phrase.dropFirst() + ".", voice: self.voice)
431
431
  }
432
432
  }
433
433
 
@@ -1,8 +1,8 @@
1
- import{a as F,b as L,c as U,d as N,e as O,f as H,g as j}from"./chunk-U2CKL755.js";import{b as B,c as D,d as A,e as P,g as M}from"./chunk-CRKOJZVK.js";import"./chunk-EBWWNNJF.js";import"./chunk-FIOB3MEX.js";import{useEffect as R,useReducer as V,useRef as G}from"react";import{Box as i,Text as o,useApp as W,useInput as J}from"ink";import{randomUUID as z}from"crypto";async function*q(e){let r=[];e.type&&r.push(`type=${encodeURIComponent(e.type)}`);let t=`${e.baseUrl.replace(/\/+$/,"")}/events${r.length?`?${r.join("&")}`:""}`,n={Accept:"text/event-stream"};e.token&&(n.Authorization=`Bearer ${e.token}`);let s=await fetch(t,{headers:n,signal:e.signal});if(!s.ok||!s.body)throw new Error(`SSE connect failed: HTTP ${s.status}`);let u=s.body.getReader(),p=new TextDecoder,y="";try{for(;;){let{done:f,value:l}=await u.read();if(f)return;y+=p.decode(l,{stream:!0});let d;for(;(d=y.indexOf(`
1
+ import{a as F,b as L,c as U,d as N,e as O,f as H,g as j}from"./chunk-U2CKL755.js";import{b as B,c as D,d as A,e as P,g as M}from"./chunk-SJPLXXCI.js";import"./chunk-IAG6FI3Q.js";import"./chunk-FIOB3MEX.js";import{useEffect as R,useReducer as V,useRef as G}from"react";import{Box as i,Text as o,useApp as W,useInput as J}from"ink";import{randomUUID as z}from"crypto";async function*q(e){let r=[];e.type&&r.push(`type=${encodeURIComponent(e.type)}`);let t=`${e.baseUrl.replace(/\/+$/,"")}/events${r.length?`?${r.join("&")}`:""}`,n={Accept:"text/event-stream"};e.token&&(n.Authorization=`Bearer ${e.token}`);let s=await fetch(t,{headers:n,signal:e.signal});if(!s.ok||!s.body)throw new Error(`SSE connect failed: HTTP ${s.status}`);let u=s.body.getReader(),p=new TextDecoder,y="";try{for(;;){let{done:f,value:l}=await u.read();if(f)return;y+=p.decode(l,{stream:!0});let d;for(;(d=y.indexOf(`
2
2
 
3
3
  `))>=0;){let h=y.slice(0,d);y=y.slice(d+2);let m=X(h);m&&(yield m)}}}finally{try{u.releaseLock()}catch{}}}function X(e){let r="message",t="";for(let s of e.split(`
4
4
  `))s.startsWith("event: ")?r=s.slice(7):s.startsWith("data: ")&&(t=s.slice(6));if(!t)return null;let n;try{n=JSON.parse(t)}catch{n=t}return{event:r,data:n}}import{jsx as a,jsxs as c}from"react/jsx-runtime";function $(e,r){return r<=0||e<0?0:e>=r?r-1:e}function E(){return`tui:${z().slice(0,12)}`}function k(e,r){let t=[...e,r];return t.length>50&&t.splice(0,t.length-50),t}function Y(e,r){switch(r.type){case"agents":return{...e,agents:r.data,cursor:{...e.cursor,agents:$(e.cursor.agents,r.data.length)}};case"processes":return{...e,processes:r.data,cursor:{...e.cursor,processes:$(e.cursor.processes,r.data.length)}};case"crons":return{...e,crons:r.data};case"event":{let t=[r.row,...e.events];t.length>200&&(t.length=200);let n=e.channels;return r.channelTally&&(n=new Map(e.channels),n.set(r.channelTally.channel,r.channelTally)),{...e,events:t,channels:n}}case"connError":return{...e,connError:r.error};case"tick":return{...e,lastTick:Date.now()};case"focus":return{...e,focus:r.pane};case"cursor":{let t=r.pane==="agents"?e.agents.length:r.pane==="processes"?e.processes.length:e.events.length;return{...e,cursor:{...e.cursor,[r.pane]:$(e.cursor[r.pane]+r.delta,t)}}}case"bottomRight":return{...e,bottomRight:r.pane};case"chatStart":return{...e,chat:{active:!0,agentId:r.agentId,chatId:E(),history:[],text:"",status:"idle",error:null}};case"chatExit":return{...e,chat:{...e.chat,active:!1}};case"chatClear":return{...e,chat:{...e.chat,chatId:E(),history:[],text:"",status:"idle",error:null}};case"chatText":return{...e,chat:{...e.chat,text:r.text}};case"chatSubmitStart":return{...e,chat:{...e.chat,history:k(e.chat.history,r.you),text:"",status:"sending",error:null}};case"chatSubmitDone":return{...e,chat:{...e.chat,history:k(e.chat.history,r.reply),status:"idle",error:null}};case"chatSubmitError":return{...e,chat:{...e.chat,history:k(e.chat.history,{role:"error",text:r.error,at:Date.now()}),status:"idle",error:r.error}};case"chatStreamStart":{let t=k(e.chat.history,r.you),n=k(t,{role:"agent",text:"",at:Date.now(),streaming:!0,tools:[]});return{...e,chat:{...e.chat,history:n,text:"",status:"sending",error:null}}}case"chatStreamDelta":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]={...n,text:n.text+r.text}),{...e,chat:{...e.chat,history:t}}}case"chatStreamThinking":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]={...n,thinking:(n.thinking??"")+r.text}),{...e,chat:{...e.chat,history:t}}}case"chatStreamTool":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]={...n,tools:[...n.tools??[],{name:r.name,arg:r.arg,error:r.error}]}),{...e,chat:{...e.chat,history:t}}}case"chatStreamEnd":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]=r.error?{...n,streaming:!1,role:"error",text:r.error}:{...n,streaming:!1,elapsedMs:r.elapsedMs,outTokens:r.outTokens}),{...e,chat:{...e.chat,history:t,status:"idle",error:r.error??null}}}case"toast":return{...e,toast:{text:r.text,color:r.color,at:Date.now()}};case"clearToast":return{...e,toast:null}}}var _={agents:[],processes:[],crons:[],events:[],channels:new Map,connError:null,lastTick:0,focus:"agents",cursor:{agents:0,processes:0,events:0},bottomRight:"crons",chat:{active:!1,agentId:null,chatId:E(),history:[],text:"",status:"idle",error:null},toast:null};function be({conn:e,pollMs:r=3e3}){let[t,n]=V(Y,_),{exit:s}=W(),u=G(null),p=G(null),y=t.agents.map(l=>l.id),f=t.chat.active?L(t.chat.text,y,process.cwd()):null;return R(()=>{if(!t.toast)return;let l=setTimeout(()=>n({type:"clearToast"}),3500);return()=>clearTimeout(l)},[t.toast]),J((l,d)=>{if(t.chat.active){if(d.escape){n({type:"chatExit"});return}if(d.ctrl&&l==="l"){n({type:"chatClear"});return}if(d.tab){let m=U(t.chat.text,p,y,process.cwd());m!==null&&n({type:"chatText",text:m});return}if(d.backspace||d.delete){p.current=null,n({type:"chatText",text:t.chat.text.slice(0,-1)});return}if(d.ctrl||d.meta)return;let h=N(l??"",d.return,t.chat.text);if(h.kind==="paste"||h.kind==="type"){p.current=null,n({type:"chatText",text:t.chat.text+h.text});return}if(h.kind==="continue"){n({type:"chatText",text:h.buffer});return}if(h.kind==="submit"){let m=h.text.trim(),g=t.chat.agentId;if(!m||!g||t.chat.status==="sending"){n({type:"chatText",text:""});return}p.current=null;let b=Date.now(),w=t.chat.chatId;n({type:"chatStreamStart",you:{role:"you",text:m,at:b}}),(async()=>{try{let C=await M(e,g,m,{channel:"tui",chatId:w,onText:x=>n({type:"chatStreamDelta",text:x}),onThinking:x=>n({type:"chatStreamThinking",text:x}),onTool:x=>{x.status==="start"&&x.name?n({type:"chatStreamTool",name:x.name,arg:x.arg}):x.status==="result"&&x.error&&n({type:"chatStreamTool",name:x.name??"tool",error:!0})}});n({type:"chatStreamEnd",elapsedMs:Date.now()-b,outTokens:C.usage?.outputTokens,error:C.error})}catch(C){n({type:"chatStreamEnd",elapsedMs:Date.now()-b,error:C?.message||String(C)})}})();return}return}if(l==="q"||d.ctrl&&l==="c"){u.current?.abort(),s();return}if(d.tab){let h=["agents","processes","events"],m=h.indexOf(t.focus);n({type:"focus",pane:h[(m+1)%h.length]});return}if(l==="a"){n({type:"focus",pane:"agents"});return}if(l==="p"){n({type:"focus",pane:"processes"});return}if(l==="e"){n({type:"focus",pane:"events"});return}if(l==="c"){n({type:"bottomRight",pane:t.bottomRight==="crons"?"channels":"crons"});return}if(d.upArrow){n({type:"cursor",pane:t.focus,delta:-1});return}if(d.downArrow){n({type:"cursor",pane:t.focus,delta:1});return}if(d.return&&t.focus==="agents"){let h=t.agents[t.cursor.agents];if(!h){n({type:"toast",text:"no agent selected",color:"yellow"});return}n({type:"chatStart",agentId:h.id});return}if(l==="k"&&t.focus==="processes"){let h=t.processes[t.cursor.processes];if(!h){n({type:"toast",text:"no process selected",color:"yellow"});return}(async()=>{try{await P(e,h.key,"tui"),n({type:"toast",text:`killed ${h.key.agentId}/${h.key.channel}/${h.key.chatId}`,color:"green"})}catch(m){n({type:"toast",text:`kill failed: ${m?.message||m}`,color:"red"})}})();return}}),R(()=>{let l=new AbortController;u.current=l;let d=!1,h=async()=>{try{let[g,b,w]=await Promise.all([B(e,l.signal).catch(()=>[]),D(e,l.signal).catch(()=>[]),A(e,l.signal).catch(()=>[])]);if(d)return;n({type:"agents",data:g}),n({type:"processes",data:b}),n({type:"crons",data:w}),n({type:"tick"}),n({type:"connError",error:null})}catch(g){d||n({type:"connError",error:g?.message||String(g)})}};h();let m=setInterval(h,r);return()=>{d=!0,clearInterval(m),l.abort()}},[e.baseUrl,e.token,r]),R(()=>{let l=!1,d=new AbortController,h=new Map;return(async()=>{let m=500;for(;!l;){try{for await(let g of q({baseUrl:e.baseUrl,token:e.token,signal:d.signal})){let b=ae(g),w;if(g.event==="channel"&&typeof g.data?.channel=="string"){let S=h.get(g.data.channel)??{channel:g.data.channel,inbound:0,outbound:0,lastAt:0,lastPreview:""},C=S.inbound+(g.data.direction==="in"?1:0),x=S.outbound+(g.data.direction==="out"?1:0);w={channel:g.data.channel,inbound:C,outbound:x,lastAt:b.at,lastPreview:(g.data.textPreview||"").slice(0,60)},h.set(g.data.channel,w)}n({type:"event",row:b,channelTally:w}),m=500}}catch(g){if(l)return;n({type:"connError",error:`events: ${g?.message||g}`})}await new Promise(g=>setTimeout(g,m)),m=Math.min(m*2,1e4)}})(),()=>{l=!0,d.abort()}},[e.baseUrl,e.token]),c(i,{flexDirection:"column",width:"100%",children:[a(K,{conn:e,lastTick:t.lastTick,connError:t.connError,chat:t.chat}),c(i,{flexDirection:"row",flexGrow:1,children:[c(i,{flexDirection:"column",width:"50%",children:[a(v,{title:"AGENTS",focused:!t.chat.active&&t.focus==="agents",children:a(Z,{rows:t.agents,cursor:t.cursor.agents,focused:!t.chat.active&&t.focus==="agents",talkingTo:t.chat.active?t.chat.agentId:null})}),a(v,{title:"PROCESSES",focused:!t.chat.active&&t.focus==="processes",children:a(ee,{rows:t.processes,cursor:t.cursor.processes,focused:!t.chat.active&&t.focus==="processes"})})]}),t.chat.active?a(oe,{chat:t.chat,suggest:f,agents:t.agents}):c(i,{flexDirection:"column",width:"50%",children:[a(v,{title:"LIVE EVENTS",focused:t.focus==="events",children:a(ne,{rows:t.events,cursor:t.cursor.events,focused:t.focus==="events"})}),a(v,{title:t.bottomRight==="crons"?"CRONS / SCHEDULES":"CHANNELS",focused:!1,children:t.bottomRight==="crons"?a(te,{rows:t.crons}):a(re,{rows:Array.from(t.channels.values())})})]})]}),a(Q,{focus:t.focus,bottomRight:t.bottomRight,chat:t.chat,toast:t.toast})]})}function K({conn:e,lastTick:r,connError:t,chat:n}){let s=t?c(o,{color:"red",children:["\u25CF ",t]}):r===0?a(o,{color:"yellow",children:"\u25CF connecting\u2026"}):a(o,{color:"green",children:"\u25CF live"}),u=n.active?c(o,{color:"green",bold:!0,children:["CHAT @",n.agentId]}):a(o,{bold:!0,children:"agentx tui"});return c(i,{paddingX:1,justifyContent:"space-between",children:[a(i,{children:u}),a(o,{dimColor:!0,children:e.baseUrl}),a(i,{children:s})]})}function Q({focus:e,bottomRight:r,chat:t,toast:n}){let s=t.active?["[Enter] send","[Esc] exit chat","[Ctrl-L] new session"]:[`[Tab/a/p/e] focus (now: ${e})`,e==="agents"?"[Enter] chat":null,e==="processes"?"[k] kill":null,`[c] ${r==="crons"?"\u2192channels":"\u2192crons"}`,"[\u2191/\u2193] move","[q] quit"].filter(Boolean);return c(i,{paddingX:1,borderStyle:"single",borderTop:!0,borderBottom:!1,borderLeft:!1,borderRight:!1,justifyContent:"space-between",children:[a(o,{dimColor:!0,children:s.join(" \xB7 ")}),n?a(o,{color:n.color,children:n.text}):a(o,{children:" "})]})}function v({title:e,focused:r,children:t}){return c(i,{flexDirection:"column",width:"100%",borderStyle:r?"double":"single",borderColor:r?"cyan":void 0,paddingX:1,children:[c(o,{bold:!0,color:r?"cyan":"white",children:[e,r?" \u25CF":""]}),a(i,{flexDirection:"column",marginTop:1,children:t})]})}function I(e,r){return e&&r?{inverse:!0}:{}}function Z({rows:e,cursor:r,focused:t,talkingTo:n}){return e.length===0?a(o,{dimColor:!0,children:"no agents"}):a(i,{flexDirection:"column",children:e.slice(0,12).map((s,u)=>{let p=I(t,u===r),y=s.errors>0?"red":s.active>0?"yellow":"green",f=s.id===n;return c(i,{children:[a(o,{...p,color:y,children:"\u25CF"}),c(o,{...p,children:[" ",T(s.name,14)]}),c(o,{...p,children:[T(s.tier,6)," "]}),c(o,{...p,children:[s.active,"/",s.total]}),s.errors>0?c(o,{...p,color:"red",children:[" err=",s.errors]}):null,f?a(o,{color:"green",children:" \u2194 chatting"}):null]},s.id)})})}function ee({rows:e,cursor:r,focused:t}){if(e.length===0)return a(o,{dimColor:!0,children:"no live processes"});let n=Date.now();return a(i,{flexDirection:"column",children:e.slice(0,12).map((s,u)=>{let p=I(t,u===r),y=Math.max(0,Math.round((n-(s.lastTurnAt||s.spawnedAt))/1e3)),f=s.state==="dead"?"red":s.state==="warm-hot"?"green":"yellow";return c(i,{children:[a(o,{...p,color:f,children:T(s.state,10)}),a(o,{...p,children:T(s.key.agentId,12)}),a(o,{...p,children:T(s.key.channel,10)}),c(o,{...p,children:[s.turnCount,"t ",y,"s"]})]},`${s.key.agentId}-${s.key.channel}-${s.key.chatId}-${u}`)})})}function te({rows:e}){return e.length===0?a(o,{dimColor:!0,children:"no crons"}):a(i,{flexDirection:"column",children:e.slice(0,12).map(r=>{let t=r.enabled?r.consecutiveErrors>0?a(o,{color:"red",children:"err"}):a(o,{color:"green",children:"ok"}):a(o,{dimColor:!0,children:"off"}),n=r.nextRun?new Date(r.nextRun).toLocaleTimeString():"\u2014";return c(i,{children:[a(o,{children:T(r.id,16)}),a(o,{dimColor:!0,children:T(r.schedule,12)}),a(o,{children:T(n,10)}),a(i,{children:t})]},r.id)})})}function re({rows:e}){if(e.length===0)return a(o,{dimColor:!0,children:"(waiting for channel events\u2026)"});let r=[...e].sort((t,n)=>n.lastAt-t.lastAt);return a(i,{flexDirection:"column",children:r.slice(0,12).map(t=>c(i,{children:[a(o,{children:T(t.channel,12)}),c(o,{color:"cyan",children:["\u2190",T(String(t.inbound),4)]}),c(o,{color:"magenta",children:["\u2192",T(String(t.outbound),4)]}),a(o,{dimColor:!0,children:t.lastPreview})]},t.channel))})}function ne({rows:e,cursor:r,focused:t}){return e.length===0?a(o,{dimColor:!0,children:"(waiting for events\u2026)"}):a(i,{flexDirection:"column",children:e.slice(0,18).map((n,s)=>{let u=I(t,s===r);return c(i,{children:[a(o,{...u,dimColor:!0,children:new Date(n.at).toLocaleTimeString().padEnd(10)}),a(o,{...u,color:ce(n.kind),children:T(n.kind,8)}),a(o,{...u,children:n.line})]},`${n.at}-${s}`)})})}function oe({chat:e,suggest:r,agents:t}){let n=e.history.slice(-14),s=t.find(f=>f.id===e.agentId),u=H(s?.tier??"",s?.model),p=e.status==="sending"?[...e.history].reverse().find(f=>f.role==="agent"&&f.streaming):void 0,y=e.status==="sending"?a(O,{startedAt:p?.at??Date.now(),phase:p?.text?"responding":"thinking",indent:""}):e.error?c(o,{color:"red",children:["\u25CF ",e.error]}):a(o,{dimColor:!0,children:"\u25CF ready"});return c(i,{flexDirection:"column",width:"50%",borderStyle:"double",borderColor:"green",paddingX:1,children:[c(i,{justifyContent:"space-between",children:[c(o,{bold:!0,color:"green",children:["CHAT \xB7 @",e.agentId]}),a(i,{children:y})]}),c(o,{children:[c(o,{color:u.color,bold:!0,children:[u.glyph," ",u.label]}),s?.model?c(o,{dimColor:!0,children:[" ",j(s.model)]}):null,c(o,{dimColor:!0,children:[" \xB7 ",e.chatId]})]}),a(i,{flexDirection:"column",flexGrow:1,marginTop:1,children:n.length===0?a(o,{dimColor:!0,children:"(type a message and press Enter \u2014 Esc exits, Ctrl-L starts fresh)"}):n.map((f,l)=>a(se,{turn:f,agentId:e.agentId},`${f.at}-${l}`))}),r?c(o,{dimColor:!0,children:[a(o,{color:"cyan",children:"\u21B9"})," ",r.items.slice(0,5).map((f,l)=>l===0?a(o,{color:"cyan",children:f},l):c(o,{children:[" ",f]},l))]}):null,c(i,{children:[a(o,{color:"cyan",children:"you \u203A "}),a(o,{children:e.text}),a(o,{inverse:!0,children:" "})]})]})}function se({turn:e,agentId:r}){if(e.role==="you")return c(i,{flexDirection:"column",marginBottom:1,children:[a(o,{color:"cyan",children:"you"}),e.text.split(`
5
5
  `).map((s,u)=>c(o,{children:[" ",s]},u))]});if(e.role==="error")return c(i,{flexDirection:"column",marginBottom:1,children:[a(o,{color:"red",children:"error"}),c(o,{color:"red",children:[" ",e.text]})]});let t=e.elapsedMs!=null?` \xB7 ${(e.elapsedMs/1e3).toFixed(1)}s${e.outTokens!=null?`, ${e.outTokens} tok`:""}`:e.streaming?" \xB7 \u2026":"",n=F(e.text,58,{balance:e.streaming});return c(i,{flexDirection:"column",marginBottom:1,children:[c(o,{color:"green",children:["@",r??"agent",a(o,{dimColor:!0,children:t})]}),e.thinking?e.thinking.split(`
6
6
  `).filter(s=>s.trim()).slice(-2).map((s,u)=>c(o,{dimColor:!0,italic:!0,children:[" ",s]},`th${u}`)):null,(e.tools??[]).map((s,u)=>c(o,{color:s.error?"red":"green",children:[" \u25CF ",c(o,{bold:!s.error,dimColor:s.error,children:[s.name,s.arg?c(o,{dimColor:!0,children:["(",s.arg,")"]}):null,s.error?" failed":""]})]},`tool${u}`)),n.split(`
7
7
  `).slice(0,14).map((s,u)=>c(o,{children:[" ",s]},u))]})}function ae(e){let r=e.data??{},t=typeof r.at=="string"||typeof r.at=="number"?new Date(r.at).getTime():Date.now(),n="";switch(e.event){case"run":n=`${r.phase??""} ${r.workflowId??""} ${(r.runId??"").slice(0,8)} ${r.nodeId??""}`.trim();break;case"task":n=`${r.phase??""} ${r.workflowId??""} ${Array.isArray(r.assignedTo)?r.assignedTo.join(","):""} ${r.title?`"${r.title}"`:""}`.trim();break;case"signal":n=`${r.name??""} (${r.scope??""}) ${r.workflowId??""}`.trim();break;case"mesh":n=`${r.peer??""} ${r.healthy?"healthy":"lost"} ${r.delta??""}`.trim();break;case"channel":{n=`${r.direction==="in"?"\u2190":"\u2192"} ${r.channel??""}:${r.chatId??""} ${r.textPreview??""}`.trim();break}case"status":n=`node=${r.node??""} agents=${r.agents??0} active=${(r.active??[]).length}`;break;default:n=typeof r=="string"?r:JSON.stringify(r).slice(0,80)}return{at:Number.isFinite(t)?t:Date.now(),kind:e.event,line:n}}function ce(e){switch(e){case"run":return"cyan";case"task":return"magenta";case"signal":return"green";case"mesh":return"blue";case"channel":return"yellow";default:return"white"}}function T(e,r){let t=(e??"").toString();return t.length>=r?t.slice(0,r-1)+" ":t+" ".repeat(r-t.length)}export{be as App};
8
- //# sourceMappingURL=App-WY6GOC3O.js.map
8
+ //# sourceMappingURL=App-HSRGG5VA.js.map
@@ -1,7 +1,7 @@
1
- import{a as Y,b as Z,c as _,d as ee,e as te,f as ne,g as re}from"./chunk-U2CKL755.js";import{b as Q,g as X}from"./chunk-CRKOJZVK.js";import"./chunk-EBWWNNJF.js";import"./chunk-FIOB3MEX.js";import{useCallback as oe,useMemo as H,useRef as P,useState as k}from"react";import{Box as g,Text as t,useApp as he,useInput as xe,useStdout as Te}from"ink";import{randomUUID as se}from"crypto";var m={accent:"cyan",agent:"green",working:"magenta",error:"red",muted:"gray"},q="\u25C7 agentx",F="\u258C";import{jsx as c,jsxs as o}from"react/jsx-runtime";var ie="/help /agent <id> /agents /clear /who /exit",be=[{cmd:"/agent",desc:"switch the target agent (/agent <id>)"},{cmd:"/agents",desc:"list registered agents"},{cmd:"/clear",desc:"start a fresh session"},{cmd:"/who",desc:"show agent + chat id"},{cmd:"/help",desc:"list commands"},{cmd:"/exit",desc:"quit the chat"}];function Pe({conn:n,agentId:p,channel:f,chatId:a,agents:h}){let{exit:w}=he(),{stdout:O}=Te(),V=Math.max(40,Math.min((O?.columns||80)-4,100)),[z,U]=k([]),[$,S]=k(null),[T,le]=k(p),[A,ae]=k(h),[E,ue]=k(a),[y,x]=k(""),[J,de]=k(""),W=P([]),u=P(-1),M=P(null),R=P(null),v=$?.live??!1,j=H(()=>Z(y,A.map(s=>s.id),process.cwd()),[y,A]),N=H(()=>{let s=/^\/(\w*)$/.exec(y);if(!s)return null;let e=be.filter(r=>r.cmd.slice(1).startsWith(s[1].toLowerCase()));return e.length?e:null},[y]),b=s=>de(s),K=oe(s=>{let e=se().slice(0,8),r=Date.now(),i=new AbortController;R.current=i;let d={id:e,you:s,agentId:T,segs:[],live:!0,startedAt:r};S({...d});let L=()=>S({...d,segs:d.segs.map(C=>({...C}))});(async()=>{try{let C=await X(n,T,s,{channel:f,chatId:E,signal:i.signal,onText:l=>{let I=d.segs[d.segs.length-1];I&&I.type==="text"?I.content=(I.content??"")+l:d.segs.push({type:"text",content:l}),L()},onThinking:l=>{d.thinking=(d.thinking??"")+l,L()},onTool:l=>{l.status==="start"&&l.name?(d.segs.push({type:"tool",name:l.name,arg:l.arg}),L()):l.status==="result"&&l.error&&(d.segs.push({type:"tool",name:l.name??"tool",error:!0}),L())}});U(l=>[...l,{...d,live:!1,elapsedMs:Date.now()-r,outTokens:C.usage?.outputTokens,error:C.error}]),S(null)}catch(C){let l=i.signal.aborted||C?.name==="AbortError";U(I=>[...I,{...d,live:!1,elapsedMs:Date.now()-r,interrupted:l,error:l?void 0:C?.message||String(C)}]),S(null)}finally{R.current===i&&(R.current=null)}})()},[n,T,f,E]),me=oe(s=>{let e=s.trim();if(e){if(W.current.push(e),u.current=-1,e==="/exit"||e==="/quit"){w();return}if(e==="/help"){b(ie);return}if(e==="/who"){b(`@${T} \xB7 chatId=${E} \xB7 channel=${f}`);return}if(e==="/clear"){let r=`chat-cli:${se().slice(0,12)}`;ue(r),U([]),b(`fresh chatId=${r} (warm process dropped)`);return}if(e==="/agents"){Q(n).then(r=>{ae(r),b(r.map(i=>i.id===T?`[${i.id}]`:i.id).join(" "))}).catch(r=>b(`failed to list agents: ${r?.message||r}`));return}if(e.startsWith("/agent ")){let r=e.slice(7).replace(/^@/,"").trim();if(!r){b("usage: /agent <id>");return}if(!A.find(i=>i.id===r)){b(`unknown agent: @${r}`);return}le(r),b(`now talking to @${r}`);return}if(e.startsWith("/")){b(`unknown command \xB7 ${ie}`);return}K(e)}},[n,T,A,f,E,w,K]);xe((s,e)=>{if(e.ctrl&&s==="c"){w();return}if(e.escape){if(v&&R.current){R.current.abort();return}w();return}if(e.tab){if(N){x(N[0].cmd+" ");return}let i=_(y,M,A.map(d=>d.id),process.cwd());i!==null&&(x(i),u.current=-1);return}if(e.upArrow){let i=W.current;if(!i.length)return;u.current=u.current<0?i.length-1:Math.max(0,u.current-1),x(i[u.current]??"");return}if(e.downArrow){let i=W.current;if(u.current<0)return;if(u.current=u.current+1,u.current>=i.length){u.current=-1,x("");return}x(i[u.current]??"");return}if(e.backspace||e.delete){M.current=null,x(i=>i.slice(0,-1));return}if(e.ctrl||e.meta)return;let r=ee(s??"",e.return,y);switch(r.kind){case"paste":M.current=null,x(i=>i+r.text),u.current=-1;return;case"type":M.current=null,x(i=>i+r.text),u.current=-1;return;case"continue":x(r.buffer);return;case"submit":if(v)return;M.current=null,x(""),me(r.text);return;case"none":return}});let G=v?m.working:m.accent,ge=y.length?y.split(`
1
+ import{a as Y,b as Z,c as _,d as ee,e as te,f as ne,g as re}from"./chunk-U2CKL755.js";import{b as Q,g as X}from"./chunk-SJPLXXCI.js";import"./chunk-IAG6FI3Q.js";import"./chunk-FIOB3MEX.js";import{useCallback as oe,useMemo as H,useRef as P,useState as k}from"react";import{Box as g,Text as t,useApp as he,useInput as xe,useStdout as Te}from"ink";import{randomUUID as se}from"crypto";var m={accent:"cyan",agent:"green",working:"magenta",error:"red",muted:"gray"},q="\u25C7 agentx",F="\u258C";import{jsx as c,jsxs as o}from"react/jsx-runtime";var ie="/help /agent <id> /agents /clear /who /exit",be=[{cmd:"/agent",desc:"switch the target agent (/agent <id>)"},{cmd:"/agents",desc:"list registered agents"},{cmd:"/clear",desc:"start a fresh session"},{cmd:"/who",desc:"show agent + chat id"},{cmd:"/help",desc:"list commands"},{cmd:"/exit",desc:"quit the chat"}];function Pe({conn:n,agentId:p,channel:f,chatId:a,agents:h}){let{exit:w}=he(),{stdout:O}=Te(),V=Math.max(40,Math.min((O?.columns||80)-4,100)),[z,U]=k([]),[$,S]=k(null),[T,le]=k(p),[A,ae]=k(h),[E,ue]=k(a),[y,x]=k(""),[J,de]=k(""),W=P([]),u=P(-1),M=P(null),R=P(null),v=$?.live??!1,j=H(()=>Z(y,A.map(s=>s.id),process.cwd()),[y,A]),N=H(()=>{let s=/^\/(\w*)$/.exec(y);if(!s)return null;let e=be.filter(r=>r.cmd.slice(1).startsWith(s[1].toLowerCase()));return e.length?e:null},[y]),b=s=>de(s),K=oe(s=>{let e=se().slice(0,8),r=Date.now(),i=new AbortController;R.current=i;let d={id:e,you:s,agentId:T,segs:[],live:!0,startedAt:r};S({...d});let L=()=>S({...d,segs:d.segs.map(C=>({...C}))});(async()=>{try{let C=await X(n,T,s,{channel:f,chatId:E,signal:i.signal,onText:l=>{let I=d.segs[d.segs.length-1];I&&I.type==="text"?I.content=(I.content??"")+l:d.segs.push({type:"text",content:l}),L()},onThinking:l=>{d.thinking=(d.thinking??"")+l,L()},onTool:l=>{l.status==="start"&&l.name?(d.segs.push({type:"tool",name:l.name,arg:l.arg}),L()):l.status==="result"&&l.error&&(d.segs.push({type:"tool",name:l.name??"tool",error:!0}),L())}});U(l=>[...l,{...d,live:!1,elapsedMs:Date.now()-r,outTokens:C.usage?.outputTokens,error:C.error}]),S(null)}catch(C){let l=i.signal.aborted||C?.name==="AbortError";U(I=>[...I,{...d,live:!1,elapsedMs:Date.now()-r,interrupted:l,error:l?void 0:C?.message||String(C)}]),S(null)}finally{R.current===i&&(R.current=null)}})()},[n,T,f,E]),me=oe(s=>{let e=s.trim();if(e){if(W.current.push(e),u.current=-1,e==="/exit"||e==="/quit"){w();return}if(e==="/help"){b(ie);return}if(e==="/who"){b(`@${T} \xB7 chatId=${E} \xB7 channel=${f}`);return}if(e==="/clear"){let r=`chat-cli:${se().slice(0,12)}`;ue(r),U([]),b(`fresh chatId=${r} (warm process dropped)`);return}if(e==="/agents"){Q(n).then(r=>{ae(r),b(r.map(i=>i.id===T?`[${i.id}]`:i.id).join(" "))}).catch(r=>b(`failed to list agents: ${r?.message||r}`));return}if(e.startsWith("/agent ")){let r=e.slice(7).replace(/^@/,"").trim();if(!r){b("usage: /agent <id>");return}if(!A.find(i=>i.id===r)){b(`unknown agent: @${r}`);return}le(r),b(`now talking to @${r}`);return}if(e.startsWith("/")){b(`unknown command \xB7 ${ie}`);return}K(e)}},[n,T,A,f,E,w,K]);xe((s,e)=>{if(e.ctrl&&s==="c"){w();return}if(e.escape){if(v&&R.current){R.current.abort();return}w();return}if(e.tab){if(N){x(N[0].cmd+" ");return}let i=_(y,M,A.map(d=>d.id),process.cwd());i!==null&&(x(i),u.current=-1);return}if(e.upArrow){let i=W.current;if(!i.length)return;u.current=u.current<0?i.length-1:Math.max(0,u.current-1),x(i[u.current]??"");return}if(e.downArrow){let i=W.current;if(u.current<0)return;if(u.current=u.current+1,u.current>=i.length){u.current=-1,x("");return}x(i[u.current]??"");return}if(e.backspace||e.delete){M.current=null,x(i=>i.slice(0,-1));return}if(e.ctrl||e.meta)return;let r=ee(s??"",e.return,y);switch(r.kind){case"paste":M.current=null,x(i=>i+r.text),u.current=-1;return;case"type":M.current=null,x(i=>i+r.text),u.current=-1;return;case"continue":x(r.buffer);return;case"submit":if(v)return;M.current=null,x(""),me(r.text);return;case"none":return}});let G=v?m.working:m.accent,ge=y.length?y.split(`
2
2
  `):[""],B=A.find(s=>s.id===T),D=ne(B?.tier??"",B?.model),pe=O?.rows||24,fe=z.slice(-40);return o(g,{flexDirection:"column",height:pe,children:[o(g,{flexDirection:"column",flexGrow:1,justifyContent:"flex-end",overflow:"hidden",children:[z.length===0&&!$?o(g,{flexDirection:"column",children:[o(t,{children:[c(t,{color:m.accent,bold:!0,children:q}),c(t,{dimColor:!0,children:" \u2014 chat with "}),o(t,{color:m.accent,children:["@",T]}),c(t,{dimColor:!0,children:" via "}),o(t,{color:D.color,bold:!0,children:[D.glyph," ",D.label]}),B?.model?o(t,{dimColor:!0,children:[" (",B.model,")"]}):null]}),c(t,{dimColor:!0,children:"streaming \xB7 markdown \xB7 tools \xB7 type a message, or / for commands"})]}):null,fe.map(s=>c(ce,{turn:s,width:V},s.id)),$?c(ce,{turn:$,width:V}):null]}),N?c(g,{flexDirection:"column",marginTop:1,children:N.map((s,e)=>o(g,{children:[c(t,{color:e===0?m.accent:m.muted,bold:e===0,children:s.cmd.padEnd(10)}),c(t,{dimColor:!0,children:s.desc})]},s.cmd))}):j?o(g,{marginTop:1,children:[c(t,{color:m.accent,children:"\u21B9 "}),j.items.map((s,e)=>o(t,{color:e===0?m.accent:m.muted,children:[s,e<j.items.length-1?" ":""]},e))]}):J?c(g,{marginTop:1,children:c(t,{dimColor:!0,children:J})}):null,c(g,{borderStyle:"single",borderColor:G,borderLeft:!1,borderRight:!1,borderDimColor:!v,width:"100%",children:c(g,{flexDirection:"column",width:"100%",children:ge.map((s,e,r)=>o(g,{children:[c(t,{color:G,bold:!0,children:e===0?"\u203A ":" "}),c(t,{children:s}),e===r.length-1&&!v?c(t,{color:G,children:"\u258F"}):null]},e))})}),o(g,{justifyContent:"space-between",children:[o(t,{children:[c(t,{color:m.accent,bold:!0,children:q}),o(t,{dimColor:!0,children:[" \xB7 @",T," \xB7 "]}),o(t,{color:D.color,bold:!0,children:[D.glyph," ",D.label]}),B?.model?o(t,{dimColor:!0,children:[" ",re(B.model)]}):null]}),o(t,{dimColor:!0,children:["\u21B5 send / cmds @ mention esc ",v?"stop":"exit"]})]})]})}function ce({turn:n,width:p}){let f=H(()=>ye(n,p),[n,p]),a=n.segs.some(h=>h.type==="text"&&h.content);return o(g,{flexDirection:"column",marginBottom:1,children:[o(t,{color:m.accent,bold:!0,children:[F," you"]}),n.you.split(`
3
3
  `).map((h,w)=>o(t,{color:"white",children:[" ",h]},`u${w}`)),o(t,{color:m.agent,bold:!0,children:[F," ",n.agentId??"agent"]}),n.thinking?c(g,{flexDirection:"column",children:we(n.thinking,n.live?3:2).map((h,w)=>o(t,{dimColor:!0,italic:!0,children:[" ",h]},`th${w}`))}):null,f,n.live?c(te,{startedAt:n.startedAt,phase:a?"responding":"thinking",hint:"esc to interrupt"}):n.interrupted?o(t,{dimColor:!0,children:[" \u2298 interrupted \xB7 ",(n.elapsedMs/1e3).toFixed(1),"s"]}):n.error?o(t,{color:"red",children:[" \u2717 ",n.error]}):n.elapsedMs!=null?o(t,{dimColor:!0,children:[" \xB7 ",(n.elapsedMs/1e3).toFixed(1),"s",n.outTokens!=null?`, ${n.outTokens} tok`:""]}):null]})}function we(n,p){return n.split(`
4
4
  `).filter(a=>a.trim()).slice(-p)}function ye(n,p){let f=[];return n.segs.forEach((a,h)=>{a.type==="tool"?f.push(o(t,{color:a.error?"red":"green",children:[" ","\u25CF ",o(t,{bold:!a.error,dimColor:a.error,children:[a.name,a.arg?o(t,{dimColor:!0,children:["(",a.arg,")"]}):null,a.error?" failed":""]})]},h)):a.content&&f.push(c(t,{children:Ce(Y(a.content,p,{balance:n.live}))},h))}),f}function Ce(n){return n.split(`
5
5
  `).map(p=>p.length?" "+p:p).join(`
6
6
  `)}export{Pe as ChatApp};
7
- //# sourceMappingURL=ChatApp-H7W5V2QL.js.map
7
+ //# sourceMappingURL=ChatApp-4FOU6Q3S.js.map
@@ -1,2 +1,2 @@
1
- import{A as a,B as b,C as c}from"./chunk-BTMNOTTO.js";import"./chunk-SFQUP3BP.js";import"./chunk-QOYAX2FT.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-VDMYBPYB.js";import"./chunk-DSYKYZMT.js";import"./chunk-FIOB3MEX.js";export{a as createAgentContext,b as generate,c as generateStream};
2
- //# sourceMappingURL=agent-6MW3J5FN.js.map
1
+ import{A as a,B as b,C as c}from"./chunk-WBGROLOX.js";import"./chunk-SFQUP3BP.js";import"./chunk-QOYAX2FT.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-VDMYBPYB.js";import"./chunk-DSYKYZMT.js";import"./chunk-FIOB3MEX.js";export{a as createAgentContext,b as generate,c as generateStream};
2
+ //# sourceMappingURL=agent-54NUP2Z3.js.map