agentix-cli 0.102.2 → 0.103.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/apps/mac-voice/Sources/AgentXVoice/Character.swift +17 -5
  2. package/apps/mac-voice/Sources/AgentXVoice/CharacterMath.swift +13 -0
  3. package/apps/mac-voice/Sources/AgentXVoice/CharacterSim.swift +14 -16
  4. package/apps/mac-voice/Sources/AgentXVoice/CharacterSimFrame.swift +15 -0
  5. package/apps/mac-voice/Sources/AgentXVoice/Guide.swift +11 -6
  6. package/apps/mac-voice/Sources/AgentXVoice/GuideMath.swift +14 -2
  7. package/apps/mac-voice/Sources/AgentXVoice/IdlePlay.swift +113 -0
  8. package/apps/mac-voice/Sources/AgentXVoice/SettingsLook.swift +11 -0
  9. package/apps/mac-voice/Sources/AgentXVoice/SettingsWindow.swift +4 -1
  10. package/apps/mac-voice/Sources/AgentXVoice/VoiceSettings.swift +8 -2
  11. package/apps/mac-voice/Sources/AgentXVoice/main.swift +13 -2
  12. package/dist/{App-EYUDDF6K.js → App-A7WZMOGA.js} +2 -2
  13. package/dist/{ChatApp-F5LZLBI3.js → ChatApp-EWNT36ZE.js} +2 -2
  14. package/dist/{agent-XF3BWMOB.js → agent-MUGC4RZP.js} +2 -2
  15. package/dist/{board-dashboard-TVSOWW32.js → board-dashboard-IRMCGCU5.js} +4 -4
  16. package/dist/{chunk-ZJXDBVBT.js → chunk-25RQQ55U.js} +2 -2
  17. package/dist/{chunk-YOCQ2MWL.js → chunk-32BPHJ4I.js} +2 -2
  18. package/dist/{chunk-AYNZAOJN.js → chunk-76KJXOEV.js} +2 -2
  19. package/dist/{chunk-CDM4G3CW.js → chunk-7XLCM6CH.js} +185 -185
  20. package/dist/{chunk-CDM4G3CW.js.map → chunk-7XLCM6CH.js.map} +1 -1
  21. package/dist/{chunk-V6W3FFW7.js → chunk-BEFKOHRC.js} +2 -2
  22. package/dist/{chunk-QRD3ZG27.js → chunk-BUAVQUHR.js} +2 -2
  23. package/dist/{chunk-PYCTWI2I.js → chunk-CUS7WGZT.js} +2 -2
  24. package/dist/{chunk-EXPIKLUN.js → chunk-FZ7VHFL5.js} +2 -2
  25. package/dist/{chunk-RWSCQMLD.js → chunk-HE4PDESB.js} +2 -2
  26. package/dist/{chunk-HMDM5MCR.js → chunk-HVPRCHRG.js} +2 -2
  27. package/dist/{chunk-ECM2QEXF.js → chunk-IA3BC7M3.js} +10 -10
  28. package/dist/chunk-IA3BC7M3.js.map +1 -0
  29. package/dist/{chunk-S337S2M7.js → chunk-IOPAWHKW.js} +2 -2
  30. package/dist/{chunk-Y6POV2X5.js → chunk-MVLNVMWI.js} +2 -2
  31. package/dist/{chunk-HQJW3BC5.js → chunk-N4IWNCH3.js} +2 -2
  32. package/dist/{chunk-MWLX3QCD.js → chunk-NWNVHL57.js} +2 -2
  33. package/dist/{chunk-5UO6BBDG.js → chunk-OLUAEBHJ.js} +2 -2
  34. package/dist/{chunk-XO55WKCX.js → chunk-Q57GZPDA.js} +2 -2
  35. package/dist/{chunk-TXFIMVVF.js → chunk-UXTAK5WD.js} +5 -5
  36. package/dist/{chunk-GSFB46YC.js → chunk-WTFNEPJ7.js} +2 -2
  37. package/dist/{chunk-EEZI2K7S.js → chunk-XHUOWYNY.js} +2 -2
  38. package/dist/{chunk-N57ES35J.js → chunk-ZBKKOUNJ.js} +2 -2
  39. package/dist/cli.js +1 -1
  40. package/dist/{client-526OCGWD.js → client-ZX7AY7H3.js} +2 -2
  41. package/dist/config-BXOS2PLC.js +2 -0
  42. package/dist/config-mutator-KRAUD3CA.js +2 -0
  43. package/dist/{doctor-AC5ONEO2.js → doctor-P3QQP33C.js} +2 -2
  44. package/dist/index.d.ts +11 -0
  45. package/dist/index.js +1 -1
  46. package/dist/{program-M5LDV6UQ.js → program-2OEZFPW6.js} +169 -169
  47. package/dist/program-2OEZFPW6.js.map +1 -0
  48. package/dist/{promote-CNKIINYK.js → promote-FHX4Q62Z.js} +2 -2
  49. package/dist/{query-A2N2LIR7.js → query-WBK3YEPV.js} +2 -2
  50. package/dist/{registry-REKWHOHS.js → registry-NQHC7ZAV.js} +2 -2
  51. package/dist/{request-planner-MS5BS6HY.js → request-planner-3LA5RAKG.js} +2 -2
  52. package/dist/{review-candidates-2F4QK6QQ.js → review-candidates-QV66TOCL.js} +2 -2
  53. package/dist/{routing-SU5Q4IPM.js → routing-HF4XVD4Y.js} +2 -2
  54. package/dist/schedule-tool-V66FX5XL.js +2 -0
  55. package/dist/scheduler-XVMWASQR.js +2 -0
  56. package/dist/{seat-PZBFMYQL.js → seat-NQTZUIKH.js} +2 -2
  57. package/dist/{select-D3XOFXA5.js → select-3GVC5RDA.js} +2 -2
  58. package/dist/{set-enabled-DNDRQ3CZ.js → set-enabled-SXCE2MZX.js} +2 -2
  59. package/dist/{tool-FB6SPVIS.js → tool-23GOQNUW.js} +2 -2
  60. package/dist/{tool-RRGOHJW6.js → tool-E6JBENAN.js} +2 -2
  61. package/dist/wiki-VM2RTHVF.js +2 -0
  62. package/package.json +1 -1
  63. package/dist/chunk-ECM2QEXF.js.map +0 -1
  64. package/dist/config-HCJ5NZW5.js +0 -2
  65. package/dist/config-mutator-PGR2OGWC.js +0 -2
  66. package/dist/program-M5LDV6UQ.js.map +0 -1
  67. package/dist/schedule-tool-24KHZY2Z.js +0 -2
  68. package/dist/scheduler-ALDZOUKJ.js +0 -2
  69. package/dist/wiki-3DH2QYTX.js +0 -2
  70. /package/dist/{App-EYUDDF6K.js.map → App-A7WZMOGA.js.map} +0 -0
  71. /package/dist/{ChatApp-F5LZLBI3.js.map → ChatApp-EWNT36ZE.js.map} +0 -0
  72. /package/dist/{agent-XF3BWMOB.js.map → agent-MUGC4RZP.js.map} +0 -0
  73. /package/dist/{board-dashboard-TVSOWW32.js.map → board-dashboard-IRMCGCU5.js.map} +0 -0
  74. /package/dist/{chunk-ZJXDBVBT.js.map → chunk-25RQQ55U.js.map} +0 -0
  75. /package/dist/{chunk-YOCQ2MWL.js.map → chunk-32BPHJ4I.js.map} +0 -0
  76. /package/dist/{chunk-AYNZAOJN.js.map → chunk-76KJXOEV.js.map} +0 -0
  77. /package/dist/{chunk-V6W3FFW7.js.map → chunk-BEFKOHRC.js.map} +0 -0
  78. /package/dist/{chunk-QRD3ZG27.js.map → chunk-BUAVQUHR.js.map} +0 -0
  79. /package/dist/{chunk-PYCTWI2I.js.map → chunk-CUS7WGZT.js.map} +0 -0
  80. /package/dist/{chunk-EXPIKLUN.js.map → chunk-FZ7VHFL5.js.map} +0 -0
  81. /package/dist/{chunk-RWSCQMLD.js.map → chunk-HE4PDESB.js.map} +0 -0
  82. /package/dist/{chunk-HMDM5MCR.js.map → chunk-HVPRCHRG.js.map} +0 -0
  83. /package/dist/{chunk-S337S2M7.js.map → chunk-IOPAWHKW.js.map} +0 -0
  84. /package/dist/{chunk-Y6POV2X5.js.map → chunk-MVLNVMWI.js.map} +0 -0
  85. /package/dist/{chunk-HQJW3BC5.js.map → chunk-N4IWNCH3.js.map} +0 -0
  86. /package/dist/{chunk-MWLX3QCD.js.map → chunk-NWNVHL57.js.map} +0 -0
  87. /package/dist/{chunk-5UO6BBDG.js.map → chunk-OLUAEBHJ.js.map} +0 -0
  88. /package/dist/{chunk-XO55WKCX.js.map → chunk-Q57GZPDA.js.map} +0 -0
  89. /package/dist/{chunk-TXFIMVVF.js.map → chunk-UXTAK5WD.js.map} +0 -0
  90. /package/dist/{chunk-GSFB46YC.js.map → chunk-WTFNEPJ7.js.map} +0 -0
  91. /package/dist/{chunk-EEZI2K7S.js.map → chunk-XHUOWYNY.js.map} +0 -0
  92. /package/dist/{chunk-N57ES35J.js.map → chunk-ZBKKOUNJ.js.map} +0 -0
  93. /package/dist/{client-526OCGWD.js.map → client-ZX7AY7H3.js.map} +0 -0
  94. /package/dist/{config-HCJ5NZW5.js.map → config-BXOS2PLC.js.map} +0 -0
  95. /package/dist/{config-mutator-PGR2OGWC.js.map → config-mutator-KRAUD3CA.js.map} +0 -0
  96. /package/dist/{doctor-AC5ONEO2.js.map → doctor-P3QQP33C.js.map} +0 -0
  97. /package/dist/{promote-CNKIINYK.js.map → promote-FHX4Q62Z.js.map} +0 -0
  98. /package/dist/{query-A2N2LIR7.js.map → query-WBK3YEPV.js.map} +0 -0
  99. /package/dist/{registry-REKWHOHS.js.map → registry-NQHC7ZAV.js.map} +0 -0
  100. /package/dist/{request-planner-MS5BS6HY.js.map → request-planner-3LA5RAKG.js.map} +0 -0
  101. /package/dist/{review-candidates-2F4QK6QQ.js.map → review-candidates-QV66TOCL.js.map} +0 -0
  102. /package/dist/{routing-SU5Q4IPM.js.map → routing-HF4XVD4Y.js.map} +0 -0
  103. /package/dist/{schedule-tool-24KHZY2Z.js.map → schedule-tool-V66FX5XL.js.map} +0 -0
  104. /package/dist/{scheduler-ALDZOUKJ.js.map → scheduler-XVMWASQR.js.map} +0 -0
  105. /package/dist/{seat-PZBFMYQL.js.map → seat-NQTZUIKH.js.map} +0 -0
  106. /package/dist/{select-D3XOFXA5.js.map → select-3GVC5RDA.js.map} +0 -0
  107. /package/dist/{set-enabled-DNDRQ3CZ.js.map → set-enabled-SXCE2MZX.js.map} +0 -0
  108. /package/dist/{tool-FB6SPVIS.js.map → tool-23GOQNUW.js.map} +0 -0
  109. /package/dist/{tool-RRGOHJW6.js.map → tool-E6JBENAN.js.map} +0 -0
  110. /package/dist/{wiki-3DH2QYTX.js.map → wiki-VM2RTHVF.js.map} +0 -0
