@pingagent/sdk 0.1.15 → 0.1.17
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 +113 -91
- package/dist/chunk-GU5W4KRD.js +5847 -0
- package/dist/chunk-HSVC3KDT.js +5611 -0
- package/dist/chunk-IB7OSFZS.js +5951 -0
- package/dist/index.d.ts +261 -1
- package/dist/index.js +37 -1
- package/dist/web-server.js +314 -44
- package/package.json +3 -3
package/dist/web-server.js
CHANGED
|
@@ -2,7 +2,9 @@ import {
|
|
|
2
2
|
CollaborationEventManager,
|
|
3
3
|
CollaborationProjectionOutboxManager,
|
|
4
4
|
ContactManager,
|
|
5
|
+
HumanDeliveryBindingManager,
|
|
5
6
|
LocalStore,
|
|
7
|
+
NotificationIntentManager,
|
|
6
8
|
OperatorSeenStateManager,
|
|
7
9
|
PingAgentClient,
|
|
8
10
|
TrustPolicyAuditManager,
|
|
@@ -12,6 +14,7 @@ import {
|
|
|
12
14
|
decideContactPolicy,
|
|
13
15
|
decideTaskPolicy,
|
|
14
16
|
defaultTrustPolicyDoc,
|
|
17
|
+
deriveOpenClawAgentState,
|
|
15
18
|
deriveTransportHealth,
|
|
16
19
|
ensureTokenValid,
|
|
17
20
|
getActiveSessionFilePath,
|
|
@@ -19,6 +22,7 @@ import {
|
|
|
19
22
|
getSessionMapFilePath,
|
|
20
23
|
getTrustRecommendationActionLabel,
|
|
21
24
|
listPendingDecisionViews,
|
|
25
|
+
listRecentBindingsForSession,
|
|
22
26
|
loadIdentity,
|
|
23
27
|
normalizeTrustPolicyDoc,
|
|
24
28
|
readCurrentActiveSessionKey,
|
|
@@ -28,12 +32,13 @@ import {
|
|
|
28
32
|
readTransportPreference,
|
|
29
33
|
removeSessionBinding,
|
|
30
34
|
setSessionBinding,
|
|
35
|
+
summarizeHumanDelivery,
|
|
31
36
|
summarizeSinceLastSeen,
|
|
32
37
|
summarizeTrustPolicyAudit,
|
|
33
38
|
switchTransportPreference,
|
|
34
39
|
updateStoredToken,
|
|
35
40
|
upsertTrustPolicyRecommendation
|
|
36
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-IB7OSFZS.js";
|
|
37
42
|
|
|
38
43
|
// src/web-server.ts
|
|
39
44
|
import * as fs from "fs";
|
|
@@ -259,8 +264,8 @@ function getHostPanelHtml() {
|
|
|
259
264
|
<div class="audit-list" id="decisionInboxList"></div>
|
|
260
265
|
</div>
|
|
261
266
|
<div class="card">
|
|
262
|
-
<h2>
|
|
263
|
-
<div id="projectionOutboxSummary" class="muted small" style="margin-bottom:12px">Loading
|
|
267
|
+
<h2>Human Delivery</h2>
|
|
268
|
+
<div id="projectionOutboxSummary" class="muted small" style="margin-bottom:12px">Loading human-delivery state\u2026</div>
|
|
264
269
|
<div class="audit-list" id="projectionOutboxList"></div>
|
|
265
270
|
</div>
|
|
266
271
|
</div>
|
|
@@ -277,7 +282,7 @@ function getHostPanelHtml() {
|
|
|
277
282
|
<option value="balanced">balanced</option>
|
|
278
283
|
<option value="strict">strict</option>
|
|
279
284
|
</select>
|
|
280
|
-
<div class="muted small">Balanced is the default: key conclusions, handoffs, repairs, and required decisions are
|
|
285
|
+
<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>
|
|
281
286
|
<div class="row-actions">
|
|
282
287
|
<button class="action-btn" id="saveProjectionPresetBtn">Save projection preset</button>
|
|
283
288
|
</div>
|
|
@@ -540,6 +545,96 @@ function getHostPanelHtml() {
|
|
|
540
545
|
}).join('');
|
|
541
546
|
}
|
|
542
547
|
|
|
548
|
+
function renderHumanDeliveryIntents(intents, emptyLabel) {
|
|
549
|
+
if (!Array.isArray(intents) || !intents.length) {
|
|
550
|
+
return '<div class="empty">' + esc(emptyLabel || 'No notification intents yet.') + '</div>';
|
|
551
|
+
}
|
|
552
|
+
return intents.map(function (intent) {
|
|
553
|
+
const bindingLabel = intent.resolved_binding_id ? ('binding=' + intent.resolved_binding_id) : 'binding=(unresolved)';
|
|
554
|
+
const ackLabel = intent.acknowledged_at
|
|
555
|
+
? (' \xB7 ack=' + fmtTs(intent.acknowledged_at))
|
|
556
|
+
: '';
|
|
557
|
+
const actions = [];
|
|
558
|
+
if (intent.status === 'failed' || intent.status === 'unresolved') {
|
|
559
|
+
actions.push('<button class="secondary-btn human-delivery-action-btn" data-action="retry_intent" data-intent-id="' + esc(intent.id) + '">Retry</button>');
|
|
560
|
+
actions.push('<button class="secondary-btn human-delivery-action-btn" data-action="re_resolve_intent_binding" data-intent-id="' + esc(intent.id) + '">Re-resolve</button>');
|
|
561
|
+
} else if (intent.status === 'pending' || intent.status === 'bound' || intent.status === 'sent') {
|
|
562
|
+
actions.push('<button class="secondary-btn human-delivery-action-btn" data-action="re_resolve_intent_binding" data-intent-id="' + esc(intent.id) + '">Re-resolve</button>');
|
|
563
|
+
}
|
|
564
|
+
if (intent.status !== 'canceled' && intent.status !== 'acknowledged') {
|
|
565
|
+
actions.push('<button class="danger-btn human-delivery-action-btn" data-action="cancel_intent" data-intent-id="' + esc(intent.id) + '">Cancel</button>');
|
|
566
|
+
}
|
|
567
|
+
return '<div class="audit-row"><div class="top"><strong>' + esc(intent.title || intent.summary || '(no title)') + '</strong>' +
|
|
568
|
+
'<span class="badge">' + esc(intent.status || 'pending') + '</span></div>' +
|
|
569
|
+
'<div class="muted small">' + esc(fmtTs(intent.created_at || intent.updated_at)) + ' \xB7 type=' + esc(intent.intent_type || 'collaboration_update') + ' \xB7 ' + esc(bindingLabel) + esc(ackLabel) + '</div>' +
|
|
570
|
+
'<div style="margin-top:8px">' + esc(intent.summary || '(no summary)') + '</div>' +
|
|
571
|
+
(intent.last_error ? '<div class="muted small" style="margin-top:8px;color:#fecaca">' + esc(intent.last_error) + '</div>' : '') +
|
|
572
|
+
(actions.length ? '<div class="row-actions" style="margin-top:10px">' + actions.join('') + '</div>' : '') +
|
|
573
|
+
'</div>';
|
|
574
|
+
}).join('');
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function renderRecentBindings(bindings) {
|
|
578
|
+
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>';
|
|
580
|
+
}
|
|
581
|
+
return bindings.map(function (binding) {
|
|
582
|
+
const actions = binding.status === 'active'
|
|
583
|
+
? '<div class="row-actions" style="margin-top:10px"><button class="secondary-btn human-delivery-action-btn" data-action="mark_binding_stale" data-binding-id="' + esc(binding.id) + '">Mark stale</button></div>'
|
|
584
|
+
: '';
|
|
585
|
+
return '<div class="audit-row"><div class="top"><strong>' + esc(binding.channel + ' -> ' + binding.to) + '</strong>' +
|
|
586
|
+
'<span class="badge">' + esc(binding.status || 'active') + '</span></div>' +
|
|
587
|
+
'<div class="muted small">' + esc(fmtTs(binding.last_inbound_at || binding.updated_at)) + ' \xB7 owner=' + esc(binding.owner_ref || '(none)') + '</div>' +
|
|
588
|
+
'<div class="muted small" style="margin-top:8px">session=' + esc(binding.source_session_key || '(none)') + ' \xB7 conversation=' + esc(binding.source_conversation_id || '(none)') + '</div>' +
|
|
589
|
+
actions +
|
|
590
|
+
'</div>';
|
|
591
|
+
}).join('');
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function renderHumanDeliveryCapabilities(humanDelivery) {
|
|
595
|
+
const capabilities = Array.isArray(humanDelivery && humanDelivery.channel_capabilities) ? humanDelivery.channel_capabilities : [];
|
|
596
|
+
if (!capabilities.length) {
|
|
597
|
+
return '<div class="empty">No channel capability data yet.</div>';
|
|
598
|
+
}
|
|
599
|
+
return capabilities.map(function (cap) {
|
|
600
|
+
const supportLabel = cap.supports_explicit_send ? 'explicit-send' : 'unsupported';
|
|
601
|
+
const canary = cap.last_canary_at
|
|
602
|
+
? (' \xB7 canary=' + (cap.last_canary_ok ? 'ok' : 'failed') + ' @ ' + fmtTs(cap.last_canary_at))
|
|
603
|
+
: ' \xB7 canary=pending';
|
|
604
|
+
return '<div class="audit-row"><div class="top"><strong>' + esc(cap.channel) + '</strong><span class="badge">' + esc(supportLabel) + '</span></div>' +
|
|
605
|
+
'<div class="muted small">configured=' + esc(cap.configured ? 'true' : 'false') + ' \xB7 thread=' + esc(cap.supports_thread_id ? 'yes' : 'no') + ' \xB7 account=' + esc(cap.supports_account_id ? 'yes' : 'no') + ' \xB7 dry_run=' + esc(cap.supports_dry_run ? 'yes' : 'no') + esc(canary) + '</div>' +
|
|
606
|
+
(cap.last_canary_error ? '<div class="muted small" style="margin-top:8px;color:#fecaca">' + esc(cap.last_canary_error) + '</div>' : '') +
|
|
607
|
+
'</div>';
|
|
608
|
+
}).join('');
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
async function runHumanDeliveryAction(action, payload) {
|
|
612
|
+
return api('/api/runtime/human-delivery/action', {
|
|
613
|
+
method: 'POST',
|
|
614
|
+
headers: { 'Content-Type': 'application/json' },
|
|
615
|
+
body: JSON.stringify(Object.assign({ action: action }, payload || {})),
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function wireHumanDeliveryActionButtons(root) {
|
|
620
|
+
if (!root || !root.querySelectorAll) return;
|
|
621
|
+
root.querySelectorAll('.human-delivery-action-btn').forEach(function (btn) {
|
|
622
|
+
btn.addEventListener('click', async function () {
|
|
623
|
+
const action = btn.getAttribute('data-action');
|
|
624
|
+
if (!action) return;
|
|
625
|
+
const intentId = btn.getAttribute('data-intent-id');
|
|
626
|
+
const bindingId = btn.getAttribute('data-binding-id');
|
|
627
|
+
await runHumanDeliveryAction(action, {
|
|
628
|
+
intent_id: intentId ? Number(intentId) : undefined,
|
|
629
|
+
binding_id: bindingId ? Number(bindingId) : undefined,
|
|
630
|
+
});
|
|
631
|
+
await refreshAll();
|
|
632
|
+
if (state.selectedSessionKey) await loadSession(state.selectedSessionKey);
|
|
633
|
+
setTab(state.tab || 'runtime');
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
|
|
543
638
|
async function markSeen(scopeType, scopeKey) {
|
|
544
639
|
return api('/api/runtime/seen', {
|
|
545
640
|
method: 'POST',
|
|
@@ -772,7 +867,7 @@ function getHostPanelHtml() {
|
|
|
772
867
|
event.approval_required
|
|
773
868
|
? '<span class="badge">' + esc(event.approval_status === 'pending' ? 'review' : event.approval_status) + '</span>'
|
|
774
869
|
: '',
|
|
775
|
-
event.target_human_session ? '<span class="badge">human
|
|
870
|
+
event.target_human_session ? '<span class="badge">human delivery</span>' : '',
|
|
776
871
|
].filter(Boolean).join('');
|
|
777
872
|
const actions = event.approval_required && event.approval_status === 'pending'
|
|
778
873
|
? '<div class="row-actions" style="margin-top:10px">' +
|
|
@@ -804,8 +899,14 @@ function getHostPanelHtml() {
|
|
|
804
899
|
const overdueDecisions = pendingDecisions.filter(function (event) { return !!event.overdue; });
|
|
805
900
|
const pendingOutbox = data && Array.isArray(data.projectionOutboxPending) ? data.projectionOutboxPending : [];
|
|
806
901
|
const failedOutbox = data && Array.isArray(data.projectionOutboxFailed) ? data.projectionOutboxFailed : [];
|
|
807
|
-
|
|
808
|
-
|
|
902
|
+
const pendingIntents = data && Array.isArray(data.notificationIntentsPending) ? data.notificationIntentsPending : [];
|
|
903
|
+
const unresolvedIntents = data && Array.isArray(data.notificationIntentsUnresolved) ? data.notificationIntentsUnresolved : [];
|
|
904
|
+
const failedIntents = data && Array.isArray(data.notificationIntentsFailed) ? data.notificationIntentsFailed : [];
|
|
905
|
+
const humanDelivery = data && data.humanDelivery ? data.humanDelivery : null;
|
|
906
|
+
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);
|
|
809
910
|
document.getElementById('decisionInboxList').innerHTML = pendingDecisions.length
|
|
810
911
|
? pendingDecisions.map(function (event) {
|
|
811
912
|
return '<div class="audit-row"><div class="top"><strong>' + esc(event.summary || '(no summary)') + '</strong>' +
|
|
@@ -824,20 +925,26 @@ function getHostPanelHtml() {
|
|
|
824
925
|
: '<div class="empty">No pending collaboration decisions.</div>';
|
|
825
926
|
|
|
826
927
|
const combinedOutbox = pendingOutbox.concat(failedOutbox);
|
|
827
|
-
document.getElementById('projectionOutboxSummary').textContent =
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
928
|
+
document.getElementById('projectionOutboxSummary').textContent = boundChannelReply
|
|
929
|
+
? ('bound reply mode \xB7 pending=' + pendingIntents.length + ' \xB7 unresolved=' + unresolvedIntents.length + ' \xB7 failed=' + failedIntents.length)
|
|
930
|
+
: (combinedOutbox.length
|
|
931
|
+
? 'Legacy projection outbox remains visible for audit and compatibility. Failed rows stay visible until delivery recovers.'
|
|
932
|
+
: 'No legacy projection-outbox rows need attention.');
|
|
933
|
+
document.getElementById('projectionOutboxList').innerHTML = boundChannelReply
|
|
934
|
+
? renderHumanDeliveryIntents(
|
|
935
|
+
pendingIntents.concat(unresolvedIntents).concat(failedIntents),
|
|
936
|
+
'No pending, unresolved, or failed human-delivery intents.',
|
|
937
|
+
)
|
|
938
|
+
: (combinedOutbox.length
|
|
939
|
+
? combinedOutbox.map(function (entry) {
|
|
940
|
+
return '<div class="audit-row"><div class="top"><strong>' + esc(entry.summary || '(no summary)') + '</strong>' +
|
|
941
|
+
'<span class="badge">' + esc(entry.status || 'pending') + '</span></div>' +
|
|
942
|
+
'<div class="muted small">' + esc(fmtTs(entry.created_at)) + ' \xB7 kind=' + esc(entry.projection_kind || 'collaboration_update') + ' \xB7 attempts=' + esc(entry.attempt_count || 0) + '</div>' +
|
|
943
|
+
'<div class="muted small" style="margin-top:8px">target=' + esc(entry.target_human_session || '(missing)') + ' \xB7 session=' + esc(entry.session_key || '(none)') + '</div>' +
|
|
944
|
+
(entry.last_error ? '<div style="margin-top:8px">' + esc(entry.last_error) + '</div>' : '') +
|
|
945
|
+
'</div>';
|
|
946
|
+
}).join('')
|
|
947
|
+
: '<div class="empty">No pending or failed legacy projection deliveries.</div>');
|
|
841
948
|
|
|
842
949
|
document.querySelectorAll('.inbox-decision-btn').forEach(function (btn) {
|
|
843
950
|
btn.addEventListener('click', async function () {
|
|
@@ -864,6 +971,7 @@ function getHostPanelHtml() {
|
|
|
864
971
|
setTab('runtime');
|
|
865
972
|
});
|
|
866
973
|
});
|
|
974
|
+
wireHumanDeliveryActionButtons(document.getElementById('projectionOutboxList'));
|
|
867
975
|
}
|
|
868
976
|
|
|
869
977
|
function getOverviewSessions() {
|
|
@@ -901,22 +1009,41 @@ function getHostPanelHtml() {
|
|
|
901
1009
|
if (!overview) return;
|
|
902
1010
|
syncSelectedSessionFromOverview();
|
|
903
1011
|
const ingressState = ingressStatusModel(overview);
|
|
1012
|
+
const agentState = overview.agentState || null;
|
|
904
1013
|
const transportHealth = overview.transportHealth || null;
|
|
905
1014
|
const sinceLastSeen = overview.sinceLastSeen || null;
|
|
1015
|
+
const humanDelivery = overview.humanDelivery || null;
|
|
906
1016
|
const overdueDecisions = Array.isArray(overview.pendingDecisions)
|
|
907
1017
|
? overview.pendingDecisions.filter(function (event) { return !!event.overdue; })
|
|
908
1018
|
: [];
|
|
1019
|
+
const statusClass = agentState && (agentState.blocked || agentState.degraded)
|
|
1020
|
+
? 'degraded'
|
|
1021
|
+
: ingressState.className;
|
|
1022
|
+
const statusLabel = agentState && agentState.state
|
|
1023
|
+
? agentState.state
|
|
1024
|
+
: ingressState.label;
|
|
1025
|
+
const statusDetail = agentState && agentState.summary
|
|
1026
|
+
? agentState.summary
|
|
1027
|
+
: ingressState.detail;
|
|
909
1028
|
document.getElementById('activationCard').innerHTML =
|
|
910
1029
|
'<div class="status-strip">' +
|
|
911
1030
|
'<div class="status-main">' +
|
|
912
|
-
'<h2>
|
|
913
|
-
'<div class="status-state ' +
|
|
914
|
-
'<div class="muted small">' + esc(
|
|
1031
|
+
'<h2>Agent Runtime</h2>' +
|
|
1032
|
+
'<div class="status-state ' + statusClass + '">' + esc(statusLabel) + '</div>' +
|
|
1033
|
+
'<div class="muted small">' + esc(statusDetail) + '</div>' +
|
|
1034
|
+
(agentState && agentState.next_action
|
|
1035
|
+
? '<div class="muted small">next_action=' + esc(agentState.next_action) + '</div>'
|
|
1036
|
+
: '') +
|
|
1037
|
+
(agentState && agentState.reason
|
|
1038
|
+
? '<div class="muted small">reason=' + esc(agentState.reason) + '</div>'
|
|
1039
|
+
: '') +
|
|
915
1040
|
'<div class="muted small">Public link: ' + esc(overview.publicSelf && overview.publicSelf.public_url ? overview.publicSelf.public_url : '(not ready yet)') + '</div>' +
|
|
916
1041
|
'<div class="summary-pills">' +
|
|
917
1042
|
'<span class="pill">pending_decisions=' + esc((overview.pendingDecisions || []).length) + '</span>' +
|
|
918
1043
|
'<span class="pill">overdue=' + esc(overdueDecisions.length) + '</span>' +
|
|
919
|
-
'<span class="pill">
|
|
1044
|
+
'<span class="pill">projection_policy=' + esc(overview.collaborationProjection && overview.collaborationProjection.preset ? overview.collaborationProjection.preset : 'balanced') + '</span>' +
|
|
1045
|
+
(humanDelivery ? '<span class="pill">human_delivery=' + esc(humanDelivery.mode || 'projection_outbox') + '</span>' : '') +
|
|
1046
|
+
(agentState && agentState.active_session_key ? '<span class="pill">active_session=ready</span>' : '') +
|
|
920
1047
|
'</div>' +
|
|
921
1048
|
'</div>' +
|
|
922
1049
|
'<div style="min-width:320px">' +
|
|
@@ -945,18 +1072,28 @@ function getHostPanelHtml() {
|
|
|
945
1072
|
{ label: 'Tasks', value: overview.tasksTotal, sub: 'recent local task threads' },
|
|
946
1073
|
{ label: 'Audit', value: overview.auditSummary.total_events, sub: 'policy / runtime audit events' },
|
|
947
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' },
|
|
948
1076
|
{ label: 'Recommendations', value: overview.recommendationSummary ? overview.recommendationSummary.total : overview.recommendations.length, sub: overview.recommendationSummary ? JSON.stringify(overview.recommendationSummary.by_status || {}) : 'learned policy suggestions' },
|
|
949
1077
|
{ 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' },
|
|
950
1078
|
];
|
|
951
1079
|
document.getElementById('statsGrid').innerHTML = stats.map(function (item) {
|
|
952
1080
|
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>';
|
|
953
1081
|
}).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>' +
|
|
1084
|
+
'<div class="audit-list" style="margin-top:8px">' + renderHumanDeliveryCapabilities(humanDelivery) + '</div></div>';
|
|
954
1085
|
|
|
955
1086
|
const toggleUnreadBtn = document.getElementById('toggleUnreadBtn');
|
|
956
1087
|
if (toggleUnreadBtn) toggleUnreadBtn.textContent = 'Unread only: ' + (state.showUnreadOnly ? 'on' : 'off');
|
|
957
1088
|
const sessions = getVisibleSessions();
|
|
958
1089
|
if (!sessions.length) {
|
|
959
|
-
document.getElementById('sessionList').innerHTML = '<div class="empty">' + (
|
|
1090
|
+
document.getElementById('sessionList').innerHTML = '<div class="empty">' + (
|
|
1091
|
+
state.showUnreadOnly
|
|
1092
|
+
? 'No unread sessions.'
|
|
1093
|
+
: (agentState && agentState.summary
|
|
1094
|
+
? agentState.summary + ' Wait for inbound work or review pending decisions.'
|
|
1095
|
+
: 'No sessions yet.')
|
|
1096
|
+
) + '</div>';
|
|
960
1097
|
} else {
|
|
961
1098
|
document.getElementById('sessionList').innerHTML = sessions.map(function (session) {
|
|
962
1099
|
const active = session.session_key === state.selectedSessionKey ? ' active' : '';
|
|
@@ -966,7 +1103,7 @@ function getHostPanelHtml() {
|
|
|
966
1103
|
? '<span class="badge alert">new ' + esc(countSinceLastSeen(session.since_last_seen)) + '</span>'
|
|
967
1104
|
: ''),
|
|
968
1105
|
session.binding_alert
|
|
969
|
-
? '<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="
|
|
1106
|
+
? '<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>'
|
|
970
1107
|
: '',
|
|
971
1108
|
].filter(Boolean).join('');
|
|
972
1109
|
return '<div class="session-row' + active + '" data-session="' + esc(session.session_key) + '">' +
|
|
@@ -974,7 +1111,7 @@ function getHostPanelHtml() {
|
|
|
974
1111
|
'<div class="muted small">unread=' + esc(session.unread_count) + ' \xB7 last=' + esc(fmtTs(session.last_remote_activity_at || session.updated_at)) + '</div>' +
|
|
975
1112
|
(state.detailMode === 'advanced'
|
|
976
1113
|
? '<div class="muted small" style="margin-top:6px">conversation=' + esc(session.conversation_id || '(none)') + '</div>' +
|
|
977
|
-
'<div class="muted small">
|
|
1114
|
+
'<div class="muted small">compatibility_route=' + esc(session.mapped_work_session || '(unbound)') + (session.is_active_work_session ? ' \xB7 active_route=true' : '') + '</div>'
|
|
978
1115
|
: '') +
|
|
979
1116
|
'<div class="muted small" style="margin-top:6px">' + esc(session.last_message_preview || '(no preview)') + '</div>' +
|
|
980
1117
|
'</div>';
|
|
@@ -1022,7 +1159,7 @@ function getHostPanelHtml() {
|
|
|
1022
1159
|
});
|
|
1023
1160
|
|
|
1024
1161
|
const tasks = Array.isArray(overview.tasks) ? overview.tasks : [];
|
|
1025
|
-
document.getElementById('taskList').innerHTML
|
|
1162
|
+
document.getElementById('taskList').innerHTML += tasks.length
|
|
1026
1163
|
? tasks.map(function (task) {
|
|
1027
1164
|
return '<div class="task-row"><div class="top"><strong>' + esc(task.title || task.task_id) + '</strong><span class="badge">' + esc(task.status) + '</span></div>' +
|
|
1028
1165
|
'<div class="muted small">task_id=' + esc(task.task_id) + ' \xB7 session=' + esc(task.session_key) + '</div>' +
|
|
@@ -1031,7 +1168,7 @@ function getHostPanelHtml() {
|
|
|
1031
1168
|
(task.error_message ? '<div style="margin-top:8px;color:#fca5a5">' + esc(task.error_message) + '</div>' : '') +
|
|
1032
1169
|
renderHandoffBlock(task.handoff) +
|
|
1033
1170
|
'</div>';
|
|
1034
|
-
|
|
1171
|
+
}).join('')
|
|
1035
1172
|
: '<div class="empty">No recent task threads.</div>';
|
|
1036
1173
|
|
|
1037
1174
|
if (subscription) {
|
|
@@ -1125,10 +1262,13 @@ conversation=' + result.conversation_id));
|
|
|
1125
1262
|
? detail.collaborationProjection.preset
|
|
1126
1263
|
: 'balanced';
|
|
1127
1264
|
const projectionOutbox = Array.isArray(detail.projectionOutbox) ? detail.projectionOutbox : [];
|
|
1265
|
+
const recentBindings = Array.isArray(detail.recentBindings) ? detail.recentBindings : [];
|
|
1266
|
+
const recentNotificationIntents = Array.isArray(detail.recentNotificationIntents) ? detail.recentNotificationIntents : [];
|
|
1128
1267
|
const sinceLastSeen = detail.sinceLastSeen || null;
|
|
1129
1268
|
const deliveryTimeline = Array.isArray(detail.deliveryTimeline) ? detail.deliveryTimeline : [];
|
|
1130
1269
|
const projectionPreview = detail.projectionPreview || null;
|
|
1131
1270
|
const transportHealth = detail.transportHealth || null;
|
|
1271
|
+
const humanDelivery = detail.humanDelivery || null;
|
|
1132
1272
|
const isAdvanced = state.detailMode === 'advanced';
|
|
1133
1273
|
const sessionLink = buildSessionLink(session.session_key);
|
|
1134
1274
|
const summaryPills = [];
|
|
@@ -1136,8 +1276,8 @@ conversation=' + result.conversation_id));
|
|
|
1136
1276
|
if (task && task.action) summaryPills.push('<span class="pill">task=' + esc(task.action) + '</span>');
|
|
1137
1277
|
if (session.trust_state) summaryPills.push('<span class="pill">trust=' + esc(session.trust_state) + '</span>');
|
|
1138
1278
|
if (Number(session.unread_count || 0) > 0) summaryPills.push('<span class="pill">unread=' + esc(session.unread_count) + '</span>');
|
|
1139
|
-
if (binding && binding.
|
|
1140
|
-
|
|
1279
|
+
if (binding && binding.target_key) summaryPills.push('<span class="pill">human reply target ready</span>');
|
|
1280
|
+
if (isAdvanced && activeWorkSession) summaryPills.push('<span class="pill">compatibility route available</span>');
|
|
1141
1281
|
const policyBlock = isAdvanced
|
|
1142
1282
|
? '<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>'
|
|
1143
1283
|
: '<div class="summary-pills" style="margin-top:8px">' + summaryPills.join('') + '</div>' +
|
|
@@ -1155,8 +1295,8 @@ conversation=' + result.conversation_id));
|
|
|
1155
1295
|
? '<div style="margin-top:8px">' +
|
|
1156
1296
|
'<div class="muted small">session=' + esc(session.session_key) + '</div>' +
|
|
1157
1297
|
'<div class="muted small">conversation=' + esc(session.conversation_id || '(none)') + '</div>' +
|
|
1158
|
-
'<div class="muted small">
|
|
1159
|
-
'<div class="muted small">
|
|
1298
|
+
'<div class="muted small">compatibility_active_session=' + esc(activeWorkSession || '(none)') + '</div>' +
|
|
1299
|
+
'<div class="muted small">reply_binding=' + esc(binding ? (binding.target_key || binding.session_key || '(bound)') : '(unbound)') + '</div>' +
|
|
1160
1300
|
'</div>'
|
|
1161
1301
|
: '') +
|
|
1162
1302
|
(openRecommendation
|
|
@@ -1164,11 +1304,11 @@ conversation=' + result.conversation_id));
|
|
|
1164
1304
|
: (reopenRecommendation ? '<div class="muted small" style="margin-top:8px">trust_action=' + esc(recommendationActionLabel(reopenRecommendation)) + '</div>' : '')) +
|
|
1165
1305
|
(summaryPills.length ? '<div class="summary-pills">' + summaryPills.join('') + '</div>' : '') +
|
|
1166
1306
|
(pendingCollaborationEvents.length
|
|
1167
|
-
? '<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
|
|
1307
|
+
? '<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.' +
|
|
1168
1308
|
(pendingCollaborationEvents[0].overdue ? ' This item is overdue.' : '') + '</div></div>'
|
|
1169
1309
|
: '') +
|
|
1170
1310
|
(bindingAlert
|
|
1171
|
-
? '<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>
|
|
1311
|
+
? '<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>'
|
|
1172
1312
|
: '') +
|
|
1173
1313
|
'<div class="row-actions">' +
|
|
1174
1314
|
(session.trust_state === 'pending'
|
|
@@ -1183,11 +1323,16 @@ conversation=' + result.conversation_id));
|
|
|
1183
1323
|
(!openRecommendation && reopenRecommendation
|
|
1184
1324
|
? '<button class="secondary-btn reopen-session-recommendation-btn" data-session="' + esc(session.session_key) + '">Reopen</button>'
|
|
1185
1325
|
: '') +
|
|
1186
|
-
'<button class="action-btn bind-current-btn" data-conversation="' + esc(session.conversation_id || '') + '">Attach to Current Chat</button>' +
|
|
1187
1326
|
'<button class="secondary-btn mark-read-btn" data-session="' + esc(session.session_key) + '">Mark read</button>' +
|
|
1188
1327
|
'<button class="secondary-btn copy-session-link-btn" data-session="' + esc(session.session_key) + '">Copy Session Link</button>' +
|
|
1189
|
-
|
|
1328
|
+
(isAdvanced
|
|
1329
|
+
? '<button class="action-btn bind-current-btn" data-conversation="' + esc(session.conversation_id || '') + '">Repair Compatibility Route</button>' +
|
|
1330
|
+
'<button class="danger-btn clear-binding-btn" data-conversation="' + esc(session.conversation_id || '') + '">Clear Compatibility Route</button>'
|
|
1331
|
+
: '') +
|
|
1190
1332
|
'</div>' +
|
|
1333
|
+
(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>'
|
|
1335
|
+
: '') +
|
|
1191
1336
|
'<div class="form-grid" style="margin-top:16px">' +
|
|
1192
1337
|
'<label class="label">Reply in this session</label>' +
|
|
1193
1338
|
'<textarea id="sessionReplyInput" placeholder="Send a text reply in this session"></textarea>' +
|
|
@@ -1275,9 +1420,9 @@ conversation=' + result.conversation_id));
|
|
|
1275
1420
|
'</div>';
|
|
1276
1421
|
}).join('') : '<div class="empty">No audit events for this session.</div>') +
|
|
1277
1422
|
'</div></div>' +
|
|
1278
|
-
'<div><div class="label">Human
|
|
1423
|
+
'<div><div class="label">Human Delivery Posture</div><div class="audit-list" style="margin-top:8px">' +
|
|
1279
1424
|
'<div class="audit-row"><div class="top"><strong>Projection policy</strong><span class="badge">' + esc(projectionPreset) + '</span></div>' +
|
|
1280
|
-
'<div class="muted small">The collaboration session keeps the raw transcript.
|
|
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>' +
|
|
1281
1426
|
'<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>' +
|
|
1282
1427
|
(sinceLastSeen
|
|
1283
1428
|
? '<div class="summary-pills" style="margin-top:8px">' +
|
|
@@ -1289,6 +1434,9 @@ conversation=' + result.conversation_id));
|
|
|
1289
1434
|
(transportHealth
|
|
1290
1435
|
? '<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>'
|
|
1291
1436
|
: '') +
|
|
1437
|
+
(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>'
|
|
1439
|
+
: '') +
|
|
1292
1440
|
(projectionOutbox.length
|
|
1293
1441
|
? '<div class="muted small" style="margin-top:8px">outbox=' + esc(projectionOutbox[0].status || 'pending') + ' \xB7 target=' + esc(projectionOutbox[0].target_human_session || '(missing)') + '</div>'
|
|
1294
1442
|
: '<div class="muted small" style="margin-top:8px">outbox=clear</div>') +
|
|
@@ -1307,6 +1455,16 @@ conversation=' + result.conversation_id));
|
|
|
1307
1455
|
'</div></div>' +
|
|
1308
1456
|
'</div>';
|
|
1309
1457
|
|
|
1458
|
+
el.innerHTML +=
|
|
1459
|
+
'<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">' +
|
|
1461
|
+
renderRecentBindings(recentBindings) +
|
|
1462
|
+
'</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.') +
|
|
1465
|
+
'</div></div>' +
|
|
1466
|
+
'</div>';
|
|
1467
|
+
|
|
1310
1468
|
el.querySelectorAll('.approve-session-btn').forEach(function (btn) {
|
|
1311
1469
|
btn.addEventListener('click', async function () {
|
|
1312
1470
|
const sessionKey = btn.getAttribute('data-session');
|
|
@@ -1447,6 +1605,7 @@ conversation=' + result.conversation_id));
|
|
|
1447
1605
|
setTab('runtime');
|
|
1448
1606
|
});
|
|
1449
1607
|
}
|
|
1608
|
+
wireHumanDeliveryActionButtons(el);
|
|
1450
1609
|
}
|
|
1451
1610
|
|
|
1452
1611
|
async function promptBindCurrentChat(conversationId, previousBinding, remoteDid) {
|
|
@@ -1456,7 +1615,7 @@ conversation=' + result.conversation_id));
|
|
|
1456
1615
|
const previous = previousBinding || (state.session && state.session.binding ? state.session.binding.session_key : null) || '(unbound)';
|
|
1457
1616
|
const targetRemoteDid = remoteDid || (state.session && state.session.session ? state.session.session.remote_did : null) || '(unknown)';
|
|
1458
1617
|
const confirmed = window.confirm(
|
|
1459
|
-
'
|
|
1618
|
+
'Repair the legacy current-thread compatibility route for this PingAgent session?' +
|
|
1460
1619
|
'
|
|
1461
1620
|
|
|
1462
1621
|
Conversation: ' + conversationId +
|
|
@@ -1464,9 +1623,9 @@ Conversation: ' + conversationId +
|
|
|
1464
1623
|
Remote DID: ' + targetRemoteDid +
|
|
1465
1624
|
'
|
|
1466
1625
|
|
|
1467
|
-
Current OpenClaw
|
|
1626
|
+
Current OpenClaw compatibility target: ' + (current || '(none)') +
|
|
1468
1627
|
'
|
|
1469
|
-
Previous
|
|
1628
|
+
Previous compatibility route: ' + previous
|
|
1470
1629
|
);
|
|
1471
1630
|
if (!confirmed) return;
|
|
1472
1631
|
await api('/api/runtime/session-bindings/bind-current', {
|
|
@@ -2154,6 +2313,24 @@ function readSinceLastSeenState(storePath, operatorId, scopeType, scopeKey) {
|
|
|
2154
2313
|
store.close();
|
|
2155
2314
|
}
|
|
2156
2315
|
}
|
|
2316
|
+
function readHumanDeliveryState(storePath, limit = 12) {
|
|
2317
|
+
const store = new LocalStore(storePath);
|
|
2318
|
+
try {
|
|
2319
|
+
return summarizeHumanDelivery(store, limit);
|
|
2320
|
+
} finally {
|
|
2321
|
+
store.close();
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
function buildOpenClawAgentStatePayload(input) {
|
|
2325
|
+
return deriveOpenClawAgentState({
|
|
2326
|
+
runtime_status: input.runtimeStatus ?? null,
|
|
2327
|
+
sessions: input.sessions ?? [],
|
|
2328
|
+
pending_decisions: Array.isArray(input.pendingDecisions) ? input.pendingDecisions.length : 0,
|
|
2329
|
+
human_delivery: input.humanDelivery ?? null,
|
|
2330
|
+
transport_health: input.transportHealth ?? null,
|
|
2331
|
+
blocked_reason: input.blockedReason ?? null
|
|
2332
|
+
});
|
|
2333
|
+
}
|
|
2157
2334
|
function buildPolicyDecisionShape(identityPath, remoteDid, opts) {
|
|
2158
2335
|
const policy = readTrustPolicyDoc(identityPath);
|
|
2159
2336
|
const runtimeMode = opts?.runtimeMode ?? getRuntimeMode();
|
|
@@ -2268,13 +2445,30 @@ async function buildRuntimeOverviewPayload(ctx) {
|
|
|
2268
2445
|
const projectionOutbox = readProjectionOutboxState(ctx.storePath, 20);
|
|
2269
2446
|
const transportHealth = readTransportHealthState(ctx.storePath);
|
|
2270
2447
|
const sinceLastSeen = readSinceLastSeenState(ctx.storePath, "host_panel", "global");
|
|
2448
|
+
const humanDelivery = readHumanDeliveryState(ctx.storePath, 20);
|
|
2271
2449
|
const decisionStore = new LocalStore(ctx.storePath);
|
|
2272
2450
|
let decisionViews = [];
|
|
2451
|
+
let humanDeliveryDetails = null;
|
|
2273
2452
|
try {
|
|
2274
2453
|
decisionViews = listPendingDecisionViews(decisionStore, 100);
|
|
2454
|
+
const bindingManager = new HumanDeliveryBindingManager(decisionStore);
|
|
2455
|
+
const intentManager = new NotificationIntentManager(decisionStore);
|
|
2456
|
+
humanDeliveryDetails = {
|
|
2457
|
+
unresolved_intents: intentManager.listByStatus("unresolved", 20),
|
|
2458
|
+
failed_intents: intentManager.listByStatus("failed", 20),
|
|
2459
|
+
acknowledged_intents: intentManager.listByStatus("acknowledged", 20),
|
|
2460
|
+
recent_bindings: bindingManager.listRecent(20)
|
|
2461
|
+
};
|
|
2275
2462
|
} finally {
|
|
2276
2463
|
decisionStore.close();
|
|
2277
2464
|
}
|
|
2465
|
+
const agentState = buildOpenClawAgentStatePayload({
|
|
2466
|
+
runtimeStatus: ingressRuntime,
|
|
2467
|
+
sessions,
|
|
2468
|
+
pendingDecisions: decisionViews,
|
|
2469
|
+
humanDelivery,
|
|
2470
|
+
transportHealth
|
|
2471
|
+
});
|
|
2278
2472
|
return {
|
|
2279
2473
|
did: ctx.myDid,
|
|
2280
2474
|
serverUrl: ctx.serverUrl,
|
|
@@ -2313,7 +2507,10 @@ async function buildRuntimeOverviewPayload(ctx) {
|
|
|
2313
2507
|
collaborationSummary,
|
|
2314
2508
|
projectionOutbox,
|
|
2315
2509
|
transportHealth,
|
|
2510
|
+
agentState,
|
|
2316
2511
|
sinceLastSeen,
|
|
2512
|
+
humanDelivery,
|
|
2513
|
+
humanDeliveryDetails,
|
|
2317
2514
|
pendingDecisions: decisionViews,
|
|
2318
2515
|
recentCollaborationEvents: collaborationEventManager.listRecent(20),
|
|
2319
2516
|
sessions: sessions.map((session) => ({
|
|
@@ -2381,12 +2578,17 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
|
|
|
2381
2578
|
});
|
|
2382
2579
|
const outboxStore = new LocalStore(ctx.storePath);
|
|
2383
2580
|
let projectionOutbox = [];
|
|
2581
|
+
let notificationIntents = [];
|
|
2582
|
+
let recentBindings = [];
|
|
2384
2583
|
let sinceLastSeen = null;
|
|
2385
2584
|
let deliveryTimeline = [];
|
|
2386
2585
|
let projectionPreview = null;
|
|
2387
2586
|
let pendingDecisionViews = [];
|
|
2587
|
+
let humanDelivery = null;
|
|
2388
2588
|
try {
|
|
2389
2589
|
projectionOutbox = new CollaborationProjectionOutboxManager(outboxStore).listBySession(session.session_key, 20);
|
|
2590
|
+
notificationIntents = new NotificationIntentManager(outboxStore).listBySession(session.session_key, 20);
|
|
2591
|
+
recentBindings = listRecentBindingsForSession(outboxStore, session.session_key, session.conversation_id, 20);
|
|
2390
2592
|
sinceLastSeen = summarizeSinceLastSeen(outboxStore, {
|
|
2391
2593
|
operator_id: "host_panel",
|
|
2392
2594
|
scope_type: "session",
|
|
@@ -2400,9 +2602,17 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
|
|
|
2400
2602
|
5
|
|
2401
2603
|
);
|
|
2402
2604
|
pendingDecisionViews = listPendingDecisionViews(outboxStore, 100).filter((event) => event.session_key === session.session_key).slice(0, 20);
|
|
2605
|
+
humanDelivery = summarizeHumanDelivery(outboxStore, 20);
|
|
2403
2606
|
} finally {
|
|
2404
2607
|
outboxStore.close();
|
|
2405
2608
|
}
|
|
2609
|
+
const agentState = buildOpenClawAgentStatePayload({
|
|
2610
|
+
runtimeStatus: readIngressRuntimeStatus(),
|
|
2611
|
+
sessions: sessionManager.listRecentSessions(50),
|
|
2612
|
+
pendingDecisions: pendingDecisionViews,
|
|
2613
|
+
humanDelivery,
|
|
2614
|
+
transportHealth: readTransportHealthState(ctx.storePath)
|
|
2615
|
+
});
|
|
2406
2616
|
return {
|
|
2407
2617
|
session,
|
|
2408
2618
|
sessionSummary: sessionSummaryManager.get(session.session_key),
|
|
@@ -2411,6 +2621,7 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
|
|
|
2411
2621
|
collaborationSummary: collaborationEventManager.summarize(200),
|
|
2412
2622
|
ingressRuntime: readIngressRuntimeStatus(),
|
|
2413
2623
|
transportHealth: readTransportHealthState(ctx.storePath),
|
|
2624
|
+
agentState,
|
|
2414
2625
|
binding,
|
|
2415
2626
|
bindingAlert,
|
|
2416
2627
|
activeWorkSession,
|
|
@@ -2419,9 +2630,12 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
|
|
|
2419
2630
|
sessionBindingAlertsPath: getSessionBindingAlertsFilePath(),
|
|
2420
2631
|
collaborationProjection: policy.collaboration_projection,
|
|
2421
2632
|
projectionOutbox,
|
|
2633
|
+
recentBindings,
|
|
2634
|
+
recentNotificationIntents: notificationIntents,
|
|
2422
2635
|
sinceLastSeen,
|
|
2423
2636
|
deliveryTimeline,
|
|
2424
2637
|
projectionPreview,
|
|
2638
|
+
humanDelivery,
|
|
2425
2639
|
policyExplain: buildPolicyDecisionShape(ctx.identityPath, session.remote_did, { runtimeMode: getRuntimeMode() }),
|
|
2426
2640
|
tasks: tasks.map((task) => ({
|
|
2427
2641
|
...task,
|
|
@@ -2504,6 +2718,57 @@ async function handleApi(pathname, req, ctx) {
|
|
|
2504
2718
|
transportPreference: readTransportPreference()
|
|
2505
2719
|
};
|
|
2506
2720
|
}
|
|
2721
|
+
if (parts[1] === "human-delivery") {
|
|
2722
|
+
const store = new LocalStore(ctx.storePath);
|
|
2723
|
+
try {
|
|
2724
|
+
const bindingManager = new HumanDeliveryBindingManager(store);
|
|
2725
|
+
const intentManager = new NotificationIntentManager(store);
|
|
2726
|
+
if (req.method === "GET") {
|
|
2727
|
+
return {
|
|
2728
|
+
humanDelivery: summarizeHumanDelivery(store, 20),
|
|
2729
|
+
recentBindings: bindingManager.listRecent(50),
|
|
2730
|
+
unresolvedIntents: intentManager.listByStatus("unresolved", 50),
|
|
2731
|
+
failedIntents: intentManager.listByStatus("failed", 50),
|
|
2732
|
+
acknowledgedIntents: intentManager.listByStatus("acknowledged", 50)
|
|
2733
|
+
};
|
|
2734
|
+
}
|
|
2735
|
+
if (req.method === "POST" && parts[2] === "action") {
|
|
2736
|
+
const body = await readBody(req);
|
|
2737
|
+
const action = String(body?.action ?? "").trim();
|
|
2738
|
+
const intentId = Number(body?.intent_id);
|
|
2739
|
+
const bindingId = Number(body?.binding_id);
|
|
2740
|
+
let intent = null;
|
|
2741
|
+
let binding = null;
|
|
2742
|
+
if (action === "retry_intent") {
|
|
2743
|
+
if (!Number.isInteger(intentId) || intentId <= 0) throw new Error("intent_id is required");
|
|
2744
|
+
intent = intentManager.markPending(intentId, { clear_error: true, clear_ack: false, clear_binding: false });
|
|
2745
|
+
} else if (action === "re_resolve_intent_binding") {
|
|
2746
|
+
if (!Number.isInteger(intentId) || intentId <= 0) throw new Error("intent_id is required");
|
|
2747
|
+
intent = intentManager.markPending(intentId, { clear_error: true, clear_ack: false, clear_binding: true });
|
|
2748
|
+
} else if (action === "cancel_intent") {
|
|
2749
|
+
if (!Number.isInteger(intentId) || intentId <= 0) throw new Error("intent_id is required");
|
|
2750
|
+
intent = intentManager.markCanceled(intentId, "manual_cancel");
|
|
2751
|
+
} else if (action === "mark_binding_stale") {
|
|
2752
|
+
if (!Number.isInteger(bindingId) || bindingId <= 0) throw new Error("binding_id is required");
|
|
2753
|
+
binding = bindingManager.markStale(bindingId);
|
|
2754
|
+
} else {
|
|
2755
|
+
throw new Error("action must be retry_intent, re_resolve_intent_binding, cancel_intent, or mark_binding_stale");
|
|
2756
|
+
}
|
|
2757
|
+
return {
|
|
2758
|
+
ok: true,
|
|
2759
|
+
action,
|
|
2760
|
+
intent,
|
|
2761
|
+
binding,
|
|
2762
|
+
humanDelivery: summarizeHumanDelivery(store, 20),
|
|
2763
|
+
unresolvedIntents: intentManager.listByStatus("unresolved", 50),
|
|
2764
|
+
failedIntents: intentManager.listByStatus("failed", 50),
|
|
2765
|
+
acknowledgedIntents: intentManager.listByStatus("acknowledged", 50)
|
|
2766
|
+
};
|
|
2767
|
+
}
|
|
2768
|
+
} finally {
|
|
2769
|
+
store.close();
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2507
2772
|
if (parts[1] === "transport-switch" && req.method === "POST") {
|
|
2508
2773
|
const body = await readBody(req);
|
|
2509
2774
|
const mode = String(body?.mode ?? "").trim().toLowerCase();
|
|
@@ -2618,10 +2883,15 @@ async function handleApi(pathname, req, ctx) {
|
|
|
2618
2883
|
const outboxManager = new CollaborationProjectionOutboxManager(store);
|
|
2619
2884
|
if (req.method === "GET") {
|
|
2620
2885
|
const pendingDecisions = listPendingDecisionViews(store, 100);
|
|
2886
|
+
const intentManager = new NotificationIntentManager(store);
|
|
2621
2887
|
return {
|
|
2622
2888
|
pendingDecisions,
|
|
2623
2889
|
projectionOutboxPending: outboxManager.listByStatus("pending", 50),
|
|
2624
|
-
projectionOutboxFailed: outboxManager.listByStatus("failed", 50)
|
|
2890
|
+
projectionOutboxFailed: outboxManager.listByStatus("failed", 50),
|
|
2891
|
+
notificationIntentsPending: intentManager.listByStatus("pending", 50).concat(intentManager.listByStatus("bound", 50)),
|
|
2892
|
+
notificationIntentsUnresolved: intentManager.listByStatus("unresolved", 50),
|
|
2893
|
+
notificationIntentsFailed: intentManager.listByStatus("failed", 50),
|
|
2894
|
+
humanDelivery: summarizeHumanDelivery(store, 20)
|
|
2625
2895
|
};
|
|
2626
2896
|
}
|
|
2627
2897
|
if (parts[2] === "resolve" && req.method === "POST") {
|