@pellux/goodvibes-tui 0.26.0 → 0.27.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 (79) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +12 -7
  3. package/docs/foundation-artifacts/operator-contract.json +4 -1
  4. package/package.json +2 -2
  5. package/src/cli/bundle-command.ts +2 -1
  6. package/src/cli/service-posture.ts +2 -1
  7. package/src/core/conversation-rendering.ts +2 -2
  8. package/src/core/conversation.ts +49 -2
  9. package/src/core/session-recovery.ts +24 -18
  10. package/src/core/system-message-router.ts +42 -26
  11. package/src/core/turn-event-wiring.ts +12 -16
  12. package/src/daemon/handlers/calendar/caldav-client.ts +2 -1
  13. package/src/daemon/handlers/inbox/index.ts +2 -1
  14. package/src/daemon/handlers/inbox/poller.ts +2 -1
  15. package/src/daemon/handlers/inbox/providers/discord.ts +2 -1
  16. package/src/daemon/handlers/inbox/providers/email.ts +2 -1
  17. package/src/daemon/handlers/inbox/providers/route-util.ts +2 -1
  18. package/src/daemon/handlers/inbox/providers/slack.ts +2 -1
  19. package/src/daemon/handlers/triage/integration.ts +2 -1
  20. package/src/daemon/handlers/triage/pipeline.ts +2 -1
  21. package/src/daemon/handlers/triage/tagger/discord.ts +2 -1
  22. package/src/daemon/handlers/triage/tagger/imap.ts +4 -3
  23. package/src/export/gist-uploader.ts +3 -1
  24. package/src/input/command-registry.ts +1 -0
  25. package/src/input/commands/cloudflare-runtime.ts +2 -1
  26. package/src/input/commands/guidance-runtime.ts +2 -4
  27. package/src/input/commands/health-runtime.ts +13 -7
  28. package/src/input/commands/knowledge.ts +2 -1
  29. package/src/input/commands/runtime-services.ts +40 -10
  30. package/src/input/handler-command-route.ts +1 -1
  31. package/src/input/handler-interactions.ts +2 -1
  32. package/src/input/handler-modal-routes.ts +2 -1
  33. package/src/input/handler-onboarding-cloudflare.ts +2 -1
  34. package/src/input/handler-onboarding.ts +8 -8
  35. package/src/input/handler-ui-state.ts +1 -1
  36. package/src/input/tts-settings-actions.ts +2 -1
  37. package/src/main.ts +52 -59
  38. package/src/panels/agent-inspector-panel.ts +72 -2
  39. package/src/panels/agent-logs-panel.ts +127 -19
  40. package/src/panels/base-panel.ts +2 -1
  41. package/src/panels/builtin/agent.ts +3 -1
  42. package/src/panels/builtin/development.ts +1 -0
  43. package/src/panels/builtin/session.ts +1 -1
  44. package/src/panels/context-visualizer-panel.ts +24 -14
  45. package/src/panels/cost-tracker-panel.ts +7 -13
  46. package/src/panels/debug-panel.ts +11 -22
  47. package/src/panels/docs-panel.ts +14 -24
  48. package/src/panels/file-explorer-panel.ts +2 -1
  49. package/src/panels/file-preview-panel.ts +2 -1
  50. package/src/panels/git-panel.ts +10 -17
  51. package/src/panels/marketplace-panel.ts +2 -1
  52. package/src/panels/memory-panel.ts +2 -1
  53. package/src/panels/project-planning-panel.ts +5 -4
  54. package/src/panels/provider-health-panel.ts +56 -67
  55. package/src/panels/schedule-panel.ts +4 -7
  56. package/src/panels/session-browser-panel.ts +13 -21
  57. package/src/panels/skills-panel.ts +2 -1
  58. package/src/panels/tasks-panel.ts +2 -7
  59. package/src/panels/thinking-panel.ts +6 -11
  60. package/src/panels/token-budget-panel.ts +31 -15
  61. package/src/panels/tool-inspector-panel.ts +10 -18
  62. package/src/panels/work-plan-panel.ts +2 -1
  63. package/src/panels/wrfc-panel.ts +37 -35
  64. package/src/renderer/agent-detail-modal.ts +2 -2
  65. package/src/renderer/modal-utils.ts +0 -10
  66. package/src/renderer/process-modal.ts +2 -2
  67. package/src/runtime/bootstrap-command-context.ts +3 -0
  68. package/src/runtime/bootstrap-command-parts.ts +3 -1
  69. package/src/runtime/bootstrap-core.ts +31 -31
  70. package/src/runtime/bootstrap-shell.ts +1 -0
  71. package/src/runtime/onboarding/apply.ts +4 -3
  72. package/src/runtime/onboarding/snapshot.ts +4 -3
  73. package/src/runtime/process-lifecycle.ts +195 -0
  74. package/src/runtime/services.ts +4 -1
  75. package/src/runtime/wrfc-persistence.ts +20 -5
  76. package/src/shell/ui-openers.ts +3 -2
  77. package/src/verification/live-verifier.ts +4 -3
  78. package/src/version.ts +1 -1
  79. package/src/core/context-auto-compact.ts +0 -110
