agentix-cli 0.56.0 → 0.58.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 (116) hide show
  1. package/apps/mac-voice/Sources/AgentXVoice/AgentClient.swift +43 -0
  2. package/apps/mac-voice/Sources/AgentXVoice/Config.swift +19 -5
  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 +131 -169
  6. package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +174 -35
  7. package/apps/mac-voice/Sources/AgentXVoice/PillPlacement.swift +70 -0
  8. package/apps/mac-voice/Sources/AgentXVoice/ResultCard.swift +26 -22
  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 +22 -6
  12. package/apps/mac-voice/Sources/AgentXVoice/VoiceSettings.swift +101 -0
  13. package/apps/mac-voice/Sources/AgentXVoice/main.swift +132 -79
  14. package/dist/{App-4EM7IPB3.js → App-Z5VOAUXB.js} +2 -2
  15. package/dist/{ChatApp-FYTEEGSU.js → ChatApp-BE7ONQKS.js} +2 -2
  16. package/dist/{agent-RZ46ZB4W.js → agent-Q74EISKP.js} +2 -2
  17. package/dist/{board-dashboard-NRRYCDZW.js → board-dashboard-A7PQOI6C.js} +4 -4
  18. package/dist/{chunk-W4YMMWJL.js → chunk-2MIOMYT2.js} +2 -2
  19. package/dist/{chunk-EZN2YXRY.js → chunk-547G3HYQ.js} +2 -2
  20. package/dist/{chunk-LKJSPO3S.js → chunk-5LLGIX5M.js} +2 -2
  21. package/dist/{chunk-WOMQ2VGA.js → chunk-5W7NJHCS.js} +2 -2
  22. package/dist/{chunk-4HEVSWO3.js → chunk-6OABTCRA.js} +2 -2
  23. package/dist/{chunk-UIVHSYCN.js → chunk-6QNPCVRN.js} +3 -3
  24. package/dist/chunk-6QNPCVRN.js.map +1 -0
  25. package/dist/{chunk-CELYD5QW.js → chunk-CQUVUVXR.js} +2 -2
  26. package/dist/chunk-ERL6AS3O.js +15 -0
  27. package/dist/chunk-ERL6AS3O.js.map +1 -0
  28. package/dist/{chunk-EJ5ISSWW.js → chunk-I2LYULR4.js} +5 -5
  29. package/dist/{chunk-76AHNHWC.js → chunk-KKGFZEXS.js} +2 -2
  30. package/dist/{chunk-6AK6WKE5.js → chunk-KW7TIOS3.js} +2 -2
  31. package/dist/{chunk-SA23V7WX.js → chunk-NKMFHEGV.js} +2 -2
  32. package/dist/{chunk-WKMTT2UY.js → chunk-OZZPJ2YG.js} +197 -188
  33. package/dist/chunk-OZZPJ2YG.js.map +1 -0
  34. package/dist/{chunk-IL2XLW4H.js → chunk-POL7RAJQ.js} +13 -13
  35. package/dist/{chunk-D3LSPFHK.js → chunk-Q6SMLSRO.js} +2 -2
  36. package/dist/{chunk-723K4SAW.js → chunk-RXEAACVH.js} +2 -2
  37. package/dist/{chunk-O3XG2NVR.js → chunk-U5YBQJ5Q.js} +2 -2
  38. package/dist/{chunk-XEOAKD5C.js → chunk-V7ZK6ZIO.js} +2 -2
  39. package/dist/{chunk-M4AG2YNX.js → chunk-VI7GBDKV.js} +2 -2
  40. package/dist/{chunk-GSOL2LW6.js → chunk-VVBGPT6W.js} +2 -2
  41. package/dist/{chunk-C3VRBEJ7.js → chunk-XTD3SZIP.js} +2 -2
  42. package/dist/cli.js +1 -1
  43. package/dist/client-EFP2PKA3.js +2 -0
  44. package/dist/config-P6YXC6XZ.js +2 -0
  45. package/dist/config-mutator-3GKXLTHH.js +2 -0
  46. package/dist/doctor-JEWMHXYH.js +2 -0
  47. package/dist/index.d.ts +109 -3
  48. package/dist/index.js +1 -1
  49. package/dist/program-7DUQJDSQ.js +2 -0
  50. package/dist/{promote-T4UJROEH.js → promote-KVPLDSKT.js} +2 -2
  51. package/dist/query-AGOMCYTT.js +2 -0
  52. package/dist/{registry-DX2RI7I7.js → registry-AUOI4TW2.js} +2 -2
  53. package/dist/{request-planner-B7DE7VJR.js → request-planner-LMZ5ZAXC.js} +2 -2
  54. package/dist/{review-candidates-4YJHIBAF.js → review-candidates-WCBXLLDS.js} +2 -2
  55. package/dist/{routing-MZG3UODH.js → routing-FBASYKUL.js} +2 -2
  56. package/dist/schedule-tool-RJRJ63SS.js +2 -0
  57. package/dist/scheduler-ATA7Z44S.js +2 -0
  58. package/dist/{seat-2PEI7JRX.js → seat-DBFW3Z5P.js} +2 -2
  59. package/dist/{select-4KIUR27O.js → select-6CNVVFA5.js} +2 -2
  60. package/dist/{set-enabled-SE7HQKPF.js → set-enabled-AYZH3KSB.js} +2 -2
  61. package/dist/{tool-5VMD7IE4.js → tool-PKS4NBZ6.js} +2 -2
  62. package/dist/wiki-QW6BDNEQ.js +2 -0
  63. package/package.json +1 -1
  64. package/dist/chunk-U4H35SJS.js +0 -15
  65. package/dist/chunk-U4H35SJS.js.map +0 -1
  66. package/dist/chunk-UIVHSYCN.js.map +0 -1
  67. package/dist/chunk-WKMTT2UY.js.map +0 -1
  68. package/dist/client-IA7ZPE56.js +0 -2
  69. package/dist/config-HPN2F7LH.js +0 -2
  70. package/dist/config-mutator-7GPTMTVB.js +0 -2
  71. package/dist/doctor-IBWQVQV4.js +0 -2
  72. package/dist/program-G6DIFJUF.js +0 -2
  73. package/dist/query-VUJC4Y5O.js +0 -2
  74. package/dist/schedule-tool-A4OFQ4YZ.js +0 -2
  75. package/dist/scheduler-RHJW2V4M.js +0 -2
  76. package/dist/wiki-2QFLAH4U.js +0 -2
  77. /package/dist/{App-4EM7IPB3.js.map → App-Z5VOAUXB.js.map} +0 -0
  78. /package/dist/{ChatApp-FYTEEGSU.js.map → ChatApp-BE7ONQKS.js.map} +0 -0
  79. /package/dist/{agent-RZ46ZB4W.js.map → agent-Q74EISKP.js.map} +0 -0
  80. /package/dist/{board-dashboard-NRRYCDZW.js.map → board-dashboard-A7PQOI6C.js.map} +0 -0
  81. /package/dist/{chunk-W4YMMWJL.js.map → chunk-2MIOMYT2.js.map} +0 -0
  82. /package/dist/{chunk-EZN2YXRY.js.map → chunk-547G3HYQ.js.map} +0 -0
  83. /package/dist/{chunk-LKJSPO3S.js.map → chunk-5LLGIX5M.js.map} +0 -0
  84. /package/dist/{chunk-WOMQ2VGA.js.map → chunk-5W7NJHCS.js.map} +0 -0
  85. /package/dist/{chunk-4HEVSWO3.js.map → chunk-6OABTCRA.js.map} +0 -0
  86. /package/dist/{chunk-CELYD5QW.js.map → chunk-CQUVUVXR.js.map} +0 -0
  87. /package/dist/{chunk-EJ5ISSWW.js.map → chunk-I2LYULR4.js.map} +0 -0
  88. /package/dist/{chunk-76AHNHWC.js.map → chunk-KKGFZEXS.js.map} +0 -0
  89. /package/dist/{chunk-6AK6WKE5.js.map → chunk-KW7TIOS3.js.map} +0 -0
  90. /package/dist/{chunk-SA23V7WX.js.map → chunk-NKMFHEGV.js.map} +0 -0
  91. /package/dist/{chunk-IL2XLW4H.js.map → chunk-POL7RAJQ.js.map} +0 -0
  92. /package/dist/{chunk-D3LSPFHK.js.map → chunk-Q6SMLSRO.js.map} +0 -0
  93. /package/dist/{chunk-723K4SAW.js.map → chunk-RXEAACVH.js.map} +0 -0
  94. /package/dist/{chunk-O3XG2NVR.js.map → chunk-U5YBQJ5Q.js.map} +0 -0
  95. /package/dist/{chunk-XEOAKD5C.js.map → chunk-V7ZK6ZIO.js.map} +0 -0
  96. /package/dist/{chunk-M4AG2YNX.js.map → chunk-VI7GBDKV.js.map} +0 -0
  97. /package/dist/{chunk-GSOL2LW6.js.map → chunk-VVBGPT6W.js.map} +0 -0
  98. /package/dist/{chunk-C3VRBEJ7.js.map → chunk-XTD3SZIP.js.map} +0 -0
  99. /package/dist/{client-IA7ZPE56.js.map → client-EFP2PKA3.js.map} +0 -0
  100. /package/dist/{config-HPN2F7LH.js.map → config-P6YXC6XZ.js.map} +0 -0
  101. /package/dist/{config-mutator-7GPTMTVB.js.map → config-mutator-3GKXLTHH.js.map} +0 -0
  102. /package/dist/{doctor-IBWQVQV4.js.map → doctor-JEWMHXYH.js.map} +0 -0
  103. /package/dist/{program-G6DIFJUF.js.map → program-7DUQJDSQ.js.map} +0 -0
  104. /package/dist/{promote-T4UJROEH.js.map → promote-KVPLDSKT.js.map} +0 -0
  105. /package/dist/{query-VUJC4Y5O.js.map → query-AGOMCYTT.js.map} +0 -0
  106. /package/dist/{registry-DX2RI7I7.js.map → registry-AUOI4TW2.js.map} +0 -0
  107. /package/dist/{request-planner-B7DE7VJR.js.map → request-planner-LMZ5ZAXC.js.map} +0 -0
  108. /package/dist/{review-candidates-4YJHIBAF.js.map → review-candidates-WCBXLLDS.js.map} +0 -0
  109. /package/dist/{routing-MZG3UODH.js.map → routing-FBASYKUL.js.map} +0 -0
  110. /package/dist/{schedule-tool-A4OFQ4YZ.js.map → schedule-tool-RJRJ63SS.js.map} +0 -0
  111. /package/dist/{scheduler-RHJW2V4M.js.map → scheduler-ATA7Z44S.js.map} +0 -0
  112. /package/dist/{seat-2PEI7JRX.js.map → seat-DBFW3Z5P.js.map} +0 -0
  113. /package/dist/{select-4KIUR27O.js.map → select-6CNVVFA5.js.map} +0 -0
  114. /package/dist/{set-enabled-SE7HQKPF.js.map → set-enabled-AYZH3KSB.js.map} +0 -0
  115. /package/dist/{tool-5VMD7IE4.js.map → tool-PKS4NBZ6.js.map} +0 -0
  116. /package/dist/{wiki-2QFLAH4U.js.map → wiki-QW6BDNEQ.js.map} +0 -0
