agentix-cli 0.55.0 → 0.57.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 (117) hide show
  1. package/apps/mac-voice/Sources/AgentXVoice/AgentClient.swift +46 -0
  2. package/apps/mac-voice/Sources/AgentXVoice/Config.swift +7 -0
  3. package/apps/mac-voice/Sources/AgentXVoice/Hotkey.swift +8 -2
  4. package/apps/mac-voice/Sources/AgentXVoice/HotkeySpec.swift +80 -0
  5. package/apps/mac-voice/Sources/AgentXVoice/Orb.swift +311 -0
  6. package/apps/mac-voice/Sources/AgentXVoice/OrbMath.swift +57 -0
  7. package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +14 -1
  8. package/apps/mac-voice/Sources/AgentXVoice/ResultCard.swift +33 -1
  9. package/apps/mac-voice/Sources/AgentXVoice/SettingsWindow.swift +518 -0
  10. package/apps/mac-voice/Sources/AgentXVoice/Speech.swift +8 -2
  11. package/apps/mac-voice/Sources/AgentXVoice/StatusMenu.swift +23 -1
  12. package/apps/mac-voice/Sources/AgentXVoice/VoiceSettings.swift +101 -0
  13. package/apps/mac-voice/Sources/AgentXVoice/main.swift +161 -39
  14. package/apps/mac-voice/build.sh +1 -1
  15. package/dist/{App-4EM7IPB3.js → App-Z5VOAUXB.js} +2 -2
  16. package/dist/{ChatApp-FYTEEGSU.js → ChatApp-BE7ONQKS.js} +2 -2
  17. package/dist/{agent-RZ46ZB4W.js → agent-Q74EISKP.js} +2 -2
  18. package/dist/{board-dashboard-NRRYCDZW.js → board-dashboard-A7PQOI6C.js} +4 -4
  19. package/dist/{chunk-W4YMMWJL.js → chunk-2MIOMYT2.js} +2 -2
  20. package/dist/{chunk-EZN2YXRY.js → chunk-547G3HYQ.js} +2 -2
  21. package/dist/{chunk-LKJSPO3S.js → chunk-5LLGIX5M.js} +2 -2
  22. package/dist/{chunk-WOMQ2VGA.js → chunk-5W7NJHCS.js} +2 -2
  23. package/dist/{chunk-4HEVSWO3.js → chunk-6OABTCRA.js} +2 -2
  24. package/dist/{chunk-UIVHSYCN.js → chunk-6QNPCVRN.js} +3 -3
  25. package/dist/chunk-6QNPCVRN.js.map +1 -0
  26. package/dist/{chunk-CELYD5QW.js → chunk-CQUVUVXR.js} +2 -2
  27. package/dist/chunk-ERL6AS3O.js +15 -0
  28. package/dist/chunk-ERL6AS3O.js.map +1 -0
  29. package/dist/{chunk-EJ5ISSWW.js → chunk-I2LYULR4.js} +5 -5
  30. package/dist/{chunk-76AHNHWC.js → chunk-KKGFZEXS.js} +2 -2
  31. package/dist/{chunk-6AK6WKE5.js → chunk-KW7TIOS3.js} +2 -2
  32. package/dist/{chunk-SA23V7WX.js → chunk-NKMFHEGV.js} +2 -2
  33. package/dist/{chunk-K4RDE6YU.js → chunk-OZZPJ2YG.js} +197 -188
  34. package/dist/chunk-OZZPJ2YG.js.map +1 -0
  35. package/dist/{chunk-JC5Q5MPI.js → chunk-POL7RAJQ.js} +17 -17
  36. package/dist/{chunk-D3LSPFHK.js → chunk-Q6SMLSRO.js} +2 -2
  37. package/dist/{chunk-723K4SAW.js → chunk-RXEAACVH.js} +2 -2
  38. package/dist/{chunk-O3XG2NVR.js → chunk-U5YBQJ5Q.js} +2 -2
  39. package/dist/{chunk-XEOAKD5C.js → chunk-V7ZK6ZIO.js} +2 -2
  40. package/dist/{chunk-M4AG2YNX.js → chunk-VI7GBDKV.js} +2 -2
  41. package/dist/{chunk-GSOL2LW6.js → chunk-VVBGPT6W.js} +2 -2
  42. package/dist/{chunk-C3VRBEJ7.js → chunk-XTD3SZIP.js} +2 -2
  43. package/dist/cli.js +1 -1
  44. package/dist/client-EFP2PKA3.js +2 -0
  45. package/dist/config-P6YXC6XZ.js +2 -0
  46. package/dist/config-mutator-3GKXLTHH.js +2 -0
  47. package/dist/doctor-JEWMHXYH.js +2 -0
  48. package/dist/index.d.ts +109 -3
  49. package/dist/index.js +1 -1
  50. package/dist/program-7DUQJDSQ.js +2 -0
  51. package/dist/{promote-T4UJROEH.js → promote-KVPLDSKT.js} +2 -2
  52. package/dist/query-AGOMCYTT.js +2 -0
  53. package/dist/{registry-DX2RI7I7.js → registry-AUOI4TW2.js} +2 -2
  54. package/dist/{request-planner-B7DE7VJR.js → request-planner-LMZ5ZAXC.js} +2 -2
  55. package/dist/{review-candidates-4YJHIBAF.js → review-candidates-WCBXLLDS.js} +2 -2
  56. package/dist/{routing-MZG3UODH.js → routing-FBASYKUL.js} +2 -2
  57. package/dist/schedule-tool-RJRJ63SS.js +2 -0
  58. package/dist/scheduler-ATA7Z44S.js +2 -0
  59. package/dist/{seat-2PEI7JRX.js → seat-DBFW3Z5P.js} +2 -2
  60. package/dist/{select-4KIUR27O.js → select-6CNVVFA5.js} +2 -2
  61. package/dist/{set-enabled-SE7HQKPF.js → set-enabled-AYZH3KSB.js} +2 -2
  62. package/dist/{tool-5VMD7IE4.js → tool-PKS4NBZ6.js} +2 -2
  63. package/dist/wiki-QW6BDNEQ.js +2 -0
  64. package/package.json +1 -1
  65. package/dist/chunk-K4RDE6YU.js.map +0 -1
  66. package/dist/chunk-U4H35SJS.js +0 -15
  67. package/dist/chunk-U4H35SJS.js.map +0 -1
  68. package/dist/chunk-UIVHSYCN.js.map +0 -1
  69. package/dist/client-IA7ZPE56.js +0 -2
  70. package/dist/config-HPN2F7LH.js +0 -2
  71. package/dist/config-mutator-7GPTMTVB.js +0 -2
  72. package/dist/doctor-IBWQVQV4.js +0 -2
  73. package/dist/program-D5TAPSIH.js +0 -2
  74. package/dist/query-VUJC4Y5O.js +0 -2
  75. package/dist/schedule-tool-A4OFQ4YZ.js +0 -2
  76. package/dist/scheduler-RHJW2V4M.js +0 -2
  77. package/dist/wiki-2QFLAH4U.js +0 -2
  78. /package/dist/{App-4EM7IPB3.js.map → App-Z5VOAUXB.js.map} +0 -0
  79. /package/dist/{ChatApp-FYTEEGSU.js.map → ChatApp-BE7ONQKS.js.map} +0 -0
  80. /package/dist/{agent-RZ46ZB4W.js.map → agent-Q74EISKP.js.map} +0 -0
  81. /package/dist/{board-dashboard-NRRYCDZW.js.map → board-dashboard-A7PQOI6C.js.map} +0 -0
  82. /package/dist/{chunk-W4YMMWJL.js.map → chunk-2MIOMYT2.js.map} +0 -0
  83. /package/dist/{chunk-EZN2YXRY.js.map → chunk-547G3HYQ.js.map} +0 -0
  84. /package/dist/{chunk-LKJSPO3S.js.map → chunk-5LLGIX5M.js.map} +0 -0
  85. /package/dist/{chunk-WOMQ2VGA.js.map → chunk-5W7NJHCS.js.map} +0 -0
  86. /package/dist/{chunk-4HEVSWO3.js.map → chunk-6OABTCRA.js.map} +0 -0
  87. /package/dist/{chunk-CELYD5QW.js.map → chunk-CQUVUVXR.js.map} +0 -0
  88. /package/dist/{chunk-EJ5ISSWW.js.map → chunk-I2LYULR4.js.map} +0 -0
  89. /package/dist/{chunk-76AHNHWC.js.map → chunk-KKGFZEXS.js.map} +0 -0
  90. /package/dist/{chunk-6AK6WKE5.js.map → chunk-KW7TIOS3.js.map} +0 -0
  91. /package/dist/{chunk-SA23V7WX.js.map → chunk-NKMFHEGV.js.map} +0 -0
  92. /package/dist/{chunk-JC5Q5MPI.js.map → chunk-POL7RAJQ.js.map} +0 -0
  93. /package/dist/{chunk-D3LSPFHK.js.map → chunk-Q6SMLSRO.js.map} +0 -0
  94. /package/dist/{chunk-723K4SAW.js.map → chunk-RXEAACVH.js.map} +0 -0
  95. /package/dist/{chunk-O3XG2NVR.js.map → chunk-U5YBQJ5Q.js.map} +0 -0
  96. /package/dist/{chunk-XEOAKD5C.js.map → chunk-V7ZK6ZIO.js.map} +0 -0
  97. /package/dist/{chunk-M4AG2YNX.js.map → chunk-VI7GBDKV.js.map} +0 -0
  98. /package/dist/{chunk-GSOL2LW6.js.map → chunk-VVBGPT6W.js.map} +0 -0
  99. /package/dist/{chunk-C3VRBEJ7.js.map → chunk-XTD3SZIP.js.map} +0 -0
  100. /package/dist/{client-IA7ZPE56.js.map → client-EFP2PKA3.js.map} +0 -0
  101. /package/dist/{config-HPN2F7LH.js.map → config-P6YXC6XZ.js.map} +0 -0
  102. /package/dist/{config-mutator-7GPTMTVB.js.map → config-mutator-3GKXLTHH.js.map} +0 -0
  103. /package/dist/{doctor-IBWQVQV4.js.map → doctor-JEWMHXYH.js.map} +0 -0
  104. /package/dist/{program-D5TAPSIH.js.map → program-7DUQJDSQ.js.map} +0 -0
  105. /package/dist/{promote-T4UJROEH.js.map → promote-KVPLDSKT.js.map} +0 -0
  106. /package/dist/{query-VUJC4Y5O.js.map → query-AGOMCYTT.js.map} +0 -0
  107. /package/dist/{registry-DX2RI7I7.js.map → registry-AUOI4TW2.js.map} +0 -0
  108. /package/dist/{request-planner-B7DE7VJR.js.map → request-planner-LMZ5ZAXC.js.map} +0 -0
  109. /package/dist/{review-candidates-4YJHIBAF.js.map → review-candidates-WCBXLLDS.js.map} +0 -0
  110. /package/dist/{routing-MZG3UODH.js.map → routing-FBASYKUL.js.map} +0 -0
  111. /package/dist/{schedule-tool-A4OFQ4YZ.js.map → schedule-tool-RJRJ63SS.js.map} +0 -0
  112. /package/dist/{scheduler-RHJW2V4M.js.map → scheduler-ATA7Z44S.js.map} +0 -0
  113. /package/dist/{seat-2PEI7JRX.js.map → seat-DBFW3Z5P.js.map} +0 -0
  114. /package/dist/{select-4KIUR27O.js.map → select-6CNVVFA5.js.map} +0 -0
  115. /package/dist/{set-enabled-SE7HQKPF.js.map → set-enabled-AYZH3KSB.js.map} +0 -0
  116. /package/dist/{tool-5VMD7IE4.js.map → tool-PKS4NBZ6.js.map} +0 -0
  117. /package/dist/{wiki-2QFLAH4U.js.map → wiki-QW6BDNEQ.js.map} +0 -0
