aimeat 1.33.2 → 1.35.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 (328) hide show
  1. package/dist/locales/en.json +437 -15
  2. package/dist/locales/fi.json +437 -15
  3. package/dist/public/cortex-bundled/aimeat-charts.js +3 -3
  4. package/dist/public/cortex-bundled/aimeat-charts.yaml +3 -3
  5. package/dist/public/cortex-bundled/aimeat-i18n.js +123 -0
  6. package/dist/public/cortex-bundled/aimeat-i18n.yaml +64 -0
  7. package/dist/public/css/views/landing.css +2 -0
  8. package/dist/public/css/views/secretary.css +464 -2
  9. package/dist/public/js/i18n.js +9 -0
  10. package/dist/public/js/services/apps.js +4 -2
  11. package/dist/public/js/services/memory.js +9 -3
  12. package/dist/public/js/services/offers.js +1 -1
  13. package/dist/public/js/services/organisms.js +49 -17
  14. package/dist/public/js/services/secretary-helpers.js +266 -17
  15. package/dist/public/js/services/secretary-policy.js +28 -2
  16. package/dist/public/js/services/secretary-rules.js +70 -0
  17. package/dist/public/js/theme.js +20 -0
  18. package/dist/public/lib/VENDORED-STYLING.md +31 -0
  19. package/dist/public/lib/aimeat-daisyui-bridge.css +48 -0
  20. package/dist/public/lib/chartjs@4.js +14 -0
  21. package/dist/public/lib/daisyui@5.css +7 -0
  22. package/dist/public/lib/tailwindcss@4.js +8 -0
  23. package/dist/public/llms-template.txt +9 -6
  24. package/dist/public/showroom.html +104 -0
  25. package/dist/public/showroom.js +247 -0
  26. package/dist/public/spa.html +18 -16
  27. package/dist/public/views/aimeat-os.js +3 -2
  28. package/dist/public/views/landing.js +50 -6
  29. package/dist/public/views/profile/agents/agent-card.js +13 -4
  30. package/dist/public/views/profile/agents/state-detector.js +6 -0
  31. package/dist/public/views/profile/agents/tab-integration.js +11 -2
  32. package/dist/public/views/profile/apps-tab.js +46 -0
  33. package/dist/public/views/profile/extensions-tab.js +4 -0
  34. package/dist/public/views/profile/inbox-tab.js +1 -1
  35. package/dist/public/views/profile/landing-page.js +8 -3
  36. package/dist/public/views/profile/memory-tab.js +37 -6
  37. package/dist/public/views/profile/organisms/home.js +32 -0
  38. package/dist/public/views/profile/organisms/workspace-list.js +36 -5
  39. package/dist/public/views/profile/organisms/workspace.js +29 -4
  40. package/dist/public/views/profile/organisms-tab.js +61 -8
  41. package/dist/public/views/secretary/calendar.js +327 -0
  42. package/dist/public/views/secretary/cards-reach.js +16 -17
  43. package/dist/public/views/secretary/cards.js +217 -79
  44. package/dist/public/views/secretary/dashboard.js +617 -0
  45. package/dist/public/views/secretary/quality.js +156 -0
  46. package/dist/public/views/secretary/use-autonomy.js +17 -2
  47. package/dist/public/views/secretary/use-freshness.js +50 -0
  48. package/dist/public/views/secretary/use-intake.js +23 -4
  49. package/dist/public/views/secretary/use-layout.js +122 -0
  50. package/dist/public/views/secretary/use-quick-actions.js +98 -0
  51. package/dist/public/views/secretary/use-specialists.js +103 -0
  52. package/dist/public/views/secretary/use-triggers.js +159 -0
  53. package/dist/public/views/secretary/use-whats-next.js +340 -0
  54. package/dist/public/views/secretary/workflow-design.js +112 -0
  55. package/dist/public/views/secretary.js +836 -44
  56. package/dist/scripts/check-no-max-tokens.d.ts +2 -0
  57. package/dist/scripts/check-no-max-tokens.d.ts.map +1 -0
  58. package/dist/scripts/check-no-max-tokens.js +71 -0
  59. package/dist/scripts/check-no-max-tokens.js.map +1 -0
  60. package/dist/src/cli/connect/mcp/tools/organisms.d.ts +2 -0
  61. package/dist/src/cli/connect/mcp/tools/organisms.d.ts.map +1 -1
  62. package/dist/src/cli/connect/mcp/tools/organisms.js +9 -1
  63. package/dist/src/cli/connect/mcp/tools/organisms.js.map +1 -1
  64. package/dist/src/cli/connect/onboarding-prompt.d.ts +3 -0
  65. package/dist/src/cli/connect/onboarding-prompt.d.ts.map +1 -1
  66. package/dist/src/cli/connect/onboarding-prompt.js +10 -3
  67. package/dist/src/cli/connect/onboarding-prompt.js.map +1 -1
  68. package/dist/src/data/aimeat-iam-package.d.ts +25 -0
  69. package/dist/src/data/aimeat-iam-package.d.ts.map +1 -0
  70. package/dist/src/data/aimeat-iam-package.js +312 -0
  71. package/dist/src/data/aimeat-iam-package.js.map +1 -0
  72. package/dist/src/data/aimeat-marketplace-package.d.ts +25 -0
  73. package/dist/src/data/aimeat-marketplace-package.d.ts.map +1 -0
  74. package/dist/src/data/aimeat-marketplace-package.js +231 -0
  75. package/dist/src/data/aimeat-marketplace-package.js.map +1 -0
  76. package/dist/src/data/app-templates.d.ts +48 -0
  77. package/dist/src/data/app-templates.d.ts.map +1 -0
  78. package/dist/src/data/app-templates.js +856 -0
  79. package/dist/src/data/app-templates.js.map +1 -0
  80. package/dist/src/data/example-packages.d.ts.map +1 -1
  81. package/dist/src/data/example-packages.js +3 -1
  82. package/dist/src/data/example-packages.js.map +1 -1
  83. package/dist/src/generated/api-types.d.ts +689 -7
  84. package/dist/src/generated/api-types.d.ts.map +1 -1
  85. package/dist/src/generated/prisma-postgres/edge.js +10 -3
  86. package/dist/src/generated/prisma-postgres/index-browser.js +7 -0
  87. package/dist/src/generated/prisma-postgres/index.d.ts +205 -2
  88. package/dist/src/generated/prisma-postgres/index.js +10 -3
  89. package/dist/src/generated/prisma-postgres/package.json +1 -1
  90. package/dist/src/generated/prisma-postgres/schema.prisma +23 -13
  91. package/dist/src/generated/prisma-postgres/wasm.js +10 -3
  92. package/dist/src/mcp/agent-onboarding.d.ts +4 -0
  93. package/dist/src/mcp/agent-onboarding.d.ts.map +1 -1
  94. package/dist/src/mcp/agent-onboarding.js +28 -2
  95. package/dist/src/mcp/agent-onboarding.js.map +1 -1
  96. package/dist/src/mcp/annotations.d.ts.map +1 -1
  97. package/dist/src/mcp/annotations.js +1 -0
  98. package/dist/src/mcp/annotations.js.map +1 -1
  99. package/dist/src/mcp/catalog/definitions.d.ts.map +1 -1
  100. package/dist/src/mcp/catalog/definitions.js +16 -1
  101. package/dist/src/mcp/catalog/definitions.js.map +1 -1
  102. package/dist/src/mcp/catalog/surfaces.js +3 -3
  103. package/dist/src/mcp/catalog/surfaces.js.map +1 -1
  104. package/dist/src/mcp/index.d.ts +3 -0
  105. package/dist/src/mcp/index.d.ts.map +1 -1
  106. package/dist/src/mcp/index.js +11 -0
  107. package/dist/src/mcp/index.js.map +1 -1
  108. package/dist/src/mcp/organisms.d.ts.map +1 -1
  109. package/dist/src/mcp/organisms.js +48 -8
  110. package/dist/src/mcp/organisms.js.map +1 -1
  111. package/dist/src/mcp/workspaces.d.ts.map +1 -1
  112. package/dist/src/mcp/workspaces.js +6 -4
  113. package/dist/src/mcp/workspaces.js.map +1 -1
  114. package/dist/src/models/agent-onboarding-schemas.d.ts +47 -0
  115. package/dist/src/models/agent-onboarding-schemas.d.ts.map +1 -1
  116. package/dist/src/models/agent-onboarding-schemas.js +117 -0
  117. package/dist/src/models/agent-onboarding-schemas.js.map +1 -1
  118. package/dist/src/models/offer-schemas.d.ts +8 -8
  119. package/dist/src/routes/agent-onboarding.d.ts +5 -0
  120. package/dist/src/routes/agent-onboarding.d.ts.map +1 -1
  121. package/dist/src/routes/agent-onboarding.js +21 -2
  122. package/dist/src/routes/agent-onboarding.js.map +1 -1
  123. package/dist/src/routes/ai.d.ts +8 -4
  124. package/dist/src/routes/ai.d.ts.map +1 -1
  125. package/dist/src/routes/ai.js +38 -14
  126. package/dist/src/routes/ai.js.map +1 -1
  127. package/dist/src/routes/app-grants.d.ts +2 -0
  128. package/dist/src/routes/app-grants.d.ts.map +1 -1
  129. package/dist/src/routes/app-grants.js +7 -1
  130. package/dist/src/routes/app-grants.js.map +1 -1
  131. package/dist/src/routes/app-templates.d.ts +16 -0
  132. package/dist/src/routes/app-templates.d.ts.map +1 -0
  133. package/dist/src/routes/app-templates.js +40 -0
  134. package/dist/src/routes/app-templates.js.map +1 -0
  135. package/dist/src/routes/apps.d.ts.map +1 -1
  136. package/dist/src/routes/apps.js +55 -5
  137. package/dist/src/routes/apps.js.map +1 -1
  138. package/dist/src/routes/auth.d.ts +3 -0
  139. package/dist/src/routes/auth.d.ts.map +1 -1
  140. package/dist/src/routes/auth.js +9 -4
  141. package/dist/src/routes/auth.js.map +1 -1
  142. package/dist/src/routes/bootstrap.d.ts.map +1 -1
  143. package/dist/src/routes/bootstrap.js +1 -0
  144. package/dist/src/routes/bootstrap.js.map +1 -1
  145. package/dist/src/routes/generator.d.ts.map +1 -1
  146. package/dist/src/routes/generator.js +3 -2
  147. package/dist/src/routes/generator.js.map +1 -1
  148. package/dist/src/routes/lib-ai.d.ts +0 -1
  149. package/dist/src/routes/lib-ai.d.ts.map +1 -1
  150. package/dist/src/routes/lib-ai.js +13 -6
  151. package/dist/src/routes/lib-ai.js.map +1 -1
  152. package/dist/src/routes/lib-header.d.ts +2 -0
  153. package/dist/src/routes/lib-header.d.ts.map +1 -1
  154. package/dist/src/routes/lib-header.js +7 -16
  155. package/dist/src/routes/lib-header.js.map +1 -1
  156. package/dist/src/routes/lib-markdown.d.ts +18 -0
  157. package/dist/src/routes/lib-markdown.d.ts.map +1 -0
  158. package/dist/src/routes/lib-markdown.js +236 -0
  159. package/dist/src/routes/lib-markdown.js.map +1 -0
  160. package/dist/src/routes/libs.d.ts +22 -0
  161. package/dist/src/routes/libs.d.ts.map +1 -1
  162. package/dist/src/routes/libs.js +133 -10
  163. package/dist/src/routes/libs.js.map +1 -1
  164. package/dist/src/routes/memory.d.ts.map +1 -1
  165. package/dist/src/routes/memory.js +21 -2
  166. package/dist/src/routes/memory.js.map +1 -1
  167. package/dist/src/routes/openrouter.d.ts +4 -0
  168. package/dist/src/routes/openrouter.d.ts.map +1 -1
  169. package/dist/src/routes/openrouter.js +17 -0
  170. package/dist/src/routes/openrouter.js.map +1 -1
  171. package/dist/src/routes/organisms.d.ts.map +1 -1
  172. package/dist/src/routes/organisms.js +107 -7
  173. package/dist/src/routes/organisms.js.map +1 -1
  174. package/dist/src/routes/portal.d.ts +9 -0
  175. package/dist/src/routes/portal.d.ts.map +1 -1
  176. package/dist/src/routes/portal.js +1 -1
  177. package/dist/src/routes/portal.js.map +1 -1
  178. package/dist/src/routes/public-stats.d.ts +3 -0
  179. package/dist/src/routes/public-stats.d.ts.map +1 -1
  180. package/dist/src/routes/public-stats.js +9 -2
  181. package/dist/src/routes/public-stats.js.map +1 -1
  182. package/dist/src/routes/secretary.d.ts +34 -0
  183. package/dist/src/routes/secretary.d.ts.map +1 -0
  184. package/dist/src/routes/secretary.js +311 -0
  185. package/dist/src/routes/secretary.js.map +1 -0
  186. package/dist/src/routes/specialists.d.ts.map +1 -1
  187. package/dist/src/routes/specialists.js +21 -0
  188. package/dist/src/routes/specialists.js.map +1 -1
  189. package/dist/src/routes/subdomains.js +1 -1
  190. package/dist/src/routes/subdomains.js.map +1 -1
  191. package/dist/src/routes/workflows.d.ts +9 -3
  192. package/dist/src/routes/workflows.d.ts.map +1 -1
  193. package/dist/src/routes/workflows.js +9 -3
  194. package/dist/src/routes/workflows.js.map +1 -1
  195. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  196. package/dist/src/server-bootstrap/routes-loader.js +4 -0
  197. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  198. package/dist/src/server-bootstrap/service-init.d.ts.map +1 -1
  199. package/dist/src/server-bootstrap/service-init.js +7 -0
  200. package/dist/src/server-bootstrap/service-init.js.map +1 -1
  201. package/dist/src/server-bootstrap/static-files.d.ts +4 -0
  202. package/dist/src/server-bootstrap/static-files.d.ts.map +1 -1
  203. package/dist/src/server-bootstrap/static-files.js +18 -0
  204. package/dist/src/server-bootstrap/static-files.js.map +1 -1
  205. package/dist/src/services/archive.d.ts +74 -0
  206. package/dist/src/services/archive.d.ts.map +1 -0
  207. package/dist/src/services/archive.js +132 -0
  208. package/dist/src/services/archive.js.map +1 -0
  209. package/dist/src/services/cortex-seeder.d.ts +4 -1
  210. package/dist/src/services/cortex-seeder.d.ts.map +1 -1
  211. package/dist/src/services/cortex-seeder.js +25 -6
  212. package/dist/src/services/cortex-seeder.js.map +1 -1
  213. package/dist/src/services/directory.d.ts.map +1 -1
  214. package/dist/src/services/directory.js +3 -2
  215. package/dist/src/services/directory.js.map +1 -1
  216. package/dist/src/services/generator-prompt-seeds.d.ts.map +1 -1
  217. package/dist/src/services/generator-prompt-seeds.js +5 -4
  218. package/dist/src/services/generator-prompt-seeds.js.map +1 -1
  219. package/dist/src/services/message-send.d.ts.map +1 -1
  220. package/dist/src/services/message-send.js +14 -0
  221. package/dist/src/services/message-send.js.map +1 -1
  222. package/dist/src/services/onboarding-guide.d.ts +53 -0
  223. package/dist/src/services/onboarding-guide.d.ts.map +1 -0
  224. package/dist/src/services/onboarding-guide.js +81 -0
  225. package/dist/src/services/onboarding-guide.js.map +1 -0
  226. package/dist/src/services/organism-search.d.ts +6 -2
  227. package/dist/src/services/organism-search.d.ts.map +1 -1
  228. package/dist/src/services/organism-search.js +2 -2
  229. package/dist/src/services/organism-search.js.map +1 -1
  230. package/dist/src/services/owner-memory.d.ts +1 -0
  231. package/dist/src/services/owner-memory.d.ts.map +1 -1
  232. package/dist/src/services/owner-memory.js.map +1 -1
  233. package/dist/src/services/owner-session.d.ts +1 -0
  234. package/dist/src/services/owner-session.d.ts.map +1 -1
  235. package/dist/src/services/owner-session.js +10 -2
  236. package/dist/src/services/owner-session.js.map +1 -1
  237. package/dist/src/services/package-seeder.d.ts +22 -0
  238. package/dist/src/services/package-seeder.d.ts.map +1 -0
  239. package/dist/src/services/package-seeder.js +30 -0
  240. package/dist/src/services/package-seeder.js.map +1 -0
  241. package/dist/src/services/presence.d.ts +3 -0
  242. package/dist/src/services/presence.d.ts.map +1 -1
  243. package/dist/src/services/presence.js +10 -0
  244. package/dist/src/services/presence.js.map +1 -1
  245. package/dist/src/services/prompt-defaults.d.ts.map +1 -1
  246. package/dist/src/services/prompt-defaults.js +8 -3
  247. package/dist/src/services/prompt-defaults.js.map +1 -1
  248. package/dist/src/services/scheduler.d.ts +50 -2
  249. package/dist/src/services/scheduler.d.ts.map +1 -1
  250. package/dist/src/services/scheduler.js +454 -26
  251. package/dist/src/services/scheduler.js.map +1 -1
  252. package/dist/src/services/secretary-authoring.d.ts +71 -0
  253. package/dist/src/services/secretary-authoring.d.ts.map +1 -0
  254. package/dist/src/services/secretary-authoring.js +336 -0
  255. package/dist/src/services/secretary-authoring.js.map +1 -0
  256. package/dist/src/services/secretary-clarify.d.ts +57 -0
  257. package/dist/src/services/secretary-clarify.d.ts.map +1 -0
  258. package/dist/src/services/secretary-clarify.js +104 -0
  259. package/dist/src/services/secretary-clarify.js.map +1 -0
  260. package/dist/src/services/secretary-feed.d.ts +11 -0
  261. package/dist/src/services/secretary-feed.d.ts.map +1 -0
  262. package/dist/src/services/secretary-feed.js +28 -0
  263. package/dist/src/services/secretary-feed.js.map +1 -0
  264. package/dist/src/services/secretary-tick.d.ts +243 -3
  265. package/dist/src/services/secretary-tick.d.ts.map +1 -1
  266. package/dist/src/services/secretary-tick.js +273 -3
  267. package/dist/src/services/secretary-tick.js.map +1 -1
  268. package/dist/src/services/secretary-workbrief.d.ts +144 -0
  269. package/dist/src/services/secretary-workbrief.d.ts.map +1 -0
  270. package/dist/src/services/secretary-workbrief.js +315 -0
  271. package/dist/src/services/secretary-workbrief.js.map +1 -0
  272. package/dist/src/services/secretary-worktier.d.ts +118 -0
  273. package/dist/src/services/secretary-worktier.d.ts.map +1 -0
  274. package/dist/src/services/secretary-worktier.js +157 -0
  275. package/dist/src/services/secretary-worktier.js.map +1 -0
  276. package/dist/src/services/skill-bundle/generator.d.ts +3 -0
  277. package/dist/src/services/skill-bundle/generator.d.ts.map +1 -1
  278. package/dist/src/services/skill-bundle/generator.js +19 -0
  279. package/dist/src/services/skill-bundle/generator.js.map +1 -1
  280. package/dist/src/services/specialist-runner.d.ts +36 -0
  281. package/dist/src/services/specialist-runner.d.ts.map +1 -0
  282. package/dist/src/services/specialist-runner.js +91 -0
  283. package/dist/src/services/specialist-runner.js.map +1 -0
  284. package/dist/src/services/specialist.d.ts +9 -0
  285. package/dist/src/services/specialist.d.ts.map +1 -1
  286. package/dist/src/services/specialist.js +42 -0
  287. package/dist/src/services/specialist.js.map +1 -1
  288. package/dist/src/services/structure-overview.d.ts +13 -0
  289. package/dist/src/services/structure-overview.d.ts.map +1 -1
  290. package/dist/src/services/structure-overview.js +34 -11
  291. package/dist/src/services/structure-overview.js.map +1 -1
  292. package/dist/src/services/system-agent-responder.d.ts +36 -0
  293. package/dist/src/services/system-agent-responder.d.ts.map +1 -0
  294. package/dist/src/services/system-agent-responder.js +107 -0
  295. package/dist/src/services/system-agent-responder.js.map +1 -0
  296. package/dist/src/services/system-agent.d.ts +34 -1
  297. package/dist/src/services/system-agent.d.ts.map +1 -1
  298. package/dist/src/services/system-agent.js +73 -0
  299. package/dist/src/services/system-agent.js.map +1 -1
  300. package/dist/src/storage/interface.d.ts +33 -0
  301. package/dist/src/storage/interface.d.ts.map +1 -1
  302. package/dist/src/storage/interface.js.map +1 -1
  303. package/dist/src/storage/providers/mongodb/index.d.ts +37 -0
  304. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  305. package/dist/src/storage/providers/mongodb/index.js +132 -12
  306. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  307. package/dist/src/storage/providers/sqlite/index.d.ts +23 -1
  308. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  309. package/dist/src/storage/providers/sqlite/index.js +60 -8
  310. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  311. package/dist/src/storage/providers/sqlite/repos/memory.d.ts +29 -1
  312. package/dist/src/storage/providers/sqlite/repos/memory.d.ts.map +1 -1
  313. package/dist/src/storage/providers/sqlite/repos/memory.js +109 -10
  314. package/dist/src/storage/providers/sqlite/repos/memory.js.map +1 -1
  315. package/dist/src/storage/providers/sqlite/schema.d.ts +3 -0
  316. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  317. package/dist/src/storage/providers/sqlite/schema.js +64 -9
  318. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  319. package/dist/src/storage/repositories/app.repository.d.ts +15 -0
  320. package/dist/src/storage/repositories/app.repository.d.ts.map +1 -1
  321. package/dist/src/storage/repositories/memory.repository.d.ts +41 -1
  322. package/dist/src/storage/repositories/memory.repository.d.ts.map +1 -1
  323. package/dist/src/storage/repositories/organism.repository.d.ts +2 -1
  324. package/dist/src/storage/repositories/organism.repository.d.ts.map +1 -1
  325. package/dist/static/app-catalog.html +469 -122
  326. package/package.json +2 -1
  327. package/prisma/schema.postgres.prisma +10 -0
  328. package/prisma/schema.prisma +12 -0
