agentix-cli 0.54.0 → 0.56.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.
- package/apps/mac-voice/Sources/AgentXVoice/AgentClient.swift +29 -0
- package/apps/mac-voice/Sources/AgentXVoice/Config.swift +7 -0
- package/apps/mac-voice/Sources/AgentXVoice/Orb.swift +311 -0
- package/apps/mac-voice/Sources/AgentXVoice/OrbMath.swift +57 -0
- package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +14 -1
- package/apps/mac-voice/Sources/AgentXVoice/ResultCard.swift +33 -1
- package/apps/mac-voice/Sources/AgentXVoice/StatusMenu.swift +74 -3
- package/apps/mac-voice/Sources/AgentXVoice/main.swift +197 -14
- package/apps/mac-voice/build.sh +1 -1
- package/dist/{App-VRTLAPRF.js → App-4EM7IPB3.js} +2 -2
- package/dist/{ChatApp-YENWPZT4.js → ChatApp-FYTEEGSU.js} +2 -2
- package/dist/{agent-HVUGHYBF.js → agent-RZ46ZB4W.js} +2 -2
- package/dist/{board-dashboard-3XJVYDB3.js → board-dashboard-NRRYCDZW.js} +826 -180
- package/dist/board-dashboard-NRRYCDZW.js.map +1 -0
- package/dist/{chunk-M67AN4J7.js → chunk-4HEVSWO3.js} +2 -2
- package/dist/{chunk-KLDGJSR5.js → chunk-6AK6WKE5.js} +2 -2
- package/dist/{chunk-ENRJ2QEH.js → chunk-723K4SAW.js} +2 -2
- package/dist/{chunk-UYMK3XNB.js → chunk-76AHNHWC.js} +2 -2
- package/dist/{chunk-XWU7O3S5.js → chunk-C3VRBEJ7.js} +2 -2
- package/dist/{chunk-HQMISQ7L.js → chunk-CELYD5QW.js} +2 -2
- package/dist/{chunk-33QV5O6P.js → chunk-D3LSPFHK.js} +2 -2
- package/dist/chunk-EJ5ISSWW.js +151 -0
- package/dist/chunk-EJ5ISSWW.js.map +1 -0
- package/dist/{chunk-4HNHHKJF.js → chunk-EZN2YXRY.js} +2 -2
- package/dist/{chunk-BKDUCPZ3.js → chunk-GSOL2LW6.js} +2 -2
- package/dist/{chunk-TLFJVLVQ.js → chunk-IL2XLW4H.js} +152 -151
- package/dist/chunk-IL2XLW4H.js.map +1 -0
- package/dist/{chunk-WLRHL2P7.js → chunk-LKJSPO3S.js} +2 -2
- package/dist/{chunk-37EABIOJ.js → chunk-M4AG2YNX.js} +2 -2
- package/dist/{chunk-ZOJUHPD5.js → chunk-O3XG2NVR.js} +18 -18
- package/dist/{chunk-ZOJUHPD5.js.map → chunk-O3XG2NVR.js.map} +1 -1
- package/dist/{chunk-QYHABRLM.js → chunk-SA23V7WX.js} +2 -2
- package/dist/chunk-U4H35SJS.js +15 -0
- package/dist/chunk-U4H35SJS.js.map +1 -0
- package/dist/{chunk-3RCFUIYR.js → chunk-UIVHSYCN.js} +9 -9
- package/dist/chunk-UIVHSYCN.js.map +1 -0
- package/dist/{chunk-3W2KMI5O.js → chunk-W4YMMWJL.js} +2 -2
- package/dist/{chunk-6JHA3ZE6.js → chunk-WKMTT2UY.js} +211 -197
- package/dist/chunk-WKMTT2UY.js.map +1 -0
- package/dist/chunk-WOMQ2VGA.js +5 -0
- package/dist/{chunk-QX4R5WY7.js.map → chunk-WOMQ2VGA.js.map} +1 -1
- package/dist/chunk-X5C5W3E4.js +3 -0
- package/dist/chunk-X5C5W3E4.js.map +1 -0
- package/dist/{chunk-2TJYZF4E.js → chunk-XEOAKD5C.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/client-IA7ZPE56.js +2 -0
- package/dist/config-HPN2F7LH.js +2 -0
- package/dist/config-mutator-7GPTMTVB.js +2 -0
- package/dist/doctor-IBWQVQV4.js +2 -0
- package/dist/index.d.ts +185 -19
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/program-G6DIFJUF.js +2 -0
- package/dist/{promote-H6ZHJXTY.js → promote-T4UJROEH.js} +2 -2
- package/dist/query-VUJC4Y5O.js +2 -0
- package/dist/registry-DX2RI7I7.js +2 -0
- package/dist/{request-planner-ABD4WIGM.js → request-planner-B7DE7VJR.js} +2 -2
- package/dist/{review-candidates-B6574GOJ.js → review-candidates-4YJHIBAF.js} +2 -2
- package/dist/routing-MZG3UODH.js +2 -0
- package/dist/schedule-tool-A4OFQ4YZ.js +2 -0
- package/dist/scheduler-RHJW2V4M.js +2 -0
- package/dist/seat-2PEI7JRX.js +2 -0
- package/dist/{select-Y7SBUCGJ.js → select-4KIUR27O.js} +2 -2
- package/dist/{set-enabled-ARKMDLXN.js → set-enabled-SE7HQKPF.js} +2 -2
- package/dist/subscriptions-RYVPHVIV.js +2 -0
- package/dist/{tool-5EVYUN2G.js → tool-5VMD7IE4.js} +2 -2
- package/dist/wiki-2QFLAH4U.js +2 -0
- package/dist/wiki-2QFLAH4U.js.map +1 -0
- package/package.json +1 -1
- package/dist/board-dashboard-3XJVYDB3.js.map +0 -1
- package/dist/chunk-3RCFUIYR.js.map +0 -1
- package/dist/chunk-4FFWNPFX.js +0 -15
- package/dist/chunk-4FFWNPFX.js.map +0 -1
- package/dist/chunk-6JHA3ZE6.js.map +0 -1
- package/dist/chunk-BMSPRUEJ.js +0 -150
- package/dist/chunk-BMSPRUEJ.js.map +0 -1
- package/dist/chunk-QX4R5WY7.js +0 -5
- package/dist/chunk-TLFJVLVQ.js.map +0 -1
- package/dist/client-N4XQEAVL.js +0 -2
- package/dist/config-KHKIY6LI.js +0 -2
- package/dist/config-mutator-ZZKFQ7HJ.js +0 -2
- package/dist/doctor-QICAXIQB.js +0 -2
- package/dist/program-ZWNGK3RB.js +0 -2
- package/dist/query-FTE4FLYK.js +0 -2
- package/dist/registry-HDOFVNRK.js +0 -2
- package/dist/routing-BGKZOHOA.js +0 -2
- package/dist/schedule-tool-UDHXTKWV.js +0 -2
- package/dist/scheduler-NQM3HVUY.js +0 -2
- package/dist/seat-VGST5AQB.js +0 -2
- package/dist/wiki-GREI2TSP.js +0 -2
- /package/dist/{App-VRTLAPRF.js.map → App-4EM7IPB3.js.map} +0 -0
- /package/dist/{ChatApp-YENWPZT4.js.map → ChatApp-FYTEEGSU.js.map} +0 -0
- /package/dist/{agent-HVUGHYBF.js.map → agent-RZ46ZB4W.js.map} +0 -0
- /package/dist/{chunk-M67AN4J7.js.map → chunk-4HEVSWO3.js.map} +0 -0
- /package/dist/{chunk-KLDGJSR5.js.map → chunk-6AK6WKE5.js.map} +0 -0
- /package/dist/{chunk-ENRJ2QEH.js.map → chunk-723K4SAW.js.map} +0 -0
- /package/dist/{chunk-UYMK3XNB.js.map → chunk-76AHNHWC.js.map} +0 -0
- /package/dist/{chunk-XWU7O3S5.js.map → chunk-C3VRBEJ7.js.map} +0 -0
- /package/dist/{chunk-HQMISQ7L.js.map → chunk-CELYD5QW.js.map} +0 -0
- /package/dist/{chunk-33QV5O6P.js.map → chunk-D3LSPFHK.js.map} +0 -0
- /package/dist/{chunk-4HNHHKJF.js.map → chunk-EZN2YXRY.js.map} +0 -0
- /package/dist/{chunk-BKDUCPZ3.js.map → chunk-GSOL2LW6.js.map} +0 -0
- /package/dist/{chunk-WLRHL2P7.js.map → chunk-LKJSPO3S.js.map} +0 -0
- /package/dist/{chunk-37EABIOJ.js.map → chunk-M4AG2YNX.js.map} +0 -0
- /package/dist/{chunk-QYHABRLM.js.map → chunk-SA23V7WX.js.map} +0 -0
- /package/dist/{chunk-3W2KMI5O.js.map → chunk-W4YMMWJL.js.map} +0 -0
- /package/dist/{chunk-2TJYZF4E.js.map → chunk-XEOAKD5C.js.map} +0 -0
- /package/dist/{client-N4XQEAVL.js.map → client-IA7ZPE56.js.map} +0 -0
- /package/dist/{config-KHKIY6LI.js.map → config-HPN2F7LH.js.map} +0 -0
- /package/dist/{config-mutator-ZZKFQ7HJ.js.map → config-mutator-7GPTMTVB.js.map} +0 -0
- /package/dist/{doctor-QICAXIQB.js.map → doctor-IBWQVQV4.js.map} +0 -0
- /package/dist/{program-ZWNGK3RB.js.map → program-G6DIFJUF.js.map} +0 -0
- /package/dist/{promote-H6ZHJXTY.js.map → promote-T4UJROEH.js.map} +0 -0
- /package/dist/{query-FTE4FLYK.js.map → query-VUJC4Y5O.js.map} +0 -0
- /package/dist/{registry-HDOFVNRK.js.map → registry-DX2RI7I7.js.map} +0 -0
- /package/dist/{request-planner-ABD4WIGM.js.map → request-planner-B7DE7VJR.js.map} +0 -0
- /package/dist/{review-candidates-B6574GOJ.js.map → review-candidates-4YJHIBAF.js.map} +0 -0
- /package/dist/{routing-BGKZOHOA.js.map → routing-MZG3UODH.js.map} +0 -0
- /package/dist/{schedule-tool-UDHXTKWV.js.map → schedule-tool-A4OFQ4YZ.js.map} +0 -0
- /package/dist/{scheduler-NQM3HVUY.js.map → scheduler-RHJW2V4M.js.map} +0 -0
- /package/dist/{seat-VGST5AQB.js.map → seat-2PEI7JRX.js.map} +0 -0
- /package/dist/{select-Y7SBUCGJ.js.map → select-4KIUR27O.js.map} +0 -0
- /package/dist/{set-enabled-ARKMDLXN.js.map → set-enabled-SE7HQKPF.js.map} +0 -0
- /package/dist/{wiki-GREI2TSP.js.map → subscriptions-RYVPHVIV.js.map} +0 -0
- /package/dist/{tool-5EVYUN2G.js.map → tool-5VMD7IE4.js.map} +0 -0
|
@@ -19,6 +19,11 @@ 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?
|
|
@@ -47,6 +52,72 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
47
52
|
private var followUp: String?
|
|
48
53
|
/// "stop" while our own turn was running: drop its answer.
|
|
49
54
|
private var abandoned = false
|
|
55
|
+
/// The agent our own turn is asking. Usually the target; "Writer, …"
|
|
56
|
+
/// sends one question to Writer and leaves the target alone.
|
|
57
|
+
private var turnAgent = ""
|
|
58
|
+
/// Questions in flight per agent: our turn plus any asides.
|
|
59
|
+
private var inFlight: [String: Int] = [:]
|
|
60
|
+
/// Bumped by every stop, so an aside answered after it stays quiet.
|
|
61
|
+
private var stopGeneration = 0
|
|
62
|
+
/// Questions for an agent that already has one in flight, oldest
|
|
63
|
+
/// first. One /ask per agent at a time: a second question waits here
|
|
64
|
+
/// for the first answer instead of racing it into the same session.
|
|
65
|
+
private var waitingAsides: [String: [String]] = [:]
|
|
66
|
+
/// Asides not yet finished: asked, answering, or waiting to be spoken.
|
|
67
|
+
/// While any are open the microphone does not reopen by itself, or it
|
|
68
|
+
/// would hear the next answer as the listener's words.
|
|
69
|
+
private var openAsides = 0
|
|
70
|
+
|
|
71
|
+
/// /ask, counted in flight for the menu while it thinks.
|
|
72
|
+
private func ask(_ heard: String, agent: String) async throws -> AgentClient.Answer {
|
|
73
|
+
track(agent, 1)
|
|
74
|
+
defer { track(agent, -1) }
|
|
75
|
+
return try await AgentClient.ask(heard, agent: agent)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private func track(_ agent: String, _ delta: Int) {
|
|
79
|
+
let n = inFlight[agent, default: 0] + delta
|
|
80
|
+
inFlight[agent] = n > 0 ? n : nil
|
|
81
|
+
publishThinking()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// The menu's per-agent count: questions asked plus questions waiting.
|
|
85
|
+
private func publishThinking() {
|
|
86
|
+
var counts = inFlight
|
|
87
|
+
for (agent, words) in waitingAsides { counts[agent, default: 0] += words.count }
|
|
88
|
+
statusMenu.thinking = counts
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// Who the panel and orb name: a by-name answer being spoken, else the
|
|
92
|
+
/// agent our turn is asking, else the target.
|
|
93
|
+
private var shownAgent: String {
|
|
94
|
+
if let asideSpeaker { return asideSpeaker }
|
|
95
|
+
return busy && !turnAgent.isEmpty ? turnAgent : Config.effectiveAgentID
|
|
96
|
+
}
|
|
97
|
+
|
|
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: "")
|
|
119
|
+
}
|
|
120
|
+
}
|
|
50
121
|
|
|
51
122
|
/// ⌘⌥V. Everything except the hotkey lives in `agentx paste`.
|
|
52
123
|
private func smartPaste() {
|
|
@@ -94,12 +165,26 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
94
165
|
func applicationDidFinishLaunching(_ note: Notification) {
|
|
95
166
|
NSApp.setActivationPolicy(.accessory)
|
|
96
167
|
retireOlderInstances()
|
|
97
|
-
panel.onRender = { [weak self] state in
|
|
168
|
+
panel.onRender = { [weak self] state in
|
|
169
|
+
self?.statusMenu.show(state)
|
|
170
|
+
self?.renderOrb(state)
|
|
171
|
+
}
|
|
98
172
|
panel.contextMenu = { [weak self] in self?.statusMenu.menu ?? NSMenu() }
|
|
99
173
|
panel.agentName = { [weak self] in
|
|
100
|
-
|
|
174
|
+
guard let self else { return "" }
|
|
175
|
+
let who = self.shownAgent
|
|
176
|
+
return who.isEmpty ? "" : self.statusMenu.name(of: who)
|
|
101
177
|
}
|
|
102
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) }
|
|
187
|
+
}
|
|
103
188
|
panel.render(.idle)
|
|
104
189
|
statusMenu.onPillChanged = { [weak self] on in
|
|
105
190
|
guard let self else { return }
|
|
@@ -221,6 +306,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
221
306
|
openedAt = Date()
|
|
222
307
|
patience = followUp ? followUpPatience : clickPatience
|
|
223
308
|
silentClose = followUp
|
|
309
|
+
asideSpeaker = nil
|
|
224
310
|
panel.render(.listening)
|
|
225
311
|
|
|
226
312
|
listenPoll?.invalidate()
|
|
@@ -283,6 +369,10 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
283
369
|
private func stopSpeaking() {
|
|
284
370
|
Log.info("stop: silencing every voice")
|
|
285
371
|
if busy { silenced = true }
|
|
372
|
+
stopGeneration += 1
|
|
373
|
+
waitingAsides.removeAll()
|
|
374
|
+
publishThinking()
|
|
375
|
+
asideSpeaker = nil
|
|
286
376
|
lastSpokeAt = Date()
|
|
287
377
|
Task { await Speech.stopAll() }
|
|
288
378
|
if !recorder.isRecording { panel.render(.idle) }
|
|
@@ -306,6 +396,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
306
396
|
Log.info("door: opened\(busy ? " (a turn is running)" : "")")
|
|
307
397
|
do {
|
|
308
398
|
try recorder.start()
|
|
399
|
+
asideSpeaker = nil
|
|
309
400
|
panel.render(.listening)
|
|
310
401
|
} catch {
|
|
311
402
|
panel.render(.error(error.localizedDescription))
|
|
@@ -345,6 +436,8 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
345
436
|
return
|
|
346
437
|
}
|
|
347
438
|
Log.info("heard: \(heard)")
|
|
439
|
+
heardLine = heard
|
|
440
|
+
if orb.isVisible { orb.model.line = heard }
|
|
348
441
|
let hushed = await door.value
|
|
349
442
|
// Always through the door, even with nothing hushed: the
|
|
350
443
|
// listener's turn is over, so the daemon's queue plays on.
|
|
@@ -354,17 +447,103 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
354
447
|
if !midTurn { panel.render(.idle); busy = false }
|
|
355
448
|
return
|
|
356
449
|
}
|
|
450
|
+
// "Writer, …" sends this one question to Writer; the target stays.
|
|
451
|
+
let agent = await AgentClient.address(heard, target: Config.effectiveAgentID)
|
|
357
452
|
if midTurn {
|
|
453
|
+
// Another agent: ask it alongside ours rather than waiting.
|
|
454
|
+
if agent != turnAgent && !Self.isStop(heard) {
|
|
455
|
+
askAside(heard, agent: agent)
|
|
456
|
+
panel.render(.working("Asked \(statusMenu.name(of: agent))", 0))
|
|
457
|
+
return
|
|
458
|
+
}
|
|
358
459
|
// Our own turn is still thinking. The agent cannot change
|
|
359
460
|
// course mid-turn, so his words go next and the stale
|
|
360
|
-
// answer is not spoken; "stop" just drops it
|
|
361
|
-
|
|
362
|
-
|
|
461
|
+
// answer is not spoken; "stop" just drops it, and every
|
|
462
|
+
// aside with it.
|
|
463
|
+
if Self.isStop(heard) {
|
|
464
|
+
abandoned = true; followUp = nil
|
|
465
|
+
stopGeneration += 1; waitingAsides.removeAll(); publishThinking()
|
|
466
|
+
Log.info("door: stop → dropping the turn in flight")
|
|
467
|
+
} else {
|
|
468
|
+
followUp = heard
|
|
469
|
+
Log.info("door: \"\(heard)\" → next, instead of the answer in flight")
|
|
470
|
+
}
|
|
363
471
|
panel.render(.working("Got it — one moment", 0))
|
|
364
472
|
return
|
|
365
473
|
}
|
|
366
|
-
|
|
474
|
+
// That agent is still answering an aside: this question waits
|
|
475
|
+
// behind it rather than running beside it.
|
|
476
|
+
if inFlight[agent] != nil && !Self.isStop(heard) {
|
|
477
|
+
askAside(heard, agent: agent)
|
|
478
|
+
panel.render(.working("Queued for \(statusMenu.name(of: agent))", 0))
|
|
479
|
+
busy = false; resetSoon()
|
|
480
|
+
return
|
|
481
|
+
}
|
|
482
|
+
await runTurn(heard, agent: agent)
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/// A question for another agent while our own turn thinks. It runs
|
|
487
|
+
/// alongside; the answer waits its turn in the daemon's speaking queue,
|
|
488
|
+
/// so it never talks over ours. An agent already answering an aside
|
|
489
|
+
/// gets this one next, not at the same time.
|
|
490
|
+
private func askAside(_ heard: String, agent: String) {
|
|
491
|
+
if inFlight[agent] != nil || waitingAsides[agent] != nil {
|
|
492
|
+
waitingAsides[agent, default: []].append(heard)
|
|
493
|
+
publishThinking()
|
|
494
|
+
Log.info("aside → \(agent) (waits for its answer in flight): \(heard)")
|
|
495
|
+
return
|
|
496
|
+
}
|
|
497
|
+
runAside(heard, agent: agent)
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
private func runAside(_ heard: String, agent: String) {
|
|
501
|
+
let generation = stopGeneration
|
|
502
|
+
openAsides += 1
|
|
503
|
+
Log.info("aside → \(agent): \(heard)")
|
|
504
|
+
Task { @MainActor in
|
|
505
|
+
defer { openAsides -= 1 }
|
|
506
|
+
let result: Result<AgentClient.Answer, Error>
|
|
507
|
+
do { result = .success(try await ask(heard, agent: agent)) }
|
|
508
|
+
catch { result = .failure(error) }
|
|
509
|
+
// The next question for this agent goes now, while this
|
|
510
|
+
// answer waits to be spoken.
|
|
511
|
+
startNextAside(for: agent)
|
|
512
|
+
guard generation == stopGeneration else { return }
|
|
513
|
+
switch result {
|
|
514
|
+
case .success(let answer):
|
|
515
|
+
Log.info("aside answer (\(answer.agentID ?? agent)): \(answer.text)")
|
|
516
|
+
if ResultCard.isWorthShowing(spoken: answer.text, written: answer.written,
|
|
517
|
+
buttons: answer.buttons, imageURL: answer.imageURL) {
|
|
518
|
+
card.show(spoken: answer.text, written: answer.written,
|
|
519
|
+
buttons: answer.buttons, imageURL: answer.imageURL)
|
|
520
|
+
}
|
|
521
|
+
// Nothing else on screen: the orb shows this answer, in
|
|
522
|
+
// this agent's colour, while it is spoken.
|
|
523
|
+
let onScreen = !busy && !recorder.isRecording
|
|
524
|
+
if onScreen { asideSpeaker = agent; panel.render(.saying(answer.text)) }
|
|
525
|
+
await Speech.say(answer.text, agentID: answer.agentID ?? agent, kind: "answer", voice: answer.voice)
|
|
526
|
+
if onScreen && asideSpeaker == agent {
|
|
527
|
+
asideSpeaker = nil
|
|
528
|
+
if !busy && !recorder.isRecording { panel.render(.idle) }
|
|
529
|
+
}
|
|
530
|
+
case .failure(let error):
|
|
531
|
+
Log.warn("aside failed (\(agent)): \(error.localizedDescription)")
|
|
532
|
+
await Speech.say("Sorry, \(statusMenu.name(of: agent)) couldn't answer that.", agentID: nil, kind: "line", voice: nil)
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/// Send the oldest question waiting for `agent`, if any.
|
|
538
|
+
private func startNextAside(for agent: String) {
|
|
539
|
+
guard var waiting = waitingAsides[agent], !waiting.isEmpty else {
|
|
540
|
+
waitingAsides[agent] = nil
|
|
541
|
+
return
|
|
367
542
|
}
|
|
543
|
+
let next = waiting.removeFirst()
|
|
544
|
+
waitingAsides[agent] = waiting.isEmpty ? nil : waiting
|
|
545
|
+
runAside(next, agent: agent)
|
|
546
|
+
publishThinking()
|
|
368
547
|
}
|
|
369
548
|
|
|
370
549
|
static func isStop(_ s: String) -> Bool {
|
|
@@ -374,12 +553,13 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
374
553
|
|
|
375
554
|
/// One question and its spoken answer. Words said through the door
|
|
376
555
|
/// while it thinks replace the answer with the next turn.
|
|
377
|
-
private func runTurn(_ heard: String) async {
|
|
556
|
+
private func runTurn(_ heard: String, agent: String) async {
|
|
378
557
|
silenced = false
|
|
558
|
+
turnAgent = agent
|
|
379
559
|
do {
|
|
380
|
-
beginNarration()
|
|
560
|
+
beginNarration(agent)
|
|
381
561
|
|
|
382
|
-
let answer = try await
|
|
562
|
+
let answer = try await ask(heard, agent: agent)
|
|
383
563
|
endNarration()
|
|
384
564
|
if abandoned {
|
|
385
565
|
abandoned = false
|
|
@@ -390,7 +570,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
390
570
|
followUp = nil
|
|
391
571
|
Log.info("superseded answer (\(answer.agentID ?? "?")): \(answer.text)")
|
|
392
572
|
panel.render(.thinking)
|
|
393
|
-
return await runTurn(next)
|
|
573
|
+
return await runTurn(next, agent: agent)
|
|
394
574
|
}
|
|
395
575
|
if let v = answer.voice { voice = v }
|
|
396
576
|
Log.info("answer (\(answer.agentID ?? "?")): \(answer.text)")
|
|
@@ -419,7 +599,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
419
599
|
if recorder.isRecording { busy = false; return }
|
|
420
600
|
// Stopped: no follow-up window, the listener asked for quiet.
|
|
421
601
|
if silenced { silenced = false; panel.render(.idle); busy = false; return }
|
|
422
|
-
if let next = followUp { followUp = nil; return await runTurn(next) }
|
|
602
|
+
if let next = followUp { followUp = nil; return await runTurn(next, agent: agent) }
|
|
423
603
|
panel.render(.idle)
|
|
424
604
|
// Leave the microphone open for a moment. Say nothing and
|
|
425
605
|
// it closes itself; start talking and the conversation
|
|
@@ -428,12 +608,15 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
428
608
|
// A live lesson now runs on screen and speaks for itself; an
|
|
429
609
|
// open mic would hear the agent. Option-Space is the door.
|
|
430
610
|
if ["teach", "watch", "act"].contains(answer.presenceMode ?? "") { return }
|
|
611
|
+
// Another agent's answer is still to come; an open mic would
|
|
612
|
+
// take it for the listener's words.
|
|
613
|
+
if openAsides > 0 { return }
|
|
431
614
|
listenHandsFree(followUp: true)
|
|
432
615
|
} catch {
|
|
433
616
|
endNarration()
|
|
434
617
|
Log.warn("turn failed: \(error.localizedDescription)")
|
|
435
618
|
if abandoned { abandoned = false; panel.render(.idle); busy = false; return }
|
|
436
|
-
if let next = followUp { followUp = nil; return await runTurn(next) }
|
|
619
|
+
if let next = followUp { followUp = nil; return await runTurn(next, agent: agent) }
|
|
437
620
|
panel.render(.error(short(error.localizedDescription)))
|
|
438
621
|
// Say it aloud too — a voice assistant that fails only in
|
|
439
622
|
// a 230px label has failed silently for anyone not looking.
|
|
@@ -449,7 +632,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
449
632
|
/// say what the agent is doing but arrive irregularly, and a 1-second
|
|
450
633
|
/// ticker proves the thing is still alive between them. Together they
|
|
451
634
|
/// answer both "what is it doing" and "is it stuck".
|
|
452
|
-
private func beginNarration() {
|
|
635
|
+
private func beginNarration(_ agent: String) {
|
|
453
636
|
startedAt = Date()
|
|
454
637
|
lastStep = "Thinking…"
|
|
455
638
|
spokenSteps.removeAll()
|
|
@@ -458,7 +641,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
458
641
|
|
|
459
642
|
// Progress delivers on its own serial queue; hop to main before
|
|
460
643
|
// touching any view.
|
|
461
|
-
progress = Progress(agentID:
|
|
644
|
+
progress = Progress(agentID: agent) { [weak self] step, voice in
|
|
462
645
|
Task { @MainActor in
|
|
463
646
|
guard let self, self.busy else { return }
|
|
464
647
|
if let voice { self.voice = voice }
|
package/apps/mac-voice/build.sh
CHANGED
|
@@ -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-
|
|
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(`
|
|
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-
|
|
8
|
+
//# sourceMappingURL=App-4EM7IPB3.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-
|
|
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(`
|
|
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-
|
|
7
|
+
//# sourceMappingURL=ChatApp-FYTEEGSU.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{A as a,B as b,C as c}from"./chunk-
|
|
2
|
-
//# sourceMappingURL=agent-
|
|
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
|