instar 0.27.2 → 0.28.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 (59) hide show
  1. package/.claude/skills/build/SKILL.md +268 -0
  2. package/README.md +2 -1
  3. package/dashboard/index.html +501 -491
  4. package/dist/cli.js +81 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +16 -9
  8. package/dist/commands/init.js.map +1 -1
  9. package/dist/commands/listener.d.ts +46 -0
  10. package/dist/commands/listener.d.ts.map +1 -0
  11. package/dist/commands/listener.js +467 -0
  12. package/dist/commands/listener.js.map +1 -0
  13. package/dist/commands/server.d.ts.map +1 -1
  14. package/dist/commands/server.js +101 -3
  15. package/dist/commands/server.js.map +1 -1
  16. package/dist/commands/setup.d.ts.map +1 -1
  17. package/dist/commands/setup.js +84 -21
  18. package/dist/commands/setup.js.map +1 -1
  19. package/dist/core/AgentRegistry.d.ts.map +1 -1
  20. package/dist/core/AgentRegistry.js +30 -2
  21. package/dist/core/AgentRegistry.js.map +1 -1
  22. package/dist/core/PostUpdateMigrator.d.ts +2 -1
  23. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  24. package/dist/core/PostUpdateMigrator.js +29 -28
  25. package/dist/core/PostUpdateMigrator.js.map +1 -1
  26. package/dist/core/types.d.ts +32 -0
  27. package/dist/core/types.d.ts.map +1 -1
  28. package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
  29. package/dist/lifeline/TelegramLifeline.js +10 -2
  30. package/dist/lifeline/TelegramLifeline.js.map +1 -1
  31. package/dist/server/routes.d.ts.map +1 -1
  32. package/dist/server/routes.js +87 -0
  33. package/dist/server/routes.js.map +1 -1
  34. package/dist/threadline/PipeSessionSpawner.d.ts +123 -0
  35. package/dist/threadline/PipeSessionSpawner.d.ts.map +1 -0
  36. package/dist/threadline/PipeSessionSpawner.js +343 -0
  37. package/dist/threadline/PipeSessionSpawner.js.map +1 -0
  38. package/dist/threadline/ThreadResumeMap.d.ts +22 -0
  39. package/dist/threadline/ThreadResumeMap.d.ts.map +1 -1
  40. package/dist/threadline/ThreadResumeMap.js +37 -0
  41. package/dist/threadline/ThreadResumeMap.js.map +1 -1
  42. package/dist/threadline/ThreadlineBootstrap.d.ts.map +1 -1
  43. package/dist/threadline/ThreadlineBootstrap.js +155 -72
  44. package/dist/threadline/ThreadlineBootstrap.js.map +1 -1
  45. package/dist/threadline/WakeSocketServer.d.ts +49 -0
  46. package/dist/threadline/WakeSocketServer.d.ts.map +1 -0
  47. package/dist/threadline/WakeSocketServer.js +115 -0
  48. package/dist/threadline/WakeSocketServer.js.map +1 -0
  49. package/dist/threadline/listener-daemon.d.ts +94 -0
  50. package/dist/threadline/listener-daemon.d.ts.map +1 -0
  51. package/dist/threadline/listener-daemon.js +550 -0
  52. package/dist/threadline/listener-daemon.js.map +1 -0
  53. package/package.json +2 -1
  54. package/src/data/builtin-manifest.json +91 -91
  55. package/upgrades/0.28.0.md +54 -0
  56. package/upgrades/NEXT.md +35 -0
  57. /package/.claude/skills/autonomous/{skill.md → SKILL.md} +0 -0
  58. /package/.claude/skills/secret-setup/{skill.md → SKILL.md} +0 -0
  59. /package/.claude/skills/setup-wizard/{skill.md → SKILL.md} +0 -0
@@ -1555,6 +1555,24 @@
1555
1555
  }
1556
1556
 
1557
1557
  /* ── Jobs Tab ─────────────────────────────────────────── */
