@yeaft/webchat-agent 0.1.633 → 0.1.634
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 +1 -1
- package/unify/dream-v2/apply.js +41 -72
- package/unify/dream-v2/prompts/index.js +52 -0
- package/unify/dream-v2/triage.js +20 -53
package/package.json
CHANGED
package/unify/dream-v2/apply.js
CHANGED
|
@@ -26,6 +26,7 @@ import { withDreamMarker } from './state.js';
|
|
|
26
26
|
import { batchSourcesForApply, needsBatchedApply, truncateMessage } from './segment.js';
|
|
27
27
|
import { snapshotScope } from './snapshot.js';
|
|
28
28
|
import { parseJsonSafe } from './triage.js';
|
|
29
|
+
import { render } from './prompts/index.js';
|
|
29
30
|
|
|
30
31
|
const SYSTEM = `You are the Apply stage of a dream pipeline. You rewrite a single scope's memory.md and summary.md based on recent group conversations. Reply with strict JSON only — no prose, no fences.`;
|
|
31
32
|
|
|
@@ -42,53 +43,17 @@ const SYSTEM = `You are the Apply stage of a dream pipeline. You rewrite a singl
|
|
|
42
43
|
* }} ctx
|
|
43
44
|
*/
|
|
44
45
|
export function buildUpdatePrompt(ctx) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
lines.push(ctx.memoryMd || '');
|
|
57
|
-
lines.push('"""');
|
|
58
|
-
lines.push('');
|
|
59
|
-
lines.push('Current summary.md:');
|
|
60
|
-
lines.push('"""');
|
|
61
|
-
lines.push(ctx.summaryMd || '');
|
|
62
|
-
lines.push('"""');
|
|
63
|
-
lines.push('');
|
|
64
|
-
lines.push('Recent conversations:');
|
|
65
|
-
for (const src of (ctx.sources || [])) {
|
|
66
|
-
lines.push('');
|
|
67
|
-
lines.push(`[group/${src.groupId}]`);
|
|
68
|
-
for (const m of (src.diff || [])) {
|
|
69
|
-
const head = `[${m.role || 'message'}${m.kind === 'overlap' ? ' (already processed)' : ''}]`;
|
|
70
|
-
lines.push(head);
|
|
71
|
-
lines.push(truncateMessage(m.body || ''));
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
lines.push('');
|
|
75
|
-
lines.push('Task:');
|
|
76
|
-
lines.push('- Extract from these conversations what is relevant to THIS scope.');
|
|
77
|
-
lines.push('- Integrate it into memory.md (reorganize sections if needed).');
|
|
78
|
-
lines.push('- Drop stale or contradicted entries.');
|
|
79
|
-
lines.push('- Rewrite summary.md (1–3 sentences).');
|
|
80
|
-
lines.push('- The same conversations are being processed for OTHER scopes too.');
|
|
81
|
-
lines.push(' Only handle what is relevant here. Ignore the rest.');
|
|
82
|
-
lines.push('');
|
|
83
|
-
lines.push('Hard rules:');
|
|
84
|
-
lines.push('- Never read or reference any other scope\'s files.');
|
|
85
|
-
lines.push('- Never modify VP system-prompt, group charter, or user preferences.');
|
|
86
|
-
lines.push('- If something contradicts a charter, annotate with');
|
|
87
|
-
lines.push(' "⚠️ contradicts charter — verify which is current" and continue.');
|
|
88
|
-
lines.push('');
|
|
89
|
-
lines.push('Reply with strict JSON of the shape:');
|
|
90
|
-
lines.push('{ "memory_md": "...", "summary_md": "..." }');
|
|
91
|
-
return lines.join('\n');
|
|
46
|
+
const batchHeader = (ctx.batchInfo && ctx.batchInfo.total > 1)
|
|
47
|
+
? `This is batch ${ctx.batchInfo.index} of ${ctx.batchInfo.total}.\nEarlier batches have already been folded into the current memory.md below.\n`
|
|
48
|
+
: '';
|
|
49
|
+
const sources = renderSourceBlocks(ctx.sources);
|
|
50
|
+
return render('update', {
|
|
51
|
+
target: ctx.target,
|
|
52
|
+
batchHeader,
|
|
53
|
+
memoryMd: ctx.memoryMd || '',
|
|
54
|
+
summaryMd: ctx.summaryMd || '',
|
|
55
|
+
sources,
|
|
56
|
+
});
|
|
92
57
|
}
|
|
93
58
|
|
|
94
59
|
/**
|
|
@@ -102,36 +67,40 @@ export function buildUpdatePrompt(ctx) {
|
|
|
102
67
|
* }} ctx
|
|
103
68
|
*/
|
|
104
69
|
export function buildCreatePrompt(ctx) {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
lines.push('Source conversations:');
|
|
111
|
-
for (const src of (ctx.sources || [])) {
|
|
70
|
+
const sources = renderSourceBlocks(ctx.sources);
|
|
71
|
+
let siblingsBlock = '';
|
|
72
|
+
if (ctx.siblingTopics && ctx.siblingTopics.length > 0) {
|
|
73
|
+
const lines = ['For tone reference, sibling/parent topic summaries:'];
|
|
74
|
+
for (const t of ctx.siblingTopics) lines.push(` - ${t.path}: ${oneLine(t.summary)}`);
|
|
112
75
|
lines.push('');
|
|
113
|
-
lines.
|
|
76
|
+
siblingsBlock = lines.join('\n');
|
|
77
|
+
}
|
|
78
|
+
return render('create', {
|
|
79
|
+
target: ctx.target,
|
|
80
|
+
sources,
|
|
81
|
+
siblingsBlock,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Render a list of `(groupId, diff)` source blocks for inclusion in the
|
|
87
|
+
* update / create prompts. Single-source aware: omits leading blank line
|
|
88
|
+
* if there's only one source, to keep small prompts compact.
|
|
89
|
+
*
|
|
90
|
+
* @param {Array<{ groupId: string, diff: Array<object> }>} sources
|
|
91
|
+
*/
|
|
92
|
+
function renderSourceBlocks(sources) {
|
|
93
|
+
const out = [];
|
|
94
|
+
for (const src of (sources || [])) {
|
|
95
|
+
out.push('');
|
|
96
|
+
out.push(`[group/${src.groupId}]`);
|
|
114
97
|
for (const m of (src.diff || [])) {
|
|
115
98
|
const head = `[${m.role || 'message'}${m.kind === 'overlap' ? ' (already processed)' : ''}]`;
|
|
116
|
-
|
|
117
|
-
|
|
99
|
+
out.push(head);
|
|
100
|
+
out.push(truncateMessage(m.body || ''));
|
|
118
101
|
}
|
|
119
102
|
}
|
|
120
|
-
|
|
121
|
-
if (ctx.siblingTopics && ctx.siblingTopics.length > 0) {
|
|
122
|
-
lines.push('For tone reference, sibling/parent topic summaries:');
|
|
123
|
-
for (const t of ctx.siblingTopics) {
|
|
124
|
-
lines.push(` - ${t.path}: ${oneLine(t.summary)}`);
|
|
125
|
-
}
|
|
126
|
-
lines.push('');
|
|
127
|
-
}
|
|
128
|
-
lines.push('Task:');
|
|
129
|
-
lines.push('1. Write memory.md from scratch with reasonable section structure.');
|
|
130
|
-
lines.push('2. Write summary.md (1–3 sentences).');
|
|
131
|
-
lines.push('');
|
|
132
|
-
lines.push('Reply with strict JSON of the shape:');
|
|
133
|
-
lines.push('{ "memory_md": "...", "summary_md": "..." }');
|
|
134
|
-
return lines.join('\n');
|
|
103
|
+
return out.join('\n').replace(/^\n/, '');
|
|
135
104
|
}
|
|
136
105
|
|
|
137
106
|
/**
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dream-v2/prompts/index.js — load + render the four dream prompts.
|
|
3
|
+
*
|
|
4
|
+
* Prompts live as `.md` files in this directory. We read once and cache.
|
|
5
|
+
* Rendering is plain `{{name}}` substitution; no nested logic, no escaping —
|
|
6
|
+
* the prompts are inputs to an LLM and the templates are author-controlled.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFileSync } from 'fs';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
|
|
15
|
+
const FILES = {
|
|
16
|
+
triagePass1: 'triage-pass1.md',
|
|
17
|
+
triagePass2: 'triage-pass2.md',
|
|
18
|
+
update: 'update.md',
|
|
19
|
+
create: 'create.md',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** @type {Record<string, string>} */
|
|
23
|
+
const cache = {};
|
|
24
|
+
|
|
25
|
+
function load(name) {
|
|
26
|
+
if (cache[name]) return cache[name];
|
|
27
|
+
const file = FILES[name];
|
|
28
|
+
if (!file) throw new Error(`prompts: unknown template ${name}`);
|
|
29
|
+
const txt = readFileSync(join(__dirname, file), 'utf8');
|
|
30
|
+
cache[name] = txt;
|
|
31
|
+
return txt;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Render a template with `{{name}}` substitution. Missing keys throw —
|
|
36
|
+
* silent rendering of half-built prompts has bitten us before.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} name
|
|
39
|
+
* @param {Record<string, string>} vars
|
|
40
|
+
*/
|
|
41
|
+
export function render(name, vars) {
|
|
42
|
+
const tpl = load(name);
|
|
43
|
+
return tpl.replace(/\{\{(\w+)\}\}/g, (_m, key) => {
|
|
44
|
+
if (!(key in vars)) throw new Error(`prompts.${name}: missing var ${key}`);
|
|
45
|
+
return vars[key];
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Test-only: drop the in-memory cache so a fresh read re-loads from disk. */
|
|
50
|
+
export function _resetCache() {
|
|
51
|
+
for (const k of Object.keys(cache)) delete cache[k];
|
|
52
|
+
}
|
package/unify/dream-v2/triage.js
CHANGED
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
|
|
38
38
|
import { isValidTopic } from '../memory/store-v2.js';
|
|
39
39
|
import { truncateMessage } from './segment.js';
|
|
40
|
+
import { render } from './prompts/index.js';
|
|
40
41
|
|
|
41
42
|
const SYSTEM = `You are the Triage stage of a dream pipeline that decides which scopes a recent group conversation should affect. Reply with strict JSON only — no prose, no markdown fences.`;
|
|
42
43
|
|
|
@@ -87,38 +88,21 @@ export function applyHardRules({ groupId, messages }) {
|
|
|
87
88
|
* @param {{ groupId: string, messages: Array<object>, topicSummaries: Array<{ path: string, summary: string }> }} ctx
|
|
88
89
|
*/
|
|
89
90
|
export function buildPass1Prompt(ctx) {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
'',
|
|
95
|
-
'Do NOT mention vp/, group/, or feature/ scopes — those are handled by hard rules.',
|
|
96
|
-
'',
|
|
97
|
-
`Group: ${ctx.groupId}`,
|
|
98
|
-
'',
|
|
99
|
-
'Existing topic scopes (path — summary):',
|
|
100
|
-
];
|
|
101
|
-
if (!ctx.topicSummaries || ctx.topicSummaries.length === 0) {
|
|
102
|
-
lines.push(' (none)');
|
|
103
|
-
} else {
|
|
104
|
-
for (const t of ctx.topicSummaries) {
|
|
105
|
-
lines.push(` - ${t.path} — ${oneLine(t.summary)}`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
lines.push('', 'Conversation:');
|
|
91
|
+
const topicSummaries = (!ctx.topicSummaries || ctx.topicSummaries.length === 0)
|
|
92
|
+
? ' (none)'
|
|
93
|
+
: ctx.topicSummaries.map(t => ` - ${t.path} — ${oneLine(t.summary)}`).join('\n');
|
|
94
|
+
const conv = [];
|
|
109
95
|
for (const m of (ctx.messages || [])) {
|
|
110
96
|
const head = `[${m.role || 'message'}${m.kind === 'overlap' ? ' (already processed)' : ''}]`;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
97
|
+
conv.push(head);
|
|
98
|
+
conv.push(truncateMessage(m.body || ''));
|
|
99
|
+
conv.push('');
|
|
114
100
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
lines.push('}');
|
|
121
|
-
return lines.join('\n');
|
|
101
|
+
return render('triagePass1', {
|
|
102
|
+
groupId: ctx.groupId,
|
|
103
|
+
topicSummaries,
|
|
104
|
+
conversation: conv.join('\n').trimEnd(),
|
|
105
|
+
});
|
|
122
106
|
}
|
|
123
107
|
|
|
124
108
|
/**
|
|
@@ -127,30 +111,13 @@ export function buildPass1Prompt(ctx) {
|
|
|
127
111
|
* @param {{ description: string, existingTopics: Array<{ path: string, summary: string }> }} ctx
|
|
128
112
|
*/
|
|
129
113
|
export function buildPass2Prompt(ctx) {
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
`Description: ${ctx.description}`,
|
|
138
|
-
'',
|
|
139
|
-
'Existing topics:',
|
|
140
|
-
];
|
|
141
|
-
if (!ctx.existingTopics || ctx.existingTopics.length === 0) {
|
|
142
|
-
lines.push(' (none)');
|
|
143
|
-
} else {
|
|
144
|
-
for (const t of ctx.existingTopics) {
|
|
145
|
-
lines.push(` - ${t.path} — ${oneLine(t.summary)}`);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
lines.push('');
|
|
149
|
-
lines.push('Reply with strict JSON, exactly one of:');
|
|
150
|
-
lines.push(' { "decision": "match", "path": "<existing path>" }');
|
|
151
|
-
lines.push(' { "decision": "new", "path": "<new ≤2-segment path>" }');
|
|
152
|
-
lines.push(' { "decision": "none" }');
|
|
153
|
-
return lines.join('\n');
|
|
114
|
+
const existingTopics = (!ctx.existingTopics || ctx.existingTopics.length === 0)
|
|
115
|
+
? ' (none)'
|
|
116
|
+
: ctx.existingTopics.map(t => ` - ${t.path} — ${oneLine(t.summary)}`).join('\n');
|
|
117
|
+
return render('triagePass2', {
|
|
118
|
+
description: ctx.description,
|
|
119
|
+
existingTopics,
|
|
120
|
+
});
|
|
154
121
|
}
|
|
155
122
|
|
|
156
123
|
/**
|