rewritable 0.12.0 → 0.13.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rewritable",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "CLI for re-writeable: emit and import single-file rwa documents.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2322,6 +2322,18 @@ async function renderActionsModePanel(panel) {
2322
2322
  const skillRows = skillAffs.length ? skillAffs.map(a =>
2323
2323
  '<div class="rwa-mode-row"><div><div class="rwa-mode-title">' + escRuntimeHtml(a.name) + '</div><div class="rwa-mode-meta">' + escRuntimeHtml(a.kind || 'skill') + ' · ' + escRuntimeHtml(a.skillId) + '</div></div><button type="button" data-action-skill="' + escRuntimeHtml(a.skillId) + '">Open in Skills</button></div>'
2324
2324
  ).join('') : '<div class="rwa-mode-empty">No installed skills.</div>';
2325
+ // Intelligences (rwa-agent/1 roles) — list + activate/deactivate. Activating offers the role's
2326
+ // recommended model (intelligence/0.2 I-A). A verified role can be activated; unverified cannot.
2327
+ const agentList = (typeof runtimeListAgents === 'function') ? runtimeListAgents() : [];
2328
+ const activeRole = (runtimeAgentActive() || {}).role || null;
2329
+ const agentRows = agentList.length ? agentList.map(a => {
2330
+ const isActive = a.role === activeRole;
2331
+ const meta = (a.verified ? 'verified' : 'unverified') + (isActive ? ' · active' : '');
2332
+ const btn = !a.verified ? '' : (isActive
2333
+ ? '<button type="button" data-agent-off="1">Deactivate</button>'
2334
+ : '<button type="button" data-agent-on="' + escRuntimeHtml(a.role) + '">Activate</button>');
2335
+ return '<div class="rwa-mode-row"><div><div class="rwa-mode-title">' + escRuntimeHtml(a.role) + '</div><div class="rwa-mode-meta">' + escRuntimeHtml('intelligence · ' + meta) + '</div></div>' + btn + '</div>';
2336
+ }).join('') : '<div class="rwa-mode-empty">No intelligences installed.</div>';
2325
2337
  panel.innerHTML = [
2326
2338
  '<div class="rwa-mode-section">',
2327
2339
  '<div class="rwa-mode-kicker">Activity</div>',
@@ -2336,6 +2348,7 @@ async function renderActionsModePanel(panel) {
2336
2348
  '<div class="rwa-mode-section"><div class="rwa-mode-kicker">Recent runs</div>' + histRows + '</div>',
2337
2349
  '<div class="rwa-mode-section"><div class="rwa-mode-kicker">Live affordances</div>' + affRows + '</div>',
2338
2350
  '<div class="rwa-mode-section"><div class="rwa-mode-kicker">Installed skill actions</div>' + skillRows + '</div>',
2351
+ '<div class="rwa-mode-section"><div class="rwa-mode-kicker">Intelligences</div>' + agentRows + '</div>',
2339
2352
  ].join('');
2340
2353
  const undoBtn = panel.querySelector('#rwa-actions-undo');
2341
2354
  if (undoBtn) undoBtn.addEventListener('click', () => runtimeUndo());
@@ -2352,6 +2365,13 @@ async function renderActionsModePanel(panel) {
2352
2365
  panel.querySelectorAll('[data-action-skill]').forEach(btn => {
2353
2366
  btn.addEventListener('click', () => runtimeSetMode('skills'));
2354
2367
  });
2368
+ panel.querySelectorAll('[data-agent-on]').forEach(btn => btn.addEventListener('click', () => {
2369
+ try { runtimeActivateAgent(btn.getAttribute('data-agent-on')); } // setActive + offer recommended model
2370
+ catch (e) { if (typeof setStatus === 'function') setStatus('err', '✗ ' + (e && (e.code || e.message))); }
2371
+ renderActionsModePanel(panel);
2372
+ }));
2373
+ const agentOff = panel.querySelector('[data-agent-off]');
2374
+ if (agentOff) agentOff.addEventListener('click', () => { runtimeSetActiveAgent(null); renderActionsModePanel(panel); });
2355
2375
  }
2356
2376
 
2357
2377
  // ─── Agent (rwa-edit/1) ─────────────────────────────────────────────
@@ -8083,6 +8103,65 @@ window.__rwaClassifyInstallText = classifyInstallText;
8083
8103
  window.__rwaInstallFromText = routeInstallFromText;
8084
8104
  window.__rwaHandleCarrierDrop = handleCarrierDrop;
8085
8105
 
8106
+ // ── intelligence/0.2 I-A (docs/specs/rwa-intelligence-spec.md §6) — recommended model on activation.
8107
+ // A carrier may carry a NON-SECRET recommended_model / recommended_backend on its rwa-agent/1
8108
+ // ENVELOPE (OUTSIDE the signed `agent`, so canonicalAgent is unchanged and the signature still
8109
+ // verifies — this stays seed-only). On activation the runtime OFFERS to apply it to sessionStorage
8110
+ // behind a one-line consent: it never auto-applies, never sets a base-URL, never touches the API
8111
+ // key. A recommendation is a suggestion, not a stored credential (key/model are sessionStorage-only).
8112
+ const REC_MODEL_RE = /^[A-Za-z0-9._:\/-]{1,200}$/; // a model id: provider/name, dots, colon (ollama), hyphen
8113
+ const REC_BACKENDS = ['openrouter', 'ollama', 'lmstudio', 'atomic', 'bridge', 'bridge-session'];
8114
+ function getRecommendation(envelope) {
8115
+ const e = envelope || {};
8116
+ const out = {};
8117
+ if (typeof e.recommended_model === 'string' && REC_MODEL_RE.test(e.recommended_model.trim())) out.model = e.recommended_model.trim();
8118
+ if (typeof e.recommended_backend === 'string' && REC_BACKENDS.includes(e.recommended_backend.trim())) out.backend = e.recommended_backend.trim();
8119
+ return (out.model || out.backend) ? out : null;
8120
+ }
8121
+ function applyRecommendation(rec) {
8122
+ const r = rec || {};
8123
+ const applied = {};
8124
+ if (r.model && REC_MODEL_RE.test(String(r.model))) { sessionStorage.setItem(RWA.K_MODEL, String(r.model)); applied.model = String(r.model); }
8125
+ if (r.backend && REC_BACKENDS.includes(String(r.backend))) { sessionStorage.setItem(RWA.K_BACKEND, String(r.backend)); applied.backend = String(r.backend); }
8126
+ try { const m = document.getElementById('rwa-model'); if (m && applied.model) m.value = applied.model; const b = document.getElementById('rwa-backend'); if (b && applied.backend) { b.value = applied.backend; b.dispatchEvent(new Event('change')); } } catch (_) {}
8127
+ return applied;
8128
+ }
8129
+ // Offer the role's recommended model, if any and if it differs from the current session. Fire-and-
8130
+ // forget consent dialog; resolves with the choice. Activation already happened — this only proposes.
8131
+ function offerRecommendedModel(role) {
8132
+ const recRec = Array.from(installedAgents.values()).find(a => a.role === role);
8133
+ const rec = recRec && getRecommendation(recRec.envelope);
8134
+ if (!rec) return Promise.resolve({ offered: false });
8135
+ const curModel = sessionStorage.getItem(RWA.K_MODEL) || RWA.MODEL;
8136
+ const curBackend = sessionStorage.getItem(RWA.K_BACKEND) || 'openrouter';
8137
+ if ((!rec.model || rec.model === curModel) && (!rec.backend || rec.backend === curBackend)) return Promise.resolve({ offered: false });
8138
+ return new Promise(resolve => {
8139
+ const prev = document.getElementById('rwa-model-offer'); if (prev) prev.remove();
8140
+ const overlay = document.createElement('div'); overlay.id = 'rwa-model-offer';
8141
+ overlay.style.cssText = 'position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;';
8142
+ const card = document.createElement('div');
8143
+ card.style.cssText = 'background:#fff;max-width:480px;width:92%;border-radius:18px;padding:24px 26px;font:14px/1.5 var(--font-ui,system-ui);box-shadow:0 12px 48px rgba(0,0,0,.28);';
8144
+ const what = [rec.model ? 'model <code>' + _skEsc(rec.model) + '</code>' : '', rec.backend ? 'backend <code>' + _skEsc(rec.backend) + '</code>' : ''].filter(Boolean).join(' on ');
8145
+ card.innerHTML =
8146
+ '<h2 style="margin:0 0 .4em;font-size:1.15rem">Use this intelligence’s recommended ' + (rec.model ? 'model' : 'backend') + '?</h2>' +
8147
+ '<p style="margin:.2em 0;color:#444">The <strong>' + _skEsc(role) + '</strong> role suggests ' + what + ' for ⌘K. This is a <em>recommendation</em> (not part of the signed role) — applying it changes only your session’s model/backend selection. Your API key is untouched.</p>' +
8148
+ '<div style="display:flex;gap:10px;margin-top:1.2em;justify-content:flex-end">' +
8149
+ '<button data-act="keep" style="padding:9px 16px;border:1px solid #ccc;border-radius:10px;background:#fff;cursor:pointer">Keep current</button>' +
8150
+ '<button data-act="apply" style="padding:9px 16px;border:none;border-radius:10px;background:var(--gray-900,#111);color:#fff;cursor:pointer">Use it</button>' +
8151
+ '</div>';
8152
+ overlay.appendChild(card); document.body.appendChild(overlay);
8153
+ const close = (choice) => { overlay.remove(); resolve(choice); };
8154
+ card.querySelector('[data-act=keep]').onclick = () => close({ offered: true, applied: null });
8155
+ card.querySelector('[data-act=apply]').onclick = () => close({ offered: true, applied: applyRecommendation(rec) });
8156
+ });
8157
+ }
8158
+ // Activate a role AND offer its recommended model — the "on activation" entry the UI calls.
8159
+ function runtimeActivateAgent(role) { runtimeSetActiveAgent(role); return offerRecommendedModel(role); }
8160
+ window.__rwaGetRecommendation = getRecommendation;
8161
+ window.__rwaApplyRecommendation = applyRecommendation;
8162
+ window.__rwaOfferRecommendedModel = offerRecommendedModel;
8163
+ window.__rwaActivateAgent = runtimeActivateAgent;
8164
+
8086
8165
  // §4/§5a — does a network: host pattern admit a host? Mirror of cli/src/skill-manifest.mjs
8087
8166
  // matchNetworkOrigin (keep in step). The bridge's per-call origin check.
8088
8167
  function _skMatchNetworkOrigin(pattern, host) {
@@ -9615,7 +9694,7 @@ document.addEventListener('keydown', e => {
9615
9694
  discoverSkills: runtimeDiscoverSkills, // v0.9 §11 (I6) — GET the marketplace index (opt-in network)
9616
9695
  fetchSkillFromIndex: runtimeFetchSkillFromIndex, // v0.9 §11 (I6) — fetch + client-side-verify an indexed skill
9617
9696
  vault: { get: runtimeVaultGet, set: runtimeVaultSet, has: runtimeVaultHas, namespaces: runtimeVaultNamespaces, unlock: runtimeVaultUnlock, lock: runtimeVaultLock, isLocked: runtimeVaultIsLocked, export: runtimeVaultExport, import: runtimeVaultImport }, // v0.8 §6 + v0.9 §14 (I13) portable export/import
9618
- agents: { list: runtimeListAgents, active: runtimeAgentActive, setActive: runtimeSetActiveAgent, install: runtimeInstallAgent, uninstall: runtimeUninstallAgent, message: runtimeAgentMessage, showInstallDialog: showAgentInstallDialog }, // v0.9 §12 — multi-agent roles
9697
+ agents: { list: runtimeListAgents, active: runtimeAgentActive, setActive: runtimeSetActiveAgent, activate: runtimeActivateAgent, install: runtimeInstallAgent, uninstall: runtimeUninstallAgent, message: runtimeAgentMessage, showInstallDialog: showAgentInstallDialog, offerModel: offerRecommendedModel }, // v0.9 §12 — multi-agent roles; `activate` = setActive + offer recommended model (intelligence/0.2 I-A)
9619
9698
  hookLog: runtimeHookLog, // v0.9 §9 — the hook audit trail (rwa_hook_log)
9620
9699
  };
9621
9700
  // `status` is a getter so each read returns a fresh snapshot of