1558
+ .jobs-summary { display: flex; gap: 6px; padding: 0 12px 8px; flex-wrap: wrap; }
1559
+ .jobs-summary-card { flex: 1; min-width: 50px; text-align: center; padding: 6px 4px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; }
1560
+ .jobs-summary-val { font-size: 16px; font-weight: 700; color: var(--text-bright); }
1561
+ .jobs-summary-label { font-size: 9px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.3px; }
1562
+ .jobs-summary-card.failing .jobs-summary-val { color: var(--red); }
1563
+ .jobs-summary-card.running .jobs-summary-val { color: var(--blue); }
1564
+ .sparkline-legend { display: flex; gap: 10px; padding: 4px 0 8px; font-size: 10px; color: var(--text-dim); }
1565
+ .sparkline-legend-item { display: flex; align-items: center; gap: 4px; }
1566
+ .sparkline-legend-dot { width: 8px; height: 8px; border-radius: 2px; }
1567
+ .sparkline-legend-dot.success { background: var(--accent); }
1568
+ .sparkline-legend-dot.failure { background: var(--red); }
1569
+ .sparkline-legend-dot.skipped { background: #555; }
1570
+ .job-config-section { margin-top: 16px; border-top: 1px solid var(--border); padding-top: 12px; }
1571
+ .job-config-title { font-size: 11px; font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 10px; cursor: pointer; display: flex; align-items: center; gap: 6px; }
1572
+ .job-config-title:hover { color: var(--text); }
1573
+ .job-config-grid { display: grid; grid-template-columns: 120px 1fr; gap: 4px 12px; font-size: 12px; }
1574
+ .job-config-key { color: var(--text-dim); padding: 4px 0; }
1575
+ .job-config-val { color: var(--text); padding: 4px 0; word-break: break-word; }
1558
1576
  .jobs-container {
1559
1577
  display: flex;
1560
1578
  grid-column: 1 / -1;
@@ -1929,7 +1947,99 @@
1929
1947
  .spark.s-pending { background: var(--blue); }
1930
1948
  .spark.s-skipped { background: #333; }
1931
1949
 
1932
- /* ── Discovery Tab ──────────────────────────────────────── */
1950
+ /* ── Features Tab (was Discovery) ──────────────────────────── */
1951
+ .features-container {
1952
+ grid-column: 1 / -1;
1953
+ overflow-y: auto;
1954
+ background: var(--bg);
1955
+ padding: 20px;
1956
+ }
1957
+ .features-main { max-width: 960px; margin: 0 auto; }
1958
+ .features-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
1959
+ .features-header h2 { font-size: 16px; font-weight: 600; color: var(--text-bright); }
1960
+ .features-subtitle { font-size: 12px; color: var(--text-dim); margin-bottom: 20px; line-height: 1.4; }
1961
+ .features-refresh { font-size: 11px; padding: 4px 12px; border-radius: 4px; border: 1px solid var(--border); background: transparent; color: var(--text-dim); cursor: pointer; }
1962
+ .features-refresh:hover { border-color: var(--text-dim); color: var(--text); }
1963
+
1964
+ /* Autonomy Profile Card */
1965
+ .autonomy-card {
1966
+ background: var(--bg-panel);
1967
+ border: 1px solid var(--border);
1968
+ border-radius: 10px;
1969
+ padding: 18px 22px;
1970
+ margin-bottom: 24px;
1971
+ display: flex;
1972
+ align-items: center;
1973
+ gap: 18px;
1974
+ }
1975
+ .autonomy-card-icon { font-size: 28px; flex-shrink: 0; }
1976
+ .autonomy-card-info { flex: 1; }
1977
+ .autonomy-card-label { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
1978
+ .autonomy-card-level { font-size: 16px; font-weight: 600; color: var(--text-bright); margin-bottom: 4px; text-transform: capitalize; }
1979
+ .autonomy-card-desc { font-size: 12px; color: var(--text-dim); line-height: 1.4; }
1980
+ .autonomy-card-btn { font-size: 11px; padding: 6px 16px; border-radius: 6px; border: 1px solid var(--border); background: transparent; color: var(--text); cursor: pointer; white-space: nowrap; }
1981
+ .autonomy-card-btn:hover { border-color: var(--accent); color: var(--accent); }
1982
+
1983
+ /* Profile Selector Modal */
1984
+ .profile-selector { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin: 16px 0; }
1985
+ .profile-option { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 8px; padding: 14px 16px; cursor: pointer; transition: border-color 0.15s; }
1986
+ .profile-option:hover { border-color: rgba(255,255,255,0.2); }
1987
+ .profile-option.selected { border-color: var(--accent); background: rgba(74,222,128,0.05); }
1988
+ .profile-option-name { font-size: 14px; font-weight: 600; color: var(--text-bright); margin-bottom: 4px; text-transform: capitalize; }
1989
+ .profile-option-desc { font-size: 11px; color: var(--text-dim); line-height: 1.4; }
1990
+ .profile-option-badge { display: inline-block; font-size: 9px; padding: 1px 6px; border-radius: 3px; margin-top: 6px; }
1991
+ .profile-option-badge.recommended { background: rgba(74,222,128,0.15); color: var(--accent); }
1992
+
1993
+ /* Feature Category Section */
1994
+ .feat-category { margin-bottom: 24px; }
1995
+ .feat-category-title { font-size: 11px; font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 10px; padding-bottom: 6px; border-bottom: 1px solid var(--border); }
1996
+ .feat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 10px; }
1997
+
1998
+ /* Feature Card */
1999
+ .feat-card {
2000
+ background: var(--bg-panel);
2001
+ border: 1px solid var(--border);
2002
+ border-radius: 10px;
2003
+ padding: 14px 16px;
2004
+ display: flex;
2005
+ flex-direction: column;
2006
+ gap: 8px;
2007
+ cursor: pointer;
2008
+ transition: border-color 0.15s, transform 0.1s;
2009
+ }
2010
+ .feat-card:hover { border-color: rgba(255,255,255,0.15); transform: translateY(-1px); }
2011
+ .feat-card-top { display: flex; align-items: center; gap: 10px; }
2012
+ .feat-card-name { font-size: 14px; font-weight: 600; color: var(--text-bright); flex: 1; }
2013
+ .feat-card-arrow { font-size: 11px; color: var(--text-dim); }
2014
+ .feat-card-desc { font-size: 12px; color: var(--text-dim); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
2015
+
2016
+ /* Feature Toggle */
2017
+ .feat-toggle { position: relative; display: inline-block; width: 36px; height: 20px; flex-shrink: 0; }
2018
+ .feat-toggle input { opacity: 0; width: 0; height: 0; }
2019
+ .feat-toggle-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background: #333; border-radius: 20px; transition: 0.2s; }
2020
+ .feat-toggle-slider:before { position: absolute; content: ""; height: 14px; width: 14px; left: 3px; bottom: 3px; background: #888; border-radius: 50%; transition: 0.2s; }
2021
+ .feat-toggle input:checked + .feat-toggle-slider { background: rgba(74,222,128,0.3); }
2022
+ .feat-toggle input:checked + .feat-toggle-slider:before { transform: translateX(16px); background: var(--accent); }
2023
+
2024
+ /* Feature Detail View */
2025
+ .feat-detail-view { display: none; }
2026
+ .feat-detail-view.active { display: block; }
2027
+ .feat-detail-back { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-dim); cursor: pointer; border: none; background: none; padding: 0; margin-bottom: 16px; }
2028
+ .feat-detail-back:hover { color: var(--text); }
2029
+ .feat-detail-header { margin-bottom: 20px; }
2030
+ .feat-detail-title { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
2031
+ .feat-detail-title h3 { font-size: 18px; font-weight: 600; color: var(--text-bright); margin: 0; flex: 1; }
2032
+ .feat-detail-category { font-size: 10px; padding: 2px 8px; border-radius: 3px; background: rgba(255,255,255,0.06); color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; }
2033
+ .feat-detail-description { font-size: 13px; color: var(--text-dim); line-height: 1.6; max-width: 700px; margin-bottom: 20px; }
2034
+ .feat-detail-section { margin-bottom: 20px; }
2035
+ .feat-detail-section-title { font-size: 11px; font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 10px; padding-bottom: 6px; border-bottom: 1px solid var(--border); }
2036
+ .feat-data-item { display: flex; gap: 12px; padding: 10px 14px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 6px; margin-bottom: 6px; font-size: 12px; }
2037
+ .feat-data-icon { font-size: 14px; flex-shrink: 0; margin-top: 1px; }
2038
+ .feat-data-content { flex: 1; }
2039
+ .feat-data-label { font-weight: 500; color: var(--text-bright); margin-bottom: 2px; }
2040
+ .feat-data-desc { color: var(--text-dim); font-size: 11px; line-height: 1.4; }
2041
+ .feat-reversibility { font-size: 12px; color: var(--text-dim); line-height: 1.5; padding: 12px 14px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 6px; }
2042
+
1933
2043
  /* ── Systems Tab ──────────────────────────────────────────── */
1934
2044
  .systems-container { grid-column: 1 / -1; overflow-y: auto; background: var(--bg); padding: 20px; }
1935
2045
  .systems-main { max-width: 960px; margin: 0 auto; }
@@ -2026,250 +2136,7 @@
2026
2136
  .cap-content-empty { padding: 16px; text-align: center; color: var(--text-dim); font-size: 12px; }
2027
2137
  .cap-stat-card:hover { border-color: var(--accent); transition: border-color 0.15s; }
2028
2138
 
2029
- .discovery-container {
2030
- grid-column: 1 / -1;
2031
- overflow-y: auto;
2032
- background: var(--bg);
2033
- padding: 20px;
2034
- }
2035
-
2036
- .discovery-main {
2037
- max-width: 900px;
2038
- margin: 0 auto;
2039
- }
2040
-
2041
- .discovery-header {
2042
- display: flex;
2043
- align-items: center;
2044
- justify-content: space-between;
2045
- margin-bottom: 20px;
2046
- }
2047
-
2048
- .discovery-header h2 {
2049
- font-size: 16px;
2050
- font-weight: 600;
2051
- color: var(--text-bright);
2052
- }
2053
-
2054
- .discovery-refresh {
2055
- font-size: 11px;
2056
- padding: 4px 12px;
2057
- border-radius: 4px;
2058
- border: 1px solid var(--border);
2059
- background: transparent;
2060
- color: var(--text-dim);
2061
- cursor: pointer;
2062
- }
2063
-
2064
- .discovery-refresh:hover {
2065
- border-color: var(--text-dim);
2066
- color: var(--text);
2067
- }
2068
-
2069
- .discovery-section {
2070
- margin-bottom: 24px;
2071
- }
2072
-
2073
- .discovery-section h3 {
2074
- font-size: 13px;
2075
- font-weight: 600;
2076
- color: var(--text-bright);
2077
- margin-bottom: 12px;
2078
- }
2079
-
2080
- .funnel-chart {
2081
- display: flex;
2082
- gap: 4px;
2083
- align-items: flex-end;
2084
- height: 120px;
2085
- padding: 12px 16px;
2086
- background: var(--bg-panel);
2087
- border: 1px solid var(--border);
2088
- border-radius: 8px;
2089
- }
2090
-
2091
- .funnel-bar {
2092
- flex: 1;
2093
- display: flex;
2094
- flex-direction: column;
2095
- align-items: center;
2096
- gap: 4px;
2097
- }
2098
-
2099
- .funnel-bar-fill {
2100
- width: 100%;
2101
- border-radius: 3px 3px 0 0;
2102
- min-height: 2px;
2103
- transition: height 0.3s;
2104
- }
2105
-
2106
- .funnel-bar-label {
2107
- font-size: 10px;
2108
- color: var(--text-dim);
2109
- text-align: center;
2110
- white-space: nowrap;
2111
- }
2112
-
2113
- .funnel-bar-count {
2114
- font-size: 12px;
2115
- font-weight: 600;
2116
- color: var(--text-bright);
2117
- }
2118
-
2119
- .discovery-filter-bar {
2120
- display: flex;
2121
- gap: 4px;
2122
- margin-bottom: 12px;
2123
- flex-wrap: wrap;
2124
- }
2125
-
2126
- .discovery-filter {
2127
- font-size: 11px;
2128
- padding: 2px 8px;
2129
- border-radius: 10px;
2130
- border: 1px solid var(--border);
2131
- background: transparent;
2132
- color: var(--text-dim);
2133
- cursor: pointer;
2134
- transition: all 0.15s;
2135
- }
2136
-
2137
- .discovery-filter:hover {
2138
- border-color: var(--text-dim);
2139
- color: var(--text);
2140
- }
2141
-
2142
- .discovery-filter.active {
2143
- background: var(--accent-dim);
2144
- border-color: var(--accent-dim);
2145
- color: #000;
2146
- }
2147
-
2148
- .feature-grid {
2149
- display: grid;
2150
- grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
2151
- gap: 10px;
2152
- }
2153
-
2154
- .feature-card {
2155
- background: var(--bg-panel);
2156
- border: 1px solid var(--border);
2157
- border-radius: 8px;
2158
- padding: 12px 14px;
2159
- font-size: 12px;
2160
- }
2161
-
2162
- .feature-card-top {
2163
- display: flex;
2164
- align-items: center;
2165
- gap: 8px;
2166
- margin-bottom: 6px;
2167
- }
2168
-
2169
- .feature-card-name {
2170
- font-weight: 500;
2171
- color: var(--text-bright);
2172
- flex: 1;
2173
- }
2174
-
2175
- .feature-state-badge {
2176
- font-size: 10px;
2177
- padding: 1px 6px;
2178
- border-radius: 3px;
2179
- font-weight: 600;
2180
- }
2181
-
2182
- .feature-state-badge.enabled { background: #0f2f0f; color: var(--accent); }
2183
- .feature-state-badge.undiscovered { background: #1a1a1a; color: #666; }
2184
- .feature-state-badge.aware { background: #0f1f2f; color: var(--blue); }
2185
- .feature-state-badge.interested { background: #1f1f0f; color: #eab308; }
2186
- .feature-state-badge.deferred { background: #1a1a1a; color: #888; }
2187
- .feature-state-badge.declined { background: #2f0f0f; color: var(--red); }
2188
- .feature-state-badge.disabled { background: #1a1a1a; color: #555; }
2189
-
2190
- .feature-card-desc {
2191
- color: var(--text-dim);
2192
- font-size: 11px;
2193
- line-height: 1.4;
2194
- margin-bottom: 4px;
2195
- }
2196
-
2197
- .feature-card-meta {
2198
- display: flex;
2199
- gap: 8px;
2200
- color: var(--text-dim);
2201
- font-size: 10px;
2202
- }
2203
-
2204
- .feature-card-meta .tier { text-transform: uppercase; letter-spacing: 0.5px; }
2205
- .feature-card-meta .cooldown { color: var(--orange); }
2206
-
2207
- .event-log {
2208
- background: var(--bg-panel);
2209
- border: 1px solid var(--border);
2210
- border-radius: 8px;
2211
- max-height: 300px;
2212
- overflow-y: auto;
2213
- }
2214
-
2215
- .event-row {
2216
- display: flex;
2217
- gap: 10px;
2218
- padding: 8px 14px;
2219
- border-bottom: 1px solid var(--border);
2220
- font-size: 11px;
2221
- align-items: center;
2222
- }
2223
-
2224
- .event-row:last-child { border-bottom: none; }
2225
-
2226
- .event-time {
2227
- color: var(--text-dim);
2228
- flex-shrink: 0;
2229
- width: 70px;
2230
- }
2231
-
2232
- .event-feature {
2233
- color: var(--text-bright);
2234
- font-weight: 500;
2235
- flex-shrink: 0;
2236
- width: 140px;
2237
- }
2238
-
2239
- .event-transition {
2240
- color: var(--text);
2241
- flex: 1;
2242
- }
2243
-
2244
- .event-arrow { color: var(--text-dim); }
2245
-
2246
- .digest-item {
2247
- background: var(--bg-panel);
2248
- border: 1px solid var(--border);
2249
- border-radius: 6px;
2250
- padding: 10px 14px;
2251
- margin-bottom: 8px;
2252
- font-size: 12px;
2253
- }
2254
-
2255
- .digest-item-title {
2256
- color: var(--text-bright);
2257
- font-weight: 500;
2258
- margin-bottom: 2px;
2259
- }
2260
-
2261
- .digest-item-desc {
2262
- color: var(--text-dim);
2263
- font-size: 11px;
2264
- }
2265
-
2266
- /* Discovery metrics summary */
2267
- .discovery-metrics {
2268
- display: flex;
2269
- gap: 12px;
2270
- margin-bottom: 16px;
2271
- flex-wrap: wrap;
2272
- }
2139
+ /* (Legacy discovery styles removed — replaced by Features tab above) */
2273
2140
 
2274
2141
  .metric-card {
2275
2142
  flex: 1;
@@ -2542,10 +2409,10 @@
2542
2409
  <nav class="tab-bar">
2543
2410
  <button class="tab active" data-tab="sessions" onclick="switchTab('sessions')">Sessions <span class="tab-count" id="tabSessionCount">0</span></button>
2544
2411
  <button class="tab" data-tab="files" onclick="switchTab('files')">Files</button>
2545
- <button class="tab" data-tab="dropzone" onclick="switchTab('dropzone')">Drop Zone</button>
2412
+ <button class="tab" data-tab="dropzone" onclick="switchTab('dropzone')">Send Content</button>
2546
2413
  <button class="tab" data-tab="jobs" onclick="switchTab('jobs')">Jobs <span class="tab-count" id="tabJobCount">0</span></button>
2547
- <button class="tab" data-tab="discovery" onclick="switchTab('discovery')">Discovery</button>
2548
- <button class="tab" data-tab="systems" onclick="switchTab('systems')">Systems</button>
2414
+ <button class="tab" data-tab="features" onclick="switchTab('features')">Features</button>
2415
+ <button class="tab" data-tab="systems" onclick="switchTab('systems')">Health</button>
2549
2416
  </nav>
2550
2417
  </div>
2551
2418
  <div class="vital-signs" id="vitalSigns">
@@ -2588,7 +2455,7 @@
2588
2455
  <div class="session-list" id="sessionList">
2589
2456
  <div class="empty-state" id="emptyState">
2590
2457
  <div class="icon">&#x1F4AD;</div>
2591
- <p>No running sessions<br>Sessions will appear here when spawned</p>
2458
+ <p>No sessions running<br>Create one to start working with your agent</p>
2592
2459
  </div>
2593
2460
  </div>
2594
2461
  </aside>
@@ -2700,12 +2567,12 @@
2700
2567
  </div>
2701
2568
  </div>
2702
2569
 
2703
- <!-- Drop Zone tab -->
2570
+ <!-- Send Content tab (was Drop Zone) -->
2704
2571
  <div class="dropzone-container" id="dropzoneTab" style="display:none">
2705
2572
  <div class="dropzone-panel">
2706
2573
  <div class="dropzone-header">
2707
- <h2>Drop Zone</h2>
2708
- <p class="dropzone-subtitle">Send large text content to your agent session</p>
2574
+ <h2>Send Content</h2>
2575
+ <p class="dropzone-subtitle">Paste text content to send directly to a running session</p>
2709
2576
  </div>
2710
2577
 
2711
2578
  <div class="dropzone-form">
@@ -2751,8 +2618,11 @@
2751
2618
  <option value="lastRun">Sort: Last Run</option>
2752
2619
  </select>
2753
2620
  </div>
2621
+ <div style="font-size:11px;color:var(--text-dim);padding:0 12px 8px;line-height:1.4">Monitor and control your scheduled tasks</div>
2622
+ <div id="jobsSummary" class="jobs-summary"></div>
2754
2623
  <div class="jobs-filter-bar" id="jobsFilterBar">
2755
2624
  <button class="jobs-filter-chip active" data-filter="all" onclick="setJobFilter('all')">All</button>
2625
+ <button class="jobs-filter-chip" data-filter="running" onclick="setJobFilter('running')">Running</button>
2756
2626
  <button class="jobs-filter-chip" data-filter="failing" onclick="setJobFilter('failing')">Failing</button>
2757
2627
  <button class="jobs-filter-chip" data-filter="disabled" onclick="setJobFilter('disabled')">Disabled</button>
2758
2628
  </div>
@@ -2764,22 +2634,23 @@
2764
2634
  <div class="jobs-detail-empty" id="jobsDetailEmpty">
2765
2635
  <div style="text-align:center">
2766
2636
  <div style="font-size:24px;margin-bottom:8px">&#x2699;</div>
2767
- <p>Select a job to view details</p>
2637
+ <p>Select a job to view its schedule, history, and configuration</p>
2768
2638
  </div>
2769
2639
  </div>
2770
2640
  <div class="jobs-detail-content" id="jobsDetailContent" style="display:none"></div>
2771
2641
  </div>
2772
2642
  </div>
2773
2643
 
2774
- <!-- Systems Tab -->
2644
+ <!-- Health Tab (was Systems) -->
2775
2645
  <div class="systems-container" id="systemsTab" style="display:none">
2776
2646
  <div class="systems-main">
2777
2647
  <!-- Overview (card grid) -->
2778
2648
  <div id="systemsOverview">
2779
2649
  <div class="systems-header">
2780
- <h2>Systems</h2>
2650
+ <h2>Health</h2>
2781
2651
  <button class="systems-refresh" onclick="loadSystems()">Refresh</button>
2782
2652
  </div>
2653
+ <div style="font-size:12px;color:var(--text-dim);margin-bottom:16px;line-height:1.4">Your agent's health and running processes</div>
2783
2654
  <div id="systemsBanner">
2784
2655
  <div style="padding:20px;color:var(--text-dim);text-align:center">Loading...</div>
2785
2656
  </div>
@@ -2789,54 +2660,57 @@
2789
2660
  </div>
2790
2661
  <!-- Detail view (single capability) -->
2791
2662
  <div class="cap-detail-view" id="systemsDetailView">
2792
- <button class="cap-detail-back" onclick="showSystemsOverview()">&#9664; Back to overview</button>
2663
+ <button class="cap-detail-back" onclick="showSystemsOverview()">&#9664; Back to Health</button>
2793
2664
  <div id="systemsDetailContent"></div>
2794
2665
  </div>
2795
2666
  </div>
2796
2667
  </div>
2797
2668
 
2798
- <!-- Discovery Tab -->
2799
- <div class="discovery-container" id="discoveryTab" style="display:none">
2800
- <div class="discovery-main">
2801
- <div class="discovery-header">
2802
- <h2>Feature Discovery</h2>
2803
- <button class="discovery-refresh" onclick="loadDiscovery()">Refresh</button>
2669
+ <!-- Features Tab (was Discovery) -->
2670
+ <div class="features-container" id="featuresTab" style="display:none">
2671
+ <div class="features-main">
2672
+ <div class="features-header">
2673
+ <h2>Features</h2>
2674
+ <button class="features-refresh" onclick="loadFeatures()">Refresh</button>
2804
2675
  </div>
2805
-
2806
- <!-- Funnel Visualization -->
2807
- <div class="discovery-section">
2808
- <h3>Discovery Funnel</h3>
2809
- <div class="funnel-chart" id="funnelChart">
2810
- <div style="padding:20px;color:var(--text-dim);text-align:center">Loading...</div>
2676
+ <div class="features-subtitle">Browse and configure your agent's capabilities. Features are opt-in and can be enabled or disabled at any time.</div>
2677
+
2678
+ <!-- Autonomy Profile -->
2679
+ <div class="autonomy-card" id="autonomyCard">
2680
+ <div class="autonomy-card-icon" id="autonomyIcon">&#x1F6E1;</div>
2681
+ <div class="autonomy-card-info">
2682
+ <div class="autonomy-card-label">Autonomy Profile</div>
2683
+ <div class="autonomy-card-level" id="autonomyLevel">Loading...</div>
2684
+ <div class="autonomy-card-desc" id="autonomyDesc"></div>
2811
2685
  </div>
2686
+ <button class="autonomy-card-btn" onclick="showProfileSelector()">Change</button>
2812
2687
  </div>
2813
2688
 
2814
- <!-- Feature States Grid -->
2815
- <div class="discovery-section">
2816
- <h3>Feature States</h3>
2817
- <div class="discovery-filter-bar">
2818
- <button class="discovery-filter active" data-filter="all" onclick="setDiscoveryFilter('all')">All</button>
2819
- <button class="discovery-filter" data-filter="enabled" onclick="setDiscoveryFilter('enabled')">Enabled</button>
2820
- <button class="discovery-filter" data-filter="undiscovered" onclick="setDiscoveryFilter('undiscovered')">Undiscovered</button>
2821
- <button class="discovery-filter" data-filter="aware" onclick="setDiscoveryFilter('aware')">Aware</button>
2822
- <button class="discovery-filter" data-filter="cooldown" onclick="setDiscoveryFilter('cooldown')">In Cooldown</button>
2823
- </div>
2824
- <div class="feature-grid" id="featureGrid"></div>
2689
+ <!-- Feature Categories (populated by JS) -->
2690
+ <div id="featureCatalog">
2691
+ <div style="padding:20px;color:var(--text-dim);text-align:center">Loading features...</div>
2825
2692
  </div>
2693
+ </div>
2826
2694
 
2827
- <!-- Digest Section -->
2828
- <div class="discovery-section" id="digestSection" style="display:none">
2829
- <h3>Digest</h3>
2830
- <div id="digestContent"></div>
2831
- </div>
2695
+ <!-- Feature Detail View (hidden by default) -->
2696
+ <div class="features-main feat-detail-view" id="featDetailView">
2697
+ <button class="feat-detail-back" onclick="showFeaturesOverview()">&#9664; Back to Features</button>
2698
+ <div id="featDetailContent"></div>
2699
+ </div>
2832
2700
 
2833
- <!-- Event Log -->
2834
- <div class="discovery-section">
2835
- <h3>Recent Events</h3>
2836
- <div class="event-log" id="eventLog">
2837
- <div style="padding:12px;color:var(--text-dim);text-align:center">No events yet</div>
2838
- </div>
2701
+ <!-- Profile Selector (hidden by default) -->
2702
+ <div class="features-main feat-detail-view" id="profileSelectorView">
2703
+ <button class="feat-detail-back" onclick="showFeaturesOverview()">&#9664; Back to Features</button>
2704
+ <div class="feat-detail-header">
2705
+ <div class="feat-detail-title"><h3>Choose Autonomy Profile</h3></div>
2706
+ <div class="feat-detail-description">Your autonomy profile controls how independently your agent operates. Higher autonomy means fewer interruptions, but less oversight.</div>
2839
2707
  </div>
2708
+ <div class="profile-selector" id="profileSelector"></div>
2709
+ <div style="display:flex;justify-content:flex-end;gap:8px;margin-top:16px">
2710
+ <button class="autonomy-card-btn" onclick="showFeaturesOverview()">Cancel</button>
2711
+ <button class="autonomy-card-btn" id="profileConfirmBtn" style="border-color:var(--accent);color:var(--accent)" onclick="confirmProfileChange()">Apply Profile</button>
2712
+ </div>
2713
+ <div id="profileHistory" style="margin-top:24px"></div>
2840
2714
  </div>
2841
2715
  </div>
2842
2716
 
@@ -3131,20 +3005,20 @@
3131
3005
  if (tele.toolsUsed && tele.toolsUsed.length > 0) {
3132
3006
  const toolList = tele.toolsUsed.slice(0, 4).join(', ');
3133
3007
  const extra = tele.toolsUsed.length > 4 ? ` +${tele.toolsUsed.length - 4}` : '';
3134
- parts.push(`<span class="telemetry-tag">${escapeHtml(toolList)}${extra}</span>`);
3008
+ parts.push(`<span class="telemetry-tag" title="Tools used in this session">${escapeHtml(toolList)}${extra}</span>`);
3135
3009
  }
3136
3010
  if (tele.subagentsSpawned && tele.subagentsSpawned.length > 0) {
3137
- parts.push(`<span class="telemetry-tag active-subagents">${tele.subagentsSpawned.length} subagent${tele.subagentsSpawned.length > 1 ? 's' : ''}</span>`);
3011
+ parts.push(`<span class="telemetry-tag active-subagents" title="Background tasks spawned by this session">${tele.subagentsSpawned.length} background task${tele.subagentsSpawned.length > 1 ? 's' : ''}</span>`);
3138
3012
  }
3139
3013
  if (tele.lastActivity) {
3140
3014
  const lastMs = Date.now() - new Date(tele.lastActivity).getTime();
3141
3015
  const lastMins = Math.floor(lastMs / 60000);
3142
3016
  if (lastMins > 10) {
3143
- parts.push(`<span class="telemetry-tag stale">idle ${lastMins}m</span>`);
3017
+ parts.push(`<span class="telemetry-tag stale" title="No activity for ${lastMins} minutes">waiting ${lastMins}m</span>`);
3144
3018
  }
3145
3019
  }
3146
3020
  if (tele.eventCount) {
3147
- parts.push(`<span class="telemetry-tag">${tele.eventCount} events</span>`);
3021
+ parts.push(`<span class="telemetry-tag" title="Total events in this session">${tele.eventCount} events</span>`);
3148
3022
  }
3149
3023
  if (parts.length > 0) {
3150
3024
  telemetryHtml = `<div class="session-telemetry">${parts.join('')}</div>`;
@@ -3773,10 +3647,10 @@
3773
3647
  onDeactivate: () => { disconnectJobsSSE(); },
3774
3648
  },
3775
3649
  {
3776
- id: 'discovery',
3777
- panels: ['discoveryTab'],
3650
+ id: 'features',
3651
+ panels: ['featuresTab'],
3778
3652
  display: ['flex'],
3779
- onActivate: () => { if (!discoveryLoaded) loadDiscovery(); },
3653
+ onActivate: () => { if (!featuresLoaded) loadFeatures(); },
3780
3654
  },
3781
3655
  {
3782
3656
  id: 'systems',
@@ -4638,7 +4512,7 @@
4638
4512
  <span class="dz-paste-meta">${chars} chars &middot; ${age}</span>
4639
4513
  </div>
4640
4514
  <div style="display:flex;align-items:center;gap:8px">
4641
- <span class="dz-paste-status ${p.status}">${p.status}</span>
4515
+ <span class="dz-paste-status ${p.status}">${p.status === 'notified' ? 'delivered' : p.status === 'queued' || p.status === 'written' ? 'waiting' : p.status}</span>
4642
4516
  <button class="dz-paste-delete" onclick="deletePaste('${esc(p.pasteId)}')" title="Delete">&times;</button>
4643
4517
  </div>
4644
4518
  </div>`;
@@ -4674,10 +4548,10 @@
4674
4548
  const chars = data.contentLength?.toLocaleString() || content.length.toLocaleString();
4675
4549
  if (data.status === 'notified') {
4676
4550
  status.className = 'dropzone-status success';
4677
- status.textContent = `Sent ${chars} chars to session "${data.sessionName}"`;
4551
+ status.textContent = `Delivered ${chars} chars to session "${data.sessionName}"`;
4678
4552
  } else {
4679
4553
  status.className = 'dropzone-status queued';
4680
- status.textContent = `Queued ${chars} chars \u2014 will be delivered when a session starts.`;
4554
+ status.textContent = `Waiting for session \u2014 ${chars} chars will be delivered when a session starts.`;
4681
4555
  }
4682
4556
  status.style.display = 'block';
4683
4557
 
@@ -4763,8 +4637,8 @@
4763
4637
  const sessVital = document.getElementById('vitalSessions');
4764
4638
  const cur = h.sessions?.current ?? 0;
4765
4639
  const max = h.sessions?.max ?? 0;
4766
- sessEl.textContent = cur + '/' + max;
4767
- if (cur >= max) {
4640
+ sessEl.textContent = cur + (max > 0 ? '/' + max : '') + ' session' + (cur !== 1 ? 's' : '');
4641
+ if (max > 0 && cur >= max) {
4768
4642
  sessVital.className = 'vital warn';
4769
4643
  } else {
4770
4644
  sessVital.className = 'vital';
@@ -4874,6 +4748,7 @@
4874
4748
  const data = await apiFetch('/jobs');
4875
4749
  const jobs = data.jobs || data;
4876
4750
  jobsData = Array.isArray(jobs) ? jobs : [];
4751
+ renderJobsSummary();
4877
4752
  renderJobList();
4878
4753
  } catch (e) {
4879
4754
  document.getElementById('jobsList').innerHTML =
@@ -4881,6 +4756,19 @@
4881
4756
  }
4882
4757
  }
4883
4758
 
4759
+ function renderJobsSummary() {
4760
+ const total = jobsData.length;
4761
+ const running = jobsData.filter(j => j.state && j.state.lastResult === 'pending').length;
4762
+ const failing = jobsData.filter(j => j.enabled && (j.state?.consecutiveFailures || 0) > 0).length;
4763
+ const disabled = jobsData.filter(j => !j.enabled).length;
4764
+ const healthy = total - failing - disabled;
4765
+ document.getElementById('jobsSummary').innerHTML = `
4766
+ <div class="jobs-summary-card"><div class="jobs-summary-val">${total}</div><div class="jobs-summary-label">Total</div></div>
4767
+ <div class="jobs-summary-card${running > 0 ? ' running' : ''}"><div class="jobs-summary-val">${running}</div><div class="jobs-summary-label">Running</div></div>
4768
+ <div class="jobs-summary-card${failing > 0 ? ' failing' : ''}"><div class="jobs-summary-val">${failing}</div><div class="jobs-summary-label">Failing</div></div>
4769
+ <div class="jobs-summary-card"><div class="jobs-summary-val">${disabled}</div><div class="jobs-summary-label">Disabled</div></div>`;
4770
+ }
4771
+
4884
4772
  function setJobFilter(filter) {
4885
4773
  jobFilter = filter;
4886
4774
  document.querySelectorAll('.jobs-filter-chip').forEach(c => {
@@ -4894,6 +4782,9 @@
4894
4782
  const sort = document.getElementById('jobsSort').value;
4895
4783
 
4896
4784
  let filtered = jobsData.filter(j => {
4785
+ if (jobFilter === 'running') {
4786
+ return j.state && j.state.lastResult === 'pending';
4787
+ }
4897
4788
  if (jobFilter === 'failing') {
4898
4789
  const s = j.state || {};
4899
4790
  return (s.consecutiveFailures || 0) > 0;
@@ -4943,6 +4834,8 @@
4943
4834
  const lastResult = s.lastResult || '—';
4944
4835
  const lastTime = s.lastRun ? timeAgo(new Date(s.lastRun)) : 'never';
4945
4836
  const isActive = selectedJob === j.slug ? ' active' : '';
4837
+ const displayName = j.name || j.slug;
4838
+ const desc = j.description ? '<div style="font-size:10px;color:var(--text-dim);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + esc(j.description) + '</div>' : '';
4946
4839
 
4947
4840
  const priorityBadge = (j.priority === 'critical' || j.priority === 'high')
4948
4841
  ? '<span class="priority-badge ' + esc(j.priority) + '">' + esc(j.priority) + '</span>' : '';
@@ -4950,9 +4843,10 @@
4950
4843
  return '<div class="job-item' + isActive + '" onclick="selectJob(\'' + esc(j.slug) + '\')">'
4951
4844
  + '<div class="job-item-top">'
4952
4845
  + '<span class="job-status-dot ' + dotClass + '"></span>'
4953
- + '<span class="job-item-name">' + esc(j.slug) + '</span>'
4846
+ + '<span class="job-item-name">' + esc(displayName) + '</span>'
4954
4847
  + (failures > 0 ? '<span class="job-failure-count">' + failures + ' fail</span>' : '')
4955
4848
  + '</div>'
4849
+ + desc
4956
4850
  + '<div class="job-item-meta">'
4957
4851
  + '<span>' + esc(schedule) + '</span>'
4958
4852
  + '<span class="model-badge">' + esc(j.model || '—') + '</span>'
@@ -4991,23 +4885,46 @@
4991
4885
  let statusText = 'Healthy';
4992
4886
  let statusClass = 'success';
4993
4887
  if (!job.enabled) { statusText = 'Disabled'; statusClass = ''; }
4994
- else if (failures >= 3) { statusText = 'Failing (' + failures + ' consecutive)'; statusClass = 'error'; }
4995
- else if (failures > 0) { statusText = 'Warning (' + failures + ' failures)'; statusClass = 'error'; }
4996
- else if (isRunning) { statusText = 'Running'; statusClass = ''; }
4997
-
4998
- const tags = (job.tags || []).map(t => esc(t)).join(', ') || '—';
4888
+ else if (failures >= 3) { statusText = 'This job has failed ' + failures + ' times in a row'; statusClass = 'error'; }
4889
+ else if (failures > 0) { statusText = failures + ' recent failure' + (failures > 1 ? 's' : ''); statusClass = 'error'; }
4890
+ else if (isRunning) { statusText = 'Running now'; statusClass = ''; }
4891
+
4892
+ const displayName = job.name || job.slug;
4893
+ const nextTimeAbs = s.nextScheduled ? new Date(s.nextScheduled).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '—';
4894
+ const lastTimeAbs = s.lastRun ? new Date(s.lastRun).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : 'Never';
4895
+
4896
+ // Supervision explanation
4897
+ const supervisionLabels = { tier0: 'Basic (no AI verification)', tier1: 'AI-supervised (Haiku validates)', tier2: 'Full AI (Sonnet/Opus handles reasoning)' };
4898
+ const execTypeLabels = { skill: 'Runs a skill', prompt: 'Runs a prompt', script: 'Runs a script' };
4899
+
4900
+ // Build config section
4901
+ let configHtml = '<div class="job-config-section">'
4902
+ + '<div class="job-config-title" onclick="this.nextElementSibling.style.display=this.nextElementSibling.style.display===\'none\'?\'grid\':\'none\'">&#9654; Configuration</div>'
4903
+ + '<div class="job-config-grid" style="display:none">'
4904
+ + '<div class="job-config-key">Schedule</div><div class="job-config-val">' + esc(schedule) + ' (' + esc(job.schedule) + ')</div>'
4905
+ + '<div class="job-config-key">Model</div><div class="job-config-val">' + esc(job.model || 'default') + '</div>'
4906
+ + '<div class="job-config-key">Priority</div><div class="job-config-val">' + esc(job.priority || 'medium') + '</div>'
4907
+ + '<div class="job-config-key">Execution</div><div class="job-config-val">' + esc(execTypeLabels[job.execute?.type] || job.execute?.type || '—') + '</div>';
4908
+ if (job.supervision) {
4909
+ configHtml += '<div class="job-config-key">Supervision</div><div class="job-config-val">' + esc(supervisionLabels[job.supervision] || job.supervision) + '</div>';
4910
+ }
4911
+ if (job.tags && job.tags.length > 0) {
4912
+ configHtml += '<div class="job-config-key">Tags</div><div class="job-config-val">' + job.tags.map(t => esc(t)).join(', ') + '</div>';
4913
+ }
4914
+ if (job.gate) {
4915
+ configHtml += '<div class="job-config-key">Pre-flight check</div><div class="job-config-val">Yes (runs before each execution)</div>';
4916
+ }
4917
+ if (job.machines && job.machines.length > 0) {
4918
+ configHtml += '<div class="job-config-key">Machines</div><div class="job-config-val">' + job.machines.map(m => esc(m)).join(', ') + '</div>';
4919
+ }
4920
+ configHtml += '</div></div>';
4999
4921
 
5000
4922
  detail.innerHTML = '<div class="job-detail-header">'
5001
4923
  + '<div>'
5002
4924
  + '<button class="back-btn" onclick="jobsGoBack()" style="display:none;margin-right:8px">&larr;</button>'
5003
- + '<h3>' + esc(job.slug) + '</h3>'
5004
- + '<div class="job-desc">' + esc(job.description || job.name || '') + '</div>'
5005
- + '<div class="job-meta-line">'
5006
- + '<span>' + esc(job.schedule) + ' (' + esc(schedule) + ')</span>'
5007
- + '<span class="model-badge">' + esc(job.model || '—') + '</span>'
5008
- + (job.priority ? '<span class="priority-badge ' + esc(job.priority) + '">' + esc(job.priority) + '</span>' : '')
5009
- + '<span>Tags: ' + tags + '</span>'
5010
- + '</div></div>'
4925
+ + '<h3>' + esc(displayName) + '</h3>'
4926
+ + '<div class="job-desc">' + esc(job.description || '') + '</div>'
4927
+ + '</div>'
5011
4928
  + '<div class="job-detail-actions">'
5012
4929
  + '<button class="job-run-btn" id="jobRunBtn" onclick="runJob(\'' + esc(job.slug) + '\')"'
5013
4930
  + (isRunning ? ' disabled' : '') + '>'
@@ -5018,14 +4935,16 @@
5018
4935
  + '</div></div>'
5019
4936
  + '<div class="job-state-card">'
5020
4937
  + '<div class="state-row"><span>Status</span><span class="state-val ' + statusClass + '">' + esc(statusText) + '</span></div>'
5021
- + '<div class="state-row"><span>Last Run</span><span class="state-val">' + esc(lastTime) + '</span></div>'
4938
+ + '<div class="state-row"><span>Last Run</span><span class="state-val">' + esc(lastTimeAbs) + ' (' + esc(lastTime) + ')</span></div>'
5022
4939
  + '<div class="state-row"><span>Last Result</span><span class="state-val ' + (lastResult === 'success' ? 'success' : failures > 0 ? 'error' : '') + '">' + esc(lastResult) + '</span></div>'
5023
- + (s.lastError ? '<div class="state-row"><span>Error</span><span class="state-val error">' + esc(s.lastError) + '</span></div>' : '')
5024
- + '<div class="state-row"><span>Next Run</span><span class="state-val">' + esc(nextTime) + '</span></div>'
4940
+ + (s.lastError ? '<div class="state-row"><span>Error</span><span class="state-val error" style="word-break:break-word">' + esc(s.lastError) + '</span></div>' : '')
4941
+ + '<div class="state-row"><span>Next Run</span><span class="state-val">' + esc(nextTimeAbs) + '</span></div>'
5025
4942
  + '</div>'
5026
4943
  + '<div id="jobSparkline" class="job-sparkline"></div>'
4944
+ + '<div class="sparkline-legend"><div class="sparkline-legend-item"><div class="sparkline-legend-dot success"></div>Success</div><div class="sparkline-legend-item"><div class="sparkline-legend-dot failure"></div>Failure</div><div class="sparkline-legend-item"><div class="sparkline-legend-dot skipped"></div>Skipped</div></div>'
5027
4945
  + '<div class="job-history-section"><h4>Run History</h4>'
5028
- + '<div id="jobHistoryTable">Loading...</div></div>';
4946
+ + '<div id="jobHistoryTable">Loading...</div></div>'
4947
+ + configHtml;
5029
4948
 
5030
4949
  // Show back button on mobile
5031
4950
  if (window.innerWidth <= 768) {
@@ -5072,12 +4991,12 @@
5072
4991
  const result = r.result || '—';
5073
4992
  const dur = r.durationSeconds != null ? r.durationSeconds + 's'
5074
4993
  : r.durationMs != null ? Math.round(r.durationMs / 1000) + 's' : '—';
5075
- const error = r.error ? esc(r.error).substring(0, 80) : '—';
4994
+ const error = r.error ? esc(r.error) : '—';
5076
4995
  return '<tr>'
5077
4996
  + '<td>' + esc(time) + '</td>'
5078
4997
  + '<td><span class="result-badge ' + esc(result) + '">' + esc(result) + '</span></td>'
5079
4998
  + '<td>' + esc(dur) + '</td>'
5080
- + '<td style="color:var(--text-dim);max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + error + '</td>'
4999
+ + '<td style="color:var(--text-dim);max-width:300px;word-break:break-word" title="' + esc(r.error || '') + '">' + error + '</td>'
5081
5000
  + '</tr>';
5082
5001
  }).join('') + '</tbody></table>';
5083
5002
  } catch (e) {
@@ -5240,198 +5159,289 @@
5240
5159
  }
5241
5160
  }
5242
5161
 
5243
- // ── Discovery Tab ──────────────────────────────────────
5244
- let discoveryLoaded = false;
5245
- let discoveryData = null;
5246
- let discoveryFilter = 'all';
5162
+ // ── Features Tab (was Discovery) ──────────────────────────
5163
+ let featuresLoaded = false;
5164
+ let featuresData = null; // { features: [], autonomy: {} }
5165
+ let selectedProfile = null;
5247
5166
 
5248
- const FUNNEL_COLORS = {
5249
- undiscovered: '#444',
5250
- aware: 'var(--blue)',
5251
- interested: '#eab308',
5252
- deferred: '#888',
5253
- declined: 'var(--red)',
5254
- enabled: 'var(--accent)',
5255
- disabled: '#555',
5167
+ const AUTONOMY_PROFILES = {
5168
+ cautious: { icon: '&#x1F6E1;', desc: 'You approve everything. Maximum oversight, minimum agent independence.' },
5169
+ supervised: { icon: '&#x1F441;', desc: 'Routine tasks run automatically. You approve important decisions. Recommended for most users.' },
5170
+ collaborative: { icon: '&#x1F91D;', desc: 'Agent handles most decisions independently. Keeps you informed of what it does.' },
5171
+ autonomous: { icon: '&#x1F680;', desc: 'Full self-governance. Agent handles everything and reports results.' },
5256
5172
  };
5257
5173
 
5258
- const FUNNEL_ORDER = ['undiscovered', 'aware', 'interested', 'deferred', 'declined', 'enabled', 'disabled'];
5174
+ const CATEGORY_LABELS = {
5175
+ communication: 'Communication',
5176
+ infrastructure: 'Infrastructure',
5177
+ intelligence: 'Intelligence',
5178
+ safety: 'Safety & Trust',
5179
+ };
5180
+ const CATEGORY_ORDER = ['safety', 'communication', 'infrastructure', 'intelligence'];
5259
5181
 
5260
- async function loadDiscovery() {
5261
- discoveryLoaded = true;
5182
+ async function loadFeatures() {
5183
+ featuresLoaded = true;
5262
5184
  try {
5263
- discoveryData = await apiFetch('/features/analytics');
5264
- renderDiscoveryMetrics();
5265
- renderFunnel();
5266
- renderFeatureGrid();
5267
- renderDigest();
5268
- renderEventLog();
5185
+ const [rawFeatures, autonomy] = await Promise.all([
5186
+ apiFetch('/features'),
5187
+ apiFetch('/autonomy').catch(() => ({ profile: 'supervised' })),
5188
+ ]);
5189
+ // API returns { features: [{ definition: {...}, state: {...} }] }
5190
+ const featureList = rawFeatures?.features || rawFeatures || [];
5191
+ const flattened = featureList.map(f => {
5192
+ if (f.definition) {
5193
+ return { ...f.definition, ...f.state, discoveryState: f.state?.discoveryState, enabled: f.state?.enabled };
5194
+ }
5195
+ return f;
5196
+ });
5197
+ featuresData = { features: flattened, autonomy: autonomy || {} };
5198
+ renderAutonomyCard();
5199
+ renderFeatureCatalog();
5269
5200
  } catch (e) {
5270
- document.getElementById('funnelChart').innerHTML =
5271
- '<div style="padding:20px;color:var(--red);text-align:center">Failed to load discovery data</div>';
5201
+ document.getElementById('featureCatalog').innerHTML =
5202
+ '<div style="padding:20px;color:var(--red);text-align:center">Failed to load features</div>';
5272
5203
  }
5273
5204
  }
5274
5205
 
5275
- function renderDiscoveryMetrics() {
5276
- if (!discoveryData) return;
5277
- const d = discoveryData;
5278
- const metricsHtml = `
5279
- <div class="discovery-metrics">
5280
- <div class="metric-card">
5281
- <div class="metric-value">${d.totalFeatures}</div>
5282
- <div class="metric-label">Total Features</div>
5283
- </div>
5284
- <div class="metric-card">
5285
- <div class="metric-value">${d.enabledCount}</div>
5286
- <div class="metric-label">Enabled</div>
5287
- </div>
5288
- <div class="metric-card">
5289
- <div class="metric-value">${Math.round(d.discoveryRate * 100)}%</div>
5290
- <div class="metric-label">Discovery Rate</div>
5291
- </div>
5292
- <div class="metric-card">
5293
- <div class="metric-value">${d.cooldowns.filter(c => c.cooldownExpiresAt).length}</div>
5294
- <div class="metric-label">In Cooldown</div>
5295
- </div>
5296
- </div>`;
5297
- // Insert before funnel
5298
- const section = document.querySelector('.discovery-section');
5299
- if (section && !document.getElementById('discoveryMetrics')) {
5300
- const div = document.createElement('div');
5301
- div.id = 'discoveryMetrics';
5302
- div.innerHTML = metricsHtml;
5303
- section.parentNode.insertBefore(div, section);
5304
- }
5305
- }
5306
-
5307
- function renderFunnel() {
5308
- if (!discoveryData) return;
5309
- const funnel = discoveryData.funnel;
5310
- const maxVal = Math.max(1, ...Object.values(funnel));
5311
- const chart = document.getElementById('funnelChart');
5312
-
5313
- chart.innerHTML = FUNNEL_ORDER.map(state => {
5314
- const count = funnel[state] || 0;
5315
- const height = Math.max(2, (count / maxVal) * 80);
5316
- const color = FUNNEL_COLORS[state] || '#444';
5317
- return `<div class="funnel-bar">
5318
- <div class="funnel-bar-count">${count}</div>
5319
- <div class="funnel-bar-fill" style="height:${height}px;background:${color}"></div>
5320
- <div class="funnel-bar-label">${state}</div>
5321
- </div>`;
5322
- }).join('');
5323
- }
5324
-
5325
- function setDiscoveryFilter(filter) {
5326
- discoveryFilter = filter;
5327
- document.querySelectorAll('.discovery-filter').forEach(c => {
5328
- c.classList.toggle('active', c.dataset.filter === filter);
5329
- });
5330
- renderFeatureGrid();
5206
+ function renderAutonomyCard() {
5207
+ if (!featuresData) return;
5208
+ const profile = featuresData.autonomy.profile || featuresData.autonomy.autonomyProfile || 'supervised';
5209
+ const info = AUTONOMY_PROFILES[profile] || AUTONOMY_PROFILES.supervised;
5210
+ document.getElementById('autonomyIcon').innerHTML = info.icon;
5211
+ document.getElementById('autonomyLevel').textContent = profile;
5212
+ document.getElementById('autonomyDesc').textContent = info.desc;
5331
5213
  }
5332
5214
 
5333
- function renderFeatureGrid() {
5334
- if (!discoveryData) return;
5335
- const grid = document.getElementById('featureGrid');
5336
- const cooldowns = discoveryData.cooldowns || [];
5215
+ function renderFeatureCatalog() {
5216
+ if (!featuresData) return;
5217
+ const catalog = document.getElementById('featureCatalog');
5218
+ const features = featuresData.features;
5337
5219
 
5338
- let filtered = cooldowns;
5339
- if (discoveryFilter === 'enabled') {
5340
- filtered = cooldowns.filter(c => c.discoveryState === 'enabled');
5341
- } else if (discoveryFilter === 'undiscovered') {
5342
- filtered = cooldowns.filter(c => c.discoveryState === 'undiscovered');
5343
- } else if (discoveryFilter === 'aware') {
5344
- filtered = cooldowns.filter(c => ['aware', 'interested', 'deferred'].includes(c.discoveryState));
5345
- } else if (discoveryFilter === 'cooldown') {
5346
- filtered = cooldowns.filter(c => c.cooldownExpiresAt || c.quieted);
5220
+ // Group by category
5221
+ const groups = {};
5222
+ for (const f of features) {
5223
+ const cat = f.category || 'other';
5224
+ if (!groups[cat]) groups[cat] = [];
5225
+ groups[cat].push(f);
5347
5226
  }
5348
5227
 
5349
- if (filtered.length === 0) {
5350
- grid.innerHTML = '<div style="padding:16px;color:var(--text-dim);text-align:center">No features match filter</div>';
5351
- return;
5228
+ let html = '';
5229
+ for (const cat of CATEGORY_ORDER) {
5230
+ const items = groups[cat];
5231
+ if (!items || items.length === 0) continue;
5232
+ const label = CATEGORY_LABELS[cat] || cat;
5233
+ html += `<div class="feat-category">
5234
+ <div class="feat-category-title">${esc(label)}</div>
5235
+ <div class="feat-grid">`;
5236
+ for (const f of items) {
5237
+ const isEnabled = f.discoveryState === 'enabled' || f.enabled;
5238
+ const oneLiner = f.oneLiner || f.description || '';
5239
+ html += `<div class="feat-card" onclick="showFeatureDetail('${esc(f.id)}')">
5240
+ <div class="feat-card-top">
5241
+ <span class="feat-card-name">${esc(f.name)}</span>
5242
+ <label class="feat-toggle" onclick="event.stopPropagation()">
5243
+ <input type="checkbox" ${isEnabled ? 'checked' : ''} onchange="toggleFeature('${esc(f.id)}', this.checked)">
5244
+ <span class="feat-toggle-slider"></span>
5245
+ </label>
5246
+ <span class="feat-card-arrow">&#9654;</span>
5247
+ </div>
5248
+ <div class="feat-card-desc">${esc(oneLiner)}</div>
5249
+ </div>`;
5250
+ }
5251
+ html += '</div></div>';
5352
5252
  }
5353
5253
 
5354
- grid.innerHTML = filtered.map(c => {
5355
- const cooldownNote = c.cooldownExpiresAt
5356
- ? '<span class="cooldown">cooldown until ' + new Date(c.cooldownExpiresAt).toLocaleTimeString() + '</span>'
5357
- : c.quieted ? '<span class="cooldown">quieted (' + c.surfaceCount + '/' + c.maxSurfaces + ')</span>' : '';
5254
+ // Handle uncategorized
5255
+ for (const cat of Object.keys(groups)) {
5256
+ if (CATEGORY_ORDER.includes(cat)) continue;
5257
+ const items = groups[cat];
5258
+ html += `<div class="feat-category">
5259
+ <div class="feat-category-title">${esc(cat)}</div>
5260
+ <div class="feat-grid">`;
5261
+ for (const f of items) {
5262
+ const isEnabled = f.discoveryState === 'enabled' || f.enabled;
5263
+ html += `<div class="feat-card" onclick="showFeatureDetail('${esc(f.id)}')">
5264
+ <div class="feat-card-top">
5265
+ <span class="feat-card-name">${esc(f.name)}</span>
5266
+ <label class="feat-toggle" onclick="event.stopPropagation()">
5267
+ <input type="checkbox" ${isEnabled ? 'checked' : ''} onchange="toggleFeature('${esc(f.id)}', this.checked)">
5268
+ <span class="feat-toggle-slider"></span>
5269
+ </label>
5270
+ <span class="feat-card-arrow">&#9654;</span>
5271
+ </div>
5272
+ <div class="feat-card-desc">${esc(f.oneLiner || f.description || '')}</div>
5273
+ </div>`;
5274
+ }
5275
+ html += '</div></div>';
5276
+ }
5358
5277
 
5359
- return `<div class="feature-card">
5360
- <div class="feature-card-top">
5361
- <span class="feature-card-name">${esc(c.featureName)}</span>
5362
- <span class="feature-state-badge ${esc(c.discoveryState)}">${esc(c.discoveryState)}</span>
5363
- </div>
5364
- <div class="feature-card-meta">
5365
- <span>surfaced ${c.surfaceCount}/${c.maxSurfaces}</span>
5366
- ${cooldownNote}
5367
- </div>
5368
- </div>`;
5369
- }).join('');
5278
+ catalog.innerHTML = html || '<div style="padding:20px;color:var(--text-dim);text-align:center">No features found</div>';
5370
5279
  }
5371
5280
 
5372
- function renderDigest() {
5373
- if (!discoveryData) return;
5374
- const section = document.getElementById('digestSection');
5375
- const content = document.getElementById('digestContent');
5376
- const changed = discoveryData.changedDisabled || [];
5377
- const unused = discoveryData.unusedEnabled || [];
5378
-
5379
- if (changed.length === 0 && unused.length === 0) {
5380
- section.style.display = 'none';
5381
- return;
5281
+ async function toggleFeature(featureId, enable) {
5282
+ try {
5283
+ const to = enable ? 'enabled' : 'disabled';
5284
+ await apiFetch(`/features/${featureId}/transition`, {
5285
+ method: 'POST',
5286
+ headers: { 'Content-Type': 'application/json' },
5287
+ body: JSON.stringify({ to }),
5288
+ });
5289
+ // Refresh features data
5290
+ featuresLoaded = false;
5291
+ await loadFeatures();
5292
+ } catch (e) {
5293
+ showToast('Failed to update feature: ' + (e.message || e), 'error');
5294
+ featuresLoaded = false;
5295
+ await loadFeatures(); // Reset toggle state
5382
5296
  }
5297
+ }
5383
5298
 
5384
- section.style.display = '';
5385
- let html = '';
5386
-
5387
- if (changed.length > 0) {
5388
- html += '<h4 style="font-size:12px;color:var(--text-dim);margin-bottom:8px">Disabled features with updates</h4>';
5389
- html += changed.map(f =>
5390
- `<div class="digest-item">
5391
- <div class="digest-item-title">${esc(f.featureName)}</div>
5392
- <div class="digest-item-desc">Version ${esc(f.currentVersion)} available</div>
5393
- </div>`
5394
- ).join('');
5299
+ function showFeatureDetail(featureId) {
5300
+ if (!featuresData) return;
5301
+ const f = featuresData.features.find(feat => feat.id === featureId);
5302
+ if (!f) return;
5303
+
5304
+ // Hide overview, show detail
5305
+ document.getElementById('featureCatalog').parentNode.querySelector('.features-header').style.display = 'none';
5306
+ document.getElementById('featureCatalog').parentNode.querySelector('.features-subtitle').style.display = 'none';
5307
+ document.getElementById('autonomyCard').style.display = 'none';
5308
+ document.getElementById('featureCatalog').style.display = 'none';
5309
+ document.getElementById('featDetailView').classList.add('active');
5310
+
5311
+ const isEnabled = f.discoveryState === 'enabled' || f.enabled;
5312
+ const content = document.getElementById('featDetailContent');
5313
+
5314
+ let dataHtml = '';
5315
+ if (f.dataImplications && f.dataImplications.length > 0) {
5316
+ dataHtml = `<div class="feat-detail-section">
5317
+ <div class="feat-detail-section-title">Data & Privacy</div>
5318
+ ${f.dataImplications.map(d => `
5319
+ <div class="feat-data-item">
5320
+ <div class="feat-data-icon">&#x1F4CB;</div>
5321
+ <div class="feat-data-content">
5322
+ <div class="feat-data-label">${esc(d.dataType)}</div>
5323
+ <div class="feat-data-desc">${esc(d.description || '')}${d.retention ? ' Retention: ' + esc(d.retention) + '.' : ''}${d.destination ? ' Destination: ' + esc(d.destination) + '.' : ''}</div>
5324
+ </div>
5325
+ </div>`).join('')}
5326
+ </div>`;
5395
5327
  }
5396
5328
 
5397
- if (unused.length > 0) {
5398
- html += '<h4 style="font-size:12px;color:var(--text-dim);margin:12px 0 8px">Enabled but unused (&gt;15 days)</h4>';
5399
- html += unused.map(f =>
5400
- `<div class="digest-item">
5401
- <div class="digest-item-title">${esc(f.featureName)}</div>
5402
- <div class="digest-item-desc">${f.daysSinceActivity} days since last activity</div>
5403
- </div>`
5404
- ).join('');
5329
+ let reversibilityHtml = '';
5330
+ if (f.reversibilityNote) {
5331
+ reversibilityHtml = `<div class="feat-detail-section">
5332
+ <div class="feat-detail-section-title">How to Undo</div>
5333
+ <div class="feat-reversibility">${esc(f.reversibilityNote)}</div>
5334
+ </div>`;
5405
5335
  }
5406
5336
 
5407
- content.innerHTML = html;
5337
+ content.innerHTML = `
5338
+ <div class="feat-detail-header">
5339
+ <div class="feat-detail-title">
5340
+ <h3>${esc(f.name)}</h3>
5341
+ <span class="feat-detail-category">${esc(CATEGORY_LABELS[f.category] || f.category || '')}</span>
5342
+ <label class="feat-toggle">
5343
+ <input type="checkbox" ${isEnabled ? 'checked' : ''} onchange="toggleFeature('${esc(f.id)}', this.checked)">
5344
+ <span class="feat-toggle-slider"></span>
5345
+ </label>
5346
+ </div>
5347
+ </div>
5348
+ <div class="feat-detail-description">${esc(f.fullDescription || f.oneLiner || f.description || '')}</div>
5349
+ ${dataHtml}
5350
+ ${reversibilityHtml}
5351
+ `;
5352
+ }
5353
+
5354
+ function showFeaturesOverview() {
5355
+ document.getElementById('featDetailView').classList.remove('active');
5356
+ document.getElementById('profileSelectorView').classList.remove('active');
5357
+ const container = document.getElementById('featureCatalog').parentNode;
5358
+ container.querySelector('.features-header').style.display = '';
5359
+ container.querySelector('.features-subtitle').style.display = '';
5360
+ document.getElementById('autonomyCard').style.display = '';
5361
+ document.getElementById('featureCatalog').style.display = '';
5362
+ }
5363
+
5364
+ function showProfileSelector() {
5365
+ // Hide overview, show selector
5366
+ document.getElementById('featureCatalog').parentNode.querySelector('.features-header').style.display = 'none';
5367
+ document.getElementById('featureCatalog').parentNode.querySelector('.features-subtitle').style.display = 'none';
5368
+ document.getElementById('autonomyCard').style.display = 'none';
5369
+ document.getElementById('featureCatalog').style.display = 'none';
5370
+ document.getElementById('profileSelectorView').classList.add('active');
5371
+
5372
+ const currentProfile = featuresData?.autonomy?.profile || featuresData?.autonomy?.autonomyProfile || 'supervised';
5373
+ selectedProfile = currentProfile;
5374
+
5375
+ const selector = document.getElementById('profileSelector');
5376
+ selector.innerHTML = Object.entries(AUTONOMY_PROFILES).map(([key, info]) => {
5377
+ const isSelected = key === currentProfile;
5378
+ const isCurrent = key === currentProfile;
5379
+ const recommended = key === 'supervised' ? '<div class="profile-option-badge recommended">Recommended</div>' : '';
5380
+ return `<div class="profile-option${isSelected ? ' selected' : ''}" onclick="selectProfile('${key}')">
5381
+ <div class="profile-option-name">${info.icon} ${key}</div>
5382
+ <div class="profile-option-desc">${info.desc}</div>
5383
+ ${isCurrent ? '<div class="profile-option-badge" style="background:rgba(255,255,255,0.06);color:var(--text-dim)">Current</div>' : ''}
5384
+ ${recommended}
5385
+ </div>`;
5386
+ }).join('');
5387
+
5388
+ // Load profile history
5389
+ loadProfileHistory();
5408
5390
  }
5409
5391
 
5410
- function renderEventLog() {
5411
- if (!discoveryData) return;
5412
- const log = document.getElementById('eventLog');
5413
- const events = discoveryData.recentEvents || [];
5392
+ function selectProfile(profile) {
5393
+ selectedProfile = profile;
5394
+ document.querySelectorAll('.profile-option').forEach(el => {
5395
+ el.classList.toggle('selected', el.querySelector('.profile-option-name').textContent.toLowerCase().includes(profile));
5396
+ });
5397
+ }
5414
5398
 
5415
- if (events.length === 0) {
5416
- log.innerHTML = '<div style="padding:12px;color:var(--text-dim);text-align:center">No events yet</div>';
5399
+ async function confirmProfileChange() {
5400
+ if (!selectedProfile) return;
5401
+ const currentProfile = featuresData?.autonomy?.profile || featuresData?.autonomy?.autonomyProfile || 'supervised';
5402
+ if (selectedProfile === currentProfile) {
5403
+ showFeaturesOverview();
5417
5404
  return;
5418
5405
  }
5419
5406
 
5420
- log.innerHTML = events.slice(0, 30).map(e => {
5421
- const time = new Date(e.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
5422
- const date = new Date(e.timestamp).toLocaleDateString([], { month: 'short', day: 'numeric' });
5423
- const surfaceLabel = e.surfacedAs ? ` (${e.surfacedAs})` : '';
5424
- return `<div class="event-row">
5425
- <span class="event-time">${esc(date)} ${esc(time)}</span>
5426
- <span class="event-feature">${esc(e.featureId)}</span>
5427
- <span class="event-transition">
5428
- <span class="feature-state-badge ${esc(e.previousState)}">${esc(e.previousState)}</span>
5429
- <span class="event-arrow">&rarr;</span>
5430
- <span class="feature-state-badge ${esc(e.newState)}">${esc(e.newState)}</span>
5431
- ${surfaceLabel}
5432
- </span>
5433
- </div>`;
5434
- }).join('');
5407
+ try {
5408
+ await apiFetch('/autonomy/profile', {
5409
+ method: 'POST',
5410
+ headers: { 'Content-Type': 'application/json' },
5411
+ body: JSON.stringify({ profile: selectedProfile }),
5412
+ });
5413
+ // Refresh autonomy data
5414
+ const autonomy = await apiFetch('/autonomy').catch(() => ({ profile: selectedProfile }));
5415
+ featuresData.autonomy = autonomy;
5416
+ renderAutonomyCard();
5417
+ showToast('Autonomy profile updated to ' + selectedProfile, 'success');
5418
+ showFeaturesOverview();
5419
+ } catch (e) {
5420
+ showToast('Failed to change profile: ' + (e.message || e), 'error');
5421
+ }
5422
+ }
5423
+
5424
+ async function loadProfileHistory() {
5425
+ try {
5426
+ const history = await apiFetch('/autonomy/history');
5427
+ const container = document.getElementById('profileHistory');
5428
+ if (!history || history.length === 0) {
5429
+ container.innerHTML = '';
5430
+ return;
5431
+ }
5432
+ container.innerHTML = `
5433
+ <div class="feat-detail-section-title">Profile History</div>
5434
+ ${history.slice(0, 10).map(h => {
5435
+ const time = h.timestamp ? new Date(h.timestamp).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '';
5436
+ return `<div style="display:flex;gap:10px;padding:6px 0;font-size:12px;border-bottom:1px solid var(--border)">
5437
+ <span style="color:var(--text-dim);min-width:100px">${esc(time)}</span>
5438
+ <span style="color:var(--text)">${esc(h.from || '?')} &rarr; ${esc(h.to || '?')}</span>
5439
+ ${h.reason ? '<span style="color:var(--text-dim);flex:1">' + esc(h.reason) + '</span>' : ''}
5440
+ </div>`;
5441
+ }).join('')}`;
5442
+ } catch (e) {
5443
+ // History not available, that's fine
5444
+ }
5435
5445
  }
5436
5446
 
5437
5447
  // ── Systems Tab ──────────────────────────────────────────────
@@ -5501,7 +5511,7 @@
5501
5511
  <div class="cap-card-metric">${esc(cap.metric)}</div>
5502
5512
  </div>`;
5503
5513
  }).join('');
5504
- capsEl.innerHTML = `<div class="cap-section-title">Active Capabilities</div><div class="cap-grid">${cards}</div>`;
5514
+ capsEl.innerHTML = `<div class="cap-section-title">Active Subsystems</div><div class="cap-grid">${cards}</div>`;
5505
5515
  } else {
5506
5516
  capsEl.innerHTML = '';
5507
5517
  }
@@ -5526,8 +5536,8 @@
5526
5536
  function buildPreviewStats(stats) {
5527
5537
  if (!stats || typeof stats !== 'object') return '';
5528
5538
  const map = {
5529
- recoveries: 'Recoveries', interventions: 'Interventions', activeCases: 'Active',
5530
- coherencePassed: 'Passed', coherenceFailed: 'Failed', memoryPercent: 'Memory',
5539
+ recoveries: 'Recovered', interventions: 'Auto-fixed', activeCases: 'Active',
5540
+ coherencePassed: 'Checks OK', coherenceFailed: 'Issues', memoryPercent: 'Memory',
5531
5541
  enabledJobs: 'Jobs', activeJobSessions: 'Running', queueLength: 'Queued',
5532
5542
  weeklyUsage: 'Weekly', fiveHourRate: '5h Rate',
5533
5543
  topicMappings: 'Topics', totalMessages: 'Messages',
@@ -5612,15 +5622,15 @@
5612
5622
  function buildStatCards(stats, capId) {
5613
5623
  if (!stats || typeof stats !== 'object') return '';
5614
5624
  const map = {
5615
- interventions: 'Interventions', recoveries: 'Recoveries', sessionDeaths: 'Deaths',
5616
- llmOverrides: 'LLM Overrides', activeCases: 'Active Cases', totalTriages: 'Triages',
5625
+ interventions: 'Auto-fixes', recoveries: 'Recovered', sessionDeaths: 'Crashes',
5626
+ llmOverrides: 'AI Decisions', activeCases: 'Active Issues', totalTriages: 'Investigated',
5617
5627
  cooldowns: 'Cooldowns',
5618
- coherenceChecks: 'Checks', coherencePassed: 'Passed', coherenceFailed: 'Failed', coherenceCorrected: 'Corrected',
5628
+ coherenceChecks: 'Consistency Checks', coherencePassed: 'Passed', coherenceFailed: 'Issues Found', coherenceCorrected: 'Auto-corrected',
5619
5629
  memoryPercent: 'Memory %', memoryFreeGB: 'Free GB', memoryTotalGB: 'Total GB',
5620
- trackedProcesses: 'Tracked', orphanProcesses: 'Orphans', totalMemoryMB: 'Memory MB',
5630
+ trackedProcesses: 'Processes', orphanProcesses: 'Cleanup Needed', totalMemoryMB: 'Memory MB',
5621
5631
  totalJobs: 'Total Jobs', enabledJobs: 'Enabled', queueLength: 'Queued', activeJobSessions: 'Running',
5622
5632
  weeklyUsage: 'Weekly %', fiveHourRate: '5h Rate %',
5623
- topicMappings: 'Topics', pendingStalls: 'Pending', totalMessages: 'Messages',
5633
+ topicMappings: 'Topics', pendingStalls: 'Needs Attention', totalMessages: 'Messages',
5624
5634
  proposals: 'Proposals', learnings: 'Learnings', learningsApplied: 'Applied',
5625
5635
  gaps: 'Gaps', actions: 'Actions', overdueActions: 'Overdue',
5626
5636
  };