feat(box): passkey fetch, agent key-approval flow, unified lookups, tmux agent UX
- box passkey [show|fetch] (+ muse passkey): documents VM-only passkey (/srv/box/passkey.txt, fallback /etc/netvm/passkey.txt on 34.139.37.135), probes VM over SSH with graceful fallback; --json supported. No secrets on bl. - approvals: request_key_approval / check_node_key_request; KEY_APPROVAL status surfaced in `box approvals check`; allow/deny resolve + audit to box-ctl.jsonl; never auto-approved. New `box approvals request-key <node> --reason`. - box lookup (summary|fleet|threads|unread|approvals|key|docs) and docs-lookup engine with lookup_internal/ database (docs_internal symlink). - muse-tmux: non-TTY attach falls back to scrollback capture; prune NameError fix. - box/muse passthrough for tmux/muse/docs; thread list/view alias + prefix resolve. - Docs: AGENTS.md, AGENT-TOOLING.md, BOX-WEB-SURFACE-GUIDE.md, README. - Tests: key-approval + passkey tests; sync stale sidechat UUIDs and manifest name. - .gitignore runtime trackers (subagent-sessions, conversation-nudge-tracker).
This commit is contained in:
+169
-1
@@ -67,7 +67,7 @@
|
||||
|
||||
// Check hash on load
|
||||
const hash = window.location.hash.replace('#', '');
|
||||
if (['dashboard', 'dms', 'jobs', 'loops'].includes(hash)) {
|
||||
if (['dashboard', 'dms', 'jobs', 'loops', 'lookup'].includes(hash)) {
|
||||
switchTab(hash);
|
||||
}
|
||||
}
|
||||
@@ -1188,6 +1188,8 @@
|
||||
codeEl.textContent = `curl -s -H "Authorization: Bearer <token>" \\\n https://box.muse-dev.online/api/box/timers`;
|
||||
} else if (currentTab === 'loops') {
|
||||
codeEl.textContent = `curl -s -H "Authorization: Bearer <token>" \\\n https://box.muse-dev.online/api/box/loop/status\n\ncurl -s -H "Authorization: Bearer <token>" \\\n https://box.muse-dev.online/api/box/loop/health`;
|
||||
} else if (currentTab === 'lookup') {
|
||||
codeEl.textContent = `curl -s -H "Authorization: Bearer <token>" \\\n https://box.muse-dev.online/api/box/lookup\n\n# Or CLI lookup on bl:\nbox lookup surfaces\nbox lookup parse "[RESULT 7fce46e0] OK Done"`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1278,6 +1280,171 @@
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 8. Lookup & Grammar Surface Integration
|
||||
// -------------------------------------------------------------------------
|
||||
window.setLookupPreset = function (text) {
|
||||
const input = document.getElementById('lookup-test-input');
|
||||
if (input) {
|
||||
input.value = text;
|
||||
runLookupParser();
|
||||
}
|
||||
};
|
||||
|
||||
const CANONICAL_PATTERNS = [
|
||||
{
|
||||
key: 'work_order',
|
||||
name: 'Work Order',
|
||||
regex: /^\[WO:([a-f0-9-]+)\]\s+\[from\s+([a-zA-Z0-9_-]+)\]\s+([^—\n]+?)\s*—\s*(.+)$/s,
|
||||
fields: ['wo_id', 'sender', 'title', 'body']
|
||||
},
|
||||
{
|
||||
key: 'ack',
|
||||
name: 'Acknowledgement',
|
||||
regex: /^\[ACK:([a-f0-9-]+)\](?:\s+\[from\s+([a-zA-Z0-9_-]+)\])?/,
|
||||
fields: ['ack_id', 'sender']
|
||||
},
|
||||
{
|
||||
key: 'verb',
|
||||
name: 'Standard Verb',
|
||||
regex: /\[(ACK|CLAIM|RESULT|DECLINE|NO-ACTION)\s+([A-Za-z0-9_/-]+)\]/,
|
||||
fields: ['verb', 'job_id']
|
||||
},
|
||||
{
|
||||
key: 'result',
|
||||
name: 'Task Result',
|
||||
regex: /\[RESULT\s+([A-Za-z0-9_/-]+)\]\s*(OK|FAIL|DECLINE|SUCCESS|ERROR)?\s*(.*?)(?=\[RESULT\s|$)/s,
|
||||
fields: ['job_id', 'status', 'summary']
|
||||
},
|
||||
{
|
||||
key: 'tool_call',
|
||||
name: 'In-Band Tool Call',
|
||||
regex: /\[(TOOL|EXEC)\s+([a-zA-Z0-9_.-]+)\s+(\{.*?\})\]/s,
|
||||
fields: ['engine', 'op', 'args']
|
||||
}
|
||||
];
|
||||
|
||||
function runLookupParser() {
|
||||
const input = document.getElementById('lookup-test-input');
|
||||
const resBox = document.getElementById('lookup-parse-result');
|
||||
if (!input || !resBox) return;
|
||||
|
||||
const val = input.value.trim();
|
||||
if (!val) {
|
||||
resBox.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const matches = [];
|
||||
CANONICAL_PATTERNS.forEach(pat => {
|
||||
const m = pat.regex.exec(val);
|
||||
if (m) {
|
||||
const tokens = {};
|
||||
pat.fields.forEach((f, idx) => {
|
||||
tokens[f] = m[idx + 1] !== undefined ? m[idx + 1].trim() : null;
|
||||
});
|
||||
matches.push({
|
||||
key: pat.key,
|
||||
name: pat.name,
|
||||
matchedText: m[0],
|
||||
tokens: tokens
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
resBox.style.display = 'block';
|
||||
if (matches.length === 0) {
|
||||
resBox.innerHTML = `
|
||||
<div style="color: var(--warn); display: flex; align-items: center; gap: 8px;">
|
||||
<span>⚠</span>
|
||||
<span>No canonical sentence pattern matched this input. Check required tags: [WO:id] [from sender], [ACK:id], [RESULT id] OK, or [CLAIM id].</span>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
let html = `<div style="color: var(--ok); font-weight: 700; margin-bottom: 8px;">✔ Valid Syntax — Matched ${matches.length} Pattern(s):</div>`;
|
||||
matches.forEach(m => {
|
||||
html += `
|
||||
<div style="border-top: 1px solid var(--border); padding-top: 8px; margin-top: 8px;">
|
||||
<div style="font-weight: 600; color: var(--accent); margin-bottom: 4px;">Pattern: ${m.name} (<code>${m.key}</code>)</div>
|
||||
<div style="color: var(--muted); font-size: 11px; margin-bottom: 6px;">Matched Chunk: <span style="color: var(--fg);">${m.matchedText}</span></div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
`;
|
||||
Object.entries(m.tokens).forEach(([k, v]) => {
|
||||
html += `
|
||||
<div style="background: var(--bg); border: 1px solid var(--border); padding: 4px 8px; border-radius: 4px; font-size: 12px;">
|
||||
<span style="color: var(--muted);">${k}:</span> <strong style="color: var(--ok);">${v || '-'}</strong>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
html += `</div></div>`;
|
||||
});
|
||||
resBox.innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
function renderLookupSurfaces() {
|
||||
const tbody = document.getElementById('lookup-surfaces-tbody');
|
||||
if (!tbody) return;
|
||||
|
||||
const surfaces = [
|
||||
{ name: 'Fleet Pulse', id: 'dashboard', selector: '.tab-btn[data-tab="dashboard"]', route: 'GET /api/box/fleet', hint: 'Real-time CDP status, page titles, and queue depths across all nodes.' },
|
||||
{ name: 'DMs & Work Orders', id: 'dms', selector: '.tab-btn[data-tab="dms"]', route: 'GET /api/box/dm/log?limit=50', hint: 'Chronological audit trail of signed work orders, acks, and agent replies.' },
|
||||
{ name: 'Timers & Jobs', id: 'jobs', selector: '.tab-btn[data-tab="jobs"]', route: 'GET /api/box/timers', hint: 'Systemd user timers with hot-trigger buttons (POST /api/box/jobs/{name}/trigger).' },
|
||||
{ name: 'Loop & Strategy', id: 'loops', selector: '.tab-btn[data-tab="loops"]', route: 'GET /api/box/loop/health', hint: 'Fleet loop velocity, open SLA deadlines, strategies, and runtime variables.' },
|
||||
{ name: 'Lookup & Grammar', id: 'lookup', selector: '.tab-btn[data-tab="lookup"]', route: 'GET /api/box/lookup', hint: 'Interactive regex testing, sentence grammar, and DOM surface maps.' },
|
||||
{ name: 'Approvals Surface', id: 'approvals', selector: '[data-testid="approval-dialog"]', route: 'GET /api/approvals', hint: 'Permission modals and operator elevation requests (exit code 2 contract).' }
|
||||
];
|
||||
|
||||
tbody.innerHTML = surfaces.map(s => `
|
||||
<tr>
|
||||
<td style="font-weight: 600; color: var(--fg);">${s.name}</td>
|
||||
<td><code>#tab-${s.id}</code></td>
|
||||
<td><code style="color: var(--accent);">${s.selector}</code></td>
|
||||
<td><code style="color: var(--ok);">${s.route}</code></td>
|
||||
<td style="color: var(--muted); font-size: 12px;">${s.hint}</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function renderLookupSentences() {
|
||||
const grid = document.getElementById('lookup-sentence-grid');
|
||||
if (!grid) return;
|
||||
|
||||
const cards = [
|
||||
{ name: 'Work Order', tag: '[WO:<id>]', template: '[WO:{id}] [from {sender}] {title} — {body}', lifecycle: 'open -> claimed -> resolved', desc: 'Dispatches actionable, trackable tasking between operators or from orchestrator.' },
|
||||
{ name: 'Acknowledgement', tag: '[ACK:<id>]', template: '[ACK:{id}] [from {sender}]', lifecycle: 'open -> acknowledged', desc: 'Confirms receipt of task without closing the tracked loop.' },
|
||||
{ name: 'Task Claim', tag: '[CLAIM <id>]', template: '[CLAIM {job_id}]', lifecycle: 'acknowledged -> claimed', desc: 'Declares worker ownership of a task or swarm slot, preventing duplicate runs.' },
|
||||
{ name: 'Task Result', tag: '[RESULT <id>]', template: '[RESULT {job_id}] {status} {summary}', lifecycle: 'claimed -> resolved', desc: 'Submits completion evidence and resolves the active loop.' },
|
||||
{ name: 'Task Decline', tag: '[DECLINE <id>]', template: '[DECLINE {job_id}] {reason}', lifecycle: 'claimed -> declined', desc: 'Explicit rejection of a task with reason, prompting fallback.' },
|
||||
{ name: 'In-Band Tool Call', tag: '[TOOL <op> <args>]', template: '[TOOL {op} {args_json}]', lifecycle: 'in-flight execution', desc: 'Inline tool directive parsed and executed directly by response-harvester.' }
|
||||
];
|
||||
|
||||
grid.innerHTML = cards.map(c => `
|
||||
<div class="node-card" style="padding: 16px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
||||
<span style="font-weight: 700; color: var(--fg);">${c.name}</span>
|
||||
<span class="node-status-badge badge-active">${c.tag}</span>
|
||||
</div>
|
||||
<div style="font-family: var(--font-mono); font-size: 12px; color: var(--accent); background: var(--bg); padding: 6px 8px; border-radius: 4px; margin-bottom: 8px; word-break: break-all;">${c.template}</div>
|
||||
<div style="font-size: 12px; color: var(--muted); margin-bottom: 8px;">${c.desc}</div>
|
||||
<div style="font-size: 11px; color: var(--muted); text-transform: uppercase;">Lifecycle: <span style="color: var(--ok);">${c.lifecycle}</span></div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function setupLookupTab() {
|
||||
const parseBtn = document.getElementById('lookup-parse-btn');
|
||||
const input = document.getElementById('lookup-test-input');
|
||||
if (parseBtn) parseBtn.addEventListener('click', runLookupParser);
|
||||
if (input) {
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') runLookupParser();
|
||||
});
|
||||
}
|
||||
renderLookupSurfaces();
|
||||
renderLookupSentences();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 9. Initialization
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -1291,6 +1458,7 @@
|
||||
setupTabs();
|
||||
setupFilters();
|
||||
setupCurlDrawer();
|
||||
setupLookupTab();
|
||||
|
||||
// Initial Fetch & Start Polling
|
||||
refreshData();
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
Loop & Strategy
|
||||
<span id="loops-count-badge" class="tab-badge">0</span>
|
||||
</button>
|
||||
<button class="tab-btn" data-tab="lookup">
|
||||
Lookup & Grammar
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- Main Container -->
|
||||
@@ -242,6 +245,63 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Tab 5: Lookup & Grammar -->
|
||||
<section id="tab-lookup" class="tab-pane">
|
||||
<!-- Section 1: Interactive Regex & Sentence Parser -->
|
||||
<div class="section-heading">
|
||||
<div class="section-title">Interactive Sentence & Regex Parser</div>
|
||||
<div class="section-subtitle">Test and validate agent sentence structures against lookup_internal patterns</div>
|
||||
</div>
|
||||
<div class="table-card" style="padding: 16px;">
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 12px;">
|
||||
<input type="text" id="lookup-test-input" placeholder="Type or paste an agent message (e.g. [WO:7fce46e0] [from super] Audit — Check stats)..." style="flex: 1; padding: 10px 14px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); font-family: var(--font-mono); font-size: 13px;">
|
||||
<button id="lookup-parse-btn" class="btn-control" style="background: var(--accent); color: #fff; font-weight: 600; padding: 0 18px;">Parse & Validate</button>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 12px;">
|
||||
<span style="font-size: 11px; color: var(--muted); text-transform: uppercase; align-self: center;">Presets:</span>
|
||||
<button class="btn-control preset-btn" onclick="setLookupPreset('[WO:7fce46e0] [from super] Audit exec.muse-dev.online — Check 200 OK codes')">[WO]</button>
|
||||
<button class="btn-control preset-btn" onclick="setLookupPreset('[ACK:7fce46e0] [from 646]')">[ACK]</button>
|
||||
<button class="btn-control preset-btn" onclick="setLookupPreset('[CLAIM 7fce46e0]')">[CLAIM]</button>
|
||||
<button class="btn-control preset-btn" onclick="setLookupPreset('[RESULT 7fce46e0] OK 14 endpoints verified healthy')">[RESULT]</button>
|
||||
<button class="btn-control preset-btn" onclick="setLookupPreset('[TOOL followup.create {"in_m": 5, "prompt": "Recheck reachability"}]')">[TOOL]</button>
|
||||
</div>
|
||||
<div id="lookup-parse-result" style="display: none; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 12px; font-family: var(--font-mono); font-size: 13px;">
|
||||
<!-- Rendered dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section 2: Sentence Structure Catalog -->
|
||||
<div class="section-heading" style="margin-top: 24px;">
|
||||
<div class="section-title">Protocol Grammar & Conversational Contracts</div>
|
||||
<div class="section-subtitle">Authoritative patterns from lookup_internal/sentence_structure.json</div>
|
||||
</div>
|
||||
<div id="lookup-sentence-grid" class="grid-nodes">
|
||||
<!-- Rendered dynamically by box.js -->
|
||||
</div>
|
||||
|
||||
<!-- Section 3: Assistive Surfaces Catalog -->
|
||||
<div class="section-heading" style="margin-top: 24px;">
|
||||
<div class="section-title">Assistive Surfaces Map for box.muse-dev.online</div>
|
||||
<div class="section-subtitle">DOM Selectors, data-testids, and REST endpoints for browser agents</div>
|
||||
</div>
|
||||
<div class="table-card">
|
||||
<table id="lookup-surfaces-table" class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 120px;">Surface</th>
|
||||
<th style="width: 150px;">Tab / Pane ID</th>
|
||||
<th style="width: 240px;">DOM Tab Selector</th>
|
||||
<th style="width: 240px;">API Route</th>
|
||||
<th>Assistive Purpose & Hint</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="lookup-surfaces-tbody">
|
||||
<!-- Rendered dynamically by box.js -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- 'View as curl' Drawer -->
|
||||
|
||||
Reference in New Issue
Block a user