@dev-mainsequence/codex-plugin 0.7.9 → 0.7.10
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mainsequence",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.10",
|
|
4
4
|
"description": "Message MainSequence agents in Codex chat, recover saved replies, discover project MCPs and run developer commands using your shared session.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "MainSequence"
|
package/build-info.json
CHANGED
|
@@ -38,6 +38,30 @@ let preparationGeneration=0, recipientKinds=[];
|
|
|
38
38
|
let recipient = null, draftRequest = null, messageBusy = false, deliveryGeneration = 0, savedRequests = [];
|
|
39
39
|
let sequence = 0, generation = 0, connected = false, busy = false, snapshot = null, catalog = 'agents', offset = 0, currentPage = null, addingTools = false;
|
|
40
40
|
const assetCache = new Map(), toolCache = new Map(), activity = [];
|
|
41
|
+
// Agent icons change in Command Center without any event reaching the panel. While the Agents list or an
|
|
42
|
+
// agent's details stay on screen, the shown icons are re-read once a minute (the shared transport keeps a
|
|
43
|
+
// directory read for 30 s, so this is a fresh read without invalidating other panels' caches) and only a
|
|
44
|
+
// changed icon is redrawn, in place. The timer ends with the screen and pauses after 240 re-checks.
|
|
45
|
+
const ICON_RECHECK_MS = 60000, ICON_RECHECK_LIMIT = 240, drawnIcons = new Map();
|
|
46
|
+
let iconWatch = null, iconChecks = 0;
|
|
47
|
+
function watchIcons(run, load) {
|
|
48
|
+
clearTimeout(iconWatch);
|
|
49
|
+
iconWatch = setTimeout(async () => {
|
|
50
|
+
if (!connected || run !== generation || iconChecks >= ICON_RECHECK_LIMIT) return;
|
|
51
|
+
iconChecks++;
|
|
52
|
+
try {
|
|
53
|
+
const current = await load();
|
|
54
|
+
if (run !== generation) return;
|
|
55
|
+
for (const { uid, icon: next } of current) {
|
|
56
|
+
if (JSON.stringify(next ?? null) === JSON.stringify(assetCache.get(uid) ?? null)) continue;
|
|
57
|
+
if (next) assetCache.set(uid, next); else assetCache.delete(uid);
|
|
58
|
+
const drawn = drawnIcons.get(uid) || new Set();
|
|
59
|
+
for (const entry of [...drawn]) { drawn.delete(entry); entry.el.replaceWith(icon(uid, entry.large)); }
|
|
60
|
+
}
|
|
61
|
+
} catch { /* A failed re-check keeps the drawn icons; the next one tries again. */ }
|
|
62
|
+
if (run === generation) watchIcons(run, load);
|
|
63
|
+
}, ICON_RECHECK_MS);
|
|
64
|
+
}
|
|
41
65
|
const environmentKey = state => state?.account?.checked ? 'mainsequence.environment.' + JSON.stringify([state.account.backend,state.account.username]) : null;
|
|
42
66
|
function rememberedEnvironment(state) {try {const key=environmentKey(state);return key ? localStorage.getItem(key) : null;} catch {return null;}}
|
|
43
67
|
function rememberEnvironment() {try {const key=environmentKey(snapshot),uid=$('environment').value;if(key && snapshot.environments.some(e=>e.uid===uid))localStorage.setItem(key,uid);} catch {}}
|
|
@@ -64,7 +88,7 @@ function empty(title, detail) { return group('empty', node('h2', title), caption
|
|
|
64
88
|
function facts(values) { const dl = node('dl', undefined, 'facts'); for (const [k, v] of values) if (v) dl.append(node('dt', k), node('dd', v)); return dl; }
|
|
65
89
|
function details(label, content) { const d = node('details'); d.append(node('summary', label), content); return d; }
|
|
66
90
|
function controls() { const ready = connected && snapshot?.contextId && $('environment').value; $('refresh').disabled = !connected || busy; $('environment').disabled = !snapshot?.contextId; $('search').disabled = !ready; $('search-button').disabled = !ready || busy; $('previous').disabled = busy || offset === 0; $('next').disabled = busy || !currentPage || currentPage.semantic || offset + currentPage.results.length >= currentPage.count || !currentPage.results.length; $('directory').setAttribute('aria-busy', String(busy)); for (const id of ['send-message', 'draft-message']) $(id).disabled = messageBusy || !recipient || !ready; $('message').disabled = messageBusy; $('response-kind').disabled = messageBusy || !recipient || recipientKinds.length < 2 || Boolean(recipient.taskRequestId); $('refresh-deliveries').disabled = !connected || !snapshot?.messagingAvailable; }
|
|
67
|
-
function clearResults() { $('code-filters').hidden=catalog!=='code';$('search-button').hidden=catalog==='code'; $('results').replaceChildren(); $('pager').hidden = true; currentPage = null; $('confirmation').replaceChildren(); }
|
|
91
|
+
function clearResults() { clearTimeout(iconWatch); drawnIcons.clear(); $('code-filters').hidden=catalog!=='code';$('search-button').hidden=catalog==='code'; $('results').replaceChildren(); $('pager').hidden = true; currentPage = null; $('confirmation').replaceChildren(); }
|
|
68
92
|
function clearComposer() { ++preparationGeneration; recipient = null; recipientKinds=[]; draftRequest = null; $('response-kind-field').hidden=true; $('composer').hidden = true; $('message').value = ''; }
|
|
69
93
|
function errorText(error) { return [error.result && error.code ? error.code + ': ' + error.message : error.message, error.effects?.message, error.result ? JSON.stringify(error.result.structuredContent || error.result.content || error.result, null, 2) : ''].filter(Boolean).join('\n'); }
|
|
70
94
|
function failure(error) { $('notice').textContent = errorText(error); if (['CONTEXT_CHANGED', 'AUTH_REJECTED', 'NOT_AUTHENTICATED'].includes(error.code)) { snapshot = null; workspaceContext=null;operationCache=[];clearComposer(); ++deliveryGeneration; savedRequests = []; assetCache.clear(); $('deliveries').hidden = true; $('delivery-results').replaceChildren(); $('environment').value = ''; clearResults(); accountIndicator('Session needs refreshing','error'); } }
|
|
@@ -75,7 +99,8 @@ async function view(kind, args = {}, target = selection()) { return call('mainse
|
|
|
75
99
|
function stateLabel(request) { if(request.task) return 'Task · '+(request.task.status || (request.task.terminal ? 'finished' : 'accepted')); return { pending: 'Waiting for Codex', preparing: 'Preparing agent', sending: 'Waiting for reply', delivered: 'Reply received', failed: 'Could not send', uncertain: 'Delivery unknown · resend blocked', cancelled: 'Cancelled', expired: 'Expired' }[request.state] || request.state; }
|
|
76
100
|
function latestAgent(uid, env) { return [...savedRequests].reverse().find(r => r.agentUid === uid && r.environmentUid === env); }
|
|
77
101
|
function defaults(agent) { return [agent.defaults?.provider, agent.defaults?.model, agent.defaults?.thinking].filter(Boolean).join(' · '); }
|
|
78
|
-
function icon(uid, large = false) { const
|
|
102
|
+
function icon(uid, large = false) { const e = drawIcon(uid, large); if (!drawnIcons.has(uid)) drawnIcons.set(uid, new Set()); drawnIcons.get(uid).add({ el: e, large }); return e; }
|
|
103
|
+
function drawIcon(uid, large = false) { const asset = assetCache.get(uid), reduced = typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches; const source = asset && (reduced ? asset.stillSource : asset.source); if (source && /^data:image\/(png|webp|svg\+xml);base64,[A-Za-z0-9+/=]+$/.test(source)) { const e = node(asset.rendering === 'mask' ? 'span' : 'img', undefined, 'agent-icon' + (large ? ' large' : '') + (asset.rendering === 'mask' ? ' mask' : '')); if (asset.rendering === 'mask') e.style.setProperty('--agent-image', 'url("' + source + '")'); else { e.src = source; e.alt = ''; } e.style.imageRendering = asset.imageRendering; e.setAttribute('aria-hidden', 'true'); return e; } const e = node('span', '◇', 'agent-icon agent-placeholder' + (large ? ' large' : '')); e.setAttribute('aria-hidden', 'true'); return e; }
|
|
79
104
|
// A small Markdown subset; every content node is text. Remote HTML and links never execute.
|
|
80
105
|
function markdown(value) { const root = node('div', undefined, 'markdown'); let list; for (const line of String(value || '').split('\n')) { if (/^[-*] /.test(line)) { if (!list) { list = node('ul'); root.append(list); } list.append(node('li', line.slice(2))); } else { list = null; root.append(node(/^#{1,6} /.test(line) ? 'h3' : 'p', line.replace(/^#{1,6} /, ''))); } } return root; }
|
|
81
106
|
function responseKinds(kinds, selected) {
|
|
@@ -125,14 +150,14 @@ async function refresh() {
|
|
|
125
150
|
function pageControls(page) { currentPage = page; $('pager').hidden = page.count <= 20; $('page').textContent = page.semantic ? `${page.results.length} ranked results` : `${page.results.length ? offset + 1 : 0}–${offset + page.results.length} of ${page.count}`; }
|
|
126
151
|
function back(label, fn) { return button('← ' + label, fn, 'plain muted'); }
|
|
127
152
|
async function detail(load, draw) { const run = ++generation; busy = true; clearResults(); $('list-toolbar').hidden = true; $('notice').textContent = 'Loading…'; controls(); try { const data = await load(); if (run !== generation) return; $('notice').textContent = ''; draw(data, run); } catch (error) { if (run === generation) { failure(error); append($('results'),back(catalog[0].toUpperCase() + catalog.slice(1), () => discover(offset))); } } finally { if (run === generation) { busy = false; controls(); } } }
|
|
128
|
-
async function agentDetail(agent, target, run) { if (run !== generation) return; return detail(() => view('agent', { agentUid: agent.uid }, target), ({ agent: full }, detailRun) => { if(full.icon)assetCache.set(full.uid,full.icon);else assetCache.delete(full.uid); const output = $('results'); append(output,back('Agents', () => discover(offset)), group('detail-heading row', icon(full.uid, true), group('grow', node('h2', full.name), caption(defaults(full))), talkButton(full, target)), facts([['Environment', environmentName()], ['Branch', full.repositoryBranch], ['Deployment', full.runtimeState === 'unknown' ? 'Status unknown' : full.runtimeState]]), markdown(full.description), button('New conversation…',()=>review('New conversation with '+full.name,'Start a fresh agent session. Existing replies and saved requests remain in this Codex chat.',generation,async()=>{conversationKeys.set(full.uid,crypto.randomUUID());draftRequest=null;talk(full,target);})), node('h3','Skills')); for (const skill of full.skills || []) { const section = group('detail-section', node('h3', skill.name), markdown(skill.description)); for (const example of skill.examples || []) { section.append(button(example, () => { if (detailRun !== generation) return; talk(full, target); $('message').value = example; $('message').focus(); }, 'example')); } append(output,section); } append(output,details('Technical details', facts([['Agent ID', full.uid], ['Response types', (full.responseKinds || []).join(', ')], ['Runtime release', full.runtimeReleaseUid]]))); }); }
|
|
153
|
+
async function agentDetail(agent, target, run) { if (run !== generation) return; return detail(() => view('agent', { agentUid: agent.uid }, target), ({ agent: full }, detailRun) => { if(full.icon)assetCache.set(full.uid,full.icon);else assetCache.delete(full.uid); iconChecks = 0; watchIcons(detailRun, async () => { const again = (await view('agent', { agentUid: full.uid }, target)).agent; return [{ uid: again.uid, icon: again.icon ?? null }]; }); const output = $('results'); append(output,back('Agents', () => discover(offset)), group('detail-heading row', icon(full.uid, true), group('grow', node('h2', full.name), caption(defaults(full))), talkButton(full, target)), facts([['Environment', environmentName()], ['Branch', full.repositoryBranch], ['Deployment', full.runtimeState === 'unknown' ? 'Status unknown' : full.runtimeState]]), markdown(full.description), button('New conversation…',()=>review('New conversation with '+full.name,'Start a fresh agent session. Existing replies and saved requests remain in this Codex chat.',generation,async()=>{conversationKeys.set(full.uid,crypto.randomUUID());draftRequest=null;talk(full,target);})), node('h3','Skills')); for (const skill of full.skills || []) { const section = group('detail-section', node('h3', skill.name), markdown(skill.description)); for (const example of skill.examples || []) { section.append(button(example, () => { if (detailRun !== generation) return; talk(full, target); $('message').value = example; $('message').focus(); }, 'example')); } append(output,section); } append(output,details('Technical details', facts([['Agent ID', full.uid], ['Response types', (full.responseKinds || []).join(', ')], ['Runtime release', full.runtimeReleaseUid]]))); }); }
|
|
129
154
|
function review(title, description, run, action) { const box = group('review', node('h3', title), node('p', description), group('row')); const controls = box.children[2], cancel = button('Cancel', () => box.replaceChildren()); const confirm = button('Confirm', async () => { if (run !== generation) return; confirm.disabled = true; cancel.disabled = true; try { await action(); if (run === generation) $('confirmation').replaceChildren(); } catch (error) { if (run === generation) { box.append(node('pre', errorText(error))); cancel.disabled = false; } } }, 'primary'); controls.append(confirm, cancel); $('confirmation').replaceChildren(box); }
|
|
130
155
|
async function connection(item, target, run, connect) { if (run !== generation) return; review((connect ? 'Connect ' : 'Disconnect ') + item.name, (connect ? 'Make this MCP available to this account in ' : 'Remove the saved connection from ') + environmentName() + '. This updates the shared devtools connection list.', run, async () => { const data = await call('mainsequence_connection', { ...target, releaseUid: item.uid, action: connect ? 'connect' : 'disconnect' }); activity.push({ title: (connect ? 'Connected ' : 'Disconnected ') + item.name, summary: data.summary }); await discover(offset); }); }
|
|
131
156
|
function toolRow(item, target, run) { return group('item row', group('grow', button(item.name, () => item.saved ? toolDetail(item, target, run) : connection(item, target, run, true), 'name'), caption(item.saved ? (toolCache.has(target.contextId+item.uid) ? toolCache.get(target.contextId+item.uid).tools.length+' tools' : 'Connected') : item.repository || 'Available MCP')), button(item.saved ? 'Disconnect' : 'Connect', () => connection(item, target, run, !item.saved))); }
|
|
132
157
|
async function toolDetail(item, target, run) { if (run !== generation) return; return detail(async()=>{try{const data=await call('mainsequence_tools',{...target,releaseUid:item.uid});toolCache.set(target.contextId+item.uid,data);return data;}catch(error){const cached=toolCache.get(target.contextId+item.uid);if(!cached)throw error;return {...cached,catalogError:error.message};}}, (data, detailRun) => { const output = $('results'); append(output,back('Tools', () => { addingTools = false; return discover(0); }), group('detail-heading row between', group('grow', node('h2', item.name), caption(`Connected · ${data.tools.length} tools`)), button('Reload tools', () => toolDetail(item, target, generation)), button('Disconnect', () => connection(item, target, generation, false)))); if(data.catalogError) append(output,caption('Showing the previous catalog. Reload failed: '+data.catalogError)); if (!data.tools.length) append(output,empty('No tools exposed', 'Reload after the MCP publishes its catalog.')); for (const tool of data.tools) { const row = group('item', node('h3', tool.name), markdown(tool.description)); const input = node('textarea'); input.setAttribute('aria-label', 'JSON arguments for ' + tool.name); input.value = '{}'; const result = node('div'); const advanced = details('Schema and execution', group('stack', node('pre', JSON.stringify(tool.inputSchema, null, 2)), caption('Arguments (JSON)'), input, button('Review call', () => { let args; try { args = JSON.parse(input.value); if (!args || typeof args !== 'object' || Array.isArray(args)) throw Error(); } catch { result.replaceChildren(caption('Enter a JSON object matching the schema.')); return; } review('Run ' + tool.name + '?', item.name + ' · ' + environmentName() + '\n' + JSON.stringify(args, null, 2), detailRun, async () => { const response = await call('mainsequence_call_tool', { ...target, releaseUid: item.uid, tool: tool.name, arguments: args }); activity.push({ title: tool.name, summary: 'Tool call completed' }); if (detailRun === generation) result.replaceChildren(node('pre', JSON.stringify(response.result, null, 2))); }); }), result)); row.append(advanced); append(output,row); } }); }
|
|
133
158
|
function renderHome() { const a = snapshot?.account; const output = $('results'); if (!a?.authenticated || !$('environment').value) append(output,card('Next steps', node('h3', a?.authenticated ? 'Choose an environment' : 'Sign in to MainSequence'), caption(a?.authenticated ? 'Use the environment selector above to browse your projects, agents and tools.' : 'Use /ms login in Codex to restore the shared session.'))); append(output,card('This session', facts([['Account', a?.username], ['Environment', environmentName()], ['Backend', a?.backend]]), caption('Your MainSequence agent replies are saved in this native Codex conversation.'), group('row wrap', button('Talk to an agent', () => tab('agents')), button('Connected tools', () => tab('tools'))))); if(snapshot?.contextId && $('environment').value) {const session=card('Local workspace',caption('Open a folder to inspect its branch, SDK and repository status.'),button('Open in Code',()=>tab('code')),button('All deployments',()=>deployments()));append(output,session);} const recent = savedRequests.slice(-3).reverse(); append(output,card('Recent', ...(recent.length ? recent.map(r => group('item', node('h3', r.agentName), caption(stateLabel(r)))) : [caption('No agent messages in this chat yet.')]), button('See all activity', () => tab('activity')))); }
|
|
134
159
|
function renderActivity() { const output = $('results'); append(output,group('row between', node('h2', 'Activity'), button('Refresh messages', async () => { await loadDeliveries(); if (catalog === 'activity') { $('results').replaceChildren(); renderActivity(); } }))); for (const request of [...savedRequests].reverse()) append(output,group('item', group('row between', node('h3', request.agentName), node('small', stateLabel(request))), caption(snapshot?.environments.find(e => e.uid === request.environmentUid)?.name || ''), request.receipt && node('p', request.receipt.message, 'receipt'), request.task && group('row wrap',button('Continue task',()=>continueTask(request)),button('Refresh task',async()=>{await call('mainsequence_agent_task',{action:'get',requestId:request.requestId});await loadDeliveries();discover();}),!request.task.terminal && button('Cancel task',()=>review('Cancel agent task','Request cancellation of this accepted task. Its saved request will not be sent again.',generation,async()=>{await call('mainsequence_agent_task',{action:'cancel',requestId:request.requestId});await loadDeliveries();discover();}))), request.reason && caption(request.reason), details('Message details', group('stack', node('p', request.message), caption('Request ' + request.requestId + ' · Dispatches ' + request.sendAttempts))), button(request.state === 'delivered' ? 'Show reply in Codex' : 'View recovery', () => { $('deliveries').open = true; $('deliveries').scrollIntoView?.({ block: 'nearest' }); }))); for(const op of [...operationCache].reverse()) append(output,operationRow(op)); for (const item of [...activity].reverse()) append(output,group('item', node('h3', item.title), caption(item.summary))); if (!savedRequests.length && !activity.length) append(output,empty('No activity yet', 'Agent messages and MCP connection changes appear here.')); }
|
|
135
|
-
async function discover(nextOffset = 0, fresh = false) { const run = ++generation; offset = nextOffset; clearResults(); busy = false; $('list-toolbar').hidden = !['agents', 'projects', 'code'].includes(catalog) && !(catalog === 'tools' && addingTools); $('notice').textContent = ''; controls(); if (catalog === 'home') { renderHome(); return; } if (catalog === 'activity') { try { await operations(); } catch(e) { failure(e); } renderActivity(); return; } if (catalog === 'settings' && !$('environment').value) { await renderSettings(null, run); return; } if (!snapshot?.contextId || !$('environment').value) { $('notice').textContent = snapshot?.contextId ? 'Choose an environment to continue.' : 'Refresh or sign in to continue.'; return; } const target = selection(); busy = true; controls(); $('notice').textContent = 'Loading…'; try { if (catalog === 'settings') await renderSettings(target, run); else if (catalog === 'code') { await renderCode(target,run); } else if (catalog === 'projects') { const page = await view('projects', { search: $('search').value, offset }, target); if (run !== generation) return; pageControls(page); if (!page.results.length) append($('results'),empty('No projects found', 'Try another search or environment.')); for (const project of page.results) append($('results'),group('item row', group('grow', button(project.name, () => projectDetail(project, target, run), 'name'), caption(project.description?.split('\n')[0])), caption((project.code_repository_branch_uids || []).length + ' branches'))); } else if (catalog === 'tools' && !addingTools) { const page = await view('connections', {}, target); if (run !== generation) return; append($('results'),group('row between', node('h2', 'Connected in ' + environmentName()), button('Add MCP', () => { addingTools = true; return discover(0); }))); if (!page.results.length) append($('results'),empty('No connected MCPs', 'Add an MCP to make its tools available in Codex.')); for (const item of page.results) append($('results'),toolRow(item, target, run)); } else { const page = await call('mainsequence_discover', { ...target, catalog, search: $('search').value, offset, ...(fresh ? {refresh:true} : {}) }); if (run !== generation) return; pageControls(page); if (catalog === 'tools') append($('results'),back('Tools', () => { addingTools = false; return discover(0); })); if (!page.results.length) append($('results'),empty(catalog === 'agents' ? 'No agents found' : 'No MCPs found', 'Try another search or environment.')); if(catalog==='agents') append($('results'),caption(page.semantic?page.results.length+' matching agents':page.count+' agents · showing '+(page.results.length?offset+1:0)+'–'+(offset+page.results.length))); const rows = group('stack'); rows.style.gap = '0'; append($('results'),rows); for (const item of page.results) rows.append(catalog === 'agents' ? agentRow(item, target, run) : toolRow(item, target, run)); } if (run === generation) $('notice').textContent = ''; } catch (error) { if (run === generation) failure(error); } finally { if (run === generation) { busy = false; controls(); } } }
|
|
160
|
+
async function discover(nextOffset = 0, fresh = false) { const run = ++generation; offset = nextOffset; clearResults(); busy = false; $('list-toolbar').hidden = !['agents', 'projects', 'code'].includes(catalog) && !(catalog === 'tools' && addingTools); $('notice').textContent = ''; controls(); if (catalog === 'home') { renderHome(); return; } if (catalog === 'activity') { try { await operations(); } catch(e) { failure(e); } renderActivity(); return; } if (catalog === 'settings' && !$('environment').value) { await renderSettings(null, run); return; } if (!snapshot?.contextId || !$('environment').value) { $('notice').textContent = snapshot?.contextId ? 'Choose an environment to continue.' : 'Refresh or sign in to continue.'; return; } const target = selection(); busy = true; controls(); $('notice').textContent = 'Loading…'; try { if (catalog === 'settings') await renderSettings(target, run); else if (catalog === 'code') { await renderCode(target,run); } else if (catalog === 'projects') { const page = await view('projects', { search: $('search').value, offset }, target); if (run !== generation) return; pageControls(page); if (!page.results.length) append($('results'),empty('No projects found', 'Try another search or environment.')); for (const project of page.results) append($('results'),group('item row', group('grow', button(project.name, () => projectDetail(project, target, run), 'name'), caption(project.description?.split('\n')[0])), caption((project.code_repository_branch_uids || []).length + ' branches'))); } else if (catalog === 'tools' && !addingTools) { const page = await view('connections', {}, target); if (run !== generation) return; append($('results'),group('row between', node('h2', 'Connected in ' + environmentName()), button('Add MCP', () => { addingTools = true; return discover(0); }))); if (!page.results.length) append($('results'),empty('No connected MCPs', 'Add an MCP to make its tools available in Codex.')); for (const item of page.results) append($('results'),toolRow(item, target, run)); } else { const page = await call('mainsequence_discover', { ...target, catalog, search: $('search').value, offset, ...(fresh ? {refresh:true} : {}) }); if (run !== generation) return; pageControls(page); if (catalog === 'tools') append($('results'),back('Tools', () => { addingTools = false; return discover(0); })); if (!page.results.length) append($('results'),empty(catalog === 'agents' ? 'No agents found' : 'No MCPs found', 'Try another search or environment.')); if(catalog==='agents') append($('results'),caption(page.semantic?page.results.length+' matching agents':page.count+' agents · showing '+(page.results.length?offset+1:0)+'–'+(offset+page.results.length))); const rows = group('stack'); rows.style.gap = '0'; append($('results'),rows); for (const item of page.results) rows.append(catalog === 'agents' ? agentRow(item, target, run) : toolRow(item, target, run)); if (catalog === 'agents') { iconChecks = 0; watchIcons(run, async () => (await call('mainsequence_discover', { ...target, catalog: 'agents', search: $('search').value, offset })).results.map(a => ({ uid: a.uid, icon: a.icon ?? null }))); } } if (run === generation) $('notice').textContent = ''; } catch (error) { if (run === generation) failure(error); } finally { if (run === generation) { busy = false; controls(); } } }
|
|
136
161
|
function tab(next, discoverView=true) { catalog = next; addingTools = false; $('search').value = ''; $('search').placeholder = 'Search ' + ({ code: 'repositories', tools: 'MCPs' }[next] || next); $('search').setAttribute('aria-label', $('search').placeholder); for (const key of sections) { $(key + '-tab').setAttribute('aria-selected', String(key === next)); $(key + '-tab').setAttribute('tabindex', ['activity','settings'].includes(key) || key === next ? '0' : '-1'); } $('directory').setAttribute('aria-labelledby', next + '-tab'); return discoverView ? discover(0) : undefined; }
|
|
137
162
|
$('refresh').onclick = () => snapshot?.contextId ? discover(offset, true) : refresh(); $('environment').onchange = () => { rememberEnvironment();clearComposer(); workspaceContext=null; assetCache.clear(); addingTools = false; $('search').value = ''; return discover(0); }; $('search-form').onsubmit = e => { e.preventDefault(); return discover(0); }; $('previous').onclick = () => discover(Math.max(0, offset - 20)); $('next').onclick = () => discover(offset + 20); $('switch-agent').onclick = () => tab('agents');
|
|
138
163
|
for (const key of sections) { $(key + '-tab').onclick = () => tab(key); $(key + '-tab').onkeydown = e => { if (['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) { e.preventDefault(); const next = e.key === 'Home' ? sections[0] : e.key === 'End' ? sections.at(-1) : sections[(sections.indexOf(key) + (e.key === 'ArrowRight' ? 1 : sections.length - 1)) % sections.length]; $(next + '-tab').focus(); void tab(next); } }; }
|
|
@@ -246,7 +271,7 @@ $('send-message').onclick = () => sendMessage(true); $('draft-message').onclick
|
|
|
246
271
|
$('close-composer').onclick = () => { clearComposer(); controls(); };
|
|
247
272
|
$('refresh-deliveries').onclick = loadDeliveries;
|
|
248
273
|
|
|
249
|
-
function teardown() { ++deliveryGeneration; ++generation; connected = false; for (const p of pending.values()) { clearTimeout(p.timeout); p.reject(new Error('The panel closed.')); } pending.clear(); controls(); }
|
|
274
|
+
function teardown() { ++deliveryGeneration; ++generation; connected = false; clearTimeout(iconWatch); for (const p of pending.values()) { clearTimeout(p.timeout); p.reject(new Error('The panel closed.')); } pending.clear(); controls(); }
|
|
250
275
|
addEventListener('message', event => {
|
|
251
276
|
if (event.source !== parent || event.data?.jsonrpc !== '2.0') return;
|
|
252
277
|
const message = event.data, p = pending.get(message.id);
|
|
@@ -419,8 +419,8 @@ without any image-fetch fallback. The legacy `view: assets` request remains a
|
|
|
419
419
|
network-free no-op for old panel HTML; reopen the current panel to render icons.
|
|
420
420
|
|
|
421
421
|
The shared devtools transport still coalesces the combined response across chat
|
|
422
|
-
processes. There is no periodic agent/icon refresh. Explicit Refresh
|
|
423
|
-
fresh directory. No change is made to the shared login or Claude.
|
|
422
|
+
processes. There is no periodic agent/icon refresh (until 0.7.10, below). Explicit Refresh
|
|
423
|
+
requests a fresh directory. No change is made to the shared login or Claude.
|
|
424
424
|
|
|
425
425
|
## Direct project tools, timeouts and distinct refusals — 0.7.8
|
|
426
426
|
|
|
@@ -520,3 +520,55 @@ with bounded paging and filters.`). `tools/call ms_crm_contacts_list_68ee6e71` w
|
|
|
520
520
|
the same release's tools from the same shared file under the `127.0.0.1:8000` names. The record
|
|
521
521
|
is the local `artifacts/live-acceptance-0.7.9.json`. The pre-backend listing of every scope is a
|
|
522
522
|
devtools follow-up: a fresh server's first list could be scoped to the configured backend.
|
|
523
|
+
|
|
524
|
+
Published on 2026-10-23 (UTC) from tag `v0.7.9` on main `9cddf65`, the merge of the acceptance
|
|
525
|
+
record above, by the release workflow (run 40032861384: `npm ci` from the registry devtools
|
|
526
|
+
archive, `release:check`, `npm publish --provenance` through trusted publishing). The registry
|
|
527
|
+
serves `@dev-mainsequence/codex-plugin@0.7.9` as `latest`, 70 files, integrity
|
|
528
|
+
`sha512-5EmHyWdb3V+DB8EJmKsfK/P8bvMP+2DO04ntwuuO7EqDwbIVo5DNCOJctJJAiQlHqdVKNgbFSoEMUKjHKKL0iA==`,
|
|
529
|
+
read from the registry after publishing (a local `npm pack` of the same files has another
|
|
530
|
+
integrity). The devtools marketplace entry was advanced from 0.4.2 to 0.7.9
|
|
531
|
+
(mainsequence-sdk/mainsequence-devtools#20, main `18cbfc8`). In an isolated `CODEX_HOME` with
|
|
532
|
+
the Codex CLI 0.163.0, `codex plugin marketplace add
|
|
533
|
+
https://github.com/mainsequence-sdk/mainsequence-devtools.git`, `codex plugin marketplace upgrade
|
|
534
|
+
mainsequence` and `codex plugin add mainsequence@mainsequence` installed `mainsequence@mainsequence`
|
|
535
|
+
0.7.9 from the npm package into `plugins/cache/mainsequence/mainsequence/0.7.9`, with the
|
|
536
|
+
manifest, `dist/panel-server.js`, `dist/runner.js`, hooks, skills and `.mcp.json` in place and
|
|
537
|
+
`build-info.json` naming devtools 0.3.12 by its registry integrity; `codex plugin list` reported
|
|
538
|
+
it installed and enabled. The owner's own Codex home (local `mainsequence-local` marketplace,
|
|
539
|
+
0.7.8 installed) was not touched; upgrading it is a user step: `codex plugin marketplace upgrade
|
|
540
|
+
mainsequence`, `codex plugin add mainsequence@mainsequence`, then review the hook and start a new
|
|
541
|
+
chat.
|
|
542
|
+
|
|
543
|
+
## Agent icons follow Command Center — 0.7.10
|
|
544
|
+
|
|
545
|
+
An icon changed in Command Center reached an open panel only after Refresh or a new app card:
|
|
546
|
+
the Agents list drew an agent's inline icon when it listed agents and nothing re-read the
|
|
547
|
+
directory while the list stayed on screen. (The Claude Desktop plugin had the same gap and fixed
|
|
548
|
+
it in its 0.4.20 with the per-agent icon route and entity tags; the Codex panel takes icons
|
|
549
|
+
inline from the directory, so it re-reads that instead.)
|
|
550
|
+
|
|
551
|
+
- While the Agents list or an agent's details stay on screen, the panel re-reads what it shows
|
|
552
|
+
once a minute: the directory page (`mainsequence_discover`, same search and offset) for the
|
|
553
|
+
list, the agent view for the details. The read carries no `refresh` flag: the shared
|
|
554
|
+
transport keeps a directory read for 30 s, so a read a minute later is fresh without
|
|
555
|
+
invalidating other panels' caches, unlike the Refresh button.
|
|
556
|
+
- Only a changed icon is redrawn, in place: the list, drafts and scroll position stay. A
|
|
557
|
+
removed icon shows the placeholder; a failed re-read keeps the drawn icons and tries again at
|
|
558
|
+
the next minute.
|
|
559
|
+
- The timer ends with the screen (another section, a detail or list change, environment
|
|
560
|
+
change, teardown) and pauses after 240 re-reads until the screen is opened again. Nothing is
|
|
561
|
+
stored; the inline icons live in the panel's memory as before.
|
|
562
|
+
|
|
563
|
+
Verification on 2026-10-24: `npm run release:check` from the registry devtools 0.3.12 archive,
|
|
564
|
+
56 tests (new: the panel UI test drives the minute timer through the vm harness and checks an
|
|
565
|
+
unchanged, changed, removed, restored and failed re-read, the detail view's re-read, the timer
|
|
566
|
+
ending on another section and on teardown, and that the Refresh button alone sends `refresh`),
|
|
567
|
+
release metadata, the standalone and npm archives, the npm archive exercised as Codex installs
|
|
568
|
+
it. Live acceptance with the built server over stdio and the server-stamped panel page driven
|
|
569
|
+
in a minimal DOM (Production, seven agents, the shared machine session): the list drew seven
|
|
570
|
+
icons; two minute re-reads each read the directory page without `refresh` (14–17 s per read
|
|
571
|
+
against the local backend, in the background), found no change and redrew nothing; the timer
|
|
572
|
+
ended when Tools was shown, came back on Agents and ended on teardown. No icon was changed in
|
|
573
|
+
Command Center during the run; the changed, removed and failed paths are the UI test's.
|
|
574
|
+
Publication records for 0.7.10 follow below once released.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev-mainsequence/codex-plugin",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.10",
|
|
4
4
|
"description": "MainSequence for Codex: durable agent messaging in native chat, Agents and Tools discovery, developer commands and conversational MCPs",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|