@dadado/agent-kit-cli 6.3.0 → 6.5.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.
- package/dashboard/dashboard-data.mjs +14 -2
- package/dashboard/dashboard.html +190 -32
- package/dashboard/lib/chat-sessions.d.mts +63 -0
- package/dashboard/lib/chat-sessions.mjs +430 -0
- package/dashboard/{logo-cursor.svg → logo-bare-metal.svg} +1 -1
- package/dashboard/logo-marketplace.svg +1 -1
- package/dashboard/serve.mjs +5 -1
- package/dashboard/social-preview.svg +2 -2
- package/dist/{add-VA2W7I6P.js → add-IT6XFPKT.js} +2 -2
- package/dist/{chunk-NNKOWUWM.js → chunk-236IYK6L.js} +64 -28
- package/dist/{chunk-YRFSTHR5.js → chunk-2OIPMQHA.js} +13 -219
- package/dist/{chunk-JEI6HACW.js → chunk-2TCRKQHD.js} +35 -379
- package/dist/{chunk-U26MRIWN.js → chunk-34BBPQKI.js} +1 -1
- package/dist/{chunk-GUQYNUV4.js → chunk-4XZXM2ZA.js} +1 -1
- package/dist/{chunk-Z7RYIHHX.js → chunk-5LQP4JGH.js} +3 -3
- package/dist/{chunk-6KUSG7BJ.js → chunk-5WSFO35Y.js} +4 -1
- package/dist/{chunk-MAPOQEA2.js → chunk-6RRG3WOE.js} +8 -1
- package/dist/{chunk-OWWFKGKS.js → chunk-EI3VQ7FU.js} +2 -2
- package/dist/{chunk-QTBTKEXK.js → chunk-FM32XR7A.js} +1 -1
- package/dist/{chunk-KVY425NM.js → chunk-GIZNM73K.js} +4 -2
- package/dist/{chunk-MO63KZBP.js → chunk-HKYJG2FJ.js} +8 -6
- package/dist/{chunk-SV2BR2LZ.js → chunk-I2D75F4I.js} +7 -7
- package/dist/{chunk-25J2SMJQ.js → chunk-IN4MQ3P3.js} +1 -1
- package/dist/{chunk-OVOXRH5W.js → chunk-MEZUW3LL.js} +1 -1
- package/dist/chunk-MTLQN57O.js +380 -0
- package/dist/chunk-R6WH5P7R.js +223 -0
- package/dist/{chunk-VQBI3JVS.js → chunk-TNO7GTAJ.js} +2 -2
- package/dist/{chunk-Z7ABX5QI.js → chunk-U7XOLYDG.js} +4 -2
- package/dist/{chunk-WMVMBPCR.js → chunk-WOGBBJCX.js} +1 -1
- package/dist/{chunk-6GYKOHIL.js → chunk-YIOTJQHL.js} +2 -2
- package/dist/{contribute-NLM2BN6Z.js → contribute-V7PV5UMK.js} +3 -3
- package/dist/{countdown-QXJD6TDB.js → countdown-YONBTSJ2.js} +10 -8
- package/dist/{diff-HHG4JEDE.js → diff-GWIHGH65.js} +4 -4
- package/dist/{doctor-TWF2M3S3.js → doctor-FLIQCVFN.js} +8 -8
- package/dist/engine-info-UQNVNOA2.js +89 -0
- package/dist/{ground-control-NMYYIVCV.js → ground-control-6W2AKTKI.js} +5 -4
- package/dist/{guard-EHXF76ID.js → guard-ZZRQW3DJ.js} +107 -8
- package/dist/{hook-VP7FHRSZ.js → hook-2SX2V2T6.js} +10 -4
- package/dist/index.js +26 -22
- package/dist/{init-UOTV4ASJ.js → init-4B2IRH27.js} +11 -11
- package/dist/{install-VCHPXQV3.js → install-3JVTG35O.js} +11 -11
- package/dist/{run-R66G276Q.js → run-AYT6CRHI.js} +12 -9
- package/dist/{run-plan-24Z5K4P3.js → run-plan-5XIISG3W.js} +11 -7
- package/dist/sessions-O7MYEMCT.js +343 -0
- package/dist/{setup-global-M6LKGJHQ.js → setup-global-6JM5XTAT.js} +5 -5
- package/dist/{status-5UKVIKOI.js → status-PMAI7NH3.js} +6 -6
- package/dist/{update-VQBGEUWS.js → update-3PLY4FZA.js} +9 -9
- package/package.json +1 -1
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
import { execFileSync } from "node:child_process";
|
|
8
8
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { dirname, join, resolve } from "node:path";
|
|
10
|
+
import { collectChats } from "./lib/chat-sessions.mjs";
|
|
10
11
|
import { collectGateExitTickets } from "./lib/gate-exit-tickets.mjs";
|
|
11
12
|
import {
|
|
12
13
|
allowlistConfig,
|
|
13
14
|
MAX_STRING,
|
|
14
15
|
parseGitStatusShort,
|
|
16
|
+
resolveBroadcastAuth,
|
|
15
17
|
resolveSnapshotRepoRoot,
|
|
16
18
|
truncateStr,
|
|
17
19
|
} from "./lib/guards.mjs";
|
|
@@ -133,7 +135,7 @@ function redactTerminalOutput(text) {
|
|
|
133
135
|
|
|
134
136
|
const SNAPSHOT = {
|
|
135
137
|
_schema: {
|
|
136
|
-
version: "1.
|
|
138
|
+
version: "1.7.0",
|
|
137
139
|
description: "Mission Control dashboard data model",
|
|
138
140
|
fields: {
|
|
139
141
|
generatedAt: "ISO-8601 timestamp of snapshot generation",
|
|
@@ -154,6 +156,8 @@ const SNAPSHOT = {
|
|
|
154
156
|
"Newest headless agent-kit run / tick logs from <state root>/loop-logs (AGENT_KIT_STATE_ROOT: .cursor by default, or .agent-kit; no ~/.cursor dependency): format, session id, capped last agent text, result, LOOP_TICK_RESULT line, HITL gate ids and reply stamps (1.5.0)",
|
|
155
157
|
gateExit:
|
|
156
158
|
"Pending operator tickets (read-only, never fetches): {marker: absent|ok|invalid, base, tickets[] (stamp, headline, askId, labels, trigger, openedAt, url, branch, branchState unmerged|merged|none, inMarker), skipped[] (latest launcher skip records), truncated} from .cursor/context/gate-exit.json and gate-exit/<stamp> branches; same contract as agent-kit ground-control status (1.6.0)",
|
|
159
|
+
chats:
|
|
160
|
+
"Saved agent chats of this workspace: {withheld, vendors[]} with one envelope per vendor {vendor: claude|codex|cursor, status: ok|unsupported|none, projectDir, sessions[] ({id, title, titleSource: custom|ai|first-prompt|none, modified unix ms, sizeBytes}, newest first, capped at 200), total}; same contract as agent-kit sessions --json. withheld is true (vendors empty) in broadcast mode: chat titles never leave the machine (1.7.0)",
|
|
157
161
|
processes:
|
|
158
162
|
"Running process snapshots (node, serve.mjs, git operations) with elapsed time and a generated narration per process",
|
|
159
163
|
skills: "Available skills discovered in .cursor/skills/",
|
|
@@ -162,7 +166,7 @@ const SNAPSHOT = {
|
|
|
162
166
|
},
|
|
163
167
|
},
|
|
164
168
|
generatedAt: new Date().toISOString(),
|
|
165
|
-
dashboardDataVersion: "1.
|
|
169
|
+
dashboardDataVersion: "1.7.0",
|
|
166
170
|
plans: [],
|
|
167
171
|
system: {
|
|
168
172
|
repoRoot: ROOT,
|
|
@@ -175,6 +179,7 @@ const SNAPSHOT = {
|
|
|
175
179
|
terminals: [],
|
|
176
180
|
runLogs: [],
|
|
177
181
|
gateExit: { marker: "absent", base: "staging", tickets: [], skipped: [], truncated: false },
|
|
182
|
+
chats: { withheld: false, vendors: [] },
|
|
178
183
|
processes: [],
|
|
179
184
|
skills: [],
|
|
180
185
|
health: { status: "ok", checks: [] },
|
|
@@ -789,6 +794,13 @@ if (withinSnapshotBudget(200)) {
|
|
|
789
794
|
SNAPSHOT.gateExit = collectGateExitTickets(ROOT, { redact: redactTerminalOutput });
|
|
790
795
|
}
|
|
791
796
|
|
|
797
|
+
// 7d. Saved agent chats (~/.claude/projects/<slug>/<uuid>.jsonl), read-only. Titles are private:
|
|
798
|
+
// a broadcast (non-loopback, token-gated) server never gets them (serve.mjs strips them too).
|
|
799
|
+
SNAPSHOT.chats = collectChats({
|
|
800
|
+
cwd: ROOT,
|
|
801
|
+
withhold: resolveBroadcastAuth(process.env).broadcast !== false,
|
|
802
|
+
});
|
|
803
|
+
|
|
792
804
|
// 8. Config
|
|
793
805
|
const configPath = join(ROOT, ".cursor", "context", "config.json");
|
|
794
806
|
if (existsSync(configPath)) {
|
package/dashboard/dashboard.html
CHANGED
|
@@ -5,19 +5,25 @@
|
|
|
5
5
|
<meta charset="UTF-8">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>Mission Control</title>
|
|
8
|
-
<link rel="icon" type="image/svg+xml" id="favicon" href="/logo.svg">
|
|
8
|
+
<link rel="icon" type="image/svg+xml" id="favicon" href="/logo-bare-metal.svg">
|
|
9
9
|
<script>
|
|
10
|
-
/* Restore dashboard skin before first paint (namespaced local preference).
|
|
10
|
+
/* Restore dashboard skin before first paint (namespaced local preference).
|
|
11
|
+
Default Bare Metal; a stored 'cursor' (the pre-rename id) is rewritten to
|
|
12
|
+
'bare-metal'. Keep in sync with normalizeDashboardSkin below. */
|
|
11
13
|
(function () {
|
|
12
14
|
var key = 'agent-kit:dashboard-skin';
|
|
13
|
-
var skin = '
|
|
15
|
+
var skin = 'bare-metal';
|
|
14
16
|
try {
|
|
15
17
|
var stored = localStorage.getItem(key);
|
|
16
|
-
if (stored === 'cursor'
|
|
18
|
+
if (stored === 'cursor') {
|
|
19
|
+
localStorage.setItem(key, 'bare-metal');
|
|
20
|
+
} else if (stored === 'bare-metal' || stored === 'legacy') {
|
|
21
|
+
skin = stored;
|
|
22
|
+
}
|
|
17
23
|
} catch (e) { /* private mode / blocked storage */ }
|
|
18
24
|
document.documentElement.setAttribute('data-dashboard-skin', skin);
|
|
19
25
|
var fav = document.getElementById('favicon');
|
|
20
|
-
if (fav) fav.setAttribute('href', skin === '
|
|
26
|
+
if (fav) fav.setAttribute('href', skin === 'legacy' ? '/logo.svg' : '/logo-bare-metal.svg');
|
|
21
27
|
})();
|
|
22
28
|
/* Restore Crew Monitor row density before first paint; auto-pick by viewport
|
|
23
29
|
when the operator has no stored preference. Same namespaced-key + blocked-
|
|
@@ -38,18 +44,10 @@
|
|
|
38
44
|
<style>
|
|
39
45
|
/* ===== Reset & Base ===== */
|
|
40
46
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
41
|
-
/*
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
--bg-secondary: #111620;
|
|
46
|
-
--bg-card: #161c2a;
|
|
47
|
-
--bg-card-hover: #1b2335;
|
|
48
|
-
--border: #1e2a3a;
|
|
49
|
-
--border-active: #2a3a5a;
|
|
50
|
-
--text-primary: #e2e8f0;
|
|
51
|
-
--text-secondary: #8899aa;
|
|
52
|
-
--text-muted: #6d8094; /* landing uses #7f93a8 (AA over marketing gradient); see docs/design-system.md */
|
|
47
|
+
/* Shared tokens (both Interface Skins): semantic colors and structure.
|
|
48
|
+
A skin overrides only the 9 surface variables below; the token sets are
|
|
49
|
+
published in docs/contracts/mission-control-skins.json. */
|
|
50
|
+
:root {
|
|
53
51
|
--green: #22c55e;
|
|
54
52
|
--green-bg: rgba(34,197,94,0.12);
|
|
55
53
|
--yellow: #eab308;
|
|
@@ -112,8 +110,9 @@ html[data-dashboard-skin="legacy"] {
|
|
|
112
110
|
/* Shared status / badge circles (#statusDot and .dot base). */
|
|
113
111
|
--mc-status-dot-size: 7px;
|
|
114
112
|
}
|
|
115
|
-
/*
|
|
116
|
-
|
|
113
|
+
/* Bare Metal skin (default): neutral dark surfaces. */
|
|
114
|
+
:root,
|
|
115
|
+
html[data-dashboard-skin="bare-metal"] {
|
|
117
116
|
--bg-primary: #141414;
|
|
118
117
|
--bg-secondary: #181818;
|
|
119
118
|
--bg-card: #1c1c1c;
|
|
@@ -122,7 +121,19 @@ html[data-dashboard-skin="cursor"] {
|
|
|
122
121
|
--border-active: #3a3a3a;
|
|
123
122
|
--text-primary: #e4e4e4;
|
|
124
123
|
--text-secondary: #a0a0a0;
|
|
125
|
-
--text-muted: #
|
|
124
|
+
--text-muted: #8b8b8b; /* >= 4.5:1 (WCAG AA) on every bg-* surface incl. hover; see docs/design-system.md */
|
|
125
|
+
}
|
|
126
|
+
/* Legacy skin: the original blue-slate Mission Control surfaces. */
|
|
127
|
+
html[data-dashboard-skin="legacy"] {
|
|
128
|
+
--bg-primary: #0b0e14;
|
|
129
|
+
--bg-secondary: #111620;
|
|
130
|
+
--bg-card: #161c2a;
|
|
131
|
+
--bg-card-hover: #1b2335;
|
|
132
|
+
--border: #1e2a3a;
|
|
133
|
+
--border-active: #2a3a5a;
|
|
134
|
+
--text-primary: #e2e8f0;
|
|
135
|
+
--text-secondary: #8899aa;
|
|
136
|
+
--text-muted: #7f93a8; /* same as the landing; >= 4.5:1 (WCAG AA) on every bg-* surface incl. hover; see docs/design-system.md */
|
|
126
137
|
}
|
|
127
138
|
html, body {
|
|
128
139
|
height: 100%;
|
|
@@ -934,6 +945,31 @@ body.mc-fullscreen .top-tabs-row {
|
|
|
934
945
|
}
|
|
935
946
|
.terminal-item:hover { background: var(--bg-card-hover); }
|
|
936
947
|
|
|
948
|
+
/* ===== Chats (saved agent sessions) ===== */
|
|
949
|
+
.chat-list { display: flex; flex-direction: column; gap: 4px; }
|
|
950
|
+
.chat-item {
|
|
951
|
+
display: flex;
|
|
952
|
+
align-items: center;
|
|
953
|
+
gap: 10px;
|
|
954
|
+
padding: 6px 10px;
|
|
955
|
+
border-radius: 6px;
|
|
956
|
+
font-size: 12px;
|
|
957
|
+
color: var(--text-secondary);
|
|
958
|
+
}
|
|
959
|
+
.chat-item:hover { background: var(--bg-card-hover); }
|
|
960
|
+
.chat-title {
|
|
961
|
+
flex: 1;
|
|
962
|
+
min-width: 0;
|
|
963
|
+
overflow: hidden;
|
|
964
|
+
text-overflow: ellipsis;
|
|
965
|
+
white-space: nowrap;
|
|
966
|
+
color: var(--text-primary);
|
|
967
|
+
}
|
|
968
|
+
.chat-title[data-untitled="true"] { color: var(--text-muted); font-style: italic; }
|
|
969
|
+
.chat-age,
|
|
970
|
+
.chat-id { color: var(--text-muted); font-family: var(--mc-font-mono); white-space: nowrap; }
|
|
971
|
+
.chat-note { font-size: 12px; color: var(--text-muted); margin: 8px 10px 0; }
|
|
972
|
+
|
|
937
973
|
/* ===== Healthcenter ===== */
|
|
938
974
|
.healthcenter {
|
|
939
975
|
display: flex;
|
|
@@ -1230,7 +1266,7 @@ body.mc-fullscreen .top-tabs-row {
|
|
|
1230
1266
|
.git-hygiene-paths {
|
|
1231
1267
|
font-family: var(--mc-font-mono);
|
|
1232
1268
|
font-size: 11px;
|
|
1233
|
-
color: var(--text-
|
|
1269
|
+
color: var(--text-secondary); /* muted fails AA on the --yellow-bg tint */
|
|
1234
1270
|
margin-top: 4px;
|
|
1235
1271
|
word-break: break-all;
|
|
1236
1272
|
}
|
|
@@ -3649,7 +3685,7 @@ html[data-monitor-density="comfortable"] .live-activity-feed .monitor-row .monit
|
|
|
3649
3685
|
<!-- Header: identity + one transport/freshness signal + refresh -->
|
|
3650
3686
|
<div class="header">
|
|
3651
3687
|
<button type="button" class="header-brand" id="headerBrand" onclick="return goHome()" aria-label="Home, Overview" title="Home, Overview">
|
|
3652
|
-
<img class="logo" id="headerLogo" src="/logo.svg" width="28" height="28" alt="" aria-hidden="true"> Mission Control
|
|
3688
|
+
<img class="logo" id="headerLogo" src="/logo-bare-metal.svg" width="28" height="28" alt="" aria-hidden="true"> Mission Control
|
|
3653
3689
|
<span class="header-workspace" id="headerWorkspace" hidden></span>
|
|
3654
3690
|
</button>
|
|
3655
3691
|
<span class="header-version" id="version" hidden>v—</span>
|
|
@@ -3711,6 +3747,10 @@ html[data-monitor-density="comfortable"] .live-activity-feed .monitor-row .monit
|
|
|
3711
3747
|
Terminals
|
|
3712
3748
|
<span class="top-tab-badge" id="navTerminalsBadge">0</span>
|
|
3713
3749
|
</a>
|
|
3750
|
+
<a class="top-tab nav-more-item" role="menuitem" href="#" data-section="chats" tabindex="0" onclick="return showSection('chats')" aria-label="Chats section">
|
|
3751
|
+
Chats
|
|
3752
|
+
<span class="top-tab-badge" id="navChatsBadge">0</span>
|
|
3753
|
+
</a>
|
|
3714
3754
|
<a class="top-tab nav-more-item" role="menuitem" href="#" data-section="processes" tabindex="0" onclick="return showSection('processes')" aria-label="Processes section">
|
|
3715
3755
|
Processes
|
|
3716
3756
|
<span class="top-tab-badge" id="navProcessesBadge">0</span>
|
|
@@ -3721,11 +3761,11 @@ html[data-monitor-density="comfortable"] .live-activity-feed .monitor-row .monit
|
|
|
3721
3761
|
<div class="nav-more-separator" role="separator"></div>
|
|
3722
3762
|
<div class="nav-more-group" role="group" aria-labelledby="navSkinsLabel">
|
|
3723
3763
|
<div class="nav-more-group-label" id="navSkinsLabel" data-icon="skins">Skins</div>
|
|
3724
|
-
<button type="button" class="nav-more-item" role="menuitemradio" aria-checked="true" data-dashboard-skin-option="
|
|
3725
|
-
|
|
3764
|
+
<button type="button" class="nav-more-item" role="menuitemradio" aria-checked="true" data-dashboard-skin-option="bare-metal" tabindex="0" onclick="return setDashboardSkin('bare-metal')">
|
|
3765
|
+
Bare Metal
|
|
3726
3766
|
</button>
|
|
3727
|
-
<button type="button" class="nav-more-item" role="menuitemradio" aria-checked="false" data-dashboard-skin-option="
|
|
3728
|
-
|
|
3767
|
+
<button type="button" class="nav-more-item" role="menuitemradio" aria-checked="false" data-dashboard-skin-option="legacy" tabindex="0" onclick="return setDashboardSkin('legacy')">
|
|
3768
|
+
Legacy
|
|
3729
3769
|
</button>
|
|
3730
3770
|
</div>
|
|
3731
3771
|
</div>
|
|
@@ -3882,6 +3922,7 @@ const SECTION_IDS = [
|
|
|
3882
3922
|
'devops',
|
|
3883
3923
|
'memory',
|
|
3884
3924
|
'terminals',
|
|
3925
|
+
'chats',
|
|
3885
3926
|
'processes',
|
|
3886
3927
|
'config',
|
|
3887
3928
|
];
|
|
@@ -4061,17 +4102,29 @@ function goCockpitAnchor(anchorId) {
|
|
|
4061
4102
|
|
|
4062
4103
|
// ===== Dashboard visual skins (More menu; local preference only) =====
|
|
4063
4104
|
const DASHBOARD_SKIN_KEY = 'agent-kit:dashboard-skin';
|
|
4064
|
-
const DASHBOARD_SKINS = ['
|
|
4105
|
+
const DASHBOARD_SKINS = ['bare-metal', 'legacy'];
|
|
4106
|
+
const DASHBOARD_SKIN_DEFAULT = 'bare-metal';
|
|
4107
|
+
/** Pre-rename ids still found in browser storage. */
|
|
4108
|
+
const DASHBOARD_SKIN_ALIASES = { cursor: 'bare-metal' };
|
|
4065
4109
|
|
|
4066
4110
|
function normalizeDashboardSkin(skin) {
|
|
4067
|
-
|
|
4111
|
+
if (Object.prototype.hasOwnProperty.call(DASHBOARD_SKIN_ALIASES, skin)) {
|
|
4112
|
+
return DASHBOARD_SKIN_ALIASES[skin];
|
|
4113
|
+
}
|
|
4114
|
+
return DASHBOARD_SKINS.includes(skin) ? skin : DASHBOARD_SKIN_DEFAULT;
|
|
4068
4115
|
}
|
|
4069
4116
|
|
|
4070
4117
|
function getStoredDashboardSkin() {
|
|
4071
4118
|
try {
|
|
4072
|
-
|
|
4119
|
+
const stored = localStorage.getItem(DASHBOARD_SKIN_KEY);
|
|
4120
|
+
const skin = normalizeDashboardSkin(stored);
|
|
4121
|
+
// Rewrite an aliased value (e.g. 'cursor') so storage holds the current id.
|
|
4122
|
+
if (stored !== null && stored !== skin && Object.prototype.hasOwnProperty.call(DASHBOARD_SKIN_ALIASES, stored)) {
|
|
4123
|
+
localStorage.setItem(DASHBOARD_SKIN_KEY, skin);
|
|
4124
|
+
}
|
|
4125
|
+
return skin;
|
|
4073
4126
|
} catch (e) {
|
|
4074
|
-
return
|
|
4127
|
+
return DASHBOARD_SKIN_DEFAULT;
|
|
4075
4128
|
}
|
|
4076
4129
|
}
|
|
4077
4130
|
|
|
@@ -4084,7 +4137,7 @@ function applyDashboardSkin(skin, opts) {
|
|
|
4084
4137
|
el.setAttribute('aria-checked', selected ? 'true' : 'false');
|
|
4085
4138
|
el.classList.toggle('active', selected);
|
|
4086
4139
|
});
|
|
4087
|
-
const logoSrc = next === '
|
|
4140
|
+
const logoSrc = next === 'legacy' ? '/logo.svg' : '/logo-bare-metal.svg';
|
|
4088
4141
|
const headerLogo = document.getElementById('headerLogo');
|
|
4089
4142
|
if (headerLogo) headerLogo.setAttribute('src', logoSrc);
|
|
4090
4143
|
const favicon = document.getElementById('favicon');
|
|
@@ -6321,6 +6374,98 @@ function showTerminalPopup(content, title) {
|
|
|
6321
6374
|
document.body.appendChild(overlay);
|
|
6322
6375
|
}
|
|
6323
6376
|
|
|
6377
|
+
/**
|
|
6378
|
+
* POSIX single-quote for a pasted shell command.
|
|
6379
|
+
* Mirror of `shellQuote` in dashboard/lib/chat-sessions.mjs (inline HTML cannot import it).
|
|
6380
|
+
*/
|
|
6381
|
+
function chatShellQuote(value) {
|
|
6382
|
+
const s = String(value ?? '');
|
|
6383
|
+
if (/^[A-Za-z0-9_./-]+$/.test(s)) return s;
|
|
6384
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
6385
|
+
}
|
|
6386
|
+
|
|
6387
|
+
/** Mirror of `claudeResumeCommand` in dashboard/lib/chat-sessions.mjs. */
|
|
6388
|
+
function chatResumeCommand(workspace, id) {
|
|
6389
|
+
return `cd ${chatShellQuote(workspace)} && claude --resume ${chatShellQuote(id)}`;
|
|
6390
|
+
}
|
|
6391
|
+
|
|
6392
|
+
/** Claude Code envelope of the snapshot `chats` field (null when absent or withheld). */
|
|
6393
|
+
function claudeChatsEnvelope(d) {
|
|
6394
|
+
const vendors = Array.isArray(d?.chats?.vendors) ? d.chats.vendors : [];
|
|
6395
|
+
return vendors.find((v) => v && v.vendor === 'claude') || null;
|
|
6396
|
+
}
|
|
6397
|
+
|
|
6398
|
+
function chatsTotal(d) {
|
|
6399
|
+
const claude = claudeChatsEnvelope(d);
|
|
6400
|
+
return claude && claude.status === 'ok' && Number.isFinite(claude.total) ? claude.total : 0;
|
|
6401
|
+
}
|
|
6402
|
+
|
|
6403
|
+
/** Chats section body. Titles are user content: escapeHtml / escapeAttr only. */
|
|
6404
|
+
function renderChatsSection(d) {
|
|
6405
|
+
const chats = d?.chats || null;
|
|
6406
|
+
const workspace = d?.system?.repoRoot || '';
|
|
6407
|
+
const claude = claudeChatsEnvelope(d);
|
|
6408
|
+
const sessions = claude && claude.status === 'ok' && Array.isArray(claude.sessions) ? claude.sessions : [];
|
|
6409
|
+
const total = chatsTotal(d);
|
|
6410
|
+
const unsupported = (Array.isArray(chats?.vendors) ? chats.vendors : [])
|
|
6411
|
+
.filter((v) => v && v.status === 'unsupported')
|
|
6412
|
+
.map((v) => ({ codex: 'Codex', cursor: 'Cursor' }[v.vendor] || String(v.vendor || '')));
|
|
6413
|
+
let subtitle = `${total} saved`;
|
|
6414
|
+
if (sessions.length < total) subtitle = `${sessions.length} newest of ${total}`;
|
|
6415
|
+
let body;
|
|
6416
|
+
if (chats?.withheld === true) {
|
|
6417
|
+
subtitle = 'hidden';
|
|
6418
|
+
body = renderEmptyStateCta({
|
|
6419
|
+
iconKind: 'chats',
|
|
6420
|
+
headline: 'Chats stay private',
|
|
6421
|
+
support: 'This Mission Control is broadcast on the network, so saved chat titles are left out. Open it on this machine to see them.',
|
|
6422
|
+
});
|
|
6423
|
+
} else if (sessions.length === 0) {
|
|
6424
|
+
body = renderEmptyStateCta({
|
|
6425
|
+
iconKind: 'chats',
|
|
6426
|
+
headline: 'No saved chats',
|
|
6427
|
+
support: 'The agent CLI saves each session per folder. Start claude in this workspace folder and it shows up here, ready to resume.',
|
|
6428
|
+
});
|
|
6429
|
+
} else {
|
|
6430
|
+
body = `<div class="chat-list">${sessions.map((s) => {
|
|
6431
|
+
const id = String(s.id || '');
|
|
6432
|
+
const untitled = !s.title;
|
|
6433
|
+
const title = untitled ? 'Untitled chat' : String(s.title);
|
|
6434
|
+
const modifiedIso = Number.isFinite(s.modified) ? new Date(s.modified).toISOString() : '';
|
|
6435
|
+
const cmd = chatResumeCommand(workspace, id);
|
|
6436
|
+
return `
|
|
6437
|
+
<div class="chat-item">
|
|
6438
|
+
<span class="chat-title"${untitled ? ' data-untitled="true"' : ''} title="${escapeAttr(title)}">${escapeHtml(title)}</span>
|
|
6439
|
+
<span class="chat-age" title="${escapeAttr(modifiedIso)}">${escapeHtml(relativeTime(modifiedIso))}</span>
|
|
6440
|
+
<span class="chat-id" title="${escapeAttr(id)}">${escapeHtml(id.slice(0, 8))}</span>
|
|
6441
|
+
<button type="button" class="process-copy-btn chat-resume-btn" data-chat-resume="${escapeAttr(cmd)}" aria-label="${escapeAttr(`Copy the resume command for chat ${title}`)}" title="${escapeAttr(copyActionTitle('resume command', 'terminal'))}">Copy resume</button>
|
|
6442
|
+
</div>`;
|
|
6443
|
+
}).join('')}</div>`;
|
|
6444
|
+
}
|
|
6445
|
+
const note = unsupported.length
|
|
6446
|
+
? `<p class="chat-note">${escapeHtml(unsupported.map((name) => `${name}: not supported yet`).join(' \u00b7 '))}</p>`
|
|
6447
|
+
: '';
|
|
6448
|
+
return `
|
|
6449
|
+
<div class="content-section" id="section-chats">
|
|
6450
|
+
<div class="section-title">
|
|
6451
|
+
Chats
|
|
6452
|
+
<span class="section-subtitle">${escapeHtml(subtitle)}</span>
|
|
6453
|
+
</div>
|
|
6454
|
+
${body}
|
|
6455
|
+
${note}
|
|
6456
|
+
</div>
|
|
6457
|
+
`;
|
|
6458
|
+
}
|
|
6459
|
+
|
|
6460
|
+
function wireChatResumeButtons() {
|
|
6461
|
+
document.querySelectorAll('.chat-resume-btn[data-chat-resume]').forEach((btn) => {
|
|
6462
|
+
btn.addEventListener('click', (e) => {
|
|
6463
|
+
e.stopPropagation();
|
|
6464
|
+
copyForPaste(btn.dataset.chatResume, 'resume command', 'terminal');
|
|
6465
|
+
});
|
|
6466
|
+
});
|
|
6467
|
+
}
|
|
6468
|
+
|
|
6324
6469
|
function wireTerminalExpandButtons() {
|
|
6325
6470
|
document.querySelectorAll('.terminal-expand-btn[data-terminal-id]').forEach((btn) => {
|
|
6326
6471
|
btn.addEventListener('click', (e) => {
|
|
@@ -7107,7 +7252,7 @@ function nowMetaIconSvg(kind, opts) {
|
|
|
7107
7252
|
* features >= 1 unit. Detail that cannot meet the floor is drawn filled
|
|
7108
7253
|
* (fill="currentColor" stroke="none"), never as a sub-stroke stroked shape.
|
|
7109
7254
|
* Static path markup only; never concatenate untrusted text into the SVG.
|
|
7110
|
-
* @param {'current-mission'|'monitor'|'field-report'|'checklist'|'more-sections'|'overview'|'plans'|'activity'|'agents'|'skills'|'skins'|'commands'|'health'|'git'|'devops'|'memory'|'terminals'|'processes'|'config'} kind
|
|
7255
|
+
* @param {'current-mission'|'monitor'|'field-report'|'checklist'|'more-sections'|'overview'|'plans'|'activity'|'agents'|'skills'|'skins'|'commands'|'health'|'git'|'devops'|'memory'|'terminals'|'chats'|'processes'|'config'} kind
|
|
7111
7256
|
* @param {{ decorative?: boolean }} [opts] decorative true (default): aria-hidden next to a visible label.
|
|
7112
7257
|
* decorative false: role=img + aria-label + title for icon-only controls (e.g. more-sections).
|
|
7113
7258
|
*/
|
|
@@ -7132,6 +7277,7 @@ function spaceIconSvg(kind, opts) {
|
|
|
7132
7277
|
devops: 'DevOps',
|
|
7133
7278
|
memory: 'Memory',
|
|
7134
7279
|
terminals: 'Terminals',
|
|
7280
|
+
chats: 'Chats',
|
|
7135
7281
|
processes: 'Processes',
|
|
7136
7282
|
config: 'Config',
|
|
7137
7283
|
};
|
|
@@ -7212,6 +7358,9 @@ function spaceIconSvg(kind, opts) {
|
|
|
7212
7358
|
terminals:
|
|
7213
7359
|
'<path d="M1.5 3.5h13a1 1 0 011 1v7a1 1 0 01-1 1H1.5a1 1 0 01-1-1v-7a1 1 0 011-1z"/>' +
|
|
7214
7360
|
'<path d="M5.5 12.5v2M10.5 12.5v2M4 14.5h8"/>',
|
|
7361
|
+
// Speech bubble (chats) — Feather message-square on the 16 grid
|
|
7362
|
+
chats:
|
|
7363
|
+
'<path d="M14 10a1.5 1.5 0 01-1.5 1.5H5L2 14.5v-11A1.5 1.5 0 013.5 2h9A1.5 1.5 0 0114 3.5z"/>',
|
|
7215
7364
|
// Layers (processes)
|
|
7216
7365
|
processes:
|
|
7217
7366
|
'<path d="M8 2.5l6 3-6 3-6-3 6-3z"/>' +
|
|
@@ -8158,6 +8307,11 @@ function renderUnsafe() {
|
|
|
8158
8307
|
const navTerminalsBadge = document.getElementById('navTerminalsBadge');
|
|
8159
8308
|
navTerminalsBadge.textContent = d.terminals.length;
|
|
8160
8309
|
navTerminalsBadge.className = 'top-tab-badge';
|
|
8310
|
+
const navChatsBadge = document.getElementById('navChatsBadge');
|
|
8311
|
+
if (navChatsBadge) {
|
|
8312
|
+
navChatsBadge.textContent = chatsTotal(d);
|
|
8313
|
+
navChatsBadge.className = 'top-tab-badge';
|
|
8314
|
+
}
|
|
8161
8315
|
const navProcessesBadge = document.getElementById('navProcessesBadge');
|
|
8162
8316
|
navProcessesBadge.textContent = d.processes.length;
|
|
8163
8317
|
navProcessesBadge.className = 'top-tab-badge';
|
|
@@ -8732,6 +8886,9 @@ function renderUnsafe() {
|
|
|
8732
8886
|
</div>
|
|
8733
8887
|
`);
|
|
8734
8888
|
|
|
8889
|
+
// ===== Chats =====
|
|
8890
|
+
parts.push(renderChatsSection(d));
|
|
8891
|
+
|
|
8735
8892
|
// ===== Processes =====
|
|
8736
8893
|
const processes = d.processes || [];
|
|
8737
8894
|
// Detached audit-session visibility (phase3): host-wide agent-kit-audit-* PTYs.
|
|
@@ -8894,6 +9051,7 @@ function renderUnsafe() {
|
|
|
8894
9051
|
contentEl.innerHTML = parts.join('');
|
|
8895
9052
|
|
|
8896
9053
|
wireTerminalExpandButtons();
|
|
9054
|
+
wireChatResumeButtons();
|
|
8897
9055
|
|
|
8898
9056
|
updateTransportChrome();
|
|
8899
9057
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const CHAT_SESSIONS_CAP: number;
|
|
2
|
+
export const CHAT_TITLE_MAX: number;
|
|
3
|
+
export const CHAT_NAMED_TITLE_MAX: number;
|
|
4
|
+
export const CHAT_FULL_SCAN_MAX_BYTES: number;
|
|
5
|
+
export const CHAT_HEAD_BYTES: number;
|
|
6
|
+
export const CHAT_TAIL_BYTES: number;
|
|
7
|
+
export const CHAT_SCAN_BUDGET_BYTES: number;
|
|
8
|
+
export const CHAT_VENDORS: readonly ["claude", "codex", "cursor"];
|
|
9
|
+
|
|
10
|
+
export type ChatVendor = "claude" | "codex" | "cursor";
|
|
11
|
+
export type ChatTitleSource = "custom" | "ai" | "first-prompt" | "none";
|
|
12
|
+
|
|
13
|
+
export interface ChatSession {
|
|
14
|
+
id: string;
|
|
15
|
+
title: string | null;
|
|
16
|
+
titleSource: ChatTitleSource;
|
|
17
|
+
/** Last write, unix milliseconds. */
|
|
18
|
+
modified: number;
|
|
19
|
+
sizeBytes: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ChatSessionsEnvelope {
|
|
23
|
+
vendor: ChatVendor;
|
|
24
|
+
status: "ok" | "unsupported" | "none";
|
|
25
|
+
projectDir: string | null;
|
|
26
|
+
sessions: ChatSession[];
|
|
27
|
+
total: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ChatsSnapshot {
|
|
31
|
+
withheld: boolean;
|
|
32
|
+
vendors: ChatSessionsEnvelope[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ChatTitle {
|
|
36
|
+
title: string | null;
|
|
37
|
+
titleSource: ChatTitleSource;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ListChatSessionsOptions {
|
|
41
|
+
cwd: string;
|
|
42
|
+
home?: string | null;
|
|
43
|
+
cap?: number;
|
|
44
|
+
/** Bytes read in full before older transcripts fall back to a small head + tail window. */
|
|
45
|
+
scanBudgetBytes?: number;
|
|
46
|
+
cache?: Map<string, ChatTitle>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function claudeProjectSlug(cwd: string): string;
|
|
50
|
+
export function claudeProjectDir(cwd: string, opts?: { home?: string | null }): string | null;
|
|
51
|
+
export function cleanChatTitle(value: unknown, max?: number): string;
|
|
52
|
+
export function readChatTitle(
|
|
53
|
+
filePath: string,
|
|
54
|
+
window?: { fullScanMaxBytes?: number; headBytes?: number; tailBytes?: number },
|
|
55
|
+
): ChatTitle;
|
|
56
|
+
export function listClaudeSessions(opts: ListChatSessionsOptions): ChatSessionsEnvelope;
|
|
57
|
+
export function listChatSessions(opts: ListChatSessionsOptions): ChatSessionsEnvelope[];
|
|
58
|
+
export function collectChats(opts: ListChatSessionsOptions & { withhold?: boolean }): ChatsSnapshot;
|
|
59
|
+
export function withholdChats<T extends Record<string, unknown>>(snapshot: T): T;
|
|
60
|
+
export function withholdChatsFromPayload(payload: string): string;
|
|
61
|
+
export function shellQuote(value: string): string;
|
|
62
|
+
export function claudeResumeCommand(workspace: string, id: string): string;
|
|
63
|
+
export function formatChatAge(modifiedMs: number, nowMs?: number): string;
|