diff --git a/package.json b/package.json
index bd07f17..448abfe 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "amerc-site",
- "version": "1.1.0-chat",
+ "version": "1.2.0-legioncards",
"private": true,
"type": "module",
"scripts": {
diff --git a/server/amerc-api.mjs b/server/amerc-api.mjs
index 57a679b..c8efa9a 100644
--- a/server/amerc-api.mjs
+++ b/server/amerc-api.mjs
@@ -76,6 +76,12 @@ db.exec(`
try { db.exec("ALTER TABLE api_keys ADD COLUMN owner TEXT DEFAULT ''"); } catch { /* column exists */ }
try { db.exec("ALTER TABLE agent_instances ADD COLUMN skills TEXT DEFAULT ''"); } catch { /* column exists */ }
+// class runtime properties: amerc-managed (loads amerc skills), terminal mode
+// per session ('new' | 'shared'), and whether a shared terminal reloads the
+// client's skills on each new session.
+try { db.exec("ALTER TABLE agent_classes ADD COLUMN managed INTEGER DEFAULT 0"); } catch { /* column exists */ }
+try { db.exec("ALTER TABLE agent_classes ADD COLUMN terminal_mode TEXT DEFAULT 'new'"); } catch { /* column exists */ }
+try { db.exec("ALTER TABLE agent_classes ADD COLUMN skills_reload INTEGER DEFAULT 1"); } catch { /* column exists */ }
// ---- live chat plumbing (in-memory; survives nothing, stores nothing) -------
// waiters: long-poll watchers per session, woken when a message lands.
@@ -464,8 +470,9 @@ const server = http.createServer(async (req, res) => {
const tags = Array.isArray(b.tags) ? b.tags.join(',') : (b.tags || '');
let slug = slugify(b.slug || b.name); let n = 1;
while (db.prepare('SELECT id FROM agent_classes WHERE slug=?').get(slug)) slug = `${slugify(b.name)}-${++n}`;
- const info = db.prepare('INSERT INTO agent_classes(slug,name,owner,tags,description,kind,command,visibility,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?)')
- .run(slug, b.name || 'Agent', who(id), tags, b.description || '', b.kind || 'codex', b.command || '', b.visibility === 'private' ? 'private' : 'public', now, now);
+ const info = db.prepare('INSERT INTO agent_classes(slug,name,owner,tags,description,kind,command,visibility,managed,terminal_mode,skills_reload,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)')
+ .run(slug, b.name || 'Agent', who(id), tags, b.description || '', b.kind || 'codex', b.command || '', b.visibility === 'private' ? 'private' : 'public',
+ b.managed ? 1 : 0, b.terminalMode === 'shared' ? 'shared' : 'new', b.skillsReload === false ? 0 : 1, now, now);
return send(res, 200, { ok: true, id: Number(info.lastInsertRowid), slug });
}
if ((m = path.match(/^\/agents\/classes\/(\d+)$/)) && method === 'GET') {
@@ -481,8 +488,10 @@ const server = http.createServer(async (req, res) => {
if (!owns(c)) return send(res, 403, { ok: false, error: 'not owner' });
if (method === 'DELETE') { db.prepare('DELETE FROM agent_classes WHERE id=?').run(c.id); db.prepare('DELETE FROM agent_instances WHERE class_id=?').run(c.id); return send(res, 200, { ok: true }); }
const b = await readBody(req) || {}; const tags = Array.isArray(b.tags) ? b.tags.join(',') : (b.tags ?? c.tags);
- db.prepare('UPDATE agent_classes SET name=?,tags=?,description=?,kind=?,command=?,visibility=?,updated_at=? WHERE id=?')
- .run(b.name ?? c.name, tags, b.description ?? c.description, b.kind ?? c.kind, b.command ?? c.command, b.visibility ?? c.visibility, Date.now(), c.id);
+ db.prepare('UPDATE agent_classes SET name=?,tags=?,description=?,kind=?,command=?,visibility=?,managed=?,terminal_mode=?,skills_reload=?,updated_at=? WHERE id=?')
+ .run(b.name ?? c.name, tags, b.description ?? c.description, b.kind ?? c.kind, b.command ?? c.command, b.visibility ?? c.visibility,
+ b.managed != null ? (b.managed ? 1 : 0) : c.managed, b.terminalMode != null ? (b.terminalMode === 'shared' ? 'shared' : 'new') : c.terminal_mode,
+ b.skillsReload != null ? (b.skillsReload ? 1 : 0) : c.skills_reload, Date.now(), c.id);
return send(res, 200, { ok: true });
}
// subscription token for a class
diff --git a/src/AgentPlatform.jsx b/src/AgentPlatform.jsx
index 27ab2a3..4f2730a 100644
--- a/src/AgentPlatform.jsx
+++ b/src/AgentPlatform.jsx
@@ -8,6 +8,24 @@ const TAG_EMOJI = { backend: '๐ ๏ธ', model: '๐ง ', frontend: '๐จ', ui: '
const classEmoji = (c) => { for (const t of (c.tags || [])) if (TAG_EMOJI[t]) return TAG_EMOJI[t]; return '๐ก๏ธ'; };
function avatarStyle(name) { let h = 0; for (const ch of String(name)) h = (h * 31 + ch.charCodeAt(0)) >>> 0; const a = h % 360, b = (a + 70 + h % 90) % 360; return { background: `linear-gradient(135deg, hsl(${a} 68% 46%), hsl(${b} 64% 32%))` }; }
+// One agent card, used identically in Browse Agents and My Legion.
+export function AgentCard({ c, onClick }) {
+ return (
+
+ );
+}
+
// ---- Browse Agents: all public classes, filter by tag --------------------
export function AgentBrowse({ auth, setRoute }) {
const [classes, setClasses] = useState([]);
@@ -82,20 +100,7 @@ export function AgentBrowse({ auth, setRoute }) {
))}
- {loaded && classes.map((c) => (
-
- ))}
+ {loaded && classes.map((c) => openClass(c)} />)}
{loaded && classes.length > 0 && (