@@ -19,9 +19,6 @@ 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
22
  /// The agent whose by-name answer is on screen now, while no turn runs.
26
23
  private var asideSpeaker: String?
27
24
  private let recorder = Recorder()
@@ -29,6 +26,14 @@ final class App: NSObject, NSApplicationDelegate {
29
26
  private var pasteHotkey: Hotkey?
30
27
  private var stopHotkey: Hotkey?
31
28
  private var menuHotkey: Hotkey?
29
+ /// Hold to ask one agent, from agents[].voice.hotkey.
30
+ private var agentHotkeys: [Hotkey] = []
31
+ private let settingsWindow = SettingsWindow()
32
+ /// The saved settings: shortcuts and the speech-to-text engine. Read
33
+ /// from the daemon at launch and after every save; defaults until then.
34
+ private var settings: VoiceSettings?
35
+ /// The agent a per-agent shortcut is asking; nil for the talk key.
36
+ private var forcedAgent: String?
32
37
  /// Set by a stop: the answer being spoken ends without reopening the mic.
33
38
  private var silenced = false
34
39
  private var busy = false
@@ -88,35 +93,96 @@ final class App: NSObject, NSApplicationDelegate {
88
93
  statusMenu.thinking = counts
89
94
  }
90
95
 
91
- /// Who the panel and orb name: a by-name answer being spoken, else the
92
- /// agent our turn is asking, else the target.
96
+ // --- Shortcuts ---
97
+ //
98
+ // Talk, stop and smart paste come from agentx.json (voice.hotkeys),
99
+ // and each agent may have its own (agents[].voice.hotkey). ⌘⌥A, which
100
+ // opens the menu, is fixed. Saved in the settings window, they are
101
+ // registered again at once: no restart.
102
+
103
+ private func registerHotkeys() {
104
+ unregisterHotkeys()
105
+ let keys = settings?.general.hotkeys
106
+ func spec(_ text: String?, _ fallback: String) -> HotkeySpec {
107
+ text.flatMap(HotkeySpec.init) ?? HotkeySpec(fallback)!
108
+ }
109
+ let register = { (key: Hotkey, spec: HotkeySpec, what: String) in
110
+ if !key.register(spec) { Log.warn("shortcut \(spec.text ?? "?") for \(what) is taken by another app") }
111
+ }
112
+
113
+ // Hold to talk; release sends.
114
+ let talk = Hotkey(id: 1,
115
+ onPress: { [weak self] in self?.forcedAgent = nil; self?.startListening() },
116
+ onRelease: { [weak self] in self?.stopAndSend() })
117
+ register(talk, spec(keys?.talk, "opt+space"), "talk")
118
+ hotkey = talk
119
+
120
+ // Smart paste fires on RELEASE so the modifiers are up before cmd-V
121
+ // is sent; pressed while they are held it is a different chord.
122
+ let paste = Hotkey(id: 2, onPress: {}, onRelease: { [weak self] in self?.smartPaste() })
123
+ register(paste, spec(keys?.paste, "cmd+opt+v"), "smart paste")
124
+ pasteHotkey = paste
125
+
126
+ // Stop every voice now. ⌘. is the Mac's own "cancel"; Option keeps
127
+ // it from reaching the app in front.
128
+ let stop = Hotkey(id: 3, onPress: {}, onRelease: { [weak self] in self?.stopSpeaking() })
129
+ register(stop, spec(keys?.stop, "cmd+opt+period"), "stop")
130
+ stopHotkey = stop
131
+
132
+ // Open the menu-bar menu without the mouse.
133
+ let menu = Hotkey(id: 4, onPress: {}, onRelease: { [weak self] in self?.statusMenu.open() })
134
+ register(menu, HotkeySpec("cmd+opt+a")!, "the menu")
135
+ menuHotkey = menu
136
+
137
+ // Hold an agent's own shortcut to ask it; the target stays.
138
+ for (i, agent) in (settings?.agents ?? []).enumerated() {
139
+ guard let text = agent.voice.hotkey, let s = HotkeySpec(text) else { continue }
140
+ let id = agent.id
141
+ let key = Hotkey(id: UInt32(10 + i),
142
+ onPress: { [weak self] in self?.forcedAgent = id; self?.startListening() },
143
+ onRelease: { [weak self] in self?.stopAndSend() })
144
+ register(key, s, "asking \(agent.name)")
145
+ agentHotkeys.append(key)
146
+ }
147
+ }
148
+
149
+ /// Dropping a Hotkey unregisters it.
150
+ private func unregisterHotkeys() {
151
+ hotkey = nil; pasteHotkey = nil; stopHotkey = nil; menuHotkey = nil
152
+ agentHotkeys.removeAll()
153
+ }
154
+
155
+ /// Settings from the daemon: shortcuts now, the rest on next use.
156
+ private func apply(_ saved: VoiceSettings) {
157
+ settings = saved
158
+ if settingsWindow.model.recording == nil { registerHotkeys() }
159
+ // Colours may have changed.
160
+ statusMenu.refresh()
161
+ Log.info("settings: talk \(saved.general.hotkeys.talk), speech to text \(saved.general.stt)")
162
+ }
163
+
164
+ /// Who the pill names and whose colour its orb wears: the agent a
165
+ /// shortcut is asking, a by-name answer being spoken, else the agent
166
+ /// our turn is asking, else the target.
93
167
  private var shownAgent: String {
168
+ if let forcedAgent, recorder.isRecording { return forcedAgent }
94
169
  if let asideSpeaker { return asideSpeaker }
95
170
  return busy && !turnAgent.isEmpty ? turnAgent : Config.effectiveAgentID
96
171
  }
97
172
 
98
- /// The orb follows the pill's state: listening, thinking or speaking
99
- /// show it; idle and errors hide it.
100
- private func renderOrb(_ state: Panel.State) {
101
- guard Config.showOrb else { orb.hide(); return }
102
- let who = shownAgent
103
- let name = who.isEmpty ? "" : statusMenu.name(of: who)
104
- let tint = statusMenu.color(of: who)
105
- switch state {
106
- case .idle, .error:
107
- orb.hide()
108
- case .listening:
109
- heardLine = ""
110
- orb.show(.listening, name: name, tint: tint, line: "Listening…", status: "")
111
- case .thinking:
112
- orb.show(.thinking, name: name, tint: tint, line: heardLine, status: "Thinking…")
113
- case .working(let step, let secs):
114
- orb.show(.thinking, name: name, tint: tint, line: heardLine, status: "\(step) · \(secs)s")
115
- case .speaking:
116
- orb.show(.speaking, name: name, tint: tint, line: heardLine, status: "")
117
- case .saying(let text):
118
- orb.show(.speaking, name: name, tint: tint, line: text, status: "")
173
+ /// Close, Esc or "Hide pill": the pill goes and the voice stops, the
174
+ /// same stop as ⌘⌥. An open microphone closes without sending. The
175
+ /// next talk key brings the pill back.
176
+ private func dismissPill() {
177
+ Log.info("pill: dismissed")
178
+ panel.dismiss()
179
+ if recorder.isRecording {
180
+ stopPolling()
181
+ _ = recorder.stop()
182
+ forcedAgent = nil
183
+ talkCheck = nil
119
184
  }
185
+ stopSpeaking()
120
186
  }
121
187
 
122
188
  /// ⌘⌥V. Everything except the hotkey lives in `agentx paste`.
@@ -165,30 +231,33 @@ final class App: NSObject, NSApplicationDelegate {
165
231
  func applicationDidFinishLaunching(_ note: Notification) {
166
232
  NSApp.setActivationPolicy(.accessory)
167
233
  retireOlderInstances()
168
- panel.onRender = { [weak self] state in
169
- self?.statusMenu.show(state)
170
- self?.renderOrb(state)
171
- }
234
+ panel.onRender = { [weak self] state in self?.statusMenu.show(state) }
172
235
  panel.contextMenu = { [weak self] in self?.statusMenu.menu ?? NSMenu() }
173
236
  panel.agentName = { [weak self] in
174
237
  guard let self else { return "" }
175
238
  let who = self.shownAgent
176
239
  return who.isEmpty ? "" : self.statusMenu.name(of: who)
177
240
  }
178
- panel.alwaysVisible = Config.showPill
179
- panel.yieldsActiveStates = Config.showOrb
180
- orb.levelSource = { [weak self] in self?.recorder.level ?? 0 }
181
- card.anchor = { [weak self] in self?.orb.contentFrame }
182
- statusMenu.onOrbChanged = { [weak self] on in
183
- guard let self else { return }
184
- self.panel.yieldsActiveStates = on
185
- if !on { self.orb.hide() }
186
- if !self.busy && !self.recorder.isRecording { self.panel.render(.idle) }
241
+ panel.agentTint = { [weak self] in
242
+ guard let self else { return Brand.accent }
243
+ return self.statusMenu.color(of: self.shownAgent)
187
244
  }
245
+ panel.alwaysVisible = Config.showPill
246
+ panel.orb.setAnimated(Config.animatedOrb)
247
+ panel.orb.levelSource = { [weak self] in self?.recorder.level ?? 0 }
248
+ panel.onDismiss = { [weak self] in self?.dismissPill() }
249
+ panel.onMove = { [weak self] in self?.card.follow() }
250
+ card.anchor = { [weak self] in self?.panel.frame ?? .zero }
251
+ statusMenu.pillVisible = { [weak self] in self?.panel.isVisible ?? false }
252
+ statusMenu.onHidePill = { [weak self] in self?.dismissPill() }
253
+ statusMenu.onResetPosition = { [weak self] in self?.panel.resetPosition() }
254
+ statusMenu.onAnimatedOrbChanged = { [weak self] on in self?.panel.orb.setAnimated(on) }
188
255
  panel.render(.idle)
189
256
  statusMenu.onPillChanged = { [weak self] on in
190
257
  guard let self else { return }
191
258
  self.panel.alwaysVisible = on
259
+ // Asking for the pill brings back one that was dismissed.
260
+ if on { self.panel.summon() }
192
261
  if !self.busy && !self.recorder.isRecording { self.panel.render(.idle) }
193
262
  }
194
263
  statusMenu.onTargetChanged = { [weak self] id in
@@ -199,12 +268,19 @@ final class App: NSObject, NSApplicationDelegate {
199
268
  }
200
269
  statusMenu.refresh()
201
270
 
202
- hotkey = Hotkey(
203
- id: 1,
204
- onPress: { [weak self] in self?.startListening() },
205
- onRelease: { [weak self] in self?.stopAndSend() })
206
- hotkey?.register()
271
+ registerHotkeys()
207
272
  panel.onClick = { [weak self] in self?.toggleListening() }
273
+ statusMenu.onSettings = { [weak self] in self?.settingsWindow.show() }
274
+ settingsWindow.model.onSaved = { [weak self] saved in self?.apply(saved) }
275
+ // While a shortcut field waits for keys, ours stand aside so the
276
+ // keys reach the field.
277
+ settingsWindow.model.onRecording = { [weak self] recording in
278
+ guard let self else { return }
279
+ if recording { self.unregisterHotkeys() } else { self.registerHotkeys() }
280
+ }
281
+ Task { @MainActor in
282
+ if let saved = await AgentClient.settings() { apply(saved) }
283
+ }
208
284
 
209
285
  // Turning the hold OFF is the moment anything that piled up
210
286
  // becomes welcome. Without this the queue is a hole rather than a
@@ -217,36 +293,8 @@ final class App: NSObject, NSApplicationDelegate {
217
293
  Log.info(nowOn ? "notifications held" : "notifications delivering")
218
294
  }
219
295
 
220
- // ⌘⌥V: reshape the clipboard for wherever the caret is, then paste.
221
- // Fires on RELEASE so the modifiers are up before cmd-V is sent —
222
- // pressing it while ⌘⌥ are still held produces a different chord
223
- // in the destination app.
224
- pasteHotkey = Hotkey(
225
- id: 2,
226
- onPress: {},
227
- onRelease: { [weak self] in self?.smartPaste() })
228
- pasteHotkey?.register(keyCode: UInt32(kVK_ANSI_V),
229
- modifiers: UInt32(cmdKey | optionKey))
230
-
231
- // ⌘⌥. (and the menu): stop every voice now. ⌘. is the Mac's
232
- // own "cancel"; Option keeps it from reaching the app in front.
233
- stopHotkey = Hotkey(
234
- id: 3,
235
- onPress: {},
236
- onRelease: { [weak self] in self?.stopSpeaking() })
237
- stopHotkey?.register(keyCode: UInt32(kVK_ANSI_Period),
238
- modifiers: UInt32(cmdKey | optionKey))
239
296
  statusMenu.onStop = { [weak self] in self?.stopSpeaking() }
240
297
 
241
- // ⌘⌥A: open the menu-bar menu without the mouse; arrows, digits
242
- // and Return work from there.
243
- menuHotkey = Hotkey(
244
- id: 4,
245
- onPress: {},
246
- onRelease: { [weak self] in self?.statusMenu.open() })
247
- menuHotkey?.register(keyCode: UInt32(kVK_ANSI_A),
248
- modifiers: UInt32(cmdKey | optionKey))
249
-
250
298
  // The target does not wait for the microphone: the menu shows it
251
299
  // either way.
252
300
  Task { @MainActor in
@@ -383,6 +431,8 @@ final class App: NSObject, NSApplicationDelegate {
383
431
  // two ways of talking never fight over the microphone.
384
432
  stopPolling()
385
433
  guard !recorder.isRecording else { return }
434
+ // A dismissed pill comes back with the talk key.
435
+ panel.summon()
386
436
  // Option-Space is the door to everything spoken, always: our own
387
437
  // answer or step line stops now, and the daemon hushes any talk,
388
438
  // lesson or narration, remembering which it was. Even mid-turn —
@@ -409,6 +459,8 @@ final class App: NSObject, NSApplicationDelegate {
409
459
  // hush now; whatever was talking gets the words, same as the key.
410
460
  let door = talkCheck ?? Task { await AgentClient.hush() }
411
461
  talkCheck = nil
462
+ let forced = forcedAgent
463
+ forcedAgent = nil
412
464
  guard let wav = recorder.stop() else {
413
465
  // Nothing said, so no door follows the hush: let the queue play on.
414
466
  Task { _ = await door.value; await AgentClient.resume() }
@@ -422,7 +474,7 @@ final class App: NSObject, NSApplicationDelegate {
422
474
 
423
475
  Task { @MainActor in
424
476
  var heard = ""
425
- do { heard = try await Speech.transcribe(wav: wav) }
477
+ do { heard = try await Speech.transcribe(wav: wav, engine: settings?.general.stt ?? "auto") }
426
478
  catch { Log.warn("transcription failed: \(error.localizedDescription)") }
427
479
  guard !heard.isEmpty else {
428
480
  _ = await door.value
@@ -436,8 +488,6 @@ final class App: NSObject, NSApplicationDelegate {
436
488
  return
437
489
  }
438
490
  Log.info("heard: \(heard)")
439
- heardLine = heard
440
- if orb.isVisible { orb.model.line = heard }
441
491
  let hushed = await door.value
442
492
  // Always through the door, even with nothing hushed: the
443
493
  // listener's turn is over, so the daemon's queue plays on.
@@ -447,8 +497,11 @@ final class App: NSObject, NSApplicationDelegate {
447
497
  if !midTurn { panel.render(.idle); busy = false }
448
498
  return
449
499
  }
450
- // "Writer, …" sends this one question to Writer; the target stays.
451
- let agent = await AgentClient.address(heard, target: Config.effectiveAgentID)
500
+ // "Writer, …" sends this one question to Writer; the target
501
+ // stays. An agent's own shortcut already said who.
502
+ let agent: String
503
+ if let forced { agent = forced }
504
+ else { agent = await AgentClient.address(heard, target: Config.effectiveAgentID) }
452
505
  if midTurn {
453
506
  // Another agent: ask it alongside ours rather than waiting.
454
507
  if agent != turnAgent && !Self.isStop(heard) {
@@ -518,8 +571,8 @@ final class App: NSObject, NSApplicationDelegate {
518
571
  card.show(spoken: answer.text, written: answer.written,
519
572
  buttons: answer.buttons, imageURL: answer.imageURL)
520
573
  }
521
- // Nothing else on screen: the orb shows this answer, in
522
- // this agent's colour, while it is spoken.
574
+ // Nothing else on screen: the pill shows this answer, its
575
+ // orb in this agent's colour, while it is spoken.
523
576
  let onScreen = !busy && !recorder.isRecording
524
577
  if onScreen { asideSpeaker = agent; panel.render(.saying(answer.text)) }
525
578
  await Speech.say(answer.text, agentID: answer.agentID ?? agent, kind: "answer", voice: answer.voice)
@@ -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