aimeat 1.7.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 (261) hide show
  1. package/dist/.env.example +4 -1
  2. package/dist/locales/en.json +573 -10
  3. package/dist/locales/fi.json +572 -9
  4. package/dist/public/css/views/admin-agent-integration.css +304 -0
  5. package/dist/public/css/views/agents-detail.css +1697 -120
  6. package/dist/public/css/views/profile.css +22 -2
  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/js/services/generator-spec-validate.js +23 -0
  13. package/dist/public/js/services/memory.js +39 -0
  14. package/dist/public/lib/live-updates.js +9 -4
  15. package/dist/public/llms-template.txt +106 -0
  16. package/dist/public/spa.html +7 -2
  17. package/dist/public/views/admin/agent-integration-tab.js +390 -0
  18. package/dist/public/views/admin/federation-tab.js +5 -5
  19. package/dist/public/views/admin/prompts-tab.js +9 -10
  20. package/dist/public/views/admin.js +2 -0
  21. package/dist/public/views/profile/access-tab.js +18 -0
  22. package/dist/public/views/profile/agents/agent-card.js +396 -0
  23. package/dist/public/views/profile/agents/shared-board.js +97 -0
  24. package/dist/public/views/profile/agents/state-detector.js +62 -0
  25. package/dist/public/views/profile/agents/tab-activity.js +232 -0
  26. package/dist/public/views/profile/agents/tab-agent-config.js +199 -0
  27. package/dist/public/views/profile/agents/tab-data-access.js +304 -0
  28. package/dist/public/views/profile/agents/tab-directives.js +178 -0
  29. package/dist/public/views/profile/agents/tab-integration.js +505 -0
  30. package/dist/public/views/profile/agents/tab-messages.js +278 -0
  31. package/dist/public/views/profile/agents/tab-services.js +17 -0
  32. package/dist/public/views/profile/agents/tab-tasks.js +17 -0
  33. package/dist/public/views/profile/agents-messages-subtab.js +12 -10
  34. package/dist/public/views/profile/agents-services-subtab.js +17 -11
  35. package/dist/public/views/profile/agents-tab.js +159 -255
  36. package/dist/public/views/profile/agents-task-builder.js +403 -0
  37. package/dist/public/views/profile/agents-tasks-subtab.js +186 -140
  38. package/dist/public/views/profile/generator-detail.js +8 -1
  39. package/dist/public/views/profile/memory-tab.js +371 -7
  40. package/dist/public/views/profile/wallet-tab.js +14 -0
  41. package/dist/public/views/public-knowledge-viewer.js +650 -0
  42. package/dist/src/auth/middleware.d.ts.map +1 -1
  43. package/dist/src/auth/middleware.js +17 -0
  44. package/dist/src/auth/middleware.js.map +1 -1
  45. package/dist/src/config.d.ts +1 -0
  46. package/dist/src/config.d.ts.map +1 -1
  47. package/dist/src/config.js +1 -0
  48. package/dist/src/config.js.map +1 -1
  49. package/dist/src/middleware/readiness-gate.d.ts +18 -0
  50. package/dist/src/middleware/readiness-gate.d.ts.map +1 -0
  51. package/dist/src/middleware/readiness-gate.js +50 -0
  52. package/dist/src/middleware/readiness-gate.js.map +1 -0
  53. package/dist/src/models/agent-capabilities-schemas.d.ts +3 -0
  54. package/dist/src/models/agent-capabilities-schemas.d.ts.map +1 -1
  55. package/dist/src/models/agent-capabilities-schemas.js +3 -0
  56. package/dist/src/models/agent-capabilities-schemas.js.map +1 -1
  57. package/dist/src/models/agent-directives-schemas.d.ts +12 -0
  58. package/dist/src/models/agent-directives-schemas.d.ts.map +1 -1
  59. package/dist/src/models/agent-directives-schemas.js +7 -0
  60. package/dist/src/models/agent-directives-schemas.js.map +1 -1
  61. package/dist/src/models/agent-onboarding-schemas.d.ts +45 -0
  62. package/dist/src/models/agent-onboarding-schemas.d.ts.map +1 -0
  63. package/dist/src/models/agent-onboarding-schemas.js +91 -0
  64. package/dist/src/models/agent-onboarding-schemas.js.map +1 -0
  65. package/dist/src/models/webhook-schemas.d.ts +266 -0
  66. package/dist/src/models/webhook-schemas.d.ts.map +1 -0
  67. package/dist/src/models/webhook-schemas.js +137 -0
  68. package/dist/src/models/webhook-schemas.js.map +1 -0
  69. package/dist/src/routes/admin-agent-integration.d.ts +24 -0
  70. package/dist/src/routes/admin-agent-integration.d.ts.map +1 -0
  71. package/dist/src/routes/admin-agent-integration.js +186 -0
  72. package/dist/src/routes/admin-agent-integration.js.map +1 -0
  73. package/dist/src/routes/admin-monitoring.js +12 -7
  74. package/dist/src/routes/admin-monitoring.js.map +1 -1
  75. package/dist/src/routes/agent-activity.d.ts.map +1 -1
  76. package/dist/src/routes/agent-activity.js +17 -0
  77. package/dist/src/routes/agent-activity.js.map +1 -1
  78. package/dist/src/routes/agent-capabilities.d.ts +1 -0
  79. package/dist/src/routes/agent-capabilities.d.ts.map +1 -1
  80. package/dist/src/routes/agent-capabilities.js +9 -0
  81. package/dist/src/routes/agent-capabilities.js.map +1 -1
  82. package/dist/src/routes/agent-directives.d.ts +5 -1
  83. package/dist/src/routes/agent-directives.d.ts.map +1 -1
  84. package/dist/src/routes/agent-directives.js +74 -20
  85. package/dist/src/routes/agent-directives.js.map +1 -1
  86. package/dist/src/routes/agent-integration.d.ts +1 -0
  87. package/dist/src/routes/agent-integration.d.ts.map +1 -1
  88. package/dist/src/routes/agent-integration.js +128 -11
  89. package/dist/src/routes/agent-integration.js.map +1 -1
  90. package/dist/src/routes/agent-messages.d.ts +5 -1
  91. package/dist/src/routes/agent-messages.d.ts.map +1 -1
  92. package/dist/src/routes/agent-messages.js +20 -1
  93. package/dist/src/routes/agent-messages.js.map +1 -1
  94. package/dist/src/routes/agent-onboarding.d.ts +23 -0
  95. package/dist/src/routes/agent-onboarding.d.ts.map +1 -0
  96. package/dist/src/routes/agent-onboarding.js +374 -0
  97. package/dist/src/routes/agent-onboarding.js.map +1 -0
  98. package/dist/src/routes/agent-skill-bundle.d.ts +16 -0
  99. package/dist/src/routes/agent-skill-bundle.d.ts.map +1 -0
  100. package/dist/src/routes/agent-skill-bundle.js +133 -0
  101. package/dist/src/routes/agent-skill-bundle.js.map +1 -0
  102. package/dist/src/routes/agent-tasks.d.ts +8 -1
  103. package/dist/src/routes/agent-tasks.d.ts.map +1 -1
  104. package/dist/src/routes/agent-tasks.js +192 -14
  105. package/dist/src/routes/agent-tasks.js.map +1 -1
  106. package/dist/src/routes/agent-telemetry.d.ts +15 -0
  107. package/dist/src/routes/agent-telemetry.d.ts.map +1 -0
  108. package/dist/src/routes/agent-telemetry.js +99 -0
  109. package/dist/src/routes/agent-telemetry.js.map +1 -0
  110. package/dist/src/routes/agent-webhook.d.ts +18 -0
  111. package/dist/src/routes/agent-webhook.d.ts.map +1 -0
  112. package/dist/src/routes/agent-webhook.js +243 -0
  113. package/dist/src/routes/agent-webhook.js.map +1 -0
  114. package/dist/src/routes/agents.d.ts.map +1 -1
  115. package/dist/src/routes/agents.js +203 -4
  116. package/dist/src/routes/agents.js.map +1 -1
  117. package/dist/src/routes/auth.d.ts.map +1 -1
  118. package/dist/src/routes/auth.js +45 -2
  119. package/dist/src/routes/auth.js.map +1 -1
  120. package/dist/src/routes/federation-auth.d.ts.map +1 -1
  121. package/dist/src/routes/federation-auth.js +3 -1
  122. package/dist/src/routes/federation-auth.js.map +1 -1
  123. package/dist/src/routes/federation-peer.d.ts.map +1 -1
  124. package/dist/src/routes/federation-peer.js +11 -2
  125. package/dist/src/routes/federation-peer.js.map +1 -1
  126. package/dist/src/routes/federation-sync.d.ts +1 -0
  127. package/dist/src/routes/federation-sync.d.ts.map +1 -1
  128. package/dist/src/routes/federation-sync.js +29 -0
  129. package/dist/src/routes/federation-sync.js.map +1 -1
  130. package/dist/src/routes/ghii.d.ts.map +1 -1
  131. package/dist/src/routes/ghii.js +42 -5
  132. package/dist/src/routes/ghii.js.map +1 -1
  133. package/dist/src/routes/knowledge.d.ts.map +1 -1
  134. package/dist/src/routes/knowledge.js +19 -7
  135. package/dist/src/routes/knowledge.js.map +1 -1
  136. package/dist/src/routes/memory.d.ts +9 -0
  137. package/dist/src/routes/memory.d.ts.map +1 -1
  138. package/dist/src/routes/memory.js +307 -12
  139. package/dist/src/routes/memory.js.map +1 -1
  140. package/dist/src/routes/portal.d.ts.map +1 -1
  141. package/dist/src/routes/portal.js +1 -0
  142. package/dist/src/routes/portal.js.map +1 -1
  143. package/dist/src/routes/prompts.d.ts +1 -0
  144. package/dist/src/routes/prompts.d.ts.map +1 -1
  145. package/dist/src/routes/prompts.js +40 -1
  146. package/dist/src/routes/prompts.js.map +1 -1
  147. package/dist/src/routes/sharing-groups.d.ts.map +1 -1
  148. package/dist/src/routes/sharing-groups.js +8 -2
  149. package/dist/src/routes/sharing-groups.js.map +1 -1
  150. package/dist/src/routes/wallet.d.ts.map +1 -1
  151. package/dist/src/routes/wallet.js +2 -0
  152. package/dist/src/routes/wallet.js.map +1 -1
  153. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  154. package/dist/src/server-bootstrap/routes-loader.js +47 -3
  155. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  156. package/dist/src/services/config-schema.d.ts.map +1 -1
  157. package/dist/src/services/config-schema.js +2 -1
  158. package/dist/src/services/config-schema.js.map +1 -1
  159. package/dist/src/services/core-jobs.d.ts.map +1 -1
  160. package/dist/src/services/core-jobs.js +3 -2
  161. package/dist/src/services/core-jobs.js.map +1 -1
  162. package/dist/src/services/federation-helpers.d.ts +1 -0
  163. package/dist/src/services/federation-helpers.d.ts.map +1 -1
  164. package/dist/src/services/federation-helpers.js +1 -1
  165. package/dist/src/services/federation-helpers.js.map +1 -1
  166. package/dist/src/services/generator-autopilot.d.ts.map +1 -1
  167. package/dist/src/services/generator-autopilot.js +125 -26
  168. package/dist/src/services/generator-autopilot.js.map +1 -1
  169. package/dist/src/services/generator-prompts/index.d.ts +1 -1
  170. package/dist/src/services/generator-prompts/index.d.ts.map +1 -1
  171. package/dist/src/services/generator-prompts/index.js +1 -1
  172. package/dist/src/services/generator-prompts/index.js.map +1 -1
  173. package/dist/src/services/generator-prompts/spec-validate.d.ts +1 -0
  174. package/dist/src/services/generator-prompts/spec-validate.d.ts.map +1 -1
  175. package/dist/src/services/generator-prompts/spec-validate.js +20 -0
  176. package/dist/src/services/generator-prompts/spec-validate.js.map +1 -1
  177. package/dist/src/services/generator-prompts/validate.d.ts +7 -1
  178. package/dist/src/services/generator-prompts/validate.d.ts.map +1 -1
  179. package/dist/src/services/generator-prompts/validate.js +70 -0
  180. package/dist/src/services/generator-prompts/validate.js.map +1 -1
  181. package/dist/src/services/onboarding-validator.d.ts +22 -0
  182. package/dist/src/services/onboarding-validator.d.ts.map +1 -0
  183. package/dist/src/services/onboarding-validator.js +202 -0
  184. package/dist/src/services/onboarding-validator.js.map +1 -0
  185. package/dist/src/services/platform-detector.d.ts +27 -0
  186. package/dist/src/services/platform-detector.d.ts.map +1 -0
  187. package/dist/src/services/platform-detector.js +73 -0
  188. package/dist/src/services/platform-detector.js.map +1 -0
  189. package/dist/src/services/prompt-defaults.d.ts +1 -0
  190. package/dist/src/services/prompt-defaults.d.ts.map +1 -1
  191. package/dist/src/services/prompt-defaults.js +875 -47
  192. package/dist/src/services/prompt-defaults.js.map +1 -1
  193. package/dist/src/services/prompt-seeder.js +1 -1
  194. package/dist/src/services/prompt-seeder.js.map +1 -1
  195. package/dist/src/services/readiness-scorer.d.ts +34 -0
  196. package/dist/src/services/readiness-scorer.d.ts.map +1 -0
  197. package/dist/src/services/readiness-scorer.js +87 -0
  198. package/dist/src/services/readiness-scorer.js.map +1 -0
  199. package/dist/src/services/skill-bundle/generator.d.ts +17 -0
  200. package/dist/src/services/skill-bundle/generator.d.ts.map +1 -0
  201. package/dist/src/services/skill-bundle/generator.js +387 -0
  202. package/dist/src/services/skill-bundle/generator.js.map +1 -0
  203. package/dist/src/services/skill-bundle/generic-adapter.d.ts +16 -0
  204. package/dist/src/services/skill-bundle/generic-adapter.d.ts.map +1 -0
  205. package/dist/src/services/skill-bundle/generic-adapter.js +96 -0
  206. package/dist/src/services/skill-bundle/generic-adapter.js.map +1 -0
  207. package/dist/src/services/skill-bundle/hermes-adapter.d.ts +25 -0
  208. package/dist/src/services/skill-bundle/hermes-adapter.d.ts.map +1 -0
  209. package/dist/src/services/skill-bundle/hermes-adapter.js +302 -0
  210. package/dist/src/services/skill-bundle/hermes-adapter.js.map +1 -0
  211. package/dist/src/services/skill-bundle/types.d.ts +54 -0
  212. package/dist/src/services/skill-bundle/types.d.ts.map +1 -0
  213. package/dist/src/services/skill-bundle/types.js +8 -0
  214. package/dist/src/services/skill-bundle/types.js.map +1 -0
  215. package/dist/src/services/task-stall-detector.d.ts +10 -2
  216. package/dist/src/services/task-stall-detector.d.ts.map +1 -1
  217. package/dist/src/services/task-stall-detector.js +34 -5
  218. package/dist/src/services/task-stall-detector.js.map +1 -1
  219. package/dist/src/services/webhook-dispatcher.d.ts +71 -0
  220. package/dist/src/services/webhook-dispatcher.d.ts.map +1 -0
  221. package/dist/src/services/webhook-dispatcher.js +259 -0
  222. package/dist/src/services/webhook-dispatcher.js.map +1 -0
  223. package/dist/src/storage/interface.d.ts +82 -2
  224. package/dist/src/storage/interface.d.ts.map +1 -1
  225. package/dist/src/storage/providers/mongodb/index.d.ts +20 -1
  226. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  227. package/dist/src/storage/providers/mongodb/index.js +254 -3
  228. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  229. package/dist/src/storage/providers/sqlite/index.d.ts +18 -1
  230. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  231. package/dist/src/storage/providers/sqlite/index.js +173 -4
  232. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  233. package/dist/src/storage/providers/sqlite/repos/agent-directives.d.ts.map +1 -1
  234. package/dist/src/storage/providers/sqlite/repos/agent-directives.js +8 -4
  235. package/dist/src/storage/providers/sqlite/repos/agent-directives.js.map +1 -1
  236. package/dist/src/storage/providers/sqlite/repos/agent.d.ts.map +1 -1
  237. package/dist/src/storage/providers/sqlite/repos/agent.js +25 -4
  238. package/dist/src/storage/providers/sqlite/repos/agent.js.map +1 -1
  239. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  240. package/dist/src/storage/providers/sqlite/schema.js +73 -1
  241. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  242. package/dist/src/storage/repositories/agent-onboarding.repository.d.ts +16 -0
  243. package/dist/src/storage/repositories/agent-onboarding.repository.d.ts.map +1 -0
  244. package/dist/src/storage/repositories/agent-onboarding.repository.js +8 -0
  245. package/dist/src/storage/repositories/agent-onboarding.repository.js.map +1 -0
  246. package/dist/src/storage/repositories/agent-webhook.repository.d.ts +21 -0
  247. package/dist/src/storage/repositories/agent-webhook.repository.d.ts.map +1 -0
  248. package/dist/src/storage/repositories/agent-webhook.repository.js +2 -0
  249. package/dist/src/storage/repositories/agent-webhook.repository.js.map +1 -0
  250. package/dist/src/storage/repositories/agent.repository.d.ts +1 -0
  251. package/dist/src/storage/repositories/agent.repository.d.ts.map +1 -1
  252. package/dist/src/storage/repositories/index.d.ts +2 -0
  253. package/dist/src/storage/repositories/index.d.ts.map +1 -1
  254. package/dist/src/utils/env-config.d.ts.map +1 -1
  255. package/dist/src/utils/env-config.js +7 -1
  256. package/dist/src/utils/env-config.js.map +1 -1
  257. package/dist/src/utils/url-validator.d.ts.map +1 -1
  258. package/dist/src/utils/url-validator.js +3 -0
  259. package/dist/src/utils/url-validator.js.map +1 -1
  260. package/package.json +1 -1
  261. package/prisma/schema.prisma +67 -7
