codemini-cli 0.6.3 → 0.6.4
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/codemini-web/dist/assets/{AboutDialog-jgqGjQgl.js → AboutDialog-MRopwNIL.js} +2 -2
- package/codemini-web/dist/assets/CodeWikiPanel-UpK5xGE3.js +1 -0
- package/codemini-web/dist/assets/ConfigDialog-CNl28wsj.js +1 -0
- package/codemini-web/dist/assets/GitDiffDialog-gSysUg2J.js +3 -0
- package/codemini-web/dist/assets/{MemoryDialog-BhxQgG0I.js → MemoryDialog-DFUmo3Kl.js} +3 -3
- package/codemini-web/dist/assets/MessageBubble-CGnnViv0.js +12 -0
- package/codemini-web/dist/assets/PatchDiff-B8rwvEg5.js +230 -0
- package/codemini-web/dist/assets/ProjectSelector-BF59M1zb.js +1 -0
- package/codemini-web/dist/assets/{SkillDialog-DxS43NpR.js → SkillDialog-CQTjbSiw.js} +4 -4
- package/codemini-web/dist/assets/SoulDialog-BLjUGqqB.js +1 -0
- package/codemini-web/dist/assets/chevron-right--85xg7qk.js +1 -0
- package/codemini-web/dist/assets/{chunk-BO2N2NFS-Budy_hfO.js → chunk-BO2N2NFS-6uELoidu.js} +6 -6
- package/codemini-web/dist/assets/{highlighted-body-OFNGDK62-CQS1PAvD.js → highlighted-body-OFNGDK62-gb1UMBZ5.js} +1 -1
- package/codemini-web/dist/assets/index-1xqD0R5t.css +2 -0
- package/codemini-web/dist/assets/index-CDXQGwPs.js +65 -0
- package/codemini-web/dist/assets/{input-CNQgbKe6.js → input-Ca8O_061.js} +1 -1
- package/codemini-web/dist/assets/lib-BXWizt13.js +1 -0
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-ROliF8Yd.js +1 -0
- package/codemini-web/dist/assets/{pencil-Ce_LFiEh.js → pencil-BhT11Ztp.js} +1 -1
- package/codemini-web/dist/assets/{refresh-cw-BKL-AZu5.js → refresh-cw-D7R5Lth6.js} +1 -1
- package/codemini-web/dist/assets/select-DBvcHBzs.js +1 -0
- package/codemini-web/dist/assets/{trash-2-KmAlCwXd.js → trash-2-BfNZcWfX.js} +1 -1
- package/codemini-web/dist/index.html +2 -2
- package/codemini-web/lib/runtime-bridge.js +325 -296
- package/codemini-web/server.js +310 -243
- package/package.json +1 -1
- package/src/core/agent-loop.js +188 -97
- package/src/core/chat-runtime.js +674 -571
- package/src/core/config-store.js +11 -3
- package/src/core/git-oplog-change-tracker.js +387 -0
- package/src/core/non-git-backup.js +116 -0
- package/src/core/paths.js +123 -123
- package/src/core/session-store.js +148 -99
- package/src/core/tools.js +499 -456
- package/src/tui/chat-app.js +196 -56
- package/codemini-web/dist/assets/CodeWikiPanel-EPuoerNv.js +0 -1
- package/codemini-web/dist/assets/ConfigDialog-B5IGZCc9.js +0 -1
- package/codemini-web/dist/assets/GitDiffDialog-Bb_Tw5ZK.js +0 -222
- package/codemini-web/dist/assets/MessageBubble-wUff4GP4.js +0 -6
- package/codemini-web/dist/assets/ProjectSelector-C0leTf6f.js +0 -1
- package/codemini-web/dist/assets/SoulDialog-XDTEGWvH.js +0 -1
- package/codemini-web/dist/assets/chevron-right-Dbzw7YzA.js +0 -1
- package/codemini-web/dist/assets/index-D0EGtNPr.js +0 -65
- package/codemini-web/dist/assets/index-wOUf3WkN.css +0 -2
- package/codemini-web/dist/assets/lib-BOngVP_M.js +0 -11
- package/codemini-web/dist/assets/lib-DrOTTm_N.js +0 -1
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-DrBu5KyC.js +0 -1
- package/codemini-web/dist/assets/select-BZXfigic.js +0 -1
package/src/core/paths.js
CHANGED
|
@@ -1,123 +1,123 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
const GLOBAL_APP_DIR = 'codemini-global';
|
|
5
|
-
const PROJECT_APP_DIR = '.codemini';
|
|
6
|
-
const PROJECT_INDEX_DIR = '.codemini';
|
|
7
|
-
|
|
8
|
-
export function getBaseConfigDir() {
|
|
9
|
-
if (process.env.CODEMINI_GLOBAL_DIR) {
|
|
10
|
-
return process.env.CODEMINI_GLOBAL_DIR;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
if (process.platform === 'win32') {
|
|
14
|
-
const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
|
|
15
|
-
return path.join(appData, GLOBAL_APP_DIR);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (process.platform === 'darwin') {
|
|
19
|
-
return path.join(os.homedir(), 'Library', 'Preferences', GLOBAL_APP_DIR);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (process.env.XDG_CONFIG_HOME) {
|
|
23
|
-
return path.join(process.env.XDG_CONFIG_HOME, GLOBAL_APP_DIR);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return path.join(process.cwd(), '.codemini-global');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function getLegacyConfigDir() {
|
|
30
|
-
return getBaseConfigDir();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function getConfigFilePath() {
|
|
34
|
-
return path.join(getBaseConfigDir(), 'config.json');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function getSessionsDir() {
|
|
38
|
-
return path.join(getBaseConfigDir(), 'sessions');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function getSkillsDir() {
|
|
42
|
-
return path.join(getBaseConfigDir(), 'skills');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function getSkillRegistryPath() {
|
|
46
|
-
return path.join(getBaseConfigDir(), 'skill-registry.json');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function getCommandsDir() {
|
|
50
|
-
return path.join(getBaseConfigDir(), 'commands');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function getMemoryDir() {
|
|
54
|
-
return path.join(getBaseConfigDir(), 'memory');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function getInputHistoryFilePath() {
|
|
58
|
-
return path.join(getBaseConfigDir(), 'input-history.json');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function getProjectWorkspaceDir(cwd = process.cwd()) {
|
|
62
|
-
return path.join(cwd, PROJECT_APP_DIR);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function getProjectCommandsDir(cwd = process.cwd()) {
|
|
66
|
-
return path.join(getProjectWorkspaceDir(cwd), 'commands');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function getProjectSkillsDir(cwd = process.cwd()) {
|
|
70
|
-
return path.join(getProjectWorkspaceDir(cwd), 'skills');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function getProjectSpecsDir(cwd = process.cwd(), sessionId = '') {
|
|
74
|
-
return sessionId
|
|
75
|
-
? path.join(getProjectWorkspaceDir(cwd), 'specs', String(sessionId))
|
|
76
|
-
: path.join(getProjectWorkspaceDir(cwd), 'specs');
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function getProjectPlansDir(cwd = process.cwd(), sessionId = '') {
|
|
80
|
-
return sessionId
|
|
81
|
-
? path.join(getProjectWorkspaceDir(cwd), 'plans', String(sessionId))
|
|
82
|
-
: path.join(getProjectWorkspaceDir(cwd), 'plans');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function getProjectCheckpointsDir(cwd = process.cwd()) {
|
|
86
|
-
return path.join(getProjectWorkspaceDir(cwd), 'checkpoints');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function getProjectTasksDir(cwd = process.cwd()) {
|
|
90
|
-
return path.join(getProjectWorkspaceDir(cwd), 'tasks');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function getProjectLegacyTasksFilePath(cwd = process.cwd()) {
|
|
94
|
-
return path.join(getProjectWorkspaceDir(cwd), 'tasks.json');
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function getProjectMapPath(cwd = process.cwd()) {
|
|
98
|
-
return path.join(cwd, PROJECT_INDEX_DIR, 'project-map.json');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function getFileIndexPath(cwd = process.cwd()) {
|
|
102
|
-
return path.join(cwd, PROJECT_INDEX_DIR, 'file-index.json');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function getProjectIndexDir(cwd = process.cwd()) {
|
|
106
|
-
return path.join(cwd, PROJECT_INDEX_DIR);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function getProjectMemoryDir(cwd = process.cwd()) {
|
|
110
|
-
return path.join(getProjectIndexDir(cwd), 'memory');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export function getInboxDir() {
|
|
114
|
-
return path.join(getMemoryDir(), 'inbox');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function getArchiveDir() {
|
|
118
|
-
return path.join(getMemoryDir(), 'archive');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function getDreamAuditDir() {
|
|
122
|
-
return path.join(getMemoryDir(), 'audit');
|
|
123
|
-
}
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const GLOBAL_APP_DIR = 'codemini-global';
|
|
5
|
+
const PROJECT_APP_DIR = '.codemini';
|
|
6
|
+
const PROJECT_INDEX_DIR = '.codemini';
|
|
7
|
+
|
|
8
|
+
export function getBaseConfigDir() {
|
|
9
|
+
if (process.env.CODEMINI_GLOBAL_DIR) {
|
|
10
|
+
return process.env.CODEMINI_GLOBAL_DIR;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (process.platform === 'win32') {
|
|
14
|
+
const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
|
|
15
|
+
return path.join(appData, GLOBAL_APP_DIR);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (process.platform === 'darwin') {
|
|
19
|
+
return path.join(os.homedir(), 'Library', 'Preferences', GLOBAL_APP_DIR);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (process.env.XDG_CONFIG_HOME) {
|
|
23
|
+
return path.join(process.env.XDG_CONFIG_HOME, GLOBAL_APP_DIR);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return path.join(process.cwd(), '.codemini-global');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getLegacyConfigDir() {
|
|
30
|
+
return getBaseConfigDir();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getConfigFilePath() {
|
|
34
|
+
return path.join(getBaseConfigDir(), 'config.json');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getSessionsDir() {
|
|
38
|
+
return path.join(getBaseConfigDir(), 'sessions');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getSkillsDir() {
|
|
42
|
+
return path.join(getBaseConfigDir(), 'skills');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function getSkillRegistryPath() {
|
|
46
|
+
return path.join(getBaseConfigDir(), 'skill-registry.json');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function getCommandsDir() {
|
|
50
|
+
return path.join(getBaseConfigDir(), 'commands');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function getMemoryDir() {
|
|
54
|
+
return path.join(getBaseConfigDir(), 'memory');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function getInputHistoryFilePath() {
|
|
58
|
+
return path.join(getBaseConfigDir(), 'input-history.json');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function getProjectWorkspaceDir(cwd = process.cwd()) {
|
|
62
|
+
return path.join(cwd, PROJECT_APP_DIR);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getProjectCommandsDir(cwd = process.cwd()) {
|
|
66
|
+
return path.join(getProjectWorkspaceDir(cwd), 'commands');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function getProjectSkillsDir(cwd = process.cwd()) {
|
|
70
|
+
return path.join(getProjectWorkspaceDir(cwd), 'skills');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getProjectSpecsDir(cwd = process.cwd(), sessionId = '') {
|
|
74
|
+
return sessionId
|
|
75
|
+
? path.join(getProjectWorkspaceDir(cwd), 'specs', String(sessionId))
|
|
76
|
+
: path.join(getProjectWorkspaceDir(cwd), 'specs');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getProjectPlansDir(cwd = process.cwd(), sessionId = '') {
|
|
80
|
+
return sessionId
|
|
81
|
+
? path.join(getProjectWorkspaceDir(cwd), 'plans', String(sessionId))
|
|
82
|
+
: path.join(getProjectWorkspaceDir(cwd), 'plans');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function getProjectCheckpointsDir(cwd = process.cwd()) {
|
|
86
|
+
return path.join(getProjectWorkspaceDir(cwd), 'checkpoints');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function getProjectTasksDir(cwd = process.cwd()) {
|
|
90
|
+
return path.join(getProjectWorkspaceDir(cwd), 'tasks');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function getProjectLegacyTasksFilePath(cwd = process.cwd()) {
|
|
94
|
+
return path.join(getProjectWorkspaceDir(cwd), 'tasks.json');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function getProjectMapPath(cwd = process.cwd()) {
|
|
98
|
+
return path.join(cwd, PROJECT_INDEX_DIR, 'project-map.json');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function getFileIndexPath(cwd = process.cwd()) {
|
|
102
|
+
return path.join(cwd, PROJECT_INDEX_DIR, 'file-index.json');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getProjectIndexDir(cwd = process.cwd()) {
|
|
106
|
+
return path.join(cwd, PROJECT_INDEX_DIR);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function getProjectMemoryDir(cwd = process.cwd()) {
|
|
110
|
+
return path.join(getProjectIndexDir(cwd), 'memory');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function getInboxDir() {
|
|
114
|
+
return path.join(getMemoryDir(), 'inbox');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function getArchiveDir() {
|
|
118
|
+
return path.join(getMemoryDir(), 'archive');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function getDreamAuditDir() {
|
|
122
|
+
return path.join(getMemoryDir(), 'audit');
|
|
123
|
+
}
|
|
@@ -17,7 +17,7 @@ function createSessionId() {
|
|
|
17
17
|
return `${ts}-${rand}`;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function sanitizeToolCall(tc, index) {
|
|
20
|
+
function sanitizeToolCall(tc, index) {
|
|
21
21
|
const id = String(tc?.id || `tc-${index + 1}`);
|
|
22
22
|
const fnName = String(tc?.function?.name || tc?.name || '').trim();
|
|
23
23
|
const fnArgsRaw = tc?.function?.arguments ?? tc?.arguments ?? '{}';
|
|
@@ -31,58 +31,67 @@ function sanitizeToolCall(tc, index) {
|
|
|
31
31
|
arguments: fnArgs
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
|
-
if (Number.isFinite(Number(tc?.durationMs))) out.durationMs = Number(tc.durationMs);
|
|
34
|
+
if (Number.isFinite(Number(tc?.durationMs))) out.durationMs = Number(tc.durationMs);
|
|
35
35
|
if (typeof tc?.summary === 'string' && tc.summary.trim()) out.summary = tc.summary.trim();
|
|
36
36
|
if (typeof tc?.status === 'string' && tc.status.trim()) out.status = tc.status.trim();
|
|
37
|
+
if (tc?.resultMeta && typeof tc.resultMeta === 'object' && !Array.isArray(tc.resultMeta)) {
|
|
38
|
+
out.resultMeta = { ...tc.resultMeta };
|
|
39
|
+
}
|
|
37
40
|
const fileChange = sanitizeFileChange(tc?.fileChange);
|
|
38
41
|
if (fileChange) out.fileChange = fileChange;
|
|
42
|
+
if (Array.isArray(tc?.fileChanges)) {
|
|
43
|
+
const fileChanges = tc.fileChanges.map(sanitizeFileChange).filter(Boolean);
|
|
44
|
+
if (fileChanges.length > 0) out.fileChanges = fileChanges;
|
|
45
|
+
}
|
|
39
46
|
return out;
|
|
40
47
|
}
|
|
41
|
-
|
|
42
|
-
function sanitizeFileChange(change) {
|
|
43
|
-
if (!change || typeof change !== 'object') return null;
|
|
44
|
-
const filePath = String(change.path || '').trim();
|
|
45
|
-
if (!filePath) return null;
|
|
46
|
-
const action = String(change.action || '').trim();
|
|
47
|
-
return {
|
|
48
|
-
path: filePath,
|
|
49
|
-
action: action === 'create' || action === 'delete' ? action : 'edit',
|
|
48
|
+
|
|
49
|
+
function sanitizeFileChange(change) {
|
|
50
|
+
if (!change || typeof change !== 'object') return null;
|
|
51
|
+
const filePath = String(change.path || '').trim();
|
|
52
|
+
if (!filePath) return null;
|
|
53
|
+
const action = String(change.action || '').trim();
|
|
54
|
+
return {
|
|
55
|
+
path: filePath,
|
|
56
|
+
action: action === 'create' || action === 'delete' ? action : 'edit',
|
|
50
57
|
linesAdded: Math.max(0, Math.round(Number(change.linesAdded || 0))),
|
|
51
58
|
linesRemoved: Math.max(0, Math.round(Number(change.linesRemoved || 0))),
|
|
52
59
|
changedLine: Math.max(0, Math.round(Number(change.changedLine || 0))),
|
|
53
|
-
diffPreview: String(change.diffPreview || '')
|
|
60
|
+
diffPreview: String(change.diffPreview || ''),
|
|
61
|
+
changeSetId: String(change.changeSetId || ''),
|
|
62
|
+
patchRef: String(change.patchRef || '')
|
|
54
63
|
};
|
|
55
64
|
}
|
|
56
65
|
|
|
57
|
-
function normalizeWhitespace(value) {
|
|
58
|
-
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function sanitizeUsage(usage) {
|
|
62
|
-
if (!usage || typeof usage !== 'object') return null;
|
|
63
|
-
const out = {};
|
|
64
|
-
const copyNumber = (from, to = from) => {
|
|
65
|
-
const value = Number(usage?.[from]);
|
|
66
|
-
if (Number.isFinite(value)) out[to] = Math.max(0, Math.round(value));
|
|
67
|
-
};
|
|
68
|
-
copyNumber('inputTokens');
|
|
69
|
-
copyNumber('outputTokens');
|
|
70
|
-
copyNumber('totalTokens');
|
|
71
|
-
copyNumber('cachedInputTokens');
|
|
72
|
-
copyNumber('cacheMissInputTokens');
|
|
73
|
-
copyNumber('cacheWriteInputTokens');
|
|
74
|
-
copyNumber('reasoningOutputTokens');
|
|
75
|
-
copyNumber('requests');
|
|
76
|
-
if (Array.isArray(usage.raw) && usage.raw.length > 0) {
|
|
77
|
-
out.raw = usage.raw.filter((item) => item && typeof item === 'object').map((item) => ({ ...item }));
|
|
78
|
-
} else if (usage.raw && typeof usage.raw === 'object') {
|
|
79
|
-
out.raw = [{ ...usage.raw }];
|
|
80
|
-
}
|
|
81
|
-
return Object.keys(out).length ? out : null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function stripMarkdown(value) {
|
|
85
|
-
return normalizeWhitespace(value)
|
|
66
|
+
function normalizeWhitespace(value) {
|
|
67
|
+
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function sanitizeUsage(usage) {
|
|
71
|
+
if (!usage || typeof usage !== 'object') return null;
|
|
72
|
+
const out = {};
|
|
73
|
+
const copyNumber = (from, to = from) => {
|
|
74
|
+
const value = Number(usage?.[from]);
|
|
75
|
+
if (Number.isFinite(value)) out[to] = Math.max(0, Math.round(value));
|
|
76
|
+
};
|
|
77
|
+
copyNumber('inputTokens');
|
|
78
|
+
copyNumber('outputTokens');
|
|
79
|
+
copyNumber('totalTokens');
|
|
80
|
+
copyNumber('cachedInputTokens');
|
|
81
|
+
copyNumber('cacheMissInputTokens');
|
|
82
|
+
copyNumber('cacheWriteInputTokens');
|
|
83
|
+
copyNumber('reasoningOutputTokens');
|
|
84
|
+
copyNumber('requests');
|
|
85
|
+
if (Array.isArray(usage.raw) && usage.raw.length > 0) {
|
|
86
|
+
out.raw = usage.raw.filter((item) => item && typeof item === 'object').map((item) => ({ ...item }));
|
|
87
|
+
} else if (usage.raw && typeof usage.raw === 'object') {
|
|
88
|
+
out.raw = [{ ...usage.raw }];
|
|
89
|
+
}
|
|
90
|
+
return Object.keys(out).length ? out : null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function stripMarkdown(value) {
|
|
94
|
+
return normalizeWhitespace(value)
|
|
86
95
|
.replace(/```[\s\S]*?```/g, ' ')
|
|
87
96
|
.replace(/`([^`]*)`/g, '$1')
|
|
88
97
|
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
|
|
@@ -111,56 +120,63 @@ function sanitizeMessage(msg) {
|
|
|
111
120
|
};
|
|
112
121
|
|
|
113
122
|
if (typeof msg?.model_content === 'string' && msg.model_content) out.model_content = msg.model_content;
|
|
114
|
-
if (msg?.tool_call_id) out.tool_call_id = String(msg.tool_call_id);
|
|
115
|
-
if (Number.isFinite(Number(msg?.tool_duration_ms))) out.tool_duration_ms = Number(msg.tool_duration_ms);
|
|
123
|
+
if (msg?.tool_call_id) out.tool_call_id = String(msg.tool_call_id);
|
|
124
|
+
if (Number.isFinite(Number(msg?.tool_duration_ms))) out.tool_duration_ms = Number(msg.tool_duration_ms);
|
|
116
125
|
if (typeof msg?.tool_summary === 'string' && msg.tool_summary.trim()) out.tool_summary = msg.tool_summary.trim();
|
|
117
126
|
if (typeof msg?.tool_status === 'string' && msg.tool_status.trim()) out.tool_status = msg.tool_status.trim();
|
|
127
|
+
if (msg?.tool_result_meta && typeof msg.tool_result_meta === 'object' && !Array.isArray(msg.tool_result_meta)) {
|
|
128
|
+
out.tool_result_meta = { ...msg.tool_result_meta };
|
|
129
|
+
}
|
|
118
130
|
const toolFileChange = sanitizeFileChange(msg?.tool_file_change);
|
|
119
131
|
if (toolFileChange) out.tool_file_change = toolFileChange;
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
out.reasoning_content = msg.reasoning_content;
|
|
124
|
-
}
|
|
125
|
-
if (typeof msg?.reasoning_started_at === 'string' && msg.reasoning_started_at.trim()) {
|
|
126
|
-
out.reasoning_started_at = msg.reasoning_started_at;
|
|
127
|
-
}
|
|
128
|
-
if (typeof msg?.reasoning_ended_at === 'string' && msg.reasoning_ended_at.trim()) {
|
|
129
|
-
out.reasoning_ended_at = msg.reasoning_ended_at;
|
|
130
|
-
}
|
|
131
|
-
if (Number.isFinite(Number(msg?.reasoning_duration_ms))) {
|
|
132
|
-
out.reasoning_duration_ms = Math.max(0, Math.round(Number(msg.reasoning_duration_ms)));
|
|
132
|
+
if (Array.isArray(msg?.tool_file_changes)) {
|
|
133
|
+
const toolFileChanges = msg.tool_file_changes.map(sanitizeFileChange).filter(Boolean);
|
|
134
|
+
if (toolFileChanges.length > 0) out.tool_file_changes = toolFileChanges;
|
|
133
135
|
}
|
|
134
|
-
if (
|
|
136
|
+
if (typeof msg?.name === 'string' && msg.name.trim()) out.name = msg.name.trim();
|
|
137
|
+
if (typeof msg?.at === 'string' && msg.at.trim()) out.at = msg.at;
|
|
138
|
+
if (typeof msg?.reasoning_content === 'string' && msg.reasoning_content) {
|
|
139
|
+
out.reasoning_content = msg.reasoning_content;
|
|
140
|
+
}
|
|
141
|
+
if (typeof msg?.reasoning_started_at === 'string' && msg.reasoning_started_at.trim()) {
|
|
142
|
+
out.reasoning_started_at = msg.reasoning_started_at;
|
|
143
|
+
}
|
|
144
|
+
if (typeof msg?.reasoning_ended_at === 'string' && msg.reasoning_ended_at.trim()) {
|
|
145
|
+
out.reasoning_ended_at = msg.reasoning_ended_at;
|
|
146
|
+
}
|
|
147
|
+
if (Number.isFinite(Number(msg?.reasoning_duration_ms))) {
|
|
148
|
+
out.reasoning_duration_ms = Math.max(0, Math.round(Number(msg.reasoning_duration_ms)));
|
|
149
|
+
}
|
|
150
|
+
if (Array.isArray(msg?.reasoning_details) && msg.reasoning_details.length > 0) {
|
|
135
151
|
out.reasoning_details = msg.reasoning_details
|
|
136
152
|
.filter((detail) => detail && typeof detail === 'object')
|
|
137
153
|
.map((detail) => ({ ...detail }));
|
|
138
154
|
}
|
|
139
155
|
|
|
140
|
-
if (Array.isArray(msg?.tool_calls)) {
|
|
141
|
-
const toolCalls = msg.tool_calls.map(sanitizeToolCall).filter(Boolean);
|
|
142
|
-
if (toolCalls.length > 0) out.tool_calls = toolCalls;
|
|
143
|
-
}
|
|
144
|
-
if (Array.isArray(msg?.file_changes)) {
|
|
145
|
-
const fileChanges = msg.file_changes.map(sanitizeFileChange).filter(Boolean);
|
|
146
|
-
if (fileChanges.length > 0) out.file_changes = fileChanges;
|
|
147
|
-
}
|
|
148
|
-
const usage = sanitizeUsage(msg?.usage);
|
|
149
|
-
if (usage) out.usage = usage;
|
|
150
|
-
|
|
151
|
-
if (Array.isArray(msg?.plan_transcript)) {
|
|
152
|
-
out.plan_transcript = msg.plan_transcript
|
|
153
|
-
.filter((entry) => entry && typeof entry === 'object')
|
|
154
|
-
.map((entry) => {
|
|
155
|
-
const { usage, ...rest } = entry;
|
|
156
|
-
const cleanUsage = sanitizeUsage(usage);
|
|
157
|
-
return {
|
|
158
|
-
...rest,
|
|
159
|
-
...(cleanUsage ? { usage: cleanUsage } : {}),
|
|
160
|
-
segments: Array.isArray(entry.segments) ? entry.segments : []
|
|
161
|
-
};
|
|
162
|
-
});
|
|
163
|
-
}
|
|
156
|
+
if (Array.isArray(msg?.tool_calls)) {
|
|
157
|
+
const toolCalls = msg.tool_calls.map(sanitizeToolCall).filter(Boolean);
|
|
158
|
+
if (toolCalls.length > 0) out.tool_calls = toolCalls;
|
|
159
|
+
}
|
|
160
|
+
if (Array.isArray(msg?.file_changes)) {
|
|
161
|
+
const fileChanges = msg.file_changes.map(sanitizeFileChange).filter(Boolean);
|
|
162
|
+
if (fileChanges.length > 0) out.file_changes = fileChanges;
|
|
163
|
+
}
|
|
164
|
+
const usage = sanitizeUsage(msg?.usage);
|
|
165
|
+
if (usage) out.usage = usage;
|
|
166
|
+
|
|
167
|
+
if (Array.isArray(msg?.plan_transcript)) {
|
|
168
|
+
out.plan_transcript = msg.plan_transcript
|
|
169
|
+
.filter((entry) => entry && typeof entry === 'object')
|
|
170
|
+
.map((entry) => {
|
|
171
|
+
const { usage, ...rest } = entry;
|
|
172
|
+
const cleanUsage = sanitizeUsage(usage);
|
|
173
|
+
return {
|
|
174
|
+
...rest,
|
|
175
|
+
...(cleanUsage ? { usage: cleanUsage } : {}),
|
|
176
|
+
segments: Array.isArray(entry.segments) ? entry.segments : []
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
}
|
|
164
180
|
|
|
165
181
|
return out;
|
|
166
182
|
}
|
|
@@ -319,6 +335,25 @@ async function writeSessionIndex(index) {
|
|
|
319
335
|
await fs.rename(tempPath, filePath);
|
|
320
336
|
}
|
|
321
337
|
|
|
338
|
+
async function removeSessionIndexEntry(sessionId, removedFileNames = []) {
|
|
339
|
+
try {
|
|
340
|
+
const id = String(sessionId || '').trim();
|
|
341
|
+
const removed = new Set(removedFileNames.map((name) => String(name || '').trim()).filter(Boolean));
|
|
342
|
+
const index = await readSessionIndex();
|
|
343
|
+
if (!index) return false;
|
|
344
|
+
const files = Array.isArray(index.files)
|
|
345
|
+
? index.files.filter((entry) => !removed.has(String(entry?.name || '')))
|
|
346
|
+
: [];
|
|
347
|
+
const sessions = Array.isArray(index.sessions)
|
|
348
|
+
? index.sessions.filter((entry) => String(entry?.id || '').trim() !== id)
|
|
349
|
+
: [];
|
|
350
|
+
await writeSessionIndex({ files, sessions });
|
|
351
|
+
return true;
|
|
352
|
+
} catch {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
322
357
|
async function rebuildSessionIndex(fileMeta = null) {
|
|
323
358
|
const files = await listSessionFiles();
|
|
324
359
|
const sessionsById = new Map();
|
|
@@ -460,37 +495,51 @@ export async function deleteSession(sessionId) {
|
|
|
460
495
|
throw new Error('Invalid session id');
|
|
461
496
|
}
|
|
462
497
|
|
|
463
|
-
const files = await listSessionFiles();
|
|
464
498
|
const targets = new Set();
|
|
465
|
-
for (const file of files) {
|
|
466
|
-
const fileId = sessionIdFromFileName(path.basename(file));
|
|
467
|
-
if (fileId === id) {
|
|
468
|
-
targets.add(file);
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
try {
|
|
472
|
-
const parsed = file.endsWith(SESSION_JSONL_EXT) ? await loadLatestJsonlObject(file) : await tryReadJson(file);
|
|
473
|
-
if (String(parsed?.id || '').trim() === id) targets.add(file);
|
|
474
|
-
} catch {}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
let removed = 0;
|
|
478
499
|
const fallbackTargets = [
|
|
479
500
|
sessionPathById(id, SESSION_JSONL_EXT),
|
|
480
501
|
sessionPathById(id, SESSION_LEGACY_EXT)
|
|
481
502
|
];
|
|
482
|
-
for (const file of
|
|
503
|
+
for (const file of fallbackTargets) targets.add(file);
|
|
504
|
+
|
|
505
|
+
let removed = 0;
|
|
506
|
+
const removedFileNames = [];
|
|
507
|
+
for (const file of targets) {
|
|
483
508
|
try {
|
|
484
509
|
await fs.unlink(file);
|
|
485
510
|
removed += 1;
|
|
511
|
+
removedFileNames.push(path.basename(file));
|
|
486
512
|
} catch (error) {
|
|
487
513
|
if (error?.code !== 'ENOENT') throw error;
|
|
488
514
|
}
|
|
489
515
|
}
|
|
516
|
+
|
|
517
|
+
if (removed === 0) {
|
|
518
|
+
const files = await listSessionFiles();
|
|
519
|
+
for (const file of files) {
|
|
520
|
+
try {
|
|
521
|
+
const parsed = file.endsWith(SESSION_JSONL_EXT) ? await loadLatestJsonlObject(file) : await tryReadJson(file);
|
|
522
|
+
if (String(parsed?.id || '').trim() === id) targets.add(file);
|
|
523
|
+
} catch {}
|
|
524
|
+
}
|
|
525
|
+
for (const file of targets) {
|
|
526
|
+
try {
|
|
527
|
+
await fs.unlink(file);
|
|
528
|
+
removed += 1;
|
|
529
|
+
removedFileNames.push(path.basename(file));
|
|
530
|
+
} catch (error) {
|
|
531
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
490
536
|
if (removed > 0) {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
537
|
+
const updated = await removeSessionIndexEntry(id, removedFileNames);
|
|
538
|
+
if (!updated) {
|
|
539
|
+
try {
|
|
540
|
+
await rebuildSessionIndex();
|
|
541
|
+
} catch {}
|
|
542
|
+
}
|
|
494
543
|
}
|
|
495
544
|
return { removed };
|
|
496
545
|
}
|