@@ -72,6 +72,8 @@ final class CharacterHost {
72
72
  /// Sent somewhere, and what its bubble says there (#562).
73
73
  var sent: Bool { guide.showing != nil }
74
74
  var caption: String? { guide.caption }
75
+ /// The state it was asked to show by name (#570), there or where it rests.
76
+ private var asked: CharacterMath.Mood?
75
77
  /// Told when it is sent somewhere, to its next stop or back.
76
78
  var onGuide: (() -> Void)?
77
79
  /// A still picture: Reduce Motion, or "Animated orb" off.
@@ -136,6 +138,10 @@ final class CharacterHost {
136
138
 
137
139
  /// `voice.stroll`: a slow walk now and then while it has nothing to do.
138
140
  var strolls = false
141
+ /// `voice.animations` as the wait between two small animations by
142
+ /// itself (`IdlePlay.gap`), and AgentX's own hold, which stops them.
143
+ var animates = 0.0
144
+ private var quiet = false
139
145
 
140
146
  /// "Animated orb" in the menu.
141
147
  func setAnimated(_ on: Bool) {
@@ -151,8 +157,10 @@ final class CharacterHost {
151
157
  redraw()
152
158
  }
153
159
 
154
- /// Go beside `rect` and mark it, or with nil go back to where it rests.
155
- func guide(to rect: NSRect?, mark kind: GuideMath.Mark = .none, caption: String? = nil) {
160
+ /// Go beside `rect` and mark it, or with nil go back to where it
161
+ /// rests; in the state `asked`, or with nil its real one.
162
+ func guide(to rect: NSRect?, mark kind: GuideMath.Mark = .none, caption: String? = nil, asked mood: CharacterMath.Mood? = nil) {
163
+ asked = shown && !hidden ? mood : nil
156
164
  if shown, !hidden, carried == nil, let rect {
157
165
  guide.show(rect, kind, caption: caption, color: view.stops[2], animated: !still)
158
166
  } else {
@@ -239,10 +247,13 @@ final class CharacterHost {
239
247
 
240
248
  let frame: CharacterSim.Frame
241
249
  if still {
242
- frame = CharacterSim.still(activity, home: home)
250
+ frame = CharacterSim.still(activity, asked: asked, sent: guide.showing != nil, home: home)
243
251
  } else {
244
252
  if activity == .listening, let read = levelSource {
245
253
  level = OrbMath.smooth(level, toward: OrbMath.level(fromRMS: read()))
254
+ } else if asked == .listening {
255
+ // Asked to listen, there is no microphone to follow: a slow swell.
256
+ level = 0.45 + 0.3 * sin(now * 5)
246
257
  } else {
247
258
  level = 0
248
259
  }
@@ -259,11 +270,12 @@ final class CharacterHost {
259
270
  edgesRead = now
260
271
  // The window list counts down from the top of the main screen.
261
272
  let top = first.frame.maxY - rest.y
273
+ quiet = animates > 0 && Hold.isOn
262
274
  edges = strolls && Config.playMode ? Meets.edges(of: Meets.windows(), band: (top - Self.head)...top) : []
263
275
  }
264
276
  frame = sim.step(to: now,
265
- CharacterSim.Input(activity: activity, level: level, pointer: pointer, held: held, sent: guide.showing != nil, strolls: strolls,
266
- edges: edges, shows: bubbleShows?() ?? false, plays: Config.playMode, down: NSEvent.pressedMouseButtons & 1 != 0,
277
+ CharacterSim.Input(activity: activity, level: level, pointer: pointer, held: held, sent: guide.showing != nil, asked: asked, strolls: strolls,
278
+ edges: edges, shows: bubbleShows?() ?? false, plays: Config.playMode, animates: quiet ? 0 : animates, down: NSEvent.pressedMouseButtons & 1 != 0,
267
279
  home: home, range: Double(spot.ends.lowerBound)...Double(spot.ends.upperBound)))
268
280
  }
269
281
  let origin = NSPoint(x: (CGFloat(frame.x) - Self.size.width / 2).rounded(), y: (rest.y - Self.ground).rounded())
@@ -31,6 +31,19 @@ enum CharacterMath {
31
31
  }
32
32
  }
33
33
 
34
+ /// The state shown when one was asked for by name, or it was sent to
35
+ /// show something (#570). Someone talking, a ringing call and a call
36
+ /// between turns always show: the real turn comes first. Otherwise
37
+ /// the state asked for; and sent somewhere with none asked for, it
38
+ /// does not take the busy look, which would hide the move.
39
+ static func mood(for activity: Activity, asked: Mood?, sent: Bool) -> Mood {
40
+ switch activity {
41
+ case .listening, .ringing, .waiting: return mood(for: activity)
42
+ case .thinking: return asked ?? (sent ? .idle : .working)
43
+ case .idle, .speaking: return asked ?? mood(for: activity)
44
+ }
45
+ }
46
+
34
47
  /// The state it goes through on the way from `from` to `to`, if any:
35
48
  /// it notices you before it listens and when it wakes, and nods once
36
49
  /// it has heard you.
@@ -44,6 +44,8 @@ struct CharacterSim {
44
44
  private var met: Double?
45
45
  /// Its games with the pointer, and the crouch and hop they give it.
46
46
  private(set) var play = PointerPlay()
47
+ /// Its small animations by itself (#571).
48
+ private(set) var idle = IdlePlay()
47
49
  private var crouch = 0.0, hop = 0.0
48
50
  private var face = 0.0, lean = 0.0
49
51
  private var glance = (x: 0.0, y: 0.0)
@@ -117,8 +119,8 @@ struct CharacterSim {
117
119
 
118
120
  // Which state. A change starts from the pose it has now, so a
119
121
  // change in the middle of another is still one smooth move.
120
- if input.activity != .idle { restSince = now }
121
- var want = M.mood(for: input.activity)
122
+ if input.activity != .idle || input.asked != nil { restSince = now }
123
+ var want = M.mood(for: input.activity, asked: input.asked, sent: input.sent)
122
124
  if want == .idle && now - restSince >= Self.dozeAfter { want = .dozing }
123
125
  if want != goal {
124
126
  goal = want
@@ -130,6 +132,10 @@ struct CharacterSim {
130
132
  }
131
133
  if let at = pending, now >= at { pending = nil; go(goal, now) }
132
134
  var pose = change.pose(at: now)
135
+ // Idle, left alone and standing: a small animation now and then.
136
+ let alone = mood == .idle && input.activity == .idle && input.asked == nil && !input.sent && !input.held && !input.shows
137
+ && !near && game == PointerPlay.Out() && play.game == nil && abs(speed) < 40 && strolled == 0 && strollTo == 0
138
+ let own = idle.step(now, dt, free: alone, gap: input.animates, dozeIn: restSince + Self.dozeAfter - now)
133
139
 
134
140
  // Along the edge: a spring a little short of critical, so it
135
141
  // arrives with a small overshoot.
@@ -144,11 +150,11 @@ struct CharacterSim {
144
150
  let stopped = strollTo != 0 && abs(strollTo - strolled) < 0.5 ? met : nil
145
151
  let turned = stopped.map { $0 > x ? 0.7 : -0.7 } ?? 0
146
152
  toward(&face, min(max(speed / 200 + turned, -1), 1), 10)
147
- toward(&lean, min(max(speed / 480, -1), 1) * 13, 8)
153
+ toward(&lean, min(max(speed / 480, -1), 1) * 13 + own.tilt, 8)
148
154
  // A game's crouch and hop are followed, not taken at once, so a
149
155
  // game cut short by work does not snap.
150
- toward(&crouch, game.crouch, 20)
151
- toward(&hop, game.hop, 20)
156
+ toward(&crouch, game.crouch + own.crouch, 20)
157
+ toward(&hop, game.hop + own.hop, 20)
152
158
  pose.sx *= 1 + 0.14 * crouch
153
159
  pose.sy *= 1 - 0.22 * crouch
154
160
  pose.lift += hop - 8 * crouch
@@ -180,8 +186,8 @@ struct CharacterSim {
180
186
  look = (dx / far * pull, dy / far * pull)
181
187
  }
182
188
  if stopped != nil { look = (turned * 5, -2) }
183
- toward(&glance.x, look.x + face * 2, 9)
184
- toward(&glance.y, look.y, 9)
189
+ toward(&glance.x, look.x + face * 2 + own.gx, 9)
190
+ toward(&glance.y, look.y + own.gy, 9)
185
191
 
186
192
  // What keeps it alive: breath, bob, blinks, and the voice.
187
193
  let t = now - (began ?? now)
@@ -201,6 +207,7 @@ struct CharacterSim {
201
207
  nextBlink = now + 2.2 + 4.5 * M.rnd(blinks) // uneven, never the same loop twice
202
208
  }
203
209
  if now >= nextBlink { pose.open *= 1 - sin(.pi * (now - nextBlink) / Self.blink) }
210
+ pose.open *= 1 - own.shut
204
211
 
205
212
  return Frame(pose: pose, x: x, face: face, level: input.level, voice: voice, t: t,
206
213
  dots: dots.compactMap { dot(at: now, $0) }, stars: bursts.flatMap { stars(at: now, $0) })
@@ -248,15 +255,6 @@ struct CharacterSim {
248
255
  strolled = 0; strollTo = 0; strollNext = nil
249
256
  }
250
257
 
251
- /// The state alone, where it rests: for Reduce Motion, which shows a
252
- /// still picture that changes between states and never a loop.
253
- static func still(_ activity: M.Activity, home: Double) -> Frame {
254
- // Its marks at full strength, so each state reads without motion:
255
- // the arcs of its voice only while it speaks.
256
- let pose = M.pose(M.mood(for: activity))
257
- return Frame(pose: pose, x: home, level: 1, voice: pose.speak)
258
- }
259
-
260
258
  private mutating func go(_ to: M.Mood, _ now: Double) {
261
259
  change = M.Transition(from: change.pose(at: now), to: M.pose(to), start: now, duration: M.duration(into: to))
262
260
  mood = to
@@ -15,6 +15,9 @@ extension CharacterSim {
15
15
  /// The answering agent sent it to show something (#482): it goes
16
16
  /// to `home` at once, awake, whatever the pointer did before.
17
17
  var sent = false
18
+ /// The state it was asked to show by name (#570), in place of
19
+ /// what the assistant is doing.
20
+ var asked: M.Mood?
18
21
  /// `voice.stroll`: with nothing to do, it takes a slow stroll
19
22
  /// beside where it rests now and then (#482).
20
23
  var strolls = false
@@ -26,6 +29,9 @@ extension CharacterSim {
26
29
  var shows = false
27
30
  /// "Play mode" is ticked: idle, it plays with the pointer (#505).
28
31
  var plays = false
32
+ /// `voice.animations`: the shortest wait between two small
33
+ /// animations by itself, in seconds (#571). 0: none.
34
+ var animates = 0.0
29
35
  /// The mouse button is down.
30
36
  var down = false
31
37
  /// Where it rests, and how far it may go.
@@ -56,4 +62,13 @@ extension CharacterSim {
56
62
  var dots: [Mark] = []
57
63
  var stars: [Mark] = []
58
64
  }
65
+
66
+ /// The state alone, where it rests: for Reduce Motion, which shows a
67
+ /// still picture that changes between states and never a loop.
68
+ static func still(_ activity: M.Activity, asked: M.Mood? = nil, sent: Bool = false, home: Double) -> Frame {
69
+ // Its marks at full strength, so each state reads without motion:
70
+ // the arcs of its voice only while it speaks.
71
+ let pose = M.pose(M.mood(for: activity, asked: asked, sent: sent))
72
+ return Frame(pose: pose, x: home, level: 1, voice: pose.speak)
73
+ }
59
74
  }
@@ -13,6 +13,9 @@ enum GuideClient {
13
13
  /// What its bubble says at the stop (#562). Nil: no bubble there,
14
14
  /// and a daemon too old to send one.
15
15
  let text: String?
16
+ /// The state it shows meanwhile (#570). Nil: its real state, and
17
+ /// a daemon too old to send one.
18
+ let expression: String?
16
19
  }
17
20
 
18
21
  /// GET /voice/guide: the command after `seq`, as soon as there is
@@ -37,8 +40,9 @@ final class GuideWatcher {
37
40
  private var task: Task<Void, Never>?
38
41
  /// The character is on screen.
39
42
  var wanted: (() -> Bool)?
40
- /// Show this, in AppKit coordinates, with this caption, or go home.
41
- var onCommand: ((NSRect?, GuideMath.Mark, String?) -> Void)?
43
+ /// Show this, in AppKit coordinates, with this caption and in this
44
+ /// state, or go home.
45
+ var onCommand: ((NSRect?, GuideMath.Mark, String?, CharacterMath.Mood?) -> Void)?
42
46
 
43
47
  /// (Re)start the wait. Called again when the character comes on screen
44
48
  /// or leaves it, so a wait already open is ended: cancelling it closes
@@ -56,7 +60,7 @@ final class GuideWatcher {
56
60
  private func turn() async {
57
61
  guard wanted?() == true, let command = await GuideClient.next(after: seq) else {
58
62
  // Hidden, or the daemon is gone: nothing is left on screen.
59
- if away { away = false; onCommand?(nil, .none, nil) }
63
+ if away { away = false; onCommand?(nil, .none, nil, nil) }
60
64
  try? await Task.sleep(nanoseconds: 2_000_000_000)
61
65
  return
62
66
  }
@@ -66,9 +70,10 @@ final class GuideWatcher {
66
70
  GuideMath.toAppKit(CGRect(x: $0.x, y: $0.y, width: $0.width, height: $0.height),
67
71
  primaryHeight: NSScreen.screens.first?.frame.maxY ?? 0)
68
72
  }
69
- away = rect != nil
70
- Log.info("guide: \(rect.map { "to \(Int($0.minX)),\(Int($0.minY)) \(Int($0.width))x\(Int($0.height)) \(command.mark)" } ?? "home")")
71
- onCommand?(rect, GuideMath.Mark(rawValue: command.mark) ?? .box, rect == nil ? nil : command.text)
73
+ let asked = command.expression.flatMap { CharacterMath.Mood(rawValue: $0) }
74
+ away = rect != nil || asked != nil
75
+ Log.info("guide: \(rect.map { "to \(Int($0.minX)),\(Int($0.minY)) \(Int($0.width))x\(Int($0.height)) \(command.mark)" } ?? "home")\(asked.map { ", \($0.rawValue)" } ?? "")")
76
+ onCommand?(rect, GuideMath.Mark(rawValue: command.mark) ?? .box, rect == nil ? nil : command.text, asked)
72
77
  }
73
78
  }
74
79
 
@@ -46,9 +46,21 @@ enum GuideMath {
46
46
  /// At a stop the bubble says the command's caption, and with none
47
47
  /// there is no bubble; the idle hint is for where it rests, not for
48
48
  /// a stop, the way there or a play. `away`: sent somewhere, or playing.
49
- static func idle(caption: String?, away: Bool) -> Idle {
49
+ /// `hint`: the hint may show (hintShows).
50
+ static func idle(caption: String?, away: Bool, hint: Bool = true) -> Idle {
50
51
  if let caption, !caption.isEmpty { return .caption(caption) }
51
- return away ? .nothing : .hint
52
+ return away || !hint ? .nothing : .hint
53
+ }
54
+
55
+ /// Seconds after the app starts that the character's bubble says the hint.
56
+ static let hintSeconds = 6.0
57
+
58
+ /// Whether the idle hint may show (#576). A pill of its own always
59
+ /// says it. The character's bubble says it only for the first seconds
60
+ /// after the app starts, then there is no bubble at idle; "notifications
61
+ /// held" stays, since nothing else on screen says it.
62
+ static func hintShows(character: Bool, sinceStart: Double, held: Bool) -> Bool {
63
+ !character || held || sinceStart < hintSeconds
52
64
  }
53
65
 
54
66
  /// What a label needs beyond the width of its words (#569): a text
@@ -0,0 +1,113 @@
1
+ import Foundation
2
+
3
+ /// Small animations by itself (#571): idle and left alone, the character
4
+ /// now and then looks around, hops or sways, just for the fun of it, and
5
+ /// stretches with a yawn before it dozes. `voice.animations` says how
6
+ /// often, or not at all.
7
+ ///
8
+ /// It only decides what an animation adds to the pose; `CharacterSim`
9
+ /// follows it, so one cut short by work does not snap. Foundation only,
10
+ /// and the same animations in the same order on every run, so the tests
11
+ /// can count on them.
12
+ struct IdlePlay {
13
+ typealias M = CharacterMath
14
+
15
+ /// The animations, by name. The yawn comes before it dozes; the
16
+ /// others are picked now and then.
17
+ enum Act: String, CaseIterable { case look, hop, sway, yawn }
18
+
19
+ /// `voice.animations` as the shortest wait between two, in seconds.
20
+ /// The wait is up to twice that. 0: none.
21
+ static func gap(_ often: String?) -> Double {
22
+ switch often {
23
+ case "rarely": return 60
24
+ case "sometimes": return 25
25
+ case "often": return 10
26
+ default: return 0
27
+ }
28
+ }
29
+
30
+ /// Seconds an animation takes.
31
+ static func length(_ act: Act) -> Double {
32
+ switch act {
33
+ case .look, .yawn: return 2.6
34
+ case .hop: return 0.9
35
+ case .sway: return 1.8
36
+ }
37
+ }
38
+
39
+ /// This close to dozing, in seconds, no animation starts but the yawn.
40
+ static let calm = 6.0
41
+
42
+ /// What an animation adds to the pose: gaze and height in drawing
43
+ /// units, lean in degrees, 0…1 crouched (below 0: stretched tall) and
44
+ /// 0…1 how far the eyes are shut.
45
+ struct Out: Equatable {
46
+ var gx = 0.0, gy = 0.0, tilt = 0.0, hop = 0.0, crouch = 0.0, shut = 0.0
47
+ }
48
+
49
+ private(set) var act: Act?
50
+ private(set) var played = 0
51
+ private var began = 0.0
52
+ private var next: Double?
53
+ private var yawned = false
54
+ private var turn = 0
55
+ private var shut = 0.0
56
+
57
+ /// One frame. `free`: idle, left alone and standing where it is.
58
+ /// `dozeIn`: seconds until it dozes.
59
+ mutating func step(_ now: Double, _ dt: Double, free: Bool, gap: Double, dozeIn: Double) -> Out {
60
+ var out = Out()
61
+ if !free || gap <= 0 {
62
+ act = nil; next = nil
63
+ } else {
64
+ if dozeIn > Self.calm { yawned = false }
65
+ if let on = act, now - began >= Self.length(on) { act = nil; next = nil }
66
+ if act == nil {
67
+ if !yawned && dozeIn <= Self.length(.yawn) {
68
+ yawned = true
69
+ start(.yawn, now)
70
+ } else if next == nil {
71
+ next = now + gap * (1 + M.rnd(played, 5))
72
+ } else if now >= next!, dozeIn > Self.calm {
73
+ // One of the other two, so the same never comes twice.
74
+ turn = (turn + 1 + min(Int(M.rnd(played, 6) * 2), 1)) % 3
75
+ start([Act.look, .hop, .sway][turn], now)
76
+ }
77
+ }
78
+ if let on = act { out = Self.shape(on, (now - began) / Self.length(on)) }
79
+ }
80
+ // The eyes are followed here; the rest by the sim.
81
+ shut += (out.shut - shut) * (1 - exp(-dt * 20))
82
+ out.shut = shut
83
+ return out
84
+ }
85
+
86
+ /// Play `act` now, whatever the wait.
87
+ mutating func start(_ act: Act, _ now: Double) {
88
+ self.act = act
89
+ began = now
90
+ played += 1
91
+ }
92
+
93
+ /// An animation `p` of the way through, 0…1. Each starts and ends at
94
+ /// nothing, so it joins the resting pose at both ends.
95
+ static func shape(_ act: Act, _ p: Double) -> Out {
96
+ switch act {
97
+ case .look:
98
+ // To one side, to the other, and back.
99
+ return Out(gx: 8 * sin(2 * .pi * p), gy: -1.5 * sin(.pi * p))
100
+ case .hop:
101
+ // A short crouch, then up and down again.
102
+ return Out(hop: 26 * sin(.pi * M.span(p, 0.3, 1)),
103
+ crouch: 0.7 * M.ease(p / 0.3) * (1 - M.ease(M.span(p, 0.3, 0.5))))
104
+ case .sway:
105
+ // Two swings from side to side, the widest in the middle.
106
+ return Out(tilt: 10 * sin(4 * .pi * p) * sin(.pi * p))
107
+ case .yawn:
108
+ // It stretches tall, looks up, and shuts its eyes a moment.
109
+ return Out(gy: -2 * sin(.pi * p), crouch: -0.8 * sin(.pi * M.span(p, 0, 0.8)),
110
+ shut: M.ease(M.span(p, 0.15, 0.4)) * (1 - M.ease(M.span(p, 0.75, 1))))
111
+ }
112
+ }
113
+ }
@@ -59,6 +59,8 @@ struct LookSection: View {
59
59
  var startReduced: Binding<Bool>?
60
60
  /// Nil hides the control: the daemon is older than the stroll.
61
61
  var stroll: Binding<Bool>?
62
+ /// Nil hides the control: the daemon is older than the animations.
63
+ var animations: Binding<String>?
62
64
 
63
65
  var body: some View {
64
66
  Section {
@@ -74,6 +76,15 @@ struct LookSection: View {
74
76
  Toggle("Character strolls when idle", isOn: stroll)
75
77
  .disabled(look != "character")
76
78
  }
79
+ if let animations {
80
+ Picker("Character plays when idle", selection: animations) {
81
+ Text("Never").tag("off")
82
+ Text("Rarely").tag("rarely")
83
+ Text("Sometimes").tag("sometimes")
84
+ Text("Often").tag("often")
85
+ }
86
+ .disabled(look != "character")
87
+ }
77
88
  } header: {
78
89
  Text("Assistant")
79
90
  } footer: {
@@ -381,7 +381,10 @@ private struct GeneralTab: View {
381
381
  set: { model.draft?.general.startReduced = $0 }),
382
382
  stroll: draft.general.stroll == nil ? nil
383
383
  : Binding(get: { model.draft?.general.stroll ?? false },
384
- set: { model.draft?.general.stroll = $0 }))
384
+ set: { model.draft?.general.stroll = $0 }),
385
+ animations: draft.general.animations == nil ? nil
386
+ : Binding(get: { model.draft?.general.animations ?? "off" },
387
+ set: { model.draft?.general.animations = $0 }))
385
388
  }
386
389
  if draft.general.card != nil {
387
390
  AnswerCardSection(card: Binding(get: { model.draft?.general.card ?? .standard },
@@ -35,14 +35,18 @@ struct VoiceSettings: Codable, Equatable {
35
35
  /// The character takes a stroll when it has nothing to do. Nil
36
36
  /// from a daemon older than the stroll.
37
37
  var stroll: Bool?
38
+ /// How often the character plays a small animation by itself:
39
+ /// "off", "rarely", "sometimes" or "often". Nil from a daemon
40
+ /// older than the animations.
41
+ var animations: String?
38
42
 
39
43
  init(provider: String, fallback: String, stt: String, localStt: String = "mlx-whisper",
40
44
  endOfTurn: String = "vad", hotkeys: Hotkeys, card: Card? = nil, look: String? = nil,
41
- startReduced: Bool? = nil, stroll: Bool? = nil) {
45
+ startReduced: Bool? = nil, stroll: Bool? = nil, animations: String? = nil) {
42
46
  self.provider = provider; self.fallback = fallback; self.stt = stt
43
47
  self.localStt = localStt; self.endOfTurn = endOfTurn; self.hotkeys = hotkeys
44
48
  self.card = card; self.look = look; self.startReduced = startReduced
45
- self.stroll = stroll
49
+ self.stroll = stroll; self.animations = animations
46
50
  }
47
51
 
48
52
  /// A daemon from before these fields leaves them out: the defaults.
@@ -58,6 +62,7 @@ struct VoiceSettings: Codable, Equatable {
58
62
  look = try c.decodeIfPresent(String.self, forKey: .look)
59
63
  startReduced = try c.decodeIfPresent(Bool.self, forKey: .startReduced)
60
64
  stroll = try c.decodeIfPresent(Bool.self, forKey: .stroll)
65
+ animations = try c.decodeIfPresent(String.self, forKey: .animations)
61
66
  }
62
67
  }
63
68
  struct Voice: Codable, Equatable {
@@ -119,6 +124,7 @@ struct VoiceSettings: Codable, Equatable {
119
124
  if let look = self.general.look, look != old.general.look { general["look"] = look }
120
125
  if let start = self.general.startReduced, start != old.general.startReduced { general["startReduced"] = start }
121
126
  if let stroll = self.general.stroll, stroll != old.general.stroll { general["stroll"] = stroll }
127
+ if let often = self.general.animations, often != old.general.animations { general["animations"] = often }
122
128
  var keys: [String: Any] = [:]
123
129
  if self.general.hotkeys.talk != old.general.hotkeys.talk { keys["talk"] = self.general.hotkeys.talk }
124
130
  if self.general.hotkeys.stop != old.general.hotkeys.stop { keys["stop"] = self.general.hotkeys.stop }
@@ -88,6 +88,8 @@ final class App: NSObject, NSApplicationDelegate {
88
88
  private let guideWatcher = GuideWatcher()
89
89
  /// A play runs, or its picture is being taken (#562).
90
90
  private var playing = false
91
+ /// When the app started, for the hint in the character's bubble.
92
+ private let launched = ProcessInfo.processInfo.systemUptime
91
93
  private let ringer = Ringer()
92
94
  /// The call ringing on the pill now.
93
95
  private var ringingCall: IncomingCall?
@@ -218,7 +220,9 @@ final class App: NSObject, NSApplicationDelegate {
218
220
  panel.setShowsOrb(!asCharacter)
219
221
  character.setShown(asCharacter)
220
222
  character.strolls = saved.general.stroll == true
223
+ character.animates = IdlePlay.gap(saved.general.animations)
221
224
  if !asCharacter { panel.detach() }
225
+ idleWords()
222
226
  // voice.startReduced is how the assistant starts: read once.
223
227
  if !startApplied {
224
228
  startApplied = true
@@ -240,8 +244,12 @@ final class App: NSObject, NSApplicationDelegate {
240
244
 
241
245
  /// What the idle bubble says (#562): no hint at a guide stop, on the
242
246
  /// way there or during a play; the stop's caption if it has one.
247
+ /// The character's bubble says the hint only just after the app
248
+ /// starts, then there is none at idle (#576).
243
249
  private func idleWords() {
244
- panel.setIdle(GuideMath.idle(caption: character.caption, away: character.sent || playing))
250
+ let hint = GuideMath.hintShows(character: settings?.general.look == "character",
251
+ sinceStart: ProcessInfo.processInfo.systemUptime - launched, held: Hold.isOn)
252
+ panel.setIdle(GuideMath.idle(caption: character.caption, away: character.sent || playing, hint: hint))
245
253
  }
246
254
 
247
255
  /// "Play on this page" (#505): one picture of the character's screen, the
@@ -507,6 +515,7 @@ final class App: NSObject, NSApplicationDelegate {
507
515
  // and this switch is not that.
508
516
  statusMenu.onHoldChanged = { [weak self] nowOn in
509
517
  guard let self else { return }
518
+ self.idleWords()
510
519
  self.panel.render(.idle)
511
520
  if !nowOn { Hold.flushHeld() }
512
521
  Log.info(nowOn ? "notifications held" : "notifications delivering")
@@ -526,8 +535,10 @@ final class App: NSObject, NSApplicationDelegate {
526
535
  callWatcher.start()
527
536
  // The answering agent sends the character to something (#482).
528
537
  guideWatcher.wanted = { [weak self] in self?.character.onScreen == true }
529
- guideWatcher.onCommand = { [weak self] rect, mark, caption in self?.character.guide(to: rect, mark: mark, caption: caption) }
538
+ guideWatcher.onCommand = { [weak self] rect, mark, caption, asked in self?.character.guide(to: rect, mark: mark, caption: caption, asked: asked) }
530
539
  character.onGuide = { [weak self] in self?.idleWords() }
540
+ // The hint has had its seconds: no bubble at idle from here.
541
+ DispatchQueue.main.asyncAfter(deadline: .now() + GuideMath.hintSeconds) { [weak self] in self?.idleWords() }
531
542
  guideWatcher.start()
532
543
  character.onScreenChanged = { [weak self] in self?.guideWatcher.start() }
533
544
 
@@ -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-PYCTWI2I.js";import"./chunk-ECM2QEXF.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-CUS7WGZT.js";import"./chunk-IA3BC7M3.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-EYUDDF6K.js.map
8
+ //# sourceMappingURL=App-A7WZMOGA.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-PYCTWI2I.js";import"./chunk-ECM2QEXF.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-CUS7WGZT.js";import"./chunk-IA3BC7M3.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-F5LZLBI3.js.map
7
+ //# sourceMappingURL=ChatApp-EWNT36ZE.js.map
@@ -1,2 +1,2 @@
1
- import{A as a,B as b,C as c}from"./chunk-HMDM5MCR.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-XF3BWMOB.js.map
1
+ import{A as a,B as b,C as c}from"./chunk-HVPRCHRG.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-MUGC4RZP.js.map