@pellux/goodvibes-agent 1.0.42 → 1.0.44

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.
@@ -5,9 +5,9 @@ import type {
5
5
  AgentWorkspaceLocalEditor,
6
6
  AgentWorkspaceRuntimeSnapshot,
7
7
  } from '../input/agent-workspace.ts';
8
- import { formatAgentRecordReviewState, formatAgentRecordSource } from '../agent/record-labels.ts';
8
+ import { formatAgentRecordReviewState } from '../agent/record-labels.ts';
9
9
  import type { Line } from '../types/grid.ts';
10
- import { wrapText } from '../utils/terminal-width.ts';
10
+ import { truncateDisplay, wrapText } from '../utils/terminal-width.ts';
11
11
  import { GLYPHS } from './ui-primitives.ts';
12
12
  import {
13
13
  getFullscreenWorkspaceMetrics,
@@ -62,31 +62,78 @@ function actionCommand(action: AgentWorkspaceAction): string {
62
62
  return action.command ?? '(guidance)';
63
63
  }
64
64
 
65
- function setupChecklistLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
66
- const readyCount = snapshot.setupChecklist.filter((item) => item.status === 'ready').length;
67
- const recommendedCount = snapshot.setupChecklist.filter((item) => item.status === 'recommended').length;
68
- const blockedCount = snapshot.setupChecklist.filter((item) => item.status === 'blocked').length;
65
+ function setupCounts(snapshot: AgentWorkspaceRuntimeSnapshot): { ready: number; recommended: number; optional: number; blocked: number } {
66
+ return {
67
+ ready: snapshot.setupChecklist.filter((item) => item.status === 'ready').length,
68
+ recommended: snapshot.setupChecklist.filter((item) => item.status === 'recommended').length,
69
+ optional: snapshot.setupChecklist.filter((item) => item.status === 'optional').length,
70
+ blocked: snapshot.setupChecklist.filter((item) => item.status === 'blocked').length,
71
+ };
72
+ }
73
+
74
+ function setupStatusLabel(status: AgentWorkspaceRuntimeSnapshot['setupChecklist'][number]['status']): string {
75
+ return status === 'ready'
76
+ ? 'Ready'
77
+ : status === 'recommended'
78
+ ? 'Recommended'
79
+ : status === 'blocked'
80
+ ? 'Blocked'
81
+ : 'Optional';
82
+ }
83
+
84
+ function compactText(text: string, maxWidth = 104): string {
85
+ const normalized = text.replace(/\s+/g, ' ').trim();
86
+ if (normalized.length === 0) return '';
87
+ const firstSentence = normalized.match(/^.*?[.!?](?:\s|$)/)?.[0]?.trim();
88
+ const source = firstSentence && firstSentence.length <= maxWidth ? firstSentence : normalized;
89
+ return truncateDisplay(source, maxWidth, '...');
90
+ }
91
+
92
+ function actionMetaLine(action: AgentWorkspaceAction): ContextLine {
93
+ return {
94
+ text: `${actionCommand(action)}; ${action.safety}`,
95
+ fg: action.kind === 'command' ? PALETTE.info : safetyColor(action),
96
+ };
97
+ }
98
+
99
+ function setupAttentionItems(snapshot: AgentWorkspaceRuntimeSnapshot, limit: number): AgentWorkspaceRuntimeSnapshot['setupChecklist'] {
100
+ return [
101
+ ...snapshot.setupChecklist.filter((item) => item.status === 'blocked'),
102
+ ...snapshot.setupChecklist.filter((item) => item.status === 'recommended'),
103
+ ...snapshot.setupChecklist.filter((item) => item.status === 'optional'),
104
+ ].slice(0, limit);
105
+ }
106
+
107
+ function conciseSetupLine(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine {
108
+ const counts = setupCounts(snapshot);
109
+ return {
110
+ text: `Setup: ${counts.ready}/${snapshot.setupChecklist.length} ready; ${counts.recommended} recommended; ${counts.blocked} blocked.`,
111
+ fg: counts.blocked > 0 ? PALETTE.warn : PALETTE.info,
112
+ };
113
+ }
114
+
115
+ function setupOverviewLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
116
+ const counts = setupCounts(snapshot);
117
+ const nextItems = setupAttentionItems(snapshot, 3);
69
118
  const lines: ContextLine[] = [
70
- { text: 'Setup Checklist', fg: PALETTE.title, bold: true },
71
- { text: `${readyCount}/${snapshot.setupChecklist.length} ready; ${recommendedCount} recommended; ${blockedCount} blocked`, fg: blockedCount > 0 ? PALETTE.warn : PALETTE.info },
119
+ { text: 'Setup Overview', fg: PALETTE.title, bold: true },
120
+ { text: `${counts.ready}/${snapshot.setupChecklist.length} ready; ${counts.recommended} recommended; ${counts.optional} optional; ${counts.blocked} blocked.`, fg: counts.blocked > 0 ? PALETTE.warn : PALETTE.info },
121
+ { text: `Chat: ${snapshot.provider} / ${snapshot.modelDisplayName}.`, fg: PALETTE.info },
122
+ { text: `Local: ${snapshot.localPersonaCount} personas, ${snapshot.localSkillCount} skills, ${snapshot.localRoutineCount} routines, ${snapshot.localMemoryCount} memories.`, fg: PALETTE.info },
72
123
  ];
73
- for (const item of snapshot.setupChecklist) {
74
- const command = item.command ? ` -> ${item.command}` : '';
124
+ if (nextItems.length > 0) {
125
+ const item = nextItems[0]!;
75
126
  lines.push({
76
- text: `${item.status.toUpperCase()} ${item.label}${command}`,
127
+ text: `Next: ${item.label} (${setupStatusLabel(item.status).toLowerCase()})`,
77
128
  fg: setupStatusColor(item.status),
78
129
  bold: item.status === 'blocked',
79
130
  });
80
- lines.push({ text: ` ${item.detail}`, fg: PALETTE.muted });
81
131
  }
82
132
  return lines;
83
133
  }
84
134
 
85
135
  function homeNextActionLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
86
- const blocked = snapshot.setupChecklist.filter((item) => item.status === 'blocked');
87
- const recommended = snapshot.setupChecklist.filter((item) => item.status === 'recommended');
88
- const optional = snapshot.setupChecklist.filter((item) => item.status === 'optional');
89
- const candidates = [...blocked, ...recommended, ...optional].slice(0, 5);
136
+ const candidates = setupAttentionItems(snapshot, 3);
90
137
  if (candidates.length === 0) {
91
138
  return [
92
139
  { text: 'Next Actions', fg: PALETTE.title, bold: true },
@@ -95,13 +142,11 @@ function homeNextActionLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLi
95
142
  }
96
143
  const lines: ContextLine[] = [{ text: 'Next Actions', fg: PALETTE.title, bold: true }];
97
144
  for (const item of candidates) {
98
- const command = item.command ? ` -> ${item.command}` : '';
99
145
  lines.push({
100
- text: `${item.status.toUpperCase()} ${item.label}${command}`,
146
+ text: `${setupStatusLabel(item.status)}: ${item.label}`,
101
147
  fg: setupStatusColor(item.status),
102
148
  bold: item.status === 'blocked',
103
149
  });
104
- lines.push({ text: ` ${item.detail}`, fg: PALETTE.muted });
105
150
  }
106
151
  return lines;
107
152
  }
@@ -115,79 +160,41 @@ function companionAccessLine(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLi
115
160
  : 'missing';
116
161
  const error = access.tokenError ? `; token read error ${access.tokenError}` : '';
117
162
  return {
118
- text: `Companion: ${access.surface}; token ${tokenState}; QR ${access.qrCommand}; manual token text hidden${error}.`,
163
+ text: `Companion: ${access.surface}; token ${tokenState}; QR ${access.qrCommand}${error}.`,
119
164
  fg: access.pairingReady ? PALETTE.good : PALETTE.warn,
120
165
  };
121
166
  }
122
167
 
123
- function discoverySummaryLine(label: string, summary: AgentWorkspaceRuntimeSnapshot['discoveredBehavior']['personas'], actionLabel: string): ContextLine[] {
124
- if (summary.count === 0) return [];
125
- const names = summary.names.length > 0
126
- ? ` ${summary.names.join(', ')}${summary.count > summary.names.length ? `, +${summary.count - summary.names.length} more` : ''}.`
127
- : '';
128
- return [
129
- { text: `${label}: ${summary.count} discovered; project ${summary.projectLocalCount}; global ${summary.globalCount}.`, fg: PALETTE.info, bold: true },
130
- { text: ` Choose ${actionLabel} to preview, then use the import form after review.${names}`, fg: PALETTE.muted },
131
- ];
132
- }
133
-
134
- function behaviorDiscoveryLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
135
- const lines: ContextLine[] = [
136
- ...discoverySummaryLine('Discovered personas', snapshot.discoveredBehavior.personas, 'Personas -> Discover persona files'),
137
- ...discoverySummaryLine('Discovered skills', snapshot.discoveredBehavior.skills, 'Skills -> Discover skill files'),
138
- ...discoverySummaryLine('Discovered routines', snapshot.discoveredBehavior.routines, 'Routines -> Discover routine files'),
139
- ];
140
- if (lines.length === 0) return [];
141
- return [
142
- { text: '' },
143
- { text: 'Discovered Behavior Files', fg: PALETTE.title, bold: true },
144
- ...lines,
145
- ];
146
- }
147
-
148
- function localLibraryLines(
168
+ function compactLocalLibraryLines(
149
169
  title: string,
150
170
  items: readonly AgentWorkspaceRuntimeSnapshot['localPersonas'][number][],
151
171
  emptyText: string,
152
172
  selectedId: string | null,
153
173
  ): ContextLine[] {
154
- const lines: ContextLine[] = [
155
- { text: title, fg: PALETTE.title, bold: true },
156
- ];
174
+ const lines: ContextLine[] = [];
157
175
  if (items.length === 0) {
158
- lines.push({ text: emptyText, fg: PALETTE.warn });
176
+ lines.push({ text: `${title}: 0. ${emptyText}`, fg: PALETTE.warn });
159
177
  return lines;
160
178
  }
161
- for (const item of items.slice(0, 8)) {
162
- const selected = item.id === selectedId;
163
- const status = [
164
- selected ? 'selected' : '',
165
- item.active ? 'active' : '',
166
- item.enabled === true ? 'enabled' : item.enabled === false ? 'disabled' : '',
167
- item.scope && item.cls ? `${item.scope}/${item.cls}` : '',
168
- item.confidence !== undefined ? `${item.confidence}%` : '',
169
- item.requirementCount !== undefined && item.requirementCount > 0
170
- ? (item.missingRequirementCount && item.missingRequirementCount > 0 ? `needs ${item.missingRequirementCount}/${item.requirementCount}` : `ready ${item.requirementCount}/${item.requirementCount}`)
171
- : '',
172
- formatAgentRecordReviewState(item.reviewState),
173
- item.source ? `origin ${item.source}` : '',
174
- item.startCount !== undefined ? `starts ${item.startCount}` : '',
175
- ].filter(Boolean).join(' / ');
176
- const tags = item.tags.length > 0 ? ` tags ${item.tags.join(',')}` : '';
177
- const triggers = item.triggers.length > 0 ? ` triggers ${item.triggers.join(',')}` : '';
178
- const marker = selected ? `${GLYPHS.navigation.selected} ` : '';
179
- lines.push({
180
- text: `${marker}${item.id}: ${item.name} (${status})`,
181
- fg: item.reviewState === 'stale' ? PALETTE.warn : PALETTE.info,
182
- bold: selected || item.active === true,
183
- });
184
- lines.push({ text: ` ${item.description}${tags}${triggers}`, fg: PALETTE.muted });
185
- if (item.missingRequirements && item.missingRequirements.length > 0) {
186
- lines.push({ text: ` missing setup: ${item.missingRequirements.join(', ')}`, fg: PALETTE.warn });
187
- }
188
- }
189
- if (items.length > 8) {
190
- lines.push({ text: `${items.length - 8} more item(s). Open the library command for the full list.`, fg: PALETTE.dim });
179
+ const selected = items.find((item) => item.id === selectedId) ?? items[0]!;
180
+ const status = [
181
+ selected.active ? 'active' : '',
182
+ selected.enabled === true ? 'enabled' : selected.enabled === false ? 'disabled' : '',
183
+ selected.scope && selected.cls ? `${selected.scope}/${selected.cls}` : '',
184
+ selected.confidence !== undefined ? `${selected.confidence}%` : '',
185
+ selected.requirementCount !== undefined && selected.requirementCount > 0
186
+ ? (selected.missingRequirementCount && selected.missingRequirementCount > 0 ? `needs ${selected.missingRequirementCount}/${selected.requirementCount}` : `ready ${selected.requirementCount}/${selected.requirementCount}`)
187
+ : '',
188
+ formatAgentRecordReviewState(selected.reviewState),
189
+ selected.startCount !== undefined ? `starts ${selected.startCount}` : '',
190
+ ].filter(Boolean).join(', ');
191
+ lines.push({
192
+ text: `${title}: ${items.length}; selected ${selected.name}${status ? ` (${status})` : ''}.`,
193
+ fg: selected.reviewState === 'stale' ? PALETTE.warn : PALETTE.info,
194
+ bold: selected.active === true,
195
+ });
196
+ if (selected.missingRequirements && selected.missingRequirements.length > 0) {
197
+ lines.push({ text: `Missing setup: ${selected.missingRequirements.join(', ')}`, fg: PALETTE.warn });
191
198
  }
192
199
  return lines;
193
200
  }
@@ -234,23 +241,18 @@ function routineNextActionLine(snapshot: AgentWorkspaceRuntimeSnapshot): Context
234
241
  return { text: 'Next routine action: Start selected in the main conversation, inspect receipts, or reconcile connected schedules.', fg: PALETTE.info, bold: true };
235
242
  }
236
243
 
237
- function routineScheduleReceiptLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
244
+ function compactRoutineReceiptLine(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine {
238
245
  const latest = snapshot.latestRoutineScheduleReceipt;
239
- const lines: ContextLine[] = [
240
- {
241
- text: `Promotion receipts: ${snapshot.routineScheduleReceiptCount}; created: ${snapshot.successfulRoutineScheduleReceiptCount}; failed: ${snapshot.failedRoutineScheduleReceiptCount}`,
242
- fg: snapshot.failedRoutineScheduleReceiptCount > 0 ? PALETTE.warn : PALETTE.info,
243
- },
244
- ];
245
- if (latest) {
246
- lines.push({
247
- text: `Latest receipt: ${latest.id} ${latest.status} routine=${latest.routineId} schedule="${latest.scheduleName}" ${latest.scheduleKind} ${latest.scheduleValue}`,
248
- fg: latest.status === 'failed' ? PALETTE.warn : PALETTE.good,
249
- });
250
- } else {
251
- lines.push({ text: 'No schedule promotion receipts yet. Confirm a routine promotion from Automation when ready.', fg: PALETTE.muted });
246
+ if (!latest) {
247
+ return {
248
+ text: `Promotion receipts: ${snapshot.routineScheduleReceiptCount}; none created yet.`,
249
+ fg: PALETTE.muted,
250
+ };
252
251
  }
253
- return lines;
252
+ return {
253
+ text: `Promotion receipts: ${snapshot.routineScheduleReceiptCount}; latest ${latest.status} ${latest.routineId}.`,
254
+ fg: latest.status === 'failed' ? PALETTE.warn : PALETTE.good,
255
+ };
254
256
  }
255
257
 
256
258
  function automationNextActionLine(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine {
@@ -272,253 +274,107 @@ function automationNextActionLine(snapshot: AgentWorkspaceRuntimeSnapshot): Cont
272
274
  return { text: 'Next automation action: Create a reminder, or create/import a routine before recurring workflow promotion.', fg: PALETTE.warn, bold: true };
273
275
  }
274
276
 
275
- function profileLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
276
- const lines: ContextLine[] = [
277
- { text: 'Agent Profiles', fg: PALETTE.title, bold: true },
278
- ];
279
- if (snapshot.runtimeProfiles.length === 0) {
280
- lines.push({ text: 'No isolated Agent profiles yet. Use Create Agent profile in this workspace.', fg: PALETTE.warn });
281
- return lines;
282
- }
283
- for (const profile of snapshot.runtimeProfiles.slice(0, 6)) {
284
- const starter = profile.starterTemplateId ? ` starter=${profile.starterTemplateId}` : ' starter=none';
285
- const created = profile.createdAt ? ` created ${profile.createdAt.slice(0, 10)}` : '';
286
- const states = [
287
- profile.id === snapshot.activeRuntimeProfile ? 'active' : '',
288
- profile.id === snapshot.selectedRuntimeProfile ? 'default' : '',
289
- ].filter(Boolean).join(', ');
290
- const stateText = states ? ` [${states}]` : '';
291
- lines.push({
292
- text: `${profile.id}${stateText}${starter}${created}`,
293
- fg: profile.id === snapshot.selectedRuntimeProfile ? PALETTE.good : PALETTE.info,
294
- bold: profile.id === snapshot.activeRuntimeProfile || profile.id === snapshot.selectedRuntimeProfile,
295
- });
296
- lines.push({ text: ` home: ${profile.homeDirectory}`, fg: PALETTE.muted });
297
- }
298
- if (snapshot.runtimeProfiles.length > 6) {
299
- lines.push({ text: `${snapshot.runtimeProfiles.length - 6} more profile(s).`, fg: PALETTE.dim });
300
- }
301
- return lines;
302
- }
303
-
304
- function starterTemplateLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLine[] {
305
- const lines: ContextLine[] = [
306
- { text: 'Starter Templates', fg: PALETTE.title, bold: true },
307
- ];
308
- for (const template of snapshot.runtimeStarterTemplates.slice(0, 6)) {
309
- const origin = template.source === 'local' ? 'Local' : formatAgentRecordSource(template.source);
310
- lines.push({
311
- text: `${template.id}: ${template.name} [${origin}]`,
312
- fg: template.source === 'local' ? PALETTE.good : PALETTE.info,
313
- bold: template.id === 'research',
314
- });
315
- lines.push({
316
- text: ` ${template.description} Persona ${template.personaName}; skills ${template.skillNames.join(', ')}; routines ${template.routineNames.join(', ')}.`,
317
- fg: PALETTE.muted,
318
- });
319
- }
320
- if (snapshot.runtimeStarterTemplates.length > 6) {
321
- lines.push({ text: `${snapshot.runtimeStarterTemplates.length - 6} more starter template(s).`, fg: PALETTE.dim });
322
- }
323
- return lines;
324
- }
325
-
326
277
  function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCategory, snapshot: AgentWorkspaceRuntimeSnapshot | null): ContextLine[] {
327
278
  if (!snapshot) return [{ text: 'Runtime context is not loaded yet.', fg: PALETTE.warn }];
328
- const base: ContextLine[] = [{ text: 'Live Agent Context', fg: PALETTE.title, bold: true }];
279
+ const base: ContextLine[] = [];
329
280
  if (category.id === 'home') {
330
281
  base.push(
331
282
  { text: `Chat route: ${snapshot.provider} / ${snapshot.modelDisplayName}`, fg: PALETTE.info },
332
283
  { text: `Session: ${snapshot.sessionId}`, fg: PALETTE.muted },
333
284
  { text: `Policy: ${snapshot.executionPolicy}; delegated review ${snapshot.delegatedReviewPolicy}`, fg: PALETTE.good },
334
- { text: `Knowledge: ${snapshot.knowledgeRoute}; ${snapshot.knowledgeIsolation}; no fallback`, fg: PALETTE.good },
335
- { text: `Local: ${snapshot.localMemoryCount} memory, ${snapshot.localNoteCount} notes, ${snapshot.localPersonaCount} personas, ${snapshot.localSkillCount} skills, ${snapshot.localRoutineCount} routines.`, fg: PALETTE.info },
336
- { text: `Channels: ${snapshot.channels.filter((channel) => channel.ready).length}/${snapshot.channels.length} ready; MCP ${snapshot.mcpConnectedServerCount}/${snapshot.mcpServerCount} connected; voice/media ${snapshot.voiceProviderCount}/${snapshot.mediaProviderCount}.`, fg: PALETTE.info },
337
- { text: '' },
285
+ conciseSetupLine(snapshot),
338
286
  ...homeNextActionLines(snapshot),
339
287
  );
340
288
  } else if (category.id === 'setup') {
341
289
  base.push(
342
- { text: `Connection: ${snapshot.runtimeBaseUrl}`, fg: PALETTE.info },
343
- { text: 'Agent role: interactive operator TUI; setup changes here are Agent-local.', fg: PALETTE.good },
344
- ...setupChecklistLines(snapshot),
345
- ...behaviorDiscoveryLines(snapshot),
346
- { text: '' },
347
- { text: `Workspace: ${snapshot.workingDirectory}`, fg: PALETTE.muted },
348
- { text: `Home: ${snapshot.homeDirectory}`, fg: PALETTE.muted },
290
+ ...setupOverviewLines(snapshot),
349
291
  );
350
292
  } else if (category.id === 'artifacts') {
351
293
  const mediaReady = snapshot.voiceMediaReadiness.readyMediaProviderCount;
352
294
  base.push(
353
- { text: `Chat route: ${snapshot.provider} / ${snapshot.modelDisplayName}`, fg: PALETTE.info },
354
- { text: `Agent Knowledge route: ${snapshot.knowledgeRoute}`, fg: PALETTE.info },
355
- { text: `Media providers: ${mediaReady}/${snapshot.mediaProviderCount} ready; generation-capable ${snapshot.mediaGenerationProviderCount}.`, fg: mediaReady > 0 ? PALETTE.good : PALETTE.warn },
356
- { text: 'Use this area for concrete files and generated output: attach images, export transcripts, ingest reviewed sources, inspect source records, and generate media.', fg: PALETTE.good },
357
- { text: 'Agent Knowledge ingest writes only to the isolated Agent segment. Default knowledge and non-Agent routes are not fallback storage.', fg: PALETTE.warn },
358
- { text: 'Generated media is stored as artifacts and referenced by id; the TUI should not print inline base64.', fg: PALETTE.muted },
359
- { text: `Workspace path: ${snapshot.workingDirectory}`, fg: PALETTE.muted },
295
+ { text: `Chat: ${snapshot.provider} / ${snapshot.modelDisplayName}; Knowledge: ${snapshot.knowledgeRoute}`, fg: PALETTE.info },
296
+ { text: `Media: ${mediaReady}/${snapshot.mediaProviderCount} ready; generation ${snapshot.mediaGenerationProviderCount}.`, fg: mediaReady > 0 ? PALETTE.good : PALETTE.warn },
297
+ { text: 'Files: attach, export, inspect, ingest reviewed sources, or generate media.', fg: PALETTE.good },
298
+ { text: 'Knowledge ingest and media generation require explicit actions.', fg: PALETTE.warn },
360
299
  );
361
300
  } else if (category.id === 'channels') {
362
301
  const enabledCount = snapshot.channels.filter((channel) => channel.enabled).length;
363
302
  const readyCount = snapshot.channels.filter((channel) => channel.ready).length;
364
303
  const configuredDefaults = snapshot.channels.filter((channel) => channel.defaultTarget === 'configured').length;
365
- const readyChannels = snapshot.channels.filter((channel) => channel.ready).map((channel) => channel.label);
366
304
  const needsTarget = snapshot.channels.filter((channel) => channel.setupState === 'needs-target');
367
305
  const needsConfig = snapshot.channels.filter((channel) => channel.setupState === 'needs-config');
368
306
  const nextAttentionChannel = needsConfig[0] ?? needsTarget[0] ?? snapshot.channels.find((channel) => !channel.enabled);
369
- const disabledChannels = snapshot.channels.filter((channel) => !channel.enabled).map((channel) => channel.label);
370
- const disabledPreview = disabledChannels.slice(0, 6).join(', ');
371
- const disabledSuffix = disabledChannels.length > 6 ? `, +${disabledChannels.length - 6} more` : '';
372
- const orderedChannels = [
373
- ...snapshot.channels.filter((channel) => channel.enabled),
374
- ...snapshot.channels.filter((channel) => !channel.enabled),
375
- ].slice(0, 3);
376
307
  base.push(
377
- { text: `GoodVibes API: ${snapshot.runtimeBaseUrl}`, fg: PALETTE.info },
308
+ { text: `API: ${snapshot.runtimeBaseUrl}`, fg: PALETTE.info },
378
309
  companionAccessLine(snapshot),
379
- { text: `Readiness: ${readyCount}/${snapshot.channels.length} ready; ${enabledCount} enabled; ${configuredDefaults} default target(s) configured.`, fg: PALETTE.info },
380
- { text: 'Setup path: pair companion -> inspect readiness -> review accounts/policies/status -> fix one channel -> add explicit notification target if needed.', fg: PALETTE.good },
381
- { text: `Next channel action: ${nextAttentionChannel ? `${nextAttentionChannel.label} - ${nextAttentionChannel.nextStep}` : 'All enabled channels are ready; keep delivery explicit and review policies before sending.'}`, fg: nextAttentionChannel ? PALETTE.warn : PALETTE.good },
382
- { text: `Ready channels: ${readyChannels.join(', ') || 'none'}.`, fg: readyChannels.length > 0 ? PALETTE.good : PALETTE.warn },
383
- { text: `Needs default target: ${needsTarget.map((channel) => `${channel.label} -> ${channel.defaultTargetKeys.join('|')}`).join(', ') || 'none'}.`, fg: needsTarget.length > 0 ? PALETTE.warn : PALETTE.muted },
384
- { text: `Needs config: ${needsConfig.map((channel) => `${channel.label} -> ${channel.missingRequiredKeys.join('|')}`).join(', ') || 'none'}.`, fg: needsConfig.length > 0 ? PALETTE.warn : PALETTE.muted },
385
- { text: `Disabled channels: ${disabledPreview || 'none'}${disabledSuffix}.`, fg: PALETTE.dim },
386
- { text: 'Safety: no secret values; sends and public exposure require explicit user action and Agent policy.', fg: PALETTE.warn },
310
+ { text: `Channels: ${readyCount}/${snapshot.channels.length} ready; ${enabledCount} enabled; ${configuredDefaults} target(s).`, fg: PALETTE.info },
311
+ { text: `Next: ${nextAttentionChannel ? `${nextAttentionChannel.label} - ${compactText(nextAttentionChannel.nextStep)}` : 'All enabled channels ready.'}`, fg: nextAttentionChannel ? PALETTE.warn : PALETTE.good },
312
+ { text: 'Safety: secrets hidden; sends require explicit action.', fg: PALETTE.warn },
387
313
  );
388
- for (const channel of orderedChannels) {
389
- const ready = channel.ready ? 'ready' : `${channel.missingConfigCount} missing`;
390
- base.push({
391
- text: `${channel.label}: ${channel.setupState}; ${ready}; target ${channel.defaultTarget}; delivery ${channel.delivery}; risk ${channel.risk}.`,
392
- fg: channel.ready ? PALETTE.good : channel.enabled ? PALETTE.warn : PALETTE.dim,
393
- });
394
- }
395
314
  } else if (category.id === 'knowledge') {
396
315
  base.push(
397
- { text: `Route family: ${snapshot.knowledgeRoute}/{status,ask,search}`, fg: PALETTE.info },
398
- { text: `Isolation: ${snapshot.knowledgeIsolation}; no default knowledge or non-Agent fallback`, fg: PALETTE.good },
399
- { text: 'Ingest: URL, URL-list, and bookmark imports require explicit --yes and write only to Agent Knowledge.', fg: PALETTE.info },
400
- { text: 'Review: queue, issues, candidates, reports, reindex, and consolidation stay inside the Agent segment.', fg: PALETTE.muted },
401
- { text: 'Agent-owned content appears here only after explicit Agent knowledge ingestion.', fg: PALETTE.muted },
316
+ { text: `Route: ${snapshot.knowledgeRoute}; isolation ${snapshot.knowledgeIsolation}.`, fg: PALETTE.info },
317
+ { text: 'Ask/search, ingest, review, reindex, and reports stay Agent-owned.', fg: PALETTE.good },
318
+ { text: 'Ingest requires explicit confirmation.', fg: PALETTE.warn },
402
319
  );
403
320
  } else if (category.id === 'research') {
404
321
  base.push(
405
- { text: `Chat route: ${snapshot.provider} / ${snapshot.modelDisplayName}`, fg: PALETTE.info },
406
- { text: `Agent Knowledge route: ${snapshot.knowledgeRoute}`, fg: PALETTE.info },
407
- { text: `Browser tools: ${snapshot.voiceMediaReadiness.browserToolState}; public base URL ${snapshot.browserToolPublicBaseUrl}.`, fg: snapshot.browserToolExposureEnabled ? PALETTE.warn : PALETTE.muted },
408
- { text: snapshot.voiceMediaReadiness.browserToolNextStep, fg: PALETTE.muted },
409
- { text: 'Research requests are submitted to the main conversation. Agent may use connected read-only web tools when the user asks.', fg: PALETTE.good },
410
- { text: 'URL references and web research do not ingest into Agent Knowledge. Ingestion is a separate confirmed Agent Knowledge action.', fg: PALETTE.warn },
411
- { text: 'Use Agent Knowledge ask/search first for known source-backed context; use web research for current or external sources.', fg: PALETTE.info },
412
- { text: 'External sends, browser-side effects, package installs, and connected-host mutations are outside this read-only research lane.', fg: PALETTE.warn },
322
+ { text: `Chat: ${snapshot.provider} / ${snapshot.modelDisplayName}; Knowledge: ${snapshot.knowledgeRoute}`, fg: PALETTE.info },
323
+ { text: `Browser: ${snapshot.voiceMediaReadiness.browserToolState}; public URL ${snapshot.browserToolPublicBaseUrl}.`, fg: snapshot.browserToolExposureEnabled ? PALETTE.warn : PALETTE.muted },
324
+ { text: 'Research is read-only. Knowledge ingest is a separate confirmed action.', fg: PALETTE.good },
325
+ { text: compactText(snapshot.voiceMediaReadiness.browserToolNextStep), fg: PALETTE.muted },
413
326
  );
414
327
  } else if (category.id === 'tools') {
415
328
  base.push(
416
329
  { text: `MCP servers: ${snapshot.mcpConnectedServerCount}/${snapshot.mcpServerCount} connected; quarantined ${snapshot.mcpQuarantinedServerCount}; allow-all ${snapshot.mcpAllowAllServerCount}.`, fg: snapshot.mcpQuarantinedServerCount > 0 || snapshot.mcpAllowAllServerCount > 0 ? PALETTE.warn : PALETTE.info },
417
- { text: 'Open MCP workspace for live server status, tool inventory, config paths, and confirmed add/remove/reload actions.', fg: PALETTE.info },
418
- { text: 'Add/update requires typed confirmation and dispatches through the TUI command router.', fg: PALETTE.good },
419
- { text: 'Trust changes remain explicit; allow-all is kept behind the settings workspace.', fg: PALETTE.warn },
420
- { text: 'Useful first actions: /mcp review, /mcp tools, /mcp config, and Add MCP server.', fg: PALETTE.muted },
421
- { text: 'Normal assistant chat can use tools serially when policy allows; onboarding does not start hidden tool work.', fg: PALETTE.muted },
330
+ { text: 'Add/update/reload and trust changes require confirmation.', fg: PALETTE.good },
331
+ { text: 'Start: /mcp review, /mcp tools, /mcp config, Add MCP server.', fg: PALETTE.muted },
422
332
  );
423
333
  } else if (category.id === 'voice-media') {
424
334
  const readiness = snapshot.voiceMediaReadiness;
425
- const voiceRows = readiness.voiceProviders.slice(0, 6);
426
- const mediaRows = readiness.mediaProviders.slice(0, 6);
427
335
  base.push(
428
- { text: `Voice providers: ${snapshot.voiceProviderCount}; streaming TTS: ${snapshot.voiceStreamingProviderCount}; STT: ${snapshot.voiceSttProviderCount}; realtime: ${snapshot.voiceRealtimeProviderCount}.`, fg: PALETTE.info },
429
- { text: `Voice interaction: ${snapshot.voiceSurfaceEnabled ? 'enabled' : 'disabled'}; ready providers ${readiness.readyVoiceProviderCount}/${snapshot.voiceProviderCount}.`, fg: snapshot.voiceSurfaceEnabled ? PALETTE.warn : PALETTE.muted },
430
- { text: `TTS config: provider ${snapshot.ttsProvider}; voice ${snapshot.ttsVoice}; response model ${snapshot.ttsResponseModel}.`, fg: PALETTE.info },
431
- { text: `Selected TTS readiness: ${readiness.selectedTtsProviderLabel} -> ${readiness.selectedTtsProviderStatus}; voice ${readiness.ttsVoiceConfigured ? 'configured' : 'default'}; response route ${readiness.ttsResponseRouteConfigured ? 'configured' : 'chat route'}.`, fg: readiness.selectedTtsProviderStatus === 'ready' ? PALETTE.good : PALETTE.warn },
432
- { text: `Media providers: ${snapshot.mediaProviderCount}; understanding: ${snapshot.mediaUnderstandingProviderCount}; generation: ${snapshot.mediaGenerationProviderCount}.`, fg: PALETTE.info },
433
- { text: `Ready media providers: ${readiness.readyMediaProviderCount}/${snapshot.mediaProviderCount}.`, fg: readiness.readyMediaProviderCount > 0 ? PALETTE.good : PALETTE.warn },
434
- { text: `Browser tools: ${readiness.browserToolState}; public base URL ${snapshot.browserToolPublicBaseUrl}.`, fg: snapshot.browserToolExposureEnabled ? PALETTE.warn : PALETTE.muted },
435
- { text: readiness.browserToolNextStep, fg: PALETTE.muted },
436
- { text: 'Voice provider readiness', fg: PALETTE.title, bold: true },
437
- );
438
- for (const provider of voiceRows) {
439
- const selected = provider.selected ? 'selected; ' : '';
440
- const missing = provider.missingSecretKeyOptions.length > 0 ? `; needs ${provider.missingSecretKeyOptions.join('|')}` : '';
441
- base.push({
442
- text: `${provider.label}: ${selected}${provider.setupState}; ${provider.features.join(', ') || 'registered'}${missing}.`,
443
- fg: provider.setupState === 'ready' ? PALETTE.good : provider.setupState === 'needs-secret' ? PALETTE.warn : PALETTE.muted,
444
- });
445
- }
446
- if (snapshot.voiceProviderCount > voiceRows.length) base.push({ text: `${snapshot.voiceProviderCount - voiceRows.length} more voice provider(s).`, fg: PALETTE.dim });
447
- base.push({ text: 'Media provider readiness', fg: PALETTE.title, bold: true });
448
- for (const provider of mediaRows) {
449
- const missing = provider.missingSecretKeyOptions.length > 0 ? `; needs ${provider.missingSecretKeyOptions.join('|')}` : '';
450
- base.push({
451
- text: `${provider.label}: ${provider.setupState}; ${provider.features.join(', ') || 'registered'}${missing}.`,
452
- fg: provider.setupState === 'ready' ? PALETTE.good : provider.setupState === 'needs-secret' ? PALETTE.warn : PALETTE.muted,
453
- });
454
- }
455
- if (snapshot.mediaProviderCount > mediaRows.length) base.push({ text: `${snapshot.mediaProviderCount - mediaRows.length} more media provider(s).`, fg: PALETTE.dim });
456
- for (const step of readiness.nextSteps.slice(0, 4)) base.push({ text: `Next: ${step}`, fg: PALETTE.info });
457
- base.push(
458
- { text: 'No secret values are rendered. Voice, browser, and generated media side effects require explicit user action.', fg: PALETTE.warn },
459
- { text: 'Image input uses prompt attachments; media generation/provider setup stays behind explicit commands and configured providers.', fg: PALETTE.muted },
336
+ { text: `Voice: ${readiness.readyVoiceProviderCount}/${snapshot.voiceProviderCount} ready; TTS ${snapshot.ttsProvider}; voice ${snapshot.ttsVoice}.`, fg: readiness.readyVoiceProviderCount > 0 ? PALETTE.good : PALETTE.warn },
337
+ { text: `Media: ${readiness.readyMediaProviderCount}/${snapshot.mediaProviderCount} ready; generation ${snapshot.mediaGenerationProviderCount}.`, fg: readiness.readyMediaProviderCount > 0 ? PALETTE.good : PALETTE.warn },
338
+ { text: `Browser: ${readiness.browserToolState}; public URL ${snapshot.browserToolPublicBaseUrl}.`, fg: snapshot.browserToolExposureEnabled ? PALETTE.warn : PALETTE.muted },
339
+ { text: readiness.nextSteps[0] ? `Next: ${compactText(readiness.nextSteps[0])}` : 'Next: voice/media setup is ready.', fg: readiness.nextSteps.length > 0 ? PALETTE.info : PALETTE.good },
340
+ { text: 'Secrets hidden; voice, browser, and media side effects require explicit action.', fg: PALETTE.warn },
460
341
  );
461
342
  } else if (category.id === 'profiles') {
462
343
  const defaultProfile = snapshot.selectedRuntimeProfile
463
344
  ? `${snapshot.selectedRuntimeProfile}${snapshot.selectedRuntimeProfileExists ? '' : ' (missing)'}`
464
345
  : '(base Agent home)';
465
346
  base.push(
466
- { text: `Active Agent profile: ${snapshot.activeRuntimeProfile}`, fg: PALETTE.info },
467
- { text: `Default for next launch: ${defaultProfile}`, fg: snapshot.selectedRuntimeProfileExists || !snapshot.selectedRuntimeProfile ? PALETTE.info : PALETTE.warn },
468
- { text: `Agent profiles under this home: ${snapshot.runtimeProfileCount}`, fg: PALETTE.info },
469
- { text: `Starter templates: ${snapshot.runtimeStarterTemplateCount}; local custom: ${snapshot.localStarterTemplateCount}`, fg: PALETTE.info },
470
- { text: `Starter ids: ${snapshot.runtimeStarterTemplates.map((template) => template.id).join(', ') || 'none'}`, fg: PALETTE.info },
471
- { text: 'Starter Templates', fg: PALETTE.title, bold: true },
472
- { text: `Agent profile root: ${snapshot.runtimeProfileRoot}`, fg: PALETTE.muted },
473
- { text: '' },
474
- ...profileLines(snapshot),
475
- { text: '' },
476
- ...starterTemplateLines(snapshot),
477
- { text: '' },
478
- { text: 'Named Agent profiles isolate local config, sessions, memory, personas, skills, routines, setup, and bundles.', fg: PALETTE.good },
479
- { text: 'Starter authoring: browse, export, edit, import, and create Agent profiles from inside this workspace.', fg: PALETTE.info },
480
- { text: 'The connected GoodVibes host stays shared unless that host is configured separately.', fg: PALETTE.warn },
481
- { text: 'Portable bundles require explicit export/import commands with real paths and --yes.', fg: PALETTE.muted },
347
+ { text: `Profiles: active ${snapshot.activeRuntimeProfile}; default ${defaultProfile}.`, fg: snapshot.selectedRuntimeProfileExists || !snapshot.selectedRuntimeProfile ? PALETTE.info : PALETTE.warn },
348
+ { text: `Local profiles: ${snapshot.runtimeProfileCount}; starters ${snapshot.runtimeStarterTemplateCount}; custom ${snapshot.localStarterTemplateCount}.`, fg: PALETTE.info },
349
+ { text: `Starter ids: ${truncateDisplay(snapshot.runtimeStarterTemplates.map((template) => template.id).join(', ') || 'none', 96, '...')}`, fg: PALETTE.muted },
350
+ { text: 'Profiles isolate local Agent config, sessions, memory, personas, skills, routines, setup, and bundles.', fg: PALETTE.good },
482
351
  );
483
352
  } else if (category.id === 'memory') {
484
353
  base.push(
485
- { text: `Session memories: ${snapshot.sessionMemoryCount}`, fg: PALETTE.info },
486
- { text: `Agent memory: ${snapshot.localMemoryCount}; prompt-active: ${snapshot.localMemoryPromptActiveCount}; review queue: ${snapshot.localMemoryReviewQueueCount}`, fg: PALETTE.info },
487
- { text: `Scratchpad notes: ${snapshot.localNoteCount}; review queue: ${snapshot.localNoteReviewQueueCount}`, fg: PALETTE.info },
488
- { text: `Local routines: ${snapshot.localRoutineCount}; enabled: ${snapshot.enabledRoutineCount}`, fg: PALETTE.info },
489
- { text: `Local skills: ${snapshot.localSkillCount}; enabled: ${snapshot.enabledSkillCount}; bundles: ${snapshot.localSkillBundleCount}; active skills: ${snapshot.activeSkillCount}`, fg: PALETTE.info },
490
- { text: `Local personas: ${snapshot.localPersonaCount}; active: ${snapshot.activePersonaName}`, fg: PALETTE.info },
491
- { text: 'Durable memory, scratchpad notes, routines, skills, and personas remain Agent-local until shared registry contracts exist.', fg: PALETTE.good },
492
- { text: 'Secrets are rejected/redacted; store secret references instead of secret values.', fg: PALETTE.warn },
493
- { text: '' },
494
- ...localLibraryLines('Agent Memory', snapshot.localMemories, 'No Agent memory yet. Create one here with Create memory.', workspace.selectedLocalLibraryItem('memory')?.id ?? null),
354
+ { text: `Memory: ${snapshot.localMemoryCount}; prompt ${snapshot.localMemoryPromptActiveCount}; queue ${snapshot.localMemoryReviewQueueCount}; session ${snapshot.sessionMemoryCount}.`, fg: PALETTE.info },
355
+ { text: `Notes: ${snapshot.localNoteCount}; skills ${snapshot.localSkillCount}/${snapshot.enabledSkillCount}; routines ${snapshot.localRoutineCount}/${snapshot.enabledRoutineCount}; personas ${snapshot.localPersonaCount}.`, fg: PALETTE.info },
356
+ { text: `Active persona: ${snapshot.activePersonaName}.`, fg: PALETTE.info },
357
+ ...compactLocalLibraryLines('Agent Memory', snapshot.localMemories, 'Create one with Create memory.', workspace.selectedLocalLibraryItem('memory')?.id ?? null),
358
+ { text: 'Secrets are rejected or redacted; use secret references.', fg: PALETTE.warn },
495
359
  );
496
360
  } else if (category.id === 'notes') {
497
361
  base.push(
498
362
  { text: `Scratchpad notes: ${snapshot.localNoteCount}; review queue: ${snapshot.localNoteReviewQueueCount}`, fg: PALETTE.info },
499
- { text: 'Notes are Agent-local working context for source triage, temporary decisions, and operator handoff.', fg: PALETTE.good },
500
- { text: 'Notes do not become memory and are not ingested into Agent Knowledge unless the user takes a separate explicit action.', fg: PALETTE.warn },
501
- { text: 'Use reviewed notes to prefill durable memory, skills, routines, or personas, or to decide that a reviewed source deserves Agent Knowledge ingest.', fg: PALETTE.muted },
502
- { text: '' },
503
- ...localLibraryLines('Scratchpad Notes', snapshot.localNotes, 'No local notes yet. Create one here with Create note.', workspace.selectedLocalLibraryItem('note')?.id ?? null),
363
+ ...compactLocalLibraryLines('Scratchpad Notes', snapshot.localNotes, 'Create one with Create note.', workspace.selectedLocalLibraryItem('note')?.id ?? null),
364
+ { text: 'Notes stay local unless promoted by explicit action.', fg: PALETTE.warn },
504
365
  );
505
366
  } else if (category.id === 'personas') {
506
367
  base.push(
507
368
  { text: `Personas: ${snapshot.localPersonaCount}; active: ${snapshot.activePersonaName}`, fg: PALETTE.info },
508
- { text: 'Personas are local behavior profiles for the serial main-conversation assistant, not separate Agent jobs.', fg: PALETTE.good },
509
- { text: 'Use them for tone, role, domain constraints, tool posture, and repeatable operating preferences.', fg: PALETTE.muted },
510
- { text: '' },
511
- ...localLibraryLines('Persona Library', snapshot.localPersonas, 'No local personas yet. Create one here with Create persona.', workspace.selectedLocalLibraryItem('persona')?.id ?? null),
369
+ ...compactLocalLibraryLines('Persona Library', snapshot.localPersonas, 'Create one with Create persona.', workspace.selectedLocalLibraryItem('persona')?.id ?? null),
370
+ { text: 'Personas shape the serial main-conversation assistant.', fg: PALETTE.good },
512
371
  );
513
372
  } else if (category.id === 'skills') {
514
373
  base.push(
515
374
  { text: `Skills: ${snapshot.localSkillCount}; enabled: ${snapshot.enabledSkillCount}; bundles: ${snapshot.localSkillBundleCount}; enabled bundles: ${snapshot.enabledSkillBundleCount}; active skills: ${snapshot.activeSkillCount}`, fg: PALETTE.info },
516
- { text: 'Skills are reusable local procedures the assistant can apply from the main conversation.', fg: PALETTE.good },
517
- { text: 'Enabled skills and enabled bundles are injected as operating guidance; secret-looking content is rejected.', fg: PALETTE.warn },
518
- { text: '' },
519
- ...localLibraryLines('Skill Library', snapshot.localSkills, 'No local skills yet. Create one here with Create skill.', workspace.selectedLocalLibraryItem('skill')?.id ?? null),
520
- { text: '' },
521
- ...localLibraryLines('Skill Bundles', snapshot.localSkillBundles, 'No local skill bundles yet. Use Skill bundles and Create bundle after creating skills.', null),
375
+ ...compactLocalLibraryLines('Skill Library', snapshot.localSkills, 'Create one with Create skill.', workspace.selectedLocalLibraryItem('skill')?.id ?? null),
376
+ ...compactLocalLibraryLines('Skill Bundles', snapshot.localSkillBundles, 'Create one after adding skills.', null),
377
+ { text: 'Enabled skills/bundles become operating guidance; secrets are rejected.', fg: PALETTE.warn },
522
378
  );
523
379
  } else if (category.id === 'routines') {
524
380
  const ready = readyRoutineItems(snapshot);
@@ -527,42 +383,34 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
527
383
  base.push(
528
384
  { text: `Routines: ${snapshot.localRoutineCount}; enabled: ${snapshot.enabledRoutineCount}`, fg: PALETTE.info },
529
385
  { text: `Schedule-ready routines: ${ready.length}; setup gaps: ${needsSetup.length}; review needed: ${needsReview.length}`, fg: needsSetup.length > 0 || needsReview.length > 0 ? PALETTE.warn : PALETTE.good },
530
- { text: 'Routines are repeatable main-conversation workflows. Starting one does not create hidden jobs.', fg: PALETTE.good },
531
- { text: 'Scheduling a reviewed routine is explicit and requires a confirmed schedule command.', fg: PALETTE.warn },
532
386
  routineNextActionLine(snapshot),
533
- ...routineScheduleReceiptLines(snapshot),
534
- { text: '' },
535
- ...localLibraryLines('Routine Library', snapshot.localRoutines, 'No local routines yet. Create one here with Create routine.', workspace.selectedLocalLibraryItem('routine')?.id ?? null),
387
+ compactRoutineReceiptLine(snapshot),
388
+ ...compactLocalLibraryLines('Routine Library', snapshot.localRoutines, 'Create one with Create routine.', workspace.selectedLocalLibraryItem('routine')?.id ?? null),
389
+ { text: 'Scheduling requires a confirmed action.', fg: PALETTE.warn },
536
390
  );
537
391
  } else if (category.id === 'work') {
538
392
  base.push(
539
- { text: 'Work plan and approvals are read or explicitly confirmed through public operator routes.', fg: PALETTE.info },
540
- { text: 'This workspace does not approve, deny, cancel, or mutate requests by selection alone.', fg: PALETTE.good },
541
- { text: 'Approve, deny, and cancel forms require an approval id and typed confirmation.', fg: PALETTE.warn },
393
+ { text: 'Work plans and approvals are read or explicitly confirmed.', fg: PALETTE.info },
394
+ { text: 'Selection alone does not approve, deny, cancel, or mutate requests.', fg: PALETTE.good },
395
+ { text: 'Approval actions require id plus typed confirmation.', fg: PALETTE.warn },
542
396
  );
543
397
  } else if (category.id === 'automation') {
544
398
  const ready = readyRoutineItems(snapshot);
545
399
  base.push(
546
- { text: 'Automation and schedules default to read-only observability; side effects require confirmed forms.', fg: PALETTE.info },
547
- { text: 'Confirmed reminders and routine promotion use connected schedules only.', fg: PALETTE.info },
548
- { text: 'Local scheduler mutation controls remain blocked; job/run/schedule controls go through the connected host.', fg: PALETTE.warn },
549
- { text: 'Reminder path: Create reminder -> choose at/every/cron -> optional delivery target -> confirm yes.', fg: PALETTE.good },
550
- { text: 'Operator path: choose job/run/schedule action -> enter id -> type yes -> dispatch once.', fg: PALETTE.good },
551
- { text: 'Routine path: Routines -> resolve setup -> review selected -> Promote routine -> Reconcile schedules.', fg: PALETTE.good },
552
- { text: `Schedule-ready routines: ${ready.length}; local promotion receipts: ${snapshot.routineScheduleReceiptCount}`, fg: ready.length > 0 ? PALETTE.good : PALETTE.warn },
400
+ { text: `Automation: ${ready.length} schedule-ready routine(s); receipts ${snapshot.routineScheduleReceiptCount}.`, fg: ready.length > 0 ? PALETTE.good : PALETTE.warn },
553
401
  automationNextActionLine(snapshot),
554
- ...routineScheduleReceiptLines(snapshot),
402
+ compactRoutineReceiptLine(snapshot),
403
+ { text: 'Reminders and routine promotion require confirmation.', fg: PALETTE.warn },
555
404
  );
556
405
  } else if (category.id === 'delegate') {
557
406
  base.push(
558
- { text: 'Build/fix/review work is handed to GoodVibes TUI/shared-session contracts.', fg: PALETTE.info },
407
+ { text: 'Build/fix/review work is handed to GoodVibes TUI.', fg: PALETTE.info },
559
408
  { text: `Delegated review policy: ${snapshot.delegatedReviewPolicy}`, fg: PALETTE.warn },
560
- { text: 'Agent does not create coding-role Agent jobs.', fg: PALETTE.good },
409
+ { text: 'No coding-role Agent jobs are created here.', fg: PALETTE.good },
561
410
  );
562
411
  }
563
412
  if (snapshot.warnings.length > 0) {
564
- base.push({ text: '', dim: true }, { text: 'Warnings', fg: PALETTE.warn, bold: true });
565
- for (const warning of snapshot.warnings) base.push({ text: warning, fg: PALETTE.warn });
413
+ base.push({ text: `Warnings: ${snapshot.warnings.map((warning) => compactText(warning, 60)).join('; ')}`, fg: PALETTE.warn });
566
414
  }
567
415
  return base;
568
416
  }
@@ -571,14 +419,13 @@ function editorContextLines(editor: AgentWorkspaceLocalEditor): ContextLine[] {
571
419
  const selected = editor.fields[editor.selectedFieldIndex];
572
420
  const lines: ContextLine[] = [
573
421
  { text: editor.title, fg: PALETTE.title, bold: true },
574
- { text: editor.message, fg: editor.message.includes('required') || editor.message.includes('cannot') || editor.message.includes('Cannot') ? PALETTE.warn : PALETTE.info },
575
- { text: 'Enter advances fields and saves from the final field. Ctrl-J adds a line inside multiline fields. Esc cancels without writing.', fg: PALETTE.muted },
422
+ { text: compactText(editor.message), fg: editor.message.includes('required') || editor.message.includes('cannot') || editor.message.includes('Cannot') ? PALETTE.warn : PALETTE.info },
423
+ { text: 'Enter next/save; Ctrl-J newline; Esc cancel.', fg: PALETTE.muted },
576
424
  ];
577
425
  if (selected) {
578
426
  lines.push(
579
- { text: '' },
580
427
  { text: `Editing: ${selected.label}${selected.required ? ' (required)' : ''}`, fg: PALETTE.title, bold: true },
581
- { text: selected.hint, fg: PALETTE.muted },
428
+ { text: compactText(selected.hint), fg: PALETTE.muted },
582
429
  );
583
430
  }
584
431
  return lines;
@@ -588,9 +435,6 @@ function buildContextRows(workspace: AgentWorkspace, category: AgentWorkspaceCat
588
435
  const lines: ContextLine[] = [
589
436
  { text: category.label, fg: PALETTE.title, bold: true },
590
437
  { text: category.summary, fg: PALETTE.subtitle },
591
- { text: '' },
592
- { text: category.detail, fg: PALETTE.text },
593
- { text: '' },
594
438
  ...(workspace.actionSearchActive ? [
595
439
  { text: 'Action Search', fg: PALETTE.title, bold: true },
596
440
  {
@@ -599,30 +443,25 @@ function buildContextRows(workspace: AgentWorkspace, category: AgentWorkspaceCat
599
443
  : 'Type to search every Agent workspace action.',
600
444
  fg: workspace.actionSearchQuery.length > 0 && workspace.actionSearchResults.length === 0 ? PALETTE.warn : PALETTE.info,
601
445
  },
602
- { text: 'Enter opens the selected result. Esc clears search and returns to normal workspace navigation.', fg: PALETTE.muted },
603
- { text: '' },
446
+ { text: 'Enter opens; Esc clears.', fg: PALETTE.muted },
604
447
  ] satisfies ContextLine[] : []),
605
448
  ...(workspace.localEditor ? editorContextLines(workspace.localEditor) : []),
606
- ...(workspace.localEditor ? [{ text: '' }] : []),
607
- ...snapshotLines(workspace, category, workspace.runtimeSnapshot),
608
449
  ];
609
450
 
610
- if (action) {
611
- lines.push(
612
- { text: '' },
451
+ const selectedActionLines: ContextLine[] = action
452
+ ? [
613
453
  { text: `Selected: ${action.label}`, fg: PALETTE.title, bold: true },
614
- { text: action.detail, fg: PALETTE.text },
615
- { text: `Command: ${actionCommand(action)}`, fg: action.kind === 'command' ? PALETTE.info : PALETTE.muted },
616
- { text: `Safety: ${action.safety}`, fg: safetyColor(action) },
617
- );
618
- }
454
+ actionMetaLine(action),
455
+ ]
456
+ : [];
457
+ const snapshotContextLines = snapshotLines(workspace, category, workspace.runtimeSnapshot);
458
+ lines.push(...selectedActionLines, ...snapshotContextLines);
619
459
 
620
460
  if (workspace.lastActionResult) {
621
461
  lines.push(
622
- { text: '' },
623
462
  { text: 'Action Result', fg: PALETTE.title, bold: true },
624
463
  { text: workspace.lastActionResult.title, fg: actionResultColor(workspace.lastActionResult), bold: true },
625
- { text: workspace.lastActionResult.detail, fg: PALETTE.text },
464
+ { text: compactText(workspace.lastActionResult.detail), fg: PALETTE.text },
626
465
  );
627
466
  if (workspace.lastActionResult.command) {
628
467
  lines.push({ text: `Command: ${workspace.lastActionResult.command}`, fg: PALETTE.muted });
@@ -758,13 +597,12 @@ function footerText(workspace: AgentWorkspace): string {
758
597
  export function renderAgentWorkspace(workspace: AgentWorkspace, width: number, height: number): Line[] {
759
598
  const category = workspace.selectedActionCategory;
760
599
  const action = workspace.selectedAction;
761
- const setupCategory = category.id === 'setup';
762
600
  const layoutOptions = {
763
601
  width,
764
602
  height,
765
603
  leftWidth: width < 90 ? undefined : 30,
766
- contextRatio: setupCategory ? 0.86 : 0.62,
767
- minContextRows: setupCategory ? 18 : 10,
604
+ contextRatio: 0.4,
605
+ minContextRows: 10,
768
606
  };
769
607
  const metrics = getFullscreenWorkspaceMetrics(layoutOptions);
770
608