@pingagent/sdk 0.1.17 → 0.1.19

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,10 +5,14 @@ import {
5
5
  HumanDeliveryBindingManager,
6
6
  LocalStore,
7
7
  NotificationIntentManager,
8
+ OpenClawExecutionAdapter,
8
9
  OperatorSeenStateManager,
9
10
  PingAgentClient,
11
+ SessionManager,
10
12
  TrustPolicyAuditManager,
11
13
  TrustRecommendationManager,
14
+ buildActionContract,
15
+ buildActionInboxSummary,
12
16
  buildDeliveryTimeline,
13
17
  buildProjectionPreview,
14
18
  decideContactPolicy,
@@ -23,6 +27,7 @@ import {
23
27
  getTrustRecommendationActionLabel,
24
28
  listPendingDecisionViews,
25
29
  listRecentBindingsForSession,
30
+ listRuntimeAdapterDescriptors,
26
31
  loadIdentity,
27
32
  normalizeTrustPolicyDoc,
28
33
  readCurrentActiveSessionKey,
@@ -38,7 +43,7 @@ import {
38
43
  switchTransportPreference,
39
44
  updateStoredToken,
40
45
  upsertTrustPolicyRecommendation
41
- } from "./chunk-IB7OSFZS.js";
46
+ } from "./chunk-JWBNSM4N.js";
42
47
 
43
48
  // src/web-server.ts
44
49
  import * as fs from "fs";
@@ -197,7 +202,7 @@ function getHostPanelHtml() {
197
202
  <div class="profile-list" id="profileList"></div>
198
203
  <div class="nav">
199
204
  <button id="navRuntime" class="active">Runtime</button>
200
- <button id="navDecisions">Decisions</button>
205
+ <button id="navDecisions">Action Inbox</button>
201
206
  <button id="navPolicy">Policy</button>
202
207
  </div>
203
208
  <div class="link-row">
@@ -259,12 +264,12 @@ function getHostPanelHtml() {
259
264
  <section id="decisionsPanel" class="panel">
260
265
  <div class="grid two-col">
261
266
  <div class="card">
262
- <h2>Decision Inbox</h2>
263
- <div id="decisionInboxSummary" class="muted small" style="margin-bottom:12px">Loading pending decisions\u2026</div>
267
+ <h2>Action Inbox</h2>
268
+ <div id="decisionInboxSummary" class="muted small" style="margin-bottom:12px">Loading pending actions\u2026</div>
264
269
  <div class="audit-list" id="decisionInboxList"></div>
265
270
  </div>
266
271
  <div class="card">
267
- <h2>Human Delivery</h2>
272
+ <h2>Callback / Delivery</h2>
268
273
  <div id="projectionOutboxSummary" class="muted small" style="margin-bottom:12px">Loading human-delivery state\u2026</div>
269
274
  <div class="audit-list" id="projectionOutboxList"></div>
270
275
  </div>
@@ -574,9 +579,92 @@ function getHostPanelHtml() {
574
579
  }).join('');
575
580
  }
576
581
 
582
+ function renderActionInboxItems(items) {
583
+ if (!Array.isArray(items) || !items.length) {
584
+ return '<div class="empty">No pending actions right now.</div>';
585
+ }
586
+ return items.map(function (item) {
587
+ const actionContract = item.action_contract || {};
588
+ const badges = [
589
+ '<span class="badge">' + esc(item.status || 'pending_callback') + '</span>',
590
+ '<span class="badge">' + esc(item.risk_level || 'moderate') + '</span>',
591
+ item.overdue ? '<span class="badge alert">timed_out</span>' : '',
592
+ ].filter(Boolean).join('');
593
+ const actions = [];
594
+ if (item.source === 'decision' && item.event_id) {
595
+ actions.push('<button class="action-btn inbox-decision-btn" data-event-id="' + esc(item.event_id) + '" data-decision="approved">Approve</button>');
596
+ actions.push('<button class="danger-btn inbox-decision-btn" data-event-id="' + esc(item.event_id) + '" data-decision="rejected">Reject</button>');
597
+ }
598
+ if (item.source === 'notification_intent' && item.intent_id) {
599
+ actions.push('<button class="secondary-btn human-delivery-action-btn" data-action="retry_intent" data-intent-id="' + esc(item.intent_id) + '">Retry</button>');
600
+ actions.push('<button class="secondary-btn human-delivery-action-btn" data-action="re_resolve_intent_binding" data-intent-id="' + esc(item.intent_id) + '">Re-resolve</button>');
601
+ actions.push('<button class="danger-btn human-delivery-action-btn" data-action="cancel_intent" data-intent-id="' + esc(item.intent_id) + '">Cancel</button>');
602
+ }
603
+ if (item.source === 'external_escalation' && item.escalation_id) {
604
+ if (item.status === 'pending_approval') {
605
+ actions.push('<button class="action-btn openclaw-escalation-action-btn" data-action="approve" data-escalation-id="' + esc(item.escalation_id) + '">Approve</button>');
606
+ actions.push('<button class="danger-btn openclaw-escalation-action-btn" data-action="reject" data-escalation-id="' + esc(item.escalation_id) + '">Reject</button>');
607
+ } else {
608
+ actions.push('<button class="secondary-btn openclaw-escalation-action-btn" data-action="retry" data-escalation-id="' + esc(item.escalation_id) + '">Retry</button>');
609
+ }
610
+ actions.push('<button class="danger-btn openclaw-escalation-action-btn" data-action="cancel" data-escalation-id="' + esc(item.escalation_id) + '">Cancel</button>');
611
+ }
612
+ if (item.session_key) {
613
+ actions.push('<button class="secondary-btn inbox-open-detail-btn" data-session-key="' + esc(item.session_key || '') + '">Open Detail</button>');
614
+ }
615
+ const deadlineLine = item.deadline
616
+ ? '<div class="muted small" style="margin-top:8px">deadline=' + esc(fmtTs(item.deadline)) + '</div>'
617
+ : '';
618
+ const consequenceLine = item.consequence
619
+ ? '<div class="muted small" style="margin-top:8px">consequence=' + esc(item.consequence) + '</div>'
620
+ : '';
621
+ return '<div class="audit-row"><div class="top"><strong>' + esc(item.title || item.summary || '(no title)') + '</strong><div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end">' + badges + '</div></div>' +
622
+ '<div class="muted small">' + esc(item.source || 'runtime') + ' \xB7 required_action=' + esc(item.required_action || actionContract.required_action || '(review)') + (actionContract && actionContract.response_state ? ' \xB7 response_state=' + esc(actionContract.response_state) : '') + '</div>' +
623
+ '<div class="muted small" style="margin-top:8px">session=' + esc(item.session_key || '(none)') + ' \xB7 conversation=' + esc(item.conversation_id || '(none)') + '</div>' +
624
+ '<div style="margin-top:8px">' + esc(item.summary || '(no summary)') + '</div>' +
625
+ deadlineLine +
626
+ consequenceLine +
627
+ (actionContract && actionContract.callback_target
628
+ ? '<div class="muted small" style="margin-top:8px">callback_target=' + esc(actionContract.callback_target) + '</div>'
629
+ : '') +
630
+ (actionContract && actionContract.policy_scope && actionContract.policy_scope.designated_target
631
+ ? '<div class="muted small" style="margin-top:8px">designated_target=' + esc(actionContract.policy_scope.designated_target) + '</div>'
632
+ : '') +
633
+ (actions.length ? '<div class="row-actions" style="margin-top:10px">' + actions.join('') + '</div>' : '') +
634
+ '</div>';
635
+ }).join('');
636
+ }
637
+
638
+ function renderOpenClawAdapterExecution(adapter) {
639
+ if (!adapter) return '';
640
+ const escalations = Array.isArray(adapter.recent_escalations) ? adapter.recent_escalations : [];
641
+ const resumes = Array.isArray(adapter.recent_callback_resumes) ? adapter.recent_callback_resumes : [];
642
+ const escalationBlock = escalations.length
643
+ ? escalations.slice(0, 8).map(function (item) {
644
+ return '<div class="audit-row"><div class="top"><strong>Escalation #' + esc(item.id) + '</strong><span class="badge">' + esc(item.status || 'pending') + '</span></div>' +
645
+ '<div class="muted small">risk=' + esc(item.risk_level || 'moderate') + ' \xB7 token_required=' + esc(item.token_required ? 'true' : 'false') + ' \xB7 session=' + esc(item.session_key || '(none)') + '</div>' +
646
+ '<div style="margin-top:8px">' + esc(item.required_action || '(no action)') + '</div>' +
647
+ '</div>';
648
+ }).join('')
649
+ : '<div class="empty">No recent escalations.</div>';
650
+ const resumeBlock = resumes.length
651
+ ? resumes.slice(0, 8).map(function (item) {
652
+ return '<div class="audit-row"><div class="top"><strong>Callback #' + esc(item.id) + '</strong><span class="badge">' + esc(item.resume_delivery_status || 'pending') + '</span></div>' +
653
+ '<div class="muted small">response_state=' + esc(item.response_state || 'acknowledged') + ' \xB7 accepted=' + esc(item.accepted ? 'true' : 'false') + '</div>' +
654
+ (item.rejection_reason ? '<div class="muted small" style="margin-top:8px;color:#fecaca">' + esc(item.rejection_reason) + '</div>' : '') +
655
+ '</div>';
656
+ }).join('')
657
+ : '<div class="empty">No callback resumes yet.</div>';
658
+ return '<div style="margin-top:12px">' +
659
+ '<div class="label">OpenClaw Adapter Execution</div>' +
660
+ '<div class="muted small" style="margin-top:6px">pending_tokens=' + esc(adapter.token_required_pending || 0) + ' \xB7 blocked=' + esc(adapter.blocked_count || 0) + ' \xB7 failed=' + esc(adapter.failed_count || 0) + ' \xB7 expired=' + esc(adapter.expired_count || 0) + '</div>' +
661
+ '<div class="audit-list" style="margin-top:8px">' + escalationBlock + resumeBlock + '</div>' +
662
+ '</div>';
663
+ }
664
+
577
665
  function renderRecentBindings(bindings) {
578
666
  if (!Array.isArray(bindings) || !bindings.length) {
579
- return '<div class="empty">No human reply targets recorded yet. Once a person talks to OpenClaw from any channel, the explicit reply target will appear here.</div>';
667
+ return '<div class="empty">No external callback targets recorded yet. Once a person or designated target replies from any attached channel, the explicit target binding will appear here.</div>';
580
668
  }
581
669
  return bindings.map(function (binding) {
582
670
  const actions = binding.status === 'active'
@@ -635,6 +723,50 @@ function getHostPanelHtml() {
635
723
  });
636
724
  }
637
725
 
726
+ async function runOpenClawEscalationAction(action, escalationId) {
727
+ if (!action || !escalationId) return null;
728
+ if (action === 'retry') {
729
+ return api('/api/runtime-adapters/openclaw/escalations/' + encodeURIComponent(String(escalationId)) + '/retry', {
730
+ method: 'POST',
731
+ headers: { 'Content-Type': 'application/json' },
732
+ });
733
+ }
734
+ if (action === 'cancel') {
735
+ return api('/api/runtime-adapters/openclaw/escalations/' + encodeURIComponent(String(escalationId)) + '/cancel', {
736
+ method: 'POST',
737
+ headers: { 'Content-Type': 'application/json' },
738
+ });
739
+ }
740
+ if (action === 'approve' || action === 'reject') {
741
+ return api('/api/runtime-adapters/openclaw/callback-resumes', {
742
+ method: 'POST',
743
+ headers: { 'Content-Type': 'application/json' },
744
+ body: JSON.stringify({
745
+ external_escalation_id: escalationId,
746
+ response_state: action === 'approve' ? 'approved' : 'rejected',
747
+ resolution_source: 'host_panel',
748
+ message_ref: 'host_panel:' + action + ':' + String(escalationId),
749
+ }),
750
+ });
751
+ }
752
+ return null;
753
+ }
754
+
755
+ function wireOpenClawEscalationActionButtons(root) {
756
+ if (!root || !root.querySelectorAll) return;
757
+ root.querySelectorAll('.openclaw-escalation-action-btn').forEach(function (btn) {
758
+ btn.addEventListener('click', async function () {
759
+ const action = btn.getAttribute('data-action');
760
+ const escalationId = Number(btn.getAttribute('data-escalation-id'));
761
+ if (!action || !Number.isInteger(escalationId) || escalationId <= 0) return;
762
+ await runOpenClawEscalationAction(action, escalationId);
763
+ await refreshAll();
764
+ if (state.selectedSessionKey) await loadSession(state.selectedSessionKey);
765
+ setTab(state.tab || 'runtime');
766
+ });
767
+ });
768
+ }
769
+
638
770
  async function markSeen(scopeType, scopeKey) {
639
771
  return api('/api/runtime/seen', {
640
772
  method: 'POST',
@@ -895,34 +1027,25 @@ function getHostPanelHtml() {
895
1027
 
896
1028
  function renderDecisionInbox() {
897
1029
  const data = state.decisions;
1030
+ const actionInbox = data && data.actionInbox ? data.actionInbox : { items: [], total: 0, pending_approval: 0, pending_callback: 0, pending_escalation: 0, blocked: 0, degraded: 0, timed_out: 0, high_risk: 0 };
898
1031
  const pendingDecisions = data && Array.isArray(data.pendingDecisions) ? data.pendingDecisions : [];
899
- const overdueDecisions = pendingDecisions.filter(function (event) { return !!event.overdue; });
900
1032
  const pendingOutbox = data && Array.isArray(data.projectionOutboxPending) ? data.projectionOutboxPending : [];
901
1033
  const failedOutbox = data && Array.isArray(data.projectionOutboxFailed) ? data.projectionOutboxFailed : [];
902
1034
  const pendingIntents = data && Array.isArray(data.notificationIntentsPending) ? data.notificationIntentsPending : [];
903
1035
  const unresolvedIntents = data && Array.isArray(data.notificationIntentsUnresolved) ? data.notificationIntentsUnresolved : [];
904
1036
  const failedIntents = data && Array.isArray(data.notificationIntentsFailed) ? data.notificationIntentsFailed : [];
905
1037
  const humanDelivery = data && data.humanDelivery ? data.humanDelivery : null;
1038
+ const openclawAdapter = data && data.openclawAdapter ? data.openclawAdapter : null;
906
1039
  const boundChannelReply = !!(humanDelivery && humanDelivery.mode === 'bound_channel_reply');
907
- document.getElementById('decisionInboxSummary').textContent = boundChannelReply
908
- ? ('pending=' + pendingDecisions.length + ' \xB7 overdue=' + overdueDecisions.length + ' \xB7 notify_pending=' + pendingIntents.length + ' \xB7 unresolved=' + unresolvedIntents.length + ' \xB7 notify_failed=' + failedIntents.length)
909
- : ('pending=' + pendingDecisions.length + ' \xB7 overdue=' + overdueDecisions.length + ' \xB7 projection_pending=' + pendingOutbox.length + ' \xB7 projection_failed=' + failedOutbox.length);
910
- document.getElementById('decisionInboxList').innerHTML = pendingDecisions.length
911
- ? pendingDecisions.map(function (event) {
912
- return '<div class="audit-row"><div class="top"><strong>' + esc(event.summary || '(no summary)') + '</strong>' +
913
- '<div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end"><span class="badge">' + esc(event.severity || 'warning') + '</span>' +
914
- (event.overdue ? '<span class="badge alert">overdue</span>' : '') + '</div></div>' +
915
- '<div class="muted small">' + esc(fmtTs(event.ts_ms)) + ' \xB7 session=' + esc(event.session_key || '(none)') + ' \xB7 target=' + esc(event.target_human_session || '(none)') +
916
- (event.overdue ? ' \xB7 overdue_by=' + esc(Math.ceil((event.overdue_by_ms || 0) / 60000)) + 'm' : '') + '</div>' +
917
- '<div class="muted small" style="margin-top:8px">detail_ref=' + esc(event.conversation_id || '(none)') + '</div>' +
918
- '<div class="row-actions">' +
919
- '<button class="action-btn inbox-decision-btn" data-event-id="' + esc(event.id) + '" data-decision="approved">Approve</button>' +
920
- '<button class="danger-btn inbox-decision-btn" data-event-id="' + esc(event.id) + '" data-decision="rejected">Reject</button>' +
921
- '<button class="secondary-btn inbox-open-detail-btn" data-session-key="' + esc(event.session_key || '') + '">Open Detail</button>' +
922
- '</div>' +
923
- '</div>';
924
- }).join('')
925
- : '<div class="empty">No pending collaboration decisions.</div>';
1040
+ document.getElementById('decisionInboxSummary').textContent =
1041
+ 'total=' + (actionInbox.total || 0) +
1042
+ ' \xB7 approvals=' + (actionInbox.pending_approval || 0) +
1043
+ ' \xB7 callbacks=' + (actionInbox.pending_callback || 0) +
1044
+ ' \xB7 escalations=' + (actionInbox.pending_escalation || 0) +
1045
+ ' \xB7 blocked=' + (openclawAdapter ? (openclawAdapter.blocked_count || 0) : actionInbox.blocked || 0) +
1046
+ ' \xB7 timed_out=' + (actionInbox.timed_out || 0) +
1047
+ ' \xB7 high_risk=' + (actionInbox.high_risk || 0);
1048
+ document.getElementById('decisionInboxList').innerHTML = renderActionInboxItems(actionInbox.items);
926
1049
 
927
1050
  const combinedOutbox = pendingOutbox.concat(failedOutbox);
928
1051
  document.getElementById('projectionOutboxSummary').textContent = boundChannelReply
@@ -934,7 +1057,7 @@ function getHostPanelHtml() {
934
1057
  ? renderHumanDeliveryIntents(
935
1058
  pendingIntents.concat(unresolvedIntents).concat(failedIntents),
936
1059
  'No pending, unresolved, or failed human-delivery intents.',
937
- )
1060
+ ) + renderOpenClawAdapterExecution(openclawAdapter)
938
1061
  : (combinedOutbox.length
939
1062
  ? combinedOutbox.map(function (entry) {
940
1063
  return '<div class="audit-row"><div class="top"><strong>' + esc(entry.summary || '(no summary)') + '</strong>' +
@@ -972,6 +1095,7 @@ function getHostPanelHtml() {
972
1095
  });
973
1096
  });
974
1097
  wireHumanDeliveryActionButtons(document.getElementById('projectionOutboxList'));
1098
+ wireOpenClawEscalationActionButtons(document.getElementById('decisionInboxList'));
975
1099
  }
976
1100
 
977
1101
  function getOverviewSessions() {
@@ -1013,9 +1137,7 @@ function getHostPanelHtml() {
1013
1137
  const transportHealth = overview.transportHealth || null;
1014
1138
  const sinceLastSeen = overview.sinceLastSeen || null;
1015
1139
  const humanDelivery = overview.humanDelivery || null;
1016
- const overdueDecisions = Array.isArray(overview.pendingDecisions)
1017
- ? overview.pendingDecisions.filter(function (event) { return !!event.overdue; })
1018
- : [];
1140
+ const actionInbox = overview.actionInbox || { total: 0, timed_out: 0, high_risk: 0 };
1019
1141
  const statusClass = agentState && (agentState.blocked || agentState.degraded)
1020
1142
  ? 'degraded'
1021
1143
  : ingressState.className;
@@ -1039,8 +1161,9 @@ function getHostPanelHtml() {
1039
1161
  : '') +
1040
1162
  '<div class="muted small">Public link: ' + esc(overview.publicSelf && overview.publicSelf.public_url ? overview.publicSelf.public_url : '(not ready yet)') + '</div>' +
1041
1163
  '<div class="summary-pills">' +
1042
- '<span class="pill">pending_decisions=' + esc((overview.pendingDecisions || []).length) + '</span>' +
1043
- '<span class="pill">overdue=' + esc(overdueDecisions.length) + '</span>' +
1164
+ '<span class="pill">action_inbox=' + esc(actionInbox.total || 0) + '</span>' +
1165
+ '<span class="pill">timed_out=' + esc(actionInbox.timed_out || 0) + '</span>' +
1166
+ '<span class="pill">high_risk=' + esc(actionInbox.high_risk || 0) + '</span>' +
1044
1167
  '<span class="pill">projection_policy=' + esc(overview.collaborationProjection && overview.collaborationProjection.preset ? overview.collaborationProjection.preset : 'balanced') + '</span>' +
1045
1168
  (humanDelivery ? '<span class="pill">human_delivery=' + esc(humanDelivery.mode || 'projection_outbox') + '</span>' : '') +
1046
1169
  (agentState && agentState.active_session_key ? '<span class="pill">active_session=ready</span>' : '') +
@@ -1071,16 +1194,16 @@ function getHostPanelHtml() {
1071
1194
  { label: 'Unread', value: overview.unreadTotal, sub: 'session-first inbox state' },
1072
1195
  { label: 'Tasks', value: overview.tasksTotal, sub: 'recent local task threads' },
1073
1196
  { label: 'Audit', value: overview.auditSummary.total_events, sub: 'policy / runtime audit events' },
1074
- { label: 'Collaboration', value: overview.collaborationSummary ? overview.collaborationSummary.total_events : 0, sub: overview.collaborationSummary ? ('pending_review=' + overview.collaborationSummary.pending_approvals) : 'projected external collaboration events' },
1075
- { label: 'Human Delivery', value: humanDelivery ? ((humanDelivery.pending_intents || 0) + '/' + (humanDelivery.active_bindings || 0)) : '-', sub: humanDelivery ? ('mode=' + (humanDelivery.mode || 'projection_outbox') + ' unresolved=' + (humanDelivery.unresolved_intents || 0) + ' failed=' + (humanDelivery.failed_intents || 0)) : 'binding-based human notification state' },
1197
+ { label: 'Action Inbox', value: actionInbox.total || 0, sub: 'high_risk=' + (actionInbox.high_risk || 0) + ' timed_out=' + (actionInbox.timed_out || 0) },
1198
+ { label: 'External Delivery', value: humanDelivery ? ((humanDelivery.pending_intents || 0) + '/' + (humanDelivery.active_bindings || 0)) : '-', sub: humanDelivery ? ('mode=' + (humanDelivery.mode || 'projection_outbox') + ' unresolved=' + (humanDelivery.unresolved_intents || 0) + ' failed=' + (humanDelivery.failed_intents || 0)) : 'binding-based callback delivery state' },
1076
1199
  { label: 'Recommendations', value: overview.recommendationSummary ? overview.recommendationSummary.total : overview.recommendations.length, sub: overview.recommendationSummary ? JSON.stringify(overview.recommendationSummary.by_status || {}) : 'learned policy suggestions' },
1077
1200
  { label: 'Public Link', value: overview.publicSelf && overview.publicSelf.public_url ? 'ready' : 'disabled', sub: overview.publicSelf && overview.publicSelf.public_url ? overview.publicSelf.public_url : 'create a hosted shareable profile link' },
1078
1201
  ];
1079
1202
  document.getElementById('statsGrid').innerHTML = stats.map(function (item) {
1080
1203
  return '<div class="card"><div class="label">' + esc(item.label) + '</div><div class="value">' + esc(item.value) + '</div><div class="muted small">' + esc(item.sub) + '</div></div>';
1081
1204
  }).join('');
1082
- document.getElementById('taskList').innerHTML = '<div class="task-row"><div class="top"><strong>Human Delivery Channels</strong><span class="badge">' + esc((humanDelivery && humanDelivery.supported_channels ? humanDelivery.supported_channels.length : 0) + '/' + (humanDelivery && humanDelivery.channel_capabilities ? humanDelivery.channel_capabilities.length : 0)) + '</span></div>' +
1083
- '<div class="muted small">Capabilities and canary state for explicit human-delivery replies.</div>' +
1205
+ document.getElementById('taskList').innerHTML = '<div class="task-row"><div class="top"><strong>External Callback Channels</strong><span class="badge">' + esc((humanDelivery && humanDelivery.supported_channels ? humanDelivery.supported_channels.length : 0) + '/' + (humanDelivery && humanDelivery.channel_capabilities ? humanDelivery.channel_capabilities.length : 0)) + '</span></div>' +
1206
+ '<div class="muted small">Capabilities and canary state for explicit external callback delivery.</div>' +
1084
1207
  '<div class="audit-list" style="margin-top:8px">' + renderHumanDeliveryCapabilities(humanDelivery) + '</div></div>';
1085
1208
 
1086
1209
  const toggleUnreadBtn = document.getElementById('toggleUnreadBtn');
@@ -1091,7 +1214,7 @@ function getHostPanelHtml() {
1091
1214
  state.showUnreadOnly
1092
1215
  ? 'No unread sessions.'
1093
1216
  : (agentState && agentState.summary
1094
- ? agentState.summary + ' Wait for inbound work or review pending decisions.'
1217
+ ? agentState.summary + ' Wait for inbound work or review the Action Inbox when callback, escalation, repair, or approval work appears.'
1095
1218
  : 'No sessions yet.')
1096
1219
  ) + '</div>';
1097
1220
  } else {
@@ -1331,7 +1454,7 @@ conversation=' + result.conversation_id));
1331
1454
  : '') +
1332
1455
  '</div>' +
1333
1456
  (isAdvanced
1334
- ? '<div class="muted small" style="margin-top:8px">Compatibility routing is repair-only. The default human-delivery path uses Human Delivery Binding plus Notification Intent.</div>'
1457
+ ? '<div class="muted small" style="margin-top:8px">Compatibility routing is repair-only. The default external callback path uses External Target Binding and Action / Callback Intent.</div>'
1335
1458
  : '') +
1336
1459
  '<div class="form-grid" style="margin-top:16px">' +
1337
1460
  '<label class="label">Reply in this session</label>' +
@@ -1420,9 +1543,9 @@ conversation=' + result.conversation_id));
1420
1543
  '</div>';
1421
1544
  }).join('') : '<div class="empty">No audit events for this session.</div>') +
1422
1545
  '</div></div>' +
1423
- '<div><div class="label">Human Delivery Posture</div><div class="audit-list" style="margin-top:8px">' +
1546
+ '<div><div class="label">External Delivery Posture</div><div class="audit-list" style="margin-top:8px">' +
1424
1547
  '<div class="audit-row"><div class="top"><strong>Projection policy</strong><span class="badge">' + esc(projectionPreset) + '</span></div>' +
1425
- '<div class="muted small">The collaboration session keeps the raw transcript. Human-visible updates now flow through Human Delivery Binding plus Notification Intent. Projection outbox remains visible for audit and compatibility.</div>' +
1548
+ '<div class="muted small">The collaboration session keeps the raw transcript. External replies and human-visible updates now flow through External Target Binding and Action / Callback Intent. Projection outbox remains visible for audit and compatibility.</div>' +
1426
1549
  '<div style="margin-top:8px">Use this detail view to inspect every raw message, task, handoff, audit event, and runtime change before taking action.</div>' +
1427
1550
  (sinceLastSeen
1428
1551
  ? '<div class="summary-pills" style="margin-top:8px">' +
@@ -1435,7 +1558,7 @@ conversation=' + result.conversation_id));
1435
1558
  ? '<div class="muted small" style="margin-top:8px">transport=' + esc(transportHealth.transport_mode || 'bridge') + ' \xB7 state=' + esc(transportHealth.state || 'Ready') + ' \xB7 retry_queue=' + esc(transportHealth.retry_queue_length || 0) + '</div>'
1436
1559
  : '') +
1437
1560
  (humanDelivery
1438
- ? '<div class="muted small" style="margin-top:8px">human_delivery=' + esc(humanDelivery.mode || 'projection_outbox') + ' \xB7 unresolved=' + esc(humanDelivery.unresolved_intents || 0) + ' \xB7 failed=' + esc(humanDelivery.failed_intents || 0) + '</div>'
1561
+ ? '<div class="muted small" style="margin-top:8px">external_delivery=' + esc(humanDelivery.mode || 'projection_outbox') + ' \xB7 unresolved=' + esc(humanDelivery.unresolved_intents || 0) + ' \xB7 failed=' + esc(humanDelivery.failed_intents || 0) + '</div>'
1439
1562
  : '') +
1440
1563
  (projectionOutbox.length
1441
1564
  ? '<div class="muted small" style="margin-top:8px">outbox=' + esc(projectionOutbox[0].status || 'pending') + ' \xB7 target=' + esc(projectionOutbox[0].target_human_session || '(missing)') + '</div>'
@@ -1457,11 +1580,11 @@ conversation=' + result.conversation_id));
1457
1580
 
1458
1581
  el.innerHTML +=
1459
1582
  '<div class="grid two-col" style="margin-top:16px">' +
1460
- '<div><div class="label">Human Reply Targets</div><div class="audit-list" style="margin-top:8px">' +
1583
+ '<div><div class="label">External Target Bindings</div><div class="audit-list" style="margin-top:8px">' +
1461
1584
  renderRecentBindings(recentBindings) +
1462
1585
  '</div></div>' +
1463
- '<div><div class="label">Notification Intents</div><div class="audit-list" style="margin-top:8px">' +
1464
- renderHumanDeliveryIntents(recentNotificationIntents, 'No notification intents for this session yet.') +
1586
+ '<div><div class="label">Action / Callback Intents</div><div class="audit-list" style="margin-top:8px">' +
1587
+ renderHumanDeliveryIntents(recentNotificationIntents, 'No action or callback intents for this session yet.') +
1465
1588
  '</div></div>' +
1466
1589
  '</div>';
1467
1590
 
@@ -2003,6 +2126,9 @@ function resolvePath(p) {
2003
2126
  if (!p || !p.startsWith("~")) return p;
2004
2127
  return path.join(process.env.HOME || process.env.USERPROFILE || "", p.slice(1));
2005
2128
  }
2129
+ function isRecord(value) {
2130
+ return !!value && typeof value === "object" && !Array.isArray(value);
2131
+ }
2006
2132
  function findOpenClawInstallScript() {
2007
2133
  const explicit = process.env.PINGAGENT_OPENCLAW_INSTALL_BIN?.trim();
2008
2134
  if (explicit) return { kind: "script", cmd: process.execPath, args: [path.resolve(explicit)] };
@@ -2321,6 +2447,15 @@ function readHumanDeliveryState(storePath, limit = 12) {
2321
2447
  store.close();
2322
2448
  }
2323
2449
  }
2450
+ function readOpenClawAdapterState(storePath, limit = 20) {
2451
+ const store = new LocalStore(storePath);
2452
+ try {
2453
+ const adapter = new OpenClawExecutionAdapter(store);
2454
+ return adapter.buildOverview(limit);
2455
+ } finally {
2456
+ store.close();
2457
+ }
2458
+ }
2324
2459
  function buildOpenClawAgentStatePayload(input) {
2325
2460
  return deriveOpenClawAgentState({
2326
2461
  runtime_status: input.runtimeStatus ?? null,
@@ -2381,9 +2516,9 @@ function formatRetentionLabel(ttlMs) {
2381
2516
  }
2382
2517
  function describeHostedTier(tier) {
2383
2518
  if (tier === "plus") return "shareable identity + higher relay + first alias";
2384
- if (tier === "pro") return "multi-identity communication + audit export";
2519
+ if (tier === "pro") return "multi-identity escalation + callback governance + audit export";
2385
2520
  if (tier === "enterprise") return "high-scale governance + operational controls";
2386
- return "free communication-first entry tier";
2521
+ return "free escalation-first entry tier";
2387
2522
  }
2388
2523
  async function buildRuntimeOverviewPayload(ctx) {
2389
2524
  const client = ctx.client;
@@ -2446,8 +2581,10 @@ async function buildRuntimeOverviewPayload(ctx) {
2446
2581
  const transportHealth = readTransportHealthState(ctx.storePath);
2447
2582
  const sinceLastSeen = readSinceLastSeenState(ctx.storePath, "host_panel", "global");
2448
2583
  const humanDelivery = readHumanDeliveryState(ctx.storePath, 20);
2584
+ const openclawAdapter = readOpenClawAdapterState(ctx.storePath, 20);
2449
2585
  const decisionStore = new LocalStore(ctx.storePath);
2450
2586
  let decisionViews = [];
2587
+ let actionInbox = null;
2451
2588
  let humanDeliveryDetails = null;
2452
2589
  try {
2453
2590
  decisionViews = listPendingDecisionViews(decisionStore, 100);
@@ -2469,6 +2606,14 @@ async function buildRuntimeOverviewPayload(ctx) {
2469
2606
  humanDelivery,
2470
2607
  transportHealth
2471
2608
  });
2609
+ actionInbox = buildActionInboxSummary({
2610
+ pending_decisions: decisionViews,
2611
+ notification_intents: humanDelivery?.recent_notification_intents ?? [],
2612
+ external_escalations: openclawAdapter.recent_escalations,
2613
+ agent_state: agentState,
2614
+ include_runtime: true,
2615
+ limit: 30
2616
+ });
2472
2617
  return {
2473
2618
  did: ctx.myDid,
2474
2619
  serverUrl: ctx.serverUrl,
@@ -2508,8 +2653,10 @@ async function buildRuntimeOverviewPayload(ctx) {
2508
2653
  projectionOutbox,
2509
2654
  transportHealth,
2510
2655
  agentState,
2656
+ actionInbox,
2511
2657
  sinceLastSeen,
2512
2658
  humanDelivery,
2659
+ openclawAdapter,
2513
2660
  humanDeliveryDetails,
2514
2661
  pendingDecisions: decisionViews,
2515
2662
  recentCollaborationEvents: collaborationEventManager.listRecent(20),
@@ -2585,6 +2732,8 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
2585
2732
  let projectionPreview = null;
2586
2733
  let pendingDecisionViews = [];
2587
2734
  let humanDelivery = null;
2735
+ let actionInbox = null;
2736
+ let openclawAdapter = null;
2588
2737
  try {
2589
2738
  projectionOutbox = new CollaborationProjectionOutboxManager(outboxStore).listBySession(session.session_key, 20);
2590
2739
  notificationIntents = new NotificationIntentManager(outboxStore).listBySession(session.session_key, 20);
@@ -2603,6 +2752,7 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
2603
2752
  );
2604
2753
  pendingDecisionViews = listPendingDecisionViews(outboxStore, 100).filter((event) => event.session_key === session.session_key).slice(0, 20);
2605
2754
  humanDelivery = summarizeHumanDelivery(outboxStore, 20);
2755
+ openclawAdapter = new OpenClawExecutionAdapter(outboxStore).buildOverview(50);
2606
2756
  } finally {
2607
2757
  outboxStore.close();
2608
2758
  }
@@ -2613,6 +2763,14 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
2613
2763
  humanDelivery,
2614
2764
  transportHealth: readTransportHealthState(ctx.storePath)
2615
2765
  });
2766
+ actionInbox = buildActionInboxSummary({
2767
+ pending_decisions: pendingDecisionViews,
2768
+ notification_intents: notificationIntents,
2769
+ external_escalations: (openclawAdapter?.recent_escalations ?? []).filter((item) => item.session_key === session.session_key),
2770
+ agent_state: agentState,
2771
+ include_runtime: false,
2772
+ limit: 20
2773
+ });
2616
2774
  return {
2617
2775
  session,
2618
2776
  sessionSummary: sessionSummaryManager.get(session.session_key),
@@ -2636,6 +2794,15 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
2636
2794
  deliveryTimeline,
2637
2795
  projectionPreview,
2638
2796
  humanDelivery,
2797
+ openclawAdapter: openclawAdapter ? {
2798
+ ...openclawAdapter,
2799
+ recent_escalations: openclawAdapter.recent_escalations.filter((item) => item.session_key === session.session_key),
2800
+ recent_callback_resumes: openclawAdapter.recent_callback_resumes.filter((item) => {
2801
+ const payloadSessionKey = typeof item.detail?.session_key === "string" ? item.detail.session_key : null;
2802
+ return payloadSessionKey === session.session_key;
2803
+ })
2804
+ } : null,
2805
+ actionInbox,
2639
2806
  policyExplain: buildPolicyDecisionShape(ctx.identityPath, session.remote_did, { runtimeMode: getRuntimeMode() }),
2640
2807
  tasks: tasks.map((task) => ({
2641
2808
  ...task,
@@ -2884,14 +3051,32 @@ async function handleApi(pathname, req, ctx) {
2884
3051
  if (req.method === "GET") {
2885
3052
  const pendingDecisions = listPendingDecisionViews(store, 100);
2886
3053
  const intentManager = new NotificationIntentManager(store);
3054
+ const humanDelivery = summarizeHumanDelivery(store, 20);
3055
+ const openclawAdapter = new OpenClawExecutionAdapter(store).buildOverview(40);
3056
+ const actionInbox = buildActionInboxSummary({
3057
+ pending_decisions: pendingDecisions,
3058
+ notification_intents: humanDelivery.recent_notification_intents,
3059
+ external_escalations: openclawAdapter.recent_escalations,
3060
+ agent_state: buildOpenClawAgentStatePayload({
3061
+ runtimeStatus: readIngressRuntimeStatus(),
3062
+ sessions: new SessionManager(store).listRecentSessions(50),
3063
+ pendingDecisions,
3064
+ humanDelivery,
3065
+ transportHealth: readTransportHealthState(ctx.storePath)
3066
+ }),
3067
+ include_runtime: true,
3068
+ limit: 40
3069
+ });
2887
3070
  return {
2888
3071
  pendingDecisions,
3072
+ actionInbox,
2889
3073
  projectionOutboxPending: outboxManager.listByStatus("pending", 50),
2890
3074
  projectionOutboxFailed: outboxManager.listByStatus("failed", 50),
2891
3075
  notificationIntentsPending: intentManager.listByStatus("pending", 50).concat(intentManager.listByStatus("bound", 50)),
2892
3076
  notificationIntentsUnresolved: intentManager.listByStatus("unresolved", 50),
2893
3077
  notificationIntentsFailed: intentManager.listByStatus("failed", 50),
2894
- humanDelivery: summarizeHumanDelivery(store, 20)
3078
+ humanDelivery,
3079
+ openclawAdapter
2895
3080
  };
2896
3081
  }
2897
3082
  if (parts[2] === "resolve" && req.method === "POST") {
@@ -3405,6 +3590,96 @@ async function handleApi(pathname, req, ctx) {
3405
3590
  }
3406
3591
  }
3407
3592
  }
3593
+ if (parts[0] === "runtime-adapters") {
3594
+ if (req.method === "GET" && (!parts[1] || parts[1] === "overview")) {
3595
+ const openclaw = readOpenClawAdapterState(ctx.storePath, 20);
3596
+ return {
3597
+ adapters: listRuntimeAdapterDescriptors().map((adapter) => ({
3598
+ ...adapter,
3599
+ health: adapter.id === "openclaw" ? openclaw.health : null
3600
+ })),
3601
+ openclaw
3602
+ };
3603
+ }
3604
+ if (parts[1] === "openclaw") {
3605
+ if ((parts[2] === "overview" || !parts[2]) && req.method === "GET") {
3606
+ return readOpenClawAdapterState(ctx.storePath, 20);
3607
+ }
3608
+ const store = new LocalStore(ctx.storePath);
3609
+ try {
3610
+ const adapter = new OpenClawExecutionAdapter(store);
3611
+ if (parts[2] === "escalations" && req.method === "POST" && !parts[3]) {
3612
+ const body = await readBody(req);
3613
+ const actionContract = buildActionContract({
3614
+ body: isRecord(body?.action_contract) ? body.action_contract : body
3615
+ });
3616
+ const created = adapter.enqueueEscalation({
3617
+ run_id: typeof body?.run_id === "string" ? body.run_id : void 0,
3618
+ workflow_id: typeof body?.workflow_id === "string" ? body.workflow_id : void 0,
3619
+ session_key: String(body?.session_key ?? "").trim(),
3620
+ conversation_id: typeof body?.conversation_id === "string" ? body.conversation_id : void 0,
3621
+ source_event_id: Number.isInteger(body?.source_event_id) ? Number(body.source_event_id) : void 0,
3622
+ source_intent_id: Number.isInteger(body?.source_intent_id) ? Number(body.source_intent_id) : void 0,
3623
+ action_contract: actionContract,
3624
+ callback_target: typeof body?.callback_target === "string" ? body.callback_target : void 0,
3625
+ metadata: isRecord(body?.metadata) ? body.metadata : void 0
3626
+ });
3627
+ const dispatched = adapter.dispatchEscalation(created.escalation.id);
3628
+ return {
3629
+ ok: true,
3630
+ ...created,
3631
+ escalation: dispatched.escalation,
3632
+ notification_intent: dispatched.intent ?? created.notification_intent,
3633
+ dispatch: dispatched,
3634
+ overview: adapter.buildOverview(20)
3635
+ };
3636
+ }
3637
+ if (parts[2] === "callback-resumes" && req.method === "POST") {
3638
+ const body = await readBody(req);
3639
+ const resolutionSource = String(body?.resolution_source ?? "external").trim();
3640
+ const result = await adapter.resumeCallback({
3641
+ external_escalation_id: Number.isInteger(body?.external_escalation_id) ? Number(body.external_escalation_id) : void 0,
3642
+ run_id: typeof body?.run_id === "string" ? body.run_id : void 0,
3643
+ workflow_id: typeof body?.workflow_id === "string" ? body.workflow_id : void 0,
3644
+ response_state: String(body?.response_state ?? "acknowledged"),
3645
+ response_payload: isRecord(body?.response_payload) ? body.response_payload : void 0,
3646
+ message_ref: typeof body?.message_ref === "string" ? body.message_ref : void 0,
3647
+ callback_target: typeof body?.callback_target === "string" ? body.callback_target : void 0,
3648
+ capability_token: typeof body?.capability_token === "string" ? body.capability_token : void 0,
3649
+ binding_id: Number.isInteger(body?.binding_id) ? Number(body.binding_id) : void 0,
3650
+ trusted_local: resolutionSource === "host_panel" || resolutionSource === "mcp"
3651
+ });
3652
+ return {
3653
+ ok: true,
3654
+ ...result,
3655
+ overview: adapter.buildOverview(20)
3656
+ };
3657
+ }
3658
+ if (parts[2] === "escalations" && parts[3] && parts[4] === "retry" && req.method === "POST") {
3659
+ const escalationId = Number(parts[3]);
3660
+ if (!Number.isInteger(escalationId) || escalationId <= 0) throw new Error("valid escalation id is required");
3661
+ const retried = adapter.retryEscalation(escalationId);
3662
+ return {
3663
+ ok: true,
3664
+ ...retried,
3665
+ overview: adapter.buildOverview(20)
3666
+ };
3667
+ }
3668
+ if (parts[2] === "escalations" && parts[3] && parts[4] === "cancel" && req.method === "POST") {
3669
+ const escalationId = Number(parts[3]);
3670
+ if (!Number.isInteger(escalationId) || escalationId <= 0) throw new Error("valid escalation id is required");
3671
+ const canceled = adapter.cancelEscalation(escalationId);
3672
+ return {
3673
+ ok: true,
3674
+ escalation: canceled,
3675
+ overview: adapter.buildOverview(20)
3676
+ };
3677
+ }
3678
+ } finally {
3679
+ store.close();
3680
+ }
3681
+ }
3682
+ }
3408
3683
  if (parts[0] === "public") {
3409
3684
  if ((!parts[1] || parts[1] === "self") && req.method === "GET") {
3410
3685
  await maybeEnsureHostedPublicLink(ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pingagent/sdk",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -35,8 +35,8 @@
35
35
  "uuid": "^11.0.0",
36
36
  "ws": "^8.0.0",
37
37
  "@pingagent/protocol": "0.1.1",
38
- "@pingagent/a2a": "0.1.1",
39
- "@pingagent/schemas": "0.1.4"
38
+ "@pingagent/schemas": "0.1.4",
39
+ "@pingagent/a2a": "0.1.1"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/better-sqlite3": "^7.6.0",