agentix-cli 0.136.1 → 0.137.1
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 +11 -1
- package/apps/mac-voice/Sources/AgentXVoice/Character.swift +18 -5
- package/apps/mac-voice/Sources/AgentXVoice/Floor.swift +46 -0
- package/apps/mac-voice/Sources/AgentXVoice/FloorWatcher.swift +78 -0
- package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +12 -1
- package/apps/mac-voice/Sources/AgentXVoice/StatusMenu.swift +8 -0
- package/apps/mac-voice/Sources/AgentXVoice/main.swift +89 -10
- package/dist/{App-D6W6IV2L.js → App-TOAWET76.js} +2 -2
- package/dist/{ChatApp-X7TNCFGW.js → ChatApp-4DRCRKFS.js} +2 -2
- package/dist/{absorb-eval-COPVH2M2.js → absorb-eval-OXNL5QGQ.js} +2 -2
- package/dist/{agent-TMTYLAOV.js → agent-GALMGMNJ.js} +2 -2
- package/dist/{board-dashboard-OSI4X5ZJ.js → board-dashboard-VXQYQY22.js} +4 -4
- package/dist/{board-dashboard-OSI4X5ZJ.js.map → board-dashboard-VXQYQY22.js.map} +1 -1
- package/dist/{chunk-AK3XOA5N.js → chunk-23LWLYOK.js} +2 -2
- package/dist/{chunk-IPMXIPTS.js → chunk-2JKKGS77.js} +2 -2
- package/dist/{chunk-QW3L5IIC.js → chunk-36HFRKIH.js} +2 -2
- package/dist/{chunk-HZZW35FY.js → chunk-3K3LRLZX.js} +2 -2
- package/dist/{chunk-NVZ66ITP.js → chunk-5ADXRVYT.js} +5 -5
- package/dist/{chunk-NVZ66ITP.js.map → chunk-5ADXRVYT.js.map} +1 -1
- package/dist/{chunk-GL3H7XMI.js → chunk-5RHCM3L7.js} +2 -2
- package/dist/{chunk-63LV77EG.js → chunk-635B6SXI.js} +2 -2
- package/dist/{chunk-FQLSV2C4.js → chunk-7JUISIE7.js} +2 -2
- package/dist/{chunk-XXIZGNLT.js → chunk-ADQUFEPJ.js} +233 -233
- package/dist/chunk-ADQUFEPJ.js.map +1 -0
- package/dist/{chunk-5J5USW2O.js → chunk-B6PMIUVV.js} +2 -2
- package/dist/{chunk-GRMCAO3Q.js → chunk-BWXRAPTX.js} +2 -2
- package/dist/{chunk-WRJFKA43.js → chunk-BXXP6DTB.js} +2 -2
- package/dist/{chunk-OW5EXDVY.js → chunk-E3AEOQMK.js} +2 -2
- package/dist/{chunk-VA24JS2S.js → chunk-GIQKQD2B.js} +2 -2
- package/dist/{chunk-5X37CXJF.js → chunk-IIFHX625.js} +2 -2
- package/dist/{chunk-EVTTAM7S.js → chunk-JC5KLH2K.js} +2 -2
- package/dist/{chunk-EGDY762K.js → chunk-K6UV4A2T.js} +2 -2
- package/dist/{chunk-EGDY762K.js.map → chunk-K6UV4A2T.js.map} +1 -1
- package/dist/chunk-KHYF6KTY.js +54 -0
- package/dist/chunk-KHYF6KTY.js.map +1 -0
- package/dist/chunk-KQ7KWFZY.js +4 -0
- package/dist/chunk-KQ7KWFZY.js.map +1 -0
- package/dist/{chunk-QHZFMO6M.js → chunk-LK4YO2FE.js} +2 -2
- package/dist/{chunk-QHZFMO6M.js.map → chunk-LK4YO2FE.js.map} +1 -1
- package/dist/{chunk-G26UHWS4.js → chunk-MXDWLBIA.js} +2 -2
- package/dist/{chunk-M5AHI2PZ.js → chunk-PLPDG6GE.js} +2 -2
- package/dist/{chunk-PV623BLN.js → chunk-PNYBZPPB.js} +3 -3
- package/dist/{chunk-4AFA6LQZ.js → chunk-RWMDDMYX.js} +2 -2
- package/dist/{chunk-66DPMVUC.js → chunk-SQ37I2MD.js} +2 -2
- package/dist/{chunk-KZXQD6ZK.js → chunk-XZIKAHFU.js} +2 -2
- package/dist/{chunk-HEJTE7OC.js → chunk-Z2QOQBUS.js} +2 -2
- package/dist/{chunk-XGOBZCYC.js → chunk-Z5OD225A.js} +2 -2
- package/dist/{chunk-FRTELYZ4.js → chunk-ZYUVKFX2.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/{client-SMOWSRJY.js → client-PWE4BJH4.js} +2 -2
- package/dist/{config-OGUDWYAA.js → config-E53FBZ47.js} +2 -2
- package/dist/{config-mutate-BH27TMWK.js → config-mutate-ZEHN6JMZ.js} +2 -2
- package/dist/config-mutator-AK775RTM.js +2 -0
- package/dist/{contributions-H6T65TCY.js → contributions-CLLCLS3O.js} +2 -2
- package/dist/{doctor-ZOXHT7ZQ.js → doctor-XKT5FWPO.js} +2 -2
- package/dist/{follow-up-tool-GUBBXNEZ.js → follow-up-tool-ZFRZUZLI.js} +2 -2
- package/dist/{hub-7AMS6ADP.js → hub-N5ADP3LO.js} +2 -2
- package/dist/index.d.ts +74 -35
- package/dist/index.js +1 -1
- package/dist/ntfy-ZDNBUEIX.js +2 -0
- package/dist/{program-IUUHETTV.js → program-MJ6S6X6T.js} +208 -208
- package/dist/program-MJ6S6X6T.js.map +1 -0
- package/dist/{promote-VCNVE5JD.js → promote-M4UBD2R6.js} +2 -2
- package/dist/push-HVGOC4VN.js +2 -0
- package/dist/push-bridge-NAQ7WQJX.js +2 -0
- package/dist/{query-6G27CBZQ.js → query-KMQHF5HW.js} +2 -2
- package/dist/{query-settings-G4M75LVQ.js → query-settings-PENAE4UG.js} +2 -2
- package/dist/{registry-MMNG5VZ6.js → registry-GYKSYSDD.js} +2 -2
- package/dist/{request-planner-T3VCZVVQ.js → request-planner-GBX6DHRQ.js} +2 -2
- package/dist/{review-candidates-C3O3CENL.js → review-candidates-SYMRJGI2.js} +2 -2
- package/dist/{routing-LD35TJM5.js → routing-S3L44JLL.js} +2 -2
- package/dist/schedule-tool-FXUFRZHR.js +2 -0
- package/dist/{scheduler-I3QB4GI3.js → scheduler-AW4F5ZKS.js} +2 -2
- package/dist/{seat-4Y3EW3KU.js → seat-QBBNCOFO.js} +2 -2
- package/dist/{select-LZKN3FER.js → select-ZCFIMYWS.js} +2 -2
- package/dist/{set-enabled-WSTOHJDE.js → set-enabled-4F27A3U2.js} +2 -2
- package/dist/{tool-NLMIUOHR.js → tool-G6XTO24J.js} +2 -2
- package/dist/{tool-AKXO2EJU.js → tool-M462KSMF.js} +2 -2
- package/dist/{wiki-ZQMDUXTE.js → wiki-JXVHIEZI.js} +2 -2
- package/dist/wiki-enrich-6CSUPULH.js +2 -0
- package/package.json +1 -1
- package/dist/chunk-54OORW5I.js +0 -54
- package/dist/chunk-54OORW5I.js.map +0 -1
- package/dist/chunk-KZKDVIOE.js +0 -4
- package/dist/chunk-KZKDVIOE.js.map +0 -1
- package/dist/chunk-XXIZGNLT.js.map +0 -1
- package/dist/config-mutator-Q62AS2TR.js +0 -2
- package/dist/ntfy-52EXHVMD.js +0 -2
- package/dist/program-IUUHETTV.js.map +0 -1
- package/dist/push-DID6RZYI.js +0 -2
- package/dist/push-bridge-325SXDGN.js +0 -2
- package/dist/schedule-tool-46TVGEYU.js +0 -2
- package/dist/wiki-enrich-NPNOSTRM.js +0 -2
- /package/dist/{App-D6W6IV2L.js.map → App-TOAWET76.js.map} +0 -0
- /package/dist/{ChatApp-X7TNCFGW.js.map → ChatApp-4DRCRKFS.js.map} +0 -0
- /package/dist/{absorb-eval-COPVH2M2.js.map → absorb-eval-OXNL5QGQ.js.map} +0 -0
- /package/dist/{agent-TMTYLAOV.js.map → agent-GALMGMNJ.js.map} +0 -0
- /package/dist/{chunk-AK3XOA5N.js.map → chunk-23LWLYOK.js.map} +0 -0
- /package/dist/{chunk-IPMXIPTS.js.map → chunk-2JKKGS77.js.map} +0 -0
- /package/dist/{chunk-QW3L5IIC.js.map → chunk-36HFRKIH.js.map} +0 -0
- /package/dist/{chunk-HZZW35FY.js.map → chunk-3K3LRLZX.js.map} +0 -0
- /package/dist/{chunk-GL3H7XMI.js.map → chunk-5RHCM3L7.js.map} +0 -0
- /package/dist/{chunk-63LV77EG.js.map → chunk-635B6SXI.js.map} +0 -0
- /package/dist/{chunk-FQLSV2C4.js.map → chunk-7JUISIE7.js.map} +0 -0
- /package/dist/{chunk-5J5USW2O.js.map → chunk-B6PMIUVV.js.map} +0 -0
- /package/dist/{chunk-GRMCAO3Q.js.map → chunk-BWXRAPTX.js.map} +0 -0
- /package/dist/{chunk-WRJFKA43.js.map → chunk-BXXP6DTB.js.map} +0 -0
- /package/dist/{chunk-OW5EXDVY.js.map → chunk-E3AEOQMK.js.map} +0 -0
- /package/dist/{chunk-VA24JS2S.js.map → chunk-GIQKQD2B.js.map} +0 -0
- /package/dist/{chunk-5X37CXJF.js.map → chunk-IIFHX625.js.map} +0 -0
- /package/dist/{chunk-EVTTAM7S.js.map → chunk-JC5KLH2K.js.map} +0 -0
- /package/dist/{chunk-G26UHWS4.js.map → chunk-MXDWLBIA.js.map} +0 -0
- /package/dist/{chunk-M5AHI2PZ.js.map → chunk-PLPDG6GE.js.map} +0 -0
- /package/dist/{chunk-PV623BLN.js.map → chunk-PNYBZPPB.js.map} +0 -0
- /package/dist/{chunk-4AFA6LQZ.js.map → chunk-RWMDDMYX.js.map} +0 -0
- /package/dist/{chunk-66DPMVUC.js.map → chunk-SQ37I2MD.js.map} +0 -0
- /package/dist/{chunk-KZXQD6ZK.js.map → chunk-XZIKAHFU.js.map} +0 -0
- /package/dist/{chunk-HEJTE7OC.js.map → chunk-Z2QOQBUS.js.map} +0 -0
- /package/dist/{chunk-XGOBZCYC.js.map → chunk-Z5OD225A.js.map} +0 -0
- /package/dist/{chunk-FRTELYZ4.js.map → chunk-ZYUVKFX2.js.map} +0 -0
- /package/dist/{client-SMOWSRJY.js.map → client-PWE4BJH4.js.map} +0 -0
- /package/dist/{config-OGUDWYAA.js.map → config-E53FBZ47.js.map} +0 -0
- /package/dist/{config-mutate-BH27TMWK.js.map → config-mutate-ZEHN6JMZ.js.map} +0 -0
- /package/dist/{config-mutator-Q62AS2TR.js.map → config-mutator-AK775RTM.js.map} +0 -0
- /package/dist/{contributions-H6T65TCY.js.map → contributions-CLLCLS3O.js.map} +0 -0
- /package/dist/{doctor-ZOXHT7ZQ.js.map → doctor-XKT5FWPO.js.map} +0 -0
- /package/dist/{follow-up-tool-GUBBXNEZ.js.map → follow-up-tool-ZFRZUZLI.js.map} +0 -0
- /package/dist/{hub-7AMS6ADP.js.map → hub-N5ADP3LO.js.map} +0 -0
- /package/dist/{ntfy-52EXHVMD.js.map → ntfy-ZDNBUEIX.js.map} +0 -0
- /package/dist/{promote-VCNVE5JD.js.map → promote-M4UBD2R6.js.map} +0 -0
- /package/dist/{push-DID6RZYI.js.map → push-HVGOC4VN.js.map} +0 -0
- /package/dist/{push-bridge-325SXDGN.js.map → push-bridge-NAQ7WQJX.js.map} +0 -0
- /package/dist/{query-6G27CBZQ.js.map → query-KMQHF5HW.js.map} +0 -0
- /package/dist/{query-settings-G4M75LVQ.js.map → query-settings-PENAE4UG.js.map} +0 -0
- /package/dist/{registry-MMNG5VZ6.js.map → registry-GYKSYSDD.js.map} +0 -0
- /package/dist/{request-planner-T3VCZVVQ.js.map → request-planner-GBX6DHRQ.js.map} +0 -0
- /package/dist/{review-candidates-C3O3CENL.js.map → review-candidates-SYMRJGI2.js.map} +0 -0
- /package/dist/{routing-LD35TJM5.js.map → routing-S3L44JLL.js.map} +0 -0
- /package/dist/{schedule-tool-46TVGEYU.js.map → schedule-tool-FXUFRZHR.js.map} +0 -0
- /package/dist/{scheduler-I3QB4GI3.js.map → scheduler-AW4F5ZKS.js.map} +0 -0
- /package/dist/{seat-4Y3EW3KU.js.map → seat-QBBNCOFO.js.map} +0 -0
- /package/dist/{select-LZKN3FER.js.map → select-ZCFIMYWS.js.map} +0 -0
- /package/dist/{set-enabled-WSTOHJDE.js.map → set-enabled-4F27A3U2.js.map} +0 -0
- /package/dist/{tool-NLMIUOHR.js.map → tool-G6XTO24J.js.map} +0 -0
- /package/dist/{tool-AKXO2EJU.js.map → tool-M462KSMF.js.map} +0 -0
- /package/dist/{wiki-ZQMDUXTE.js.map → wiki-JXVHIEZI.js.map} +0 -0
- /package/dist/{wiki-enrich-NPNOSTRM.js.map → wiki-enrich-6CSUPULH.js.map} +0 -0
|
@@ -27,6 +27,9 @@ enum AgentClient {
|
|
|
27
27
|
let voice: VoiceChoice?
|
|
28
28
|
/// Presence mode the daemon chose for this turn (talk, teach, …).
|
|
29
29
|
var presenceMode: String? = nil
|
|
30
|
+
/// The daemon took the words for the floor holder's own sound
|
|
31
|
+
/// (#924): no agent was asked and nothing is said back.
|
|
32
|
+
var dropped = false
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
/// What, if anything, to say about the step now running.
|
|
@@ -265,7 +268,8 @@ enum AgentClient {
|
|
|
265
268
|
return await agents()?.first?.id ?? ""
|
|
266
269
|
}
|
|
267
270
|
|
|
268
|
-
|
|
271
|
+
/// `handsFree`: the microphone had opened by itself, no key held.
|
|
272
|
+
static func ask(_ message: String, agent: String, handsFree: Bool = false) async throws -> Answer {
|
|
269
273
|
var req = URLRequest(url: URL(string: "\(Config.daemonURL)/ask")!)
|
|
270
274
|
req.httpMethod = "POST"
|
|
271
275
|
// An agent turn routinely takes minutes. The default 60s would cut
|
|
@@ -275,6 +279,7 @@ enum AgentClient {
|
|
|
275
279
|
var payload: [String: Any] = ["message": message, "session": Config.voiceSession]
|
|
276
280
|
// No agent known: the daemon answers with its default agent.
|
|
277
281
|
if !agent.isEmpty { payload["agent"] = agent }
|
|
282
|
+
if handsFree { payload["handsFree"] = true }
|
|
278
283
|
req.httpBody = try JSONSerialization.data(withJSONObject: payload)
|
|
279
284
|
|
|
280
285
|
let (data, response) = try await URLSession.shared.data(for: req)
|
|
@@ -291,9 +296,14 @@ enum AgentClient {
|
|
|
291
296
|
let ui: Ui?
|
|
292
297
|
let error: String?
|
|
293
298
|
let duration: Int?
|
|
299
|
+
let dropped: String?
|
|
294
300
|
}
|
|
295
301
|
let reply = try? JSONDecoder().decode(Reply.self, from: data)
|
|
296
302
|
let voice = reply?.voice
|
|
303
|
+
if reply?.dropped != nil {
|
|
304
|
+
return Answer(text: "", written: nil, buttons: [], imageURL: nil, durationMs: nil,
|
|
305
|
+
agentID: reply?.agentId, voice: voice, dropped: true)
|
|
306
|
+
}
|
|
297
307
|
|
|
298
308
|
// 202 means accepted-but-busy: the daemon has already written a
|
|
299
309
|
// speakable explanation into `text`. Falling through to the error
|
|
@@ -35,6 +35,10 @@ final class CharacterHost {
|
|
|
35
35
|
private var hidden = false
|
|
36
36
|
/// On the page, playing (#580): its window is out until it is back.
|
|
37
37
|
private var away = false
|
|
38
|
+
/// A recording holds the floor (#924): out until it ends.
|
|
39
|
+
private var concealed = false
|
|
40
|
+
/// Hidden, by the owner or for a recording.
|
|
41
|
+
private var out: Bool { hidden || concealed }
|
|
38
42
|
private var animated = true
|
|
39
43
|
private var reduceMotion = NSWorkspace.shared.accessibilityDisplayShouldReduceMotion
|
|
40
44
|
private var activity = CharacterMath.Activity.idle
|
|
@@ -94,7 +98,7 @@ final class CharacterHost {
|
|
|
94
98
|
/// A still picture: Reduce Motion, or "Animated orb" off.
|
|
95
99
|
private var still: Bool { !animated || reduceMotion }
|
|
96
100
|
/// The look is the character: whether it is on screen. Nil with the orb.
|
|
97
|
-
var onScreen: Bool? { shown ? !
|
|
101
|
+
var onScreen: Bool? { shown ? !out && !away : nil }
|
|
98
102
|
/// Told when it comes on screen or leaves it: shown or hidden, or the look changed.
|
|
99
103
|
var onScreenChanged: (() -> Void)?
|
|
100
104
|
|
|
@@ -146,6 +150,15 @@ final class CharacterHost {
|
|
|
146
150
|
onScreenChanged?()
|
|
147
151
|
}
|
|
148
152
|
|
|
153
|
+
/// Out for a recording that holds the floor (#924), and back as it
|
|
154
|
+
/// was when the recording ends: hidden stays hidden.
|
|
155
|
+
func setConcealed(_ on: Bool) {
|
|
156
|
+
guard concealed != on else { return }
|
|
157
|
+
concealed = on
|
|
158
|
+
run()
|
|
159
|
+
onScreenChanged?()
|
|
160
|
+
}
|
|
161
|
+
|
|
149
162
|
/// Hide it, as its bubble is hidden: gone until it is asked back.
|
|
150
163
|
func setHidden(_ on: Bool) {
|
|
151
164
|
guard hidden != on else { return }
|
|
@@ -187,8 +200,8 @@ final class CharacterHost {
|
|
|
187
200
|
/// Go beside `rect` and mark it, or with nil go back to where it
|
|
188
201
|
/// rests; in the state `asked`, or with nil its real one.
|
|
189
202
|
func guide(to rect: NSRect?, mark kind: GuideMath.Mark = .none, caption: String? = nil, asked mood: CharacterMath.Mood? = nil) {
|
|
190
|
-
asked = shown && !
|
|
191
|
-
if shown, !
|
|
203
|
+
asked = shown && !out && !away ? mood : nil
|
|
204
|
+
if shown, !out, !away, carried == nil, let rect {
|
|
192
205
|
guide.show(rect, kind, caption: caption, color: view.stops[2], animated: !still)
|
|
193
206
|
} else {
|
|
194
207
|
guide.end()
|
|
@@ -200,14 +213,14 @@ final class CharacterHost {
|
|
|
200
213
|
/// Standing still, it is drawn again when something changes: its
|
|
201
214
|
/// state, or its bubble showing or hiding.
|
|
202
215
|
func redraw() {
|
|
203
|
-
if shown && !
|
|
216
|
+
if shown && !out && still { tick() }
|
|
204
217
|
}
|
|
205
218
|
|
|
206
219
|
/// The frames run only while it is on screen and may move.
|
|
207
220
|
private func run() {
|
|
208
221
|
timer?.invalidate()
|
|
209
222
|
timer = nil
|
|
210
|
-
guard shown, !
|
|
223
|
+
guard shown, !out, !away else { carried = nil; guide(to: nil); window.orderOut(nil); return }
|
|
211
224
|
tick()
|
|
212
225
|
window.orderFrontRegardless()
|
|
213
226
|
// Still, it only watches for the hand that moves it.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// The daemon's floor (#924): who may speak and show a bubble on this
|
|
4
|
+
/// Mac. A demo, a recording or a script takes it; until it lets go, an
|
|
5
|
+
/// answer waits to be shown and spoken, the microphone does not open by
|
|
6
|
+
/// itself, and for a recording the character and the pill stay out.
|
|
7
|
+
/// See src/voice/floor.ts; FloorWatcher.swift follows it.
|
|
8
|
+
struct FloorState: Decodable, Equatable {
|
|
9
|
+
var seq = 0
|
|
10
|
+
var held = false
|
|
11
|
+
/// Who holds it, as the owner reads it.
|
|
12
|
+
var holder: String?
|
|
13
|
+
/// The holder plays sound of its own.
|
|
14
|
+
var sound = false
|
|
15
|
+
/// A recording: nothing of the assistant shows.
|
|
16
|
+
var hide = false
|
|
17
|
+
/// Milliseconds since 1970, as the daemon sends them.
|
|
18
|
+
var expiresAt: Double?
|
|
19
|
+
|
|
20
|
+
static let free = FloorState()
|
|
21
|
+
|
|
22
|
+
/// As it is at `now` (seconds since 1970): a hold past its time is
|
|
23
|
+
/// over, whether or not the daemon was there to say so.
|
|
24
|
+
func at(_ now: TimeInterval) -> FloorState {
|
|
25
|
+
guard held, let expiresAt, now * 1000 >= expiresAt else { return self }
|
|
26
|
+
return FloorState(seq: seq)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// An answer waits: shown and spoken once the floor is free.
|
|
30
|
+
var holdsAnswers: Bool { held }
|
|
31
|
+
/// The microphone must not open by itself, and a window it opened is
|
|
32
|
+
/// thrown away: it would hear the holder and take it for the owner.
|
|
33
|
+
var closesOpenMicrophone: Bool { held && sound }
|
|
34
|
+
/// The character, the pill and every bubble stay off the screen.
|
|
35
|
+
var hidesEverything: Bool { held && hide }
|
|
36
|
+
|
|
37
|
+
/// The menu's row while it is held: who, and for how long at most.
|
|
38
|
+
func menuTitle(now: TimeInterval) -> String? {
|
|
39
|
+
guard held else { return nil }
|
|
40
|
+
let who = holder ?? "Someone"
|
|
41
|
+
guard let expiresAt else { return "\(who) holds the floor" }
|
|
42
|
+
let left = max(0, Int((expiresAt / 1000 - now).rounded(.up)))
|
|
43
|
+
let time = left >= 90 ? "\(Int((Double(left) / 60).rounded())) min" : "\(left) s"
|
|
44
|
+
return "\(who) holds the floor (up to \(time))"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// The daemon's /voice/floor route (#924).
|
|
4
|
+
enum FloorClient {
|
|
5
|
+
/// GET /voice/floor: the floor after `seq`, as soon as it changes;
|
|
6
|
+
/// the current one after 25 s; nil when the daemon cannot be reached
|
|
7
|
+
/// or is too old to know the route.
|
|
8
|
+
static func next(after seq: Int) async -> FloorState? {
|
|
9
|
+
guard let url = URL(string: "\(Config.daemonURL)/voice/floor?after=\(seq)") else { return nil }
|
|
10
|
+
var req = URLRequest(url: url)
|
|
11
|
+
req.timeoutInterval = 35
|
|
12
|
+
guard let (data, response) = try? await URLSession.shared.data(for: req),
|
|
13
|
+
(response as? HTTPURLResponse)?.statusCode == 200 else { return nil }
|
|
14
|
+
return try? JSONDecoder().decode(FloorState.self, from: data)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// The owner takes the floor back, whoever holds it.
|
|
18
|
+
static func takeBack() async {
|
|
19
|
+
guard let url = URL(string: "\(Config.daemonURL)/voice/floor") else { return }
|
|
20
|
+
var req = URLRequest(url: url)
|
|
21
|
+
req.httpMethod = "DELETE"
|
|
22
|
+
req.timeoutInterval = 2
|
|
23
|
+
_ = try? await URLSession.shared.data(for: req)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Follows the floor for as long as the app runs. A hold ends at its
|
|
28
|
+
/// time limit whether or not the daemon is there to say so: nothing of
|
|
29
|
+
/// the assistant stays hidden behind a holder nobody can ask about.
|
|
30
|
+
@MainActor
|
|
31
|
+
final class FloorWatcher {
|
|
32
|
+
private var last = FloorState.free
|
|
33
|
+
private var task: Task<Void, Never>?
|
|
34
|
+
private var expiry: Timer?
|
|
35
|
+
/// The floor now.
|
|
36
|
+
var state: FloorState { last.at(Date().timeIntervalSince1970) }
|
|
37
|
+
/// Told of every change: taken, renewed, freed.
|
|
38
|
+
var onChange: ((FloorState) -> Void)?
|
|
39
|
+
|
|
40
|
+
func start() {
|
|
41
|
+
task?.cancel()
|
|
42
|
+
task = Task { [weak self] in
|
|
43
|
+
while !Task.isCancelled {
|
|
44
|
+
guard let self else { return }
|
|
45
|
+
await self.turn()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private func turn() async {
|
|
51
|
+
guard let next = await FloorClient.next(after: last.seq) else {
|
|
52
|
+
// The daemon is restarting or gone. A hold is kept until its
|
|
53
|
+
// own time limit: a recording must not have the character
|
|
54
|
+
// walk back in because one request failed.
|
|
55
|
+
try? await Task.sleep(nanoseconds: 2_000_000_000)
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
set(next)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private func set(_ next: FloorState) {
|
|
62
|
+
guard next != last else { return }
|
|
63
|
+
let before = state
|
|
64
|
+
last = next
|
|
65
|
+
// A hold ends at its time even if the daemon is not there to say so.
|
|
66
|
+
expiry?.invalidate()
|
|
67
|
+
expiry = nil
|
|
68
|
+
if next.held, let at = next.expiresAt {
|
|
69
|
+
expiry = Timer.scheduledTimer(withTimeInterval: max(0, at / 1000 - Date().timeIntervalSince1970) + 0.1, repeats: false) { _ in
|
|
70
|
+
Task { @MainActor [weak self] in
|
|
71
|
+
guard let self else { return }
|
|
72
|
+
self.onChange?(self.state)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if state != before { onChange?(state) }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -36,6 +36,9 @@ final class Panel: NSPanel {
|
|
|
36
36
|
let separator = NSBox()
|
|
37
37
|
/// Hidden by close, Esc or the menu until the next talk key.
|
|
38
38
|
var dismissed = false
|
|
39
|
+
/// A recording holds the floor (#924): out until it ends, whatever is
|
|
40
|
+
/// rendered meanwhile, then back as it would be.
|
|
41
|
+
private(set) var concealed = false
|
|
39
42
|
/// Full, or reduced (#457): the orb alone, a small circle with no words
|
|
40
43
|
/// and no buttons, dragged and remembered apart from the pill; with the
|
|
41
44
|
/// character standing in for the orb, the character alone, its speech
|
|
@@ -531,6 +534,14 @@ final class Panel: NSPanel {
|
|
|
531
534
|
render(current)
|
|
532
535
|
}
|
|
533
536
|
|
|
537
|
+
/// Out for a recording, and back when it ends.
|
|
538
|
+
@MainActor
|
|
539
|
+
func setConcealed(_ on: Bool) {
|
|
540
|
+
guard concealed != on else { return }
|
|
541
|
+
concealed = on
|
|
542
|
+
render(current)
|
|
543
|
+
}
|
|
544
|
+
|
|
534
545
|
/// The orb at the pill's head, or none while the character shows.
|
|
535
546
|
@MainActor
|
|
536
547
|
func setShowsOrb(_ on: Bool) {
|
|
@@ -642,7 +653,7 @@ final class Panel: NSPanel {
|
|
|
642
653
|
// A caption shows like any words; away with none, the hint does not.
|
|
643
654
|
let quiet = state.isMeta && !expanded && busyCount == 0
|
|
644
655
|
let gone = reduced && !showsOrb
|
|
645
|
-
if dismissed || gone || (quiet && idle == .nothing) || (quiet && idle == .hint && !alwaysVisible && !reduced) { hide() } else { show() }
|
|
656
|
+
if dismissed || concealed || gone || (quiet && idle == .nothing) || (quiet && idle == .hint && !alwaysVisible && !reduced) { hide() } else { show() }
|
|
646
657
|
// After showing or hiding: a still character draws its bubble's
|
|
647
658
|
// tail only while the bubble is on screen.
|
|
648
659
|
onLook?(state, tint, colors)
|
|
@@ -39,6 +39,9 @@ final class StatusMenu: NSObject, NSMenuDelegate {
|
|
|
39
39
|
|
|
40
40
|
/// Set by the app.
|
|
41
41
|
var onStop: (() -> Void)?
|
|
42
|
+
/// Who holds the floor (#924): a row says so, with a way to take it back.
|
|
43
|
+
var floor = FloorState.free { didSet { if floor != oldValue { rebuild() } } }
|
|
44
|
+
var onTakeFloorBack: (() -> Void)?
|
|
42
45
|
var onHoldChanged: ((Bool) -> Void)?
|
|
43
46
|
var onTargetChanged: ((String) -> Void)?
|
|
44
47
|
var onPillChanged: ((Bool) -> Void)?
|
|
@@ -245,6 +248,10 @@ final class StatusMenu: NSObject, NSMenuDelegate {
|
|
|
245
248
|
recent.addItems(to: menu, name: name(of:))
|
|
246
249
|
|
|
247
250
|
menu.addItem(.separator())
|
|
251
|
+
if let held = floor.menuTitle(now: Date().timeIntervalSince1970) {
|
|
252
|
+
menu.addItem(disabled(held))
|
|
253
|
+
menu.addItem(action("Take the floor back", #selector(takeFloorBack), key: ""))
|
|
254
|
+
}
|
|
248
255
|
menu.addItem(action("Stop speaking", #selector(stop), key: ".", modifiers: [.command, .option]))
|
|
249
256
|
let hold = action("Hold notifications", #selector(toggleHold), key: "")
|
|
250
257
|
hold.state = Hold.isOn ? .on : .off
|
|
@@ -316,6 +323,7 @@ final class StatusMenu: NSObject, NSMenuDelegate {
|
|
|
316
323
|
}
|
|
317
324
|
|
|
318
325
|
@objc private func stop() { onStop?() }
|
|
326
|
+
@objc private func takeFloorBack() { onTakeFloorBack?() }
|
|
319
327
|
|
|
320
328
|
@objc private func toggleHold() {
|
|
321
329
|
onHoldChanged?(Hold.toggle())
|
|
@@ -91,6 +91,13 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
91
91
|
// An agent ringing the owner (#321).
|
|
92
92
|
private let callWatcher = CallWatcher()
|
|
93
93
|
private let guideWatcher = GuideWatcher()
|
|
94
|
+
/// Who may speak and show a bubble on this Mac (#924).
|
|
95
|
+
private let floorWatcher = FloorWatcher()
|
|
96
|
+
/// Who held the floor at its last change, for the log.
|
|
97
|
+
private var floorHolder: String?
|
|
98
|
+
/// The microphone is open with no key held and no click: a follow-up
|
|
99
|
+
/// window. What it hears while a holder plays sound is thrown away.
|
|
100
|
+
private var openedByItself = false
|
|
94
101
|
/// A play runs, or its picture is being taken (#562).
|
|
95
102
|
private var playing = false
|
|
96
103
|
/// When the app started, for the hint in the character's bubble.
|
|
@@ -105,10 +112,35 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
105
112
|
private var activeCall: IncomingCall?
|
|
106
113
|
|
|
107
114
|
/// /ask, counted in flight for the menu while it thinks.
|
|
108
|
-
private func ask(_ heard: String, agent: String) async throws -> AgentClient.Answer {
|
|
115
|
+
private func ask(_ heard: String, agent: String, handsFree: Bool = false) async throws -> AgentClient.Answer {
|
|
109
116
|
track(agent, 1)
|
|
110
117
|
defer { track(agent, -1) }
|
|
111
|
-
return try await AgentClient.ask(heard, agent: agent)
|
|
118
|
+
return try await AgentClient.ask(heard, agent: agent, handsFree: handsFree)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// The floor was taken, renewed or freed (#924). A recording takes
|
|
122
|
+
/// the character and the pill off the screen; a holder that plays
|
|
123
|
+
/// sound closes a microphone that had opened by itself.
|
|
124
|
+
private func floorChanged(_ floor: FloorState) {
|
|
125
|
+
if floor.holder != floorHolder {
|
|
126
|
+
floorHolder = floor.holder
|
|
127
|
+
// Just taken: an answer being read or said leaves the screen.
|
|
128
|
+
// Its line is cut on the daemon and plays again after.
|
|
129
|
+
if floor.held && !recorder.isRecording { panel.collapse(); panel.render(rest) }
|
|
130
|
+
Log.info(floor.held
|
|
131
|
+
? "floor: held by \(floor.holder ?? "?")\(floor.sound ? ", plays sound" : "")\(floor.hide ? ", everything hidden" : "")"
|
|
132
|
+
: "floor: free")
|
|
133
|
+
}
|
|
134
|
+
character.setConcealed(floor.hidesEverything)
|
|
135
|
+
panel.setConcealed(floor.hidesEverything)
|
|
136
|
+
statusMenu.floor = floor
|
|
137
|
+
if floor.closesOpenMicrophone && recorder.isRecording && openedByItself {
|
|
138
|
+
Log.info("floor: closing the microphone it opened by itself")
|
|
139
|
+
openedByItself = false
|
|
140
|
+
stopPolling()
|
|
141
|
+
_ = recorder.stop()
|
|
142
|
+
panel.render(rest)
|
|
143
|
+
}
|
|
112
144
|
}
|
|
113
145
|
|
|
114
146
|
private func track(_ agent: String, _ delta: Int) {
|
|
@@ -245,7 +277,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
245
277
|
private var canPlay: Bool {
|
|
246
278
|
character.onScreen == true && Config.animatedOrb
|
|
247
279
|
&& !NSWorkspace.shared.accessibilityDisplayShouldReduceMotion
|
|
248
|
-
&& !busy && !recorder.isRecording && !play.running
|
|
280
|
+
&& !busy && !recorder.isRecording && !play.running && !floorWatcher.state.held
|
|
249
281
|
}
|
|
250
282
|
|
|
251
283
|
/// What the idle bubble says (#562): no hint at a guide stop, on the
|
|
@@ -551,6 +583,9 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
551
583
|
DispatchQueue.main.asyncAfter(deadline: .now() + GuideMath.hintSeconds) { [weak self] in self?.idleWords() }
|
|
552
584
|
guideWatcher.start()
|
|
553
585
|
character.onScreenChanged = { [weak self] in self?.guideWatcher.start() }
|
|
586
|
+
floorWatcher.onChange = { [weak self] floor in self?.floorChanged(floor) }
|
|
587
|
+
statusMenu.onTakeFloorBack = { Task { await FloorClient.takeBack() } }
|
|
588
|
+
floorWatcher.start()
|
|
554
589
|
|
|
555
590
|
// The target does not wait for the microphone: the menu shows it
|
|
556
591
|
// either way.
|
|
@@ -598,6 +633,8 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
598
633
|
guard !busy, !recorder.isRecording else { return }
|
|
599
634
|
// A replay is playing (#492): the follow-up window would hear it.
|
|
600
635
|
if followUp && replaying != nil { return }
|
|
636
|
+
// So it would the floor's holder, and take it for the owner (#924).
|
|
637
|
+
if followUp && floorWatcher.state.closesOpenMicrophone { return }
|
|
601
638
|
recorder.endOfTurn = settings?.general.endOfTurn ?? "vad"
|
|
602
639
|
do {
|
|
603
640
|
try recorder.start()
|
|
@@ -606,6 +643,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
606
643
|
return
|
|
607
644
|
}
|
|
608
645
|
if !followUp && lessonOn { talkCheck = Task { await AgentClient.hush() } }
|
|
646
|
+
openedByItself = followUp
|
|
609
647
|
openedAt = Date()
|
|
610
648
|
patience = followUp ? (activeCall == nil ? followUpPatience : callPatience) : clickPatience
|
|
611
649
|
silentClose = followUp
|
|
@@ -739,6 +777,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
739
777
|
lastSpokeAt = Date()
|
|
740
778
|
talkCheck = Task { await AgentClient.hush() }
|
|
741
779
|
Log.info("door: opened\(busy ? " (a turn is running)" : "")")
|
|
780
|
+
openedByItself = false
|
|
742
781
|
// Push-to-talk ends on key release; no end-of-turn detection needed.
|
|
743
782
|
recorder.endOfTurn = "hold"
|
|
744
783
|
do {
|
|
@@ -752,6 +791,16 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
752
791
|
|
|
753
792
|
private func stopAndSend() {
|
|
754
793
|
guard recorder.isRecording else { return }
|
|
794
|
+
let handsFree = openedByItself
|
|
795
|
+
openedByItself = false
|
|
796
|
+
// The floor's holder was playing sound and nobody asked for the
|
|
797
|
+
// microphone: what it heard is the holder, not the owner (#924).
|
|
798
|
+
if handsFree && floorWatcher.state.closesOpenMicrophone {
|
|
799
|
+
Log.info("floor: dropped what the open microphone heard")
|
|
800
|
+
_ = recorder.stop()
|
|
801
|
+
panel.render(rest)
|
|
802
|
+
return
|
|
803
|
+
}
|
|
755
804
|
// Clicked or hands-free: nothing hushed yet. They did speak, so
|
|
756
805
|
// hush now; whatever was talking gets the words, same as the key.
|
|
757
806
|
let door = talkCheck ?? Task { await AgentClient.hush() }
|
|
@@ -780,7 +829,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
780
829
|
if !midTurn {
|
|
781
830
|
panel.render(.error("Didn't catch that"))
|
|
782
831
|
// Say it aloud: someone not looking would think it was sent.
|
|
783
|
-
await
|
|
832
|
+
await apologise("Sorry, I didn't hear that. Please say it again.", voice: voice)
|
|
784
833
|
busy = false; resetSoon()
|
|
785
834
|
}
|
|
786
835
|
return
|
|
@@ -845,10 +894,33 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
845
894
|
busy = false; resetSoon()
|
|
846
895
|
return
|
|
847
896
|
}
|
|
848
|
-
await runTurn(heard, agent: agent)
|
|
897
|
+
await runTurn(heard, agent: agent, handsFree: handsFree)
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/// Someone holds the floor (#924): the answer is kept, and shown and
|
|
902
|
+
/// said once it is free. Until then nothing of it is on screen.
|
|
903
|
+
private func waitForFloor(_ agent: String?, ownTurn: Bool = true) async {
|
|
904
|
+
let floor = floorWatcher.state
|
|
905
|
+
guard floor.holdsAnswers else { return }
|
|
906
|
+
Log.info("floor: answer\(agent.map { " (\($0))" } ?? "") kept until \(floor.holder ?? "the holder") lets go")
|
|
907
|
+
if !recorder.isRecording { panel.render(rest) }
|
|
908
|
+
// The owner's own words end the wait: "stop" drops the answer,
|
|
909
|
+
// anything else replaces it, and neither should wait for the holder.
|
|
910
|
+
let generation = stopGeneration
|
|
911
|
+
while floorWatcher.state.holdsAnswers && generation == stopGeneration && !(ownTurn && (abandoned || followUp != nil || silenced)) {
|
|
912
|
+
try? await Task.sleep(nanoseconds: 250_000_000)
|
|
849
913
|
}
|
|
850
914
|
}
|
|
851
915
|
|
|
916
|
+
/// An apology said aloud, for someone not looking. Not while the
|
|
917
|
+
/// floor is held: the line would wait behind the holder with the
|
|
918
|
+
/// turn still open, and the pill already says it.
|
|
919
|
+
private func apologise(_ text: String, voice: VoiceChoice?) async {
|
|
920
|
+
guard !floorWatcher.state.held else { return }
|
|
921
|
+
await Speech.say(text, agentID: nil, kind: "line", voice: voice)
|
|
922
|
+
}
|
|
923
|
+
|
|
852
924
|
/// A question for another agent while our own turn thinks. It runs
|
|
853
925
|
/// alongside; the answer waits its turn in the daemon's speaking queue,
|
|
854
926
|
/// so it never talks over ours. An agent already answering an aside
|
|
@@ -875,6 +947,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
875
947
|
// The next question for this agent goes now, while this
|
|
876
948
|
// answer waits to be spoken.
|
|
877
949
|
startNextAside(for: agent)
|
|
950
|
+
if case .success(let answer) = result { await waitForFloor(answer.agentID ?? agent, ownTurn: false) }
|
|
878
951
|
guard generation == stopGeneration else { return }
|
|
879
952
|
switch result {
|
|
880
953
|
case .success(let answer):
|
|
@@ -896,7 +969,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
896
969
|
}
|
|
897
970
|
case .failure(let error):
|
|
898
971
|
Log.warn("aside failed (\(agent)): \(error.localizedDescription)")
|
|
899
|
-
await
|
|
972
|
+
await apologise("Sorry, \(statusMenu.name(of: agent)) couldn't answer that.", voice: nil)
|
|
900
973
|
}
|
|
901
974
|
}
|
|
902
975
|
}
|
|
@@ -920,14 +993,20 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
920
993
|
|
|
921
994
|
/// One question and its spoken answer. Words said through the door
|
|
922
995
|
/// while it thinks replace the answer with the next turn.
|
|
923
|
-
private func runTurn(_ heard: String, agent: String) async {
|
|
996
|
+
private func runTurn(_ heard: String, agent: String, handsFree: Bool = false) async {
|
|
924
997
|
silenced = false
|
|
925
998
|
turnAgent = agent
|
|
926
999
|
do {
|
|
927
1000
|
beginNarration(agent)
|
|
928
1001
|
|
|
929
|
-
let answer = try await ask(heard, agent: agent)
|
|
1002
|
+
let answer = try await ask(heard, agent: agent, handsFree: handsFree)
|
|
930
1003
|
endNarration()
|
|
1004
|
+
// The daemon took the words for the floor holder's sound.
|
|
1005
|
+
if answer.dropped {
|
|
1006
|
+
Log.info("floor: the daemon dropped hands-free words")
|
|
1007
|
+
panel.render(.idle); busy = false; return
|
|
1008
|
+
}
|
|
1009
|
+
await waitForFloor(answer.agentID)
|
|
931
1010
|
if abandoned {
|
|
932
1011
|
abandoned = false
|
|
933
1012
|
Log.info("dropped answer (\(answer.agentID ?? "?")): \(answer.text)")
|
|
@@ -988,7 +1067,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
988
1067
|
panel.render(.error(short(error.localizedDescription)))
|
|
989
1068
|
// Say it aloud too — a voice assistant that fails only in
|
|
990
1069
|
// a 230px label has failed silently for anyone not looking.
|
|
991
|
-
await
|
|
1070
|
+
await apologise("Sorry, that didn't work.", voice: voice)
|
|
992
1071
|
resetSoon()
|
|
993
1072
|
}
|
|
994
1073
|
busy = false
|
|
@@ -1078,7 +1157,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
1078
1157
|
private var rest: Panel.State { activeCall == nil ? .idle : .onCall }
|
|
1079
1158
|
|
|
1080
1159
|
/// A new call can ring: no call in progress, no turn, microphone closed.
|
|
1081
|
-
private var canRing: Bool { activeCall == nil && !busy && !recorder.isRecording }
|
|
1160
|
+
private var canRing: Bool { activeCall == nil && !busy && !recorder.isRecording && !floorWatcher.state.held }
|
|
1082
1161
|
|
|
1083
1162
|
private func polled(_ state: RingingCalls) {
|
|
1084
1163
|
switch CallModel.action(ringing: ringingCall?.id, calls: state.calls, canRing: canRing) {
|
|
@@ -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-IIFHX625.js";import"./chunk-KHYF6KTY.js";import"./chunk-BRK6M6LA.js";import"./chunk-UIBUZ2N3.js";import"./chunk-2QNT5JDS.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-TOAWET76.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-IIFHX625.js";import"./chunk-KHYF6KTY.js";import"./chunk-BRK6M6LA.js";import"./chunk-UIBUZ2N3.js";import"./chunk-2QNT5JDS.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-4DRCRKFS.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o}from"./chunk-
|
|
2
|
-
//# sourceMappingURL=absorb-eval-
|
|
1
|
+
import{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o}from"./chunk-B6PMIUVV.js";import"./chunk-XZIKAHFU.js";import"./chunk-PNYBZPPB.js";import"./chunk-SKSYII3K.js";import"./chunk-E3AEOQMK.js";import"./chunk-WPNRDEHW.js";import"./chunk-MWNCS57L.js";import"./chunk-4LWFJ4MZ.js";import"./chunk-BIJHMWXK.js";import"./chunk-SQ37I2MD.js";import"./chunk-72SZI2UW.js";import"./chunk-FCPTN6NF.js";import"./chunk-MCI3DIWW.js";import"./chunk-6TURWSMN.js";import"./chunk-QD6IICK7.js";import"./chunk-QOYAX2FT.js";import"./chunk-2PJKGZWP.js";import"./chunk-7I7IEX4S.js";import"./chunk-KHYF6KTY.js";import"./chunk-BRK6M6LA.js";import"./chunk-UIBUZ2N3.js";import"./chunk-2QNT5JDS.js";import"./chunk-RGDKHYY2.js";import"./chunk-QIJLFPWE.js";export{i as JUDGE_ENTRY_CHARS,e as WEAK_CITATION,j as buildJudgePrompt,l as buildScorecard,f as checkArticle,n as envelopeUsage,h as findDuplicates,k as parseJudgeReply,b as pickSample,m as renderScorecard,a as seededRandom,o as summariseRuns,d as termOverlap,c as terms,g as uncitedWithFacts};
|
|
2
|
+
//# sourceMappingURL=absorb-eval-OXNL5QGQ.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-ZYUVKFX2.js";import"./chunk-SFQUP3BP.js";import"./chunk-QOYAX2FT.js";import"./chunk-2PJKGZWP.js";import"./chunk-7I7IEX4S.js";import"./chunk-JHCI6XCJ.js";import"./chunk-DSYKYZMT.js";import"./chunk-QIJLFPWE.js";export{a as createAgentContext,b as generate,c as generateStream};
|
|
2
|
+
//# sourceMappingURL=agent-GALMGMNJ.js.map
|