@@ -1,13 +1,18 @@
1
1
  /**
2
2
  * @file agents-tasks-subtab.js
3
3
  * @description Task queue sub-tab for agent detail view.
4
- * Shows queued, active, and completed tasks with inline creation form.
5
- * Listens for live updates to refresh task data automatically.
4
+ * Shows queued, active, and completed tasks with todo plans,
5
+ * "Start this task" approval button, and progress tracking.
6
+ * Filter pills allow switching between all/active/queued/completed/failed views.
6
7
  * @structure
7
- * - AgentTasksSubtab (default export) -- main component
8
- * - TaskItem -- individual task row with expand/collapse
9
- * - TaskCreateForm -- inline task creation form
8
+ * - AgentTasksSubtab (default export) -- main component with filter pills
9
+ * - TaskItem -- task row with expand/collapse, todo list, start button
10
10
  * @version-history
11
+ * v3.2.0 -- 2026-05-24 -- Add interactive filter pills, scope/rules display, migrate to pf-agd- CSS prefix
12
+ * v3.1.0 -- 2026-05-24 -- Fix: use correct locale key for empty state
13
+ * v3.0.0 -- 2026-05-22 -- Replace TaskCreateForm with TaskCreationBuilder (design spec split-panel)
14
+ * v2.1.0 -- 2026-05-22 -- Fix: use camelCase field names from API; add task and todo timestamps
15
+ * v2.0.0 -- 2026-05-22 -- Add todo rendering, Start button, progress tracking
11
16
  * v1.0.0 -- 2026-05-21 -- Initial creation for Agent Dashboard Phase 1
12
17
  */
