martty 0.2.26 → 0.2.27-beta.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/lib/stats-view.js CHANGED
@@ -1,34 +1,48 @@
1
1
  /** Built-in Client Plugin: standard ACP usage and timing in the composer dock. */
2
2
 
3
3
  export const name = 'stats-view'
4
- export const inject = ['acpSessionStats', 'tuiSlots']
4
+ export const inject = ['acpSessionStats', 'acpSessionStatus', 'tuiSlots']
5
5
 
6
6
  export function apply(ctx) {
7
7
  let current = ctx.acpSessionStats.current()
8
+ let status = ctx.acpSessionStatus.current()
8
9
  let panel
9
10
  const stopSlot = ctx.tuiSlots.inject('conversation.composer.dock', () => {
10
11
  panel = ctx.tuiSlots.register(
11
12
  { name: 'conversation.composer.dock', id: 'stats' },
12
- nodesOf(current),
13
+ nodesOf(current, status),
13
14
  )
14
15
  return () => panel.dispose()
15
16
  })
16
17
  const stopStats = ctx.acpSessionStats.subscribe((snapshot) => {
17
18
  current = snapshot
18
- panel?.update(nodesOf(current))
19
+ panel?.update(nodesOf(current, status))
20
+ })
21
+ const stopStatus = ctx.acpSessionStatus.subscribe((snapshot) => {
22
+ status = snapshot
23
+ panel?.update(nodesOf(current, status))
19
24
  })
20
25
  return () => {
21
26
  stopStats?.()
27
+ stopStatus?.()
22
28
  stopSlot?.()
23
29
  }
24
30
  }
25
31
 
26
- function nodesOf(snapshot) {
32
+ function nodesOf(snapshot, status) {
27
33
  const usage = snapshot?.usage ?? {}
28
34
  const stats = snapshot?.stats ?? {}
35
+ const model = status?.model
29
36
  const nodes = []
30
37
  if ((usage.input ?? 0) > 0 || (usage.output ?? 0) > 0) {
31
- nodes.push(node('tokens', `Input ${formatTokens(usage.input)} tok · Output ${formatTokens(usage.output)} tok`))
38
+ nodes.push(node('tokens', `↑${formatTokens(usage.input)} · ↓${formatTokens(usage.output)}`))
39
+ const used = (usage.input ?? 0) + (usage.output ?? 0)
40
+ + (usage.cached ?? 0) + (usage.reasoning ?? 0)
41
+ const size = contextWindowOf(model)
42
+ if (used > 0 && size > 0) {
43
+ const pct = Math.min(100, used / size * 100).toFixed(1)
44
+ nodes.push(node('context', `${pct}%/${contextSizeLabel(size)}`))
45
+ }
32
46
  }
33
47
  if ((stats.turns ?? 0) > 0 || (stats.steps ?? 0) > 0) {
34
48
  nodes.push(node(
@@ -68,6 +82,31 @@ function node(id, title) {
68
82
 
69
83
  function plural(value, singular) { return value === 1 ? singular : `${singular}s` }
70
84
 
85
+ /**
86
+ * Context window of the current model (tokens). The ACP surface does not
87
+ * carry the window size, so the known DeepSeek family sizes stand in;
88
+ * unknown models fall back to a common 128K.
89
+ */
90
+ function contextWindowOf(model) {
91
+ switch (model) {
92
+ case 'deepseek-v4-flash':
93
+ case 'deepseek-v4-pro':
94
+ return 1_000_000
95
+ default:
96
+ return 128_000
97
+ }
98
+ }
99
+
100
+ /** `1000000` → `1.0M`, `128000` → `128K` — keeps the one-decimal look of
101
+ * the percentage side (e.g. `17.5%/1.0M`). */
102
+ function contextSizeLabel(size) {
103
+ if (size >= 1_000_000) {
104
+ const scaled = size / 1_000_000
105
+ return `${scaled >= 100 ? Math.round(scaled) : scaled.toFixed(1)}M`
106
+ }
107
+ return formatTokens(size)
108
+ }
109
+
71
110
  function formatTokens(value = 0) {
72
111
  if (value < 1000) return String(value)
73
112
  const unit = value < 1_000_000 ? 1000 : 1_000_000
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "martty",
3
- "version": "0.2.26",
3
+ "version": "0.2.27-beta.0",
4
4
  "description": "Terminal-native ACP client UI; Cordis client tree, any ACP agent",
5
5
  "license": "MIT",
6
6
  "repository": {
Binary file
Binary file
Binary file
Binary file
Binary file