@shareai-lab/kode-sdk 2.7.2 → 2.7.3

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 CHANGED
@@ -142,9 +142,12 @@ See [docs/en/guides/architecture.md](./docs/en/guides/architecture.md) for detai
142
142
  | **Guides** | |
143
143
  | [Events](./docs/en/guides/events.md) | Three-channel event system |
144
144
  | [Tools](./docs/en/guides/tools.md) | Built-in tools & custom tools |
145
+ | [Skills](./docs/en/guides/skills.md) | Skills system for reusable prompts |
145
146
  | [Providers](./docs/en/guides/providers.md) | Model provider configuration |
146
147
  | [Database](./docs/en/guides/database.md) | SQLite/PostgreSQL persistence |
147
148
  | [Resume & Fork](./docs/en/guides/resume-fork.md) | Crash recovery & branching |
149
+ | **Project** | |
150
+ | [Contribution Guide](./docs/en/contribution.md) | How to contribute |
148
151
  | **Reference** | |
149
152
  | [API Reference](./docs/en/reference/api.md) | Complete API documentation |
150
153
  | [Examples](./docs/en/examples/playbooks.md) | All examples explained |
package/README.zh-CN.md CHANGED
@@ -102,9 +102,12 @@ npm run example:room # 多Agent协作
102
102
  | **使用指南** | |
103
103
  | [事件系统](./docs/zh-CN/guides/events.md) | 三通道事件系统 |
104
104
  | [工具系统](./docs/zh-CN/guides/tools.md) | 内置工具与自定义工具 |
105
+ | [Skills 系统](./docs/zh-CN/guides/skills.md) | Skills 可复用提示词系统 |
105
106
  | [Provider 配置](./docs/zh-CN/guides/providers.md) | 模型 Provider 配置 |
106
107
  | [数据库存储](./docs/zh-CN/guides/database.md) | SQLite/PostgreSQL 持久化 |
107
108
  | [恢复与分叉](./docs/zh-CN/guides/resume-fork.md) | 崩溃恢复与分支 |
109
+ | **项目** | |
110
+ | [贡献指南](./docs/zh-CN/contribution.md) | 提交 PR 的要求与流程 |
108
111
  | **参考** | |
109
112
  | [API 参考](./docs/zh-CN/reference/api.md) | 完整 API 文档 |
110
113
  | [示例集](./docs/zh-CN/examples/playbooks.md) | 所有示例详解 |
@@ -9,6 +9,7 @@ import { SandboxFactory } from '../infra/sandbox-factory';
9
9
  import { ModelProvider, ModelConfig } from '../infra/provider';
10
10
  import { ToolRegistry, ToolInstance, ToolDescriptor } from '../tools/registry';
11
11
  import { ContextManagerOptions } from './context-manager';
12
+ import { ResumeError } from './errors';
12
13
  import { SendOptions as QueueSendOptions } from './agent/message-queue';
13
14
  export interface ModelFactory {
14
15
  (config: ModelConfig): ModelProvider;
@@ -180,6 +181,12 @@ export declare class Agent {
180
181
  strategy?: ResumeStrategy;
181
182
  overrides?: Partial<AgentConfig>;
182
183
  }): Promise<Agent>;
184
+ static resumeOrCreate(agentId: string, config: AgentConfig, deps: AgentDependencies, opts?: {
185
+ autoRun?: boolean;
186
+ strategy?: ResumeStrategy;
187
+ overrides?: Partial<AgentConfig>;
188
+ onCorrupted?: (agentId: string, error: ResumeError) => Promise<void> | void;
189
+ }): Promise<Agent>;
183
190
  private ensureProcessing;
184
191
  private runStep;
185
192
  private executeTools;
