@pingagent/sdk 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/pingagent.js CHANGED
@@ -40,6 +40,9 @@ import {
40
40
  listPendingDecisionViews,
41
41
  summarizeHumanDelivery,
42
42
  listRecentBindingsForSession,
43
+ buildActionInboxSummary,
44
+ OpenClawExecutionAdapter,
45
+ deriveOpenClawAgentState,
43
46
  deriveTransportHealth,
44
47
  readTransportPreference,
45
48
  switchTransportPreference,
@@ -122,9 +125,9 @@ function formatRetentionLabel(ttlMs) {
122
125
 
123
126
  function describeHostedTier(tier) {
124
127
  if (tier === 'plus') return 'shareable identity + first alias + higher relay';
125
- if (tier === 'pro') return 'multi-identity communication + audit export';
128
+ if (tier === 'pro') return 'multi-identity escalation + callback governance + audit export';
126
129
  if (tier === 'enterprise') return 'high-scale governance + operational controls';
127
- return 'free communication-first entry tier';
130
+ return 'free escalation-first entry tier';
128
131
  }
129
132
 
130
133
  function openStore(identityPath) {
@@ -208,15 +211,16 @@ function getHostPanelSurfaceUrl() {
208
211
 
209
212
  function getSurfaceRecommendationLines(primaryCommandPrefix = 'npx @pingagent/sdk', secondaryCommandPrefix = 'pingagent') {
210
213
  return [
211
- 'With GUI: Host Panel',
214
+ 'GUI repair / approvals / audit: Host Panel',
212
215
  ` Start locally: ${primaryCommandPrefix} web`,
213
216
  ...(secondaryCommandPrefix && secondaryCommandPrefix !== primaryCommandPrefix ? [` Or via local bin: ${secondaryCommandPrefix} web`] : []),
214
217
  ` URL when running: ${getHostPanelSurfaceUrl()}`,
215
- 'Headless / low-token: TUI',
218
+ 'Headless repair surface: TUI',
216
219
  ` ${primaryCommandPrefix} host tui`,
217
220
  ...(secondaryCommandPrefix && secondaryCommandPrefix !== primaryCommandPrefix ? [` ${secondaryCommandPrefix} host tui`] : []),
218
221
  ` ${primaryCommandPrefix} host tui --once`,
219
- 'MCP: agent/runtime control surface, not the default human operator UI',
222
+ 'Agent default loop: pingagent_recent_sessions -> pingagent_focus_session -> pingagent_reply',
223
+ 'MCP: agent/runtime control surface',
220
224
  ];
221
225
  }
222
226
 
@@ -457,12 +461,28 @@ function buildHostState(identityPath, selectedSessionKey = null, historyPageInde
457
461
  ? buildProjectionPreview(store, selectedSession.session_key, policy.doc.collaboration_projection?.preset || 'balanced', 5)
458
462
  : null;
459
463
  const humanDelivery = summarizeHumanDelivery(store, 20);
464
+ const agentState = deriveOpenClawAgentState({
465
+ runtime_status: ingressRuntime,
466
+ sessions,
467
+ pending_decisions: pendingCollaborationEventsGlobal.length,
468
+ human_delivery: humanDelivery,
469
+ transport_health: transportHealth,
470
+ });
460
471
  const selectedRecentBindings = selectedSession
461
472
  ? listRecentBindingsForSession(store, selectedSession.session_key, selectedSession.conversation_id, 12)
462
473
  : [];
463
474
  const selectedRecentNotificationIntents = selectedSession
464
475
  ? new NotificationIntentManager(store).listBySession(selectedSession.session_key, 12)
465
476
  : [];
477
+ const openClawAdapter = new OpenClawExecutionAdapter(store).buildOverview(20);
478
+ const actionInbox = buildActionInboxSummary({
479
+ pending_decisions: pendingCollaborationEventsGlobal,
480
+ notification_intents: new NotificationIntentManager(store).listRecent(20),
481
+ external_escalations: openClawAdapter.recent_escalations,
482
+ agent_state: agentState,
483
+ include_runtime: true,
484
+ limit: 20,
485
+ });
466
486
  return {
467
487
  identity,
468
488
  runtimeMode,
@@ -470,6 +490,7 @@ function buildHostState(identityPath, selectedSessionKey = null, historyPageInde
470
490
  ingressRuntime,
471
491
  transportPreference,
472
492
  transportHealth,
493
+ agentState,
473
494
  activeChatSessionFile: getActiveSessionFilePath(),
474
495
  sessionMapPath: getSessionMapFilePath(),
475
496
  sessionBindingAlertsPath: getSessionBindingAlertsFilePath(),
@@ -477,6 +498,8 @@ function buildHostState(identityPath, selectedSessionKey = null, historyPageInde
477
498
  policyDoc: policy.doc,
478
499
  projectionPreset: policy.doc.collaboration_projection?.preset || 'balanced',
479
500
  humanDelivery,
501
+ actionInbox,
502
+ openClawAdapter,
480
503
  sinceLastSeenGlobal,
481
504
  sessions: sessionsWithSeen,
482
505
  tasks: taskManager.listRecent(30).map((task) => ({
@@ -556,22 +579,42 @@ function renderHostTuiScreen(hostState, uiState) {
556
579
  last_canary_ok: null,
557
580
  last_canary_at: null,
558
581
  };
582
+ const agentState = hostState.agentState || {
583
+ state: 'activating',
584
+ summary: 'Activation is still in progress.',
585
+ next_action: 'Wait for activation to finish.',
586
+ reason: null,
587
+ };
588
+ const actionInbox = hostState.actionInbox || {
589
+ total: 0,
590
+ pending_approval: 0,
591
+ pending_callback: 0,
592
+ pending_escalation: 0,
593
+ blocked: 0,
594
+ degraded: 0,
595
+ timed_out: 0,
596
+ high_risk: 0,
597
+ critical_risk: 0,
598
+ items: [],
599
+ };
559
600
  const sinceLastSeenGlobal = hostState.sinceLastSeenGlobal || {};
560
601
  const lines = [
561
602
  'PingAgent Host TUI',
562
603
  `DID: ${hostState.identity.did}`,
563
604
  `status=${formatStatusLine(uiState?.statusLevel || 'info', uiState?.statusMessage || '(ready)')}${statusTs}${statusCountdown}`,
564
- `runtime_mode=${hostState.runtimeMode} receive_mode=${hostState.ingressRuntime?.receive_mode || 'webhook'} current_openclaw_chat=${hostState.activeChatSession || '(none)'}`,
605
+ `agent_state=${agentState.state} runtime_mode=${hostState.runtimeMode} receive_mode=${hostState.ingressRuntime?.receive_mode || 'webhook'}`,
606
+ `agent_summary=${agentState.summary}`,
607
+ `agent_next_action=${agentState.next_action}`,
565
608
  `ingress=${ingressLabel}${degraded ? ' action=[f] fix-now' : ''}`,
566
609
  `transport=${transportHealth.transport_mode} preferred=${transportHealth.preferred_transport_mode} state=${transportHealth.state} retry_queue=${transportHealth.retry_queue_length} failures=${transportHealth.consecutive_failures}`,
567
610
  `human_delivery mode=${humanDelivery.mode || 'projection_outbox'} active_bindings=${humanDelivery.active_bindings ?? 0} pending=${humanDelivery.pending_intents ?? 0} unresolved=${humanDelivery.unresolved_intents ?? 0} failed=${humanDelivery.failed_intents ?? 0} acked=${humanDelivery.acknowledged_intents ?? 0}`,
611
+ `action_inbox total=${actionInbox.total} approvals=${actionInbox.pending_approval} callbacks=${actionInbox.pending_callback} escalations=${actionInbox.pending_escalation} blocked=${actionInbox.blocked} timed_out=${actionInbox.timed_out} degraded=${actionInbox.degraded}`,
568
612
  `human_delivery_channels supported=${(humanDelivery.supported_channels || []).join(',') || '(none)'} unsupported=${(humanDelivery.unsupported_channels || []).join(',') || '(none)'} canary_ok=${typeof humanDelivery.last_canary_ok === 'boolean' ? String(humanDelivery.last_canary_ok) : '(unknown)'} at=${humanDelivery.last_canary_at || '(none)'}`,
569
613
  uiState?.publicLinkUrl ? `public_link=${uiState.publicLinkUrl}` : null,
570
- `sessions=${sessions.length} unread_total=${hostState.unreadTotal ?? 0} alert_sessions=${hostState.alertSessions ?? 0} view=${view} projection=${hostState.projectionPreset || 'balanced'}`,
614
+ `sessions=${sessions.length} unread_total=${hostState.unreadTotal ?? 0} repair_alert_sessions=${hostState.alertSessions ?? 0} view=${view} projection=${hostState.projectionPreset || 'balanced'}`,
571
615
  `since_last_seen external=${sinceLastSeenGlobal.new_external_messages ?? 0} conclusions=${sinceLastSeenGlobal.new_conclusions ?? 0} decisions=${sinceLastSeenGlobal.new_decisions ?? 0} failures=${sinceLastSeenGlobal.new_failures ?? 0}`,
572
616
  `policy=${hostState.policyPath}`,
573
- `chat_link_map=${hostState.sessionMapPath}`,
574
- `chat_link_alerts=${hostState.sessionBindingAlertsPath}`,
617
+ agentState.reason ? `agent_reason=${truncateLine(agentState.reason, 120)}` : null,
575
618
  hostState.ingressRuntime?.hooks_last_error ? `hooks_error=${truncateLine(hostState.ingressRuntime.hooks_last_error, 120)}` : null,
576
619
  '',
577
620
  ].filter(Boolean);
@@ -586,11 +629,10 @@ function renderHostTuiScreen(hostState, uiState) {
586
629
  lines.push('- a: approve selected pending contact');
587
630
  lines.push('- m: mark selected session as read');
588
631
  lines.push('- t: open task list view for selected session');
589
- lines.push('- i: open global decision inbox');
632
+ lines.push('- i: open Action Inbox approval slice');
590
633
  lines.push('- x: cancel selected task (in task views)');
591
634
  lines.push('- p: multiline reply prompt (detail view)');
592
635
  lines.push('- S: edit carry-forward summary (detail view)');
593
- lines.push('- d: try demo agent preset');
594
636
  lines.push('- o: open local history paging (detail view)');
595
637
  lines.push('- n / p: older / newer history page (history view)');
596
638
  lines.push('- s or /: search local history (history view)');
@@ -603,8 +645,6 @@ function renderHostTuiScreen(hostState, uiState) {
603
645
  lines.push('- A: apply first open trust recommendation for selected session');
604
646
  lines.push('- D: dismiss current open recommendation');
605
647
  lines.push('- R: reopen dismissed/superseded recommendation');
606
- lines.push('- B: attach selected session to the current OpenClaw chat');
607
- lines.push('- C: detach the selected chat link');
608
648
  lines.push('- q: quit');
609
649
  } else if (view === 'history') {
610
650
  lines.push('Conversation History');
@@ -663,8 +703,8 @@ function renderHostTuiScreen(hostState, uiState) {
663
703
  lines.push(...tasks.map((task, idx) => `${idx === selectedTaskIndex ? '>' : ' '} ${task.task_id} [${task.status}] ${truncateLine(task.title || task.result_summary || '', 70)}`));
664
704
  }
665
705
  } else if (view === 'decisions') {
666
- lines.push('Decision Inbox');
667
- lines.push(`pending=${pendingCollaborationEventsGlobal.length}`);
706
+ lines.push('Action Inbox (approval-only slice)');
707
+ lines.push(`pending_approvals=${pendingCollaborationEventsGlobal.length} action_inbox_total=${actionInbox.total}`);
668
708
  lines.push('actions=[u] approve [U] reject [Enter/l] open-session [j/k] select [h/Esc] back');
669
709
  lines.push('');
670
710
  lines.push(...(pendingCollaborationEventsGlobal.length
@@ -714,13 +754,12 @@ function renderHostTuiScreen(hostState, uiState) {
714
754
  lines.push(`remote=${selected.remote_did || '(unknown)'}`);
715
755
  lines.push(`trust=${selected.trust_state} unread=${selected.unread_count}`);
716
756
  lines.push(`last_preview=${selected.last_message_preview || '(none)'}`);
717
- lines.push(`chat_link=${selected.binding?.session_key || '(none)'}`);
718
- lines.push(`current_openclaw_chat=${hostState.activeChatSession || '(none)'}`);
757
+ lines.push(`compatibility_route=${selected.binding?.session_key || '(none)'}`);
719
758
  if (selected.binding_alert) {
720
- lines.push('needs_reconnect=true');
721
- lines.push(`warning=${selected.binding_alert.message}`);
759
+ lines.push('compatibility_route_stale=true');
760
+ lines.push(`repair_warning=${selected.binding_alert.message}`);
722
761
  } else {
723
- lines.push('needs_reconnect=false');
762
+ lines.push('compatibility_route_stale=false');
724
763
  }
725
764
  if (openRecommendation) {
726
765
  lines.push(`trust_action=${getTrustRecommendationActionLabel(openRecommendation)}`);
@@ -734,12 +773,13 @@ function renderHostTuiScreen(hostState, uiState) {
734
773
  lines.push('summary_objective=(none)');
735
774
  }
736
775
  if (pendingCollaborationEvents.length > 0) {
737
- lines.push(`pending_collaboration_decisions=${pendingCollaborationEvents.length}`);
738
- lines.push(`next_decision=${truncateLine(pendingCollaborationEvents[0].summary || '(none)', 100)}`);
776
+ lines.push(`action_inbox_pending_approvals=${pendingCollaborationEvents.length}`);
777
+ lines.push(`next_approval=${truncateLine(pendingCollaborationEvents[0].summary || '(none)', 100)}`);
739
778
  if (pendingCollaborationEvents[0].overdue) {
740
- lines.push(`next_decision_overdue=${Math.ceil((pendingCollaborationEvents[0].overdue_by_ms || 0) / 60000)}m`);
779
+ lines.push(`next_approval_overdue=${Math.ceil((pendingCollaborationEvents[0].overdue_by_ms || 0) / 60000)}m`);
741
780
  }
742
781
  }
782
+ lines.push(`action_inbox_total=${actionInbox.total} callbacks=${actionInbox.pending_callback} escalations=${actionInbox.pending_escalation} blocked=${actionInbox.blocked} timed_out=${actionInbox.timed_out}`);
743
783
  const sessionSeen = selected.since_last_seen || {};
744
784
  lines.push(`since_last_seen external=${sessionSeen.new_external_messages ?? 0} conclusions=${sessionSeen.new_conclusions ?? 0} decisions=${sessionSeen.new_decisions ?? 0} failures=${sessionSeen.new_failures ?? 0}`);
745
785
  const actionBar = [
@@ -748,17 +788,17 @@ function renderHostTuiScreen(hostState, uiState) {
748
788
  '[D] dismiss-rec',
749
789
  '[R] reopen-rec',
750
790
  '[m] mark-read',
751
- '[d] demo',
752
791
  '[p] reply',
753
792
  '[S] summary',
754
- pendingCollaborationEvents.length ? '[u] approve-decision' : null,
755
- pendingCollaborationEvents.length ? '[U] reject-decision' : null,
793
+ pendingCollaborationEvents.length ? '[u] approve-action' : null,
794
+ pendingCollaborationEvents.length ? '[U] reject-action' : null,
756
795
  '[o] history',
757
796
  '[t] tasks',
758
- '[B] attach-chat',
759
- '[C] detach-chat',
760
797
  ].filter(Boolean).join(' ');
761
798
  lines.push(`actions=${actionBar}`);
799
+ if (selected.binding || selected.binding_alert) {
800
+ lines.push('compatibility_note=Legacy current-thread routing is available for repair only; the default human-delivery path uses bindings + notification intents.');
801
+ }
762
802
  lines.push('');
763
803
  lines.push('Carry-Forward Summary');
764
804
  if (selectedSummary) {
@@ -827,7 +867,7 @@ function renderHostTuiScreen(hostState, uiState) {
827
867
  }
828
868
 
829
869
  lines.push('');
830
- lines.push('Keys: ↑/↓ or j/k select Enter/l open Esc/h back g/G jump r refresh a approve u/U decide A apply-rec D dismiss-rec R reopen-rec d demo m read p reply o history s search t tasks x cancel-task y dump f fix-hooks b transport->bridge c transport->channel B attach-chat C detach-chat ? help q quit');
870
+ lines.push('Keys: ↑/↓ or j/k select Enter/l open Esc/h back g/G jump r refresh a approve u/U resolve-approval A apply-rec D dismiss-rec R reopen-rec m read p reply o history s search t tasks x cancel-task y dump f fix-hooks b transport->bridge c transport->channel ? help q quit');
831
871
  return lines.join('\n');
832
872
  }
833
873
 
@@ -1338,7 +1378,7 @@ async function runHostTui(identityPath, opts) {
1338
1378
  if (key?.name === 'i') {
1339
1379
  uiState.view = 'decisions';
1340
1380
  uiState.selectedDecisionIndex = 0;
1341
- setStatus('Opened decision inbox.', 'info');
1381
+ setStatus('Opened the Action Inbox approval slice.', 'info');
1342
1382
  latestState = redraw();
1343
1383
  latestState = rerenderAfterSeenUpdate(latestState, { forceGlobal: true });
1344
1384
  return;
@@ -1548,17 +1588,17 @@ async function runHostTui(identityPath, opts) {
1548
1588
  if (!selected?.conversation_id) return;
1549
1589
  const current = latestState.activeChatSession || '(none)';
1550
1590
  const previous = selected.binding?.session_key || '(none)';
1551
- const confirmed = await confirmAction(`Attach chat link for conversation ${selected.conversation_id}\nRemote DID: ${selected.remote_did || '(unknown)'}\nCurrent OpenClaw chat: ${current}\nPrevious chat link: ${previous}\nProceed?`);
1591
+ const confirmed = await confirmAction(`Repair legacy compatibility route for conversation ${selected.conversation_id}\nRemote DID: ${selected.remote_did || '(unknown)'}\nCurrent compatibility target: ${current}\nPrevious compatibility route: ${previous}\nProceed?`);
1552
1592
  if (confirmed) {
1553
1593
  if (!latestState.activeChatSession) {
1554
- setStatus('Attach failed: no active OpenClaw chat.', 'err');
1594
+ setStatus('Compatibility repair failed: no active compatibility target.', 'err');
1555
1595
  latestState = redraw();
1556
1596
  return;
1557
1597
  }
1558
1598
  setSessionBinding(selected.conversation_id, latestState.activeChatSession);
1559
- setStatus(`Attached chat link ${selected.conversation_id} -> ${latestState.activeChatSession}`, 'ok');
1599
+ setStatus(`Compatibility route repaired ${selected.conversation_id} -> ${latestState.activeChatSession}`, 'ok');
1560
1600
  } else {
1561
- setStatus('Attach chat link cancelled.', 'warn');
1601
+ setStatus('Compatibility repair cancelled.', 'warn');
1562
1602
  }
1563
1603
  latestState = redraw();
1564
1604
  return;
@@ -1567,7 +1607,7 @@ async function runHostTui(identityPath, opts) {
1567
1607
  const selected = (latestState.sessions || []).find((session) => session.session_key === uiState.selectedSessionKey);
1568
1608
  if (!selected?.conversation_id) return;
1569
1609
  removeSessionBinding(selected.conversation_id);
1570
- setStatus(`Detached chat link for ${selected.conversation_id}`, 'ok');
1610
+ setStatus(`Cleared compatibility route for ${selected.conversation_id}`, 'ok');
1571
1611
  latestState = redraw();
1572
1612
  return;
1573
1613
  }
@@ -3520,88 +3560,52 @@ async function runHostBootstrap(opts) {
3520
3560
  process.exit(1);
3521
3561
  }
3522
3562
 
3523
- const steps = [];
3524
- const runStep = (label, args) => {
3525
- const result = runOpenClawInstall(args);
3526
- steps.push({ label, args, result });
3527
- return { label, args, result };
3528
- };
3529
-
3530
- const installStep = runStep('install', []);
3531
- const hooksStep = installStep.result.ok ? runStep('hooks repair', ['fix-hooks']) : null;
3532
- const verifyStep = installStep.result.ok && hooksStep?.result.ok
3533
- ? runStep('runtime verify', ['verify-runtime', '--fix-hooks'])
3534
- : null;
3535
-
3563
+ const installStep = runOpenClawInstall([]);
3564
+ const steps = [{ label: 'activate', result: installStep }];
3536
3565
  let runnerStep = null;
3537
- if (verifyStep?.result.ok && opts.write) {
3566
+ if (installStep.ok && opts.write) {
3538
3567
  const runnerArgs = ['init-runner', '--ingress', '--panel'];
3539
3568
  if (template) runnerArgs.push('--template', template);
3540
3569
  const supportsWrite = !template || template === 'launchd' || template === 'systemd';
3541
3570
  if (supportsWrite) runnerArgs.push('--write');
3542
- runnerStep = runStep(supportsWrite ? 'runner setup' : 'runner template', runnerArgs);
3571
+ const result = runOpenClawInstall(runnerArgs);
3572
+ steps.push({ label: supportsWrite ? 'runner setup' : 'runner template', result });
3573
+ runnerStep = { result };
3574
+ }
3575
+
3576
+ console.log('PingAgent Host Bootstrap (compatibility alias)');
3577
+ console.log('=============================================');
3578
+ console.log('bootstrap_role=compatibility_alias');
3579
+ console.log('preferred_entry=npx @pingagent/openclaw-install');
3580
+ console.log('');
3581
+
3582
+ if (installStep.stdout.trim()) {
3583
+ console.log(installStep.stdout.trim());
3584
+ }
3585
+ if (installStep.stderr.trim()) {
3586
+ console.error(installStep.stderr.trim());
3543
3587
  }
3544
3588
 
3545
3589
  if (runnerStep?.result.stdout && opts.write) {
3590
+ console.log('');
3546
3591
  console.log(runnerStep.result.stdout.trim());
3547
3592
  if (runnerStep.result.stderr.trim()) console.error(runnerStep.result.stderr.trim());
3548
- console.log('');
3549
3593
  }
3550
3594
 
3551
3595
  const failed = steps.find((step) => !step.result.ok) ?? null;
3552
- const formatStepStatus = (step, fallback = 'skipped') => (step ? (step.result.ok ? 'ok' : 'failed') : fallback);
3553
- const runnerStatus = !opts.write
3554
- ? 'not_written'
3555
- : !runnerStep
3556
- ? 'skipped'
3557
- : !runnerStep.result.ok
3558
- ? 'failed'
3559
- : (template && template !== 'launchd' && template !== 'systemd')
3560
- ? 'template_printed_not_started'
3561
- : 'written_not_started';
3562
- const installerSource = installStep.result.source || 'unknown';
3563
-
3564
- console.log('PingAgent Host Bootstrap');
3565
- console.log('========================');
3566
- console.log(`install=${formatStepStatus(installStep)}`);
3567
- console.log(`hooks_repair=${formatStepStatus(hooksStep)}`);
3568
- console.log(`runtime_verify=${formatStepStatus(verifyStep)}`);
3569
- console.log(`installer_source=${installerSource}`);
3570
- console.log(`runner=${runnerStatus}`);
3571
- console.log(`host_panel_url=${getHostPanelSurfaceUrl()}`);
3572
- console.log('host_panel_started_by_bootstrap=false');
3573
- console.log('host_panel_start=npx @pingagent/sdk web');
3574
- console.log('host_panel_start_local=pingagent web');
3575
- console.log('tui=npx @pingagent/sdk host tui');
3576
- console.log('tui_local=pingagent host tui');
3577
- console.log('');
3578
- console.log('Control surfaces:');
3579
- for (const line of getSurfaceRecommendationLines('npx @pingagent/sdk', 'pingagent')) console.log(line);
3580
- console.log('');
3581
- if (!opts.write) {
3582
- console.log('Next steps:');
3583
- console.log(' Bootstrap validates and repairs config, but it does not start long-lived daemons.');
3584
- console.log(' Start the Host Panel now with: npx @pingagent/sdk web (or pingagent web)');
3585
- console.log(' Use the headless surface now with: npx @pingagent/sdk host tui (or pingagent host tui)');
3586
- console.log(` Re-run with: npx @pingagent/sdk host bootstrap --write${template ? ` --template ${template}` : ''}`);
3587
- console.log(' Manual path: npx @pingagent/openclaw-install init-runner --ingress --panel');
3588
- } else if (runnerStep?.result.ok) {
3589
- console.log('Next steps:');
3590
- console.log(' Runner files/templates were generated, but bootstrap did not start those services.');
3591
- if (!template || template === 'launchd' || template === 'systemd') {
3592
- console.log(' Follow the printed launchctl/systemctl instructions to start them.');
3593
- } else {
3594
- console.log(' Start the generated runner with your chosen process manager.');
3596
+ if (!failed) {
3597
+ console.log('');
3598
+ console.log('Compatibility surfaces:');
3599
+ for (const line of getSurfaceRecommendationLines('npx @pingagent/sdk', 'pingagent')) console.log(line);
3600
+ if (opts.write && runnerStep?.result.ok) {
3601
+ console.log('');
3602
+ console.log('runner_files=written');
3595
3603
  }
3596
3604
  }
3597
3605
 
3598
3606
  if (failed) {
3599
- const stdout = failed.result.stdout.trim();
3600
- const stderr = failed.result.stderr.trim();
3601
3607
  console.error('');
3602
- console.error(`Bootstrap failed during ${failed.label}.`);
3603
- if (stdout) console.error(stdout);
3604
- if (stderr) console.error(stderr);
3608
+ console.error(`Bootstrap alias failed during ${failed.label}.`);
3605
3609
  process.exit(1);
3606
3610
  }
3607
3611
  }
@@ -3612,7 +3616,7 @@ const host = program
3612
3616
 
3613
3617
  host
3614
3618
  .command('bootstrap')
3615
- .description('Run the idempotent OpenClaw activation flow and print the recommended Host Panel / TUI surfaces')
3619
+ .description('Compatibility alias for the OpenClaw activation flow. Prefer npx @pingagent/openclaw-install.')
3616
3620
  .option('--write', 'Write launchd/systemd runner files when supported')
3617
3621
  .option('--template <name>', 'Runner template: launchd, systemd, docker, pm2, or supervisord')
3618
3622
  .action(async (opts) => {
@@ -3621,7 +3625,7 @@ host
3621
3625
 
3622
3626
  host
3623
3627
  .command('tui')
3624
- .description('Start the headless / low-token terminal UI for runtime, sessions, chat links, and repair actions')
3628
+ .description('Start the headless terminal repair / audit / approval surface for OpenClaw hosts')
3625
3629
  .option('--once', 'Print one snapshot and exit')
3626
3630
  .option('--refresh-ms <ms>', 'Refresh interval in interactive mode', '2000')
3627
3631
  .option('--profile <name>', 'Use profile from ~/.pingagent/<name>')
@@ -3636,7 +3640,7 @@ host
3636
3640
 
3637
3641
  program
3638
3642
  .command('web')
3639
- .description('Start the Host Panel, the primary GUI surface for runtime inspection, trust policy, and repair. Use pingagent host tui for headless or low-token operation.')
3643
+ .description('Start the Host Panel GUI for repair, audit, approvals, and advanced runtime inspection. Use pingagent host tui for headless operation.')
3640
3644
  .option('--port <port>', 'Port for the web server', '3846')
3641
3645
  .action(async (opts) => {
3642
3646
  const serverUrl = process.env.PINGAGENT_SERVER_URL || DEFAULT_SERVER;