@yeaft/webchat-agent 0.1.854 → 0.1.857

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 (174) hide show
  1. package/bin/yeaft-stats.js +3 -3
  2. package/connection/message-router.js +73 -44
  3. package/context.js +4 -4
  4. package/expert-roles.js +1 -1
  5. package/index.js +1 -1
  6. package/package.json +4 -4
  7. package/service/index.js +1 -1
  8. package/{unify → yeaft}/attachments.js +7 -7
  9. package/{unify → yeaft}/cli.js +4 -4
  10. package/{unify → yeaft}/compact/compactor.js +10 -10
  11. package/{unify → yeaft}/compact/orchestrator.js +7 -13
  12. package/{unify → yeaft}/config-api.js +12 -12
  13. package/{unify → yeaft}/config.js +19 -19
  14. package/{unify → yeaft}/conversation/migrate-messages-threadid.js +1 -1
  15. package/{unify → yeaft}/conversation/persist.js +194 -65
  16. package/{unify → yeaft}/conversation/search.js +28 -23
  17. package/{unify → yeaft}/debug-trace.js +1 -1
  18. package/{unify → yeaft}/dream-v2/apply.js +28 -11
  19. package/{unify → yeaft}/dream-v2/limits.js +3 -3
  20. package/{unify → yeaft}/dream-v2/prompts/index.js +8 -1
  21. package/{unify → yeaft}/dream-v2/runner.js +13 -6
  22. package/{unify → yeaft}/dream-v2/session-wiring.js +2 -2
  23. package/{unify → yeaft}/dream-v2/triage.js +13 -8
  24. package/{unify → yeaft}/effort.js +2 -2
  25. package/{unify → yeaft}/engine.js +10 -10
  26. package/{unify → yeaft}/eval/run-eval.js +11 -11
  27. package/{unify → yeaft}/eval/runner.js +1 -1
  28. package/{unify → yeaft}/groups/feature-flag.js +2 -2
  29. package/{unify → yeaft}/groups/index.js +2 -2
  30. package/{unify → yeaft}/groups/pre-flow.js +15 -3
  31. package/{unify → yeaft}/groups/project-doc.js +1 -1
  32. package/{unify → yeaft}/history-compact.js +3 -3
  33. package/{unify → yeaft}/index.js +1 -1
  34. package/{unify → yeaft}/init.js +15 -9
  35. package/{unify → yeaft}/llm/adapter.js +1 -1
  36. package/{unify → yeaft}/llm/anthropic.js +2 -2
  37. package/{unify → yeaft}/llm/models-dev.js +10 -3
  38. package/{unify → yeaft}/llm/openai-responses.js +2 -2
  39. package/{unify → yeaft}/llm/router.js +1 -1
  40. package/{unify → yeaft}/mcp.js +1 -1
  41. package/{unify → yeaft}/memory/adjust.js +2 -4
  42. package/{unify → yeaft}/memory/ams-registry.js +1 -1
  43. package/{unify → yeaft}/memory/ams.js +2 -4
  44. package/{unify → yeaft}/memory/preflow.js +2 -6
  45. package/{unify → yeaft}/memory/seed-backfill.js +42 -2
  46. package/{unify → yeaft}/memory/segment.js +1 -1
  47. package/{unify → yeaft}/memory/store-v2.js +120 -73
  48. package/{unify → yeaft}/models.js +3 -3
  49. package/{unify → yeaft}/pair-sanitize.js +2 -2
  50. package/{unify → yeaft}/personas.js +1 -1
  51. package/{unify → yeaft}/prompts.js +1 -1
  52. package/{unify → yeaft}/routing/index.js +1 -1
  53. package/{unify → yeaft}/routing/router.js +1 -1
  54. package/{unify → yeaft}/session.js +19 -9
  55. package/{unify → yeaft}/skills.js +2 -2
  56. package/{unify → yeaft}/stats/format.js +1 -1
  57. package/{unify → yeaft}/stop-hooks.js +1 -1
  58. package/{unify → yeaft}/sub-agent/runner.js +1 -1
  59. package/{unify → yeaft}/tools/ask-user.js +2 -2
  60. package/{unify → yeaft}/tools/enter-worktree.js +1 -1
  61. package/{unify → yeaft}/tools/exit-worktree.js +1 -1
  62. package/{unify → yeaft}/tools/mcp-tools.js +1 -1
  63. package/{unify → yeaft}/tools/registry.js +2 -2
  64. package/{unify → yeaft}/tools/skill.js +1 -1
  65. package/{unify → yeaft}/tools/start-plan.js +1 -1
  66. package/{unify → yeaft}/tools/types.js +3 -3
  67. package/{unify → yeaft}/tools/web-search.js +1 -1
  68. package/{unify → yeaft}/turn-utils.js +1 -1
  69. package/{unify → yeaft}/vp/index.js +1 -1
  70. package/{unify → yeaft}/vp/registry.js +1 -1
  71. package/{unify → yeaft}/vp/vp-bridge.js +8 -8
  72. package/{unify → yeaft}/vp/vp-crud.js +7 -24
  73. package/{unify → yeaft}/web-bridge.js +230 -230
  74. /package/{unify → yeaft}/archive/tool-results.js +0 -0
  75. /package/{unify → yeaft}/archive/trace.js +0 -0
  76. /package/{unify → yeaft}/archive/turn-archive.js +0 -0
  77. /package/{unify → yeaft}/compact/decisions-log.js +0 -0
  78. /package/{unify → yeaft}/compact/triggers.js +0 -0
  79. /package/{unify → yeaft}/compact/turn-group.js +0 -0
  80. /package/{unify → yeaft}/dream-v2/merge.js +0 -0
  81. /package/{unify → yeaft}/dream-v2/prompts/create.md +0 -0
  82. /package/{unify → yeaft}/dream-v2/prompts/extract-group.md +0 -0
  83. /package/{unify → yeaft}/dream-v2/prompts/extract-topic.md +0 -0
  84. /package/{unify → yeaft}/dream-v2/prompts/extract-user.md +0 -0
  85. /package/{unify → yeaft}/dream-v2/prompts/extract-vp.md +0 -0
  86. /package/{unify → yeaft}/dream-v2/prompts/summarize-scope.md +0 -0
  87. /package/{unify → yeaft}/dream-v2/prompts/triage-pass1.md +0 -0
  88. /package/{unify → yeaft}/dream-v2/prompts/triage-pass2.md +0 -0
  89. /package/{unify → yeaft}/dream-v2/prompts/update.md +0 -0
  90. /package/{unify → yeaft}/dream-v2/schedule.js +0 -0
  91. /package/{unify → yeaft}/dream-v2/segment.js +0 -0
  92. /package/{unify → yeaft}/dream-v2/snapshot.js +0 -0
  93. /package/{unify → yeaft}/dream-v2/state.js +0 -0
  94. /package/{unify → yeaft}/eval/cases/e2e.js +0 -0
  95. /package/{unify → yeaft}/eval/cases/memory.js +0 -0
  96. /package/{unify → yeaft}/eval/cases/skills.js +0 -0
  97. /package/{unify → yeaft}/eval/cases/tool-use.js +0 -0
  98. /package/{unify → yeaft}/groups/coordinator.js +0 -0
  99. /package/{unify → yeaft}/groups/group-config.js +0 -0
  100. /package/{unify → yeaft}/groups/group-crud.js +0 -0
  101. /package/{unify → yeaft}/groups/group-store.js +0 -0
  102. /package/{unify → yeaft}/groups/ids.js +0 -0
  103. /package/{unify → yeaft}/groups/roster.js +0 -0
  104. /package/{unify → yeaft}/groups/seed-default.js +0 -0
  105. /package/{unify → yeaft}/llm/credentials/github-copilot.js +0 -0
  106. /package/{unify → yeaft}/llm/credentials/index.js +0 -0
  107. /package/{unify → yeaft}/memory/budget.js +0 -0
  108. /package/{unify → yeaft}/memory/consolidate.js +0 -0
  109. /package/{unify → yeaft}/memory/index-db.js +0 -0
  110. /package/{unify → yeaft}/memory/keywords.js +0 -0
  111. /package/{unify → yeaft}/memory/segment-store.js +0 -0
  112. /package/{unify → yeaft}/memory/segment-sync.js +0 -0
  113. /package/{unify → yeaft}/memory/summary-store.js +0 -0
  114. /package/{unify → yeaft}/router/continuity.js +0 -0
  115. /package/{unify → yeaft}/router/thinking.js +0 -0
  116. /package/{unify → yeaft}/routing/loop-guard.js +0 -0
  117. /package/{unify → yeaft}/stats/tool-usage.js +0 -0
  118. /package/{unify → yeaft}/storage/atomic.js +0 -0
  119. /package/{unify → yeaft}/storage/compact.js +0 -0
  120. /package/{unify → yeaft}/storage/index.js +0 -0
  121. /package/{unify → yeaft}/storage/jsonl-index.js +0 -0
  122. /package/{unify → yeaft}/storage/jsonl-log.js +0 -0
  123. /package/{unify → yeaft}/storage/shard-index.js +0 -0
  124. /package/{unify → yeaft}/storage/shard-store.js +0 -0
  125. /package/{unify → yeaft}/sub-agent/spawned-prompt.js +0 -0
  126. /package/{unify → yeaft}/templates/base.md +0 -0
  127. /package/{unify → yeaft}/templates/common-rules.md +0 -0
  128. /package/{unify → yeaft}/templates/harness/router-handoff.md +0 -0
  129. /package/{unify → yeaft}/templates/harness/router-shape.md +0 -0
  130. /package/{unify → yeaft}/templates/harness/worker-shape.md +0 -0
  131. /package/{unify → yeaft}/templates/identity-yeaft.md +0 -0
  132. /package/{unify → yeaft}/templates/mode-dream.md +0 -0
  133. /package/{unify → yeaft}/templates/mode-unified.md +0 -0
  134. /package/{unify → yeaft}/templates/personas/explorer.md +0 -0
  135. /package/{unify → yeaft}/templates/personas/implementer.md +0 -0
  136. /package/{unify → yeaft}/templates/personas/researcher.md +0 -0
  137. /package/{unify → yeaft}/templates/personas/reviewer.md +0 -0
  138. /package/{unify → yeaft}/templates/plan-instruction.md +0 -0
  139. /package/{unify → yeaft}/templates/tool-guidance.md +0 -0
  140. /package/{unify → yeaft}/tool-folding/exec-log.js +0 -0
  141. /package/{unify → yeaft}/tool-folding/fallback-stub.js +0 -0
  142. /package/{unify → yeaft}/tool-folding/index.js +0 -0
  143. /package/{unify → yeaft}/tool-folding/reflection-prompt.js +0 -0
  144. /package/{unify → yeaft}/tool-folding/t1-reflector.js +0 -0
  145. /package/{unify → yeaft}/tool-folding/t2-reflector.js +0 -0
  146. /package/{unify → yeaft}/tools/agent.js +0 -0
  147. /package/{unify → yeaft}/tools/apply-patch.js +0 -0
  148. /package/{unify → yeaft}/tools/bash.js +0 -0
  149. /package/{unify → yeaft}/tools/close-agent.js +0 -0
  150. /package/{unify → yeaft}/tools/file-edit.js +0 -0
  151. /package/{unify → yeaft}/tools/file-read.js +0 -0
  152. /package/{unify → yeaft}/tools/file-write.js +0 -0
  153. /package/{unify → yeaft}/tools/glob.js +0 -0
  154. /package/{unify → yeaft}/tools/grep.js +0 -0
  155. /package/{unify → yeaft}/tools/history-search.js +0 -0
  156. /package/{unify → yeaft}/tools/image-generation.js +0 -0
  157. /package/{unify → yeaft}/tools/index.js +0 -0
  158. /package/{unify → yeaft}/tools/js-repl.js +0 -0
  159. /package/{unify → yeaft}/tools/list-agents.js +0 -0
  160. /package/{unify → yeaft}/tools/list-dir.js +0 -0
  161. /package/{unify → yeaft}/tools/notebook-edit.js +0 -0
  162. /package/{unify → yeaft}/tools/route-forward.js +0 -0
  163. /package/{unify → yeaft}/tools/send-message.js +0 -0
  164. /package/{unify → yeaft}/tools/todo-write.js +0 -0
  165. /package/{unify → yeaft}/tools/view-image.js +0 -0
  166. /package/{unify → yeaft}/tools/wait-agent.js +0 -0
  167. /package/{unify → yeaft}/tools/web-fetch.js +0 -0
  168. /package/{unify → yeaft}/vp/seed-defaults.js +0 -0
  169. /package/{unify → yeaft}/vp/seed-topup.js +0 -0
  170. /package/{unify → yeaft}/vp/stock-ids.js +0 -0
  171. /package/{unify → yeaft}/vp/thread-classifier.js +0 -0
  172. /package/{unify → yeaft}/vp/vp-loader.js +0 -0
  173. /package/{unify → yeaft}/vp/vp-store.js +0 -0
  174. /package/{unify → yeaft}/vp-status-broker.js +0 -0
