@pellux/goodvibes-agent 1.5.5 → 1.5.6

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 (165) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +1 -1
  3. package/dist/package/main.js +94130 -79335
  4. package/docs/tools-and-commands.md +2 -1
  5. package/package.json +6 -3
  6. package/src/agent/autonomy-schedule-format.ts +1 -1
  7. package/src/agent/autonomy-schedule.ts +3 -3
  8. package/src/agent/calendar/calendar-oauth-service.ts +324 -0
  9. package/src/agent/calendar-subscription-registry.ts +497 -0
  10. package/src/agent/email/email-service.ts +57 -0
  11. package/src/agent/email/smtp-client.ts +30 -10
  12. package/src/agent/memory-prompt.ts +143 -12
  13. package/src/agent/operator-actions.ts +26 -22
  14. package/src/agent/prompt-context-receipts.ts +45 -18
  15. package/src/agent/reminder-schedule-format.ts +1 -1
  16. package/src/agent/reminder-schedule.ts +3 -3
  17. package/src/agent/routine-schedule-format.ts +2 -2
  18. package/src/agent/routine-schedule-promotion.ts +5 -5
  19. package/src/agent/routine-schedule-receipts.ts +2 -2
  20. package/src/agent/schedule-edit-format.ts +1 -1
  21. package/src/agent/schedule-edit.ts +5 -5
  22. package/src/agent/session-registration.ts +281 -0
  23. package/src/agent/vibe-file.ts +50 -0
  24. package/src/cli/local-library-command-shared.ts +146 -0
  25. package/src/cli/management.ts +2 -1
  26. package/src/cli/personas-command.ts +252 -0
  27. package/src/cli/resume-relaunch-notice.ts +151 -0
  28. package/src/cli/routines-command.ts +25 -2
  29. package/src/cli/service-posture.ts +3 -2
  30. package/src/cli/skill-bundle-command.ts +175 -0
  31. package/src/cli/skills-command.ts +309 -0
  32. package/src/cli/status.ts +43 -7
  33. package/src/cli/tui-startup.ts +24 -1
  34. package/src/config/agent-settings-policy.ts +0 -1
  35. package/src/config/secret-config.ts +4 -0
  36. package/src/core/conversation-rendering.ts +20 -11
  37. package/src/core/focus-tracker.ts +41 -0
  38. package/src/core/system-message-noise.ts +108 -0
  39. package/src/core/system-message-router.ts +54 -1
  40. package/src/core/thinking-overlay.ts +83 -0
  41. package/src/input/agent-workspace-access-command-editor-submission.ts +75 -108
  42. package/src/input/agent-workspace-access-command-editors.ts +108 -128
  43. package/src/input/agent-workspace-activation.ts +15 -0
  44. package/src/input/agent-workspace-basic-command-editor-submission.ts +292 -513
  45. package/src/input/agent-workspace-basic-command-editors.ts +433 -564
  46. package/src/input/agent-workspace-calendar-connect-editor.ts +116 -0
  47. package/src/input/agent-workspace-calendar-oauth-editor.ts +152 -0
  48. package/src/input/agent-workspace-calendar-subscribe-editor.ts +128 -0
  49. package/src/input/agent-workspace-categories.ts +7 -2
  50. package/src/input/agent-workspace-channel-command-editor-submission.ts +38 -52
  51. package/src/input/agent-workspace-channel-command-editors.ts +42 -46
  52. package/src/input/agent-workspace-command-editor-engine.ts +133 -0
  53. package/src/input/agent-workspace-command-editor.ts +4 -0
  54. package/src/input/agent-workspace-direct-editor-submission.ts +59 -0
  55. package/src/input/agent-workspace-email-connect-editor.ts +162 -0
  56. package/src/input/agent-workspace-knowledge-command-editor-submission.ts +52 -81
  57. package/src/input/agent-workspace-knowledge-command-editors.ts +71 -71
  58. package/src/input/agent-workspace-library-command-editor-submission.ts +10 -28
  59. package/src/input/agent-workspace-library-command-editors.ts +16 -2
  60. package/src/input/agent-workspace-live-counters.ts +55 -0
  61. package/src/input/agent-workspace-mcp-command-editor-submission.ts +60 -82
  62. package/src/input/agent-workspace-mcp-command-editors.ts +52 -0
  63. package/src/input/agent-workspace-media-command-editor-submission.ts +12 -39
  64. package/src/input/agent-workspace-media-command-editors.ts +10 -8
  65. package/src/input/agent-workspace-memory-command-editor-submission.ts +76 -151
  66. package/src/input/agent-workspace-memory-command-editors.ts +116 -141
  67. package/src/input/agent-workspace-operations-command-editor-submission.ts +131 -184
  68. package/src/input/agent-workspace-operations-command-editors.ts +150 -162
  69. package/src/input/agent-workspace-provider-command-editor-submission.ts +60 -106
  70. package/src/input/agent-workspace-provider-command-editors.ts +58 -68
  71. package/src/input/agent-workspace-search.ts +8 -1
  72. package/src/input/agent-workspace-session-command-editor-submission.ts +104 -132
  73. package/src/input/agent-workspace-session-command-editors.ts +160 -195
  74. package/src/input/agent-workspace-settings.ts +40 -1
  75. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +55 -71
  76. package/src/input/agent-workspace-skill-bundle-command-editors.ts +65 -69
  77. package/src/input/agent-workspace-snapshot-builders.ts +431 -0
  78. package/src/input/agent-workspace-snapshot-config.ts +43 -0
  79. package/src/input/agent-workspace-snapshot.ts +198 -432
  80. package/src/input/agent-workspace-task-command-editor-submission.ts +34 -44
  81. package/src/input/agent-workspace-task-command-editors.ts +27 -28
  82. package/src/input/agent-workspace-types.ts +35 -1
  83. package/src/input/agent-workspace.ts +5 -14
  84. package/src/input/command-registry.ts +23 -1
  85. package/src/input/commands/calendar-connect-runtime.ts +226 -0
  86. package/src/input/commands/calendar-runtime.ts +110 -7
  87. package/src/input/commands/calendar-subscription-runtime.ts +225 -0
  88. package/src/input/commands/email-runtime.ts +100 -40
  89. package/src/input/commands/knowledge.ts +1 -1
  90. package/src/input/commands/network-scan-runtime.ts +75 -0
  91. package/src/input/commands/operator-actions-runtime.ts +9 -6
  92. package/src/input/commands/personas-runtime.ts +1 -1
  93. package/src/input/commands/schedule-runtime.ts +4 -4
  94. package/src/input/commands/session-content.ts +13 -1
  95. package/src/input/commands/session-workflow.ts +15 -20
  96. package/src/input/commands/session.ts +3 -1
  97. package/src/input/commands/shell-core.ts +20 -6
  98. package/src/input/commands.ts +2 -2
  99. package/src/input/delete-key-policy.ts +46 -0
  100. package/src/input/feed-context-factory.ts +10 -0
  101. package/src/input/handler-feed.ts +87 -0
  102. package/src/input/handler-modal-routes.ts +6 -1
  103. package/src/input/handler.ts +5 -0
  104. package/src/input/panel-paste-flood-guard.ts +94 -0
  105. package/src/input/settings-modal-types.ts +5 -3
  106. package/src/input/settings-modal.ts +21 -0
  107. package/src/main.ts +36 -36
  108. package/src/permissions/approval-posture.ts +141 -0
  109. package/src/renderer/agent-workspace-context-lines.ts +10 -2
  110. package/src/renderer/compositor.ts +27 -4
  111. package/src/renderer/diff.ts +61 -18
  112. package/src/renderer/fullscreen-primitives.ts +37 -18
  113. package/src/renderer/markdown.ts +20 -10
  114. package/src/renderer/modal-factory.ts +25 -15
  115. package/src/renderer/overlay-box.ts +23 -12
  116. package/src/renderer/process-indicator.ts +8 -3
  117. package/src/renderer/prompt-content-width.ts +16 -0
  118. package/src/renderer/settings-modal-helpers.ts +2 -0
  119. package/src/renderer/settings-modal.ts +2 -0
  120. package/src/renderer/startup-theme-probe.ts +35 -0
  121. package/src/renderer/status-glyphs.ts +11 -15
  122. package/src/renderer/system-message.ts +17 -2
  123. package/src/renderer/term-caps.ts +318 -0
  124. package/src/renderer/terminal-bg-probe.ts +373 -0
  125. package/src/renderer/terminal-escapes.ts +24 -0
  126. package/src/renderer/theme-mode-config.ts +87 -0
  127. package/src/renderer/theme.ts +241 -0
  128. package/src/renderer/thinking.ts +12 -3
  129. package/src/renderer/tool-call.ts +7 -3
  130. package/src/renderer/ui-factory.ts +92 -36
  131. package/src/renderer/ui-primitives.ts +33 -93
  132. package/src/runtime/bootstrap-core.ts +15 -0
  133. package/src/runtime/bootstrap-hook-bridge.ts +6 -0
  134. package/src/runtime/bootstrap-shell.ts +2 -0
  135. package/src/runtime/bootstrap.ts +68 -5
  136. package/src/runtime/calendar-boot-refresh.ts +105 -0
  137. package/src/runtime/lan-scan-consent.ts +253 -0
  138. package/src/runtime/services.ts +127 -2
  139. package/src/runtime/session-spine-rest-transport.ts +160 -0
  140. package/src/runtime/terminal-output-guard.ts +6 -1
  141. package/src/runtime/ui-services.ts +3 -0
  142. package/src/shell/agent-workspace-fullscreen.ts +5 -0
  143. package/src/shell/terminal-focus-mode.ts +120 -0
  144. package/src/shell/ui-openers.ts +13 -4
  145. package/src/tools/agent-harness-autonomy-intake.ts +5 -5
  146. package/src/tools/agent-harness-autonomy-queue.ts +1 -1
  147. package/src/tools/agent-harness-background-processes.ts +2 -1
  148. package/src/tools/agent-harness-metadata.ts +16 -8
  149. package/src/tools/agent-harness-operator-methods.ts +44 -8
  150. package/src/tools/agent-harness-personal-ops-discovery.ts +57 -10
  151. package/src/tools/agent-harness-personal-ops-lanes.ts +17 -5
  152. package/src/tools/agent-harness-personal-ops-operations.ts +7 -7
  153. package/src/tools/agent-harness-personal-ops-types.ts +6 -0
  154. package/src/tools/agent-harness-prompt-context.ts +26 -12
  155. package/src/tools/agent-harness-workspace-actions.ts +4 -0
  156. package/src/tools/agent-local-registry-args.ts +117 -0
  157. package/src/tools/agent-local-registry-memory.ts +227 -0
  158. package/src/tools/agent-local-registry-tool.ts +19 -237
  159. package/src/tools/agent-operator-briefing-tool.ts +2 -2
  160. package/src/tools/agent-operator-method-tool.ts +13 -0
  161. package/src/tools/agent-policy-explanation.ts +39 -4
  162. package/src/tools/agent-schedule-tool.ts +5 -5
  163. package/src/utils/terminal-width.ts +98 -1
  164. package/src/version.ts +1 -1
  165. package/src/cli/local-library-command.ts +0 -825
