clay-server 2.47.0-beta.1 → 2.47.0-beta.2
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/lib/daemon.js +38 -8
- package/lib/kiro-defaults.js +22 -0
- package/lib/project-connection.js +1 -0
- package/lib/project-notifications.js +1 -1
- package/lib/project-sessions.js +13 -2
- package/lib/project.js +31 -7
- package/lib/public/app.js +6 -0
- package/lib/public/css/mobile-nav.css +53 -9
- package/lib/public/css/sidebar.css +24 -1
- package/lib/public/css/tui-attention.css +4 -63
- package/lib/public/index.html +4 -0
- package/lib/public/kiro-avatar.svg +14 -0
- package/lib/public/modules/app-messages.js +11 -10
- package/lib/public/modules/app-panels.js +4 -1
- package/lib/public/modules/app-rendering.js +12 -0
- package/lib/public/modules/input.js +5 -3
- package/lib/public/modules/mate-sidebar.js +3 -3
- package/lib/public/modules/session-tui-view.js +4 -40
- package/lib/public/modules/sidebar-mates.js +1 -1
- package/lib/public/modules/sidebar-mobile.js +56 -23
- package/lib/public/modules/sidebar-sessions.js +126 -63
- package/lib/public/modules/tools.js +1 -1
- package/lib/sdk-bridge.js +50 -5
- package/lib/sdk-message-processor.js +4 -2
- package/lib/server.js +4 -0
- package/lib/users-preferences.js +3 -1
- package/lib/yoke/adapters/kiro.js +1156 -0
- package/lib/yoke/index.js +56 -4
- package/lib/yoke/kiro-acp-server.js +328 -0
- package/package.json +2 -1
package/lib/daemon.js
CHANGED
|
@@ -648,6 +648,29 @@ var relay = createServer({
|
|
|
648
648
|
}
|
|
649
649
|
return { ok: false, error: "Project not found" };
|
|
650
650
|
},
|
|
651
|
+
onGetProjectLastVendor: function (slug) {
|
|
652
|
+
for (var i = 0; i < config.projects.length; i++) {
|
|
653
|
+
if (config.projects[i].slug === slug) {
|
|
654
|
+
return { vendor: config.projects[i].lastVendor || null };
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return { vendor: null };
|
|
658
|
+
},
|
|
659
|
+
onSetProjectLastVendor: function (slug, vendor) {
|
|
660
|
+
for (var i = 0; i < config.projects.length; i++) {
|
|
661
|
+
if (config.projects[i].slug === slug) {
|
|
662
|
+
if (config.projects[i].lastVendor === vendor) return { ok: true };
|
|
663
|
+
if (vendor) {
|
|
664
|
+
config.projects[i].lastVendor = vendor;
|
|
665
|
+
} else {
|
|
666
|
+
delete config.projects[i].lastVendor;
|
|
667
|
+
}
|
|
668
|
+
saveConfig(config);
|
|
669
|
+
return { ok: true };
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return { ok: false, error: "Project not found" };
|
|
673
|
+
},
|
|
651
674
|
onGetProjectMcpServers: function (slug) {
|
|
652
675
|
for (var i = 0; i < config.projects.length; i++) {
|
|
653
676
|
if (config.projects[i].slug === slug) {
|
|
@@ -968,21 +991,28 @@ var relay = createServer({
|
|
|
968
991
|
},
|
|
969
992
|
});
|
|
970
993
|
|
|
971
|
-
|
|
972
|
-
|
|
994
|
+
// Reclaim idle out-of-process agent runtimes. Any adapter that owns a child
|
|
995
|
+
// process may implement the optional shutdownIfIdle(ms); adapters without one
|
|
996
|
+
// (Claude, which runs in-process or under sdk-worker) are simply skipped.
|
|
997
|
+
// The CLAY_CODEX_* names are kept as aliases for backwards compatibility.
|
|
998
|
+
var IDLE_MS = Number(process.env.CLAY_ADAPTER_IDLE_MS) || Number(process.env.CLAY_CODEX_IDLE_MS) || 5 * 60 * 1000;
|
|
999
|
+
var REAP_MS = Number(process.env.CLAY_ADAPTER_REAPER_MS) || Number(process.env.CLAY_CODEX_REAPER_MS) || 60 * 1000;
|
|
973
1000
|
var reaperHandle = setInterval(function () {
|
|
974
1001
|
if (!relay || typeof relay.forEachProject !== "function") return;
|
|
975
1002
|
relay.forEachProject(function (ctx) {
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1003
|
+
if (!ctx || !ctx.adapters) return;
|
|
1004
|
+
Object.keys(ctx.adapters).forEach(function (vendor) {
|
|
1005
|
+
try {
|
|
1006
|
+
var adapter = ctx.adapters[vendor];
|
|
1007
|
+
if (!adapter || typeof adapter.shutdownIfIdle !== "function") return;
|
|
1008
|
+
var result = adapter.shutdownIfIdle(IDLE_MS);
|
|
979
1009
|
if (result && typeof result.catch === "function") {
|
|
980
1010
|
result.catch(function (e) {
|
|
981
|
-
console.error("[daemon]
|
|
1011
|
+
console.error("[daemon] " + vendor + " idle reclaim failed:", e && e.message ? e.message : e);
|
|
982
1012
|
});
|
|
983
1013
|
}
|
|
984
|
-
}
|
|
985
|
-
}
|
|
1014
|
+
} catch (e) {}
|
|
1015
|
+
});
|
|
986
1016
|
});
|
|
987
1017
|
}, REAP_MS);
|
|
988
1018
|
if (reaperHandle && typeof reaperHandle.unref === "function") {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Kiro-specific default values. Single source of truth — do not duplicate
|
|
2
|
+
// elsewhere. Consumed by the server-side vendor plumbing and sent to clients
|
|
3
|
+
// via the config state so the UI renders the right controls.
|
|
4
|
+
|
|
5
|
+
var KIRO_DEFAULTS = {
|
|
6
|
+
// Kiro CLI 2.18.1 exposes the next-generation agent as the v3 engine. Its
|
|
7
|
+
// general-purpose mode id is "vibe" (displayed as "Default").
|
|
8
|
+
engine: "v3",
|
|
9
|
+
mode: "vibe",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function getKiroConfig(sm) {
|
|
13
|
+
return {
|
|
14
|
+
engine: (sm && sm.kiroEngine) || KIRO_DEFAULTS.engine,
|
|
15
|
+
mode: (sm && sm.kiroMode) || KIRO_DEFAULTS.mode,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
KIRO_DEFAULTS: KIRO_DEFAULTS,
|
|
21
|
+
getKiroConfig: getKiroConfig,
|
|
22
|
+
};
|
|
@@ -143,6 +143,7 @@ function attachConnection(ctx) {
|
|
|
143
143
|
sendTo(ws, { type: "model_info", model: sm.currentModel, models: initialModels, vendor: initialVendor, availableVendors: sm.availableVendors || [], installedVendors: sm.installedVendors || [] });
|
|
144
144
|
}
|
|
145
145
|
sendTo(ws, { type: "config_state", model: sm.currentModel || "", mode: sm.currentPermissionMode || "default", effort: sm.currentEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
146
|
+
sendTo(ws, { type: "last_vendor", vendor: sm.lastVendor || "" });
|
|
146
147
|
sendTo(ws, Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
|
|
147
148
|
sendTo(ws, { type: "term_list", terminals: tm.list() });
|
|
148
149
|
// Context sources sent after session is resolved (per-session storage)
|
|
@@ -21,7 +21,7 @@ function generateId() {
|
|
|
21
21
|
var formatters = {
|
|
22
22
|
auth_required: function (data) {
|
|
23
23
|
var vendor = data.vendor || "claude";
|
|
24
|
-
var title = data.title || ((vendor === "codex" ? "Codex" : "Claude Code") + " is not logged in");
|
|
24
|
+
var title = data.title || ((vendor === "codex" ? "Codex" : (vendor === "kiro" ? "Kiro CLI" : "Claude Code")) + " is not logged in");
|
|
25
25
|
return {
|
|
26
26
|
type: "auth_required",
|
|
27
27
|
title: title,
|
package/lib/project-sessions.js
CHANGED
|
@@ -424,13 +424,13 @@ function attachSessions(ctx) {
|
|
|
424
424
|
if (ws._clayUser && usersModule.isMultiUser()) sessionOpts.ownerId = ws._clayUser.id;
|
|
425
425
|
if (msg.sessionVisibility) sessionOpts.sessionVisibility = msg.sessionVisibility;
|
|
426
426
|
if (msg.vendor) sessionOpts.vendor = msg.vendor;
|
|
427
|
-
// Mode resolution: codex sessions are always GUI (no TUI adapter).
|
|
427
|
+
// Mode resolution: codex and kiro sessions are always GUI (no TUI adapter).
|
|
428
428
|
// Claude sessions honor the explicit msg.mode if provided, otherwise
|
|
429
429
|
// fall back to the user's claudeOpenMode preference. This is what
|
|
430
430
|
// makes the sidebar's "Claude" icon button create the right kind of
|
|
431
431
|
// session without the client needing to know the preference.
|
|
432
432
|
var requestedMode;
|
|
433
|
-
if (msg.vendor === "codex") {
|
|
433
|
+
if (msg.vendor === "codex" || msg.vendor === "kiro") {
|
|
434
434
|
requestedMode = "gui";
|
|
435
435
|
} else if (msg.mode === "tui" || msg.mode === "gui") {
|
|
436
436
|
requestedMode = msg.mode;
|
|
@@ -495,6 +495,17 @@ function attachSessions(ctx) {
|
|
|
495
495
|
newSess = sm.createSession(sessionOpts, ws);
|
|
496
496
|
}
|
|
497
497
|
ws._clayActiveSession = newSess.localId;
|
|
498
|
+
// Remember the vendor only when the client asked for one explicitly.
|
|
499
|
+
// new_session without a vendor (mate sidebar, notification banner,
|
|
500
|
+
// debate) falls through to the default adapter and must not clobber
|
|
501
|
+
// the user's remembered pick.
|
|
502
|
+
if (msg.vendor && sm.lastVendor !== msg.vendor) {
|
|
503
|
+
sm.lastVendor = msg.vendor;
|
|
504
|
+
if (typeof opts.onSetProjectLastVendor === "function") {
|
|
505
|
+
opts.onSetProjectLastVendor(slug, msg.vendor);
|
|
506
|
+
}
|
|
507
|
+
send({ type: "last_vendor", vendor: msg.vendor });
|
|
508
|
+
}
|
|
498
509
|
// Apply project-level email defaults to new session
|
|
499
510
|
if (typeof ctx._email === "object" && ctx._email.getEmailDefaults) {
|
|
500
511
|
var emailDefaults = ctx._email.getEmailDefaults();
|
package/lib/project.js
CHANGED
|
@@ -166,7 +166,7 @@ function createProjectContext(opts) {
|
|
|
166
166
|
var sessionTitleMigrationScheduled = false;
|
|
167
167
|
|
|
168
168
|
// --- YOKE adapters (multi-vendor, lazy init) ---
|
|
169
|
-
var _yokeState = yoke.createAdapters({ cwd: cwd, slug: slug });
|
|
169
|
+
var _yokeState = yoke.createAdapters({ cwd: cwd, slug: slug, osUsers: osUsers });
|
|
170
170
|
var adapters = _yokeState.adapters;
|
|
171
171
|
var defaultVendor = adapters.claude ? "claude" : Object.keys(adapters)[0] || "claude";
|
|
172
172
|
var adapter = adapters[defaultVendor] || null;
|
|
@@ -428,6 +428,12 @@ function createProjectContext(opts) {
|
|
|
428
428
|
var _srvEffort = typeof opts.onGetServerDefaultEffort === "function" ? opts.onGetServerDefaultEffort() : null;
|
|
429
429
|
sm.currentEffort = (_projEffort && _projEffort.effort) || (_srvEffort && _srvEffort.effort) || "medium";
|
|
430
430
|
|
|
431
|
+
// Last vendor the user started a session with in this project. Seeds the
|
|
432
|
+
// sidebar's "New session" button so it defaults to whatever they used last
|
|
433
|
+
// instead of always launching Claude.
|
|
434
|
+
var _projLastVendor = typeof opts.onGetProjectLastVendor === "function" ? opts.onGetProjectLastVendor(slug) : null;
|
|
435
|
+
sm.lastVendor = (_projLastVendor && _projLastVendor.vendor) || null;
|
|
436
|
+
|
|
431
437
|
var _projModel = typeof opts.onGetProjectDefaultModel === "function" ? opts.onGetProjectDefaultModel(slug) : null;
|
|
432
438
|
var _srvModel = typeof opts.onGetServerDefaultModel === "function" ? opts.onGetServerDefaultModel() : null;
|
|
433
439
|
sm._savedDefaultModel = (_projModel && _projModel.model) || (_srvModel && _srvModel.model) || null;
|
|
@@ -897,10 +903,12 @@ function createProjectContext(opts) {
|
|
|
897
903
|
(async function() {
|
|
898
904
|
if (msg.vendor) {
|
|
899
905
|
try {
|
|
906
|
+
var modelLinuxUser = getLinuxUserForWs(ws);
|
|
900
907
|
var vendorAdapter = adapters[msg.vendor] || null;
|
|
901
908
|
if (!vendorAdapter) {
|
|
902
909
|
vendorAdapter = await yoke.lazyCreateAdapter(adapters, msg.vendor, {
|
|
903
910
|
cwd: cwd,
|
|
911
|
+
linuxUser: modelLinuxUser || undefined,
|
|
904
912
|
clayPort: serverPort,
|
|
905
913
|
clayTls: serverTls,
|
|
906
914
|
clayAuthToken: serverAuthToken,
|
|
@@ -1528,14 +1536,30 @@ function createProjectContext(opts) {
|
|
|
1528
1536
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
1529
1537
|
} catch (e) {}
|
|
1530
1538
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1539
|
+
// Shut down every adapter that owns a child process. shutdown() is
|
|
1540
|
+
// optional on the YOKE contract, so adapters without one are skipped.
|
|
1541
|
+
var shutdowns = [];
|
|
1542
|
+
if (adapters) {
|
|
1543
|
+
Object.keys(adapters).forEach(function(vendor) {
|
|
1544
|
+
var adapter = adapters[vendor];
|
|
1545
|
+
if (!adapter || typeof adapter.shutdown !== "function") return;
|
|
1546
|
+
// Shared adapter instances (e.g. Claude) are reused across projects,
|
|
1547
|
+
// so tearing one down here would kill other projects' sessions.
|
|
1548
|
+
if (adapter.shared) return;
|
|
1549
|
+
try {
|
|
1550
|
+
shutdowns.push(Promise.resolve(adapter.shutdown()).catch(function(err) {
|
|
1551
|
+
console.error("[project] " + vendor + " shutdown failed for " + slug + ":", err && err.message ? err.message : err);
|
|
1552
|
+
return false;
|
|
1553
|
+
}));
|
|
1554
|
+
} catch (err) {
|
|
1555
|
+
console.error("[project] " + vendor + " shutdown threw for " + slug + ":", err && err.message ? err.message : err);
|
|
1556
|
+
}
|
|
1536
1557
|
});
|
|
1537
1558
|
}
|
|
1538
|
-
return
|
|
1559
|
+
if (!shutdowns.length) return Promise.resolve(true);
|
|
1560
|
+
return Promise.all(shutdowns).then(function(results) {
|
|
1561
|
+
return results.every(function(r) { return r !== false; });
|
|
1562
|
+
});
|
|
1539
1563
|
}
|
|
1540
1564
|
|
|
1541
1565
|
// --- Status info ---
|
package/lib/public/app.js
CHANGED
|
@@ -310,6 +310,12 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
310
310
|
// panels
|
|
311
311
|
currentModel: "",
|
|
312
312
|
currentModels: [],
|
|
313
|
+
// Project's last-used vendor; seeds the sidebar's "New session" button.
|
|
314
|
+
lastVendor: "",
|
|
315
|
+
// How Claude sessions open: "gui" (default) or "tui". The server sends
|
|
316
|
+
// claude_open_mode_changed on connect; this seeds it beforehand so the
|
|
317
|
+
// new-session menu doesn't flash the TUI-only entry.
|
|
318
|
+
claudeOpenMode: "gui",
|
|
313
319
|
currentMode: "default",
|
|
314
320
|
currentEffort: "medium",
|
|
315
321
|
currentBetas: [],
|
|
@@ -358,18 +358,16 @@
|
|
|
358
358
|
background: rgba(var(--overlay-rgb), 0.06);
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
-
/*
|
|
362
|
-
|
|
363
|
-
same way the desktop sidebar already does. */
|
|
361
|
+
/* Split "New session" row: main action on the last-used vendor + a chevron
|
|
362
|
+
that expands .mobile-vendor-list below it. Mirrors the desktop sidebar. */
|
|
364
363
|
.mobile-session-new-row {
|
|
365
364
|
display: flex;
|
|
366
365
|
gap: 6px;
|
|
367
366
|
padding: 6px 8px 8px;
|
|
368
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
369
367
|
}
|
|
370
368
|
|
|
371
|
-
.mobile-session-new-row .mobile-session-new-
|
|
372
|
-
flex: 1 1
|
|
369
|
+
.mobile-session-new-row .mobile-session-new-main {
|
|
370
|
+
flex: 1 1 auto;
|
|
373
371
|
width: auto;
|
|
374
372
|
margin-bottom: 0;
|
|
375
373
|
padding: 10px 12px;
|
|
@@ -377,15 +375,61 @@
|
|
|
377
375
|
border-radius: 8px;
|
|
378
376
|
background: rgba(var(--overlay-rgb), 0.03);
|
|
379
377
|
color: var(--text);
|
|
380
|
-
justify-content: center;
|
|
381
378
|
gap: 8px;
|
|
382
379
|
}
|
|
383
|
-
.mobile-session-new-row .mobile-session-new-
|
|
380
|
+
.mobile-session-new-row .mobile-session-new-chevron {
|
|
381
|
+
flex: 0 0 auto;
|
|
382
|
+
width: 44px;
|
|
383
|
+
margin-bottom: 0;
|
|
384
|
+
padding: 10px 0;
|
|
385
|
+
border: 1px solid var(--border-subtle);
|
|
386
|
+
border-radius: 8px;
|
|
387
|
+
background: rgba(var(--overlay-rgb), 0.03);
|
|
388
|
+
color: var(--text-muted);
|
|
389
|
+
justify-content: center;
|
|
390
|
+
gap: 0;
|
|
391
|
+
}
|
|
392
|
+
.mobile-session-new-row .mobile-session-new-chevron.expanded svg {
|
|
393
|
+
transform: rotate(180deg);
|
|
394
|
+
}
|
|
395
|
+
.mobile-session-new-row .mobile-session-new-main:active,
|
|
396
|
+
.mobile-session-new-row .mobile-session-new-chevron:active {
|
|
397
|
+
background: rgba(var(--overlay-rgb), 0.1);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.mobile-vendor-list {
|
|
401
|
+
display: flex;
|
|
402
|
+
flex-direction: column;
|
|
403
|
+
gap: 4px;
|
|
404
|
+
padding: 0 8px 8px;
|
|
384
405
|
border-bottom: 1px solid var(--border-subtle);
|
|
385
406
|
}
|
|
386
|
-
.mobile-
|
|
407
|
+
.mobile-vendor-list.hidden { display: none; }
|
|
408
|
+
|
|
409
|
+
.mobile-vendor-list .mobile-session-new-vendor {
|
|
410
|
+
width: 100%;
|
|
411
|
+
margin-bottom: 0;
|
|
412
|
+
padding: 10px 12px;
|
|
413
|
+
border: 1px solid var(--border-subtle);
|
|
414
|
+
border-radius: 8px;
|
|
415
|
+
background: rgba(var(--overlay-rgb), 0.03);
|
|
416
|
+
color: var(--text);
|
|
417
|
+
font-weight: 500;
|
|
418
|
+
gap: 8px;
|
|
419
|
+
}
|
|
420
|
+
.mobile-vendor-list .mobile-session-new-vendor.active { font-weight: 700; }
|
|
421
|
+
/* Uninstalled vendors stay tappable: the tap opens the vendor's homepage. */
|
|
422
|
+
.mobile-vendor-list .mobile-session-new-vendor.disabled { opacity: 0.45; }
|
|
423
|
+
.mobile-vendor-list .mobile-session-new-vendor:active {
|
|
387
424
|
background: rgba(var(--overlay-rgb), 0.1);
|
|
388
425
|
}
|
|
426
|
+
.mobile-vendor-note {
|
|
427
|
+
margin-left: auto;
|
|
428
|
+
font-size: 12px;
|
|
429
|
+
font-weight: 500;
|
|
430
|
+
color: var(--text-dimmer);
|
|
431
|
+
}
|
|
432
|
+
|
|
389
433
|
.mobile-session-new-icon {
|
|
390
434
|
width: 18px;
|
|
391
435
|
height: 18px;
|
|
@@ -993,7 +993,7 @@
|
|
|
993
993
|
|
|
994
994
|
.session-top-actions {
|
|
995
995
|
display: grid;
|
|
996
|
-
grid-template-columns:
|
|
996
|
+
grid-template-columns: minmax(0, 1fr);
|
|
997
997
|
gap: 2px;
|
|
998
998
|
padding: 0 0 2px;
|
|
999
999
|
}
|
|
@@ -1240,6 +1240,29 @@
|
|
|
1240
1240
|
.session-ctx-item.session-ctx-delete { color: var(--error); }
|
|
1241
1241
|
.session-ctx-item.session-ctx-delete:hover { background: var(--error-8); }
|
|
1242
1242
|
|
|
1243
|
+
/* --- New-session vendor picker --- */
|
|
1244
|
+
.session-new-menu { min-width: 200px; }
|
|
1245
|
+
|
|
1246
|
+
.session-ctx-sep {
|
|
1247
|
+
height: 1px;
|
|
1248
|
+
margin: 4px 0;
|
|
1249
|
+
background: var(--border);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
.session-new-vendor .session-new-vendor-name { flex: 1 1 auto; min-width: 0; }
|
|
1253
|
+
.session-new-vendor.active { color: var(--text); font-weight: 600; }
|
|
1254
|
+
|
|
1255
|
+
/* Uninstalled vendors stay clickable on purpose: the click opens the
|
|
1256
|
+
vendor's homepage instead of creating a session. */
|
|
1257
|
+
.session-new-vendor.disabled { opacity: 0.45; }
|
|
1258
|
+
.session-new-vendor.disabled:hover { opacity: 0.7; }
|
|
1259
|
+
|
|
1260
|
+
.session-new-vendor-note {
|
|
1261
|
+
font-size: 11px;
|
|
1262
|
+
color: var(--text-dimmer);
|
|
1263
|
+
white-space: nowrap;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1243
1266
|
/* --- Session inline rename --- */
|
|
1244
1267
|
.session-rename-input {
|
|
1245
1268
|
width: 100%;
|
|
@@ -49,69 +49,10 @@ body.tui-suspended #tui-resume-bar {
|
|
|
49
49
|
.tui-resume-hint { display: none; }
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
/* Policy
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
display: flex;
|
|
57
|
-
align-items: center;
|
|
58
|
-
gap: 8px;
|
|
59
|
-
flex-shrink: 0;
|
|
60
|
-
padding: 6px 10px;
|
|
61
|
-
margin-bottom: 4px;
|
|
62
|
-
border-radius: 6px;
|
|
63
|
-
background: rgba(80, 250, 123, 0.06);
|
|
64
|
-
border: 1px solid rgba(80, 250, 123, 0.15);
|
|
65
|
-
color: #b8b8b8;
|
|
66
|
-
font-size: 11px;
|
|
67
|
-
line-height: 1.3;
|
|
68
|
-
font-family: "Roboto Mono", Menlo, Monaco, monospace;
|
|
69
|
-
}
|
|
70
|
-
.tui-policy-icon {
|
|
71
|
-
color: #50fa7b;
|
|
72
|
-
font-size: 9px;
|
|
73
|
-
line-height: 1;
|
|
74
|
-
flex-shrink: 0;
|
|
75
|
-
}
|
|
76
|
-
.tui-policy-text {
|
|
77
|
-
flex: 1 1 auto;
|
|
78
|
-
min-width: 0;
|
|
79
|
-
}
|
|
80
|
-
.tui-policy-learn-more {
|
|
81
|
-
flex-shrink: 0;
|
|
82
|
-
padding: 2px 8px;
|
|
83
|
-
border: 1px solid rgba(80, 250, 123, 0.3);
|
|
84
|
-
border-radius: 4px;
|
|
85
|
-
background: transparent;
|
|
86
|
-
color: #50fa7b;
|
|
87
|
-
font: inherit;
|
|
88
|
-
font-size: 10px;
|
|
89
|
-
cursor: pointer;
|
|
90
|
-
transition: background 0.15s, border-color 0.15s;
|
|
91
|
-
}
|
|
92
|
-
.tui-policy-learn-more:hover {
|
|
93
|
-
background: rgba(80, 250, 123, 0.1);
|
|
94
|
-
border-color: rgba(80, 250, 123, 0.5);
|
|
95
|
-
}
|
|
96
|
-
.tui-policy-dismiss {
|
|
97
|
-
flex-shrink: 0;
|
|
98
|
-
width: 18px;
|
|
99
|
-
height: 18px;
|
|
100
|
-
padding: 0;
|
|
101
|
-
border: none;
|
|
102
|
-
border-radius: 4px;
|
|
103
|
-
background: transparent;
|
|
104
|
-
color: #888;
|
|
105
|
-
font-size: 14px;
|
|
106
|
-
line-height: 1;
|
|
107
|
-
cursor: pointer;
|
|
108
|
-
}
|
|
109
|
-
.tui-policy-dismiss:hover {
|
|
110
|
-
background: rgba(255, 255, 255, 0.06);
|
|
111
|
-
color: #ddd;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/* Policy info modal. Themed via CSS vars so it follows light/dark. */
|
|
52
|
+
/* Policy info modal. Themed via CSS vars so it follows light/dark.
|
|
53
|
+
The modal itself is gone; .tui-policy-modal-* rules stay because
|
|
54
|
+
What's New article bodies (lib/whats-new-content.js) still use the
|
|
55
|
+
-links class for their footer links. */
|
|
115
56
|
.tui-policy-modal-backdrop {
|
|
116
57
|
position: fixed;
|
|
117
58
|
inset: 0;
|
package/lib/public/index.html
CHANGED
|
@@ -513,6 +513,10 @@
|
|
|
513
513
|
<img src="/codex-avatar.png" class="vendor-toggle-icon" alt="Codex">
|
|
514
514
|
<span class="vendor-toggle-label">Codex</span>
|
|
515
515
|
</button>
|
|
516
|
+
<button id="vendor-btn-kiro" class="vendor-toggle-btn" data-vendor="kiro">
|
|
517
|
+
<img src="/kiro-avatar.svg" class="vendor-toggle-icon" alt="Kiro">
|
|
518
|
+
<span class="vendor-toggle-label">Kiro CLI</span>
|
|
519
|
+
</button>
|
|
516
520
|
</div>
|
|
517
521
|
<div id="active-vendor-indicator" class="hidden" title="Session vendor">
|
|
518
522
|
<img id="active-vendor-icon" alt="">
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="Kiro CLI">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="kiroBg" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#8b5cf6"/>
|
|
5
|
+
<stop offset="1" stop-color="#6d28d9"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect width="128" height="128" rx="28" fill="url(#kiroBg)"/>
|
|
9
|
+
<g fill="#ffffff">
|
|
10
|
+
<rect x="34" y="34" width="12" height="60" rx="3"/>
|
|
11
|
+
<rect x="52" y="60" width="42" height="12" rx="3" transform="rotate(-45 73 66)"/>
|
|
12
|
+
<rect x="52" y="60" width="42" height="12" rx="3" transform="rotate(45 73 66)"/>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -42,7 +42,7 @@ import { handleMcpServersState } from './mcp-ui.js';
|
|
|
42
42
|
import { handleLoopRegistryUpdated, handleScheduleRunStarted, handleScheduleRunFinished, handleLoopScheduled, isSchedulerOpen, enterCraftingMode, exitCraftingMode, handleLoopRegistryFiles } from './scheduler.js';
|
|
43
43
|
|
|
44
44
|
// --- App module imports ---
|
|
45
|
-
import { scrollToBottom, addToMessages, addUserMessage, addSystemMessage, removeMatePreThinking, appendDelta, finalizeAssistantBlock, addConflictMessage, addContextOverflowMessage, showSuggestionChips, armStickyBottom } from './app-rendering.js';
|
|
45
|
+
import { scrollToBottom, addToMessages, addUserMessage, addSystemMessage, removeMatePreThinking, appendDelta, finalizeAssistantBlock, addConflictMessage, addContextOverflowMessage, showSuggestionChips, armStickyBottom, VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
46
46
|
import { setActivity, startUrgentBlink, stopUrgentBlink, blinkSessionDot, updateCrossProjectBlink } from './app-favicon.js';
|
|
47
47
|
import { setStatus } from './app-connection.js';
|
|
48
48
|
import { handleWhatsNewState, handleWhatsNewSeenResult, setKnownEntries as setWhatsNewKnownEntries } from './whats-new.js';
|
|
@@ -579,6 +579,12 @@ export function processMessage(msg) {
|
|
|
579
579
|
updateSessionPresence(msg.presence || {});
|
|
580
580
|
break;
|
|
581
581
|
|
|
582
|
+
// Project's last-used vendor. Seeds the sidebar's "New session" button
|
|
583
|
+
// on connect and keeps other tabs in sync after someone picks a vendor.
|
|
584
|
+
case "last_vendor":
|
|
585
|
+
store.set({ lastVendor: msg.vendor || "" });
|
|
586
|
+
break;
|
|
587
|
+
|
|
582
588
|
case "cursor_move":
|
|
583
589
|
handleRemoteCursorMove(msg);
|
|
584
590
|
break;
|
|
@@ -696,15 +702,10 @@ export function processMessage(msg) {
|
|
|
696
702
|
}
|
|
697
703
|
}
|
|
698
704
|
if (_avi && _avIcon) {
|
|
699
|
-
if (msg.vendor
|
|
700
|
-
_avIcon.src =
|
|
701
|
-
_avIcon.alt =
|
|
702
|
-
_avi.title = "
|
|
703
|
-
_avi.classList.remove("hidden");
|
|
704
|
-
} else if (msg.vendor === "codex") {
|
|
705
|
-
_avIcon.src = "/codex-avatar.png";
|
|
706
|
-
_avIcon.alt = "Codex";
|
|
707
|
-
_avi.title = "Codex session";
|
|
705
|
+
if (msg.vendor && VENDOR_AVATARS[msg.vendor]) {
|
|
706
|
+
_avIcon.src = VENDOR_AVATARS[msg.vendor];
|
|
707
|
+
_avIcon.alt = VENDOR_NAMES[msg.vendor] || msg.vendor;
|
|
708
|
+
_avi.title = (VENDOR_NAMES[msg.vendor] || msg.vendor) + " session";
|
|
708
709
|
_avi.classList.remove("hidden");
|
|
709
710
|
} else {
|
|
710
711
|
_avi.classList.add("hidden");
|
|
@@ -88,6 +88,7 @@ var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
|
88
88
|
var EFFORT_LEVELS_BY_VENDOR = {
|
|
89
89
|
claude: ["low", "medium", "high", "xhigh", "max"],
|
|
90
90
|
codex: ["minimal", "low", "medium", "high", "xhigh"],
|
|
91
|
+
kiro: ["low", "medium", "high", "xhigh", "max"],
|
|
91
92
|
};
|
|
92
93
|
var THINKING_OPTIONS = ["disabled", "adaptive", "budget"];
|
|
93
94
|
var CODEX_APPROVAL_OPTIONS = [
|
|
@@ -418,7 +419,8 @@ export function initPanels() {
|
|
|
418
419
|
var vendorToggleWrap = $("vendor-toggle-wrap");
|
|
419
420
|
var vendorBtnClaude = $("vendor-btn-claude");
|
|
420
421
|
var vendorBtnCodex = $("vendor-btn-codex");
|
|
421
|
-
var
|
|
422
|
+
var vendorBtnKiro = $("vendor-btn-kiro");
|
|
423
|
+
var vendorBtns = { claude: vendorBtnClaude, codex: vendorBtnCodex, kiro: vendorBtnKiro };
|
|
422
424
|
|
|
423
425
|
function updateVendorToggle() {
|
|
424
426
|
var installed = store.get('installedVendors') || [];
|
|
@@ -447,6 +449,7 @@ export function initPanels() {
|
|
|
447
449
|
|
|
448
450
|
if (vendorBtnClaude) vendorBtnClaude.addEventListener("click", function() { onVendorClick("claude"); });
|
|
449
451
|
if (vendorBtnCodex) vendorBtnCodex.addEventListener("click", function() { onVendorClick("codex"); });
|
|
452
|
+
if (vendorBtnKiro) vendorBtnKiro.addEventListener("click", function() { onVendorClick("kiro"); });
|
|
450
453
|
|
|
451
454
|
// --- Reactive UI sync ---
|
|
452
455
|
store.subscribe(function (state, prev) {
|
|
@@ -17,10 +17,22 @@ import { getScheduledMsgEl } from './app-rate-limit.js';
|
|
|
17
17
|
export var VENDOR_AVATARS = {
|
|
18
18
|
claude: "/claude-code-avatar.png",
|
|
19
19
|
codex: "/codex-avatar.png",
|
|
20
|
+
kiro: "/kiro-avatar.svg",
|
|
20
21
|
};
|
|
21
22
|
export var VENDOR_NAMES = {
|
|
22
23
|
claude: "Claude Code",
|
|
23
24
|
codex: "Codex",
|
|
25
|
+
kiro: "Kiro CLI",
|
|
26
|
+
};
|
|
27
|
+
// Display order for every vendor Clay knows about, installed or not. Pickers
|
|
28
|
+
// render the full list so a missing CLI reads as "not installed yet" rather
|
|
29
|
+
// than "Clay doesn't support it".
|
|
30
|
+
export var VENDOR_ORDER = ["claude", "codex", "kiro"];
|
|
31
|
+
// Where to send the user when they pick a vendor whose CLI isn't installed.
|
|
32
|
+
export var VENDOR_HOMEPAGES = {
|
|
33
|
+
claude: "https://claude.com/product/claude-code",
|
|
34
|
+
codex: "https://openai.com/codex/",
|
|
35
|
+
kiro: "https://kiro.dev/",
|
|
24
36
|
};
|
|
25
37
|
var NEW_MSG_BTN_DEFAULT = "\u2193 Latest";
|
|
26
38
|
var NEW_MSG_BTN_ACTIVITY = "\u2193 New activity";
|
|
@@ -300,9 +300,11 @@ export function sendMessage() {
|
|
|
300
300
|
_vtw2.classList.remove("locked");
|
|
301
301
|
}
|
|
302
302
|
if (_avi && _avIcon) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
303
|
+
var _vendorAvatars = { claude: "/claude-code-avatar.png", codex: "/codex-avatar.png", kiro: "/kiro-avatar.svg" };
|
|
304
|
+
var _vendorNames = { claude: "Claude", codex: "Codex", kiro: "Kiro CLI" };
|
|
305
|
+
_avIcon.src = _vendorAvatars[_committedVendor] || _vendorAvatars.claude;
|
|
306
|
+
_avIcon.alt = _vendorNames[_committedVendor] || _vendorNames.claude;
|
|
307
|
+
_avi.title = (_vendorNames[_committedVendor] || _vendorNames.claude) + " session";
|
|
306
308
|
_avi.classList.remove("hidden");
|
|
307
309
|
}
|
|
308
310
|
} else if (_vtw2) {
|
|
@@ -158,9 +158,9 @@ export function showMateSidebar(mateId, mateData) {
|
|
|
158
158
|
if (mateVendorWrap) {
|
|
159
159
|
var available = store.get('availableVendors') || [];
|
|
160
160
|
var mateVendor = mateData.vendor || "claude";
|
|
161
|
-
var vendorIcons = { claude: "/claude-code-avatar.png", codex: "/codex-avatar.png" };
|
|
162
|
-
var vendorNames = { claude: "Claude Code", codex: "Codex" };
|
|
163
|
-
var vendorKeys = ["claude", "codex"];
|
|
161
|
+
var vendorIcons = { claude: "/claude-code-avatar.png", codex: "/codex-avatar.png", kiro: "/kiro-avatar.svg" };
|
|
162
|
+
var vendorNames = { claude: "Claude Code", codex: "Codex", kiro: "Kiro CLI" };
|
|
163
|
+
var vendorKeys = ["claude", "codex", "kiro"];
|
|
164
164
|
mateVendorWrap.innerHTML = "";
|
|
165
165
|
for (var vi = 0; vi < vendorKeys.length; vi++) {
|
|
166
166
|
var vk = vendorKeys[vi];
|