@pellux/goodvibes-agent 1.2.0 → 1.4.1

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 (85) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/dist/package/{ast-grep-napi.linux-x64-gnu-swtppvy9.node → ast-grep-napi.linux-x64-gnu-mkk8xwww.node} +0 -0
  3. package/dist/package/{ast-grep-napi.linux-x64-musl-ttfcdtap.node → ast-grep-napi.linux-x64-musl-ryqtgdv6.node} +0 -0
  4. package/dist/package/main.js +65740 -57156
  5. package/docs/README.md +3 -3
  6. package/docs/connected-host.md +2 -2
  7. package/docs/getting-started.md +11 -9
  8. package/docs/tools-and-commands.md +12 -10
  9. package/docs/voice-and-live-tts.md +2 -2
  10. package/package.json +1 -1
  11. package/release/release-notes.md +15 -144
  12. package/release/release-readiness.json +36 -36
  13. package/src/agent/competitive-feature-inventory.ts +42 -44
  14. package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
  15. package/src/agent/setup-wizard.ts +267 -24
  16. package/src/cli/tui-startup.ts +2 -2
  17. package/src/input/agent-workspace-activation.ts +2 -2
  18. package/src/input/agent-workspace-categories.ts +2 -2
  19. package/src/input/agent-workspace-onboarding-categories.ts +4 -4
  20. package/src/input/agent-workspace-onboarding-finish.ts +21 -0
  21. package/src/input/agent-workspace-settings.ts +47 -1
  22. package/src/input/agent-workspace-setup-snapshot.ts +17 -6
  23. package/src/input/agent-workspace-setup.ts +20 -26
  24. package/src/input/agent-workspace-snapshot.ts +16 -3
  25. package/src/input/agent-workspace-types.ts +5 -2
  26. package/src/input/agent-workspace.ts +21 -6
  27. package/src/input/handler.ts +67 -5
  28. package/src/input/setup-wizard-live-receipts.ts +76 -0
  29. package/src/renderer/agent-workspace-context-lines.ts +17 -33
  30. package/src/renderer/agent-workspace.ts +434 -21
  31. package/src/runtime/tool-permission-safety.ts +1 -1
  32. package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
  33. package/src/tools/agent-harness-agent-orchestration.ts +216 -128
  34. package/src/tools/agent-harness-autonomy-live-records.ts +154 -0
  35. package/src/tools/agent-harness-autonomy-queue-types.ts +1 -1
  36. package/src/tools/agent-harness-autonomy-queue.ts +19 -8
  37. package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
  38. package/src/tools/agent-harness-background-processes.ts +8 -4
  39. package/src/tools/agent-harness-browser-cockpit-route.ts +188 -34
  40. package/src/tools/agent-harness-browser-control.ts +12 -4
  41. package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
  42. package/src/tools/agent-harness-device-live-read-models.ts +366 -0
  43. package/src/tools/agent-harness-execution-posture.ts +3 -0
  44. package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
  45. package/src/tools/agent-harness-local-model-benchmarks.ts +71 -1
  46. package/src/tools/agent-harness-local-model-endpoints.ts +469 -354
  47. package/src/tools/agent-harness-local-model-smoke.ts +277 -0
  48. package/src/tools/agent-harness-local-model-url.ts +78 -0
  49. package/src/tools/agent-harness-media-posture.ts +27 -12
  50. package/src/tools/agent-harness-memory-external-providers.ts +796 -0
  51. package/src/tools/agent-harness-memory-posture.ts +253 -137
  52. package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
  53. package/src/tools/agent-harness-memory-refinement.ts +340 -0
  54. package/src/tools/agent-harness-mode-catalog.ts +4 -2
  55. package/src/tools/agent-harness-model-provider-health.ts +139 -42
  56. package/src/tools/agent-harness-model-readiness.ts +31 -5
  57. package/src/tools/agent-harness-model-routing-types.ts +61 -0
  58. package/src/tools/agent-harness-model-routing.ts +31 -6
  59. package/src/tools/agent-harness-pairing-posture.ts +30 -9
  60. package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
  61. package/src/tools/agent-harness-personal-ops-lanes.ts +81 -15
  62. package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
  63. package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
  64. package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
  65. package/src/tools/agent-harness-personal-ops-records.ts +176 -224
  66. package/src/tools/agent-harness-personal-ops-types.ts +19 -1
  67. package/src/tools/agent-harness-personal-ops.ts +18 -11
  68. package/src/tools/agent-harness-remote-read-models.ts +541 -0
  69. package/src/tools/agent-harness-research-briefing.ts +26 -7
  70. package/src/tools/agent-harness-research-live-read-models.ts +500 -0
  71. package/src/tools/agent-harness-research-runs.ts +15 -3
  72. package/src/tools/agent-harness-research-workflow.ts +92 -19
  73. package/src/tools/agent-harness-setup-model-helpers.ts +1 -0
  74. package/src/tools/agent-harness-setup-posture.ts +7 -7
  75. package/src/tools/agent-harness-setup-smoke.ts +26 -1
  76. package/src/tools/agent-harness-tool-schema.ts +23 -1
  77. package/src/tools/agent-harness-tool-types.ts +5 -0
  78. package/src/tools/agent-harness-tool.ts +9 -1
  79. package/src/tools/agent-harness-workspace-actions.ts +1 -1
  80. package/src/tools/agent-memory-tool.ts +40 -1
  81. package/src/tools/agent-model-compare-run.ts +13 -0
  82. package/src/tools/agent-research-runner.ts +367 -0
  83. package/src/tools/agent-research-tool.ts +7 -179
  84. package/src/tools/agent-route-planner-candidates-surfaces.ts +1 -1
  85. package/src/version.ts +1 -1
@@ -3,6 +3,7 @@ export type AgentSetupWizardStatus = 'complete' | 'active' | 'blocked';
3
3
  export type AgentSetupWizardStepStatus = 'done' | 'current' | 'pending' | 'blocked';
4
4
  export type AgentSetupWizardCheckpointStatus = 'available' | 'none' | 'stale' | 'unavailable';
