aimeat 1.32.1 → 1.33.2

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.
Files changed (208) hide show
  1. package/dist/locales/en.json +359 -2
  2. package/dist/locales/fi.json +359 -2
  3. package/dist/public/css/views/admin.css +10 -0
  4. package/dist/public/css/views/agents-detail.css +8 -0
  5. package/dist/public/css/views/my-company.css +41 -0
  6. package/dist/public/css/views/profile.css +7 -1
  7. package/dist/public/css/views/secretary.css +482 -1
  8. package/dist/public/js/services/admin.js +7 -0
  9. package/dist/public/js/services/agents.js +18 -3
  10. package/dist/public/js/services/generator-prompts-build.js +5 -1
  11. package/dist/public/js/services/organisms.js +16 -0
  12. package/dist/public/js/services/secretary-helpers.js +197 -0
  13. package/dist/public/js/services/secretary-policy.js +63 -0
  14. package/dist/public/js/services/secretary-routing.js +69 -0
  15. package/dist/public/spa.html +14 -0
  16. package/dist/public/views/admin/apps-tab.js +208 -0
  17. package/dist/public/views/admin.js +3 -0
  18. package/dist/public/views/my-company.js +247 -3
  19. package/dist/public/views/profile/agents/agent-card.js +15 -0
  20. package/dist/public/views/profile/apps-tab.js +4 -0
  21. package/dist/public/views/profile/extensions-tab.js +30 -1
  22. package/dist/public/views/profile/generator-settings.js +28 -1
  23. package/dist/public/views/secretary/cards-reach.js +235 -0
  24. package/dist/public/views/secretary/cards.js +419 -0
  25. package/dist/public/views/secretary/use-access.js +95 -0
  26. package/dist/public/views/secretary/use-autonomy.js +92 -0
  27. package/dist/public/views/secretary/use-create-resource.js +86 -0
  28. package/dist/public/views/secretary/use-crew.js +173 -0
  29. package/dist/public/views/secretary/use-guided-plan.js +112 -0
  30. package/dist/public/views/secretary/use-intake.js +332 -0
  31. package/dist/public/views/secretary/use-knowledge.js +104 -0
  32. package/dist/public/views/secretary/use-learning.js +140 -0
  33. package/dist/public/views/secretary.js +327 -34
  34. package/dist/src/cli/connect/mcp/tools/agent-management.d.ts +8 -8
  35. package/dist/src/cli/connect/mcp/tools/agent-management.js +1 -1
  36. package/dist/src/cli/connect/mcp/tools/agent-management.js.map +1 -1
  37. package/dist/src/enterprise/loader.d.ts.map +1 -1
  38. package/dist/src/enterprise/loader.js +2 -0
  39. package/dist/src/enterprise/loader.js.map +1 -1
  40. package/dist/src/enterprise/provider.d.ts +67 -1
  41. package/dist/src/enterprise/provider.d.ts.map +1 -1
  42. package/dist/src/enterprise/provider.js +17 -0
  43. package/dist/src/enterprise/provider.js.map +1 -1
  44. package/dist/src/generated/api-types.d.ts +744 -49
  45. package/dist/src/generated/api-types.d.ts.map +1 -1
  46. package/dist/src/generated/prisma-postgres/edge.js +7 -3
  47. package/dist/src/generated/prisma-postgres/index-browser.js +4 -0
  48. package/dist/src/generated/prisma-postgres/index.d.ts +117 -1
  49. package/dist/src/generated/prisma-postgres/index.js +7 -3
  50. package/dist/src/generated/prisma-postgres/package.json +1 -1
  51. package/dist/src/generated/prisma-postgres/schema.prisma +16 -12
  52. package/dist/src/generated/prisma-postgres/wasm.js +7 -3
  53. package/dist/src/mcp/agent-management.d.ts +2 -0
  54. package/dist/src/mcp/agent-management.d.ts.map +1 -1
  55. package/dist/src/mcp/agent-management.js +5 -1
  56. package/dist/src/mcp/agent-management.js.map +1 -1
  57. package/dist/src/mcp/catalog/definitions.js +3 -3
  58. package/dist/src/mcp/catalog/definitions.js.map +1 -1
  59. package/dist/src/mcp/catalog/scopes.d.ts +46 -0
  60. package/dist/src/mcp/catalog/scopes.d.ts.map +1 -1
  61. package/dist/src/mcp/catalog/scopes.js +81 -0
  62. package/dist/src/mcp/catalog/scopes.js.map +1 -1
  63. package/dist/src/mcp/catalog/surfaces.js +2 -2
  64. package/dist/src/mcp/catalog/surfaces.js.map +1 -1
  65. package/dist/src/routes/agent-directives.d.ts +12 -1
  66. package/dist/src/routes/agent-directives.d.ts.map +1 -1
  67. package/dist/src/routes/agent-directives.js +30 -4
  68. package/dist/src/routes/agent-directives.js.map +1 -1
  69. package/dist/src/routes/agents.d.ts +3 -0
  70. package/dist/src/routes/agents.d.ts.map +1 -1
  71. package/dist/src/routes/agents.js +11 -4
  72. package/dist/src/routes/agents.js.map +1 -1
  73. package/dist/src/routes/ai.d.ts +2 -0
  74. package/dist/src/routes/ai.d.ts.map +1 -1
  75. package/dist/src/routes/ai.js +15 -2
  76. package/dist/src/routes/ai.js.map +1 -1
  77. package/dist/src/routes/apps.d.ts +14 -0
  78. package/dist/src/routes/apps.d.ts.map +1 -1
  79. package/dist/src/routes/apps.js +168 -3
  80. package/dist/src/routes/apps.js.map +1 -1
  81. package/dist/src/routes/extensions.d.ts +4 -0
  82. package/dist/src/routes/extensions.d.ts.map +1 -1
  83. package/dist/src/routes/extensions.js +75 -11
  84. package/dist/src/routes/extensions.js.map +1 -1
  85. package/dist/src/routes/libs.d.ts +6 -0
  86. package/dist/src/routes/libs.d.ts.map +1 -1
  87. package/dist/src/routes/libs.js +163 -0
  88. package/dist/src/routes/libs.js.map +1 -1
  89. package/dist/src/routes/oauth-login.d.ts +16 -6
  90. package/dist/src/routes/oauth-login.d.ts.map +1 -1
  91. package/dist/src/routes/oauth-login.js +290 -51
  92. package/dist/src/routes/oauth-login.js.map +1 -1
  93. package/dist/src/routes/openrouter.d.ts +2 -0
  94. package/dist/src/routes/openrouter.d.ts.map +1 -1
  95. package/dist/src/routes/openrouter.js +8 -1
  96. package/dist/src/routes/openrouter.js.map +1 -1
  97. package/dist/src/routes/organism-templates.d.ts +22 -0
  98. package/dist/src/routes/organism-templates.d.ts.map +1 -0
  99. package/dist/src/routes/organism-templates.js +110 -0
  100. package/dist/src/routes/organism-templates.js.map +1 -0
  101. package/dist/src/routes/schedules.d.ts +2 -0
  102. package/dist/src/routes/schedules.d.ts.map +1 -1
  103. package/dist/src/routes/schedules.js +3 -1
  104. package/dist/src/routes/schedules.js.map +1 -1
  105. package/dist/src/routes/specialists.d.ts +28 -0
  106. package/dist/src/routes/specialists.d.ts.map +1 -0
  107. package/dist/src/routes/specialists.js +227 -0
  108. package/dist/src/routes/specialists.js.map +1 -0
  109. package/dist/src/routes/subdomains.d.ts +3 -0
  110. package/dist/src/routes/subdomains.d.ts.map +1 -1
  111. package/dist/src/routes/subdomains.js +12 -2
  112. package/dist/src/routes/subdomains.js.map +1 -1
  113. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  114. package/dist/src/server-bootstrap/routes-loader.js +11 -3
  115. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  116. package/dist/src/services/ai-completion.d.ts +8 -0
  117. package/dist/src/services/ai-completion.d.ts.map +1 -1
  118. package/dist/src/services/ai-completion.js +7 -1
  119. package/dist/src/services/ai-completion.js.map +1 -1
  120. package/dist/src/services/discovery/setup.d.ts +2 -1
  121. package/dist/src/services/discovery/setup.d.ts.map +1 -1
  122. package/dist/src/services/discovery/setup.js +2 -0
  123. package/dist/src/services/discovery/setup.js.map +1 -1
  124. package/dist/src/services/discovery/sources/memory-source.d.ts +2 -0
  125. package/dist/src/services/discovery/sources/memory-source.d.ts.map +1 -1
  126. package/dist/src/services/discovery/sources/memory-source.js +8 -1
  127. package/dist/src/services/discovery/sources/memory-source.js.map +1 -1
  128. package/dist/src/services/discovery/sources/template-source.d.ts +17 -0
  129. package/dist/src/services/discovery/sources/template-source.d.ts.map +1 -0
  130. package/dist/src/services/discovery/sources/template-source.js +51 -0
  131. package/dist/src/services/discovery/sources/template-source.js.map +1 -0
  132. package/dist/src/services/discovery/types.d.ts +1 -1
  133. package/dist/src/services/discovery/types.d.ts.map +1 -1
  134. package/dist/src/services/extension-secrets.d.ts +63 -0
  135. package/dist/src/services/extension-secrets.d.ts.map +1 -0
  136. package/dist/src/services/extension-secrets.js +122 -0
  137. package/dist/src/services/extension-secrets.js.map +1 -0
  138. package/dist/src/services/handbooks/appdev.d.ts +1 -1
  139. package/dist/src/services/handbooks/appdev.d.ts.map +1 -1
  140. package/dist/src/services/handbooks/appdev.js +1 -1
  141. package/dist/src/services/openrouter.d.ts +1 -1
  142. package/dist/src/services/openrouter.d.ts.map +1 -1
  143. package/dist/src/services/openrouter.js +18 -2
  144. package/dist/src/services/openrouter.js.map +1 -1
  145. package/dist/src/services/organism-export.d.ts +2 -0
  146. package/dist/src/services/organism-export.d.ts.map +1 -1
  147. package/dist/src/services/organism-export.js +24 -5
  148. package/dist/src/services/organism-export.js.map +1 -1
  149. package/dist/src/services/organism-import.d.ts +11 -0
  150. package/dist/src/services/organism-import.d.ts.map +1 -1
  151. package/dist/src/services/organism-import.js +16 -1
  152. package/dist/src/services/organism-import.js.map +1 -1
  153. package/dist/src/services/organism-template.d.ts +160 -0
  154. package/dist/src/services/organism-template.d.ts.map +1 -0
  155. package/dist/src/services/organism-template.js +202 -0
  156. package/dist/src/services/organism-template.js.map +1 -0
  157. package/dist/src/services/scheduler.d.ts +45 -0
  158. package/dist/src/services/scheduler.d.ts.map +1 -1
  159. package/dist/src/services/scheduler.js +358 -5
  160. package/dist/src/services/scheduler.js.map +1 -1
  161. package/dist/src/services/secretary-tick.d.ts +127 -0
  162. package/dist/src/services/secretary-tick.d.ts.map +1 -0
  163. package/dist/src/services/secretary-tick.js +169 -0
  164. package/dist/src/services/secretary-tick.js.map +1 -0
  165. package/dist/src/services/secretary.d.ts +98 -2
  166. package/dist/src/services/secretary.d.ts.map +1 -1
  167. package/dist/src/services/secretary.js +130 -12
  168. package/dist/src/services/secretary.js.map +1 -1
  169. package/dist/src/services/specialist-policy.d.ts +39 -0
  170. package/dist/src/services/specialist-policy.d.ts.map +1 -0
  171. package/dist/src/services/specialist-policy.js +59 -0
  172. package/dist/src/services/specialist-policy.js.map +1 -0
  173. package/dist/src/services/specialist.d.ts +77 -0
  174. package/dist/src/services/specialist.d.ts.map +1 -0
  175. package/dist/src/services/specialist.js +137 -0
  176. package/dist/src/services/specialist.js.map +1 -0
  177. package/dist/src/services/structure-overview.d.ts +3 -0
  178. package/dist/src/services/structure-overview.d.ts.map +1 -1
  179. package/dist/src/services/structure-overview.js +20 -10
  180. package/dist/src/services/structure-overview.js.map +1 -1
  181. package/dist/src/services/system-agent.d.ts +39 -0
  182. package/dist/src/services/system-agent.d.ts.map +1 -0
  183. package/dist/src/services/system-agent.js +43 -0
  184. package/dist/src/services/system-agent.js.map +1 -0
  185. package/dist/src/storage/interface.d.ts +7 -2
  186. package/dist/src/storage/interface.d.ts.map +1 -1
  187. package/dist/src/storage/interface.js.map +1 -1
  188. package/dist/src/storage/providers/mongodb/index.d.ts +5 -0
  189. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  190. package/dist/src/storage/providers/mongodb/index.js +31 -4
  191. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  192. package/dist/src/storage/providers/sqlite/index.d.ts +5 -0
  193. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  194. package/dist/src/storage/providers/sqlite/index.js +26 -6
  195. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  196. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  197. package/dist/src/storage/providers/sqlite/schema.js +8 -0
  198. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  199. package/dist/src/storage/repositories/app.repository.d.ts +17 -0
  200. package/dist/src/storage/repositories/app.repository.d.ts.map +1 -1
  201. package/dist/src/utils/app-badge.d.ts +26 -0
  202. package/dist/src/utils/app-badge.d.ts.map +1 -0
  203. package/dist/src/utils/app-badge.js +56 -0
  204. package/dist/src/utils/app-badge.js.map +1 -0
  205. package/dist/static/app-catalog.html +315 -71
  206. package/package.json +1 -1
  207. package/prisma/schema.postgres.prisma +4 -0
  208. package/prisma/schema.prisma +4 -0
