agentix-cli 0.90.0 → 0.91.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/apps/mac-voice/Sources/AgentXVoice/Character.swift +152 -0
  2. package/apps/mac-voice/Sources/AgentXVoice/CharacterDraw.swift +218 -0
  3. package/apps/mac-voice/Sources/AgentXVoice/CharacterMath.swift +145 -0
  4. package/apps/mac-voice/Sources/AgentXVoice/CharacterSim.swift +241 -0
  5. package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +35 -3
  6. package/apps/mac-voice/Sources/AgentXVoice/SettingsLook.swift +19 -0
  7. package/apps/mac-voice/Sources/AgentXVoice/SettingsWindow.swift +4 -0
  8. package/apps/mac-voice/Sources/AgentXVoice/VoiceSettings.swift +7 -2
  9. package/apps/mac-voice/Sources/AgentXVoice/main.swift +11 -0
  10. package/dist/{App-LC34QQPN.js → App-PBPYFVBE.js} +2 -2
  11. package/dist/{ChatApp-FB72AK7M.js → ChatApp-QC7QAQEW.js} +2 -2
  12. package/dist/{agent-OZ4XFAD4.js → agent-BQEENICU.js} +2 -2
  13. package/dist/{board-dashboard-YBWSJ7J2.js → board-dashboard-ZLDVYPZ4.js} +4 -4
  14. package/dist/{chunk-XSVGD64O.js → chunk-24P6UR3L.js} +12 -12
  15. package/dist/chunk-24P6UR3L.js.map +1 -0
  16. package/dist/{chunk-RLXEF2X6.js → chunk-4SHWIGTM.js} +5 -5
  17. package/dist/{chunk-A5DFX47N.js → chunk-4TKGR3XF.js} +2 -2
  18. package/dist/{chunk-UWH7NTGV.js → chunk-AVA245WL.js} +2 -2
  19. package/dist/{chunk-ISIJAZGO.js → chunk-EBHHMJZX.js} +2 -2
  20. package/dist/{chunk-3SEFVZA7.js → chunk-EJPQPU5J.js} +2 -2
  21. package/dist/{chunk-QYFF2WJ2.js → chunk-ES2NYGS2.js} +2 -2
  22. package/dist/{chunk-N2OMSKIQ.js → chunk-HXM35HRM.js} +2 -2
  23. package/dist/{chunk-LCWE2GHC.js → chunk-HY6ILQ4T.js} +2 -2
  24. package/dist/{chunk-MU4S6LQA.js → chunk-L73YON3H.js} +2 -2
  25. package/dist/{chunk-64UK5VG4.js → chunk-NIQEKVWY.js} +2 -2
  26. package/dist/{chunk-LS6KXRXY.js → chunk-OKT5RCXP.js} +2 -2
  27. package/dist/{chunk-BEJCSZOQ.js → chunk-OS34M3FV.js} +2 -2
  28. package/dist/{chunk-JF5UGNMG.js → chunk-PJZ4ZHQS.js} +2 -2
  29. package/dist/{chunk-TA2BYYNZ.js → chunk-RS6BNRVF.js} +2 -2
  30. package/dist/{chunk-3GN255MT.js → chunk-S4JD5FEE.js} +2 -2
  31. package/dist/{chunk-MBVRADNG.js → chunk-SH5Y7N4W.js} +2 -2
  32. package/dist/{chunk-JLCYQZRT.js → chunk-SQOBKSP3.js} +2 -2
  33. package/dist/{chunk-TS7JDPOY.js → chunk-URVWZ5FM.js} +2 -2
  34. package/dist/{chunk-JVQXD35E.js → chunk-VCO46KQZ.js} +172 -172
  35. package/dist/{chunk-JVQXD35E.js.map → chunk-VCO46KQZ.js.map} +1 -1
  36. package/dist/{chunk-26UFDYRZ.js → chunk-ZVXKCXTP.js} +2 -2
  37. package/dist/cli.js +1 -1
  38. package/dist/{client-IPZRCNE7.js → client-WFEXHCNH.js} +2 -2
  39. package/dist/config-PPYPER75.js +2 -0
  40. package/dist/config-mutator-QQDBVKWM.js +2 -0
  41. package/dist/{doctor-BTHIQN7F.js → doctor-NRKGBTVD.js} +2 -2
  42. package/dist/index.d.ts +17 -2
  43. package/dist/index.js +1 -1
  44. package/dist/{program-PURSFZBM.js → program-TIKUIL2G.js} +165 -165
  45. package/dist/program-TIKUIL2G.js.map +1 -0
  46. package/dist/{promote-RDXNGZKX.js → promote-PY73YR6C.js} +2 -2
  47. package/dist/{query-OGMNU45Y.js → query-F3R5KXA7.js} +2 -2
  48. package/dist/{registry-LKIOKDIG.js → registry-YBMYCRQH.js} +2 -2
  49. package/dist/{request-planner-EP6QJAK5.js → request-planner-CVNEV6NJ.js} +2 -2
  50. package/dist/{review-candidates-BJPY54JI.js → review-candidates-5QTF5JRG.js} +2 -2
  51. package/dist/{routing-PZNFU3OV.js → routing-BJLETKHZ.js} +2 -2
  52. package/dist/schedule-tool-E2PRGDLW.js +2 -0
  53. package/dist/scheduler-53POJWDI.js +2 -0
  54. package/dist/{seat-4OONQEDH.js → seat-OORCKWER.js} +2 -2
  55. package/dist/{select-BR3NBXW7.js → select-OGMDS37Y.js} +2 -2
  56. package/dist/{set-enabled-Z3NKON2M.js → set-enabled-IKSHTUUF.js} +2 -2
  57. package/dist/{tool-SV55IZS3.js → tool-6FVBXUTV.js} +2 -2
  58. package/dist/{tool-DWMZ5OWI.js → tool-CKIO3NBX.js} +2 -2
  59. package/dist/wiki-4CPXUAPW.js +2 -0
  60. package/package.json +2 -2
  61. package/dist/chunk-XSVGD64O.js.map +0 -1
  62. package/dist/config-HZTKLB6A.js +0 -2
  63. package/dist/config-mutator-6S4X6WMU.js +0 -2
  64. package/dist/program-PURSFZBM.js.map +0 -1
  65. package/dist/schedule-tool-C3CE6GJG.js +0 -2
  66. package/dist/scheduler-PJMGEJMR.js +0 -2
  67. package/dist/wiki-EWEJOKP7.js +0 -2
  68. /package/dist/{App-LC34QQPN.js.map → App-PBPYFVBE.js.map} +0 -0
  69. /package/dist/{ChatApp-FB72AK7M.js.map → ChatApp-QC7QAQEW.js.map} +0 -0
  70. /package/dist/{agent-OZ4XFAD4.js.map → agent-BQEENICU.js.map} +0 -0
  71. /package/dist/{board-dashboard-YBWSJ7J2.js.map → board-dashboard-ZLDVYPZ4.js.map} +0 -0
  72. /package/dist/{chunk-RLXEF2X6.js.map → chunk-4SHWIGTM.js.map} +0 -0
  73. /package/dist/{chunk-A5DFX47N.js.map → chunk-4TKGR3XF.js.map} +0 -0
  74. /package/dist/{chunk-UWH7NTGV.js.map → chunk-AVA245WL.js.map} +0 -0
  75. /package/dist/{chunk-ISIJAZGO.js.map → chunk-EBHHMJZX.js.map} +0 -0
  76. /package/dist/{chunk-3SEFVZA7.js.map → chunk-EJPQPU5J.js.map} +0 -0
  77. /package/dist/{chunk-QYFF2WJ2.js.map → chunk-ES2NYGS2.js.map} +0 -0
  78. /package/dist/{chunk-N2OMSKIQ.js.map → chunk-HXM35HRM.js.map} +0 -0
  79. /package/dist/{chunk-LCWE2GHC.js.map → chunk-HY6ILQ4T.js.map} +0 -0
  80. /package/dist/{chunk-MU4S6LQA.js.map → chunk-L73YON3H.js.map} +0 -0
  81. /package/dist/{chunk-64UK5VG4.js.map → chunk-NIQEKVWY.js.map} +0 -0
  82. /package/dist/{chunk-LS6KXRXY.js.map → chunk-OKT5RCXP.js.map} +0 -0
  83. /package/dist/{chunk-BEJCSZOQ.js.map → chunk-OS34M3FV.js.map} +0 -0
  84. /package/dist/{chunk-JF5UGNMG.js.map → chunk-PJZ4ZHQS.js.map} +0 -0
  85. /package/dist/{chunk-TA2BYYNZ.js.map → chunk-RS6BNRVF.js.map} +0 -0
  86. /package/dist/{chunk-3GN255MT.js.map → chunk-S4JD5FEE.js.map} +0 -0
  87. /package/dist/{chunk-MBVRADNG.js.map → chunk-SH5Y7N4W.js.map} +0 -0
  88. /package/dist/{chunk-JLCYQZRT.js.map → chunk-SQOBKSP3.js.map} +0 -0
  89. /package/dist/{chunk-TS7JDPOY.js.map → chunk-URVWZ5FM.js.map} +0 -0
  90. /package/dist/{chunk-26UFDYRZ.js.map → chunk-ZVXKCXTP.js.map} +0 -0
  91. /package/dist/{client-IPZRCNE7.js.map → client-WFEXHCNH.js.map} +0 -0
  92. /package/dist/{config-HZTKLB6A.js.map → config-PPYPER75.js.map} +0 -0
  93. /package/dist/{config-mutator-6S4X6WMU.js.map → config-mutator-QQDBVKWM.js.map} +0 -0
  94. /package/dist/{doctor-BTHIQN7F.js.map → doctor-NRKGBTVD.js.map} +0 -0
  95. /package/dist/{promote-RDXNGZKX.js.map → promote-PY73YR6C.js.map} +0 -0
  96. /package/dist/{query-OGMNU45Y.js.map → query-F3R5KXA7.js.map} +0 -0
  97. /package/dist/{registry-LKIOKDIG.js.map → registry-YBMYCRQH.js.map} +0 -0
  98. /package/dist/{request-planner-EP6QJAK5.js.map → request-planner-CVNEV6NJ.js.map} +0 -0
  99. /package/dist/{review-candidates-BJPY54JI.js.map → review-candidates-5QTF5JRG.js.map} +0 -0
  100. /package/dist/{routing-PZNFU3OV.js.map → routing-BJLETKHZ.js.map} +0 -0
  101. /package/dist/{schedule-tool-C3CE6GJG.js.map → schedule-tool-E2PRGDLW.js.map} +0 -0
  102. /package/dist/{scheduler-PJMGEJMR.js.map → scheduler-53POJWDI.js.map} +0 -0
  103. /package/dist/{seat-4OONQEDH.js.map → seat-OORCKWER.js.map} +0 -0
  104. /package/dist/{select-BR3NBXW7.js.map → select-OGMDS37Y.js.map} +0 -0
  105. /package/dist/{set-enabled-Z3NKON2M.js.map → set-enabled-IKSHTUUF.js.map} +0 -0
  106. /package/dist/{tool-SV55IZS3.js.map → tool-6FVBXUTV.js.map} +0 -0
  107. /package/dist/{tool-DWMZ5OWI.js.map → tool-CKIO3NBX.js.map} +0 -0
  108. /package/dist/{wiki-EWEJOKP7.js.map → wiki-4CPXUAPW.js.map} +0 -0