13
18
  import { h } from 'preact';
@@ -16,86 +21,195 @@ import htm from 'htm';
16
21
  const html = htm.bind(h);
17
22
  import { t } from '/js/i18n.js';
18
23
  import { timeAgo } from '/js/utils.js';
19
- import { listTasks, createTask, deleteTask, listEvents } from '/js/services/agent-tasks.js';
24
+ import { listTasks, deleteTask, startTask, listEvents } from '/js/services/agent-tasks.js';
25
+ import TaskCreationBuilder from './agents-task-builder.js';
26
+
27
+ const TASK_FILTERS = ['all', 'active', 'queued', 'completed', 'failed'];
20
28
 
21
29
  function statusLabel(status) {
22
- const key = `profile.agents.tasks.status.${status}`;
30
+ const key = `profile.agents.tasks.${status}`;
23
31
  const val = t(key);
24
- // Fallback to capitalized status if translation key not found
25
32
  return val !== key ? val : status.charAt(0).toUpperCase() + status.slice(1);
26
33
  }
27
34
 
28
- function TaskItem({ task, agentName, showToast }) {
35
+ function todoStatusIcon(status) {
36
+ if (status === 'done') return '✅';
37
+ if (status === 'failed') return '❌';
38
+ if (status === 'skipped') return '⏭';
39
+ if (status === 'active') return '▶';
40
+ return '⬜';
41
+ }
42
+
43
+ function todoProgress(todos) {
44
+ if (!todos || todos.length === 0) return null;
45
+ const done = todos.filter(td => td.status === 'done').length;
46
+ return `${done}/${todos.length}`;
47
+ }
48
+
49
+ function TaskItem({ task, agentName, showToast, onRefresh }) {
29
50
  const [expanded, setExpanded] = useState(false);
30
51
  const [events, setEvents] = useState(null);
31
52
  const [loadingEvents, setLoadingEvents] = useState(false);
53
+ const [starting, setStarting] = useState(false);
32
54
 
33
55
  async function handleExpand(e) {
34
56
  e.stopPropagation();
35
- if (expanded) {
36
- setExpanded(false);
37
- return;
38
- }
57
+ if (expanded) { setExpanded(false); return; }
39
58
  setExpanded(true);
40
59
  if (!events) {
41
60
  setLoadingEvents(true);
42
61
  try {
43
62
  const resp = await listEvents(agentName, task.id);
44
63
  setEvents(resp?.data?.events || []);
45
- } catch {
46
- setEvents([]);
47
- }
64
+ } catch { setEvents([]); }
48
65
  setLoadingEvents(false);
49
66
  }
50
67
  }
51
68
 
69
+ async function handleStart(e) {
70
+ e.stopPropagation();
71
+ setStarting(true);
72
+ try {
73
+ await startTask(agentName, task.id);
74
+ showToast(t('profile.agents.tasks.taskStarted'));
75
+ onRefresh();
76
+ } catch (err) {
77
+ showToast(err.message || t('profile.agents.tasks.startError'), true);
78
+ }
79
+ setStarting(false);
80
+ }
81
+
52
82
  async function handleDelete(e) {
53
83
  e.stopPropagation();
54
84
  try {
55
85
  await deleteTask(agentName, task.id);
56
- const msg = t('profile.agents.tasks.deleted');
57
- showToast(msg !== 'profile.agents.tasks.deleted' ? msg : 'Task deleted');
86
+ showToast(t('profile.agents.tasks.taskDeleted'));
87
+ onRefresh();
58
88
  } catch (err) {
59
- showToast(err.message || 'Failed to delete task', true);
89
+ showToast(err.message || t('profile.agents.tasks.deleteError'), true);
60
90
  }
61
91
  }
62
92
 
93
+ const todos = task.todos || [];
94
+ const hasTodos = todos.length > 0;
95
+ const progress = todoProgress(todos);
96
+ const isQueued = task.status === 'queued' || task.status === 'draft';
97
+ const isActive = task.status === 'active';
98
+ const canStart = isQueued && hasTodos;
99
+ const totalMinutes = todos.reduce((sum, td) => sum + (td.estimateMinutes || 0), 0);
100
+ const aimeatSteps = todos.filter(td => td.environment === 'aimeat').length;
101
+ const agentSteps = todos.filter(td => td.environment === 'agent').length;
102
+
103
+ function formatDateTime(iso) {
104
+ if (!iso) return '';
105
+ const d = new Date(iso);
106
+ return d.toLocaleDateString([], { day: 'numeric', month: 'short' }) + ' ' +
107
+ d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
108
+ }
109
+
63
110
  return html`
64
111
  <div>
65
- <div class="agd-task-item" onClick=${handleExpand}>
66
- <span class="agd-task-title">${task.title || task.id}</span>
67
- <div class="agd-task-meta">
68
- ${task.created_at && html`<span class="agd-task-time">${timeAgo(task.created_at)}</span>`}
69
- <span class="agd-status agd-status-${task.status || 'draft'}">${statusLabel(task.status || 'draft')}</span>
112
+ <div class="pf-agd-task-item" onClick=${handleExpand}>
113
+ <div class="pf-agd-task-title-row">
114
+ <span class="pf-agd-task-title">${task.title || task.id}</span>
115
+ ${progress && html`<span class="pf-agd-todo-progress">${progress}</span>`}
116
+ </div>
117
+ <div class="pf-agd-task-meta">
118
+ ${task.createdAt && html`<span class="pf-agd-task-time">${timeAgo(task.createdAt)}</span>`}
119
+ <span class="pf-agd-status pf-agd-status-${task.status || 'draft'}">${statusLabel(task.status || 'draft')}</span>
70
120
  </div>
71
121
  </div>
72
122
  ${expanded && html`
73
- <div class="agd-task-expanded">
74
- ${task.description && html`<div class="agd-task-desc">${task.description}</div>`}
123
+ <div class="pf-agd-task-expanded">
124
+ ${task.description && html`<div class="pf-agd-task-desc">${task.description}</div>`}
125
+
75
126
  ${task.scope && html`
76
- <div class="agd-task-desc">
77
- <strong>${t('profile.agents.tasks.scope') !== 'profile.agents.tasks.scope' ? t('profile.agents.tasks.scope') : 'Scope'}:</strong> ${Array.isArray(task.scope) ? task.scope.join(', ') : task.scope}
127
+ <div class="pf-agd-info-row">
128
+ <span class="pf-agd-info-label">${t('profile.agents.detail.tasks.scope')}</span>
129
+ <span class="pf-agd-info-value">${Array.isArray(task.scope) ? task.scope.join(', ') : task.scope}</span>
130
+ </div>
131
+ `}
132
+ ${task.rules && task.rules.length > 0 && html`
133
+ <div class="pf-agd-info-row">
134
+ <span class="pf-agd-info-label">${t('profile.agents.detail.tasks.rules')}</span>
135
+ <span class="pf-agd-info-value">
136
+ ${Array.isArray(task.rules) ? task.rules.map(r => html`<div key=${r}>${r}</div>`) : task.rules}
137
+ </span>
138
+ </div>
139
+ `}
140
+
141
+ <div class="pf-agd-task-timestamps">
142
+ ${task.createdAt && html`<span>${t('profile.agents.tasks.created')}: ${formatDateTime(task.createdAt)}</span>`}
143
+ ${task.updatedAt && task.updatedAt !== task.createdAt && html`<span>${t('profile.agents.tasks.updated')}: ${formatDateTime(task.updatedAt)}</span>`}
144
+ ${task.completedAt && html`<span>${t('profile.agents.tasks.completed')}: ${formatDateTime(task.completedAt)}</span>`}
145
+ </div>
146
+
147
+ ${hasTodos && html`
148
+ <div class="pf-agd-todo-section">
149
+ <div class="pf-agd-todo-header">
150
+ <strong>${t('profile.agents.tasks.todoLabel')}</strong>
151
+ <span class="pf-agd-todo-summary">
152
+ ${aimeatSteps > 0 && html`<span class="pf-agd-env-badge pf-agd-env-aimeat">${t('profile.agents.tasks.envAimeat')}: ${aimeatSteps}</span>`}
153
+ ${agentSteps > 0 && html`<span class="pf-agd-env-badge pf-agd-env-agent">${t('profile.agents.tasks.envAgent')}: ${agentSteps}</span>`}
154
+ ${totalMinutes > 0 && html`<span class="pf-agd-todo-time">~${totalMinutes} ${t('profile.agents.tasks.minuteShort')}</span>`}
155
+ </span>
156
+ </div>
157
+ <div class="pf-agd-todo-list">
158
+ ${todos.map((td, i) => html`
159
+ <div class="pf-agd-todo-item pf-agd-todo-${td.status || 'pending'}" key=${td.id || i}>
160
+ <span class="pf-agd-todo-icon">${todoStatusIcon(td.status || 'pending')}</span>
161
+ <div class="pf-agd-todo-content">
162
+ <div class="pf-agd-todo-title">
163
+ ${td.title}
164
+ <span class="pf-agd-env-badge pf-agd-env-${td.environment || 'agent'}">${td.environment === 'aimeat' ? t('profile.agents.tasks.envAimeat') : t('profile.agents.tasks.envAgent')}</span>
165
+ ${td.estimateMinutes && html`
166
+ <span class="pf-agd-todo-est">${td.estimateMinutes} ${t('profile.agents.tasks.minuteShort')}</span>
167
+ `}
168
+ </div>
169
+ ${td.description && html`<div class="pf-agd-todo-desc">${td.description}</div>`}
170
+ ${td.environmentReason && html`
171
+ <div class="pf-agd-todo-reason">${td.environmentReason}</div>
172
+ `}
173
+ ${td.verification && html`<div class="pf-agd-todo-verify">${td.verification}</div>`}
174
+ ${td.completedAt && html`<div class="pf-agd-todo-completed-at">${formatDateTime(td.completedAt)}</div>`}
175
+ </div>
176
+ </div>
177
+ `)}
178
+ </div>
78
179
  </div>
79
180
  `}
80
- ${loadingEvents && html`<div class="agd-empty">${t('profile.loading')}</div>`}
181
+
182
+ ${!hasTodos && isQueued && html`
183
+ <div class="pf-agd-todo-waiting">
184
+ ${t('profile.agents.tasks.builder.waitingTodos')}
185
+ </div>
186
+ `}
187
+
188
+ ${loadingEvents && html`<div class="pf-agd-empty">${t('profile.loading')}</div>`}
81
189
  ${events && events.length > 0 && html`
82
- <div class="agd-event-log">
190
+ <div class="pf-agd-event-log">
83
191
  ${events.map(ev => html`
84
- <div class="agd-event-item" key=${ev.id || ev.timestamp}>
85
- <span class="agd-event-time">${ev.timestamp ? timeAgo(ev.timestamp) : ''}</span>
86
- <span class="agd-event-type">${ev.type || ev.event || ''}</span>
87
- <span>${ev.message || ev.data || ''}</span>
192
+ <div class="pf-agd-event-item" key=${ev.id || ev.timestamp}>
193
+ <span class="pf-agd-event-time">${ev.timestamp ? timeAgo(ev.timestamp) : ''}</span>
194
+ <span class="pf-agd-event-type">${ev.type || ''}</span>
195
+ <span>${ev.message || ''}</span>
88
196
  </div>
89
197
  `)}
90
198
  </div>
91
199
  `}
92
- ${events && events.length === 0 && html`
93
- <div class="agd-empty">${t('profile.agents.tasks.noEvents') !== 'profile.agents.tasks.noEvents' ? t('profile.agents.tasks.noEvents') : 'No events recorded'}</div>
200
+ ${events && events.length === 0 && !isQueued && html`
201
+ <div class="pf-agd-empty">${t('profile.agents.tasks.noEventsRecorded')}</div>
94
202
  `}
95
- <div class="agd-task-actions">
96
- <button class="btn-danger btn-sm" onClick=${handleDelete}>
97
- ${t('profile.agents.tasks.delete') !== 'profile.agents.tasks.delete' ? t('profile.agents.tasks.delete') : 'Delete'}
98
- </button>
203
+
204
+ <div class="pf-agd-task-actions">
205
+ ${canStart && html`
206
+ <button class="btn-primary btn-sm" onClick=${handleStart} disabled=${starting}>
207
+ ${starting ? t('profile.agents.tasks.starting') : t('profile.agents.tasks.startThisTask')}
208
+ </button>
209
+ `}
210
+ ${(isQueued || task.status === 'draft') && html`
211
+ <button class="btn-danger btn-sm" onClick=${handleDelete}>${t('profile.agents.tasks.delete')}</button>
212
+ `}
99
213
  </div>
100
214
  </div>
101
215
  `}
@@ -103,69 +217,11 @@ function TaskItem({ task, agentName, showToast }) {
103
217
  `;
104
218
  }
105
219
 
106
- function TaskCreateForm({ agentName, showToast, onCreated, onCancel }) {
107
- const [title, setTitle] = useState('');
108
- const [description, setDescription] = useState('');
109
- const [status, setStatus] = useState('queued');
110
- const [saving, setSaving] = useState(false);
111
-
112
- async function handleSubmit(e) {
113
- e.preventDefault();
114
- if (!title.trim()) return;
115
- setSaving(true);
116
- try {
117
- await createTask(agentName, {
118
- title: title.trim(),
119
- description: description.trim() || undefined,
120
- status,
121
- });
122
- const msg = t('profile.agents.tasks.created');
123
- showToast(msg !== 'profile.agents.tasks.created' ? msg : 'Task created');
124
- onCreated();
125
- } catch (err) {
126
- showToast(err.message || 'Failed to create task', true);
127
- }
128
- setSaving(false);
129
- }
130
-
131
- return html`
132
- <form class="agd-create-form" onSubmit=${handleSubmit}>
133
- <div class="agd-form-field">
134
- <label>${t('profile.agents.tasks.titleLabel') !== 'profile.agents.tasks.titleLabel' ? t('profile.agents.tasks.titleLabel') : 'Title'}</label>
135
- <input type="text" value=${title} onInput=${(e) => setTitle(e.target.value)}
136
- placeholder=${t('profile.agents.tasks.titlePlaceholder') !== 'profile.agents.tasks.titlePlaceholder' ? t('profile.agents.tasks.titlePlaceholder') : 'What should the agent do?'}
137
- required />
138
- </div>
139
- <div class="agd-form-field">
140
- <label>${t('profile.agents.tasks.descriptionLabel') !== 'profile.agents.tasks.descriptionLabel' ? t('profile.agents.tasks.descriptionLabel') : 'Description'}</label>
141
- <textarea value=${description} onInput=${(e) => setDescription(e.target.value)}
142
- placeholder=${t('profile.agents.tasks.descriptionPlaceholder') !== 'profile.agents.tasks.descriptionPlaceholder' ? t('profile.agents.tasks.descriptionPlaceholder') : 'Detailed instructions (optional)'}></textarea>
143
- </div>
144
- <div class="agd-form-field">
145
- <label>${t('profile.agents.tasks.initialStatus') !== 'profile.agents.tasks.initialStatus' ? t('profile.agents.tasks.initialStatus') : 'Initial status'}</label>
146
- <select value=${status} onChange=${(e) => setStatus(e.target.value)}>
147
- <option value="draft">${statusLabel('draft')}</option>
148
- <option value="queued">${statusLabel('queued')}</option>
149
- </select>
150
- </div>
151
- <div class="agd-form-actions">
152
- <button type="submit" class="btn-primary btn-sm" disabled=${saving || !title.trim()}>
153
- ${saving
154
- ? (t('profile.agents.tasks.creating') !== 'profile.agents.tasks.creating' ? t('profile.agents.tasks.creating') : 'Creating...')
155
- : (t('profile.agents.tasks.createBtn') !== 'profile.agents.tasks.createBtn' ? t('profile.agents.tasks.createBtn') : 'Create Task')}
156
- </button>
157
- <button type="button" class="btn-outline btn-sm" onClick=${onCancel}>
158
- ${t('profile.agents.scopeUi.cancel')}
159
- </button>
160
- </div>
161
- </form>
162
- `;
163
- }
164
-
165
220
  export default function AgentTasksSubtab({ agentName, session, showToast }) {
166
221
  const [tasks, setTasks] = useState(null);
167
222
  const [showCreate, setShowCreate] = useState(false);
168
223
  const [error, setError] = useState(null);
224
+ const [filter, setFilter] = useState('all');
169
225
 
170
226
  async function loadTasks() {
171
227
  try {
@@ -178,11 +234,8 @@ export default function AgentTasksSubtab({ agentName, session, showToast }) {
178
234
  }
179
235
  }
180
236
 
181
- useEffect(() => {
182
- loadTasks();
183
- }, [agentName]);
237
+ useEffect(() => { loadTasks(); }, [agentName]);
184
238
 
185
- // Live update listener
186
239
  const loadRef = useRef(loadTasks);
187
240
  loadRef.current = loadTasks;
188
241
  useEffect(() => {
@@ -197,57 +250,50 @@ export default function AgentTasksSubtab({ agentName, session, showToast }) {
197
250
  }
198
251
 
199
252
  if (tasks === null) {
200
- return html`<div class="agd-empty">${t('profile.loading')}</div>`;
253
+ return html`<div class="pf-agd-empty">${t('profile.loading')}</div>`;
201
254
  }
202
255
 
203
- // Group tasks by status category
204
- const active = tasks.filter(t => t.status === 'active');
205
- const queued = tasks.filter(t => t.status === 'queued' || t.status === 'draft');
206
- const completed = tasks.filter(t => t.status === 'done' || t.status === 'failed' || t.status === 'stalled');
256
+ const filtered = filter === 'all' ? tasks : tasks.filter(task => {
257
+ switch (filter) {
258
+ case 'active': return task.status === 'active';
259
+ case 'queued': return task.status === 'queued' || task.status === 'draft';
260
+ case 'completed': return task.status === 'done';
261
+ case 'failed': return task.status === 'failed' || task.status === 'stalled';
262
+ default: return true;
263
+ }
264
+ });
207
265
 
208
266
  return html`
209
267
  <div>
210
- <div class="agd-section-header">
211
- <span class="agd-section-title">
212
- ${t('profile.agents.tasks.title') !== 'profile.agents.tasks.title' ? t('profile.agents.tasks.title') : 'Tasks'}
213
- ${tasks.length > 0 && html` (${tasks.length})`}
268
+ <div class="pf-agd-section-header">
269
+ <span class="pf-agd-section-title">
270
+ ${t('profile.agents.tasks.title')}${tasks.length > 0 ? ` (${tasks.length})` : ''}
214
271
  </span>
215
- <button class="btn-outline btn-sm" onClick=${(e) => { e.stopPropagation(); setShowCreate(!showCreate); }}>
216
- ${showCreate ? '-' : '+'} ${t('profile.agents.tasks.newTask') !== 'profile.agents.tasks.newTask' ? t('profile.agents.tasks.newTask') : 'New Task'}
272
+ <button class="btn-outline btn-sm" onClick=${() => setShowCreate(!showCreate)}>
273
+ ${showCreate ? '-' : '+'} ${t('profile.agents.tasks.newTask')}
217
274
  </button>
218
275
  </div>
219
276
 
220
277
  ${showCreate && html`
221
- <${TaskCreateForm} agentName=${agentName} showToast=${showToast} onCreated=${handleCreated} onCancel=${() => setShowCreate(false)} />
222
- `}
223
-
224
- ${error && html`<div class="agd-empty">${error}</div>`}
225
-
226
- ${active.length > 0 && html`
227
- <div class="agd-section-title" style="margin-top:0.5rem;margin-bottom:0.25rem">
228
- ${t('profile.agents.tasks.activeSection') !== 'profile.agents.tasks.activeSection' ? t('profile.agents.tasks.activeSection') : 'Active'}
229
- </div>
230
- ${active.map(task => html`<${TaskItem} key=${task.id} task=${task} agentName=${agentName} showToast=${showToast} />`)}
278
+ <${TaskCreationBuilder} agentName=${agentName} session=${session} showToast=${showToast} onClose=${handleCreated} />
231
279
  `}
232
280
 
233
- ${queued.length > 0 && html`
234
- <div class="agd-section-title" style="margin-top:0.5rem;margin-bottom:0.25rem">
235
- ${t('profile.agents.tasks.queuedSection') !== 'profile.agents.tasks.queuedSection' ? t('profile.agents.tasks.queuedSection') : 'Queued'}
236
- </div>
237
- ${queued.map(task => html`<${TaskItem} key=${task.id} task=${task} agentName=${agentName} showToast=${showToast} />`)}
238
- `}
281
+ ${error && html`<div class="pf-agd-empty">${error}</div>`}
239
282
 
240
- ${completed.length > 0 && html`
241
- <div class="agd-section-title" style="margin-top:0.5rem;margin-bottom:0.25rem">
242
- ${t('profile.agents.tasks.completedSection') !== 'profile.agents.tasks.completedSection' ? t('profile.agents.tasks.completedSection') : 'Completed'}
243
- </div>
244
- ${completed.map(task => html`<${TaskItem} key=${task.id} task=${task} agentName=${agentName} showToast=${showToast} />`)}
245
- `}
283
+ <div class="pf-agd-filter-bar">
284
+ ${TASK_FILTERS.map(f => html`
285
+ <button key=${f}
286
+ class="pf-agd-filter-pill ${filter === f ? 'pf-agd-filter-pill--active' : ''}"
287
+ onClick=${() => setFilter(f)}>
288
+ ${t(`profile.agents.detail.tasks.filter.${f}`)}
289
+ </button>
290
+ `)}
291
+ </div>
246
292
 
247
- ${tasks.length === 0 && !showCreate && html`
248
- <div class="agd-empty">
249
- ${t('profile.agents.tasks.empty') !== 'profile.agents.tasks.empty' ? t('profile.agents.tasks.empty') : 'No tasks yet. Create one to get started.'}
250
- </div>
293
+ ${filtered.length > 0 ? filtered.map(task => html`
294
+ <${TaskItem} key=${task.id} task=${task} agentName=${agentName} showToast=${showToast} onRefresh=${loadTasks} />
295
+ `) : html`
296
+ <div class="pf-agd-empty">${t('profile.agents.detail.empty.tasks')}</div>
251
297
  `}
252
298
  </div>
253
299
  `;
@@ -32,7 +32,7 @@ import {
32
32
  // DEPRECATED: browser-side prompt builders no longer used. Prompts loaded from database via API.
33
33
  // import { buildComponentPrompt, buildFixPrompt, buildReflectionPrompt, buildTestPrompt } from '/js/services/generator-prompts.js';
34
34
  import { validateComponent } from '/js/services/generator-validate.js';
35
- import { validateExtensionSpec, validateDataApiSpec, validateComponentSpec, validateAppDomainSpec } from '/js/services/generator-spec-validate.js';
35
+ import { validateExtensionSpec, validateDataApiSpec, validateComponentSpec, validateAppDomainSpec, validateAppSpec } from '/js/services/generator-spec-validate.js';
36
36
  import { verifyContract } from '/js/services/generator-contract.js';
37
37
  import { smokeTest } from '/js/services/generator-smoke.js';
38
38
  import { createBundle } from '/js/services/generator-context-bundle.js';
@@ -672,6 +672,7 @@ export function ComponentDetail({ component, project, components, projectId, int
672
672
  // Validate spec structure
673
673
  let sv;
674
674
  if (component.type === 'extension') sv = validateExtensionSpec(parsed);
675
+ else if (component.type === 'app') sv = validateAppSpec(parsed);
675
676
  else if (component.subtype === 'data') sv = validateDataApiSpec(parsed);
676
677
  else if (component.subtype === 'component') sv = validateComponentSpec(parsed);
677
678
  else if (component.subtype === 'app-domain') sv = validateAppDomainSpec(parsed);
@@ -705,6 +706,12 @@ export function ComponentDetail({ component, project, components, projectId, int
705
706
  try {
706
707
  const parsed = JSON.parse(specResult);
707
708
  await saveSpec(projectId, component.id, parsed);
709
+ // Rebuild code prompt now that the spec is saved
710
+ const freshPrompt = await loadPromptFromBackend(projectId, component.id, 'code');
711
+ setGeneratedPrompt(freshPrompt);
712
+ if (component.prompt) {
713
+ await saveComponent(projectId, { ...component, prompt: freshPrompt, spec: parsed });
714
+ }
708
715
  showToast?.('Spec saved');
709
716
  onUpdate();
710
717
  } catch (e) {