zhitalk 0.0.7 → 0.0.9

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.
@@ -113,6 +113,14 @@ function createAgentGraph(toolList) {
113
113
  modelMessages = modelMessages.slice(0, 300);
114
114
  // 修复因中断/截断导致的未完成 tool_calls
115
115
  modelMessages = ensureCompleteToolCalls(modelMessages);
116
+ // 清理异常消息:某些 API(如 GLM)可能在流式响应中产生 role 为 undefined 的 ChatMessage
117
+ modelMessages = modelMessages.filter((msg) => {
118
+ const msgType = msg._getType?.();
119
+ if (msgType === 'generic' && msg.role === undefined) {
120
+ return false;
121
+ }
122
+ return true;
123
+ });
116
124
  const messages = [new SystemMessage(systemPrompt), ...modelMessages];
117
125
  const response = await modelWithTheseTools.invoke(messages, config);
118
126
  return { messages: [response] };
package/dist/agent/cli.js CHANGED
@@ -7,6 +7,7 @@ import { getModelContextLimit } from './context.js';
7
7
  import { color, formatToolLog } from './colors.js';
8
8
  import { threadId, commands } from './commands.js';
9
9
  import { runSessionStartHooks } from './hooks.js';
10
+ import { getModelConfig } from './config.js';
10
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
11
12
  const pkg = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf-8'));
12
13
  function createInterface() {
@@ -22,6 +23,7 @@ async function printBanner() {
22
23
  const info = [
23
24
  `${color.gray('Description')}: ${pkg.description}`,
24
25
  `${color.gray('Version')}: ${pkg.version}`,
26
+ `${color.gray('Model')}: ${getModelConfig().model}`,
25
27
  `${color.gray('Author')}: ${pkg.author}`,
26
28
  `${color.gray('Docs')}: ${pkg.docs}`,
27
29
  ].join('\n');
@@ -90,7 +92,7 @@ async function chat(userInput) {
90
92
  const limit = getModelContextLimit();
91
93
  const percentage = (usageMetadata.total_tokens / limit) * 100;
92
94
  const percentageStr = percentage.toFixed(1);
93
- const tokenText = `Tokens: ${usageMetadata.total_tokens.toLocaleString()} / ${limit.toLocaleString()} (${percentageStr}%)`;
95
+ const tokenText = `${getModelConfig().model}, Tokens: ${usageMetadata.total_tokens.toLocaleString()} / ${limit.toLocaleString()} (${percentageStr}%)`;
94
96
  if (percentage >= 80) {
95
97
  process.stdout.write('\n' +
96
98
  color.error(tokenText) +
@@ -2,16 +2,20 @@ import { ChatOpenAI } from '@langchain/openai';
2
2
  import { getModelConfig } from './config.js';
3
3
  const modelConfig = getModelConfig();
4
4
  export function createModel(options) {
5
+ const modelKwargs = {};
6
+ if (modelConfig.model.startsWith('kimi')) {
7
+ modelKwargs.thinking = { type: 'disabled' };
8
+ }
9
+ // GLM 模型的流式 function calling 存在兼容性问题,禁用流式输出
10
+ const isGlm = modelConfig.model.toLowerCase().startsWith('glm');
5
11
  return new ChatOpenAI({
6
12
  model: modelConfig.model,
7
13
  apiKey: modelConfig.apiKey,
8
14
  configuration: {
9
15
  baseURL: modelConfig.baseURL,
10
16
  },
11
- streaming: options?.streaming ?? true,
12
- modelKwargs: {
13
- thinking: { type: 'disabled' },
14
- },
17
+ streaming: isGlm ? false : (options?.streaming ?? true),
18
+ modelKwargs,
15
19
  });
16
20
  }
17
21
  export async function checkModel() {
@@ -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
  }
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.7",
3
+ "version": "0.0.9",
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",