zhitalk 0.0.5 → 0.0.7
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 +2 -2
- package/dist/agent/agent.js +86 -63
- package/dist/agent/cli.js +39 -73
- package/dist/agent/colors.js +6 -44
- package/dist/agent/commands.js +22 -28
- package/dist/agent/config.js +20 -62
- package/dist/agent/context.js +14 -12
- package/dist/agent/db.js +17 -27
- package/dist/agent/hooks.d.ts +1 -1
- package/dist/agent/hooks.js +13 -22
- package/dist/agent/mcp/client.js +25 -29
- package/dist/agent/mcp/index.d.ts +1 -1
- package/dist/agent/mcp/index.js +8 -13
- package/dist/agent/mcp/wrapper.d.ts +2 -2
- package/dist/agent/mcp/wrapper.js +5 -8
- package/dist/agent/model.js +6 -10
- package/dist/agent/permission/exec.js +8 -11
- package/dist/agent/permission/is-dangerous-path.js +126 -18
- package/dist/agent/permission/is-safe-domains.js +1 -4
- package/dist/agent/permission/network.js +3 -6
- package/dist/agent/permission/read.js +3 -6
- package/dist/agent/permission/util.js +16 -26
- package/dist/agent/permission/write.js +5 -8
- package/dist/agent/prompt.js +9 -15
- package/dist/agent/skills.js +19 -24
- package/dist/agent/tools/agent_tool.js +2 -38
- package/dist/agent/tools/exec_tool.js +4 -7
- package/dist/agent/tools/load_skill_tool.js +3 -6
- package/dist/agent/tools/memory_create_tool.js +4 -10
- package/dist/agent/tools/memory_delete_tool.js +4 -10
- package/dist/agent/tools/memory_retrieve_tool.js +3 -6
- package/dist/agent/tools/profile_update_tool.js +11 -14
- package/dist/agent/tools/read_file_tool.js +4 -6
- package/dist/agent/tools/run_js_tool.js +3 -6
- package/dist/agent/tools/run_py_tool.js +4 -7
- package/dist/agent/tools/search.js +1 -4
- package/dist/agent/tools/web_fetch_tool.js +1 -4
- package/dist/agent/tools/web_search_tool.js +5 -8
- package/dist/agent/tools/write_file_tool.js +8 -9
- package/dist/agent/tools.js +76 -81
- package/dist/agent/utils.d.ts +1 -0
- package/dist/agent/utils.js +13 -6
- package/dist/index.js +23 -49
- package/dist/install.js +14 -50
- package/jest.config.js +11 -0
- package/package.json +8 -7
- package/dist/agent/permission/dangerous-path.json +0 -115
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Zhitalk
|
|
1
|
+
# 智语 Zhitalk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[智语 Zhitalk](https://zhitalk.chat/) 是一个 AI Agent 个人助手,像 OpenClaw 小龙虾一样。 包含 tools skills memory hook subagent MCP-server 等 Agent 功能。你可以和它聊天,给它分配任务,让它操作文件...
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
package/dist/agent/agent.js
CHANGED
|
@@ -1,41 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const read_1 = require("./permission/read");
|
|
17
|
-
const write_1 = require("./permission/write");
|
|
18
|
-
const exec_1 = require("./permission/exec");
|
|
19
|
-
const network_1 = require("./permission/network");
|
|
20
|
-
const hooks_1 = require("./hooks");
|
|
21
|
-
const model_1 = require("./model");
|
|
1
|
+
import { SqliteSaver } from '@langchain/langgraph-checkpoint-sqlite';
|
|
2
|
+
import { SystemMessage, AIMessage, HumanMessage, ToolMessage, } from '@langchain/core/messages';
|
|
3
|
+
import { StateGraph, Annotation, START, END, interrupt, Command, } from '@langchain/langgraph';
|
|
4
|
+
import { messagesStateReducer } from '@langchain/langgraph';
|
|
5
|
+
import { DB_PATH } from './db.js';
|
|
6
|
+
import { tools, maybePersistedOutput, initTools } from './tools.js';
|
|
7
|
+
import { compressMessages } from './context.js';
|
|
8
|
+
import { systemPrompt } from './prompt.js';
|
|
9
|
+
import { formatToolLog } from './colors.js';
|
|
10
|
+
import { checkReadPermission } from './permission/read.js';
|
|
11
|
+
import { checkWritePermission } from './permission/write.js';
|
|
12
|
+
import { checkExecPermission } from './permission/exec.js';
|
|
13
|
+
import { checkNetworkPermission } from './permission/network.js';
|
|
14
|
+
import { runPreToolUseHooks, runPostToolUseHooks } from './hooks.js';
|
|
15
|
+
import { createModel } from './model.js';
|
|
22
16
|
// ── 模型 ──────────────────────────────────────────────────
|
|
23
|
-
const model =
|
|
17
|
+
const model = createModel({ streaming: true });
|
|
24
18
|
// ── State Schema ──────────────────────────────────────────
|
|
25
|
-
const StateAnnotation =
|
|
26
|
-
messages:
|
|
27
|
-
reducer:
|
|
19
|
+
const StateAnnotation = Annotation.Root({
|
|
20
|
+
messages: Annotation({
|
|
21
|
+
reducer: messagesStateReducer,
|
|
28
22
|
default: () => [],
|
|
29
23
|
}),
|
|
30
|
-
contextSummary:
|
|
24
|
+
contextSummary: Annotation({
|
|
31
25
|
reducer: (_prev, next) => next,
|
|
32
26
|
default: () => null,
|
|
33
27
|
}),
|
|
34
|
-
compressionCount:
|
|
28
|
+
compressionCount: Annotation({
|
|
35
29
|
reducer: (_prev, next) => next,
|
|
36
30
|
default: () => 0,
|
|
37
31
|
}),
|
|
38
|
-
lastCompressedIndex:
|
|
32
|
+
lastCompressedIndex: Annotation({
|
|
39
33
|
reducer: (_prev, next) => next,
|
|
40
34
|
default: () => 0,
|
|
41
35
|
}),
|
|
@@ -43,10 +37,10 @@ const StateAnnotation = langgraph_1.Annotation.Root({
|
|
|
43
37
|
// ── Graph Nodes ───────────────────────────────────────────
|
|
44
38
|
function shouldContinue(state) {
|
|
45
39
|
const lastMessage = state.messages[state.messages.length - 1];
|
|
46
|
-
if (
|
|
40
|
+
if (AIMessage.isInstance(lastMessage) && lastMessage.tool_calls?.length) {
|
|
47
41
|
return 'tools';
|
|
48
42
|
}
|
|
49
|
-
return
|
|
43
|
+
return END;
|
|
50
44
|
}
|
|
51
45
|
function simplifyToolMessages(messages) {
|
|
52
46
|
const toolIndices = [];
|
|
@@ -65,15 +59,41 @@ function simplifyToolMessages(messages) {
|
|
|
65
59
|
const toolMsg = msg;
|
|
66
60
|
if (toolMsg.name === 'read_file')
|
|
67
61
|
return msg;
|
|
68
|
-
return new
|
|
62
|
+
return new ToolMessage({
|
|
69
63
|
content: `[Previous: used ${toolMsg.name}]`,
|
|
70
64
|
tool_call_id: toolMsg.tool_call_id,
|
|
71
65
|
name: toolMsg.name,
|
|
72
66
|
});
|
|
73
67
|
});
|
|
74
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* 确保每个带有 tool_calls 的 AIMessage 后面都有对应的 ToolMessage。
|
|
71
|
+
* 当中断(如 ESC 取消)导致 tool 未执行时,消息历史会留下未完成的
|
|
72
|
+
* tool_calls,LLM API 会因此报错。此函数为缺失的 tool_calls 插入
|
|
73
|
+
* 占位 ToolMessage,使消息历史重新合法。
|
|
74
|
+
*/
|
|
75
|
+
function ensureCompleteToolCalls(messages) {
|
|
76
|
+
const toolMessageIds = new Set(messages
|
|
77
|
+
.filter((msg) => msg.type === 'tool')
|
|
78
|
+
.map((msg) => msg.tool_call_id));
|
|
79
|
+
const result = [];
|
|
80
|
+
for (const msg of messages) {
|
|
81
|
+
result.push(msg);
|
|
82
|
+
if (AIMessage.isInstance(msg) && msg.tool_calls?.length) {
|
|
83
|
+
const missing = msg.tool_calls.filter((call) => call.id && !toolMessageIds.has(call.id));
|
|
84
|
+
for (const call of missing) {
|
|
85
|
+
result.push(new ToolMessage({
|
|
86
|
+
content: 'Tool execution was interrupted or failed to complete.',
|
|
87
|
+
tool_call_id: call.id ?? '',
|
|
88
|
+
name: call.name ?? '',
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
75
95
|
// ── 记忆 ──────────────────────────────────────────────────
|
|
76
|
-
const checkpointer =
|
|
96
|
+
const checkpointer = SqliteSaver.fromConnString(DB_PATH);
|
|
77
97
|
// ── Agent Graph 工厂 ──────────────────────────────────────
|
|
78
98
|
function createAgentGraph(toolList) {
|
|
79
99
|
const modelWithTheseTools = model.bindTools(toolList);
|
|
@@ -81,7 +101,7 @@ function createAgentGraph(toolList) {
|
|
|
81
101
|
let modelMessages = state.messages ?? [];
|
|
82
102
|
// compact context
|
|
83
103
|
if (state.contextSummary && state.lastCompressedIndex > 0) {
|
|
84
|
-
const summaryMsg = new
|
|
104
|
+
const summaryMsg = new SystemMessage(`历史对话摘要:\n\n${state.contextSummary}`);
|
|
85
105
|
modelMessages = [
|
|
86
106
|
summaryMsg,
|
|
87
107
|
...modelMessages.slice(state.lastCompressedIndex),
|
|
@@ -91,7 +111,9 @@ function createAgentGraph(toolList) {
|
|
|
91
111
|
modelMessages = simplifyToolMessages(modelMessages);
|
|
92
112
|
// keep recent 300 items
|
|
93
113
|
modelMessages = modelMessages.slice(0, 300);
|
|
94
|
-
|
|
114
|
+
// 修复因中断/截断导致的未完成 tool_calls
|
|
115
|
+
modelMessages = ensureCompleteToolCalls(modelMessages);
|
|
116
|
+
const messages = [new SystemMessage(systemPrompt), ...modelMessages];
|
|
95
117
|
const response = await modelWithTheseTools.invoke(messages, config);
|
|
96
118
|
return { messages: [response] };
|
|
97
119
|
}
|
|
@@ -102,12 +124,12 @@ function createAgentGraph(toolList) {
|
|
|
102
124
|
.map((msg) => msg.tool_call_id));
|
|
103
125
|
let aiMessage;
|
|
104
126
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
105
|
-
if (
|
|
127
|
+
if (AIMessage.isInstance(messages[i])) {
|
|
106
128
|
aiMessage = messages[i];
|
|
107
129
|
break;
|
|
108
130
|
}
|
|
109
131
|
}
|
|
110
|
-
if (!aiMessage || !
|
|
132
|
+
if (!aiMessage || !AIMessage.isInstance(aiMessage)) {
|
|
111
133
|
throw new Error('ToolNode only accepts AIMessages as input.');
|
|
112
134
|
}
|
|
113
135
|
const toolCalls = aiMessage.tool_calls?.filter((call) => call.id == null || !toolMessageIds.has(call.id)) ?? [];
|
|
@@ -122,16 +144,16 @@ function createAgentGraph(toolList) {
|
|
|
122
144
|
const level = tool?.permission_level;
|
|
123
145
|
let decision;
|
|
124
146
|
if (level === 'read') {
|
|
125
|
-
decision =
|
|
147
|
+
decision = checkReadPermission(call);
|
|
126
148
|
}
|
|
127
149
|
else if (level === 'write') {
|
|
128
|
-
decision =
|
|
150
|
+
decision = checkWritePermission(call);
|
|
129
151
|
}
|
|
130
152
|
else if (level === 'exec') {
|
|
131
|
-
decision =
|
|
153
|
+
decision = checkExecPermission(call);
|
|
132
154
|
}
|
|
133
155
|
else if (level === 'network') {
|
|
134
|
-
decision =
|
|
156
|
+
decision = checkNetworkPermission(call);
|
|
135
157
|
}
|
|
136
158
|
else if (level === 'mcp') {
|
|
137
159
|
decision = { action: 'confirm' };
|
|
@@ -143,7 +165,7 @@ function createAgentGraph(toolList) {
|
|
|
143
165
|
allowCalls.push(call);
|
|
144
166
|
}
|
|
145
167
|
else if (decision.action === 'block') {
|
|
146
|
-
blockMessages.push(new
|
|
168
|
+
blockMessages.push(new ToolMessage({
|
|
147
169
|
content: decision.reason,
|
|
148
170
|
tool_call_id: call.id ?? '',
|
|
149
171
|
name: call.name,
|
|
@@ -156,18 +178,18 @@ function createAgentGraph(toolList) {
|
|
|
156
178
|
async function executeCalls(calls) {
|
|
157
179
|
return Promise.all(calls.map(async (call) => {
|
|
158
180
|
const tool = toolList.find((t) => t.name === call.name);
|
|
159
|
-
console.log(
|
|
181
|
+
console.log(formatToolLog(call.name, JSON.stringify(call.args)));
|
|
160
182
|
try {
|
|
161
183
|
const threadId = config?.configurable?.thread_id || '';
|
|
162
184
|
// PreToolUse hook
|
|
163
|
-
const preResult = await
|
|
185
|
+
const preResult = await runPreToolUseHooks({
|
|
164
186
|
toolName: call.name,
|
|
165
187
|
toolArgs: call.args,
|
|
166
188
|
toolCallId: call.id ?? '',
|
|
167
189
|
threadId,
|
|
168
190
|
});
|
|
169
191
|
if (preResult.action === 'block') {
|
|
170
|
-
return new
|
|
192
|
+
return new ToolMessage({
|
|
171
193
|
content: preResult.reason,
|
|
172
194
|
tool_call_id: call.id ?? '',
|
|
173
195
|
name: call.name,
|
|
@@ -178,7 +200,7 @@ function createAgentGraph(toolList) {
|
|
|
178
200
|
const output = await tool.invoke({ ...call, type: 'tool_call' }, config);
|
|
179
201
|
let content = typeof output === 'string' ? output : JSON.stringify(output);
|
|
180
202
|
// PostToolUse hook
|
|
181
|
-
const postResult = await
|
|
203
|
+
const postResult = await runPostToolUseHooks({
|
|
182
204
|
toolName: call.name,
|
|
183
205
|
toolArgs: call.args,
|
|
184
206
|
toolOutput: content,
|
|
@@ -194,15 +216,15 @@ function createAgentGraph(toolList) {
|
|
|
194
216
|
if (preResult.action === 'inject') {
|
|
195
217
|
content = `[Hook injection]\n${preResult.message}\n\n${content}`;
|
|
196
218
|
}
|
|
197
|
-
const finalContent = await
|
|
198
|
-
return new
|
|
219
|
+
const finalContent = await maybePersistedOutput(content, call.id ?? '');
|
|
220
|
+
return new ToolMessage({
|
|
199
221
|
content: finalContent,
|
|
200
222
|
tool_call_id: call.id ?? '',
|
|
201
223
|
name: call.name,
|
|
202
224
|
});
|
|
203
225
|
}
|
|
204
226
|
catch (e) {
|
|
205
|
-
return new
|
|
227
|
+
return new ToolMessage({
|
|
206
228
|
content: `Error: ${e.message}\n Please fix your mistakes.`,
|
|
207
229
|
tool_call_id: call.id ?? '',
|
|
208
230
|
name: call.name,
|
|
@@ -214,9 +236,9 @@ function createAgentGraph(toolList) {
|
|
|
214
236
|
if (confirmCalls.length === 0) {
|
|
215
237
|
return { messages: [...allowOutputs, ...blockMessages] };
|
|
216
238
|
}
|
|
217
|
-
const result =
|
|
239
|
+
const result = interrupt({ toolCalls: confirmCalls });
|
|
218
240
|
if (result !== 'approved') {
|
|
219
|
-
const deniedMessages = confirmCalls.map((call) => new
|
|
241
|
+
const deniedMessages = confirmCalls.map((call) => new ToolMessage({
|
|
220
242
|
content: 'Tool execution was denied by the user.',
|
|
221
243
|
tool_call_id: call.id ?? '',
|
|
222
244
|
name: call.name,
|
|
@@ -228,13 +250,13 @@ function createAgentGraph(toolList) {
|
|
|
228
250
|
const confirmOutputs = await executeCalls(confirmCalls);
|
|
229
251
|
return { messages: [...allowOutputs, ...blockMessages, ...confirmOutputs] };
|
|
230
252
|
}
|
|
231
|
-
const workflow = new
|
|
253
|
+
const workflow = new StateGraph(StateAnnotation)
|
|
232
254
|
.addNode('model_request', modelRequest)
|
|
233
255
|
.addNode('tools', toolNode)
|
|
234
|
-
.addEdge(
|
|
256
|
+
.addEdge(START, 'model_request')
|
|
235
257
|
.addConditionalEdges('model_request', shouldContinue, {
|
|
236
258
|
tools: 'tools',
|
|
237
|
-
[
|
|
259
|
+
[END]: END,
|
|
238
260
|
})
|
|
239
261
|
.addEdge('tools', 'model_request');
|
|
240
262
|
return workflow.compile({
|
|
@@ -244,10 +266,10 @@ function createAgentGraph(toolList) {
|
|
|
244
266
|
// ── Agent 创建 ────────────────────────────────────────────
|
|
245
267
|
let agent = null;
|
|
246
268
|
let subAgent = null;
|
|
247
|
-
async function initAgent() {
|
|
248
|
-
await
|
|
249
|
-
agent = createAgentGraph(
|
|
250
|
-
subAgent = createAgentGraph(
|
|
269
|
+
export async function initAgent() {
|
|
270
|
+
await initTools();
|
|
271
|
+
agent = createAgentGraph(tools);
|
|
272
|
+
subAgent = createAgentGraph(tools.filter((t) => t.name !== 'agent_tool'));
|
|
251
273
|
}
|
|
252
274
|
function getAgent() {
|
|
253
275
|
if (!agent)
|
|
@@ -264,12 +286,13 @@ async function _runAgent(compiledAgent, userMessage, onToken, onToolConfirmation
|
|
|
264
286
|
const config = { configurable: { thread_id: threadId } };
|
|
265
287
|
let fullResponse = '';
|
|
266
288
|
let usageMetadata;
|
|
267
|
-
let input = { messages: [new
|
|
289
|
+
let input = { messages: [new HumanMessage(userMessage)] };
|
|
268
290
|
while (true) {
|
|
269
291
|
const stream = await compiledAgent.stream(input, {
|
|
270
292
|
...config,
|
|
271
293
|
streamMode: 'messages',
|
|
272
294
|
signal,
|
|
295
|
+
recursionLimit: 50,
|
|
273
296
|
});
|
|
274
297
|
for await (const chunk of stream) {
|
|
275
298
|
if (signal?.aborted) {
|
|
@@ -296,7 +319,7 @@ async function _runAgent(compiledAgent, userMessage, onToken, onToolConfirmation
|
|
|
296
319
|
if (!interruptedTask)
|
|
297
320
|
break;
|
|
298
321
|
const confirmed = await onToolConfirmation(interruptedTask.interrupts[0].value.toolCalls);
|
|
299
|
-
input = new
|
|
322
|
+
input = new Command({ resume: confirmed ? 'approved' : 'denied' });
|
|
300
323
|
}
|
|
301
324
|
return { response: fullResponse, usageMetadata };
|
|
302
325
|
}
|
|
@@ -308,7 +331,7 @@ async function _runAgent(compiledAgent, userMessage, onToken, onToolConfirmation
|
|
|
308
331
|
* @param {string} threadId - 会话 ID,相同 ID 自动续上历史记录
|
|
309
332
|
* @returns {Promise<{ response: string; usageMetadata?: UsageMetadata }>} 完整的 AI 回复文本及 token 使用信息
|
|
310
333
|
*/
|
|
311
|
-
async function runAgentStream(userMessage, onToken, onToolConfirmation, threadId = 'default-session', signal) {
|
|
334
|
+
export async function runAgentStream(userMessage, onToken, onToolConfirmation, threadId = 'default-session', signal) {
|
|
312
335
|
return _runAgent(getAgent(), userMessage, onToken, onToolConfirmation, threadId, signal);
|
|
313
336
|
}
|
|
314
337
|
/**
|
|
@@ -316,14 +339,14 @@ async function runAgentStream(userMessage, onToken, onToolConfirmation, threadId
|
|
|
316
339
|
* @param {string} prompt - 给 subagent 的任务提示
|
|
317
340
|
* @returns {Promise<string>} subagent 的最终回复
|
|
318
341
|
*/
|
|
319
|
-
async function runSubAgent(prompt) {
|
|
342
|
+
export async function runSubAgent(prompt) {
|
|
320
343
|
const threadId = `subagent-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
321
344
|
const result = await _runAgent(getSubAgent(), prompt.trim(), () => { }, // 不需要流式输出
|
|
322
345
|
async () => true, // subagent 自动确认 tools
|
|
323
346
|
threadId, undefined);
|
|
324
347
|
return result.response;
|
|
325
348
|
}
|
|
326
|
-
async function compressContext(threadId) {
|
|
349
|
+
export async function compressContext(threadId) {
|
|
327
350
|
const config = { configurable: { thread_id: threadId } };
|
|
328
351
|
const currentState = await getAgent().getState(config);
|
|
329
352
|
const messages = currentState.values.messages || [];
|
|
@@ -335,7 +358,7 @@ async function compressContext(threadId) {
|
|
|
335
358
|
return { didCompress: false, count };
|
|
336
359
|
}
|
|
337
360
|
const toCompress = messages.slice(lastIndex, messages.length - recentKeep);
|
|
338
|
-
const newSummary = await
|
|
361
|
+
const newSummary = await compressMessages(toCompress, existingSummary);
|
|
339
362
|
await getAgent().updateState(config, {
|
|
340
363
|
contextSummary: newSummary,
|
|
341
364
|
lastCompressedIndex: messages.length - recentKeep,
|
package/dist/agent/cli.js
CHANGED
|
@@ -1,48 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.interactiveChat = interactiveChat;
|
|
37
|
-
const readline = __importStar(require("readline"));
|
|
38
|
-
const fs_1 = require("fs");
|
|
39
|
-
const path_1 = require("path");
|
|
40
|
-
const agent_1 = require("./agent");
|
|
41
|
-
const context_1 = require("./context");
|
|
42
|
-
const colors_1 = require("./colors");
|
|
43
|
-
const commands_1 = require("./commands");
|
|
44
|
-
const hooks_1 = require("./hooks");
|
|
45
|
-
const pkg = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../../package.json'), 'utf-8'));
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { runAgentStream, compressContext } from './agent.js';
|
|
6
|
+
import { getModelContextLimit } from './context.js';
|
|
7
|
+
import { color, formatToolLog } from './colors.js';
|
|
8
|
+
import { threadId, commands } from './commands.js';
|
|
9
|
+
import { runSessionStartHooks } from './hooks.js';
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf-8'));
|
|
46
12
|
function createInterface() {
|
|
47
13
|
return readline.createInterface({
|
|
48
14
|
input: process.stdin,
|
|
@@ -50,14 +16,14 @@ function createInterface() {
|
|
|
50
16
|
});
|
|
51
17
|
}
|
|
52
18
|
async function printBanner() {
|
|
53
|
-
const figlet = (await
|
|
54
|
-
const { default: boxen } = await
|
|
55
|
-
console.log(
|
|
19
|
+
const figlet = (await import('figlet')).default;
|
|
20
|
+
const { default: boxen } = await import('boxen');
|
|
21
|
+
console.log(color.banner(figlet.textSync(pkg.name, { font: 'Slant' })));
|
|
56
22
|
const info = [
|
|
57
|
-
`${
|
|
58
|
-
`${
|
|
59
|
-
`${
|
|
60
|
-
`${
|
|
23
|
+
`${color.gray('Description')}: ${pkg.description}`,
|
|
24
|
+
`${color.gray('Version')}: ${pkg.version}`,
|
|
25
|
+
`${color.gray('Author')}: ${pkg.author}`,
|
|
26
|
+
`${color.gray('Docs')}: ${pkg.docs}`,
|
|
61
27
|
].join('\n');
|
|
62
28
|
console.log(boxen(info, {
|
|
63
29
|
padding: 1,
|
|
@@ -67,7 +33,7 @@ async function printBanner() {
|
|
|
67
33
|
console.log('Usage:');
|
|
68
34
|
console.log(' ESC - Cancel AI request');
|
|
69
35
|
console.log(' exit - Exit the chat');
|
|
70
|
-
for (const [name, cmd] of
|
|
36
|
+
for (const [name, cmd] of commands) {
|
|
71
37
|
console.log(` /${name.padEnd(8)} - ${cmd.description}`);
|
|
72
38
|
}
|
|
73
39
|
console.log();
|
|
@@ -83,12 +49,12 @@ function prompt(question) {
|
|
|
83
49
|
}
|
|
84
50
|
async function chat(userInput) {
|
|
85
51
|
const rl = createInterface();
|
|
86
|
-
process.stdout.write('\n' +
|
|
52
|
+
process.stdout.write('\n' + color.aiPrefix());
|
|
87
53
|
const controller = new AbortController();
|
|
88
54
|
// 监听 ESC 键,中断 AI 请求
|
|
89
55
|
const escListener = (_str, key) => {
|
|
90
56
|
if (key.name === 'escape' || key.name === 'esc') {
|
|
91
|
-
process.stdout.write('\n\n' +
|
|
57
|
+
process.stdout.write('\n\n' + color.cancelled('[Cancelled]') + '\n');
|
|
92
58
|
controller.abort();
|
|
93
59
|
rl.close();
|
|
94
60
|
}
|
|
@@ -97,18 +63,18 @@ async function chat(userInput) {
|
|
|
97
63
|
process.stdin.on('keypress', escListener);
|
|
98
64
|
let usageMetadata;
|
|
99
65
|
try {
|
|
100
|
-
const result = await
|
|
66
|
+
const result = await runAgentStream(userInput, (token) => {
|
|
101
67
|
process.stdout.write(token);
|
|
102
68
|
}, async (toolCalls) => {
|
|
103
69
|
for (const call of toolCalls) {
|
|
104
|
-
console.log(
|
|
70
|
+
console.log(formatToolLog(call.name, JSON.stringify(call.args)));
|
|
105
71
|
}
|
|
106
72
|
const answer = await new Promise((resolve) => {
|
|
107
73
|
rl.question('确认执行以上工具? (y/n): ', resolve);
|
|
108
74
|
});
|
|
109
75
|
return (answer.trim().toLowerCase() === 'y' ||
|
|
110
76
|
answer.trim().toLowerCase() === 'yes');
|
|
111
|
-
},
|
|
77
|
+
}, threadId, controller.signal);
|
|
112
78
|
usageMetadata = result.usageMetadata;
|
|
113
79
|
}
|
|
114
80
|
catch (err) {
|
|
@@ -121,23 +87,23 @@ async function chat(userInput) {
|
|
|
121
87
|
rl.close();
|
|
122
88
|
}
|
|
123
89
|
if (usageMetadata) {
|
|
124
|
-
const limit =
|
|
90
|
+
const limit = getModelContextLimit();
|
|
125
91
|
const percentage = (usageMetadata.total_tokens / limit) * 100;
|
|
126
92
|
const percentageStr = percentage.toFixed(1);
|
|
127
93
|
const tokenText = `Tokens: ${usageMetadata.total_tokens.toLocaleString()} / ${limit.toLocaleString()} (${percentageStr}%)`;
|
|
128
94
|
if (percentage >= 80) {
|
|
129
95
|
process.stdout.write('\n' +
|
|
130
|
-
|
|
96
|
+
color.error(tokenText) +
|
|
131
97
|
'\n' +
|
|
132
|
-
|
|
98
|
+
color.error('警告:Context window 接近大模型接口上限,即将压缩 Context,可能会丢失信息') +
|
|
133
99
|
'\n' +
|
|
134
|
-
|
|
100
|
+
color.error('建议输入 /new 命令开启新会话'));
|
|
135
101
|
try {
|
|
136
|
-
const result = await
|
|
102
|
+
const result = await compressContext(threadId);
|
|
137
103
|
if (result.didCompress) {
|
|
138
|
-
process.stdout.write('\n' +
|
|
104
|
+
process.stdout.write('\n' + color.error(`Context 已压缩(第 ${result.count} 次),已保留最近 6 条消息`) + '\n');
|
|
139
105
|
if (result.count >= 3) {
|
|
140
|
-
process.stdout.write(
|
|
106
|
+
process.stdout.write(color.error('强烈建议输入 /new 命令开启新会话,以避免信息丢失') + '\n');
|
|
141
107
|
}
|
|
142
108
|
}
|
|
143
109
|
}
|
|
@@ -146,32 +112,32 @@ async function chat(userInput) {
|
|
|
146
112
|
}
|
|
147
113
|
}
|
|
148
114
|
else {
|
|
149
|
-
process.stdout.write('\n' +
|
|
115
|
+
process.stdout.write('\n' + color.tokenInfo(tokenText));
|
|
150
116
|
}
|
|
151
117
|
}
|
|
152
118
|
process.stdout.write('\n\n');
|
|
153
119
|
}
|
|
154
|
-
async function interactiveChat() {
|
|
120
|
+
export async function interactiveChat() {
|
|
155
121
|
await printBanner();
|
|
156
|
-
await
|
|
122
|
+
await runSessionStartHooks(threadId);
|
|
157
123
|
while (true) {
|
|
158
|
-
const userInput = await prompt(
|
|
124
|
+
const userInput = await prompt(color.userPrefix());
|
|
159
125
|
if (!userInput.trim())
|
|
160
126
|
continue;
|
|
161
127
|
if (userInput.toLowerCase() === 'exit') {
|
|
162
|
-
console.log(
|
|
128
|
+
console.log('Bye Bye~ Have a nice day~\n');
|
|
163
129
|
break;
|
|
164
130
|
}
|
|
165
131
|
if (userInput.startsWith('/')) {
|
|
166
132
|
const parts = userInput.trim().split(/\s+/);
|
|
167
133
|
const cmdName = parts[0].slice(1);
|
|
168
134
|
const args = parts.slice(1);
|
|
169
|
-
const cmd =
|
|
135
|
+
const cmd = commands.get(cmdName);
|
|
170
136
|
if (cmd) {
|
|
171
137
|
await cmd.execute(args);
|
|
172
138
|
}
|
|
173
139
|
else {
|
|
174
|
-
console.log(
|
|
140
|
+
console.log(color.error(`Unknown command: ${cmdName}`));
|
|
175
141
|
}
|
|
176
142
|
continue;
|
|
177
143
|
}
|
|
@@ -179,7 +145,7 @@ async function interactiveChat() {
|
|
|
179
145
|
await chat(userInput);
|
|
180
146
|
}
|
|
181
147
|
catch (err) {
|
|
182
|
-
console.error(
|
|
148
|
+
console.error(color.error(`请求出错: ${err.message}`));
|
|
183
149
|
}
|
|
184
150
|
}
|
|
185
151
|
}
|
package/dist/agent/colors.js
CHANGED
|
@@ -1,44 +1,6 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.color = void 0;
|
|
37
|
-
exports.initColors = initColors;
|
|
38
|
-
exports.formatToolLog = formatToolLog;
|
|
39
1
|
let _chalk = null;
|
|
40
|
-
async function initColors() {
|
|
41
|
-
const mod = (await
|
|
2
|
+
export async function initColors() {
|
|
3
|
+
const mod = (await import('chalk'));
|
|
42
4
|
_chalk = mod.default;
|
|
43
5
|
}
|
|
44
6
|
function createPlainFallback() {
|
|
@@ -55,7 +17,7 @@ function ck() {
|
|
|
55
17
|
}
|
|
56
18
|
return _chalk;
|
|
57
19
|
}
|
|
58
|
-
|
|
20
|
+
export const color = {
|
|
59
21
|
banner: (text) => ck().cyanBright.bold(text),
|
|
60
22
|
userPrefix: () => ck().greenBright.bold('You: '),
|
|
61
23
|
aiPrefix: () => ck().cyanBright.bold('AI: '),
|
|
@@ -72,10 +34,10 @@ exports.color = {
|
|
|
72
34
|
info: (text) => ck().blueBright(text),
|
|
73
35
|
good: (text) => ck().greenBright(text),
|
|
74
36
|
};
|
|
75
|
-
function formatToolLog(name, detail) {
|
|
76
|
-
const parts = [
|
|
37
|
+
export function formatToolLog(name, detail) {
|
|
38
|
+
const parts = [color.toolTag(), color.toolName(name), color.toolAction()];
|
|
77
39
|
if (detail) {
|
|
78
|
-
parts.push(
|
|
40
|
+
parts.push(color.toolArg(detail));
|
|
79
41
|
}
|
|
80
42
|
return '\n' + parts.join(' ');
|
|
81
43
|
}
|