zhitalk 0.0.6 → 0.0.8

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.
@@ -66,6 +66,32 @@ function simplifyToolMessages(messages) {
66
66
  });
67
67
  });
68
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
+ }
69
95
  // ── 记忆 ──────────────────────────────────────────────────
70
96
  const checkpointer = SqliteSaver.fromConnString(DB_PATH);
71
97
  // ── Agent Graph 工厂 ──────────────────────────────────────
@@ -85,6 +111,8 @@ function createAgentGraph(toolList) {
85
111
  modelMessages = simplifyToolMessages(modelMessages);
86
112
  // keep recent 300 items
87
113
  modelMessages = modelMessages.slice(0, 300);
114
+ // 修复因中断/截断导致的未完成 tool_calls
115
+ modelMessages = ensureCompleteToolCalls(modelMessages);
88
116
  const messages = [new SystemMessage(systemPrompt), ...modelMessages];
89
117
  const response = await modelWithTheseTools.invoke(messages, config);
90
118
  return { messages: [response] };
@@ -264,6 +292,7 @@ async function _runAgent(compiledAgent, userMessage, onToken, onToolConfirmation
264
292
  ...config,
265
293
  streamMode: 'messages',
266
294
  signal,
295
+ recursionLimit: 50,
267
296
  });
268
297
  for await (const chunk of stream) {
269
298
  if (signal?.aborted) {
package/dist/agent/cli.js CHANGED
@@ -125,7 +125,7 @@ export async function interactiveChat() {
125
125
  if (!userInput.trim())
126
126
  continue;
127
127
  if (userInput.toLowerCase() === 'exit') {
128
- console.log(color.goodbye('再见!'));
128
+ console.log('Bye Bye~ Have a nice day~\n');
129
129
  break;
130
130
  }
131
131
  if (userInput.startsWith('/')) {
@@ -13,6 +13,8 @@ const MODEL_CONTEXT_LIMITS = {
13
13
  'deepseek-v3': 65536,
14
14
  'deepseek-v3.1': 131072,
15
15
  'deepseek-v4': 1048576,
16
+ 'deepseek-v4-pro': 1048576,
17
+ 'deepseek-v4-flash': 1048576,
16
18
  'deepseek-r1': 65536,
17
19
  // MiniMax
18
20
  'minimax-text-01': 1048576,
@@ -24,6 +26,10 @@ const MODEL_CONTEXT_LIMITS = {
24
26
  'glm-4-air': 131072,
25
27
  'glm-4-flash': 131072,
26
28
  'glm-4v': 131072,
29
+ 'glm-5-turbo': 200000,
30
+ 'glm-5': 200000,
31
+ 'glm-5.1': 200000,
32
+ 'glm-5.2': 1000000,
27
33
  // Qwen (Alibaba)
28
34
  'qwen-max': 32768,
29
35
  'qwen-plus': 131072,
@@ -2,7 +2,7 @@ import { isSafeCommand, isChangingDirectory, isScriptExecution, isDangerousOpera
2
2
  export function checkExecPermission(toolCall) {
3
3
  const command = toolCall.args?.command;
4
4
  if (typeof command !== 'string') {
5
- return { action: 'confirm' };
5
+ return { action: 'allow' };
6
6
  }
7
7
  if (isSafeCommand(command)) {
8
8
  return { action: 'allow' };
@@ -27,5 +27,5 @@ export function checkExecPermission(toolCall) {
27
27
  reason: dangerCheck.reason,
28
28
  };
29
29
  }
30
- return { action: 'confirm' };
30
+ return { action: 'allow' };
31
31
  }
@@ -1,6 +1,120 @@
1
1
  import path from 'path';
2
2
  import os from 'os';
3
- import dangerousPaths from './dangerous-path.json' with { type: 'json' };
3
+ const dangerousPaths = {
4
+ macos: [
5
+ '~/.ssh',
6
+ '~/.gnupg',
7
+ '~/.aws',
8
+ '~/.azure',
9
+ '~/.config/gcloud',
10
+ '~/.kube',
11
+ '~/.docker',
12
+ '~/.netrc',
13
+ '~/Library/Keychains',
14
+ '~/Library/Application Support/Google/Chrome',
15
+ '~/Library/Application Support/Chromium',
16
+ '~/Library/Application Support/Firefox',
17
+ '~/Library/Application Support/Slack',
18
+ '~/Library/Application Support/Discord',
19
+ '~/Library/Application Support/Telegram',
20
+ '~/Library/Application Support/Signal',
21
+ '~/.zshrc',
22
+ '~/.bashrc',
23
+ '~/.bash_profile',
24
+ '~/.profile',
25
+ '~/.env',
26
+ '~/.env.*',
27
+ '~/.gitconfig',
28
+ '~/.git-credentials',
29
+ '~/.npmrc',
30
+ '~/.pnpmrc',
31
+ '~/.yarnrc',
32
+ '~/.pypirc',
33
+ '~/.m2',
34
+ '~/.gradle',
35
+ '~/.terraform',
36
+ '~/.config/gh',
37
+ '~/.config/gitlab',
38
+ '~/.circleci',
39
+ '~/.vercel',
40
+ '~/.netlify',
41
+ '~/Library/Containers',
42
+ '/private',
43
+ '/etc',
44
+ '/var',
45
+ ],
46
+ linux: [
47
+ '~/.ssh',
48
+ '~/.gnupg',
49
+ '~/.pki',
50
+ '~/.aws',
51
+ '~/.azure',
52
+ '~/.config/gcloud',
53
+ '~/.kube',
54
+ '~/.docker',
55
+ '~/.netrc',
56
+ '~/.local/share/keyrings',
57
+ '~/.config/google-chrome',
58
+ '~/.config/chromium',
59
+ '~/.mozilla/firefox',
60
+ '~/.config/Slack',
61
+ '~/.config/discord',
62
+ '~/.local/share/TelegramDesktop',
63
+ '~/.zshrc',
64
+ '~/.bashrc',
65
+ '~/.bash_profile',
66
+ '~/.profile',
67
+ '~/.env',
68
+ '~/.env.*',
69
+ '~/.gitconfig',
70
+ '~/.git-credentials',
71
+ '~/.npmrc',
72
+ '~/.pnpmrc',
73
+ '~/.yarnrc',
74
+ '~/.pypirc',
75
+ '~/.m2',
76
+ '~/.gradle',
77
+ '~/.terraform',
78
+ '~/.config/gh',
79
+ '~/.config/gitlab',
80
+ '~/.circleci',
81
+ '~/.vercel',
82
+ '~/.netlify',
83
+ '/etc/shadow',
84
+ '/etc/sudoers',
85
+ '/proc',
86
+ '/sys',
87
+ '/var/lib',
88
+ '/root',
89
+ ],
90
+ windows: [
91
+ '%USERPROFILE%\\.ssh',
92
+ '%USERPROFILE%\\.gnupg',
93
+ '%USERPROFILE%\\.aws',
94
+ '%USERPROFILE%\\.azure',
95
+ '%USERPROFILE%\\.kube',
96
+ '%USERPROFILE%\\.docker',
97
+ '%LOCALAPPDATA%\\Google\\Chrome\\User Data',
98
+ '%LOCALAPPDATA%\\Chromium\\User Data',
99
+ '%APPDATA%\\Mozilla\\Firefox',
100
+ '%APPDATA%\\Slack',
101
+ '%APPDATA%\\Discord',
102
+ '%APPDATA%\\Telegram Desktop',
103
+ '%USERPROFILE%\\.gitconfig',
104
+ '%USERPROFILE%\\.git-credentials',
105
+ '%USERPROFILE%\\.npmrc',
106
+ '%USERPROFILE%\\.pnpmrc',
107
+ '%USERPROFILE%\\.yarnrc',
108
+ '%USERPROFILE%\\.m2',
109
+ '%USERPROFILE%\\.gradle',
110
+ '%APPDATA%',
111
+ '%LOCALAPPDATA%',
112
+ 'C:\\Windows\\System32',
113
+ 'C:\\Windows\\System32\\config\\SAM',
114
+ 'C:\\Windows\\System32\\config\\SECURITY',
115
+ 'C:\\Windows\\System32\\config\\SYSTEM',
116
+ ],
117
+ };
4
118
  function expandPath(filepath) {
5
119
  let expanded = filepath;
6
120
  if (expanded.startsWith('~')) {
@@ -39,6 +39,20 @@ const memoryPrompt = `## Memory Management Rules
39
39
  const dateTimePrompt = `## Current DateTime
40
40
 
41
41
  ${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}`;
42
- export const systemPrompt = skillsText
43
- ? `${basePrompt}\n\n${profilePrompt}\n\n${memoryPrompt}\n\n## Available Skills\n\nYou have access to the following skills. When a user's request matches a skill's description, you MUST call the \`load_skill\` tool to load that skill's full instructions, then follow them.\n\n${skillsText}\n\n${dateTimePrompt}`
44
- : `${basePrompt}\n\n${profilePrompt}\n\n${memoryPrompt}\n\n${dateTimePrompt}`;
42
+ const skillList = skillsText
43
+ ? `You have access to the following skills. When a user's request matches a skill's description, you MUST call the \`load_skill\` tool to load that skill's full instructions, then follow them.
44
+
45
+ ${skillsText}`
46
+ : 'There are no skills available.';
47
+ const skillPrompt = `## Skills
48
+
49
+ ${skillList}\n\nNote: If you want to add a new skill, it must be placed in the ${path.join(WORKSPACE_DIR, 'skills')} directory.`;
50
+ export const systemPrompt = [
51
+ basePrompt,
52
+ profilePrompt,
53
+ memoryPrompt,
54
+ skillPrompt,
55
+ dateTimePrompt,
56
+ ]
57
+ .filter(Boolean)
58
+ .join('\n\n');
@@ -1,6 +1,5 @@
1
1
  import { readdirSync, readFileSync, statSync } from 'fs';
2
2
  import { join } from 'path';
3
- import { homedir } from 'os';
4
3
  import { WORKSPACE_DIR } from './config.js';
5
4
  const skills = [];
6
5
  const skillContentMap = new Map();
@@ -29,9 +28,8 @@ export function discoverSkills() {
29
28
  skills.length = 0;
30
29
  skillContentMap.clear();
31
30
  const skillDirs = [
32
- join(homedir(), '.agents', 'skills'),
33
- join(WORKSPACE_DIR, '.agents', 'skills'),
34
31
  join(WORKSPACE_DIR, 'skills'),
32
+ join(process.cwd(), '.agents', 'skills'),
35
33
  ];
36
34
  for (const skillsDir of skillDirs) {
37
35
  let entries;
@@ -73,14 +71,13 @@ export function loadSkill(name) {
73
71
  return skillContentMap.get(name) ?? null;
74
72
  }
75
73
  export function getSkillsListText() {
76
- const lines = skills.map((s) => `- **${s.name}**: ${s.description}`);
77
- if (skills.length > 0) {
74
+ const lines = [];
75
+ for (const s of skills) {
76
+ lines.push(`**${s.name}**:`);
77
+ lines.push(` - description: ${s.description}`);
78
+ lines.push(` - dirPath: ${s.dirPath}`);
78
79
  lines.push('');
79
- lines.push('skills 的目录:');
80
- lines.push(join(homedir(), '.agents', 'skills'));
81
- lines.push(join(WORKSPACE_DIR, '.agents', 'skills'));
82
- lines.push(join(WORKSPACE_DIR, 'skills'));
83
- lines.push(`如果增加新 skill,必须放在 ${join(WORKSPACE_DIR, 'skills')} 目录下`);
84
80
  }
81
+ // console.log('skills...\n', lines.join('\n'))
85
82
  return lines.join('\n');
86
83
  }
@@ -1,7 +1,8 @@
1
1
  import { readFile } from 'fs/promises';
2
+ import { resolvePath } from '../utils.js';
2
3
  export async function readFileTool({ filepath }) {
3
4
  try {
4
- const content = await readFile(filepath, 'utf-8');
5
+ const content = await readFile(resolvePath(filepath), 'utf-8');
5
6
  return content;
6
7
  }
7
8
  catch (err) {
@@ -1,10 +1,12 @@
1
1
  import { writeFile, mkdir } from 'fs/promises';
2
2
  import { dirname } from 'path';
3
+ import { resolvePath } from '../utils.js';
3
4
  export async function writeFileTool({ filepath, content }) {
4
5
  try {
5
- await mkdir(dirname(filepath), { recursive: true });
6
- await writeFile(filepath, content, 'utf-8');
7
- return `File "${filepath}" written successfully.`;
6
+ const absolutePath = resolvePath(filepath);
7
+ await mkdir(dirname(absolutePath), { recursive: true });
8
+ await writeFile(absolutePath, content, 'utf-8');
9
+ return `File "${absolutePath}" written successfully.`;
8
10
  }
9
11
  catch (err) {
10
12
  return `Error: ${err.message}`;
@@ -1,2 +1,3 @@
1
+ export declare function resolvePath(filepath: string): string;
1
2
  export declare function formatRelativeTime(ts: string): string;
2
3
  export declare function truncate(str: string | null, maxLen: number): string;
@@ -1,3 +1,14 @@
1
+ import { resolve, join } from 'path';
2
+ import { homedir } from 'os';
3
+ export function resolvePath(filepath) {
4
+ if (filepath.startsWith('~/')) {
5
+ filepath = join(homedir(), filepath.slice(2));
6
+ }
7
+ else if (filepath === '~') {
8
+ filepath = homedir();
9
+ }
10
+ return resolve(filepath);
11
+ }
1
12
  export function formatRelativeTime(ts) {
2
13
  const diffMs = Date.now() - new Date(ts).getTime();
3
14
  const diffMin = Math.floor(diffMs / 60000);
package/dist/install.js CHANGED
@@ -4,7 +4,7 @@ import * as os from 'os';
4
4
  import { execSync } from 'child_process';
5
5
  import { WORKSPACE_DIR, CONFIG_PATH } from './agent/config.js';
6
6
  import { initDb } from './agent/db.js';
7
- const SKILLS_DIR = path.join(WORKSPACE_DIR, '.agents', 'skills');
7
+ const SKILLS_DIR = path.join(WORKSPACE_DIR, 'skills');
8
8
  function run(command, options) {
9
9
  execSync(command, { stdio: 'inherit', ...options });
10
10
  }
@@ -18,8 +18,8 @@ function installSkill(name, repo, skillPath, targetDir, tempDir) {
18
18
  const extractDir = path.join(tempDir, `${name}-extract`);
19
19
  fs.mkdirSync(extractDir, { recursive: true });
20
20
  try {
21
- run(`curl -fsSL -o "${tarPath}" "https://github.com/${repo}/archive/refs/heads/main.tar.gz"`, { timeout: 20000 });
22
- run(`tar -xzf "${tarPath}" -C "${extractDir}"`, { timeout: 20000 });
21
+ run(`curl -fsSL -o "${tarPath}" "https://github.com/${repo}/archive/refs/heads/main.tar.gz"`, { timeout: 30000 });
22
+ run(`tar -xzf "${tarPath}" -C "${extractDir}"`, { timeout: 30000 });
23
23
  const subDir = fs.readdirSync(extractDir)[0];
24
24
  fs.cpSync(path.join(extractDir, subDir, skillPath), targetDir, {
25
25
  recursive: true,
@@ -27,8 +27,8 @@ function installSkill(name, repo, skillPath, targetDir, tempDir) {
27
27
  console.log(` ✅ ${name} 安装完成`);
28
28
  }
29
29
  catch (error) {
30
- if (error.killed) {
31
- console.log(` ⏱️ ${name} 安装超时(超过 20s),已跳过,继续下一步`);
30
+ if (error.killed || error.code === 'ETIMEDOUT') {
31
+ console.log(` ⏱️ ${name} 安装超时(网络较慢),已跳过,继续下一步`);
32
32
  return;
33
33
  }
34
34
  throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zhitalk",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "A personal AI Agent like OpenClaw, including tools, skills, memory, hook, sub-agent, MCP server, etc. Run in the terminal.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,115 +0,0 @@
1
- {
2
- "macos": [
3
- "~/.ssh",
4
- "~/.gnupg",
5
- "~/.aws",
6
- "~/.azure",
7
- "~/.config/gcloud",
8
- "~/.kube",
9
- "~/.docker",
10
- "~/.netrc",
11
- "~/Library/Keychains",
12
- "~/Library/Application Support/Google/Chrome",
13
- "~/Library/Application Support/Chromium",
14
- "~/Library/Application Support/Firefox",
15
- "~/Library/Application Support/Slack",
16
- "~/Library/Application Support/Discord",
17
- "~/Library/Application Support/Telegram",
18
- "~/Library/Application Support/Signal",
19
- "~/.zshrc",
20
- "~/.bashrc",
21
- "~/.bash_profile",
22
- "~/.profile",
23
- "~/.env",
24
- "~/.env.*",
25
- "~/.gitconfig",
26
- "~/.git-credentials",
27
- "~/.npmrc",
28
- "~/.pnpmrc",
29
- "~/.yarnrc",
30
- "~/.pypirc",
31
- "~/.m2",
32
- "~/.gradle",
33
- "~/.terraform",
34
- "~/.config/gh",
35
- "~/.config/gitlab",
36
- "~/.circleci",
37
- "~/.vercel",
38
- "~/.netlify",
39
- "~/Library/Containers",
40
- "/private",
41
- "/etc",
42
- "/var"
43
- ],
44
- "linux": [
45
- "~/.ssh",
46
- "~/.gnupg",
47
- "~/.pki",
48
- "~/.aws",
49
- "~/.azure",
50
- "~/.config/gcloud",
51
- "~/.kube",
52
- "~/.docker",
53
- "~/.netrc",
54
- "~/.local/share/keyrings",
55
- "~/.config/google-chrome",
56
- "~/.config/chromium",
57
- "~/.mozilla/firefox",
58
- "~/.config/Slack",
59
- "~/.config/discord",
60
- "~/.local/share/TelegramDesktop",
61
- "~/.zshrc",
62
- "~/.bashrc",
63
- "~/.bash_profile",
64
- "~/.profile",
65
- "~/.env",
66
- "~/.env.*",
67
- "~/.gitconfig",
68
- "~/.git-credentials",
69
- "~/.npmrc",
70
- "~/.pnpmrc",
71
- "~/.yarnrc",
72
- "~/.pypirc",
73
- "~/.m2",
74
- "~/.gradle",
75
- "~/.terraform",
76
- "~/.config/gh",
77
- "~/.config/gitlab",
78
- "~/.circleci",
79
- "~/.vercel",
80
- "~/.netlify",
81
- "/etc/shadow",
82
- "/etc/sudoers",
83
- "/proc",
84
- "/sys",
85
- "/var/lib",
86
- "/root"
87
- ],
88
- "windows": [
89
- "%USERPROFILE%\\.ssh",
90
- "%USERPROFILE%\\.gnupg",
91
- "%USERPROFILE%\\.aws",
92
- "%USERPROFILE%\\.azure",
93
- "%USERPROFILE%\\.kube",
94
- "%USERPROFILE%\\.docker",
95
- "%LOCALAPPDATA%\\Google\\Chrome\\User Data",
96
- "%LOCALAPPDATA%\\Chromium\\User Data",
97
- "%APPDATA%\\Mozilla\\Firefox",
98
- "%APPDATA%\\Slack",
99
- "%APPDATA%\\Discord",
100
- "%APPDATA%\\Telegram Desktop",
101
- "%USERPROFILE%\\.gitconfig",
102
- "%USERPROFILE%\\.git-credentials",
103
- "%USERPROFILE%\\.npmrc",
104
- "%USERPROFILE%\\.pnpmrc",
105
- "%USERPROFILE%\\.yarnrc",
106
- "%USERPROFILE%\\.m2",
107
- "%USERPROFILE%\\.gradle",
108
- "%APPDATA%",
109
- "%LOCALAPPDATA%",
110
- "C:\\Windows\\System32",
111
- "C:\\Windows\\System32\\config\\SAM",
112
- "C:\\Windows\\System32\\config\\SECURITY",
113
- "C:\\Windows\\System32\\config\\SYSTEM"
114
- ]
115
- }