@yeaft/webchat-agent 0.1.486 → 0.1.488

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 (43) 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/pipeline/dispatcher.js +373 -0
  6. package/unify/session.js +21 -0
  7. package/unify/skills.js +15 -9
  8. package/unify/tools/agent.js +0 -1
  9. package/unify/tools/apply-patch.js +0 -1
  10. package/unify/tools/ask-user.js +0 -1
  11. package/unify/tools/bash.js +0 -1
  12. package/unify/tools/close-agent.js +0 -1
  13. package/unify/tools/enter-worktree.js +0 -1
  14. package/unify/tools/exit-worktree.js +0 -1
  15. package/unify/tools/file-edit.js +0 -1
  16. package/unify/tools/file-read.js +0 -1
  17. package/unify/tools/file-write.js +0 -1
  18. package/unify/tools/glob.js +0 -1
  19. package/unify/tools/grep.js +0 -1
  20. package/unify/tools/history-search.js +0 -1
  21. package/unify/tools/image-generation.js +0 -1
  22. package/unify/tools/js-repl.js +0 -2
  23. package/unify/tools/list-agents.js +0 -1
  24. package/unify/tools/list-dir.js +0 -1
  25. package/unify/tools/mcp-tools.js +0 -2
  26. package/unify/tools/memory-query.js +0 -1
  27. package/unify/tools/memory-read.js +0 -1
  28. package/unify/tools/memory-search.js +0 -1
  29. package/unify/tools/memory-write.js +0 -1
  30. package/unify/tools/notebook-edit.js +0 -1
  31. package/unify/tools/request-permissions.js +0 -1
  32. package/unify/tools/send-message.js +0 -1
  33. package/unify/tools/skill.js +0 -1
  34. package/unify/tools/task-tools.js +0 -8
  35. package/unify/tools/thread-tools.js +0 -7
  36. package/unify/tools/tool-search.js +47 -56
  37. package/unify/tools/types.js +3 -6
  38. package/unify/tools/view-image.js +0 -1
  39. package/unify/tools/wait-agent.js +0 -1
  40. package/unify/tools/web-fetch.js +0 -1
  41. package/unify/tools/web-search.js +0 -1
  42. package/unify/tools/write-stdin.js +0 -1
  43. package/unify/web-bridge.js +281 -181
package/unify/session.js CHANGED
@@ -27,7 +27,10 @@ import { initThreadStore } from './threads/store.js';
27
27
  import { Engine } from './engine.js';
28
28
  import { createThreadEngineRegistry } from './threads/engine-registry.js';
29
29
  import { MAIN_THREAD_ID } from './threads/store.js';
30
+ import { getThreadStore } from './threads/store.js';
30
31
  import { createIntentClassifier } from './router/intent-classifier.js';
32
+ import { initInputQueueStore } from './input-queue/store.js';
33
+ import { createDispatcher } from './pipeline/dispatcher.js';
31
34
  import { join } from 'path';
32
35
 
33
36
  /**
@@ -193,6 +196,22 @@ export async function loadSession(options = {}) {
193
196
  // config as the engines so it can use primaryModel for classification.
194
197
  const router = createIntentClassifier({ adapter, trace, config });
195
198
 
199
+ // task-310 Phase 2 integration: wire InputQueue + Dispatcher so the
200
+ // web-bridge can submit `unify_chat` inputs through the unified pipeline
201
+ // (queue → router → engineRegistry → EngineInstance). In read-only mode
202
+ // the queue is memory-only (no disk writes).
203
+ const inputQueue = initInputQueueStore({
204
+ yeaftDir: config._readOnly ? null : yeaftDir,
205
+ force: true,
206
+ });
207
+ const dispatcher = createDispatcher({
208
+ inputQueue,
209
+ router,
210
+ engineRegistry,
211
+ threadStore: getThreadStore(),
212
+ trace,
213
+ });
214
+
196
215
  // ─── 10. Build session ─────────────────────────────────
197
216
  const status = {
198
217
  skills: skillManager.size,
@@ -224,6 +243,8 @@ export async function loadSession(options = {}) {
224
243
  engine,
225
244
  engineRegistry,
226
245
  router,
246
+ inputQueue,
247
+ dispatcher,
227
248
  adapter,
228
249
  config,
229
250
  conversationStore,
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,