@@ -0,0 +1,617 @@
1
+ /**
2
+ * @file secretary/dashboard.js
3
+ * @description Dashboard-first information architecture for the Secretary view (B1). Pure presentational
4
+ * render functions (props in → htm out, no hooks/state) that frame the Secretary's daily loop:
5
+ * a core quick-action row, a "Today" status strip (reliability · budget · next scheduled run ·
6
+ * last-scan + stale flag + refresh), a read-only "where things stand" orientation panel, and the
7
+ * collapsible "Manage & setup" disclosure header that tucks the set-up-once config cards away.
8
+ * The view (views/secretary.js) owns all state/handlers and passes them in; the config cards
9
+ * themselves still live in ./cards.js / ./cards-reach.js. Redesign: docs/internal/2026-06-25-secretary-view-redesign.md.
10
+ * @structure quickActionRow · dashStatus · standPanel · briefTrayCard · actionItemsCard · routinesCard · quickActionsManager · manageHeader (one render function each)
11
+ * @usage import { quickActionRow, dashStatus, standPanel, briefTrayCard, actionItemsCard, routinesCard, quickActionsManager, manageHeader } from '/views/secretary/dashboard.js';
12
+ * @version-history
13
+ * v0.10.0 — 2026-07-01 — Light-vs-heavy seam: briefTrayCard — the Work Brief tray. The cheap Secretary
14
+ * detects structural gaps and proposes grounded briefs; this renders each with "Open in Claude Desktop"
15
+ * (CopyButton with an MCP preface + the brief) and "Send to a connected Builder" (lane b dispatch), so
16
+ * heavy work goes to a big AI via MCP and never gets cheap-authored here.
17
+ * v0.9.0 — 2026-06-28 — strategyCard: optional steering frame (vision/mission/principles/risks +
18
+ * current→target + ordered milestone gates with status/focus/reorder/goal-linking + AI re-plan).
19
+ * v0.8.0 — 2026-06-28 — secretaryDangerCard: a guarded "Reset Secretary" control (type RESET to wipe
20
+ * the brain + all secretary.* + self-organisms/workspaces + specialists) for the Manage & setup area.
21
+ * v0.7.0 — 2026-06-28 — triggersCard: "When it fires" binding (remind / run a workflow / run a
22
+ * specialist) + a bound-action badge in the trigger list; takes workflows + specialists props.
23
+ * v0.6.0 — 2026-06-28 — G3: dashStatus "Scan" button calls onReconcile (a real discover scan that
24
+ * stamps lastScanAt) with a scanning state, replacing the bare re-fetch; G2 cadence badge on routines.
25
+ * v0.5.0 — 2026-06-28 — G5: actionItemsCard renders the action-item label via t() from the item's
26
+ * structured labelKind + summary (legacy server-composed `text` kept as a fallback).
27
+ * v0.4.0 — 2026-06-28 — B5: actionItemsCard — the tick-derived follow-up action-items, one-click handle/dismiss.
28
+ * v0.3.0 — 2026-06-27 — B3: quickActionsManager — pin/dismiss proposed shortcuts, rename/remove/reorder
29
+ * active ones, "Suggest shortcuts".
30
+ * v0.2.0 — 2026-06-27 — B2: routinesCard — active Routines on the dashboard (status · last result ·
31
+ * next step · Advance).
32
+ * v0.1.0 — 2026-06-27 — B1: dashboard-first IA — quick-action row, Today status strip, where-things-stand
33
+ * panel, Manage disclosure header.
34
+ */
35
+ import { h } from 'preact';
36
+ import htm from 'htm';
37
+ const html = htm.bind(h);
38
+ import { t } from '/js/i18n.js';
39
+ import { Markdown } from '/components/Markdown.js';
40
+ import { CopyButton } from '/components/CopyButton.js';
41
+
42
+ /** Core (+ later dynamic) quick actions: a button row above the chat. `items` are descriptors
43
+ * `{ key, label, title?, primary?, disabled?, hidden?, onClick }` so B3 can extend with dynamic actions. */
44
+ export function quickActionRow(p) {
45
+ const items = (p.items || []).filter((it) => it && !it.hidden);
46
+ if (items.length === 0) return null;
47
+ return html`
48
+ <div class="sec-quick">
49
+ ${items.map((it) => html`
50
+ <button class="${it.primary ? 'btn-primary' : 'btn-outline'} btn-sm sec-quick-btn" key=${it.key}
51
+ disabled=${!!it.disabled} title=${it.title || ''} onClick=${it.onClick}>${it.label}</button>`)}
52
+ </div>`;
53
+ }
54
+
55
+ /** "Today" status strip: reliability · budget · next scheduled run · last-scan + stale flag + refresh. */
56
+ export function dashStatus(p) {
57
+ const rel = p.reliability;
58
+ const relText = (rel && typeof rel.score === 'number') ? `${rel.score}/100` : t('secretary.reliabilityBuilding');
59
+ const budgetText = p.budgetInfo ? `${p.budgetInfo.remaining} / ${p.budgetInfo.budget}` : t('secretary.noLimit');
60
+ const sched = p.schedule;
61
+ const nextText = (sched && sched.nextRunAt) ? new Date(sched.nextRunAt).toLocaleString() : '—';
62
+ const paused = sched && !sched.enabled;
63
+ const lastText = p.lastScan ? new Date(p.lastScan).toLocaleString() : t('secretary.dash.never');
64
+ return html`
65
+ <section class="sec-card sec-dash">
66
+ <div class="sec-card-head">
67
+ <h2 class="sec-h2">${t('secretary.dash.todayTitle')}</h2>
68
+ <button class="btn-ghost btn-sm" disabled=${!!p.scanning} onClick=${p.onReconcile}>↻ ${p.scanning ? t('secretary.dash.scanning') : t('secretary.dash.reconcile')}</button>
69
+ </div>
70
+ <div class="sec-dash-strip">
71
+ <div class="sec-chip">
72
+ <span class="sec-chip-k">${t('secretary.reliability')}</span>
73
+ <span class="sec-chip-v">${relText}${rel && rel.count ? html` <span class="sec-hint">(${rel.count})</span>` : null}</span>
74
+ </div>
75
+ <div class="sec-chip">
76
+ <span class="sec-chip-k">${t('secretary.dash.budgetLabel')}</span>
77
+ <span class="sec-chip-v">${budgetText}</span>
78
+ </div>
79
+ <div class="sec-chip">
80
+ <span class="sec-chip-k">${t('secretary.dash.nextRunLabel')}</span>
81
+ <span class="sec-chip-v">${nextText}${paused ? html` <span class="sec-hint">(${t('secretary.auto.paused')})</span>` : null}</span>
82
+ </div>
83
+ <div class="sec-chip ${p.stale ? 'stale' : ''}">
84
+ <span class="sec-chip-k">${t('secretary.dash.lastScanLabel')}</span>
85
+ <span class="sec-chip-v">${lastText}${p.stale ? html` <span class="sec-chip-badge">${t('secretary.dash.stale')}</span>` : null}</span>
86
+ </div>
87
+ </div>
88
+ </section>`;
89
+ }
90
+
91
+ /** Read-only "where things stand" orientation summary (the core "Missä mennään?" quick action). */
92
+ export function standPanel(p) {
93
+ const s = p.stand;
94
+ if (!s || (!s.loading && !s.text && !s.needModel)) return null;
95
+ const title = p.title || t('secretary.dash.standTitle');
96
+ const thinking = p.thinking || t('secretary.dash.standThinking');
97
+ return html`
98
+ <section class="sec-card sec-stand">
99
+ <div class="sec-card-head">
100
+ <h2 class="sec-h2">${title}</h2>
101
+ ${!s.loading ? html`<button class="btn-ghost btn-sm" onClick=${p.onDismiss}>${t('secretary.dash.dismiss')}</button>` : null}
102
+ </div>
103
+ ${s.loading
104
+ ? html`<div class="sec-hint">${thinking}<span class="sec-spin"></span></div>`
105
+ : s.needModel
106
+ ? html`<div class="sec-hint sec-warn">${t('secretary.next.needBigModel')}</div>`
107
+ : html`
108
+ ${s.generatedAt ? html`<div class="sec-hint sec-stand-when">${t('secretary.dash.standAt')} ${new Date(s.generatedAt).toLocaleString()} · <button class="sec-linkbtn" onClick=${p.onRefresh}>${t('secretary.dash.refreshStand')}</button></div>` : null}
109
+ ${s.issues && s.issues.length ? html`<div class="sec-hint sec-warn">⚠ ${t('secretary.next.verifyFlag')}: ${s.issues.join('; ')}</div>` : null}
110
+ <div class="sec-stand-body"><${Markdown} text=${s.text} /></div>`}
111
+ </section>`;
112
+ }
113
+
114
+ /** Compose the paste a Builder receives in Claude Desktop: a connect-first preface + the full brief.
115
+ * The preface tells the assistant to connect via the appdev MCP if it isn't already, so pasting into a
116
+ * Claude Desktop that has NOT been wired to the node still leads somewhere instead of silently failing. */
117
+ function heavyWorkPrompt(b) {
118
+ return `I'd like you to carry out a work brief on my AIMEAT node using the appdev MCP tools (the aimeat_* tools).
119
+
120
+ FIRST — make sure you can reach my node: if you are not already connected to my AIMEAT node via the appdev MCP, connect now. In the AIMEAT app open Profile → Agents → "Connect a New Agent" for the exact device-authorization steps (or read the node's /llms.txt). You need the aimeat_* tools available before you can do any of the work below.
121
+
122
+ THEN — carry out the brief end-to-end with those tools: build/fill via aimeat_workspace_* / aimeat_organism_*, ground everything in the real sources listed, and never invent facts. If a records space has no real data, leave it empty rather than make something up.
123
+
124
+ ${b.brief || ''}`;
125
+ }
126
+
127
+ /** One Work Brief row: objective + ws + a markdown preview + the two heavy-work lanes (Open in Claude
128
+ * Desktop / Send to a connected Builder) + Done. The seam made physical — the cheap Secretary proposed
129
+ * this; a Builder (a big AI via MCP) executes it. */
130
+ function briefRow(b, p) {
131
+ const builders = p.builders || [];
132
+ const dispatched = b.status === 'dispatched';
133
+ return html`
134
+ <div class="sec-brief" key=${b.id}>
135
+ <div class="sec-brief-top">
136
+ <span class="sec-brief-badge ${dispatched ? 'is-dispatched' : ''}">${dispatched ? t('secretary.brief.dispatchedBadge') : t('secretary.brief.heavyBadge')}</span>
137
+ <span class="sec-brief-obj">${b.objective}</span>
138
+ ${b.wsName ? html`<span class="sec-hint sec-brief-ws">${b.wsName}</span>` : null}
139
+ </div>
140
+ <details class="sec-brief-detail">
141
+ <summary>${t('secretary.brief.preview')}</summary>
142
+ <div class="sec-brief-md"><${Markdown} text=${b.brief || ''} /></div>
143
+ </details>
144
+ ${dispatched ? html`<div class="sec-hint sec-brief-wait">${t('secretary.brief.dispatchedNote')}</div>` : null}
145
+ <div class="sec-brief-actions">
146
+ <${CopyButton} text=${heavyWorkPrompt(b)} className="btn-primary btn-sm" label=${t('secretary.brief.openClaude')} />
147
+ ${builders.length ? html`
148
+ <select class="sec-select" disabled=${dispatched || p.dispatching === b.id}
149
+ onChange=${(e) => { const v = e.target.value; e.target.value = ''; if (v) p.onDispatch(b.id, v); }}>
150
+ <option value="">${dispatched ? t('secretary.brief.waiting') : t('secretary.brief.sendBuilder')}</option>
151
+ ${builders.map((a) => html`<option value=${a.gaii} key=${a.gaii}>${a.name}</option>`)}
152
+ </select>` : html`<span class="sec-hint">${t('secretary.brief.noBuilder')}</span>`}
153
+ <button class="btn-ghost btn-sm" onClick=${() => p.onResolve(b.id)}>${t('secretary.brief.done')}</button>
154
+ </div>
155
+ </div>`;
156
+ }
157
+
158
+ /** The Work Brief tray: heavy work the Secretary detected and prepared for a Builder. Null when empty
159
+ * (no gaps → nothing to hand off). The clear divide between "watch + propose" (cheap, here) and
160
+ * "build + author" (a big AI via MCP). */
161
+ export function briefTrayCard(p) {
162
+ const briefs = p.briefs || [];
163
+ if (!briefs.length) return null;
164
+ return html`
165
+ <section class="sec-card sec-brief-tray">
166
+ <div class="sec-card-head">
167
+ <h2 class="sec-h2">${t('secretary.brief.startHere')} ${t('secretary.brief.title')} <span class="sec-count">${briefs.length}</span></h2>
168
+ <span class="sec-hint">${t('secretary.brief.subtitle')}</span>
169
+ </div>
170
+ <p class="sec-brief-banner">${t('secretary.brief.mcpNote')} <a class="sec-linkbtn" href="/v1/profile">${t('secretary.brief.connectLink')}</a></p>
171
+ ${briefs.map((b) => briefRow(b, p))}
172
+ </section>`;
173
+ }
174
+
175
+ /** "What's next" — the Secretary's proposed next actions, each with Do it / Skip; doing one runs it. */
176
+ export function whatsNextPanel(p) {
177
+ const n = p.answer;
178
+ if (!n) return null;
179
+ return html`
180
+ <section class="sec-card sec-stand sec-next-answer">
181
+ <div class="sec-card-head">
182
+ <h2 class="sec-h2">${t('secretary.next.title')}</h2>
183
+ ${!n.loading ? html`<button class="btn-ghost btn-sm" onClick=${p.onDismiss}>${t('secretary.dash.dismiss')}</button>` : null}
184
+ </div>
185
+ ${n.loading
186
+ ? html`<div class="sec-hint">${t('secretary.next.thinking')}<span class="sec-spin"></span></div>`
187
+ : n.needModel
188
+ ? html`<div class="sec-hint sec-warn">${t('secretary.next.needBigModel')}</div>`
189
+ : (n.actions && n.actions.length
190
+ ? html`<ul class="sec-next-actions">
191
+ ${n.actions.map((a) => html`
192
+ <li class="sec-next-action sec-next-action--${a.status}" key=${a.id}>
193
+ <div class="sec-next-action-main">
194
+ <div class="sec-next-action-sum">${a.summary}</div>
195
+ ${a.why ? html`<div class="sec-hint">${a.why}</div>` : null}
196
+ ${a.result ? html`<div class="sec-hint">→ ${a.result}${a.href ? html` · <a href=${a.href} target="_blank" rel="noopener">${t('secretary.next.openResult')} ↗</a>` : null}</div>` : null}
197
+ ${a.issues && a.issues.length ? html`<div class="sec-hint sec-warn">⚠ ${t('secretary.next.verifyFlag')}: ${a.issues.join('; ')}</div>` : null}
198
+ ${a.preview ? html`
199
+ <button class="sec-linkbtn sec-next-toggle" onClick=${() => p.onTogglePreview && p.onTogglePreview(a)}>${a.expanded ? t('secretary.next.hideResult') : t('secretary.next.showResult')}</button>
200
+ ${a.noteKey ? html` · <button class="sec-linkbtn sec-next-discard" onClick=${() => p.onDiscard && p.onDiscard(a)}>${t('secretary.next.discard')}</button>` : null}
201
+ ${a.expanded ? html`<div class="sec-next-preview"><${Markdown} text=${a.preview} /></div>` : null}` : null}
202
+ ${a.status === 'prompt' && a.promptText ? html`
203
+ <div class="sec-next-prompt">
204
+ <div class="sec-hint sec-warn">${t('secretary.next.needBigModel')}</div>
205
+ <div class="sec-prompt-box">${a.promptText}</div>
206
+ <div class="sec-next-action-btns">
207
+ <button class="btn-outline btn-sm" onClick=${() => navigator.clipboard && navigator.clipboard.writeText(a.promptText)}>${t('secretary.next.copyPrompt')}</button>
208
+ </div>
209
+ <textarea class="sec-paste" placeholder=${t('secretary.next.pastePlaceholder')} value=${(p.pasteDrafts && p.pasteDrafts[a.id]) || ''} onInput=${(e) => p.onPasteInput && p.onPasteInput(a.id, e.target.value)}></textarea>
210
+ <div class="sec-next-action-btns">
211
+ <button class="btn-primary btn-sm" onClick=${() => p.onSavePrompt && p.onSavePrompt(a)}>${t('secretary.next.saveResult')}</button>
212
+ <button class="btn-ghost btn-sm" onClick=${() => p.onSkip(a)}>${t('secretary.next.skipIt')}</button>
213
+ </div>
214
+ </div>` : null}
215
+ </div>
216
+ ${a.status === 'done' ? html`<span class="sec-step-status sec-done">${t('secretary.next.done')}</span>`
217
+ : a.status === 'skipped' ? html`<span class="sec-hint">${t('secretary.next.skipped')}</span>`
218
+ : a.status === 'discarded' ? html`<span class="sec-hint">${t('secretary.next.discarded')}</span>`
219
+ : a.status === 'asked' ? html`<span class="sec-hint">⏳ ${t('secretary.next.asked')} · <a href="/v1/profile?tab=messages" target="_blank" rel="noopener">${t('secretary.next.openInbox')}</a></span>`
220
+ : a.status === 'prompt' ? null
221
+ : html`<div class="sec-next-action-btns">
222
+ <button class="btn-primary btn-sm" disabled=${a.status === 'doing'} onClick=${() => p.onDo(a)}>${a.status === 'doing' ? html`${t('secretary.next.running')}<span class="sec-spin"></span>` : t('secretary.next.doIt')}</button>
223
+ <button class="btn-ghost btn-sm" onClick=${() => p.onSkip(a)}>${t('secretary.next.skipIt')}</button>
224
+ </div>`}
225
+ </li>`)}
226
+ </ul>`
227
+ : html`<div class="sec-hint">${t('secretary.next.nothingNow')}</div>`)}
228
+ </section>`;
229
+ }
230
+
231
+ /** Action-items the tick derived (B5): one-click handle (open routine / check delegate result) or dismiss. */
232
+ export function actionItemsCard(p) {
233
+ if (!p.actionItems || p.actionItems.length === 0) return null;
234
+ return html`
235
+ <section class="sec-card sec-action-items">
236
+ <h2 class="sec-h2">${t('secretary.items.title')}</h2>
237
+ <ul class="sec-item-list">
238
+ ${p.actionItems.map((it) => {
239
+ const kind = (it.suggestedAction && it.suggestedAction.kind) || 'advance';
240
+ // G5: the tick stores a structured labelKind + summary — render the label in the user's language
241
+ // here. Fall back to a legacy server-composed `text` for any item written before G5.
242
+ const label = it.labelKind ? t('secretary.items.text.' + it.labelKind, { summary: it.summary || '' }) : (it.text || '');
243
+ return html`
244
+ <li class="sec-item-row" key=${it.id}>
245
+ <div class="sec-item-text">${(label)}</div>
246
+ <div class="sec-item-actions">
247
+ <button class="btn-primary btn-sm" onClick=${() => p.handleActionItem(it)}>${t('secretary.items.handle.' + kind)}</button>
248
+ <button class="btn-ghost btn-sm" onClick=${() => p.dismissActionItem(it)}>${t('secretary.items.dismiss')}</button>
249
+ </div>
250
+ </li>`;
251
+ })}
252
+ </ul>
253
+ </section>`;
254
+ }
255
+
256
+ /** Active Routines on the dashboard (B2): title · status · last result · next step, with Advance. */
257
+ export function routinesCard(p) {
258
+ if (!p.activeRoutines || p.activeRoutines.length === 0) return null;
259
+ return html`
260
+ <section class="sec-card sec-routines">
261
+ <h2 class="sec-h2">${t('secretary.next.routinesTitle')}</h2>
262
+ <ul class="sec-routine-list">
263
+ ${p.activeRoutines.map((r) => {
264
+ const next = p.nextPendingStep(r);
265
+ const last = (r.results && r.results[0]) || null;
266
+ return html`
267
+ <li class="sec-routine-row" key=${r.id}>
268
+ <div class="sec-routine-main">
269
+ <div class="sec-routine-title">${(r.title)}${r.cadence ? html` <span class="sec-cadence-badge">${t('secretary.next.cadence.' + r.cadence)}</span>` : null}</div>
270
+ ${last ? html`<div class="sec-hint">${t('secretary.next.last')}: ${(last.summary)}</div>` : null}
271
+ ${next
272
+ ? html`<div class="sec-hint">${t('secretary.next.next')}: ${(next.summary)}</div>`
273
+ : html`<div class="sec-hint">${t('secretary.next.allDone')}</div>`}
274
+ </div>
275
+ ${/* Always offer to open the routine — even with no pending step, so a delegated/waiting routine can be reopened to Check result. */ ''}
276
+ <button class="btn-outline btn-sm" onClick=${() => p.advance(r)}>${t('secretary.next.advance')}</button>
277
+ </li>`;
278
+ })}
279
+ </ul>
280
+ </section>`;
281
+ }
282
+
283
+ /** B3 quick-actions manager (✎): pin/dismiss proposed shortcuts, rename/remove/reorder active ones, suggest more. */
284
+ export function quickActionsManager(p) {
285
+ if (!p.managing) return null;
286
+ return html`
287
+ <section class="sec-card sec-qa-manage">
288
+ <div class="sec-card-head">
289
+ <h2 class="sec-h2">${t('secretary.qa.title')}</h2>
290
+ <button class="btn-ghost btn-sm" onClick=${p.toggleManage}>${t('secretary.qa.close')}</button>
291
+ </div>
292
+ <p class="sec-hint">${t('secretary.qa.hint')}</p>
293
+
294
+ ${p.proposed.length > 0 ? html`
295
+ <h3 class="sec-h3">${t('secretary.qa.proposedTitle')}</h3>
296
+ <ul class="sec-qa-list">
297
+ ${p.proposed.map((a) => html`
298
+ <li class="sec-qa-row proposed" key=${a.id}>
299
+ <div class="sec-qa-main">
300
+ <span class="sec-qa-label">${(a.label)} <span class="sec-qa-kind">${t('secretary.qa.kind.' + a.kind)}</span></span>
301
+ <div class="sec-hint">${a.kind === 'prompt' ? (a.prompt) : t('secretary.qa.composeTarget') + ': ' + (a.target)}</div>
302
+ </div>
303
+ <button class="btn-primary btn-sm" onClick=${() => p.approve(a)}>${t('secretary.qa.pin')}</button>
304
+ <button class="btn-ghost btn-sm" onClick=${() => p.dismiss(a)}>${t('secretary.qa.dismiss')}</button>
305
+ </li>`)}
306
+ </ul>` : null}
307
+
308
+ <h3 class="sec-h3">${t('secretary.qa.activeTitle')}</h3>
309
+ ${p.activeActions.length === 0
310
+ ? html`<div class="sec-hint">${t('secretary.qa.empty')}</div>`
311
+ : html`<ul class="sec-qa-list">
312
+ ${p.activeActions.map((a) => html`
313
+ <li class="sec-qa-row" key=${a.id}>
314
+ <div class="sec-qa-main">
315
+ ${p.editingId === a.id
316
+ ? html`<input class="sec-input" value=${p.editLabel} onInput=${(e) => p.setEditLabel(e.target.value)}
317
+ onKeyDown=${(e) => { if (e.key === 'Enter') p.saveRename(a); if (e.key === 'Escape') p.cancelRename(); }} />`
318
+ : html`<span class="sec-qa-label">${(a.label)} <span class="sec-qa-kind">${t('secretary.qa.kind.' + a.kind)}</span></span>`}
319
+ </div>
320
+ ${p.editingId === a.id
321
+ ? html`
322
+ <button class="btn-primary btn-sm" onClick=${() => p.saveRename(a)}>${t('secretary.qa.save')}</button>
323
+ <button class="btn-ghost btn-sm" onClick=${p.cancelRename}>${t('secretary.cancel')}</button>`
324
+ : html`
325
+ <button class="btn-ghost btn-sm" title=${t('secretary.qa.moveUp')} onClick=${() => p.move(a, -1)}>↑</button>
326
+ <button class="btn-ghost btn-sm" title=${t('secretary.qa.moveDown')} onClick=${() => p.move(a, 1)}>↓</button>
327
+ <button class="btn-ghost btn-sm" onClick=${() => p.startRename(a)}>${t('secretary.qa.rename')}</button>
328
+ <button class="btn-ghost btn-sm" onClick=${() => p.dismiss(a)}>${t('secretary.qa.remove')}</button>`}
329
+ </li>`)}
330
+ </ul>`}
331
+
332
+ <div class="sec-actions">
333
+ <button class="btn-outline btn-sm" disabled=${p.suggesting} onClick=${p.suggest}>${p.suggesting ? t('secretary.qa.suggesting') : t('secretary.qa.suggest')}</button>
334
+ </div>
335
+ </section>`;
336
+ }
337
+
338
+ /** Collapsible "Manage & setup" disclosure header — tucks the set-up-once config cards out of the default view. */
339
+ export function manageHeader(p) {
340
+ return html`
341
+ <section class="sec-card sec-manage">
342
+ <button class="sec-manage-toggle" onClick=${p.onToggle} aria-expanded=${p.open ? 'true' : 'false'}>
343
+ <span class="sec-manage-title">${t('secretary.dash.manage')}</span>
344
+ <span class="sec-manage-meta">${p.crewSummary ? p.crewSummary + ' · ' : ''}${p.open ? '▾' : '▸'}</span>
345
+ </button>
346
+ ${!p.open ? html`<p class="sec-hint sec-manage-hint">${t('secretary.dash.manageHint')}</p>` : null}
347
+ </section>`;
348
+ }
349
+
350
+ /** Triggers (Slice 3): list the owner's triggers + an add-form for all four kinds; pause/resume/delete.
351
+ * The tick fires them; this is just management. `goals`/`routines` feed the completion-target picker. */
352
+ export function triggersCard(p) {
353
+ const f = p.form;
354
+ const kindLabel = (tr) => {
355
+ if (tr.kind === 'recurring') return `${t('secretary.trig.kind.recurring')} · ${t('secretary.trig.cad.' + (tr.cadence || 'weekly'))}`;
356
+ if (tr.kind === 'time') return `${t('secretary.trig.kind.time')} · ${tr.nextFireAt ? new Date(tr.nextFireAt).toLocaleDateString() : '—'}`;
357
+ if (tr.kind === 'completion') return t('secretary.trig.kind.completion');
358
+ if (tr.kind === 'condition') return `${t('secretary.trig.kind.condition')} · ${t('secretary.trig.ct.' + ((tr.condition && tr.condition.type) || 'memory_count'))}`;
359
+ return tr.kind;
360
+ };
361
+ // What it does when it fires (badge): a bound workflow / specialist, else a plain reminder.
362
+ const actLabel = (tr) => {
363
+ const a = tr.action || {};
364
+ if (a.kind === 'workflow' && a.workflowId) return `▶️ ${a.workflowId}`;
365
+ if (a.kind === 'specialist' && a.specialistName) return `🧩 ${a.specialistName}`;
366
+ return null;
367
+ };
368
+ const workflows = p.workflows || [];
369
+ const specialists = p.specialists || [];
370
+ return html`
371
+ <section class="sec-card sec-triggers">
372
+ <div class="sec-card-head">
373
+ <h2 class="sec-h2">${t('secretary.trig.title')}</h2>
374
+ ${!f ? html`<button class="btn-outline btn-sm" onClick=${p.openForm}>+ ${t('secretary.trig.add')}</button>` : null}
375
+ </div>
376
+ <p class="sec-hint">${t('secretary.trig.hint')}</p>
377
+ ${p.triggers.length === 0 && !f ? html`<div class="sec-hint">${t('secretary.trig.empty')}</div>` : null}
378
+ ${p.triggers.length ? html`<ul class="sec-trig-list">
379
+ ${p.triggers.map((tr) => html`
380
+ <li class="sec-trig-row ${tr.status}" key=${tr.id}>
381
+ <div class="sec-trig-main">
382
+ <div class="sec-trig-label">${tr.label}${actLabel(tr) ? html` <span class="sec-trig-bound">${actLabel(tr)}</span>` : null}</div>
383
+ <div class="sec-hint">${kindLabel(tr)}${tr.status === 'proposed' ? ' · ' + t('secretary.trig.proposedBadge') : tr.status === 'paused' ? ' · ' + t('secretary.trig.paused') : tr.status === 'fired' ? ' · ' + t('secretary.trig.fired') : ''}</div>
384
+ </div>
385
+ <div class="sec-trig-btns">
386
+ ${tr.status === 'proposed'
387
+ ? html`<button class="btn-primary btn-sm" onClick=${() => p.armTrigger(tr)}>${t('secretary.trig.arm')}</button>
388
+ <button class="btn-ghost btn-sm" onClick=${() => p.removeTrigger(tr)}>${t('secretary.trig.dismiss')}</button>`
389
+ : html`<button class="btn-ghost btn-sm" onClick=${() => p.togglePause(tr)}>${tr.status === 'paused' ? t('secretary.trig.resume') : t('secretary.trig.pause')}</button>
390
+ <button class="btn-ghost btn-sm" title=${t('secretary.remove')} onClick=${() => p.removeTrigger(tr)}>✕</button>`}
391
+ </div>
392
+ </li>`)}
393
+ </ul>` : null}
394
+ ${f ? html`
395
+ <div class="sec-form sec-trig-form">
396
+ <input class="sec-input" placeholder=${t('secretary.trig.labelPlaceholder')} value=${f.label} onInput=${(e) => p.setField('label', e.target.value)} />
397
+ <select class="sec-input" value=${f.kind} onChange=${(e) => p.setField('kind', e.target.value)}>
398
+ <option value="recurring" selected=${f.kind === 'recurring'}>${t('secretary.trig.kind.recurring')}</option>
399
+ <option value="time" selected=${f.kind === 'time'}>${t('secretary.trig.kind.time')}</option>
400
+ <option value="completion" selected=${f.kind === 'completion'}>${t('secretary.trig.kind.completion')}</option>
401
+ <option value="condition" selected=${f.kind === 'condition'}>${t('secretary.trig.kind.condition')}</option>
402
+ </select>
403
+ ${f.kind === 'recurring' ? html`<select class="sec-input" value=${f.cadence} onChange=${(e) => p.setField('cadence', e.target.value)}>
404
+ <option value="daily" selected=${f.cadence === 'daily'}>${t('secretary.trig.cad.daily')}</option>
405
+ <option value="weekly" selected=${f.cadence === 'weekly'}>${t('secretary.trig.cad.weekly')}</option>
406
+ <option value="monthly" selected=${f.cadence === 'monthly'}>${t('secretary.trig.cad.monthly')}</option></select>` : null}
407
+ ${f.kind === 'time' ? html`<input class="sec-input" type="datetime-local" value=${f.when} onInput=${(e) => p.setField('when', e.target.value)} />` : null}
408
+ ${f.kind === 'completion' ? html`<select class="sec-input" value=${f.targetId} onChange=${(e) => p.setField('targetId', e.target.value)}>
409
+ <option value="" selected=${!f.targetId}>${t('secretary.trig.pickTarget')}</option>
410
+ ${(p.goals || []).filter((g) => g.status !== 'done').map((g) => html`<option value=${g.id} key=${g.id}>${t('secretary.trig.goalOpt')}: ${g.title}</option>`)}
411
+ ${(p.routines || []).filter((r) => r.status === 'active').map((r) => html`<option value=${r.id} key=${r.id}>${t('secretary.trig.routineOpt')}: ${r.title}</option>`)}
412
+ </select>` : null}
413
+ ${f.kind === 'condition' ? html`
414
+ <select class="sec-input" value=${f.condType} onChange=${(e) => p.setField('condType', e.target.value)}>
415
+ <option value="memory_count" selected=${f.condType === 'memory_count'}>${t('secretary.trig.ct.memory_count')}</option>
416
+ <option value="workspace_count" selected=${f.condType === 'workspace_count'}>${t('secretary.trig.ct.workspace_count')}</option>
417
+ <option value="no_activity" selected=${f.condType === 'no_activity'}>${t('secretary.trig.ct.no_activity')}</option>
418
+ <option value="memory_exists" selected=${f.condType === 'memory_exists'}>${t('secretary.trig.ct.memory_exists')}</option>
419
+ </select>
420
+ <input class="sec-input" placeholder=${t('secretary.trig.prefixPlaceholder')} value=${f.condPrefix} onInput=${(e) => p.setField('condPrefix', e.target.value)} />
421
+ ${f.condType === 'no_activity'
422
+ ? html`<input class="sec-input sec-input-sm" type="number" min="1" value=${f.condDays} onInput=${(e) => p.setField('condDays', e.target.value)} title=${t('secretary.trig.days')} />`
423
+ : (f.condType !== 'memory_exists' ? html`<input class="sec-input sec-input-sm" type="number" min="1" value=${f.condValue} onInput=${(e) => p.setField('condValue', e.target.value)} title=${t('secretary.trig.count')} />` : null)}` : null}
424
+ <label class="sec-hint sec-trig-actlabel">${t('secretary.trig.act.label')}</label>
425
+ <select class="sec-input" value=${f.actionKind} onChange=${(e) => p.setField('actionKind', e.target.value)}>
426
+ <option value="remind" selected=${f.actionKind === 'remind'}>${t('secretary.trig.act.remind')}</option>
427
+ ${workflows.length ? html`<option value="workflow" selected=${f.actionKind === 'workflow'}>${t('secretary.trig.act.workflow')}</option>` : null}
428
+ ${specialists.length ? html`<option value="specialist" selected=${f.actionKind === 'specialist'}>${t('secretary.trig.act.specialist')}</option>` : null}
429
+ </select>
430
+ ${f.actionKind === 'workflow' ? html`<select class="sec-input" value=${f.actionWorkflowId} onChange=${(e) => p.setField('actionWorkflowId', e.target.value)}>
431
+ <option value="" selected=${!f.actionWorkflowId}>${t('secretary.trig.act.pickWorkflow')}</option>
432
+ ${workflows.map((w) => html`<option value=${w.id} key=${w.id} selected=${f.actionWorkflowId === w.id}>${w.name || w.id}</option>`)}
433
+ </select>` : null}
434
+ ${f.actionKind === 'specialist' ? html`
435
+ <select class="sec-input" value=${f.actionSpecialistName} onChange=${(e) => p.setField('actionSpecialistName', e.target.value)}>
436
+ <option value="" selected=${!f.actionSpecialistName}>${t('secretary.trig.act.pickSpecialist')}</option>
437
+ ${specialists.map((s) => html`<option value=${s.name} key=${s.name} selected=${f.actionSpecialistName === s.name}>${s.display_name || s.name}</option>`)}
438
+ </select>
439
+ <textarea class="sec-paste" rows="2" placeholder=${t('secretary.trig.act.promptPlaceholder')} value=${f.actionPrompt} onInput=${(e) => p.setField('actionPrompt', e.target.value)}></textarea>` : null}
440
+ <div class="sec-actions">
441
+ <button class="btn-ghost btn-sm" onClick=${p.closeForm}>${t('secretary.cancel')}</button>
442
+ <button class="btn-primary btn-sm" disabled=${p.adding || !f.label.trim()} onClick=${p.createTrigger}>${p.adding ? t('secretary.saving') : t('secretary.trig.create')}</button>
443
+ </div>
444
+ </div>` : null}
445
+ </section>`;
446
+ }
447
+
448
+ /** Phase 3 — "Design a workflow": state outcome → the Secretary composes a chain of agent+offer steps,
449
+ * proposes how to arm it (manual / schedule / event), and saves it. p = { state, on* handlers }. */
450
+ export function workflowDesignPanel(p) {
451
+ const s = p.state;
452
+ if (!s) return null;
453
+ const trig = s.trigger || { kind: 'manual' };
454
+ return html`
455
+ <section class="sec-card sec-stand sec-wf">
456
+ <div class="sec-card-head">
457
+ <h2 class="sec-h2">${t('secretary.wf.title')}</h2>
458
+ <button class="btn-ghost btn-sm" onClick=${p.onDiscard}>${t('secretary.dash.dismiss')}</button>
459
+ </div>
460
+ ${s.saved
461
+ ? html`<div class="sec-hint">✓ ${t('secretary.wf.saved', { id: s.saved })}</div>`
462
+ : s.draft
463
+ ? html`
464
+ ${s.draft.description ? html`<div class="sec-hint">${s.draft.description}</div>` : null}
465
+ <div class="sec-h3">${t('secretary.wf.steps')}</div>
466
+ <ol class="sec-wf-steps">
467
+ ${s.draft.steps.map((st) => html`<li key=${st.id}><strong>${st.agent}</strong> · ${st.offer} — ${st.description}${st.after && st.after.length ? html` <span class="sec-hint">(${t('secretary.wf.after')} ${st.after.join(', ')})</span>` : null}</li>`)}
468
+ </ol>
469
+ <div class="sec-h3">${t('secretary.wf.arming')}</div>
470
+ <select class="sec-band" value=${trig.kind} onChange=${(e) => p.onTrigKind(e.target.value)}>
471
+ <option value="manual" selected=${trig.kind === 'manual'}>${t('secretary.wf.trigManual')}</option>
472
+ <option value="schedule" selected=${trig.kind === 'schedule'}>${t('secretary.wf.trigSchedule')}</option>
473
+ <option value="event" selected=${trig.kind === 'event'}>${t('secretary.wf.trigEvent')}</option>
474
+ </select>
475
+ ${trig.kind === 'schedule' ? html`<input class="sec-input sec-wf-cron" value=${trig.cron || '0 9 * * *'} placeholder="0 18 * * *" onInput=${(e) => p.onCron(e.target.value)} />` : null}
476
+ ${s.errors && s.errors.length ? html`<div class="sec-hint sec-warn">⚠ ${s.errors.join('; ')}</div>` : null}
477
+ <div class="sec-next-action-btns">
478
+ <button class="btn-primary btn-sm" disabled=${s.saving} onClick=${p.onSave}>${s.saving ? t('secretary.saving') : t('secretary.wf.save')}</button>
479
+ <button class="btn-ghost btn-sm" onClick=${p.onRedo}>${t('secretary.wf.redo')}</button>
480
+ </div>`
481
+ : s.designing
482
+ ? html`<div class="sec-hint">${t('secretary.wf.designing')}<span class="sec-spin"></span></div>`
483
+ : s.error === 'needModel'
484
+ ? html`<div class="sec-hint sec-warn">${t('secretary.next.needBigModel')}</div>`
485
+ : s.error === 'noSteps'
486
+ ? html`<div class="sec-hint sec-warn">${t('secretary.wf.noSteps')}${s.title ? `: ${s.title}` : ''}</div>`
487
+ : html`
488
+ <p class="sec-hint">${t('secretary.wf.prompt')}</p>
489
+ <textarea class="sec-paste sec-wf-outcome" placeholder=${t('secretary.wf.placeholder')} value=${s.outcome || ''} onInput=${(e) => p.onOutcome(e.target.value)}></textarea>
490
+ <div class="sec-next-action-btns">
491
+ <button class="btn-primary btn-sm" disabled=${!(s.outcome || '').trim()} onClick=${p.onDesign}>${t('secretary.wf.design')}</button>
492
+ </div>`}
493
+ </section>`;
494
+ }
495
+
496
+ /** Strategy — the optional steering frame: vision · mission · principles · risks · current → target ·
497
+ * ordered milestones (gates, not tasks). Shows only the filled parts. The owner edits the text/list
498
+ * fields in one Edit mode, manages milestones inline (status cycle, reorder, remove, add, link goals),
499
+ * and can Re-plan (AI re-proposes a coherent strategy after a change). Rendered only when enabled. */
500
+ export function strategyCard(p) {
501
+ const s = p.strategy || {};
502
+ const ms = s.milestones || [];
503
+ const goals = p.goals || [];
504
+ const focusIdx = ms.findIndex((m) => m.status !== 'reached'); // focus = first not-yet-reached gate
505
+ const statusLabel = (st) => t('secretary.strat.status.' + (st || 'not-started'));
506
+ const e = p.edit; // text/list edit draft, or null
507
+ const rp = p.replan; // { note } | { busy } | { draft } | null
508
+ const list = (arr) => (arr || []).filter(Boolean);
509
+ return html`
510
+ <section class="sec-card sec-strategy">
511
+ <div class="sec-card-head">
512
+ <h2 class="sec-h2">${t('secretary.strat.title')}</h2>
513
+ ${!e && !rp ? html`<div class="sec-trig-btns">
514
+ <button class="btn-ghost btn-sm" onClick=${p.onStartReplan}>${t('secretary.strat.replan')}</button>
515
+ <button class="btn-ghost btn-sm" onClick=${p.onStartEdit}>${t('secretary.strat.edit')}</button>
516
+ </div>` : null}
517
+ </div>
518
+
519
+ ${rp ? html`
520
+ ${rp.draft ? html`
521
+ <div class="sec-hint">${t('secretary.strat.replanReady')}</div>
522
+ <div class="sec-dir-state">
523
+ <div><span class="sec-dir-lbl">${t('secretary.strat.target')}</span> ${rp.draft.target || '—'}</div>
524
+ <div class="sec-hint">${(rp.draft.milestones || []).map((m) => m.title).join(' → ') || '—'}</div>
525
+ </div>
526
+ <div class="sec-actions">
527
+ <button class="btn-ghost btn-sm" onClick=${p.onCancelReplan}>${t('secretary.cancel')}</button>
528
+ <button class="btn-primary btn-sm" onClick=${p.onApplyReplan}>${t('secretary.strat.replanApply')}</button>
529
+ </div>`
530
+ : html`
531
+ <label class="sec-hint">${t('secretary.strat.replanPrompt')}</label>
532
+ <textarea class="sec-paste" rows="2" value=${rp.note || ''} onInput=${(ev) => p.onReplanNote(ev.target.value)}></textarea>
533
+ <div class="sec-actions">
534
+ <button class="btn-ghost btn-sm" disabled=${rp.busy} onClick=${p.onCancelReplan}>${t('secretary.cancel')}</button>
535
+ <button class="btn-primary btn-sm" disabled=${rp.busy || !(rp.note || '').trim()} onClick=${p.onRunReplan}>${rp.busy ? html`${t('secretary.strat.replanning')}<span class="sec-spin"></span>` : t('secretary.strat.replanRun')}</button>
536
+ </div>`}`
537
+ : e ? html`
538
+ <label class="sec-hint">${t('secretary.strat.vision')}</label>
539
+ <textarea class="sec-paste" rows="2" value=${e.vision} onInput=${(ev) => p.onEditField('vision', ev.target.value)}></textarea>
540
+ <label class="sec-hint">${t('secretary.strat.mission')}</label>
541
+ <textarea class="sec-paste" rows="2" value=${e.mission} onInput=${(ev) => p.onEditField('mission', ev.target.value)}></textarea>
542
+ <label class="sec-hint">${t('secretary.strat.current')}</label>
543
+ <textarea class="sec-paste" rows="2" value=${e.current} onInput=${(ev) => p.onEditField('current', ev.target.value)}></textarea>
544
+ <label class="sec-hint">${t('secretary.strat.target')}</label>
545
+ <textarea class="sec-paste" rows="2" value=${e.target} onInput=${(ev) => p.onEditField('target', ev.target.value)}></textarea>
546
+ <label class="sec-hint">${t('secretary.strat.principles')} ${t('secretary.strat.onePerLine')}</label>
547
+ <textarea class="sec-paste" rows="2" value=${e.principles} onInput=${(ev) => p.onEditField('principles', ev.target.value)}></textarea>
548
+ <label class="sec-hint">${t('secretary.strat.risks')} ${t('secretary.strat.onePerLine')}</label>
549
+ <textarea class="sec-paste" rows="2" value=${e.risks} onInput=${(ev) => p.onEditField('risks', ev.target.value)}></textarea>
550
+ <div class="sec-actions">
551
+ <button class="btn-ghost btn-sm" onClick=${p.onCancelEdit}>${t('secretary.cancel')}</button>
552
+ <button class="btn-primary btn-sm" onClick=${p.onSaveEdit}>${t('secretary.save')}</button>
553
+ </div>`
554
+ : html`
555
+ ${s.vision ? html`<div class="sec-dir-line"><span class="sec-dir-lbl">${t('secretary.strat.vision')}</span> ${s.vision}</div>` : null}
556
+ ${s.mission ? html`<div class="sec-dir-line"><span class="sec-dir-lbl">${t('secretary.strat.mission')}</span> ${s.mission}</div>` : null}
557
+ <div class="sec-dir-state">
558
+ <div class="sec-dir-now"><span class="sec-dir-lbl">${t('secretary.strat.current')}</span> ${s.current ? html`<span>${s.current}</span>` : html`<span class="sec-hint">${t('secretary.strat.empty')}</span>`}</div>
559
+ <div class="sec-dir-arrow">↓</div>
560
+ <div class="sec-dir-goal"><span class="sec-dir-lbl">${t('secretary.strat.target')}</span> ${s.target ? html`<span>${s.target}</span>` : html`<span class="sec-hint">${t('secretary.strat.empty')}</span>`}</div>
561
+ </div>
562
+ ${list(s.principles).length ? html`<div class="sec-dir-line"><span class="sec-dir-lbl">${t('secretary.strat.principles')}</span><ul class="sec-dir-bul">${list(s.principles).map((x, i) => html`<li key=${i}>${x}</li>`)}</ul></div>` : null}
563
+ ${list(s.risks).length ? html`<div class="sec-dir-line"><span class="sec-dir-lbl">${t('secretary.strat.risks')}</span><ul class="sec-dir-bul">${list(s.risks).map((x, i) => html`<li key=${i}>${x}</li>`)}</ul></div>` : null}`}
564
+
565
+ ${!e && !rp ? html`
566
+ <div class="sec-h3">${t('secretary.strat.milestones')}</div>
567
+ ${ms.length === 0 ? html`<p class="sec-hint">${t('secretary.strat.noMilestones')}</p>` : html`
568
+ <ol class="sec-dir-ms">
569
+ ${ms.map((m, i) => html`
570
+ <li class=${`sec-dir-ms-row${i === focusIdx ? ' focus' : ''}${m.status === 'reached' ? ' reached' : ''}`} key=${m.id || (m.title + i)}>
571
+ <button class=${`sec-dir-pill sec-dir-${m.status}`} title=${t('secretary.strat.advance')} onClick=${() => p.onCycle(i)}>${statusLabel(m.status)}</button>
572
+ <div class="sec-dir-ms-main">
573
+ <div class="sec-dir-ms-title">${m.title}${i === focusIdx ? html` <span class="sec-dir-focus">${t('secretary.strat.focus')}</span>` : null}</div>
574
+ ${m.enables ? html`<div class="sec-hint">${t('secretary.strat.enables')}: ${m.enables}</div>` : null}
575
+ ${m.criterion ? html`<div class="sec-hint">${t('secretary.strat.criterion')}: ${m.criterion}</div>` : null}
576
+ ${goals.length ? html`<details class="sec-dir-goals"><summary class="sec-hint">${t('secretary.strat.linkGoals')} (${(m.goalRefs || []).length})</summary>
577
+ ${goals.map((g) => html`<label class="sec-hint sec-dir-goal-opt" key=${g.id}>
578
+ <input type="checkbox" checked=${(m.goalRefs || []).includes(g.id)} onChange=${() => p.onToggleGoal(i, g.id)} /> ${g.title}${g.status === 'done' ? ' ✓' : ''}
579
+ </label>`)}
580
+ </details>` : null}
581
+ </div>
582
+ <div class="sec-trig-btns">
583
+ <button class="sec-linkbtn" disabled=${i === 0} title=${t('secretary.layout.up')} onClick=${() => p.onMove(i, -1)}>↑</button>
584
+ <button class="sec-linkbtn" disabled=${i === ms.length - 1} title=${t('secretary.layout.down')} onClick=${() => p.onMove(i, 1)}>↓</button>
585
+ <button class="sec-linkbtn sec-next-discard" title=${t('secretary.remove')} onClick=${() => p.onRemove(i)}>✕</button>
586
+ </div>
587
+ </li>`)}
588
+ </ol>`}
589
+ <div class="sec-note-bar">
590
+ <input class="sec-input sec-input-sm" placeholder=${t('secretary.strat.addPlaceholder')} value=${p.msInput || ''}
591
+ onInput=${(ev) => p.onMsInput(ev.target.value)}
592
+ onKeyDown=${(ev) => { if (ev.key === 'Enter') { ev.preventDefault(); p.onAdd(); } }} />
593
+ <button class="btn-outline btn-sm" disabled=${!(p.msInput || '').trim()} onClick=${p.onAdd}>${t('secretary.strat.add')}</button>
594
+ </div>
595
+ <button class="sec-linkbtn sec-dir-off" onClick=${p.onDisable}>${t('secretary.strat.stop')}</button>` : null}
596
+ </section>`;
597
+ }
598
+
599
+ /** "Reset Secretary" — a guarded full wipe (start over). The owner types RESET to confirm; the view's
600
+ * handler calls POST /v1/secretary/reset and reloads. Lives at the bottom of "Manage & setup". */
601
+ export function secretaryDangerCard(p) {
602
+ return html`
603
+ <section class="sec-card sec-danger">
604
+ <h2 class="sec-h2">${t('secretary.reset.title')}</h2>
605
+ <p class="sec-hint">${t('secretary.reset.hint')}</p>
606
+ ${!p.show
607
+ ? html`<button class="btn-danger btn-sm" onClick=${p.onOpen}>${t('secretary.reset.start')}</button>`
608
+ : html`
609
+ <p class="sec-warn">⚠ ${t('secretary.reset.warn')}</p>
610
+ <label class="sec-hint">${t('secretary.reset.confirmLabel')}</label>
611
+ <input class="sec-input" value=${p.text} onInput=${(e) => p.onText(e.target.value)} placeholder="RESET" />
612
+ <div class="sec-actions">
613
+ <button class="btn-ghost btn-sm" disabled=${p.busy} onClick=${p.onCancel}>${t('secretary.cancel')}</button>
614
+ <button class="btn-danger-solid btn-sm" disabled=${p.busy || (p.text || '').trim().toUpperCase() !== 'RESET'} onClick=${p.onConfirm}>${p.busy ? t('secretary.reset.resetting') : t('secretary.reset.confirm')}</button>
615
+ </div>`}
616
+ </section>`;
617
+ }