@@ -10,10 +10,11 @@
10
10
  *
11
11
  * 2. `WrfcPersistence.rehydrate(router)` is called once on boot (after the
12
12
  * SystemMessageRouter is available). It reads the snapshot, identifies
13
- * chains that were in a non-terminal state at last write, and emits a
14
- * high-priority 'wrfc' system message per interrupted chain. The
15
- * `interruptedChains` accessor makes the data available for panel reads
16
- * without coupling this module to wrfc-panel.ts.
13
+ * chains that were in a non-terminal state at last write, emits a
14
+ * high-priority 'wrfc' system message per interrupted chain, and re-imports
15
+ * each interrupted chain into the WrfcController so it reappears as a panel
16
+ * row and can be resumed by the operator. The `interruptedChains` accessor
17
+ * additionally exposes the recovered set for inspection.
17
18
  *
18
19
  * 3. Snapshot lifecycle:
19
20
  * - Terminal chains ('passed' | 'failed') are pruned from the snapshot
@@ -57,6 +58,14 @@ interface WrfcSnapshot {
57
58
  /** Subset of WrfcController needed by this module. */
58
59
  export interface WrfcControllerReader {
59
60
  listChains(): WrfcChain[];
61
+ /**
62
+ * Re-import a chain recovered from a previous process so it reappears in the
63
+ * controller's in-memory map and becomes selectable/resumable from the panel.
64
+ * Optional so read-only test doubles can omit it; the real WrfcController
65
+ * provides it. `force` is left at its default — on a fresh start the map is
66
+ * empty so importing never clobbers a live chain.
67
+ */
68
+ importChain?(chain: WrfcChain, force?: boolean): boolean;
60
69
  }
61
70
 
62
71
  export interface WrfcPersistenceOptions {
@@ -64,7 +73,7 @@ export interface WrfcPersistenceOptions {
64
73
  readonly snapshotPath: string;
65
74
  /** Factory for the current SystemMessageRouter — may return null before it is wired. */
66
75
  readonly getSystemMessageRouter: () => SystemMessageRouter | null;
67
- /** WrfcController readeronly listChains() is needed. */
76
+ /** WrfcController access — listChains() (read) plus optional importChain() (re-import on rehydrate). */
68
77
  readonly controller: WrfcControllerReader;
69
78
  }
70
79
 
@@ -140,6 +149,12 @@ class WrfcPersistenceImpl implements WrfcPersistence {
140
149
 
141
150
  const router = this.getSystemMessageRouter();
142
151
  for (const chain of interrupted) {
152
+ // Re-import so the chain reappears in the controller's in-memory map and
153
+ // becomes selectable/resumable from the panel. On a fresh process start
154
+ // the map is empty, so importChain (force=false) never clobbers a live
155
+ // chain. The accessor is optional for read-only test doubles.
156
+ this.controller.importChain?.(chain);
157
+
143
158
  const msg =
144
159
  `[WRFC] Chain ${chain.id.slice(0, 12)} (${chain.task.slice(0, 60).trim()}) ` +
145
160
  `was interrupted by a restart — state was '${chain.state}' ` +
@@ -13,6 +13,7 @@ import type { SecretsManager } from '@pellux/goodvibes-sdk/platform/config';
13
13
  import type { ServiceInspectionQuery } from '../runtime/ui-service-queries.ts';
14
14
  import type { ModelPickerTargetInfo } from '../input/model-picker.ts';
15
15
  import { syncServiceSettingToPlatform } from './service-settings-sync.ts';
16
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
16
17
 
17
18
  type WireShellUiOpenersOptions = {
18
19
  commandContext: CommandContext;
@@ -203,7 +204,7 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
203
204
  input.modelPicker.openAllModels(models, getCurrentModelForPickerTarget());
204
205
  render();
205
206
  })().catch((error: unknown) => {
206
- commandContext.print?.(`Model picker failed to open: ${error instanceof Error ? error.message : String(error)}`);
207
+ commandContext.print?.(`Model picker failed to open: ${summarizeError(error)}`);
207
208
  render();
208
209
  });
209
210
  };
@@ -223,7 +224,7 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
223
224
  input.modelPicker.openProviders(providers, getCurrentProviderForPickerTarget());
224
225
  render();
225
226
  })().catch((error: unknown) => {
226
- commandContext.print?.(`Provider picker failed to open: ${error instanceof Error ? error.message : String(error)}`);
227
+ commandContext.print?.(`Provider picker failed to open: ${summarizeError(error)}`);
227
228
  render();
228
229
  });
