@yeaft/webchat-agent 0.1.992 → 0.1.994

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.992",
3
+ "version": "0.1.994",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -9,7 +9,9 @@ Source session conversations:
9
9
  {{siblingsBlock}}
10
10
  Task:
11
11
  1. Write memory.md from scratch with a reasonable section structure.
12
- 2. Write summary.md (1–3 sentences).
12
+ 2. Prioritize reusable experience: workflows, preferences, pitfalls, corrections, project conventions, review/merge/tag lessons, and rules that should change future execution.
13
+ 3. Keep current PR/review/blocker detail only when it is still actionable.
14
+ 4. Write summary.md (1–3 sentences) with reusable lessons before current state.
13
15
 
14
16
  Reply with strict JSON of the shape:
15
17
  { "memory_md": "...", "summary_md": "..." }
@@ -25,7 +27,9 @@ Scope path: {{target}} (must be ≤2 levels)
25
27
  {{siblingsBlock}}
26
28
  任务:
27
29
  1. 从零编写 memory.md,并使用合理的章节结构。
28
- 2. 编写 summary.md(1–3 句)。
30
+ 2. 优先保留可复用经验:workflow、preference、pitfall、correction、project convention、review/merge/tag 教训,以及会改变后续执行方式的规则。
31
+ 3. 当前 PR、review、阻塞细节只有仍可执行时才保留。
32
+ 4. 编写 summary.md(1–3 句),先写可复用经验,再写当前状态。
29
33
 
30
34
  只回复严格 JSON,结构如下:
31
35
  { "memory_md": "...", "summary_md": "..." }
@@ -8,12 +8,14 @@ The target session id is provided as `{{sessionId}}`.
8
8
  ## What to extract for `sessions/<id>` scope
9
9
 
10
10
  - **purpose** — what this session exists to do, its charter or mission
11
- - **members** — users, VPs, roles, and what each is responsible for
12
- - **conventions** — how the session works: rituals, naming, languages, review flow
11
+ - **members** — users, session members, roles, and what each is responsible for
12
+ - **workflow** — reusable ways this session should work: review flow, merge/tag ritual, handoff order, testing expectations
13
+ - **preferences** — user or project preferences that affect future execution in this session
14
+ - **pitfalls and corrections** — mistakes, rejected approaches, and user corrections that should prevent repeated errors
13
15
  - **shared decisions** — durable agreements the session should remember
14
16
  - **shared context** — domain or project knowledge the whole session relies on
15
- - **current state** — recent PRs, reviews, blockers, next steps, or release status that matter now
16
- - **lessons** — corrections or decisions that should guide future turns in this session
17
+ - **current state** — recent PRs, reviews, blockers, next steps, or release status that matter now, but only when still actionable
18
+ - **lessons** — generalized experience that should guide future turns in this session
17
19
 
18
20
  ## What NOT to extract here
19
21
 
@@ -26,7 +28,9 @@ The target session id is provided as `{{sessionId}}`.
26
28
 
27
29
  Return a JSON array only. Each item must have `kind`, `body`, `tags`, `sourceMessages`, and `confidence`.
28
30
 
29
- Make the segment useful to a future session member who needs the big picture plus the latest actionable detail.
31
+ Allowed `kind` values include `workflow`, `preference`, `lesson`, `pitfall`, `correction`, `project-convention`, `decision`, `goal`, `fact`, and `context`.
32
+
33
+ Make the segment useful to a future session member who needs the big picture plus the latest actionable detail. Prefer stable, reusable memory over a chronology of completed actions. A completion record is useful only if it changes what future work should do next.
30
34
 
31
35
  <!-- lang:zh -->
32
36
  # Dream Extract — Session Scope
@@ -38,12 +42,14 @@ Make the segment useful to a future session member who needs the big picture plu
38
42
  ## `sessions/<id>` scope 应抽取什么
39
43
 
40
44
  - **purpose** — 这个 session 存在的目的、charter 或 mission
