agentix-cli 0.70.1 → 0.72.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/CallModel.swift +55 -0
- package/apps/mac-voice/Sources/AgentXVoice/Calls.swift +172 -0
- package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +28 -3
- package/apps/mac-voice/Sources/AgentXVoice/StatusMenu.swift +2 -0
- package/apps/mac-voice/Sources/AgentXVoice/main.swift +108 -4
- package/dist/{App-LFQTOADB.js → App-244WEQJR.js} +2 -2
- package/dist/{ChatApp-RNMG2HWX.js → ChatApp-SFP6SHUU.js} +2 -2
- package/dist/{agent-7IW6472I.js → agent-PMNBXRFW.js} +2 -2
- package/dist/{board-dashboard-MYWXVYU3.js → board-dashboard-J34PSEJS.js} +491 -217
- package/dist/board-dashboard-J34PSEJS.js.map +1 -0
- package/dist/{chunk-4HCQEGXO.js → chunk-335JCO5R.js} +184 -179
- package/dist/chunk-335JCO5R.js.map +1 -0
- package/dist/{chunk-FEQ4W4XA.js → chunk-55N4VTWX.js} +2 -2
- package/dist/{chunk-5FD7HE2J.js → chunk-576L7ZFD.js} +2 -2
- package/dist/{chunk-D3RJMC37.js → chunk-7XR5LGLD.js} +2 -2
- package/dist/{chunk-YFROK44Z.js → chunk-DMWFRKWR.js} +2 -2
- package/dist/{chunk-7334DGUO.js → chunk-FMUOVVXX.js} +2 -2
- package/dist/chunk-FWQVXZ5T.js +154 -0
- package/dist/chunk-FWQVXZ5T.js.map +1 -0
- package/dist/{chunk-KTGPH77J.js → chunk-J6RZOP7Z.js} +2 -2
- package/dist/{chunk-DXAWW7ZQ.js → chunk-MESWF2ZM.js} +2 -2
- package/dist/chunk-MYVHVJWZ.js +2 -0
- package/dist/chunk-MYVHVJWZ.js.map +1 -0
- package/dist/{chunk-2DWDXT7G.js → chunk-OSHG52OO.js} +65 -65
- package/dist/chunk-OSHG52OO.js.map +1 -0
- package/dist/{chunk-MTPCD4NV.js → chunk-OWRPCMGW.js} +236 -211
- package/dist/chunk-OWRPCMGW.js.map +1 -0
- package/dist/{chunk-RKZGENSE.js → chunk-PBRI3MTE.js} +2 -2
- package/dist/{chunk-KTNOY4LW.js → chunk-PC5LKW3L.js} +2 -2
- package/dist/{chunk-4J4YVJFH.js → chunk-PHSYP22H.js} +2 -2
- package/dist/{chunk-7PWXCN22.js → chunk-PY54XYAG.js} +2 -2
- package/dist/{chunk-LKA5UUGT.js → chunk-QKV7FN67.js} +2 -2
- package/dist/chunk-RAYOK4GI.js +15 -0
- package/dist/chunk-RAYOK4GI.js.map +1 -0
- package/dist/{chunk-RN7G3D53.js → chunk-TT4T6CB3.js} +2 -2
- package/dist/{chunk-3ZRFES25.js → chunk-UFPWMNUW.js} +2 -2
- package/dist/{chunk-MOUSATFW.js → chunk-VWMO4C72.js} +2 -2
- package/dist/{chunk-O2QVQVOV.js → chunk-YVLFX35J.js} +2 -2
- package/dist/{chunk-N4EIYNN4.js → chunk-Z5TXWHDN.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/client-L4JAPXTE.js +2 -0
- package/dist/config-X7BHZ75G.js +2 -0
- package/dist/config-mutator-R2KBGTZJ.js +2 -0
- package/dist/doctor-5VFQVAVC.js +2 -0
- package/dist/index.d.ts +151 -11
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/opencode-launch-ECMEY2QZ.js +2 -0
- package/dist/opencode-launch-ECMEY2QZ.js.map +1 -0
- package/dist/opencode-process-ENS4MXHM.js +2 -0
- package/dist/program-6C3G65YA.js +2 -0
- package/dist/{promote-USGFU5NN.js → promote-36XF4LH7.js} +2 -2
- package/dist/{query-NLSKOIWJ.js → query-YFZ6GWHU.js} +2 -2
- package/dist/registry-WJSGG62F.js +2 -0
- package/dist/{request-planner-VOWMXWRI.js → request-planner-OXRWVBXI.js} +2 -2
- package/dist/{review-candidates-F6RLNHS2.js → review-candidates-5XZWP2BN.js} +2 -2
- package/dist/{routing-2LAWEIUG.js → routing-XEPCPLVM.js} +2 -2
- package/dist/schedule-tool-WQCO2YUG.js +2 -0
- package/dist/scheduler-4NQ62XDK.js +2 -0
- package/dist/{seat-4LLK2XBM.js → seat-FGFHW363.js} +2 -2
- package/dist/{select-NJHTHKEQ.js → select-N6CIZF2S.js} +2 -2
- package/dist/{set-enabled-HPR6VJCG.js → set-enabled-L7Z6H7EG.js} +2 -2
- package/dist/{tool-XQMGKZP5.js → tool-OSH6GHIF.js} +2 -2
- package/dist/wiki-E46QDRS7.js +2 -0
- package/dist/wiki-E46QDRS7.js.map +1 -0
- package/package.json +1 -1
- package/dist/board-dashboard-MYWXVYU3.js.map +0 -1
- package/dist/chunk-2DWDXT7G.js.map +0 -1
- package/dist/chunk-4HCQEGXO.js.map +0 -1
- package/dist/chunk-E6CYC4HQ.js +0 -131
- package/dist/chunk-E6CYC4HQ.js.map +0 -1
- package/dist/chunk-MTPCD4NV.js.map +0 -1
- package/dist/chunk-XXBW7CFN.js +0 -15
- package/dist/chunk-XXBW7CFN.js.map +0 -1
- package/dist/client-IQIBHXVS.js +0 -2
- package/dist/config-7AK5TI2D.js +0 -2
- package/dist/config-mutator-EI5JNMY4.js +0 -2
- package/dist/doctor-CTWQODQG.js +0 -2
- package/dist/program-ZKGWGTPV.js +0 -2
- package/dist/registry-T7SCVP55.js +0 -2
- package/dist/schedule-tool-GOQFT2XQ.js +0 -2
- package/dist/scheduler-GJVRJQ7K.js +0 -2
- package/dist/wiki-RQ5P4WWJ.js +0 -2
- /package/dist/{App-LFQTOADB.js.map → App-244WEQJR.js.map} +0 -0
- /package/dist/{ChatApp-RNMG2HWX.js.map → ChatApp-SFP6SHUU.js.map} +0 -0
- /package/dist/{agent-7IW6472I.js.map → agent-PMNBXRFW.js.map} +0 -0
- /package/dist/{chunk-FEQ4W4XA.js.map → chunk-55N4VTWX.js.map} +0 -0
- /package/dist/{chunk-5FD7HE2J.js.map → chunk-576L7ZFD.js.map} +0 -0
- /package/dist/{chunk-D3RJMC37.js.map → chunk-7XR5LGLD.js.map} +0 -0
- /package/dist/{chunk-YFROK44Z.js.map → chunk-DMWFRKWR.js.map} +0 -0
- /package/dist/{chunk-7334DGUO.js.map → chunk-FMUOVVXX.js.map} +0 -0
- /package/dist/{chunk-KTGPH77J.js.map → chunk-J6RZOP7Z.js.map} +0 -0
- /package/dist/{chunk-DXAWW7ZQ.js.map → chunk-MESWF2ZM.js.map} +0 -0
- /package/dist/{chunk-RKZGENSE.js.map → chunk-PBRI3MTE.js.map} +0 -0
- /package/dist/{chunk-KTNOY4LW.js.map → chunk-PC5LKW3L.js.map} +0 -0
- /package/dist/{chunk-4J4YVJFH.js.map → chunk-PHSYP22H.js.map} +0 -0
- /package/dist/{chunk-7PWXCN22.js.map → chunk-PY54XYAG.js.map} +0 -0
- /package/dist/{chunk-LKA5UUGT.js.map → chunk-QKV7FN67.js.map} +0 -0
- /package/dist/{chunk-RN7G3D53.js.map → chunk-TT4T6CB3.js.map} +0 -0
- /package/dist/{chunk-3ZRFES25.js.map → chunk-UFPWMNUW.js.map} +0 -0
- /package/dist/{chunk-MOUSATFW.js.map → chunk-VWMO4C72.js.map} +0 -0
- /package/dist/{chunk-O2QVQVOV.js.map → chunk-YVLFX35J.js.map} +0 -0
- /package/dist/{chunk-N4EIYNN4.js.map → chunk-Z5TXWHDN.js.map} +0 -0
- /package/dist/{client-IQIBHXVS.js.map → client-L4JAPXTE.js.map} +0 -0
- /package/dist/{config-7AK5TI2D.js.map → config-X7BHZ75G.js.map} +0 -0
- /package/dist/{config-mutator-EI5JNMY4.js.map → config-mutator-R2KBGTZJ.js.map} +0 -0
- /package/dist/{doctor-CTWQODQG.js.map → doctor-5VFQVAVC.js.map} +0 -0
- /package/dist/{program-ZKGWGTPV.js.map → opencode-process-ENS4MXHM.js.map} +0 -0
- /package/dist/{promote-USGFU5NN.js.map → program-6C3G65YA.js.map} +0 -0
- /package/dist/{query-NLSKOIWJ.js.map → promote-36XF4LH7.js.map} +0 -0
- /package/dist/{registry-T7SCVP55.js.map → query-YFZ6GWHU.js.map} +0 -0
- /package/dist/{routing-2LAWEIUG.js.map → registry-WJSGG62F.js.map} +0 -0
- /package/dist/{request-planner-VOWMXWRI.js.map → request-planner-OXRWVBXI.js.map} +0 -0
- /package/dist/{review-candidates-F6RLNHS2.js.map → review-candidates-5XZWP2BN.js.map} +0 -0
- /package/dist/{schedule-tool-GOQFT2XQ.js.map → routing-XEPCPLVM.js.map} +0 -0
- /package/dist/{scheduler-GJVRJQ7K.js.map → schedule-tool-WQCO2YUG.js.map} +0 -0
- /package/dist/{seat-4LLK2XBM.js.map → scheduler-4NQ62XDK.js.map} +0 -0
- /package/dist/{wiki-RQ5P4WWJ.js.map → seat-FGFHW363.js.map} +0 -0
- /package/dist/{select-NJHTHKEQ.js.map → select-N6CIZF2S.js.map} +0 -0
- /package/dist/{set-enabled-HPR6VJCG.js.map → set-enabled-L7Z6H7EG.js.map} +0 -0
- /package/dist/{tool-XQMGKZP5.js.map → tool-OSH6GHIF.js.map} +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// An agent ringing the owner (#321): the daemon's call, and the decisions
|
|
4
|
+
/// the widget makes about it. Foundation only, so Tests/Calls runs it
|
|
5
|
+
/// without AppKit. The network and the pill are in Calls.swift.
|
|
6
|
+
struct IncomingCall: Decodable, Equatable {
|
|
7
|
+
let id: String
|
|
8
|
+
let agentId: String
|
|
9
|
+
let reason: String
|
|
10
|
+
let urgency: String
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/// GET /calls/ringing.
|
|
14
|
+
struct RingingCalls: Decodable {
|
|
15
|
+
let calls: [IncomingCall]
|
|
16
|
+
let ringSound: String
|
|
17
|
+
let ringSeconds: Int
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
enum CallModel {
|
|
21
|
+
enum Action: Equatable {
|
|
22
|
+
case none
|
|
23
|
+
/// Start ringing this call.
|
|
24
|
+
case ring(IncomingCall)
|
|
25
|
+
/// The call ringing here stopped ringing on the daemon: missed,
|
|
26
|
+
/// declined or answered somewhere else.
|
|
27
|
+
case stop
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// What to do after a poll. `ringing` is the call ringing here, if
|
|
31
|
+
/// any. A new call rings only when the widget is free (`canRing`: no
|
|
32
|
+
/// call in progress, no turn, microphone closed); until then it stays
|
|
33
|
+
/// on the daemon and is offered again on the next poll.
|
|
34
|
+
static func action(ringing: String?, calls: [IncomingCall], canRing: Bool) -> Action {
|
|
35
|
+
if let ringing {
|
|
36
|
+
return calls.contains { $0.id == ringing } ? .none : .stop
|
|
37
|
+
}
|
|
38
|
+
guard canRing, let first = calls.first else { return .none }
|
|
39
|
+
return .ring(first)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// "Call back in…" choices, in minutes.
|
|
43
|
+
static let laterChoices = [5, 15, 30]
|
|
44
|
+
|
|
45
|
+
/// Said during a call to end it.
|
|
46
|
+
static func isHangUp(_ text: String) -> Bool {
|
|
47
|
+
text.range(of: #"^\s*(hang up|end (the )?call|good ?bye|bye( bye)?|talk (to you )?later)[\s.!]*$"#,
|
|
48
|
+
options: [.regularExpression, .caseInsensitive]) != nil
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// The pill's words while it rings.
|
|
52
|
+
static func ringingText(name: String, reason: String) -> String {
|
|
53
|
+
name.isEmpty ? "Incoming call · \(reason)" : "\(name) is calling · \(reason)"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
|
|
3
|
+
/// An agent ringing the owner (#321): the daemon's /calls routes, the ring
|
|
4
|
+
/// sound, the poll, and the buttons on the pill. Decisions are in
|
|
5
|
+
/// CallModel.swift; main.swift wires them to the conversation.
|
|
6
|
+
enum CallClient {
|
|
7
|
+
/// GET /calls/ringing, or nil when the daemon cannot be reached. The
|
|
8
|
+
/// poll is also how the daemon knows the widget is running.
|
|
9
|
+
static func ringing() async -> RingingCalls? {
|
|
10
|
+
guard let url = URL(string: "\(Config.daemonURL)/calls/ringing") else { return nil }
|
|
11
|
+
var req = URLRequest(url: url)
|
|
12
|
+
req.timeoutInterval = 3
|
|
13
|
+
guard let (data, response) = try? await URLSession.shared.data(for: req),
|
|
14
|
+
(response as? HTTPURLResponse)?.statusCode == 200 else { return nil }
|
|
15
|
+
return try? JSONDecoder().decode(RingingCalls.self, from: data)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private struct Answered: Decodable { let opener: String }
|
|
19
|
+
|
|
20
|
+
/// Pick up: the opener to send through /ask, or nil when the call is
|
|
21
|
+
/// no longer ringing.
|
|
22
|
+
static func answer(_ id: String) async -> String? {
|
|
23
|
+
guard let data = await post(id, "answer", [:]) else { return nil }
|
|
24
|
+
return (try? JSONDecoder().decode(Answered.self, from: data))?.opener
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static func decline(_ id: String) async { _ = await post(id, "decline", [:]) }
|
|
28
|
+
static func later(_ id: String, minutes: Int) async { _ = await post(id, "later", ["minutes": minutes]) }
|
|
29
|
+
static func hangUp(_ id: String) async { _ = await post(id, "hangup", [:]) }
|
|
30
|
+
|
|
31
|
+
/// The body on 200, else nil.
|
|
32
|
+
private static func post(_ id: String, _ action: String, _ body: [String: Any]) async -> Data? {
|
|
33
|
+
guard let url = URL(string: "\(Config.daemonURL)/calls/\(id)/\(action)") else { return nil }
|
|
34
|
+
var req = URLRequest(url: url)
|
|
35
|
+
req.httpMethod = "POST"
|
|
36
|
+
req.timeoutInterval = 5
|
|
37
|
+
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
38
|
+
req.httpBody = try? JSONSerialization.data(withJSONObject: body)
|
|
39
|
+
guard let (data, response) = try? await URLSession.shared.data(for: req) else { return nil }
|
|
40
|
+
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
|
|
41
|
+
if status != 200 { Log.warn("call \(action) \(id): HTTP \(status)") }
|
|
42
|
+
return status == 200 ? data : nil
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Polls /calls/ringing every two seconds for the life of the app.
|
|
47
|
+
@MainActor
|
|
48
|
+
final class CallWatcher {
|
|
49
|
+
private var timer: Timer?
|
|
50
|
+
private var polling = false
|
|
51
|
+
/// Each poll that reached the daemon.
|
|
52
|
+
var onPoll: ((RingingCalls) -> Void)?
|
|
53
|
+
|
|
54
|
+
func start() {
|
|
55
|
+
timer?.invalidate()
|
|
56
|
+
timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { _ in
|
|
57
|
+
Task { @MainActor [weak self] in await self?.poll() }
|
|
58
|
+
}
|
|
59
|
+
Task { await poll() }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private func poll() async {
|
|
63
|
+
guard !polling else { return }
|
|
64
|
+
polling = true
|
|
65
|
+
defer { polling = false }
|
|
66
|
+
if let state = await CallClient.ringing() { onPoll?(state) }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// The ring: a system sound, again every few seconds until stopped.
|
|
71
|
+
@MainActor
|
|
72
|
+
final class Ringer {
|
|
73
|
+
private var timer: Timer?
|
|
74
|
+
private var sound: NSSound?
|
|
75
|
+
|
|
76
|
+
func start(sound name: String) {
|
|
77
|
+
stop()
|
|
78
|
+
// Names only, as the daemon checks them: never a path from the wire.
|
|
79
|
+
guard name.range(of: #"^[\w -]+$"#, options: .regularExpression) != nil,
|
|
80
|
+
let s = NSSound(contentsOfFile: "/System/Library/Sounds/\(name).aiff", byReference: true) else { return }
|
|
81
|
+
sound = s
|
|
82
|
+
s.play()
|
|
83
|
+
timer = Timer.scheduledTimer(withTimeInterval: 2.5, repeats: true) { _ in
|
|
84
|
+
Task { @MainActor [weak self] in
|
|
85
|
+
guard let s = self?.sound, !s.isPlaying else { return }
|
|
86
|
+
s.play()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
func stop() {
|
|
92
|
+
timer?.invalidate(); timer = nil
|
|
93
|
+
sound?.stop(); sound = nil
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// The pill's call buttons: Answer, Later and Decline while it rings;
|
|
98
|
+
/// Hang up during the call.
|
|
99
|
+
final class CallBar: NSView {
|
|
100
|
+
enum Mode { case hidden, ringing, connected }
|
|
101
|
+
|
|
102
|
+
static func width(_ mode: Mode) -> CGFloat {
|
|
103
|
+
switch mode {
|
|
104
|
+
case .hidden: return 0
|
|
105
|
+
case .ringing: return 3 * 24 + 2 * 4
|
|
106
|
+
case .connected: return 24
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
var onAnswer: (() -> Void)?
|
|
111
|
+
var onDecline: (() -> Void)?
|
|
112
|
+
var onLater: ((Int) -> Void)?
|
|
113
|
+
var onHangUp: (() -> Void)?
|
|
114
|
+
|
|
115
|
+
private let answer = CallBar.button("phone.fill", "Answer", .systemGreen)
|
|
116
|
+
private let later = CallBar.button("clock.fill", "Call back later", .secondaryLabelColor)
|
|
117
|
+
private let decline = CallBar.button("phone.down.fill", "Decline", .systemRed)
|
|
118
|
+
private let hangUp = CallBar.button("phone.down.fill", "Hang up", .systemRed)
|
|
119
|
+
private(set) var mode = Mode.hidden
|
|
120
|
+
|
|
121
|
+
override init(frame: NSRect) {
|
|
122
|
+
super.init(frame: frame)
|
|
123
|
+
for b in [answer, later, decline, hangUp] { b.target = self; addSubview(b) }
|
|
124
|
+
answer.action = #selector(answerClicked)
|
|
125
|
+
later.action = #selector(laterClicked)
|
|
126
|
+
decline.action = #selector(declineClicked)
|
|
127
|
+
hangUp.action = #selector(hangUpClicked)
|
|
128
|
+
show(.hidden)
|
|
129
|
+
}
|
|
130
|
+
required init?(coder: NSCoder) { fatalError("not used") }
|
|
131
|
+
|
|
132
|
+
private static func button(_ symbol: String, _ label: String, _ tint: NSColor) -> NSButton {
|
|
133
|
+
let b = NSButton(frame: NSRect(x: 0, y: 0, width: 24, height: 24))
|
|
134
|
+
b.isBordered = false
|
|
135
|
+
b.bezelStyle = .regularSquare
|
|
136
|
+
b.imagePosition = .imageOnly
|
|
137
|
+
b.image = NSImage(systemSymbolName: symbol, accessibilityDescription: label)?
|
|
138
|
+
.withSymbolConfiguration(.init(pointSize: 15, weight: .semibold))
|
|
139
|
+
b.contentTintColor = tint
|
|
140
|
+
b.toolTip = label
|
|
141
|
+
b.setAccessibilityLabel(label)
|
|
142
|
+
return b
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
func show(_ mode: Mode) {
|
|
146
|
+
self.mode = mode
|
|
147
|
+
isHidden = mode == .hidden
|
|
148
|
+
answer.isHidden = mode != .ringing
|
|
149
|
+
later.isHidden = mode != .ringing
|
|
150
|
+
decline.isHidden = mode != .ringing
|
|
151
|
+
hangUp.isHidden = mode != .connected
|
|
152
|
+
let shown = mode == .ringing ? [answer, later, decline] : mode == .connected ? [hangUp] : []
|
|
153
|
+
for (i, b) in shown.enumerated() { b.frame.origin = NSPoint(x: CGFloat(i) * 28, y: 0) }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@objc private func answerClicked() { onAnswer?() }
|
|
157
|
+
@objc private func declineClicked() { onDecline?() }
|
|
158
|
+
@objc private func hangUpClicked() { onHangUp?() }
|
|
159
|
+
|
|
160
|
+
@objc private func laterClicked() {
|
|
161
|
+
let menu = NSMenu()
|
|
162
|
+
for minutes in CallModel.laterChoices {
|
|
163
|
+
let item = NSMenuItem(title: "Call back in \(minutes) min", action: #selector(laterChosen(_:)), keyEquivalent: "")
|
|
164
|
+
item.target = self
|
|
165
|
+
item.tag = minutes
|
|
166
|
+
menu.addItem(item)
|
|
167
|
+
}
|
|
168
|
+
menu.popUp(positioning: nil, at: NSPoint(x: 0, y: later.frame.maxY), in: self)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@objc private func laterChosen(_ item: NSMenuItem) { onLater?(item.tag) }
|
|
172
|
+
}
|
|
@@ -26,6 +26,8 @@ final class Panel: NSPanel {
|
|
|
26
26
|
/// Busy agents shown in the mini row; 0 hides it.
|
|
27
27
|
private(set) var busyCount = 0
|
|
28
28
|
private let closeButton = NSButton()
|
|
29
|
+
/// Answer, Later and Decline while an agent rings; Hang up during the call.
|
|
30
|
+
let callBar = CallBar(frame: NSRect(x: 0, y: 15, width: 0, height: 24))
|
|
29
31
|
/// The orb, the words and the close button: the pill itself, which
|
|
30
32
|
/// stays where it is while the widget grows into an answer.
|
|
31
33
|
let row = RowView(frame: NSRect(origin: .zero, size: Panel.size))
|
|
@@ -138,6 +140,10 @@ final class Panel: NSPanel {
|
|
|
138
140
|
/// follow it at all in a noisy room.
|
|
139
141
|
case saying(String)
|
|
140
142
|
case error(String)
|
|
143
|
+
/// An agent is calling: the words already name it and its reason.
|
|
144
|
+
case ringing(String)
|
|
145
|
+
/// In a call, between turns: a click talks.
|
|
146
|
+
case onCall
|
|
141
147
|
|
|
142
148
|
var text: String {
|
|
143
149
|
switch self {
|
|
@@ -149,6 +155,8 @@ final class Panel: NSPanel {
|
|
|
149
155
|
// "·" is the system's own separator glyph.
|
|
150
156
|
case .working(let what, let secs): return "\(what) · \(secs)s"
|
|
151
157
|
case .error(let m): return m
|
|
158
|
+
case .ringing(let text): return text
|
|
159
|
+
case .onCall: return "On call · click to talk"
|
|
152
160
|
}
|
|
153
161
|
}
|
|
154
162
|
var color: NSColor {
|
|
@@ -162,6 +170,8 @@ final class Panel: NSPanel {
|
|
|
162
170
|
case .thinking, .working: return Brand.primaryBright
|
|
163
171
|
case .speaking, .saying: return Brand.accentDeep
|
|
164
172
|
case .error: return Brand.alert
|
|
173
|
+
case .ringing: return Brand.accent
|
|
174
|
+
case .onCall: return Brand.accentDeep
|
|
165
175
|
}
|
|
166
176
|
}
|
|
167
177
|
|
|
@@ -173,10 +183,11 @@ final class Panel: NSPanel {
|
|
|
173
183
|
/// Idle and errors hold it still.
|
|
174
184
|
var orbPhase: OrbModel.Phase {
|
|
175
185
|
switch self {
|
|
176
|
-
case .idle, .error: return .idle
|
|
186
|
+
case .idle, .error, .onCall: return .idle
|
|
177
187
|
case .listening: return .listening
|
|
178
188
|
case .thinking, .working: return .thinking
|
|
179
|
-
|
|
189
|
+
// Ringing pulses, so the pill is seen as well as heard.
|
|
190
|
+
case .speaking, .saying, .ringing: return .speaking
|
|
180
191
|
}
|
|
181
192
|
}
|
|
182
193
|
}
|
|
@@ -247,6 +258,7 @@ final class Panel: NSPanel {
|
|
|
247
258
|
row.addSubview(closeButton)
|
|
248
259
|
miniOrbs.isHidden = true
|
|
249
260
|
row.addSubview(miniOrbs)
|
|
261
|
+
row.addSubview(callBar)
|
|
250
262
|
surface.layoutContent = { [weak self] bounds in self?.layoutContent(bounds) }
|
|
251
263
|
MainActor.assumeIsolated { layoutContent(surface.bounds) }
|
|
252
264
|
surface.addTrackingArea(NSTrackingArea(rect: .zero,
|
|
@@ -279,7 +291,11 @@ final class Panel: NSPanel {
|
|
|
279
291
|
// the words give up that room while they show.
|
|
280
292
|
let miniWidth = busyCount > 0 ? MiniOrbsHost.width(busyCount) : 0
|
|
281
293
|
miniOrbs.frame = NSRect(x: bounds.width - 28 - miniWidth, y: (h - 24) / 2, width: miniWidth, height: 24)
|
|
282
|
-
|
|
294
|
+
// The call buttons sit left of those, and the words give way again.
|
|
295
|
+
let callWidth = CallBar.width(callBar.mode)
|
|
296
|
+
let callX = bounds.width - 28 - (miniWidth > 0 ? miniWidth + 4 : 0) - callWidth
|
|
297
|
+
callBar.frame = NSRect(x: callX, y: (h - 24) / 2, width: callWidth, height: 24)
|
|
298
|
+
let clipWidth = bounds.width - 88 - (miniWidth > 0 ? miniWidth + 4 : 0) - (callWidth > 0 ? callWidth + 4 : 0)
|
|
283
299
|
if let clip, clip.frame.width != clipWidth {
|
|
284
300
|
clip.frame.size.width = clipWidth
|
|
285
301
|
// Text that scrolled may fit now, and the other way round.
|
|
@@ -433,6 +449,14 @@ final class Panel: NSPanel {
|
|
|
433
449
|
onRender?(state)
|
|
434
450
|
}
|
|
435
451
|
|
|
452
|
+
/// The call buttons for this moment of a call; `.hidden` outside one.
|
|
453
|
+
@MainActor
|
|
454
|
+
func showCall(_ mode: CallBar.Mode) {
|
|
455
|
+
guard mode != callBar.mode else { return }
|
|
456
|
+
callBar.show(mode)
|
|
457
|
+
if let content = contentView { layoutContent(content.bounds) }
|
|
458
|
+
}
|
|
459
|
+
|
|
436
460
|
/// The mini orbs: one per busy agent, or none to hide the row. The
|
|
437
461
|
/// pill stays on screen while the row shows, even when idle.
|
|
438
462
|
@MainActor
|
|
@@ -456,6 +480,7 @@ final class Panel: NSPanel {
|
|
|
456
480
|
let name = agentName()
|
|
457
481
|
if name.isEmpty { return state.text }
|
|
458
482
|
if case .error = state { return state.text }
|
|
483
|
+
if case .ringing = state { return state.text }
|
|
459
484
|
return "\(name) · \(state.text)"
|
|
460
485
|
}
|
|
461
486
|
|
|
@@ -122,6 +122,8 @@ final class StatusMenu: NSObject, NSMenuDelegate {
|
|
|
122
122
|
case .thinking, .working: ("ellipsis.circle", "Thinking")
|
|
123
123
|
case .speaking, .saying: ("speaker.wave.2.fill", "Speaking")
|
|
124
124
|
case .error: ("exclamationmark.triangle", "Error")
|
|
125
|
+
case .ringing: ("phone.arrow.down.left.fill", "Incoming call")
|
|
126
|
+
case .onCall: ("phone.fill", "On a call")
|
|
125
127
|
}
|
|
126
128
|
guard item.button?.toolTip != "AgentX Voice — \(label)" else { return }
|
|
127
129
|
let image = NSImage(systemSymbolName: symbol, accessibilityDescription: "AgentX Voice: \(label)")
|
|
@@ -72,6 +72,14 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
72
72
|
/// would hear the next answer as the listener's words.
|
|
73
73
|
private var openAsides = 0
|
|
74
74
|
|
|
75
|
+
// An agent ringing the owner (#321).
|
|
76
|
+
private let callWatcher = CallWatcher()
|
|
77
|
+
private let ringer = Ringer()
|
|
78
|
+
/// The call ringing on the pill now.
|
|
79
|
+
private var ringingCall: IncomingCall?
|
|
80
|
+
/// The call in progress: every turn goes to its agent until hang-up.
|
|
81
|
+
private var activeCall: IncomingCall?
|
|
82
|
+
|
|
75
83
|
/// /ask, counted in flight for the menu while it thinks.
|
|
76
84
|
private func ask(_ heard: String, agent: String) async throws -> AgentClient.Answer {
|
|
77
85
|
track(agent, 1)
|
|
@@ -198,6 +206,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
198
206
|
/// shortcut is asking, a by-name answer being spoken, else the agent
|
|
199
207
|
/// our turn is asking, else the target.
|
|
200
208
|
private var shownAgent: String {
|
|
209
|
+
if let call = ringingCall ?? activeCall { return call.agentId }
|
|
201
210
|
if let forcedAgent, recorder.isRecording { return forcedAgent }
|
|
202
211
|
if let asideSpeaker { return asideSpeaker }
|
|
203
212
|
return busy && !turnAgent.isEmpty ? turnAgent : Config.effectiveAgentID
|
|
@@ -216,6 +225,9 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
216
225
|
/// next talk key brings the pill back.
|
|
217
226
|
private func dismissPill() {
|
|
218
227
|
Log.info("pill: dismissed")
|
|
228
|
+
// Closing the pill declines a ringing call and ends one in progress.
|
|
229
|
+
if ringingCall != nil { endRinging { await CallClient.decline($0) } }
|
|
230
|
+
if activeCall != nil { hangUp() }
|
|
219
231
|
panel.dismiss()
|
|
220
232
|
if recorder.isRecording {
|
|
221
233
|
stopPolling()
|
|
@@ -348,6 +360,13 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
348
360
|
|
|
349
361
|
statusMenu.onStop = { [weak self] in self?.stopSpeaking() }
|
|
350
362
|
|
|
363
|
+
panel.callBar.onAnswer = { [weak self] in self?.answerCall() }
|
|
364
|
+
panel.callBar.onDecline = { [weak self] in self?.endRinging { await CallClient.decline($0) } }
|
|
365
|
+
panel.callBar.onLater = { [weak self] minutes in self?.endRinging { await CallClient.later($0, minutes: minutes) } }
|
|
366
|
+
panel.callBar.onHangUp = { [weak self] in self?.hangUp() }
|
|
367
|
+
callWatcher.onPoll = { [weak self] state in self?.polled(state) }
|
|
368
|
+
callWatcher.start()
|
|
369
|
+
|
|
351
370
|
// The target does not wait for the microphone: the menu shows it
|
|
352
371
|
// either way.
|
|
353
372
|
Task { @MainActor in
|
|
@@ -380,6 +399,8 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
380
399
|
private let followUpPatience: TimeInterval = 4.0
|
|
381
400
|
/// How long a clicked session waits for you to start talking.
|
|
382
401
|
private let clickPatience: TimeInterval = 8.0
|
|
402
|
+
/// In a call the other side waits longer for an answer.
|
|
403
|
+
private let callPatience: TimeInterval = 10.0
|
|
383
404
|
|
|
384
405
|
private var listenPoll: Timer?
|
|
385
406
|
private var openedAt = Date()
|
|
@@ -398,7 +419,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
398
419
|
return
|
|
399
420
|
}
|
|
400
421
|
openedAt = Date()
|
|
401
|
-
patience = followUp ? followUpPatience : clickPatience
|
|
422
|
+
patience = followUp ? (activeCall == nil ? followUpPatience : callPatience) : clickPatience
|
|
402
423
|
silentClose = followUp
|
|
403
424
|
asideSpeaker = nil
|
|
404
425
|
panel.render(.listening)
|
|
@@ -428,7 +449,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
428
449
|
if Date().timeIntervalSince(openedAt) >= patience {
|
|
429
450
|
stopPolling()
|
|
430
451
|
_ = recorder.stop()
|
|
431
|
-
panel.render(silentClose ?
|
|
452
|
+
panel.render(silentClose ? rest : .error("Didn't catch that"))
|
|
432
453
|
if !silentClose { resetSoon() }
|
|
433
454
|
}
|
|
434
455
|
}
|
|
@@ -459,7 +480,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
459
480
|
asideSpeaker = nil
|
|
460
481
|
lastSpokeAt = Date()
|
|
461
482
|
Task { await Speech.stopAll() }
|
|
462
|
-
if !recorder.isRecording { panel.render(
|
|
483
|
+
if !recorder.isRecording { panel.render(rest) }
|
|
463
484
|
}
|
|
464
485
|
|
|
465
486
|
private func startListening() {
|
|
@@ -527,6 +548,13 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
527
548
|
return
|
|
528
549
|
}
|
|
529
550
|
Log.info("heard: \(heard)")
|
|
551
|
+
if activeCall != nil && CallModel.isHangUp(heard) {
|
|
552
|
+
_ = await door.value
|
|
553
|
+
await AgentClient.resume()
|
|
554
|
+
if !midTurn { busy = false }
|
|
555
|
+
hangUp()
|
|
556
|
+
return
|
|
557
|
+
}
|
|
530
558
|
let hushed = await door.value
|
|
531
559
|
// Always through the door, even with nothing hushed: the
|
|
532
560
|
// listener's turn is over, so the daemon's queue plays on.
|
|
@@ -540,6 +568,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
540
568
|
// stays. An agent's own shortcut already said who.
|
|
541
569
|
let agent: String
|
|
542
570
|
if let forced { agent = forced }
|
|
571
|
+
else if let call = activeCall { agent = call.agentId }
|
|
543
572
|
else {
|
|
544
573
|
// Any agent on the mesh, by name. One on another node is
|
|
545
574
|
// remembered, so the pill and the menu can name and colour it.
|
|
@@ -797,6 +826,81 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
797
826
|
}
|
|
798
827
|
}
|
|
799
828
|
|
|
829
|
+
// --- Calls (#321) ---
|
|
830
|
+
//
|
|
831
|
+
// An agent rings: the pill shows who and why, rings, and offers Answer,
|
|
832
|
+
// Later and Decline. Answering sends the daemon's opener through /ask,
|
|
833
|
+
// so the agent speaks first; then the usual hands-free loop runs with
|
|
834
|
+
// the caller as the agent until hang-up.
|
|
835
|
+
|
|
836
|
+
/// At rest: waiting for the next words in a call, else idle.
|
|
837
|
+
private var rest: Panel.State { activeCall == nil ? .idle : .onCall }
|
|
838
|
+
|
|
839
|
+
private func polled(_ state: RingingCalls) {
|
|
840
|
+
let free = activeCall == nil && !busy && !recorder.isRecording
|
|
841
|
+
switch CallModel.action(ringing: ringingCall?.id, calls: state.calls, canRing: free) {
|
|
842
|
+
case .none:
|
|
843
|
+
return
|
|
844
|
+
case .stop:
|
|
845
|
+
Log.info("call: stopped ringing")
|
|
846
|
+
ringer.stop()
|
|
847
|
+
ringingCall = nil
|
|
848
|
+
panel.showCall(.hidden)
|
|
849
|
+
panel.render(.idle)
|
|
850
|
+
case .ring(let call):
|
|
851
|
+
Log.info("call: \(call.agentId) is calling: \(call.reason)")
|
|
852
|
+
ringingCall = call
|
|
853
|
+
panel.summon()
|
|
854
|
+
panel.showCall(.ringing)
|
|
855
|
+
panel.render(.ringing(CallModel.ringingText(name: statusMenu.name(of: call.agentId), reason: call.reason)))
|
|
856
|
+
ringer.start(sound: state.ringSound)
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/// Decline or later: the ring stops and the pill goes back to rest.
|
|
861
|
+
private func endRinging(_ send: @escaping (String) async -> Void) {
|
|
862
|
+
guard let call = ringingCall else { return }
|
|
863
|
+
ringer.stop()
|
|
864
|
+
ringingCall = nil
|
|
865
|
+
panel.showCall(.hidden)
|
|
866
|
+
panel.render(.idle)
|
|
867
|
+
Task { await send(call.id) }
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
private func answerCall() {
|
|
871
|
+
// A turn of our own is still running: keep ringing until it ends.
|
|
872
|
+
guard let call = ringingCall, !busy else { return }
|
|
873
|
+
if recorder.isRecording { stopPolling(); _ = recorder.stop() }
|
|
874
|
+
ringer.stop()
|
|
875
|
+
ringingCall = nil
|
|
876
|
+
busy = true
|
|
877
|
+
panel.render(.thinking)
|
|
878
|
+
Task { @MainActor in
|
|
879
|
+
guard let opener = await CallClient.answer(call.id) else {
|
|
880
|
+
busy = false
|
|
881
|
+
panel.showCall(.hidden)
|
|
882
|
+
panel.render(.error("The call ended"))
|
|
883
|
+
resetSoon()
|
|
884
|
+
return
|
|
885
|
+
}
|
|
886
|
+
Log.info("call: answered \(call.id)")
|
|
887
|
+
activeCall = call
|
|
888
|
+
panel.showCall(.connected)
|
|
889
|
+
await runTurn(opener, agent: call.agentId)
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/// Hang up: the button, "bye", or closing the pill during a call.
|
|
894
|
+
private func hangUp() {
|
|
895
|
+
guard let call = activeCall else { return }
|
|
896
|
+
Log.info("call: hung up \(call.id)")
|
|
897
|
+
activeCall = nil
|
|
898
|
+
panel.showCall(.hidden)
|
|
899
|
+
if recorder.isRecording { stopPolling(); _ = recorder.stop() }
|
|
900
|
+
stopSpeaking()
|
|
901
|
+
Task { await CallClient.hangUp(call.id) }
|
|
902
|
+
}
|
|
903
|
+
|
|
800
904
|
private func short(_ s: String) -> String {
|
|
801
905
|
s.count > 40 ? String(s.prefix(38)) + "…" : s
|
|
802
906
|
}
|
|
@@ -805,7 +909,7 @@ final class App: NSObject, NSApplicationDelegate {
|
|
|
805
909
|
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
|
|
806
910
|
MainActor.assumeIsolated {
|
|
807
911
|
guard let self, !self.recorder.isRecording, !self.busy else { return }
|
|
808
|
-
self.panel.render(.
|
|
912
|
+
self.panel.render(self.rest)
|
|
809
913
|
}
|
|
810
914
|
}
|
|
811
915
|
}
|
|
@@ -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-7XR5LGLD.js";import"./chunk-RAYOK4GI.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-244WEQJR.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-7XR5LGLD.js";import"./chunk-RAYOK4GI.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-SFP6SHUU.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-Z5TXWHDN.js";import"./chunk-SFQUP3BP.js";import"./chunk-QOYAX2FT.js";import"./chunk-5MUJZODV.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-PMNBXRFW.js.map
|