229
230
  };
@@ -3,6 +3,7 @@ import { join, resolve } from 'node:path';
3
3
  import { spawn } from 'node:child_process';
4
4
  import { auditGoodVibesHome } from '../config/goodvibes-home-audit.ts';
5
5
  import { buildVerificationLedger } from './verification-ledger.ts';
6
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
6
7
 
7
8
  export type LiveVerificationStatus = 'pass' | 'warn' | 'fail' | 'skip';
8
9
 
@@ -111,7 +112,7 @@ function runCommand(command: string, args: string[], cwd: string, timeoutMs = 15
111
112
  resolveCommand({
112
113
  exitCode: -1,
113
114
  stdout: '',
114
- stderr: error instanceof Error ? error.message : String(error),
115
+ stderr: summarizeError(error),
115
116
  timedOut,
116
117
  });
117
118
  });
@@ -161,7 +162,7 @@ function commandCheck(
161
162
  title,
162
163
  status: 'fail',
163
164
  summary: 'Command succeeded but did not return valid JSON.',
164
- detail: error instanceof Error ? error.message : String(error),
165
+ detail: summarizeError(error),
165
166
  };
166
167
  }
167
168
  }
@@ -208,7 +209,7 @@ async function fetchCheck(
208
209
  title,
209
210
  status: 'fail',
210
211
  summary: 'Request failed.',
211
- detail: error instanceof Error ? error.message : String(error),
212
+ detail: summarizeError(error),
212
213
  };
213
214
  }
214
215
  }
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '0.26.0';
9
+ let _version = '0.27.0';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
12
12
  _version = pkg.version ?? _version;