41
- - **members** — 用户、VP、角色,以及各自负责什么
42
- - **conventions** — session 的协作方式:流程、命名、语言、review 规则
45
+ - **members** — 用户、session member、角色,以及各自负责什么
46
+ - **workflow** — 之后可复用的工作方式:review 流程、merge/tag 节奏、交接顺序、测试要求
47
+ - **preferences** — 会影响后续执行的用户偏好或项目偏好
48
+ - **pitfalls and corrections** — 已犯错误、被拒绝的做法、用户纠偏,避免之后重复
43
49
  - **shared decisions** — session 之后应该记住的稳定决策
44
50
  - **shared context** — 整个 session 依赖的领域或项目信息
45
- - **current state** — 当前重要的 PR、review、阻塞、下一步或 release 状态
46
- - **lessons** — 未来 turn 应继续遵守的纠正和决策
51
+ - **current state** — 当前重要的 PR、review、阻塞、下一步或 release 状态,但只保留仍会影响下一步的内容
52
+ - **lessons** — 未来 turn 应继续遵守的规则化经验
47
53
 
48
54
  ## 不要在这里抽取什么
49
55
 
@@ -56,4 +62,6 @@ Make the segment useful to a future session member who needs the big picture plu
56
62
 
57
63
  只返回 JSON array。每一项必须包含 `kind`、`body`、`tags`、`sourceMessages` 和 `confidence`。
58
64
 
59
- segment 应帮助未来的 session member 快速理解大背景和最新可执行细节。
65
+ 允许的 `kind` 包括 `workflow`、`preference`、`lesson`、`pitfall`、`correction`、`project-convention`、`decision`、`goal`、`fact` 和 `context`。
66
+
67
+ segment 应帮助未来的 session member 快速理解大背景和最新可执行细节。优先保留可复用经验,不要只写完成事项流水账;只有仍会改变下一步执行方式的完成记录才值得保留。
@@ -8,7 +8,9 @@ The target scope is `{{scope}}` and contains `{{segmentCount}}` segments listed
8
8
  ## Goals
9
9
 
10
10
  - A future session member should grasp the gist of this scope from your summary alone, without reading all segments.
11
+ - Prioritize reusable experience: workflows, project/user preferences, pitfalls, corrections, review/merge/tag lessons, and conventions that change future behavior.
11
12
  - Keep specifics that matter: names, numbers, decisions, durable views, current blockers, and next steps.
13
+ - Do not turn the summary into a chronology of completed work. Mention completed items only when they affect future execution.
12
14
  - Stay faithful: do not invent facts not present in the segments. Do not soften decisions or opinions.
13
15
  - Write in the same language the segments are mostly in, unless the surrounding language directive says otherwise.
14
16
 
@@ -33,7 +35,9 @@ Return only the summary text.
33
35
  ## 目标
34
36
 
35
37
  - 未来的 session member 只读 summary,也应能理解这个 scope 的核心意思。
38
+ - 优先保留可复用经验:工作流、项目/用户偏好、坑点、纠偏、review/merge/tag 教训,以及会改变后续行为的约定。
36
39
  - 保留重要具体信息:名字、数字、决策、稳定观点、当前阻塞和下一步。
40
+ - 不要把 summary 写成完成事项流水账;已完成事项只有会影响后续执行时才写。
37
41
  - 忠于事实:不要编造 segments 中没有的事实。不要弱化已经明确的决策或观点。
38
42
  - 使用 segments 主要使用的语言;如果外层语言指令另有要求,以外层语言指令为准。
39
43
 
@@ -18,9 +18,11 @@ Recent session conversations:
18
18
 
19
19
  Task:
20
20
  - Extract from these conversations what is relevant to THIS scope.
21
+ - Prefer reusable experience over chronology: workflows, preferences, pitfalls, corrections, project conventions, review/merge/tag lessons, and rules that should change future execution.
22
+ - Keep current PR/review/blocker detail only when it is still actionable.
21
23
  - Integrate it into memory.md (reorganize sections if needed).