@@ -16,12 +16,18 @@
16
16
  * v0.2.1 — 2026-06-23 — order tracking (my orders + orders received) with live status
17
17
  * v0.3.0 — 2026-06-23 — members & roles + customers + revenue-split receipt
18
18
  * v0.4.0 — 2026-06-23 — company Settings tab (admin): name, Y-tunnus, description, commission %
19
+ * v0.5.0 — 2026-06-24 — Secretary Phase 6 (slice 3): company Secretary admin subtab — enable +
20
+ * read-only locked brain + company-admin capabilities + enterprise scopes (GET/POST /v1/orgs/:slug/secretary).
21
+ * v0.6.0 — 2026-06-24 — Secretary Phase 6 (slice 2): Governance admin subtab — CFO cost & ROI surface
22
+ * (revenue / orders / cost-per-task / commission income + per-agent table) from GET /v1/orgs/:slug/governance.
23
+ * v0.7.0 — 2026-06-24 — Secretary Phase 6 (slice 1): Company model admin subtab — prompt-driven builder
24
+ * (describe → generate in-app / copy-prompt → review → apply) for departments/roles/people/reporting (GET/PUT /v1/orgs/:slug/model).
19
25
  */
20
26
  import { h } from 'preact';
21
- import { useState, useEffect } from 'preact/hooks';
27
+ import { useState, useEffect, useCallback } from 'preact/hooks';
22
28
  import htm from 'htm';
