agentix-cli 0.70.0 → 0.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/apps/mac-voice/Sources/AgentXVoice/CallModel.swift +55 -0
  2. package/apps/mac-voice/Sources/AgentXVoice/Calls.swift +172 -0
  3. package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +28 -3
  4. package/apps/mac-voice/Sources/AgentXVoice/StatusMenu.swift +2 -0
  5. package/apps/mac-voice/Sources/AgentXVoice/main.swift +108 -4
  6. package/dist/{App-Z66ABY7H.js → App-PRD5JNO4.js} +2 -2
  7. package/dist/{ChatApp-Z2LZ7IM3.js → ChatApp-BKS6OJ3L.js} +2 -2
  8. package/dist/{agent-V223TG6H.js → agent-QF2JQ72Z.js} +2 -2
  9. package/dist/{board-dashboard-HR3PQPFW.js → board-dashboard-AROMOAKS.js} +4 -4
  10. package/dist/{board-dashboard-HR3PQPFW.js.map → board-dashboard-AROMOAKS.js.map} +1 -1
  11. package/dist/{chunk-PHDS2HIN.js → chunk-3C2SQDIH.js} +240 -229
  12. package/dist/chunk-3C2SQDIH.js.map +1 -0
  13. package/dist/{chunk-YBF2QVZ7.js → chunk-4XBYHKYG.js} +2 -2
  14. package/dist/{chunk-OPUPVGYP.js → chunk-5Y7QRFSL.js} +2 -2
  15. package/dist/{chunk-W7SEPJFH.js → chunk-6LTD7XL5.js} +2 -2
  16. package/dist/chunk-AAA6NGY5.js +6 -0
  17. package/dist/chunk-AAA6NGY5.js.map +1 -0
  18. package/dist/chunk-AHOGFVHA.js +15 -0
  19. package/dist/{chunk-DTX4I5LQ.js.map → chunk-AHOGFVHA.js.map} +1 -1
  20. package/dist/{chunk-OVX6NRMD.js → chunk-BO4WLXXC.js} +2 -2
  21. package/dist/{chunk-6QDOUOKD.js → chunk-D233M73Q.js} +2 -2
  22. package/dist/{chunk-BBIWAJRX.js → chunk-DJMUP6TK.js} +2 -2
  23. package/dist/{chunk-ML6CNRBK.js → chunk-EMRQ6W5J.js} +2 -2
  24. package/dist/{chunk-ML6CNRBK.js.map → chunk-EMRQ6W5J.js.map} +1 -1
  25. package/dist/{chunk-3RC4LO2G.js → chunk-HXW4NAWF.js} +2 -2
  26. package/dist/{chunk-KLHG5ZQH.js → chunk-IK2XG65P.js} +2 -2
  27. package/dist/{chunk-YK7GFEGO.js → chunk-JBVABB26.js} +2 -2
  28. package/dist/{chunk-3IY2YWXP.js → chunk-JU67IZLC.js} +3 -3
  29. package/dist/chunk-JU67IZLC.js.map +1 -0
  30. package/dist/{chunk-P2RYUODK.js → chunk-K6CHA6JJ.js} +2 -2
  31. package/dist/{chunk-LHLNARRW.js → chunk-MT3BD6WG.js} +186 -184
  32. package/dist/chunk-MT3BD6WG.js.map +1 -0
  33. package/dist/{chunk-FBDIJEKL.js → chunk-OTMDMGHE.js} +9 -9
  34. package/dist/chunk-OTMDMGHE.js.map +1 -0
  35. package/dist/{chunk-SN5N2FPG.js → chunk-RCKICCTT.js} +2 -2
  36. package/dist/{chunk-FSYA4GNU.js → chunk-SELIVR6U.js} +2 -2
  37. package/dist/{chunk-FUR3T3IN.js → chunk-SFWA67GQ.js} +2 -2
  38. package/dist/{chunk-YL223TEU.js → chunk-WLX4766Y.js} +2 -2
  39. package/dist/{chunk-TTVVXBCM.js → chunk-WPNRDEHW.js} +2 -2
  40. package/dist/chunk-XJQNIFZ4.js +49 -0
  41. package/dist/chunk-XJQNIFZ4.js.map +1 -0
  42. package/dist/cli.js +1 -1
  43. package/dist/cli.js.map +1 -1
  44. package/dist/client-UPBQVXYM.js +2 -0
  45. package/dist/config-UY5K6SCL.js +2 -0
  46. package/dist/config-mutator-7KOSRAQF.js +2 -0
  47. package/dist/doctor-GFPSIJGB.js +2 -0
  48. package/dist/index.d.ts +106 -9
  49. package/dist/index.js +1 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/migrate-v2-W7RRFM24.js +4 -0
  52. package/dist/{migrate-v2-ZOIMGITH.js.map → migrate-v2-W7RRFM24.js.map} +1 -1
  53. package/dist/program-XMGZOYX3.js +2 -0
  54. package/dist/promote-YLKNYYIU.js +2 -0
  55. package/dist/query-CVRXEG43.js +2 -0
  56. package/dist/registry-HZL363TA.js +2 -0
  57. package/dist/{request-planner-NMOAOZIM.js → request-planner-76NCBYBW.js} +2 -2
  58. package/dist/{review-candidates-FLY6HZEN.js → review-candidates-ZUXMX6BJ.js} +2 -2
  59. package/dist/{routing-SMIZ77CT.js → routing-EIGWDKH7.js} +2 -2
  60. package/dist/schedule-tool-PSZMP7C4.js +2 -0
  61. package/dist/scheduler-S24PXNB6.js +2 -0
  62. package/dist/{seat-F3S2HG5G.js → seat-QOY6FA4N.js} +2 -2
  63. package/dist/{select-5GC7CTPQ.js → select-JHKHO5BO.js} +2 -2
  64. package/dist/{set-enabled-53ZPZ5GR.js → set-enabled-DVJIUOKX.js} +2 -2
  65. package/dist/{tool-FRTZS4TT.js → tool-WKSLNQ6H.js} +2 -2
  66. package/dist/wiki-PTXUMSIR.js +2 -0
  67. package/package.json +1 -1
  68. package/dist/chunk-3IY2YWXP.js.map +0 -1
  69. package/dist/chunk-DTX4I5LQ.js +0 -15
  70. package/dist/chunk-FBDIJEKL.js.map +0 -1
  71. package/dist/chunk-LHLNARRW.js.map +0 -1
  72. package/dist/chunk-PHDS2HIN.js.map +0 -1
  73. package/dist/chunk-TFVUOC2C.js +0 -2
  74. package/dist/chunk-TFVUOC2C.js.map +0 -1
  75. package/dist/chunk-TLDQWTSW.js +0 -49
  76. package/dist/chunk-TLDQWTSW.js.map +0 -1
  77. package/dist/chunk-WLMQ5JMX.js +0 -6
  78. package/dist/chunk-WLMQ5JMX.js.map +0 -1
  79. package/dist/client-GDTX6LFH.js +0 -2
  80. package/dist/config-FZ4D4EAV.js +0 -2
  81. package/dist/config-mutator-ETG5V4C5.js +0 -2
  82. package/dist/doctor-JBIRBRLB.js +0 -2
  83. package/dist/graph-FJ53FDWK.js +0 -2
  84. package/dist/migrate-v2-ZOIMGITH.js +0 -4
  85. package/dist/program-UTELWTXC.js +0 -2
  86. package/dist/promote-B5AOKPQR.js +0 -2
  87. package/dist/query-QLJNQUUP.js +0 -2
  88. package/dist/registry-CTZFLXGB.js +0 -2
  89. package/dist/schedule-tool-NJFROYKY.js +0 -2
  90. package/dist/scheduler-5N4FC2FQ.js +0 -2
  91. package/dist/wiki-X76JUHXT.js +0 -2
  92. package/dist/wiki-X76JUHXT.js.map +0 -1
  93. /package/dist/{App-Z66ABY7H.js.map → App-PRD5JNO4.js.map} +0 -0
  94. /package/dist/{ChatApp-Z2LZ7IM3.js.map → ChatApp-BKS6OJ3L.js.map} +0 -0
  95. /package/dist/{agent-V223TG6H.js.map → agent-QF2JQ72Z.js.map} +0 -0
  96. /package/dist/{chunk-YBF2QVZ7.js.map → chunk-4XBYHKYG.js.map} +0 -0
  97. /package/dist/{chunk-OPUPVGYP.js.map → chunk-5Y7QRFSL.js.map} +0 -0
  98. /package/dist/{chunk-W7SEPJFH.js.map → chunk-6LTD7XL5.js.map} +0 -0
  99. /package/dist/{chunk-OVX6NRMD.js.map → chunk-BO4WLXXC.js.map} +0 -0
  100. /package/dist/{chunk-6QDOUOKD.js.map → chunk-D233M73Q.js.map} +0 -0
  101. /package/dist/{chunk-BBIWAJRX.js.map → chunk-DJMUP6TK.js.map} +0 -0
  102. /package/dist/{chunk-3RC4LO2G.js.map → chunk-HXW4NAWF.js.map} +0 -0
  103. /package/dist/{chunk-KLHG5ZQH.js.map → chunk-IK2XG65P.js.map} +0 -0
  104. /package/dist/{chunk-YK7GFEGO.js.map → chunk-JBVABB26.js.map} +0 -0
  105. /package/dist/{chunk-P2RYUODK.js.map → chunk-K6CHA6JJ.js.map} +0 -0
  106. /package/dist/{chunk-SN5N2FPG.js.map → chunk-RCKICCTT.js.map} +0 -0
  107. /package/dist/{chunk-FSYA4GNU.js.map → chunk-SELIVR6U.js.map} +0 -0
  108. /package/dist/{chunk-FUR3T3IN.js.map → chunk-SFWA67GQ.js.map} +0 -0
  109. /package/dist/{chunk-YL223TEU.js.map → chunk-WLX4766Y.js.map} +0 -0
  110. /package/dist/{chunk-TTVVXBCM.js.map → chunk-WPNRDEHW.js.map} +0 -0
  111. /package/dist/{client-GDTX6LFH.js.map → client-UPBQVXYM.js.map} +0 -0
  112. /package/dist/{config-FZ4D4EAV.js.map → config-UY5K6SCL.js.map} +0 -0
  113. /package/dist/{config-mutator-ETG5V4C5.js.map → config-mutator-7KOSRAQF.js.map} +0 -0
  114. /package/dist/{doctor-JBIRBRLB.js.map → doctor-GFPSIJGB.js.map} +0 -0
  115. /package/dist/{graph-FJ53FDWK.js.map → program-XMGZOYX3.js.map} +0 -0
  116. /package/dist/{program-UTELWTXC.js.map → promote-YLKNYYIU.js.map} +0 -0
  117. /package/dist/{promote-B5AOKPQR.js.map → query-CVRXEG43.js.map} +0 -0
  118. /package/dist/{query-QLJNQUUP.js.map → registry-HZL363TA.js.map} +0 -0
  119. /package/dist/{request-planner-NMOAOZIM.js.map → request-planner-76NCBYBW.js.map} +0 -0
  120. /package/dist/{review-candidates-FLY6HZEN.js.map → review-candidates-ZUXMX6BJ.js.map} +0 -0
  121. /package/dist/{registry-CTZFLXGB.js.map → routing-EIGWDKH7.js.map} +0 -0
  122. /package/dist/{routing-SMIZ77CT.js.map → schedule-tool-PSZMP7C4.js.map} +0 -0
  123. /package/dist/{schedule-tool-NJFROYKY.js.map → scheduler-S24PXNB6.js.map} +0 -0
  124. /package/dist/{scheduler-5N4FC2FQ.js.map → seat-QOY6FA4N.js.map} +0 -0
  125. /package/dist/{select-5GC7CTPQ.js.map → select-JHKHO5BO.js.map} +0 -0
  126. /package/dist/{set-enabled-53ZPZ5GR.js.map → set-enabled-DVJIUOKX.js.map} +0 -0
  127. /package/dist/{tool-FRTZS4TT.js.map → tool-WKSLNQ6H.js.map} +0 -0
  128. /package/dist/{seat-F3S2HG5G.js.map → wiki-PTXUMSIR.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
- case .speaking, .saying: return .speaking
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
- let clipWidth = bounds.width - 88 - (miniWidth > 0 ? miniWidth + 4 : 0)
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 ? .idle : .error("Didn't catch that"))
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(.idle) }
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(.idle)
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-OVX6NRMD.js";import"./chunk-DTX4I5LQ.js";import"./chunk-QIJLFPWE.js";import{useEffect as R,useReducer as V,useRef as G}from"react";import{Box as i,Text as o,useApp as W,useInput as J}from"ink";import{randomUUID as z}from"crypto";async function*q(e){let r=[];e.type&&r.push(`type=${encodeURIComponent(e.type)}`);let t=`${e.baseUrl.replace(/\/+$/,"")}/events${r.length?`?${r.join("&")}`:""}`,n={Accept:"text/event-stream"};e.token&&(n.Authorization=`Bearer ${e.token}`);let s=await fetch(t,{headers:n,signal:e.signal});if(!s.ok||!s.body)throw new Error(`SSE connect failed: HTTP ${s.status}`);let u=s.body.getReader(),p=new TextDecoder,y="";try{for(;;){let{done:f,value:l}=await u.read();if(f)return;y+=p.decode(l,{stream:!0});let d;for(;(d=y.indexOf(`
1
+ import{a as F,b as L,c as U,d as N,e as O,f as H,g as j}from"./chunk-U2CKL755.js";import{b as B,c as D,d as A,e as P,g as M}from"./chunk-BO4WLXXC.js";import"./chunk-AHOGFVHA.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-Z66ABY7H.js.map
8
+ //# sourceMappingURL=App-PRD5JNO4.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-OVX6NRMD.js";import"./chunk-DTX4I5LQ.js";import"./chunk-QIJLFPWE.js";import{useCallback as oe,useMemo as H,useRef as P,useState as k}from"react";import{Box as g,Text as t,useApp as he,useInput as xe,useStdout as Te}from"ink";import{randomUUID as se}from"crypto";var m={accent:"cyan",agent:"green",working:"magenta",error:"red",muted:"gray"},q="\u25C7 agentx",F="\u258C";import{jsx as c,jsxs as o}from"react/jsx-runtime";var ie="/help /agent <id> /agents /clear /who /exit",be=[{cmd:"/agent",desc:"switch the target agent (/agent <id>)"},{cmd:"/agents",desc:"list registered agents"},{cmd:"/clear",desc:"start a fresh session"},{cmd:"/who",desc:"show agent + chat id"},{cmd:"/help",desc:"list commands"},{cmd:"/exit",desc:"quit the chat"}];function Pe({conn:n,agentId:p,channel:f,chatId:a,agents:h}){let{exit:w}=he(),{stdout:O}=Te(),V=Math.max(40,Math.min((O?.columns||80)-4,100)),[z,U]=k([]),[$,S]=k(null),[T,le]=k(p),[A,ae]=k(h),[E,ue]=k(a),[y,x]=k(""),[J,de]=k(""),W=P([]),u=P(-1),M=P(null),R=P(null),v=$?.live??!1,j=H(()=>Z(y,A.map(s=>s.id),process.cwd()),[y,A]),N=H(()=>{let s=/^\/(\w*)$/.exec(y);if(!s)return null;let e=be.filter(r=>r.cmd.slice(1).startsWith(s[1].toLowerCase()));return e.length?e:null},[y]),b=s=>de(s),K=oe(s=>{let e=se().slice(0,8),r=Date.now(),i=new AbortController;R.current=i;let d={id:e,you:s,agentId:T,segs:[],live:!0,startedAt:r};S({...d});let L=()=>S({...d,segs:d.segs.map(C=>({...C}))});(async()=>{try{let C=await X(n,T,s,{channel:f,chatId:E,signal:i.signal,onText:l=>{let I=d.segs[d.segs.length-1];I&&I.type==="text"?I.content=(I.content??"")+l:d.segs.push({type:"text",content:l}),L()},onThinking:l=>{d.thinking=(d.thinking??"")+l,L()},onTool:l=>{l.status==="start"&&l.name?(d.segs.push({type:"tool",name:l.name,arg:l.arg}),L()):l.status==="result"&&l.error&&(d.segs.push({type:"tool",name:l.name??"tool",error:!0}),L())}});U(l=>[...l,{...d,live:!1,elapsedMs:Date.now()-r,outTokens:C.usage?.outputTokens,error:C.error}]),S(null)}catch(C){let l=i.signal.aborted||C?.name==="AbortError";U(I=>[...I,{...d,live:!1,elapsedMs:Date.now()-r,interrupted:l,error:l?void 0:C?.message||String(C)}]),S(null)}finally{R.current===i&&(R.current=null)}})()},[n,T,f,E]),me=oe(s=>{let e=s.trim();if(e){if(W.current.push(e),u.current=-1,e==="/exit"||e==="/quit"){w();return}if(e==="/help"){b(ie);return}if(e==="/who"){b(`@${T} \xB7 chatId=${E} \xB7 channel=${f}`);return}if(e==="/clear"){let r=`chat-cli:${se().slice(0,12)}`;ue(r),U([]),b(`fresh chatId=${r} (warm process dropped)`);return}if(e==="/agents"){Q(n).then(r=>{ae(r),b(r.map(i=>i.id===T?`[${i.id}]`:i.id).join(" "))}).catch(r=>b(`failed to list agents: ${r?.message||r}`));return}if(e.startsWith("/agent ")){let r=e.slice(7).replace(/^@/,"").trim();if(!r){b("usage: /agent <id>");return}if(!A.find(i=>i.id===r)){b(`unknown agent: @${r}`);return}le(r),b(`now talking to @${r}`);return}if(e.startsWith("/")){b(`unknown command \xB7 ${ie}`);return}K(e)}},[n,T,A,f,E,w,K]);xe((s,e)=>{if(e.ctrl&&s==="c"){w();return}if(e.escape){if(v&&R.current){R.current.abort();return}w();return}if(e.tab){if(N){x(N[0].cmd+" ");return}let i=_(y,M,A.map(d=>d.id),process.cwd());i!==null&&(x(i),u.current=-1);return}if(e.upArrow){let i=W.current;if(!i.length)return;u.current=u.current<0?i.length-1:Math.max(0,u.current-1),x(i[u.current]??"");return}if(e.downArrow){let i=W.current;if(u.current<0)return;if(u.current=u.current+1,u.current>=i.length){u.current=-1,x("");return}x(i[u.current]??"");return}if(e.backspace||e.delete){M.current=null,x(i=>i.slice(0,-1));return}if(e.ctrl||e.meta)return;let r=ee(s??"",e.return,y);switch(r.kind){case"paste":M.current=null,x(i=>i+r.text),u.current=-1;return;case"type":M.current=null,x(i=>i+r.text),u.current=-1;return;case"continue":x(r.buffer);return;case"submit":if(v)return;M.current=null,x(""),me(r.text);return;case"none":return}});let G=v?m.working:m.accent,ge=y.length?y.split(`
1
+ import{a as Y,b as Z,c as _,d as ee,e as te,f as ne,g as re}from"./chunk-U2CKL755.js";import{b as Q,g as X}from"./chunk-BO4WLXXC.js";import"./chunk-AHOGFVHA.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-Z2LZ7IM3.js.map
7
+ //# sourceMappingURL=ChatApp-BKS6OJ3L.js.map
@@ -1,2 +1,2 @@
1
- import{A as a,B as b,C as c}from"./chunk-3RC4LO2G.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-V223TG6H.js.map
1
+ import{A as a,B as b,C as c}from"./chunk-HXW4NAWF.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-QF2JQ72Z.js.map