@yeaft/webchat-agent 0.1.486 → 0.1.487

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.
Files changed (40) hide show
  1. package/package.json +1 -1
  2. package/unify/cli.js +5 -28
  3. package/unify/engine.js +8 -20
  4. package/unify/eval/cases/tool-use.js +1 -22
  5. package/unify/skills.js +15 -9
  6. package/unify/tools/agent.js +0 -1
  7. package/unify/tools/apply-patch.js +0 -1
  8. package/unify/tools/ask-user.js +0 -1
  9. package/unify/tools/bash.js +0 -1
  10. package/unify/tools/close-agent.js +0 -1
  11. package/unify/tools/enter-worktree.js +0 -1
  12. package/unify/tools/exit-worktree.js +0 -1
  13. package/unify/tools/file-edit.js +0 -1
  14. package/unify/tools/file-read.js +0 -1
  15. package/unify/tools/file-write.js +0 -1
  16. package/unify/tools/glob.js +0 -1
  17. package/unify/tools/grep.js +0 -1
  18. package/unify/tools/history-search.js +0 -1
  19. package/unify/tools/image-generation.js +0 -1
  20. package/unify/tools/js-repl.js +0 -2
  21. package/unify/tools/list-agents.js +0 -1
  22. package/unify/tools/list-dir.js +0 -1
  23. package/unify/tools/mcp-tools.js +0 -2
  24. package/unify/tools/memory-query.js +0 -1
  25. package/unify/tools/memory-read.js +0 -1
  26. package/unify/tools/memory-search.js +0 -1
  27. package/unify/tools/memory-write.js +0 -1
  28. package/unify/tools/notebook-edit.js +0 -1
  29. package/unify/tools/request-permissions.js +0 -1
  30. package/unify/tools/send-message.js +0 -1
  31. package/unify/tools/skill.js +0 -1
  32. package/unify/tools/task-tools.js +0 -8
  33. package/unify/tools/thread-tools.js +0 -7
  34. package/unify/tools/tool-search.js +47 -56
  35. package/unify/tools/types.js +3 -6
  36. package/unify/tools/view-image.js +0 -1
  37. package/unify/tools/wait-agent.js +0 -1
  38. package/unify/tools/web-fetch.js +0 -1
  39. package/unify/tools/web-search.js +0 -1
  40. package/unify/tools/write-stdin.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.486",
3
+ "version": "0.1.487",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/unify/cli.js CHANGED
@@ -37,7 +37,6 @@ import { consolidate } from './memory/consolidate.js';
37
37
 