5
5
  export type AgentSetupWizardCloseoutStatus = 'complete' | 'ready-to-finish' | 'needs-smoke-evidence' | 'blocked';
6
+ export type AgentSetupWizardReceiptSchemaStatus = 'certified' | 'legacy';
6
7
 
7
8
  export interface AgentSetupWizardSourceItem {
8
9
  readonly id: string;
@@ -25,6 +26,8 @@ export interface AgentSetupWizardSmokeHistory {
25
26
  readonly trend: string;
26
27
  readonly latestResult: string | null;
27
28
  readonly previousResult: string | null;
29
+ readonly latestEvidenceId?: string | null;
30
+ readonly latestEvidenceAt?: string | null;
28
31
  readonly resultCounts: Readonly<Record<string, number>>;
29
32
  readonly blockedCheckFrequency: readonly AgentSetupWizardBlockedCheckFrequency[];
30
33
  readonly inspectLatestRoute: string | null;
@@ -33,6 +36,26 @@ export interface AgentSetupWizardSmokeHistory {
33
36
  readonly reason?: string;
34
37
  }
35
38
 
39
+ export type AgentSetupWizardDurableReceiptStatus = 'ready' | 'blocked' | 'failed' | 'unknown';
40
+
41
+ export interface AgentSetupWizardDurableReceipt {
42
+ readonly stepId: string;
43
+ readonly stepLabel?: string;
44
+ readonly status: AgentSetupWizardDurableReceiptStatus;
45
+ readonly receiptId: string;
46
+ readonly recordedAt: string;
47
+ readonly summary: string;
48
+ readonly inspectRoute: string | null;
49
+ readonly source?: string;
50
+ readonly schemaStatus?: AgentSetupWizardReceiptSchemaStatus;
51
+ readonly schemaVersion?: string;
52
+ readonly provenance?: readonly string[];
53
+ readonly publicationGuarantee?: string;
54
+ readonly eventCursor?: string;
55
+ readonly eventSequence?: number;
56
+ readonly publisher?: string;
57
+ }
58
+
36
59
  export interface AgentSetupWizardRepeatedBlocker {
37
60
  readonly setupItemId: string;
38
61
  readonly checkId: string;
@@ -52,6 +75,36 @@ export interface AgentSetupWizardStep {
52
75
  readonly backtrackRoute: string | null;
53
76
  }
54
77
 
78
+ export type AgentSetupWizardStepHistoryKind = 'checkpoint' | 'setup-smoke' | 'durable-receipt';
79
+
80
+ export interface AgentSetupWizardStepHistoryEntry {
81
+ readonly id: string;
82
+ readonly stepId: string;
83
+ readonly stepLabel: string;
84
+ readonly kind: AgentSetupWizardStepHistoryKind;
85
+ readonly receiptId: string;
86
+ readonly recordedAt: string;
87
+ readonly summary: string;
88
+ readonly inspectRoute: string | null;
89
+ readonly source?: string;
90
+ readonly receiptStatus?: string;
91
+ readonly satisfiesReceipt?: boolean;
92
+ readonly schemaStatus?: AgentSetupWizardReceiptSchemaStatus;
93
+ readonly schemaVersion?: string;
94
+ readonly provenance?: readonly string[];
95
+ readonly publicationGuarantee?: string;
96
+ readonly eventCursor?: string;
97
+ readonly eventSequence?: number;
98
+ readonly publisher?: string;
99
+ }
100
+
101
+ export interface AgentSetupWizardReceiptGap {
102
+ readonly stepId: string;
103
+ readonly stepLabel: string;
104
+ readonly requiredReceipt: string;
105
+ readonly summary: string;
106
+ }
107
+
55
108
  export interface AgentSetupWizardCloseout {
56
109
  readonly status: AgentSetupWizardCloseoutStatus;
57
110
  readonly label: string;
@@ -95,6 +148,15 @@ export interface AgentSetupWizardCheckpointAutoAdvance {
95
148
  readonly inspectRoute: string;
96
149
  }
97
150
 
151
+ export interface AgentSetupWizardDiagnostic {
152
+ readonly repeatedBlocker: AgentSetupWizardRepeatedBlocker | null;
153
+ readonly smokeHistory: AgentSetupWizardSmokeHistory;
154
+ readonly stepHistory: readonly AgentSetupWizardStepHistoryEntry[];
155
+ readonly receiptGaps: readonly AgentSetupWizardReceiptGap[];
156
+ readonly closeout: AgentSetupWizardCloseout;
157
+ readonly checkpoint: AgentSetupWizardCheckpoint;
158
+ }
159
+
98
160
  export interface AgentSetupWizard {
99
161
  readonly available: true;
100
162
  readonly status: AgentSetupWizardStatus;
@@ -105,11 +167,8 @@ export interface AgentSetupWizard {
105
167
  readonly progressLabel: string;
106
168
  readonly next: string;
107
169
  readonly reviewRoute: string;
108
- readonly repeatedBlocker: AgentSetupWizardRepeatedBlocker | null;
109
- readonly smokeHistory: AgentSetupWizardSmokeHistory;
110
- readonly closeout: AgentSetupWizardCloseout;
111
- readonly checkpoint: AgentSetupWizardCheckpoint;
112
170
  readonly steps: readonly AgentSetupWizardStep[];
171
+ readonly _diagnostic: AgentSetupWizardDiagnostic;
113
172
  }
114
173
 
115
174
  export interface BuildAgentSetupWizardInput {
@@ -120,6 +179,8 @@ export interface BuildAgentSetupWizardInput {
120
179
  readonly setupMarkerExists?: boolean;
121
180
  readonly finishRoute?: string;
122
181
  readonly finishUserRoute?: string;
182
+ readonly receiptRequiredStepIds?: readonly string[];
183
+ readonly durableReceipts?: readonly AgentSetupWizardDurableReceipt[];
123
184
  readonly repeatedBlockerAliases?: Readonly<Record<string, readonly string[]>>;
124
185
  readonly reviewRoute?: string;
125
186
  }
@@ -305,9 +366,168 @@ function buildStep(item: AgentSetupWizardSourceItem, currentId: string | null):
305
366
  };
306
367
  }
307
368
 
369
+ function validIso(value: string | null | undefined): string | null {
370
+ if (!value || Number.isNaN(Date.parse(value))) return null;
371
+ return new Date(value).toISOString();
372
+ }
373
+
374
+ function setupReceiptKind(stepId: string): string {
375
+ if (stepId === 'runtime' || stepId === 'connected-host-readiness') return 'connected-host service status receipt';
376
+ if (stepId === 'connected-host-auth') return 'connected-host auth receipt';
377
+ if (stepId === 'install-smoke') return 'setup smoke evidence receipt';
378
+ if (stepId === 'browser-pwa') return 'connected-host browser/PWA first-run receipt';
379
+ return 'durable setup receipt';
380
+ }
381
+
382
+ function setupStepAliases(stepId: string): readonly string[] {
383
+ if (stepId === 'runtime' || stepId === 'connected-host-readiness') return ['runtime', 'connected-host-readiness'];
384
+ return [stepId];
385
+ }
386
+
387
+ function setupStepMatches(left: string, right: string): boolean {
388
+ const leftAliases = new Set(setupStepAliases(left));
389
+ return setupStepAliases(right).some((alias) => leftAliases.has(alias));
390
+ }
391
+
392
+ function durableReceiptSatisfies(receipt: AgentSetupWizardDurableReceipt): boolean {
393
+ return receipt.status === 'ready' && Boolean(validIso(receipt.recordedAt)) && receipt.receiptId.trim().length > 0;
394
+ }
395
+
396
+ export function setupStepHasSatisfyingReceipt(
397
+ receipts: readonly AgentSetupWizardDurableReceipt[],
398
+ stepId: string,
399
+ ): boolean {
400
+ return receipts.some((receipt) => durableReceiptSatisfies(receipt) && setupStepMatches(stepId, receipt.stepId));
401
+ }
402
+
403
+ function latestSatisfyingReceipt(
404
+ receipts: readonly AgentSetupWizardDurableReceipt[],
405
+ stepId: string,
406
+ ): AgentSetupWizardDurableReceipt | null {
407
+ return receipts
408
+ .filter((receipt) => durableReceiptSatisfies(receipt) && setupStepMatches(stepId, receipt.stepId))
409
+ .sort((left, right) => Date.parse(right.recordedAt) - Date.parse(left.recordedAt) || left.receiptId.localeCompare(right.receiptId))[0] ?? null;
410
+ }
411
+
412
+ function applyDurableReceiptStatus(
413
+ items: readonly AgentSetupWizardSourceItem[],
414
+ receipts: readonly AgentSetupWizardDurableReceipt[],
415
+ ): readonly AgentSetupWizardSourceItem[] {
416
+ if (receipts.length === 0) return items;
417
+ return items.map((item) => {
418
+ const receipt = latestSatisfyingReceipt(receipts, item.id);
419
+ if (!receipt) return item;
420
+ return {
421
+ ...item,
422
+ status: 'ready',
423
+ detail: `${item.detail} Durable receipt ${receipt.receiptId}: ${receipt.summary}`,
424
+ };
425
+ });
426
+ }
427
+
428
+ function buildStepHistory(input: {
429
+ readonly items: readonly AgentSetupWizardSourceItem[];
430
+ readonly smokeHistory: AgentSetupWizardSmokeHistory;
431
+ readonly checkpoint: AgentSetupWizardCheckpoint;
432
+ readonly durableReceipts: readonly AgentSetupWizardDurableReceipt[];
433
+ }): readonly AgentSetupWizardStepHistoryEntry[] {
434
+ const byId = new Map(input.items.map((item) => [item.id, item]));
435
+ const history: AgentSetupWizardStepHistoryEntry[] = [];
436
+
437
+ const checkpointAt = validIso(input.checkpoint.savedAt);
438
+ if (input.checkpoint.status === 'available' && input.checkpoint.currentStepId && checkpointAt) {
439
+ const item = byId.get(input.checkpoint.currentStepId);
440
+ const stepLabel = item?.label ?? input.checkpoint.currentStepLabel ?? input.checkpoint.currentStepId;
441
+ const receiptId = `setup-wizard-checkpoint:${input.checkpoint.currentStepId}:${checkpointAt}`;
442
+ history.push({
443
+ id: `setup-step-history:${input.checkpoint.currentStepId}:checkpoint:${checkpointAt}`,
444
+ stepId: input.checkpoint.currentStepId,
445
+ stepLabel,
446
+ kind: 'checkpoint',
447
+ receiptId,
448
+ recordedAt: checkpointAt,
449
+ summary: `Saved checkpoint for ${stepLabel}.`,
450
+ inspectRoute: input.checkpoint.inspectRoute,
451
+ source: input.checkpoint.source ?? undefined,
452
+ satisfiesReceipt: false,
453
+ });
454
+ }
455
+
456
+ const smokeAt = validIso(input.smokeHistory.latestEvidenceAt);
457
+ if (input.smokeHistory.latestEvidenceId && smokeAt) {
458
+ const item = byId.get('install-smoke');
459
+ const result = input.smokeHistory.latestResult ?? 'unknown';
460
+ const ready = result === 'ready-for-user-run';
461
+ history.push({
462
+ id: `setup-step-history:install-smoke:setup-smoke:${input.smokeHistory.latestEvidenceId}`,
463
+ stepId: 'install-smoke',
464
+ stepLabel: item?.label ?? 'Install smoke',
465
+ kind: 'setup-smoke',
466
+ receiptId: input.smokeHistory.latestEvidenceId,
467
+ recordedAt: smokeAt,
468
+ summary: `Latest setup smoke evidence is ${result}.`,
469
+ inspectRoute: input.smokeHistory.inspectLatestRoute,
470
+ source: 'agent-setup-smoke-evidence',
471
+ receiptStatus: result,
472
+ satisfiesReceipt: ready,
473
+ });
474
+ }
475
+
476
+ for (const receipt of input.durableReceipts) {
477
+ const recordedAt = validIso(receipt.recordedAt);
478
+ if (!recordedAt || receipt.receiptId.trim().length === 0) continue;
479
+ const item = input.items.find((candidate) => setupStepMatches(candidate.id, receipt.stepId));
480
+ const stepLabel = receipt.stepLabel ?? item?.label ?? receipt.stepId;
481
+ history.push({
482
+ id: `setup-step-history:${receipt.stepId}:durable-receipt:${receipt.receiptId}`,
483
+ stepId: receipt.stepId,
484
+ stepLabel,
485
+ kind: 'durable-receipt',
486
+ receiptId: receipt.receiptId,
487
+ recordedAt,
488
+ summary: receipt.summary,
489
+ inspectRoute: receipt.inspectRoute,
490
+ source: receipt.source,
491
+ receiptStatus: receipt.status,
492
+ satisfiesReceipt: durableReceiptSatisfies(receipt),
493
+ schemaStatus: receipt.schemaStatus,
494
+ schemaVersion: receipt.schemaVersion,
495
+ provenance: receipt.provenance,
496
+ publicationGuarantee: receipt.publicationGuarantee,
497
+ eventCursor: receipt.eventCursor,
498
+ eventSequence: receipt.eventSequence,
499
+ publisher: receipt.publisher,
500
+ });
501
+ }
502
+
503
+ return history.sort((left, right) => Date.parse(right.recordedAt) - Date.parse(left.recordedAt) || left.id.localeCompare(right.id));
504
+ }
505
+
506
+ function buildReceiptGaps(input: {
507
+ readonly items: readonly AgentSetupWizardSourceItem[];
508
+ readonly stepHistory: readonly AgentSetupWizardStepHistoryEntry[];
509
+ readonly receiptRequiredStepIds: readonly string[];
510
+ }): readonly AgentSetupWizardReceiptGap[] {
511
+ return input.receiptRequiredStepIds
512
+ .filter((stepId) => !input.stepHistory.some((entry) => entry.satisfiesReceipt === true && setupStepMatches(stepId, entry.stepId)))
513
+ .map((stepId) => {
514
+ const item = input.items.find((candidate) => candidate.id === stepId);
515
+ const stepLabel = item?.label ?? stepId;
516
+ const requiredReceipt = setupReceiptKind(stepId);
517
+ return {
518
+ stepId,
519
+ stepLabel,
520
+ requiredReceipt,
521
+ summary: `${stepLabel} still needs a stable ${requiredReceipt} id and timestamp before release closeout can count it as durable setup evidence.`,
522
+ };
523
+ });
524
+ }
525
+
308
526
  function buildCloseout(input: {
309
527
  readonly items: readonly AgentSetupWizardSourceItem[];
310
528
  readonly smokeHistory: AgentSetupWizardSmokeHistory;
529
+ readonly setupSmokeReceiptReady: boolean;
530
+ readonly stepHistory: readonly AgentSetupWizardStepHistoryEntry[];
311
531
  readonly criticalStepIds: readonly string[];
312
532
  readonly setupMarkerExists: boolean;
313
533
  readonly reviewRoute: string;
@@ -318,14 +538,21 @@ function buildCloseout(input: {
318
538
  const primaryBlocker = input.items.find((item) => (
319
539
  critical.has(item.id) && item.status === 'blocked'
320
540
  )) ?? null;
321
- const smokeReady = input.smokeHistory.status === 'available' && input.smokeHistory.latestResult === 'ready-for-user-run';
541
+ const smokeReady = input.setupSmokeReceiptReady
542
+ || (input.smokeHistory.status === 'available' && input.smokeHistory.latestResult === 'ready-for-user-run');
322
543
  const latestSmoke = input.smokeHistory.status === 'available'
323
544
  ? input.smokeHistory.latestResult ?? 'unknown'
324
- : input.smokeHistory.status;
545
+ : input.setupSmokeReceiptReady ? 'durable receipt ready' : input.smokeHistory.status;
546
+ const satisfyingSetupReceipts = input.stepHistory.filter((entry) => entry.kind === 'durable-receipt' && entry.satisfiesReceipt === true);
547
+ const certifiedSetupReceipts = satisfyingSetupReceipts.filter((entry) => entry.schemaStatus === 'certified');
548
+ const eventStreamSetupReceipts = satisfyingSetupReceipts.filter((entry) => entry.eventCursor);
325
549
  const evidence = [
326
550
  `critical setup blockers: ${primaryBlocker ? `${primaryBlocker.label} (${primaryBlocker.status})` : 'none'}`,
327
551
  `setup marker: ${input.setupMarkerExists ? 'present' : 'missing'}`,
328
552
  `latest setup smoke: ${latestSmoke}`,
553
+ `setup smoke receipt: ${input.setupSmokeReceiptReady ? 'ready' : 'missing'}`,
554
+ `certified setup receipts: ${certifiedSetupReceipts.length}/${satisfyingSetupReceipts.length}`,
555
+ `setup receipt event streams: ${eventStreamSetupReceipts.length}`,
329
556
  `setup smoke history: ${input.smokeHistory.status}; total ${input.smokeHistory.total}; trend ${input.smokeHistory.trend}`,
330
557
  ];
331
558
  const policy = 'Setup closeout is advisory until a confirmed finish route writes the local onboarding marker. Optional setup recommendations do not block closeout, but unresolved critical blockers or missing ready setup smoke evidence do.';
@@ -401,14 +628,23 @@ function buildCloseout(input: {
401
628
  export function buildAgentSetupWizard(input: BuildAgentSetupWizardInput): AgentSetupWizard {
402
629
  const smokeHistory = input.smokeHistory ?? emptyAgentSetupSmokeHistory();
403
630
  const inputCheckpoint = input.checkpoint ?? emptyAgentSetupWizardCheckpoint();
404
- const repeated = itemFromRepeatedBlockers(input.items, smokeHistory, input.repeatedBlockerAliases ?? {});
405
- const blocking = firstBlockingItem(input.items);
406
- const rawCheckpointItem = itemFromCheckpoint(input.items, inputCheckpoint);
631
+ const durableReceipts = input.durableReceipts ?? [];
632
+ const items = applyDurableReceiptStatus(input.items, durableReceipts);
633
+ const repeated = itemFromRepeatedBlockers(items, smokeHistory, input.repeatedBlockerAliases ?? {});
634
+ const blocking = firstBlockingItem(items);
635
+ const rawCheckpointItem = itemFromCheckpoint(items, inputCheckpoint);
407
636
  const checkpointItem = blocking && rawCheckpointItem?.id !== blocking.id ? null : rawCheckpointItem;
408
- const checkpoint = buildCheckpoint(inputCheckpoint, input.items, checkpointItem, blocking);
409
- const current = repeated?.item ?? blocking ?? checkpointItem ?? firstAttentionItem(input.items);
637
+ const checkpoint = buildCheckpoint(inputCheckpoint, items, checkpointItem, blocking);
638
+ const current = repeated?.item ?? blocking ?? checkpointItem ?? firstAttentionItem(items);
410
639
  const currentId = current?.id ?? null;
411
- const steps = input.items.map((item) => buildStep(item, currentId));
640
+ const steps = items.map((item) => buildStep(item, currentId));
641
+ const stepHistory = buildStepHistory({ items, smokeHistory, checkpoint, durableReceipts });
642
+ const setupSmokeReceiptReady = stepHistory.some((entry) => entry.satisfiesReceipt === true && setupStepMatches('install-smoke', entry.stepId));
643
+ const receiptGaps = buildReceiptGaps({
644
+ items,
645
+ stepHistory,
646
+ receiptRequiredStepIds: input.receiptRequiredStepIds ?? [],
647
+ });
412
648
  const completedSteps = steps.filter((step) => step.status === 'done').length;
413
649
  const status: AgentSetupWizardStatus = completedSteps === steps.length
414
650
  ? 'complete'
@@ -420,6 +656,17 @@ export function buildAgentSetupWizard(input: BuildAgentSetupWizardInput): AgentS
420
656
  ? `${current.label}: ${repeated.blocker.summary} ${current.detail}`
421
657
  : `${current.label}: ${current.detail}`
422
658
  : 'Setup wizard is complete; rerun setup smoke if this machine was upgraded or moved.';
659
+ const closeout = buildCloseout({
660
+ items,
661
+ smokeHistory,
662
+ setupSmokeReceiptReady,
663
+ stepHistory,
664
+ criticalStepIds: input.closeoutCriticalStepIds ?? [],
665
+ setupMarkerExists: input.setupMarkerExists === true,
666
+ reviewRoute: input.reviewRoute ?? DEFAULT_AGENT_SETUP_WIZARD_REVIEW_ROUTE,
667
+ finishRoute: input.finishRoute ?? DEFAULT_AGENT_SETUP_WIZARD_FINISH_ROUTE,
668
+ finishUserRoute: input.finishUserRoute ?? DEFAULT_AGENT_SETUP_WIZARD_FINISH_USER_ROUTE,
669
+ });
423
670
  return {
424
671
  available: true,
425
672
  status,
@@ -430,18 +677,14 @@ export function buildAgentSetupWizard(input: BuildAgentSetupWizardInput): AgentS
430
677
  progressLabel: `${completedSteps}/${steps.length} setup step(s) ready`,
431
678
  next,
432
679
  reviewRoute: input.reviewRoute ?? DEFAULT_AGENT_SETUP_WIZARD_REVIEW_ROUTE,
433
- repeatedBlocker: repeated?.blocker ?? null,
434
- smokeHistory,
435
- closeout: buildCloseout({
436
- items: input.items,
437
- smokeHistory,
438
- criticalStepIds: input.closeoutCriticalStepIds ?? [],
439
- setupMarkerExists: input.setupMarkerExists === true,
440
- reviewRoute: input.reviewRoute ?? DEFAULT_AGENT_SETUP_WIZARD_REVIEW_ROUTE,
441
- finishRoute: input.finishRoute ?? DEFAULT_AGENT_SETUP_WIZARD_FINISH_ROUTE,
442
- finishUserRoute: input.finishUserRoute ?? DEFAULT_AGENT_SETUP_WIZARD_FINISH_USER_ROUTE,
443
- }),
444
- checkpoint,
445
680
  steps,
681
+ _diagnostic: {
682
+ repeatedBlocker: repeated?.blocker ?? null,
683
+ smokeHistory,
684
+ stepHistory,
685
+ receiptGaps,
686
+ closeout,
687
+ checkpoint,
688
+ },
446
689
  };
447
690
  }
@@ -102,14 +102,14 @@ export function applyInitialTuiCliState(options: {
102
102
  const onboardingCompletionMarker = readOnboardingCompletionMarker(shellPaths, 'user');
103
103
  const seededPrompt = cli.flags.prompt ?? (cli.rawCommand === undefined && cli.positionals.length > 0 ? cli.positionals.join(' ') : undefined);
104
104
  if (cli.command === 'onboarding') {
105
- input.openAgentWorkspace(commandContext);
105
+ input.openAgentWorkspace(commandContext, undefined, 'ONBOARDING');
106
106
  } else if (cli.command === 'sessions' && cli.commandArgs[0] === 'resume') {
107
107
  const target = cli.commandArgs.slice(1).join(' ').trim();
108
108
  if (target) {
109
109
  void commandRegistry.execute('session', ['resume', target], commandContext).then(() => render());
110
110
  }
111
111
  } else if (!onboardingCompletionMarker.payload) {
112
- input.openAgentWorkspace(commandContext, 'setup');
112
+ input.openAgentWorkspace(commandContext, 'setup', 'ONBOARDING');
113
113
  }
114
114
 
115
115
  if (seededPrompt) {
@@ -52,7 +52,7 @@ interface AgentWorkspaceActivationHost {
52
52
  openSettingsModalAction(action: AgentWorkspaceCategory['actions'][number], requestRender?: () => void): void;
53
53
  completeOnboarding(): void;
54
54
  hasCommandDispatch(): boolean;
55
- dispatchWorkspaceCommand: AgentWorkspaceCommandDispatcher;
55
+ dispatchWorkspaceCommand(command: string, behavior?: 'inline' | 'compose' | 'exit'): void;
56
56
  commitActionSearchSelection(): boolean;
57
57
  }
58
58
 
@@ -186,7 +186,7 @@ export function activateAgentWorkspaceSelection(
186
186
  command: action.command,
187
187
  safety: action.safety,
188
188
  };
189
- workspace.dispatchWorkspaceCommand(action.command);
189
+ workspace.dispatchWorkspaceCommand(action.command, action.commandBehavior);
190
190
  }
191
191
 
192
192
  export function createAgentWorkspaceEditor(
@@ -455,8 +455,8 @@ export const AGENT_WORKSPACE_CATEGORIES: readonly AgentWorkspaceCategory[] = [
455
455
  detail: 'Voice, media, browser tools, and image-capable flows are first-class Agent tools. Connected-host administration stays outside Agent and side effects stay explicit.',
456
456
  actions: [
457
457
  { id: 'voice-workflow-posture', label: 'Voice workflows', detail: 'Inspect push-to-talk, voice memo transcription, spoken responses, and wake-word readiness with device action:"voice".', kind: 'guidance', safety: 'read-only' },
458
- { id: 'device-capability-map', label: 'Device capability map', detail: 'Inspect pairing, mobile command routing, browser/PWA, notifications, browser/desktop control, and camera/location readiness with device action:"status" without claiming unpublished contracts.', kind: 'guidance', safety: 'read-only' },
459
- { id: 'browser-cockpit-readiness', label: 'Browser/PWA readiness', detail: 'Inspect the connected browser cockpit/PWA route, workspace category coverage, mobile controls, and first-run receipt gaps with computer action:"browser" before opening a browser.', kind: 'guidance', safety: 'read-only' },
458
+ { id: 'device-capability-map', label: 'Device capability map', detail: 'Inspect pairing, mobile command routing, browser/PWA, notifications, browser/desktop control, and certified camera/location readiness with device action:"status" without claiming uncertified contracts.', kind: 'guidance', safety: 'read-only' },
459
+ { id: 'browser-cockpit-readiness', label: 'Browser/PWA readiness', detail: 'Inspect the connected browser cockpit/PWA route, certified workspace category coverage, mobile controls, and first-run receipt evidence with computer action:"browser" before opening a browser.', kind: 'guidance', safety: 'read-only' },
460
460
  { id: 'tts-config', label: 'Configure live TTS', detail: 'Open the settings workspace at the TTS group.', command: '/config tts', kind: 'command', safety: 'safe' },
461
461
  { id: 'tts-provider', label: 'Choose TTS provider', detail: 'Open provider/model routing for spoken responses through the settings flow.', command: '/config tts.provider', kind: 'command', safety: 'safe' },
462
462
  { id: 'voice-review', label: 'Voice review', detail: 'Inspect local voice posture before enabling spoken interaction.', command: '/voice review', kind: 'command', safety: 'read-only' },
@@ -103,10 +103,10 @@ export const AGENT_WORKSPACE_ONBOARDING_DETAIL_CATEGORIES: readonly AgentWorkspa
103
103
  summary: 'Create/import memory, personas, skills, routines, notes, and Knowledge.',
104
104
  detail: 'Use this page to seed the assistant with durable local context. These forms create Agent-owned records or ingest reviewed sources.',
105
105
  actions: [
106
- { id: 'context-vibe-status', label: 'Inspect VIBE.md', detail: 'Show project/global VIBE.md personality files, blocked files, and init paths before relying on a custom assistant feel.', command: '/vibe status', kind: 'command', safety: 'read-only' },
107
- { id: 'context-project-files', label: 'Inspect project context', detail: 'List secret-scanned .hermes.md, HERMES.md, AGENTS.md, CLAUDE.md, SOUL.md, and Cursor context files with blocked/truncated status.', command: 'context action:"files"', kind: 'guidance', safety: 'read-only' },
108
- { id: 'context-project-file', label: 'Inspect one context file', detail: 'Inspect one loaded or blocked project context file by id, target, or query through the read-only context-file route.', command: 'context action:"file"', kind: 'guidance', safety: 'read-only' },
109
- { id: 'context-prompt-context', label: 'Prompt context', detail: 'Inspect the applied prompt context order, selected memory/persona/skill/routine records, suppressed context, and approximate token budget.', command: 'context action:"prompt"', kind: 'guidance', safety: 'read-only' },
106
+ { id: 'context-vibe-status', label: 'Inspect VIBE.md', detail: 'Show project/global VIBE.md personality files, blocked files, and init paths before relying on a custom assistant feel.', kind: 'guidance', safety: 'read-only' },
107
+ { id: 'context-project-files', label: 'Inspect project context', detail: 'List secret-scanned .hermes.md, HERMES.md, AGENTS.md, CLAUDE.md, SOUL.md, and Cursor context files with blocked/truncated status.', kind: 'guidance', safety: 'read-only' },
108
+ { id: 'context-project-file', label: 'Inspect one context file', detail: 'Inspect one loaded or blocked project context file by id, target, or query through the read-only context-file control.', kind: 'guidance', safety: 'read-only' },
109
+ { id: 'context-prompt-context', label: 'Prompt context', detail: 'Inspect the applied prompt context order, selected memory/persona/skill/routine records, suppressed context, and approximate token budget.', kind: 'guidance', safety: 'read-only' },
110
110
  { id: 'context-profile-from-discovered', label: 'Profile from discovered files', detail: 'Create an isolated Agent profile from reviewed local persona, skill, and routine files.', editorKind: 'profile-from-discovered', kind: 'editor', safety: 'safe' },
111
111
  { id: 'context-persona-discovery', label: 'Import persona files', detail: 'Import discovered persona files into the Agent persona registry.', editorKind: 'persona-discovery-import', kind: 'editor', safety: 'safe' },
112
112
  { id: 'context-skill-discovery', label: 'Import skill files', detail: 'Import discovered skill files into the Agent skill registry.', editorKind: 'skill-discovery-import', kind: 'editor', safety: 'safe' },
@@ -0,0 +1,21 @@
1
+ import type { AgentWorkspaceAction, AgentWorkspaceCategory } from './agent-workspace-types.ts';
2
+
3
+ export const ONBOARDING_CRITICAL_STEP_IDS = ['runtime', 'connected-host-auth', 'provider-model'] as const;
4
+
5
+ export const ONBOARDING_COMPLETE_SYNTHETIC_ACTION: AgentWorkspaceAction = {
6
+ id: 'onboarding-apply-close',
7
+ label: 'Apply & close',
8
+ detail: 'Acknowledge onboarding as finished, persist the user completion marker, and close the fullscreen Agent workspace.',
9
+ kind: 'onboarding-complete',
10
+ safety: 'safe',
11
+ };
12
+
13
+ export function shouldShowOnboardingFinishFooter(
14
+ category: AgentWorkspaceCategory,
15
+ baseActions: readonly AgentWorkspaceAction[],
16
+ ): boolean {
17
+ return (
18
+ category.group === 'ONBOARDING'
19
+ && baseActions.every((a) => a.kind !== 'onboarding-complete')
20
+ );
21
+ }
@@ -56,6 +56,12 @@ export interface TuiSettingsImportOutcome extends SettingMutationOutcome {
56
56
  readonly runtimeSnapshot: AgentWorkspaceRuntimeSnapshot | null;
57
57
  }
58
58
 
59
+ export interface AgentWorkspaceSettingActionDisplay {
60
+ readonly setting: string;
61
+ readonly defaultValue: string;
62
+ readonly currentValue: string;
63
+ }
64
+
59
65
  type TuiImportStatus = 'would_import' | 'unchanged' | 'skipped';
60
66
 
61
67
  interface TuiSettingsImportSource {
@@ -173,10 +179,31 @@ function valuesMatch(left: unknown, right: unknown): boolean {
173
179
  return JSON.stringify(left) === JSON.stringify(right);
174
180
  }
175
181
 
182
+ function isSecretLikeSettingKey(key: string): boolean {
183
+ const normalized = key.replace(/([a-z0-9])([A-Z])/g, '$1.$2').toLowerCase();
184
+ if (normalized.endsWith('secret.policy')) return false;
185
+ return /(?:secret|token|password|api[-_.]?key|api\.key|signing)/i.test(normalized);
186
+ }
187
+
188
+ function compactSettingValue(key: string, value: unknown): string {
189
+ if (value === undefined || value === null) return '(unset)';
190
+ if (isSecretLikeSettingKey(key)) return value === '' ? '(empty)' : '(secret)';
191
+ if (typeof value === 'boolean') return value ? 'true' : 'false';
192
+ if (typeof value === 'number') return Number.isFinite(value) ? String(value) : '(invalid)';
193
+ if (typeof value === 'string') {
194
+ if (value.length === 0) return '(empty)';
195
+ if (value.startsWith('goodvibes://secrets/')) return '(secret)';
196
+ return value.length > 36 ? `${value.slice(0, 33)}...` : value;
197
+ }
198
+ if (Array.isArray(value)) return `[${value.length} item${value.length === 1 ? '' : 's'}]`;
199
+ if (isRecord(value)) return '{...}';
200
+ return String(value);
201
+ }
202
+
176
203
  function redactImportValue(key: string, value: unknown): unknown {
177
204
  if (typeof value !== 'string') return value;
178
205
  if (!value) return value;
179
- if (/(?:secret|token|password|api[-_.]?key|signing)/i.test(key)) {
206
+ if (isSecretLikeSettingKey(key)) {
180
207
  return value.startsWith('goodvibes://secrets/') ? '<secret-ref>' : '<redacted>';
181
208
  }
182
209
  return value;
@@ -369,6 +396,25 @@ export function buildAgentWorkspaceSettingActionEffect(
369
396
  };
370
397
  }
371
398
 
399
+ export function buildAgentWorkspaceSettingActionDisplay(
400
+ context: CommandContext | null,
401
+ action: AgentWorkspaceAction,
402
+ ): AgentWorkspaceSettingActionDisplay | null {
403
+ const settingKey = action.settingKey?.trim();
404
+ const configManager = context?.platform?.configManager;
405
+ if (!settingKey || !configManager) return null;
406
+
407
+ const setting = agentWorkspaceSettingSchema(context, settingKey);
408
+ if (!setting) return null;
409
+ const currentValue = configManager.get(settingKey as ConfigKey);
410
+
411
+ return {
412
+ setting: action.label || settingKey,
413
+ defaultValue: compactSettingValue(settingKey, setting.default),
414
+ currentValue: compactSettingValue(settingKey, currentValue),
415
+ };
416
+ }
417
+
372
418
  export async function applyAgentWorkspaceSettingValue(
373
419
  context: CommandContext | null,
374
420
  setting: ConfigSetting,
@@ -2,7 +2,7 @@ import type { ArtifactDescriptor } from '@pellux/goodvibes-sdk/platform/artifact
2
2
  import type { CommandContext } from './command-registry.ts';
3
3
  import { readSetupWizardCheckpoint } from '../agent/setup-wizard-checkpoint.ts';
4
4
  import { readOnboardingCompletionMarker } from '../runtime/onboarding/index.ts';
5
- import { DEFAULT_AGENT_SETUP_WIZARD_CLEAR_CHECKPOINT_ROUTE, DEFAULT_AGENT_SETUP_WIZARD_INSPECT_CHECKPOINT_ROUTE, DEFAULT_AGENT_SETUP_WIZARD_MARK_CHECKPOINT_ROUTE, DEFAULT_AGENT_SETUP_WIZARD_RERUN_SMOKE_ROUTE, DEFAULT_AGENT_SETUP_WIZARD_SAVE_SMOKE_ROUTE, buildAgentSetupWizard, emptyAgentSetupSmokeHistory, emptyAgentSetupWizardCheckpoint, type AgentSetupWizard, type AgentSetupWizardBlockedCheckFrequency, type AgentSetupWizardCheckpoint, type AgentSetupWizardSmokeHistory, type AgentSetupWizardSourceItem } from '../agent/setup-wizard.ts';
5
+ import { DEFAULT_AGENT_SETUP_WIZARD_CLEAR_CHECKPOINT_ROUTE, DEFAULT_AGENT_SETUP_WIZARD_INSPECT_CHECKPOINT_ROUTE, DEFAULT_AGENT_SETUP_WIZARD_MARK_CHECKPOINT_ROUTE, DEFAULT_AGENT_SETUP_WIZARD_RERUN_SMOKE_ROUTE, DEFAULT_AGENT_SETUP_WIZARD_SAVE_SMOKE_ROUTE, buildAgentSetupWizard, emptyAgentSetupSmokeHistory, emptyAgentSetupWizardCheckpoint, type AgentSetupWizard, type AgentSetupWizardBlockedCheckFrequency, type AgentSetupWizardCheckpoint, type AgentSetupWizardDurableReceipt, type AgentSetupWizardSmokeHistory, type AgentSetupWizardSourceItem } from '../agent/setup-wizard.ts';
6
6
  import type { AgentWorkspaceSetupChecklistItem } from './agent-workspace-setup.ts';
7
7
  import { readArtifactMetadataString, readArtifactMetadataStringList } from './agent-workspace-artifact-metadata.ts';
8
8
 
@@ -30,6 +30,12 @@ function setupSmokeEvidenceTrend(artifacts: readonly ArtifactDescriptor[]): stri
30
30
  return 'unchanged';
31
31
  }
32
32
 
33
+ function artifactCreatedAtIso(artifact: ArtifactDescriptor): string | null {
34
+ const createdAt = artifact.createdAt;
35
+ if (typeof createdAt !== 'number' || !Number.isFinite(createdAt)) return null;
36
+ return new Date(createdAt).toISOString();
37
+ }
38
+
33
39
  function setupSmokeBlockedCheckFrequency(artifacts: readonly ArtifactDescriptor[]): readonly AgentSetupWizardBlockedCheckFrequency[] {
34
40
  const counts = new Map<string, number>();
35
41
  for (const artifact of artifacts) {
@@ -67,6 +73,8 @@ export function buildSetupSmokeHistory(artifacts: readonly ArtifactDescriptor[],
67
73
  trend: setupSmokeEvidenceTrend(setupSmokeArtifacts),
68
74
  latestResult: readArtifactMetadataString(latest.metadata, 'result') || 'unknown',
69
75
  previousResult: previous ? readArtifactMetadataString(previous.metadata, 'result') || 'unknown' : null,
76
+ latestEvidenceId: latest.id,
77
+ latestEvidenceAt: artifactCreatedAtIso(latest),
70
78
  resultCounts,
71
79
  blockedCheckFrequency: setupSmokeBlockedCheckFrequency(setupSmokeArtifacts),
72
80
  inspectLatestRoute: `agent_artifacts show artifactId:"${latest.id}" includeContent:false`,
@@ -120,13 +128,12 @@ export function buildSetupWizardCheckpoint(context: CommandContext): AgentSetupW
120
128
  }
121
129
 
122
130
  function setupChecklistUserRoute(item: AgentWorkspaceSetupChecklistItem): string {
123
- return item.command ?? 'Start';
131
+ return item.breadcrumb ?? 'Start';
124
132
  }
125
133
 
126
134
  function setupChecklistModelRoute(item: AgentWorkspaceSetupChecklistItem): string {
127
135
  if (item.id === 'runtime') return 'agent_harness mode:"setup_item" setupItemId:"connected-host-readiness"';
128
136
  if (item.id === 'provider-model') return 'models action:"status"';
129
- if (item.id === 'install-smoke') return DEFAULT_AGENT_SETUP_WIZARD_RERUN_SMOKE_ROUTE;
130
137
  if (item.id === 'subscriptions') return 'models action:"providers"';
131
138
  if (item.id === 'agent-knowledge') return 'agent_knowledge mode:"status"';
132
139
  if (item.id === 'profile') return 'agent_harness mode:"workspace_action" actionId:"profile-template-show"';
@@ -154,12 +161,10 @@ function setupChecklistActionId(item: AgentWorkspaceSetupChecklistItem): string
154
161
  }
155
162
 
156
163
  const SETUP_WIZARD_SNAPSHOT_BLOCKER_ALIASES: Readonly<Record<string, readonly string[]>> = {
157
- 'agent-binary': ['runtime', 'install-smoke'],
164
+ 'agent-binary': ['runtime'],
158
165
  'connected-host-status': ['runtime'],
159
166
  'connected-host-auth': ['connected-host-auth'],
160
167
  'provider-model': ['provider-model'],
161
- 'setup-posture': ['install-smoke'],
162
- 'first-assistant-turn': ['install-smoke'],
163
168
  };
164
169
 
165
170
  export function buildWorkspaceSetupWizard(
@@ -167,6 +172,7 @@ export function buildWorkspaceSetupWizard(
167
172
  smokeHistory: AgentSetupWizardSmokeHistory,
168
173
  checkpoint: AgentSetupWizardCheckpoint,
169
174
  setupMarkerExists: boolean,
175
+ durableReceipts: readonly AgentSetupWizardDurableReceipt[] = [],
170
176
  ): AgentSetupWizard {
171
177
  const items: AgentSetupWizardSourceItem[] = checklist.map((item) => ({
172
178
  id: item.id,
@@ -182,6 +188,11 @@ export function buildWorkspaceSetupWizard(
182
188
  smokeHistory,
183
189
  checkpoint,
184
190
  closeoutCriticalStepIds: ['runtime', 'connected-host-auth', 'provider-model'],
191
+ receiptRequiredStepIds: [
192
+ 'runtime',
193
+ 'connected-host-auth',
194
+ ],
195
+ durableReceipts,
185
196
  setupMarkerExists,
186
197
  repeatedBlockerAliases: SETUP_WIZARD_SNAPSHOT_BLOCKER_ALIASES,
187
198
  });