@@ -3,6 +3,13 @@ import { UIFactory } from './ui-factory.ts';
3
3
  import { renderCodeBlock } from './code-block.ts';
4
4
  import { getDisplayWidth } from '../utils/terminal-width.ts';
5
5
  import { LAYOUT } from './layout.ts';
6
+ import { activeTheme } from './theme.ts';
7
+
8
+ // Transcript tokens are read live per render (const T = activeTheme() at the top
9
+ // of each render function below) so a dark→light repaint re-resolves with no
10
+ // module reload. Dark values are byte-identical to the agent's prior static
11
+ // reads; the only dark change is inline code losing its dark background box (it
12
+ // gains bold instead — the near-black box was unreadable on a light terminal).
6
13
 
7
14
  export interface MarkdownRenderOptions {
8
15
  codeBlockLineNumbers?: boolean;
@@ -56,6 +63,7 @@ export function renderMarkdownTracked(
56
63
  width: number,
57
64
  options: MarkdownRenderOptions = {},
58
65
  ): { lines: ReturnType<typeof renderMarkdown>; codeBlocks: CodeBlockSpan[] } {
66
+ const T = activeTheme();
59
67
  const lines: ReturnType<typeof renderMarkdown> = [];
60
68
  const codeBlocks: CodeBlockSpan[] = [];
61
69
  const rawLines = text.split('\n');
@@ -106,17 +114,17 @@ export function renderMarkdownTracked(
106
114
  const h2 = raw.match(/^## (.+)/);
107
115
  const h1 = raw.match(/^# (.+)/);
108
116
  if (h1) {
109
- lines.push(UIFactory.stringToLine(' '.repeat(indent) + h1[1].toUpperCase(), width, { fg: '#00ffff', bold: true }));
117
+ lines.push(UIFactory.stringToLine(' '.repeat(indent) + h1[1].toUpperCase(), width, { fg: T.heading1, bold: true }));
110
118
  lines.push(UIFactory.stringToLine(' '.repeat(indent) + '━'.repeat(Math.min(getDisplayWidth(h1[1]), contentWidth)), width, { fg: '244' }));
111
119
  continue;
112
120
  }
113
121
  if (h2) {
114
- lines.push(UIFactory.stringToLine(' '.repeat(indent) + h2[1], width, { fg: '#00ffff', bold: true }));
122
+ lines.push(UIFactory.stringToLine(' '.repeat(indent) + h2[1], width, { fg: T.heading2, bold: true }));
115
123
  lines.push(UIFactory.stringToLine(' '.repeat(indent) + '─'.repeat(Math.min(getDisplayWidth(h2[1]), contentWidth)), width, { fg: '240' }));
116
124
  continue;
117
125
  }
118
126
  if (h3) {
119
- lines.push(UIFactory.stringToLine(' '.repeat(indent) + h3[1], width, { fg: '111', bold: true }));
127
+ lines.push(UIFactory.stringToLine(' '.repeat(indent) + h3[1], width, { fg: T.heading3, bold: true }));
120
128
  continue;
121
129
  }
122
130
 
@@ -130,7 +138,7 @@ export function renderMarkdownTracked(
130
138
  const rendered = renderInlineMarkdown(taskMatch[3]);
131
139
  const prefix = ' '.repeat(bulletX) + checkbox;
132
140
  const style = checked ? { fg: '244', strikethrough: true } : {};
133
- lines.push(...compositeInlineLine(prefix, rendered, width, { fg: checked ? '#22c55e' : '252', ...style }, textStartX));
141
+ lines.push(...compositeInlineLine(prefix, rendered, width, { fg: checked ? T.checkboxChecked : '252', ...style }, textStartX));
134
142
  continue;
135
143
  }
136
144
 
@@ -220,6 +228,7 @@ function stripMarkdown(text: string): string {
220
228
  * truncates long content, and renders with proper styling.
221
229
  */
222
230
  function renderTable(rows: string[], width: number, indent: number): Line[] {
231
+ const T = activeTheme();
223
232
  const lines: Line[] = [];
224
233
 
225
234
  // Parse rows into cells, skip separator
@@ -300,14 +309,14 @@ function renderTable(rows: string[], width: number, indent: number): Line[] {
300
309
  }
301
310
  let style: Partial<Cell> = {};
302
311
  if (token.type === 'code') {
303
- style = { fg: '#ffcc00', bg: '#1a1a1a' };
312
+ style = { fg: T.inlineCodeFg, bold: true };
304
313
  } else if (token.type === 'link') {
305
- style = { fg: '#00aaff', underline: true };
314
+ style = { fg: T.link, underline: true };
306
315
  } else {
307
316
  style = { ...token.style };
308
317
  }
309
318
  if (isHdr) {
310
- style.fg = style.fg || '#00ffff';
319
+ style.fg = style.fg || T.heading1;
311
320
  style.bold = true;
312
321
  } else {
313
322
  style.fg = style.fg || '252';
@@ -320,7 +329,7 @@ function renderTable(rows: string[], width: number, indent: number): Line[] {
320
329
 
321
330
  // Pad remaining space
322
331
  while (w < maxW) {
323
- cells.push(createStyledCell(' ', isHdr ? { fg: '#00ffff' } : { fg: '252' }));
332
+ cells.push(createStyledCell(' ', isHdr ? { fg: T.heading1 } : { fg: '252' }));
324
333
  w++;
325
334
  }
326
335
  return cells;
@@ -518,6 +527,7 @@ function compositeInlineLine(
518
527
  prefixStyle: Partial<Cell>,
519
528
  textStartX: number
520
529
  ): Line[] {
530
+ const T = activeTheme();
521
531
  const lines: Line[] = [];
522
532
 
523
533
  // Flatten tokens to [char, style] pairs
@@ -528,14 +538,14 @@ function compositeInlineLine(
528
538
  if (token.type === 'text') {
529
539
  for (const ch of token.text) chars.push({ char: ch, style: token.style });
530
540
  } else if (token.type === 'code') {
531
- for (const ch of token.text) chars.push({ char: ch, style: { fg: '#ffcc00', bg: '#1a1a1a' } });
541
+ for (const ch of token.text) chars.push({ char: ch, style: { fg: T.inlineCodeFg, bold: true } });
532
542
  } else if (token.type === 'link') {
533
543
  // Resolve URL: if url is empty or relative, treat as text; if it's a file path, use file:// protocol
534
544
  let resolvedUrl = token.url;
535
545
  if (resolvedUrl && !resolvedUrl.startsWith('http') && !resolvedUrl.startsWith('file://') && resolvedUrl.startsWith('/')) {
536
546
  resolvedUrl = `file://${resolvedUrl}`;
537
547
  }
538
- for (const ch of token.text) chars.push({ char: ch, style: { fg: '#00aaff', underline: true, link: resolvedUrl || undefined } });
548
+ for (const ch of token.text) chars.push({ char: ch, style: { fg: T.link, underline: true, link: resolvedUrl || undefined } });
539
549
  }
540
550
  }
541
551
 
@@ -8,7 +8,8 @@ import {
8
8
  putOverlayText,
9
9
  } from './overlay-box.ts';
10
10
  import { getOverlayMaxWidth } from './overlay-viewport.ts';
11
- import { GLYPHS, UI_TONES } from './ui-primitives.ts';
11
+ import { GLYPHS } from './ui-primitives.ts';
12
+ import { activeUiTones, registerThemeRefresh } from './theme.ts';
12
13
 
13
14
  // ── Helpers ──────────────────────────────────────────────────────────────────
14
15
 
@@ -90,20 +91,29 @@ export interface ModalConfig {
90
91
 
91
92
  // ── Defaults ─────────────────────────────────────────────────────────────────
92
93
 
93
- const DEFAULT_STYLE: Required<ModalStyle> = {
94
- titleFg: UI_TONES.fg.primary,
95
- borderFg: UI_TONES.fg.dim,
96
- hintFg: UI_TONES.fg.muted,
97
- selectedFg: UI_TONES.fg.primary,
98
- selectedBg: UI_TONES.bg.selected,
99
- textFg: UI_TONES.fg.primary,
100
- accentFg: UI_TONES.state.info,
101
- titleRowFg: UI_TONES.fg.secondary,
102
- titleBg: UI_TONES.bg.title,
103
- sectionBg: UI_TONES.bg.section,
104
- inputBg: UI_TONES.bg.input,
105
- surfaceBg: UI_TONES.bg.surface,
106
- };
94
+ // Built from the mode-resolved chrome tones (activeUiTones) and rebuilt IN PLACE
95
+ // on a mode flip via the registered refresher (read by reference across modal
96
+ // call sites). These paint the OPAQUE dark modal surface, so in the SDK light
97
+ // tones the fg/bg roles stay dark (only state.info flips) — dark is byte-identical.
98
+ function buildModalStyle(): Required<ModalStyle> {
99
+ const t = activeUiTones();
100
+ return {
101
+ titleFg: t.fg.primary,
102
+ borderFg: t.fg.dim,
103
+ hintFg: t.fg.muted,
104
+ selectedFg: t.fg.primary,
105
+ selectedBg: t.bg.selected,
106
+ textFg: t.fg.primary,
107
+ accentFg: t.state.info,
108
+ titleRowFg: t.fg.secondary,
109
+ titleBg: t.bg.title,
110
+ sectionBg: t.bg.section,
111
+ inputBg: t.bg.input,
112
+ surfaceBg: t.bg.surface,
113
+ };
114
+ }
115
+ const DEFAULT_STYLE: Required<ModalStyle> = buildModalStyle();
116
+ registerThemeRefresh(() => Object.assign(DEFAULT_STYLE, buildModalStyle()));
107
117
 
108
118
  // ── ModalFactory ─────────────────────────────────────────────────────────────
109
119
 
@@ -1,6 +1,7 @@
1
1
  import { createStyledCell, type Line } from '../types/grid.ts';
2
2
  import { getOverlayMaxWidth } from './overlay-viewport.ts';
3
- import { GLYPHS, UI_TONES } from './ui-primitives.ts';
3
+ import { GLYPHS } from './ui-primitives.ts';
4
+ import { activeUiTones, registerThemeRefresh } from './theme.ts';
4
5
  import { fillWidth, makeLine, writeText } from './fullscreen-primitives.ts';
5
6
 
6
7
  export interface OverlayBoxPalette {
@@ -15,17 +16,27 @@ export interface OverlayBoxPalette {
15
16
  readonly bodyBg: string;
16
17
  }
17
18
 
18
- export const DEFAULT_OVERLAY_PALETTE: Readonly<OverlayBoxPalette> = {
19
- borderFg: UI_TONES.fg.secondary,
20
- titleFg: UI_TONES.fg.primary,
21
- bodyFg: UI_TONES.fg.primary,
22
- mutedFg: UI_TONES.fg.dim,
23
- selectedBg: UI_TONES.bg.selected,
24
- titleBg: UI_TONES.bg.title,
25
- sectionBg: UI_TONES.bg.section,
26
- inputBg: UI_TONES.bg.input,
27
- bodyBg: UI_TONES.bg.surface,
28
- } as const;
19
+ // Built from the mode-resolved chrome tones (activeUiTones) and rebuilt IN PLACE
20
+ // on a mode flip via the registered refresher (read by reference across the
21
+ // overlay call sites). Opaque dark overlay surface → fg/bg roles stay dark in
22
+ // the SDK light tones; dark is byte-identical to the prior static reads.
23
+ function buildOverlayPalette(): OverlayBoxPalette {
24
+ const t = activeUiTones();
25
+ return {
26
+ borderFg: t.fg.secondary,
27
+ titleFg: t.fg.primary,
28
+ bodyFg: t.fg.primary,
29
+ mutedFg: t.fg.dim,
30
+ selectedBg: t.bg.selected,
31
+ titleBg: t.bg.title,
32
+ sectionBg: t.bg.section,
33
+ inputBg: t.bg.input,
34
+ bodyBg: t.bg.surface,
35
+ };
36
+ }
37
+
38
+ export const DEFAULT_OVERLAY_PALETTE: Readonly<OverlayBoxPalette> = buildOverlayPalette();
39
+ registerThemeRefresh(() => Object.assign(DEFAULT_OVERLAY_PALETTE as OverlayBoxPalette, buildOverlayPalette()));
29
40
 
30
41
  export interface OverlayBoxLayout {
31
42
  readonly margin: number;
@@ -1,7 +1,8 @@
1
1
  import { type Line } from '../types/grid.ts';
2
2
  import { UIFactory } from './ui-factory.ts';
3
3
  import { getDisplayWidth } from '../utils/terminal-width.ts';
4
- import { GLYPHS } from './ui-primitives.ts';
4
+ import { GLYPHS, UI_TONES } from './ui-primitives.ts';
5
+ import { activeUiTones } from './theme.ts';
5
6
 
6
7
  /** Truncate a string to fit within maxWidth display columns. */
7
8
  function truncateToWidth(text: string, maxWidth: number): string {
@@ -39,7 +40,9 @@ export function renderProcessIndicator(
39
40
  const renderFocusedStatus = (text: string): Line[] => {
40
41
  const bg = '#31506f';
41
42
  const fg = '#eefaff';
42
- const markerFg = '#7dd3fc';
43
+ // Opaque highlight bar (bg/fg fixed) — marker sourced from the shared
44
+ // browser accent token (dark == the prior browser-cyan marker).
45
+ const markerFg = UI_TONES.accent.browser;
43
46
  const line = UIFactory.stringToLine(' '.repeat(width), width, { fg: '238' });
44
47
  const prefix = `${GLYPHS.navigation.selected} `;
45
48
  const body = truncateToWidth(text, Math.max(0, width - 8));
@@ -93,5 +96,7 @@ export function renderProcessIndicator(
93
96
  : '';
94
97
  const label = `${parts.join(` ${GLYPHS.navigation.pipeSeparator} `)}${progressSuffix}`;
95
98
  const hint = ` ${GLYPHS.status.pending} Enter to view`;
96
- return renderPlainStatus(`${label}${hint}`, { fg: '#00ffff', bold: true });
99
+ // Active-status label paints on the transparent terminal bg → read the live
100
+ // brand accent so it flips to a legible value in light mode (dark == brand cyan).
101
+ return renderPlainStatus(`${label}${hint}`, { fg: activeUiTones().accent.brand, bold: true });
97
102
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * computePromptContentWidth — content width available for prompt text inside
3
+ * the footer input box, after the box margin, inner padding, and the ' > '
4
+ * prefix are subtracted.
5
+ *
6
+ * Floors at 1: on a hostile-narrow terminal the raw subtraction
7
+ * (boxWidth - 4 - 3) can go zero or negative, and a non-positive content
8
+ * width breaks downstream text-wrapping and cursor-position math (which
9
+ * assume at least one column to place a character in).
10
+ */
11
+ export function computePromptContentWidth(terminalColumns: number): number {
12
+ const w = terminalColumns || 80;
13
+ const boxMargin = 2;
14
+ const boxWidth = w - (boxMargin * 2);
15
+ return Math.max(1, boxWidth - 4 - 3); // minus padding (4) minus prefix width (3: ' > ')
16
+ }
@@ -92,6 +92,8 @@ export const CATEGORY_LABELS: Record<(typeof SETTINGS_CATEGORIES)[number], strin
92
92
  watchers: 'Watchers',
93
93
  network: 'Network',
94
94
  orchestration: 'Orchestration',
95
+ planner: 'Planner',
96
+ daemon: 'Daemon',
95
97
  runtime: 'Runtime',
96
98
  sandbox: 'Sandbox',
97
99
  batch: 'Batch',
@@ -41,6 +41,8 @@ const CATEGORY_INFO: Record<SettingsCategory, string> = {
41
41
  watchers: 'Polling watcher and heartbeat behavior for runtime recovery and periodic checks.',
42
42
  network: 'Outbound TLS and remote fetch network policy.',
43
43
  orchestration: 'Visible agent orchestration limits such as recursion and active-agent caps.',
44
+ planner: 'Planning-decomposition agent limits: decomposition strategy, max turns, token ceiling, and wall-clock timeout before falling back to the deterministic heuristic path.',
45
+ daemon: 'Whether the local session daemon runs, and whether it is embedded in this surface process instead of spawned as a detached background service.',
44
46
  runtime: 'Runtime service limits and event bus settings.',
45
47
  sandbox: 'Isolation settings for REPL, MCP, and VM-backed sessions.',
46
48
  batch: 'Batch queue backend, limits, and provider batching behavior.',
@@ -0,0 +1,35 @@
1
+ /**
2
+ * startup-theme-probe.ts (W4-R4) — install the OSC-11 background probe at
3
+ * startup and wire its resolved mode to the ported theme system.
4
+ *
5
+ * Thin composition seam extracted from main.ts: it binds R2's
6
+ * installBackgroundThemeProbe to theme.ts's setActiveThemeMode (applyThemeMode)
7
+ * so forced dark/light applies before first paint and auto (TTY only) probes and
8
+ * repaints once if light wins. The returned handle's filterInput() must gate the
9
+ * stdin data handler so the OSC-11 reply never reaches the tokenizer.
10
+ */
11
+
12
+ import { installBackgroundThemeProbe, type ThemeProbeHandle } from './terminal-bg-probe.ts';
13
+ import { setActiveThemeMode } from './theme.ts';
14
+ import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
15
+
16
+ export interface StartupThemeProbeDeps {
17
+ readonly configManager: Pick<ConfigManager, 'get'>;
18
+ readonly stdout: NodeJS.WriteStream;
19
+ /** The caller's terminal-output guard wrapper (allowTerminalWrite). */
20
+ readonly writeAllowed: (write: () => void) => void;
21
+ /** Reset the compositor diff so the repaint after a light reply is full. */
22
+ readonly resetDiff: () => void;
23
+ readonly render: () => void;
24
+ }
25
+
26
+ export function installStartupThemeProbe(deps: StartupThemeProbeDeps): ThemeProbeHandle {
27
+ return installBackgroundThemeProbe({
28
+ configManager: deps.configManager,
29
+ applyThemeMode: setActiveThemeMode,
30
+ isTTY: Boolean(deps.stdout.isTTY),
31
+ env: process.env,
32
+ writeQuery: (b) => deps.writeAllowed(() => deps.stdout.write(b)),
33
+ requestRepaint: () => { deps.resetDiff(); deps.render(); },
34
+ });
35
+ }
@@ -1,21 +1,17 @@
1
1
  // ---------------------------------------------------------------------------
2
2
  // status-glyphs.ts — canonical glyph map for status states.
3
3
  //
4
- // Extracted as a neutral module so both status-token.ts and polish.ts can
5
- // import from here without creating a circular ESM dependency.
4
+ // W4-R4: STATE_GLYPHS is no longer hardcoded here. It is the SDK presentation
5
+ // contract (@pellux/goodvibes-sdk/platform/presentation, landed by W4-S1),
6
+ // aliased to GLYPHS.status so the four semantic glyphs are spelled out in
7
+ // exactly one place and can never drift from the registry again. Re-exported
8
+ // under the historical names so status-token.ts and polish.ts import unchanged.
6
9
  //
7
- // Glyphs:
8
- // good ✓ (CHECK MARK U+2713)
9
- // warn ⚠ (WARNING SIGN U+26A0)
10
- // bad ✕ (MULTIPLICATION X U+2715)
11
- // info ○ (WHITE CIRCLE U+25CB)
10
+ // Glyphs (the reconciled TUI-reference definitions):
11
+ // good ✓ (CHECK MARK U+2713) — GLYPHS.status.success
12
+ // warn ⚠ (WARNING SIGN U+26A0) — GLYPHS.status.warn
13
+ // bad ✕ (MULTIPLICATION X U+2715) — GLYPHS.status.failure
14
+ // info ○ (WHITE CIRCLE U+25CB) — GLYPHS.status.info
12
15
  // ---------------------------------------------------------------------------
13
16
 
14
- export type StatusState = 'good' | 'warn' | 'bad' | 'info';
15
-
16
- export const STATE_GLYPHS: Record<StatusState, string> = {
17
- good: '\u2713', // ✓
18
- warn: '\u26a0', // ⚠
19
- bad: '\u2715', // ✕
20
- info: '\u25cb', // ○
21
- };
17
+ export { STATE_GLYPHS, type StatusState } from '@pellux/goodvibes-sdk/platform/presentation';
@@ -1,6 +1,7 @@
1
1
  import { type Line } from '../types/grid.ts';
2
2
  import { BORDERS, COLORS } from './layout.ts';
3
3
  import { renderConversationNotice } from './conversation-surface.ts';
4
+ import { activeUiTones, getActiveThemeMode } from './theme.ts';
4
5
 
5
6
  /** Exported for use by typeOverride callers and tests. */
6
7
  export type SystemMessageType = 'error' | 'warning' | 'info';
@@ -74,9 +75,23 @@ export function renderSystemMessage(
74
75
  const border = msgType === 'error' ? BORDERS.ERROR
75
76
  : msgType === 'warning' ? BORDERS.WARNING
76
77
  : BORDERS.INFO;
77
- const textColor = msgType === 'info' ? COLORS.DIM_TEXT : border.color;
78
+ // System-message notices paint the marker and body on the TRANSPARENT
79
+ // terminal background, so the accent/body must resolve per render to stay
80
+ // legible on a light terminal. Dark is byte-identical to today: the agent's
81
+ // BORDERS.* accents (which include a yellow/cyan that are unreadable on light)
82
+ // are preserved in dark and only swapped for the legible chrome/state tones in
83
+ // light mode. (chrome.bad dark == BORDERS.ERROR.color; only warn/info/faint
84
+ // differ, and only in light.)
85
+ const t = activeUiTones();
86
+ const light = getActiveThemeMode() === 'light';
87
+ const accent = light
88
+ ? (msgType === 'error' ? t.chrome.bad : msgType === 'warning' ? t.chrome.warn : t.state.info)
89
+ : border.color;
90
+ const textColor = msgType === 'info'
91
+ ? (light ? t.chrome.faint : COLORS.DIM_TEXT)
92
+ : accent;
78
93
  return renderConversationNotice(content, width, {
79
- accent: border.color,
94
+ accent,
80
95
  text: textColor,
81
96
  dim: msgType === 'info',
82
97
  }, border.char);