@yeaft/webchat-agent 1.0.377 → 1.0.378

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.
@@ -0,0 +1,180 @@
1
+ import { COLLAB_TOOL_POLICY } from './registry.js';
2
+
3
+ /**
4
+ * Built-ins whose schemas stay visible on every provider request. Registration
5
+ * remains broader than exposure so legacy aliases and conditional tools can
6
+ * still be resolved without paying their schema cost on unrelated turns.
7
+ */
8
+ export const ALWAYS_VISIBLE_TOOL_NAMES = Object.freeze([
9
+ 'Skill',
10
+ 'EnterWorktree',
11
+ 'ExitWorktree',
12
+ 'AskUser',
13
+ 'DiscoverTools',
14
+ 'WebSearch',
15
+ 'WebFetch',
16
+ 'Bash',
17
+ 'FileRead',
18
+ 'FileWrite',
19
+ 'FileEdit',
20
+ 'Glob',
21
+ 'Grep',
22
+ 'ListDir',
23
+ 'StartPlan',
24
+ 'TodoWrite',
25
+ 'ViewImage',
26
+ ]);
27
+
28
+ export const BACKGROUND_TASK_TOOL_NAMES = Object.freeze([
29
+ 'ListTasks',
30
+ 'ReadTaskLog',
31
+ 'CancelTask',
32
+ ]);
33
+
34
+ export const SUB_AGENT_MANAGEMENT_TOOL_NAMES = Object.freeze([
35
+ 'PromptAgent',
36
+ 'WaitAgent',
37
+ 'CloseAgent',
38
+ 'ListAgents',
39
+ ]);
40
+
41
+ export const CONDITIONAL_BUILTIN_TOOL_NAMES = new Set([
42
+ 'HistorySearch',
43
+ 'DiskUsage',
44
+ 'ApplyPatch',
45
+ 'ListTasks',
46
+ 'ReadTaskLog',
47
+ 'CancelTask',
48
+ 'SpawnAgent',
49
+ 'PromptAgent',
50
+ 'WaitAgent',
51
+ 'CloseAgent',
52
+ 'ListAgents',
53
+ 'RouteForward',
54
+ 'CreateWorkItem',
55
+ 'JsRepl',
56
+ 'NotebookEdit',
57
+ 'ImageGeneration',
58
+ ]);
59
+
60
+ const HISTORY_INTENT_RE = /(?:\bhistory\b|\b(?:prior|previous) (?:chat|conversation|discussion)\b|\bprevious(?:ly)? discussed\b|\bwhat did we (?:decide|discuss|say|agree)\b|\b(?:our|the) (?:earlier|last) decision\b|历史|之前(?:的)?(?:对话|讨论|会话|决定)|过去(?:的)?会话|我们(?:之前|上次)(?:决定|讨论|说)了什么)/iu;
61
+ const DISK_INTENT_RE = /(?:\bdisk (?:usage|space|full)\b|\bstorage (?:usage|space|full)\b|\blargest director|\benospc\b|\bno space left on device\b|磁盘(?:占用|空间|已满)|存储空间|目录占用|空间不足)/iu;
62
+ const PATCH_INTENT_RE = /(?:\bapply (?:a )?patch\b|\bunified diff\b|\bpatch file\b|应用补丁|统一 diff|补丁文件)/iu;
63
+ const TASK_INTENT_RE = /(?:\bbackground (?:task|job|command|process)\b|\btask[_-][a-z0-9]+\b|\btask log\b|后台(?:任务|命令|进程)|任务日志)/iu;
64
+ const SUB_AGENT_INTENT_RE = /(?:\bsub[ -]?agent\b|\bagent(?:s)?\b|\bparallel(?:ize| work| task| review)?\b|\bindependent(?:ly| review)?\b|\banother (?:worker|reviewer|agent)\b|\bdelegate\b|\b(?:run|start|launch|spawn) (?:the |a )?(?:task|child)\b|子 ?Agent|并行(?:处理|工作|任务|审查)?|独立(?:处理|审查)?|另一个(?:人|助手|Agent)|委派)/iu;
65
+ const WORK_ITEM_INTENT_RE = /(?:\bwork ?center\b|\bwork ?item\b|\bdurable tracking\b|\bcross[- ]turn\b|\blong[- ]running goal\b|\bacross multiple (?:turns|sessions)\b|\buntil (?:it is|it's) finished\b|工作中心|工作项|持久(?:任务|跟踪)|跨 ?turn|跨多个会话|长期任务|持续跟踪)/iu;
66
+ const REPL_INTENT_RE = /(?:\bjs ?repl\b|\bjavascript (?:calculation|experiment|evaluation)\b|\bcalculate\b|\bdata transform\b|JavaScript (?:计算|实验|求值)|数据转换|快速计算)/iu;
67
+ const NOTEBOOK_INTENT_RE = /(?:\.ipynb\b|\bjupyter\b|\bnotebook (?:cell|file)\b|Jupyter|笔记本单元格)/iu;
68
+ const IMAGE_GENERATION_INTENT_RE = /(?:\b(?:generate|make|design|draw) (?:me |us )?(?:an? |the )?(?:image|picture|logo|icon|illustration|graphic)\b|\bcreate (?:me |us )?(?:an? |the )?(?:illustration|image|picture|logo|icon|graphic)\b|生成(?:一张)?(?:图片|图像|标志|图标|插图)|创建(?:一张)?(?:插图|图片|图像|标志|图标)|画(?:一张)?(?:图|图片|图标))/iu;
69
+ const MCP_INTENT_RE = /(?:\bmcp\b|model context protocol|模型上下文协议)/iu;
70
+
71
+ function messageText(message) {
72
+ const content = message?.content;
73
+ if (typeof content === 'string') return content;
74
+ if (!Array.isArray(content)) return '';
75
+ return content
76
+ .filter(part => part?.type === 'text' && typeof part.text === 'string')
77
+ .map(part => part.text)
78
+ .join('\n');
79
+ }
80
+
81
+ /**
82
+ * Build a bounded intent window from the current request and recent context.
83
+ * A short "continue" / "yes" turn can therefore retain a conditional tool
84
+ * selected by the immediately preceding discussion without making every old
85
+ * topic permanently activate tools.
86
+ */
87
+ export function buildToolIntentText(prompt, messages = []) {
88
+ const recent = Array.isArray(messages) ? messages.slice(-6) : [];
89
+ const pieces = [...recent.map(messageText), typeof prompt === 'string' ? prompt : '']
90
+ .filter(Boolean);
91
+ return pieces.join('\n').slice(-12_000);
92
+ }
93
+
94
+ function matchedMcpTools(intentText, toolNames) {
95
+ const mcpTools = toolNames.filter(name => name.startsWith('mcp__'));
96
+ const normalized = intentText.toLowerCase();
97
+ const explicitMatches = mcpTools.filter(name => {
98
+ if (normalized.includes(name.toLowerCase())) return true;
99
+ const [, server = '', tool = ''] = name.split('__');
100
+ if (tool && normalized.includes(tool.toLowerCase())) return true;
101
+ return server && tool
102
+ && normalized.includes(server.toLowerCase())
103
+ && normalized.includes(tool.toLowerCase());
104
+ });
105
+ if (explicitMatches.length > 0) return explicitMatches;
106
+ return MCP_INTENT_RE.test(intentText) ? mcpTools : [];
107
+ }
108
+
109
+ /**
110
+ * Resolve the canonical tool names exposed to one provider request.
111
+ *
112
+ * Unknown caller-registered tools remain visible for compatibility. Only
113
+ * Yeaft built-ins and flattened MCP tools participate in conditional hiding.
114
+ *
115
+ * @param {{
116
+ * toolNames: string[],
117
+ * prompt?: string,
118
+ * messages?: object[],
119
+ * collabToolPolicy?: string|null,
120
+ * activeTasks?: object[],
121
+ * subAgentToolsActivated?: boolean,
122
+ * imageGenerationConfigured?: boolean,
123
+ * }} opts
124
+ * @returns {Set<string>}
125
+ */
126
+ export function resolveActiveToolNames({
127
+ toolNames = [],
128
+ prompt = '',
129
+ messages = [],
130
+ collabToolPolicy = null,
131
+ activeTasks = [],
132
+ subAgentToolsActivated = false,
133
+ imageGenerationConfigured = false,
134
+ } = {}) {
135
+ const registered = new Set(Array.isArray(toolNames) ? toolNames : []);
136
+ const active = new Set(ALWAYS_VISIBLE_TOOL_NAMES.filter(name => registered.has(name)));
137
+ const intentText = buildToolIntentText(prompt, messages);
138
+ const tasks = Array.isArray(activeTasks) ? activeTasks : [];
139
+ const hasActiveTasks = tasks.length > 0;
140
+ const hasSubAgentTask = tasks.some(task => task?.kind === 'sub_agent');
141
+
142
+ if (HISTORY_INTENT_RE.test(intentText)) active.add('HistorySearch');
143
+ if (DISK_INTENT_RE.test(intentText)) active.add('DiskUsage');
144
+ if (PATCH_INTENT_RE.test(intentText)) active.add('ApplyPatch');
145
+
146
+ if (hasActiveTasks || TASK_INTENT_RE.test(intentText)) {
147
+ for (const name of BACKGROUND_TASK_TOOL_NAMES) active.add(name);
148
+ }
149
+
150
+ if (SUB_AGENT_INTENT_RE.test(intentText) || subAgentToolsActivated || hasSubAgentTask) {
151
+ active.add('SpawnAgent');
152
+ for (const name of SUB_AGENT_MANAGEMENT_TOOL_NAMES) active.add(name);
153
+ }
154
+
155
+ if (collabToolPolicy === COLLAB_TOOL_POLICY.MULTI_VP) active.add('RouteForward');
156
+ if (WORK_ITEM_INTENT_RE.test(intentText)) active.add('CreateWorkItem');
157
+ if (REPL_INTENT_RE.test(intentText)) active.add('JsRepl');
158
+ if (NOTEBOOK_INTENT_RE.test(intentText)) active.add('NotebookEdit');
159
+ if (imageGenerationConfigured && IMAGE_GENERATION_INTENT_RE.test(intentText)) active.add('ImageGeneration');
160
+
161
+ for (const name of matchedMcpTools(intentText, toolNames)) active.add(name);
162
+
163
+ // Extra tools supplied by an embedding caller have no Yeaft activation
164
+ // policy. Preserve the historical contract and expose them by default.
165
+ for (const name of registered) {
166
+ if (ALWAYS_VISIBLE_TOOL_NAMES.includes(name)) continue;
167
+ if (CONDITIONAL_BUILTIN_TOOL_NAMES.has(name)) continue;
168
+ if (name.startsWith('mcp__')) continue;
169
+ active.add(name);
170
+ }
171
+
172
+ // Never advertise deprecated schema-only compatibility shims. Direct
173
+ // registry execution without an active-set fence remains backward compatible.
174
+ active.delete('JsReplReset');
175
+
176
+ for (const name of [...active]) {
177
+ if (!registered.has(name)) active.delete(name);
178
+ }
179
+ return active;
180
+ }
@@ -0,0 +1,198 @@
1
+ import { defineTool } from './types.js';
2
+
3
+ export const TOOL_DISCOVERY_MAX_RESULTS = 24;
4
+ export const TOOL_DISCOVERY_MAX_NAME_CHARS = 256;
5
+ export const TOOL_DISCOVERY_MAX_DESCRIPTION_CHARS = 320;
6
+ export const TOOL_DISCOVERY_MAX_OUTPUT_BYTES = 12 * 1024;
7
+
8
+ const TOKEN_RE = /[\p{L}\p{N}]+/gu;
9
+ const STOP_WORDS = new Set([
10
+ 'a', 'an', 'and', 'are', 'as', 'at', 'be', 'by', 'can', 'do', 'for', 'from',
11
+ 'how', 'i', 'in', 'is', 'it', 'me', 'my', 'of', 'on', 'or', 'the', 'this',
12
+ 'to', 'us', 'we', 'what', 'when', 'where', 'which', 'who', 'why', 'with', 'you',
13
+ ]);
14
+
15
+ function lowerTokens(value) {
16
+ const text = String(value || '');
17
+ if (!text) return [];
18
+ const expanded = text
19
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
20
+ .replace(/[_-]+/g, ' ');
21
+ return (expanded.match(TOKEN_RE) || []).map(token => token.toLocaleLowerCase());
22
+ }
23
+
24
+ function tokens(value) {
25
+ return lowerTokens(value).filter(token => token.length > 1 && !STOP_WORDS.has(token));
26
+ }
27
+
28
+ function truncate(value, maxChars) {
29
+ const text = String(value || '').replace(/\s+/g, ' ').trim();
30
+ return text.length <= maxChars ? text : `${text.slice(0, maxChars - 1)}…`;
31
+ }
32
+
33
+ function toolSearchText(tool) {
34
+ return [
35
+ tool.name,
36
+ tool.description,
37
+ ...Object.keys(tool.parameters?.properties || {}),
38
+ ].map(value => String(value || '')).join(' ');
39
+ }
40
+
41
+ function scoreTool(tool, queryTokens) {
42
+ if (queryTokens.length === 0) return 0;
43
+ const name = String(tool.name || '').toLocaleLowerCase();
44
+ const text = toolSearchText(tool).toLocaleLowerCase();
45
+ const haystackTokens = new Set(tokens(text));
46
+ let score = 0;
47
+ for (const token of queryTokens) {
48
+ if (name.includes(token)) score += 8;
49
+ else if (haystackTokens.has(token)) score += 4;
50
+ else if (token.length >= 5 && text.includes(token)) score += 1;
51
+ }
52
+ return score;
53
+ }
54
+
55
+ function normalizeCursor(value) {
56
+ if (value == null || value === '') return 0;
57
+ const cursor = Number(value);
58
+ return Number.isInteger(cursor) && cursor >= 0 ? cursor : null;
59
+ }
60
+
61
+ function validCandidate(tool) {
62
+ return typeof tool?.name === 'string'
63
+ && tool.name.length > 0
64
+ && tool.name.length <= TOOL_DISCOVERY_MAX_NAME_CHARS;
65
+ }
66
+
67
+ function byteLength(value) {
68
+ return Buffer.byteLength(JSON.stringify(value), 'utf8');
69
+ }
70
+
71
+ /**
72
+ * Return one deterministic page from the complete hidden tool directory.
73
+ * Lexical scoring only improves ordering; it never decides reachability.
74
+ */
75
+ export function discoverToolCapabilities({
76
+ query,
77
+ candidates = [],
78
+ cursor = 0,
79
+ maxResults = TOOL_DISCOVERY_MAX_RESULTS,
80
+ } = {}) {
81
+ const boundedMax = Math.max(1, Math.min(Number(maxResults) || TOOL_DISCOVERY_MAX_RESULTS, TOOL_DISCOVERY_MAX_RESULTS));
82
+ const queryTokens = [...new Set(tokens(query))];
83
+ let omittedInvalid = 0;
84
+ const scored = [];
85
+ for (const tool of Array.isArray(candidates) ? candidates : []) {
86
+ if (!validCandidate(tool)) {
87
+ omittedInvalid += 1;
88
+ continue;
89
+ }
90
+ scored.push({
91
+ tool,
92
+ score: scoreTool(tool, queryTokens),
93
+ });
94
+ }
95
+ scored.sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name));
96
+
97
+ const normalizedCursor = normalizeCursor(cursor);
98
+ if (normalizedCursor == null || normalizedCursor > scored.length) {
99
+ return {
100
+ tools: [],
101
+ next_cursor: null,
102
+ total: scored.length,
103
+ omitted_invalid: omittedInvalid,
104
+ restart_required: true,
105
+ message: 'The hidden tool directory changed or the cursor is invalid. Restart discovery without a cursor.',
106
+ };
107
+ }
108
+ const start = normalizedCursor;
109
+ const tools = [];
110
+ let index = start;
111
+ while (index < scored.length && tools.length < boundedMax) {
112
+ const tool = scored[index].tool;
113
+ const item = {
114
+ name: tool.name,
115
+ description: truncate(tool.description, TOOL_DISCOVERY_MAX_DESCRIPTION_CHARS),
116
+ };
117
+ const hasMoreAfterItem = index + 1 < scored.length;
118
+ const tentative = {
119
+ tools: [...tools, item],
120
+ next_cursor: hasMoreAfterItem ? index + 1 : null,
121
+ total: scored.length,
122
+ omitted_invalid: omittedInvalid,
123
+ };
124
+ if (byteLength(tentative) > TOOL_DISCOVERY_MAX_OUTPUT_BYTES) break;
125
+ tools.push(item);
126
+ index += 1;
127
+ }
128
+
129
+ const nextCursor = index < scored.length && tools.length > 0 ? index : null;
130
+ const result = {
131
+ tools,
132
+ next_cursor: nextCursor,
133
+ total: scored.length,
134
+ omitted_invalid: omittedInvalid,
135
+ };
136
+ if (byteLength(result) > TOOL_DISCOVERY_MAX_OUTPUT_BYTES) {
137
+ return { tools: [], next_cursor: null, total: 0, omitted_invalid: omittedInvalid };
138
+ }
139
+ return result;
140
+ }
141
+
142
+ export default defineTool({
143
+ name: 'DiscoverTools',
144
+ description: {
145
+ en: `Discover registered tools whose full schemas are not currently active.
146
+
147
+ Use this when the visible tools do not clearly cover the user's request. Search by the user's goal or capability, not by a guessed tool name. The result is a bounded page from the complete hidden tool directory, ordered by likely relevance; lexical similarity affects ordering only and never removes capabilities. If the target is absent, continue with next_cursor until found or the directory is exhausted. If restart_required is true, discard the cursor and restart without one because the registered directory changed. Returned tool schemas become available on the next model loop.`,
148
+ zh: `发现已注册但当前未激活完整 schema 的工具。
149
+
150
+ 当可见工具不能明确覆盖用户请求时使用。应按用户目标或能力搜索,不要猜工具名。结果是完整隐藏工具目录中的有界分页,并按可能相关性排序;词法相似度只影响顺序,绝不会让能力不可达。如果当前页没有目标,应使用 next_cursor 继续翻页直到找到或目录耗尽。如果 restart_required 为 true,说明注册目录已变化,应丢弃游标并在不带游标的情况下重新开始。返回工具的 schema 会在下一轮模型调用中可用。`,
151
+ },
152
+ parameters: {
153
+ type: 'object',
154
+ properties: {
155
+ query: {
156
+ type: 'string',
157
+ description: {
158
+ en: 'User goal or capability used to rank the complete hidden directory',
159
+ zh: '用于排序完整隐藏目录的用户目标或能力',
160
+ },
161
+ },
162
+ cursor: {
163
+ type: 'number',
164
+ description: {
165
+ en: 'Pagination cursor returned by a previous discovery page',
166
+ zh: '上一页发现结果返回的分页游标',
167
+ },
168
+ },
169
+ max_results: {
170
+ type: 'number',
171
+ description: {
172
+ en: 'Maximum directory entries to activate (default and maximum: 24)',
173
+ zh: '最多激活的目录项数(默认及上限为 24)',
174
+ },
175
+ },
176
+ },
177
+ required: ['query'],
178
+ },
179
+ isConcurrencySafe: () => true,
180
+ isReadOnly: () => true,
181
+ async execute(input, ctx = {}) {
182
+ const query = typeof input?.query === 'string' ? input.query.trim() : '';
183
+ if (!query) return JSON.stringify({ error: 'query is required' });
184
+ if (typeof ctx?.discoverTools !== 'function') {
185
+ return JSON.stringify({ error: 'Tool discovery is unavailable in this runtime.' });
186
+ }
187
+ const result = await ctx.discoverTools({
188
+ query,
189
+ cursor: input?.cursor,
190
+ maxResults: input?.max_results,
191
+ });
192
+ const output = JSON.stringify(result);
193
+ if (Buffer.byteLength(output, 'utf8') > TOOL_DISCOVERY_MAX_OUTPUT_BYTES) {
194
+ throw new Error('Tool discovery violated its bounded result budget');
195
+ }
196
+ return output;
197
+ },
198
+ });
@@ -28,6 +28,7 @@ import askUser from './ask-user.js';
28
28
  import webSearch from './web-search.js';
