agentix-cli 0.90.0 → 0.91.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/mac-voice/Sources/AgentXVoice/Character.swift +152 -0
- package/apps/mac-voice/Sources/AgentXVoice/CharacterDraw.swift +218 -0
- package/apps/mac-voice/Sources/AgentXVoice/CharacterMath.swift +145 -0
- package/apps/mac-voice/Sources/AgentXVoice/CharacterSim.swift +241 -0
- package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +35 -3
- package/apps/mac-voice/Sources/AgentXVoice/SettingsLook.swift +19 -0
- package/apps/mac-voice/Sources/AgentXVoice/SettingsWindow.swift +4 -0
- package/apps/mac-voice/Sources/AgentXVoice/VoiceSettings.swift +7 -2
- package/apps/mac-voice/Sources/AgentXVoice/main.swift +11 -0
- package/dist/{App-LC34QQPN.js → App-PBPYFVBE.js} +2 -2
- package/dist/{ChatApp-FB72AK7M.js → ChatApp-QC7QAQEW.js} +2 -2
- package/dist/{agent-OZ4XFAD4.js → agent-BQEENICU.js} +2 -2
- package/dist/{board-dashboard-YBWSJ7J2.js → board-dashboard-ZLDVYPZ4.js} +4 -4
- package/dist/{chunk-XSVGD64O.js → chunk-24P6UR3L.js} +12 -12
- package/dist/chunk-24P6UR3L.js.map +1 -0
- package/dist/{chunk-RLXEF2X6.js → chunk-4SHWIGTM.js} +5 -5
- package/dist/{chunk-A5DFX47N.js → chunk-4TKGR3XF.js} +2 -2
- package/dist/{chunk-JVQXD35E.js → chunk-7RDNVR5I.js} +172 -172
- package/dist/{chunk-JVQXD35E.js.map → chunk-7RDNVR5I.js.map} +1 -1
- package/dist/{chunk-UWH7NTGV.js → chunk-AVA245WL.js} +2 -2
- package/dist/{chunk-ISIJAZGO.js → chunk-EBHHMJZX.js} +2 -2
- package/dist/{chunk-3SEFVZA7.js → chunk-EJPQPU5J.js} +2 -2
- package/dist/{chunk-QYFF2WJ2.js → chunk-ES2NYGS2.js} +2 -2
- package/dist/{chunk-N2OMSKIQ.js → chunk-HXM35HRM.js} +2 -2
- package/dist/{chunk-LCWE2GHC.js → chunk-HY6ILQ4T.js} +2 -2
- package/dist/{chunk-MU4S6LQA.js → chunk-L73YON3H.js} +2 -2
- package/dist/{chunk-64UK5VG4.js → chunk-NIQEKVWY.js} +2 -2
- package/dist/{chunk-LS6KXRXY.js → chunk-OKT5RCXP.js} +2 -2
- package/dist/{chunk-BEJCSZOQ.js → chunk-OS34M3FV.js} +2 -2
- package/dist/{chunk-JF5UGNMG.js → chunk-PJZ4ZHQS.js} +2 -2
- package/dist/{chunk-TA2BYYNZ.js → chunk-RS6BNRVF.js} +2 -2
- package/dist/{chunk-3GN255MT.js → chunk-S4JD5FEE.js} +2 -2
- package/dist/{chunk-MBVRADNG.js → chunk-SH5Y7N4W.js} +2 -2
- package/dist/{chunk-JLCYQZRT.js → chunk-SQOBKSP3.js} +2 -2
- package/dist/{chunk-TS7JDPOY.js → chunk-URVWZ5FM.js} +2 -2
- package/dist/{chunk-26UFDYRZ.js → chunk-ZVXKCXTP.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/{client-IPZRCNE7.js → client-WFEXHCNH.js} +2 -2
- package/dist/config-PPYPER75.js +2 -0
- package/dist/config-mutator-QQDBVKWM.js +2 -0
- package/dist/{doctor-BTHIQN7F.js → doctor-NRKGBTVD.js} +2 -2
- package/dist/index.d.ts +17 -2
- package/dist/index.js +1 -1
- package/dist/{program-PURSFZBM.js → program-7YKH5WTC.js} +165 -165
- package/dist/program-7YKH5WTC.js.map +1 -0
- package/dist/{promote-RDXNGZKX.js → promote-PY73YR6C.js} +2 -2
- package/dist/{query-OGMNU45Y.js → query-F3R5KXA7.js} +2 -2
- package/dist/{registry-LKIOKDIG.js → registry-YBMYCRQH.js} +2 -2
- package/dist/{request-planner-EP6QJAK5.js → request-planner-CVNEV6NJ.js} +2 -2
- package/dist/{review-candidates-BJPY54JI.js → review-candidates-5QTF5JRG.js} +2 -2
- package/dist/{routing-PZNFU3OV.js → routing-BJLETKHZ.js} +2 -2
- package/dist/schedule-tool-E2PRGDLW.js +2 -0
- package/dist/scheduler-53POJWDI.js +2 -0
- package/dist/{seat-4OONQEDH.js → seat-OORCKWER.js} +2 -2
- package/dist/{select-BR3NBXW7.js → select-OGMDS37Y.js} +2 -2
- package/dist/{set-enabled-Z3NKON2M.js → set-enabled-IKSHTUUF.js} +2 -2
- package/dist/{tool-SV55IZS3.js → tool-6FVBXUTV.js} +2 -2
- package/dist/{tool-DWMZ5OWI.js → tool-CKIO3NBX.js} +2 -2
- package/dist/wiki-4CPXUAPW.js +2 -0
- package/package.json +1 -1
- package/dist/chunk-XSVGD64O.js.map +0 -1
- package/dist/config-HZTKLB6A.js +0 -2
- package/dist/config-mutator-6S4X6WMU.js +0 -2
- package/dist/program-PURSFZBM.js.map +0 -1
- package/dist/schedule-tool-C3CE6GJG.js +0 -2
- package/dist/scheduler-PJMGEJMR.js +0 -2
- package/dist/wiki-EWEJOKP7.js +0 -2
- /package/dist/{App-LC34QQPN.js.map → App-PBPYFVBE.js.map} +0 -0
- /package/dist/{ChatApp-FB72AK7M.js.map → ChatApp-QC7QAQEW.js.map} +0 -0
- /package/dist/{agent-OZ4XFAD4.js.map → agent-BQEENICU.js.map} +0 -0
- /package/dist/{board-dashboard-YBWSJ7J2.js.map → board-dashboard-ZLDVYPZ4.js.map} +0 -0
- /package/dist/{chunk-RLXEF2X6.js.map → chunk-4SHWIGTM.js.map} +0 -0
- /package/dist/{chunk-A5DFX47N.js.map → chunk-4TKGR3XF.js.map} +0 -0
- /package/dist/{chunk-UWH7NTGV.js.map → chunk-AVA245WL.js.map} +0 -0
- /package/dist/{chunk-ISIJAZGO.js.map → chunk-EBHHMJZX.js.map} +0 -0
- /package/dist/{chunk-3SEFVZA7.js.map → chunk-EJPQPU5J.js.map} +0 -0
- /package/dist/{chunk-QYFF2WJ2.js.map → chunk-ES2NYGS2.js.map} +0 -0
- /package/dist/{chunk-N2OMSKIQ.js.map → chunk-HXM35HRM.js.map} +0 -0
- /package/dist/{chunk-LCWE2GHC.js.map → chunk-HY6ILQ4T.js.map} +0 -0
- /package/dist/{chunk-MU4S6LQA.js.map → chunk-L73YON3H.js.map} +0 -0
- /package/dist/{chunk-64UK5VG4.js.map → chunk-NIQEKVWY.js.map} +0 -0
- /package/dist/{chunk-LS6KXRXY.js.map → chunk-OKT5RCXP.js.map} +0 -0
- /package/dist/{chunk-BEJCSZOQ.js.map → chunk-OS34M3FV.js.map} +0 -0
- /package/dist/{chunk-JF5UGNMG.js.map → chunk-PJZ4ZHQS.js.map} +0 -0
- /package/dist/{chunk-TA2BYYNZ.js.map → chunk-RS6BNRVF.js.map} +0 -0
- /package/dist/{chunk-3GN255MT.js.map → chunk-S4JD5FEE.js.map} +0 -0
- /package/dist/{chunk-MBVRADNG.js.map → chunk-SH5Y7N4W.js.map} +0 -0
- /package/dist/{chunk-JLCYQZRT.js.map → chunk-SQOBKSP3.js.map} +0 -0
- /package/dist/{chunk-TS7JDPOY.js.map → chunk-URVWZ5FM.js.map} +0 -0
- /package/dist/{chunk-26UFDYRZ.js.map → chunk-ZVXKCXTP.js.map} +0 -0
- /package/dist/{client-IPZRCNE7.js.map → client-WFEXHCNH.js.map} +0 -0
- /package/dist/{config-HZTKLB6A.js.map → config-PPYPER75.js.map} +0 -0
- /package/dist/{config-mutator-6S4X6WMU.js.map → config-mutator-QQDBVKWM.js.map} +0 -0
- /package/dist/{doctor-BTHIQN7F.js.map → doctor-NRKGBTVD.js.map} +0 -0
- /package/dist/{promote-RDXNGZKX.js.map → promote-PY73YR6C.js.map} +0 -0
- /package/dist/{query-OGMNU45Y.js.map → query-F3R5KXA7.js.map} +0 -0
- /package/dist/{registry-LKIOKDIG.js.map → registry-YBMYCRQH.js.map} +0 -0
- /package/dist/{request-planner-EP6QJAK5.js.map → request-planner-CVNEV6NJ.js.map} +0 -0
- /package/dist/{review-candidates-BJPY54JI.js.map → review-candidates-5QTF5JRG.js.map} +0 -0
- /package/dist/{routing-PZNFU3OV.js.map → routing-BJLETKHZ.js.map} +0 -0
- /package/dist/{schedule-tool-C3CE6GJG.js.map → schedule-tool-E2PRGDLW.js.map} +0 -0
- /package/dist/{scheduler-PJMGEJMR.js.map → scheduler-53POJWDI.js.map} +0 -0
- /package/dist/{seat-4OONQEDH.js.map → seat-OORCKWER.js.map} +0 -0
- /package/dist/{select-BR3NBXW7.js.map → select-OGMDS37Y.js.map} +0 -0
- /package/dist/{set-enabled-Z3NKON2M.js.map → set-enabled-IKSHTUUF.js.map} +0 -0
- /package/dist/{tool-SV55IZS3.js.map → tool-6FVBXUTV.js.map} +0 -0
- /package/dist/{tool-DWMZ5OWI.js.map → tool-CKIO3NBX.js.map} +0 -0
- /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
|
|
299
|
-
|
|
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(
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
7
|
+
//# sourceMappingURL=ChatApp-QC7QAQEW.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{A as a,B as b,C as c}from"./chunk-
|
|
2
|
-
//# sourceMappingURL=agent-
|
|
1
|
+
import{A as a,B as b,C as c}from"./chunk-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
|