@@ -0,0 +1,241 @@
1
+ import Foundation
2
+
3
+ /// The character from one moment to the next: which state it is in, where
4
+ /// it is along the edge, where it looks, and the marks it leaves. Stepped
5
+ /// once a frame; everything it returns is ready to draw. Foundation only,
6
+ /// so the tests can run it without a window.
7
+ ///
8
+ /// Along the edge it is measured in screen points; the drawing itself in
9
+ /// its own units (CharacterMath), `unit` points each.
10
+ struct CharacterSim {
11
+ typealias M = CharacterMath
12
+
13
+ struct Input {
14
+ var activity = M.Activity.idle
15
+ /// 0…1: the microphone while listening.
16
+ var level = 0.0
17
+ /// The pointer: along the edge, and its height above it. Nil when
18
+ /// it is on another screen.
19
+ var pointer: (x: Double, y: Double)?
20
+ /// Where it rests, and how far it may go.
21
+ var home = 0.0
22
+ var range: ClosedRange<Double> = 0...0
23
+ }
24
+
25
+ /// A trail dot or a star, where and how it is drawn this frame.
26
+ struct Mark: Equatable {
27
+ /// Along the edge and above it, in points.
28
+ var x, y: Double
29
+ /// Radius in drawing units, opacity, turn in degrees, and which
30
+ /// palette stop colours it.
31
+ var r, alpha, turn: Double
32
+ var shade: Int
33
+ }
34
+
35
+ struct Frame {
36
+ var pose = M.Pose()
37
+ var x = 0.0
38
+ /// -1 turned left, 0 facing you, 1 turned right.
39
+ var face = 0.0
40
+ var level = 0.0
41
+ /// 0…1: the rise and fall of its own voice while speaking.
42
+ var voice = 0.0
43
+ /// Seconds since it appeared, for the marks' own loops.
44
+ var t = 0.0
45
+ var dots: [Mark] = []
46
+ var stars: [Mark] = []
47
+ }
48
+
49
+ /// Points per drawing unit: a body 56 points across.
50
+ let unit: Double
51
+ /// Idle this long, it dozes.
52
+ static let dozeAfter = 120.0
53
+ /// How far it keeps from the pointer, and how long before it comes back.
54
+ static let clear = 150.0
55
+ static let awayFor = 4.0
56
+
57
+ private(set) var mood = M.Mood.idle
58
+ private var goal = M.Mood.idle
59
+ private var change = M.Transition(from: M.Pose(), to: M.Pose(), start: 0, duration: 0.5)
60
+ /// When the passing state it is in gives way to `goal`.
61
+ private var pending: Double?
62
+ private var began: Double?
63
+ private var last = 0.0
64
+ private var restSince = 0.0
65
+
66
+ private(set) var x = 0.0
67
+ private(set) var speed = 0.0
68
+ private var target = 0.0
69
+ private var awayUntil = 0.0
70
+ private var steppedAside = false
71
+ private var face = 0.0, lean = 0.0
72
+ private var glance = (x: 0.0, y: 0.0)
73
+
74
+ private var nextBlink = 0.0
75
+ private var blinks = 0
76
+ private struct Dot { var born, x, h, way: Double; var n: Int }
77
+ private struct Burst { var born, x, h: Double; var seed: Int }
78
+ private var dots: [Dot] = []
79
+ private var bursts: [Burst] = []
80
+ private var dotClock = 0.0
81
+ private var marks = 0
82
+
83
+ init(unit: Double = 0.56) { self.unit = unit }
84
+
85
+ /// The pointer is this close: it steps aside.
86
+ private var reach: Double { 50 * unit + 46 }
87
+ private var tall: Double { 114 * unit + 40 }
88
+ /// The middle of the body above the edge, in points.
89
+ private func height(_ pose: M.Pose) -> Double { (pose.lift + 50 * pose.sy) * unit }
90
+
91
+ mutating func step(to now: Double, _ input: Input) -> Frame {
92
+ if began == nil {
93
+ began = now; last = now; restSince = now; nextBlink = now + 2
94
+ x = input.home; target = input.home
95
+ }
96
+ let dt = min(max(now - last, 0), 0.1)
97
+ last = now
98
+
99
+ // The pointer comes close: out of its way. Left alone: back home,
100
+ // unless the pointer is resting there.
101
+ let near = input.pointer.map { abs($0.x - x) < reach && $0.y < tall } ?? false
102
+ if near {
103
+ target = M.aside(x: x, pointer: input.pointer!.x, clear: Self.clear, range: input.range)
104
+ awayUntil = now + Self.awayFor
105
+ restSince = now
106
+ steppedAside = true
107
+ } else if now >= awayUntil {
108
+ let taken = input.pointer.map { abs($0.x - input.home) < Self.clear && $0.y < tall } ?? false
109
+ if !taken {
110
+ if target != input.home { steppedAside = false }
111
+ target = min(max(input.home, input.range.lowerBound), input.range.upperBound)
112
+ }
113
+ }
114
+
115
+ // Which state. A change starts from the pose it has now, so a
116
+ // change in the middle of another is still one smooth move.
117
+ if input.activity != .idle { restSince = now }
118
+ var want = M.mood(for: input.activity)
119
+ if want == .idle && now - restSince >= Self.dozeAfter { want = .dozing }
120
+ if want != goal {
121
+ goal = want
122
+ if let via = M.passing(from: mood, to: want), via != mood {
123
+ go(via, now); pending = now + M.hold(via)
124
+ } else {
125
+ go(want, now); pending = nil
126
+ }
127
+ }
128
+ if let at = pending, now >= at { pending = nil; go(goal, now) }
129
+ var pose = change.pose(at: now)
130
+
131
+ // Along the edge: a spring a little short of critical, so it
132
+ // arrives with a small overshoot.
133
+ let wasMoving = abs(speed) > 40
134
+ speed += (26 * (target - x) - 8.4 * speed) * dt
135
+ speed = min(max(speed, -480), 480)
136
+ x += speed * dt
137
+ let moving = abs(speed) > 40
138
+ let way: Double = speed >= 0 ? 1 : -1
139
+ func toward(_ value: inout Double, _ to: Double, _ rate: Double) { value += (to - value) * (1 - exp(-dt * rate)) }
140
+ toward(&face, min(max(speed / 200, -1), 1), 10)
141
+ toward(&lean, min(max(speed / 480, -1), 1) * 13, 8)
142
+
143
+ // It leaves dots behind while it moves, and stars when it has
144
+ // stepped aside or understood.
145
+ if abs(speed) > 60 {
146
+ dotClock += dt
147
+ while dotClock >= 0.05 {
148
+ dotClock -= 0.05
149
+ marks += 1
150
+ dots.append(Dot(born: now, x: x, h: height(pose), way: way, n: marks))
151
+ }
152
+ }
153
+ if wasMoving && !moving && steppedAside {
154
+ steppedAside = false
155
+ burst(now, pose)
156
+ }
157
+ dots.removeAll { now - $0.born >= Self.dotLife }
158
+ bursts.removeAll { now - $0.born >= Self.starLife }
159
+
160
+ // The eyes follow the pointer while it has nothing else to look at.
161
+ var look = (x: 0.0, y: 0.0)
162
+ if let p = input.pointer, [.idle, .notices, .calling, .asking].contains(mood) {
163
+ let dx = p.x - x, dy = height(pose) - p.y
164
+ let far = max(hypot(dx, dy), 1)
165
+ let pull = min(far / 60, 1) * 4.5
166
+ look = (dx / far * pull, dy / far * pull)
167
+ }
168
+ toward(&glance.x, look.x + face * 2, 9)
169
+ toward(&glance.y, look.y, 9)
170
+
171
+ // What keeps it alive: breath, bob, blinks, and the voice.
172
+ let t = now - (began ?? now)
173
+ func wave(_ period: Double) -> Double { sin(2 * .pi * t / period) }
174
+ let z = pose.sleep
175
+ let breath = (1 - z) * 0.012 * wave(2.8) + z * 0.04 * wave(3.4)
176
+ let voice = pose.speak * OrbMath.speakingEnvelope(at: t)
177
+ pose.sx *= (1 - breath / 2) * (1 + pose.hear * 0.05 * input.level)
178
+ pose.sy *= (1 + breath) * (1 + pose.hear * 0.05 * input.level + 0.035 * voice)
179
+ pose.lift += (1 - z) * 4 * wave(1.4) + z * 1.5 * wave(3.4) + 5 * voice
180
+ + pose.ring * 7 * abs(wave(1.25)) + 6 * min(abs(speed) / 300, 1)
181
+ pose.tilt += lean
182
+ pose.gx += glance.x
183
+ pose.gy += glance.y
184
+ if now >= nextBlink + Self.blink {
185
+ blinks += 1
186
+ nextBlink = now + 2.2 + 4.5 * M.rnd(blinks) // uneven, never the same loop twice
187
+ }
188
+ if now >= nextBlink { pose.open *= 1 - sin(.pi * (now - nextBlink) / Self.blink) }
189
+
190
+ return Frame(pose: pose, x: x, face: face, level: input.level, voice: voice, t: t,
191
+ dots: dots.compactMap { dot(at: now, $0) }, stars: bursts.flatMap { stars(at: now, $0) })
192
+ }
193
+
194
+ /// The state alone, where it rests: for Reduce Motion, which shows a
195
+ /// still picture that changes between states and never a loop.
196
+ static func still(_ activity: M.Activity, home: Double) -> Frame {
197
+ // Its marks at full strength, so each state reads without motion.
198
+ Frame(pose: M.pose(M.mood(for: activity)), x: home, level: 1, voice: 1)
199
+ }
200
+
201
+ private mutating func go(_ to: M.Mood, _ now: Double) {
202
+ change = M.Transition(from: change.pose(at: now), to: M.pose(to), start: now, duration: M.duration(into: to))
203
+ mood = to
204
+ if to == .understood { burst(now, change.from) }
205
+ }
206
+
207
+ private mutating func burst(_ now: Double, _ pose: M.Pose) {
208
+ marks += 1
209
+ bursts.append(Burst(born: now, x: x, h: height(pose), seed: marks))
210
+ }
211
+
212
+ private static let blink = 0.2
213
+ private static let dotLife = 0.8
214
+ private static let starLife = 0.9
215
+ /// A dot this far from the body has faded out: its window ends soon after.
216
+ static let dotReach = 200.0
217
+
218
+ /// A dot left behind: born at the body, it drifts up, shrinks and fades.
219
+ private func dot(at now: Double, _ d: Dot) -> Mark? {
220
+ let a = (now - d.born) / Self.dotLife
221
+ guard a >= 0, a < 1 else { return nil }
222
+ let r = (3.4 + 4.4 * M.rnd(d.n)) * pow(1 - a, 0.8) * min(a / 0.12, 1)
223
+ return Mark(x: d.x - d.way * (30 + 16 * a) * unit,
224
+ y: d.h - (10 + (M.rnd(d.n, 1) - 0.5) * 26 - 16 * a * M.rnd(d.n, 2)) * unit,
225
+ r: r, alpha: 0.8 * (1 - a) * min(max((Self.dotReach - abs(d.x - x)) / 60, 0), 1), turn: 0, shade: 2 + d.n % 2)
226
+ }
227
+
228
+ /// A small burst of stars: they fly out over the top, turn, and fade.
229
+ private func stars(at now: Double, _ b: Burst) -> [Mark] {
230
+ let a = (now - b.born) / Self.starLife
231
+ guard a >= 0, a < 1 else { return [] }
232
+ let n = 6
233
+ return (0..<n).map { i in
234
+ let angle = -Double.pi * (0.08 + 0.84 * (Double(i) + 0.5 * M.rnd(i, b.seed)) / (Double(n) - 0.5))
235
+ let far = 50 + (24 + 26 * M.rnd(i, b.seed + 1)) * (1 - pow(1 - a, 3))
236
+ return Mark(x: b.x + cos(angle) * far * unit, y: b.h - sin(angle) * far * unit,
237
+ r: (6 + 6 * M.rnd(i, b.seed + 2)) * pow(sin(.pi * a), 0.6),
238
+ alpha: min(2.2 * (1 - a), 1), turn: 120 * a + 40 * Double(i), shade: 1 + i % 3)
239
+ }
240
+ }
241
+ }
@@ -105,6 +105,13 @@ final class Panel: NSPanel {
105
105
  /// Stay on screen when idle. Off: the pill shows only while active.
106
106
  var alwaysVisible = false
107
107
 
108
+ /// Told what the orb is shown, so the character can show the same.
109
+ var onLook: ((State, NSColor, [NSColor]?) -> Void)?
110
+
111
+ /// Off while the character stands in for the orb (Character.swift):
112
+ /// the words start where the orb was.
113
+ private(set) var showsOrb = true
114
+
108
115
  /// Told of every state rendered, so the menu-bar icon can follow.
109
116
  var onRender: ((State) -> Void)?
110
117
 
@@ -175,6 +182,18 @@ final class Panel: NSPanel {
175
182
  }
176
183
  }