@@ -1,110 +0,0 @@
1
- /**
2
- * Auto-compaction helper — TASK-058.
3
- *
4
- * Evaluates whether auto-compact should run after a turn completes and, if so,
5
- * triggers compaction and posts an honest transcript notice.
6
- *
7
- * behavior.autoCompactThreshold: SDK schema range [10, 100], default 80.
8
- * Auto-compact is active whenever the threshold is in its valid range (>0).
9
- * The display/suggestion path (hint + meter) is always active regardless.
10
- */
11
-
12
- import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
13
- import type { ConversationManager } from './conversation';
14
- import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
15
- import type { SystemMessageRouter } from './system-message-router';
16
- import { logger } from '@pellux/goodvibes-sdk/platform/utils';
17
- import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
18
- import { getLastCompactionEvent } from '@pellux/goodvibes-sdk/platform/core';
19
- import type { CompactionContext } from '@pellux/goodvibes-sdk/platform/core';
20
- import { buildCompactionPreview, buildCompactionAfterNotice } from '../renderer/compaction-preview.ts';
21
-
22
- export interface AutoCompactDeps {
23
- readonly configManager: Pick<ConfigManager, 'get'>;
24
- readonly conversation: ConversationManager;
25
- readonly providerRegistry: ProviderRegistry;
26
- readonly systemMessageRouter: SystemMessageRouter;
27
- readonly model: string;
28
- readonly provider: string;
29
- readonly lastInputTokens: number;
30
- readonly contextWindow: number;
31
- }
32
-
33
- /**
34
- * Run after each TURN_COMPLETED event.
35
- *
36
- * Reads behavior.autoCompactThreshold from config (SDK default: 80, range [10, 100]).
37
- * When usage is at or above the threshold, compacts the conversation and posts
38
- * an honest transcript notice so the user understands any summary discontinuity.
39
- *
40
- * This function is intentionally non-throwing; failures are logged and
41
- * surfaced via the system message router.
42
- */
43
- export async function maybeAutoCompact(deps: AutoCompactDeps): Promise<void> {
44
- // SDK schema default is 80; valid range is [10, 100]. The ?? 0 fallback is a
45
- // defensive guard for missing/null values only — not a normal operating state.
46
- const rawThreshold = Number(deps.configManager.get('behavior.autoCompactThreshold') ?? 0);
47
- const thresholdPct = Number.isFinite(rawThreshold) ? rawThreshold : 0;
48
-
49
- // Defensive guard: skip only when threshold is missing/non-positive (real config defaults to 80).
50
- if (thresholdPct <= 0 || deps.contextWindow <= 0) return;
51
-
52
- const usagePct = Math.min(100, Math.round((Math.max(0, deps.lastInputTokens) / deps.contextWindow) * 100));
53
- if (usagePct < thresholdPct) return;
54
-
55
- try {
56
- logger.debug('auto-compact triggered', { usagePct, thresholdPct });
57
- // Pre-compact preview — uses buildCompactionPreview for an honest estimate.
58
- const messages = deps.conversation.getMessagesForLLM();
59
- const sessionMemoryStore = deps.conversation.getSessionMemoryStore();
60
- const sessionMemories = sessionMemoryStore?.list() ?? [];
61
- const pinnedMemoryCount = sessionMemories.length;
62
- const preview = buildCompactionPreview({
63
- messages,
64
- contextWindow: deps.contextWindow,
65
- pinnedMemoryCount,
66
- trigger: 'auto',
67
- });
68
- deps.systemMessageRouter.routeSystemMessage(preview, 'high');
69
- const eventBefore = getLastCompactionEvent();
70
- const compactionCtx: CompactionContext = {
71
- messages,
72
- sessionMemories,
73
- agents: [],
74
- wrfcChains: [],
75
- activePlan: null,
76
- lineageEntries: [],
77
- compactionCount: 0,
78
- contextWindow: deps.contextWindow,
79
- trigger: 'auto',
80
- extractionModelId: deps.model,
81
- extractionProvider: deps.provider,
82
- };
83
- await deps.conversation.compact(
84
- deps.providerRegistry,
85
- deps.model,
86
- 'auto',
87
- deps.provider,
88
- compactionCtx,
89
- );
90
- // Post-compact notice using real CompactionEvent figures.
91
- const eventAfter = getLastCompactionEvent();
92
- if (eventAfter !== null && eventAfter !== eventBefore) {
93
- deps.systemMessageRouter.routeSystemMessage(
94
- buildCompactionAfterNotice({ event: eventAfter, pinnedMemoryCount }),
95
- 'low',
96
- );
97
- } else {
98
- deps.systemMessageRouter.routeSystemMessage(
99
- '[Context] Auto-compact complete — older turns summarised. Use /compact to compact again manually.',
100
- 'low',
101
- );
102
- }
103
- } catch (err) {
104
- logger.error('auto-compact failed', { error: summarizeError(err) });
105
- deps.systemMessageRouter.routeSystemMessage(
106
- `[Context] Auto-compact failed: ${summarizeError(err)}. Use /compact to try manually.`,
107
- 'high',
108
- );
109
- }
110
- }