persona-core-opencode 1.0.0 → 1.0.2

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 ADDED
@@ -0,0 +1,604 @@
1
+ # persona-core-opencode
2
+
3
+ 使用 [opencode](https://github.com/opencode-ai/opencode) 作为执行引擎的 PersonaCore 实现。
4
+
5
+ PersonaCore 是一个 AI 人格/角色框架,允许你定义具有特定能力和行为风格的 AI 角色,并通过程序化的方式与其交互。
6
+
7
+ ## 特性
8
+
9
+ - **声明式人格定义**:通过 TypeScript 类型定义人格的系统提示和可用能力
10
+ - **动态工具生成**:根据 ActionNodeType 自动生成 opencode 兼容的工具文件
11
+ - **多种执行类型**:支持 API 调用、文件读写、HTTP 请求、LLM 调用等
12
+ - **会话管理**:自动管理会话状态,支持多轮对话
13
+ - **工具调用追踪**:记录所有工具调用历史
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ npm install persona-core-opencode
19
+ ```
20
+
21
+ ### 前置要求
22
+
23
+ - Node.js >= 18
24
+ - [opencode CLI](https://github.com/opencode-ai/opencode) 已安装并配置
25
+
26
+ ## 快速开始
27
+
28
+ ### 1. 创建客户端
29
+
30
+ ```typescript
31
+ import { PersonaCoreClient, CreatePersonaInput, ActionNodeType } from 'persona-core-opencode';
32
+
33
+ // 创建客户端实例
34
+ const client = await PersonaCoreClient.create({
35
+ workspaceRoot: './workspace', // 可选:工作空间根目录
36
+ opencodePath: 'opencode', // 可选:opencode 命令路径
37
+ defaultTimeoutMs: 60000, // 可选:默认超时时间
38
+ });
39
+ ```
40
+
41
+ ### 2. 定义并创建 Persona
42
+
43
+ ```typescript
44
+ // 定义动作类型
45
+ const actionTypes: ActionNodeType[] = [
46
+ {
47
+ kind: 'action',
48
+ id: 'save_note',
49
+ name: '保存笔记',
50
+ description: '将笔记内容保存到文件',
51
+ executionType: 'file_write',
52
+ inputFields: [
53
+ { name: 'filePath', required: true, typeHint: 'string', description: '文件路径' },
54
+ { name: 'content', required: true, typeHint: 'string', description: '笔记内容' },
55
+ ],
56
+ outputFields: [
57
+ { name: 'success', required: true, typeHint: 'boolean' },
58
+ { name: 'filePath', required: true, typeHint: 'string' },
59
+ ],
60
+ },
61
+ ];
62
+
63
+ // 创建 Persona
64
+ const personaId = await client.createPersona({
65
+ name: '笔记助手',
66
+ description: '帮助用户记录和整理笔记',
67
+ systemPrompt: `你是一个专业的笔记助手。
68
+ 你可以使用 save_note 工具来保存用户的笔记。
69
+ 始终使用友好、专业的语气与用户交流。`,
70
+ actionTypes,
71
+ });
72
+
73
+ console.log(`Persona created: ${personaId}`);
74
+ ```
75
+
76
+ ### 3. 发送消息
77
+
78
+ ```typescript
79
+ // 发送消息并获取响应
80
+ const result = await client.sendMessage(
81
+ personaId,
82
+ '请帮我记录今天的会议纪要:项目进度良好,下周发布新版本。'
83
+ );
84
+
85
+ console.log('Response:', result.response);
86
+ console.log('Tool calls:', result.toolCalls);
87
+
88
+ // 继续对话(使用相同的 session)
89
+ const followUp = await client.sendMessage(
90
+ personaId,
91
+ '再补充一点:需要准备发布文档。',
92
+ { sessionId: result.sessionId }
93
+ );
94
+ ```
95
+
96
+ ### 4. 清理资源
97
+
98
+ ```typescript
99
+ // 删除 Persona
100
+ await client.deletePersona(personaId);
101
+
102
+ // 或清理所有资源
103
+ await client.cleanup();
104
+ ```
105
+
106
+ ## 核心概念
107
+
108
+ ### Persona(人格)
109
+
110
+ Persona 是 PersonaCore 的核心概念,代表一个具有特定能力和行为风格的 AI 角色:
111
+
112
+ ```typescript
113
+ interface Persona {
114
+ id: PersonaId; // 唯一标识
115
+ name: string; // 名称
116
+ description?: string; // 描述
117
+ systemPrompt: string; // 系统提示词,定义角色的行为风格
118
+ actionTypes: ActionNodeType[]; // 可用的动作类型
119
+ createdAt: Date;
120
+ updatedAt: Date;
121
+ }
122
+ ```
123
+
124
+ ### ActionNodeType(动作节点类型)
125
+
126
+ 定义 Persona 可以执行的具体动作:
127
+
128
+ ```typescript
129
+ interface ActionNodeType {
130
+ kind: 'action';
131
+ id: ActionTypeId; // 动作唯一标识
132
+ name: string; // 语义名称
133
+ description?: string; // 描述
134
+ executionType: ActionExecutionType; // 执行类型
135
+ apiConfig?: ApiCallConfig; // API 调用配置
136
+ asyncMode?: AsyncModeConfig; // 异步模式配置
137
+ inputFields: FieldDefinition[]; // 输入字段
138
+ outputFields: FieldDefinition[]; // 输出字段
139
+ }
140
+ ```
141
+
142
+ ### 执行类型(ActionExecutionType)
143
+
144
+ 系统内置的执行类型:
145
+
146
+ | 类型 | 说明 | 用途 |
147
+ |-----|------|------|
148
+ | `api_call` | HTTP API 调用 | 调用外部 API 服务 |
149
+ | `file_write` | 文件写入 | 保存内容到文件 |
150
+ | `file_read` | 文件读取 | 读取文件内容 |
151
+ | `http_request` | 通用 HTTP 请求 | 发送 HTTP 请求 |
152
+ | `llm_call` | LLM 调用 | 调用语言模型处理 |
153
+ | `database_query` | 数据库查询 | 执行数据库操作 |
154
+ | `shell_command` | Shell 命令 | 执行系统命令 |
155
+
156
+ ## API 参考
157
+
158
+ ### PersonaCoreClient
159
+
160
+ 主要的客户端 API 类。
161
+
162
+ #### 构造函数
163
+
164
+ ```typescript
165
+ const client = new PersonaCoreClient(config?: PersonaCoreClientConfig);
166
+ // 或使用静态工厂方法
167
+ const client = await PersonaCoreClient.create(config?: PersonaCoreClientConfig);
168
+ ```
169
+
170
+ **配置选项:**
171
+
172
+ | 属性 | 类型 | 默认值 | 说明 |
173
+ |-----|------|-------|------|
174
+ | `workspaceRoot` | `string` | `process.cwd()` | 工作空间根目录 |
175
+ | `opencodePath` | `string` | `'opencode'` | opencode 命令路径 |
176
+ | `defaultTimeoutMs` | `number` | `30000` | 默认超时时间(毫秒) |
177
+
178
+ #### 方法
179
+
180
+ ##### `createPersona(input: CreatePersonaInput): Promise<string>`
181
+
182
+ 创建新的 Persona。
183
+
184
+ ```typescript
185
+ const personaId = await client.createPersona({
186
+ name: 'My Assistant',
187
+ systemPrompt: 'You are a helpful assistant.',
188
+ actionTypes: [...],
189
+ });
190
+ ```
191
+
192
+ ##### `deletePersona(personaId: string): Promise<void>`
193
+
194
+ 删除 Persona 及其关联的所有 session。
195
+
196
+ ```typescript
197
+ await client.deletePersona(personaId);
198
+ ```
199
+
200
+ ##### `getPersona(personaId: string): RegisteredPersona | undefined`
201
+
202
+ 获取已注册的 Persona。
203
+
204
+ ```typescript
205
+ const persona = client.getPersona(personaId);
206
+ ```
207
+
208
+ ##### `hasPersona(personaId: string): boolean`
209
+
210
+ 检查 Persona 是否存在。
211
+
212
+ ```typescript
213
+ if (client.hasPersona(personaId)) {
214
+ // Persona exists
215
+ }
216
+ ```
217
+
218
+ ##### `sendMessage(personaId: string, message: string, options?: SendMessageOptions): Promise<SessionExecutionResult>`
219
+
220
+ 向 Persona 发送消息并获取响应。
221
+
222
+ ```typescript
223
+ const result = await client.sendMessage(personaId, 'Hello!', {
224
+ sessionId: 'existing-session-id', // 可选:继续已有会话
225
+ timeoutMs: 60000, // 可选:超时时间
226
+ });
227
+ ```
228
+
229
+ **返回值 `SessionExecutionResult`:**
230
+
231
+ ```typescript
232
+ interface SessionExecutionResult {
233
+ sessionId: string; // 会话 ID
234
+ success: boolean; // 是否成功
235
+ response?: string; // 响应文本
236
+ toolCalls: ToolCallRecord[]; // 工具调用记录
237
+ error?: string; // 错误信息
238
+ }
239
+
240
+ interface ToolCallRecord {
241
+ tool: string; // 工具名称
242
+ args: Record<string, unknown>; // 调用参数
243
+ result?: string; // 调用结果
244
+ error?: string; // 错误信息
245
+ }
246
+ ```
247
+
248
+ ##### `cleanup(): Promise<void>`
249
+
250
+ 清理所有资源(关闭 session、删除工作空间)。
251
+
252
+ ```typescript
253
+ await client.cleanup();
254
+ ```
255
+
256
+ ## API 调用配置
257
+
258
+ 对于 `api_call` 类型的动作,可以通过 `apiConfig` 配置详细的 HTTP 请求:
259
+
260
+ ```typescript
261
+ const apiAction: ActionNodeType = {
262
+ kind: 'action',
263
+ id: 'search_web',
264
+ name: '网页搜索',
265
+ description: '搜索网页内容',
266
+ executionType: 'api_call',
267
+ apiConfig: {
268
+ url: 'https://api.example.com/search?q=${query}',
269
+ method: 'GET',
270
+ headers: {
271
+ type: 'static',
272
+ values: {
273
+ 'Authorization': 'Bearer ${API_KEY}',
274
+ 'Content-Type': 'application/json',
275
+ },
276
+ },
277
+ response: {
278
+ type: 'json',
279
+ extract: {
280
+ results: 'data.items',
281
+ total: 'meta.total',
282
+ },
283
+ },
284
+ timeoutMs: 30000,
285
+ },
286
+ inputFields: [
287
+ { name: 'query', required: true, typeHint: 'string', description: '搜索关键词' },
288
+ ],
289
+ outputFields: [
290
+ { name: 'results', required: true, typeHint: 'Array<{title: string, url: string}>' },
291
+ { name: 'total', required: true, typeHint: 'number' },
292
+ ],
293
+ };
294
+ ```
295
+
296
+ ### URL 模板
297
+
298
+ URL 支持变量替换:
299
+
300
+ ```typescript
301
+ url: 'https://api.example.com/users/${userId}/posts/${postId}'
302
+ ```
303
+
304
+ ### 请求头配置
305
+
306
+ **静态请求头:**
307
+
308
+ ```typescript
309
+ headers: {
310
+ type: 'static',
311
+ values: {
312
+ 'Authorization': 'Bearer token',
313
+ 'X-Custom-Header': 'value',
314
+ },
315
+ }
316
+ ```
317
+
318
+ **模板请求头(使用 Nunjucks):**
319
+
320
+ ```typescript
321
+ headers: {
322
+ type: 'template',
323
+ template: '{"Authorization": "Bearer {{ apiKey }}", "X-User": "{{ userId }}"}',
324
+ }
325
+ ```
326
+
327
+ ### 请求体配置
328
+
329
+ **JSON 请求体:**
330
+
331
+ ```typescript
332
+ body: {
333
+ contentType: 'json',
334
+ includeFields: ['title', 'content'], // 只包含指定字段
335
+ // 或
336
+ excludeFields: ['internal'], // 排除指定字段
337
+ }
338
+ ```
339
+
340
+ **模板请求体:**
341
+
342
+ ```typescript
343
+ body: {
344
+ contentType: 'template',
345
+ template: '{"query": "{{ query }}", "limit": {{ limit | default(10) }}}',
346
+ }
347
+ ```
348
+
349
+ ### 响应处理
350
+
351
+ **提取字段(使用 JSONPath):**
352
+
353
+ ```typescript
354
+ response: {
355
+ type: 'json',
356
+ extract: {
357
+ imageUrl: 'data.images.0.url',
358
+ status: 'meta.status',
359
+ },
360
+ }
361
+ ```
362
+
363
+ **保存到文件:**
364
+
365
+ ```typescript
366
+ response: {
367
+ type: 'json',
368
+ saveToFile: {
369
+ enabled: true,
370
+ pathField: 'outputPath', // 从输入参数获取路径
371
+ dataPath: 'data.image', // 从响应中提取数据
372
+ dataFormat: 'base64', // 数据格式
373
+ outputField: 'savedFilePath', // 输出字段名
374
+ },
375
+ }
376
+ ```
377
+
378
+ ## 异步任务支持
379
+
380
+ 对于需要轮询等待结果的异步 API,可以配置 `asyncMode`:
381
+
382
+ ```typescript
383
+ const asyncAction: ActionNodeType = {
384
+ kind: 'action',
385
+ id: 'generate_video',
386
+ name: '生成视频',
387
+ executionType: 'api_call',
388
+ apiConfig: {
389
+ url: 'https://api.example.com/video/generate',
390
+ method: 'POST',
391
+ body: { contentType: 'json' },
392
+ },
393
+ asyncMode: {
394
+ isAsync: true,
395
+ defaultCheckIntervalMs: 5000,
396
+ defaultTimeoutMs: 300000,
397
+ taskIdPath: 'data.taskId',
398
+ statusCheck: {
399
+ url: 'https://api.example.com/video/status/${taskId}',
400
+ method: 'GET',
401
+ extract: {
402
+ statusPath: 'status',
403
+ completedValues: ['completed', 'success'],
404
+ failedValues: ['failed', 'error'],
405
+ progressPath: 'progress',
406
+ },
407
+ },
408
+ resultFetch: {
409
+ extractFromStatus: {
410
+ videoUrl: 'data.videoUrl',
411
+ },
412
+ },
413
+ },
414
+ inputFields: [...],
415
+ outputFields: [...],
416
+ };
417
+ ```
418
+
419
+ ## 项目结构
420
+
421
+ ```
422
+ persona-core-opencode/
423
+ ├── src/
424
+ │ ├── index.ts # 主入口,导出公共 API
425
+ │ ├── client/
426
+ │ │ └── personaCoreClient.ts # 客户端 API
427
+ │ ├── persona/
428
+ │ │ └── personaRegistry.ts # Persona 注册和管理
429
+ │ ├── session/
430
+ │ │ ├── sessionManager.ts # 会话管理和执行
431
+ │ │ └── sessionHandle.ts # 会话句柄
432
+ │ ├── generators/
433
+ │ │ └── toolFileGenerator.ts # 工具文件生成
434
+ │ └── types/
435
+ │ ├── common.ts # 通用类型
436
+ │ ├── nodes.ts # 节点类型定义
437
+ │ └── persona.ts # Persona 类型定义
438
+ ├── suites/ # 示例 Persona 套件
439
+ │ ├── comic/ # 漫画生成 Persona
440
+ │ └── humor/ # 幽默安慰师 Persona
441
+ ├── tests/ # 测试文件
442
+ │ ├── unit/ # 单元测试
443
+ │ └── integration/ # 集成测试
444
+ ├── package.json
445
+ ├── tsconfig.json
446
+ └── vitest.config.ts
447
+ ```
448
+
449
+ ## 工作原理
450
+
451
+ ```
452
+ ┌─────────────────────────────────────────────────────────────────┐
453
+ │ PersonaCoreClient │
454
+ ├─────────────────────────────────────────────────────────────────┤
455
+ │ createPersona() → PersonaRegistry.registerPersona() │
456
+ │ │ │ │
457
+ │ │ ├── 创建工作空间目录 │
458
+ │ │ ├── 生成 .opencode/tools/*.ts │
459
+ │ │ └── 生成 .opencode/rules/persona.md │
460
+ │ │ │
461
+ │ sendMessage() → SessionManager.executeThinking() │
462
+ │ │ │ │
463
+ │ │ ├── 启动 opencode run CLI │
464
+ │ │ ├── 通过 stdin 发送消息 │
465
+ │ │ ├── 解析 stdout JSONL 输出 │
466
+ │ │ └── 返回响应和工具调用记录 │
467
+ └─────────────────────────────────────────────────────────────────┘
468
+ ```
469
+
470
+ 1. **Persona 注册**:当创建 Persona 时,系统会:
471
+ - 在工作空间中创建目录结构
472
+ - 根据 `actionTypes` 生成 opencode 工具文件(TypeScript)
473
+ - 将 `systemPrompt` 保存为 opencode 规则文件
474
+
475
+ 2. **消息执行**:当发送消息时,系统会:
476
+ - 创建或复用 session
477
+ - 启动 `opencode run` CLI 进程
478
+ - 通过 stdin 发送用户消息
479
+ - 解析 stdout 的 JSONL 输出
480
+ - 提取响应文本和工具调用记录
481
+
482
+ ## 环境变量
483
+
484
+ 参考 `.env.example` 配置所需的环境变量:
485
+
486
+ ```bash
487
+ # LLM Provider(根据实际使用的 API 配置)
488
+ DEEPSEEK_KEY=your-api-key
489
+ DEEPSEEK_MODEL=deepseek-v3
490
+ DEEPSEEK_URL=https://api.deepseek.com
491
+
492
+ # 外部服务 API
493
+ SERPER_KEY=your-serper-key # 网页搜索
494
+ JINA_KEY=your-jina-key # 网页内容获取
495
+
496
+ # 对象存储(如需要保存文件到 OSS)
497
+ OSS_ENDPOINT=http://localhost:9000
498
+ OSS_ACCESS_KEY_ID=minioadmin
499
+ OSS_SECRET_ACCESS_KEY=minioadmin
500
+ ```
501
+
502
+ ## 开发
503
+
504
+ ### 安装依赖
505
+
506
+ ```bash
507
+ npm install
508
+ ```
509
+
510
+ ### 构建
511
+
512
+ ```bash
513
+ npm run build
514
+ ```
515
+
516
+ ### 运行测试
517
+
518
+ ```bash
519
+ # 运行所有测试
520
+ npm run test
521
+
522
+ # 监听模式
523
+ npm run test:watch
524
+
525
+ # 类型检查
526
+ npm run typecheck
527
+ ```
528
+
529
+ ### 运行示例套件
530
+
531
+ ```bash
532
+ npm run suite
533
+ ```
534
+
535
+ ### 发布
536
+
537
+ ```bash
538
+ # 补丁版本
539
+ npm run release:patch
540
+
541
+ # 次要版本
542
+ npm run release:minor
543
+
544
+ # 主要版本
545
+ npm run release:major
546
+ ```
547
+
548
+ ## 示例:创建一个幽默安慰师
549
+
550
+ ```typescript
551
+ import { PersonaCoreClient, ActionNodeType } from 'persona-core-opencode';
552
+
553
+ const actionTypes: ActionNodeType[] = [
554
+ {
555
+ kind: 'action',
556
+ id: 'reply',
557
+ name: '回复',
558
+ description: '生成幽默的回复内容并保存到文件',
559
+ executionType: 'file_write',
560
+ inputFields: [
561
+ { name: 'filePath', required: true, typeHint: 'string', description: '保存路径' },
562
+ { name: 'content', required: true, typeHint: 'string', description: '回复内容' },
563
+ ],
564
+ outputFields: [
565
+ { name: 'success', required: true, typeHint: 'boolean' },
566
+ { name: 'filePath', required: true, typeHint: 'string' },
567
+ ],
568
+ },
569
+ ];
570
+
571
+ async function main() {
572
+ const client = await PersonaCoreClient.create();
573
+
574
+ const personaId = await client.createPersona({
575
+ name: '幽默安慰师',
576
+ description: '用幽默的方式安慰别人',
577
+ systemPrompt: `你是一个幽默的人,特别擅长用幽默的话安慰别人。
578
+
579
+ 你的特点:
580
+ 1. 总能用轻松幽默的方式化解对方的负面情绪
581
+ 2. 善于用比喻和类比来让人开心
582
+ 3. 会在安慰中加入恰到好处的调侃,但不会伤害对方
583
+
584
+ 当用户向你倾诉时,使用 reply 工具保存你的幽默回复。`,
585
+ actionTypes,
586
+ });
587
+
588
+ const result = await client.sendMessage(
589
+ personaId,
590
+ '今天代码写了一天都没跑通,太难受了...'
591
+ );
592
+
593
+ console.log('幽默安慰师的回复:', result.response);
594
+ console.log('保存的文件:', result.toolCalls);
595
+
596
+ await client.cleanup();
597
+ }
598
+
599
+ main().catch(console.error);
600
+ ```
601
+
602
+ ## 许可证
603
+
604
+ MIT License
@@ -34,7 +34,7 @@ export declare class PersonaRegistry {
34
34
  constructor(config?: PersonaRegistryConfig);
35
35
  /**
36
36
  * 注册 Persona
37
- * 1. 生成唯一 ID
37
+ * 1. 生成唯一 ID(如果 input.id 已提供,则直接使用)
38
38
  * 2. 创建工作目录
39
39
  * 3. 生成 tool 文件
40
40
  */
@@ -67,12 +67,10 @@ export declare class PersonaRegistry {
67
67
  * 获取工作空间根目录
68
68
  */
69
69
  getWorkspaceRoot(): string;
70
- /**
71
- * 生成唯一 ID
72
- */
73
- private generateId;
74
70
  /**
75
71
  * 生成 Persona 唯一 ID
72
+ * @param name Persona 名称
73
+ * @returns 生成的唯一 ID(格式:safeName-shortUuid)
76
74
  */
77
75
  private generatePersonaId;
78
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"personaRegistry.d.ts","sourceRoot":"","sources":["../../src/persona/personaRegistry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGlE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW;IACX,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,cAAc;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,kBAAkB,CAA6C;gBAE3D,MAAM,GAAE,qBAA0B;IAI9C;;;;;OAKG;IACG,eAAe,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC;IA6DjE;;OAEG;IACG,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAUhE;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAI5D;;OAEG;IACH,cAAc,IAAI,iBAAiB,EAAE;IAIrC;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC;;OAEG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiB5D;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAM9B;;OAEG;IACH,gBAAgB,IAAI,MAAM;IAI1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAKlB;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAK1B"}
1
+ {"version":3,"file":"personaRegistry.d.ts","sourceRoot":"","sources":["../../src/persona/personaRegistry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGlE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW;IACX,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,cAAc;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,kBAAkB,CAA6C;gBAE3D,MAAM,GAAE,qBAA0B;IAI9C;;;;;OAKG;IACG,eAAe,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC;IAgEjE;;OAEG;IACG,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAUhE;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAI5D;;OAEG;IACH,cAAc,IAAI,iBAAiB,EAAE;IAIrC;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC;;OAEG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiB5D;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAM9B;;OAEG;IACH,gBAAgB,IAAI,MAAM;IAI1B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;CAK1B"}
@@ -17,15 +17,19 @@ export class PersonaRegistry {
17
17
  }
18
18
  /**
19
19
  * 注册 Persona
20
- * 1. 生成唯一 ID
20
+ * 1. 生成唯一 ID(如果 input.id 已提供,则直接使用)
21
21
  * 2. 创建工作目录
22
22
  * 3. 生成 tool 文件
23
23
  */
24
24
  async registerPersona(input) {
25
+ // 如果提供了 id,直接使用;否则生成新的 id
26
+ const personaId = input.id
27
+ ? input.id
28
+ : this.generatePersonaId(input.name);
25
29
  // 创建 Persona 对象
26
30
  const now = new Date();
27
31
  const persona = {
28
- id: input.id || this.generateId(input.name),
32
+ id: personaId,
29
33
  name: input.name,
30
34
  description: input.description,
31
35
  systemPrompt: input.systemPrompt,
@@ -33,7 +37,6 @@ export class PersonaRegistry {
33
37
  createdAt: now,
34
38
  updatedAt: now,
35
39
  };
36
- const personaId = this.generatePersonaId(persona);
37
40
  const workspacePath = path.resolve(this.workspaceRoot, personaId);
38
41
  const toolsPath = path.join(workspacePath, '.opencode', 'tools');
39
42
  // 创建目录结构
@@ -132,19 +135,14 @@ ${persona.systemPrompt}
132
135
  getWorkspaceRoot() {
133
136
  return this.workspaceRoot;
134
137
  }
135
- /**
136
- * 生成唯一 ID
137
- */
138
- generateId(name) {
139
- const safeName = name.replace(/[^a-zA-Z0-9-]/g, '-').toLowerCase();
140
- return safeName;
141
- }
142
138
  /**
143
139
  * 生成 Persona 唯一 ID
140
+ * @param name Persona 名称
141
+ * @returns 生成的唯一 ID(格式:safeName-shortUuid)
144
142
  */
145
- generatePersonaId(persona) {
143
+ generatePersonaId(name) {
146
144
  const shortUuid = uuidv4().slice(0, 8);
147
- const safeName = persona.id.replace(/[^a-zA-Z0-9-]/g, '-');
145
+ const safeName = name.replace(/[^a-zA-Z0-9-]/g, '-').toLowerCase();
148
146
  return `${safeName}-${shortUuid}`;
149
147
  }
150
148
  }
@@ -1 +1 @@
1
- {"version":3,"file":"personaRegistry.js","sourceRoot":"","sources":["../../src/persona/personaRegistry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"personaRegistry.js","sourceRoot":"","sources":["../../src/persona/personaRegistry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "persona-core-opencode",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "PersonaCore implementation using opencode as the execution engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",