22
24
  - Drop stale or contradicted entries.
23
- - Rewrite summary.md (1–3 sentences).
25
+ - Rewrite summary.md (1–3 sentences) so the summary highlights reusable lessons first, then current state.
24
26
  - The same conversations are being processed for OTHER scopes too.
25
27
  Only handle what is relevant here. Ignore the rest.
26
28
 
@@ -53,9 +55,11 @@ Scope: {{target}}
53
55
 
54
56
  任务:
55
57
  - 从这些对话中提取与 THIS scope 相关的内容。
58
+ - 优先保留可复用经验,而不是流水账:workflow、preference、pitfall、correction、project convention、review/merge/tag 教训,以及会改变后续执行方式的规则。
59
+ - 当前 PR、review、阻塞细节只有仍可执行时才保留。
56
60
  - 将内容整合进 memory.md(必要时重组章节)。
57
61
  - 删除过期或被推翻的条目。
58
- - 重写 summary.md(1–3 句)。
62
+ - 重写 summary.md(1–3 句),summary 先突出可复用经验,再写当前状态。
59
63
  - 同一批对话也会被处理到 OTHER scopes。
60
64
  这里只处理与当前 scope 相关的内容,忽略其他内容。
61
65
 
@@ -17,7 +17,19 @@ const MAX_MESSAGES = 80;
17
17
  const MAX_BODY_CHARS = 1200;
18
18
  const MAX_SEGMENTS_PER_SCOPE = 64;
19
19
  const RECENT_MESSAGE_COUNT = 8;
20
- const VALID_KINDS = new Set(['fact', 'preference', 'decision', 'lesson', 'relation', 'goal', 'context']);
20
+ const VALID_KINDS = new Set([
21
+ 'fact',
22
+ 'preference',
23
+ 'decision',
24
+ 'lesson',
25
+ 'relation',
26
+ 'goal',
27
+ 'context',
28
+ 'workflow',
29
+ 'pitfall',
30
+ 'correction',
31
+ 'project-convention',
32
+ ]);
21
33
 
