claude-simple 0.2.22 → 0.2.24
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/{app.0.2.22.min.js → app.0.2.24.min.js} +83 -79
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/public/agent-panel.js +2 -1
- package/public/chat.js +9 -1
- package/public/index.html +2 -2
- package/public/markdown.js +12 -0
- package/public/shell/session-list.js +2 -1
- package/public/sw.js +3 -3
- package/src/agents/grok.js +53 -8
- package/src/agents/sessions.js +15 -8
- /package/dist/{style.0.2.22.min.css → style.0.2.24.min.css} +0 -0
package/dist/index.html
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
|
6
6
|
<title>Claude Code</title>
|
|
7
|
-
<link rel="stylesheet" href="/style.0.2.
|
|
7
|
+
<link rel="stylesheet" href="/style.0.2.24.min.css">
|
|
8
8
|
<link rel="stylesheet" href="/highlight.min.css">
|
|
9
9
|
<script src="/marked.min.js"></script>
|
|
10
10
|
<script src="/highlight.min.js"></script>
|
|
11
11
|
</head>
|
|
12
12
|
<body class="h-dvh overflow-hidden flex flex-col bg-base-100 text-base-content">
|
|
13
13
|
<div id="root" class="flex flex-col flex-1 overflow-hidden"></div>
|
|
14
|
-
<script type="module" src="/app.0.2.
|
|
14
|
+
<script type="module" src="/app.0.2.24.min.js"></script>
|
|
15
15
|
</body>
|
|
16
16
|
</html>
|
package/package.json
CHANGED
package/public/agent-panel.js
CHANGED
|
@@ -6,6 +6,7 @@ import { esc, $ } from './lib.js';
|
|
|
6
6
|
import { api, authHeaders, currentMachineId } from './api.js';
|
|
7
7
|
import { hubMode, ctx, currentAgent, currentProject } from './state.js';
|
|
8
8
|
import { renderMermaid } from './mermaid.js';
|
|
9
|
+
import { normalizeMarkdownText } from './markdown.js';
|
|
9
10
|
|
|
10
11
|
let panelOpen = false;
|
|
11
12
|
let mode = 'chat'; // chat | report | history | config
|
|
@@ -29,7 +30,7 @@ const LAST_SESSION_KEY = 'ma_last_session_id';
|
|
|
29
30
|
function mdHtml(text) {
|
|
30
31
|
if (typeof marked !== 'undefined') {
|
|
31
32
|
try {
|
|
32
|
-
return marked.parse(
|
|
33
|
+
return marked.parse(normalizeMarkdownText(text), { breaks: true, gfm: true });
|
|
33
34
|
} catch { /* fallthrough */ }
|
|
34
35
|
}
|
|
35
36
|
return esc(text).replace(/\n/g, '<br>');
|
package/public/chat.js
CHANGED
|
@@ -8,6 +8,7 @@ import { initWakeLock, setWakeLockDesired } from './wake-lock.js';
|
|
|
8
8
|
import { pushNotification } from './notifications.js';
|
|
9
9
|
import { renderMermaid } from './mermaid.js';
|
|
10
10
|
import { renderMath } from './math.js';
|
|
11
|
+
import { normalizeMarkdownText } from './markdown.js';
|
|
11
12
|
|
|
12
13
|
function assistantLabel() {
|
|
13
14
|
return AGENT_LABELS[currentAgent.peek()] || 'Assistant';
|
|
@@ -1524,6 +1525,13 @@ function ensureMarked() {
|
|
|
1524
1525
|
}
|
|
1525
1526
|
return `<div class="table-wrap"><table><thead>${header}</thead><tbody>${body}</tbody></table></div>`;
|
|
1526
1527
|
},
|
|
1528
|
+
// Agent output is untrusted Markdown, not application HTML. In
|
|
1529
|
+
// particular agy's native transcript may contain `<truncated N bytes>`;
|
|
1530
|
+
// letting marked emit that raw tag can unbalance the surrounding message
|
|
1531
|
+
// DOM. Show HTML-like content literally instead.
|
|
1532
|
+
html(raw) {
|
|
1533
|
+
return escapeCode(raw);
|
|
1534
|
+
},
|
|
1527
1535
|
};
|
|
1528
1536
|
|
|
1529
1537
|
try {
|
|
@@ -1545,7 +1553,7 @@ function renderMarkdown(text) {
|
|
|
1545
1553
|
if (typeof marked === 'undefined') return esc(text);
|
|
1546
1554
|
try {
|
|
1547
1555
|
ensureMarked();
|
|
1548
|
-
return marked.parse(
|
|
1556
|
+
return marked.parse(normalizeMarkdownText(text), { breaks: true, gfm: true });
|
|
1549
1557
|
} catch {
|
|
1550
1558
|
return esc(text);
|
|
1551
1559
|
}
|
package/public/index.html
CHANGED
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
<script src="/vendor/mermaid.min.js"></script>
|
|
86
86
|
<link rel="stylesheet" href="/vendor/katex.min.css">
|
|
87
87
|
<script src="/vendor/katex.min.js"></script>
|
|
88
|
-
<link rel="stylesheet" href="style.css?v=ui-
|
|
88
|
+
<link rel="stylesheet" href="style.css?v=ui-20">
|
|
89
89
|
</head>
|
|
90
90
|
<body class="h-dvh overflow-hidden flex flex-col bg-base-100 text-base-content">
|
|
91
91
|
<div id="root" class="flex flex-col flex-1 overflow-hidden"></div>
|
|
@@ -101,6 +101,6 @@
|
|
|
101
101
|
}
|
|
102
102
|
}, 10000);
|
|
103
103
|
</script>
|
|
104
|
-
<script type="module" src="app.js?v=ui-
|
|
104
|
+
<script type="module" src="app.js?v=ui-20"></script>
|
|
105
105
|
</body>
|
|
106
106
|
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Markdown input normalisation shared by chat, history and Meta Agent views.
|
|
2
|
+
// Some agent transcript formats use `<truncated N bytes>` as a transport
|
|
3
|
+
// placeholder. It is not HTML, but a Markdown parser will otherwise pass it
|
|
4
|
+
// through as an element and a malformed placeholder can corrupt the rest of a
|
|
5
|
+
// message's DOM.
|
|
6
|
+
const TRUNCATED_TRANSCRIPT_MARKER = /(?:<|\[)\s*(?:…\s*)?truncated\s+([\d,]+)\s+bytes\s*(?:>|\])/gi;
|
|
7
|
+
|
|
8
|
+
export function normalizeMarkdownText(value) {
|
|
9
|
+
return String(value ?? '').replace(TRUNCATED_TRANSCRIPT_MARKER, (_match, bytes) => {
|
|
10
|
+
return `\n\n> ⚠️ 原始记录已截断(${bytes} bytes)\n\n`;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { api, NOT_MODIFIED } from '../api.js';
|
|
14
14
|
import { getCachedSessions, setCachedSessions, getCachedWorkspaces, setCachedWorkspaces } from '../cache.js';
|
|
15
15
|
import { formatTime, agentBadge, agentCountPills, shortPath } from './util.js';
|
|
16
|
+
import { normalizeMarkdownText } from '../markdown.js';
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
function normalizeSessions(v) {
|
|
@@ -424,7 +425,7 @@ export function mdToHtml(text) {
|
|
|
424
425
|
if (typeof marked === 'undefined') return `<pre class="whitespace-pre-wrap text-xs">${esc(text)}</pre>`;
|
|
425
426
|
try {
|
|
426
427
|
// Prefer shared renderer if chat has configured marked
|
|
427
|
-
return marked.parse(
|
|
428
|
+
return marked.parse(normalizeMarkdownText(text), { breaks: true, gfm: true });
|
|
428
429
|
} catch {
|
|
429
430
|
return `<pre class="whitespace-pre-wrap text-xs">${esc(text)}</pre>`;
|
|
430
431
|
}
|
package/public/sw.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/* Agent UI service worker — cache shell assets only.
|
|
2
2
|
* Never intercepts WebSocket or mutates API semantics beyond offline fallback.
|
|
3
3
|
* Bump CACHE when shipping UI that must invalidate old shells. */
|
|
4
|
-
const CACHE = 'agent-ui-shell-
|
|
4
|
+
const CACHE = 'agent-ui-shell-v25';
|
|
5
5
|
const PRECACHE = [
|
|
6
6
|
'/',
|
|
7
7
|
'/index.html',
|
|
8
8
|
'/manifest.webmanifest',
|
|
9
|
-
'/style.css?v=ui-
|
|
9
|
+
'/style.css?v=ui-20',
|
|
10
10
|
'/style.css',
|
|
11
11
|
'/ui.min.css',
|
|
12
12
|
'/vendor/mini-react.min.js',
|
|
@@ -16,7 +16,7 @@ const PRECACHE = [
|
|
|
16
16
|
'/vendor/mermaid.min.js',
|
|
17
17
|
'/vendor/katex.min.js',
|
|
18
18
|
'/vendor/katex.min.css',
|
|
19
|
-
'/app.js?v=ui-
|
|
19
|
+
'/app.js?v=ui-20',
|
|
20
20
|
'/icons/icon-192.png',
|
|
21
21
|
'/icons/icon-512.png',
|
|
22
22
|
'/icons/icon-maskable-512.png',
|
package/src/agents/grok.js
CHANGED
|
@@ -14,6 +14,36 @@ function profileKey(options) {
|
|
|
14
14
|
return `${options.model || ''}\u001f${options.effort || ''}`;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// JSON-RPC 2.0 error.code must be an integer. Node fs/spawn errors use string
|
|
18
|
+
// codes like "ENOENT"; Grok's Rust client rejects those and logs
|
|
19
|
+
// `invalid type: string "ENOENT", expected i32`.
|
|
20
|
+
export function jsonRpcErrorCode(error) {
|
|
21
|
+
const code = error?.code;
|
|
22
|
+
if (typeof code === 'number' && Number.isInteger(code)) return code;
|
|
23
|
+
return -32000;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** ACP paths must be absolute; resolve relatives against the session cwd, not the edge process cwd. */
|
|
27
|
+
export function resolveFsPath(filePath, cwd) {
|
|
28
|
+
const raw = String(filePath || '').trim();
|
|
29
|
+
if (!raw) {
|
|
30
|
+
const err = new Error('path is required');
|
|
31
|
+
err.code = -32602;
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
return path.resolve(cwd || process.cwd(), raw);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function selectPermissionOption(options) {
|
|
38
|
+
const list = Array.isArray(options) ? options : [];
|
|
39
|
+
const allow = list.find(o => o?.kind === 'allow_always')
|
|
40
|
+
|| list.find(o => o?.kind === 'allow_once')
|
|
41
|
+
|| list.find(o => typeof o?.kind === 'string' && String(o.kind).startsWith('allow'))
|
|
42
|
+
|| list[0];
|
|
43
|
+
if (!allow?.optionId) return { outcome: 'cancelled' };
|
|
44
|
+
return { outcome: 'selected', optionId: allow.optionId };
|
|
45
|
+
}
|
|
46
|
+
|
|
17
47
|
function envFromParams(envList) {
|
|
18
48
|
const env = { ...process.env };
|
|
19
49
|
if (!Array.isArray(envList)) return env;
|
|
@@ -164,6 +194,7 @@ class GrokACPClient {
|
|
|
164
194
|
this.child = spawn(cliPath || 'grok', args, { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
165
195
|
this.pending = new Map();
|
|
166
196
|
this.listeners = new Map();
|
|
197
|
+
this.sessionCwd = new Map();
|
|
167
198
|
// High ids avoid colliding with agent-originated request ids (often 0..N).
|
|
168
199
|
this.nextID = 100000;
|
|
169
200
|
this.terminals = new TerminalHost();
|
|
@@ -198,13 +229,19 @@ class GrokACPClient {
|
|
|
198
229
|
return init;
|
|
199
230
|
}
|
|
200
231
|
|
|
232
|
+
fsCwd(params) {
|
|
233
|
+
return (params?.sessionId && this.sessionCwd.get(params.sessionId)) || process.cwd();
|
|
234
|
+
}
|
|
235
|
+
|
|
201
236
|
async handleClientRequest(msg) {
|
|
202
237
|
const method = msg.method;
|
|
203
238
|
const params = msg.params || {};
|
|
204
239
|
try {
|
|
205
240
|
let result = {};
|
|
206
|
-
if (method === '
|
|
207
|
-
|
|
241
|
+
if (method === 'session/request_permission') {
|
|
242
|
+
result = { outcome: selectPermissionOption(params.options) };
|
|
243
|
+
} else if (method === 'fs/read_text_file') {
|
|
244
|
+
const filePath = resolveFsPath(params.path, this.fsCwd(params));
|
|
208
245
|
const content = await fs.promises.readFile(filePath, 'utf8');
|
|
209
246
|
const limit = Number.isFinite(params.limit) ? params.limit : null;
|
|
210
247
|
const line = Number.isFinite(params.line) ? params.line : null;
|
|
@@ -217,11 +254,13 @@ class GrokACPClient {
|
|
|
217
254
|
result = { content };
|
|
218
255
|
}
|
|
219
256
|
} else if (method === 'fs/write_text_file') {
|
|
220
|
-
const filePath =
|
|
257
|
+
const filePath = resolveFsPath(params.path, this.fsCwd(params));
|
|
258
|
+
const content = params.content == null ? '' : String(params.content);
|
|
221
259
|
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
222
|
-
await fs.promises.writeFile(filePath,
|
|
260
|
+
await fs.promises.writeFile(filePath, content, 'utf8');
|
|
223
261
|
result = {};
|
|
224
262
|
} else if (method === 'terminal/create') {
|
|
263
|
+
if (!params.cwd) params = { ...params, cwd: this.fsCwd(params) };
|
|
225
264
|
result = this.terminals.create(params);
|
|
226
265
|
} else if (method === 'terminal/output') {
|
|
227
266
|
result = this.terminals.output(params.terminalId);
|
|
@@ -244,7 +283,10 @@ class GrokACPClient {
|
|
|
244
283
|
this.write({
|
|
245
284
|
jsonrpc: '2.0',
|
|
246
285
|
id: msg.id,
|
|
247
|
-
error: {
|
|
286
|
+
error: {
|
|
287
|
+
code: jsonRpcErrorCode(error),
|
|
288
|
+
message: error.message || 'Client request failed',
|
|
289
|
+
},
|
|
248
290
|
});
|
|
249
291
|
}
|
|
250
292
|
}
|
|
@@ -291,20 +333,23 @@ class GrokACPClient {
|
|
|
291
333
|
|
|
292
334
|
async ensureSession(options) {
|
|
293
335
|
await this.ready;
|
|
336
|
+
const cwd = options.cwd || process.cwd();
|
|
294
337
|
if (options.sessionId) {
|
|
295
338
|
try {
|
|
296
|
-
await this.request('session/load', { sessionId: options.sessionId, cwd
|
|
339
|
+
await this.request('session/load', { sessionId: options.sessionId, cwd, mcpServers: [] });
|
|
297
340
|
} catch {
|
|
298
|
-
await this.request('session/resume', { sessionId: options.sessionId, cwd
|
|
341
|
+
await this.request('session/resume', { sessionId: options.sessionId, cwd, mcpServers: [] });
|
|
299
342
|
}
|
|
343
|
+
this.sessionCwd.set(options.sessionId, cwd);
|
|
300
344
|
return options.sessionId;
|
|
301
345
|
}
|
|
302
346
|
const result = await this.request('session/new', {
|
|
303
|
-
cwd
|
|
347
|
+
cwd,
|
|
304
348
|
mcpServers: [],
|
|
305
349
|
_meta: { yoloMode: true },
|
|
306
350
|
});
|
|
307
351
|
if (!result.sessionId) throw new Error('Grok ACP did not return a session id');
|
|
352
|
+
this.sessionCwd.set(result.sessionId, cwd);
|
|
308
353
|
return result.sessionId;
|
|
309
354
|
}
|
|
310
355
|
}
|
package/src/agents/sessions.js
CHANGED
|
@@ -910,12 +910,16 @@ function geminiJournalPath(sessionId, geminiHome) {
|
|
|
910
910
|
return path.join(geminiUiSessionsRoot(geminiHome), `${sessionId}.jsonl`);
|
|
911
911
|
}
|
|
912
912
|
|
|
913
|
-
// agy keeps
|
|
914
|
-
//
|
|
915
|
-
//
|
|
916
|
-
function
|
|
913
|
+
// agy keeps both a compact transport log and a lossless transcript. The
|
|
914
|
+
// compact log replaces large fields with `<truncated N bytes>`; always prefer
|
|
915
|
+
// the full file when it is available so chat history never loses message text.
|
|
916
|
+
function geminiNativeTranscriptPaths(sessionId, geminiHome) {
|
|
917
917
|
const home = geminiHome || path.join(os.homedir(), '.gemini');
|
|
918
|
-
|
|
918
|
+
const logs = path.join(home, 'antigravity-cli', 'brain', sessionId, '.system_generated', 'logs');
|
|
919
|
+
return [
|
|
920
|
+
path.join(logs, 'transcript_full.jsonl'),
|
|
921
|
+
path.join(logs, 'transcript.jsonl'),
|
|
922
|
+
];
|
|
919
923
|
}
|
|
920
924
|
|
|
921
925
|
function geminiPathFromUri(uri) {
|
|
@@ -1039,8 +1043,10 @@ async function findGeminiJournal(sessionId, geminiHome) {
|
|
|
1039
1043
|
}
|
|
1040
1044
|
|
|
1041
1045
|
async function findGeminiNativeTranscript(sessionId, geminiHome) {
|
|
1042
|
-
const file
|
|
1043
|
-
|
|
1046
|
+
for (const file of geminiNativeTranscriptPaths(sessionId, geminiHome)) {
|
|
1047
|
+
try { await fs.access(file); return file; } catch { /* try the compact fallback */ }
|
|
1048
|
+
}
|
|
1049
|
+
return null;
|
|
1044
1050
|
}
|
|
1045
1051
|
|
|
1046
1052
|
function geminiUserText(content) {
|
|
@@ -1571,7 +1577,8 @@ export async function loadSessionJsonl(sessionId, agent, { claudeConfigDirs, cod
|
|
|
1571
1577
|
let file;
|
|
1572
1578
|
if (a === 'codex') file = await findCodexSessionFile(sessionId, codexHome);
|
|
1573
1579
|
else if (a === 'cursor') file = await findCursorJournal(sessionId, cursorHome);
|
|
1574
|
-
else if (a === 'gemini') file = await
|
|
1580
|
+
else if (a === 'gemini') file = await findGeminiNativeTranscript(sessionId, geminiHome)
|
|
1581
|
+
|| await findGeminiJournal(sessionId, geminiHome);
|
|
1575
1582
|
else if (a === 'grok') {
|
|
1576
1583
|
const dir = await findGrokSessionDir(sessionId, grokHome);
|
|
1577
1584
|
file = dir ? path.join(dir, 'chat_history.jsonl') : null;
|
|
File without changes
|