somtum 2.3.0 → 3.1.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/README.md +101 -852
- package/dist/agents/claude_code.d.ts +3 -0
- package/dist/agents/claude_code.d.ts.map +1 -0
- package/dist/agents/claude_code.js +112 -0
- package/dist/agents/claude_code.js.map +1 -0
- package/dist/agents/codex.d.ts +3 -0
- package/dist/agents/codex.d.ts.map +1 -0
- package/dist/agents/codex.js +173 -0
- package/dist/agents/codex.js.map +1 -0
- package/dist/agents/index.d.ts +7 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +24 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/shared.d.ts +3 -0
- package/dist/agents/shared.d.ts.map +1 -0
- package/dist/agents/shared.js +18 -0
- package/dist/agents/shared.js.map +1 -0
- package/dist/agents/types.d.ts +11 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +65 -78
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/hook.d.ts.map +1 -1
- package/dist/cli/hook.js +1 -4
- package/dist/cli/hook.js.map +1 -1
- package/dist/cli/index.js +14 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +19 -0
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +68 -36
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/init_codex.d.ts +12 -0
- package/dist/cli/init_codex.d.ts.map +1 -0
- package/dist/cli/init_codex.js +53 -0
- package/dist/cli/init_codex.js.map +1 -0
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +2 -0
- package/dist/cli/serve.js.map +1 -1
- package/dist/cli/stats.d.ts +4 -0
- package/dist/cli/stats.d.ts.map +1 -1
- package/dist/cli/stats.js +71 -43
- package/dist/cli/stats.js.map +1 -1
- package/dist/cli/ui.d.ts +1 -1
- package/dist/cli/ui.d.ts.map +1 -1
- package/dist/cli/ui.js +1 -1
- package/dist/cli/ui.js.map +1 -1
- package/dist/core/extractor.d.ts +1 -0
- package/dist/core/extractor.d.ts.map +1 -1
- package/dist/core/extractor.js +93 -52
- package/dist/core/extractor.js.map +1 -1
- package/dist/core/ledger.d.ts +28 -0
- package/dist/core/ledger.d.ts.map +1 -0
- package/dist/core/ledger.js +43 -0
- package/dist/core/ledger.js.map +1 -0
- package/dist/core/llm_backend.d.ts +7 -0
- package/dist/core/llm_backend.d.ts.map +1 -0
- package/dist/core/llm_backend.js +44 -0
- package/dist/core/llm_backend.js.map +1 -0
- package/dist/core/memory_files.d.ts.map +1 -1
- package/dist/core/memory_files.js +2 -3
- package/dist/core/memory_files.js.map +1 -1
- package/dist/core/retriever/bm25.d.ts +1 -1
- package/dist/core/retriever/bm25.d.ts.map +1 -1
- package/dist/core/retriever/bm25.js +11 -1
- package/dist/core/retriever/bm25.js.map +1 -1
- package/dist/core/schema.d.ts +19 -4
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +10 -1
- package/dist/core/schema.js.map +1 -1
- package/dist/core/session_progress.d.ts +10 -0
- package/dist/core/session_progress.d.ts.map +1 -0
- package/dist/core/session_progress.js +39 -0
- package/dist/core/session_progress.js.map +1 -0
- package/dist/core/transcript.d.ts.map +1 -1
- package/dist/core/transcript.js +2 -108
- package/dist/core/transcript.js.map +1 -1
- package/dist/dashboard/index.html +1 -1
- package/dist/db/migrations/006_token_ledger.sql +33 -0
- package/dist/hooks/post_session.d.ts +6 -5
- package/dist/hooks/post_session.d.ts.map +1 -1
- package/dist/hooks/post_session.js +107 -57
- package/dist/hooks/post_session.js.map +1 -1
- package/dist/hooks/pre_prompt.d.ts.map +1 -1
- package/dist/hooks/pre_prompt.js +14 -1
- package/dist/hooks/pre_prompt.js.map +1 -1
- package/dist/hooks/pre_read.d.ts +16 -2
- package/dist/hooks/pre_read.d.ts.map +1 -1
- package/dist/hooks/pre_read.js +81 -26
- package/dist/hooks/pre_read.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +2 -0
- package/dist/mcp/tools.js.map +1 -1
- package/package.json +9 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude_code.d.ts","sourceRoot":"","sources":["../../src/agents/claude_code.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAiH/C,eAAO,MAAM,UAAU,EAAE,YAKxB,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { truncateToolInput, truncateToolResult } from './shared.js';
|
|
2
|
+
const FILE_TOOL_NAMES = new Set(['Read', 'Edit', 'Write', 'NotebookEdit']);
|
|
3
|
+
// Claude Code transcripts deliver tool results as user-role messages whose
|
|
4
|
+
// content is tool_result blocks. Those are not real user prompts: treating
|
|
5
|
+
// them as such splits prompt→response pairs at every tool call and pollutes
|
|
6
|
+
// the cache. Detect them so they can be reclassified as role 'tool'.
|
|
7
|
+
function isToolResultContent(content) {
|
|
8
|
+
if (!Array.isArray(content))
|
|
9
|
+
return false;
|
|
10
|
+
let sawToolResult = false;
|
|
11
|
+
for (const block of content) {
|
|
12
|
+
if (block == null || typeof block !== 'object')
|
|
13
|
+
continue;
|
|
14
|
+
const t = block.type;
|
|
15
|
+
if (t === 'tool_result')
|
|
16
|
+
sawToolResult = true;
|
|
17
|
+
else if (t === 'text')
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return sawToolResult;
|
|
21
|
+
}
|
|
22
|
+
function extractFilesFromBlock(block) {
|
|
23
|
+
if (block == null || typeof block !== 'object')
|
|
24
|
+
return [];
|
|
25
|
+
const b = block;
|
|
26
|
+
if (b.type !== 'tool_use' || b.name === undefined)
|
|
27
|
+
return [];
|
|
28
|
+
if (!FILE_TOOL_NAMES.has(b.name))
|
|
29
|
+
return [];
|
|
30
|
+
const input = b.input;
|
|
31
|
+
const p = input?.file_path ?? input?.notebook_path;
|
|
32
|
+
return typeof p === 'string' && p.length > 0 ? [p] : [];
|
|
33
|
+
}
|
|
34
|
+
function coerceContent(content) {
|
|
35
|
+
if (typeof content === 'string')
|
|
36
|
+
return { text: content, files: [] };
|
|
37
|
+
if (Array.isArray(content)) {
|
|
38
|
+
const files = [];
|
|
39
|
+
const text = content
|
|
40
|
+
.map((block) => {
|
|
41
|
+
if (block == null)
|
|
42
|
+
return '';
|
|
43
|
+
if (typeof block === 'string')
|
|
44
|
+
return block;
|
|
45
|
+
if (typeof block === 'object' &&
|
|
46
|
+
'text' in block &&
|
|
47
|
+
typeof block.text === 'string') {
|
|
48
|
+
return block.text;
|
|
49
|
+
}
|
|
50
|
+
if (typeof block === 'object' &&
|
|
51
|
+
'type' in block &&
|
|
52
|
+
block.type === 'tool_use') {
|
|
53
|
+
const b = block;
|
|
54
|
+
files.push(...extractFilesFromBlock(block));
|
|
55
|
+
return `[tool_use ${b.name ?? ''} ${truncateToolInput(JSON.stringify(b.input ?? {}))}]`;
|
|
56
|
+
}
|
|
57
|
+
if (typeof block === 'object' &&
|
|
58
|
+
'type' in block &&
|
|
59
|
+
block.type === 'tool_result') {
|
|
60
|
+
const b = block;
|
|
61
|
+
return `[tool_result ${truncateToolResult(coerceContent(b.content).text)}]`;
|
|
62
|
+
}
|
|
63
|
+
return '';
|
|
64
|
+
})
|
|
65
|
+
.filter((s) => s.length > 0)
|
|
66
|
+
.join('\n');
|
|
67
|
+
return { text, files };
|
|
68
|
+
}
|
|
69
|
+
return { text: '', files: [] };
|
|
70
|
+
}
|
|
71
|
+
function parse(raw) {
|
|
72
|
+
const lines = raw.split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
73
|
+
const turns = [];
|
|
74
|
+
for (const line of lines) {
|
|
75
|
+
let obj;
|
|
76
|
+
try {
|
|
77
|
+
obj = JSON.parse(line);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
// Meta lines (caveats, command wrappers) and sidechain (subagent) turns
|
|
83
|
+
// are not part of the main conversation.
|
|
84
|
+
if (obj.isMeta === true || obj.isSidechain === true)
|
|
85
|
+
continue;
|
|
86
|
+
let role = (obj.message?.role ?? obj.role ?? obj.type ?? 'user');
|
|
87
|
+
if (role !== 'user' && role !== 'assistant' && role !== 'system' && role !== 'tool')
|
|
88
|
+
continue;
|
|
89
|
+
const content = obj.message?.content ?? obj.content;
|
|
90
|
+
if (role === 'user' && isToolResultContent(content))
|
|
91
|
+
role = 'tool';
|
|
92
|
+
const { text, files } = coerceContent(content);
|
|
93
|
+
if (text.length === 0)
|
|
94
|
+
continue;
|
|
95
|
+
const turn = { role, text };
|
|
96
|
+
if (obj.timestamp)
|
|
97
|
+
turn.timestamp = obj.timestamp;
|
|
98
|
+
if (files.length > 0)
|
|
99
|
+
turn.files_touched = files;
|
|
100
|
+
turns.push(turn);
|
|
101
|
+
}
|
|
102
|
+
return turns;
|
|
103
|
+
}
|
|
104
|
+
// Claude Code is the fallback format: generic {role, content} JSONL parses
|
|
105
|
+
// correctly through it, so detect() accepts anything.
|
|
106
|
+
export const claudeCode = {
|
|
107
|
+
id: 'claude-code',
|
|
108
|
+
name: 'Claude Code',
|
|
109
|
+
detect: () => true,
|
|
110
|
+
parse,
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=claude_code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude_code.js","sourceRoot":"","sources":["../../src/agents/claude_code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/agents/codex.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA6K/C,eAAO,MAAM,KAAK,EAAE,YAMnB,CAAC"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { truncateToolInput, truncateToolResult } from './shared.js';
|
|
5
|
+
// User-role blocks Codex injects itself: environment, AGENTS.md, permissions,
|
|
6
|
+
// app state. Most are one <snake_case> tag wrapping the whole block, and new
|
|
7
|
+
// ones keep appearing, so match that shape as well as the known prefixes.
|
|
8
|
+
const INJECTED_PREFIXES = ['# AGENTS.md instructions', '<permissions'];
|
|
9
|
+
const INJECTED_TAG_BLOCK = /^<([a-z]+(?:_[a-z]+)+)>[\s\S]*<\/\1>$/;
|
|
10
|
+
// IDE extensions wrap the prompt as "# Context from my IDE setup: ...
|
|
11
|
+
// ## My request for Codex:\n<prompt>". The heading text varies by version.
|
|
12
|
+
const IDE_CONTEXT_PREFIX = '# Context from my IDE setup';
|
|
13
|
+
const IDE_REQUEST_MARKER = /^## My request[^\n]*:\n/m;
|
|
14
|
+
const PATCH_FILE_LINE = /^\*\*\* (?:Add|Update|Delete) File: (.+)$/gm;
|
|
15
|
+
function userText(blocks) {
|
|
16
|
+
const parts = [];
|
|
17
|
+
for (const b of blocks) {
|
|
18
|
+
const text = b.text?.trim();
|
|
19
|
+
if (!text)
|
|
20
|
+
continue;
|
|
21
|
+
if (INJECTED_PREFIXES.some((p) => text.startsWith(p)))
|
|
22
|
+
continue;
|
|
23
|
+
if (INJECTED_TAG_BLOCK.test(text))
|
|
24
|
+
continue;
|
|
25
|
+
if (text.startsWith(IDE_CONTEXT_PREFIX)) {
|
|
26
|
+
const m = IDE_REQUEST_MARKER.exec(text);
|
|
27
|
+
if (m)
|
|
28
|
+
parts.push(text.slice(m.index + m[0].length).trim());
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
parts.push(text);
|
|
32
|
+
}
|
|
33
|
+
return parts.join('\n');
|
|
34
|
+
}
|
|
35
|
+
function assistantText(blocks) {
|
|
36
|
+
return blocks
|
|
37
|
+
.map((b) => b.text ?? '')
|
|
38
|
+
.filter((t) => t.length > 0)
|
|
39
|
+
.join('\n');
|
|
40
|
+
}
|
|
41
|
+
function asText(value) {
|
|
42
|
+
if (typeof value === 'string')
|
|
43
|
+
return value;
|
|
44
|
+
if (value === undefined || value === null)
|
|
45
|
+
return '';
|
|
46
|
+
return JSON.stringify(value);
|
|
47
|
+
}
|
|
48
|
+
function patchedFiles(patch) {
|
|
49
|
+
return [...patch.matchAll(PATCH_FILE_LINE)].map((m) => m[1].trim());
|
|
50
|
+
}
|
|
51
|
+
// Guardian reviews and other subagent threads get their own rollout file.
|
|
52
|
+
// They are Codex talking to itself, not the user's session.
|
|
53
|
+
function isSubagentSession(meta) {
|
|
54
|
+
const threadSource = meta['thread_source'];
|
|
55
|
+
if (typeof threadSource === 'string' && threadSource !== 'user')
|
|
56
|
+
return true;
|
|
57
|
+
return typeof meta['source'] === 'object' && meta['source'] !== null;
|
|
58
|
+
}
|
|
59
|
+
function toTurn(line) {
|
|
60
|
+
const p = line.payload ?? {};
|
|
61
|
+
let turn = null;
|
|
62
|
+
switch (p['type']) {
|
|
63
|
+
case 'message': {
|
|
64
|
+
const blocks = (Array.isArray(p['content']) ? p['content'] : []);
|
|
65
|
+
if (p['role'] === 'user') {
|
|
66
|
+
const text = userText(blocks);
|
|
67
|
+
if (text)
|
|
68
|
+
turn = { role: 'user', text };
|
|
69
|
+
}
|
|
70
|
+
else if (p['role'] === 'assistant') {
|
|
71
|
+
const text = assistantText(blocks);
|
|
72
|
+
if (text)
|
|
73
|
+
turn = { role: 'assistant', text };
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case 'function_call':
|
|
78
|
+
turn = {
|
|
79
|
+
role: 'assistant',
|
|
80
|
+
text: `[tool_use ${asText(p['name'])} ${truncateToolInput(asText(p['arguments']))}]`,
|
|
81
|
+
};
|
|
82
|
+
break;
|
|
83
|
+
case 'custom_tool_call': {
|
|
84
|
+
const input = asText(p['input']);
|
|
85
|
+
turn = {
|
|
86
|
+
role: 'assistant',
|
|
87
|
+
text: `[tool_use ${asText(p['name'])} ${truncateToolInput(input)}]`,
|
|
88
|
+
};
|
|
89
|
+
if (p['name'] === 'apply_patch') {
|
|
90
|
+
const files = patchedFiles(input);
|
|
91
|
+
if (files.length > 0)
|
|
92
|
+
turn.files_touched = files;
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case 'function_call_output':
|
|
97
|
+
case 'custom_tool_call_output':
|
|
98
|
+
turn = { role: 'tool', text: `[tool_result ${truncateToolResult(asText(p['output']))}]` };
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
if (turn && line.timestamp)
|
|
102
|
+
turn.timestamp = line.timestamp;
|
|
103
|
+
return turn;
|
|
104
|
+
}
|
|
105
|
+
function parse(raw) {
|
|
106
|
+
const turns = [];
|
|
107
|
+
for (const lineText of raw.split(/\r?\n/)) {
|
|
108
|
+
if (lineText.trim().length === 0)
|
|
109
|
+
continue;
|
|
110
|
+
let line;
|
|
111
|
+
try {
|
|
112
|
+
line = JSON.parse(lineText);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (line.type === 'session_meta' && isSubagentSession(line.payload ?? {}))
|
|
118
|
+
return [];
|
|
119
|
+
if (line.type !== 'response_item')
|
|
120
|
+
continue;
|
|
121
|
+
const turn = toTurn(line);
|
|
122
|
+
if (turn)
|
|
123
|
+
turns.push(turn);
|
|
124
|
+
}
|
|
125
|
+
return turns;
|
|
126
|
+
}
|
|
127
|
+
function detect(raw) {
|
|
128
|
+
const nl = raw.indexOf('\n');
|
|
129
|
+
const firstLine = (nl === -1 ? raw : raw.slice(0, nl)).trim();
|
|
130
|
+
try {
|
|
131
|
+
const obj = JSON.parse(firstLine);
|
|
132
|
+
return ((obj.type === 'session_meta' || obj.type === 'response_item') &&
|
|
133
|
+
typeof obj.payload === 'object');
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function sessionsDir() {
|
|
140
|
+
return join(process.env['CODEX_HOME'] ?? join(homedir(), '.codex'), 'sessions');
|
|
141
|
+
}
|
|
142
|
+
// Codex hooks may send transcript_path: null. Rollout filenames end in the
|
|
143
|
+
// session id, so search the date folders newest-first.
|
|
144
|
+
function findTranscript(sessionId) {
|
|
145
|
+
const root = sessionsDir();
|
|
146
|
+
if (!existsSync(root))
|
|
147
|
+
return null;
|
|
148
|
+
const suffix = `${sessionId}.jsonl`;
|
|
149
|
+
const newestFirst = (dir) => readdirSync(dir).sort().reverse();
|
|
150
|
+
try {
|
|
151
|
+
for (const y of newestFirst(root)) {
|
|
152
|
+
for (const m of newestFirst(join(root, y))) {
|
|
153
|
+
for (const d of newestFirst(join(root, y, m))) {
|
|
154
|
+
const hit = readdirSync(join(root, y, m, d)).find((f) => f.endsWith(suffix));
|
|
155
|
+
if (hit)
|
|
156
|
+
return join(root, y, m, d, hit);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
export const codex = {
|
|
167
|
+
id: 'codex',
|
|
168
|
+
name: 'Codex',
|
|
169
|
+
detect,
|
|
170
|
+
parse,
|
|
171
|
+
findTranscript,
|
|
172
|
+
};
|
|
173
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/agents/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AgentAdapter, AgentId } from './types.js';
|
|
2
|
+
export type { AgentAdapter, AgentId } from './types.js';
|
|
3
|
+
export { AGENT_IDS } from './types.js';
|
|
4
|
+
export declare function getAgent(id: AgentId): AgentAdapter;
|
|
5
|
+
export declare function detectAgent(raw: string): AgentAdapter;
|
|
6
|
+
export declare function findTranscript(sessionId: string): string | null;
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agents/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAIxD,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAMvC,wBAAgB,QAAQ,CAAC,EAAE,EAAE,OAAO,GAAG,YAAY,CAIlD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAErD;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { claudeCode } from './claude_code.js';
|
|
2
|
+
import { codex } from './codex.js';
|
|
3
|
+
export { AGENT_IDS } from './types.js';
|
|
4
|
+
// Order matters: detect() runs in sequence and Claude Code accepts anything,
|
|
5
|
+
// so it must stay last.
|
|
6
|
+
const ADAPTERS = [codex, claudeCode];
|
|
7
|
+
export function getAgent(id) {
|
|
8
|
+
const agent = ADAPTERS.find((a) => a.id === id);
|
|
9
|
+
if (!agent)
|
|
10
|
+
throw new Error(`Unknown agent: ${id}`);
|
|
11
|
+
return agent;
|
|
12
|
+
}
|
|
13
|
+
export function detectAgent(raw) {
|
|
14
|
+
return ADAPTERS.find((a) => a.detect(raw)) ?? claudeCode;
|
|
15
|
+
}
|
|
16
|
+
export function findTranscript(sessionId) {
|
|
17
|
+
for (const agent of ADAPTERS) {
|
|
18
|
+
const path = agent.findTranscript?.(sessionId);
|
|
19
|
+
if (path)
|
|
20
|
+
return path;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agents/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,6EAA6E;AAC7E,wBAAwB;AACxB,MAAM,QAAQ,GAA4B,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AAE9D,MAAM,UAAU,QAAQ,CAAC,EAAW;IAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,UAAU,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/agents/shared.ts"],"names":[],"mappings":"AAKA,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD;AAMD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGtD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Tool results can be huge (full file dumps, command output). Keep enough for
|
|
2
|
+
// the extractor to understand what happened without letting a single result
|
|
3
|
+
// crowd real conversation out of the transcript token budget.
|
|
4
|
+
const MAX_TOOL_RESULT_CHARS = 1000;
|
|
5
|
+
export function truncateToolResult(text) {
|
|
6
|
+
if (text.length <= MAX_TOOL_RESULT_CHARS)
|
|
7
|
+
return text;
|
|
8
|
+
return `${text.slice(0, MAX_TOOL_RESULT_CHARS)}… [truncated]`;
|
|
9
|
+
}
|
|
10
|
+
// Tool-call arguments carry whole files (Write content, Edit strings, patches).
|
|
11
|
+
// The extractor needs to know which tool ran on what, not the payload.
|
|
12
|
+
const MAX_TOOL_INPUT_CHARS = 300;
|
|
13
|
+
export function truncateToolInput(text) {
|
|
14
|
+
if (text.length <= MAX_TOOL_INPUT_CHARS)
|
|
15
|
+
return text;
|
|
16
|
+
return `${text.slice(0, MAX_TOOL_INPUT_CHARS)}… [truncated]`;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/agents/shared.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4EAA4E;AAC5E,8DAA8D;AAC9D,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAEnC,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,IAAI,CAAC,MAAM,IAAI,qBAAqB;QAAE,OAAO,IAAI,CAAC;IACtD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,eAAe,CAAC;AAChE,CAAC;AAED,gFAAgF;AAChF,uEAAuE;AACvE,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,IAAI,CAAC,MAAM,IAAI,oBAAoB;QAAE,OAAO,IAAI,CAAC;IACrD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,eAAe,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Turn } from '../core/transcript.js';
|
|
2
|
+
export type AgentId = 'claude-code' | 'codex';
|
|
3
|
+
export declare const AGENT_IDS: readonly AgentId[];
|
|
4
|
+
export interface AgentAdapter {
|
|
5
|
+
id: AgentId;
|
|
6
|
+
name: string;
|
|
7
|
+
detect(raw: string): boolean;
|
|
8
|
+
parse(raw: string): Turn[];
|
|
9
|
+
findTranscript?(sessionId: string): string | null;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAElD,MAAM,MAAM,OAAO,GAAG,aAAa,GAAG,OAAO,CAAC;AAE9C,eAAO,MAAM,SAAS,EAAE,SAAS,OAAO,EAA6B,CAAC;AAEtE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IAEb,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,CAAC;IAE3B,cAAc,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACnD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,SAAS,GAAuB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC"}
|
package/dist/cli/doctor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAKD,wBAAgB,SAAS,CAAC,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,YAAY,CAmNnE;AAED,wBAAsB,aAAa,CACjC,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7C,OAAO,CAAC,MAAM,CAAC,CAuBjB"}
|
package/dist/cli/doctor.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { execSync } from 'node:child_process';
|
|
4
3
|
import { openDb, hasFts5, appliedVersions } from '../core/db.js';
|
|
5
4
|
import { MemoryStore } from '../core/store.js';
|
|
6
5
|
import { PromptCache } from '../core/cache.js';
|
|
7
6
|
import { resolveProjectId } from '../core/project_id.js';
|
|
7
|
+
import { pickBackend } from '../core/llm_backend.js';
|
|
8
|
+
import { TokenLedger } from '../core/ledger.js';
|
|
8
9
|
import { loadConfig, projectDir, GLOBAL_DIR, GLOBAL_CONFIG_PATH } from '../config.js';
|
|
10
|
+
// Too few gated reads says nothing about whether gating pays off.
|
|
11
|
+
const GATE_VERDICT_MIN_EVENTS = 10;
|
|
9
12
|
export function runDoctor(opts = {}) {
|
|
10
13
|
const cwd = opts.cwd ?? process.cwd();
|
|
11
14
|
const checks = [];
|
|
@@ -80,40 +83,26 @@ export function runDoctor(opts = {}) {
|
|
|
80
83
|
? `applied: [${applied.join(', ')}]`
|
|
81
84
|
: `missing migrations: [${missing.join(', ')}]`,
|
|
82
85
|
});
|
|
83
|
-
// 7. Memory count
|
|
86
|
+
// 7. Memory count
|
|
84
87
|
const store = new MemoryStore(db);
|
|
85
88
|
const memCount = store.countByProject(projectId);
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
checks.push({ name: 'memories', ok: true, detail: `${memCount} memories` });
|
|
90
|
+
// 8. Metered token use, and whether file gating is paying for itself
|
|
91
|
+
const ledger = new TokenLedger(db).summary(projectId);
|
|
88
92
|
checks.push({
|
|
89
|
-
name: '
|
|
93
|
+
name: 'token_overhead',
|
|
90
94
|
ok: true,
|
|
91
|
-
detail: `${
|
|
95
|
+
detail: `${ledger.overhead_tokens} tokens metered (extraction ${ledger.extraction.tokens}, summaries ${ledger.file_summary.tokens}, injected ${ledger.injection.tokens}); see \`somtum stats\``,
|
|
92
96
|
});
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
const ratio = saved / spent;
|
|
96
|
-
const ok = ratio >= 1.5;
|
|
97
|
+
const gate = ledger.file_gate;
|
|
98
|
+
if (gate.denied.events >= GATE_VERDICT_MIN_EVENTS && gate.net_tokens < 0) {
|
|
97
99
|
checks.push({
|
|
98
|
-
name: '
|
|
99
|
-
ok,
|
|
100
|
-
detail:
|
|
101
|
-
|
|
102
|
-
? {}
|
|
103
|
-
: {
|
|
104
|
-
fix: [
|
|
105
|
-
'somtum config set retrieval.strategy bm25 # match what is actually running',
|
|
106
|
-
'# or, to enable real hybrid retrieval:',
|
|
107
|
-
'somtum config set retrieval.embeddings.enabled true && somtum reindex',
|
|
108
|
-
'# or reduce injection overhead:',
|
|
109
|
-
'somtum config set injection.k 2',
|
|
110
|
-
],
|
|
111
|
-
}),
|
|
100
|
+
name: 'file_gating',
|
|
101
|
+
ok: false,
|
|
102
|
+
detail: `costing more than it saves: net ${gate.net_tokens} tokens over ${gate.denied.events} gated reads`,
|
|
103
|
+
fix: ['somtum config set file_gating.enabled false'],
|
|
112
104
|
});
|
|
113
105
|
}
|
|
114
|
-
else {
|
|
115
|
-
checks.push({ name: 'breakeven_ratio', ok: true, detail: 'n/a (no extraction cost yet)' });
|
|
116
|
-
}
|
|
117
106
|
// 9. Stale memories: older than 90 days, never confirmed via retrieval
|
|
118
107
|
const staleCount = store.countStale(projectId, 90);
|
|
119
108
|
checks.push({
|
|
@@ -143,74 +132,72 @@ export function runDoctor(opts = {}) {
|
|
|
143
132
|
? `enabled (model: ${config.retrieval.embeddings.model}) — run \`somtum reindex\` to embed missing`
|
|
144
133
|
: 'disabled (set retrieval.embeddings.enabled=true to enable)',
|
|
145
134
|
});
|
|
146
|
-
// 11. Extraction
|
|
147
|
-
const
|
|
148
|
-
const hasApiKey = Boolean(apiKey && apiKey.trim().length > 0);
|
|
149
|
-
let claudeCliAvailable = false;
|
|
150
|
-
if (!hasApiKey) {
|
|
151
|
-
try {
|
|
152
|
-
execSync('claude --version', { stdio: 'pipe' });
|
|
153
|
-
claudeCliAvailable = true;
|
|
154
|
-
}
|
|
155
|
-
catch {
|
|
156
|
-
// not in PATH
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
const apiKeyOk = hasApiKey || claudeCliAvailable;
|
|
135
|
+
// 11. Extraction backend: Anthropic credentials, claude CLI or codex CLI
|
|
136
|
+
const backend = pickBackend(config);
|
|
160
137
|
checks.push({
|
|
161
|
-
name: '
|
|
162
|
-
ok:
|
|
163
|
-
detail:
|
|
164
|
-
? '
|
|
165
|
-
:
|
|
166
|
-
|
|
167
|
-
: 'Neither ANTHROPIC_API_KEY nor claude CLI available — extraction will fail',
|
|
168
|
-
...(apiKeyOk
|
|
138
|
+
name: 'extraction_backend',
|
|
139
|
+
ok: backend !== null,
|
|
140
|
+
detail: backend
|
|
141
|
+
? `${backend}${config.extraction.backend === 'auto' ? ' (auto)' : ''}`
|
|
142
|
+
: 'none available — extraction will fail',
|
|
143
|
+
...(backend
|
|
169
144
|
? {}
|
|
170
145
|
: {
|
|
171
146
|
fix: [
|
|
172
|
-
'
|
|
173
|
-
'
|
|
174
|
-
'
|
|
147
|
+
'Claude Code: which claude (reinstall or add it to PATH if nothing prints)',
|
|
148
|
+
'Codex: which codex',
|
|
149
|
+
'API key: export ANTHROPIC_API_KEY="sk-ant-..." >> ~/.zshrc && source ~/.zshrc',
|
|
175
150
|
'Docs: https://riz007.github.io/somtum/troubleshooting.html#memories-0',
|
|
176
151
|
],
|
|
177
152
|
}),
|
|
178
153
|
});
|
|
179
|
-
// 12.
|
|
180
|
-
const
|
|
181
|
-
|
|
154
|
+
// 12. Hooks: Claude Code (.claude/settings.json) and/or Codex (.codex/hooks.json)
|
|
155
|
+
const hookFiles = [join('.claude', 'settings.json'), join('.codex', 'hooks.json')];
|
|
156
|
+
const installedIn = [];
|
|
157
|
+
const unreadable = [];
|
|
158
|
+
for (const rel of hookFiles) {
|
|
159
|
+
const path = join(cwd, rel);
|
|
160
|
+
if (!existsSync(path))
|
|
161
|
+
continue;
|
|
182
162
|
try {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
checks.push({
|
|
187
|
-
name: 'hooks_installed',
|
|
188
|
-
ok: Boolean(hasSessionEnd),
|
|
189
|
-
detail: hasSessionEnd
|
|
190
|
-
? 'somtum hooks found in .claude/settings.json'
|
|
191
|
-
: 'somtum hooks not found — run `somtum init`',
|
|
192
|
-
...(hasSessionEnd
|
|
193
|
-
? {}
|
|
194
|
-
: { fix: ['somtum init --all # run from the project root you open with Claude Code'] }),
|
|
195
|
-
});
|
|
163
|
+
const hooks = JSON.parse(readFileSync(path, 'utf8'))['hooks'];
|
|
164
|
+
if (hooks && JSON.stringify(hooks).includes('somtum'))
|
|
165
|
+
installedIn.push(rel);
|
|
196
166
|
}
|
|
197
167
|
catch {
|
|
198
|
-
|
|
199
|
-
name: 'hooks_installed',
|
|
200
|
-
ok: false,
|
|
201
|
-
detail: 'could not parse .claude/settings.json',
|
|
202
|
-
fix: ['somtum init --all'],
|
|
203
|
-
});
|
|
168
|
+
unreadable.push(rel);
|
|
204
169
|
}
|
|
205
170
|
}
|
|
206
|
-
|
|
171
|
+
const claudeSettings = join(cwd, '.claude', 'settings.json');
|
|
172
|
+
const preReadInstalled = existsSync(claudeSettings) && readFileSync(claudeSettings, 'utf8').includes('hook pre_read');
|
|
173
|
+
if (preReadInstalled && !config.file_gating.enabled) {
|
|
207
174
|
checks.push({
|
|
208
|
-
name: '
|
|
175
|
+
name: 'file_gating',
|
|
209
176
|
ok: false,
|
|
210
|
-
detail: '.
|
|
211
|
-
fix: [
|
|
177
|
+
detail: 'PreToolUse hook installed but file_gating.enabled is false (the default since v3.1)',
|
|
178
|
+
fix: [
|
|
179
|
+
'somtum config set file_gating.enabled true # keep using it; check `somtum stats` after a few sessions',
|
|
180
|
+
'# or remove the PreToolUse somtum entry from .claude/settings.json',
|
|
181
|
+
],
|
|
212
182
|
});
|
|
213
183
|
}
|
|
184
|
+
checks.push({
|
|
185
|
+
name: 'hooks_installed',
|
|
186
|
+
ok: installedIn.length > 0,
|
|
187
|
+
detail: installedIn.length > 0
|
|
188
|
+
? `somtum hooks found in ${installedIn.join(', ')}`
|
|
189
|
+
: unreadable.length > 0
|
|
190
|
+
? `could not parse ${unreadable.join(', ')}`
|
|
191
|
+
: 'no somtum hooks in .claude/settings.json or .codex/hooks.json',
|
|
192
|
+
...(installedIn.length > 0
|
|
193
|
+
? {}
|
|
194
|
+
: {
|
|
195
|
+
fix: [
|
|
196
|
+
'somtum init # Claude Code, from the project root',
|
|
197
|
+
'somtum init --agent codex # Codex, from the project root',
|
|
198
|
+
],
|
|
199
|
+
}),
|
|
200
|
+
});
|
|
214
201
|
}
|
|
215
202
|
finally {
|
|
216
203
|
db.close();
|