22
34
  /**
23
35
  * @param {{
@@ -61,7 +73,10 @@ export async function extractAndWriteMemorySegments(opts) {
61
73
  }
62
74
 
63
75
  const recent = scope === `sessions/${opts.sessionId}`
64
- ? [buildRecentSegment({ scope, messages, now })]
76
+ ? [
77
+ buildRecentSegment({ scope, messages, now }),
78
+ buildRecentExperienceSegment({ scope, extracted, now }),
79
+ ].filter(Boolean)
65
80
  : [];
66
81
  if (extracted.length === 0 && recent.length === 0) continue;
67
82
 
@@ -184,8 +199,47 @@ function buildRecentSegment({ scope, messages, now }) {
184
199
  });
185
200
  }
186
201
 
202
+ function buildRecentExperienceSegment({ scope, extracted, now }) {
203
+ const experienceSegments = extracted
204
+ .filter(isExperienceSegment)
205
+ .slice(-RECENT_MESSAGE_COUNT);
206
+ if (experienceSegments.length === 0) return null;
207
+
208
+ const lines = experienceSegments
209
+ .map(seg => oneLine(seg.body))
210
+ .filter(Boolean);
211
+ if (lines.length === 0) return null;
212
+
213
+ const sourceMessages = [...new Set(experienceSegments.flatMap(seg => (
214
+ Array.isArray(seg.sourceMessages) ? seg.sourceMessages.map(String).filter(Boolean) : []
215
+ )))];
216
+ const body = [
217
+ 'Reusable session experience from the latest Dream pass:',
218
+ ...lines.map(line => `- ${line}`),
219
+ ].join('\n');
220
+
221
+ return makeSegment({
222
+ scope,
223
+ kind: 'lesson',
224
+ tags: ['recent', 'experience', 'workflow'],
225
+ sourceMessages,
226
+ createdAt: now,
227
+ updatedAt: now,
228
+ body,
229
+ });
230
+ }
231
+
232
+ function isExperienceSegment(seg) {
233
+ if (!seg) return false;
234
+ if (['workflow', 'preference', 'pitfall', 'correction', 'project-convention', 'lesson'].includes(seg.kind)) {
235
+ return true;
236
+ }
237
+ const tags = Array.isArray(seg.tags) ? seg.tags.map(tag => String(tag).toLowerCase()) : [];
238
+ return tags.some(tag => ['workflow', 'preference', 'pitfall', 'correction', 'project-convention', 'lesson', 'experience'].includes(tag));
239
+ }
240
+
187
241
  function mergeSegments(existing, incoming) {
188
- const incomingRecent = incoming.filter(isRecentSegment).slice(-1);
242
+ const incomingRecent = latestRecentByFamily(incoming.filter(isRecentSegment));
189
243
  const incomingPermanent = incoming.filter(seg => !isRecentSegment(seg));
190
244
  const byKey = new Map();
191
245
 
@@ -216,6 +270,21 @@ function segmentMergeKey(seg) {
216
270
  return `id:${seg.id}`;
217
271
  }
218
272
 
273
+ function latestRecentByFamily(segments) {
274
+ const byFamily = new Map();
275
+ for (const seg of segments) {
276
+ byFamily.set(recentFamily(seg), seg);
277
+ }
278
+ return [...byFamily.values()];
279
+ }
280
+
281
+ function recentFamily(seg) {
282
+ const tags = Array.isArray(seg.tags) ? seg.tags.map(String) : [];
283
+ if (tags.includes('experience')) return 'experience';
284
+ if (tags.includes('current')) return 'current';
285
+ return 'recent';
286
+ }
287
+
219
288
  function isRecentSegment(seg) {
220
289
  return Array.isArray(seg.tags) && seg.tags.includes('recent');
221
290
  }
@@ -11,7 +11,7 @@
11
11
  * ---
12
12
  * id: seg_<8hex>
13
13
  * scope: feature/auth
14
- * kind: decision # fact|preference|decision|lesson|relation|goal|context
14
+ * kind: decision # fact|preference|decision|lesson|relation|goal|context|workflow|pitfall|correction|project-convention
15
15
  * tags: [auth, jwt]
16
16
  * sourceMessages: [m_142, m_143]
17
17
  * createdAt: 2026-04-29T10:11:12Z
@@ -49,6 +49,7 @@ import { createHash } from 'node:crypto';
49
49
 
50
50
  export const KIND_VALUES = new Set([
51
51
  'fact', 'preference', 'decision', 'lesson', 'relation', 'goal', 'context',
52
+ 'workflow', 'pitfall', 'correction', 'project-convention',
52
53
  ]);
53
54
 
54
55
  const SCOPE_RE = /^(user|group\/[\w-]+(?:\/(?:user|vp\/[\w-]+|feature\/[\w-]+|topic\/[\w-]+(?:\/[\w-]+)?))?|sessions\/[\w-]+(?:\/(?:user|vp\/[\w-]+|feature\/[\w-]+|topic\/[\w-]+(?:\/[\w-]+)?))?|chat\/[\w-]+(?:\/vp\/[\w-]+)?|session\/[\w-]+(?:\/vp\/[\w-]+)?)$/;
package/yeaft/prompts.js CHANGED
@@ -470,9 +470,6 @@ function renderActiveScope(activeScope, lang) {
470
470
  : '';
471
471
  if (session) lines.push(`session_id: ${session}`);
472
472
 
473
- const sessionMember = firstNonEmptyString(activeScope.sessionMember, activeScope.vpId);
474
- if (sessionMember) lines.push(`session_member: ${sessionMember}`);
475
-
476
473
  const membersLine = renderSessionMembersLine(activeScope.sessionMembers || activeScope.members);
477
474
  if (membersLine) lines.push(`session_members: ${membersLine}`);
478
475