ltcai 4.0.1 → 4.2.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 (192) hide show
  1. package/README.md +33 -24
  2. package/desktop/electron/main.cjs +44 -0
  3. package/docs/CHANGELOG.md +84 -0
  4. package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +65 -0
  5. package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +70 -0
  6. package/docs/V4_1_VALIDATION_REPORT.md +47 -0
  7. package/docs/V4_2_BRAIN_CORE_ARCHITECTURE.md +97 -0
  8. package/docs/V4_2_STORAGE_MIGRATION_REPORT.md +91 -0
  9. package/docs/V4_2_VALIDATION_REPORT.md +89 -0
  10. package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +31 -26
  11. package/frontend/index.html +24 -0
  12. package/frontend/openapi.json +14436 -0
  13. package/frontend/src/App.tsx +184 -0
  14. package/frontend/src/api/client.ts +320 -0
  15. package/frontend/src/api/openapi.ts +16921 -0
  16. package/frontend/src/components/primitives.tsx +204 -0
  17. package/frontend/src/components/ui/badge.tsx +27 -0
  18. package/frontend/src/components/ui/button.tsx +37 -0
  19. package/frontend/src/components/ui/card.tsx +22 -0
  20. package/frontend/src/components/ui/input.tsx +16 -0
  21. package/frontend/src/components/ui/textarea.tsx +16 -0
  22. package/frontend/src/lib/utils.ts +33 -0
  23. package/frontend/src/main.tsx +23 -0
  24. package/frontend/src/pages/Act.tsx +245 -0
  25. package/frontend/src/pages/Ask.tsx +200 -0
  26. package/frontend/src/pages/Brain.tsx +267 -0
  27. package/frontend/src/pages/Capture.tsx +158 -0
  28. package/frontend/src/pages/Library.tsx +187 -0
  29. package/frontend/src/pages/System.tsx +378 -0
  30. package/frontend/src/routes.ts +85 -0
  31. package/frontend/src/store/appStore.ts +54 -0
  32. package/frontend/src/styles.css +107 -0
  33. package/kg_schema.py +1 -1
  34. package/knowledge_graph.py +4 -4
  35. package/lattice_brain/__init__.py +70 -0
  36. package/lattice_brain/_kg_common.py +1 -0
  37. package/lattice_brain/archive.py +133 -0
  38. package/lattice_brain/context.py +3 -0
  39. package/lattice_brain/conversations.py +3 -0
  40. package/lattice_brain/core.py +82 -0
  41. package/lattice_brain/discovery.py +1 -0
  42. package/lattice_brain/documents.py +1 -0
  43. package/lattice_brain/embeddings.py +82 -0
  44. package/lattice_brain/identity.py +13 -0
  45. package/lattice_brain/ingest.py +1 -0
  46. package/lattice_brain/memory.py +3 -0
  47. package/lattice_brain/network.py +1 -0
  48. package/lattice_brain/projection.py +1 -0
  49. package/lattice_brain/provenance.py +1 -0
  50. package/lattice_brain/retrieval.py +1 -0
  51. package/lattice_brain/schema.py +1 -0
  52. package/lattice_brain/storage/__init__.py +22 -0
  53. package/lattice_brain/storage/base.py +72 -0
  54. package/lattice_brain/storage/docker.py +105 -0
  55. package/lattice_brain/storage/factory.py +31 -0
  56. package/lattice_brain/storage/migration.py +190 -0
  57. package/lattice_brain/storage/postgres.py +123 -0
  58. package/lattice_brain/storage/sqlite.py +128 -0
  59. package/lattice_brain/store.py +3 -0
  60. package/lattice_brain/write_master.py +1 -0
  61. package/latticeai/__init__.py +1 -1
  62. package/latticeai/api/portability.py +69 -0
  63. package/latticeai/api/setup.py +5 -4
  64. package/latticeai/api/static_routes.py +4 -4
  65. package/latticeai/app_factory.py +17 -10
  66. package/latticeai/brain/__init__.py +6 -6
  67. package/latticeai/brain/_kg_common.py +1 -1
  68. package/latticeai/brain/network.py +1 -1
  69. package/latticeai/brain/retrieval.py +15 -0
  70. package/latticeai/brain/store.py +22 -6
  71. package/latticeai/core/config.py +8 -0
  72. package/latticeai/core/marketplace.py +1 -1
  73. package/latticeai/core/multi_agent.py +1 -1
  74. package/latticeai/core/workspace_os.py +1 -1
  75. package/latticeai/services/kg_portability.py +82 -1
  76. package/package.json +55 -15
  77. package/scripts/build_frontend_assets.mjs +38 -0
  78. package/scripts/bump_version.py +4 -1
  79. package/scripts/export_openapi.py +31 -0
  80. package/scripts/lint_frontend.mjs +91 -0
  81. package/scripts/migrate_brain_storage.py +53 -0
  82. package/scripts/run_python.mjs +47 -0
  83. package/scripts/wheel_smoke.py +3 -0
  84. package/src-tauri/Cargo.lock +4833 -0
  85. package/src-tauri/Cargo.toml +19 -0
  86. package/src-tauri/build.rs +3 -0
  87. package/src-tauri/capabilities/default.json +7 -0
  88. package/src-tauri/src/main.rs +78 -0
  89. package/src-tauri/tauri.conf.json +39 -0
  90. package/static/app/asset-manifest.json +32 -0
  91. package/static/app/assets/core-CwxXejkd.js +2 -0
  92. package/static/app/assets/core-CwxXejkd.js.map +1 -0
  93. package/static/app/assets/index-CDjiH_se.css +2 -0
  94. package/static/app/assets/index-C_HAkbAg.js +333 -0
  95. package/static/app/assets/index-C_HAkbAg.js.map +1 -0
  96. package/static/app/index.html +25 -0
  97. package/static/manifest.json +2 -2
  98. package/static/sw.js +4 -4
  99. package/scripts/build_v3_assets.mjs +0 -170
  100. package/scripts/lint_v3.mjs +0 -120
  101. package/static/v3/asset-manifest.json +0 -63
  102. package/static/v3/css/lattice.base.49deefb5.css +0 -128
  103. package/static/v3/css/lattice.base.css +0 -128
  104. package/static/v3/css/lattice.components.cde18231.css +0 -472
  105. package/static/v3/css/lattice.components.css +0 -472
  106. package/static/v3/css/lattice.shell.29d36d85.css +0 -452
  107. package/static/v3/css/lattice.shell.css +0 -452
  108. package/static/v3/css/lattice.tokens.304cbc40.css +0 -135
  109. package/static/v3/css/lattice.tokens.css +0 -135
  110. package/static/v3/css/lattice.views.0a18b6c5.css +0 -360
  111. package/static/v3/css/lattice.views.css +0 -360
  112. package/static/v3/index.html +0 -68
  113. package/static/v3/js/app.c5c80c46.js +0 -26
  114. package/static/v3/js/app.js +0 -26
  115. package/static/v3/js/core/api.ba0fbf14.js +0 -625
  116. package/static/v3/js/core/api.js +0 -625
  117. package/static/v3/js/core/components.f25b3b93.js +0 -230
  118. package/static/v3/js/core/components.js +0 -230
  119. package/static/v3/js/core/dom.a2773eb0.js +0 -148
  120. package/static/v3/js/core/dom.js +0 -148
  121. package/static/v3/js/core/i18n.880e1fec.js +0 -575
  122. package/static/v3/js/core/i18n.js +0 -575
  123. package/static/v3/js/core/router.584570f2.js +0 -37
  124. package/static/v3/js/core/router.js +0 -37
  125. package/static/v3/js/core/routes.37522821.js +0 -101
  126. package/static/v3/js/core/routes.js +0 -101
  127. package/static/v3/js/core/shell.e3f6bbfa.js +0 -420
  128. package/static/v3/js/core/shell.js +0 -420
  129. package/static/v3/js/core/store.7b2aa044.js +0 -123
  130. package/static/v3/js/core/store.js +0 -123
  131. package/static/v3/js/views/account.eff40715.js +0 -143
  132. package/static/v3/js/views/account.js +0 -143
  133. package/static/v3/js/views/activity.0d271ef9.js +0 -67
  134. package/static/v3/js/views/activity.js +0 -67
  135. package/static/v3/js/views/admin-audit.660a1fb1.js +0 -185
  136. package/static/v3/js/views/admin-audit.js +0 -185
  137. package/static/v3/js/views/admin-permissions.a7ae5f09.js +0 -177
  138. package/static/v3/js/views/admin-permissions.js +0 -177
  139. package/static/v3/js/views/admin-policies.3658fd86.js +0 -102
  140. package/static/v3/js/views/admin-policies.js +0 -102
  141. package/static/v3/js/views/admin-private-vpc.7d342d36.js +0 -135
  142. package/static/v3/js/views/admin-private-vpc.js +0 -135
  143. package/static/v3/js/views/admin-security.07c66b72.js +0 -180
  144. package/static/v3/js/views/admin-security.js +0 -180
  145. package/static/v3/js/views/admin-users.f7ac7b43.js +0 -166
  146. package/static/v3/js/views/admin-users.js +0 -166
  147. package/static/v3/js/views/agents.17c5288d.js +0 -564
  148. package/static/v3/js/views/agents.js +0 -564
  149. package/static/v3/js/views/chat.e250e2cc.js +0 -624
  150. package/static/v3/js/views/chat.js +0 -624
  151. package/static/v3/js/views/files.adad14c1.js +0 -365
  152. package/static/v3/js/views/files.js +0 -365
  153. package/static/v3/js/views/graph-canvas.17c15d65.js +0 -509
  154. package/static/v3/js/views/graph-canvas.js +0 -509
  155. package/static/v3/js/views/home.24f8b8ae.js +0 -200
  156. package/static/v3/js/views/home.js +0 -200
  157. package/static/v3/js/views/hooks.37895880.js +0 -220
  158. package/static/v3/js/views/hooks.js +0 -220
  159. package/static/v3/js/views/hybrid-search.2fb63ed9.js +0 -194
  160. package/static/v3/js/views/hybrid-search.js +0 -194
  161. package/static/v3/js/views/knowledge-graph.4d09c537.js +0 -529
  162. package/static/v3/js/views/knowledge-graph.js +0 -529
  163. package/static/v3/js/views/marketplace.ab0583d4.js +0 -141
  164. package/static/v3/js/views/marketplace.js +0 -141
  165. package/static/v3/js/views/mcp.99b5c6a7.js +0 -114
  166. package/static/v3/js/views/mcp.js +0 -114
  167. package/static/v3/js/views/memory.4ebdf474.js +0 -147
  168. package/static/v3/js/views/memory.js +0 -147
  169. package/static/v3/js/views/models.a1ffa147.js +0 -256
  170. package/static/v3/js/views/models.js +0 -256
  171. package/static/v3/js/views/my-computer.d9d9ae1c.js +0 -463
  172. package/static/v3/js/views/my-computer.js +0 -463
  173. package/static/v3/js/views/network.52a4f181.js +0 -97
  174. package/static/v3/js/views/network.js +0 -97
  175. package/static/v3/js/views/pipeline.c522f1ce.js +0 -157
  176. package/static/v3/js/views/pipeline.js +0 -157
  177. package/static/v3/js/views/planning.4876fd77.js +0 -174
  178. package/static/v3/js/views/planning.js +0 -174
  179. package/static/v3/js/views/runs.b63b2afa.js +0 -144
  180. package/static/v3/js/views/runs.js +0 -144
  181. package/static/v3/js/views/settings.b7140634.js +0 -317
  182. package/static/v3/js/views/settings.js +0 -317
  183. package/static/v3/js/views/skills.c6c2f965.js +0 -109
  184. package/static/v3/js/views/skills.js +0 -109
  185. package/static/v3/js/views/snapshots.6f5db095.js +0 -135
  186. package/static/v3/js/views/snapshots.js +0 -135
  187. package/static/v3/js/views/tools.e4f11276.js +0 -108
  188. package/static/v3/js/views/tools.js +0 -108
  189. package/static/v3/js/views/workflows.7752225a.js +0 -213
  190. package/static/v3/js/views/workflows.js +0 -213
  191. package/static/v3/js/views/workspace-admin.c466029b.js +0 -156
  192. package/static/v3/js/views/workspace-admin.js +0 -156
