@zhongqian97-code/ecode 0.5.30 → 0.5.32
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/dist/index.js +287 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1380,10 +1380,10 @@ function metadataPathFromLogFile(logFilePath2) {
|
|
|
1380
1380
|
const dir = path6.dirname(logFilePath2);
|
|
1381
1381
|
return path6.join(dir, `${base}-session.json`);
|
|
1382
1382
|
}
|
|
1383
|
-
function createSessionMetadata(logFilePath2, model) {
|
|
1383
|
+
function createSessionMetadata(logFilePath2, model, id) {
|
|
1384
1384
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1385
1385
|
return {
|
|
1386
|
-
id: crypto2.randomUUID(),
|
|
1386
|
+
id: id ?? crypto2.randomUUID(),
|
|
1387
1387
|
startTime: now,
|
|
1388
1388
|
lastActivity: now,
|
|
1389
1389
|
cwd: process.cwd(),
|
|
@@ -5700,6 +5700,21 @@ function generateAdminHtml(version2) {
|
|
|
5700
5700
|
to { opacity: 1; transform: translateX(0); }
|
|
5701
5701
|
}
|
|
5702
5702
|
|
|
5703
|
+
/* \u2500\u2500 Skill dropdown \u2500\u2500 */
|
|
5704
|
+
#skill-dropdown .skill-item {
|
|
5705
|
+
padding: 8px 12px;
|
|
5706
|
+
cursor: pointer;
|
|
5707
|
+
border-bottom: 1px solid #30363d;
|
|
5708
|
+
display: flex;
|
|
5709
|
+
gap: 8px;
|
|
5710
|
+
align-items: baseline;
|
|
5711
|
+
}
|
|
5712
|
+
#skill-dropdown .skill-item:last-child { border-bottom: none; }
|
|
5713
|
+
#skill-dropdown .skill-item:hover,
|
|
5714
|
+
#skill-dropdown .skill-item.selected { background: #2d333b; }
|
|
5715
|
+
#skill-dropdown .skill-name { color: #79c0ff; font-weight: 600; font-size: 0.9em; }
|
|
5716
|
+
#skill-dropdown .skill-desc { color: #8b949e; font-size: 0.8em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 300px; }
|
|
5717
|
+
|
|
5703
5718
|
/* \u2500\u2500 Mobile \u2500\u2500 */
|
|
5704
5719
|
@media (max-width: 600px) {
|
|
5705
5720
|
#hamburger { display: block; }
|
|
@@ -5711,6 +5726,22 @@ function generateAdminHtml(version2) {
|
|
|
5711
5726
|
transition: transform .2s ease;
|
|
5712
5727
|
}
|
|
5713
5728
|
#sidebar.open { transform: translateX(0); }
|
|
5729
|
+
|
|
5730
|
+
/* Fix input bar on mobile */
|
|
5731
|
+
#input-bar {
|
|
5732
|
+
position: fixed;
|
|
5733
|
+
bottom: 0;
|
|
5734
|
+
left: 0;
|
|
5735
|
+
right: 0;
|
|
5736
|
+
bottom: env(safe-area-inset-bottom, 0px);
|
|
5737
|
+
z-index: 40;
|
|
5738
|
+
padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
|
|
5739
|
+
}
|
|
5740
|
+
|
|
5741
|
+
/* Add bottom padding to chat so messages don't hide behind fixed input */
|
|
5742
|
+
#messages {
|
|
5743
|
+
padding-bottom: 80px;
|
|
5744
|
+
}
|
|
5714
5745
|
}
|
|
5715
5746
|
</style>
|
|
5716
5747
|
</head>
|
|
@@ -5749,6 +5780,7 @@ function generateAdminHtml(version2) {
|
|
|
5749
5780
|
<div class="empty-chat">\u2190 \u4ECE\u5DE6\u4FA7\u9009\u62E9\u4F1A\u8BDD\uFF0C\u6216\u70B9\u51FB"\u65B0\u5EFA\u4F1A\u8BDD"\u5F00\u59CB</div>
|
|
5750
5781
|
</div>
|
|
5751
5782
|
<div id="ws-status" class="ws-status"></div>
|
|
5783
|
+
<div id="skill-dropdown" style="display:none;position:fixed;bottom:60px;left:14px;right:14px;max-height:200px;overflow-y:auto;background:#1c2128;border:1px solid #30363d;border-radius:6px;z-index:60;"></div>
|
|
5752
5784
|
<div id="input-bar">
|
|
5753
5785
|
<textarea id="msg-input" rows="1" placeholder="\u8F93\u5165\u6D88\u606F\uFF0C\u6309 Enter \u53D1\u9001\uFF08Shift+Enter \u6362\u884C\uFF09\u2026" disabled></textarea>
|
|
5754
5786
|
<button id="send-btn" disabled>\u53D1\u9001</button>
|
|
@@ -5813,6 +5845,8 @@ function generateAdminHtml(version2) {
|
|
|
5813
5845
|
wsRetries: 0,
|
|
5814
5846
|
streamingMsgEl: null, // DOM element currently receiving delta tokens
|
|
5815
5847
|
showTools: true,
|
|
5848
|
+
skills: [], // cached skills from /api/skills
|
|
5849
|
+
skillsLoaded: false,
|
|
5816
5850
|
};
|
|
5817
5851
|
|
|
5818
5852
|
// \u2500\u2500 Toast \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -6161,6 +6195,70 @@ function generateAdminHtml(version2) {
|
|
|
6161
6195
|
.replace(/>/g, '>');
|
|
6162
6196
|
}
|
|
6163
6197
|
|
|
6198
|
+
// \u2500\u2500 Skill dropdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6199
|
+
var selectedSkillIndex = -1;
|
|
6200
|
+
|
|
6201
|
+
async function loadSkills() {
|
|
6202
|
+
if (state.skillsLoaded) return;
|
|
6203
|
+
try {
|
|
6204
|
+
const data = await apiFetch('/api/skills');
|
|
6205
|
+
state.skills = Array.isArray(data) ? data : [];
|
|
6206
|
+
state.skillsLoaded = true;
|
|
6207
|
+
} catch (e) {
|
|
6208
|
+
state.skills = [];
|
|
6209
|
+
}
|
|
6210
|
+
}
|
|
6211
|
+
|
|
6212
|
+
function showSkillDropdown(query) {
|
|
6213
|
+
const dropdown = document.getElementById('skill-dropdown');
|
|
6214
|
+
if (!dropdown) return;
|
|
6215
|
+
const q = query.toLowerCase();
|
|
6216
|
+
const filtered = state.skills.filter(function(s) {
|
|
6217
|
+
return s.name.toLowerCase().startsWith(q);
|
|
6218
|
+
});
|
|
6219
|
+
if (!filtered.length) {
|
|
6220
|
+
dropdown.style.display = 'none';
|
|
6221
|
+
selectedSkillIndex = -1;
|
|
6222
|
+
return;
|
|
6223
|
+
}
|
|
6224
|
+
selectedSkillIndex = -1;
|
|
6225
|
+
dropdown.innerHTML = filtered.map(function(s, i) {
|
|
6226
|
+
return '<div class="skill-item" data-name="' + escHtml(s.name) + '">' +
|
|
6227
|
+
'<span class="skill-name">/' + escHtml(s.name) + '</span>' +
|
|
6228
|
+
'<span class="skill-desc">' + escHtml(s.description || '') + '</span>' +
|
|
6229
|
+
'</div>';
|
|
6230
|
+
}).join('');
|
|
6231
|
+
dropdown.querySelectorAll('.skill-item').forEach(function(el) {
|
|
6232
|
+
el.addEventListener('click', function() {
|
|
6233
|
+
var name = el.getAttribute('data-name');
|
|
6234
|
+
var inputEl = document.getElementById('msg-input');
|
|
6235
|
+
inputEl.value = '/' + name + ' ';
|
|
6236
|
+
hideSkillDropdown();
|
|
6237
|
+
inputEl.focus();
|
|
6238
|
+
});
|
|
6239
|
+
});
|
|
6240
|
+
dropdown.style.display = 'block';
|
|
6241
|
+
}
|
|
6242
|
+
|
|
6243
|
+
function hideSkillDropdown() {
|
|
6244
|
+
const dropdown = document.getElementById('skill-dropdown');
|
|
6245
|
+
if (dropdown) dropdown.style.display = 'none';
|
|
6246
|
+
selectedSkillIndex = -1;
|
|
6247
|
+
}
|
|
6248
|
+
|
|
6249
|
+
function updateSkillSelection() {
|
|
6250
|
+
const dropdown = document.getElementById('skill-dropdown');
|
|
6251
|
+
if (!dropdown) return;
|
|
6252
|
+
const items = dropdown.querySelectorAll('.skill-item');
|
|
6253
|
+
items.forEach(function(el, i) {
|
|
6254
|
+
if (i === selectedSkillIndex) {
|
|
6255
|
+
el.classList.add('selected');
|
|
6256
|
+
} else {
|
|
6257
|
+
el.classList.remove('selected');
|
|
6258
|
+
}
|
|
6259
|
+
});
|
|
6260
|
+
}
|
|
6261
|
+
|
|
6164
6262
|
// \u2500\u2500 Approval modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6165
6263
|
function showApprovalModal(msg) {
|
|
6166
6264
|
const box = document.getElementById('approval-box');
|
|
@@ -6260,17 +6358,57 @@ function generateAdminHtml(version2) {
|
|
|
6260
6358
|
|
|
6261
6359
|
document.getElementById('send-btn').addEventListener('click', sendMessage);
|
|
6262
6360
|
|
|
6263
|
-
document.getElementById('msg-input').addEventListener('keydown', (e)
|
|
6361
|
+
document.getElementById('msg-input').addEventListener('keydown', function(e) {
|
|
6362
|
+
const dropdown = document.getElementById('skill-dropdown');
|
|
6363
|
+
const dropdownVisible = dropdown && dropdown.style.display !== 'none';
|
|
6364
|
+
if (dropdownVisible) {
|
|
6365
|
+
const items = dropdown.querySelectorAll('.skill-item');
|
|
6366
|
+
if (e.key === 'ArrowDown') {
|
|
6367
|
+
e.preventDefault();
|
|
6368
|
+
selectedSkillIndex = Math.min(selectedSkillIndex + 1, items.length - 1);
|
|
6369
|
+
updateSkillSelection();
|
|
6370
|
+
return;
|
|
6371
|
+
}
|
|
6372
|
+
if (e.key === 'ArrowUp') {
|
|
6373
|
+
e.preventDefault();
|
|
6374
|
+
selectedSkillIndex = Math.max(selectedSkillIndex - 1, -1);
|
|
6375
|
+
updateSkillSelection();
|
|
6376
|
+
return;
|
|
6377
|
+
}
|
|
6378
|
+
if (e.key === 'Enter' && selectedSkillIndex >= 0) {
|
|
6379
|
+
e.preventDefault();
|
|
6380
|
+
const selected = items[selectedSkillIndex];
|
|
6381
|
+
if (selected) {
|
|
6382
|
+
var name = selected.getAttribute('data-name');
|
|
6383
|
+
var inputEl = document.getElementById('msg-input');
|
|
6384
|
+
inputEl.value = '/' + name + ' ';
|
|
6385
|
+
hideSkillDropdown();
|
|
6386
|
+
inputEl.focus();
|
|
6387
|
+
}
|
|
6388
|
+
return;
|
|
6389
|
+
}
|
|
6390
|
+
if (e.key === 'Escape') {
|
|
6391
|
+
e.preventDefault();
|
|
6392
|
+
hideSkillDropdown();
|
|
6393
|
+
return;
|
|
6394
|
+
}
|
|
6395
|
+
}
|
|
6264
6396
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
6265
6397
|
e.preventDefault();
|
|
6266
6398
|
sendMessage();
|
|
6267
6399
|
}
|
|
6268
6400
|
});
|
|
6269
6401
|
|
|
6270
|
-
// Auto-resize textarea
|
|
6402
|
+
// Auto-resize textarea + slash command dropdown
|
|
6271
6403
|
document.getElementById('msg-input').addEventListener('input', function() {
|
|
6272
6404
|
this.style.height = 'auto';
|
|
6273
6405
|
this.style.height = Math.min(this.scrollHeight, 120) + 'px';
|
|
6406
|
+
const val = this.value;
|
|
6407
|
+
if (val.startsWith('/')) {
|
|
6408
|
+
loadSkills().then(function() { showSkillDropdown(val.slice(1)); });
|
|
6409
|
+
} else {
|
|
6410
|
+
hideSkillDropdown();
|
|
6411
|
+
}
|
|
6274
6412
|
});
|
|
6275
6413
|
|
|
6276
6414
|
// \u2500\u2500 Hamburger (mobile) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -6382,6 +6520,13 @@ function generateAdminHtml(version2) {
|
|
|
6382
6520
|
});
|
|
6383
6521
|
});
|
|
6384
6522
|
|
|
6523
|
+
// \u2500\u2500 Skill dropdown: hide on outside click \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6524
|
+
document.addEventListener('click', function(e) {
|
|
6525
|
+
if (!e.target.closest('#skill-dropdown') && !e.target.closest('#msg-input')) {
|
|
6526
|
+
hideSkillDropdown();
|
|
6527
|
+
}
|
|
6528
|
+
});
|
|
6529
|
+
|
|
6385
6530
|
// \u2500\u2500 Mobile keyboard adaptation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6386
6531
|
document.getElementById('msg-input').addEventListener('focus', () => {
|
|
6387
6532
|
setTimeout(() => {
|
|
@@ -6389,6 +6534,15 @@ function generateAdminHtml(version2) {
|
|
|
6389
6534
|
}, 300);
|
|
6390
6535
|
});
|
|
6391
6536
|
|
|
6537
|
+
if (window.visualViewport) {
|
|
6538
|
+
window.visualViewport.addEventListener('resize', function() {
|
|
6539
|
+
const bar = document.getElementById('input-bar');
|
|
6540
|
+
if (!bar) return;
|
|
6541
|
+
const offsetBottom = window.innerHeight - window.visualViewport.height - window.visualViewport.offsetTop;
|
|
6542
|
+
bar.style.bottom = Math.max(0, offsetBottom) + 'px';
|
|
6543
|
+
});
|
|
6544
|
+
}
|
|
6545
|
+
|
|
6392
6546
|
// \u2500\u2500 Global error boundary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6393
6547
|
window.onerror = function(msg, src, line) {
|
|
6394
6548
|
const wsStatusEl = document.getElementById('ws-status');
|
|
@@ -6664,6 +6818,9 @@ async function chatRoutes(app, opts) {
|
|
|
6664
6818
|
if (typeof body.systemPrompt === "string") {
|
|
6665
6819
|
sessionOpts.systemPrompt = body.systemPrompt;
|
|
6666
6820
|
}
|
|
6821
|
+
if (opts.autoApprove) {
|
|
6822
|
+
sessionOpts.autoApproveNormal = true;
|
|
6823
|
+
}
|
|
6667
6824
|
if (!opts.config.apiKey) {
|
|
6668
6825
|
return reply.code(400).send({
|
|
6669
6826
|
success: false,
|
|
@@ -6828,6 +6985,80 @@ async function systemRoutes(app, opts) {
|
|
|
6828
6985
|
});
|
|
6829
6986
|
}
|
|
6830
6987
|
|
|
6988
|
+
// src/web/routes/skills.ts
|
|
6989
|
+
import { readdir as readdir5, readFile as readFile10 } from "fs/promises";
|
|
6990
|
+
import { join as join15 } from "path";
|
|
6991
|
+
import os2 from "os";
|
|
6992
|
+
var BUILTIN_COMMANDS = [
|
|
6993
|
+
{ name: "clear", description: "Clear conversation history", type: "builtin" },
|
|
6994
|
+
{ name: "interrupt", description: "Interrupt current operation", type: "builtin" },
|
|
6995
|
+
{ name: "help", description: "Show help information", type: "builtin" }
|
|
6996
|
+
];
|
|
6997
|
+
function parseFrontmatter2(content) {
|
|
6998
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
6999
|
+
if (!match) return null;
|
|
7000
|
+
const frontmatter = match[1];
|
|
7001
|
+
const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
|
|
7002
|
+
if (!nameMatch) return null;
|
|
7003
|
+
const descMatch = frontmatter.match(/^description:\s*(?:>-?|[|>+])?\s*(.+)$/m);
|
|
7004
|
+
if (!descMatch) return null;
|
|
7005
|
+
const name = nameMatch[1].trim();
|
|
7006
|
+
const description = descMatch[1].trim();
|
|
7007
|
+
if (!name || !description) return null;
|
|
7008
|
+
return { name, description };
|
|
7009
|
+
}
|
|
7010
|
+
async function loadAgents() {
|
|
7011
|
+
const agentsDir = join15(os2.homedir(), ".claude", "agents");
|
|
7012
|
+
let files;
|
|
7013
|
+
try {
|
|
7014
|
+
const entries = await readdir5(agentsDir);
|
|
7015
|
+
files = entries.filter((f) => f.endsWith(".md"));
|
|
7016
|
+
} catch {
|
|
7017
|
+
return [];
|
|
7018
|
+
}
|
|
7019
|
+
const items = [];
|
|
7020
|
+
for (const file of files) {
|
|
7021
|
+
try {
|
|
7022
|
+
const content = await readFile10(join15(agentsDir, file), "utf-8");
|
|
7023
|
+
const parsed = parseFrontmatter2(content);
|
|
7024
|
+
if (parsed) {
|
|
7025
|
+
items.push({ ...parsed, type: "agent" });
|
|
7026
|
+
}
|
|
7027
|
+
} catch {
|
|
7028
|
+
}
|
|
7029
|
+
}
|
|
7030
|
+
return items;
|
|
7031
|
+
}
|
|
7032
|
+
async function loadSkills() {
|
|
7033
|
+
const skillsDir = join15(process.cwd(), "skills");
|
|
7034
|
+
let subdirs;
|
|
7035
|
+
try {
|
|
7036
|
+
const entries = await readdir5(skillsDir, { withFileTypes: true });
|
|
7037
|
+
subdirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
7038
|
+
} catch {
|
|
7039
|
+
return [];
|
|
7040
|
+
}
|
|
7041
|
+
const items = [];
|
|
7042
|
+
for (const subdir of subdirs) {
|
|
7043
|
+
try {
|
|
7044
|
+
const skillFile = join15(skillsDir, subdir, "SKILL.md");
|
|
7045
|
+
const content = await readFile10(skillFile, "utf-8");
|
|
7046
|
+
const parsed = parseFrontmatter2(content);
|
|
7047
|
+
if (parsed) {
|
|
7048
|
+
items.push({ ...parsed, type: "skill" });
|
|
7049
|
+
}
|
|
7050
|
+
} catch {
|
|
7051
|
+
}
|
|
7052
|
+
}
|
|
7053
|
+
return items;
|
|
7054
|
+
}
|
|
7055
|
+
async function skillsRoutes(app) {
|
|
7056
|
+
app.get("/api/skills", async (_request, _reply) => {
|
|
7057
|
+
const [agents, skills] = await Promise.all([loadAgents(), loadSkills()]);
|
|
7058
|
+
return [...BUILTIN_COMMANDS, ...agents, ...skills];
|
|
7059
|
+
});
|
|
7060
|
+
}
|
|
7061
|
+
|
|
6831
7062
|
// src/web/server.ts
|
|
6832
7063
|
async function buildServer(opts) {
|
|
6833
7064
|
const app = Fastify({ logger: false });
|
|
@@ -6854,8 +7085,9 @@ async function buildServer(opts) {
|
|
|
6854
7085
|
await app.register(sessionsRoutes, { config: opts.config, manager: opts.manager });
|
|
6855
7086
|
await app.register(configRoutes, { config: opts.config, save: saveConfig });
|
|
6856
7087
|
await app.register(automationRoutes, { config: opts.config });
|
|
6857
|
-
await app.register(chatRoutes, { config: opts.config, manager: opts.manager });
|
|
7088
|
+
await app.register(chatRoutes, { config: opts.config, manager: opts.manager, autoApprove: opts.autoApprove });
|
|
6858
7089
|
await app.register(systemRoutes, { version: opts.version });
|
|
7090
|
+
await app.register(skillsRoutes);
|
|
6859
7091
|
await app.register(sessionHubRoutes, { manager: opts.manager });
|
|
6860
7092
|
return app;
|
|
6861
7093
|
}
|
|
@@ -6951,18 +7183,28 @@ var SessionRuntime = class {
|
|
|
6951
7183
|
messages;
|
|
6952
7184
|
config;
|
|
6953
7185
|
model;
|
|
7186
|
+
autoApproveNormal;
|
|
6954
7187
|
abortController = null;
|
|
6955
7188
|
_stopAfterToolRound = false;
|
|
6956
7189
|
_turnCount = 0;
|
|
6957
7190
|
_totalTokens = 0;
|
|
6958
7191
|
startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6959
7192
|
lastActivity = (/* @__PURE__ */ new Date()).toISOString();
|
|
6960
|
-
|
|
7193
|
+
_title = "New Session";
|
|
7194
|
+
logger;
|
|
6961
7195
|
constructor(config2, opts = {}) {
|
|
6962
7196
|
this.id = opts.sessionId ?? crypto.randomUUID();
|
|
6963
7197
|
this.config = config2;
|
|
6964
7198
|
this.llm = opts.llm ?? createProvider(resolveActiveProfile(config2));
|
|
6965
7199
|
this.model = config2.model;
|
|
7200
|
+
this.autoApproveNormal = opts.autoApproveNormal ?? false;
|
|
7201
|
+
if (config2.logDir) {
|
|
7202
|
+
this.logger = createLogger(config2.logDir, /* @__PURE__ */ new Date());
|
|
7203
|
+
const meta = createSessionMetadata(this.logger.filePath, this.model, this.id);
|
|
7204
|
+
writeSessionMetadata(this.logger.filePath, meta);
|
|
7205
|
+
} else {
|
|
7206
|
+
this.logger = null;
|
|
7207
|
+
}
|
|
6966
7208
|
const systemContent = opts.systemPrompt ?? (config2.systemPrompt ?? DEFAULT_SYSTEM_PROMPT);
|
|
6967
7209
|
this.messages = systemContent ? [{ role: "system", content: systemContent }] : [];
|
|
6968
7210
|
if (opts.initialMessages) {
|
|
@@ -6987,6 +7229,7 @@ var SessionRuntime = class {
|
|
|
6987
7229
|
*/
|
|
6988
7230
|
async submit(userInput) {
|
|
6989
7231
|
this.messages.push({ role: "user", content: userInput });
|
|
7232
|
+
this.logger?.append({ ts: (/* @__PURE__ */ new Date()).toISOString(), role: "user", content: userInput });
|
|
6990
7233
|
this.abortController = new AbortController();
|
|
6991
7234
|
const signal = this.abortController.signal;
|
|
6992
7235
|
try {
|
|
@@ -7002,6 +7245,17 @@ var SessionRuntime = class {
|
|
|
7002
7245
|
}
|
|
7003
7246
|
this._turnCount++;
|
|
7004
7247
|
this.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
|
|
7248
|
+
if (this.logger) {
|
|
7249
|
+
if (this._turnCount === 1) {
|
|
7250
|
+
this._title = generateTitle(userInput);
|
|
7251
|
+
}
|
|
7252
|
+
updateSessionMetadata(this.logger.filePath, {
|
|
7253
|
+
title: this._title,
|
|
7254
|
+
turnCount: this._turnCount,
|
|
7255
|
+
totalTokens: this._totalTokens,
|
|
7256
|
+
lastActivity: this.lastActivity
|
|
7257
|
+
});
|
|
7258
|
+
}
|
|
7005
7259
|
this._status = "idle";
|
|
7006
7260
|
this.bus.emit({ type: "session.idle", sessionId: this.id });
|
|
7007
7261
|
}
|
|
@@ -7064,12 +7318,19 @@ var SessionRuntime = class {
|
|
|
7064
7318
|
...assistantReasoning ? { reasoning_content: assistantReasoning } : {},
|
|
7065
7319
|
...lastReasoningDetails ? { reasoning_details: lastReasoningDetails } : {}
|
|
7066
7320
|
});
|
|
7321
|
+
this.logger?.append({
|
|
7322
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7323
|
+
role: "assistant",
|
|
7324
|
+
content: assistantText || null,
|
|
7325
|
+
tool_calls: toolCalls.map((tc) => ({ id: tc.id, type: "function", function: { name: tc.name, arguments: tc.arguments } }))
|
|
7326
|
+
});
|
|
7067
7327
|
for (const tc of toolCalls) {
|
|
7068
7328
|
if (signal.aborted) break;
|
|
7069
7329
|
this.bus.emit({ type: "tool.started", callId: tc.id, toolName: tc.name, argsPreview: tc.arguments });
|
|
7070
7330
|
const toolResult = await this.executeToolCall(tc, signal);
|
|
7071
7331
|
this.bus.emit({ type: "tool.completed", callId: tc.id, toolName: tc.name, output: toolResult });
|
|
7072
7332
|
this.messages.push({ role: "tool", tool_call_id: tc.id, content: toolResult });
|
|
7333
|
+
this.logger?.append({ ts: (/* @__PURE__ */ new Date()).toISOString(), role: "tool", tool_call_id: tc.id, content: toolResult });
|
|
7073
7334
|
}
|
|
7074
7335
|
if (this._stopAfterToolRound) {
|
|
7075
7336
|
this._stopAfterToolRound = false;
|
|
@@ -7082,6 +7343,7 @@ var SessionRuntime = class {
|
|
|
7082
7343
|
content: assistantText,
|
|
7083
7344
|
...assistantReasoning ? { reasoning_content: assistantReasoning } : {}
|
|
7084
7345
|
});
|
|
7346
|
+
this.logger?.append({ ts: (/* @__PURE__ */ new Date()).toISOString(), role: "assistant", content: assistantText || null });
|
|
7085
7347
|
}
|
|
7086
7348
|
break;
|
|
7087
7349
|
}
|
|
@@ -7101,17 +7363,20 @@ var SessionRuntime = class {
|
|
|
7101
7363
|
const parsed = JSON.parse(args);
|
|
7102
7364
|
const cls = classifyCommand(parsed.command, this.config.dangerousPatterns);
|
|
7103
7365
|
if (cls === "normal" || cls === "danger") {
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7366
|
+
if (cls === "normal" && this.autoApproveNormal) {
|
|
7367
|
+
} else {
|
|
7368
|
+
this._status = "awaiting_confirm";
|
|
7369
|
+
const kind = cls === "danger" ? "danger" : "normal";
|
|
7370
|
+
const prompt = cls === "danger" ? `\u26A0\uFE0F DANGEROUS COMMAND: ${parsed.command}` : `Execute command: ${parsed.command}
|
|
7107
7371
|
Proceed?`;
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7372
|
+
const { id: reqId, promise } = this.approvals.request(kind, prompt);
|
|
7373
|
+
this.bus.emit({ type: "approval.requested", requestId: reqId, kind, prompt });
|
|
7374
|
+
const approved = await promise;
|
|
7375
|
+
this._status = "tool_calling";
|
|
7376
|
+
if (!approved) {
|
|
7377
|
+
this._stopAfterToolRound = true;
|
|
7378
|
+
return SKIP_MESSAGE;
|
|
7379
|
+
}
|
|
7115
7380
|
}
|
|
7116
7381
|
}
|
|
7117
7382
|
if (signal.aborted) return SKIP_MESSAGE;
|
|
@@ -7191,7 +7456,7 @@ Proceed?`;
|
|
|
7191
7456
|
snapshot() {
|
|
7192
7457
|
return {
|
|
7193
7458
|
id: this.id,
|
|
7194
|
-
title: this.
|
|
7459
|
+
title: this._title,
|
|
7195
7460
|
model: this.model,
|
|
7196
7461
|
status: this._status,
|
|
7197
7462
|
turnCount: this._turnCount,
|
|
@@ -7389,6 +7654,7 @@ if (rawArgs[0] === "sessions") {
|
|
|
7389
7654
|
if (rawArgs[0] === "web") {
|
|
7390
7655
|
let webPort = 4310;
|
|
7391
7656
|
let webHost = "127.0.0.1";
|
|
7657
|
+
let webAutoApprove = false;
|
|
7392
7658
|
for (let i = 1; i < rawArgs.length; i++) {
|
|
7393
7659
|
const arg = rawArgs[i];
|
|
7394
7660
|
const next = rawArgs[i + 1];
|
|
@@ -7399,6 +7665,8 @@ if (rawArgs[0] === "web") {
|
|
|
7399
7665
|
} else if (arg === "--host" && next && !next.startsWith("-")) {
|
|
7400
7666
|
webHost = next;
|
|
7401
7667
|
i++;
|
|
7668
|
+
} else if (arg === "--auto") {
|
|
7669
|
+
webAutoApprove = true;
|
|
7402
7670
|
}
|
|
7403
7671
|
}
|
|
7404
7672
|
const token = generateAccessToken();
|
|
@@ -7407,7 +7675,8 @@ if (rawArgs[0] === "web") {
|
|
|
7407
7675
|
config: finalConfig,
|
|
7408
7676
|
manager,
|
|
7409
7677
|
token,
|
|
7410
|
-
version: VERSION
|
|
7678
|
+
version: VERSION,
|
|
7679
|
+
autoApprove: webAutoApprove
|
|
7411
7680
|
});
|
|
7412
7681
|
await server.listen({ port: webPort, host: webHost });
|
|
7413
7682
|
const browserHost = webHost === "0.0.0.0" ? "localhost" : webHost;
|