@pellux/goodvibes-agent 1.4.4 → 1.5.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/CHANGELOG.md +14 -0
- package/README.md +7 -7
- package/dist/package/main.js +7424 -12241
- package/docs/README.md +2 -2
- package/docs/channels-remote-and-api.md +1 -1
- package/docs/getting-started.md +2 -2
- package/docs/release-and-publishing.md +1 -1
- package/docs/tools-and-commands.md +5 -5
- package/package.json +4 -2
- package/release/performance-snapshot.json +2 -2
- package/release/release-notes.md +11 -7
- package/release/release-readiness.json +6 -6
- package/src/agent/behavior-discovery-summary.ts +4 -18
- package/src/agent/calendar-registry.ts +322 -0
- package/src/agent/competitive-feature-inventory.ts +268 -130
- package/src/agent/document-registry.ts +5 -1
- package/src/agent/email/email-service.ts +350 -0
- package/src/agent/email/imap-client.ts +596 -0
- package/src/agent/email/smtp-client.ts +453 -0
- package/src/agent/ics-calendar.ts +662 -0
- package/src/agent/ics-timezone.ts +172 -0
- package/src/agent/markdown-frontmatter.ts +31 -0
- package/src/agent/memory-safety.ts +1 -1
- package/src/agent/note-registry.ts +3 -9
- package/src/agent/persona-discovery.ts +3 -15
- package/src/agent/persona-registry.ts +1 -10
- package/src/agent/research-source-registry.ts +5 -1
- package/src/agent/routine-discovery.ts +3 -15
- package/src/agent/runtime-profile.ts +3 -0
- package/src/agent/skill-discovery.ts +3 -15
- package/src/agent/skill-draft-proposer.ts +203 -0
- package/src/agent/skill-draft-runner.ts +201 -0
- package/src/agent/skill-registry.ts +36 -1
- package/src/agent/skill-standard.ts +99 -0
- package/src/agent/vibe-file.ts +7 -22
- package/src/cli/completion.ts +1 -1
- package/src/cli/config-overrides.ts +10 -1
- package/src/cli/redaction.ts +17 -5
- package/src/config/provider-model.ts +2 -1
- package/src/config/secret-config.ts +13 -6
- package/src/core/activity-feed.ts +97 -0
- package/src/core/away-digest.ts +161 -0
- package/src/core/conversation-rendering.ts +7 -3
- package/src/core/conversation.ts +22 -15
- package/src/core/hardware-profile.ts +362 -0
- package/src/core/last-seen-store.ts +78 -0
- package/src/core/plain-language.ts +52 -0
- package/src/core/setup-incomplete-hint.ts +90 -0
- package/src/core/system-message-router.ts +38 -87
- package/src/input/agent-workspace-basic-command-editor-submission.ts +60 -220
- package/src/input/agent-workspace-basic-command-editors.ts +39 -141
- package/src/input/agent-workspace-categories.ts +40 -125
- package/src/input/agent-workspace-command-editor.ts +4 -0
- package/src/input/agent-workspace-host-category.ts +0 -5
- package/src/input/agent-workspace-local-editor-submission.ts +3 -1
- package/src/input/agent-workspace-navigation.ts +10 -3
- package/src/input/agent-workspace-onboarding-actions.ts +132 -0
- package/src/input/agent-workspace-onboarding-categories.ts +29 -26
- package/src/input/agent-workspace-onboarding-finish.ts +11 -4
- package/src/input/agent-workspace-onboarding-state.ts +111 -0
- package/src/input/agent-workspace-profile-editor-submission.ts +260 -0
- package/src/input/agent-workspace-profile-editors.ts +155 -0
- package/src/input/agent-workspace-subscription-editor.ts +7 -0
- package/src/input/agent-workspace-types.ts +5 -1
- package/src/input/agent-workspace.ts +65 -39
- package/src/input/command-registry.ts +18 -5
- package/src/input/commands/agent-runtime-profile-runtime.ts +2 -1
- package/src/input/commands/agent-skills-runtime.ts +60 -3
- package/src/input/commands/calendar-runtime.ts +209 -0
- package/src/input/commands/channels-runtime.ts +1 -0
- package/src/input/commands/command-error.ts +9 -0
- package/src/input/commands/compat-runtime.ts +1 -0
- package/src/input/commands/config.ts +1 -0
- package/src/input/commands/conversation-runtime.ts +1 -1
- package/src/input/commands/delegation-runtime.ts +5 -6
- package/src/input/commands/email-runtime.ts +387 -0
- package/src/input/commands/experience-runtime.ts +17 -4
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +6 -1
- package/src/input/commands/knowledge-format.ts +73 -0
- package/src/input/commands/knowledge.ts +9 -74
- package/src/input/commands/local-provider-runtime.ts +2 -1
- package/src/input/commands/local-runtime.ts +10 -4
- package/src/input/commands/mcp-runtime.ts +7 -0
- package/src/input/commands/notify-runtime.ts +17 -1
- package/src/input/commands/onboarding-runtime.ts +1 -0
- package/src/input/commands/operator-actions-runtime.ts +1 -0
- package/src/input/commands/operator-runtime.ts +3 -0
- package/src/input/commands/personas-runtime.ts +1 -0
- package/src/input/commands/platform-access-runtime.ts +40 -17
- package/src/input/commands/product-runtime.ts +6 -1
- package/src/input/commands/provider-accounts-runtime.ts +3 -2
- package/src/input/commands/qrcode-runtime.ts +1 -0
- package/src/input/commands/routines-runtime.ts +1 -0
- package/src/input/commands/runtime-services.ts +0 -13
- package/src/input/commands/security-runtime.ts +1 -0
- package/src/input/commands/session-content.ts +1 -0
- package/src/input/commands/shell-core.ts +5 -2
- package/src/input/commands/subscription-runtime.ts +33 -9
- package/src/input/commands/support-bundle-runtime.ts +8 -1
- package/src/input/commands/tasks-runtime.ts +1 -0
- package/src/input/commands/tts-runtime.ts +1 -0
- package/src/input/commands/vibe-runtime.ts +1 -0
- package/src/input/commands.ts +4 -0
- package/src/input/feed-context-factory.ts +3 -12
- package/src/input/handler-command-route.ts +7 -60
- package/src/input/handler-feed-routes.ts +0 -194
- package/src/input/handler-feed.ts +2 -54
- package/src/input/handler-interactions.ts +0 -2
- package/src/input/handler-modal-stack.ts +0 -9
- package/src/input/handler-picker-routes.ts +24 -1
- package/src/input/handler-shortcuts.ts +11 -40
- package/src/input/handler-ui-state.ts +13 -0
- package/src/input/handler.ts +8 -35
- package/src/input/keybindings.ts +40 -17
- package/src/input/model-picker-local-fit.ts +130 -0
- package/src/input/submission-router.ts +0 -5
- package/src/main.ts +111 -89
- package/src/renderer/activity-sidebar.ts +186 -0
- package/src/renderer/agent-workspace-context-lines.ts +5 -48
- package/src/renderer/agent-workspace-style.ts +1 -1
- package/src/renderer/agent-workspace.ts +14 -2
- package/src/renderer/compositor.ts +37 -125
- package/src/renderer/conversation-overlays.ts +3 -3
- package/src/renderer/help-overlay.ts +7 -5
- package/src/renderer/model-workspace.ts +101 -86
- package/src/{panels → renderer}/polish.ts +3 -3
- package/src/renderer/shell-surface.ts +4 -5
- package/src/renderer/status-token.ts +31 -11
- package/src/renderer/tab-strip.ts +1 -1
- package/src/renderer/tool-call.ts +7 -8
- package/src/renderer/tool-labels.ts +92 -0
- package/src/renderer/ui-factory.ts +48 -96
- package/src/runtime/bootstrap-command-context.ts +0 -5
- package/src/runtime/bootstrap-command-parts.ts +6 -15
- package/src/runtime/bootstrap-core.ts +34 -13
- package/src/runtime/bootstrap-hook-bridge.ts +3 -1
- package/src/runtime/bootstrap-shell.ts +3 -50
- package/src/runtime/bootstrap.ts +3 -4
- package/src/runtime/context.ts +1 -1
- package/src/runtime/diagnostics/panels/index.ts +0 -1
- package/src/runtime/diagnostics/panels/policy.ts +1 -1
- package/src/runtime/execution-ledger.ts +16 -1
- package/src/runtime/index.ts +6 -1
- package/src/runtime/onboarding/index.ts +1 -0
- package/src/runtime/onboarding/onboarding-state.ts +200 -0
- package/src/{panels → runtime}/provider-account-snapshot.ts +5 -2
- package/src/runtime/services.ts +16 -4
- package/src/runtime/terminal-output-guard.ts +7 -0
- package/src/runtime/tool-permission-safety.ts +1 -1
- package/src/runtime/ui/model-picker/data-provider.ts +1 -1
- package/src/runtime/ui/model-picker/health-enrichment.ts +2 -2
- package/src/runtime/ui/model-picker/types.ts +2 -2
- package/src/runtime/ui/provider-health/data-provider.ts +3 -3
- package/src/runtime/ui/provider-health/types.ts +2 -2
- package/src/runtime/ui-services.ts +0 -2
- package/src/shell/agent-workspace-fullscreen.ts +0 -1
- package/src/shell/autonomy-surfacing.ts +272 -0
- package/src/shell/session-continuity-hints.ts +0 -6
- package/src/shell/startup-wiring.ts +221 -0
- package/src/shell/ui-openers.ts +18 -29
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-harness-background-processes.ts +4 -4
- package/src/tools/agent-harness-browser-cockpit-route.ts +3 -3
- package/src/tools/agent-harness-command-runner.ts +6 -1
- package/src/tools/agent-harness-document-ops.ts +26 -53
- package/src/tools/agent-harness-execution-history.ts +1 -13
- package/src/tools/agent-harness-execution-posture.ts +0 -15
- package/src/tools/agent-harness-keybinding-metadata.ts +28 -29
- package/src/tools/agent-harness-local-model-cookbook.ts +24 -1
- package/src/tools/agent-harness-metadata.ts +16 -0
- package/src/tools/agent-harness-mode-catalog.ts +2 -9
- package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
- package/src/tools/agent-harness-setup-posture-utils.ts +1 -1
- package/src/tools/agent-harness-tool-schema.ts +13 -14
- package/src/tools/agent-harness-tool.ts +27 -23
- package/src/tools/agent-harness-ui-surface-metadata.ts +10 -20
- package/src/tools/agent-harness-workspace-action-runner.ts +3 -4
- package/src/tools/agent-workspace-tool.ts +2 -33
- package/src/utils/terminal-width.ts +9 -3
- package/src/version.ts +1 -1
- package/src/input/agent-workspace-panel-route.ts +0 -44
- package/src/input/panel-integration-actions.ts +0 -26
- package/src/panels/approval-panel.ts +0 -149
- package/src/panels/automation-control-panel.ts +0 -212
- package/src/panels/base-panel.ts +0 -254
- package/src/panels/builtin/agent.ts +0 -58
- package/src/panels/builtin/knowledge.ts +0 -26
- package/src/panels/builtin/operations.ts +0 -121
- package/src/panels/builtin/session.ts +0 -138
- package/src/panels/builtin/shared.ts +0 -275
- package/src/panels/builtin/usage.ts +0 -21
- package/src/panels/builtin-panels.ts +0 -23
- package/src/panels/confirm-state.ts +0 -61
- package/src/panels/context-visualizer-panel.ts +0 -204
- package/src/panels/cost-tracker-panel.ts +0 -444
- package/src/panels/docs-panel.ts +0 -285
- package/src/panels/index.ts +0 -25
- package/src/panels/knowledge-panel.ts +0 -417
- package/src/panels/memory-panel.ts +0 -226
- package/src/panels/panel-list-panel.ts +0 -464
- package/src/panels/panel-manager.ts +0 -570
- package/src/panels/provider-accounts-panel.ts +0 -233
- package/src/panels/provider-health-domains.ts +0 -208
- package/src/panels/provider-health-panel.ts +0 -720
- package/src/panels/provider-health-tracker.ts +0 -115
- package/src/panels/provider-stats-panel.ts +0 -366
- package/src/panels/qr-panel.ts +0 -207
- package/src/panels/schedule-panel.ts +0 -321
- package/src/panels/scrollable-list-panel.ts +0 -491
- package/src/panels/search-focus.ts +0 -32
- package/src/panels/security-panel.ts +0 -295
- package/src/panels/session-browser-panel.ts +0 -395
- package/src/panels/session-maintenance.ts +0 -125
- package/src/panels/subscription-panel.ts +0 -263
- package/src/panels/system-messages-panel.ts +0 -230
- package/src/panels/tasks-panel.ts +0 -344
- package/src/panels/thinking-panel.ts +0 -304
- package/src/panels/token-budget-panel.ts +0 -475
- package/src/panels/tool-inspector-panel.ts +0 -436
- package/src/panels/types.ts +0 -54
- package/src/renderer/panel-composite.ts +0 -158
- package/src/renderer/panel-tab-bar.ts +0 -69
- package/src/renderer/panel-workspace-bar.ts +0 -42
- package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
- package/src/tools/agent-harness-panel-metadata.ts +0 -211
- /package/src/runtime/perf/{panel-health-monitor.ts → component-health.ts} +0 -0
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// CostTrackerPanel — per-session / per-agent / per-plan cost estimates
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
import type { Line } from '../types/grid.ts';
|
|
6
|
-
import { createStyledCell, createEmptyLine } from '../types/grid.ts';
|
|
7
|
-
import { BasePanel } from './base-panel.ts';
|
|
8
|
-
import type { AgentEvent, TurnEvent } from '@/runtime/index.ts';
|
|
9
|
-
import type { UiEventFeed } from '../runtime/ui-events.ts';
|
|
10
|
-
import {
|
|
11
|
-
buildEmptyState,
|
|
12
|
-
buildPanelLine,
|
|
13
|
-
buildStyledPanelLine,
|
|
14
|
-
buildPanelWorkspace,
|
|
15
|
-
resolveScrollablePanelSection,
|
|
16
|
-
DEFAULT_PANEL_PALETTE,
|
|
17
|
-
type PanelWorkspaceSection,
|
|
18
|
-
} from './polish.ts';
|
|
19
|
-
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
// Pricing table (USD per 1M tokens)
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
interface ModelPricing {
|
|
25
|
-
input: number;
|
|
26
|
-
output: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const MODEL_PRICING: Record<string, ModelPricing> = {
|
|
30
|
-
// Free tier
|
|
31
|
-
'openrouter/free': { input: 0, output: 0 },
|
|
32
|
-
|
|
33
|
-
// InceptionLabs
|
|
34
|
-
'mercury-2': { input: 0.50, output: 1.50 },
|
|
35
|
-
'mercury-edit': { input: 0.50, output: 1.50 },
|
|
36
|
-
|
|
37
|
-
// OpenAI
|
|
38
|
-
'gpt-5.4': { input: 5, output: 15 },
|
|
39
|
-
'gpt-5.3-chat-latest': { input: 3, output: 10 },
|
|
40
|
-
'gpt-5-mini': { input: 0.15, output: 0.60 },
|
|
41
|
-
'gpt-5-nano': { input: 0.05, output: 0.20 },
|
|
42
|
-
'gpt-oss-120b': { input: 0, output: 0 },
|
|
43
|
-
|
|
44
|
-
// Anthropic (correct registry IDs)
|
|
45
|
-
'claude-opus-4-6': { input: 15, output: 75 },
|
|
46
|
-
'claude-sonnet-4-6': { input: 3, output: 15 },
|
|
47
|
-
'claude-haiku-4-5': { input: 0.80, output: 4 },
|
|
48
|
-
|
|
49
|
-
// Google
|
|
50
|
-
'gemini-3.1-pro': { input: 1.25, output: 5 },
|
|
51
|
-
'gemini-3-flash': { input: 0.075, output: 0.30 },
|
|
52
|
-
'gemini-3.1-flash-lite': { input: 0.02, output: 0.10 },
|
|
53
|
-
'gemini-2.5-pro': { input: 1.25, output: 5 },
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Look up pricing from the model catalog.
|
|
58
|
-
* Returns { input: 0, output: 0 } for free models and unknown models.
|
|
59
|
-
*/
|
|
60
|
-
function getCostFromCatalogForPanel(modelId: string): ModelPricing {
|
|
61
|
-
if (modelId.endsWith(':free')) return { input: 0, output: 0 };
|
|
62
|
-
return { input: 0, output: 0 };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function getPricing(modelId: string): ModelPricing {
|
|
66
|
-
// 2. Hardcoded table — exact match
|
|
67
|
-
if (MODEL_PRICING[modelId]) return MODEL_PRICING[modelId]!;
|
|
68
|
-
// 1. OpenRouter :free suffix — treat as free
|
|
69
|
-
if (modelId.endsWith(':free')) return { input: 0, output: 0 };
|
|
70
|
-
// 2. Prefix match (e.g. "openrouter/free:..." or "claude-sonnet-4-6-20..")
|
|
71
|
-
for (const [key, pricing] of Object.entries(MODEL_PRICING)) {
|
|
72
|
-
if (modelId.startsWith(key) || modelId.includes(key)) return pricing;
|
|
73
|
-
}
|
|
74
|
-
// 3. Unknown model — default to free-ish safe fallback
|
|
75
|
-
return getCostFromCatalogForPanel(modelId);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function calcCost(inputTokens: number, outputTokens: number, pricing: ModelPricing): number {
|
|
79
|
-
return (inputTokens * pricing.input + outputTokens * pricing.output) / 1_000_000;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function formatCost(usd: number): string {
|
|
83
|
-
if (usd === 0) return '$0.00';
|
|
84
|
-
if (usd < 0.0001) return '<$0.0001';
|
|
85
|
-
if (usd < 0.01) return `$${usd.toFixed(4)}`;
|
|
86
|
-
return `$${usd.toFixed(3)}`;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function formatTokens(n: number): string {
|
|
90
|
-
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
|
|
91
|
-
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`;
|
|
92
|
-
return String(n);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
// Internal types
|
|
97
|
-
// ---------------------------------------------------------------------------
|
|
98
|
-
|
|
99
|
-
interface UsageSnapshot {
|
|
100
|
-
input: number;
|
|
101
|
-
output: number;
|
|
102
|
-
cacheRead: number;
|
|
103
|
-
cacheWrite: number;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
interface AgentEntry {
|
|
107
|
-
id: string; // short form, e.g. first 8 chars
|
|
108
|
-
task: string;
|
|
109
|
-
model: string;
|
|
110
|
-
inputTokens: number;
|
|
111
|
-
outputTokens: number;
|
|
112
|
-
cost: number;
|
|
113
|
-
status: 'running' | 'done' | 'failed';
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Sparkline history (rolling last N cost-per-turn values)
|
|
117
|
-
const SPARKLINE_BARS = '._-:=+*#';
|
|
118
|
-
const SPARKLINE_LEN = 16;
|
|
119
|
-
|
|
120
|
-
function buildSparkline(history: number[]): string {
|
|
121
|
-
if (history.length === 0) return '';
|
|
122
|
-
const max = Math.max(...history);
|
|
123
|
-
if (max === 0) return '.'.repeat(history.length);
|
|
124
|
-
return history
|
|
125
|
-
.map(v => {
|
|
126
|
-
const idx = Math.round((v / max) * (SPARKLINE_BARS.length - 1));
|
|
127
|
-
return SPARKLINE_BARS[idx] ?? '.';
|
|
128
|
-
})
|
|
129
|
-
.join('');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ---------------------------------------------------------------------------
|
|
133
|
-
// Colour palette
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
|
|
136
|
-
const C = {
|
|
137
|
-
header: '#ffffff',
|
|
138
|
-
label: '#aaaaaa',
|
|
139
|
-
value: '#00ff88',
|
|
140
|
-
cost: '#ffdd44',
|
|
141
|
-
alert: '#ff4444',
|
|
142
|
-
dim: '#555555',
|
|
143
|
-
model: '#88aaff',
|
|
144
|
-
running: '#88aaff',
|
|
145
|
-
done: '#00ff88',
|
|
146
|
-
failed: '#ff4444',
|
|
147
|
-
separator: '#333333',
|
|
148
|
-
bg: '',
|
|
149
|
-
} as const;
|
|
150
|
-
|
|
151
|
-
// ---------------------------------------------------------------------------
|
|
152
|
-
// CostTrackerPanel
|
|
153
|
-
// ---------------------------------------------------------------------------
|
|
154
|
-
|
|
155
|
-
export class CostTrackerPanel extends BasePanel {
|
|
156
|
-
// Session-level usage (from main orchestrator)
|
|
157
|
-
private sessionUsage: UsageSnapshot = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
158
|
-
private sessionModel = 'unknown';
|
|
159
|
-
|
|
160
|
-
// Per-turn cost history for sparkline
|
|
161
|
-
private costHistory: number[] = [];
|
|
162
|
-
private lastSessionCost = 0;
|
|
163
|
-
|
|
164
|
-
// Per-agent tracking (keyed by agent id)
|
|
165
|
-
private agents = new Map<string, AgentEntry>();
|
|
166
|
-
|
|
167
|
-
// Budget alert threshold in USD (0 = disabled)
|
|
168
|
-
private budgetThreshold: number;
|
|
169
|
-
|
|
170
|
-
// Scroll offset for agent list
|
|
171
|
-
private scrollOffset = 0;
|
|
172
|
-
|
|
173
|
-
// Unsubscribe functions
|
|
174
|
-
private unsubs: Array<() => void> = [];
|
|
175
|
-
|
|
176
|
-
// Getter for live orchestrator usage
|
|
177
|
-
private readonly getOrchestratorUsage: () => UsageSnapshot & { model?: string };
|
|
178
|
-
|
|
179
|
-
constructor(
|
|
180
|
-
turnEvents: UiEventFeed<TurnEvent>,
|
|
181
|
-
agentEvents: UiEventFeed<AgentEvent>,
|
|
182
|
-
getOrchestratorUsage: () => UsageSnapshot & { model?: string },
|
|
183
|
-
opts: { budgetThreshold?: number } = {},
|
|
184
|
-
) {
|
|
185
|
-
super('cost', 'Cost', '$', 'monitoring');
|
|
186
|
-
this.getOrchestratorUsage = getOrchestratorUsage;
|
|
187
|
-
this.budgetThreshold = opts.budgetThreshold ?? 0;
|
|
188
|
-
this.attachEvents(turnEvents, agentEvents);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// -------------------------------------------------------------------------
|
|
192
|
-
// Bus wiring
|
|
193
|
-
// -------------------------------------------------------------------------
|
|
194
|
-
|
|
195
|
-
private attachEvents(turnEvents: UiEventFeed<TurnEvent>, agentEvents: UiEventFeed<AgentEvent>): void {
|
|
196
|
-
// Refresh after every completed turn
|
|
197
|
-
this.unsubs.push(
|
|
198
|
-
turnEvents.on('TURN_COMPLETED', () => this.onTurnComplete()),
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
// Track runtime worker events for cost attribution only.
|
|
202
|
-
this.unsubs.push(
|
|
203
|
-
agentEvents.on('AGENT_SPAWNING', (payload) => {
|
|
204
|
-
this.agents.set(payload.agentId, {
|
|
205
|
-
id: payload.agentId.slice(0, 8),
|
|
206
|
-
task: payload.task.length > 40 ? payload.task.slice(0, 37) + '...' : payload.task,
|
|
207
|
-
model: 'unknown',
|
|
208
|
-
inputTokens: 0,
|
|
209
|
-
outputTokens: 0,
|
|
210
|
-
cost: 0,
|
|
211
|
-
status: 'running',
|
|
212
|
-
});
|
|
213
|
-
this.markDirty();
|
|
214
|
-
}),
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
// Agent completed — capture token data from result if available
|
|
218
|
-
this.unsubs.push(
|
|
219
|
-
agentEvents.on('AGENT_COMPLETED', (payload) => {
|
|
220
|
-
const entry = this.agents.get(payload.agentId);
|
|
221
|
-
if (entry) {
|
|
222
|
-
entry.status = 'done';
|
|
223
|
-
this.markDirty();
|
|
224
|
-
}
|
|
225
|
-
}),
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
// Agent error
|
|
229
|
-
this.unsubs.push(
|
|
230
|
-
agentEvents.on('AGENT_FAILED', (payload) => {
|
|
231
|
-
const entry = this.agents.get(payload.agentId);
|
|
232
|
-
if (entry) {
|
|
233
|
-
entry.status = 'failed';
|
|
234
|
-
this.markDirty();
|
|
235
|
-
}
|
|
236
|
-
}),
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
private onTurnComplete(): void {
|
|
241
|
-
const usage = this.getOrchestratorUsage();
|
|
242
|
-
this.sessionUsage = {
|
|
243
|
-
input: usage.input,
|
|
244
|
-
output: usage.output,
|
|
245
|
-
cacheRead: usage.cacheRead,
|
|
246
|
-
cacheWrite: usage.cacheWrite,
|
|
247
|
-
};
|
|
248
|
-
if (usage.model) this.sessionModel = usage.model;
|
|
249
|
-
|
|
250
|
-
// Record cost delta for sparkline
|
|
251
|
-
const sessionProvider = this.sessionModel.includes('/') ? this.sessionModel.split('/')[0]! : '';
|
|
252
|
-
const pricing = getPricing(this.sessionModel);
|
|
253
|
-
const totalCost = calcCost(usage.input + usage.cacheRead + usage.cacheWrite, usage.output, pricing);
|
|
254
|
-
const delta = Math.max(0, totalCost - this.lastSessionCost);
|
|
255
|
-
this.lastSessionCost = totalCost;
|
|
256
|
-
this.costHistory.push(delta);
|
|
257
|
-
if (this.costHistory.length > SPARKLINE_LEN) {
|
|
258
|
-
this.costHistory.shift();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
this.markDirty();
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// -------------------------------------------------------------------------
|
|
265
|
-
// Lifecycle
|
|
266
|
-
// -------------------------------------------------------------------------
|
|
267
|
-
|
|
268
|
-
override onActivate(): void {
|
|
269
|
-
// Sync latest usage on activation
|
|
270
|
-
const usage = this.getOrchestratorUsage();
|
|
271
|
-
this.sessionUsage = {
|
|
272
|
-
input: usage.input,
|
|
273
|
-
output: usage.output,
|
|
274
|
-
cacheRead: usage.cacheRead,
|
|
275
|
-
cacheWrite: usage.cacheWrite,
|
|
276
|
-
};
|
|
277
|
-
this.needsRender = true;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
override onDestroy(): void {
|
|
281
|
-
for (const unsub of this.unsubs) unsub();
|
|
282
|
-
this.unsubs = [];
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// -------------------------------------------------------------------------
|
|
286
|
-
// Input
|
|
287
|
-
// -------------------------------------------------------------------------
|
|
288
|
-
|
|
289
|
-
handleInput(key: string): boolean {
|
|
290
|
-
switch (key) {
|
|
291
|
-
case 'up': return this.scroll(-1);
|
|
292
|
-
case 'down': return this.scroll(1);
|
|
293
|
-
case 'pageup': return this.scroll(-10);
|
|
294
|
-
case 'pagedown': return this.scroll(10);
|
|
295
|
-
default: return false;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
private scroll(delta: number): boolean {
|
|
300
|
-
const prev = this.scrollOffset;
|
|
301
|
-
this.scrollOffset = Math.max(0, this.scrollOffset + delta);
|
|
302
|
-
if (this.scrollOffset !== prev) this.markDirty();
|
|
303
|
-
return true;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// -------------------------------------------------------------------------
|
|
307
|
-
// Render
|
|
308
|
-
// -------------------------------------------------------------------------
|
|
309
|
-
|
|
310
|
-
render(width: number, height: number): Line[] {
|
|
311
|
-
if (height <= 0 || width <= 0) return [];
|
|
312
|
-
|
|
313
|
-
const sessionProvider = this.sessionModel.includes('/') ? this.sessionModel.split('/')[0]! : '';
|
|
314
|
-
const pricing = getPricing(this.sessionModel);
|
|
315
|
-
const totalInputTokens = this.sessionUsage.input + this.sessionUsage.cacheRead + this.sessionUsage.cacheWrite;
|
|
316
|
-
const sessionCost = calcCost(this.sessionUsage.input + this.sessionUsage.cacheRead + this.sessionUsage.cacheWrite, this.sessionUsage.output, pricing);
|
|
317
|
-
const overBudget = this.budgetThreshold > 0 && sessionCost > this.budgetThreshold;
|
|
318
|
-
const sparkline = buildSparkline(this.costHistory);
|
|
319
|
-
const costStr = formatCost(sessionCost);
|
|
320
|
-
const costFg = overBudget ? C.alert : C.cost;
|
|
321
|
-
const budgetStr = this.budgetThreshold > 0
|
|
322
|
-
? ` / ${formatCost(this.budgetThreshold)}`
|
|
323
|
-
: '';
|
|
324
|
-
const alertStr = overBudget ? ' ! OVER BUDGET' : '';
|
|
325
|
-
const sessionLines: Line[] = [
|
|
326
|
-
this.renderKeyValue(width, ' Total', `${costStr}${budgetStr}${alertStr}`, costFg),
|
|
327
|
-
];
|
|
328
|
-
if (sparkline.length > 0) sessionLines.push(this.renderLabeledLine(width, ' Trend', sparkline, C.value));
|
|
329
|
-
sessionLines.push(this.renderKeyValue(width, ' Input', formatTokens(this.sessionUsage.input), C.label));
|
|
330
|
-
sessionLines.push(this.renderKeyValue(width, ' Output', formatTokens(this.sessionUsage.output), C.label));
|
|
331
|
-
if (this.sessionUsage.cacheRead > 0 || this.sessionUsage.cacheWrite > 0) {
|
|
332
|
-
sessionLines.push(this.renderKeyValue(width, ' Cache W', formatTokens(this.sessionUsage.cacheWrite), C.dim));
|
|
333
|
-
sessionLines.push(this.renderKeyValue(width, ' Cache R', formatTokens(this.sessionUsage.cacheRead), C.dim));
|
|
334
|
-
}
|
|
335
|
-
sessionLines.push(this.renderKeyValue(width, ' Total T', formatTokens(totalInputTokens + this.sessionUsage.output), C.label));
|
|
336
|
-
sessionLines.push(this.renderKeyValue(width, ' Model', this.sessionModel, C.model));
|
|
337
|
-
|
|
338
|
-
const sections: PanelWorkspaceSection[] = [
|
|
339
|
-
{
|
|
340
|
-
title: 'Session Cost',
|
|
341
|
-
lines: sessionLines,
|
|
342
|
-
},
|
|
343
|
-
];
|
|
344
|
-
|
|
345
|
-
const agentList = Array.from(this.agents.values());
|
|
346
|
-
if (agentList.length > 0) {
|
|
347
|
-
const planCost = agentList.reduce((sum, a) => sum + a.cost, 0);
|
|
348
|
-
const agentRows: Line[] = [
|
|
349
|
-
this.renderKeyValue(width, ' Plan total', formatCost(planCost + sessionCost), C.cost),
|
|
350
|
-
this.renderDivider(width),
|
|
351
|
-
];
|
|
352
|
-
for (const agent of agentList) {
|
|
353
|
-
const statusFg = agent.status === 'running' ? C.running
|
|
354
|
-
: agent.status === 'failed' ? C.failed
|
|
355
|
-
: C.done;
|
|
356
|
-
const statusIcon = agent.status === 'running' ? '…'
|
|
357
|
-
: agent.status === 'failed' ? '✕'
|
|
358
|
-
: '✓';
|
|
359
|
-
|
|
360
|
-
const agentLabel = `${statusIcon} ${agent.id}`;
|
|
361
|
-
const taskText = agent.task;
|
|
362
|
-
agentRows.push(this.renderAgent(width, agentLabel, taskText, statusFg));
|
|
363
|
-
|
|
364
|
-
if (agent.inputTokens > 0 || agent.outputTokens > 0) {
|
|
365
|
-
const tokenInfo = ` in:${formatTokens(agent.inputTokens)} out:${formatTokens(agent.outputTokens)} ${formatCost(agent.cost)}`;
|
|
366
|
-
agentRows.push(this.renderLabeledLine(width, '', tokenInfo, C.dim));
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
const sessionSection: PanelWorkspaceSection = sections[0]!;
|
|
370
|
-
const agentsSection = resolveScrollablePanelSection(width, height, {
|
|
371
|
-
intro: 'Track per-session and per-agent token spend using model pricing and live usage snapshots.',
|
|
372
|
-
footerLines: [
|
|
373
|
-
buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' scroll agents', DEFAULT_PANEL_PALETTE.dim]]),
|
|
374
|
-
],
|
|
375
|
-
palette: DEFAULT_PANEL_PALETTE,
|
|
376
|
-
beforeSections: [sessionSection],
|
|
377
|
-
section: {
|
|
378
|
-
title: 'Agents',
|
|
379
|
-
scrollableLines: agentRows,
|
|
380
|
-
scrollOffset: this.scrollOffset,
|
|
381
|
-
minRows: 4,
|
|
382
|
-
},
|
|
383
|
-
});
|
|
384
|
-
this.scrollOffset = agentsSection.scrollOffset;
|
|
385
|
-
sections.push(agentsSection.section);
|
|
386
|
-
} else {
|
|
387
|
-
sections.push({
|
|
388
|
-
title: 'Agents',
|
|
389
|
-
lines: buildEmptyState(
|
|
390
|
-
width,
|
|
391
|
-
' No delegated agent cost records',
|
|
392
|
-
'Agent-level cost estimates appear only for explicit delegated build/review session records.',
|
|
393
|
-
[],
|
|
394
|
-
DEFAULT_PANEL_PALETTE,
|
|
395
|
-
),
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
return buildPanelWorkspace(width, height, {
|
|
400
|
-
title: ' Cost Tracker',
|
|
401
|
-
intro: 'Track per-session and per-agent token spend using model pricing and live usage snapshots.',
|
|
402
|
-
sections,
|
|
403
|
-
footerLines: [
|
|
404
|
-
buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' scroll agents', DEFAULT_PANEL_PALETTE.dim]]),
|
|
405
|
-
],
|
|
406
|
-
palette: DEFAULT_PANEL_PALETTE,
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// -------------------------------------------------------------------------
|
|
411
|
-
// Render helpers
|
|
412
|
-
// -------------------------------------------------------------------------
|
|
413
|
-
|
|
414
|
-
private renderKeyValue(width: number, label: string, value: string, valueFg: string): Line {
|
|
415
|
-
const LABEL_W = 10;
|
|
416
|
-
return buildStyledPanelLine(width, [
|
|
417
|
-
{ text: label.padEnd(LABEL_W).slice(0, LABEL_W), fg: C.label, bg: C.bg },
|
|
418
|
-
{ text: ': ', fg: C.dim, bg: C.bg },
|
|
419
|
-
{ text: value, fg: valueFg, bg: C.bg, bold: true },
|
|
420
|
-
]);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
private renderLabeledLine(width: number, label: string, value: string, valueFg: string): Line {
|
|
424
|
-
return buildStyledPanelLine(width, [
|
|
425
|
-
...(label.length > 0 ? [{ text: `${label.slice(0, 10).padEnd(10)} `, fg: C.label }] : []),
|
|
426
|
-
{ text: value, fg: valueFg },
|
|
427
|
-
]);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
private renderAgent(width: number, label: string, task: string, fg: string): Line {
|
|
431
|
-
const LABEL_W = 12;
|
|
432
|
-
const remaining = width - LABEL_W - 1;
|
|
433
|
-
const trimmed = task.length > remaining ? task.slice(0, remaining - 3) + '...' : task;
|
|
434
|
-
return buildStyledPanelLine(width, [
|
|
435
|
-
{ text: `${label.padEnd(LABEL_W).slice(0, LABEL_W)} `, fg, bold: true },
|
|
436
|
-
{ text: trimmed, fg: C.label },
|
|
437
|
-
]);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
private renderDivider(width: number): Line {
|
|
441
|
-
return buildStyledPanelLine(width, [{ text: '─'.repeat(width), fg: C.separator }]);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
}
|