@yeaft/webchat-agent 0.1.762 → 0.1.766
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/bin/yeaft-stats.js +165 -0
- package/connection/message-router.js +6 -9
- package/package.json +3 -2
- package/unify/cli.js +16 -0
- package/unify/dream-v2/prompts/index.js +4 -2
- package/unify/dream-v2/session-wiring.js +5 -4
- package/unify/dream-v2/triage.js +7 -10
- package/unify/engine.js +42 -88
- package/unify/groups/pre-flow.js +5 -5
- package/unify/prompts.js +35 -202
- package/unify/session.js +18 -3
- package/unify/stats/format.js +31 -0
- package/unify/stats/tool-usage.js +324 -0
- package/unify/sub-agent/runner.js +4 -19
- package/unify/templates/plan-instruction.md +42 -0
- package/unify/templates/tool-guidance.md +41 -0
- package/unify/tools/index.js +13 -22
- package/unify/tools/start-plan.js +133 -0
- package/unify/tools/todo-write.js +125 -0
- package/unify/vp/vp-crud.js +1 -0
- package/unify/vp/vp-loader.js +2 -1
- package/unify/vp/vp-store.js +8 -0
- package/unify/web-bridge.js +93 -145
- package/unify/features/store.js +0 -583
- package/unify/features/summary.js +0 -250
- package/unify/tools/feature-tools.js +0 -713
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* summary.js — Feature multi-VP collaboration summary protocol.
|
|
3
|
-
*
|
|
4
|
-
* Owns:
|
|
5
|
-
* - postSummary() — write a `type=summary` message to the group jsonl
|
|
6
|
-
* - buildSummaryReminder() — compute the §Δ31.4 3-AND soft reminder shape
|
|
7
|
-
* - buildFeatureCtxMemories() — feature-memory top-5 (post-rip stub: returns [])
|
|
8
|
-
*
|
|
9
|
-
* Hard boundaries:
|
|
10
|
-
* - does NOT touch jsonl rotation internals (calls group.appendMessage)
|
|
11
|
-
* - does NOT write feature-memory shards — the H2-AMS rip retired the shard
|
|
12
|
-
* store; feature memory now flows through Dream V2's scope merge instead.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { join } from 'path';
|
|
16
|
-
|
|
17
|
-
// ─── §Δ31.4 soft-reminder thresholds ─────────────────────────────
|
|
18
|
-
/** Must be initiator AND members>1 AND (age≥20min OR turns≥10). */
|
|
19
|
-
export const SUMMARY_REMINDER_MIN_MEMBERS = 2;
|
|
20
|
-
export const SUMMARY_REMINDER_MIN_TURNS = 10;
|
|
21
|
-
export const SUMMARY_REMINDER_MIN_AGE_MS = 20 * 60 * 1000;
|
|
22
|
-
|
|
23
|
-
// ─── extractor limits ────────────────────────────────────────────
|
|
24
|
-
export const EXTRACT_MIN_ENTRIES = 2;
|
|
25
|
-
export const EXTRACT_MAX_ENTRIES = 5;
|
|
26
|
-
|
|
27
|
-
/** Semantic progress anchors — string labels accepted alongside 0-100 number. */
|
|
28
|
-
export const PROGRESS_ANCHORS = Object.freeze([
|
|
29
|
-
'blocked', 'planning', 'in-progress', 'review', 'milestone', 'shipped', 'done',
|
|
30
|
-
]);
|
|
31
|
-
|
|
32
|
-
/** Whitelist of R6 kinds emitted by the summary-extractor. */
|
|
33
|
-
const EXTRACT_KINDS = Object.freeze(['progress', 'decision']);
|
|
34
|
-
|
|
35
|
-
/** Shard routing for each extracted kind (§Δ25.2 feature-memory fixed set). */
|
|
36
|
-
const KIND_TO_SHARD = Object.freeze({
|
|
37
|
-
progress: 'progress',
|
|
38
|
-
decision: 'decision',
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// ─── (B) postSummary ─────────────────────────────────────────────
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Write a `type=summary` message to the group log, then auto-run the
|
|
45
|
-
* extractor to derive feature-memory entries.
|
|
46
|
-
*
|
|
47
|
-
* @param {{
|
|
48
|
-
* group: import('../groups/group-store.js').GroupHandle,
|
|
49
|
-
* featureId: string,
|
|
50
|
-
* fromVpId: string,
|
|
51
|
-
* body: string,
|
|
52
|
-
* progress?: number|string,
|
|
53
|
-
* supersedes?: string[],
|
|
54
|
-
* memoryDir: string, // groups/<g>/features/<f>/memory/
|
|
55
|
-
* now?: () => number,
|
|
56
|
-
* extractor?: (body:string) => Array<{kind:string,body:string,tags?:string[]}>
|
|
57
|
-
* }} opts
|
|
58
|
-
* @returns {{ message: any, memoryIds: string[], supersededSummaryIds: string[] }}
|
|
59
|
-
*/
|
|
60
|
-
export function postSummary(opts) {
|
|
61
|
-
const {
|
|
62
|
-
group,
|
|
63
|
-
featureId,
|
|
64
|
-
fromVpId,
|
|
65
|
-
body,
|
|
66
|
-
progress,
|
|
67
|
-
supersedes,
|
|
68
|
-
memoryDir,
|
|
69
|
-
now = () => Date.now(),
|
|
70
|
-
extractor = defaultExtractor,
|
|
71
|
-
} = opts || {};
|
|
72
|
-
|
|
73
|
-
if (!group || typeof group.appendMessage !== 'function') {
|
|
74
|
-
throw new Error('postSummary: group handle required');
|
|
75
|
-
}
|
|
76
|
-
if (!featureId) throw new Error('postSummary: featureId required');
|
|
77
|
-
if (!fromVpId) throw new Error('postSummary: fromVpId required');
|
|
78
|
-
if (typeof body !== 'string' || !body.trim()) {
|
|
79
|
-
throw new Error('postSummary: body required (non-empty string)');
|
|
80
|
-
}
|
|
81
|
-
if (progress != null) {
|
|
82
|
-
if (typeof progress === 'string') {
|
|
83
|
-
if (!PROGRESS_ANCHORS.includes(progress)) {
|
|
84
|
-
throw new Error(`postSummary: progress string must be one of: ${PROGRESS_ANCHORS.join(', ')}`);
|
|
85
|
-
}
|
|
86
|
-
} else {
|
|
87
|
-
const p = Number(progress);
|
|
88
|
-
if (!Number.isFinite(p) || p < 0 || p > 100) {
|
|
89
|
-
throw new Error('postSummary: progress must be number in [0,100] or a semantic anchor string');
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const supersedesArr = Array.isArray(supersedes)
|
|
94
|
-
? supersedes.filter((s) => typeof s === 'string' && s)
|
|
95
|
-
: [];
|
|
96
|
-
|
|
97
|
-
// 1) Append the summary message to the group jsonl log (type=summary).
|
|
98
|
-
const stored = group.appendMessage({
|
|
99
|
-
from: fromVpId,
|
|
100
|
-
role: 'assistant',
|
|
101
|
-
text: body,
|
|
102
|
-
featureId,
|
|
103
|
-
meta: {
|
|
104
|
-
type: 'summary',
|
|
105
|
-
progress: progress == null ? null : (typeof progress === 'string' ? progress : Number(progress)),
|
|
106
|
-
supersedes: supersedesArr,
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// 2) Feature-memory extraction is now owned by Dream V2 (per-group diff →
|
|
111
|
-
// triage → merge by target scope → atomic segments). The legacy in-line
|
|
112
|
-
// shard-store extractor was retired in the H2-AMS rip; we leave
|
|
113
|
-
// `memoryIds` empty so callers don't depend on inline-extracted IDs.
|
|
114
|
-
const memoryIds = [];
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
message: stored,
|
|
118
|
-
memoryIds,
|
|
119
|
-
supersededSummaryIds: supersedesArr,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/** Clamp raw extractor output to [EXTRACT_MIN_ENTRIES..EXTRACT_MAX_ENTRIES]. */
|
|
124
|
-
function clampExtracted(arr) {
|
|
125
|
-
const cleaned = arr.filter((x) => x && typeof x.body === 'string' && x.body.trim());
|
|
126
|
-
if (cleaned.length === 0) return [];
|
|
127
|
-
return cleaned.slice(0, EXTRACT_MAX_ENTRIES);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// ─── (C) default extractor ───────────────────────────────────────
|
|
131
|
-
|
|
132
|
-
export function defaultExtractor(body) {
|
|
133
|
-
if (typeof body !== 'string') return [];
|
|
134
|
-
const lines = body
|
|
135
|
-
.split(/\r?\n/)
|
|
136
|
-
.map((l) => l.replace(/^[\s*\-•]+/, '').trim())
|
|
137
|
-
.filter(Boolean);
|
|
138
|
-
const out = [];
|
|
139
|
-
for (const line of lines) {
|
|
140
|
-
const lower = line.toLowerCase();
|
|
141
|
-
let kind = 'progress';
|
|
142
|
-
if (/^(decide|decision|chose|chosen|pick|choose)\b/.test(lower)) {
|
|
143
|
-
kind = 'decision';
|
|
144
|
-
}
|
|
145
|
-
out.push({ kind, body: line });
|
|
146
|
-
if (out.length >= EXTRACT_MAX_ENTRIES) break;
|
|
147
|
-
}
|
|
148
|
-
if (out.length < EXTRACT_MIN_ENTRIES && body.trim()) {
|
|
149
|
-
while (out.length < EXTRACT_MIN_ENTRIES) {
|
|
150
|
-
out.push({ kind: 'progress', body: body.trim() });
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return out;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// ─── (D) soft reminder builder ───────────────────────────────────
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Build the `featureCtx.summaryReminder` shape consumed by 334e's prompt.
|
|
160
|
-
*
|
|
161
|
-
* §Δ31.4 conditions:
|
|
162
|
-
* (1) feature.members.length > 1
|
|
163
|
-
* (2) caller role === 'initiator' (i.e. currentVpId === feature.initiator)
|
|
164
|
-
* (3) (now - lastSummaryAt) ≥ 20 min OR nonSummaryTurns ≥ 10
|
|
165
|
-
*
|
|
166
|
-
* @param {{
|
|
167
|
-
* feature: { initiator?: string, members?: string[] },
|
|
168
|
-
* currentVpId: string,
|
|
169
|
-
* lastSummaryAt: number,
|
|
170
|
-
* nonSummaryTurns: number,
|
|
171
|
-
* now?: number,
|
|
172
|
-
* }} input
|
|
173
|
-
*/
|
|
174
|
-
export function buildSummaryReminder(input) {
|
|
175
|
-
const { feature, currentVpId, lastSummaryAt = 0, nonSummaryTurns = 0 } = input || {};
|
|
176
|
-
const now = typeof input?.now === 'number' ? input.now : Date.now();
|
|
177
|
-
const reasons = [];
|
|
178
|
-
|
|
179
|
-
if (!feature || typeof feature !== 'object') {
|
|
180
|
-
return { triggered: false, reasons: ['no-feature'], nonSummaryCount: nonSummaryTurns, lastSummaryAt, now };
|
|
181
|
-
}
|
|
182
|
-
const members = Array.isArray(feature.members) ? feature.members : [];
|
|
183
|
-
const isInitiator = !!currentVpId && feature.initiator === currentVpId;
|
|
184
|
-
|
|
185
|
-
if (!isInitiator) reasons.push('not-initiator');
|
|
186
|
-
if (members.length <= SUMMARY_REMINDER_MIN_MEMBERS - 1) reasons.push('solo-feature');
|
|
187
|
-
|
|
188
|
-
const ageMs = lastSummaryAt > 0 ? now - lastSummaryAt : Number.POSITIVE_INFINITY;
|
|
189
|
-
const ageOk = ageMs >= SUMMARY_REMINDER_MIN_AGE_MS;
|
|
190
|
-
const turnsOk = nonSummaryTurns >= SUMMARY_REMINDER_MIN_TURNS;
|
|
191
|
-
if (!ageOk && !turnsOk) reasons.push('too-soon');
|
|
192
|
-
|
|
193
|
-
const triggered = isInitiator && members.length >= SUMMARY_REMINDER_MIN_MEMBERS && (ageOk || turnsOk);
|
|
194
|
-
return {
|
|
195
|
-
triggered,
|
|
196
|
-
reasons,
|
|
197
|
-
nonSummaryCount: nonSummaryTurns,
|
|
198
|
-
lastSummaryAt,
|
|
199
|
-
now,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// ─── (E) feature_ctx top-5 feature-memory builder ────────────────
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Assemble feature-memory top-5 for 334e's `featureCtx.memories` field.
|
|
207
|
-
* Post-rip stub: the underlying shard store was retired in the H2-AMS rip;
|
|
208
|
-
* Dream V2 owns feature-scope memory now. Returning `[]` keeps the prompt
|
|
209
|
-
* shape valid until a follow-up wires the new scope-summary read path.
|
|
210
|
-
*
|
|
211
|
-
* @param {string} memoryDir unused (kept for callsite compatibility)
|
|
212
|
-
* @param {{ tags?: string[], top?: number }} [opts]
|
|
213
|
-
* @returns {Array<{body:string, shard:string, authoredBy?:string}>}
|
|
214
|
-
*/
|
|
215
|
-
export function buildFeatureCtxMemories(_memoryDir, _opts = {}) {
|
|
216
|
-
return [];
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// ─── (F) related-feature ACL fail-closed gate ─────────────────────
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Return memory/summary hints for a related feature only when ACL grants.
|
|
223
|
-
* Caller passes the FeatureStore so we can ask `canAccessRelated()`.
|
|
224
|
-
*
|
|
225
|
-
* @param {{
|
|
226
|
-
* featureStore: import('./store.js').FeatureStore,
|
|
227
|
-
* currentFeatureId: string,
|
|
228
|
-
* otherFeatureId: string,
|
|
229
|
-
* vpId: string,
|
|
230
|
-
* groupsRoot: string,
|
|
231
|
-
* top?: number,
|
|
232
|
-
* }} input
|
|
233
|
-
* @returns {null | { id:string, title:string, members:string[], updatedAt?:number, memories:Array<{body:string,shard:string}> }}
|
|
234
|
-
*/
|
|
235
|
-
export function getRelatedFeatureCtx(input) {
|
|
236
|
-
const { featureStore, currentFeatureId, otherFeatureId, vpId, groupsRoot, top = 2 } = input || {};
|
|
237
|
-
if (!featureStore || !currentFeatureId || !otherFeatureId || !vpId || !groupsRoot) return null;
|
|
238
|
-
if (!featureStore.canAccessRelated(currentFeatureId, otherFeatureId, vpId)) return null;
|
|
239
|
-
const other = featureStore.get(otherFeatureId);
|
|
240
|
-
if (!other || !other.groupId) return null;
|
|
241
|
-
const memoryDir = join(groupsRoot, other.groupId, 'features', other.id, 'memory');
|
|
242
|
-
const mems = buildFeatureCtxMemories(memoryDir, { top });
|
|
243
|
-
return {
|
|
244
|
-
id: other.id,
|
|
245
|
-
title: other.title || other.id,
|
|
246
|
-
members: Array.isArray(other.members) ? other.members.slice() : [],
|
|
247
|
-
updatedAt: other.updatedAt || 0,
|
|
248
|
-
memories: mems,
|
|
249
|
-
};
|
|
250
|
-
}
|