xiaozuoassistant 0.2.45 → 0.2.47

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.
@@ -15,9 +15,9 @@ export class BaseChannel {
15
15
  onMessage(handler) {
16
16
  this.messageHandler = handler;
17
17
  }
18
- emitMessage(sessionId, message) {
18
+ emitMessage(sessionId, message, botName) {
19
19
  if (this.messageHandler) {
20
- this.messageHandler(sessionId, message);
20
+ this.messageHandler(sessionId, message, botName);
21
21
  }
22
22
  }
23
23
  }
@@ -143,7 +143,7 @@ export class FeishuChannel extends BaseChannel {
143
143
  }
144
144
  return;
145
145
  }
146
- this.emitMessage(sessionId, content);
146
+ this.emitMessage(sessionId, content, botName);
147
147
  }
148
148
  catch (e) {
149
149
  console.error(`[Feishu-${botName}] Failed to parse message content:`, e);
@@ -20,9 +20,14 @@ export class AgentRuntime {
20
20
  return this.skills.map(skill => skill.toJSON());
21
21
  }
22
22
  async process(history, newMessage, contextPrompt, context) {
23
+ // 如果上下文中包含 botName,将其注入到系统提示词中
24
+ let identityContext = '';
25
+ if (context?.metadata?.botName) {
26
+ identityContext = `\n\n[System Identity]: Your name is "${context.metadata.botName}". You are acting as this bot. You must refer to yourself strictly as "${context.metadata.botName}" when introducing yourself or when asked about your identity. Do not use the generic name "xiaozuoAssistant" unless explicitly necessary.`;
27
+ }
23
28
  const finalSystemPrompt = contextPrompt
24
- ? `${this.systemPrompt}\n${contextPrompt}`
25
- : this.systemPrompt;
29
+ ? `${this.systemPrompt}${identityContext}\n${contextPrompt}`
30
+ : `${this.systemPrompt}${identityContext}`;
26
31
  const messages = [
27
32
  { role: 'system', content: finalSystemPrompt },
28
33
  ...history.map(m => ({ role: m.role, content: m.content, name: m.name, tool_call_id: m.tool_call_id, tool_calls: m.tool_calls })),
@@ -44,6 +44,11 @@ export class Brain {
44
44
  }
45
45
  }
46
46
  }
47
+ // 如果上下文中包含 botName,将其注入到系统提示词中
48
+ let identityContext = '';
49
+ if (context?.metadata?.botName) {
50
+ identityContext = `\n\n[System Identity]: Your name is "${context.metadata.botName}". You are acting as this bot. You must refer to yourself strictly as "${context.metadata.botName}" when introducing yourself or when asked about your identity. Do not use the generic name "xiaozuoAssistant" unless explicitly necessary.`;
51
+ }
47
52
  // Convert history messages to the format expected by OpenAI
48
53
  // Strategy: Keep last N messages to avoid context window overflow
49
54
  // TODO: A better strategy would be token-based truncation.
@@ -60,7 +65,7 @@ export class Brain {
60
65
  return msg;
61
66
  });
62
67
  const messages = [
63
- { role: 'system', content: defaultSystemPrompt + wakeupContext },
68
+ { role: 'system', content: defaultSystemPrompt + identityContext + wakeupContext },
64
69
  ...messageHistory,
65
70
  { role: 'user', content: newMessage }
66
71
  ];
@@ -18,7 +18,7 @@ export class TaskQueue {
18
18
  this.chains.set(key, next);
19
19
  }