177
184
 
185
+ /// What the character shows. A call between turns waits for you.
186
+ var activity: CharacterMath.Activity {
187
+ switch self {
188
+ case .idle, .error: return .idle
189
+ case .listening: return .listening
190
+ case .thinking, .working: return .thinking
191
+ case .speaking, .saying: return .speaking
192
+ case .ringing: return .ringing
193
+ case .onCall: return .waiting
194
+ }
195
+ }
196
+
178
197
  /// Idle is an invitation and belongs in meta type; everything else
179
198
  /// is a running commentary and belongs in body type.
180
199
  var isMeta: Bool { if case .idle = self { return true }; return false }
@@ -295,8 +314,10 @@ final class Panel: NSPanel {
295
314
  let callWidth = CallBar.width(callBar.mode)
296
315
  let callX = bounds.width - 28 - (miniWidth > 0 ? miniWidth + 4 : 0) - callWidth
297
316
  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)
299
- if let clip, clip.frame.width != clipWidth {
317
+ let head: CGFloat = showsOrb ? 54 : 18
318
+ let clipWidth = bounds.width - 34 - head - (miniWidth > 0 ? miniWidth + 4 : 0) - (callWidth > 0 ? callWidth + 4 : 0)
319
+ if let clip, clip.frame.width != clipWidth || clip.frame.minX != head {
320
+ clip.frame.origin.x = head
300
321
  clip.frame.size.width = clipWidth
301
322
  // Text that scrolled may fit now, and the other way round.
302
323
  marqueeText = ""
@@ -363,9 +384,19 @@ final class Panel: NSPanel {
363
384
  render(current)
364
385
  }
365
386
 
387
+ /// The orb at the pill's head, or none while the character shows.
388
+ @MainActor
389
+ func setShowsOrb(_ on: Bool) {
390
+ guard showsOrb != on else { return }
391
+ showsOrb = on
392
+ orb.isHidden = !on
393
+ orb.setOnScreen(on && isVisible)
394
+ if let content = contentView { layoutContent(content.bounds) }
395
+ }
396
+
366
397
  @MainActor
367
398
  func show() {
368
- orb.setOnScreen(true)
399
+ orb.setOnScreen(showsOrb)
369
400
  miniOrbs.setOnScreen(!miniOrbs.isHidden)
370
401
  if !isVisible { orderFrontRegardless() }
371
402
  }
@@ -430,6 +461,7 @@ final class Panel: NSPanel {
430
461
  default: colors = agentPalette()
431
462
  }
432
463
  orb.show(state.orbPhase, tint: tint, colors: colors)
464
+ onLook?(state, tint, colors)
433
465
 
434
466
  if state.isMeta {
435
467
  stopMarquee()
@@ -52,6 +52,25 @@ struct PalettePicker: View {
52
52
  }
53
53
  }
54
54
 
55
+ /// "Shown as": the orb, or the character that stands in for it (#458).
56
+ struct LookSection: View {
57
+ @Binding var look: String
58
+
59
+ var body: some View {
60
+ Section {
61
+ Picker("Shown as", selection: $look) {
62
+ Text("Orb").tag("orb")
63
+ Text("Character").tag("character")
64
+ }
65
+ } header: {
66
+ Text("Assistant")
67
+ } footer: {
68
+ Text("The character is the orb grown into a small creature in the agent's colours. It hovers above the bottom edge of the screen, shows what the assistant is doing, and moves out of the pointer's way. It never takes a click.")
69
+ .font(.caption).foregroundStyle(.secondary)
70
+ }
71
+ }
72
+ }
73
+
55
74
  /// "Answer in the pill": how long it stays open once spoken, and how tall
56
75
  /// it grows before it scrolls.
57
76
  struct AnswerCardSection: View {
@@ -373,6 +373,10 @@ private struct GeneralTab: View {
373
373
  Text("Shortcuts change as soon as you save. If one does nothing, another app already uses it.")
374
374
  .font(.caption).foregroundStyle(.secondary)
375
375
  }
376
+ if draft.general.look != nil {
377
+ LookSection(look: Binding(get: { model.draft?.general.look ?? "orb" },
378
+ set: { model.draft?.general.look = $0 }))
379
+ }
376
380
  if draft.general.card != nil {
377
381
  AnswerCardSection(card: Binding(get: { model.draft?.general.card ?? .standard },
378
382
  set: { model.draft?.general.card = $0 }))
@@ -26,12 +26,15 @@ struct VoiceSettings: Codable, Equatable {
26
26
  var hotkeys: Hotkeys
27
27
  /// Nil from a daemon older than the card settings.
28
28
  var card: Card?
29
+ /// What shows the assistant's state: "orb" or "character". Nil
30
+ /// from a daemon older than the character.
31
+ var look: String?
29
32
 
30
33
  init(provider: String, fallback: String, stt: String, localStt: String = "mlx-whisper",
31
- endOfTurn: String = "vad", hotkeys: Hotkeys, card: Card? = nil) {
34
+ endOfTurn: String = "vad", hotkeys: Hotkeys, card: Card? = nil, look: String? = nil) {
32
35
  self.provider = provider; self.fallback = fallback; self.stt = stt
33
36
  self.localStt = localStt; self.endOfTurn = endOfTurn; self.hotkeys = hotkeys
34
- self.card = card
37
+ self.card = card; self.look = look
35
38
  }
36
39
 
37
40
  /// A daemon from before these fields leaves them out: the defaults.
@@ -44,6 +47,7 @@ struct VoiceSettings: Codable, Equatable {
44
47
  endOfTurn = try c.decodeIfPresent(String.self, forKey: .endOfTurn) ?? "vad"
45
48
  hotkeys = try c.decode(Hotkeys.self, forKey: .hotkeys)
46
49
  card = try c.decodeIfPresent(Card.self, forKey: .card)
50
+ look = try c.decodeIfPresent(String.self, forKey: .look)
47
51
  }
48
52
  }
49
53
  struct Voice: Codable, Equatable {
@@ -102,6 +106,7 @@ struct VoiceSettings: Codable, Equatable {
102
106
  if self.general.stt != old.general.stt { general["stt"] = self.general.stt }
103
107
  if self.general.localStt != old.general.localStt { general["localStt"] = self.general.localStt }
104
108
  if self.general.endOfTurn != old.general.endOfTurn { general["endOfTurn"] = self.general.endOfTurn }
109
+ if let look = self.general.look, look != old.general.look { general["look"] = look }
105
110
  var keys: [String: Any] = [:]
106
111
  if self.general.hotkeys.talk != old.general.hotkeys.talk { keys["talk"] = self.general.hotkeys.talk }
107
112
  if self.general.hotkeys.stop != old.general.hotkeys.stop { keys["stop"] = self.general.hotkeys.stop }
@@ -18,6 +18,8 @@ import Carbon.HIToolbox
18
18
  final class App: NSObject, NSApplicationDelegate {
19
19
  private let panel = Panel()
20
20
  private let statusMenu = StatusMenu()
21
+ /// Stands in for the pill's orb when voice.look is "character".
22
+ private let character = CharacterHost()
21
23
  /// The agent whose by-name answer is on screen now, while no turn runs.
22
24
  private var asideSpeaker: String?
23
25
  private let recorder = Recorder()
@@ -199,6 +201,9 @@ final class App: NSObject, NSApplicationDelegate {
199
201
  let card = saved.general.card ?? .standard
200
202
  panel.cardTimeout = card.timeout
201
203
  panel.cardMaxHeight = card.maxHeight
204
+ let asCharacter = saved.general.look == "character"
205
+ panel.setShowsOrb(!asCharacter)
206
+ character.setShown(asCharacter)
202
207
  if settingsWindow.model.recording == nil { registerHotkeys() }
203
208
  // Colours may have changed.
204
209
  statusMenu.refresh()
@@ -307,6 +312,11 @@ final class App: NSObject, NSApplicationDelegate {
307
312
  panel.orb.setAnimated(Config.animatedOrb)
308
313
  panel.miniOrbs.setAnimated(Config.animatedOrb)
309
314
  panel.orb.levelSource = { [weak self] in self?.recorder.level ?? 0 }
315
+ character.setAnimated(Config.animatedOrb)
316
+ character.levelSource = { [weak self] in self?.recorder.level ?? 0 }
317
+ panel.onLook = { [weak self] state, tint, colors in
318
+ self?.character.show(state.activity, tint: tint, colors: colors)
319
+ }
310
320
  panel.onDismiss = { [weak self] in self?.dismissPill() }
311
321
  panel.agentPalette = { [weak self] in
312
322
  guard let self else { return nil }
@@ -319,6 +329,7 @@ final class App: NSObject, NSApplicationDelegate {
319
329
  statusMenu.onAnimatedOrbChanged = { [weak self] on in
320
330
  self?.panel.orb.setAnimated(on)
321
331
  self?.panel.miniOrbs.setAnimated(on)
332
+ self?.character.setAnimated(on)
322
333
  }
323
334
  panel.render(.idle)
324
335
  statusMenu.onPillChanged = { [weak self] on in
@@ -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-LS6KXRXY.js";import"./chunk-XSVGD64O.js";import"./chunk-RJ7DASOU.js";import"./chunk-BRK6M6LA.js";import"./chunk-2QNT5JDS.js";import"./chunk-QIJLFPWE.js";import{useEffect as R,useReducer as V,useRef as G}from"react";import{Box as i,Text as o,useApp as W,useInput as J}from"ink";import{randomUUID as z}from"crypto";async function*q(e){let r=[];e.type&&r.push(`type=${encodeURIComponent(e.type)}`);let t=`${e.baseUrl.replace(/\/+$/,"")}/events${r.length?`?${r.join("&")}`:""}`,n={Accept:"text/event-stream"};e.token&&(n.Authorization=`Bearer ${e.token}`);let s=await fetch(t,{headers:n,signal:e.signal});if(!s.ok||!s.body)throw new Error(`SSE connect failed: HTTP ${s.status}`);let u=s.body.getReader(),p=new TextDecoder,y="";try{for(;;){let{done:f,value:l}=await u.read();if(f)return;y+=p.decode(l,{stream:!0});let d;for(;(d=y.indexOf(`
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-OKT5RCXP.js";import"./chunk-24P6UR3L.js";import"./chunk-RJ7DASOU.js";import"./chunk-BRK6M6LA.js";import"./chunk-2QNT5JDS.js";import"./chunk-QIJLFPWE.js";import{useEffect as R,useReducer as V,useRef as G}from"react";import{Box as i,Text as o,useApp as W,useInput as J}from"ink";import{randomUUID as z}from"crypto";async function*q(e){let r=[];e.type&&r.push(`type=${encodeURIComponent(e.type)}`);let t=`${e.baseUrl.replace(/\/+$/,"")}/events${r.length?`?${r.join("&")}`:""}`,n={Accept:"text/event-stream"};e.token&&(n.Authorization=`Bearer ${e.token}`);let s=await fetch(t,{headers:n,signal:e.signal});if(!s.ok||!s.body)throw new Error(`SSE connect failed: HTTP ${s.status}`);let u=s.body.getReader(),p=new TextDecoder,y="";try{for(;;){let{done:f,value:l}=await u.read();if(f)return;y+=p.decode(l,{stream:!0});let d;for(;(d=y.indexOf(`
2
2
 
3
3
  `))>=0;){let h=y.slice(0,d);y=y.slice(d+2);let m=X(h);m&&(yield m)}}}finally{try{u.releaseLock()}catch{}}}function X(e){let r="message",t="";for(let s of e.split(`
4
4
  `))s.startsWith("event: ")?r=s.slice(7):s.startsWith("data: ")&&(t=s.slice(6));if(!t)return null;let n;try{n=JSON.parse(t)}catch{n=t}return{event:r,data:n}}import{jsx as a,jsxs as c}from"react/jsx-runtime";function $(e,r){return r<=0||e<0?0:e>=r?r-1:e}function E(){return`tui:${z().slice(0,12)}`}function k(e,r){let t=[...e,r];return t.length>50&&t.splice(0,t.length-50),t}function Y(e,r){switch(r.type){case"agents":return{...e,agents:r.data,cursor:{...e.cursor,agents:$(e.cursor.agents,r.data.length)}};case"processes":return{...e,processes:r.data,cursor:{...e.cursor,processes:$(e.cursor.processes,r.data.length)}};case"crons":return{...e,crons:r.data};case"event":{let t=[r.row,...e.events];t.length>200&&(t.length=200);let n=e.channels;return r.channelTally&&(n=new Map(e.channels),n.set(r.channelTally.channel,r.channelTally)),{...e,events:t,channels:n}}case"connError":return{...e,connError:r.error};case"tick":return{...e,lastTick:Date.now()};case"focus":return{...e,focus:r.pane};case"cursor":{let t=r.pane==="agents"?e.agents.length:r.pane==="processes"?e.processes.length:e.events.length;return{...e,cursor:{...e.cursor,[r.pane]:$(e.cursor[r.pane]+r.delta,t)}}}case"bottomRight":return{...e,bottomRight:r.pane};case"chatStart":return{...e,chat:{active:!0,agentId:r.agentId,chatId:E(),history:[],text:"",status:"idle",error:null}};case"chatExit":return{...e,chat:{...e.chat,active:!1}};case"chatClear":return{...e,chat:{...e.chat,chatId:E(),history:[],text:"",status:"idle",error:null}};case"chatText":return{...e,chat:{...e.chat,text:r.text}};case"chatSubmitStart":return{...e,chat:{...e.chat,history:k(e.chat.history,r.you),text:"",status:"sending",error:null}};case"chatSubmitDone":return{...e,chat:{...e.chat,history:k(e.chat.history,r.reply),status:"idle",error:null}};case"chatSubmitError":return{...e,chat:{...e.chat,history:k(e.chat.history,{role:"error",text:r.error,at:Date.now()}),status:"idle",error:r.error}};case"chatStreamStart":{let t=k(e.chat.history,r.you),n=k(t,{role:"agent",text:"",at:Date.now(),streaming:!0,tools:[]});return{...e,chat:{...e.chat,history:n,text:"",status:"sending",error:null}}}case"chatStreamDelta":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]={...n,text:n.text+r.text}),{...e,chat:{...e.chat,history:t}}}case"chatStreamThinking":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]={...n,thinking:(n.thinking??"")+r.text}),{...e,chat:{...e.chat,history:t}}}case"chatStreamTool":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]={...n,tools:[...n.tools??[],{name:r.name,arg:r.arg,error:r.error}]}),{...e,chat:{...e.chat,history:t}}}case"chatStreamEnd":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]=r.error?{...n,streaming:!1,role:"error",text:r.error}:{...n,streaming:!1,elapsedMs:r.elapsedMs,outTokens:r.outTokens}),{...e,chat:{...e.chat,history:t,status:"idle",error:r.error??null}}}case"toast":return{...e,toast:{text:r.text,color:r.color,at:Date.now()}};case"clearToast":return{...e,toast:null}}}var _={agents:[],processes:[],crons:[],events:[],channels:new Map,connError:null,lastTick:0,focus:"agents",cursor:{agents:0,processes:0,events:0},bottomRight:"crons",chat:{active:!1,agentId:null,chatId:E(),history:[],text:"",status:"idle",error:null},toast:null};function be({conn:e,pollMs:r=3e3}){let[t,n]=V(Y,_),{exit:s}=W(),u=G(null),p=G(null),y=t.agents.map(l=>l.id),f=t.chat.active?L(t.chat.text,y,process.cwd()):null;return R(()=>{if(!t.toast)return;let l=setTimeout(()=>n({type:"clearToast"}),3500);return()=>clearTimeout(l)},[t.toast]),J((l,d)=>{if(t.chat.active){if(d.escape){n({type:"chatExit"});return}if(d.ctrl&&l==="l"){n({type:"chatClear"});return}if(d.tab){let m=U(t.chat.text,p,y,process.cwd());m!==null&&n({type:"chatText",text:m});return}if(d.backspace||d.delete){p.current=null,n({type:"chatText",text:t.chat.text.slice(0,-1)});return}if(d.ctrl||d.meta)return;let h=N(l??"",d.return,t.chat.text);if(h.kind==="paste"||h.kind==="type"){p.current=null,n({type:"chatText",text:t.chat.text+h.text});return}if(h.kind==="continue"){n({type:"chatText",text:h.buffer});return}if(h.kind==="submit"){let m=h.text.trim(),g=t.chat.agentId;if(!m||!g||t.chat.status==="sending"){n({type:"chatText",text:""});return}p.current=null;let b=Date.now(),w=t.chat.chatId;n({type:"chatStreamStart",you:{role:"you",text:m,at:b}}),(async()=>{try{let C=await M(e,g,m,{channel:"tui",chatId:w,onText:x=>n({type:"chatStreamDelta",text:x}),onThinking:x=>n({type:"chatStreamThinking",text:x}),onTool:x=>{x.status==="start"&&x.name?n({type:"chatStreamTool",name:x.name,arg:x.arg}):x.status==="result"&&x.error&&n({type:"chatStreamTool",name:x.name??"tool",error:!0})}});n({type:"chatStreamEnd",elapsedMs:Date.now()-b,outTokens:C.usage?.outputTokens,error:C.error})}catch(C){n({type:"chatStreamEnd",elapsedMs:Date.now()-b,error:C?.message||String(C)})}})();return}return}if(l==="q"||d.ctrl&&l==="c"){u.current?.abort(),s();return}if(d.tab){let h=["agents","processes","events"],m=h.indexOf(t.focus);n({type:"focus",pane:h[(m+1)%h.length]});return}if(l==="a"){n({type:"focus",pane:"agents"});return}if(l==="p"){n({type:"focus",pane:"processes"});return}if(l==="e"){n({type:"focus",pane:"events"});return}if(l==="c"){n({type:"bottomRight",pane:t.bottomRight==="crons"?"channels":"crons"});return}if(d.upArrow){n({type:"cursor",pane:t.focus,delta:-1});return}if(d.downArrow){n({type:"cursor",pane:t.focus,delta:1});return}if(d.return&&t.focus==="agents"){let h=t.agents[t.cursor.agents];if(!h){n({type:"toast",text:"no agent selected",color:"yellow"});return}n({type:"chatStart",agentId:h.id});return}if(l==="k"&&t.focus==="processes"){let h=t.processes[t.cursor.processes];if(!h){n({type:"toast",text:"no process selected",color:"yellow"});return}(async()=>{try{await P(e,h.key,"tui"),n({type:"toast",text:`killed ${h.key.agentId}/${h.key.channel}/${h.key.chatId}`,color:"green"})}catch(m){n({type:"toast",text:`kill failed: ${m?.message||m}`,color:"red"})}})();return}}),R(()=>{let l=new AbortController;u.current=l;let d=!1,h=async()=>{try{let[g,b,w]=await Promise.all([B(e,l.signal).catch(()=>[]),D(e,l.signal).catch(()=>[]),A(e,l.signal).catch(()=>[])]);if(d)return;n({type:"agents",data:g}),n({type:"processes",data:b}),n({type:"crons",data:w}),n({type:"tick"}),n({type:"connError",error:null})}catch(g){d||n({type:"connError",error:g?.message||String(g)})}};h();let m=setInterval(h,r);return()=>{d=!0,clearInterval(m),l.abort()}},[e.baseUrl,e.token,r]),R(()=>{let l=!1,d=new AbortController,h=new Map;return(async()=>{let m=500;for(;!l;){try{for await(let g of q({baseUrl:e.baseUrl,token:e.token,signal:d.signal})){let b=ae(g),w;if(g.event==="channel"&&typeof g.data?.channel=="string"){let S=h.get(g.data.channel)??{channel:g.data.channel,inbound:0,outbound:0,lastAt:0,lastPreview:""},C=S.inbound+(g.data.direction==="in"?1:0),x=S.outbound+(g.data.direction==="out"?1:0);w={channel:g.data.channel,inbound:C,outbound:x,lastAt:b.at,lastPreview:(g.data.textPreview||"").slice(0,60)},h.set(g.data.channel,w)}n({type:"event",row:b,channelTally:w}),m=500}}catch(g){if(l)return;n({type:"connError",error:`events: ${g?.message||g}`})}await new Promise(g=>setTimeout(g,m)),m=Math.min(m*2,1e4)}})(),()=>{l=!0,d.abort()}},[e.baseUrl,e.token]),c(i,{flexDirection:"column",width:"100%",children:[a(K,{conn:e,lastTick:t.lastTick,connError:t.connError,chat:t.chat}),c(i,{flexDirection:"row",flexGrow:1,children:[c(i,{flexDirection:"column",width:"50%",children:[a(v,{title:"AGENTS",focused:!t.chat.active&&t.focus==="agents",children:a(Z,{rows:t.agents,cursor:t.cursor.agents,focused:!t.chat.active&&t.focus==="agents",talkingTo:t.chat.active?t.chat.agentId:null})}),a(v,{title:"PROCESSES",focused:!t.chat.active&&t.focus==="processes",children:a(ee,{rows:t.processes,cursor:t.cursor.processes,focused:!t.chat.active&&t.focus==="processes"})})]}),t.chat.active?a(oe,{chat:t.chat,suggest:f,agents:t.agents}):c(i,{flexDirection:"column",width:"50%",children:[a(v,{title:"LIVE EVENTS",focused:t.focus==="events",children:a(ne,{rows:t.events,cursor:t.cursor.events,focused:t.focus==="events"})}),a(v,{title:t.bottomRight==="crons"?"CRONS / SCHEDULES":"CHANNELS",focused:!1,children:t.bottomRight==="crons"?a(te,{rows:t.crons}):a(re,{rows:Array.from(t.channels.values())})})]})]}),a(Q,{focus:t.focus,bottomRight:t.bottomRight,chat:t.chat,toast:t.toast})]})}function K({conn:e,lastTick:r,connError:t,chat:n}){let s=t?c(o,{color:"red",children:["\u25CF ",t]}):r===0?a(o,{color:"yellow",children:"\u25CF connecting\u2026"}):a(o,{color:"green",children:"\u25CF live"}),u=n.active?c(o,{color:"green",bold:!0,children:["CHAT @",n.agentId]}):a(o,{bold:!0,children:"agentx tui"});return c(i,{paddingX:1,justifyContent:"space-between",children:[a(i,{children:u}),a(o,{dimColor:!0,children:e.baseUrl}),a(i,{children:s})]})}function Q({focus:e,bottomRight:r,chat:t,toast:n}){let s=t.active?["[Enter] send","[Esc] exit chat","[Ctrl-L] new session"]:[`[Tab/a/p/e] focus (now: ${e})`,e==="agents"?"[Enter] chat":null,e==="processes"?"[k] kill":null,`[c] ${r==="crons"?"\u2192channels":"\u2192crons"}`,"[\u2191/\u2193] move","[q] quit"].filter(Boolean);return c(i,{paddingX:1,borderStyle:"single",borderTop:!0,borderBottom:!1,borderLeft:!1,borderRight:!1,justifyContent:"space-between",children:[a(o,{dimColor:!0,children:s.join(" \xB7 ")}),n?a(o,{color:n.color,children:n.text}):a(o,{children:" "})]})}function v({title:e,focused:r,children:t}){return c(i,{flexDirection:"column",width:"100%",borderStyle:r?"double":"single",borderColor:r?"cyan":void 0,paddingX:1,children:[c(o,{bold:!0,color:r?"cyan":"white",children:[e,r?" \u25CF":""]}),a(i,{flexDirection:"column",marginTop:1,children:t})]})}function I(e,r){return e&&r?{inverse:!0}:{}}function Z({rows:e,cursor:r,focused:t,talkingTo:n}){return e.length===0?a(o,{dimColor:!0,children:"no agents"}):a(i,{flexDirection:"column",children:e.slice(0,12).map((s,u)=>{let p=I(t,u===r),y=s.errors>0?"red":s.active>0?"yellow":"green",f=s.id===n;return c(i,{children:[a(o,{...p,color:y,children:"\u25CF"}),c(o,{...p,children:[" ",T(s.name,14)]}),c(o,{...p,children:[T(s.tier,6)," "]}),c(o,{...p,children:[s.active,"/",s.total]}),s.errors>0?c(o,{...p,color:"red",children:[" err=",s.errors]}):null,f?a(o,{color:"green",children:" \u2194 chatting"}):null]},s.id)})})}function ee({rows:e,cursor:r,focused:t}){if(e.length===0)return a(o,{dimColor:!0,children:"no live processes"});let n=Date.now();return a(i,{flexDirection:"column",children:e.slice(0,12).map((s,u)=>{let p=I(t,u===r),y=Math.max(0,Math.round((n-(s.lastTurnAt||s.spawnedAt))/1e3)),f=s.state==="dead"?"red":s.state==="warm-hot"?"green":"yellow";return c(i,{children:[a(o,{...p,color:f,children:T(s.state,10)}),a(o,{...p,children:T(s.key.agentId,12)}),a(o,{...p,children:T(s.key.channel,10)}),c(o,{...p,children:[s.turnCount,"t ",y,"s"]})]},`${s.key.agentId}-${s.key.channel}-${s.key.chatId}-${u}`)})})}function te({rows:e}){return e.length===0?a(o,{dimColor:!0,children:"no crons"}):a(i,{flexDirection:"column",children:e.slice(0,12).map(r=>{let t=r.enabled?r.consecutiveErrors>0?a(o,{color:"red",children:"err"}):a(o,{color:"green",children:"ok"}):a(o,{dimColor:!0,children:"off"}),n=r.nextRun?new Date(r.nextRun).toLocaleTimeString():"\u2014";return c(i,{children:[a(o,{children:T(r.id,16)}),a(o,{dimColor:!0,children:T(r.schedule,12)}),a(o,{children:T(n,10)}),a(i,{children:t})]},r.id)})})}function re({rows:e}){if(e.length===0)return a(o,{dimColor:!0,children:"(waiting for channel events\u2026)"});let r=[...e].sort((t,n)=>n.lastAt-t.lastAt);return a(i,{flexDirection:"column",children:r.slice(0,12).map(t=>c(i,{children:[a(o,{children:T(t.channel,12)}),c(o,{color:"cyan",children:["\u2190",T(String(t.inbound),4)]}),c(o,{color:"magenta",children:["\u2192",T(String(t.outbound),4)]}),a(o,{dimColor:!0,children:t.lastPreview})]},t.channel))})}function ne({rows:e,cursor:r,focused:t}){return e.length===0?a(o,{dimColor:!0,children:"(waiting for events\u2026)"}):a(i,{flexDirection:"column",children:e.slice(0,18).map((n,s)=>{let u=I(t,s===r);return c(i,{children:[a(o,{...u,dimColor:!0,children:new Date(n.at).toLocaleTimeString().padEnd(10)}),a(o,{...u,color:ce(n.kind),children:T(n.kind,8)}),a(o,{...u,children:n.line})]},`${n.at}-${s}`)})})}function oe({chat:e,suggest:r,agents:t}){let n=e.history.slice(-14),s=t.find(f=>f.id===e.agentId),u=H(s?.tier??"",s?.model),p=e.status==="sending"?[...e.history].reverse().find(f=>f.role==="agent"&&f.streaming):void 0,y=e.status==="sending"?a(O,{startedAt:p?.at??Date.now(),phase:p?.text?"responding":"thinking",indent:""}):e.error?c(o,{color:"red",children:["\u25CF ",e.error]}):a(o,{dimColor:!0,children:"\u25CF ready"});return c(i,{flexDirection:"column",width:"50%",borderStyle:"double",borderColor:"green",paddingX:1,children:[c(i,{justifyContent:"space-between",children:[c(o,{bold:!0,color:"green",children:["CHAT \xB7 @",e.agentId]}),a(i,{children:y})]}),c(o,{children:[c(o,{color:u.color,bold:!0,children:[u.glyph," ",u.label]}),s?.model?c(o,{dimColor:!0,children:[" ",j(s.model)]}):null,c(o,{dimColor:!0,children:[" \xB7 ",e.chatId]})]}),a(i,{flexDirection:"column",flexGrow:1,marginTop:1,children:n.length===0?a(o,{dimColor:!0,children:"(type a message and press Enter \u2014 Esc exits, Ctrl-L starts fresh)"}):n.map((f,l)=>a(se,{turn:f,agentId:e.agentId},`${f.at}-${l}`))}),r?c(o,{dimColor:!0,children:[a(o,{color:"cyan",children:"\u21B9"})," ",r.items.slice(0,5).map((f,l)=>l===0?a(o,{color:"cyan",children:f},l):c(o,{children:[" ",f]},l))]}):null,c(i,{children:[a(o,{color:"cyan",children:"you \u203A "}),a(o,{children:e.text}),a(o,{inverse:!0,children:" "})]})]})}function se({turn:e,agentId:r}){if(e.role==="you")return c(i,{flexDirection:"column",marginBottom:1,children:[a(o,{color:"cyan",children:"you"}),e.text.split(`
5
5
  `).map((s,u)=>c(o,{children:[" ",s]},u))]});if(e.role==="error")return c(i,{flexDirection:"column",marginBottom:1,children:[a(o,{color:"red",children:"error"}),c(o,{color:"red",children:[" ",e.text]})]});let t=e.elapsedMs!=null?` \xB7 ${(e.elapsedMs/1e3).toFixed(1)}s${e.outTokens!=null?`, ${e.outTokens} tok`:""}`:e.streaming?" \xB7 \u2026":"",n=F(e.text,58,{balance:e.streaming});return c(i,{flexDirection:"column",marginBottom:1,children:[c(o,{color:"green",children:["@",r??"agent",a(o,{dimColor:!0,children:t})]}),e.thinking?e.thinking.split(`
6
6
  `).filter(s=>s.trim()).slice(-2).map((s,u)=>c(o,{dimColor:!0,italic:!0,children:[" ",s]},`th${u}`)):null,(e.tools??[]).map((s,u)=>c(o,{color:s.error?"red":"green",children:[" \u25CF ",c(o,{bold:!s.error,dimColor:s.error,children:[s.name,s.arg?c(o,{dimColor:!0,children:["(",s.arg,")"]}):null,s.error?" failed":""]})]},`tool${u}`)),n.split(`
7
7
  `).slice(0,14).map((s,u)=>c(o,{children:[" ",s]},u))]})}function ae(e){let r=e.data??{},t=typeof r.at=="string"||typeof r.at=="number"?new Date(r.at).getTime():Date.now(),n="";switch(e.event){case"run":n=`${r.phase??""} ${r.workflowId??""} ${(r.runId??"").slice(0,8)} ${r.nodeId??""}`.trim();break;case"task":n=`${r.phase??""} ${r.workflowId??""} ${Array.isArray(r.assignedTo)?r.assignedTo.join(","):""} ${r.title?`"${r.title}"`:""}`.trim();break;case"signal":n=`${r.name??""} (${r.scope??""}) ${r.workflowId??""}`.trim();break;case"mesh":n=`${r.peer??""} ${r.healthy?"healthy":"lost"} ${r.delta??""}`.trim();break;case"channel":{n=`${r.direction==="in"?"\u2190":"\u2192"} ${r.channel??""}:${r.chatId??""} ${r.textPreview??""}`.trim();break}case"status":n=`node=${r.node??""} agents=${r.agents??0} active=${(r.active??[]).length}`;break;default:n=typeof r=="string"?r:JSON.stringify(r).slice(0,80)}return{at:Number.isFinite(t)?t:Date.now(),kind:e.event,line:n}}function ce(e){switch(e){case"run":return"cyan";case"task":return"magenta";case"signal":return"green";case"mesh":return"blue";case"channel":return"yellow";default:return"white"}}function T(e,r){let t=(e??"").toString();return t.length>=r?t.slice(0,r-1)+" ":t+" ".repeat(r-t.length)}export{be as App};
8
- //# sourceMappingURL=App-LC34QQPN.js.map
8
+ //# sourceMappingURL=App-PBPYFVBE.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-LS6KXRXY.js";import"./chunk-XSVGD64O.js";import"./chunk-RJ7DASOU.js";import"./chunk-BRK6M6LA.js";import"./chunk-2QNT5JDS.js";import"./chunk-QIJLFPWE.js";import{useCallback as oe,useMemo as H,useRef as P,useState as k}from"react";import{Box as g,Text as t,useApp as he,useInput as xe,useStdout as Te}from"ink";import{randomUUID as se}from"crypto";var m={accent:"cyan",agent:"green",working:"magenta",error:"red",muted:"gray"},q="\u25C7 agentx",F="\u258C";import{jsx as c,jsxs as o}from"react/jsx-runtime";var ie="/help /agent <id> /agents /clear /who /exit",be=[{cmd:"/agent",desc:"switch the target agent (/agent <id>)"},{cmd:"/agents",desc:"list registered agents"},{cmd:"/clear",desc:"start a fresh session"},{cmd:"/who",desc:"show agent + chat id"},{cmd:"/help",desc:"list commands"},{cmd:"/exit",desc:"quit the chat"}];function Pe({conn:n,agentId:p,channel:f,chatId:a,agents:h}){let{exit:w}=he(),{stdout:O}=Te(),V=Math.max(40,Math.min((O?.columns||80)-4,100)),[z,U]=k([]),[$,S]=k(null),[T,le]=k(p),[A,ae]=k(h),[E,ue]=k(a),[y,x]=k(""),[J,de]=k(""),W=P([]),u=P(-1),M=P(null),R=P(null),v=$?.live??!1,j=H(()=>Z(y,A.map(s=>s.id),process.cwd()),[y,A]),N=H(()=>{let s=/^\/(\w*)$/.exec(y);if(!s)return null;let e=be.filter(r=>r.cmd.slice(1).startsWith(s[1].toLowerCase()));return e.length?e:null},[y]),b=s=>de(s),K=oe(s=>{let e=se().slice(0,8),r=Date.now(),i=new AbortController;R.current=i;let d={id:e,you:s,agentId:T,segs:[],live:!0,startedAt:r};S({...d});let L=()=>S({...d,segs:d.segs.map(C=>({...C}))});(async()=>{try{let C=await X(n,T,s,{channel:f,chatId:E,signal:i.signal,onText:l=>{let I=d.segs[d.segs.length-1];I&&I.type==="text"?I.content=(I.content??"")+l:d.segs.push({type:"text",content:l}),L()},onThinking:l=>{d.thinking=(d.thinking??"")+l,L()},onTool:l=>{l.status==="start"&&l.name?(d.segs.push({type:"tool",name:l.name,arg:l.arg}),L()):l.status==="result"&&l.error&&(d.segs.push({type:"tool",name:l.name??"tool",error:!0}),L())}});U(l=>[...l,{...d,live:!1,elapsedMs:Date.now()-r,outTokens:C.usage?.outputTokens,error:C.error}]),S(null)}catch(C){let l=i.signal.aborted||C?.name==="AbortError";U(I=>[...I,{...d,live:!1,elapsedMs:Date.now()-r,interrupted:l,error:l?void 0:C?.message||String(C)}]),S(null)}finally{R.current===i&&(R.current=null)}})()},[n,T,f,E]),me=oe(s=>{let e=s.trim();if(e){if(W.current.push(e),u.current=-1,e==="/exit"||e==="/quit"){w();return}if(e==="/help"){b(ie);return}if(e==="/who"){b(`@${T} \xB7 chatId=${E} \xB7 channel=${f}`);return}if(e==="/clear"){let r=`chat-cli:${se().slice(0,12)}`;ue(r),U([]),b(`fresh chatId=${r} (warm process dropped)`);return}if(e==="/agents"){Q(n).then(r=>{ae(r),b(r.map(i=>i.id===T?`[${i.id}]`:i.id).join(" "))}).catch(r=>b(`failed to list agents: ${r?.message||r}`));return}if(e.startsWith("/agent ")){let r=e.slice(7).replace(/^@/,"").trim();if(!r){b("usage: /agent <id>");return}if(!A.find(i=>i.id===r)){b(`unknown agent: @${r}`);return}le(r),b(`now talking to @${r}`);return}if(e.startsWith("/")){b(`unknown command \xB7 ${ie}`);return}K(e)}},[n,T,A,f,E,w,K]);xe((s,e)=>{if(e.ctrl&&s==="c"){w();return}if(e.escape){if(v&&R.current){R.current.abort();return}w();return}if(e.tab){if(N){x(N[0].cmd+" ");return}let i=_(y,M,A.map(d=>d.id),process.cwd());i!==null&&(x(i),u.current=-1);return}if(e.upArrow){let i=W.current;if(!i.length)return;u.current=u.current<0?i.length-1:Math.max(0,u.current-1),x(i[u.current]??"");return}if(e.downArrow){let i=W.current;if(u.current<0)return;if(u.current=u.current+1,u.current>=i.length){u.current=-1,x("");return}x(i[u.current]??"");return}if(e.backspace||e.delete){M.current=null,x(i=>i.slice(0,-1));return}if(e.ctrl||e.meta)return;let r=ee(s??"",e.return,y);switch(r.kind){case"paste":M.current=null,x(i=>i+r.text),u.current=-1;return;case"type":M.current=null,x(i=>i+r.text),u.current=-1;return;case"continue":x(r.buffer);return;case"submit":if(v)return;M.current=null,x(""),me(r.text);return;case"none":return}});let G=v?m.working:m.accent,ge=y.length?y.split(`
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-OKT5RCXP.js";import"./chunk-24P6UR3L.js";import"./chunk-RJ7DASOU.js";import"./chunk-BRK6M6LA.js";import"./chunk-2QNT5JDS.js";import"./chunk-QIJLFPWE.js";import{useCallback as oe,useMemo as H,useRef as P,useState as k}from"react";import{Box as g,Text as t,useApp as he,useInput as xe,useStdout as Te}from"ink";import{randomUUID as se}from"crypto";var m={accent:"cyan",agent:"green",working:"magenta",error:"red",muted:"gray"},q="\u25C7 agentx",F="\u258C";import{jsx as c,jsxs as o}from"react/jsx-runtime";var ie="/help /agent <id> /agents /clear /who /exit",be=[{cmd:"/agent",desc:"switch the target agent (/agent <id>)"},{cmd:"/agents",desc:"list registered agents"},{cmd:"/clear",desc:"start a fresh session"},{cmd:"/who",desc:"show agent + chat id"},{cmd:"/help",desc:"list commands"},{cmd:"/exit",desc:"quit the chat"}];function Pe({conn:n,agentId:p,channel:f,chatId:a,agents:h}){let{exit:w}=he(),{stdout:O}=Te(),V=Math.max(40,Math.min((O?.columns||80)-4,100)),[z,U]=k([]),[$,S]=k(null),[T,le]=k(p),[A,ae]=k(h),[E,ue]=k(a),[y,x]=k(""),[J,de]=k(""),W=P([]),u=P(-1),M=P(null),R=P(null),v=$?.live??!1,j=H(()=>Z(y,A.map(s=>s.id),process.cwd()),[y,A]),N=H(()=>{let s=/^\/(\w*)$/.exec(y);if(!s)return null;let e=be.filter(r=>r.cmd.slice(1).startsWith(s[1].toLowerCase()));return e.length?e:null},[y]),b=s=>de(s),K=oe(s=>{let e=se().slice(0,8),r=Date.now(),i=new AbortController;R.current=i;let d={id:e,you:s,agentId:T,segs:[],live:!0,startedAt:r};S({...d});let L=()=>S({...d,segs:d.segs.map(C=>({...C}))});(async()=>{try{let C=await X(n,T,s,{channel:f,chatId:E,signal:i.signal,onText:l=>{let I=d.segs[d.segs.length-1];I&&I.type==="text"?I.content=(I.content??"")+l:d.segs.push({type:"text",content:l}),L()},onThinking:l=>{d.thinking=(d.thinking??"")+l,L()},onTool:l=>{l.status==="start"&&l.name?(d.segs.push({type:"tool",name:l.name,arg:l.arg}),L()):l.status==="result"&&l.error&&(d.segs.push({type:"tool",name:l.name??"tool",error:!0}),L())}});U(l=>[...l,{...d,live:!1,elapsedMs:Date.now()-r,outTokens:C.usage?.outputTokens,error:C.error}]),S(null)}catch(C){let l=i.signal.aborted||C?.name==="AbortError";U(I=>[...I,{...d,live:!1,elapsedMs:Date.now()-r,interrupted:l,error:l?void 0:C?.message||String(C)}]),S(null)}finally{R.current===i&&(R.current=null)}})()},[n,T,f,E]),me=oe(s=>{let e=s.trim();if(e){if(W.current.push(e),u.current=-1,e==="/exit"||e==="/quit"){w();return}if(e==="/help"){b(ie);return}if(e==="/who"){b(`@${T} \xB7 chatId=${E} \xB7 channel=${f}`);return}if(e==="/clear"){let r=`chat-cli:${se().slice(0,12)}`;ue(r),U([]),b(`fresh chatId=${r} (warm process dropped)`);return}if(e==="/agents"){Q(n).then(r=>{ae(r),b(r.map(i=>i.id===T?`[${i.id}]`:i.id).join(" "))}).catch(r=>b(`failed to list agents: ${r?.message||r}`));return}if(e.startsWith("/agent ")){let r=e.slice(7).replace(/^@/,"").trim();if(!r){b("usage: /agent <id>");return}if(!A.find(i=>i.id===r)){b(`unknown agent: @${r}`);return}le(r),b(`now talking to @${r}`);return}if(e.startsWith("/")){b(`unknown command \xB7 ${ie}`);return}K(e)}},[n,T,A,f,E,w,K]);xe((s,e)=>{if(e.ctrl&&s==="c"){w();return}if(e.escape){if(v&&R.current){R.current.abort();return}w();return}if(e.tab){if(N){x(N[0].cmd+" ");return}let i=_(y,M,A.map(d=>d.id),process.cwd());i!==null&&(x(i),u.current=-1);return}if(e.upArrow){let i=W.current;if(!i.length)return;u.current=u.current<0?i.length-1:Math.max(0,u.current-1),x(i[u.current]??"");return}if(e.downArrow){let i=W.current;if(u.current<0)return;if(u.current=u.current+1,u.current>=i.length){u.current=-1,x("");return}x(i[u.current]??"");return}if(e.backspace||e.delete){M.current=null,x(i=>i.slice(0,-1));return}if(e.ctrl||e.meta)return;let r=ee(s??"",e.return,y);switch(r.kind){case"paste":M.current=null,x(i=>i+r.text),u.current=-1;return;case"type":M.current=null,x(i=>i+r.text),u.current=-1;return;case"continue":x(r.buffer);return;case"submit":if(v)return;M.current=null,x(""),me(r.text);return;case"none":return}});let G=v?m.working:m.accent,ge=y.length?y.split(`
2
2
  `):[""],B=A.find(s=>s.id===T),D=ne(B?.tier??"",B?.model),pe=O?.rows||24,fe=z.slice(-40);return o(g,{flexDirection:"column",height:pe,children:[o(g,{flexDirection:"column",flexGrow:1,justifyContent:"flex-end",overflow:"hidden",children:[z.length===0&&!$?o(g,{flexDirection:"column",children:[o(t,{children:[c(t,{color:m.accent,bold:!0,children:q}),c(t,{dimColor:!0,children:" \u2014 chat with "}),o(t,{color:m.accent,children:["@",T]}),c(t,{dimColor:!0,children:" via "}),o(t,{color:D.color,bold:!0,children:[D.glyph," ",D.label]}),B?.model?o(t,{dimColor:!0,children:[" (",B.model,")"]}):null]}),c(t,{dimColor:!0,children:"streaming \xB7 markdown \xB7 tools \xB7 type a message, or / for commands"})]}):null,fe.map(s=>c(ce,{turn:s,width:V},s.id)),$?c(ce,{turn:$,width:V}):null]}),N?c(g,{flexDirection:"column",marginTop:1,children:N.map((s,e)=>o(g,{children:[c(t,{color:e===0?m.accent:m.muted,bold:e===0,children:s.cmd.padEnd(10)}),c(t,{dimColor:!0,children:s.desc})]},s.cmd))}):j?o(g,{marginTop:1,children:[c(t,{color:m.accent,children:"\u21B9 "}),j.items.map((s,e)=>o(t,{color:e===0?m.accent:m.muted,children:[s,e<j.items.length-1?" ":""]},e))]}):J?c(g,{marginTop:1,children:c(t,{dimColor:!0,children:J})}):null,c(g,{borderStyle:"single",borderColor:G,borderLeft:!1,borderRight:!1,borderDimColor:!v,width:"100%",children:c(g,{flexDirection:"column",width:"100%",children:ge.map((s,e,r)=>o(g,{children:[c(t,{color:G,bold:!0,children:e===0?"\u203A ":" "}),c(t,{children:s}),e===r.length-1&&!v?c(t,{color:G,children:"\u258F"}):null]},e))})}),o(g,{justifyContent:"space-between",children:[o(t,{children:[c(t,{color:m.accent,bold:!0,children:q}),o(t,{dimColor:!0,children:[" \xB7 @",T," \xB7 "]}),o(t,{color:D.color,bold:!0,children:[D.glyph," ",D.label]}),B?.model?o(t,{dimColor:!0,children:[" ",re(B.model)]}):null]}),o(t,{dimColor:!0,children:["\u21B5 send / cmds @ mention esc ",v?"stop":"exit"]})]})]})}function ce({turn:n,width:p}){let f=H(()=>ye(n,p),[n,p]),a=n.segs.some(h=>h.type==="text"&&h.content);return o(g,{flexDirection:"column",marginBottom:1,children:[o(t,{color:m.accent,bold:!0,children:[F," you"]}),n.you.split(`
3
3
  `).map((h,w)=>o(t,{color:"white",children:[" ",h]},`u${w}`)),o(t,{color:m.agent,bold:!0,children:[F," ",n.agentId??"agent"]}),n.thinking?c(g,{flexDirection:"column",children:we(n.thinking,n.live?3:2).map((h,w)=>o(t,{dimColor:!0,italic:!0,children:[" ",h]},`th${w}`))}):null,f,n.live?c(te,{startedAt:n.startedAt,phase:a?"responding":"thinking",hint:"esc to interrupt"}):n.interrupted?o(t,{dimColor:!0,children:[" \u2298 interrupted \xB7 ",(n.elapsedMs/1e3).toFixed(1),"s"]}):n.error?o(t,{color:"red",children:[" \u2717 ",n.error]}):n.elapsedMs!=null?o(t,{dimColor:!0,children:[" \xB7 ",(n.elapsedMs/1e3).toFixed(1),"s",n.outTokens!=null?`, ${n.outTokens} tok`:""]}):null]})}function we(n,p){return n.split(`
4
4
  `).filter(a=>a.trim()).slice(-p)}function ye(n,p){let f=[];return n.segs.forEach((a,h)=>{a.type==="tool"?f.push(o(t,{color:a.error?"red":"green",children:[" ","\u25CF ",o(t,{bold:!a.error,dimColor:a.error,children:[a.name,a.arg?o(t,{dimColor:!0,children:["(",a.arg,")"]}):null,a.error?" failed":""]})]},h)):a.content&&f.push(c(t,{children:Ce(Y(a.content,p,{balance:n.live}))},h))}),f}function Ce(n){return n.split(`
5
5
  `).map(p=>p.length?" "+p:p).join(`
6
6
  `)}export{Pe as ChatApp};
7
- //# sourceMappingURL=ChatApp-FB72AK7M.js.map
7
+ //# sourceMappingURL=ChatApp-QC7QAQEW.js.map
@@ -1,2 +1,2 @@
1
- import{A as a,B as b,C as c}from"./chunk-64UK5VG4.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-OZ4XFAD4.js.map
1
+ import{A as a,B as b,C as c}from"./chunk-NIQEKVWY.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-BQEENICU.js.map