23
29
  import { t } from '/js/i18n.js';
24
- import { apiGet, apiPost, apiPut, apiPatch, apiDelete } from '/js/api.js';
30
+ import { api, apiGet, apiPost, apiPut, apiPatch, apiDelete } from '/js/api.js';
25
31
  import { setOfferBilling } from '/js/services/offers.js';
26
32
  import { useViewCSS } from '/components/useViewCSS.js';
27
33
  import { DeliverableBody } from '/components/ImageDeliverable.js';
@@ -501,6 +507,241 @@ function OfferPicker({ slug, alreadyListed, onListed }) {
501
507
  </div>`;
502
508
  }
503
509
 
510
+ /**
511
+ * Company Secretary panel (Secretary Phase 6, admin-only). Reads GET /v1/orgs/:slug/secretary; if not
512
+ * yet provisioned, an admin can enable it (POST). Once present it shows the agent identity, the
513
+ * read-only LOCKED brain (purpose + rules, owned by the Enterprise edition), the company-admin
514
+ * capabilities, and the granted enterprise scopes.
515
+ */
516
+ function SecretaryPanel({ slug }) {
517
+ const [sec, setSec] = useState(undefined); // undefined = loading, null = not provisioned, object = provisioned
518
+ const [busy, setBusy] = useState(false);
519
+ const [err, setErr] = useState('');
520
+
521
+ const load = useCallback(async () => {
522
+ try { const r = await apiGet(`/v1/orgs/${encodeURIComponent(slug)}/secretary`); setSec(r.data?.secretary ?? null); }
523
+ catch (e) { setErr(e.message || ''); setSec(null); }
524
+ }, [slug]);
525
+ useEffect(() => { setSec(undefined); load(); }, [load]);
526
+
527
+ async function enable() {
528
+ setBusy(true); setErr('');
529
+ try { const r = await apiPost(`/v1/orgs/${encodeURIComponent(slug)}/secretary`, {}); setSec(r.data?.secretary ?? null); }
530
+ catch (e) { setErr(e.message || ''); }
531
+ finally { setBusy(false); }
532
+ }
533
+
534
+ if (sec === undefined) return html`<p class="mc-empty">${t('myCompany.secLoading')}</p>`;
535
+
536
+ if (!sec) return html`
537
+ <div class="mc-sec">
538
+ <p class="section-desc">${t('myCompany.secIntro')}</p>
539
+ <button class="btn-primary" disabled=${busy} onClick=${enable}>${busy ? t('myCompany.secEnabling') : t('myCompany.secEnable')}</button>
540
+ ${err && html`<p class="mc-err">${err}</p>`}
541
+ </div>`;
542
+
543
+ return html`
544
+ <div class="mc-sec">
545
+ <div class="mc-sec-id">
546
+ <strong>${t('myCompany.secTitle')}</strong>
547
+ <code class="mc-mini">${sec.gaii}</code>
548
+ </div>
549
+ <div class="mc-sec-brain">
550
+ <div class="mc-section-head">
551
+ <h3 class="mc-form-title">${t('myCompany.secBrain')}</h3>
552
+ <span class="mc-lock-chip">🔒 ${t('myCompany.secLocked')}</span>
553
+ </div>
554
+ <p class="mc-sec-purpose">${sec.brain?.purpose ?? ''}</p>
555
+ <ul class="mc-sec-rules">
556
+ ${(sec.brain?.rules ?? []).map((r, i) => html`<li key=${i}>${r.description}</li>`)}
557
+ </ul>
558
+ </div>
559
+ ${(sec.capabilities ?? []).length ? html`
560
+ <div class="mc-sec-caps">
561
+ <h3 class="mc-form-title">${t('myCompany.secCaps')}</h3>
562
+ <ul class="mc-sec-cap-list">
563
+ ${sec.capabilities.map(c => html`<li key=${c.id}><strong>${c.title}</strong><span class="mc-mini"> ${c.description}</span>${c.band ? html` <span class="mc-chip">${c.band}</span>` : null}</li>`)}
564
+ </ul>
565
+ </div>` : null}
566
+ <details class="mc-sec-scopes">
567
+ <summary>${t('myCompany.secScopes')} (${(sec.scopes ?? []).length})</summary>
568
+ <div class="mc-sec-scope-tags">${(sec.scopes ?? []).map(s => html`<span class="mc-chip" key=${s}>${s}</span>`)}</div>
569
+ </details>
570
+ ${err && html`<p class="mc-err">${err}</p>`}
571
+ </div>`;
572
+ }
573
+
574
+ /**
575
+ * Financial governance / cost & ROI panel (Secretary Phase 6 slice 2, admin-only). The CFO-facing
576
+ * surface: the company's transaction ledger in financial terms (revenue, commission income, member
577
+ * payouts, cost-per-task per agent) from GET /v1/orgs/:slug/governance. Per-department + token-level
578
+ * cost attribution + decision-scored ROI land with the company model (slice 1) — noted, not faked.
579
+ */
580
+ function GovernancePanel({ slug }) {
581
+ const [gov, setGov] = useState(undefined);
582
+ const [err, setErr] = useState('');
583
+ const load = useCallback(async () => {
584
+ try { const r = await apiGet(`/v1/orgs/${encodeURIComponent(slug)}/governance`); setGov(r.data?.governance ?? null); }
585
+ catch (e) { setErr(e.message || ''); setGov(null); }
586
+ }, [slug]);
587
+ useEffect(() => { setGov(undefined); load(); }, [load]);
588
+
589
+ if (gov === undefined) return html`<p class="mc-empty">${t('myCompany.govLoading')}</p>`;
590
+ if (!gov) return html`<p class="mc-err">${err || t('myCompany.govLoading')}</p>`;
591
+
592
+ const cur = gov.currency || 'morsels';
593
+ const T = gov.totals;
594
+ const kpi = (label, value) => html`<div class="mc-gov-kpi"><div class="mc-gov-kpi-value">${value}</div><div class="mc-gov-kpi-label">${label}</div></div>`;
595
+ return html`
596
+ <div class="mc-gov">
597
+ <p class="section-desc">${t('myCompany.govIntro')}</p>
598
+ <div class="mc-gov-kpis">
599
+ ${kpi(`${T.revenue} ${cur}`, t('myCompany.govRevenue'))}
600
+ ${kpi(T.orders, t('myCompany.govOrders'))}
601
+ ${kpi(`${T.avgPerTask} ${cur}`, t('myCompany.govAvg'))}
602
+ ${kpi(`${T.orgCommission} ${cur}`, t('myCompany.govCommissionIncome'))}
603
+ </div>
604
+ <h3 class="mc-form-title">${t('myCompany.govByAgent')}</h3>
605
+ ${gov.byAgent.length === 0
606
+ ? html`<p class="mc-empty">${t('myCompany.govNoActivity')}</p>`
607
+ : html`<table class="mc-gov-table">
608
+ <thead><tr>
609
+ <th>${t('myCompany.govAgent')}</th><th>${t('myCompany.govTasks')}</th>
610
+ <th>${t('myCompany.govRevenueShort')}</th><th>${t('myCompany.govAvgShort')}</th><th>${t('myCompany.govPayout')}</th>
611
+ </tr></thead>
612
+ <tbody>
613
+ ${gov.byAgent.map(a => html`<tr key=${a.agentName + a.agentOwner}>
614
+ <td>🤖 ${a.agentName}</td><td>${a.tasks}</td><td>${a.revenue}</td><td>${a.avgPerTask}</td><td>${a.memberCut}</td>
615
+ </tr>`)}
616
+ </tbody>
617
+ </table>`}
618
+ <div class="mc-gov-levers">
619
+ <span class="mc-chip">${t('myCompany.govWallet')}: ${gov.walletBalance} ${cur}</span>
620
+ <span class="mc-chip">${t('myCompany.govCommission')}: ${gov.commissionPercent}%</span>
621
+ <span class="mc-chip">${t('myCompany.govFee')}: ${gov.feePercent}%</span>
622
+ </div>
623
+ <p class="mc-gov-note">${t('myCompany.govRoiNote')}</p>
624
+ </div>`;
625
+ }
626
+
627
+ /** Best-effort parse of a JSON object embedded in model output (tolerates prose/fences around it). */
628
+ function extractJsonObject(text) {
629
+ if (!text) return null;
630
+ const s = text.indexOf('{'); const e = text.lastIndexOf('}');
631
+ if (s < 0 || e <= s) return null;
632
+ try { const o = JSON.parse(text.slice(s, e + 1)); return (o && typeof o === 'object') ? o : null; } catch { return null; }
633
+ }
634
+
635
+ function buildModelPrompt(desc) {
636
+ return `You are designing a structured model of a real-world company organisation for the company Secretary. Based on the description, produce ONLY a JSON object (no prose, no code fences) with this exact shape:
637
+ {
638
+ "departments": [{ "name": string, "purpose": string }],
639
+ "roles": [{ "title": string, "department": string, "responsibilities": string }],
640
+ "people": [{ "name": string, "role": string, "department": string, "isAimeatUser": boolean }],
641
+ "reportingLines": [{ "from": string, "to": string }]
642
+ }
643
+ Include real people/employees (most are NOT AIMEAT users — set isAimeatUser false unless clearly a platform user). Keep it concise and realistic.
644
+
645
+ Company description:
646
+ ${desc}`;
647
+ }
648
+
649
+ /**
650
+ * Company model panel (Secretary Phase 6 slice 1, admin-only). The real-world org structure
651
+ * (departments, roles, reporting lines, people who may not be AIMEAT users) the company Secretary
652
+ * maintains. Built prompt-driven (same interview → AI-shaped JSON → apply mechanism as the personal
653
+ * self-organism): describe the company → generate in-app (owner key) or copy the prompt → review → apply.
654
+ * GET/PUT /v1/orgs/:slug/model.
655
+ */
656
+ function CompanyModelPanel({ slug }) {
657
+ const [model, setModel] = useState(undefined);
658
+ const [desc, setDesc] = useState('');
659
+ const [draft, setDraft] = useState(null); // AI-proposed model pending review
660
+ const [generating, setGenerating] = useState(false);
661
+ const [pasteOpen, setPasteOpen] = useState(false);
662
+ const [pasteText, setPasteText] = useState('');
663
+ const [msg, setMsg] = useState('');
664
+ const [err, setErr] = useState('');
665
+
666
+ const load = useCallback(async () => {
667
+ try { const r = await apiGet(`/v1/orgs/${encodeURIComponent(slug)}/model`); setModel(r.data?.model ?? null); }
668
+ catch (e) { setErr(e.message || ''); setModel(null); }
669
+ }, [slug]);
670
+ useEffect(() => { setModel(undefined); setDraft(null); load(); }, [load]);
671
+
672
+ async function generate() {
673
+ if (!desc.trim()) return;
674
+ setGenerating(true); setErr(''); setMsg('');
675
+ try {
676
+ // Long AI call on the owner's key — low-level api() with a long timeout + no retries.
677
+ const r = await api('/v1/ai/complete', { method: 'POST', body: JSON.stringify({ prompt: buildModelPrompt(desc.trim()), systemPrompt: 'Return only valid JSON, no prose.' }), timeoutMs: 1_800_000, retries: 0 });
678
+ const text = r.data?.content ?? r.data?.completion ?? r.data?.text ?? '';
679
+ const parsed = extractJsonObject(text);
680
+ if (!parsed) { setErr(t('myCompany.modelParseErr')); return; }
681
+ setDraft(parsed);
682
+ } catch (e) { setErr(e.message || ''); }
683
+ finally { setGenerating(false); }
684
+ }
685
+
686
+ function tryPaste() {
687
+ const parsed = extractJsonObject(pasteText);
688
+ if (!parsed) { setErr(t('myCompany.modelParseErr')); return; }
689
+ setErr(''); setDraft(parsed); setPasteOpen(false);
690
+ }
691
+
692
+ async function apply() {
693
+ if (!draft) return;
694
+ setErr(''); setMsg('');
695
+ try { const r = await apiPut(`/v1/orgs/${encodeURIComponent(slug)}/model`, draft); setModel(r.data?.model ?? null); setDraft(null); setDesc(''); setMsg(t('myCompany.modelApplied')); }
696
+ catch (e) { setErr(e.message || ''); }
697
+ }
698
+
699
+ async function copyPrompt() {
700
+ try { await navigator.clipboard.writeText(buildModelPrompt(desc.trim() || '<describe your company here>')); setMsg(t('myCompany.modelCopied')); } catch { /* clipboard blocked */ }
701
+ }
702
+
703
+ if (model === undefined) return html`<p class="mc-empty">${t('myCompany.modelLoading')}</p>`;
704
+
705
+ const view = draft || model;
706
+ return html`
707
+ <div class="mc-model">
708
+ <p class="section-desc">${t('myCompany.modelIntro')}</p>
709
+
710
+ <div class="mc-model-build">
711
+ <textarea class="mc-input" rows="3" placeholder=${t('myCompany.modelDescPlaceholder')} value=${desc} onInput=${e => setDesc(e.target.value)}></textarea>
712
+ <div class="mc-model-actions">
713
+ <button class="btn-primary" disabled=${!desc.trim() || generating} onClick=${generate}>${generating ? t('myCompany.modelGenerating') : t('myCompany.modelGenerate')}</button>
714
+ <button class="btn-outline btn-sm" onClick=${copyPrompt}>${t('myCompany.modelCopyPrompt')}</button>
715
+ <button class="btn-ghost btn-sm" onClick=${() => setPasteOpen(o => !o)}>${t('myCompany.modelPaste')}</button>
716
+ </div>
717
+ ${pasteOpen && html`<div class="mc-model-paste">
718
+ <textarea class="mc-input" rows="4" placeholder=${t('myCompany.modelPastePlaceholder')} value=${pasteText} onInput=${e => setPasteText(e.target.value)}></textarea>
719
+ <button class="btn-outline btn-sm" onClick=${tryPaste}>${t('myCompany.modelReview')}</button>
720
+ </div>`}
721
+ ${msg && html`<p class="mc-mini">${msg}</p>`}
722
+ ${err && html`<p class="mc-err">${err}</p>`}
723
+ </div>
724
+
725
+ ${draft && html`<div class="mc-model-review">
726
+ <span class="mc-chip">${t('myCompany.modelDraft')}</span>
727
+ <button class="btn-primary btn-sm" onClick=${apply}>${t('myCompany.modelApply')}</button>
728
+ <button class="btn-ghost btn-sm" onClick=${() => setDraft(null)}>${t('myCompany.modelDiscard')}</button>
729
+ </div>`}
730
+
731
+ ${!view ? html`<p class="mc-empty">${t('myCompany.modelEmpty')}</p>` : html`
732
+ <div class="mc-model-view">
733
+ <section><h3 class="mc-form-title">${t('myCompany.modelDepartments')} (${(view.departments || []).length})</h3>
734
+ <ul class="mc-model-list">${(view.departments || []).map((d, i) => html`<li key=${i}><strong>${d.name}</strong>${d.purpose ? html` — <span class="mc-mini">${d.purpose}</span>` : null}</li>`)}</ul></section>
735
+ <section><h3 class="mc-form-title">${t('myCompany.modelRoles')} (${(view.roles || []).length})</h3>
736
+ <ul class="mc-model-list">${(view.roles || []).map((r, i) => html`<li key=${i}><strong>${r.title}</strong>${r.department ? html` <span class="mc-chip">${r.department}</span>` : null}${r.responsibilities ? html`<div class="mc-mini">${r.responsibilities}</div>` : null}</li>`)}</ul></section>
737
+ <section><h3 class="mc-form-title">${t('myCompany.modelPeople')} (${(view.people || []).length})</h3>
738
+ <ul class="mc-model-list">${(view.people || []).map((p, i) => html`<li key=${i}><strong>${p.name}</strong>${p.role ? html` <span class="mc-mini">${p.role}${p.department ? ', ' + p.department : ''}</span>` : null}${p.isAimeatUser ? html` <span class="mc-chip">AIMEAT</span>` : null}</li>`)}</ul></section>
739
+ <section><h3 class="mc-form-title">${t('myCompany.modelReporting')} (${(view.reportingLines || []).length})</h3>
740
+ <ul class="mc-model-list">${(view.reportingLines || []).map((l, i) => html`<li key=${i}>${l.from} → ${l.to}</li>`)}</ul></section>
741
+ </div>`}
742
+ </div>`;
743
+ }
744
+
504
745
  export default function MyCompanyView() {
505
746
  useViewCSS('/css/views/my-company.css');
506
747
  const callerOwner = (typeof window !== 'undefined' && window.AIMEAT?.auth?.getSession?.()?.owner) || '';
@@ -631,7 +872,7 @@ export default function MyCompanyView() {
631
872
  </div>
632
873
 
633
874
  <div class="mc-subtabs">
634
- ${['catalog', ...(selected.role === 'admin' ? ['orders', 'customers', 'portfolio', 'settings'] : []), 'members'].map(id => html`
875
+ ${['catalog', ...(selected.role === 'admin' ? ['orders', 'customers', 'portfolio', 'secretary', 'governance', 'model', 'settings'] : []), 'members'].map(id => html`
635
876
  <button class="mc-subtab ${subTab === id ? 'active' : ''}" key=${id} onClick=${() => setSubTab(id)}>${t('myCompany.tab' + id.charAt(0).toUpperCase() + id.slice(1))}</button>`)}
636
877
  <span class="mc-role-chip">${t('myCompany.role' + (selected.role || 'member').charAt(0).toUpperCase() + (selected.role || 'member').slice(1))}</span>
637
878
  </div>
@@ -656,6 +897,9 @@ export default function MyCompanyView() {
656
897
  setSelected(s => ({ ...s, ...updated }));
657
898
  setOrgs(list => list.map(o => (o.slug === updated.slug && o.creatorOwner === updated.creatorOwner) ? { ...o, ...updated } : o));
658
899
  }} />`}
900
+ ${subTab === 'secretary' && html`<${SecretaryPanel} slug=${selected.slug} />`}
901
+ ${subTab === 'governance' && html`<${GovernancePanel} slug=${selected.slug} />`}
902
+ ${subTab === 'model' && html`<${CompanyModelPanel} slug=${selected.slug} />`}
659
903
  ${subTab === 'members' && html`<${MembersPanel} slug=${selected.slug} creatorOwner=${selected.creatorOwner} viewerRole=${selected.role} />`}
660
904
  `}