20
20
  async process(task) {
21
- const { run, channel } = task;
21
+ const { run, channel, botName } = task;
22
22
  try {
23
23
  const session = await memory.getSession(run.sessionId);
24
24
  if (!session) {
@@ -56,7 +56,8 @@ ${context}`;
56
56
  metadata: {
57
57
  workspace: session.meta.workspace,
58
58
  alias: session.meta.alias,
59
- runId: run.id
59
+ runId: run.id,
60
+ botName: botName
60
61
  }
61
62
  };
62
63
  const responseContent = await brain.processMessage(session.messages, run.userContent, enhancedSystemPrompt, ctx);
@@ -376,13 +376,14 @@ app.post('/api/config', async (req, res) => {
376
376
  const feishuChannel = new FeishuChannel();
377
377
  channels.push(feishuChannel);
378
378
  await feishuChannel.start();
379
- feishuChannel.onMessage((sessionId, message) => {
379
+ feishuChannel.onMessage((sessionId, message, botName) => {
380
380
  eventBus.emitEvent({
381
381
  type: 'message',
382
382
  payload: { content: message },
383
383
  channel: feishuChannel.name,
384
384
  sessionId: sessionId,
385
- timestamp: Date.now()
385
+ timestamp: Date.now(),
386
+ botName: botName
386
387
  });
387
388
  });
388
389
  }
@@ -775,13 +776,14 @@ for (const ch of channels) {
775
776
  channels.forEach(channel => {
776
777
  channel.start();
777
778
  // 监听通道消息 -> 发送到 EventBus
778
- channel.onMessage((sessionId, message) => {
779
+ channel.onMessage((sessionId, message, botName) => {
779
780
  eventBus.emitEvent({
780
781
  type: 'message',
781
782
  payload: { content: message },
782
783
  channel: channel.name,
783
784
  sessionId: sessionId,
784
- timestamp: Date.now()
785
+ timestamp: Date.now(),
786
+ botName: botName
785
787
  });
786
788
  });
787
789
  });
@@ -837,16 +839,18 @@ eventBus.onEvent('message', async (event) => {
837
839
  payload: { runId: run.id },
838
840
  channel: channelName,
839
841
  sessionId,
840
- timestamp: Date.now()
842
+ timestamp: Date.now(),
843
+ botName: event.botName
841
844
  });
842
845
  eventBus.emitEvent({
843
846
  type: 'run_status',
844
847
  payload: { runId: run.id, status: 'queued' },
845
848
  channel: channelName,
846
849
  sessionId,
847
- timestamp: Date.now()
850
+ timestamp: Date.now(),
851
+ botName: event.botName
848
852
  });
849
- taskQueue.enqueue({ run, channel: channelName });
853
+ taskQueue.enqueue({ run, channel: channelName, botName: event.botName });
850
854
  }
851
855
  catch (error) {
852
856
  console.error('Error processing message:', error);
@@ -0,0 +1,13 @@
1
+ import { ReadLarkDocSkill } from './lark-doc.js';
2
+ const plugin = {
3
+ metadata: {
4
+ name: 'lark-skills',
5
+ version: '1.0.0',
6
+ description: 'Lark suite integration skills (Docs, Sheets, etc.)',
7
+ author: 'XiaoZuoClaw Team'
8
+ },
9
+ onLoad: (context) => {
10
+ context.registerSkill(new ReadLarkDocSkill());
11
+ }
12
+ };
13
+ export default plugin;
@@ -0,0 +1,89 @@
1
+ import { BaseSkill } from '../../../skills/base-skill.js';
2
+ import * as lark from '@larksuiteoapi/node-sdk';
3
+ export class ReadLarkDocSkill extends BaseSkill {
4
+ constructor() {
5
+ super(...arguments);
6
+ this.name = 'read_lark_doc';
7
+ this.description = 'Read text content from a Lark/Feishu document using its URL or token';
8
+ this.parameters = {
9
+ type: 'object',
10
+ properties: {
11
+ document_url: {
12
+ type: 'string',
13
+ description: 'The URL or token of the Lark/Feishu document to read'
14
+ },
15
+ app_id: {
16
+ type: 'string',
17
+ description: 'The Lark App ID to use for authentication (optional, defaults to the first configured Feishu bot)'
18
+ },
19
+ app_secret: {
20
+ type: 'string',
21
+ description: 'The Lark App Secret to use for authentication (optional, defaults to the first configured Feishu bot)'
22
+ }
23
+ },
24
+ required: ['document_url']
25
+ };
26
+ }
27
+ async execute(args, ctx) {
28
+ try {
29
+ // Extract document token from URL
30
+ // Typical Lark doc URL formats:
31
+ // https://domain.feishu.cn/docx/TOKEN
32
+ // https://domain.feishu.cn/wiki/WIKITOKEN
33
+ let documentToken = args.document_url;
34
+ // Basic extraction logic
35
+ if (args.document_url.includes('/docx/')) {
36
+ documentToken = args.document_url.split('/docx/')[1].split(/[?#]/)[0];
37
+ }
38
+ else if (args.document_url.includes('/doc/')) {
39
+ documentToken = args.document_url.split('/doc/')[1].split(/[?#]/)[0];
40
+ }
41
+ // Fetch config to get app_id and app_secret if not provided
42
+ const configLoader = require('../../../config/loader.js').config;
43
+ let appId = args.app_id;
44
+ let appSecret = args.app_secret;
45
+ if (!appId || !appSecret) {
46
+ const feishuBots = configLoader.channels?.feishu;
47
+ if (feishuBots && Array.isArray(feishuBots) && feishuBots.length > 0) {
48
+ // If context has botName, try to find that specific bot
49
+ if (ctx?.metadata?.botName) {
50
+ const specificBot = feishuBots.find((b) => b.name === ctx.metadata.botName);
51
+ if (specificBot) {
52
+ appId = specificBot.appId;
53
+ appSecret = specificBot.appSecret;
54
+ }
55
+ }
56
+ // Fallback to first bot
57
+ if (!appId || !appSecret) {
58
+ appId = feishuBots[0].appId;
59
+ appSecret = feishuBots[0].appSecret;
60
+ }
61
+ }
62
+ else {
63
+ return { error: 'Lark App ID and Secret are required to read documents. Please configure a Feishu bot or provide them in the arguments.' };
64
+ }
65
+ }
66
+ const client = new lark.Client({
67
+ appId: appId,
68
+ appSecret: appSecret,
69
+ });
70
+ // We use the drive/v1/export API or docx/v1/documents API to read content
71
+ // Let's try the newer DOCX API first to get raw content
72
+ const res = await client.docx.document.rawContent({
73
+ path: {
74
+ document_id: documentToken,
75
+ },
76
+ });
77
+ if (res.code !== 0) {
78
+ return { error: `Failed to read Lark document. Code: ${res.code}, Msg: ${res.msg}. Make sure the App has the "docs:doc:readonly" permission and has been added as a collaborator to the document.` };
79
+ }
80
+ return {
81
+ success: true,
82
+ content: res.data?.content || "No content returned."
83
+ };
84
+ }
85
+ catch (error) {
86
+ return { error: `Failed to read Lark document: ${error.message}. Ensure the token is correct and the App has appropriate permissions.` };
87
+ }
88
+ }
89
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xiaozuoassistant",
3
- "version": "0.2.45",
3
+ "version": "0.2.47",
4
4
  "description": "A local-first personal AI assistant with multi-channel support and enhanced memory.",
5
5
  "author": "mantle.lau",
6
6
  "license": "MIT",