38
38
  function parseArgs(argv) {
39
39
  const args = {
40
- mode: 'chat',
41
40
  debug: false,
42
41
  interactive: false,
43
42
  verbose: false,
@@ -57,10 +56,6 @@ function parseArgs(argv) {
57
56
  while (i < rest.length) {
58
57
  const arg = rest[i];
59
58
  switch (arg) {
60
- case '-m':
61
- case '--mode':
62
- args.mode = rest[++i] || 'chat';
63
- break;
64
59
  case '-d':
65
60
  case '--debug':
66
61
  args.debug = true;
@@ -176,7 +171,7 @@ function handleTraceQuery(args, config) {
176
171
  // ─── Dry-run handler ───────────────────────────────────────────
177
172
 
178
173
  function handleDryRun(args, config) {
179
- const systemPrompt = buildSystemPrompt({ language: config.language, mode: args.mode });
174
+ const systemPrompt = buildSystemPrompt({ language: config.language });
180
175
  const messages = [];
181
176
 
182
177
  if (args.prompt) {
@@ -188,7 +183,6 @@ function handleDryRun(args, config) {
188
183
  console.log('--- Config ---');
189
184
  console.log(` Model: ${config.model}`);
190
185
  console.log(` Adapter: ${config.adapter || 'auto'}`);
191
- console.log(` Mode: ${args.mode}`);
192
186
  console.log(` Debug: ${config.debug}`);
193
187
  console.log();
194
188
  console.log('--- System Prompt ---');
@@ -218,7 +212,6 @@ async function runREPL(config, args) {
218
212
  });
219
213
 
220
214
  const { engine, conversationStore, memoryStore, trace, skillManager, mcpManager, toolRegistry } = session;
221
- let currentMode = args.mode;
222
215
 
223
216
  // Load persisted conversation as initial messages
224
217
  let conversationMessages = conversationStore.loadRecent(50).map(m => ({
@@ -232,7 +225,7 @@ async function runREPL(config, args) {
232
225
  const coldCount = conversationStore.countCold();
233
226
  const memStats = memoryStore.stats();
234
227
 
235
- console.log(`Yeaft Unify REPL (model: ${session.config.model}, mode: ${currentMode})`);
228
+ console.log(`Yeaft Unify REPL (model: ${session.config.model})`);
236
229
  console.log(`Conversation: ${hotCount} hot, ${coldCount} cold | Memory: ${memStats.entryCount} entries`);
237
230
  console.log(`Tools: ${session.status.tools} | Skills: ${session.status.skills}`);
238
231
  if (session.status.mcpServers.length > 0) {
@@ -247,7 +240,7 @@ async function runREPL(config, args) {
247
240
  const rl = createInterface({
248
241
  input: process.stdin,
249
242
  output: process.stdout,
250
- prompt: `yeaft:${currentMode}> `,
243
+ prompt: `yeaft> `,
251
244
  });
252
245
 
253
246
  rl.prompt();
@@ -265,7 +258,6 @@ async function runREPL(config, args) {
265
258
  switch (cmd) {
266
259
  case 'help':
267
260
  console.log('Commands:');
268
- console.log(' /mode <chat|work|dream> — Switch mode');
269
261
  console.log(' /debug — Toggle debug mode');
270
262
  console.log(' /trace <stats|recent> — Query debug trace');
271
263
  console.log(' /memory [add|clear|stats] — Memory management');
@@ -285,16 +277,6 @@ async function runREPL(config, args) {
285
277
  console.log(' /quit — Exit');
286
278
  break;
287
279
 
288
- case 'mode':
289
- if (cmdArgs[0]) {
290
- currentMode = cmdArgs[0];
291
- rl.setPrompt(`yeaft:${currentMode}> `);
292
- console.log(`Mode switched to: ${currentMode}`);
293
- } else {
294
- console.log(`Current mode: ${currentMode}`);
295
- }
296
- break;
297
-
298
280
  case 'debug':
299
281
  session.config.debug = !session.config.debug;
300
282
  console.log(`Debug mode: ${session.config.debug ? 'ON' : 'OFF'}`);
@@ -433,10 +415,9 @@ async function runREPL(config, args) {
433
415
  case 'context':
434
416
  console.log(`Context info:`);
435
417
  console.log(` Model: ${session.config.model}`);
436
- console.log(` Mode: ${currentMode}`);
437
418
  console.log(` Language: ${session.config.language}`);
438
419
  console.log(` Max context: ${session.config.maxContextTokens} tokens`);
439
- console.log(` System prompt: ${buildSystemPrompt({ language: session.config.language, mode: currentMode }).length} chars`);
420
+ console.log(` System prompt: ${buildSystemPrompt({ language: session.config.language }).length} chars`);
440
421
  console.log(` Hot messages: ${conversationStore.countHot()}`);
441
422
  console.log(` Hot tokens: ${conversationStore.hotTokens()}`);
442
423
  console.log(` Cold messages: ${conversationStore.countCold()}`);
@@ -446,13 +427,12 @@ async function runREPL(config, args) {
446
427
  break;
447
428
 
448
429
  case 'dry-run':
449
- handleDryRun({ ...args, mode: currentMode, prompt: cmdArgs.join(' ') || null }, session.config);
430
+ handleDryRun({ ...args, prompt: cmdArgs.join(' ') || null }, session.config);
450
431
  break;
451
432
 
452
433
  case 'stats': {
453
434
  const s = trace.stats();
454
435
  console.log(`Session stats:`);
455
- console.log(` Mode: ${currentMode}`);
456
436
  console.log(` Debug: ${session.config.debug}`);
457
437
  console.log(` Turns: ${s.turnCount}`);
458
438
  console.log(` Tools: ${s.toolCount}`);
@@ -607,7 +587,6 @@ async function runREPL(config, args) {
607
587
 
608
588
  for await (const event of engine.query({
609
589
  prompt: input,
610
- mode: currentMode,
611
590
  messages: conversationMessages,
612
591
  })) {
613
592
  switch (event.type) {
@@ -709,7 +688,6 @@ async function runOnce(config, args) {
709
688
 
710
689
  for await (const event of engine.query({
711
690
  prompt: args.prompt,
712
- mode: args.mode,
713
691
  messages: priorMessages,
714
692
  })) {
715
693
  switch (event.type) {
@@ -819,7 +797,6 @@ async function main() {
819
797
  console.log(' node cli.js --trace search "keyword" — Search traces');
820
798
  console.log();
821
799
  console.log('Options:');
822
- console.log(' -m, --mode <mode> Mode: chat, work, dream (default: chat)');
823
800
  console.log(' -d, --debug Enable debug tracing');
824
801
  console.log(' -i, --interactive Start REPL');
825
802
  console.log(' -v, --verbose Verbose output');
package/unify/engine.js CHANGED
@@ -167,18 +167,17 @@ export class Engine {
167
167
  /**
168
168
  * Build the system prompt with memory, compact summary, and skill content.
169
169
  *
170
- * @param {string} mode
171
170
  * @param {{ profile?: string, entries?: object[] }} [memory]
172
171
  * @param {string} [compactSummary]
173
172
  * @param {string} [prompt] — user prompt (for skill relevance matching)
174
173
  * @param {string} [memoryInjection] — task-287: prebuilt memory block (index + prefs + project)
175
174
  * @returns {string}
176
175
  */
177
- #buildSystemPrompt(mode, memory, compactSummary, prompt, memoryInjection) {
176
+ #buildSystemPrompt(memory, compactSummary, prompt, memoryInjection) {
178
177
  // Get relevant skill content if SkillManager is wired
179
178
  let skillContent = '';
180
179
  if (this.#skillManager && prompt) {
181
- skillContent = this.#skillManager.getRelevantPromptContent(prompt, mode);
180
+ skillContent = this.#skillManager.getRelevantPromptContent(prompt);
182
181
  }
183
182
 
184
183
  // Get tool names from the appropriate source
@@ -188,7 +187,6 @@ export class Engine {
188
187
 
189
188
  return buildSystemPrompt({
190
189
  language: this.#config.language || 'en',
191
- mode,
192
190
  toolNames,
193
191
  memory,
194
192
  memoryInjection,
@@ -201,10 +199,9 @@ export class Engine {
201
199
  * Build the full tool context for Phase 5 tools.
202
200
  *
203
201
  * @param {AbortSignal} [signal]
204
- * @param {string} [mode]
205
202
  * @returns {object}
206
203
  */
207
- #buildToolContext(signal, mode) {
204
+ #buildToolContext(signal) {
208
205
  return {
209
206
  signal,
210
207
  yeaftDir: this.#yeaftDir,
@@ -215,7 +212,6 @@ export class Engine {
215
212
  conversationStore: this.#conversationStore,
216
213
  adapter: this.#adapter,
217
214
  config: this.#config,
218
- mode,
219
215
  };
220
216
  }
221
217
 
@@ -265,10 +261,9 @@ export class Engine {
265
261
  *
266
262
  * @param {string} userContent
267
263
  * @param {string} assistantContent
268
- * @param {string} mode
269
264
  * @param {object[]} [toolCalls]
270
265
  */
271
- #persistMessages(userContent, assistantContent, mode, toolCalls) {
266
+ #persistMessages(userContent, assistantContent, toolCalls) {
272
267
  if (!this.#conversationStore) return;
273
268
  if (this.#config._readOnly) return;
274
269
 
@@ -288,7 +283,6 @@ export class Engine {
288
283
  this.#conversationStore.append({
289
284
  role: 'user',
290
285
  content: userContent,
291
- mode,
292
286
  threadId,
293
287
  });
294
288
 
@@ -296,7 +290,6 @@ export class Engine {
296
290
  const assistantMsg = {
297
291
  role: 'assistant',
298
292
  content: assistantContent,
299
- mode,
300
293
  model: this.#config.model,
301
294
  threadId,
302
295
  };
@@ -354,14 +347,11 @@ export class Engine {
354
347
  *
355
348
  * @param {object} params
356
349
  * @param {string} params.prompt - The user prompt (required, non-empty).
357
- * @param {'dream'} [params.mode] - Optional mode flag. Since task-297 the only
358
- * value accepted / acted on is `'dream'` (memory maintenance system prompt).
359
- * Any other value is ignored and falls through to the unified system prompt.
360
350
  * @param {Array} [params.messages] - Prior conversation messages.
361
351
  * @param {AbortSignal} [params.signal] - Abort signal.
362
352
  * @yields {EngineEvent}
363
353
  */
364
- async *query({ prompt, mode, messages = [], signal }) {
354
+ async *query({ prompt, messages = [], signal }) {
365
355
  if (!prompt || typeof prompt !== 'string' || !prompt.trim()) {
366
356
  yield {
367
357
  type: 'error',
@@ -394,7 +384,7 @@ export class Engine {
394
384
  }
395
385
 
396
386
  const compactSummary = this.#getCompactSummary();
397
- const systemPrompt = this.#buildSystemPrompt(mode, undefined, compactSummary, prompt, memoryInjection);
387
+ const systemPrompt = this.#buildSystemPrompt(undefined, compactSummary, prompt, memoryInjection);
398
388
 
399
389
  // Build conversation: existing messages + new user message
400
390
  const conversationMessages = [
@@ -423,7 +413,6 @@ export class Engine {
423
413
 
424
414
  const turnId = this.#trace.startTurn({
425
415
  traceId: this.#traceId,
426
- mode,
427
416
  turnNumber,
428
417
  });
429
418
 
@@ -590,7 +579,6 @@ export class Engine {
590
579
  // but receives both configs — messages are persisted with primary model name
591
580
  const hookResult = await runStopHooks({
592
581
  yeaftDir: this.#yeaftDir,
593
- mode,
594
582
  conversationStore: this.#conversationStore,
595
583
  memoryStore: this.#memoryStore,
596
584
  adapter: this.#adapter,
@@ -608,7 +596,7 @@ export class Engine {
608
596
  }
609
597
  } else {
610
598
  // Legacy path (no yeaftDir → use old behavior)
611
- this.#persistMessages(prompt, fullResponseText, mode, assistantMsg.toolCalls);
599
+ this.#persistMessages(prompt, fullResponseText, assistantMsg.toolCalls);
612
600
 
613
601
  const consolidated = await this.#maybeConsolidate();
614
602
  if (consolidated && consolidated.archivedCount > 0) {
@@ -620,7 +608,7 @@ export class Engine {
620
608
  }
621
609
 
622
610
  // Execute tool calls and feed results back
623
- const toolCtx = this.#buildToolContext(signal, mode);
611
+ const toolCtx = this.#buildToolContext(signal);
624
612
 
625
613
  for (const tc of toolCalls) {
626
614
  const toolStartTime = Date.now();
@@ -242,7 +242,6 @@ export const toolUseCases = [
242
242
  suite: 'tools',
243
243
  description: 'Model should read a file then modify it (sequential tools)',
244
244
  prompt: 'Read src/index.js and add a health check endpoint at /health',
245
- mode: 'work',
246
245
  registryTools: allTools,
247
246
  criteria: [
248
247
  noError,
@@ -268,9 +267,8 @@ export const toolUseCases = [
268
267
  {
269
268
  id: 'tool-multi-bash-workflow',
270
269
  suite: 'tools',
271
- description: 'Model should run git status and npm test in work mode',
270
+ description: 'Model should run git status and npm test',
272
271
  prompt: 'Check the git status and run the tests',
273
- mode: 'work',
274
272
  registryTools: allTools,
275
273
  criteria: [
276
274
  noError,
@@ -290,25 +288,6 @@ export const toolUseCases = [
290
288
  ],
291
289
  },
292
290
 
293
- // ─── Mode Awareness ───────────────────────────────────
294
-
295
- {
296
- id: 'tool-mode-chat-no-write',
297
- suite: 'tools',
298
- description: 'In chat mode, write_file should not be available (work-only tool)',
299
- prompt: 'Write "hello" to a file called greeting.txt',
300
- mode: 'chat',
301
- registryTools: allTools,
302
- criteria: [
303
- noError,
304
- toolNotCalled('write_file', {
305
- weight: 10,
306
- id: 'no-write-in-chat',
307
- description: 'write_file is work-only and should not be called in chat mode',
308
- }),
309
- ],
310
- },
311
-
312
291
  // ─── Error Handling ───────────────────────────────────
313
292
 
314
293
  {
package/unify/skills.js CHANGED
@@ -381,17 +381,21 @@ export class SkillManager {
381
381
  }
382
382
 
383
383
  /**
384
- * List all skills (metadata only — no content), optionally filtered by mode.
384
+ * List all skills (metadata only — no content).
385
385
  * This is the "progressive disclosure" list tier.
386
386
  *
387
- * @param {string} [mode] 'chat' | 'work' | undefined (all)
387
+ * task-311: the legacy `mode` parameter (chat/work filter) is accepted but
388
+ * ignored — Unify no longer has mode distinction, so every skill is treated
389
+ * as universally applicable. The `mode` field on each record is still
390
+ * surfaced for historic YAML compatibility.
391
+ *
392
+ * @param {string} [_mode] — deprecated, ignored
388
393
  * @returns {Array<{ name: string, description: string, trigger: string, mode: string, category?: string, platforms?: string[], keywords?: string[], source: string, hasReferences: boolean, hasTemplates: boolean }>}
389
394
  */
390
- list(mode) {
395
+ list(_mode) {
391
396
  const skills = [...this.#skills.values()];
392
- const filtered = mode ? skills.filter(s => s.mode === 'both' || s.mode === mode) : skills;
393
397
 
394
- return filtered.map(s => ({
398
+ return skills.map(s => ({
395
399
  name: s.name,
396
400
  description: s.description || '',
397
401
  trigger: s.trigger || '',
@@ -447,20 +451,22 @@ export class SkillManager {
447
451
  * Find skills relevant to a prompt.
448
452
  * Enhanced matching: regex triggers, keyword lists, name/description match.
449
453
  *
454
+ * task-311: the `mode` parameter is accepted but ignored (all skills are
455
+ * considered universally applicable since mode distinction was removed).
456
+ *
450
457
  * @param {string} prompt — user's prompt
451
- * @param {string} [mode] — filter by mode
458
+ * @param {string} [_mode] — deprecated, ignored
452
459
  * @returns {Skill[]}
453
460
  */
454
- findRelevant(prompt, mode) {
461
+ findRelevant(prompt, _mode) {
455
462
  if (!prompt) return [];
456
463
 
457
464
  const lowerPrompt = prompt.toLowerCase();
458
465
  const cleanPrompt = lowerPrompt.replace(/[^\w\s]/g, '');
459
466
  const promptWords = cleanPrompt.split(/\s+/).filter(w => w.length > 2);
460
467
  const allSkills = [...this.#skills.values()];
461
- const filtered = mode ? allSkills.filter(s => s.mode === 'both' || s.mode === mode) : allSkills;
462
468
 
463
- return filtered.filter(skill => {
469
+ return allSkills.filter(skill => {
464
470
  // 1. Regex or keyword trigger match
465
471
  if (skill.trigger && matchTrigger(skill.trigger, lowerPrompt, promptWords)) {
466
472
  return true;
@@ -229,7 +229,6 @@ Guidelines:
229
229
  },
230
230
  required: ['name'],
231
231
  },
232
- modes: ['work'],
233
232
  isConcurrencySafe: () => false,
234
233
  isReadOnly: () => false,
235
234
  async execute(input, ctx) {
@@ -115,7 +115,6 @@ Guidelines:
115
115
  },
116
116
  required: ['patch'],
117
117
  },
118
- modes: ['chat', 'work'],
119
118
  isConcurrencySafe: () => false,
120
119
  isReadOnly: () => false,
121
120
  isDestructive: () => false,
@@ -38,7 +38,6 @@ Guidelines:
38
38
  },
39
39
  required: ['question'],
40
40
  },
41
- modes: ['chat', 'work'],
42
41
  isConcurrencySafe: () => false,
43
42
  isReadOnly: () => true,
44
43
  async execute(input, ctx) {
@@ -137,7 +137,6 @@ Guidelines:
137
137
  },
138
138
  required: ['command'],
139
139
  },
140
- modes: ['chat', 'work'],
141
140
  isConcurrencySafe: () => false,
142
141
  isReadOnly: () => false,
143
142
  isDestructive: (input) => {
@@ -25,7 +25,6 @@ The agent's result (if any) is returned before closing.`,
25
25
  },
26
26
  required: ['agent_id'],
27
27
  },
28
- modes: ['work'],
29
28
  isConcurrencySafe: () => false,
30
29
  isReadOnly: () => false,
31
30
  async execute(input, ctx) {
@@ -39,7 +39,6 @@ Returns the worktree path and branch name.`,
39
39
  },
40
40
  },
41
41
  },
42
- modes: ['chat', 'work'],
43
42
  isDestructive: () => false,
44
43
  async execute(input, ctx) {
45
44
  const cwd = ctx?.cwd || process.cwd();
@@ -41,7 +41,6 @@ unless discard_changes is set to true.`,
41
41
  },
42
42
  required: ['path', 'action'],
43
43
  },
44
- modes: ['chat', 'work'],
45
44
  isDestructive: (input) => input?.action === 'remove',
46
45
  async execute(input, ctx) {
47
46
  const worktreePath = resolve(input.path);
@@ -46,7 +46,6 @@ Guidelines:
46
46
  },
47
47
  required: ['file_path', 'old_string', 'new_string'],
48
48
  },
49
- modes: ['chat', 'work'],
50
49
  isConcurrencySafe: () => false,
51
50
  isReadOnly: () => false,
52
51
  isDestructive: () => false,
@@ -60,7 +60,6 @@ Guidelines:
60
60
  },
61
61
  required: ['file_path'],
62
62
  },
63
- modes: ['chat', 'work'],
64
63
  isConcurrencySafe: () => true,
65
64
  isReadOnly: () => true,
66
65
  async execute(input, ctx) {
@@ -34,7 +34,6 @@ Guidelines:
34
34
  },
35
35
  required: ['file_path', 'content'],
36
36
  },
37
- modes: ['chat', 'work'],
38
37
  isConcurrencySafe: () => false,
39
38
  isReadOnly: () => false,
40
39
  isDestructive: () => false,
@@ -95,7 +95,6 @@ Guidelines:
95
95
  },
96
96
  required: ['pattern'],
97
97
  },
98
- modes: ['chat', 'work'],
99
98
  isConcurrencySafe: () => true,
100
99
  isReadOnly: () => true,
101
100
  async execute(input, ctx) {
@@ -207,7 +207,6 @@ Guidelines:
207
207
  },
208
208
  required: ['pattern'],
209
209
  },
210
- modes: ['chat', 'work'],
211
210
  isConcurrencySafe: () => true,
212
211
  isReadOnly: () => true,
213
212
  async execute(input, ctx) {
@@ -30,7 +30,6 @@ Results are returned newest-first with message role and content.`,
30
30
  },
31
31
  required: ['keyword'],
32
32
  },
33
- modes: ['chat', 'work'],
34
33
  isConcurrencySafe: () => true,
35
34
  isReadOnly: () => true,
36
35
  async execute(input, ctx) {
@@ -36,7 +36,6 @@ Guidelines:
36
36
  },
37
37
  required: ['prompt'],
38
38
  },
39
- modes: ['chat', 'work'],
40
39
  isConcurrencySafe: () => true,
41
40
  isReadOnly: () => false,
42
41
  async execute(input, ctx) {
@@ -69,7 +69,6 @@ Guidelines:
69
69
  },
70
70
  required: ['code'],
71
71
  },
72
- modes: ['chat', 'work'],
73
72
  isConcurrencySafe: () => false,
74
73
  isReadOnly: () => true,
75
74
  async execute(input, ctx) {
@@ -112,7 +111,6 @@ Use when you want a clean slate.`,
112
111
  type: 'object',
113
112
  properties: {},
114
113
  },
115
- modes: ['chat', 'work'],
116
114
  isConcurrencySafe: () => false,
117
115
  isReadOnly: () => false,
118
116
  async execute(input, ctx) {
@@ -20,7 +20,6 @@ and message counts. Use to monitor parallel task progress.`,
20
20
  },
21
21
  },
22
22
  },
23
- modes: ['work'],
24
23
  isConcurrencySafe: () => true,
25
24
  isReadOnly: () => true,
26
25
  async execute(input, ctx) {
@@ -37,7 +37,6 @@ This is better than using Bash with 'ls' because it provides structured output.`
37
37
  },
38
38
  },
39
39
  },
40
- modes: ['chat', 'work'],
41
40
  isConcurrencySafe: () => true,
42
41
  isReadOnly: () => true,
43
42
  async execute(input, ctx) {
@@ -31,7 +31,6 @@ Usage guidelines:
31
31
  },
32
32
  },
33
33
  },
34
- modes: ['chat', 'work'],
35
34
  isConcurrencySafe: () => true,
36
35
  isReadOnly: () => true,
37
36
  async execute(input, ctx) {
@@ -88,7 +87,6 @@ Usage guidelines:
88
87
  },
89
88
  required: ['tool_name'],
90
89
  },
91
- modes: ['chat', 'work'],
92
90
  async execute(input, ctx) {
93
91
  const mcpManager = ctx?.mcpManager;
94
92
 
@@ -57,7 +57,6 @@ results sorted by score descending.`,
57
57
  },
58
58
  required: ['keywords'],
59
59
  },
60
- modes: ['chat', 'work'],
61
60
  isConcurrencySafe: () => true,
62
61
  isReadOnly: () => true,
63
62
  async execute(input, ctx) {
@@ -32,7 +32,6 @@ Actions:
32
32
  },
33
33
  required: ['action'],
34
34
  },
35
- modes: ['chat', 'work'],
36
35
  isConcurrencySafe: () => true,
37
36
  isReadOnly: () => true,
38
37
  async execute(input, ctx) {
@@ -48,7 +48,6 @@ Up to ${MAX_FILES_PER_CALL} files per call. Each file is capped at ${MAX_BYTES_P
48
48
  },
49
49
  required: ['paths'],
50
50
  },
51
- modes: ['chat', 'work'],
52
51
  isConcurrencySafe: () => true,
53
52
  isReadOnly: () => true,
54
53
  async execute(input, ctx) {
@@ -51,7 +51,6 @@ Importance levels: low, normal, high, critical`,
51
51
  },
52
52
  required: ['action'],
53
53
  },
54
- modes: ['chat', 'work'],
55
54
  isConcurrencySafe: () => false,
56
55
  isReadOnly: () => false,
57
56
  async execute(input, ctx) {
@@ -48,7 +48,6 @@ Cell types: "code" or "markdown"`,
48
48
  },
49
49
  required: ['notebook_path'],
50
50
  },
51
- modes: ['chat', 'work'],
52
51
  isConcurrencySafe: () => false,
53
52
  isReadOnly: (input) => input?.action === 'read',
54
53
  async execute(input, ctx) {
@@ -37,7 +37,6 @@ The user must explicitly approve before you proceed.`,
37
37
  },
38
38
  required: ['operation'],
39
39
  },
40
- modes: ['chat', 'work'],
41
40
  isConcurrencySafe: () => false,
42
41
  isReadOnly: () => true,
43
42
  async execute(input, ctx) {
@@ -25,7 +25,6 @@ The message is queued for the agent to process.`,
25
25
  },
26
26
  required: ['agent_id', 'message'],
27
27
  },
28
- modes: ['work'],
29
28
  isConcurrencySafe: () => false,
30
29
  isReadOnly: () => false,
31
30
  async execute(input, ctx) {
@@ -56,7 +56,6 @@ Actions:
56
56
  },
57
57
  required: ['action'],
58
58
  },
59
- modes: ['chat', 'work'],
60
59
  isConcurrencySafe: () => true,
61
60
  isReadOnly: () => true,
62
61
  async execute(input, ctx) {
@@ -72,7 +72,6 @@ Use this to break down complex work into trackable items.`,
72
72
  },
73
73
  required: ['title'],
74
74
  },
75
- modes: ['work'],
76
75
  isConcurrencySafe: () => false,
77
76
  isReadOnly: () => false,
78
77
  async execute(input, ctx) {
@@ -143,7 +142,6 @@ Status values: pending, in_progress, completed, blocked, cancelled`,
143
142
  },
144
143
  required: ['task_id'],
145
144
  },
146
- modes: ['work'],
147
145
  isConcurrencySafe: () => false,
148
146
  isReadOnly: () => false,
149
147
  async execute(input, ctx) {
@@ -192,7 +190,6 @@ Shows task IDs, titles, status, and priority. Filter by status if needed.`,
192
190
  },
193
191
  },
194
192
  },
195
- modes: ['work'],
196
193
  isConcurrencySafe: () => true,
197
194
  isReadOnly: () => true,
198
195
  async execute(input, ctx) {
@@ -247,7 +244,6 @@ export const taskGet = defineTool({
247
244
  },
248
245
  required: ['task_id'],
249
246
  },
250
- modes: ['work'],
251
247
  isConcurrencySafe: () => true,
252
248
  isReadOnly: () => true,
253
249
  async execute(input, ctx) {
@@ -302,7 +298,6 @@ Use "view" to see the full log, or "append" to add a new entry.`,
302
298
  },
303
299
  required: ['task_id', 'action'],
304
300
  },
305
- modes: ['work'],
306
301
  isConcurrencySafe: () => false,
307
302
  isReadOnly: (input) => input?.action === 'view',
308
303
  async execute(input, ctx) {
@@ -358,7 +353,6 @@ memory can be rewritten to keep it current.`,
358
353
  },
359
354
  required: ['task_id', 'action'],
360
355
  },
361
- modes: ['work'],
362
356
  isConcurrencySafe: () => false,
363
357
  isReadOnly: (input) => input?.action === 'view',
364
358
  async execute(input, ctx) {
@@ -416,7 +410,6 @@ The new task is linked as a child of the original.`,
416
410
  },
417
411
  required: ['parent_task_id', 'title'],
418
412
  },
419
- modes: ['work'],
420
413
  isConcurrencySafe: () => false,
421
414
  isReadOnly: () => false,
422
415
  async execute(input, ctx) {
@@ -475,7 +468,6 @@ approach, steps, and status of the current work.`,
475
468
  },
476
469
  required: ['action'],
477
470
  },
478
- modes: ['work'],
479
471
  isConcurrencySafe: () => false,
480
472
  isReadOnly: (input) => input?.action === 'view',
481
473
  async execute(input, ctx) {
@@ -46,7 +46,6 @@ engine to the new thread — call SwitchThread to activate it.`,
46
46
  },
47
47
  required: ['name'],
48
48
  },
49
- modes: ['work'],
50
49
  isConcurrencySafe: () => false,
51
50
  isReadOnly: () => false,
52
51
  async execute(input) {
@@ -89,7 +88,6 @@ thread.`,
89
88
  },
90
89
  required: ['thread_id'],
91
90
  },
92
- modes: ['work'],
93
91
  isConcurrencySafe: () => false,
94
92
  isReadOnly: () => false,
95
93
  async execute(input) {
@@ -120,7 +118,6 @@ exposes id, name, goal, parentThreadId, status ('active'|'idle'|'archived'),
120
118
  messageCount, lastMessageAt, archived, attachedTaskId. Reads only cached
121
119
  fields — does not scan messages.`,
122
120
  parameters: { type: 'object', properties: {} },
123
- modes: ['work'],
124
121
  isConcurrencySafe: () => true,
125
122
  isReadOnly: () => true,
126
123
  async execute() {
@@ -168,7 +165,6 @@ the same thread.`,
168
165
  },
169
166
  required: ['thread_id', 'task_id'],
170
167
  },
171
- modes: ['work'],
172
168
  isConcurrencySafe: () => false,
173
169
  isReadOnly: () => false,
174
170
  async execute(input) {
@@ -227,7 +223,6 @@ This replaces the deprecated SpawnSubtask tool.`,
227
223
  },
228
224
  required: ['title'],
229
225
  },
230
- modes: ['work'],
231
226
  isConcurrencySafe: () => false,
232
227
  isReadOnly: () => false,
233
228
  async execute(input) {
@@ -287,7 +282,6 @@ Use this to cross-reference work on another thread without switching.`,
287
282
  },
288
283
  required: ['thread_id'],
289
284
  },
290
- modes: ['work'],
291
285
  isConcurrencySafe: () => true,
292
286
  isReadOnly: () => true,
293
287
  async execute(input) {
@@ -333,7 +327,6 @@ Use this to review another thread's recent activity without switching.`,
333
327
  },
334
328
  required: ['thread_id'],
335
329
  },
336
- modes: ['work'],
337
330
  isConcurrencySafe: () => true,
338
331
  isReadOnly: () => true,
339
332
  async execute(input, ctx) {
@@ -1,5 +1,9 @@
1
1
  /**
2
2
  * tool-search.js — Search available tools by name or description.
3
+ *
4
+ * task-311: chat/work mode was removed in task-297; this tool no longer
5
+ * accepts or reports a `modes` filter. Results come back as plain
6
+ * { name, description } pairs.
3
7
  */
4
8
 
5
9
  import { defineTool } from './types.js';
@@ -9,7 +13,7 @@ export default defineTool({
9
13
  description: `Search available tools by name or description keyword.
10
14
 
11
15
  Use when you're unsure which tool to use for a task.
12
- Returns matching tools with their descriptions and parameters.`,
16
+ Returns matching tools with their descriptions.`,
13
17
  parameters: {
14
18
  type: 'object',
15
19
  properties: {
@@ -17,77 +21,64 @@ Returns matching tools with their descriptions and parameters.`,
17
21
  type: 'string',
18
22
  description: 'Search keyword to match against tool names and descriptions',
19
23
  },
20
- mode: {
21
- type: 'string',
22
- enum: ['chat', 'work'],
23
- description: 'Filter by mode (optional)',
24
- },
25
24
  },
26
25
  required: ['query'],
27
26
  },
28
- modes: ['chat', 'work'],
29
27
  isConcurrencySafe: () => true,
30
28
  isReadOnly: () => true,
31
- async execute(input, ctx) {
32
- const { query, mode } = input;
29
+ async execute(input, _ctx) {
30
+ const { query } = input;
33
31
  if (!query) return JSON.stringify({ error: 'query is required' });
34
32
 
35
- // Access the tool registry through the engine context
36
- // Since we don't have direct registry access, list what we know
37
33
  const lowerQuery = query.toLowerCase();
38
34
 
39
- // Get all tool definitions from the registry if available
40
- // This is a self-referential tool — it describes the tools available to this engine
35
+ // Self-referential catalogue of tools available to the engine.
41
36
  const toolList = [
42
- { name: 'AskUser', description: 'Ask the user a question', modes: ['chat', 'work'] },
43
- { name: 'MemoryRead', description: 'Read from memory system', modes: ['chat', 'work'] },
44
- { name: 'MemoryWrite', description: 'Write to memory system', modes: ['chat', 'work'] },
45
- { name: 'MemorySearch', description: 'Search memory entries', modes: ['chat', 'work'] },
46
- { name: 'WebSearch', description: 'Search the web', modes: ['chat', 'work'] },
47
- { name: 'WebFetch', description: 'Fetch web page content', modes: ['chat', 'work'] },
48
- { name: 'HistorySearch', description: 'Search conversation history', modes: ['chat', 'work'] },
49
- { name: 'Bash', description: 'Execute shell commands', modes: ['chat', 'work'] },
50
- { name: 'FileRead', description: 'Read file with line numbers', modes: ['chat', 'work'] },
51
- { name: 'FileWrite', description: 'Write/create files', modes: ['chat', 'work'] },
52
- { name: 'FileEdit', description: 'Surgical string replacement in files', modes: ['chat', 'work'] },
53
- { name: 'Glob', description: 'Find files by pattern', modes: ['chat', 'work'] },
54
- { name: 'Grep', description: 'Search file contents', modes: ['chat', 'work'] },
55
- { name: 'ListDir', description: 'List directory contents', modes: ['chat', 'work'] },
56
- { name: 'ApplyPatch', description: 'Apply unified diff patches', modes: ['chat', 'work'] },
57
- { name: 'Agent', description: 'Create sub-agents', modes: ['work'] },
58
- { name: 'SendMessage', description: 'Send message to sub-agent', modes: ['work'] },
59
- { name: 'WaitAgent', description: 'Wait for sub-agent result', modes: ['work'] },
60
- { name: 'CloseAgent', description: 'Close a sub-agent', modes: ['work'] },
61
- { name: 'ListAgents', description: 'List all sub-agents', modes: ['work'] },
62
- { name: 'TaskCreate', description: 'Create a task', modes: ['work'] },
63
- { name: 'TaskUpdate', description: 'Update task status', modes: ['work'] },
64
- { name: 'TaskList', description: 'List all tasks', modes: ['work'] },
65
- { name: 'TaskGet', description: 'Get task details', modes: ['work'] },
66
- { name: 'FollowupTask', description: 'Create follow-up task', modes: ['work'] },
67
- { name: 'UpdatePlan', description: 'View/update execution plan', modes: ['work'] },
68
- { name: 'JsRepl', description: 'JavaScript REPL evaluation', modes: ['chat', 'work'] },
69
- { name: 'JsReplReset', description: 'Reset REPL state', modes: ['chat', 'work'] },
70
- { name: 'NotebookEdit', description: 'Edit Jupyter notebooks', modes: ['chat', 'work'] },
71
- { name: 'ImageGeneration', description: 'Generate images from text', modes: ['chat', 'work'] },
72
- { name: 'ViewImage', description: 'View image metadata', modes: ['chat', 'work'] },
73
- { name: 'RequestPermissions', description: 'Request dangerous operation permissions', modes: ['chat', 'work'] },
74
- { name: 'WriteStdin', description: 'Write to running process stdin', modes: ['chat', 'work'] },
75
- { name: 'Skill', description: 'Load skills from library', modes: ['chat', 'work'] },
76
- { name: 'EnterWorktree', description: 'Create git worktree', modes: ['chat', 'work'] },
77
- { name: 'ExitWorktree', description: 'Exit git worktree', modes: ['chat', 'work'] },
78
- { name: 'mcp_list_tools', description: 'List MCP server tools', modes: ['chat', 'work'] },
79
- { name: 'mcp_call_tool', description: 'Call MCP server tool', modes: ['chat', 'work'] },
37
+ { name: 'AskUser', description: 'Ask the user a question' },
38
+ { name: 'MemoryRead', description: 'Read from memory system' },
39
+ { name: 'MemoryWrite', description: 'Write to memory system' },
40
+ { name: 'MemorySearch', description: 'Search memory entries' },
41
+ { name: 'WebSearch', description: 'Search the web' },
42
+ { name: 'WebFetch', description: 'Fetch web page content' },
43
+ { name: 'HistorySearch', description: 'Search conversation history' },
44
+ { name: 'Bash', description: 'Execute shell commands' },
45
+ { name: 'FileRead', description: 'Read file with line numbers' },
46
+ { name: 'FileWrite', description: 'Write/create files' },
47
+ { name: 'FileEdit', description: 'Surgical string replacement in files' },
48
+ { name: 'Glob', description: 'Find files by pattern' },
49
+ { name: 'Grep', description: 'Search file contents' },
50
+ { name: 'ListDir', description: 'List directory contents' },
51
+ { name: 'ApplyPatch', description: 'Apply unified diff patches' },
52
+ { name: 'Agent', description: 'Create sub-agents' },
53
+ { name: 'SendMessage', description: 'Send message to sub-agent' },
54
+ { name: 'WaitAgent', description: 'Wait for sub-agent result' },
55
+ { name: 'CloseAgent', description: 'Close a sub-agent' },
56
+ { name: 'ListAgents', description: 'List all sub-agents' },
57
+ { name: 'TaskCreate', description: 'Create a task' },
58
+ { name: 'TaskUpdate', description: 'Update task status' },
59
+ { name: 'TaskList', description: 'List all tasks' },
60
+ { name: 'TaskGet', description: 'Get task details' },
61
+ { name: 'FollowupTask', description: 'Create follow-up task' },
62
+ { name: 'UpdatePlan', description: 'View/update execution plan' },
63
+ { name: 'JsRepl', description: 'JavaScript REPL evaluation' },
64
+ { name: 'JsReplReset', description: 'Reset REPL state' },
65
+ { name: 'NotebookEdit', description: 'Edit Jupyter notebooks' },
66
+ { name: 'ImageGeneration', description: 'Generate images from text' },
67
+ { name: 'ViewImage', description: 'View image metadata' },
68
+ { name: 'RequestPermissions', description: 'Request dangerous operation permissions' },
69
+ { name: 'WriteStdin', description: 'Write to running process stdin' },
70
+ { name: 'Skill', description: 'Load skills from library' },
71
+ { name: 'EnterWorktree', description: 'Create git worktree' },
72
+ { name: 'ExitWorktree', description: 'Exit git worktree' },
73
+ { name: 'mcp_list_tools', description: 'List MCP server tools' },
74
+ { name: 'mcp_call_tool', description: 'Call MCP server tool' },
80
75
  ];
81
76
 
82
- let results = toolList.filter(t =>
77
+ const results = toolList.filter(t =>
83
78
  t.name.toLowerCase().includes(lowerQuery) ||
84
79
  t.description.toLowerCase().includes(lowerQuery)
85
80
  );
86
81
 
87
- if (mode) {
88
- results = results.filter(t => t.modes.includes(mode));
89
- }
90
-
91
82
  return JSON.stringify({
92
83
  results,
93
84
  totalResults: results.length,
@@ -5,6 +5,9 @@
5
5
  * This ensures consistent shape and API-format conversion.
6
6
  *
7
7
  * Reference: yeaft-unify-core-systems.md §3.1
8
+ *
9
+ * task-311: the legacy `modes` field (task-297 deprecated) is now fully
10
+ * removed — Unify runs in a single unified mode.
8
11
  */
9
12
 
10
13
  /**
@@ -24,9 +27,6 @@
24
27
  * @property {string} description — LLM-facing description
25
28
  * @property {object} parameters — JSON Schema for input
26
29
  * @property {(input: object, ctx?: ToolContext) => Promise<string>} execute — execution function
27
- * @property {string[]} [modes] — @deprecated since task-297. Legacy mode filter (['chat', 'work']).
28
- * Unify no longer has mode distinction; the ToolRegistry ignores this field and exposes every
29
- * registered tool to the engine. Retained only so existing tool definitions keep loading.
30
30
  * @property {(input?: object) => boolean} [isConcurrencySafe] — can run in parallel?
31
31
  * @property {(input?: object) => boolean} [isReadOnly] — read-only operation?
32
32
  * @property {(input?: object) => boolean} [isDestructive] — destructive operation?
@@ -40,7 +40,6 @@
40
40
  * description: string,
41
41
  * parameters: object,
42
42
  * execute: (input: object, ctx?: ToolContext) => Promise<string>,
43
- * modes?: string[], // @deprecated since task-297 — ignored by ToolRegistry
44
43
  * isConcurrencySafe?: (input?: object) => boolean,
45
44
  * isReadOnly?: (input?: object) => boolean,
46
45
  * isDestructive?: (input?: object) => boolean,
@@ -52,7 +51,6 @@ export function defineTool({
52
51
  description,
53
52
  parameters,
54
53
  execute,
55
- modes = ['chat', 'work'],
56
54
  isConcurrencySafe = () => false,
57
55
  isReadOnly = () => false,
58
56
  isDestructive = () => false,
@@ -65,7 +63,6 @@ export function defineTool({
65
63
  description: description || `Tool: ${name}`,
66
64
  parameters: parameters || { type: 'object', properties: {} },
67
65
  execute,
68
- modes,
69
66
  isConcurrencySafe,
70
67
  isReadOnly,
71
68
  isDestructive,
@@ -63,7 +63,6 @@ Supports PNG, JPEG, GIF, BMP, WebP, SVG, and ICO.`,
63
63
  },
64
64
  required: ['file_path'],
65
65
  },
66
- modes: ['chat', 'work'],
67
66
  isConcurrencySafe: () => true,
68
67
  isReadOnly: () => true,
69
68
  async execute(input, ctx) {
@@ -25,7 +25,6 @@ Use after sending a task to an agent via SendMessage.`,
25
25
  },
26
26
  required: ['agent_id'],
27
27
  },
28
- modes: ['work'],
29
28
  isConcurrencySafe: () => true,
30
29
  isReadOnly: () => true,
31
30
  async execute(input, ctx) {
@@ -62,7 +62,6 @@ Guidelines:
62
62
  },
63
63
  required: ['url'],
64
64
  },
65
- modes: ['chat', 'work'],
66
65
  isConcurrencySafe: () => true,
67
66
  isReadOnly: () => true,
68
67
  async execute(input, ctx) {
@@ -32,7 +32,6 @@ Guidelines:
32
32
  },
33
33
  required: ['query'],
34
34
  },
35
- modes: ['chat', 'work'],
36
35
  isConcurrencySafe: () => true,
37
36
  isReadOnly: () => true,
38
37
  async execute(input, ctx) {
@@ -34,7 +34,6 @@ Note: For most use cases, pipe input via Bash: echo "input" | command`,
34
34
  },
35
35
  required: ['data'],
36
36
  },
37
- modes: ['chat', 'work'],
38
37
  isConcurrencySafe: () => false,
39
38
  isReadOnly: () => false,
40
39
  async execute(input, ctx) {