@yeaft/webchat-agent 1.0.514 → 1.0.516

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/yeaft/prompts.js CHANGED
@@ -9,12 +9,10 @@
9
9
  *
10
10
  * Concept layering (DESIGN-PROMPT §3):
11
11
  * ① Identity — VP persona body (or Yeaft fallback)
12
- * ② Rules — session announcement, date, mode template, tools,
13
- * tool-guidance, skills, common rules
12
+ * ② Rules — Session/Project instructions, date, runtime, and skills
14
13
  * ③ Memory — single block produced upstream by the AMS render
15
14
  * outlet and threaded through here as `memoryInjection`
16
- * ④ Active Scope — structured per-turn scope summary
17
- * (session / vp / members / envelope IDs)
15
+ * ④ Routing — multi-VP identity and handoff metadata when applicable
18
16
  *
19
17
  * Long-term semantic context comes only from the AMS Memory outlet. The
20
18
  * conversation transcript stays in the messages timeline and is bounded by
@@ -142,7 +140,6 @@ const RAW_TEMPLATES = {
142
140
  commonRules: readTemplate('common-rules.md', { required: false }),
143
141
  modeUnified: readTemplate('mode-unified.md'),
144
142
  modeDream: readTemplate('mode-dream.md'),
145
- toolGuidance: readTemplate('tool-guidance.md'),
146
143
  // Phase 1 — DESIGN.md "Migration Plan" harness fragments. Optional so
147
144
  // older deployments without the templates still boot; buildWorkerPrompt /
148
145
  // buildRouterPrompt callers will simply omit the section.
@@ -150,10 +147,6 @@ const RAW_TEMPLATES = {
150
147
  harnessRouterShape: readTemplate('harness/router-shape.md', { required: false }),
151
148
  // Phase 3b — coordinator harness rule for inter-VP forwarding.
152
149
  harnessRouterHandoff: readTemplate('harness/router-handoff.md', { required: false }),
153
- // task — StartPlan tool fallback. The `StartPlan` tool reads this when
154
- // a VP has no `planInstruction` of its own. Required so a misconfigured
155
- // install fails fast instead of injecting an empty plan instruction.
156
- planInstruction: readTemplate('plan-instruction.md'),
157
150
  };
158
151
 
159
152
  /**
@@ -168,23 +161,6 @@ function getTemplate(key, language) {
168
161
  return extractLangSection(raw, language);
169
162
  }
170
163
 
171
- /**
172
- * Default planning-instruction text returned by the `StartPlan` tool when
173
- * the active VP has no `planInstruction` override on its role.md frontmatter.
174
- *
175
- * Pulled from `templates/plan-instruction.md`. Marked required at load time
176
- * so a missing template fails fast on agent boot — preferable to silently
177
- * shipping an empty plan instruction to the LLM.
178
- *
179
- * @param {string} [language='en'] — 'en' / 'zh' (uses lang-section markers
180
- * if the template carries them; falls
181
- * back to the whole body otherwise).
182
- * @returns {string}
183
- */
184
- export function getDefaultPlanInstruction(language = 'en') {
185
- return getTemplate('planInstruction', normalizePromptLanguage(language));
186
- }
187
-
188
164
  // ─── Prompt Templates (hardcoded fallbacks) ──────────────────────
189
165
 
190
166
  const PROMPTS = {
@@ -192,12 +168,6 @@ const PROMPTS = {
192
168
  identity: 'No VP soul is active for this turn. Participate in the current session with grounded, evidence-based answers and preserve the user\'s context.',
193
169
  date: (d) => `Date: ${d}`,
194
170
  dream: 'You are in dream mode. Reflect on past conversations and consolidate memories.',
195
- // DESIGN-PROMPT §3 ④ — current session context block.
196
- activeScopeHeader: '## Current session context',
197
- activeScopeSessionIdLabel: 'Session ID',
198
- activeScopeMembersLabel: 'Session members',
199
- activeScopeTopicsLabel: 'Current focus',
200
- activeScopeEnvelopeLabel: 'Handoff',
201
171
  multiVpRoutingHeader: '## multi_vp_routing',
202
172
  sessionAnnouncementHeader: '[Session Announcement]',
203
173
  projectInstructionHeader: '[Project Instruction]',
@@ -218,12 +188,6 @@ const PROMPTS = {
218
188
  identity: '你正在当前会话中参与协作。保持用户上下文,回答要基于证据;需要工具时使用工具,但不要把自己没有实际执行过的事说成已经执行。',
219
189
  date: (d) => `日期:${d}`,
220
190
  dream: '你处于梦境模式。回顾过去的对话,整理和巩固记忆。',
221
- // DESIGN-PROMPT §3 ④ — 当前会话上下文。
222
- activeScopeHeader: '## 当前会话上下文',
223
- activeScopeSessionIdLabel: '会话 ID',
224
- activeScopeMembersLabel: '会话成员',
225
- activeScopeTopicsLabel: '当前讨论',
226
- activeScopeEnvelopeLabel: '转交消息',
227
191
  multiVpRoutingHeader: '## multi_vp_routing',
228
192
  sessionAnnouncementHeader: '[会话公告]',
229
193
  projectInstructionHeader: '[Project 指令]',
@@ -275,28 +239,22 @@ export function normalizePromptLanguage(language) {
275
239
  *
276
240
  * Prompt structure (DESIGN-PROMPT §3):
277
241
  * ① Identity — Core identity (persona or Yeaft fallback)
278
- * ② Rules — Session announcement, date, mode, tools, guidance, skills
242
+ * ② Rules — Session/Project instructions, date, runtime, and skills
279
243
  * ③ Memory — Single block produced by the AMS render outlet
280
244
  * (callers pass it as `memoryInjection`).
281
- * ④ Active Scope — Structured per-turn scope summary
282
- * (session / vp / members / envelope IDs).
283
- * (The previous standalone user_profile / core_memory blocks are
284
- * gone — those signals now arrive through AMS Resident. Task
285
- * context (`taskCtx`) was wired into Active Scope by task-334e
286
- * but never actually populated by the engine; removed 2026-05-13.)
245
+ * ④ Routing — Multi-VP identity and handoff metadata when applicable
246
+ *
247
+ * Generic Session IDs, inferred topics, active-task labels, and repeated tool
248
+ * guidance are intentionally omitted from the system prompt.
287
249
  *
288
- * Active Scope params (DESIGN-PROMPT §3 ④):
289
- * @param {object} [activeScope] — structured scope summary for this turn
290
- * @param {string} [activeScope.sessionId]
291
- * @param {string} [activeScope.sessionMember]
292
- * @param {string[]} [activeScope.sessionMembers] current session roster
293
- * @param {string[]} [activeScope.sessionTopics] bounded topic labels for this session
294
- * @param {object} [activeScope.envelope] inbound routing info (sender, intent)
250
+ * Multi-VP routing params:
251
+ * @param {object} [activeScope] — routing scope for this turn
252
+ * @param {string} [activeScope.vpId] current VP identity
253
+ * @param {string[]} [activeScope.sessionMembers] current Session roster
295
254
  *
296
255
  * @param {{
297
256
  * language?: string,
298
257
  * mode?: string,
299
- * toolNames?: string[],
300
258
  * memoryInjection?: string,
301
259
  * skillContent?: string,
302
260
  * activeScope?: object,
@@ -313,7 +271,6 @@ export function normalizePromptLanguage(language) {
313
271
  export function buildSystemPrompt({
314
272
  language = 'en',
315
273
  mode,
316
- toolNames = [],
317
274
  memoryInjection,
318
275
  skillContent,
319
276
  activeScope,
@@ -324,7 +281,6 @@ export function buildSystemPrompt({
324
281
  workCenterInstructions = '',
325
282
  projectDoc = '',
326
283
  runtimePlatform,
327
- activeTasks = '',
328
284
  promptNotices = [],
329
285
  } = {}) {
330
286
  // Normalize app locales like `zh-CN` to prompt dictionary/template keys.
@@ -405,20 +361,15 @@ export function buildSystemPrompt({
405
361
  parts.push(dreamTemplate || lang.dream);
406
362
  }
407
363
 
408
- // ─── 4. Runtime Platform + Tools + Tool Guidance ──────
364
+ // ─── 4. Runtime Platform ─────────────────────────────
365
+ // Tool schemas already carry their operational guidance. Repeating selected
366
+ // tool instructions in the system prompt wastes tokens and can drift from
367
+ // the executable schema, so only platform facts are rendered here.
409
368
  const runtimePlatformBlock = renderRuntimePlatformPrompt(runtimePlatform || getRuntimePlatformInfo(), effectiveLang);
410
369
  if (runtimePlatformBlock) {
411
370
  parts.push(runtimePlatformBlock);
412
371
  }
413
372
 
414
- if (toolNames.length > 0) {
415
- const guidance = renderActiveToolGuidance(toolNames, effectiveLang);
416
- const toolGuidanceTemplate = getTemplate('toolGuidance', effectiveLang);
417
- if (guidance && toolGuidanceTemplate) {
418
- parts.push(toolGuidanceTemplate.replace('{{guidance}}', guidance));
419
- }
420
- }
421
-
422
373
  // ─── 5. Skills Section ─────────────────────────────────
423
374
  if (skillContent) {
424
375
  parts.push(skillContent);
@@ -436,16 +387,10 @@ export function buildSystemPrompt({
436
387
  parts.push(memoryInjection.trim());
437
388
  }
438
389
 
439
- // ─── 7. Active Scope (DESIGN-PROMPT §3 ④) ──────────────
440
- // Structured per-turn scope summary. The group/vp/envelope identifiers
441
- // are rendered as a leading line. (Per-task taskCtx sub-block was
442
- // never wired and is removed 2026-05-13.)
443
- const activeScopeBlock = renderActiveScope(activeScope, lang);
444
- if (activeScopeBlock) parts.push(activeScopeBlock);
445
-
446
- const activeTaskText = typeof activeTasks === 'string' ? activeTasks.trim() : '';
447
- if (activeTaskText) parts.push(activeTaskText);
448
-
390
+ // ─── 7. Multi-VP routing ────────────────────────────────
391
+ // Generic Session IDs, inferred topics, and live task labels are runtime
392
+ // bookkeeping rather than model instructions. Keep only the routing block
393
+ // whose peer identities and RouteForward contract affect model behaviour.
449
394
  const multiVpRoutingBlock = renderMultiVpRouting(activeScope, lang);
450
395
  if (multiVpRoutingBlock) parts.push(multiVpRoutingBlock);
451
396
 
@@ -454,64 +399,6 @@ export function buildSystemPrompt({
454
399
 
455
400
  // ─── helpers ─────────────────────────────────────────────────────
456
401
 
457
- const TOOL_GUIDANCE_GROUPS = Object.freeze([
458
- {
459
- tools: ['DiscoverTools'],
460
- en: 'If the visible tools do not clearly cover the request, use `DiscoverTools` with the user goal before concluding that a capability is unavailable. If the target is absent from a page, follow `next_cursor` until found or the hidden directory is exhausted. If `restart_required` is true, restart without a cursor because the registered directory changed.',
461
- zh: '如果可见工具不能明确覆盖请求,应先按用户目标调用 `DiscoverTools`。若当前页没有目标,应按 `next_cursor` 继续翻页,直到找到或隐藏目录耗尽后,才能判断某项能力不可用。如果 `restart_required` 为 true,说明注册目录已变化,应丢弃游标重新开始。',
462
- },
463
- {
464
- tools: ['FileRead', 'FileWrite', 'FileEdit', 'Glob', 'Grep', 'ListDir', 'ApplyPatch', 'NotebookEdit'],
465
- en: 'Read existing files before editing. Use dedicated file/search tools instead of shell search or `sed -i`; make small, reviewable edits. Parallelize only proven-independent reads under the accuracy-first rule above.',
466
- zh: '编辑前先读现有文件。文件搜索和修改优先使用专用工具,不用 shell 搜索或 `sed -i`;改动保持小而可审查。只有明确满足上述准确性优先判据的读取才可并行。',
467
- },
468
- {
469
- tools: ['Bash'],
470
- en: 'Use non-interactive, deterministic shell commands, set reasonable timeouts, quote paths with spaces, and do not run destructive operations without authorization.',
471
- zh: 'Shell 命令保持非交互、确定性并设置合理 timeout;包含空格的路径要引用,未经授权不要执行破坏性操作。',
472
- },
473
- {
474
- tools: ['TodoWrite'],
475
- en: 'For non-trivial multi-step work, write a brief visible plan and call `TodoWrite` in the same assistant response as the first necessary work-tool call only when its arguments and safety do not depend on another result. Start with the smallest such call; do not speculative-batch the investigation or stop after planning unless user input genuinely blocks the first step.',
476
- zh: '非平凡多步骤任务先写简短可见计划。只有第一个工作工具调用已经确定有必要,且其参数和安全性都不依赖其他结果时,才在同一个 assistant response 中把它与 `TodoWrite` 一起发出;先执行满足条件的最小调用,不要推测性批量展开调查。只有用户信息确实阻塞第一步时才在规划后停下。',
477
- },
478
- {
479
- tools: ['SpawnAgent', 'PromptAgent', 'WaitAgent', 'CloseAgent', 'ListAgents'],
480
- en: 'Delegate only independent, bounded work. Keep ownership in the parent. After PromptAgent queues follow-up work, call WaitAgent in the same parent turn and collect the reply before ending. If a bounded wait times out, wait again with a larger bound unless the agent is stale/stalled. Relay the reply or continue the dependent work, then close the sub-agent when it is no longer needed.',
481
- zh: '只委派边界清晰且独立的工作。父级保留任务所有权。PromptAgent 排队后续工作后,必须在同一个父级 turn 调用 WaitAgent 并拿到回复再结束;有界等待超时后,除非 Agent 已 stale/stalled,否则使用更大上限继续等待;随后转述结果或继续依赖该结果的工作,不再需要时关闭子 Agent。',
482
- },
483
- {
484
- tools: ['ListTasks', 'ReadTaskLog', 'CancelTask'],
485
- en: 'Treat background tasks as live execution state, not memory facts. Inspect status or logs before retrying or cancelling work.',
486
- zh: '后台任务是实时执行状态,不是记忆事实。重试或取消前先检查状态或日志。',
487
- },
488
- {
489
- tools: ['RouteForward'],
490
- en: 'Use `RouteForward` for explicit VP-to-VP handoff; writing an @mention in ordinary text does not dispatch another VP.',
491
- zh: '显式 VP 转交必须使用 `RouteForward`;普通文本中的 @mention 不会调度另一个 VP。',
492
- },
493
- {
494
- tools: ['CreateWorkItem'],
495
- en: 'Use `CreateWorkItem` only for goals that need durable cross-turn coordination, recovery, review, waiting, or retry.',
496
- zh: '只有目标需要跨 turn 持久协调、恢复、评审、等待或重试时才使用 `CreateWorkItem`。',
497
- },
498
- ]);
499
-
500
- function renderActiveToolGuidance(toolNames, language) {
501
- const active = new Set(Array.isArray(toolNames) ? toolNames : []);
502
- const lines = [];
503
- if (active.size > 0) {
504
- lines.push(language === 'zh'
505
- ? '- 准确性优先:先用能解决当前未知的最小定向调用。只有每个调用都已经确定有必要,且其参数和安全性都不依赖同批其他结果时,才在一个响应中发出多个工具调用;否则串行执行。不要推测性扇出、重复成功的读取/搜索,也不要默认抓取多个来源;先检查证据,再决定是否扩展。'
506
- : '- Accuracy first: start with the smallest targeted call that can resolve the current uncertainty. Issue multiple tool calls in one response only when every call is already necessary and its arguments and safety do not depend on another call\'s result. Otherwise run them sequentially. Do not fan out speculatively, repeat a successful read/search, or fetch multiple sources by default; inspect evidence before expanding.');
507
- }
508
- for (const group of TOOL_GUIDANCE_GROUPS) {
509
- if (!group.tools.some(name => active.has(name))) continue;
510
- lines.push(`- ${language === 'zh' ? group.zh : group.en}`);
511
- }
512
- return lines.join('\n');
513
- }
514
-
515
402
  /**
516
403
  * Render the VP identity block when the engine is running on behalf of an
517
404
  * addressed VP. The `persona` body from role.md is the only soul source;
@@ -570,32 +457,6 @@ function selectVpPersonaBody(vpPersona, effectiveLang) {
570
457
 
571
458
 
572
459
 
573
- function renderActiveScope(activeScope, lang) {
574
- if (!activeScope || typeof activeScope !== 'object') return '';
575
-
576
- const isZh = lang === PROMPTS.zh;
577
- const separator = isZh ? ':' : ': ';
578
- const lines = [];
579
- const session = typeof activeScope.sessionId === 'string' && activeScope.sessionId.trim()
580
- ? activeScope.sessionId.trim()
581
- : '';
582
- if (session) lines.push(`${lang.activeScopeSessionIdLabel}${separator}${session}`);
583
-
584
- const membersLine = renderSessionMembersLine(activeScope.sessionMembers || activeScope.members, isZh);
585
- if (membersLine) lines.push(`${lang.activeScopeMembersLabel}${separator}${membersLine}`);
586
-
587
- const topicsLine = renderSessionTopicsLine(activeScope.sessionTopics, isZh);
588
- if (topicsLine) lines.push(`${lang.activeScopeTopicsLabel}${separator}${topicsLine}`);
589
-
590
- const envLine = renderEnvelopeLine(activeScope.envelope, isZh);
591
- if (envLine) lines.push(`${lang.activeScopeEnvelopeLabel}${separator}${envLine}`);
592
-
593
- if (lines.length === 0) return '';
594
-
595
- return `${lang.activeScopeHeader}\n${lines.join('\n')}`;
596
- }
597
-
598
-
599
460
  function firstNonEmptyString(...values) {
600
461
  for (const value of values) {
601
462
  if (typeof value === 'string' && value.trim()) return value.trim();
@@ -603,151 +464,6 @@ function firstNonEmptyString(...values) {
603
464
  return '';
604
465
  }
605
466
 
606
- function renderSessionMembersLine(members, useChineseSeparator = false) {
607
- return normalizeSessionMemberIds(members).join(useChineseSeparator ? '、' : ', ');
608
- }
609
-
610
- function renderSessionTopicsLine(topics, isZh = false) {
611
- const descriptions = [];
612
- const seen = new Set();
613
- for (const topic of normalizeSessionTopicIds(topics)) {
614
- const description = describeSessionTopic(topic, isZh);
615
- if (!description || seen.has(description)) continue;
616
- seen.add(description);
617
- descriptions.push(description);
618
- }
619
- return descriptions.join(isZh ? ';' : '; ');
620
- }
621
-
622
- function normalizeSessionTopicIds(topics) {
623
- if (!Array.isArray(topics)) return [];
624
- const clean = [];
625
- const seen = new Set();
626
- for (const topic of topics) {
627
- if (typeof topic !== 'string') continue;
628
- const id = topic.trim();
629
- if (!id || seen.has(id)) continue;
630
- seen.add(id);
631
- clean.push(id);
632
- }
633
- return clean;
634
- }
635
-
636
- function describeSessionTopic(topic, isZh = false) {
637
- const normalized = topic.toLowerCase().replace(/[_\s]+/g, '-');
638
-
639
- if (normalized.includes('dream') && normalized.includes('segments')) {
640
- return isZh
641
- ? '梦境记忆片段的抽取与整理'
642
- : 'Dream memory segment extraction and organization';
643
- }
644
- if (normalized.includes('dream') && normalized.includes('session') && normalized.includes('extraction')) {
645
- return isZh
646
- ? '梦境会话记忆的抽取质量'
647
- : 'Dream session-memory extraction quality';
648
- }
649
- if (normalized.includes('system-prompt') && normalized.includes('localization')) {
650
- return isZh
651
- ? '系统提示词的中英文一致性'
652
- : 'system prompt language localization';
653
- }
654
- if (normalized.includes('default-character') && normalized.includes('prompt-soul')) {
655
- return isZh
656
- ? '默认角色灵魂提示词的表达方式'
657
- : 'default persona soul prompt wording';
658
- }
659
- if (normalized.includes('prompt') && normalized.includes('soul')) {
660
- return isZh
661
- ? '角色灵魂提示词的表达方式'
662
- : 'persona soul prompt wording';
663
- }
664
- if (normalized.includes('openai-responses')) {
665
- return isZh
666
- ? 'Yeaft 的 OpenAI Responses 适配与模型配置'
667
- : 'Yeaft OpenAI Responses adapter and model configuration';
668
- }
669
- if (normalized.includes('model-config-isolation')) {
670
- return isZh
671
- ? 'Yeaft 的模型配置隔离'
672
- : 'Yeaft model configuration isolation';
673
- }
674
- if (normalized.includes('route-forward') || normalized.includes('handoff')) {
675
- return isZh
676
- ? 'Yeaft 的会话路由交接与可见性'
677
- : 'Yeaft session routing handoff and visibility';
678
- }
679
- if (normalized.includes('copilot-cli') || normalized.includes('chat-session')) {
680
- return isZh
681
- ? 'Copilot CLI 的聊天会话行为'
682
- : 'Copilot CLI chat session behavior';
683
- }
684
- if (normalized.includes('claude-opus')) {
685
- return isZh
686
- ? 'Yeaft 的 Claude Opus 模型接入'
687
- : 'Yeaft Claude Opus model integration';
688
- }
689
- if (normalized.includes('pr-workflow')) {
690
- return isZh
691
- ? '项目的 PR review、merge 和 tag 发布流程'
692
- : 'project PR review, merge, and tag release workflow';
693
- }
694
- if (/\bpr[-/]?\d+\b/.test(normalized) || normalized.includes('release') || /v\d+\.\d+\.\d+/.test(normalized)) {
695
- return isZh
696
- ? '最近的 PR 修复、review、merge 和 tag 发布流程'
697
- : 'recent PR fixes, review, merge, and tag release work';
698
- }
699
- if (normalized.includes('active-scope')) {
700
- return isZh
701
- ? '当前会话上下文的提示词呈现'
702
- : 'current session context prompt rendering';
703
- }
704
- if (normalized.includes('prompt') || normalized.includes('system')) {
705
- return isZh
706
- ? '近期的系统提示词调整'
707
- : humanizeTopicSlug(topic, false) || 'recent system prompt work';
708
- }
709
- if (normalized.includes('dream')) {
710
- return isZh
711
- ? '近期的 Dream 记忆维护工作'
712
- : humanizeTopicSlug(topic, false) || 'recent Dream memory work';
713
- }
714
- if (normalized.includes('session')) {
715
- return isZh
716
- ? '近期的会话上下文调整'
717
- : humanizeTopicSlug(topic, false) || 'recent session context work';
718
- }
719
-
720
- if (isZh) return '近期的项目协作事项';
721
- return humanizeTopicSlug(topic, false) || 'recent session collaboration topics';
722
- }
723
-
724
- function humanizeTopicSlug(topic, isZh = false) {
725
- if (isZh) return '';
726
- const words = topic
727
- .replace(/[\/_-]+/g, ' ')
728
- .replace(/\bv\d+(?:\.\d+)+\b/gi, '')
729
- .replace(/\bpr\s*\d+\b/gi, '')
730
- .trim()
731
- .split(/\s+/)
732
- .filter(Boolean);
733
- if (words.length === 0) return '';
734
-
735
- const normalizedWords = words.map(word => {
736
- const lower = word.toLowerCase();
737
- if (lower === 'yeaft') return 'Yeaft';
738
- if (lower === 'project') return 'project';
739
- if (lower === 'config') return 'configuration';
740
- if (lower === 'isolation') return 'isolation';
741
- if (lower === 'rendering') return 'rendering';
742
- if (lower === 'workflow') return 'workflow';
743
- if (lower === 'session') return 'session';
744
- if (lower === 'context') return 'context';
745
- return word;
746
- });
747
-
748
- return normalizedWords.join(' ');
749
- }
750
-
751
467
  function normalizeSessionMemberIds(members) {
752
468
  if (!Array.isArray(members)) return [];
753
469
  const clean = [];
@@ -764,7 +480,7 @@ function normalizeSessionMemberIds(members) {
764
480
 
765
481
  function renderMultiVpRouting(activeScope, lang) {
766
482
  if (!activeScope || typeof activeScope !== 'object') return '';
767
- const ownId = firstNonEmptyString(activeScope.sessionMember, activeScope.vpId);
483
+ const ownId = firstNonEmptyString(activeScope.vpId);
768
484
  const members = normalizeSessionMemberIds(activeScope.sessionMembers || activeScope.members);
769
485
  const peers = ownId ? members.filter((member) => member !== ownId) : members;
770
486
  if (peers.length === 0) return '';
@@ -793,33 +509,6 @@ function renderMultiVpRouting(activeScope, lang) {
793
509
  ].join('\n');
794
510
  }
795
511
 
796
- /**
797
- * Render a one-line envelope summary. Pulls the small set of routing
798
- * fields we surface to the LLM (sender, intent, originating user) and
799
- * leaves the rest in AMS. Returns '' when the envelope carries no
800
- * useful signal.
801
- *
802
- * @param {object|null|undefined} envelope
803
- * @returns {string}
804
- */
805
- function renderEnvelopeLine(envelope, isZh = false) {
806
- if (!envelope || typeof envelope !== 'object') return '';
807
- const segments = [];
808
- const fromVp = typeof envelope.fromVpId === 'string' && envelope.fromVpId.trim()
809
- ? envelope.fromVpId.trim()
810
- : (typeof envelope.senderVpId === 'string' ? envelope.senderVpId.trim() : '');
811
- if (fromVp) segments.push(`${isZh ? '来自' : 'from'}=${fromVp}`);
812
- const fromUser = typeof envelope.fromUserId === 'string' && envelope.fromUserId.trim()
813
- ? envelope.fromUserId.trim()
814
- : '';
815
- if (fromUser) segments.push(`${isZh ? '用户' : 'user'}=${fromUser}`);
816
- const intent = typeof envelope.intent === 'string' && envelope.intent.trim()
817
- ? envelope.intent.trim()
818
- : '';
819
- if (intent) segments.push(`${isZh ? '意图' : 'intent'}=${intent}`);
820
- return segments.join(' ');
821
- }
822
-
823
512
  // ─── Phase 1: Worker / Router prompt splits ──────────────────────
824
513
  //
825
514
  // DESIGN.md (multi-VP redesign) describes two distinct prompt shapes:
@@ -830,7 +519,7 @@ function renderEnvelopeLine(envelope, isZh = false) {
830
519
  // carries Layer-A summaries + UserProfile + CoreMemory, AMS OnDemand
831
520
  // carries the per-turn FTS hits. The worker shape that survives is:
832
521
  // harness/worker-shape — optional descriptive metadata
833
- // buildSystemPrompt(...) — ① Identity ② Rules ③ Memory ④ Active Scope
522
+ // buildSystemPrompt(...) — ① Identity ② Rules ③ Memory ④ Routing
834
523
  // optional taskScope/turnScope — caller-provided pass-through strings
835
524
  // `renderLayerASummaries` is no longer called inside the worker prompt
836
525
  // because AMS already renders the same summaries — calling both was
@@ -891,15 +580,12 @@ export function renderLayerASummaries(summaries, language = 'en') {
891
580
  *
892
581
  * Output sections (DESIGN-PROMPT §3 layered concepts):
893
582
  * harness/worker-shape (optional) — descriptive metadata
894
- * buildSystemPrompt(...) — ① Identity ② Rules ③ Memory ④ Active Scope
583
+ * buildSystemPrompt(...) — ① Identity ② Rules ③ Memory ④ Routing
895
584
  *
896
- * Earlier task-322 / task-334e variants accepted `taskScope` and
897
- * `turnScope` pass-through strings so callers could append their own
898
- * scope blocks. DESIGN-PROMPT v1 retired that surface — Active Scope is
899
- * now structured (`activeScope: { sessionId, vpId, envelope }`) and
900
- * rendered by `buildSystemPrompt` itself. Both pass-through params
901
- * had zero remaining callers when v1 landed; removing them prevents the
902
- * "two ways to describe scope" drift §1 set out to eliminate.
585
+ * Earlier variants accepted `taskScope` and `turnScope` pass-through
586
+ * strings so callers could append their own scope blocks. That surface is
587
+ * retired. `activeScope` now carries only the VP identity and Session roster
588
+ * needed to render the deterministic multi-VP routing contract.
903
589
  *
904
590
  * @param {{
905
591
  * language?: 'en'|'zh',
@@ -924,7 +610,7 @@ export function buildWorkerPrompt(params = {}) {
924
610
  if (shape) parts.push(shape);
925
611
  }
926
612
 
927
- // Identity + Rules + Memory + Active Scope (DESIGN-PROMPT §3).
613
+ // Identity + Rules + Memory + Routing (DESIGN-PROMPT §3).
928
614
  const baseBlock = buildSystemPrompt({ ...rest, language: effectiveLang });
929
615
  if (baseBlock) parts.push(baseBlock);
930
616
 
@@ -20,7 +20,6 @@ import { getRuntimePlatformInfo } from '../runtime-platform.js';
20
20
  const LOG_PREVIEW_BYTES = 4096;
21
21
  const SUB_AGENT_LOG_PREVIEW_BYTES = 1024 * 1024;
22
22
  const DEFAULT_CANCEL_ESCALATION_MS = 2000;
23
- const PROMPT_TASK_LIMIT = 5;
24
23
 
25
24
  function logPreviewBytesFor(task) {
26
25
  return task?.kind === 'sub_agent' ? SUB_AGENT_LOG_PREVIEW_BYTES : LOG_PREVIEW_BYTES;
@@ -55,37 +54,6 @@ function publicSnapshot(task) {
55
54
  };
56
55
  }
57
56
 
58
- function taskKindLabel(kind, language) {
59
- const zh = String(language || '').toLowerCase().startsWith('zh');
60
- if (kind === 'sub_agent') return zh ? '子 Agent' : 'sub-agent';
61
- if (kind === 'shell') return zh ? '后台命令' : 'background command';
62
- return zh ? '后台任务' : 'background task';
63
- }
64
-
65
- function safeTaskName(task) {
66
- const name = typeof task?.runtime?.name === 'string' ? task.runtime.name.trim() : '';
67
- return /^[\p{L}\p{N}][\p{L}\p{N}._-]{0,63}$/u.test(name) ? name : '';
68
- }
69
-
70
- function promptTaskLabel(task, language) {
71
- const name = task?.kind === 'sub_agent' ? safeTaskName(task) : '';
72
- if (!name) return taskKindLabel(task?.kind, language);
73
- return String(language || '').toLowerCase().startsWith('zh')
74
- ? `子 Agent ${name}`
75
- : `sub-agent ${name}`;
76
- }
77
-
78
- function taskStatusLabel(status, language) {
79
- const zh = String(language || '').toLowerCase().startsWith('zh');
80
- if (!zh) return String(status || 'running').replace(/_/g, ' ');
81
- const labels = {
82
- running: '运行中',
83
- queued: '等待中',
84
- cancelling: '正在取消',
85
- };
86
- return labels[status] || String(status || '运行中').replace(/_/g, ' ');
87
- }
88
-
89
57
  export class TaskManager {
90
58
  constructor({ yeaftDir, onEvent = null, runtimePlatform = null, cancelEscalationMs = DEFAULT_CANCEL_ESCALATION_MS } = {}) {
91
59
  if (!yeaftDir) throw new Error('TaskManager requires yeaftDir');
@@ -384,28 +352,4 @@ export class TaskManager {
384
352
  this.#emit('updated', task);
385
353
  return publicSnapshot(task);
386
354
  }
387
-
388
- renderActiveTasksForPrompt(sessionId = null, { language = 'en', limit = PROMPT_TASK_LIMIT } = {}) {
389
- const tasks = this.listActiveTasks(sessionId);
390
- if (tasks.length === 0) return '';
391
- const zh = String(language || '').toLowerCase().startsWith('zh');
392
- const maxTasks = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : PROMPT_TASK_LIMIT;
393
- const visible = tasks.slice(0, maxTasks);
394
- const lines = [zh ? '## 可能相关的任务' : '## Possibly Relevant Tasks'];
395
- lines.push(zh
396
- ? '以下任务仍在后台运行。需要进度或完整输出时使用任务工具查询;不要把它们当成记忆事实。'
397
- : 'These tasks are still running in the background. Use the task tools for progress or full output; do not treat them as memory facts.');
398
- for (const task of visible) {
399
- const title = promptTaskLabel(task, language);
400
- const detail = zh
401
- ? `${taskKindLabel(task.kind, language)},${taskStatusLabel(task.status, language)}`
402
- : `${taskKindLabel(task.kind, language)}, ${taskStatusLabel(task.status, language)}`;
403
- lines.push(`- ${title} (${detail})`);
404
- }
405
- if (tasks.length > visible.length) {
406
- const remaining = tasks.length - visible.length;
407
- lines.push(zh ? `- 另有 ${remaining} 个运行中任务,可用任务列表查看。` : `- ${remaining} more running task${remaining === 1 ? '' : 's'}; use the task list to inspect them.`);
408
- }
409
- return lines.join('\n');
410
- }
411
355
  }
@@ -2,10 +2,10 @@
2
2
 
3
3
  ## Compatibility Note
4
4
 
5
- Runtime prompt assembly now uses `core.md` plus active-tool guidance. This file remains packaged for deployments or integrations that still read the historical fragment directly.
5
+ Runtime prompt assembly now uses `core.md`; tool behavior is carried by provider tool schemas. This file remains packaged for deployments or integrations that still read the historical fragment directly.
6
6
 
7
7
  <!-- lang:zh -->
8
8
 
9
9
  ## 兼容说明
10
10
 
11
- 运行时 Prompt 现在由 `core.md` 和当前活跃工具指引组成。此文件继续随包发布,只用于仍直接读取旧 fragment 的部署或集成兼容。
11
+ 运行时 Prompt 现在使用 `core.md`;工具行为由 provider tool schema 承载。此文件继续随包发布,只用于仍直接读取旧 fragment 的部署或集成兼容。
@@ -20,7 +20,6 @@ export const ALWAYS_VISIBLE_TOOL_NAMES = Object.freeze([
20
20
  'Glob',
21
21
  'Grep',
22
22
  'ListDir',
23
- 'TodoWrite',
24
23
  'ViewImage',
25
24
  ]);
26
25
 
@@ -53,11 +52,6 @@ export const CONDITIONAL_BUILTIN_TOOL_NAMES = new Set([
53
52
  'ListAgents',
54
53
  'RouteForward',
55
54
  'CreateWorkItem',
56
- // Legacy compatibility only. New planning is a single provider response:
57
- // visible prose + TodoWrite + the first justified work-tool call. Keeping the
58
- // definition registered lets old direct callers resolve it without paying a
59
- // dedicated StartPlan -> provider -> TodoWrite round trip on every new task.
60
- 'StartPlan',
61
55
  'JsRepl',
62
56
  'NotebookEdit',
63
57
  'ImageGeneration',
@@ -27,11 +27,10 @@ const BINARY_EXTS = new Set([
27
27
  /** Max file size to read (10 MB). */
28
28
  const MAX_FILE_SIZE = 10 * 1024 * 1024;
29
29
 
30
- /** Default number of lines to read. Aligned with the "large file = >3000
31
- * lines" rule in templates/{base,common-rules,tool-guidance}.md so a
32
- * ≤3000-line file is returned in one call (no silent truncation that
33
- * would trigger a follow-up `offset:3000` call — that's exactly the
34
- * round-trip this tool's prompt guidance promises to avoid). */
30
+ /** Default number of lines to read. Aligned with the FileRead tool schema's
31
+ * "large file = >3000 lines" rule so a ≤3000-line file is returned in one
32
+ * call (no silent truncation that would trigger a follow-up `offset:3000`
33
+ * call — exactly the round-trip the tool guidance avoids). */
35
34
  const DEFAULT_LIMIT = 3000;
36
35
 
37
36
  /** Keep raw output close to the model-facing 32 KiB budget while leaving