wtt-connect 0.2.59 → 0.2.61

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wtt-connect",
3
- "version": "0.2.59",
3
+ "version": "0.2.61",
4
4
  "private": false,
5
5
  "description": "WTT-native connector daemon for Codex, Claude Code, Cursor, Gemini, ACP, and other coding agent surfaces.",
6
6
  "type": "module",
package/src/runner.js CHANGED
@@ -77,6 +77,18 @@ export class Runner {
77
77
  return buildRuntimeInfo(this.config, this.store.getRuntime());
78
78
  }
79
79
 
80
+ async buildKnowledgeContext(metadata, query) {
81
+ const meta = parseMetadata(metadata);
82
+ if (!meta || meta.kb_mode !== true) return '';
83
+ const kbTaskId = String(meta.kb_task_id || meta.kbTaskId || '').trim();
84
+ if (!kbTaskId) return '';
85
+ const result = await this.api.searchKnowledgeContext(kbTaskId, query, { limit: 8 });
86
+ return renderKnowledgeContextBlock(result?.results || [], {
87
+ taskId: kbTaskId,
88
+ scope: meta.kb_scope || meta.kbScope || 'personal',
89
+ });
90
+ }
91
+
80
92
  async onEvent(msg) {
81
93
  if (msg.type === 'shell_run') {
82
94
  await this.handleShellRun(msg);
@@ -182,6 +194,11 @@ export class Runner {
182
194
  await this.wtt.typing(topicId, 'start', { statusText: `${adapterDisplayName(adapter.name)} 正在执行${modelConfig.model ? ` · ${modelConfig.model}` : ''}`, statusKind: 'running', adapter: adapter.name, model: modelConfig.model || undefined, ttlMs: 30000 });
183
195
  const agentProfile = await this.getAgentProfile();
184
196
  const agentSoul = renderAgentSoulContext(m.metadata, agentProfile?.role_template);
197
+ const knowledgeContext = await this.buildKnowledgeContext(m.metadata, [
198
+ content,
199
+ staged.promptBlock,
200
+ transcripts.map((item) => item.text).join('\n'),
201
+ ].filter(Boolean).join('\n\n'));
185
202
  const discussionRouting = renderDiscussionRoutingInstruction(m, this.config);
186
203
  const currentDisplayName = effectiveAgentDisplayName(this.config, agentProfile);
187
204
  const isSlashPassthrough = isAgentSlashCommand(m, content);
@@ -198,7 +215,7 @@ export class Runner {
198
215
  if (localSlash !== null) {
199
216
  const reply = stripHiddenContextLeak(localSlash || '(empty response)') || '(empty response)';
200
217
  await chatStream.finish(reply);
201
- await this.wtt.publish(topicId, reply, 'CHAT_REPLY', { stream_id: chatStream.id });
218
+ await this.wtt.publish(topicId, reply, 'CHAT_REPLY', chatStream.finalMetadata());
202
219
  log('info', 'slash command replied', { topicId, adapter: adapter.name, chars: reply.length });
203
220
  return;
204
221
  }
@@ -218,6 +235,8 @@ export class Runner {
218
235
  discussionRouting ? '' : null,
219
236
  agentSoul,
220
237
  agentSoul ? '' : null,
238
+ knowledgeContext,
239
+ knowledgeContext ? '' : null,
221
240
  renderCompactSummaryBlock(compactSummary),
222
241
  compactSummary ? '' : null,
223
242
  'User message:',
@@ -241,12 +260,12 @@ export class Runner {
241
260
  const reply = prepared.text || '(empty response)';
242
261
  await chatStream.finish(reply);
243
262
  await this.maybeAttachSpeech(topicId, reply, `chat-${topicId}`);
244
- await this.wtt.publish(topicId, reply, 'CHAT_REPLY', { stream_id: chatStream.id });
263
+ await this.wtt.publish(topicId, reply, 'CHAT_REPLY', chatStream.finalMetadata());
245
264
  await this.publishGeneratedFileArtifacts(topicId, prepared.refs, { source: 'chat', sourceId: topicId, adapter: adapter.name });
246
265
  log('info', 'chat replied', { topicId, chars: reply.length });
247
266
  } catch (err) {
248
267
  await chatStream?.fail(err?.message || String(err));
249
- await this.wtt.publish(topicId, `执行失败:${err.message}`, 'CHAT_REPLY', chatStream?.id ? { stream_id: chatStream.id } : null);
268
+ await this.wtt.publish(topicId, `执行失败:${err.message}`, 'CHAT_REPLY', chatStream?.finalMetadata?.() || null);
250
269
  log('error', 'chat failed', { topicId, error: err.message });
251
270
  } finally {
252
271
  if (runtimeSelection) this.recordRuntimeSelection(runtimeSelection.adapter, runtimeSelection.modelConfig, 'idle');
@@ -267,7 +286,25 @@ export class Runner {
267
286
  this.recordRuntimeSelection(adapter.name, modelConfig, 'running');
268
287
  const agentProfile = await this.getAgentProfile();
269
288
  if (topicId) await this.wtt.typing(topicId, 'start', { statusText: `${adapterDisplayName(adapter.name)} 正在执行任务${modelConfig.model ? ` · ${modelConfig.model}` : ''}`, statusKind: 'running', adapter: adapter.name, model: modelConfig.model || undefined, ttlMs: 30000 });
270
- const prompt = buildTaskPrompt(task, this.config, staged, transcripts, agentProfile);
289
+ const taskMetadata = task.metadata || task.msg_metadata || event?.message?.metadata || event?.metadata;
290
+ const taskQuery = [
291
+ task.title,
292
+ task.description,
293
+ task.content,
294
+ task.task_request,
295
+ event?.message?.content,
296
+ staged.promptBlock,
297
+ transcripts.map((item) => item.text).join('\n'),
298
+ ].filter(Boolean).join('\n\n');
299
+ const knowledgeContext = await this.buildKnowledgeContext(taskMetadata, taskQuery);
300
+ const prompt = buildTaskPrompt(
301
+ { ...task, metadata: taskMetadata || task.metadata },
302
+ this.config,
303
+ staged,
304
+ transcripts,
305
+ agentProfile,
306
+ knowledgeContext,
307
+ );
271
308
  try {
272
309
  const output = await this.runAdapter(adapter, prompt, {
273
310
  sessionKey: `wtt:task:${taskId}:${adapter.name}:${sessionModelKey(modelConfig)}`,
@@ -610,6 +647,14 @@ export class Runner {
610
647
 
611
648
  return {
612
649
  id: streamId,
650
+ finalMetadata() {
651
+ return {
652
+ stream_id: streamId,
653
+ streamId,
654
+ adapter: adapterName,
655
+ model: modelConfig.model || modelConfig.model_id || modelConfig.modelId || '',
656
+ };
657
+ },
613
658
  async start() {
614
659
  await send('start');
615
660
  },
@@ -1415,6 +1460,31 @@ function renderAgentSoulContext(metadata, profileRole = null) {
1415
1460
  ].join('\n');
1416
1461
  }
1417
1462
 
1463
+ function renderKnowledgeContextBlock(results = [], options = {}) {
1464
+ const rows = Array.isArray(results) ? results : [];
1465
+ if (!rows.length) {
1466
+ return [
1467
+ 'WTT personal knowledge base mode is enabled, but no indexed chunk matched this request.',
1468
+ 'If the user expects a recently uploaded file to be used, say the knowledge base may still be indexing and ask for the specific file or detail needed.',
1469
+ ].join('\n');
1470
+ }
1471
+ const lines = [
1472
+ 'WTT personal knowledge base context. Use these user-provided references when relevant, cite filenames naturally, and do not claim you read files that are not represented here.',
1473
+ `knowledge_scope: ${options.scope || 'personal'}`,
1474
+ `knowledge_task_id: ${options.taskId || 'unknown'}`,
1475
+ ];
1476
+ rows.slice(0, 8).forEach((row, index) => {
1477
+ const filename = String(row.filename || row.title || row.source_id || `source-${index + 1}`).slice(0, 180);
1478
+ const chunk = row.chunk_index !== undefined ? `#${row.chunk_index}` : `#${index + 1}`;
1479
+ const content = truncateForTranscript(String(row.content || row.snippet || ''), 2400);
1480
+ if (!content.trim()) return;
1481
+ lines.push('');
1482
+ lines.push(`[KB ${index + 1}] ${filename} ${chunk}`);
1483
+ lines.push(content);
1484
+ });
1485
+ return truncateForTranscript(lines.join('\n'), 18000);
1486
+ }
1487
+
1418
1488
  function stripHiddenContextLeak(text) {
1419
1489
  return String(text || '')
1420
1490
  .replace(/\[Agent Role Template\][\s\S]*?\[\/Agent Role Template\]\s*/gi, '')
@@ -1490,7 +1560,7 @@ function taskFromEvent(taskId, event) {
1490
1560
  return { id: taskId, title: m.title || `WTT task ${taskId}`, description: m.content || '', topic_id: m.topic_id };
1491
1561
  }
1492
1562
 
1493
- function buildTaskPrompt(task, config, staged = { promptBlock: '' }, transcripts = [], agentProfile = null) {
1563
+ function buildTaskPrompt(task, config, staged = { promptBlock: '' }, transcripts = [], agentProfile = null, knowledgeContext = '') {
1494
1564
  const title = task.title || task.name || `Task ${task.id}`;
1495
1565
  const description = task.description || task.content || task.task_request || '';
1496
1566
  const agentSoul = renderAgentSoulContext(task.metadata || task.msg_metadata, agentProfile?.role_template);
@@ -1501,6 +1571,8 @@ function buildTaskPrompt(task, config, staged = { promptBlock: '' }, transcripts
1501
1571
  '',
1502
1572
  agentSoul,
1503
1573
  agentSoul ? '' : null,
1574
+ knowledgeContext,
1575
+ knowledgeContext ? '' : null,
1504
1576
  'Description:',
1505
1577
  description,
1506
1578
  staged.promptBlock,
package/src/wtt-api.js CHANGED
@@ -92,6 +92,20 @@ export class WTTApi {
92
92
  }
93
93
  }
94
94
 
95
+ async searchKnowledgeContext(kbTaskId, query, { limit = 8 } = {}) {
96
+ if (!kbTaskId || !String(query || '').trim()) return { results: [] };
97
+ const params = new URLSearchParams({ kb_task_id: String(kbTaskId) });
98
+ try {
99
+ return await this.request('POST', `/kb/context/search?${params.toString()}`, {
100
+ headers: this.agentHeaders(),
101
+ json: { query: String(query || '').slice(0, 4000), limit },
102
+ });
103
+ } catch (err) {
104
+ log('warn', 'kb_context_search failed', { kbTaskId, error: err.message });
105
+ return { results: [] };
106
+ }
107
+ }
108
+
95
109
  async runTask(taskId, payload = {}) {
96
110
  return this.request('POST', `/tasks/${taskId}/run`, { json: payload });
97
111
  }