@@ -19,11 +19,24 @@ final class App: NSObject, NSApplicationDelegate {
19
19
  private let panel = Panel()
20
20
  private let statusMenu = StatusMenu()
21
21
  private let card = ResultCard()
22
+ private let orb = OrbOverlay()
23
+ /// What the listener said this turn, shown under the orb.
24
+ private var heardLine = ""
25
+ /// The agent whose by-name answer is on screen now, while no turn runs.
26
+ private var asideSpeaker: String?
22
27
  private let recorder = Recorder()
23
28
  private var hotkey: Hotkey?
24
29
  private var pasteHotkey: Hotkey?
25
30
  private var stopHotkey: Hotkey?
26
31
  private var menuHotkey: Hotkey?
32
+ /// Hold to ask one agent, from agents[].voice.hotkey.
33
+ private var agentHotkeys: [Hotkey] = []
34
+ private let settingsWindow = SettingsWindow()
35
+ /// The saved settings: shortcuts and the speech-to-text engine. Read
36
+ /// from the daemon at launch and after every save; defaults until then.
37
+ private var settings: VoiceSettings?
38
+ /// The agent a per-agent shortcut is asking; nil for the talk key.
39
+ private var forcedAgent: String?
27
40
  /// Set by a stop: the answer being spoken ends without reopening the mic.
28
41
  private var silenced = false
29
42
  private var busy = false
@@ -83,6 +96,107 @@ final class App: NSObject, NSApplicationDelegate {
83
96
  statusMenu.thinking = counts
84
97
  }
85
98
 
99
+ // --- Shortcuts ---
100
+ //
101
+ // Talk, stop and smart paste come from agentx.json (voice.hotkeys),
102
+ // and each agent may have its own (agents[].voice.hotkey). ⌘⌥A, which
103
+ // opens the menu, is fixed. Saved in the settings window, they are
104
+ // registered again at once: no restart.
105
+
106
+ private func registerHotkeys() {
107
+ unregisterHotkeys()
108
+ let keys = settings?.general.hotkeys
109
+ func spec(_ text: String?, _ fallback: String) -> HotkeySpec {
110
+ text.flatMap(HotkeySpec.init) ?? HotkeySpec(fallback)!
111
+ }
112
+ let register = { (key: Hotkey, spec: HotkeySpec, what: String) in
113
+ if !key.register(spec) { Log.warn("shortcut \(spec.text ?? "?") for \(what) is taken by another app") }
114
+ }
115
+
116
+ // Hold to talk; release sends.
117
+ let talk = Hotkey(id: 1,
118
+ onPress: { [weak self] in self?.forcedAgent = nil; self?.startListening() },
119
+ onRelease: { [weak self] in self?.stopAndSend() })
120
+ register(talk, spec(keys?.talk, "opt+space"), "talk")
121
+ hotkey = talk
122
+
123
+ // Smart paste fires on RELEASE so the modifiers are up before cmd-V
124
+ // is sent; pressed while they are held it is a different chord.
125
+ let paste = Hotkey(id: 2, onPress: {}, onRelease: { [weak self] in self?.smartPaste() })
126
+ register(paste, spec(keys?.paste, "cmd+opt+v"), "smart paste")
127
+ pasteHotkey = paste
128
+
129
+ // Stop every voice now. ⌘. is the Mac's own "cancel"; Option keeps
130
+ // it from reaching the app in front.
131
+ let stop = Hotkey(id: 3, onPress: {}, onRelease: { [weak self] in self?.stopSpeaking() })
132
+ register(stop, spec(keys?.stop, "cmd+opt+period"), "stop")
133
+ stopHotkey = stop
134
+
135
+ // Open the menu-bar menu without the mouse.
136
+ let menu = Hotkey(id: 4, onPress: {}, onRelease: { [weak self] in self?.statusMenu.open() })
137
+ register(menu, HotkeySpec("cmd+opt+a")!, "the menu")
138
+ menuHotkey = menu
139
+
140
+ // Hold an agent's own shortcut to ask it; the target stays.
141
+ for (i, agent) in (settings?.agents ?? []).enumerated() {
142
+ guard let text = agent.voice.hotkey, let s = HotkeySpec(text) else { continue }
143
+ let id = agent.id
144
+ let key = Hotkey(id: UInt32(10 + i),
145
+ onPress: { [weak self] in self?.forcedAgent = id; self?.startListening() },
146
+ onRelease: { [weak self] in self?.stopAndSend() })
147
+ register(key, s, "asking \(agent.name)")
148
+ agentHotkeys.append(key)
149
+ }
150
+ }
151
+
152
+ /// Dropping a Hotkey unregisters it.
153
+ private func unregisterHotkeys() {
154
+ hotkey = nil; pasteHotkey = nil; stopHotkey = nil; menuHotkey = nil
155
+ agentHotkeys.removeAll()
156
+ }
157
+
158
+ /// Settings from the daemon: shortcuts now, the rest on next use.
159
+ private func apply(_ saved: VoiceSettings) {
160
+ settings = saved
161
+ if settingsWindow.model.recording == nil { registerHotkeys() }
162
+ // Colours may have changed.
163
+ statusMenu.refresh()
164
+ Log.info("settings: talk \(saved.general.hotkeys.talk), speech to text \(saved.general.stt)")
165
+ }
166
+
167
+ /// Who the panel and orb name: the agent a shortcut is asking, a
168
+ /// by-name answer being spoken, else the agent our turn is asking,
169
+ /// else the target.
170
+ private var shownAgent: String {
171
+ if let forcedAgent, recorder.isRecording { return forcedAgent }
172
+ if let asideSpeaker { return asideSpeaker }
173
+ return busy && !turnAgent.isEmpty ? turnAgent : Config.effectiveAgentID
174
+ }
175
+
176
+ /// The orb follows the pill's state: listening, thinking or speaking
177
+ /// show it; idle and errors hide it.
178
+ private func renderOrb(_ state: Panel.State) {
179
+ guard Config.showOrb else { orb.hide(); return }
180
+ let who = shownAgent
181
+ let name = who.isEmpty ? "" : statusMenu.name(of: who)
182
+ let tint = statusMenu.color(of: who)
183
+ switch state {
184
+ case .idle, .error:
185
+ orb.hide()
186
+ case .listening:
187
+ heardLine = ""
188
+ orb.show(.listening, name: name, tint: tint, line: "Listening…", status: "")
189
+ case .thinking:
190
+ orb.show(.thinking, name: name, tint: tint, line: heardLine, status: "Thinking…")
191
+ case .working(let step, let secs):
192
+ orb.show(.thinking, name: name, tint: tint, line: heardLine, status: "\(step) · \(secs)s")
193
+ case .speaking:
194
+ orb.show(.speaking, name: name, tint: tint, line: heardLine, status: "")
195
+ case .saying(let text):
196
+ orb.show(.speaking, name: name, tint: tint, line: text, status: "")
197
+ }
198
+ }
199
+
86
200
  /// ⌘⌥V. Everything except the hotkey lives in `agentx paste`.
87
201
  private func smartPaste() {
88
202
  guard !busy else { return }
@@ -129,15 +243,26 @@ final class App: NSObject, NSApplicationDelegate {
129
243
  func applicationDidFinishLaunching(_ note: Notification) {
130
244
  NSApp.setActivationPolicy(.accessory)
131
245
  retireOlderInstances()
132
- panel.onRender = { [weak self] state in self?.statusMenu.show(state) }
246
+ panel.onRender = { [weak self] state in
247
+ self?.statusMenu.show(state)
248
+ self?.renderOrb(state)
249
+ }
133
250
  panel.contextMenu = { [weak self] in self?.statusMenu.menu ?? NSMenu() }
134
251
  panel.agentName = { [weak self] in
135
252
  guard let self else { return "" }
136
- // A question sent by name shows who is answering it, not the target.
137
- let who = self.busy && !self.turnAgent.isEmpty ? self.turnAgent : Config.effectiveAgentID
253
+ let who = self.shownAgent
138
254
  return who.isEmpty ? "" : self.statusMenu.name(of: who)
139
255
  }
140
256
  panel.alwaysVisible = Config.showPill
257
+ panel.yieldsActiveStates = Config.showOrb
258
+ orb.levelSource = { [weak self] in self?.recorder.level ?? 0 }
259
+ card.anchor = { [weak self] in self?.orb.contentFrame }
260
+ statusMenu.onOrbChanged = { [weak self] on in
261
+ guard let self else { return }
262
+ self.panel.yieldsActiveStates = on
263
+ if !on { self.orb.hide() }
264
+ if !self.busy && !self.recorder.isRecording { self.panel.render(.idle) }
265
+ }
141
266
  panel.render(.idle)
142
267
  statusMenu.onPillChanged = { [weak self] on in
143
268
  guard let self else { return }
@@ -152,12 +277,19 @@ final class App: NSObject, NSApplicationDelegate {
152
277
  }
153
278
  statusMenu.refresh()
154
279
 
155
- hotkey = Hotkey(
156
- id: 1,
157
- onPress: { [weak self] in self?.startListening() },
158
- onRelease: { [weak self] in self?.stopAndSend() })
159
- hotkey?.register()
280
+ registerHotkeys()
160
281
  panel.onClick = { [weak self] in self?.toggleListening() }
282
+ statusMenu.onSettings = { [weak self] in self?.settingsWindow.show() }
283
+ settingsWindow.model.onSaved = { [weak self] saved in self?.apply(saved) }
284
+ // While a shortcut field waits for keys, ours stand aside so the
285
+ // keys reach the field.
286
+ settingsWindow.model.onRecording = { [weak self] recording in
287
+ guard let self else { return }
288
+ if recording { self.unregisterHotkeys() } else { self.registerHotkeys() }
289
+ }
290
+ Task { @MainActor in
291
+ if let saved = await AgentClient.settings() { apply(saved) }
292
+ }
161
293
 
162
294
  // Turning the hold OFF is the moment anything that piled up
163
295
  // becomes welcome. Without this the queue is a hole rather than a
@@ -170,36 +302,8 @@ final class App: NSObject, NSApplicationDelegate {
170
302
  Log.info(nowOn ? "notifications held" : "notifications delivering")
171
303
  }
172
304
 
173
- // ⌘⌥V: reshape the clipboard for wherever the caret is, then paste.
174
- // Fires on RELEASE so the modifiers are up before cmd-V is sent —
175
- // pressing it while ⌘⌥ are still held produces a different chord
176
- // in the destination app.
177
- pasteHotkey = Hotkey(
178
- id: 2,
179
- onPress: {},
180
- onRelease: { [weak self] in self?.smartPaste() })
181
- pasteHotkey?.register(keyCode: UInt32(kVK_ANSI_V),
182
- modifiers: UInt32(cmdKey | optionKey))
183
-
184
- // ⌘⌥. (and the menu): stop every voice now. ⌘. is the Mac's
185
- // own "cancel"; Option keeps it from reaching the app in front.
186
- stopHotkey = Hotkey(
187
- id: 3,
188
- onPress: {},
189
- onRelease: { [weak self] in self?.stopSpeaking() })
190
- stopHotkey?.register(keyCode: UInt32(kVK_ANSI_Period),
191
- modifiers: UInt32(cmdKey | optionKey))
192
305
  statusMenu.onStop = { [weak self] in self?.stopSpeaking() }
193
306
 
194
- // ⌘⌥A: open the menu-bar menu without the mouse; arrows, digits
195
- // and Return work from there.
196
- menuHotkey = Hotkey(
197
- id: 4,
198
- onPress: {},
199
- onRelease: { [weak self] in self?.statusMenu.open() })
200
- menuHotkey?.register(keyCode: UInt32(kVK_ANSI_A),
201
- modifiers: UInt32(cmdKey | optionKey))
202
-
203
307
  // The target does not wait for the microphone: the menu shows it
204
308
  // either way.
205
309
  Task { @MainActor in
@@ -259,6 +363,7 @@ final class App: NSObject, NSApplicationDelegate {
259
363
  openedAt = Date()
260
364
  patience = followUp ? followUpPatience : clickPatience
261
365
  silentClose = followUp
366
+ asideSpeaker = nil
262
367
  panel.render(.listening)
263
368
 
264
369
  listenPoll?.invalidate()
@@ -324,6 +429,7 @@ final class App: NSObject, NSApplicationDelegate {
324
429
  stopGeneration += 1
325
430
  waitingAsides.removeAll()
326
431
  publishThinking()
432
+ asideSpeaker = nil
327
433
  lastSpokeAt = Date()
328
434
  Task { await Speech.stopAll() }
329
435
  if !recorder.isRecording { panel.render(.idle) }
@@ -347,6 +453,7 @@ final class App: NSObject, NSApplicationDelegate {
347
453
  Log.info("door: opened\(busy ? " (a turn is running)" : "")")
348
454
  do {
349
455
  try recorder.start()
456
+ asideSpeaker = nil
350
457
  panel.render(.listening)
351
458
  } catch {
352
459
  panel.render(.error(error.localizedDescription))
@@ -359,6 +466,8 @@ final class App: NSObject, NSApplicationDelegate {
359
466
  // hush now; whatever was talking gets the words, same as the key.
360
467
  let door = talkCheck ?? Task { await AgentClient.hush() }
361
468
  talkCheck = nil
469
+ let forced = forcedAgent
470
+ forcedAgent = nil
362
471
  guard let wav = recorder.stop() else {
363
472
  // Nothing said, so no door follows the hush: let the queue play on.
364
473
  Task { _ = await door.value; await AgentClient.resume() }
@@ -372,7 +481,7 @@ final class App: NSObject, NSApplicationDelegate {
372
481
 
373
482
  Task { @MainActor in
374
483
  var heard = ""
375
- do { heard = try await Speech.transcribe(wav: wav) }
484
+ do { heard = try await Speech.transcribe(wav: wav, engine: settings?.general.stt ?? "auto") }
376
485
  catch { Log.warn("transcription failed: \(error.localizedDescription)") }
377
486
  guard !heard.isEmpty else {
378
487
  _ = await door.value
@@ -386,6 +495,8 @@ final class App: NSObject, NSApplicationDelegate {
386
495
  return
387
496
  }
388
497
  Log.info("heard: \(heard)")
498
+ heardLine = heard
499
+ if orb.isVisible { orb.model.line = heard }
389
500
  let hushed = await door.value
390
501
  // Always through the door, even with nothing hushed: the
391
502
  // listener's turn is over, so the daemon's queue plays on.
@@ -395,8 +506,11 @@ final class App: NSObject, NSApplicationDelegate {
395
506
  if !midTurn { panel.render(.idle); busy = false }
396
507
  return
397
508
  }
398
- // "Writer, …" sends this one question to Writer; the target stays.
399
- let agent = await AgentClient.address(heard, target: Config.effectiveAgentID)
509
+ // "Writer, …" sends this one question to Writer; the target
510
+ // stays. An agent's own shortcut already said who.
511
+ let agent: String
512
+ if let forced { agent = forced }
513
+ else { agent = await AgentClient.address(heard, target: Config.effectiveAgentID) }
400
514
  if midTurn {
401
515
  // Another agent: ask it alongside ours rather than waiting.
402
516
  if agent != turnAgent && !Self.isStop(heard) {
@@ -466,7 +580,15 @@ final class App: NSObject, NSApplicationDelegate {
466
580
  card.show(spoken: answer.text, written: answer.written,
467
581
  buttons: answer.buttons, imageURL: answer.imageURL)
468
582
  }
583
+ // Nothing else on screen: the orb shows this answer, in
584
+ // this agent's colour, while it is spoken.
585
+ let onScreen = !busy && !recorder.isRecording
586
+ if onScreen { asideSpeaker = agent; panel.render(.saying(answer.text)) }
469
587
  await Speech.say(answer.text, agentID: answer.agentID ?? agent, kind: "answer", voice: answer.voice)
588
+ if onScreen && asideSpeaker == agent {
589
+ asideSpeaker = nil
590
+ if !busy && !recorder.isRecording { panel.render(.idle) }
591
+ }
470
592
  case .failure(let error):
471
593
  Log.warn("aside failed (\(agent)): \(error.localizedDescription)")
472
594
  await Speech.say("Sorry, \(statusMenu.name(of: agent)) couldn't answer that.", agentID: nil, kind: "line", voice: nil)
@@ -18,7 +18,7 @@ rm -rf build && mkdir -p "$BIN" "$APP/Contents/Resources"
18
18
  swiftc -O \
19
19
  -o "$BIN/AgentXVoice" \
20
20
  Sources/AgentXVoice/*.swift \
21
- -framework AppKit -framework AVFoundation -framework Carbon \
21
+ -framework AppKit -framework AVFoundation -framework Carbon -framework SwiftUI \
22
22
  -target arm64-apple-macosx14.0
23
23
 
24
24
  cp Resources/Info.plist "$APP/Contents/Info.plist"
@@ -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-WOMQ2VGA.js";import"./chunk-U4H35SJS.js";import"./chunk-QIJLFPWE.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-5W7NJHCS.js";import"./chunk-ERL6AS3O.js";import"./chunk-QIJLFPWE.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-4EM7IPB3.js.map
8
+ //# sourceMappingURL=App-Z5VOAUXB.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-WOMQ2VGA.js";import"./chunk-U4H35SJS.js";import"./chunk-QIJLFPWE.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-5W7NJHCS.js";import"./chunk-ERL6AS3O.js";import"./chunk-QIJLFPWE.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-FYTEEGSU.js.map
7
+ //# sourceMappingURL=ChatApp-BE7ONQKS.js.map
@@ -1,2 +1,2 @@
1
- import{A as a,B as b,C as c}from"./chunk-6AK6WKE5.js";import"./chunk-SFQUP3BP.js";import"./chunk-QOYAX2FT.js";import"./chunk-SZJMS7K4.js";import"./chunk-7I7IEX4S.js";import"./chunk-VDMYBPYB.js";import"./chunk-DSYKYZMT.js";import"./chunk-QIJLFPWE.js";export{a as createAgentContext,b as generate,c as generateStream};
2
- //# sourceMappingURL=agent-RZ46ZB4W.js.map
1
+ import{A as a,B as b,C as c}from"./chunk-KW7TIOS3.js";import"./chunk-SFQUP3BP.js";import"./chunk-QOYAX2FT.js";import"./chunk-SZJMS7K4.js";import"./chunk-7I7IEX4S.js";import"./chunk-VDMYBPYB.js";import"./chunk-DSYKYZMT.js";import"./chunk-QIJLFPWE.js";export{a as createAgentContext,b as generate,c as generateStream};
2
+ //# sourceMappingURL=agent-Q74EISKP.js.map