import { html, nothing } from 'lit'; import { LightElement } from '../../lib/base.js'; export class InboxPage extends LightElement { static properties = { visible: { type: Boolean }, _data: { state: true }, _error: { state: true }, }; constructor() { super(); this.visible = false; this._data = null; this._error = null; this._pollTimer = null; this._expanded = new Set(); } updated(changed) { if (!changed.has('visible')) return; if (this.visible) { this._load(); this._startPolling(); } else { this._stopPolling(); } } disconnectedCallback() { super.disconnectedCallback(); this._stopPolling(); } _startPolling() { this._stopPolling(); this._pollTimer = setInterval(() => this._load(), 8000); } _stopPolling() { if (this._pollTimer) { clearInterval(this._pollTimer); this._pollTimer = null; } } async _load() { try { const res = await fetch('/api/inbox'); if (!res.ok) throw new Error(`HTTP ${res.status}`); this._data = await res.json(); this._error = null; } catch (e) { this._error = e.message; } } async _resolveApproval(requestId, action, note = '', toolCallId = null) { try { // Live items resolve by request_id; DB-persisted (post-restart) items carry // request_id 0 → resolve by the durable, source-agnostic tool_call_id. const url = requestId ? `/api/inbox/approvals/${requestId}/resolve` : `/api/tools/${toolCallId}/resolve`; const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action, note }), }); if (!res.ok) throw new Error(await res.text()); await this._load(); } catch (e) { this._error = e.message; } } _rejectWithNote(requestId, toolCallId) { const note = prompt('Rejection reason (optional):') ?? ''; this._resolveApproval(requestId, 'reject', note, toolCallId); } async _resolveClarification(requestId, inputEl) { const answer = inputEl.value.trim(); if (!answer) return; try { const res = await fetch(`/api/inbox/clarifications/${requestId}/resolve`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ answer }), }); if (!res.ok) throw new Error(await res.text()); await this._load(); } catch (e) { this._error = e.message; } } _toggleRaw(id) { if (this._expanded.has(id)) this._expanded.delete(id); else this._expanded.add(id); this.requestUpdate(); } _keyArgs(args) { const entries = []; for (const key of ['path', 'command', 'url', 'origin', 'destination', 'name', 'message', 'query']) { if (args[key] !== undefined) { let val = args[key]; if (typeof val === 'object') val = JSON.stringify(val); entries.push({ key, value: String(val) }); } } return entries; } _fmt(iso) { if (!iso) return ''; return new Date(iso).toLocaleString(undefined, { day: '2-digit', month: '2-digit', year: '2-digit', hour: '2-digit', minute: '2-digit', }); } _renderApprovalCard(item) { const id = `raw-${item.request_id}`; const open = this._expanded.has(id); const label = item.context_label ?? item.source; const args = item.arguments ?? {}; const keyArgs = this._keyArgs(args); return html`
${JSON.stringify(args, null, 2)}
No pending requests