@pellux/goodvibes-tui 1.16.1 → 1.18.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 +138 -9
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +5490 -1806
- package/package.json +7 -7
- package/src/cli/completions/generate.ts +5 -4
- package/src/cli/config-overrides.ts +51 -11
- package/src/cli/doctor.ts +4 -5
- package/src/cli/entrypoint.ts +13 -8
- package/src/cli/help.ts +7 -6
- package/src/cli/launch-auto-update.ts +214 -0
- package/src/cli/management-commands.ts +82 -15
- package/src/cli/management.ts +0 -2
- package/src/cli/parser.ts +5 -0
- package/src/cli/status.ts +13 -0
- package/src/cli/surface-command.ts +5 -5
- package/src/cli/tui-startup.ts +60 -97
- package/src/cli/types.ts +2 -0
- package/src/config/index.ts +1 -1
- package/src/config/tui-extension-settings.ts +33 -0
- package/src/core/composer-state.ts +1 -1
- package/src/core/consolidation-receipt.ts +41 -0
- package/src/core/conversation-line-cache.ts +3 -3
- package/src/core/conversation-rendering.ts +18 -9
- package/src/core/conversation.ts +2 -2
- package/src/core/long-task-notifier.ts +7 -7
- package/src/core/memory-provenance.ts +67 -0
- package/src/core/pairing-devices.ts +49 -0
- package/src/core/pairing-handoff.ts +101 -0
- package/src/core/pairing-offers.ts +83 -0
- package/src/core/pairing-origin.ts +78 -0
- package/src/core/pairing-tailscale-gateway.ts +56 -0
- package/src/core/power-chip-source.ts +91 -0
- package/src/core/power-status.ts +95 -0
- package/src/core/stable-host.ts +171 -0
- package/src/core/stream-event-wiring.ts +29 -6
- package/src/core/system-message-noise.ts +1 -1
- package/src/core/system-message-router.ts +6 -6
- package/src/core/turn-budget-outcome.ts +60 -0
- package/src/core/turn-cancellation.ts +40 -1
- package/src/core/turn-event-wiring.ts +14 -8
- package/src/daemon/cli.ts +64 -58
- package/src/daemon/lifecycle.ts +41 -0
- package/src/daemon/service-commands.ts +5 -5
- package/src/export/cost-utils.ts +128 -6
- package/src/input/autocomplete.ts +2 -2
- package/src/input/command-registry.ts +55 -9
- package/src/input/commands/acp-runtime.ts +72 -0
- package/src/input/commands/ci-runtime.ts +5 -1
- package/src/input/commands/codebase-runtime.ts +6 -6
- package/src/input/commands/cost-runtime.ts +1 -1
- package/src/input/commands/devices-runtime.ts +109 -0
- package/src/input/commands/git-runtime.ts +28 -4
- package/src/input/commands/graph-runtime.ts +45 -0
- package/src/input/commands/health-runtime.ts +2 -2
- package/src/input/commands/intelligence-runtime.ts +1 -1
- package/src/input/commands/local-auth-runtime.ts +1 -1
- package/src/input/commands/local-provider-runtime.ts +49 -10
- package/src/input/commands/local-runtime.ts +53 -5
- package/src/input/commands/memory.ts +2 -2
- package/src/input/commands/operator-panel-runtime.ts +5 -5
- package/src/input/commands/operator-rpc.ts +32 -11
- package/src/input/commands/permissions-provenance.ts +1 -1
- package/src/input/commands/permissions-runtime.ts +99 -8
- package/src/input/commands/planning-runtime.ts +3 -3
- package/src/input/commands/platform-sandbox-runtime.ts +1 -1
- package/src/input/commands/power-runtime.ts +56 -0
- package/src/input/commands/provider-accounts-runtime.ts +71 -6
- package/src/input/commands/provider.ts +7 -5
- package/src/input/commands/qrcode-runtime.ts +14 -19
- package/src/input/commands/queue-runtime.ts +70 -0
- package/src/input/commands/recall-files-git.ts +13 -51
- package/src/input/commands/recall-files.ts +8 -11
- package/src/input/commands/recall-review.ts +2 -2
- package/src/input/commands/relay-runtime.ts +1 -1
- package/src/input/commands/remote-runtime.ts +1 -1
- package/src/input/commands/runtime-services.ts +4 -4
- package/src/input/commands/schedule-runtime.ts +14 -7
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-workflow.ts +2 -2
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/share-runtime.ts +1 -1
- package/src/input/commands/shell-core.ts +46 -68
- package/src/input/commands/subscription-runtime.ts +1 -1
- package/src/input/commands/update-runtime.ts +153 -96
- package/src/input/commands/work-plan-runtime.ts +1 -1
- package/src/input/commands/workstream-attempts.ts +6 -1
- package/src/input/commands/workstream-runtime.ts +46 -27
- package/src/input/commands/worktree-runtime.ts +35 -8
- package/src/input/commands.ts +10 -2
- package/src/input/config-modal-types.ts +14 -3
- package/src/input/config-modal.ts +75 -13
- package/src/input/feature-unit-layout.ts +93 -82
- package/src/input/feed-context-factory.ts +2 -2
- package/src/input/handler-command-route.ts +2 -2
- package/src/input/handler-content-actions.ts +1 -1
- package/src/input/handler-feed-routes.ts +7 -7
- package/src/input/handler-feed.ts +4 -4
- package/src/input/handler-interactions.ts +3 -2
- package/src/input/handler-modal-routes.ts +12 -4
- package/src/input/handler-modal-token-routes.ts +1 -1
- package/src/input/handler-onboarding-daemon-adopt.ts +2 -2
- package/src/input/handler-onboarding.ts +3 -2
- package/src/input/handler-picker-routes.ts +26 -13
- package/src/input/handler-shortcuts.ts +29 -6
- package/src/input/handler.ts +2 -2
- package/src/input/keybindings.ts +21 -3
- package/src/input/onboarding/onboarding-feature-units.ts +34 -27
- package/src/input/onboarding/onboarding-provider-key-fields.ts +112 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +22 -21
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +17 -0
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +2 -2
- package/src/input/onboarding/onboarding-wizard-steps.ts +30 -42
- package/src/input/panel-integration-actions.ts +2 -2
- package/src/input/panel-mouse-geometry.ts +1 -1
- package/src/input/panel-paste-flood-guard.ts +1 -1
- package/src/input/provider-key-intake.ts +167 -0
- package/src/input/sandbox-exec-config.ts +2 -2
- package/src/input/selection-modal.ts +1 -1
- package/src/input/settings-modal-activation.ts +5 -0
- package/src/input/settings-modal-adjustment.ts +4 -31
- package/src/input/settings-modal-data.ts +52 -57
- package/src/input/settings-modal-mutations.ts +12 -68
- package/src/input/settings-modal-types.ts +27 -17
- package/src/input/settings-modal.ts +74 -55
- package/src/main.ts +90 -90
- package/src/panels/agent-inspector-shared.ts +4 -4
- package/src/panels/base-panel.ts +2 -2
- package/src/panels/builtin/agent.ts +4 -4
- package/src/panels/builtin/development.ts +4 -4
- package/src/panels/builtin/knowledge.ts +1 -1
- package/src/panels/builtin/operations.ts +83 -11
- package/src/panels/builtin/session.ts +5 -5
- package/src/panels/builtin/shared.ts +16 -4
- package/src/panels/builtin-modals.ts +59 -42
- package/src/panels/builtin-panels.ts +1 -1
- package/src/panels/cost-tracker-panel.ts +124 -7
- package/src/panels/diff-panel.ts +39 -6
- package/src/panels/eval-registry.ts +2 -2
- package/src/panels/fleet-acts.ts +446 -0
- package/src/panels/fleet-deep-link.ts +1 -1
- package/src/panels/fleet-gateway.ts +117 -0
- package/src/panels/fleet-observed-render.ts +128 -0
- package/src/panels/fleet-panel-format.ts +163 -11
- package/src/panels/fleet-panel-worktree-detail.ts +14 -0
- package/src/panels/fleet-panel.ts +73 -57
- package/src/panels/fleet-read-model.ts +84 -14
- package/src/panels/fleet-spawn.ts +266 -0
- package/src/panels/fleet-steer.ts +2 -2
- package/src/panels/fleet-stop.ts +41 -5
- package/src/panels/fleet-tabs.ts +15 -16
- package/src/panels/fleet-transcript.ts +5 -5
- package/src/panels/git-panel.ts +2 -2
- package/src/panels/index.ts +4 -1
- package/src/panels/local-auth-panel.ts +1 -1
- package/src/panels/memory-consolidation-gateway.ts +94 -0
- package/src/panels/modals/devices-modal.ts +161 -0
- package/src/panels/modals/hooks-modal.ts +1 -1
- package/src/panels/modals/keybindings-modal.ts +2 -2
- package/src/panels/modals/knowledge-modal.ts +1 -1
- package/src/panels/modals/marketplace-modal.ts +4 -4
- package/src/panels/modals/memory-modal.ts +153 -9
- package/src/panels/modals/modal-surface-helpers.ts +1 -1
- package/src/panels/modals/modal-theme.ts +1 -1
- package/src/panels/modals/pairing-modal.ts +198 -68
- package/src/panels/modals/planning-modal.ts +2 -2
- package/src/panels/modals/plugins-modal.ts +1 -1
- package/src/panels/modals/policy-modal.ts +1 -1
- package/src/panels/modals/security-modal.ts +7 -1
- package/src/panels/modals/skills-modal.ts +1 -1
- package/src/panels/modals/work-plan-modal.ts +3 -3
- package/src/panels/notifications-feed.ts +127 -0
- package/src/panels/notifications-panel.ts +160 -0
- package/src/panels/panel-manager.ts +9 -9
- package/src/panels/plugins-panel.ts +4 -4
- package/src/panels/project-planning-answer-actions.ts +1 -1
- package/src/panels/provider-health-routes.ts +2 -2
- package/src/panels/skills-panel.ts +4 -5
- package/src/panels/token-budget-panel.ts +11 -3
- package/src/panels/types.ts +5 -5
- package/src/panels/workstream-graph-render.ts +123 -0
- package/src/permissions/broker-approval-card.ts +201 -0
- package/src/permissions/hunk-selection.ts +28 -4
- package/src/permissions/prompt.ts +244 -29
- package/src/renderer/autocomplete-overlay.ts +107 -54
- package/src/renderer/code-block.ts +2 -2
- package/src/renderer/compaction-history-modal.ts +1 -1
- package/src/renderer/compaction-preview.ts +1 -1
- package/src/renderer/compaction-quality.ts +1 -1
- package/src/renderer/composer-fragments.ts +69 -0
- package/src/renderer/compositor.ts +2 -2
- package/src/renderer/config-modal.ts +1 -1
- package/src/renderer/conversation-overlays.ts +1 -1
- package/src/renderer/footer-tips.ts +3 -3
- package/src/renderer/hint-grammar.ts +1 -1
- package/src/renderer/modal-factory.ts +1 -1
- package/src/renderer/model-picker-overlay.ts +1 -1
- package/src/renderer/model-workspace.ts +7 -2
- package/src/renderer/panel-composite.ts +2 -2
- package/src/renderer/panel-workspace-bar.ts +1 -1
- package/src/renderer/process-indicator.ts +1 -1
- package/src/renderer/selection-modal-overlay.ts +1 -1
- package/src/renderer/session-picker-modal.ts +2 -2
- package/src/renderer/settings-modal-helpers.ts +23 -5
- package/src/renderer/settings-modal.ts +129 -39
- package/src/renderer/shell-surface.ts +6 -0
- package/src/renderer/terminal-bg-probe.ts +1 -1
- package/src/renderer/theme.ts +3 -3
- package/src/renderer/tool-call.ts +13 -7
- package/src/renderer/tool-result-summary.ts +1 -1
- package/src/renderer/turn-injection.ts +7 -6
- package/src/renderer/ui-factory.ts +38 -1
- package/src/runtime/bootstrap-command-context.ts +7 -2
- package/src/runtime/bootstrap-command-parts.ts +9 -5
- package/src/runtime/bootstrap-core.ts +41 -41
- package/src/runtime/bootstrap-hook-bridge.ts +1 -1
- package/src/runtime/bootstrap-shell.ts +13 -5
- package/src/runtime/bootstrap.ts +34 -32
- package/src/runtime/channel-composition.ts +46 -0
- package/src/runtime/code-index-services.ts +3 -4
- package/src/runtime/daemon-attach-notices.ts +120 -0
- package/src/runtime/durability-services.ts +90 -0
- package/src/runtime/embedded-service-factories.ts +40 -0
- package/src/runtime/feature-settings.ts +147 -0
- package/src/runtime/fleet-services.ts +41 -0
- package/src/runtime/idle-power-services.ts +85 -0
- package/src/runtime/index.ts +6 -21
- package/src/runtime/interaction-seams.ts +109 -0
- package/src/runtime/legacy-daemon-migration.ts +29 -19
- package/src/runtime/memory-fold.ts +1 -1
- package/src/runtime/notification-dispatch.ts +110 -0
- package/src/runtime/onboarding/apply.ts +3 -29
- package/src/runtime/onboarding/fast-path.ts +39 -6
- package/src/runtime/onboarding/provider-key-capture.ts +126 -0
- package/src/runtime/onboarding/snapshot.ts +0 -1
- package/src/runtime/onboarding/types.ts +9 -3
- package/src/runtime/power-keepawake-remote.ts +75 -0
- package/src/runtime/process-lifecycle.ts +3 -1
- package/src/runtime/provider-fallback.ts +55 -0
- package/src/runtime/provider-optimizer-wiring.ts +55 -0
- package/src/runtime/release-artifacts.ts +19 -103
- package/src/runtime/resume-notice.ts +1 -1
- package/src/runtime/services.ts +113 -113
- package/src/runtime/session-continuity-hints.ts +38 -0
- package/src/runtime/surface-feature-flags.ts +40 -9
- package/src/runtime/terminal-output-guard.ts +2 -2
- package/src/runtime/trust/workspace-registration.ts +10 -2
- package/src/runtime/trust/workspace-trust.ts +58 -31
- package/src/runtime/ui-services.ts +6 -1
- package/src/runtime/update-check.ts +15 -69
- package/src/runtime/workstream-services.ts +2 -2
- package/src/shell/blocking-input.ts +108 -132
- package/src/shell/recovery-input-helpers.ts +53 -166
- package/src/shell/ui-openers.ts +38 -6
- package/src/utils/format-duration.ts +1 -1
- package/src/verification/verification-ledger.ts +21 -4
- package/src/version.ts +1 -1
- package/src/input/commands/flags-runtime.ts +0 -328
|
@@ -22,15 +22,15 @@ import {
|
|
|
22
22
|
DEFAULT_PANEL_PALETTE,
|
|
23
23
|
type PanelWorkspaceSection,
|
|
24
24
|
} from './polish.ts';
|
|
25
|
-
import { calcSessionCost, isModelPriced, computeBudgetBreach, readBudgetAlertUsd, BUDGET_ALERT_USD_CONFIG_KEY, type BudgetAlertConfigAccess } from '../export/cost-utils.ts';
|
|
25
|
+
import { calcSessionCost, describePricingSource, isModelPriced, computeBudgetBreach, readBudgetAlertUsd, writeManualModelPrice, BUDGET_ALERT_USD_CONFIG_KEY, type BudgetAlertConfigAccess } from '../export/cost-utils.ts';
|
|
26
26
|
import { abbreviateCount } from '../utils/format-number.ts';
|
|
27
27
|
import { isTextBackspace } from '../input/delete-key-policy.ts';
|
|
28
28
|
|
|
29
29
|
// Pricing lookups are provided by ../export/cost-utils.ts (single source of truth).
|
|
30
30
|
|
|
31
|
-
/** unpriced=true renders
|
|
31
|
+
/** unpriced=true renders the explicit "price unknown" marker instead of a $0.00 that could be mistaken for a real zero cost. */
|
|
32
32
|
function formatCost(usd: number, unpriced = false): string {
|
|
33
|
-
if (unpriced) return '
|
|
33
|
+
if (unpriced) return 'price unknown';
|
|
34
34
|
if (usd === 0) return '$0.00';
|
|
35
35
|
if (usd < 0.0001) return '<$0.0001';
|
|
36
36
|
if (usd < 0.01) return `$${usd.toFixed(4)}`;
|
|
@@ -127,6 +127,12 @@ export class CostTrackerPanel extends BasePanel {
|
|
|
127
127
|
// secret so it is echoed directly.
|
|
128
128
|
private budgetEntry: string | null = null;
|
|
129
129
|
|
|
130
|
+
// Draft buffer for the in-panel manual-price entry mode ('p' key): the one
|
|
131
|
+
// action away from any price display (and from the "price unknown" marker).
|
|
132
|
+
// Format: "input,output" in USD per 1M tokens; persists to the
|
|
133
|
+
// pricing.modelPrices settings key live on Enter.
|
|
134
|
+
private priceEntry: string | null = null;
|
|
135
|
+
|
|
130
136
|
// Scroll offset for agent list
|
|
131
137
|
private scrollOffset = 0;
|
|
132
138
|
|
|
@@ -347,11 +353,12 @@ export class CostTrackerPanel extends BasePanel {
|
|
|
347
353
|
* `Panel.isCapturingTextBurst`.
|
|
348
354
|
*/
|
|
349
355
|
isCapturingTextBurst(): boolean {
|
|
350
|
-
return this.budgetEntry !== null;
|
|
356
|
+
return this.budgetEntry !== null || this.priceEntry !== null;
|
|
351
357
|
}
|
|
352
358
|
|
|
353
359
|
handleInput(key: string): boolean {
|
|
354
360
|
if (this.budgetEntry !== null) return this.handleBudgetEntryInput(key);
|
|
361
|
+
if (this.priceEntry !== null) return this.handlePriceEntryInput(key);
|
|
355
362
|
|
|
356
363
|
if (key === 'b') {
|
|
357
364
|
const threshold = this.currentBudgetThreshold();
|
|
@@ -360,6 +367,16 @@ export class CostTrackerPanel extends BasePanel {
|
|
|
360
367
|
return true;
|
|
361
368
|
}
|
|
362
369
|
|
|
370
|
+
if (key === 'p') {
|
|
371
|
+
// One action from any price display: edit the manual price for the
|
|
372
|
+
// session's provider:model. Needs a wired config (production path) —
|
|
373
|
+
// without one the key falls through rather than opening a dead editor.
|
|
374
|
+
if (!this.configAccess) return false;
|
|
375
|
+
this.priceEntry = '';
|
|
376
|
+
this.markDirty();
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
|
|
363
380
|
switch (key) {
|
|
364
381
|
case 'up': return this.scroll(-1);
|
|
365
382
|
case 'down': return this.scroll(1);
|
|
@@ -402,6 +419,52 @@ export class CostTrackerPanel extends BasePanel {
|
|
|
402
419
|
return true; // absorb everything else while entry is active
|
|
403
420
|
}
|
|
404
421
|
|
|
422
|
+
/** True when the session model can carry a manual price (pricing.modelPrices keys are `provider:model`). */
|
|
423
|
+
private canEditSessionModelPrice(): boolean {
|
|
424
|
+
return this.sessionModel !== 'unknown' && this.sessionModel.includes(':');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private handlePriceEntryInput(key: string): boolean {
|
|
428
|
+
if (key === 'escape') {
|
|
429
|
+
this.priceEntry = null;
|
|
430
|
+
this.markDirty();
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
if (key === 'enter' || key === 'return') {
|
|
434
|
+
const raw = (this.priceEntry ?? '').trim();
|
|
435
|
+
if (!this.canEditSessionModelPrice()) {
|
|
436
|
+
// The prompt already explains why; Enter just closes it.
|
|
437
|
+
this.priceEntry = null;
|
|
438
|
+
this.markDirty();
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
const parts = raw.split(/[\s,]+/).filter((part) => part.length > 0);
|
|
442
|
+
const input = Number(parts[0]);
|
|
443
|
+
const output = Number(parts[1]);
|
|
444
|
+
if (parts.length === 2 && Number.isFinite(input) && input >= 0 && Number.isFinite(output) && output >= 0) {
|
|
445
|
+
writeManualModelPrice(this.configAccess!, this.sessionModel, { input, output });
|
|
446
|
+
this.priceEntry = null;
|
|
447
|
+
}
|
|
448
|
+
// Malformed input keeps the editor open so the draft can be corrected.
|
|
449
|
+
this.markDirty();
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
452
|
+
if (isTextBackspace(key)) {
|
|
453
|
+
if (this.priceEntry && this.priceEntry.length > 0) {
|
|
454
|
+
this.priceEntry = this.priceEntry.slice(0, -1);
|
|
455
|
+
this.markDirty();
|
|
456
|
+
}
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
// Two USD amounts separated by a comma (or space) — digits, dots, comma, space.
|
|
460
|
+
if (key.length === 1 && /[0-9., ]/.test(key)) {
|
|
461
|
+
this.priceEntry = (this.priceEntry ?? '') + key;
|
|
462
|
+
this.markDirty();
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
return true; // absorb everything else while entry is active
|
|
466
|
+
}
|
|
467
|
+
|
|
405
468
|
/**
|
|
406
469
|
* Scroll the agent list. Over-consumption fix: only absorbs the key (and
|
|
407
470
|
* only advances) when the agent list is actually long enough to scroll —
|
|
@@ -427,20 +490,29 @@ export class CostTrackerPanel extends BasePanel {
|
|
|
427
490
|
render(width: number, height: number): Line[] {
|
|
428
491
|
if (height <= 0 || width <= 0) return [];
|
|
429
492
|
if (this.budgetEntry !== null) return this.renderBudgetEntryPrompt(width, height);
|
|
493
|
+
if (this.priceEntry !== null) return this.renderPriceEntryPrompt(width, height);
|
|
430
494
|
|
|
431
495
|
const totalInputTokens = this.sessionUsage.input + this.sessionUsage.cacheRead + this.sessionUsage.cacheWrite;
|
|
432
496
|
const sessionCost = calcSessionCost(this.sessionUsage.input, this.sessionUsage.output, this.sessionUsage.cacheRead, this.sessionUsage.cacheWrite, this.sessionModel);
|
|
433
497
|
const budgetThreshold = this.currentBudgetThreshold();
|
|
434
498
|
const overBudget = computeBudgetBreach(sessionCost, budgetThreshold);
|
|
435
499
|
const sparkline = buildSparkline(this.costHistory);
|
|
436
|
-
const
|
|
500
|
+
const sessionPriced = isModelPriced(this.sessionModel);
|
|
501
|
+
const costStr = formatCost(sessionCost, !sessionPriced);
|
|
437
502
|
const costFg = overBudget ? C.bad : C.cost;
|
|
503
|
+
// Dollars carry their source: "your price" for a manual entry,
|
|
504
|
+
// "catalog price, as of <date>" for the dated catalog, etc. The
|
|
505
|
+
// "price unknown" marker carries the one-key action to fix it instead.
|
|
506
|
+
const sourceDescription = sessionPriced ? describePricingSource(this.sessionModel) : null;
|
|
507
|
+
const sourceStr = sessionPriced
|
|
508
|
+
? (sourceDescription ? ` (${sourceDescription})` : '')
|
|
509
|
+
: ' — press p to set a price';
|
|
438
510
|
const budgetStr = budgetThreshold > 0
|
|
439
511
|
? ` / ${formatCost(budgetThreshold)}`
|
|
440
512
|
: '';
|
|
441
513
|
const alertStr = overBudget ? ' ! OVER BUDGET' : '';
|
|
442
514
|
const sessionLines: Line[] = [
|
|
443
|
-
this.renderKeyValue(width, ' Total', `${costStr}${budgetStr}${alertStr}`, costFg),
|
|
515
|
+
this.renderKeyValue(width, ' Total', `${costStr}${budgetStr}${alertStr}${sourceStr}`, costFg),
|
|
444
516
|
];
|
|
445
517
|
// Budget meter — the single most important glance for this panel: how much
|
|
446
518
|
// of the configured budget the session has consumed. Only shown when a
|
|
@@ -455,6 +527,13 @@ export class CostTrackerPanel extends BasePanel {
|
|
|
455
527
|
{ filled: meterFg, empty: C.separator, label: C.label },
|
|
456
528
|
{ prefix: ' Budget [', suffix: `] ${pctStr}` },
|
|
457
529
|
));
|
|
530
|
+
// Unpriced-spend honesty wherever budget state shows: a meter over a
|
|
531
|
+
// partially-unpriced session must say the % undercounts real spend.
|
|
532
|
+
const anyUnpricedSpend = (!sessionPriced && (totalInputTokens > 0 || this.sessionUsage.output > 0))
|
|
533
|
+
|| Array.from(this.agents.values()).some((agent) => agent.inputTokens > 0 && !isModelPriced(agent.model));
|
|
534
|
+
if (anyUnpricedSpend) {
|
|
535
|
+
sessionLines.push(this.renderKeyValue(width, '', 'some spend has no known price and is not counted above', C.warn));
|
|
536
|
+
}
|
|
458
537
|
}
|
|
459
538
|
if (sparkline.length > 0) sessionLines.push(this.renderLabeledLine(width, ' Trend', sparkline, C.good));
|
|
460
539
|
sessionLines.push(this.renderKeyValue(width, ' Input', formatTokens(this.sessionUsage.input), C.label));
|
|
@@ -481,9 +560,11 @@ export class CostTrackerPanel extends BasePanel {
|
|
|
481
560
|
{ keys: 'Up/Down', label: 'scroll agents' },
|
|
482
561
|
{ keys: 'PgUp/PgDn', label: 'page' },
|
|
483
562
|
{ keys: 'b', label: 'set budget' },
|
|
563
|
+
{ keys: 'p', label: 'set model price' },
|
|
484
564
|
], DEFAULT_PANEL_PALETTE)
|
|
485
565
|
: buildKeyboardHints(width, [
|
|
486
566
|
{ keys: 'b', label: 'set budget alert' },
|
|
567
|
+
{ keys: 'p', label: 'set model price' },
|
|
487
568
|
], DEFAULT_PANEL_PALETTE);
|
|
488
569
|
if (agentList.length > 0) {
|
|
489
570
|
const planCost = agentList.reduce((sum, a) => sum + a.cost, 0);
|
|
@@ -562,7 +643,7 @@ export class CostTrackerPanel extends BasePanel {
|
|
|
562
643
|
width,
|
|
563
644
|
' No agents spawned this session',
|
|
564
645
|
'Agent-level cost estimates appear here once delegated or background agents start running.',
|
|
565
|
-
//
|
|
646
|
+
// 'b' already opens the in-panel budget-entry field (see
|
|
566
647
|
// handleInput/handleBudgetEntryInput above) and is advertised in
|
|
567
648
|
// the footer's 'b: set budget' hint, so the printed
|
|
568
649
|
// '/cost budget <usd>' command was a redundant action substitute.
|
|
@@ -626,4 +707,40 @@ export class CostTrackerPanel extends BasePanel {
|
|
|
626
707
|
return workspace.slice(0, height);
|
|
627
708
|
}
|
|
628
709
|
|
|
710
|
+
// Non-masked entry for the 'p' manual-price key. Two USD-per-1M-token
|
|
711
|
+
// amounts (input,output); persists to pricing.modelPrices for the session's
|
|
712
|
+
// provider:model live. A model without a provider prefix cannot carry a
|
|
713
|
+
// manual price (the settings key is keyed provider:model) — the prompt says
|
|
714
|
+
// so instead of accepting a value it could not store.
|
|
715
|
+
private renderPriceEntryPrompt(width: number, height: number): Line[] {
|
|
716
|
+
const editable = this.canEditSessionModelPrice();
|
|
717
|
+
const draft = this.priceEntry ?? '';
|
|
718
|
+
const display = `${draft}█`;
|
|
719
|
+
const promptLines: Line[] = editable
|
|
720
|
+
? [
|
|
721
|
+
buildPanelLine(width, [[` Set your manual price for ${this.sessionModel} (USD per 1M tokens).`, C.label]]),
|
|
722
|
+
buildPanelLine(width, [[' Format: input,output — for example 3.00,15.00', C.dim]]),
|
|
723
|
+
buildPanelLine(width, [['', C.label]]),
|
|
724
|
+
buildPanelLine(width, [[' Price ', C.label], [display, C.cost]]),
|
|
725
|
+
buildPanelLine(width, [['', C.label]]),
|
|
726
|
+
buildPanelLine(width, [[' [Enter] Save [Esc] Cancel [Backspace] Delete char', C.dim]]),
|
|
727
|
+
]
|
|
728
|
+
: [
|
|
729
|
+
buildPanelLine(width, [[` The current model (${this.sessionModel}) has no provider prefix, so a`, C.label]]),
|
|
730
|
+
buildPanelLine(width, [[' manual price cannot be stored for it (prices are keyed provider:model).', C.label]]),
|
|
731
|
+
buildPanelLine(width, [['', C.label]]),
|
|
732
|
+
buildPanelLine(width, [[' [Esc] Close', C.dim]]),
|
|
733
|
+
];
|
|
734
|
+
const workspace = buildPanelWorkspace(width, height, {
|
|
735
|
+
title: ' Cost Tracker — Model Price',
|
|
736
|
+
intro: editable
|
|
737
|
+
? 'Your price wins over provider and catalog prices, immediately and everywhere.'
|
|
738
|
+
: 'Manual prices are stored per provider:model.',
|
|
739
|
+
sections: [{ lines: promptLines }],
|
|
740
|
+
palette: DEFAULT_PANEL_PALETTE,
|
|
741
|
+
});
|
|
742
|
+
while (workspace.length < height) workspace.push(createEmptyLine(width));
|
|
743
|
+
return workspace.slice(0, height);
|
|
744
|
+
}
|
|
745
|
+
|
|
629
746
|
}
|
package/src/panels/diff-panel.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type { Line } from '../types/grid.ts';
|
|
6
6
|
import { createStyledCell, createEmptyLine } from '../types/grid.ts';
|
|
7
7
|
import { truncateDisplay, getDisplayWidth } from '../utils/terminal-width.ts';
|
|
8
|
-
import { GitService } from '@pellux/goodvibes-sdk/platform/git';
|
|
8
|
+
import { GitService, type StructuredDiff, type StructuredDiffFile } from '@pellux/goodvibes-sdk/platform/git';
|
|
9
9
|
import { BasePanel } from './base-panel.ts';
|
|
10
10
|
import { UI_TONES, DIFF_TONES } from '../renderer/ui-primitives.ts';
|
|
11
11
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
@@ -27,10 +27,10 @@ import {
|
|
|
27
27
|
// that shares it, including the workspace-chrome aliases (info/dim/value/
|
|
28
28
|
// empty) so the raw-hex count never grows. The title band itself is NOT
|
|
29
29
|
// overridden — buildPanelWorkspace always falls back to the canonical
|
|
30
|
-
// DEFAULT_PANEL_PALETTE.headerBg (
|
|
30
|
+
// DEFAULT_PANEL_PALETTE.headerBg (one title band everywhere).
|
|
31
31
|
// ---------------------------------------------------------------------------
|
|
32
32
|
|
|
33
|
-
// Hunk blue is the shared DIFF_TONES token
|
|
33
|
+
// Hunk blue is the shared DIFF_TONES token — diff-view.ts (conversation)
|
|
34
34
|
// and git-panel.ts's inline diff converge onto this file's pre-existing value.
|
|
35
35
|
const HUNK_BLUE: string = DIFF_TONES.hunk;
|
|
36
36
|
// Context rows and line-number gutter use the shared theme's muted/dim
|
|
@@ -39,7 +39,7 @@ const CONTEXT_GRAY = UI_TONES.fg.muted;
|
|
|
39
39
|
const FILENAME_WHITE = '#ffffff';
|
|
40
40
|
// Add/del text colors are the shared DIFF_TONES tokens, whose values ARE this
|
|
41
41
|
// panel's shipped colors — this panel is the reference diff look (diff-view
|
|
42
|
-
// was unwired dead code until
|
|
42
|
+
// was unwired dead code until, so "majority of surfaces" was a mirage);
|
|
43
43
|
// the conversation surface converges onto these, not the reverse.
|
|
44
44
|
const ADD_GREEN: string = DIFF_TONES.add;
|
|
45
45
|
const ADD_BG = '#001a0d';
|
|
@@ -199,6 +199,19 @@ function extractDiffFilePath(trimmed: string): string {
|
|
|
199
199
|
return 'unknown';
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
/** Reconstruct one file's unified-diff text from a StructuredDiffFile (header + hunks, prefixes re-attached; no size cap). */
|
|
203
|
+
function reconstructStructuredFile(file: StructuredDiffFile): string {
|
|
204
|
+
const out: string[] = [...file.headerLines];
|
|
205
|
+
for (const hunk of file.hunks) {
|
|
206
|
+
out.push(hunk.header);
|
|
207
|
+
for (const line of hunk.lines) {
|
|
208
|
+
const prefix = line.kind === 'add' ? '+' : line.kind === 'del' ? '-' : ' ';
|
|
209
|
+
out.push(`${prefix}${line.text}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return out.join('\n');
|
|
213
|
+
}
|
|
214
|
+
|
|
202
215
|
function splitIntoDiffEntries(raw: string): DiffEntry[] {
|
|
203
216
|
const entries: DiffEntry[] = [];
|
|
204
217
|
// Split on "diff --git" / "diff --cc" / "diff --combined" lines
|
|
@@ -306,6 +319,26 @@ export class DiffPanel extends BasePanel {
|
|
|
306
319
|
this.markDirty();
|
|
307
320
|
}
|
|
308
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Ingest a StructuredDiff (GitService.diffStructured — the FULL, uncapped
|
|
324
|
+
* working-tree diff). Each file becomes one DiffEntry (unified text
|
|
325
|
+
* reconstructed) so the existing tabs/renderer work unchanged; the old
|
|
326
|
+
* 4,000-char slice is gone and a diff of any length renders complete.
|
|
327
|
+
*/
|
|
328
|
+
loadStructuredDiff(diff: StructuredDiff): void {
|
|
329
|
+
this.entries = diff.files.map((file) => {
|
|
330
|
+
const raw = reconstructStructuredFile(file);
|
|
331
|
+
return {
|
|
332
|
+
filePath: file.newPath ?? file.oldPath ?? 'unknown',
|
|
333
|
+
raw,
|
|
334
|
+
lines: parseDiff(raw),
|
|
335
|
+
};
|
|
336
|
+
});
|
|
337
|
+
this.selectedFile = 0;
|
|
338
|
+
this.scrollOffset = 0;
|
|
339
|
+
this.markDirty();
|
|
340
|
+
}
|
|
341
|
+
|
|
309
342
|
/**
|
|
310
343
|
* Defensive gate mirroring GitPanel's own notGitRepo messaging — short-
|
|
311
344
|
* circuits before any of this panel's self-load git spawns run, instead of
|
|
@@ -521,7 +554,7 @@ export class DiffPanel extends BasePanel {
|
|
|
521
554
|
}
|
|
522
555
|
}
|
|
523
556
|
|
|
524
|
-
//
|
|
557
|
+
// (the purge): 'o' used to open the currently selected file in the
|
|
525
558
|
// preview panel via a staged pendingOpenPreview flag + a
|
|
526
559
|
// handlePanelIntegrationAction cross-panel hook (the same bridge
|
|
527
560
|
// FileExplorerPanel used). 'preview' is DELETE-disposition with no
|
|
@@ -597,7 +630,7 @@ export class DiffPanel extends BasePanel {
|
|
|
597
630
|
width,
|
|
598
631
|
' No diff to display.',
|
|
599
632
|
'Load a git diff or select a changed file to populate the workspace. w=working h=HEAD s=staged self-load right here.',
|
|
600
|
-
//
|
|
633
|
+
// corrected — bare /diff loads files changed *this
|
|
601
634
|
// session* (falling back to the full HEAD diff when none are
|
|
602
635
|
// tracked yet), not the working-tree diff the w key already
|
|
603
636
|
// loads in-panel; the old summary text described the wrong
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* EvalRegistry — holds the latest evaluation harness run state.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* (the purge): extracted out of eval-panel.ts. 'eval' was
|
|
5
5
|
* DELETE-disposition (no surviving human surface — the evaluation harness
|
|
6
6
|
* is driven and reviewed via the `/eval` CLI command, input/commands/eval.ts)
|
|
7
7
|
* but this registry is still a live consumer of `/eval run` output, so it
|
|
8
8
|
* survives as a standalone read-model rather than being deleted with the
|
|
9
|
-
* view. See .goodvibes/audit/2026-07-04-wave6-briefs.json
|
|
9
|
+
* view. See .goodvibes/audit/2026-07-04-wave6-briefs.json.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { EvalSuiteResult, EvalGateResult } from '@/runtime/index.ts';
|