@@ -17,9 +17,9 @@
17
17
 
18
18
  import { homedir } from 'node:os';
19
19
  import { join } from 'node:path';
20
- import { ToolUsageStats } from '../unify/stats/tool-usage.js';
21
- import { allTools } from '../unify/tools/index.js';
22
- import { formatMs, formatPct, formatLastCalled } from '../unify/stats/format.js';
20
+ import { ToolUsageStats } from '../yeaft/stats/tool-usage.js';
21
+ import { allTools } from '../yeaft/tools/index.js';
22
+ import { formatMs, formatPct, formatLastCalled } from '../yeaft/stats/format.js';
23
23
 
24
24
  function parseArgs(argv) {
25
25
  const opts = {
@@ -35,9 +35,9 @@ import {
35
35
  import { sendToServer, flushMessageBuffer } from './buffer.js';
36
36
  import { handleRestartAgent, handleUpgradeAgent } from './upgrade.js';
37
37
  import { loadMcpServers, updateMcpConfig } from '../mcp.js';
38
- import { getLlmConfig, updateLlmConfig, getUnifySettings, updateUnifySettings, getSearchSettings, updateSearchSettings, fetchTavilyUsage } from '../unify/config-api.js';
39
- import { fetchModelsDev } from '../unify/llm/models-dev.js';
40
- import { handleUnifyGroupChat, handleUnifyModeSwitch, handleUnifyModelSwitch, resetUnifySession, handleUnifyLoadHistory, handleUnifyLoadMoreHistory, handleUnifyAbortThread, handleUnifyAbortAll, handleUnifyAbortTurn, handleUnifyVpSubscribe, handleUnifyVpCreate, handleUnifyVpUpdate, handleUnifyVpDelete, handleUnifyVpRead, handleUnifyListGroups, handleUnifyCreateGroup, handleUnifyRenameGroup, handleUnifyUpdateGroup, handleUnifyUpdateGroupConfig, handleUnifyArchiveGroup, handleUnifyDeleteGroup, handleUnifyAddMember, handleUnifyRemoveMember, handleUnifySetDefaultVp, handleUnifyDreamTrigger, handleUnifyFetchToolStats, handleUnifyFetchDebugHistory, broadcastLanguageChange } from '../unify/web-bridge.js';
38
+ import { getLlmConfig, updateLlmConfig, getYeaftSettings, updateYeaftSettings, getSearchSettings, updateSearchSettings, fetchTavilyUsage } from '../yeaft/config-api.js';
39
+ import { fetchModelsDev } from '../yeaft/llm/models-dev.js';
40
+ import { handleYeaftGroupChat, handleYeaftModeSwitch, handleYeaftModelSwitch, resetYeaftSession, handleYeaftLoadHistory, handleYeaftLoadMoreHistory, handleYeaftAbortThread, handleYeaftAbortAll, handleYeaftAbortTurn, handleYeaftVpSubscribe, handleYeaftVpCreate, handleYeaftVpUpdate, handleYeaftVpDelete, handleYeaftVpRead, handleYeaftListGroups, handleYeaftCreateGroup, handleYeaftRenameGroup, handleYeaftUpdateGroup, handleYeaftUpdateGroupConfig, handleYeaftArchiveGroup, handleYeaftDeleteGroup, handleYeaftAddMember, handleYeaftRemoveMember, handleYeaftSetDefaultVp, handleYeaftDreamTrigger, handleYeaftFetchToolStats, handleYeaftFetchDebugHistory, broadcastLanguageChange } from '../yeaft/web-bridge.js';
41
41
 
42
42
  export async function handleMessage(msg) {
43
43
  switch (msg.type) {
@@ -373,27 +373,29 @@ export async function handleMessage(msg) {
373
373
  break;
374
374
  }
375
375
 
376
- // task-318: Unify runtime settings (thread concurrency + auto-archive).
377
- // Read/write the nested `unify` section of config.json — LLM fields
378
- // untouched. On update we broadcast a `unify_settings_updated` event
376
+ // task-318: Yeaft runtime settings (thread concurrency + auto-archive).
377
+ // Read/write the nested `yeaft` section of config.json — LLM fields
378
+ // untouched. On update we broadcast a `yeaft_settings_updated` event
379
379
  // so the UI reflects the new values and in-process consumers
380
380
  // (ThreadEngineRegistry, ThreadStore) can reload their caps.
381
+ case 'get_yeaft_settings':
381
382
  case 'get_unify_settings': {
382
- const settings = getUnifySettings(ctx.CONFIG?.yeaftDir);
383
- sendToServer({ type: 'unify_settings', ...settings });
383
+ const settings = getYeaftSettings(ctx.CONFIG?.yeaftDir);
384
+ sendToServer({ type: 'yeaft_settings', ...settings });
384
385
  break;
385
386
  }
386
387
 
388
+ case 'update_yeaft_settings':
387
389
  case 'update_unify_settings': {
388
- const result = updateUnifySettings(msg.settings || msg.config || {}, ctx.CONFIG?.yeaftDir);
390
+ const result = updateYeaftSettings(msg.settings || msg.config || {}, ctx.CONFIG?.yeaftDir);
389
391
  // Let live consumers pick up the new caps without a session restart.
390
392
  // The registry/store are created per-session; we update the exported
391
393
  // accessors so subsequent dispatches see the new values.
392
- if (!result.error && ctx.unifyRuntimeSettings) {
393
- ctx.unifyRuntimeSettings.maxConcurrentThreads = result.maxConcurrentThreads;
394
- ctx.unifyRuntimeSettings.autoArchiveIdleDays = result.autoArchiveIdleDays;
394
+ if (!result.error && ctx.yeaftRuntimeSettings) {
395
+ ctx.yeaftRuntimeSettings.maxConcurrentThreads = result.maxConcurrentThreads;
396
+ ctx.yeaftRuntimeSettings.autoArchiveIdleDays = result.autoArchiveIdleDays;
395
397
  }
396
- sendToServer({ type: 'unify_settings_updated', ...result });
398
+ sendToServer({ type: 'yeaft_settings_updated', ...result });
397
399
  break;
398
400
  }
399
401
 
@@ -420,123 +422,150 @@ export async function handleMessage(msg) {
420
422
  break;
421
423
  }
422
424
 
423
- // Unify — single conversation backed by the default group.
425
+ // Yeaft — single conversation backed by the default group.
426
+ case 'yeaft_group_chat':
424
427
  case 'unify_group_chat':
425
- await handleUnifyGroupChat(msg);
428
+ await handleYeaftGroupChat(msg);
426
429
  break;
427
430
 
431
+ case 'yeaft_load_history':
428
432
  case 'unify_load_history':
429
- await handleUnifyLoadHistory(msg);
433
+ await handleYeaftLoadHistory(msg);
430
434
  break;
431
435
 
436
+ case 'yeaft_load_more_history':
432
437
  case 'unify_load_more_history':
433
- await handleUnifyLoadMoreHistory(msg);
438
+ await handleYeaftLoadMoreHistory(msg);
434
439
  break;
435
440
 
441
+ case 'yeaft_mode_switch':
436
442
  case 'unify_mode_switch':
437
- handleUnifyModeSwitch(msg);
443
+ handleYeaftModeSwitch(msg);
438
444
  break;
439
445
 
446
+ case 'yeaft_model_switch':
440
447
  case 'unify_model_switch':
441
- handleUnifyModelSwitch(msg);
448
+ handleYeaftModelSwitch(msg);
442
449
  break;
443
450
 
451
+ case 'yeaft_reset':
444
452
  case 'unify_reset':
445
- await resetUnifySession();
453
+ await resetYeaftSession();
446
454
  break;
447
455
 
456
+ case 'yeaft_abort_thread':
448
457
  case 'unify_abort_thread':
449
458
  // task-325c: user-initiated abort of an in-flight query. The
450
459
  // legacy `threadId` field on the payload is accepted but ignored
451
460
  // (H2.f.5: single-conversation model).
452
- handleUnifyAbortThread(msg);
461
+ handleYeaftAbortThread(msg);
453
462
  break;
454
463
 
464
+ case 'yeaft_abort_all':
455
465
  case 'unify_abort_all':
456
466
  // task-325c: user-initiated abort of ALL in-flight queries across
457
- // every thread. Always emits `unify_aborted` ack.
458
- handleUnifyAbortAll();
467
+ // every thread. Always emits `yeaft_aborted` ack.
468
+ handleYeaftAbortAll();
459
469
  break;
460
470
 
471
+ case 'yeaft_abort_turn':
461
472
  case 'unify_abort_turn':
462
473
  // Per-VP stop: abort a single VP turn by turnId.
463
- handleUnifyAbortTurn(msg);
474
+ handleYeaftAbortTurn(msg);
464
475
  break;
465
476
 
466
477
  // task-334-ui-a: VP library subscribe — replies with one-shot
467
478
  // vp_snapshot event. Live diff (vp_updated/vp_removed) deferred to 334h.
479
+ case 'yeaft_vp_subscribe':
468
480
  case 'unify_vp_subscribe':
469
- handleUnifyVpSubscribe(msg);
481
+ handleYeaftVpSubscribe(msg);
470
482
  break;
471
483
 
472
484
  // task-334-ui-g: VP CRUD (create / update / delete / read-single).
473
485
  // All four reply via `vp_crud_result`; VpLoader's rescan emits the
474
486
  // authoritative `vp_updated` / `vp_removed` events so the store stays
475
487
  // in sync without a bespoke ack path.
488
+ case 'yeaft_vp_create':
476
489
  case 'unify_vp_create':
477
- handleUnifyVpCreate(msg);
490
+ handleYeaftVpCreate(msg);
478
491
  break;
492
+ case 'yeaft_vp_update':
479
493
  case 'unify_vp_update':
480
- handleUnifyVpUpdate(msg);
494
+ handleYeaftVpUpdate(msg);
481
495
  break;
496
+ case 'yeaft_vp_delete':
482
497
  case 'unify_vp_delete':
483
- handleUnifyVpDelete(msg);
498
+ handleYeaftVpDelete(msg);
484
499
  break;
500
+ case 'yeaft_vp_read':
485
501
  case 'unify_vp_read':
486
- handleUnifyVpRead(msg);
502
+ handleYeaftVpRead(msg);
487
503
  break;
488
504
 
489
505
  // task-334m: Group CRUD + D1 seed wiring (§Δ10 334m + R6 §Δ31.2).
490
506
  // All handlers reply via `group_crud_result`; mutating ops additionally
491
507
  // emit `group_roster_changed` (add/remove/default) or
492
508
  // `group_list_updated` (create/rename/archive) for listener sync.
509
+ case 'yeaft_list_groups':
493
510
  case 'unify_list_groups':
494
- handleUnifyListGroups(msg);
511
+ handleYeaftListGroups(msg);
495
512
  break;
513
+ case 'yeaft_create_group':
496
514
  case 'unify_create_group':
497
- handleUnifyCreateGroup(msg);
515
+ handleYeaftCreateGroup(msg);
498
516
  break;
517
+ case 'yeaft_rename_group':
499
518
  case 'unify_rename_group':
500
- handleUnifyRenameGroup(msg);
519
+ handleYeaftRenameGroup(msg);
501
520
  break;
521
+ case 'yeaft_update_group':
502
522
  case 'unify_update_group':
503
- handleUnifyUpdateGroup(msg);
523
+ handleYeaftUpdateGroup(msg);
504
524
  break;
525
+ case 'yeaft_update_group_config':
505
526
  case 'unify_update_group_config':
506
- handleUnifyUpdateGroupConfig(msg);
527
+ handleYeaftUpdateGroupConfig(msg);
507
528
  break;
529
+ case 'yeaft_archive_group':
508
530
  case 'unify_archive_group':
509
- handleUnifyArchiveGroup(msg);
531
+ handleYeaftArchiveGroup(msg);
510
532
  break;
533
+ case 'yeaft_delete_group':
511
534
  case 'unify_delete_group':
512
- handleUnifyDeleteGroup(msg);
535
+ handleYeaftDeleteGroup(msg);
513
536
  break;
537
+ case 'yeaft_add_member':
514
538
  case 'unify_add_member':
515
- handleUnifyAddMember(msg);
539
+ handleYeaftAddMember(msg);
516
540
  break;
541
+ case 'yeaft_remove_member':
517
542
  case 'unify_remove_member':
518
- handleUnifyRemoveMember(msg);
543
+ handleYeaftRemoveMember(msg);
519
544
  break;
545
+ case 'yeaft_set_default_vp':
520
546
  case 'unify_set_default_vp':
521
- handleUnifySetDefaultVp(msg);
547
+ handleYeaftSetDefaultVp(msg);
522
548
  break;
523
549
 
524
550
  // wave-6b: manual dream trigger from VP detail page
551
+ case 'yeaft_dream_trigger':
525
552
  case 'unify_dream_trigger':
526
- await handleUnifyDreamTrigger(msg);
553
+ await handleYeaftDreamTrigger(msg);
527
554
  break;
528
555
 
529
- // 2026-05-13: per-tool call counters for the Unify debug drawer.
556
+ // 2026-05-13: per-tool call counters for the Yeaft debug drawer.
557
+ case 'yeaft_fetch_tool_stats':
530
558
  case 'unify_fetch_tool_stats':
531
- await handleUnifyFetchToolStats(msg);
559
+ await handleYeaftFetchToolStats(msg);
532
560
  break;
533
561
 
534
- // fix-vp-multi-thread (bug 4): hydrate the Unify debug panel from
562
+ // fix-vp-multi-thread (bug 4): hydrate the Yeaft debug panel from
535
563
  // the persistent SQLite trace. Without this, the panel only shows
536
564
  // turns that happened after the panel was opened — every previous
537
565
  // turn is invisible.
566
+ case 'yeaft_fetch_debug_history':
538
567
  case 'unify_fetch_debug_history':
539
- await handleUnifyFetchDebugHistory(msg);
568
+ await handleYeaftFetchDebugHistory(msg);
540
569
  break;
541
570
 
542
571
  // Expert roles definition (for ExpertPanel detail view)
package/context.js CHANGED
@@ -30,9 +30,9 @@ export default {
30
30
  sendToServer: null,
31
31
  // 由 index.js 注册的配置保存函数
32
32
  saveConfig: null,
33
- // task-318: live Unify runtime caps. Mutated in-place by
34
- // `update_unify_settings` so in-process consumers (web-bridge) can
33
+ // task-318: live Yeaft runtime caps. Mutated in-place by
34
+ // `update_yeaft_settings` so in-process consumers (web-bridge) can
35
35
  // pick up the new values without a session restart. `null` until the
36
- // Unify session has finished loading.
37
- unifyRuntimeSettings: null,
36
+ // Yeaft session has finished loading.
37
+ yeaftRuntimeSettings: null,
38
38
  };
package/expert-roles.js CHANGED
@@ -719,7 +719,7 @@ const EXPERT_ROLES = {
719
719
  messageTemplate: '你现在是 Spielberg。每个镜头都是情感传递的管道——景别选错了,情感就传错了。设计镜头方案。关注画面叙事力、情感传达、观众视线引导。\n\n',
720
720
  messageTemplateEn: 'You are Spielberg. Every shot is a conduit for emotion — wrong shot size means wrong emotion delivered. Design the shots. Focus on visual narrative power, emotional conveyance, audience gaze direction.\n\n',
721
721
  defaultMessage: '你现在是 Spielberg。每个镜头都是情感传递的管道。\n设计当前对话中关键场景的镜头方案。\n\n你的镜头设计法则:\n- **景别传情**:远景传达渺小/孤独,中景传达关系/对话,近景传达内心,特写传达转折——每个景别有它的情感频率\n- **视线引导**:观众的眼睛应该被引导到哪里?用光线、构图、运动来控制。观众看错了地方 = 你失职了\n- **运镜配合情绪**:角色奔跑时镜头不能是静止的。紧张时手持摇晃,平静时稳定推轨\n- **长镜头 vs 剪辑**:长镜头给观众沉浸感,快速剪辑制造紧迫感。选择哪个取决于这个时刻需要什么\n- **声画关系**:画面和声音是两个叙事层。可以统一(画面开心+欢快音乐),也可以对立(画面美好+不安的弦乐)——对立往往更有力\n\n给出每个关键时刻的镜头选择和理由。\n记住:一个好镜头不需要解释——观众看到就懂了。',
722
- defaultMessageEn: 'You are Spielberg. Every shot is a conduit for emotion.\nDesign shots for the key scenes discussed in the current conversation.\n\nYour shot design principles:\n- **Shot size carries emotion**: Wide conveys insignificance/loneliness, Medium conveys relationship/dialogue, Close-up conveys inner feeling, Extreme close-up conveys turning points — each size has its emotional frequency\n- **Gaze direction**: Where should the audience look? Control with lighting, composition, movement. Wrong gaze = your failure\n- **Camera matches mood**: Camera can\'t be static when characters run. Tension gets handheld shake, calm gets dolly track\n- **Long take vs editing**: Long takes give immersion, rapid cuts create urgency. The choice depends on what the moment needs\n- **Sound-image relationship**: Image and sound are two narrative layers. Can unify (happy scene + upbeat music) or contrast (beautiful scene + uneasy strings) — contrast is often more powerful\n\nGive shot choice and reasoning for each key moment.\nRemember: A good shot needs no explanation — audiences see it and understand.'
722
+ defaultMessageEn: 'You are Spielberg. Every shot is a conduit for emotion.\nDesign shots for the key scenes discussed in the current conversation.\n\nYour shot design principles:\n- **Shot size carries emotion**: Wide conveys insignificance/loneliness, Medium conveys relationship/dialogue, Close-up conveys inner feeling, Extreme close-up conveys turning points — each size has its emotional frequency\n- **Gaze direction**: Where should the audience look? Control with lighting, composition, movement. Wrong gaze = your failure\n- **Camera matches mood**: Camera can\'t be static when characters run. Tension gets handheld shake, calm gets dolly track\n- **Long take vs editing**: Long takes give immersion, rapid cuts create urgency. The choice depends on what the moment needs\n- **Sound-image relationship**: Image and sound are two narrative layers. Can yeaft (happy scene + upbeat music) or contrast (beautiful scene + uneasy strings) — contrast is often more powerful\n\nGive shot choice and reasoning for each key moment.\nRemember: A good shot needs no explanation — audiences see it and understand.'
723
723
  },
724
724
  'visual-storytelling': {
725
725
  name: '视觉叙事', nameEn: 'Visual Storytelling',
package/index.js CHANGED
@@ -61,7 +61,7 @@ function saveConfig(config) {
61
61
 
62
62
  const fileConfig = loadConfig();
63
63
 
64
- // task-fix (5-bugs): the Unify web-bridge reads `ctx.CONFIG.yeaftDir`
64
+ // task-fix (5-bugs): the Yeaft web-bridge reads `ctx.CONFIG.yeaftDir`
65
65
  // for every group / VP / memory operation. If unset, `path.join(undefined, …)`
66
66
  // throws `The "path" argument must be of type string. Received undefined`
67
67
  // and the UI surfaces "群组操作失败: …" with a raw node error. Resolve the
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.854",
3
+ "version": "0.1.857",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "yeaft-agent": "./cli.js",
9
- "yeaft": "./unify/cli.js",
9
+ "yeaft": "./yeaft/cli.js",
10
10
  "yeaft-stats": "./bin/yeaft-stats.js"
11
11
  },
12
12
  "scripts": {
@@ -35,8 +35,8 @@
35
35
  },
36
36
  "files": [
37
37
  "**/*.js",
38
- "unify/templates/**/*.md",
39
- "unify/dream-v2/prompts/**/*.md"
38
+ "yeaft/templates/**/*.md",
39
+ "yeaft/dream-v2/prompts/**/*.md"
40
40
  ],
41
41
  "license": "MIT",
42
42
  "author": "Yeaft",
package/service/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  saveServiceConfig, loadServiceConfig,
10
10
  parseServiceArgs, validateConfig
11
11
  } from './config.js';
12
- import { initYeaftDir } from '../unify/init.js';
12
+ import { initYeaftDir } from '../yeaft/init.js';
13
13
  import { getSystemdServicePath, linuxInstall, linuxUninstall, linuxStart, linuxStop, linuxRestart, linuxStatus, linuxLogs } from './linux.js';
14
14
  import { getLaunchdPlistPath, macInstall, macUninstall, macStart, macStop, macRestart, macStatus, macLogs } from './macos.js';
15
15
  import { winInstall, winUninstall, winStart, winStop, winRestart, winStatus, winLogs } from './windows.js';
@@ -1,13 +1,13 @@
1
1
  /**
2
- * attachments.js — Unify (group/feature) attachment handling.
2
+ * attachments.js — Yeaft (group/feature) attachment handling.
3
3
  *
4
4
  * Mirrors the Chat-mode pipeline implemented in `agent/workbench/transfer.js`,
5
- * adapted to the Unify architecture:
5
+ * adapted to the Yeaft architecture:
6
6
  *
7
7
  * - Chat mode has a per-conversation `state.workDir` and a single
8
8
  * in-flight Claude SDK query — `transfer.js` enqueues a constructed
9
9
  * user message into that query's `inputStream`.
10
- * - Unify mode has many VPs taking turns inside a group, no per-VP
10
+ * - Yeaft mode has many VPs taking turns inside a group, no per-VP
11
11
  * workDir, and the Engine accepts the user message via `query()`
12
12
  * args. So we (a) save attachments to a shared per-group folder
13
13
  * under the agent's CWD (so file-tools using `ctx.cwd` can read
@@ -92,7 +92,7 @@ function sanitizeBaseName(base) {
92
92
  * @param {string} [opts.subdir] Sub-folder under TEMP_UPLOAD_DIR
93
93
  * (e.g. groupId). Lets multiple groups co-exist without clobbering.
94
94
  * @param {string} [opts.cwd] Override base dir; defaults to process.cwd()
95
- * which is what unify tools (file-read, bash, ...) resolve relative
95
+ * which is what yeaft tools (file-read, bash, ...) resolve relative
96
96
  * paths against.
97
97
  * @returns {{
98
98
  * promptAttachments: Array<{name:string, path:string, mimeType:string, isImage:boolean}>,
@@ -101,7 +101,7 @@ function sanitizeBaseName(base) {
101
101
  * failed: Array<{name:string, error:string}>
102
102
  * }}
103
103
  */
104
- export function persistUnifyAttachments(files, opts = {}) {
104
+ export function persistYeaftAttachments(files, opts = {}) {
105
105
  const cwd = opts.cwd || process.cwd();
106
106
  // Subdir is OURS — it must remain ASCII-safe because we generate it
107
107
  // from groupId. Keep the existing strict policy here; this is NOT
@@ -215,7 +215,7 @@ export function persistUnifyAttachments(files, opts = {}) {
215
215
  * Strip base64 data from a resolved-files array so it can be safely
216
216
  * persisted (e.g. into a group jsonl-log or memory entry). Keeps name,
217
217
  * mimeType, isImage, and the on-disk path returned by
218
- * `persistUnifyAttachments`.
218
+ * `persistYeaftAttachments`.
219
219
  *
220
220
  * @param {Array<{name:string, path:string, mimeType:string, isImage:boolean}>} promptAttachments
221
221
  * @returns {Array<{name:string, path:string, mimeType:string, isImage:boolean}>}
@@ -231,7 +231,7 @@ export function attachmentsForPersistence(promptAttachments) {
231
231
  }
232
232
 
233
233
  /**
234
- * Resolve a persisted Unify attachment path to an on-disk file. The persisted
234
+ * Resolve a persisted Yeaft attachment path to an on-disk file. The persisted
235
235
  * path is intentionally relative (for tool use), so preview hydration must
236
236
  * keep it inside the upload root instead of serving arbitrary files.
237
237
  *
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * cli.js — Yeaft Unify CLI entry point
3
+ * cli.js — Yeaft Yeaft CLI entry point
4
4
  *
5
5
  * Uses loadSession() to wire all subsystems (Engine, ToolRegistry, SkillManager,
6
6
  * MCPManager, Dream, StopHooks, Memory) into a single session.
@@ -255,7 +255,7 @@ function handleCompactOrphans(config, { dryRun = false } = {}) {
255
255
  /**
256
256
  * One-shot group hard-delete with cascade. Removes the group directory
257
257
  * AND every persisted message stamped with that group id. Same semantics
258
- * as the web-bridge `unify_delete_group` op, but reachable from the CLI
258
+ * as the web-bridge `yeaft_delete_group` op, but reachable from the CLI
259
259
  * for scripted maintenance.
260
260
  */
261
261
  function handleDeleteGroup(config, groupId) {
@@ -315,7 +315,7 @@ async function runREPL(config, args) {
315
315
  const hotCount = conversationStore.countHot();
316
316
  const coldCount = conversationStore.countCold();
317
317
 
318
- console.log(`Yeaft Unify REPL (model: ${session.config.model})`);
318
+ console.log(`Yeaft Yeaft REPL (model: ${session.config.model})`);
319
319
  console.log(`Conversation: ${hotCount} hot, ${coldCount} cold`);
320
320
  console.log(`Tools: ${session.status.tools} | Skills: ${session.status.skills}`);
321
321
  if (session.status.mcpServers.length > 0) {
@@ -815,7 +815,7 @@ async function main() {
815
815
  }
816
816
 
817
817
  // No input and not interactive — show help
818
- console.log('Yeaft Unify CLI');
818
+ console.log('Yeaft Yeaft CLI');
819
819
  console.log();
820
820
  console.log('Usage:');
821
821
  console.log(' node cli.js "your prompt" — One-shot query');
@@ -2,7 +2,7 @@
2
2
  * compactor.js — per-group post-turn history compactor.
3
3
  *
4
4
  * Owns the orchestration that previously lived inline in
5
- * `agent/unify/web-bridge.js` as `getCompactState` /
5
+ * `agent/yeaft/web-bridge.js` as `getCompactState` /
6
6
  * `scheduleCompactAfterTurn` / `runCompactNow`. Bridge keeps WS knowledge
7
7
  * and history ownership; this class owns:
8
8
  * - per-group single-flight + anti-starvation `pending` chain,
@@ -69,7 +69,7 @@ export class Compactor {
69
69
  * locale instead of always English.
70
70
  * @param {(groupId: string, result: CompactedResult) => void} [opts.onCompacted]
71
71
  * Optional sink. Bridge wires this to send the
72
- * `unify_history_compacted` WS event. Default: no-op. Can be
72
+ * `yeaft_history_compacted` WS event. Default: no-op. Can be
73
73
  * replaced post-construction via `setOnCompacted`.
74
74
  */
75
75
  constructor({ summarize, getMaxContextTokens, getLanguage, onCompacted } = {}) {
@@ -90,8 +90,8 @@ export class Compactor {
90
90
 
91
91
  /**
92
92
  * Replace the post-success sink. Bridge calls this from
93
- * `installUnifyRuntimeBridge` once the bridge-local
94
- * `sendToServer` + `unifyConversationId` are available — keeps WS
93
+ * `installYeaftRuntimeBridge` once the bridge-local
94
+ * `sendToServer` + `yeaftConversationId` are available — keeps WS
95
95
  * knowledge out of Compactor's constructor and avoids a circular
96
96
  * import between `session.js` and `web-bridge.js`.
97
97
  */
@@ -110,7 +110,7 @@ export class Compactor {
110
110
  }
111
111
 
112
112
  /**
113
- * Entry-gate await. Bridge calls at the top of `handleUnifyGroupChat`
113
+ * Entry-gate await. Bridge calls at the top of `handleYeaftGroupChat`
114
114
  * so a brand-new turn never reads a half-mutated history mid-compact.
115
115
  * Other groups' compacts never block this gate (per-group keying).
116
116
  *
@@ -202,7 +202,7 @@ export class Compactor {
202
202
  if (!result || !result.compacted) {
203
203
  if (result && result.error) {
204
204
  console.warn(
205
- `[Unify] history compact: summarizer failed (${result.error}); ` +
205
+ `[Yeaft] history compact: summarizer failed (${result.error}); ` +
206
206
  `keeping ${result.beforeTurns} turns / ~${result.beforeTokens} tokens`
207
207
  );
208
208
  }
@@ -216,17 +216,17 @@ export class Compactor {
216
216
  // resets / route_forward bursts — log at debug, not info.
217
217
  const current = historyHandle.get();
218
218
  if (current !== snapshot) {
219
- console.debug('[Unify] history compact: history was reset during compact — discarding stale summary');
219
+ console.debug('[Yeaft] history compact: history was reset during compact — discarding stale summary');
220
220
  return;
221
221
  }
222
222
  if (current.length !== snapshotLen) {
223
- console.debug('[Unify] history compact: history was appended-to during compact — discarding stale summary');
223
+ console.debug('[Yeaft] history compact: history was appended-to during compact — discarding stale summary');
224
224
  return;
225
225
  }
226
226
 
227
227
  historyHandle.set(result.messages);
228
228
  console.log(
229
- `[Unify] history compacted (reason=${result.reason}): ` +
229
+ `[Yeaft] history compacted (reason=${result.reason}): ` +
230
230
  `turns ${result.beforeTurns}→${result.afterTurns}, ` +
231
231
  `tokens ~${result.beforeTokens}→${result.afterTokens}, ` +
232
232
  `archived ${result.archivedCount} messages`
@@ -243,7 +243,7 @@ export class Compactor {
243
243
  });
244
244
  } catch { /* sink failure must not abort the orchestrator */ }
245
245
  } catch (err) {
246
- console.warn(`[Unify] history compact: unexpected failure (groupId=${groupId})`, err?.message || err);
246
+ console.warn(`[Yeaft] history compact: unexpected failure (groupId=${groupId})`, err?.message || err);
247
247
  }
248
248
  }
249
249
 
@@ -28,7 +28,6 @@
28
28
  */
29
29
 
30
30
  import { groupTurns, pickCoolingGroups, indicesFromGroups } from './turn-group.js';
31
- import { writeSummary } from '../memory/store-v2.js';
32
31
 
33
32
  /**
34
33
  * @typedef {{
@@ -57,7 +56,7 @@ import { writeSummary } from '../memory/store-v2.js';
57
56
  * nextMessages: object[],
58
57
  * }>}
59
58
  */
60
- export async function runCompact({ messages, keepHot = 10, taskId = null, root, hooks }) {
59
+ export async function runCompact({ messages, keepHot = 10, hooks }) {
61
60
  if (!Array.isArray(messages)) {
62
61
  throw new Error('runCompact: messages array required');
63
62
  }
@@ -104,17 +103,12 @@ export async function runCompact({ messages, keepHot = 10, taskId = null, root,
104
103
  archiveResults.push({ ...g, turnId: r?.turnId });
105
104
  }
106
105
 
107
- // Track 2 — refresh task summary if applicable.
108
- let taskSummaryRefreshed = false;
109
- if (taskId && root && typeof hooks.refreshTaskSummary === 'function') {
110
- const prior = typeof hooks.readPriorTaskSummary === 'function'
111
- ? await hooks.readPriorTaskSummary() : '';
112
- const next = await hooks.refreshTaskSummary(coolingMessages, prior);
113
- if (typeof next === 'string' && next.trim()) {
114
- await writeSummary({ kind: 'feature', id: taskId }, next, { root });
115
- taskSummaryRefreshed = true;
116
- }
117
- }
106
+ // Track 2 — task summary refresh: removed. The legacy `feature/<id>` root
107
+ // scope was dropped along with the Feature system (2026-05-13); under the
108
+ // group-isolated layout feature summaries would live at
109
+ // `group/<g>/feature/<id>/` and are written by dream, not by post-turn
110
+ // compact. Engine no longer passes `taskId`/`root` to this orchestrator.
111
+ const taskSummaryRefreshed = false;
118
112
 
119
113
  // Track 3 — memory extraction.
120
114
  let extractedCount = 0;
@@ -12,7 +12,7 @@ import { existsSync, readFileSync, writeFileSync } from 'fs';
12
12
  import { join } from 'path';
13
13
  import { DEFAULT_YEAFT_DIR } from './init.js';
14
14
  import { normalizeProviderModels, serializeModelForPersistence } from './models.js';
15
- import { normaliseUnifySection } from './config.js';
15
+ import { normaliseYeaftSection } from './config.js';
16
16
 
17
17
  /**
18
18
  * Read the LLM-relevant portion of config.json.
@@ -128,31 +128,31 @@ export function updateLlmConfig(update, dir) {
128
128
  };
129
129
  }
130
130
 
131
- // ─── Unify runtime settings (task-318) ────────────────────────────
131
+ // ─── Yeaft runtime settings (task-318) ────────────────────────────
132
132
 
133
133
  /**
134
- * Read the Unify-section of config.json. Returns defaults when the file
134
+ * Read the Yeaft-section of config.json. Returns defaults when the file
135
135
  * or section is absent. Callers (UI, registry, ThreadStore) rely on a
136
- * stable shape — `normaliseUnifySection` guarantees that.
136
+ * stable shape — `normaliseYeaftSection` guarantees that.
137
137
  *
138
138
  * @param {string} [dir] — Yeaft data directory
139
139
  * @returns {{ maxConcurrentThreads: number, autoArchiveIdleDays: number } | { error: string }}
140
140
  */
141
- export function getUnifySettings(dir) {
141
+ export function getYeaftSettings(dir) {
142
142
  const root = dir || process.env.YEAFT_DIR || DEFAULT_YEAFT_DIR;
143
143
  const configPath = join(root, 'config.json');
144
- if (!existsSync(configPath)) return normaliseUnifySection(null);
144
+ if (!existsSync(configPath)) return normaliseYeaftSection(null);
145
145
  try {
146
146
  const raw = readFileSync(configPath, 'utf8');
147
147
  const json = JSON.parse(raw);
148
- return normaliseUnifySection(json.unify);
148
+ return normaliseYeaftSection(json.yeaft);
149
149
  } catch (e) {
150
150
  return { error: `Failed to read config.json: ${e.message}` };
151
151
  }
152
152
  }
153
153
 
154
154
  /**
155
- * Update the Unify-section of config.json. Merges into existing config
155
+ * Update the Yeaft-section of config.json. Merges into existing config
156
156
  * (LLM provider / model fields are untouched) and validates each field:
157
157
  * `maxConcurrentThreads` must be 1..50, `autoArchiveIdleDays` must be
158
158
  * 1..3650. Invalid values are rejected outright so the UI sees an error
@@ -163,7 +163,7 @@ export function getUnifySettings(dir) {
163
163
  * @param {string} [dir]
164
164
  * @returns {{ maxConcurrentThreads: number, autoArchiveIdleDays: number } | { error: string }}
165
165
  */
166
- export function updateUnifySettings(update, dir) {
166
+ export function updateYeaftSettings(update, dir) {
167
167
  const root = dir || process.env.YEAFT_DIR || DEFAULT_YEAFT_DIR;
168
168
  const configPath = join(root, 'config.json');
169
169
 
@@ -172,7 +172,7 @@ export function updateUnifySettings(update, dir) {
172
172
  }
173
173
 
174
174
  // Validate before touching the file. We enforce the same clamp bounds
175
- // that `normaliseUnifySection` uses for reads so round-trip is stable.
175
+ // that `normaliseYeaftSection` uses for reads so round-trip is stable.
176
176
  if (update.maxConcurrentThreads !== undefined) {
177
177
  const n = Number(update.maxConcurrentThreads);
178
178
  if (!Number.isFinite(n) || n < 1 || n > 50) {
@@ -196,7 +196,7 @@ export function updateUnifySettings(update, dir) {
196
196
  }
197
197
  }
198
198
 
199
- const prev = normaliseUnifySection(existing.unify);
199
+ const prev = normaliseYeaftSection(existing.yeaft);
200
200
  const merged = {
201
201
  maxConcurrentThreads: update.maxConcurrentThreads !== undefined
202
202
  ? Math.floor(Number(update.maxConcurrentThreads))
@@ -205,7 +205,7 @@ export function updateUnifySettings(update, dir) {
205
205
  ? Math.floor(Number(update.autoArchiveIdleDays))
206
206
  : prev.autoArchiveIdleDays,
207
207
  };
208
- existing.unify = merged;
208
+ existing.yeaft = merged;
209
209
 
210
210
  try {
211
211
  writeFileSync(configPath, JSON.stringify(existing, null, 2) + '\n', 'utf8');