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