@@ -1,157 +0,0 @@
1
- /* ============================================================================
2
- * View: Pipeline — ingest / embed / graph-build flows.
3
- * Renders each workspace workflow as a horizontal stage flow (integration-ready
4
- * against /workspace/workflows and the index APIs). Pipelines execute on the
5
- * local runtime; this surface visualizes their stages and run state.
6
- * ========================================================================== */
7
-
8
- import { timeAgo } from "../core/dom.a2773eb0.js";
9
-
10
- export async function render(ctx) {
11
- const { h, icon, api, c, toast } = ctx;
12
-
13
- const unavailable = (label) => () => toast(`${label} is not available from this read-only pipeline view.`, "warn");
14
-
15
- const statHost = h("div.lt3-statrow", c.loading({ lines: 1 }));
16
- const srcSlot = h("span", c.sourceBadge("pending"));
17
- const flowsHost = h("div.lt3-stack-6", c.loading({ lines: 3, block: true }));
18
-
19
- const rebuildBtn = h("button.lt3-btn.lt3-btn--primary", { on: { click: () => rebuild() } }, icon("refresh"), "Rebuild index");
20
-
21
- const root = h("div.lt3-stack-6",
22
- c.viewHeader({
23
- eyebrow: "Data",
24
- title: "Pipeline",
25
- sub: "Ingest, embed, and graph-build flows that turn your sources into the retrieval lattice — chunk, embed, extract entities, and link the graph.",
26
- actions: [rebuildBtn],
27
- }),
28
- c.banner(
29
- "Pipelines execute on this machine's local runtime. Use Rebuild index to re-embed every chunk and relink the knowledge graph from your current sources.",
30
- "info",
31
- "server-bolt",
32
- ),
33
- statHost,
34
- h("section",
35
- c.sectionHead("Flows", srcSlot),
36
- flowsHost,
37
- ),
38
- );
39
-
40
- load();
41
- return root;
42
-
43
- async function load() {
44
- const res = await api.get("/workspace/workflows", { workflows: [] });
45
- const pipelines = normalize(res.data);
46
- srcSlot.replaceChildren(c.sourceBadge(res.source));
47
- renderStats(pipelines);
48
- renderFlows(pipelines);
49
- }
50
-
51
- // Real pipeline run: rebuild the vector index (re-embed chunks, relink graph).
52
- async function rebuild() {
53
- rebuildBtn.disabled = true;
54
- rebuildBtn.replaceChildren(icon("loader"), "Rebuilding…");
55
- const res = await api.rebuildIndex();
56
- rebuildBtn.disabled = false;
57
- rebuildBtn.replaceChildren(icon("refresh"), "Rebuild index");
58
- if (res && res.ok && res.data && res.data.status === "completed") {
59
- const d = res.data;
60
- toast(`Index rebuilt — ${d.items_indexed} indexed, ${d.items_skipped} unchanged (${d.embedding_model}).`, "ok");
61
- load();
62
- } else {
63
- const detail = (res && res.data && (res.data.detail || res.data.error)) || "the knowledge graph is unavailable";
64
- toast(`Could not rebuild the index — ${detail}.`, "warn");
65
- }
66
- }
67
-
68
- function renderStats(pipelines) {
69
- const active = pipelines.filter((p) => isActive(p.state)).length;
70
- const stages = pipelines.reduce((sum, p) => sum + p.stages.length, 0);
71
- const throughput = pipelines.find((p) => p.throughput)?.throughput || "—";
72
- const lastRun = pipelines
73
- .map((p) => p.last_run)
74
- .filter(Boolean)
75
- .sort((a, b) => new Date(b) - new Date(a))[0];
76
- statHost.replaceChildren(
77
- c.stat({ label: "Active pipelines", value: c.fmtNum(active), icon: "player-play" }),
78
- c.stat({ label: "Total stages", value: c.fmtNum(stages), icon: "stack-2" }),
79
- c.stat({ label: "Throughput", value: throughput, icon: "gauge" }),
80
- c.stat({ label: "Last run", value: lastRun ? timeAgo(lastRun) : "—", icon: "history" }),
81
- );
82
- }
83
-
84
- function renderFlows(pipelines) {
85
- if (!pipelines.length) {
86
- flowsHost.replaceChildren(
87
- c.emptyState({
88
- icon: "git-branch-deleted",
89
- title: "No pipelines yet",
90
- body: "Connect a source and create a pipeline to ingest, embed, and build the graph.",
91
- action: h("button.lt3-btn.lt3-btn--ghost.lt3-btn--sm", { on: { click: () => rebuild() } }, icon("refresh"), "Rebuild index"),
92
- }),
93
- );
94
- return;
95
- }
96
- flowsHost.replaceChildren(...pipelines.map((p) => pipelinePanel(p)));
97
- }
98
-
99
- function pipelinePanel(p) {
100
- return c.panel({
101
- head: h("div.lt3-row", { style: { "justify-content": "space-between", "flex-wrap": "wrap", gap: "var(--lt3-space-3)" } },
102
- h("div.lt3-row-2",
103
- h("div.lt3-eyebrow", icon("git-branch"), "Pipeline"),
104
- ),
105
- c.statePill(p.state),
106
- ),
107
- children: h("div.lt3-stack-4",
108
- h("h3.lt3-panel__title", { style: { "margin-top": "calc(-1 * var(--lt3-space-2))" } }, p.name),
109
- flowDiagram(p.stages),
110
- pipelineFooter(p),
111
- ),
112
- });
113
- }
114
-
115
- function flowDiagram(stages) {
116
- const cells = [];
117
- stages.forEach((stage, i) => {
118
- if (i > 0) cells.push(h("div.lt3-flow__arrow", { "aria-hidden": "true" }, icon("chevron-right")));
119
- cells.push(
120
- h("div.lt3-stage",
121
- h("div.lt3-stage__num", String(i + 1).padStart(2, "0")),
122
- h("div.lt3-stage__name", stage),
123
- ),
124
- );
125
- });
126
- return h("div.lt3-flow", { role: "list", "aria-label": "Pipeline stages" }, cells);
127
- }
128
-
129
- function pipelineFooter(p) {
130
- return h("div.lt3-row", { style: { "justify-content": "space-between", "flex-wrap": "wrap", gap: "var(--lt3-space-3)" } },
131
- h("div.lt3-cluster",
132
- h("span.lt3-faint.lt3-row-2", { style: { "font-size": "var(--lt3-text-xs)" } }, icon("history"), p.last_run ? timeAgo(p.last_run) : "—"),
133
- h("span.lt3-faint.lt3-row-2", { style: { "font-size": "var(--lt3-text-xs)" } }, icon("gauge"), p.throughput || "—"),
134
- ),
135
- h("button.lt3-btn.lt3-btn--ghost.lt3-btn--sm", { on: { click: unavailable(`Running "${p.name}"`) } }, icon("player-play"), "Run"),
136
- );
137
- }
138
- }
139
-
140
- /* ── helpers ─────────────────────────────────────────────────────────────── */
141
- function normalize(data) {
142
- const list = Array.isArray(data) ? data : (data && data.workflows) || [];
143
- return list.map((p, i) => ({
144
- id: p.id || `pl-${i}`,
145
- name: p.name || p.label || "Untitled pipeline",
146
- state: p.state || "idle",
147
- stages: Array.isArray(p.stages) ? p.stages.map((s) => String(s))
148
- : Array.isArray(p.steps) ? p.steps.map((s) => (s && (s.action || s.name)) || String(s))
149
- : [],
150
- last_run: p.last_run || p.created_at || null,
151
- throughput: p.throughput || "",
152
- }));
153
- }
154
-
155
- function isActive(state) {
156
- return ["active", "running", "indexing", "building"].includes(String(state).toLowerCase());
157
- }
@@ -1,157 +0,0 @@
1
- /* ============================================================================
2
- * View: Pipeline — ingest / embed / graph-build flows.
3
- * Renders each workspace workflow as a horizontal stage flow (integration-ready
4
- * against /workspace/workflows and the index APIs). Pipelines execute on the
5
- * local runtime; this surface visualizes their stages and run state.
6
- * ========================================================================== */
7
-
8
- import { timeAgo } from "../core/dom.js";
9
-
10
- export async function render(ctx) {
11
- const { h, icon, api, c, toast } = ctx;
12
-
13
- const unavailable = (label) => () => toast(`${label} is not available from this read-only pipeline view.`, "warn");
14
-
15
- const statHost = h("div.lt3-statrow", c.loading({ lines: 1 }));
16
- const srcSlot = h("span", c.sourceBadge("pending"));
17
- const flowsHost = h("div.lt3-stack-6", c.loading({ lines: 3, block: true }));
18
-
19
- const rebuildBtn = h("button.lt3-btn.lt3-btn--primary", { on: { click: () => rebuild() } }, icon("refresh"), "Rebuild index");
20
-
21
- const root = h("div.lt3-stack-6",
22
- c.viewHeader({
23
- eyebrow: "Data",
24
- title: "Pipeline",
25
- sub: "Ingest, embed, and graph-build flows that turn your sources into the retrieval lattice — chunk, embed, extract entities, and link the graph.",
26
- actions: [rebuildBtn],
27
- }),
28
- c.banner(
29
- "Pipelines execute on this machine's local runtime. Use Rebuild index to re-embed every chunk and relink the knowledge graph from your current sources.",
30
- "info",
31
- "server-bolt",
32
- ),
33
- statHost,
34
- h("section",
35
- c.sectionHead("Flows", srcSlot),
36
- flowsHost,
37
- ),
38
- );
39
-
40
- load();
41
- return root;
42
-
43
- async function load() {
44
- const res = await api.get("/workspace/workflows", { workflows: [] });
45
- const pipelines = normalize(res.data);
46
- srcSlot.replaceChildren(c.sourceBadge(res.source));
47
- renderStats(pipelines);
48
- renderFlows(pipelines);
49
- }
50
-
51
- // Real pipeline run: rebuild the vector index (re-embed chunks, relink graph).
52
- async function rebuild() {
53
- rebuildBtn.disabled = true;
54
- rebuildBtn.replaceChildren(icon("loader"), "Rebuilding…");
55
- const res = await api.rebuildIndex();
56
- rebuildBtn.disabled = false;
57
- rebuildBtn.replaceChildren(icon("refresh"), "Rebuild index");
58
- if (res && res.ok && res.data && res.data.status === "completed") {
59
- const d = res.data;
60
- toast(`Index rebuilt — ${d.items_indexed} indexed, ${d.items_skipped} unchanged (${d.embedding_model}).`, "ok");
61
- load();
62
- } else {
63
- const detail = (res && res.data && (res.data.detail || res.data.error)) || "the knowledge graph is unavailable";
64
- toast(`Could not rebuild the index — ${detail}.`, "warn");
65
- }
66
- }
67
-
68
- function renderStats(pipelines) {
69
- const active = pipelines.filter((p) => isActive(p.state)).length;
70
- const stages = pipelines.reduce((sum, p) => sum + p.stages.length, 0);
71
- const throughput = pipelines.find((p) => p.throughput)?.throughput || "—";
72
- const lastRun = pipelines
73
- .map((p) => p.last_run)
74
- .filter(Boolean)
75
- .sort((a, b) => new Date(b) - new Date(a))[0];
76
- statHost.replaceChildren(
77
- c.stat({ label: "Active pipelines", value: c.fmtNum(active), icon: "player-play" }),
78
- c.stat({ label: "Total stages", value: c.fmtNum(stages), icon: "stack-2" }),
79
- c.stat({ label: "Throughput", value: throughput, icon: "gauge" }),
80
- c.stat({ label: "Last run", value: lastRun ? timeAgo(lastRun) : "—", icon: "history" }),
81
- );
82
- }
83
-
84
- function renderFlows(pipelines) {
85
- if (!pipelines.length) {
86
- flowsHost.replaceChildren(
87
- c.emptyState({
88
- icon: "git-branch-deleted",
89
- title: "No pipelines yet",
90
- body: "Connect a source and create a pipeline to ingest, embed, and build the graph.",
91
- action: h("button.lt3-btn.lt3-btn--ghost.lt3-btn--sm", { on: { click: () => rebuild() } }, icon("refresh"), "Rebuild index"),
92
- }),
93
- );
94
- return;
95
- }
96
- flowsHost.replaceChildren(...pipelines.map((p) => pipelinePanel(p)));
97
- }
98
-
99
- function pipelinePanel(p) {
100
- return c.panel({
101
- head: h("div.lt3-row", { style: { "justify-content": "space-between", "flex-wrap": "wrap", gap: "var(--lt3-space-3)" } },
102
- h("div.lt3-row-2",
103
- h("div.lt3-eyebrow", icon("git-branch"), "Pipeline"),
104
- ),
105
- c.statePill(p.state),
106
- ),
107
- children: h("div.lt3-stack-4",
108
- h("h3.lt3-panel__title", { style: { "margin-top": "calc(-1 * var(--lt3-space-2))" } }, p.name),
109
- flowDiagram(p.stages),
110
- pipelineFooter(p),
111
- ),
112
- });
113
- }
114
-
115
- function flowDiagram(stages) {
116
- const cells = [];
117
- stages.forEach((stage, i) => {
118
- if (i > 0) cells.push(h("div.lt3-flow__arrow", { "aria-hidden": "true" }, icon("chevron-right")));
119
- cells.push(
120
- h("div.lt3-stage",
121
- h("div.lt3-stage__num", String(i + 1).padStart(2, "0")),
122
- h("div.lt3-stage__name", stage),
123
- ),
124
- );
125
- });
126
- return h("div.lt3-flow", { role: "list", "aria-label": "Pipeline stages" }, cells);
127
- }
128
-
129
- function pipelineFooter(p) {
130
- return h("div.lt3-row", { style: { "justify-content": "space-between", "flex-wrap": "wrap", gap: "var(--lt3-space-3)" } },
131
- h("div.lt3-cluster",
132
- h("span.lt3-faint.lt3-row-2", { style: { "font-size": "var(--lt3-text-xs)" } }, icon("history"), p.last_run ? timeAgo(p.last_run) : "—"),
133
- h("span.lt3-faint.lt3-row-2", { style: { "font-size": "var(--lt3-text-xs)" } }, icon("gauge"), p.throughput || "—"),
134
- ),
135
- h("button.lt3-btn.lt3-btn--ghost.lt3-btn--sm", { on: { click: unavailable(`Running "${p.name}"`) } }, icon("player-play"), "Run"),
136
- );
137
- }
138
- }
139
-
140
- /* ── helpers ─────────────────────────────────────────────────────────────── */
141
- function normalize(data) {
142
- const list = Array.isArray(data) ? data : (data && data.workflows) || [];
143
- return list.map((p, i) => ({
144
- id: p.id || `pl-${i}`,
145
- name: p.name || p.label || "Untitled pipeline",
146
- state: p.state || "idle",
147
- stages: Array.isArray(p.stages) ? p.stages.map((s) => String(s))
148
- : Array.isArray(p.steps) ? p.steps.map((s) => (s && (s.action || s.name)) || String(s))
149
- : [],
150
- last_run: p.last_run || p.created_at || null,
151
- throughput: p.throughput || "",
152
- }));
153
- }
154
-
155
- function isActive(state) {
156
- return ["active", "running", "indexing", "building"].includes(String(state).toLowerCase());
157
- }
@@ -1,174 +0,0 @@
1
- /* ============================================================================
2
- * View: Planning — autonomous goal-based planning (goal → plan → execute →
3
- * review → replan). Drives the real AgentRuntime (/agents/api/run) and renders
4
- * the generated plan, execution, review, and replanning (retry) decisions, with
5
- * inspect / replay over recent runs. Synchronous runs are terminal — surfaced
6
- * honestly rather than faking a pause/stop.
7
- * ========================================================================== */
8
-
9
- const FLOW = ["Goal", "Plan", "Execute", "Review", "Replan"];
10
-
11
- export async function render(ctx) {
12
- const { h, c } = ctx;
13
-
14
- const goalInput = h("textarea.lt3-textarea", { rows: "2", placeholder: "Describe a goal — the planner decomposes it, the executor runs it, the reviewer approves or requests a replan.", "aria-label": "Goal" });
15
- const runBtn = h("button.lt3-btn.lt3-btn--primary", { on: { click: runGoal } }, c.icon("player-play"), "Generate plan & run");
16
- const resultHost = h("div");
17
- const runsHost = h("div", c.loading({ lines: 3 }));
18
- const runsSrc = h("span", c.sourceBadge("pending"));
19
-
20
- const root = h("div.lt3-stack-6",
21
- c.viewHeader({
22
- eyebrow: "Compute",
23
- title: "Autonomous Planning",
24
- sub: "Set a goal; the multi-agent runtime plans, executes, reviews, and replans on failure. Every plan, decision, and retry is inspectable and replayable.",
25
- }),
26
- flowLegend(ctx),
27
- c.panel({
28
- title: "New plan", sub: "Runs locally through planner → executor → reviewer with bounded retries.",
29
- children: h("div.lt3-stack-3", goalInput, h("div.lt3-row-2", runBtn,
30
- h("span.lt3-faint", { style: { "font-size": "var(--lt3-text-xs)" } }, "Safeguards: bounded retries, redacted context, replayable timeline."))),
31
- }),
32
- resultHost,
33
- c.panel({
34
- head: h("div.lt3-row", { style: { "justify-content": "space-between", width: "100%" } },
35
- h("div", h("div.lt3-eyebrow", "History"), h("h3.lt3-panel__title", "Recent plans & runs")), runsSrc),
36
- children: runsHost,
37
- }),
38
- );
39
-
40
- loadRuns();
41
- return root;
42
-
43
- function flowLegend(ctx2) {
44
- return h("div.lt3-cluster", { style: { gap: "var(--lt3-space-2)" } }, FLOW.map((step, i) =>
45
- h("div.lt3-row-2", { style: { gap: "var(--lt3-space-2)" } },
46
- c.pill(step, i === FLOW.length - 1 ? "warn" : "info"),
47
- i < FLOW.length - 1 ? c.icon("arrow-right") : null,
48
- )));
49
- }
50
-
51
- async function runGoal() {
52
- const goal = goalInput.value.trim();
53
- if (!goal) { ctx.toast("Enter a goal first", "info"); return; }
54
- runBtn.disabled = true;
55
- resultHost.replaceChildren(c.panel({ title: "Planning…", children: c.loading({ lines: 4, block: true }) }));
56
- const res = await ctx.api.runAgent(goal, ["planner", "executor", "reviewer"]);
57
- runBtn.disabled = false;
58
- if (!res || !res.ok || !res.data) {
59
- resultHost.replaceChildren(c.banner("Planning is unavailable — start the local server and load a model.", "warn"));
60
- return;
61
- }
62
- if (res.data.accepted && res.data.run) {
63
- resultHost.replaceChildren(renderResult(ctx, res.data.run));
64
- pollRun(res.data.run.id || res.data.run.run_id);
65
- } else {
66
- resultHost.replaceChildren(renderResult(ctx, res.data.result || res.data));
67
- }
68
- loadRuns();
69
- }
70
-
71
- async function pollRun(runId) {
72
- if (!runId) return;
73
- for (let i = 0; i < 80; i += 1) {
74
- await sleep(i < 10 ? 400 : 1200);
75
- const res = await ctx.api.agentRunDetail(runId);
76
- const run = res && res.data && res.data.run;
77
- if (!res || !res.ok || !run) return;
78
- resultHost.replaceChildren(renderResult(ctx, run));
79
- loadRuns();
80
- if (!["queued", "running", "in_progress", "cancelling"].includes(String(run.status || "").toLowerCase())) return;
81
- }
82
- }
83
-
84
- async function loadRuns() {
85
- const res = await ctx.api.agentRuntime();
86
- runsSrc.replaceChildren(c.sourceBadge(res.source));
87
- const runs = (res.data && res.data.runs) || [];
88
- if (!runs.length) {
89
- runsHost.replaceChildren(c.emptyState({ icon: "history-off", title: "No plans yet", body: "Generated plans and their runs will appear here." }));
90
- return;
91
- }
92
- runsHost.replaceChildren(c.table(
93
- [
94
- { key: "status", label: "Status", width: "1%", render: (r) => c.statePill(mapStatus(r.status)) },
95
- { key: "goal", label: "Goal / output", render: (r) => h("span.lt3-muted", trunc(r.output || r.input || r.agent_id, 90)) },
96
- { key: "retries", label: "Retries", width: "1%", render: (r) => h("span.lt3-faint", String(r.retries ?? (r.retry_history ? r.retry_history.length : 0))) },
97
- { key: "act", label: "", width: "1%", render: (r) => h("button.lt3-btn.lt3-btn--ghost.lt3-btn--sm", { on: { click: () => replay(r) } }, c.icon("player-track-next"), "Replay") },
98
- ],
99
- runs.slice(0, 20),
100
- ));
101
- }
102
-
103
- async function replay(run) {
104
- const id = run.id || run.run_id;
105
- if (!id) { ctx.toast("Run id unavailable", "info"); return; }
106
- const res = await ctx.api.agentRunReplay(id);
107
- if (res && res.ok && res.data) {
108
- resultHost.replaceChildren(c.panel({ title: `Replay · ${id}`, children: timeline(ctx, (res.data.replay && res.data.replay.timeline) || res.data.replay || []) }));
109
- resultHost.scrollIntoView({ behavior: "smooth", block: "start" });
110
- } else {
111
- ctx.toast("Replay unavailable", "err");
112
- }
113
- }
114
- }
115
-
116
- function renderResult(ctx, result) {
117
- const { h, c } = ctx;
118
- const plan = result.plan || [];
119
- const review = result.review || result.plan_review || {};
120
- const retries = result.retry_history || [];
121
- const status = mapStatus(result.status);
122
- const ok = status === "ready" || (review.outcome || "").toLowerCase() === "approve" || (review.verdict || "").toLowerCase() === "pass";
123
- return c.panel({
124
- head: h("div.lt3-row", { style: { "justify-content": "space-between", width: "100%" } },
125
- h("div", h("div.lt3-eyebrow", "Result"), h("h3.lt3-panel__title", "Plan & execution")),
126
- c.statePill(ok ? "ready" : status || "warn")),
127
- children: h("div.lt3-stack-3",
128
- h("div",
129
- h("div.lt3-eyebrow", c.icon("list-check"), "Plan"),
130
- plan.length
131
- ? h("ol", { style: { margin: "var(--lt3-space-2) 0 0", "padding-left": "1.2em" } }, plan.map((s) =>
132
- h("li", { style: { "margin-bottom": "4px" } }, h("span", s.description || s.name || `Step`), " ", c.statePill(s.status || "planned"))))
133
- : h("p.lt3-faint", { style: { margin: 0 } }, "No plan steps."),
134
- ),
135
- h("div",
136
- h("div.lt3-eyebrow", c.icon("checkup-list"), "Review"),
137
- h("p.lt3-muted", { style: { margin: "var(--lt3-space-2) 0 0" } }, review.reason || "—"),
138
- ),
139
- retries.length
140
- ? h("div",
141
- h("div.lt3-eyebrow", c.icon("refresh-alert"), `Replanning (${retries.length})`),
142
- h("div.lt3-stack-2", retries.map((r) => c.card(h("div",
143
- h("b", `Retry #${r.retry}`), h("p.lt3-muted", { style: { margin: 0, "font-size": "var(--lt3-text-sm)" } }, r.reason || "")), { flat: true }))),
144
- )
145
- : null,
146
- result.timeline ? h("details", h("summary.lt3-faint", { style: { cursor: "pointer" } }, "Timeline"), timeline(ctx, result.timeline)) : null,
147
- ),
148
- });
149
- }
150
-
151
- function timeline(ctx, events) {
152
- const { h, c } = ctx;
153
- const list = Array.isArray(events) ? events : [];
154
- if (!list.length) return h("p.lt3-faint", { style: { margin: 0 } }, "No timeline events.");
155
- return h("div.lt3-stack-2", { style: { "margin-top": "var(--lt3-space-2)" } }, list.slice(0, 60).map((e) =>
156
- h("div.lt3-row-2", { style: { "font-size": "var(--lt3-text-xs)" } },
157
- c.pill(e.event || "event", "", { dot: true }),
158
- h("span.lt3-faint", e.role || e.from || ""),
159
- e.to ? c.icon("arrow-right") : null,
160
- e.to ? h("span.lt3-faint", e.to) : null,
161
- )));
162
- }
163
-
164
- function mapStatus(s) {
165
- const v = String(s || "").toLowerCase();
166
- if (v === "ok" || v === "retried_ok") return "ready";
167
- if (v === "failed" || v === "rejected") return "failed";
168
- if (v === "running" || v === "in_progress" || v === "queued" || v === "cancelling") return "active";
169
- if (v === "cancelled" || v === "interrupted") return "warn";
170
- return v || "idle";
171
- }
172
-
173
- function trunc(s, n) { s = String(s || ""); return s.length > n ? s.slice(0, n) + "…" : s; }
174
- function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }
@@ -1,174 +0,0 @@
1
- /* ============================================================================
2
- * View: Planning — autonomous goal-based planning (goal → plan → execute →
3
- * review → replan). Drives the real AgentRuntime (/agents/api/run) and renders
4
- * the generated plan, execution, review, and replanning (retry) decisions, with
5
- * inspect / replay over recent runs. Synchronous runs are terminal — surfaced
6
- * honestly rather than faking a pause/stop.
7
- * ========================================================================== */
8
-
9
- const FLOW = ["Goal", "Plan", "Execute", "Review", "Replan"];
10
-
11
- export async function render(ctx) {
12
- const { h, c } = ctx;
13
-
14
- const goalInput = h("textarea.lt3-textarea", { rows: "2", placeholder: "Describe a goal — the planner decomposes it, the executor runs it, the reviewer approves or requests a replan.", "aria-label": "Goal" });
15
- const runBtn = h("button.lt3-btn.lt3-btn--primary", { on: { click: runGoal } }, c.icon("player-play"), "Generate plan & run");
16
- const resultHost = h("div");
17
- const runsHost = h("div", c.loading({ lines: 3 }));
18
- const runsSrc = h("span", c.sourceBadge("pending"));
19
-
20
- const root = h("div.lt3-stack-6",
21
- c.viewHeader({
22
- eyebrow: "Compute",
23
- title: "Autonomous Planning",
24
- sub: "Set a goal; the multi-agent runtime plans, executes, reviews, and replans on failure. Every plan, decision, and retry is inspectable and replayable.",
25
- }),
26
- flowLegend(ctx),
27
- c.panel({
28
- title: "New plan", sub: "Runs locally through planner → executor → reviewer with bounded retries.",
29
- children: h("div.lt3-stack-3", goalInput, h("div.lt3-row-2", runBtn,
30
- h("span.lt3-faint", { style: { "font-size": "var(--lt3-text-xs)" } }, "Safeguards: bounded retries, redacted context, replayable timeline."))),
31
- }),
32
- resultHost,
33
- c.panel({
34
- head: h("div.lt3-row", { style: { "justify-content": "space-between", width: "100%" } },
35
- h("div", h("div.lt3-eyebrow", "History"), h("h3.lt3-panel__title", "Recent plans & runs")), runsSrc),
36
- children: runsHost,
37
- }),
38
- );
39
-
40
- loadRuns();
41
- return root;
42
-
43
- function flowLegend(ctx2) {
44
- return h("div.lt3-cluster", { style: { gap: "var(--lt3-space-2)" } }, FLOW.map((step, i) =>
45
- h("div.lt3-row-2", { style: { gap: "var(--lt3-space-2)" } },
46
- c.pill(step, i === FLOW.length - 1 ? "warn" : "info"),
47
- i < FLOW.length - 1 ? c.icon("arrow-right") : null,
48
- )));
49
- }
50
-
51
- async function runGoal() {
52
- const goal = goalInput.value.trim();
53
- if (!goal) { ctx.toast("Enter a goal first", "info"); return; }
54
- runBtn.disabled = true;
55
- resultHost.replaceChildren(c.panel({ title: "Planning…", children: c.loading({ lines: 4, block: true }) }));
56
- const res = await ctx.api.runAgent(goal, ["planner", "executor", "reviewer"]);
57
- runBtn.disabled = false;
58
- if (!res || !res.ok || !res.data) {
59
- resultHost.replaceChildren(c.banner("Planning is unavailable — start the local server and load a model.", "warn"));
60
- return;
61
- }
62
- if (res.data.accepted && res.data.run) {
63
- resultHost.replaceChildren(renderResult(ctx, res.data.run));
64
- pollRun(res.data.run.id || res.data.run.run_id);
65
- } else {
66
- resultHost.replaceChildren(renderResult(ctx, res.data.result || res.data));
67
- }
68
- loadRuns();
69
- }
70
-
71
- async function pollRun(runId) {
72
- if (!runId) return;
73
- for (let i = 0; i < 80; i += 1) {
74
- await sleep(i < 10 ? 400 : 1200);
75
- const res = await ctx.api.agentRunDetail(runId);
76
- const run = res && res.data && res.data.run;
77
- if (!res || !res.ok || !run) return;
78
- resultHost.replaceChildren(renderResult(ctx, run));
79
- loadRuns();
80
- if (!["queued", "running", "in_progress", "cancelling"].includes(String(run.status || "").toLowerCase())) return;
81
- }
82
- }
83
-
84
- async function loadRuns() {
85
- const res = await ctx.api.agentRuntime();
86
- runsSrc.replaceChildren(c.sourceBadge(res.source));
87
- const runs = (res.data && res.data.runs) || [];
88
- if (!runs.length) {
89
- runsHost.replaceChildren(c.emptyState({ icon: "history-off", title: "No plans yet", body: "Generated plans and their runs will appear here." }));
90
- return;
91
- }
92
- runsHost.replaceChildren(c.table(
93
- [
94
- { key: "status", label: "Status", width: "1%", render: (r) => c.statePill(mapStatus(r.status)) },
95
- { key: "goal", label: "Goal / output", render: (r) => h("span.lt3-muted", trunc(r.output || r.input || r.agent_id, 90)) },
96
- { key: "retries", label: "Retries", width: "1%", render: (r) => h("span.lt3-faint", String(r.retries ?? (r.retry_history ? r.retry_history.length : 0))) },
97
- { key: "act", label: "", width: "1%", render: (r) => h("button.lt3-btn.lt3-btn--ghost.lt3-btn--sm", { on: { click: () => replay(r) } }, c.icon("player-track-next"), "Replay") },
98
- ],
99
- runs.slice(0, 20),
100
- ));
101
- }
102
-
103
- async function replay(run) {
104
- const id = run.id || run.run_id;
105
- if (!id) { ctx.toast("Run id unavailable", "info"); return; }
106
- const res = await ctx.api.agentRunReplay(id);
107
- if (res && res.ok && res.data) {
108
- resultHost.replaceChildren(c.panel({ title: `Replay · ${id}`, children: timeline(ctx, (res.data.replay && res.data.replay.timeline) || res.data.replay || []) }));
109
- resultHost.scrollIntoView({ behavior: "smooth", block: "start" });
110
- } else {
111
- ctx.toast("Replay unavailable", "err");
112
- }
113
- }
114
- }
115
-
116
- function renderResult(ctx, result) {
117
- const { h, c } = ctx;
118
- const plan = result.plan || [];
119
- const review = result.review || result.plan_review || {};
120
- const retries = result.retry_history || [];
121
- const status = mapStatus(result.status);
122
- const ok = status === "ready" || (review.outcome || "").toLowerCase() === "approve" || (review.verdict || "").toLowerCase() === "pass";
123
- return c.panel({
124
- head: h("div.lt3-row", { style: { "justify-content": "space-between", width: "100%" } },
125
- h("div", h("div.lt3-eyebrow", "Result"), h("h3.lt3-panel__title", "Plan & execution")),
126
- c.statePill(ok ? "ready" : status || "warn")),
127
- children: h("div.lt3-stack-3",
128
- h("div",
129
- h("div.lt3-eyebrow", c.icon("list-check"), "Plan"),
130
- plan.length
131
- ? h("ol", { style: { margin: "var(--lt3-space-2) 0 0", "padding-left": "1.2em" } }, plan.map((s) =>
132
- h("li", { style: { "margin-bottom": "4px" } }, h("span", s.description || s.name || `Step`), " ", c.statePill(s.status || "planned"))))
133
- : h("p.lt3-faint", { style: { margin: 0 } }, "No plan steps."),
134
- ),
135
- h("div",
136
- h("div.lt3-eyebrow", c.icon("checkup-list"), "Review"),
137
- h("p.lt3-muted", { style: { margin: "var(--lt3-space-2) 0 0" } }, review.reason || "—"),
138
- ),
139
- retries.length
140
- ? h("div",
141
- h("div.lt3-eyebrow", c.icon("refresh-alert"), `Replanning (${retries.length})`),
142
- h("div.lt3-stack-2", retries.map((r) => c.card(h("div",
143
- h("b", `Retry #${r.retry}`), h("p.lt3-muted", { style: { margin: 0, "font-size": "var(--lt3-text-sm)" } }, r.reason || "")), { flat: true }))),
144
- )
145
- : null,
146
- result.timeline ? h("details", h("summary.lt3-faint", { style: { cursor: "pointer" } }, "Timeline"), timeline(ctx, result.timeline)) : null,
147
- ),
148
- });
149
- }
150
-
151
- function timeline(ctx, events) {
152
- const { h, c } = ctx;
153
- const list = Array.isArray(events) ? events : [];
154
- if (!list.length) return h("p.lt3-faint", { style: { margin: 0 } }, "No timeline events.");
155
- return h("div.lt3-stack-2", { style: { "margin-top": "var(--lt3-space-2)" } }, list.slice(0, 60).map((e) =>
156
- h("div.lt3-row-2", { style: { "font-size": "var(--lt3-text-xs)" } },
157
- c.pill(e.event || "event", "", { dot: true }),
158
- h("span.lt3-faint", e.role || e.from || ""),
159
- e.to ? c.icon("arrow-right") : null,
160
- e.to ? h("span.lt3-faint", e.to) : null,
161
- )));
162
- }
163
-
164
- function mapStatus(s) {
165
- const v = String(s || "").toLowerCase();
166
- if (v === "ok" || v === "retried_ok") return "ready";
167
- if (v === "failed" || v === "rejected") return "failed";
168
- if (v === "running" || v === "in_progress" || v === "queued" || v === "cancelling") return "active";
169
- if (v === "cancelled" || v === "interrupted") return "warn";
170
- return v || "idle";
171
- }
172
-
173
- function trunc(s, n) { s = String(s || ""); return s.length > n ? s.slice(0, n) + "…" : s; }
174
- function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }