aimeat 1.8.0 → 1.9.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.
Files changed (199) hide show
  1. package/dist/.env.example +4 -1
  2. package/dist/bin/aimeat.js +0 -0
  3. package/dist/locales/en.json +527 -7
  4. package/dist/locales/fi.json +526 -6
  5. package/dist/public/css/views/admin-agent-integration.css +304 -0
  6. package/dist/public/css/views/agents-detail.css +1573 -146
  7. package/dist/public/css/views/public-knowledge-viewer.css +479 -0
  8. package/dist/public/js/i18n.js +9 -3
  9. package/dist/public/js/services/admin-agent-integration.js +52 -0
  10. package/dist/public/js/services/agent-integration.js +80 -0
  11. package/dist/public/js/services/agent-messages.js +3 -1
  12. package/dist/public/lib/live-updates.js +9 -4
  13. package/dist/public/llms-template.txt +106 -0
  14. package/dist/public/spa.html +7 -2
  15. package/dist/public/views/admin/agent-integration-tab.js +390 -0
  16. package/dist/public/views/admin.js +2 -0
  17. package/dist/public/views/profile/agents/agent-card.js +396 -0
  18. package/dist/public/views/profile/agents/shared-board.js +97 -0
  19. package/dist/public/views/profile/agents/state-detector.js +62 -0
  20. package/dist/public/views/profile/agents/tab-activity.js +232 -0
  21. package/dist/public/views/profile/agents/tab-agent-config.js +199 -0
  22. package/dist/public/views/profile/agents/tab-data-access.js +304 -0
  23. package/dist/public/views/profile/agents/tab-directives.js +178 -0
  24. package/dist/public/views/profile/agents/tab-integration.js +505 -0
  25. package/dist/public/views/profile/agents/tab-messages.js +278 -0
  26. package/dist/public/views/profile/agents/tab-services.js +17 -0
  27. package/dist/public/views/profile/agents/tab-tasks.js +17 -0
  28. package/dist/public/views/profile/agents-messages-subtab.js +11 -10
  29. package/dist/public/views/profile/agents-services-subtab.js +17 -11
  30. package/dist/public/views/profile/agents-tab.js +159 -259
  31. package/dist/public/views/profile/agents-task-builder.js +403 -0
  32. package/dist/public/views/profile/agents-tasks-subtab.js +114 -127
  33. package/dist/public/views/public-knowledge-viewer.js +650 -0
  34. package/dist/src/auth/middleware.d.ts.map +1 -1
  35. package/dist/src/auth/middleware.js +17 -0
  36. package/dist/src/auth/middleware.js.map +1 -1
  37. package/dist/src/config.d.ts +1 -0
  38. package/dist/src/config.d.ts.map +1 -1
  39. package/dist/src/config.js +1 -0
  40. package/dist/src/config.js.map +1 -1
  41. package/dist/src/middleware/readiness-gate.d.ts +18 -0
  42. package/dist/src/middleware/readiness-gate.d.ts.map +1 -0
  43. package/dist/src/middleware/readiness-gate.js +50 -0
  44. package/dist/src/middleware/readiness-gate.js.map +1 -0
  45. package/dist/src/models/agent-directives-schemas.d.ts +12 -0
  46. package/dist/src/models/agent-directives-schemas.d.ts.map +1 -1
  47. package/dist/src/models/agent-directives-schemas.js +7 -0
  48. package/dist/src/models/agent-directives-schemas.js.map +1 -1
  49. package/dist/src/models/agent-onboarding-schemas.d.ts +45 -0
  50. package/dist/src/models/agent-onboarding-schemas.d.ts.map +1 -0
  51. package/dist/src/models/agent-onboarding-schemas.js +91 -0
  52. package/dist/src/models/agent-onboarding-schemas.js.map +1 -0
  53. package/dist/src/models/webhook-schemas.d.ts +266 -0
  54. package/dist/src/models/webhook-schemas.d.ts.map +1 -0
  55. package/dist/src/models/webhook-schemas.js +137 -0
  56. package/dist/src/models/webhook-schemas.js.map +1 -0
  57. package/dist/src/routes/admin-agent-integration.d.ts +24 -0
  58. package/dist/src/routes/admin-agent-integration.d.ts.map +1 -0
  59. package/dist/src/routes/admin-agent-integration.js +186 -0
  60. package/dist/src/routes/admin-agent-integration.js.map +1 -0
  61. package/dist/src/routes/agent-activity.d.ts.map +1 -1
  62. package/dist/src/routes/agent-activity.js +17 -0
  63. package/dist/src/routes/agent-activity.js.map +1 -1
  64. package/dist/src/routes/agent-directives.d.ts +5 -1
  65. package/dist/src/routes/agent-directives.d.ts.map +1 -1
  66. package/dist/src/routes/agent-directives.js +74 -20
  67. package/dist/src/routes/agent-directives.js.map +1 -1
  68. package/dist/src/routes/agent-integration.d.ts +1 -0
  69. package/dist/src/routes/agent-integration.d.ts.map +1 -1
  70. package/dist/src/routes/agent-integration.js +128 -11
  71. package/dist/src/routes/agent-integration.js.map +1 -1
  72. package/dist/src/routes/agent-messages.d.ts +5 -1
  73. package/dist/src/routes/agent-messages.d.ts.map +1 -1
  74. package/dist/src/routes/agent-messages.js +20 -1
  75. package/dist/src/routes/agent-messages.js.map +1 -1
  76. package/dist/src/routes/agent-onboarding.d.ts +23 -0
  77. package/dist/src/routes/agent-onboarding.d.ts.map +1 -0
  78. package/dist/src/routes/agent-onboarding.js +374 -0
  79. package/dist/src/routes/agent-onboarding.js.map +1 -0
  80. package/dist/src/routes/agent-skill-bundle.d.ts +16 -0
  81. package/dist/src/routes/agent-skill-bundle.d.ts.map +1 -0
  82. package/dist/src/routes/agent-skill-bundle.js +133 -0
  83. package/dist/src/routes/agent-skill-bundle.js.map +1 -0
  84. package/dist/src/routes/agent-tasks.d.ts +7 -1
  85. package/dist/src/routes/agent-tasks.d.ts.map +1 -1
  86. package/dist/src/routes/agent-tasks.js +183 -7
  87. package/dist/src/routes/agent-tasks.js.map +1 -1
  88. package/dist/src/routes/agent-telemetry.d.ts +15 -0
  89. package/dist/src/routes/agent-telemetry.d.ts.map +1 -0
  90. package/dist/src/routes/agent-telemetry.js +99 -0
  91. package/dist/src/routes/agent-telemetry.js.map +1 -0
  92. package/dist/src/routes/agent-webhook.d.ts +18 -0
  93. package/dist/src/routes/agent-webhook.d.ts.map +1 -0
  94. package/dist/src/routes/agent-webhook.js +243 -0
  95. package/dist/src/routes/agent-webhook.js.map +1 -0
  96. package/dist/src/routes/agents.d.ts.map +1 -1
  97. package/dist/src/routes/agents.js +203 -4
  98. package/dist/src/routes/agents.js.map +1 -1
  99. package/dist/src/routes/auth.d.ts.map +1 -1
  100. package/dist/src/routes/auth.js +45 -2
  101. package/dist/src/routes/auth.js.map +1 -1
  102. package/dist/src/routes/ghii.d.ts.map +1 -1
  103. package/dist/src/routes/ghii.js +42 -5
  104. package/dist/src/routes/ghii.js.map +1 -1
  105. package/dist/src/routes/knowledge.d.ts.map +1 -1
  106. package/dist/src/routes/knowledge.js +19 -7
  107. package/dist/src/routes/knowledge.js.map +1 -1
  108. package/dist/src/routes/memory.js +3 -3
  109. package/dist/src/routes/memory.js.map +1 -1
  110. package/dist/src/routes/portal.d.ts.map +1 -1
  111. package/dist/src/routes/portal.js +1 -0
  112. package/dist/src/routes/portal.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 +16 -3
  115. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  116. package/dist/src/services/config-schema.d.ts.map +1 -1
  117. package/dist/src/services/config-schema.js +2 -1
  118. package/dist/src/services/config-schema.js.map +1 -1
  119. package/dist/src/services/core-jobs.d.ts.map +1 -1
  120. package/dist/src/services/core-jobs.js +3 -2
  121. package/dist/src/services/core-jobs.js.map +1 -1
  122. package/dist/src/services/onboarding-validator.d.ts +22 -0
  123. package/dist/src/services/onboarding-validator.d.ts.map +1 -0
  124. package/dist/src/services/onboarding-validator.js +202 -0
  125. package/dist/src/services/onboarding-validator.js.map +1 -0
  126. package/dist/src/services/platform-detector.d.ts +27 -0
  127. package/dist/src/services/platform-detector.d.ts.map +1 -0
  128. package/dist/src/services/platform-detector.js +73 -0
  129. package/dist/src/services/platform-detector.js.map +1 -0
  130. package/dist/src/services/prompt-defaults.d.ts.map +1 -1
  131. package/dist/src/services/prompt-defaults.js +129 -123
  132. package/dist/src/services/prompt-defaults.js.map +1 -1
  133. package/dist/src/services/readiness-scorer.d.ts +34 -0
  134. package/dist/src/services/readiness-scorer.d.ts.map +1 -0
  135. package/dist/src/services/readiness-scorer.js +87 -0
  136. package/dist/src/services/readiness-scorer.js.map +1 -0
  137. package/dist/src/services/skill-bundle/generator.d.ts +17 -0
  138. package/dist/src/services/skill-bundle/generator.d.ts.map +1 -0
  139. package/dist/src/services/skill-bundle/generator.js +387 -0
  140. package/dist/src/services/skill-bundle/generator.js.map +1 -0
  141. package/dist/src/services/skill-bundle/generic-adapter.d.ts +16 -0
  142. package/dist/src/services/skill-bundle/generic-adapter.d.ts.map +1 -0
  143. package/dist/src/services/skill-bundle/generic-adapter.js +96 -0
  144. package/dist/src/services/skill-bundle/generic-adapter.js.map +1 -0
  145. package/dist/src/services/skill-bundle/hermes-adapter.d.ts +25 -0
  146. package/dist/src/services/skill-bundle/hermes-adapter.d.ts.map +1 -0
  147. package/dist/src/services/skill-bundle/hermes-adapter.js +302 -0
  148. package/dist/src/services/skill-bundle/hermes-adapter.js.map +1 -0
  149. package/dist/src/services/skill-bundle/types.d.ts +54 -0
  150. package/dist/src/services/skill-bundle/types.d.ts.map +1 -0
  151. package/dist/src/services/skill-bundle/types.js +8 -0
  152. package/dist/src/services/skill-bundle/types.js.map +1 -0
  153. package/dist/src/services/task-stall-detector.d.ts +10 -2
  154. package/dist/src/services/task-stall-detector.d.ts.map +1 -1
  155. package/dist/src/services/task-stall-detector.js +34 -5
  156. package/dist/src/services/task-stall-detector.js.map +1 -1
  157. package/dist/src/services/webhook-dispatcher.d.ts +71 -0
  158. package/dist/src/services/webhook-dispatcher.d.ts.map +1 -0
  159. package/dist/src/services/webhook-dispatcher.js +259 -0
  160. package/dist/src/services/webhook-dispatcher.js.map +1 -0
  161. package/dist/src/storage/interface.d.ts +80 -2
  162. package/dist/src/storage/interface.d.ts.map +1 -1
  163. package/dist/src/storage/providers/mongodb/index.d.ts +20 -1
  164. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  165. package/dist/src/storage/providers/mongodb/index.js +250 -2
  166. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  167. package/dist/src/storage/providers/sqlite/index.d.ts +18 -1
  168. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  169. package/dist/src/storage/providers/sqlite/index.js +168 -4
  170. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  171. package/dist/src/storage/providers/sqlite/repos/agent-directives.d.ts.map +1 -1
  172. package/dist/src/storage/providers/sqlite/repos/agent-directives.js +8 -4
  173. package/dist/src/storage/providers/sqlite/repos/agent-directives.js.map +1 -1
  174. package/dist/src/storage/providers/sqlite/repos/agent.d.ts.map +1 -1
  175. package/dist/src/storage/providers/sqlite/repos/agent.js +25 -4
  176. package/dist/src/storage/providers/sqlite/repos/agent.js.map +1 -1
  177. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  178. package/dist/src/storage/providers/sqlite/schema.js +71 -1
  179. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  180. package/dist/src/storage/repositories/agent-onboarding.repository.d.ts +16 -0
  181. package/dist/src/storage/repositories/agent-onboarding.repository.d.ts.map +1 -0
  182. package/dist/src/storage/repositories/agent-onboarding.repository.js +8 -0
  183. package/dist/src/storage/repositories/agent-onboarding.repository.js.map +1 -0
  184. package/dist/src/storage/repositories/agent-webhook.repository.d.ts +21 -0
  185. package/dist/src/storage/repositories/agent-webhook.repository.d.ts.map +1 -0
  186. package/dist/src/storage/repositories/agent-webhook.repository.js +2 -0
  187. package/dist/src/storage/repositories/agent-webhook.repository.js.map +1 -0
  188. package/dist/src/storage/repositories/agent.repository.d.ts +1 -0
  189. package/dist/src/storage/repositories/agent.repository.d.ts.map +1 -1
  190. package/dist/src/storage/repositories/index.d.ts +2 -0
  191. package/dist/src/storage/repositories/index.d.ts.map +1 -1
  192. package/dist/src/utils/env-config.d.ts.map +1 -1
  193. package/dist/src/utils/env-config.js +7 -1
  194. package/dist/src/utils/env-config.js.map +1 -1
  195. package/dist/src/utils/url-validator.d.ts.map +1 -1
  196. package/dist/src/utils/url-validator.js +3 -0
  197. package/dist/src/utils/url-validator.js.map +1 -1
  198. package/package.json +97 -77
  199. package/prisma/schema.prisma +65 -7