661
905
  </section>
@@ -3,6 +3,8 @@
3
3
  * @description Agent card component with collapsed/expanded states,
4
4
  * Two-Zone Header (identity + state-dependent status), and tab bar.
5
5
  * @version-history
6
+ * v1.17.0 -- 2026-06-24 -- Secretary P5 (S-A): render a "Specialist · <role>" badge for agents tagged
7
+ * system:specialist, so the new specialist agent type is recognizable in the Agents tab.
6
8
  * v1.16.0 -- 2026-06-21 -- Unseen-change badges are now has-unseen DOTS (no count) — the
7
9
  * bulk overview supplies latest-activity timestamps, not exact since-seen counts; the
8
10
  * dot still clears on tab-open and goes red for unseen failed tasks.
@@ -208,6 +210,7 @@ export default function AgentCard({ agent, onboarding, expanded, onToggle, sessi
208
210
  <!-- Mode/platform/readiness badges live in the expanded detail only — repeated
209
211
  identically on every row they were noise, not information. -->
210
212
  <div class="pf-agd-collapsed-badges">
213
+ ${renderSpecialistBadge(agent)}
211
214
  ${agent.federate && html`<span class="pf-agd-badge pf-agd-badge--federation">${t('profile.federated')}</span>`}
212
215
  </div>
213
216
  ${renderPopOut(onPopOut, agent)}
@@ -233,6 +236,7 @@ export default function AgentCard({ agent, onboarding, expanded, onToggle, sessi
233
236
  <span class="pf-agd-zone1-name">${agent.display_name || agent.name}</span>
234
237
  </div>
235
238
  <div class="pf-agd-zone1-badges">
239
+ ${renderSpecialistBadge(agent)}
236
240
  ${renderModeBadge(agent)}
237
241
  ${renderPlatformBadge(onboarding)}
238
242
  ${renderReadinessBadge(state, onboarding)}
@@ -521,6 +525,17 @@ function renderModeBadge(agent) {
521
525
  return html`<span class="pf-agd-badge pf-agd-badge--mode pf-agd-badge--mode-${mode}" title=${t('profile.agents.mode.tooltip') || ''}>${label}</span>`;
522
526
  }
523
527
 
528
+ // Specialist badge — a specialist agent (Secretary P5 / S-A) is tagged `system:specialist` + `role:<role>`.
529
+ // Shows "Specialist · <role>" so the owner can tell it apart from ordinary agents and the secretaries.
530
+ function renderSpecialistBadge(agent) {
531
+ const tags = agent.tags || [];
532
+ if (!tags.includes('system:specialist')) return null;
533
+ const roleTag = tags.find(tag => String(tag).startsWith('role:'));
534
+ const role = roleTag ? roleTag.slice('role:'.length) : 'specialist';
535
+ const label = t('profile.agents.specialistBadge') || 'Specialist';
536
+ return html`<span class="pf-agd-badge pf-agd-badge--specialist" title=${label}>${label}${role && role !== 'specialist' ? ` · ${role}` : ''}</span>`;
537
+ }
538
+
524
539
  // Editable tag strip shown in the expanded card header. The same owner-managed
525
540
  // tags drive the list's tag filter and "Group by: Tag" (and double as the shared
526
541
  // memory namespace agents.tag.<tag>.*), so editing here is the convenient inline
@@ -2,6 +2,8 @@
2
2
  * @file apps-tab.js
3
3
  * @description Profile tab for HTML app management — upload, gallery, access code editing.
4
4
  * @version-history
5
+ * v1.3.0 — 2026-06-24 — Show a "moderated by operator: hidden" badge + hint (with reason) on My Apps
6
+ * when an operator has hidden the app; the owner sees it but cannot lift it.
5
7
  * v1.2.0 — 2026-06-20 — Add Park/Unpark control + Parked badge/hint to My Apps (hide an app
6
8
  * from the public catalogue while keeping it usable by its owner)
7
9
  * v1.1.0 — 2026-03-19 — Add launch button to My Apps list
@@ -137,8 +139,10 @@ export default function AppsTab({ session, showToast, onStats }) {
137
139
  <span class="badge badge-info">${escHtml(a.mime_type || a.content_type || 'html')}</span>
138
140
  ${a.protected ? html`<span class="badge badge-warn">\u{1F512}</span>` : ''}
139
141
  ${a.parked ? html`<span class="badge badge-dim">\u{1F17F}️ ${t('profile.apps.parkedBadge') || 'Parked'}</span>` : ''}
142
+ ${a.operator_hidden ? html`<span class="badge badge-danger">\u{1F6AB} ${t('profile.apps.operatorHiddenBadge') || 'Moderated by operator: hidden'}</span>` : ''}
140
143
  </div>
141
144
  </div>
145
+ ${a.operator_hidden ? html`<div class="text-meta-sm mb-half">${t('profile.apps.operatorHiddenHint') || 'An operator has hidden this app from public view. Only you can still see it. Contact the operator to appeal.'}${a.operator_hide_reason ? ' — ' + escHtml(a.operator_hide_reason) : ''}</div>` : ''}
142
146
  ${a.parked ? html`<div class="text-meta-sm mb-half">${t('profile.apps.parkedHint') || 'Only you can see and use this — hidden from the public catalogue.'}</div>` : ''}
143
147
  ${a.manifest?.description ? html`<div class="text-meta mb-half">${escHtml(a.manifest.description)}</div>` : ''}
144
148
  <div class="card-subtitle">
@@ -12,6 +12,8 @@
12
12
  * - Install modal — upload/paste manifest + libs
13
13
  * @usage Loaded as a lazy tab in profile.js via dynamic import.
14
14
  * @version-history
15
+ * v1.3.0 — 2026-06-24 — Secretary P5 (S-C): instance-create form renders per-instance config
16
+ * fields from configSchema; `type: secret` fields show a masked (password) input + 🔒 tag.
15
17
  * v1.0.0 — 2026-03-10 — Initial implementation with Cortex + Server extension management
16
18
  * v1.1.0 — 2026-03-17 — Refactor: replace all inline style="" attributes with CSS classes
17
19
  * v1.2.0 — 2026-06-02 — Component unification (#2): both install modals use the
@@ -370,6 +372,7 @@ export default function ExtensionsTab({ session, showToast }) {
370
372
  const [srvDetail, setSrvDetail] = useState(null);
371
373
  const [srvInstances, setSrvInstances] = useState(null);
372
374
  const [newInstanceId, setNewInstanceId] = useState('');
375
+ const [newInstanceCfg, setNewInstanceCfg] = useState({}); // per-instance config-field values (incl. secrets)
373
376
  const [testAction, setTestAction] = useState(null); // { actionId }
374
377
  const [testInput, setTestInput] = useState('{}');
375
378
  const [testResult, setTestResult] = useState(null);
@@ -484,10 +487,14 @@ export default function ExtensionsTab({ session, showToast }) {
484
487
  const id = newInstanceId.trim();
485
488
  if (!id) { showToast(t('profile.v8ext.instanceIdRequired'), true); return; }
486
489
  try {
487
- const resp = await v8Ext.createInstance(name, id, {});
490
+ // Drop empty config values so unset optional fields aren't stored as "".
491
+ const cfg = {};
492
+ for (const [k, v] of Object.entries(newInstanceCfg)) { if (v !== '' && v != null) cfg[k] = v; }
493
+ const resp = await v8Ext.createInstance(name, id, cfg);
488
494
  if (resp.ok === false) throw new Error(resp.error?.message || 'Failed');
489
495
  showToast(t('profile.v8ext.instanceCreated'));
490
496
  setNewInstanceId('');
497
+ setNewInstanceCfg({});
491
498
  setSrvInstances(await v8Ext.listInstances(name));
492
499
  } catch(e) { showToast(e.message, true); }
493
500
  }
@@ -846,6 +853,28 @@ export default function ExtensionsTab({ session, showToast }) {
846
853
  ${isActive ? html`
847
854
  <div class="ext-instance-create">
848
855
  <input class="input-field ext-instance-input" placeholder=${t('profile.v8ext.instanceIdPlaceholder')} value=${newInstanceId} onInput=${e => setNewInstanceId(e.target.value)} />
856
+ ${(() => {
857
+ const props = ext.instances?.configSchema?.properties || {};
858
+ const keys = Object.keys(props);
859
+ if (!keys.length) return null;
860
+ return keys.map(k => {
861
+ const p = props[k] || {};
862
+ const isSecret = p.type === 'secret';
863
+ return html`<div class="ext-instance-cfg-field" key=${k}>
864
+ <label class="ext-instance-cfg-label">
865
+ ${p.label || k}
866
+ ${isSecret ? html`<span class="ext-instance-cfg-secret">🔒 ${t('profile.v8ext.secretField')}</span>` : null}
867
+ ${p.description ? html`<span class="ext-instance-cfg-desc">${p.description}</span>` : null}
868
+ </label>
869
+ <input class="input-field ext-instance-input"
870
+ type=${isSecret ? 'password' : 'text'}
871
+ autocomplete=${isSecret ? 'new-password' : 'off'}
872
+ placeholder=${isSecret ? '••••••••' : (p.label || k)}
873
+ value=${newInstanceCfg[k] || ''}
874
+ onInput=${e => setNewInstanceCfg({ ...newInstanceCfg, [k]: e.target.value })} />
875
+ </div>`;
876
+ });
877
+ })()}
849
878
  <button class="btn-primary btn-sm" onClick=${() => handleCreateInstance(ext.name)}>${t('profile.v8ext.createInstance')}</button>
850
879
  </div>` : html`<div class="ext-hint">${t('profile.v8ext.activateFirst')}</div>`}
851
880
  </div>` : html`
@@ -8,6 +8,8 @@
8
8
  * @usage
9
9
  * import { OpenRouterSettings, SettingsCollectionView } from './generator-settings.js';
10
10
  * @version-history
11
+ * v1.2.0 — 2026-06-24 — Add a Vision model selector (a vision-capable model, e.g. qwen-2.5-VL) used
12
+ * for image inputs — the Secretary's doc/image intake sends attached images to this model.
11
13
  * v1.1.0 — 2026-04-01 — Add temperature, top_p, max_tokens model parameter fields
12
14
  * v1.0.0 — 2026-03-22 — Extracted from generator-tab.js (was inline in v5.1.0+)
13
15
  */
@@ -26,6 +28,7 @@ export function OpenRouterSettings({ onSettingsChange }) {
26
28
  const [hasApiKey, setHasApiKey] = useState(false);
27
29
  const [apiKey, setApiKey] = useState('');
28
30
  const [model, setModel] = useState('');
31
+ const [visionModel, setVisionModel] = useState('');
29
32
  const [models, setModels] = useState([]);
30
33
  const [modelsLoading, setModelsLoading] = useState(false);
31
34
  const [autoRetry, setAutoRetry] = useState(false);
@@ -48,6 +51,7 @@ export function OpenRouterSettings({ onSettingsChange }) {
48
51
  if (resp.ok !== false && resp.data) {
49
52
  setHasApiKey(!!resp.data.hasApiKey);
50
53
  setModel(resp.data.model || '');
54
+ setVisionModel(resp.data.visionModel || '');
51
55
  setAutoRetry(!!resp.data.autoRetry);
52
56
  setMaxRetries(resp.data.maxRetries || 3);
53
57
  setProvider(resp.data.provider || 'openrouter');
@@ -87,7 +91,7 @@ export function OpenRouterSettings({ onSettingsChange }) {
87
91
  async function handleSave() {
88
92
  setSaving(true);
89
93
  try {
90
- const body = { model, autoRetry, maxRetries: parseInt(maxRetries) || 3, provider, baseUrl };
94
+ const body = { model, visionModel: visionModel || null, autoRetry, maxRetries: parseInt(maxRetries) || 3, provider, baseUrl };
91
95
  body.temperature = temperature !== '' ? parseFloat(temperature) : null;
92
96
  body.top_p = topP !== '' ? parseFloat(topP) : null;
93
97
  body.max_tokens = maxTokens !== '' ? parseInt(maxTokens) : null;
@@ -129,6 +133,7 @@ export function OpenRouterSettings({ onSettingsChange }) {
129
133
  setHasApiKey(false);
130
134
  setApiKey('');
131
135
  setModel('');
136
+ setVisionModel('');
132
137
  setModels([]);
133
138
  setAutoRetry(false);
134
139
  setMaxRetries(3);
@@ -219,6 +224,28 @@ export function OpenRouterSettings({ onSettingsChange }) {
219
224
  }
220
225
  </div>
221
226
 
227
+ <!-- Vision model (image inputs — Secretary doc/image intake) -->
228
+ <div class="pf-gen-or-field">
229
+ <label class="pf-gen-or-label">${t('profile.generator.openrouter.visionModel')}</label>
230
+ ${modelsLoading
231
+ ? html`<span class="pf-gen-or-loading">${t('profile.loading')}</span>`
232
+ : html`
233
+ <select
234
+ class="pf-gen-or-select"
235
+ value=${visionModel}
236
+ onChange=${e => setVisionModel(e.target.value)}
237
+ disabled=${!hasApiKey && !apiKey}
238
+ >
239
+ <option value="">${t('profile.generator.openrouter.visionModelNone')}</option>
240
+ ${models.map(m => html`
241
+ <option value=${m.id}>${m.name || m.id}</option>
242
+ `)}
243
+ </select>
244
+ `
245
+ }
246
+ <span class="pf-gen-or-param-hint">${t('profile.generator.openrouter.visionModel_hint')}</span>
247
+ </div>
248
+
222
249
  <!-- Auto-retry -->
223
250
  <div class="pf-gen-or-field">
224
251
  <label class="pf-gen-or-checkbox">