@@ -200,7 +200,7 @@ class Agent {
200
200
  : template.sandbox;
201
201
  const sandbox = typeof config.sandbox === 'object' && 'exec' in config.sandbox
202
202
  ? config.sandbox
203
- : deps.sandboxFactory.create(sandboxConfig || { kind: 'local', workDir: process.cwd() });
203
+ : await deps.sandboxFactory.createAsync(sandboxConfig || { kind: 'local', workDir: process.cwd() });
204
204
  const model = config.model
205
205
  ? config.model
206
206
  : config.modelConfig
@@ -541,7 +541,7 @@ class Agent {
541
541
  }
542
542
  let sandbox;
543
543
  try {
544
- sandbox = deps.sandboxFactory.create(metadata.sandboxConfig || { kind: 'local', workDir: process.cwd() });
544
+ sandbox = await deps.sandboxFactory.createAsync(metadata.sandboxConfig || { kind: 'local', workDir: process.cwd() });
545
545
  }
546
546
  catch (error) {
547
547
  throw new errors_1.ResumeError('SANDBOX_INIT_FAILED', error?.message || 'Failed to create sandbox');
@@ -653,6 +653,30 @@ class Agent {
653
653
  const overrides = opts?.overrides ?? {};
654
654
  return Agent.resume(agentId, { ...baseConfig, ...overrides }, deps, opts);
655
655
  }
656
+ static async resumeOrCreate(agentId, config, deps, opts) {
657
+ try {
658
+ return await Agent.resumeFromStore(agentId, deps, opts);
659
+ }
660
+ catch (error) {
661
+ if (!(error instanceof errors_1.ResumeError)) {
662
+ throw error;
663
+ }
664
+ switch (error.code) {
665
+ case 'AGENT_NOT_FOUND':
666
+ return Agent.create({ ...config, agentId }, deps);
667
+ case 'CORRUPTED_DATA': {
668
+ if (opts?.onCorrupted) {
669
+ await opts.onCorrupted(agentId, error);
670
+ }
671
+ const store = Agent.requireStore(deps);
672
+ await store.delete(agentId);
673
+ return Agent.create({ ...config, agentId }, deps);
674
+ }
675
+ default:
676
+ throw error;
677
+ }
678
+ }
679
+ }
656
680
  ensureProcessing() {
657
681
  // 检查是否超时
658
682
  if (this.processingPromise) {
@@ -1231,7 +1255,7 @@ class Agent {
1231
1255
  'application/pdf',
1232
1256
  ];
1233
1257
  for (const block of blocks) {
1234
- if (block.type === 'image' || block.type === 'audio' || block.type === 'file') {
1258
+ if (block.type === 'image' || block.type === 'audio' || block.type === 'video' || block.type === 'file') {
1235
1259
  const url = block.url;
1236
1260
  const fileId = block.file_id;
1237
1261
  const base64 = block.base64;
@@ -1277,12 +1301,81 @@ class Agent {
1277
1301
  }
1278
1302
  async resolveMultimodalBlocks(blocks) {
1279
1303
  const model = this.model;
1280
- if (typeof model.uploadFile !== 'function') {
1281
- return blocks;
1282
- }
1283
- const provider = this.model.toConfig().provider;
1304
+ const config = this.model.toConfig();
1305
+ const provider = config.provider;
1306
+ const multimodal = config.multimodal || {};
1307
+ // Check provider capabilities for audio/video
1308
+ const supportsAudio = provider === 'gemini' || provider === 'openai';
1309
+ const supportsVideo = provider === 'gemini';
1284
1310
  const resolved = [];
1285
1311
  for (const block of blocks) {
1312
+ // Handle audio block with callback fallback
1313
+ if (block.type === 'audio') {
1314
+ if (!supportsAudio && multimodal.audio?.customTranscriber) {
1315
+ try {
1316
+ const transcript = await multimodal.audio.customTranscriber({
1317
+ base64: block.base64,
1318
+ url: block.url,
1319
+ mimeType: block.mime_type,
1320
+ });
1321
+ resolved.push({ type: 'text', text: `[Audio Transcript]: ${transcript}` });
1322
+ continue;
1323
+ }
1324
+ catch (error) {
1325
+ this.events.emitMonitor({
1326
+ channel: 'monitor',
1327
+ type: 'error',
1328
+ severity: 'warn',
1329
+ phase: 'system',
1330
+ message: 'audio transcription failed, falling back to placeholder',
1331
+ detail: { error: error?.message || String(error) },
1332
+ });
1333
+ }
1334
+ }
1335
+ // If supported or no callback, pass through (provider will handle or degrade)
1336
+ resolved.push(block);
1337
+ continue;
1338
+ }
1339
+ // Handle video block with callback fallback
1340
+ if (block.type === 'video') {
1341
+ if (!supportsVideo && multimodal.video?.customFrameExtractor) {
1342
+ try {
1343
+ const frames = await multimodal.video.customFrameExtractor({
1344
+ base64: block.base64,
1345
+ url: block.url,
1346
+ mimeType: block.mime_type,
1347
+ });
1348
+ // Add all extracted frames as image blocks
1349
+ for (const frame of frames) {
1350
+ resolved.push({
1351
+ type: 'image',
1352
+ base64: frame.base64,
1353
+ mime_type: frame.mimeType,
1354
+ });
1355
+ }
1356
+ resolved.push({ type: 'text', text: `[Video: ${frames.length} frames extracted]` });
1357
+ continue;
1358
+ }
1359
+ catch (error) {
1360
+ this.events.emitMonitor({
1361
+ channel: 'monitor',
1362
+ type: 'error',
1363
+ severity: 'warn',
1364
+ phase: 'system',
1365
+ message: 'video frame extraction failed, falling back to placeholder',
1366
+ detail: { error: error?.message || String(error) },
1367
+ });
1368
+ }
1369
+ }
1370
+ // If supported or no callback, pass through (provider will handle or degrade)
1371
+ resolved.push(block);
1372
+ continue;
1373
+ }
1374
+ // Handle image and file uploads (existing logic)
1375
+ if (typeof model.uploadFile !== 'function') {
1376
+ resolved.push(block);
1377
+ continue;
1378
+ }
1286
1379
  if ((block.type === 'image' || block.type === 'file') &&
1287
1380
  block.base64 &&
1288
1381
  block.mime_type &&
@@ -1,120 +1,144 @@
1
1
  /**
2
- * 技能管理器模块(路径1 - 技能管理)
2
+ * 技能管理器模块(路径1 - 技能管理)
3
3
  *
4
4
  * 设计原则 (UNIX哲学):
5
- * - 简洁: 只负责技能文件系统的CRUD操作
6
- * - 模块化: 协调OperationQueue、SandboxFileManager进行文件系统操作
7
- * - 隔离: 与Agent运行时完全隔离,不参与Agent使用
5
+ * - 简洁: 只负责技能文件系统的管理操作
6
+ * - 模块化: 单一职责,易于测试和维护
7
+ * - 隔离: 与Agent运行时完全隔离,不参与Agent使用
8
8
  *
9
9
  * ⚠️ 重要说明:
10
- * - 此模块专门用于路径1(技能管理)
11
- * - 与路径2Agent运行时)完全独立
10
+ * - 此模块专门用于路径1(技能管理)
11
+ * - 与路径2(Agent运行时)完全独立
12
12
  * - 请勿与SkillsManager混淆
13
+ *
14
+ * @see docs/skills-management-implementation-plan.md
13
15
  */
14
- import { OperationTask } from './operation-queue';
15
- import type { SkillInfo, SkillDetail, SkillFileTree, CreateSkillOptions, ArchivedSkillInfo } from './types';
16
- import { SandboxFactory } from '../../infra/sandbox-factory';
16
+ import type { SkillInfo, ArchivedSkillInfo } from './types';
17
17
  /**
18
18
  * 技能管理器类
19
19
  *
20
20
  * 职责:
21
- * - 提供所有技能管理操作的统一接口(CRUD操作)
22
- * - 协调OperationQueue、SandboxFileManager进行文件系统操作
21
+ * - 提供所有技能管理操作的统一接口(导入、复制、重命名、归档、导出)
23
22
  * - 处理业务逻辑和权限验证
23
+ * - 所有操作严格遵循Specification.md规范
24
24
  * - ❌ 不参与Agent运行时
25
25
  * - ❌ 不提供技能加载、扫描等Agent使用的功能
26
26
  */
27
27
  export declare class SkillsManagementManager {
28
- private skillsManager;
29
- private operationQueue;
30
- private sandboxFileManager;
31
28
  private skillsDir;
32
29
  private archivedDir;
33
- constructor(skillsDir: string, sandboxFactory?: SandboxFactory, archivedDir?: string);
30
+ constructor(skillsDir: string, archivedDir?: string);
34
31
  /**
35
- * 获取所有在线技能列表(不包含archived技能)
32
+ * 1. 列出在线技能
33
+ * 扫描skills目录,排除.archived子目录
34
+ * 返回技能清单及其元数据信息
36
35
  */
37
36
  listSkills(): Promise<SkillInfo[]>;
38
37
  /**
39
- * 获取单个在线技能详细信息
40
- * @param skillName 技能名称
38
+ * 2. 安装新技能
39
+ * @param source 技能来源(名称/GitHub仓库/Git URL/本地路径)
40
+ * @param onProgress 可选的进度回调函数,用于实时传递安装日志
41
+ * 执行命令: npx -y ai-agent-skills install --agent project [source]
42
+ * 直接安装到.skills目录
41
43
  */
42
- getSkillInfo(skillName: string): Promise<SkillDetail | null>;
44
+ installSkill(source: string, onProgress?: (data: {
45
+ type: 'log' | 'error';
46
+ message: string;
47
+ }) => void): Promise<void>;
43
48
  /**
44
- * 获取已归档技能列表(只读,不支持修改)
49
+ * 3. 列出归档技能
50
+ * 扫描.archived目录
51
+ * 返回归档技能清单及其元数据信息
45
52
  */
46
53
  listArchivedSkills(): Promise<ArchivedSkillInfo[]>;
47
54
  /**
48
- * 创建新技能
55
+ * 4. 导入技能
56
+ * @param zipFilePath zip文件路径
57
+ * @param originalFileName 原始上传文件名(可选,用于无嵌套目录时的技能命名)
58
+ * 验证SKILL.md格式,解压并放置在在线技能目录中
59
+ *
60
+ * 检测逻辑:
61
+ * - 如果解压后根目录直接包含SKILL.md,视为无嵌套目录,使用originalFileName作为技能名称
62
+ * - 如果根目录不包含SKILL.md但包含多个子目录,每个子目录都有SKILL.md,则批量导入
63
+ */
64
+ importSkill(zipFilePath: string, originalFileName?: string): Promise<void>;
65
+ /**
66
+ * 5. 复制技能
49
67
  * @param skillName 技能名称
50
- * @param options 技能配置(名称、描述等)
68
+ * 新技能名称: {原技能名称}-{XXXXXXXX}
51
69
  */
52
- createSkill(skillName: string, options: CreateSkillOptions): Promise<SkillDetail>;
70
+ copySkill(skillName: string): Promise<string>;
53
71
  /**
54
- * 重命名技能
55
- * @param oldName 旧技能名称
56
- * @param newName 新技能名称
72
+ * 6. 重命名技能
73
+ * @param oldName 旧技能文件夹名称
74
+ * @param newName 新技能文件夹名称
75
+ * 不支持操作归档技能
57
76
  */
58
77
  renameSkill(oldName: string, newName: string): Promise<void>;
59
78
  /**
60
- * 编辑技能文件
79
+ * 7. 在线技能转归档
61
80
  * @param skillName 技能名称
62
- * @param filePath 文件路径(相对于技能根目录,如"SKILL.md")
63
- * @param content 文件内容
64
- * @param useSandbox 是否使用sandbox(默认true)
81
+ * 归档名称: {原技能名称}-{XXXXXXXX}
82
+ */
83
+ archiveSkill(skillName: string): Promise<void>;
84
+ /**
85
+ * 8. 归档技能转在线
86
+ * @param archivedSkillName archived中的技能名称(含后缀)
87
+ * 移入前检测重名
65
88
  */
66
- editSkillFile(skillName: string, filePath: string, content: string, useSandbox?: boolean): Promise<void>;
89
+ unarchiveSkill(archivedSkillName: string): Promise<void>;
67
90
  /**
68
- * 删除技能(移动到archived)
91
+ * 9. 查看在线技能内容
69
92
  * @param skillName 技能名称
93
+ * 返回SKILL.md完整内容(包含frontmatter和正文)
70
94
  */
71
- deleteSkill(skillName: string): Promise<void>;
95
+ getOnlineSkillContent(skillName: string): Promise<string>;
72
96
  /**
73
- * 恢复已删除的技能
74
- * @param archivedSkillName archived中的技能名称(含时间戳)
97
+ * 10. 查看归档技能内容
98
+ * @param archivedSkillName 归档技能名称(含8位后缀)
99
+ * 返回SKILL.md完整内容(包含frontmatter和正文)
75
100
  */
76
- restoreSkill(archivedSkillName: string): Promise<void>;
101
+ getArchivedSkillContent(archivedSkillName: string): Promise<string>;
77
102
  /**
78
- * 获取技能文件树(仅在线技能)
103
+ * 11. 查看在线技能文件目录结构
79
104
  * @param skillName 技能名称
105
+ * 返回JSON格式的目录树结构
80
106
  */
81
- getSkillFileTree(skillName: string): Promise<SkillFileTree>;
107
+ getOnlineSkillStructure(skillName: string): Promise<object>;
82
108
  /**
83
- * 获取队列状态
109
+ * 12. 查看归档技能文件目录结构
110
+ * @param archivedSkillName 归档技能名称(含8位后缀)
111
+ * 返回JSON格式的目录树结构
84
112
  */
85
- getQueueStatus(): {
86
- length: number;
87
- processing: boolean;
88
- tasks: OperationTask[];
89
- };
113
+ getArchivedSkillStructure(archivedSkillName: string): Promise<object>;
90
114
  /**
91
- * 执行创建技能
115
+ * 13. 导出技能
116
+ * @param skillName 技能名称(在线或归档)
117
+ * @param isArchived 是否为归档技能
118
+ * 使用系统zip命令打包,放入临时目录
92
119
  */
93
- private doCreateSkill;
120
+ exportSkill(skillName: string, isArchived: boolean): Promise<string>;
94
121
  /**
95
- * 执行重命名技能
122
+ * 递归构建目录树
96
123
  */
97
- private doRenameSkill;
124
+ private buildDirectoryTree;
98
125
  /**
99
- * 执行编辑技能文件
126
+ * 解析SKILL.md的YAML frontmatter
100
127
  */
101
- private doEditSkillFile;
128
+ private parseSkillMd;
102
129
  /**
103
- * 执行删除技能
130
+ * 验证SKILL.md格式(遵循Specification.md规范)
104
131
  */
105
- private doDeleteSkill;
132
+ private validateSkillMd;
106
133
  /**
107
- * 执行恢复技能
134
+ * 生成8位随机后缀
135
+ * 规则: uuidv4 → sha256 → 全小写 → 取前8位
108
136
  */
109
- private doRestoreSkill;
137
+ private generateRandomSuffix;
110
138
  /**
111
139
  * 验证技能名称
112
140
  */
113
141
  private isValidSkillName;
114
- /**
115
- * 生成SKILL.md内容
116
- */
117
- private generateSkillMd;
118
142
  /**
119
143
  * 检查文件是否存在
120
144
  */
@@ -124,7 +148,16 @@ export declare class SkillsManagementManager {
124
148
  */
125
149
  private safeGetFileStat;
126
150
  /**
127
- * 等待任务完成
151
+ * 解压zip文件
152
+ */
153
+ private extractZip;
154
+ /**
155
+ * 跨平台的安全重命名方法
156
+ * Windows上使用"复制-删除"方式避免EPERM错误
157
+ */
158
+ private safeRename;
159
+ /**
160
+ * 创建zip文件
128
161
  */
129
- private waitForTask;
162
+ private createZip;
130
163
  }