@@ -801,6 +801,112 @@ On approval (HTTP 200):
801
801
  - `gaii` — your identity on this node
802
802
  - `token` — use for all API calls: `Authorization: Bearer <token>`
803
803
 
804
+ ## Agent API Quick Reference
805
+
806
+ All agent endpoints use `/v1/agents/me/` which resolves to your agent name. Header: `Authorization: Bearer <token>`
807
+
808
+ ### Capabilities
809
+
810
+ ```
811
+ PUT /v1/agents/me/capabilities
812
+ {
813
+ "technical": [
814
+ { "name": "memory", "type": "skill" },
815
+ { "name": "tasks", "type": "skill" },
816
+ { "name": "web_scraping", "type": "tool" }
817
+ ],
818
+ "domain": ["grocery_monitoring", "data_analysis"],
819
+ "languages": ["en", "fi"],
820
+ "modules_loaded": ["tier1", "tier1/tasks", "tier1/messages"],
821
+ "limitations": ["session-scoped runtime"]
822
+ }
823
+ ```
824
+
825
+ ### Tasks — Propose todos
826
+
827
+ ```
828
+ PATCH /v1/agents/me/tasks/{id}
829
+ {
830
+ "todos": [
831
+ { "title": "Check connectivity", "description": "Verify API access", "order": 1, "environment": "aimeat" },
832
+ { "title": "Write report", "description": "Generate analysis", "order": 2, "environment": "agent" }
833
+ ]
834
+ }
835
+ ```
836
+ Environment: `aimeat` (runs against AIMEAT API) or `agent` (runs in your local environment).
837
+
838
+ ### Tasks — Update a todo
839
+
840
+ ```
841
+ PATCH /v1/agents/me/tasks/{id}/todos/{todoId}
842
+ { "status": "done" }
843
+ ```
844
+ Valid statuses: `pending`, `active`, `done`, `failed`, `skipped`
845
+
846
+ ### Tasks — Complete
847
+
848
+ ```
849
+ POST /v1/agents/me/tasks/{id}/complete
850
+ { "summary": "All steps executed successfully" }
851
+ ```
852
+
853
+ ### Telemetry
854
+
855
+ ```
856
+ POST /v1/agents/me/telemetry
857
+ {
858
+ "type": "llm_call",
859
+ "tokens_in": 1523,
860
+ "tokens_out": 847,
861
+ "model": "qwen/qwen3.6-plus",
862
+ "duration_ms": 3200
863
+ }
864
+ ```
865
+ Types: `llm_call`, `tool_call`, `agent_report`
866
+
867
+ ### Messages — Send
868
+
869
+ ```
870
+ POST /v1/agents/me/messages
871
+ {
872
+ "thread_id": "optional-thread-id",
873
+ "content": "Hello from my agent",
874
+ "direction": "outbound"
875
+ }
876
+ ```
877
+
878
+ ### Onboarding — Confirm a step
879
+
880
+ ```
881
+ POST /v1/agents/me/onboarding/step/{stepId}
882
+ ```
883
+ Step IDs: `authenticate`, `identify_platform`, `install_skill`, `report_capabilities`, `read_directives`, `send_test_message`, `configure_delivery`, `report_telemetry`, `accept_test_task`, `complete_test_task`, `declare_services`
884
+
885
+ Some steps auto-validate when you GET /v1/agents/me/onboarding. The test task auto-starts after you propose todos.
886
+
887
+ ### Memory — Write
888
+
889
+ ```
890
+ POST /v1/memory
891
+ {
892
+ "key": "agents.my-agent.commands",
893
+ "value": [
894
+ { "name": "/status", "description": "Show agent status", "category": "general" },
895
+ { "name": "/inbox", "description": "Check inbox", "category": "tasks" }
896
+ ],
897
+ "visibility": "owner"
898
+ }
899
+ ```
900
+ The value for commands MUST be a flat array of `{ name, description, category }`. Each name starts with `/`.
901
+ Visibility: `private` (only you), `owner` (you + owner), `public` (everyone)
902
+
903
+ ### Inbox — Poll
904
+
905
+ ```
906
+ GET /v1/agents/me/inbox
907
+ ```
908
+ Returns: `{ "queued_tasks": [...], "active_tasks": [...], "pending_messages": [...] }`
909
+
804
910
  ## Connecting: MCP (OAuth 2.1)
