@pingagent/sdk 0.1.11 → 0.1.13
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 +179 -32
- package/dist/chunk-N2GCIMAW.js +3800 -0
- package/dist/index.d.ts +53 -1
- package/dist/index.js +3 -1
- package/dist/web-server.js +324 -45
- package/package.json +2 -2
package/dist/web-server.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
summarizeTrustPolicyAudit,
|
|
24
24
|
updateStoredToken,
|
|
25
25
|
upsertTrustPolicyRecommendation
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-N2GCIMAW.js";
|
|
27
27
|
|
|
28
28
|
// src/web-server.ts
|
|
29
29
|
import * as fs from "fs";
|
|
@@ -153,6 +153,20 @@ function getHostPanelHtml() {
|
|
|
153
153
|
}
|
|
154
154
|
.empty { color: #94a3b8; font-size: 13px; }
|
|
155
155
|
.link-row { display: flex; gap: 10px; margin-top: 20px; }
|
|
156
|
+
.toolbar-row { display: flex; justify-content: space-between; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
|
|
157
|
+
.toolbar-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
|
|
158
|
+
.mode-toggle { display: inline-flex; gap: 6px; padding: 4px; border-radius: 999px; border: 1px solid #334155; background: #020617; }
|
|
159
|
+
.mode-toggle button {
|
|
160
|
+
border: 0;
|
|
161
|
+
background: transparent;
|
|
162
|
+
color: #94a3b8;
|
|
163
|
+
border-radius: 999px;
|
|
164
|
+
padding: 6px 10px;
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
}
|
|
167
|
+
.mode-toggle button.active { background: #0f766e; color: #ecfeff; }
|
|
168
|
+
.summary-pills { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
|
|
169
|
+
.summary-pills .pill { font-size: 11px; }
|
|
156
170
|
@media (max-width: 1000px) {
|
|
157
171
|
.layout { grid-template-columns: 1fr; }
|
|
158
172
|
.sidebar { border-right: none; border-bottom: 1px solid #1e293b; }
|
|
@@ -196,7 +210,17 @@ function getHostPanelHtml() {
|
|
|
196
210
|
<div class="grid stats" id="statsGrid"></div>
|
|
197
211
|
<div class="grid runtime-layout" style="margin-top:16px">
|
|
198
212
|
<div class="card">
|
|
199
|
-
<
|
|
213
|
+
<div class="toolbar-row">
|
|
214
|
+
<h2 style="margin:0">Recent Sessions</h2>
|
|
215
|
+
<div class="toolbar-actions">
|
|
216
|
+
<button class="secondary-btn" id="toggleUnreadBtn" style="width:auto">Unread only: off</button>
|
|
217
|
+
<button class="secondary-btn" id="nextUnreadBtn" style="width:auto">Next unread</button>
|
|
218
|
+
<div class="mode-toggle" aria-label="Runtime detail mode">
|
|
219
|
+
<button id="detailModeBasicBtn" class="active" type="button">Basic</button>
|
|
220
|
+
<button id="detailModeAdvancedBtn" type="button">Advanced</button>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
200
224
|
<div class="sessions" id="sessionList"></div>
|
|
201
225
|
</div>
|
|
202
226
|
<div class="grid">
|
|
@@ -325,14 +349,28 @@ function getHostPanelHtml() {
|
|
|
325
349
|
</div>
|
|
326
350
|
|
|
327
351
|
<script>
|
|
352
|
+
const initialQuery = (function () {
|
|
353
|
+
const params = new URLSearchParams(window.location.search);
|
|
354
|
+
const profile = params.get('profile');
|
|
355
|
+
const sessionKey = params.get('session_key');
|
|
356
|
+
const view = params.get('view');
|
|
357
|
+
return {
|
|
358
|
+
profile: profile && profile.trim() ? profile.trim() : null,
|
|
359
|
+
sessionKey: sessionKey && sessionKey.trim() ? sessionKey.trim() : null,
|
|
360
|
+
view: view === 'policy' ? 'policy' : 'runtime',
|
|
361
|
+
};
|
|
362
|
+
})();
|
|
363
|
+
|
|
328
364
|
const state = {
|
|
329
|
-
selectedProfile: sessionStorage.getItem('pingagent_host_panel_profile') || null,
|
|
330
|
-
currentTab:
|
|
365
|
+
selectedProfile: initialQuery.profile || sessionStorage.getItem('pingagent_host_panel_profile') || null,
|
|
366
|
+
currentTab: initialQuery.view,
|
|
331
367
|
profiles: [],
|
|
332
368
|
overview: null,
|
|
333
369
|
session: null,
|
|
334
370
|
policy: null,
|
|
335
|
-
selectedSessionKey: null,
|
|
371
|
+
selectedSessionKey: initialQuery.sessionKey || null,
|
|
372
|
+
detailMode: sessionStorage.getItem('pingagent_host_panel_detail_mode') || 'basic',
|
|
373
|
+
showUnreadOnly: false,
|
|
336
374
|
};
|
|
337
375
|
|
|
338
376
|
function esc(value) {
|
|
@@ -356,6 +394,45 @@ function getHostPanelHtml() {
|
|
|
356
394
|
try { return new Date(value).toLocaleString(); } catch { return String(value); }
|
|
357
395
|
}
|
|
358
396
|
|
|
397
|
+
function syncUrlState() {
|
|
398
|
+
const url = new URL(window.location.href);
|
|
399
|
+
if (state.selectedProfile) url.searchParams.set('profile', state.selectedProfile);
|
|
400
|
+
else url.searchParams.delete('profile');
|
|
401
|
+
if (state.selectedSessionKey) url.searchParams.set('session_key', state.selectedSessionKey);
|
|
402
|
+
else url.searchParams.delete('session_key');
|
|
403
|
+
url.searchParams.set('view', state.currentTab === 'policy' ? 'policy' : 'runtime');
|
|
404
|
+
history.replaceState(null, '', url.pathname + (url.search ? url.search : ''));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function setDetailMode(mode) {
|
|
408
|
+
state.detailMode = mode === 'advanced' ? 'advanced' : 'basic';
|
|
409
|
+
sessionStorage.setItem('pingagent_host_panel_detail_mode', state.detailMode);
|
|
410
|
+
document.getElementById('detailModeBasicBtn').classList.toggle('active', state.detailMode === 'basic');
|
|
411
|
+
document.getElementById('detailModeAdvancedBtn').classList.toggle('active', state.detailMode === 'advanced');
|
|
412
|
+
if (state.overview) renderOverview();
|
|
413
|
+
if (state.session) renderSession();
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function buildSessionLink(sessionKey) {
|
|
417
|
+
const url = new URL(window.location.href);
|
|
418
|
+
if (state.selectedProfile) url.searchParams.set('profile', state.selectedProfile);
|
|
419
|
+
else url.searchParams.delete('profile');
|
|
420
|
+
if (sessionKey) url.searchParams.set('session_key', sessionKey);
|
|
421
|
+
else url.searchParams.delete('session_key');
|
|
422
|
+
url.searchParams.set('view', 'runtime');
|
|
423
|
+
return url.toString();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function copyText(text, fallbackLabel) {
|
|
427
|
+
try {
|
|
428
|
+
await navigator.clipboard.writeText(text);
|
|
429
|
+
window.alert((fallbackLabel || 'Copied') + ':
|
|
430
|
+
' + text);
|
|
431
|
+
} catch {
|
|
432
|
+
window.prompt(fallbackLabel || 'Copy', text);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
359
436
|
async function api(path, opts) {
|
|
360
437
|
let url = path;
|
|
361
438
|
if (state.selectedProfile) {
|
|
@@ -405,6 +482,7 @@ function getHostPanelHtml() {
|
|
|
405
482
|
document.getElementById('navPolicy').classList.toggle('active', tab === 'policy');
|
|
406
483
|
document.getElementById('runtimePanel').classList.toggle('active', tab === 'runtime');
|
|
407
484
|
document.getElementById('policyPanel').classList.toggle('active', tab === 'policy');
|
|
485
|
+
syncUrlState();
|
|
408
486
|
}
|
|
409
487
|
|
|
410
488
|
function renderHeader() {
|
|
@@ -494,9 +572,40 @@ function getHostPanelHtml() {
|
|
|
494
572
|
'</div>';
|
|
495
573
|
}
|
|
496
574
|
|
|
575
|
+
function getOverviewSessions() {
|
|
576
|
+
return state.overview && Array.isArray(state.overview.sessions) ? state.overview.sessions : [];
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function getVisibleSessions() {
|
|
580
|
+
const sessions = getOverviewSessions();
|
|
581
|
+
return state.showUnreadOnly
|
|
582
|
+
? sessions.filter(function (session) { return Number(session.unread_count || 0) > 0; })
|
|
583
|
+
: sessions;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function syncSelectedSessionFromOverview() {
|
|
587
|
+
const allSessions = getOverviewSessions();
|
|
588
|
+
const visibleSessions = getVisibleSessions();
|
|
589
|
+
if (!allSessions.length) {
|
|
590
|
+
state.selectedSessionKey = null;
|
|
591
|
+
state.session = null;
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
const hasSelected = allSessions.some(function (session) { return session.session_key === state.selectedSessionKey; });
|
|
595
|
+
if (!hasSelected) {
|
|
596
|
+
state.selectedSessionKey = visibleSessions.length ? visibleSessions[0].session_key : allSessions[0].session_key;
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (state.showUnreadOnly) {
|
|
600
|
+
const stillVisible = visibleSessions.some(function (session) { return session.session_key === state.selectedSessionKey; });
|
|
601
|
+
if (!stillVisible) state.selectedSessionKey = visibleSessions.length ? visibleSessions[0].session_key : null;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
497
605
|
function renderOverview() {
|
|
498
606
|
const overview = state.overview;
|
|
499
607
|
if (!overview) return;
|
|
608
|
+
syncSelectedSessionFromOverview();
|
|
500
609
|
const ingressState = ingressStatusModel(overview);
|
|
501
610
|
document.getElementById('activationCard').innerHTML =
|
|
502
611
|
'<div class="status-strip">' +
|
|
@@ -536,24 +645,27 @@ function getHostPanelHtml() {
|
|
|
536
645
|
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>';
|
|
537
646
|
}).join('');
|
|
538
647
|
|
|
539
|
-
const
|
|
648
|
+
const toggleUnreadBtn = document.getElementById('toggleUnreadBtn');
|
|
649
|
+
if (toggleUnreadBtn) toggleUnreadBtn.textContent = 'Unread only: ' + (state.showUnreadOnly ? 'on' : 'off');
|
|
650
|
+
const sessions = getVisibleSessions();
|
|
540
651
|
if (!sessions.length) {
|
|
541
|
-
document.getElementById('sessionList').innerHTML = '<div class="empty">No sessions yet
|
|
652
|
+
document.getElementById('sessionList').innerHTML = '<div class="empty">' + (state.showUnreadOnly ? 'No unread sessions.' : 'No sessions yet.') + '</div>';
|
|
542
653
|
} else {
|
|
543
|
-
if (!state.selectedSessionKey) state.selectedSessionKey = sessions[0].session_key;
|
|
544
654
|
document.getElementById('sessionList').innerHTML = sessions.map(function (session) {
|
|
545
655
|
const active = session.session_key === state.selectedSessionKey ? ' active' : '';
|
|
546
656
|
const badges = [
|
|
547
657
|
'<span class="badge ' + esc(session.trust_state) + '">' + esc(session.trust_state) + '</span>',
|
|
548
658
|
session.binding_alert
|
|
549
|
-
? '<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="
|
|
659
|
+
? '<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="OpenClaw chat link needs attention">Needs reconnect</button>'
|
|
550
660
|
: '',
|
|
551
661
|
].filter(Boolean).join('');
|
|
552
662
|
return '<div class="session-row' + active + '" data-session="' + esc(session.session_key) + '">' +
|
|
553
|
-
'<div class="top"><strong>' + esc(session.remote_did || session.
|
|
554
|
-
'<div class="muted small" style="margin-top:6px">conversation=' + esc(session.conversation_id || '(none)') + '</div>' +
|
|
663
|
+
'<div class="top"><strong>' + esc(session.remote_did || session.session_key || 'unknown') + '</strong><div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end">' + badges + '</div></div>' +
|
|
555
664
|
'<div class="muted small">unread=' + esc(session.unread_count) + ' \xB7 last=' + esc(fmtTs(session.last_remote_activity_at || session.updated_at)) + '</div>' +
|
|
556
|
-
|
|
665
|
+
(state.detailMode === 'advanced'
|
|
666
|
+
? '<div class="muted small" style="margin-top:6px">conversation=' + esc(session.conversation_id || '(none)') + '</div>' +
|
|
667
|
+
'<div class="muted small">work_session=' + esc(session.mapped_work_session || '(unbound)') + (session.is_active_work_session ? ' \xB7 active_chat=true' : '') + '</div>'
|
|
668
|
+
: '') +
|
|
557
669
|
'<div class="muted small" style="margin-top:6px">' + esc(session.last_message_preview || '(no preview)') + '</div>' +
|
|
558
670
|
'</div>';
|
|
559
671
|
}).join('');
|
|
@@ -678,25 +790,47 @@ conversation=' + result.conversation_id));
|
|
|
678
790
|
const bindingAlert = detail.bindingAlert || null;
|
|
679
791
|
const activeWorkSession = detail.activeWorkSession || null;
|
|
680
792
|
const summary = detail.sessionSummary || null;
|
|
793
|
+
const isAdvanced = state.detailMode === 'advanced';
|
|
794
|
+
const sessionLink = buildSessionLink(session.session_key);
|
|
795
|
+
const summaryPills = [];
|
|
796
|
+
if (contact && contact.action) summaryPills.push('<span class="pill">contact=' + esc(contact.action) + '</span>');
|
|
797
|
+
if (task && task.action) summaryPills.push('<span class="pill">task=' + esc(task.action) + '</span>');
|
|
798
|
+
if (session.trust_state) summaryPills.push('<span class="pill">trust=' + esc(session.trust_state) + '</span>');
|
|
799
|
+
if (Number(session.unread_count || 0) > 0) summaryPills.push('<span class="pill">unread=' + esc(session.unread_count) + '</span>');
|
|
800
|
+
if (binding && binding.session_key) summaryPills.push('<span class="pill">chat link attached</span>');
|
|
801
|
+
else if (activeWorkSession) summaryPills.push('<span class="pill">current OpenClaw chat available</span>');
|
|
802
|
+
const policyBlock = isAdvanced
|
|
803
|
+
? '<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>'
|
|
804
|
+
: '<div class="summary-pills" style="margin-top:8px">' + summaryPills.join('') + '</div>' +
|
|
805
|
+
'<div class="muted small" style="margin-top:10px">' + esc(contact.explanation || '(no contact explanation)') + '</div>' +
|
|
806
|
+
'<div class="muted small" style="margin-top:6px">' + esc(task.explanation || '(no task explanation)') + '</div>';
|
|
681
807
|
|
|
682
808
|
el.innerHTML = '' +
|
|
683
809
|
'<div class="two-col">' +
|
|
684
810
|
'<div>' +
|
|
685
811
|
'<div class="label">Session</div>' +
|
|
686
812
|
'<div style="margin-top:8px"><strong>' + esc(session.remote_did || '(unknown)') + '</strong></div>' +
|
|
687
|
-
'<div class="muted small">session=' + esc(session.session_key) + '</div>' +
|
|
688
|
-
'<div class="muted small">conversation=' + esc(session.conversation_id || '(none)') + '</div>' +
|
|
689
813
|
'<div class="muted small">trust=' + esc(session.trust_state) + ' \xB7 unread=' + esc(session.unread_count) + '</div>' +
|
|
690
814
|
'<div class="muted small">last activity=' + esc(fmtTs(session.last_remote_activity_at || session.updated_at)) + '</div>' +
|
|
691
|
-
|
|
692
|
-
|
|
815
|
+
(isAdvanced
|
|
816
|
+
? '<div style="margin-top:8px">' +
|
|
817
|
+
'<div class="muted small">session=' + esc(session.session_key) + '</div>' +
|
|
818
|
+
'<div class="muted small">conversation=' + esc(session.conversation_id || '(none)') + '</div>' +
|
|
819
|
+
'<div class="muted small">active_chat_session=' + esc(activeWorkSession || '(none)') + '</div>' +
|
|
820
|
+
'<div class="muted small">binding=' + esc(binding ? binding.session_key : '(unbound)') + '</div>' +
|
|
821
|
+
'</div>'
|
|
822
|
+
: '') +
|
|
693
823
|
(openRecommendation
|
|
694
824
|
? '<div class="muted small" style="margin-top:8px">trust_action=' + esc(recommendationActionLabel(openRecommendation)) + '</div>'
|
|
695
825
|
: (reopenRecommendation ? '<div class="muted small" style="margin-top:8px">trust_action=' + esc(recommendationActionLabel(reopenRecommendation)) + '</div>' : '')) +
|
|
826
|
+
(summaryPills.length ? '<div class="summary-pills">' + summaryPills.join('') + '</div>' : '') +
|
|
696
827
|
(bindingAlert
|
|
697
|
-
? '<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>Needs
|
|
828
|
+
? '<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>Needs reconnect</strong><div class="small" style="margin-top:6px">' + esc(isAdvanced ? (bindingAlert.message || 'Bound work session is missing. Rebind this PingAgent conversation to the current chat session.') : 'OpenClaw chat link is stale. Attach this PingAgent session to the current OpenClaw chat.') + '</div></div>'
|
|
698
829
|
: '') +
|
|
699
830
|
'<div class="row-actions">' +
|
|
831
|
+
(session.trust_state === 'pending'
|
|
832
|
+
? '<button class="action-btn approve-session-btn" data-session="' + esc(session.session_key) + '">Approve Contact</button>'
|
|
833
|
+
: '') +
|
|
700
834
|
(openRecommendation
|
|
701
835
|
? '<button class="action-btn apply-session-recommendation-btn" data-session="' + esc(session.session_key) + '">' + esc(recommendationActionLabel(openRecommendation)) + '</button>'
|
|
702
836
|
: '') +
|
|
@@ -706,13 +840,24 @@ conversation=' + result.conversation_id));
|
|
|
706
840
|
(!openRecommendation && reopenRecommendation
|
|
707
841
|
? '<button class="secondary-btn reopen-session-recommendation-btn" data-session="' + esc(session.session_key) + '">Reopen</button>'
|
|
708
842
|
: '') +
|
|
709
|
-
'<button class="action-btn bind-current-btn" data-conversation="' + esc(session.conversation_id || '') + '">
|
|
710
|
-
'<button class="
|
|
843
|
+
'<button class="action-btn bind-current-btn" data-conversation="' + esc(session.conversation_id || '') + '">Attach to Current Chat</button>' +
|
|
844
|
+
'<button class="secondary-btn mark-read-btn" data-session="' + esc(session.session_key) + '">Mark read</button>' +
|
|
845
|
+
'<button class="secondary-btn copy-session-link-btn" data-session="' + esc(session.session_key) + '">Copy Session Link</button>' +
|
|
846
|
+
'<button class="danger-btn clear-binding-btn" data-conversation="' + esc(session.conversation_id || '') + '">Detach Chat Link</button>' +
|
|
847
|
+
'</div>' +
|
|
848
|
+
'<div class="form-grid" style="margin-top:16px">' +
|
|
849
|
+
'<label class="label">Reply in this session</label>' +
|
|
850
|
+
'<textarea id="sessionReplyInput" placeholder="Send a text reply in this session"></textarea>' +
|
|
851
|
+
'<div class="row-actions"><button class="action-btn" id="sendSessionReplyBtn">Send Reply</button></div>' +
|
|
711
852
|
'</div>' +
|
|
712
853
|
'</div>' +
|
|
713
854
|
'<div>' +
|
|
714
855
|
'<div class="label">Policy Decisions</div>' +
|
|
715
|
-
|
|
856
|
+
policyBlock +
|
|
857
|
+
(isAdvanced && recommendations.length
|
|
858
|
+
? '<pre style="margin-top:12px">recommendations_debug=' + esc(JSON.stringify(recommendations.map(function (item) { return { id: item.id, status: item.status, policy: item.policy, action: item.action, current_action: item.current_action, match: item.match, confidence: item.confidence }; }), null, 2)) + '</pre>'
|
|
859
|
+
: '') +
|
|
860
|
+
(isAdvanced ? '<div class="muted small" style="margin-top:10px">permalink=' + esc(sessionLink) + '</div>' : '') +
|
|
716
861
|
'</div>' +
|
|
717
862
|
'</div>' +
|
|
718
863
|
'<div class="grid two-col" style="margin-top:16px">' +
|
|
@@ -730,9 +875,7 @@ conversation=' + result.conversation_id));
|
|
|
730
875
|
'<textarea id="sessionSummaryOpenQuestions" placeholder="Open questions">' + esc(summary && summary.open_questions ? summary.open_questions : '') + '</textarea>' +
|
|
731
876
|
'<textarea id="sessionSummaryNextAction" placeholder="Next action">' + esc(summary && summary.next_action ? summary.next_action : '') + '</textarea>' +
|
|
732
877
|
'<textarea id="sessionSummaryHandoff" placeholder="Handoff-ready summary">' + esc(summary && summary.handoff_ready_text ? summary.handoff_ready_text : '') + '</textarea>' +
|
|
733
|
-
'<div class="row-actions">' +
|
|
734
|
-
'<button class="action-btn" id="saveSessionSummaryBtn">Save Summary</button>' +
|
|
735
|
-
'</div>' +
|
|
878
|
+
'<div class="row-actions"><button class="action-btn" id="saveSessionSummaryBtn">Save Summary</button></div>' +
|
|
736
879
|
'</div>' +
|
|
737
880
|
'</div>' +
|
|
738
881
|
'</div>' +
|
|
@@ -759,8 +902,8 @@ conversation=' + result.conversation_id));
|
|
|
759
902
|
? '<button class="secondary-btn reopen-session-recommendation-btn" data-session="' + esc(session.session_key) + '">Reopen</button>'
|
|
760
903
|
: '';
|
|
761
904
|
return '<div class="recommendation-row"><div class="top"><strong>' + esc(item.policy) + '</strong><span class="badge">' + esc(item.status + ' \xB7 ' + item.action) + '</span></div>' +
|
|
762
|
-
'<div class="muted small">current=' + esc(item.current_action) + ' \xB7 confidence=' + esc(item.confidence) + '</div>' +
|
|
763
|
-
'<div class="muted small">match=' + esc(item.match) + '</div>' +
|
|
905
|
+
(isAdvanced ? '<div class="muted small">current=' + esc(item.current_action) + ' \xB7 confidence=' + esc(item.confidence) + '</div>' : '') +
|
|
906
|
+
(isAdvanced ? '<div class="muted small">match=' + esc(item.match) + '</div>' : '') +
|
|
764
907
|
'<div style="margin-top:8px">' + esc(item.reason) + '</div>' +
|
|
765
908
|
'<div class="row-actions">' + actionButton + dismissButton + reopenButton + '</div>' +
|
|
766
909
|
'</div>';
|
|
@@ -770,10 +913,10 @@ conversation=' + result.conversation_id));
|
|
|
770
913
|
'<div class="grid two-col" style="margin-top:16px">' +
|
|
771
914
|
'<div><div class="label">Recent Messages</div><div class="message-list" style="margin-top:8px">' +
|
|
772
915
|
(messages.length ? messages.map(function (msg) {
|
|
773
|
-
const
|
|
916
|
+
const messageSummary = msg.schema === 'pingagent.text@1' && msg.payload && msg.payload.text
|
|
774
917
|
? msg.payload.text
|
|
775
918
|
: JSON.stringify(msg.payload || {});
|
|
776
|
-
return '<div class="message-row"><div class="muted small">' + esc(fmtTs(msg.ts_ms)) + ' \xB7 ' + esc(msg.direction) + ' \xB7 ' + esc(msg.schema) + '</div><div style="margin-top:8px">' + esc(
|
|
919
|
+
return '<div class="message-row"><div class="muted small">' + esc(fmtTs(msg.ts_ms)) + ' \xB7 ' + esc(msg.direction) + ' \xB7 ' + esc(msg.schema) + '</div><div style="margin-top:8px">' + esc(messageSummary) + '</div></div>';
|
|
777
920
|
}).join('') : '<div class="empty">No local message history yet.</div>') +
|
|
778
921
|
'</div></div>' +
|
|
779
922
|
'<div><div class="label">Policy Audit</div><div class="audit-list" style="margin-top:8px">' +
|
|
@@ -786,6 +929,24 @@ conversation=' + result.conversation_id));
|
|
|
786
929
|
'</div></div>' +
|
|
787
930
|
'</div>';
|
|
788
931
|
|
|
932
|
+
el.querySelectorAll('.approve-session-btn').forEach(function (btn) {
|
|
933
|
+
btn.addEventListener('click', async function () {
|
|
934
|
+
const sessionKey = btn.getAttribute('data-session');
|
|
935
|
+
const result = await api('/api/runtime/session/approve', {
|
|
936
|
+
method: 'POST',
|
|
937
|
+
headers: { 'Content-Type': 'application/json' },
|
|
938
|
+
body: JSON.stringify({ session_key: sessionKey }),
|
|
939
|
+
});
|
|
940
|
+
await refreshAll();
|
|
941
|
+
const promoted = result && result.dm_conversation_id
|
|
942
|
+
? getOverviewSessions().find(function (item) { return item.conversation_id === result.dm_conversation_id; })
|
|
943
|
+
: null;
|
|
944
|
+
state.selectedSessionKey = promoted ? promoted.session_key : sessionKey;
|
|
945
|
+
renderOverview();
|
|
946
|
+
if (state.selectedSessionKey) await loadSession(state.selectedSessionKey);
|
|
947
|
+
setTab('runtime');
|
|
948
|
+
});
|
|
949
|
+
});
|
|
789
950
|
el.querySelectorAll('.bind-current-btn').forEach(function (btn) {
|
|
790
951
|
btn.addEventListener('click', async function () {
|
|
791
952
|
const conversationId = btn.getAttribute('data-conversation');
|
|
@@ -793,6 +954,22 @@ conversation=' + result.conversation_id));
|
|
|
793
954
|
await promptBindCurrentChat(conversationId);
|
|
794
955
|
});
|
|
795
956
|
});
|
|
957
|
+
el.querySelectorAll('.mark-read-btn').forEach(function (btn) {
|
|
958
|
+
btn.addEventListener('click', async function () {
|
|
959
|
+
await api('/api/runtime/session/mark-read', {
|
|
960
|
+
method: 'POST',
|
|
961
|
+
headers: { 'Content-Type': 'application/json' },
|
|
962
|
+
body: JSON.stringify({ session_key: btn.getAttribute('data-session') }),
|
|
963
|
+
});
|
|
964
|
+
await refreshAll();
|
|
965
|
+
setTab('runtime');
|
|
966
|
+
});
|
|
967
|
+
});
|
|
968
|
+
el.querySelectorAll('.copy-session-link-btn').forEach(function (btn) {
|
|
969
|
+
btn.addEventListener('click', async function () {
|
|
970
|
+
await copyText(buildSessionLink(btn.getAttribute('data-session')), 'Session link');
|
|
971
|
+
});
|
|
972
|
+
});
|
|
796
973
|
el.querySelectorAll('.clear-binding-btn').forEach(function (btn) {
|
|
797
974
|
btn.addEventListener('click', async function () {
|
|
798
975
|
await api('/api/runtime/session-bindings/clear', {
|
|
@@ -837,23 +1014,42 @@ conversation=' + result.conversation_id));
|
|
|
837
1014
|
setTab('runtime');
|
|
838
1015
|
});
|
|
839
1016
|
});
|
|
1017
|
+
const sendSessionReplyBtn = document.getElementById('sendSessionReplyBtn');
|
|
1018
|
+
if (sendSessionReplyBtn) {
|
|
1019
|
+
sendSessionReplyBtn.addEventListener('click', async function () {
|
|
1020
|
+
const input = document.getElementById('sessionReplyInput');
|
|
1021
|
+
const message = input.value.trim();
|
|
1022
|
+
if (!message) {
|
|
1023
|
+
window.alert('Reply text is required.');
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
await api('/api/runtime/session/reply', {
|
|
1027
|
+
method: 'POST',
|
|
1028
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1029
|
+
body: JSON.stringify({ session_key: session.session_key, message: message }),
|
|
1030
|
+
});
|
|
1031
|
+
input.value = '';
|
|
1032
|
+
await refreshAll();
|
|
1033
|
+
setTab('runtime');
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
840
1036
|
const saveSessionSummaryBtn = document.getElementById('saveSessionSummaryBtn');
|
|
841
1037
|
if (saveSessionSummaryBtn) {
|
|
842
1038
|
saveSessionSummaryBtn.addEventListener('click', async function () {
|
|
843
1039
|
await api('/api/runtime/session-summary', {
|
|
844
1040
|
method: 'POST',
|
|
845
1041
|
headers: { 'Content-Type': 'application/json' },
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1042
|
+
body: JSON.stringify({
|
|
1043
|
+
session_key: session.session_key,
|
|
1044
|
+
objective: document.getElementById('sessionSummaryObjective').value.trim(),
|
|
1045
|
+
context: document.getElementById('sessionSummaryContext').value.trim(),
|
|
1046
|
+
constraints: document.getElementById('sessionSummaryConstraints').value.trim(),
|
|
1047
|
+
decisions: document.getElementById('sessionSummaryDecisions').value.trim(),
|
|
1048
|
+
open_questions: document.getElementById('sessionSummaryOpenQuestions').value.trim(),
|
|
1049
|
+
next_action: document.getElementById('sessionSummaryNextAction').value.trim(),
|
|
1050
|
+
handoff_ready_text: document.getElementById('sessionSummaryHandoff').value.trim(),
|
|
1051
|
+
}),
|
|
1052
|
+
});
|
|
857
1053
|
await refreshAll();
|
|
858
1054
|
setTab('runtime');
|
|
859
1055
|
});
|
|
@@ -867,7 +1063,7 @@ conversation=' + result.conversation_id));
|
|
|
867
1063
|
const previous = previousBinding || (state.session && state.session.binding ? state.session.binding.session_key : null) || '(unbound)';
|
|
868
1064
|
const targetRemoteDid = remoteDid || (state.session && state.session.session ? state.session.session.remote_did : null) || '(unknown)';
|
|
869
1065
|
const confirmed = window.confirm(
|
|
870
|
-
'
|
|
1066
|
+
'Attach this PingAgent session to the current OpenClaw chat?' +
|
|
871
1067
|
'
|
|
872
1068
|
|
|
873
1069
|
Conversation: ' + conversationId +
|
|
@@ -875,9 +1071,9 @@ Conversation: ' + conversationId +
|
|
|
875
1071
|
Remote DID: ' + targetRemoteDid +
|
|
876
1072
|
'
|
|
877
1073
|
|
|
878
|
-
Current chat: ' + (current || '(none)') +
|
|
1074
|
+
Current OpenClaw chat: ' + (current || '(none)') +
|
|
879
1075
|
'
|
|
880
|
-
Previous
|
|
1076
|
+
Previous chat link: ' + previous
|
|
881
1077
|
);
|
|
882
1078
|
if (!confirmed) return;
|
|
883
1079
|
await api('/api/runtime/session-bindings/bind-current', {
|
|
@@ -1061,14 +1257,15 @@ Previous binding: ' + previous
|
|
|
1061
1257
|
|
|
1062
1258
|
async function loadOverview() {
|
|
1063
1259
|
state.overview = await api('/api/runtime/overview');
|
|
1260
|
+
syncSelectedSessionFromOverview();
|
|
1064
1261
|
renderHeader();
|
|
1065
1262
|
renderOverview();
|
|
1066
|
-
const sessions = state.overview && Array.isArray(state.overview.sessions) ? state.overview.sessions : [];
|
|
1067
|
-
if (!state.selectedSessionKey && sessions.length) {
|
|
1068
|
-
state.selectedSessionKey = sessions[0].session_key;
|
|
1069
|
-
}
|
|
1070
1263
|
if (state.selectedSessionKey) {
|
|
1071
1264
|
await loadSession(state.selectedSessionKey);
|
|
1265
|
+
} else {
|
|
1266
|
+
state.session = null;
|
|
1267
|
+
renderSession();
|
|
1268
|
+
syncUrlState();
|
|
1072
1269
|
}
|
|
1073
1270
|
}
|
|
1074
1271
|
|
|
@@ -1076,6 +1273,7 @@ Previous binding: ' + previous
|
|
|
1076
1273
|
if (!sessionKey) return;
|
|
1077
1274
|
state.selectedSessionKey = sessionKey;
|
|
1078
1275
|
state.session = await api('/api/runtime/session?session_key=' + encodeURIComponent(sessionKey));
|
|
1276
|
+
syncUrlState();
|
|
1079
1277
|
renderSession();
|
|
1080
1278
|
}
|
|
1081
1279
|
|
|
@@ -1096,6 +1294,30 @@ Previous binding: ' + previous
|
|
|
1096
1294
|
|
|
1097
1295
|
document.getElementById('navRuntime').addEventListener('click', function () { setTab('runtime'); });
|
|
1098
1296
|
document.getElementById('navPolicy').addEventListener('click', function () { setTab('policy'); });
|
|
1297
|
+
document.getElementById('toggleUnreadBtn').addEventListener('click', async function () {
|
|
1298
|
+
state.showUnreadOnly = !state.showUnreadOnly;
|
|
1299
|
+
syncSelectedSessionFromOverview();
|
|
1300
|
+
renderOverview();
|
|
1301
|
+
if (state.selectedSessionKey) await loadSession(state.selectedSessionKey);
|
|
1302
|
+
else {
|
|
1303
|
+
syncUrlState();
|
|
1304
|
+
renderSession();
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
document.getElementById('nextUnreadBtn').addEventListener('click', async function () {
|
|
1308
|
+
const unreadSessions = getOverviewSessions().filter(function (session) { return Number(session.unread_count || 0) > 0; });
|
|
1309
|
+
if (!unreadSessions.length) {
|
|
1310
|
+
window.alert('No unread sessions.');
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
const currentIndex = unreadSessions.findIndex(function (session) { return session.session_key === state.selectedSessionKey; });
|
|
1314
|
+
const next = unreadSessions[(currentIndex + 1 + unreadSessions.length) % unreadSessions.length];
|
|
1315
|
+
state.selectedSessionKey = next.session_key;
|
|
1316
|
+
renderOverview();
|
|
1317
|
+
await loadSession(state.selectedSessionKey);
|
|
1318
|
+
});
|
|
1319
|
+
document.getElementById('detailModeBasicBtn').addEventListener('click', function () { setDetailMode('basic'); });
|
|
1320
|
+
document.getElementById('detailModeAdvancedBtn').addEventListener('click', function () { setDetailMode('advanced'); });
|
|
1099
1321
|
document.getElementById('rulePolicy').addEventListener('change', updateRuleActionOptions);
|
|
1100
1322
|
document.getElementById('saveDefaultsBtn').addEventListener('click', async function () {
|
|
1101
1323
|
await api('/api/runtime/policy/defaults', {
|
|
@@ -1178,9 +1400,12 @@ Previous binding: ' + previous
|
|
|
1178
1400
|
});
|
|
1179
1401
|
|
|
1180
1402
|
async function init() {
|
|
1403
|
+
setTab(state.currentTab);
|
|
1404
|
+
setDetailMode(state.detailMode);
|
|
1181
1405
|
await loadProfiles();
|
|
1182
1406
|
updateRuleActionOptions();
|
|
1183
1407
|
await refreshAll();
|
|
1408
|
+
syncUrlState();
|
|
1184
1409
|
}
|
|
1185
1410
|
|
|
1186
1411
|
init().catch(function (error) {
|
|
@@ -1679,6 +1904,18 @@ async function buildSessionOverviewPayload(ctx, sessionKey) {
|
|
|
1679
1904
|
}))
|
|
1680
1905
|
};
|
|
1681
1906
|
}
|
|
1907
|
+
function resolveSessionForInput(sessionManager, input) {
|
|
1908
|
+
if (!sessionManager) return null;
|
|
1909
|
+
const sessionKey = String(input.session_key ?? "").trim();
|
|
1910
|
+
const conversationId = String(input.conversation_id ?? "").trim();
|
|
1911
|
+
const remoteDid = String(input.remote_did ?? "").trim();
|
|
1912
|
+
let session = sessionKey ? sessionManager.get(sessionKey) : null;
|
|
1913
|
+
if (!session && conversationId) session = sessionManager.getByConversationId(conversationId);
|
|
1914
|
+
if (!session && remoteDid) {
|
|
1915
|
+
session = sessionManager.listRecentSessions(100).find((item) => item.remote_did === remoteDid) ?? null;
|
|
1916
|
+
}
|
|
1917
|
+
return session ?? sessionManager.getActiveSession() ?? sessionManager.listRecentSessions(1)[0] ?? null;
|
|
1918
|
+
}
|
|
1682
1919
|
async function handleApi(pathname, req, ctx) {
|
|
1683
1920
|
const client = ctx.client;
|
|
1684
1921
|
const contactManager = ctx.contactManager;
|
|
@@ -1780,6 +2017,48 @@ async function handleApi(pathname, req, ctx) {
|
|
|
1780
2017
|
};
|
|
1781
2018
|
}
|
|
1782
2019
|
if (parts[1] === "session") {
|
|
2020
|
+
const sessionManager = ctx.client.getSessionManager();
|
|
2021
|
+
if (!sessionManager) throw new Error("Session actions require a writable local store");
|
|
2022
|
+
if (parts[2] === "reply" && req.method === "POST") {
|
|
2023
|
+
const body = await readBody(req);
|
|
2024
|
+
const session = resolveSessionForInput(sessionManager, body);
|
|
2025
|
+
if (!session?.conversation_id) throw new Error("No session selected");
|
|
2026
|
+
const text = String(body?.message ?? "").trim();
|
|
2027
|
+
if (!text) throw new Error("Missing message");
|
|
2028
|
+
const sendRes = await client.sendMessage(session.conversation_id, SCHEMA_TEXT, { text });
|
|
2029
|
+
if (!sendRes.ok) throw new Error(sendRes.error?.message ?? "Failed to send");
|
|
2030
|
+
sessionManager.focusSession(session.session_key);
|
|
2031
|
+
return {
|
|
2032
|
+
ok: true,
|
|
2033
|
+
session: sessionManager.get(session.session_key),
|
|
2034
|
+
message_id: sendRes.data?.message_id ?? null
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
if (parts[2] === "approve" && req.method === "POST") {
|
|
2038
|
+
const body = await readBody(req);
|
|
2039
|
+
const session = resolveSessionForInput(sessionManager, body);
|
|
2040
|
+
if (!session?.conversation_id) throw new Error("No session selected");
|
|
2041
|
+
const approveRes = await client.approveContact(session.conversation_id);
|
|
2042
|
+
if (!approveRes.ok) throw new Error(approveRes.error?.message ?? "Failed to approve contact");
|
|
2043
|
+
sessionManager.focusSession(session.session_key);
|
|
2044
|
+
return {
|
|
2045
|
+
ok: true,
|
|
2046
|
+
session: sessionManager.get(session.session_key),
|
|
2047
|
+
trusted: approveRes.data?.trusted ?? true,
|
|
2048
|
+
dm_conversation_id: approveRes.data?.dm_conversation_id ?? session.conversation_id
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
if (parts[2] === "mark-read" && req.method === "POST") {
|
|
2052
|
+
const body = await readBody(req);
|
|
2053
|
+
const session = resolveSessionForInput(sessionManager, body);
|
|
2054
|
+
if (!session?.session_key) throw new Error("No session selected");
|
|
2055
|
+
const updated = sessionManager.markRead(session.session_key);
|
|
2056
|
+
if (!updated) throw new Error("Failed to mark session as read");
|
|
2057
|
+
return {
|
|
2058
|
+
ok: true,
|
|
2059
|
+
session: updated
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
1783
2062
|
const url = new URL(req.url || "", "http://x");
|
|
1784
2063
|
const sessionKey = url.searchParams.get("session_key");
|
|
1785
2064
|
return buildSessionOverviewPayload(ctx, sessionKey);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pingagent/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"uuid": "^11.0.0",
|
|
36
36
|
"ws": "^8.0.0",
|
|
37
37
|
"@pingagent/protocol": "0.1.1",
|
|
38
|
-
"@pingagent/schemas": "0.1.
|
|
38
|
+
"@pingagent/schemas": "0.1.3",
|
|
39
39
|
"@pingagent/a2a": "0.1.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|