29
29
  import webFetch from './web-fetch.js';
30
30
  import historySearch from './history-search.js';
31
+ import discoverTools from './discover-tools.js';
31
32
 
32
33
  // --- P0 File tools ---
33
34
  import bash from './bash.js';
@@ -94,6 +95,7 @@ export const allTools = [
94
95
  webSearch,
95
96
  webFetch,
96
97
  historySearch,
98
+ discoverTools,
97
99
 
98
100
  // P0 File
99
101
  bash,
@@ -48,7 +48,7 @@ function isLocalizedTextObject(value) {
48
48
  return ['en', 'zh', 'zh-CN', 'en-US', 'default'].some(key => typeof value[key] === 'string' || typeof value[key] === 'function');
49
49
  }
50
50
 
51
- function localizeVisibleText(value, language, toolName) {
51
+ export function localizeVisibleText(value, language, toolName) {
52
52
  const lang = normalizeLanguage(language);
53
53
  if (typeof value === 'function') return localizeVisibleText(value(lang), lang, toolName);
54
54
  if (isLocalizedTextObject(value)) {
@@ -375,17 +375,25 @@ export class ToolRegistry {
375
375
 
376
376
  /**
377
377
  * Get tool definitions for the LLM adapter.
378
- * Returns all registered tools unless the current session shape makes a
379
- * specific collaboration tool invalid, such as RouteForward in single-VP
380
- * sessions.
378
+ *
379
+ * Registration and exposure are separate: `activeToolNames` limits which
380
+ * canonical schemas enter this provider request, while aliases remain
381
+ * registered for replay compatibility. Collaboration policy is an additional
382
+ * structural fence (for example, RouteForward is invalid in single-VP
383
+ * Sessions).
384
+ *
381
385
  * @param {string} [language='en']
382
- * @param {{ collabToolPolicy?: string }} [opts]
386
+ * @param {{ collabToolPolicy?: string, activeToolNames?: Set<string>|string[] }} [opts]
383
387
  * @returns {{ name: string, description: string, parameters: object }[]}
384
388
  */
385
389
  getToolDefs(language = 'en', opts = {}) {
386
390
  const lang = normalizeLanguage(language);
387
391
  const collabToolPolicy = normalizeCollabToolPolicy(opts?.collabToolPolicy);
392
+ const activeToolNames = opts?.activeToolNames instanceof Set
393
+ ? opts.activeToolNames
394
+ : (Array.isArray(opts?.activeToolNames) ? new Set(opts.activeToolNames) : null);
388
395
  return this.getAllTools()
396
+ .filter(t => !activeToolNames || activeToolNames.has(t.name))
389
397
  .filter(t => !isToolHiddenByCollabPolicy(t.name, collabToolPolicy))
390
398
  .map(t => {
391
399
  return {
@@ -397,16 +405,24 @@ export class ToolRegistry {
397
405
  }
398
406
 
399
407
  /**
400
- * Check whether a tool may be called under the current collaboration policy.
401
- * Unknown / absent policy keeps the historical behavior: all registered
402
- * tools remain callable.
408
+ * Check whether a tool may be called under the current exposure and
409
+ * collaboration policies. Unknown / absent policies keep the historical
410
+ * behavior for direct registry callers.
411
+ *
412
+ * Aliases inherit the canonical tool's activation: an old `Agent` tool call
413
+ * may execute only when canonical `SpawnAgent` is active for this request.
414
+ *
403
415
  * @param {string} name
404
- * @param {{ collabToolPolicy?: string }} [opts]
416
+ * @param {{ collabToolPolicy?: string, activeToolNames?: Set<string>|string[] }} [opts]
405
417
  * @returns {boolean}
406
418
  */
407
419
  isAllowed(name, opts = {}) {
408
420
  const tool = this.#tools.get(name);
409
421
  if (!tool) return false;
422
+ const activeToolNames = opts?.activeToolNames instanceof Set
423
+ ? opts.activeToolNames
424
+ : (Array.isArray(opts?.activeToolNames) ? new Set(opts.activeToolNames) : null);
425
+ if (activeToolNames && !activeToolNames.has(tool.name)) return false;
410
426
  return !isToolHiddenByCollabPolicy(tool.name, opts?.collabToolPolicy);
411
427
  }
412
428
 
@@ -22,6 +22,7 @@
22
22
  * @property {object} [skillManager] — Skill manager
23
23
  * @property {object} [trace] — debug trace
24
24
  * @property {object} [config] — engine config
25
+ * @property {(input: {query:string, cursor?:number, maxResults?:number}) => Promise<object>|object} [discoverTools] — page through hidden registered tool capabilities and activate returned entries for the next provider loop
25
26
  * @property {import('../tasks/manager.js').TaskManager} [taskManager] — Session task manager
26
27
  * @property {string} [sessionId] — current Session id
27
28
  * @property {string[]} [projectSessionIds] — same-Agent sibling Session ids in the current Project