805
911
 
806
912
  For MCP-capable clients (Claude, Cursor, etc.) that support the Model Context Protocol.
@@ -66,6 +66,8 @@
66
66
  <link rel="stylesheet" href="/css/views/capabilities.css">
67
67
  <link rel="stylesheet" href="/css/views/agents-detail.css">
68
68
  <link rel="stylesheet" href="/css/views/admin.css">
69
+ <link rel="stylesheet" href="/css/views/admin-agent-integration.css">
70
+ <link rel="stylesheet" href="/css/views/public-knowledge-viewer.css">
69
71
  <script type="importmap">
70
72
  {
71
73
  "imports": {
@@ -143,7 +145,7 @@
143
145
  "/lib/minidenticons.min.js": "/lib/minidenticons.min.js",
144
146
  "/lib/yaml.mjs": "/lib/yaml.mjs",
145
147
  "/views/profile/agents-tasks-subtab.js": "/views/profile/agents-tasks-subtab.js",
146
- "/views/profile/agents-directives-subtab.js": "/views/profile/agents-directives-subtab.js",
148
+ "/views/profile/agents-task-builder.js": "/views/profile/agents-task-builder.js",
147
149
  "/js/services/agent-tasks.js": "/js/services/agent-tasks.js",
148
150
  "/js/services/agent-directives.js": "/js/services/agent-directives.js",
149
151
  "/js/services/agent-capabilities.js": "/js/services/agent-capabilities.js",
@@ -154,7 +156,9 @@
154
156
  "/views/profile/agents-services-subtab.js": "/views/profile/agents-services-subtab.js",
155
157
  "/views/profile/agents-messages-subtab.js": "/views/profile/agents-messages-subtab.js",
156
158
  "/js/services/agent-services.js": "/js/services/agent-services.js",
157
- "/js/services/agent-messages.js": "/js/services/agent-messages.js"
159
+ "/js/services/agent-messages.js": "/js/services/agent-messages.js",
160
+ "/js/services/agent-integration.js": "/js/services/agent-integration.js",
161
+ "/js/services/admin-agent-integration.js": "/js/services/admin-agent-integration.js"
158
162
  }
159
163
  }
