@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 +106 -102
- package/dist/chunk-34F6AUBW.js +6537 -0
- package/dist/chunk-66PVWBOU.js +6412 -0
- package/dist/chunk-DJ5XF3WK.js +7857 -0
- package/dist/chunk-IB7OSFZS.js +5951 -0
- package/dist/chunk-JWBNSM4N.js +7948 -0
- package/dist/chunk-PEKTGNH6.js +7948 -0
- package/dist/chunk-V7NQC6XA.js +7948 -0
- package/dist/index.d.ts +565 -37
- package/dist/index.js +45 -1
- package/dist/web-server.js +399 -69
- package/package.json +1 -1
package/dist/web-server.js
CHANGED
|
@@ -5,15 +5,20 @@ 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,
|
|
15
19
|
decideTaskPolicy,
|
|
16
20
|
defaultTrustPolicyDoc,
|
|
21
|
+
deriveOpenClawAgentState,
|
|
17
22
|
deriveTransportHealth,
|
|
18
23
|
ensureTokenValid,
|
|
19
24
|
getActiveSessionFilePath,
|
|
@@ -22,6 +27,7 @@ import {
|
|
|
22
27
|
getTrustRecommendationActionLabel,
|
|
23
28
|
listPendingDecisionViews,
|
|
24
29
|
listRecentBindingsForSession,
|
|
30
|
+
listRuntimeAdapterDescriptors,
|
|
25
31
|
loadIdentity,
|
|
26
32
|
normalizeTrustPolicyDoc,
|
|
27
33
|
readCurrentActiveSessionKey,
|
|
@@ -37,7 +43,7 @@ import {
|
|
|
37
43
|
switchTransportPreference,
|
|
38
44
|
updateStoredToken,
|
|
39
45
|
upsertTrustPolicyRecommendation
|
|
40
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-JWBNSM4N.js";
|
|
41
47
|
|
|
42
48
|
// src/web-server.ts
|
|
43
49
|
import * as fs from "fs";
|
|
@@ -196,7 +202,7 @@ function getHostPanelHtml() {
|
|
|
196
202
|
<div class="profile-list" id="profileList"></div>
|
|
197
203
|
<div class="nav">
|
|
198
204
|
<button id="navRuntime" class="active">Runtime</button>
|
|
199
|
-
<button id="navDecisions">
|
|
205
|
+
<button id="navDecisions">Action Inbox</button>
|
|
200
206
|
<button id="navPolicy">Policy</button>
|
|
201
207
|
</div>
|
|
202
208
|
<div class="link-row">
|
|
@@ -258,13 +264,13 @@ function getHostPanelHtml() {
|
|
|
258
264
|
<section id="decisionsPanel" class="panel">
|
|
259
265
|
<div class="grid two-col">
|
|
260
266
|
<div class="card">
|
|
261
|
-
<h2>
|
|
262
|
-
<div id="decisionInboxSummary" class="muted small" style="margin-bottom:12px">Loading pending
|
|
267
|
+
<h2>Action Inbox</h2>
|
|
268
|
+
<div id="decisionInboxSummary" class="muted small" style="margin-bottom:12px">Loading pending actions\u2026</div>
|
|
263
269
|
<div class="audit-list" id="decisionInboxList"></div>
|
|
264
270
|
</div>
|
|
265
271
|
<div class="card">
|
|
266
|
-
<h2>
|
|
267
|
-
<div id="projectionOutboxSummary" class="muted small" style="margin-bottom:12px">Loading
|
|
272
|
+
<h2>Callback / Delivery</h2>
|
|
273
|
+
<div id="projectionOutboxSummary" class="muted small" style="margin-bottom:12px">Loading human-delivery state\u2026</div>
|
|
268
274
|
<div class="audit-list" id="projectionOutboxList"></div>
|
|
269
275
|
</div>
|
|
270
276
|
</div>
|
|
@@ -281,7 +287,7 @@ function getHostPanelHtml() {
|
|
|
281
287
|
<option value="balanced">balanced</option>
|
|
282
288
|
<option value="strict">strict</option>
|
|
283
289
|
</select>
|
|
284
|
-
<div class="muted small">Balanced is the default: key conclusions, handoffs, repairs, and required decisions are
|
|
290
|
+
<div class="muted small">Balanced is the default: key conclusions, handoffs, repairs, and required decisions become notification intents and are sent through bound-channel reply; ordinary progress stays summary-first.</div>
|
|
285
291
|
<div class="row-actions">
|
|
286
292
|
<button class="action-btn" id="saveProjectionPresetBtn">Save projection preset</button>
|
|
287
293
|
</div>
|
|
@@ -573,9 +579,92 @@ function getHostPanelHtml() {
|
|
|
573
579
|
}).join('');
|
|
574
580
|
}
|
|
575
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
|
+
|
|
576
665
|
function renderRecentBindings(bindings) {
|
|
577
666
|
if (!Array.isArray(bindings) || !bindings.length) {
|
|
578
|
-
return '<div class="empty">No
|
|
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>';
|
|
579
668
|
}
|
|
580
669
|
return bindings.map(function (binding) {
|
|
581
670
|
const actions = binding.status === 'active'
|
|
@@ -634,6 +723,50 @@ function getHostPanelHtml() {
|
|
|
634
723
|
});
|
|
635
724
|
}
|
|
636
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
|
+
|
|
637
770
|
async function markSeen(scopeType, scopeKey) {
|
|
638
771
|
return api('/api/runtime/seen', {
|
|
639
772
|
method: 'POST',
|
|
@@ -866,7 +999,7 @@ function getHostPanelHtml() {
|
|
|
866
999
|
event.approval_required
|
|
867
1000
|
? '<span class="badge">' + esc(event.approval_status === 'pending' ? 'review' : event.approval_status) + '</span>'
|
|
868
1001
|
: '',
|
|
869
|
-
event.target_human_session ? '<span class="badge">human
|
|
1002
|
+
event.target_human_session ? '<span class="badge">human delivery</span>' : '',
|
|
870
1003
|
].filter(Boolean).join('');
|
|
871
1004
|
const actions = event.approval_required && event.approval_status === 'pending'
|
|
872
1005
|
? '<div class="row-actions" style="margin-top:10px">' +
|
|
@@ -894,46 +1027,37 @@ function getHostPanelHtml() {
|
|
|
894
1027
|
|
|
895
1028
|
function renderDecisionInbox() {
|
|
896
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 };
|
|
897
1031
|
const pendingDecisions = data && Array.isArray(data.pendingDecisions) ? data.pendingDecisions : [];
|
|
898
|
-
const overdueDecisions = pendingDecisions.filter(function (event) { return !!event.overdue; });
|
|
899
1032
|
const pendingOutbox = data && Array.isArray(data.projectionOutboxPending) ? data.projectionOutboxPending : [];
|
|
900
1033
|
const failedOutbox = data && Array.isArray(data.projectionOutboxFailed) ? data.projectionOutboxFailed : [];
|
|
901
1034
|
const pendingIntents = data && Array.isArray(data.notificationIntentsPending) ? data.notificationIntentsPending : [];
|
|
902
1035
|
const unresolvedIntents = data && Array.isArray(data.notificationIntentsUnresolved) ? data.notificationIntentsUnresolved : [];
|
|
903
1036
|
const failedIntents = data && Array.isArray(data.notificationIntentsFailed) ? data.notificationIntentsFailed : [];
|
|
904
1037
|
const humanDelivery = data && data.humanDelivery ? data.humanDelivery : null;
|
|
1038
|
+
const openclawAdapter = data && data.openclawAdapter ? data.openclawAdapter : null;
|
|
905
1039
|
const boundChannelReply = !!(humanDelivery && humanDelivery.mode === 'bound_channel_reply');
|
|
906
|
-
document.getElementById('decisionInboxSummary').textContent =
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
(event.overdue ? ' \xB7 overdue_by=' + esc(Math.ceil((event.overdue_by_ms || 0) / 60000)) + 'm' : '') + '</div>' +
|
|
916
|
-
'<div class="muted small" style="margin-top:8px">detail_ref=' + esc(event.conversation_id || '(none)') + '</div>' +
|
|
917
|
-
'<div class="row-actions">' +
|
|
918
|
-
'<button class="action-btn inbox-decision-btn" data-event-id="' + esc(event.id) + '" data-decision="approved">Approve</button>' +
|
|
919
|
-
'<button class="danger-btn inbox-decision-btn" data-event-id="' + esc(event.id) + '" data-decision="rejected">Reject</button>' +
|
|
920
|
-
'<button class="secondary-btn inbox-open-detail-btn" data-session-key="' + esc(event.session_key || '') + '">Open Detail</button>' +
|
|
921
|
-
'</div>' +
|
|
922
|
-
'</div>';
|
|
923
|
-
}).join('')
|
|
924
|
-
: '<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);
|
|
925
1049
|
|
|
926
1050
|
const combinedOutbox = pendingOutbox.concat(failedOutbox);
|
|
927
1051
|
document.getElementById('projectionOutboxSummary').textContent = boundChannelReply
|
|
928
1052
|
? ('bound reply mode \xB7 pending=' + pendingIntents.length + ' \xB7 unresolved=' + unresolvedIntents.length + ' \xB7 failed=' + failedIntents.length)
|
|
929
1053
|
: (combinedOutbox.length
|
|
930
|
-
? '
|
|
931
|
-
: 'No
|
|
1054
|
+
? 'Legacy projection outbox remains visible for audit and compatibility. Failed rows stay visible until delivery recovers.'
|
|
1055
|
+
: 'No legacy projection-outbox rows need attention.');
|
|
932
1056
|
document.getElementById('projectionOutboxList').innerHTML = boundChannelReply
|
|
933
1057
|
? renderHumanDeliveryIntents(
|
|
934
1058
|
pendingIntents.concat(unresolvedIntents).concat(failedIntents),
|
|
935
1059
|
'No pending, unresolved, or failed human-delivery intents.',
|
|
936
|
-
)
|
|
1060
|
+
) + renderOpenClawAdapterExecution(openclawAdapter)
|
|
937
1061
|
: (combinedOutbox.length
|
|
938
1062
|
? combinedOutbox.map(function (entry) {
|
|
939
1063
|
return '<div class="audit-row"><div class="top"><strong>' + esc(entry.summary || '(no summary)') + '</strong>' +
|
|
@@ -943,7 +1067,7 @@ function getHostPanelHtml() {
|
|
|
943
1067
|
(entry.last_error ? '<div style="margin-top:8px">' + esc(entry.last_error) + '</div>' : '') +
|
|
944
1068
|
'</div>';
|
|
945
1069
|
}).join('')
|
|
946
|
-
: '<div class="empty">No pending or failed projection deliveries.</div>');
|
|
1070
|
+
: '<div class="empty">No pending or failed legacy projection deliveries.</div>');
|
|
947
1071
|
|
|
948
1072
|
document.querySelectorAll('.inbox-decision-btn').forEach(function (btn) {
|
|
949
1073
|
btn.addEventListener('click', async function () {
|
|
@@ -971,6 +1095,7 @@ function getHostPanelHtml() {
|
|
|
971
1095
|
});
|
|
972
1096
|
});
|
|
973
1097
|
wireHumanDeliveryActionButtons(document.getElementById('projectionOutboxList'));
|
|
1098
|
+
wireOpenClawEscalationActionButtons(document.getElementById('decisionInboxList'));
|
|
974
1099
|
}
|
|
975
1100
|
|
|
976
1101
|
function getOverviewSessions() {
|
|
@@ -1008,24 +1133,40 @@ function getHostPanelHtml() {
|
|
|
1008
1133
|
if (!overview) return;
|
|
1009
1134
|
syncSelectedSessionFromOverview();
|
|
1010
1135
|
const ingressState = ingressStatusModel(overview);
|
|
1136
|
+
const agentState = overview.agentState || null;
|
|
1011
1137
|
const transportHealth = overview.transportHealth || null;
|
|
1012
1138
|
const sinceLastSeen = overview.sinceLastSeen || null;
|
|
1013
1139
|
const humanDelivery = overview.humanDelivery || null;
|
|
1014
|
-
const
|
|
1015
|
-
|
|
1016
|
-
|
|
1140
|
+
const actionInbox = overview.actionInbox || { total: 0, timed_out: 0, high_risk: 0 };
|
|
1141
|
+
const statusClass = agentState && (agentState.blocked || agentState.degraded)
|
|
1142
|
+
? 'degraded'
|
|
1143
|
+
: ingressState.className;
|
|
1144
|
+
const statusLabel = agentState && agentState.state
|
|
1145
|
+
? agentState.state
|
|
1146
|
+
: ingressState.label;
|
|
1147
|
+
const statusDetail = agentState && agentState.summary
|
|
1148
|
+
? agentState.summary
|
|
1149
|
+
: ingressState.detail;
|
|
1017
1150
|
document.getElementById('activationCard').innerHTML =
|
|
1018
1151
|
'<div class="status-strip">' +
|
|
1019
1152
|
'<div class="status-main">' +
|
|
1020
|
-
'<h2>
|
|
1021
|
-
'<div class="status-state ' +
|
|
1022
|
-
'<div class="muted small">' + esc(
|
|
1153
|
+
'<h2>Agent Runtime</h2>' +
|
|
1154
|
+
'<div class="status-state ' + statusClass + '">' + esc(statusLabel) + '</div>' +
|
|
1155
|
+
'<div class="muted small">' + esc(statusDetail) + '</div>' +
|
|
1156
|
+
(agentState && agentState.next_action
|
|
1157
|
+
? '<div class="muted small">next_action=' + esc(agentState.next_action) + '</div>'
|
|
1158
|
+
: '') +
|
|
1159
|
+
(agentState && agentState.reason
|
|
1160
|
+
? '<div class="muted small">reason=' + esc(agentState.reason) + '</div>'
|
|
1161
|
+
: '') +
|
|
1023
1162
|
'<div class="muted small">Public link: ' + esc(overview.publicSelf && overview.publicSelf.public_url ? overview.publicSelf.public_url : '(not ready yet)') + '</div>' +
|
|
1024
1163
|
'<div class="summary-pills">' +
|
|
1025
|
-
'<span class="pill">
|
|
1026
|
-
'<span class="pill">
|
|
1027
|
-
'<span class="pill">
|
|
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>' +
|
|
1167
|
+
'<span class="pill">projection_policy=' + esc(overview.collaborationProjection && overview.collaborationProjection.preset ? overview.collaborationProjection.preset : 'balanced') + '</span>' +
|
|
1028
1168
|
(humanDelivery ? '<span class="pill">human_delivery=' + esc(humanDelivery.mode || 'projection_outbox') + '</span>' : '') +
|
|
1169
|
+
(agentState && agentState.active_session_key ? '<span class="pill">active_session=ready</span>' : '') +
|
|
1029
1170
|
'</div>' +
|
|
1030
1171
|
'</div>' +
|
|
1031
1172
|
'<div style="min-width:320px">' +
|
|
@@ -1053,23 +1194,29 @@ function getHostPanelHtml() {
|
|
|
1053
1194
|
{ label: 'Unread', value: overview.unreadTotal, sub: 'session-first inbox state' },
|
|
1054
1195
|
{ label: 'Tasks', value: overview.tasksTotal, sub: 'recent local task threads' },
|
|
1055
1196
|
{ label: 'Audit', value: overview.auditSummary.total_events, sub: 'policy / runtime audit events' },
|
|
1056
|
-
{ label: '
|
|
1057
|
-
{ label: '
|
|
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' },
|
|
1058
1199
|
{ label: 'Recommendations', value: overview.recommendationSummary ? overview.recommendationSummary.total : overview.recommendations.length, sub: overview.recommendationSummary ? JSON.stringify(overview.recommendationSummary.by_status || {}) : 'learned policy suggestions' },
|
|
1059
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' },
|
|
1060
1201
|
];
|
|
1061
1202
|
document.getElementById('statsGrid').innerHTML = stats.map(function (item) {
|
|
1062
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>';
|
|
1063
1204
|
}).join('');
|
|
1064
|
-
document.getElementById('taskList').innerHTML = '<div class="task-row"><div class="top"><strong>
|
|
1065
|
-
'<div class="muted small">Capabilities and canary state for explicit
|
|
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>' +
|
|
1066
1207
|
'<div class="audit-list" style="margin-top:8px">' + renderHumanDeliveryCapabilities(humanDelivery) + '</div></div>';
|
|
1067
1208
|
|
|
1068
1209
|
const toggleUnreadBtn = document.getElementById('toggleUnreadBtn');
|
|
1069
1210
|
if (toggleUnreadBtn) toggleUnreadBtn.textContent = 'Unread only: ' + (state.showUnreadOnly ? 'on' : 'off');
|
|
1070
1211
|
const sessions = getVisibleSessions();
|
|
1071
1212
|
if (!sessions.length) {
|
|
1072
|
-
document.getElementById('sessionList').innerHTML = '<div class="empty">' + (
|
|
1213
|
+
document.getElementById('sessionList').innerHTML = '<div class="empty">' + (
|
|
1214
|
+
state.showUnreadOnly
|
|
1215
|
+
? 'No unread sessions.'
|
|
1216
|
+
: (agentState && agentState.summary
|
|
1217
|
+
? agentState.summary + ' Wait for inbound work or review the Action Inbox when callback, escalation, repair, or approval work appears.'
|
|
1218
|
+
: 'No sessions yet.')
|
|
1219
|
+
) + '</div>';
|
|
1073
1220
|
} else {
|
|
1074
1221
|
document.getElementById('sessionList').innerHTML = sessions.map(function (session) {
|
|
1075
1222
|
const active = session.session_key === state.selectedSessionKey ? ' active' : '';
|
|
@@ -1079,7 +1226,7 @@ function getHostPanelHtml() {
|
|
|
1079
1226
|
? '<span class="badge alert">new ' + esc(countSinceLastSeen(session.since_last_seen)) + '</span>'
|
|
1080
1227
|
: ''),
|
|
1081
1228
|
session.binding_alert
|
|
1082
|
-
? '<button type="button" class="badge alert rebind-badge-btn" data-session="' + esc(session.session_key) + '" data-conversation="' + esc(session.conversation_id || '') + '" data-bound-session="' + esc(session.mapped_work_session || '') + '" data-remote-did="' + esc(session.remote_did || '') + '" title="
|
|
1229
|
+
? '<button type="button" class="badge alert rebind-badge-btn" data-session="' + esc(session.session_key) + '" data-conversation="' + esc(session.conversation_id || '') + '" data-bound-session="' + esc(session.mapped_work_session || '') + '" data-remote-did="' + esc(session.remote_did || '') + '" title="Legacy compatibility route needs attention">Compatibility repair</button>'
|
|
1083
1230
|
: '',
|
|
1084
1231
|
].filter(Boolean).join('');
|
|
1085
1232
|
return '<div class="session-row' + active + '" data-session="' + esc(session.session_key) + '">' +
|
|
@@ -1087,7 +1234,7 @@ function getHostPanelHtml() {
|
|
|
1087
1234
|
'<div class="muted small">unread=' + esc(session.unread_count) + ' \xB7 last=' + esc(fmtTs(session.last_remote_activity_at || session.updated_at)) + '</div>' +
|
|
1088
1235
|
(state.detailMode === 'advanced'
|
|
1089
1236
|
? '<div class="muted small" style="margin-top:6px">conversation=' + esc(session.conversation_id || '(none)') + '</div>' +
|
|
1090
|
-
'<div class="muted small">
|
|
1237
|
+
'<div class="muted small">compatibility_route=' + esc(session.mapped_work_session || '(unbound)') + (session.is_active_work_session ? ' \xB7 active_route=true' : '') + '</div>'
|
|
1091
1238
|
: '') +
|
|
1092
1239
|
'<div class="muted small" style="margin-top:6px">' + esc(session.last_message_preview || '(no preview)') + '</div>' +
|
|
1093
1240
|
'</div>';
|
|
@@ -1252,8 +1399,8 @@ conversation=' + result.conversation_id));
|
|
|
1252
1399
|
if (task && task.action) summaryPills.push('<span class="pill">task=' + esc(task.action) + '</span>');
|
|
1253
1400
|
if (session.trust_state) summaryPills.push('<span class="pill">trust=' + esc(session.trust_state) + '</span>');
|
|
1254
1401
|
if (Number(session.unread_count || 0) > 0) summaryPills.push('<span class="pill">unread=' + esc(session.unread_count) + '</span>');
|
|
1255
|
-
if (binding && binding.
|
|
1256
|
-
|
|
1402
|
+
if (binding && binding.target_key) summaryPills.push('<span class="pill">human reply target ready</span>');
|
|
1403
|
+
if (isAdvanced && activeWorkSession) summaryPills.push('<span class="pill">compatibility route available</span>');
|
|
1257
1404
|
const policyBlock = isAdvanced
|
|
1258
1405
|
? '<pre style="margin-top:8px">[Contact]\\naction=' + esc(contact.action) + '\\nsource=' + esc(contact.source) + (contact.matched_rule ? '\\nmatched_rule=' + esc(contact.matched_rule) : '') + '\\n' + esc(contact.explanation) + '\\n\\n[Task]\\naction=' + esc(task.action) + '\\nsource=' + esc(task.source) + (task.matched_rule ? '\\nmatched_rule=' + esc(task.matched_rule) : '') + '\\n' + esc(task.explanation) + '</pre>'
|
|
1259
1406
|
: '<div class="summary-pills" style="margin-top:8px">' + summaryPills.join('') + '</div>' +
|
|
@@ -1271,8 +1418,8 @@ conversation=' + result.conversation_id));
|
|
|
1271
1418
|
? '<div style="margin-top:8px">' +
|
|
1272
1419
|
'<div class="muted small">session=' + esc(session.session_key) + '</div>' +
|
|
1273
1420
|
'<div class="muted small">conversation=' + esc(session.conversation_id || '(none)') + '</div>' +
|
|
1274
|
-
'<div class="muted small">
|
|
1275
|
-
'<div class="muted small">
|
|
1421
|
+
'<div class="muted small">compatibility_active_session=' + esc(activeWorkSession || '(none)') + '</div>' +
|
|
1422
|
+
'<div class="muted small">reply_binding=' + esc(binding ? (binding.target_key || binding.session_key || '(bound)') : '(unbound)') + '</div>' +
|
|
1276
1423
|
'</div>'
|
|
1277
1424
|
: '') +
|
|
1278
1425
|
(openRecommendation
|
|
@@ -1280,11 +1427,11 @@ conversation=' + result.conversation_id));
|
|
|
1280
1427
|
: (reopenRecommendation ? '<div class="muted small" style="margin-top:8px">trust_action=' + esc(recommendationActionLabel(reopenRecommendation)) + '</div>' : '')) +
|
|
1281
1428
|
(summaryPills.length ? '<div class="summary-pills">' + summaryPills.join('') + '</div>' : '') +
|
|
1282
1429
|
(pendingCollaborationEvents.length
|
|
1283
|
-
? '<div style="margin-top:10px;padding:10px 12px;border:1px solid #f59e0b;border-radius:10px;background:rgba(120,53,15,0.25);color:#fde68a"><strong>Decision pending</strong><div class="small" style="margin-top:6px">A collaboration update needs review before
|
|
1430
|
+
? '<div style="margin-top:10px;padding:10px 12px;border:1px solid #f59e0b;border-radius:10px;background:rgba(120,53,15,0.25);color:#fde68a"><strong>Decision pending</strong><div class="small" style="margin-top:6px">A collaboration update needs review before this session is treated as settled. Resolve it in the Collaboration Feed below.' +
|
|
1284
1431
|
(pendingCollaborationEvents[0].overdue ? ' This item is overdue.' : '') + '</div></div>'
|
|
1285
1432
|
: '') +
|
|
1286
1433
|
(bindingAlert
|
|
1287
|
-
? '<div style="margin-top:10px;padding:10px 12px;border:1px solid #ef4444;border-radius:10px;background:rgba(127,29,29,0.25);color:#fecaca"><strong>
|
|
1434
|
+
? '<div style="margin-top:10px;padding:10px 12px;border:1px solid #ef4444;border-radius:10px;background:rgba(127,29,29,0.25);color:#fecaca"><strong>Compatibility route stale</strong><div class="small" style="margin-top:6px">' + esc(isAdvanced ? (bindingAlert.message || 'Legacy current-thread compatibility routing is missing. Repair it only if you intentionally depend on the compatibility path.') : 'Legacy current-thread compatibility routing is stale. Default human delivery continues to use bindings + notification intents.') + '</div></div>'
|
|
1288
1435
|
: '') +
|
|
1289
1436
|
'<div class="row-actions">' +
|
|
1290
1437
|
(session.trust_state === 'pending'
|
|
@@ -1299,11 +1446,16 @@ conversation=' + result.conversation_id));
|
|
|
1299
1446
|
(!openRecommendation && reopenRecommendation
|
|
1300
1447
|
? '<button class="secondary-btn reopen-session-recommendation-btn" data-session="' + esc(session.session_key) + '">Reopen</button>'
|
|
1301
1448
|
: '') +
|
|
1302
|
-
'<button class="action-btn bind-current-btn" data-conversation="' + esc(session.conversation_id || '') + '">Attach to Current Chat</button>' +
|
|
1303
1449
|
'<button class="secondary-btn mark-read-btn" data-session="' + esc(session.session_key) + '">Mark read</button>' +
|
|
1304
1450
|
'<button class="secondary-btn copy-session-link-btn" data-session="' + esc(session.session_key) + '">Copy Session Link</button>' +
|
|
1305
|
-
|
|
1451
|
+
(isAdvanced
|
|
1452
|
+
? '<button class="action-btn bind-current-btn" data-conversation="' + esc(session.conversation_id || '') + '">Repair Compatibility Route</button>' +
|
|
1453
|
+
'<button class="danger-btn clear-binding-btn" data-conversation="' + esc(session.conversation_id || '') + '">Clear Compatibility Route</button>'
|
|
1454
|
+
: '') +
|
|
1306
1455
|
'</div>' +
|
|
1456
|
+
(isAdvanced
|
|
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>'
|
|
1458
|
+
: '') +
|
|
1307
1459
|
'<div class="form-grid" style="margin-top:16px">' +
|
|
1308
1460
|
'<label class="label">Reply in this session</label>' +
|
|
1309
1461
|
'<textarea id="sessionReplyInput" placeholder="Send a text reply in this session"></textarea>' +
|
|
@@ -1391,9 +1543,9 @@ conversation=' + result.conversation_id));
|
|
|
1391
1543
|
'</div>';
|
|
1392
1544
|
}).join('') : '<div class="empty">No audit events for this session.</div>') +
|
|
1393
1545
|
'</div></div>' +
|
|
1394
|
-
'<div><div class="label">
|
|
1546
|
+
'<div><div class="label">External Delivery Posture</div><div class="audit-list" style="margin-top:8px">' +
|
|
1395
1547
|
'<div class="audit-row"><div class="top"><strong>Projection policy</strong><span class="badge">' + esc(projectionPreset) + '</span></div>' +
|
|
1396
|
-
'<div class="muted small">The collaboration session keeps the raw transcript.
|
|
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>' +
|
|
1397
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>' +
|
|
1398
1550
|
(sinceLastSeen
|
|
1399
1551
|
? '<div class="summary-pills" style="margin-top:8px">' +
|
|
@@ -1406,7 +1558,7 @@ conversation=' + result.conversation_id));
|
|
|
1406
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>'
|
|
1407
1559
|
: '') +
|
|
1408
1560
|
(humanDelivery
|
|
1409
|
-
? '<div class="muted small" style="margin-top:8px">
|
|
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>'
|
|
1410
1562
|
: '') +
|
|
1411
1563
|
(projectionOutbox.length
|
|
1412
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>'
|
|
@@ -1428,11 +1580,11 @@ conversation=' + result.conversation_id));
|
|
|
1428
1580
|
|
|
1429
1581
|
el.innerHTML +=
|
|
1430
1582
|
'<div class="grid two-col" style="margin-top:16px">' +
|
|
1431
|
-
'<div><div class="label">
|
|
1583
|
+
'<div><div class="label">External Target Bindings</div><div class="audit-list" style="margin-top:8px">' +
|
|
1432
1584
|
renderRecentBindings(recentBindings) +
|
|
1433
1585
|
'</div></div>' +
|
|
1434
|
-
'<div><div class="label">
|
|
1435
|
-
renderHumanDeliveryIntents(recentNotificationIntents, 'No
|
|
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.') +
|
|
1436
1588
|
'</div></div>' +
|
|
1437
1589
|
'</div>';
|
|
1438
1590
|
|
|
@@ -1586,7 +1738,7 @@ conversation=' + result.conversation_id));
|
|
|
1586
1738
|
const previous = previousBinding || (state.session && state.session.binding ? state.session.binding.session_key : null) || '(unbound)';
|
|
1587
1739
|
const targetRemoteDid = remoteDid || (state.session && state.session.session ? state.session.session.remote_did : null) || '(unknown)';
|
|
1588
1740
|
const confirmed = window.confirm(
|
|
1589
|
-
'
|
|
1741
|
+
'Repair the legacy current-thread compatibility route for this PingAgent session?' +
|
|
1590
1742
|
'
|
|
1591
1743
|
|
|
1592
1744
|
Conversation: ' + conversationId +
|
|
@@ -1594,9 +1746,9 @@ Conversation: ' + conversationId +
|
|
|
1594
1746
|
Remote DID: ' + targetRemoteDid +
|
|
1595
1747
|
'
|
|
1596
1748
|
|
|
1597
|
-
Current OpenClaw
|
|
1749
|
+
Current OpenClaw compatibility target: ' + (current || '(none)') +
|
|
1598
1750
|
'
|
|
1599
|
-
Previous
|
|
1751
|
+
Previous compatibility route: ' + previous
|
|
1600
1752
|
);
|
|
1601
1753
|
if (!confirmed) return;
|
|
1602
1754
|
await api('/api/runtime/session-bindings/bind-current', {
|
|
@@ -1974,6 +2126,9 @@ function resolvePath(p) {
|
|
|
1974
2126
|
if (!p || !p.startsWith("~")) return p;
|
|
1975
2127
|
return path.join(process.env.HOME || process.env.USERPROFILE || "", p.slice(1));
|
|
1976
2128
|
}
|
|
2129
|
+
function isRecord(value) {
|
|
2130
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2131
|
+
}
|
|
1977
2132
|
function findOpenClawInstallScript() {
|
|
1978
2133
|
const explicit = process.env.PINGAGENT_OPENCLAW_INSTALL_BIN?.trim();
|
|
1979
2134
|
if (explicit) return { kind: "script", cmd: process.execPath, args: [path.resolve(explicit)] };
|
|
@@ -2292,6 +2447,25 @@ function readHumanDeliveryState(storePath, limit = 12) {
|
|
|
2292
2447
|
store.close();
|
|
2293
2448
|
}
|
|
2294
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
|
+
}
|
|
2459
|
+
function buildOpenClawAgentStatePayload(input) {
|
|
2460
|
+
return deriveOpenClawAgentState({
|
|
2461
|
+
runtime_status: input.runtimeStatus ?? null,
|
|
2462
|
+
sessions: input.sessions ?? [],
|
|
2463
|
+
pending_decisions: Array.isArray(input.pendingDecisions) ? input.pendingDecisions.length : 0,
|
|
2464
|
+
human_delivery: input.humanDelivery ?? null,
|
|
2465
|
+
transport_health: input.transportHealth ?? null,
|
|
2466
|
+
blocked_reason: input.blockedReason ?? null
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2295
2469
|
function buildPolicyDecisionShape(identityPath, remoteDid, opts) {
|
|
2296
2470
|
const policy = readTrustPolicyDoc(identityPath);
|
|
2297
2471
|
const runtimeMode = opts?.runtimeMode ?? getRuntimeMode();
|
|
@@ -2342,9 +2516,9 @@ function formatRetentionLabel(ttlMs) {
|
|
|
2342
2516
|
}
|
|
2343
2517
|
function describeHostedTier(tier) {
|
|
2344
2518
|
if (tier === "plus") return "shareable identity + higher relay + first alias";
|
|
2345
|
-
if (tier === "pro") return "multi-identity
|
|
2519
|
+
if (tier === "pro") return "multi-identity escalation + callback governance + audit export";
|
|
2346
2520
|
if (tier === "enterprise") return "high-scale governance + operational controls";
|
|
2347
|
-
return "free
|
|
2521
|
+
return "free escalation-first entry tier";
|
|
2348
2522
|
}
|
|
2349
2523
|
async function buildRuntimeOverviewPayload(ctx) {
|
|
2350
2524
|
const client = ctx.client;
|
|
@@ -2407,8 +2581,10 @@ async function buildRuntimeOverviewPayload(ctx) {
|
|
|
2407
2581
|
const transportHealth = readTransportHealthState(ctx.storePath);
|
|
2408
2582
|
const sinceLastSeen = readSinceLastSeenState(ctx.storePath, "host_panel", "global");
|
|
2409
2583
|
const humanDelivery = readHumanDeliveryState(ctx.storePath, 20);
|
|
2584
|
+
const openclawAdapter = readOpenClawAdapterState(ctx.storePath, 20);
|
|
2410
2585
|
const decisionStore = new LocalStore(ctx.storePath);
|
|
2411
2586
|
let decisionViews = [];
|
|
2587
|
+
let actionInbox = null;
|
|
2412
2588
|
let humanDeliveryDetails = null;
|
|
2413
2589
|
try {
|
|
2414
2590
|
decisionViews = listPendingDecisionViews(decisionStore, 100);
|
|
@@ -2423,6 +2599,21 @@ async function buildRuntimeOverviewPayload(ctx) {
|
|
|
2423
2599
|
} finally {
|
|
2424
2600
|
decisionStore.close();
|
|
2425
2601
|
}
|
|
2602
|
+
const agentState = buildOpenClawAgentStatePayload({
|
|
2603
|
+
runtimeStatus: ingressRuntime,
|
|
2604
|
+
sessions,
|
|
2605
|
+
pendingDecisions: decisionViews,
|
|
2606
|
+
humanDelivery,
|
|
2607
|
+
transportHealth
|
|
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
|
+
});
|
|
2426
2617
|
return {
|
|
2427
2618
|
did: ctx.myDid,
|
|
2428
2619
|
serverUrl: ctx.serverUrl,
|
|
@@ -2461,8 +2652,11 @@ async function buildRuntimeOverviewPayload(ctx) {
|
|
|
2461
2652
|
collaborationSummary,
|
|
2462
2653
|
projectionOutbox,
|
|
2463
2654
|
transportHealth,
|
|
2655
|
+
agentState,
|
|
2656
|
+
actionInbox,
|
|
2464
2657
|
sinceLastSeen,
|
|
2465
2658
|
humanDelivery,
|
|
2659
|
+
openclawAdapter,
|
|
2466
2660
|
humanDeliveryDetails,
|
|
2467
2661
|
pendingDecisions: decisionViews,
|
|
2468
2662
|
recentCollaborationEvents: collaborationEventManager.listRecent(20),
|
|
@@ -2538,6 +2732,8 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
|
|
|
2538
2732
|
let projectionPreview = null;
|
|
2539
2733
|
let pendingDecisionViews = [];
|
|
2540
2734
|
let humanDelivery = null;
|
|
2735
|
+
let actionInbox = null;
|
|
2736
|
+
let openclawAdapter = null;
|
|
2541
2737
|
try {
|
|
2542
2738
|
projectionOutbox = new CollaborationProjectionOutboxManager(outboxStore).listBySession(session.session_key, 20);
|
|
2543
2739
|
notificationIntents = new NotificationIntentManager(outboxStore).listBySession(session.session_key, 20);
|
|
@@ -2556,9 +2752,25 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
|
|
|
2556
2752
|
);
|
|
2557
2753
|
pendingDecisionViews = listPendingDecisionViews(outboxStore, 100).filter((event) => event.session_key === session.session_key).slice(0, 20);
|
|
2558
2754
|
humanDelivery = summarizeHumanDelivery(outboxStore, 20);
|
|
2755
|
+
openclawAdapter = new OpenClawExecutionAdapter(outboxStore).buildOverview(50);
|
|
2559
2756
|
} finally {
|
|
2560
2757
|
outboxStore.close();
|
|
2561
2758
|
}
|
|
2759
|
+
const agentState = buildOpenClawAgentStatePayload({
|
|
2760
|
+
runtimeStatus: readIngressRuntimeStatus(),
|
|
2761
|
+
sessions: sessionManager.listRecentSessions(50),
|
|
2762
|
+
pendingDecisions: pendingDecisionViews,
|
|
2763
|
+
humanDelivery,
|
|
2764
|
+
transportHealth: readTransportHealthState(ctx.storePath)
|
|
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
|
+
});
|
|
2562
2774
|
return {
|
|
2563
2775
|
session,
|
|
2564
2776
|
sessionSummary: sessionSummaryManager.get(session.session_key),
|
|
@@ -2567,6 +2779,7 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
|
|
|
2567
2779
|
collaborationSummary: collaborationEventManager.summarize(200),
|
|
2568
2780
|
ingressRuntime: readIngressRuntimeStatus(),
|
|
2569
2781
|
transportHealth: readTransportHealthState(ctx.storePath),
|
|
2782
|
+
agentState,
|
|
2570
2783
|
binding,
|
|
2571
2784
|
bindingAlert,
|
|
2572
2785
|
activeWorkSession,
|
|
@@ -2581,6 +2794,15 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
|
|
|
2581
2794
|
deliveryTimeline,
|
|
2582
2795
|
projectionPreview,
|
|
2583
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,
|
|
2584
2806
|
policyExplain: buildPolicyDecisionShape(ctx.identityPath, session.remote_did, { runtimeMode: getRuntimeMode() }),
|
|
2585
2807
|
tasks: tasks.map((task) => ({
|
|
2586
2808
|
...task,
|
|
@@ -2829,14 +3051,32 @@ async function handleApi(pathname, req, ctx) {
|
|
|
2829
3051
|
if (req.method === "GET") {
|
|
2830
3052
|
const pendingDecisions = listPendingDecisionViews(store, 100);
|
|
2831
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
|
+
});
|
|
2832
3070
|
return {
|
|
2833
3071
|
pendingDecisions,
|
|
3072
|
+
actionInbox,
|
|
2834
3073
|
projectionOutboxPending: outboxManager.listByStatus("pending", 50),
|
|
2835
3074
|
projectionOutboxFailed: outboxManager.listByStatus("failed", 50),
|
|
2836
3075
|
notificationIntentsPending: intentManager.listByStatus("pending", 50).concat(intentManager.listByStatus("bound", 50)),
|
|
2837
3076
|
notificationIntentsUnresolved: intentManager.listByStatus("unresolved", 50),
|
|
2838
3077
|
notificationIntentsFailed: intentManager.listByStatus("failed", 50),
|
|
2839
|
-
humanDelivery
|
|
3078
|
+
humanDelivery,
|
|
3079
|
+
openclawAdapter
|
|
2840
3080
|
};
|
|
2841
3081
|
}
|
|
2842
3082
|
if (parts[2] === "resolve" && req.method === "POST") {
|
|
@@ -3350,6 +3590,96 @@ async function handleApi(pathname, req, ctx) {
|
|
|
3350
3590
|
}
|
|
3351
3591
|
}
|
|
3352
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
|
+
}
|
|
3353
3683
|
if (parts[0] === "public") {
|
|
3354
3684
|
if ((!parts[1] || parts[1] === "self") && req.method === "GET") {
|
|
3355
3685
|
await maybeEnsureHostedPublicLink(ctx);
|