160
164
  </script>
@@ -225,6 +229,7 @@
225
229
  '/v1/portfolio': () => import('/views/portfolio.js' + B),
226
230
  '/v1/admin': () => import('/views/admin.js' + B),
227
231
  '/v1/help': () => import('/views/help.js' + B),
232
+ '/v1/publicknowledgeviewer': () => import('/views/public-knowledge-viewer.js' + B),
228
233
  };
229
234
 
230
235
  // Match route, handling query params for special views
@@ -0,0 +1,390 @@
1
+ /**
2
+ * @file agent-integration-tab.js
3
+ * @description Admin dashboard Agent Integration tab. Four sections:
4
+ * Platform Registry, Onboarding Overview (with readiness distribution),
5
+ * Skill Bundle Management, Bundle Templates.
6
+ * @version-history
7
+ * v1.0.0 -- 2026-05-24 -- Initial creation for Governance Phase C
8
+ * v1.1.0 -- 2026-05-24 -- Fix M8 M9 F19 F20 F21 audit findings
9
+ * v1.2.0 -- 2026-05-24 -- Move readiness into onboarding, add bundle templates, add notify button
10
+ */
11
+ import { h } from 'preact';
12
+ import { useState, useEffect, useCallback } from 'preact/hooks';
13
+ import htm from 'htm';
14
+ const html = htm.bind(h);
15
+ import { t } from '/js/i18n.js';
16
+ import { useViewCSS } from '/components/useViewCSS.js';
17
+ import { num, dt, Empty } from './shared.js';
18
+ import * as api from '/js/services/admin-agent-integration.js';
19
+
20
+
21
+ export default function AgentIntegrationTab({ data, session }) {
22
+ useViewCSS('/css/views/admin-agent-integration.css');
23
+ const [platforms, setPlatforms] = useState([]);
24
+ const [onboarding, setOnboarding] = useState(null);
25
+ const [readiness, setReadiness] = useState(null);
26
+ const [bundles, setBundles] = useState(null);
27
+ const [loading, setLoading] = useState(true);
28
+ const [regenerating, setRegenerating] = useState(false);
29
+
30
+ const loadData = useCallback(async () => {
31
+ if (!session) return;
32
+ try {
33
+ const [platRes, onbRes, readRes, bundRes] = await Promise.all([
34
+ api.getPlatforms(session),
35
+ api.getOnboardingOverview(session),
36
+ api.getReadinessDistribution(session),
37
+ api.getSkillBundles(session),
38
+ ]);
39
+ setPlatforms(platRes.data?.platforms || []);
40
+ setOnboarding(onbRes.data || null);
41
+ setReadiness(readRes.data || null);
42
+ setBundles(bundRes.data?.bundles || null);
43
+ } catch { /* API unavailable */ }
44
+ setLoading(false);
45
+ }, [session]);
46
+
47
+ useEffect(() => { loadData(); }, [loadData]);
48
+
49
+ useEffect(() => {
50
+ const handler = () => { loadData(); };
51
+ window.addEventListener('aimeat-live-update', handler);
52
+ return () => window.removeEventListener('aimeat-live-update', handler);
53
+ }, [loadData]);
54
+
55
+ if (loading) return html`<div class="adm-card">${t('common.loading')}</div>`;
56
+
57
+ const handleRegenerate = async () => {
58
+ setRegenerating(true);
59
+ try {
60
+ await api.regenerateBundles(session);
61
+ await loadData();
62
+ } catch { /* ignore */ }
63
+ setRegenerating(false);
64
+ };
65
+
66
+ const totalAgents = platforms.reduce((sum, p) => sum + (p.agent_count || 0), 0);
67
+
68
+ return html`
69
+ <div>
70
+ ${renderPlatformRegistry(platforms, totalAgents, session, loadData)}
71
+ ${renderOnboardingOverview(onboarding, readiness, session, loadData)}
72
+ ${renderSkillBundles(bundles, handleRegenerate, regenerating, session)}
73
+ ${renderBundleTemplates()}
74
+ </div>
75
+ `;
76
+ }
77
+
78
+ /* ── M8: Merged Platform column (name + ID as secondary text) ── */
79
+ function renderPlatformRegistry(platforms, totalAgents, session, loadData) {
80
+ const [showForm, setShowForm] = useState(false);
81
+
82
+ return html`
83
+ <div class="adm-agi-section">
84
+ <div class="adm-agi-section-title">${t('admin.agentIntegration.platformRegistry')}</div>
85
+ ${platforms.length === 0
86
+ ? html`<${Empty} text=${t('admin.agentIntegration.noPlatforms')} />`
87
+ : html`
88
+ <table class="adm-agi-table">
89
+ <thead><tr>
90
+ <th>${t('admin.agentIntegration.platform')}</th>
91
+ <th>${t('admin.agentIntegration.agentCount')}</th>
92
+ <th>${t('admin.agentIntegration.adapter')}</th>
93
+ <th>${t('admin.agentIntegration.detectPattern')}</th>
94
+ </tr></thead>
95
+ <tbody>
96
+ ${platforms.map(p => html`
97
+ <tr>
98
+ <td>
99
+ <div>${p.display_name}</div>
100
+ <div class="adm-agi-platform-id">${p.id}</div>
101
+ </td>
102
+ <td>${num(p.agent_count)}</td>
103
+ <td class="adm-agi-mono-sm">${p.bundle_name}</td>
104
+ <td class="adm-agi-mono-sm">${p.detect_pattern || '--'}</td>
105
+ </tr>
106
+ `)}
107
+ </tbody>
108
+ </table>
109
+ <div class="adm-agi-footer">
110
+ ${t('admin.agentIntegration.totalAgents')}: ${num(totalAgents)}
111
+ </div>
112
+ `
113
+ }
114
+ ${showForm
115
+ ? html`<${AddPlatformForm} session=${session} onDone=${() => { setShowForm(false); loadData(); }} onCancel=${() => setShowForm(false)} />`
116
+ : html`<button class="btn-outline adm-agi-add-btn" onClick=${() => setShowForm(true)}>
117
+ ${t('admin.agentIntegration.addPlatform')}
118
+ </button>`
119
+ }
120
+ </div>
121
+ `;
122
+ }
123
+
124
+ /* ── F19: Inline Add Platform form ── */
125
+ function AddPlatformForm({ session, onDone, onCancel }) {
126
+ const [name, setName] = useState('');
127
+ const [id, setId] = useState('');
128
+ const [adapter, setAdapter] = useState('');
129
+ const [pattern, setPattern] = useState('');
130
+ const [submitting, setSubmitting] = useState(false);
131
+
132
+ const handleSubmit = async () => {
133
+ if (!name.trim() || !id.trim()) return;
134
+ setSubmitting(true);
135
+ try {
136
+ await api.registerPlatform(session, {
137
+ id: id.trim(),
138
+ display_name: name.trim(),
139
+ bundle_name: adapter.trim() || undefined,
140
+ detect_pattern: pattern.trim() || undefined,
141
+ });
142
+ onDone();
143
+ } catch { /* ignore */ }
144
+ setSubmitting(false);
145
+ };
146
+
147
+ return html`
148
+ <div class="adm-agi-add-form">
149
+ <div class="adm-agi-add-form-row">
150
+ <label class="adm-agi-add-form-label">${t('admin.agentIntegration.platformName')}</label>
151
+ <input class="adm-agi-add-form-input" value=${name} onInput=${e => setName(e.target.value)} placeholder=${t('admin.agentIntegration.placeholders.name')} />
152
+ </div>
153
+ <div class="adm-agi-add-form-row">
154
+ <label class="adm-agi-add-form-label">${t('admin.agentIntegration.platformId')}</label>
155
+ <input class="adm-agi-add-form-input" value=${id} onInput=${e => setId(e.target.value)} placeholder=${t('admin.agentIntegration.placeholders.id')} />
156
+ </div>
157
+ <div class="adm-agi-add-form-row">
158
+ <label class="adm-agi-add-form-label">${t('admin.agentIntegration.adapterPattern')}</label>
159
+ <input class="adm-agi-add-form-input" value=${adapter} onInput=${e => setAdapter(e.target.value)} placeholder=${t('admin.agentIntegration.placeholders.adapter')} />
160
+ </div>
161
+ <div class="adm-agi-add-form-row">
162
+ <label class="adm-agi-add-form-label">${t('admin.agentIntegration.autoDetectRegex')}</label>
163
+ <input class="adm-agi-add-form-input" value=${pattern} onInput=${e => setPattern(e.target.value)} placeholder=${t('admin.agentIntegration.placeholders.regex')} />
164
+ </div>
165
+ <div class="adm-agi-add-form-actions">
166
+ <button class="btn-primary" onClick=${handleSubmit} disabled=${submitting || !name.trim() || !id.trim()}>
167
+ ${submitting ? t('common.loading') : t('common.add')}
168
+ </button>
169
+ <button class="btn-ghost" onClick=${onCancel}>${t('common.cancel')}</button>
170
+ </div>
171
+ </div>
172
+ `;
173
+ }
174
+
175
+ /* ── M9: Not Started stat card + F20: Stuck section with suggestions + actions ── */
176
+ function renderOnboardingOverview(onboarding, readiness, session, loadData) {
177
+ if (!onboarding) return html`<div class="adm-agi-section"><${Empty} text=${t('common.noData')} /></div>`;
178
+
179
+ const stuck = onboarding.stuck || [];
180
+ const notStarted = onboarding.not_started || 0;
181
+ const readinessTotal = (onboarding.completed || 0) + (onboarding.in_progress || 0) + notStarted;
182
+
183
+ return html`
184
+ <div class="adm-agi-section">
185
+ <div class="adm-agi-section-title">${t('admin.agentIntegration.onboardingOverview')}</div>
186
+ <div class="adm-agi-stats">
187
+ <div class="adm-agi-stat-card">
188
+ <div class="adm-agi-stat-value adm-agi-stat-value--success">${num(onboarding.completed)}</div>
189
+ <div class="adm-agi-stat-label">${t('admin.agentIntegration.completed')}</div>
190
+ </div>
191
+ <div class="adm-agi-stat-card">
192
+ <div class="adm-agi-stat-value adm-agi-stat-value--accent">${num(onboarding.in_progress)}</div>
193
+ <div class="adm-agi-stat-label">${t('admin.agentIntegration.inProgress')}</div>
194
+ </div>
195
+ <div class="adm-agi-stat-card">
196
+ <div class="adm-agi-stat-value">${num(notStarted)}</div>
197
+ <div class="adm-agi-stat-label">${t('admin.agentIntegration.notStarted')}</div>
198
+ </div>
199
+ </div>
200
+ ${stuck.length > 0 ? html`
201
+ <div class="adm-agi-stuck-title">${t('admin.agentIntegration.stuckAgents')}</div>
202
+ <div class="adm-agi-stuck">
203
+ ${stuck.map(s => html`
204
+ <${StuckAgentRow} agent=${s} session=${session} onAction=${loadData} />
205
+ `)}
206
+ </div>
207
+ ` : ''}
208
+ ${renderReadinessDistribution(readiness, readinessTotal)}
209
+ </div>
210
+ `;
211
+ }
212
+
213
+ /* ── F20: Single stuck agent row with suggestion + actions ── */
214
+ function StuckAgentRow({ agent, session, onAction }) {
215
+ const [acting, setActing] = useState(false);
216
+ const suggestion = getSuggestion(agent.current_step);
217
+
218
+ const handleRemind = async () => {
219
+ setActing(true);
220
+ try {
221
+ await api.sendReminder(session, agent.agent_gaii);
222
+ onAction();
223
+ } catch { /* ignore */ }
224
+ setActing(false);
225
+ };
226
+
227
+ const handleSkip = async () => {
228
+ setActing(true);
229
+ try {
230
+ await api.skipOnboardingStep(session, agent.agent_gaii, agent.current_step);
231
+ onAction();
232
+ } catch { /* ignore */ }
233
+ setActing(false);
234
+ };
235
+
236
+ return html`
237
+ <div class="adm-agi-stuck-agent">
238
+ <div class="adm-agi-stuck-info">
239
+ <div>
240
+ <span class="adm-agi-stuck-name">${agent.agent_gaii}</span>
241
+ <div class="adm-agi-stuck-detail">${agent.current_step}</div>
242
+ </div>
243
+ <div class="adm-agi-stuck-detail">
244
+ ${t('admin.agentIntegration.stuckSince')}: ${dt(agent.stuck_since)}
245
+ </div>
246
+ <div class="adm-agi-stuck-suggestion">${suggestion}</div>
247
+ </div>
248
+ <div class="adm-agi-stuck-actions">
249
+ <button class="btn-outline adm-agi-stuck-btn" onClick=${handleRemind} disabled=${acting}>
250
+ ${t('admin.agentIntegration.sendReminder')}
251
+ </button>
252
+ <button class="btn-ghost adm-agi-stuck-btn" onClick=${handleSkip} disabled=${acting}>
253
+ ${t('admin.agentIntegration.skipStep')}
254
+ </button>
255
+ </div>
256
+ </div>
257
+ `;
258
+ }
259
+
260
+ function getSuggestion(stepId) {
261
+ if (stepId?.includes('webhook') || stepId?.includes('delivery'))
262
+ return t('admin.agentIntegration.suggestion.webhook');
263
+ if (stepId?.includes('platform') || stepId?.includes('detect'))
264
+ return t('admin.agentIntegration.suggestion.platform');
265
+ if (stepId?.includes('skill') || stepId?.includes('bundle'))
266
+ return t('admin.agentIntegration.suggestion.skillBundle');
267
+ return t('admin.agentIntegration.suggestion.default');
268
+ }
269
+
270
+ function renderReadinessDistribution(readiness, total) {
271
+ if (!readiness) return '';
272
+ const dist = readiness.distribution || {};
273
+ const levels = ['expert', 'full', 'standard', 'basic'];
274
+
275
+ return html`
276
+ <div class="adm-agi-subsection">
277
+ <div class="adm-agi-section-title">${t('admin.agentIntegration.readinessDistribution')}</div>
278
+ ${total === 0
279
+ ? html`<div class="adm-agi-empty-text">${t('common.noData')}</div>`
280
+ : html`
281
+ ${levels.map(level => {
282
+ const count = dist[level] || 0;
283
+ const pct = total > 0 ? (count / total * 100) : 0;
284
+ return html`
285
+ <div class="adm-agi-readiness-bar">
286
+ <div class="adm-agi-readiness-level">${t(`agentOnboarding.readiness.${level}`)}</div>
287
+ <div class="adm-agi-readiness-track">
288
+ <div class="adm-agi-readiness-fill" style="width:${pct}%" data-level="${level}"></div>
289
+ </div>
290
+ <div class="adm-agi-readiness-count">${count}</div>
291
+ </div>
292
+ `;
293
+ })}
294
+ <div class="adm-agi-footer">
295
+ ${t('admin.agentIntegration.totalAgents')}: ${num(total)}
296
+ </div>
297
+ `
298
+ }
299
+ </div>
300
+ `;
301
+ }
302
+
303
+ /* ── F21: Added "Current version" column to skill bundle table ── */
304
+ function renderSkillBundles(bundles, onRegenerate, regenerating, session) {
305
+ if (!bundles) return '';
306
+ const entries = Object.entries(bundles);
307
+
308
+ const handleNotify = async (platform) => {
309
+ try {
310
+ await api.notifyOutdatedAgents(session, platform);
311
+ window.dispatchEvent(new CustomEvent('aimeat-toast', { detail: { message: t('admin.agentIntegration.notifySuccess') } }));
312
+ } catch { /* ignore */ }
313
+ };
314
+
315
+ return html`
316
+ <div class="adm-agi-section">
317
+ <div class="adm-agi-section-title">${t('admin.agentIntegration.skillBundles')}</div>
318
+ ${entries.length === 0
319
+ ? html`<div class="adm-agi-empty-text">${t('common.noData')}</div>`
320
+ : html`
321
+ <table class="adm-agi-table">
322
+ <thead><tr>
323
+ <th>${t('admin.agentIntegration.platform')}</th>
324
+ <th>${t('admin.agentIntegration.agentCount')}</th>
325
+ <th>${t('admin.agentIntegration.currentVersion')}</th>
326
+ <th>${t('admin.agentIntegration.outdated')}</th>
327
+ <th></th>
328
+ </tr></thead>
329
+ <tbody>
330
+ ${entries.map(([platform, info]) => html`
331
+ <tr>
332
+ <td>${platform}</td>
333
+ <td>${num(info.agents)}</td>
334
+ <td class="adm-agi-mono-sm">${info.version || info.currentVersion || '--'}</td>
335
+ <td>${info.outdated > 0
336
+ ? html`<span class="adm-agi-outdated">${num(info.outdated)}</span>`
337
+ : html`<span class="adm-agi-zero">0</span>`
338
+ }</td>
339
+ <td>${info.outdated > 0 ? html`
340
+ <button class="btn-outline adm-agi-notify-btn" onClick=${() => handleNotify(platform)}>
341
+ ${t('admin.agentIntegration.notifyOutdated')}
342
+ </button>
343
+ ` : ''}</td>
344
+ </tr>
345
+ `)}
346
+ </tbody>
347
+ </table>
348
+ `
349
+ }
350
+ <button class="btn-outline adm-agi-regen-btn" onClick=${onRegenerate} disabled=${regenerating}>
351
+ ${regenerating ? t('common.loading') : t('admin.agentIntegration.regenerateAll')}
352
+ </button>
353
+ </div>
354
+ `;
355
+ }
356
+
357
+ /* ── Bundle Templates (Phase C stub) ── */
358
+ function renderBundleTemplates() {
359
+ const handleComingSoon = () => {
360
+ window.dispatchEvent(new CustomEvent('aimeat-toast', { detail: { message: t('admin.agentIntegration.comingSoon') } }));
361
+ };
362
+
363
+ return html`
364
+ <div class="adm-agi-section">
365
+ <div class="adm-agi-section-title">${t('admin.agentIntegration.bundleTemplates')}</div>
366
+ <div class="adm-agi-stats">
367
+ <div class="adm-agi-stat-card">
368
+ <div class="adm-agi-stat-value">0</div>
369
+ <div class="adm-agi-stat-label">${t('admin.agentIntegration.customReferences')}</div>
370
+ </div>
371
+ <div class="adm-agi-stat-card">
372
+ <div class="adm-agi-stat-value">0</div>
373
+ <div class="adm-agi-stat-label">${t('admin.agentIntegration.customScripts')}</div>
374
+ </div>
375
+ <div class="adm-agi-stat-card">
376
+ <div class="adm-agi-stat-value adm-agi-mono-sm">--</div>
377
+ <div class="adm-agi-stat-label">${t('admin.agentIntegration.lastGenerated')}</div>
378
+ </div>
379
+ </div>
380
+ <div class="adm-agi-btn-row">
381
+ <button class="btn-outline" onClick=${handleComingSoon}>
382
+ ${t('admin.agentIntegration.customizeBundle')}
383
+ </button>
384
+ <button class="btn-outline" onClick=${handleComingSoon}>
385
+ ${t('admin.agentIntegration.previewBundle')}
386
+ </button>
387
+ </div>
388
+ </div>
389
+ `;
390
+ }
@@ -50,6 +50,7 @@ import GeneratorDebugTab from './admin/generator-debug-tab.js';
50
50
  import CapabilitiesAdminTab from './admin/capabilities-tab.js';
51
51
  import AgentTasksAdminTab from './admin/agent-tasks-tab.js';
52
52
  import SharingGroupsAdminTab from './admin/sharing-groups-tab.js';
53
+ import AgentIntegrationAdminTab from './admin/agent-integration-tab.js';
53
54
 
54
55
  // ── Sidebar nav structure ──
55
56
  const NAV_GROUPS = [
@@ -68,6 +69,7 @@ const NAV_GROUPS = [
68
69
  { id: 'owners', icon: '\u{1F464}', key: 'dashboard.owners', component: OwnersTab, count: 'owners' },
69
70
  { id: 'agents', icon: '\u{1F916}', key: 'dashboard.agents', component: AgentsTab, count: 'agents' },
70
71
  { id: 'ghii', icon: '\u{1F511}', key: 'dashboard.ghii', component: GhiiTab, count: 'ghii' },
72
+ { id: 'agent-integration', icon: '\u{1F50C}', key: 'admin.tabs.agentIntegration', component: AgentIntegrationAdminTab },
71
73
  ]},
72
74
  { key: 'dashboard.navData', items: [
73
75
  { id: 'actions', icon: '\u26A1', key: 'dashboard.actions', component: ActionsTab, count: 'actions' },