@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.
- package/bin/yeaft-stats.js +3 -3
- package/connection/message-router.js +73 -44
- package/context.js +4 -4
- package/expert-roles.js +1 -1
- package/index.js +1 -1
- package/package.json +4 -4
- package/service/index.js +1 -1
- package/{unify → yeaft}/attachments.js +7 -7
- package/{unify → yeaft}/cli.js +4 -4
- package/{unify → yeaft}/compact/compactor.js +10 -10
- package/{unify → yeaft}/compact/orchestrator.js +7 -13
- package/{unify → yeaft}/config-api.js +12 -12
- package/{unify → yeaft}/config.js +19 -19
- package/{unify → yeaft}/conversation/migrate-messages-threadid.js +1 -1
- package/{unify → yeaft}/conversation/persist.js +194 -65
- package/{unify → yeaft}/conversation/search.js +28 -23
- package/{unify → yeaft}/debug-trace.js +1 -1
- package/{unify → yeaft}/dream-v2/apply.js +28 -11
- package/{unify → yeaft}/dream-v2/limits.js +3 -3
- package/{unify → yeaft}/dream-v2/prompts/index.js +8 -1
- package/{unify → yeaft}/dream-v2/runner.js +13 -6
- package/{unify → yeaft}/dream-v2/session-wiring.js +2 -2
- package/{unify → yeaft}/dream-v2/triage.js +13 -8
- package/{unify → yeaft}/effort.js +2 -2
- package/{unify → yeaft}/engine.js +10 -10
- package/{unify → yeaft}/eval/run-eval.js +11 -11
- package/{unify → yeaft}/eval/runner.js +1 -1
- package/{unify → yeaft}/groups/feature-flag.js +2 -2
- package/{unify → yeaft}/groups/index.js +2 -2
- package/{unify → yeaft}/groups/pre-flow.js +15 -3
- package/{unify → yeaft}/groups/project-doc.js +1 -1
- package/{unify → yeaft}/history-compact.js +3 -3
- package/{unify → yeaft}/index.js +1 -1
- package/{unify → yeaft}/init.js +15 -9
- package/{unify → yeaft}/llm/adapter.js +1 -1
- package/{unify → yeaft}/llm/anthropic.js +2 -2
- package/{unify → yeaft}/llm/models-dev.js +10 -3
- package/{unify → yeaft}/llm/openai-responses.js +2 -2
- package/{unify → yeaft}/llm/router.js +1 -1
- package/{unify → yeaft}/mcp.js +1 -1
- package/{unify → yeaft}/memory/adjust.js +2 -4
- package/{unify → yeaft}/memory/ams-registry.js +1 -1
- package/{unify → yeaft}/memory/ams.js +2 -4
- package/{unify → yeaft}/memory/preflow.js +2 -6
- package/{unify → yeaft}/memory/seed-backfill.js +42 -2
- package/{unify → yeaft}/memory/segment.js +1 -1
- package/{unify → yeaft}/memory/store-v2.js +120 -73
- package/{unify → yeaft}/models.js +3 -3
- package/{unify → yeaft}/pair-sanitize.js +2 -2
- package/{unify → yeaft}/personas.js +1 -1
- package/{unify → yeaft}/prompts.js +1 -1
- package/{unify → yeaft}/routing/index.js +1 -1
- package/{unify → yeaft}/routing/router.js +1 -1
- package/{unify → yeaft}/session.js +19 -9
- package/{unify → yeaft}/skills.js +2 -2
- package/{unify → yeaft}/stats/format.js +1 -1
- package/{unify → yeaft}/stop-hooks.js +1 -1
- package/{unify → yeaft}/sub-agent/runner.js +1 -1
- package/{unify → yeaft}/tools/ask-user.js +2 -2
- package/{unify → yeaft}/tools/enter-worktree.js +1 -1
- package/{unify → yeaft}/tools/exit-worktree.js +1 -1
- package/{unify → yeaft}/tools/mcp-tools.js +1 -1
- package/{unify → yeaft}/tools/registry.js +2 -2
- package/{unify → yeaft}/tools/skill.js +1 -1
- package/{unify → yeaft}/tools/start-plan.js +1 -1
- package/{unify → yeaft}/tools/types.js +3 -3
- package/{unify → yeaft}/tools/web-search.js +1 -1
- package/{unify → yeaft}/turn-utils.js +1 -1
- package/{unify → yeaft}/vp/index.js +1 -1
- package/{unify → yeaft}/vp/registry.js +1 -1
- package/{unify → yeaft}/vp/vp-bridge.js +8 -8
- package/{unify → yeaft}/vp/vp-crud.js +7 -24
- package/{unify → yeaft}/web-bridge.js +230 -230
- /package/{unify → yeaft}/archive/tool-results.js +0 -0
- /package/{unify → yeaft}/archive/trace.js +0 -0
- /package/{unify → yeaft}/archive/turn-archive.js +0 -0
- /package/{unify → yeaft}/compact/decisions-log.js +0 -0
- /package/{unify → yeaft}/compact/triggers.js +0 -0
- /package/{unify → yeaft}/compact/turn-group.js +0 -0
- /package/{unify → yeaft}/dream-v2/merge.js +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/create.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/extract-group.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/extract-topic.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/extract-user.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/extract-vp.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/summarize-scope.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/triage-pass1.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/triage-pass2.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/update.md +0 -0
- /package/{unify → yeaft}/dream-v2/schedule.js +0 -0
- /package/{unify → yeaft}/dream-v2/segment.js +0 -0
- /package/{unify → yeaft}/dream-v2/snapshot.js +0 -0
- /package/{unify → yeaft}/dream-v2/state.js +0 -0
- /package/{unify → yeaft}/eval/cases/e2e.js +0 -0
- /package/{unify → yeaft}/eval/cases/memory.js +0 -0
- /package/{unify → yeaft}/eval/cases/skills.js +0 -0
- /package/{unify → yeaft}/eval/cases/tool-use.js +0 -0
- /package/{unify → yeaft}/groups/coordinator.js +0 -0
- /package/{unify → yeaft}/groups/group-config.js +0 -0
- /package/{unify → yeaft}/groups/group-crud.js +0 -0
- /package/{unify → yeaft}/groups/group-store.js +0 -0
- /package/{unify → yeaft}/groups/ids.js +0 -0
- /package/{unify → yeaft}/groups/roster.js +0 -0
- /package/{unify → yeaft}/groups/seed-default.js +0 -0
- /package/{unify → yeaft}/llm/credentials/github-copilot.js +0 -0
- /package/{unify → yeaft}/llm/credentials/index.js +0 -0
- /package/{unify → yeaft}/memory/budget.js +0 -0
- /package/{unify → yeaft}/memory/consolidate.js +0 -0
- /package/{unify → yeaft}/memory/index-db.js +0 -0
- /package/{unify → yeaft}/memory/keywords.js +0 -0
- /package/{unify → yeaft}/memory/segment-store.js +0 -0
- /package/{unify → yeaft}/memory/segment-sync.js +0 -0
- /package/{unify → yeaft}/memory/summary-store.js +0 -0
- /package/{unify → yeaft}/router/continuity.js +0 -0
- /package/{unify → yeaft}/router/thinking.js +0 -0
- /package/{unify → yeaft}/routing/loop-guard.js +0 -0
- /package/{unify → yeaft}/stats/tool-usage.js +0 -0
- /package/{unify → yeaft}/storage/atomic.js +0 -0
- /package/{unify → yeaft}/storage/compact.js +0 -0
- /package/{unify → yeaft}/storage/index.js +0 -0
- /package/{unify → yeaft}/storage/jsonl-index.js +0 -0
- /package/{unify → yeaft}/storage/jsonl-log.js +0 -0
- /package/{unify → yeaft}/storage/shard-index.js +0 -0
- /package/{unify → yeaft}/storage/shard-store.js +0 -0
- /package/{unify → yeaft}/sub-agent/spawned-prompt.js +0 -0
- /package/{unify → yeaft}/templates/base.md +0 -0
- /package/{unify → yeaft}/templates/common-rules.md +0 -0
- /package/{unify → yeaft}/templates/harness/router-handoff.md +0 -0
- /package/{unify → yeaft}/templates/harness/router-shape.md +0 -0
- /package/{unify → yeaft}/templates/harness/worker-shape.md +0 -0
- /package/{unify → yeaft}/templates/identity-yeaft.md +0 -0
- /package/{unify → yeaft}/templates/mode-dream.md +0 -0
- /package/{unify → yeaft}/templates/mode-unified.md +0 -0
- /package/{unify → yeaft}/templates/personas/explorer.md +0 -0
- /package/{unify → yeaft}/templates/personas/implementer.md +0 -0
- /package/{unify → yeaft}/templates/personas/researcher.md +0 -0
- /package/{unify → yeaft}/templates/personas/reviewer.md +0 -0
- /package/{unify → yeaft}/templates/plan-instruction.md +0 -0
- /package/{unify → yeaft}/templates/tool-guidance.md +0 -0
- /package/{unify → yeaft}/tool-folding/exec-log.js +0 -0
- /package/{unify → yeaft}/tool-folding/fallback-stub.js +0 -0
- /package/{unify → yeaft}/tool-folding/index.js +0 -0
- /package/{unify → yeaft}/tool-folding/reflection-prompt.js +0 -0
- /package/{unify → yeaft}/tool-folding/t1-reflector.js +0 -0
- /package/{unify → yeaft}/tool-folding/t2-reflector.js +0 -0
- /package/{unify → yeaft}/tools/agent.js +0 -0
- /package/{unify → yeaft}/tools/apply-patch.js +0 -0
- /package/{unify → yeaft}/tools/bash.js +0 -0
- /package/{unify → yeaft}/tools/close-agent.js +0 -0
- /package/{unify → yeaft}/tools/file-edit.js +0 -0
- /package/{unify → yeaft}/tools/file-read.js +0 -0
- /package/{unify → yeaft}/tools/file-write.js +0 -0
- /package/{unify → yeaft}/tools/glob.js +0 -0
- /package/{unify → yeaft}/tools/grep.js +0 -0
- /package/{unify → yeaft}/tools/history-search.js +0 -0
- /package/{unify → yeaft}/tools/image-generation.js +0 -0
- /package/{unify → yeaft}/tools/index.js +0 -0
- /package/{unify → yeaft}/tools/js-repl.js +0 -0
- /package/{unify → yeaft}/tools/list-agents.js +0 -0
- /package/{unify → yeaft}/tools/list-dir.js +0 -0
- /package/{unify → yeaft}/tools/notebook-edit.js +0 -0
- /package/{unify → yeaft}/tools/route-forward.js +0 -0
- /package/{unify → yeaft}/tools/send-message.js +0 -0
- /package/{unify → yeaft}/tools/todo-write.js +0 -0
- /package/{unify → yeaft}/tools/view-image.js +0 -0
- /package/{unify → yeaft}/tools/wait-agent.js +0 -0
- /package/{unify → yeaft}/tools/web-fetch.js +0 -0
- /package/{unify → yeaft}/vp/seed-defaults.js +0 -0
- /package/{unify → yeaft}/vp/seed-topup.js +0 -0
- /package/{unify → yeaft}/vp/stock-ids.js +0 -0
- /package/{unify → yeaft}/vp/thread-classifier.js +0 -0
- /package/{unify → yeaft}/vp/vp-loader.js +0 -0
- /package/{unify → yeaft}/vp/vp-store.js +0 -0
- /package/{unify → yeaft}/vp-status-broker.js +0 -0
|
@@ -2,46 +2,48 @@
|
|
|
2
2
|
* search.js — Conversation history search
|
|
3
3
|
*
|
|
4
4
|
* Simple keyword search across hot and cold messages.
|
|
5
|
-
* Reference: yeaft-unify-core-systems.md §4.3
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
9
8
|
import { join } from 'path';
|
|
10
|
-
import { parseMessage } from './persist.js';
|
|
9
|
+
import { parseMessage, parseSeqFromId } from './persist.js';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Search messages in a directory for a keyword.
|
|
14
13
|
*
|
|
15
14
|
* @param {string} dir — messages directory
|
|
16
15
|
* @param {string} keyword — search term (case-insensitive)
|
|
17
|
-
* @param {number} limit — max results
|
|
18
16
|
* @returns {object[]} — matching messages
|
|
19
17
|
*/
|
|
20
|
-
function searchDir(dir, keyword
|
|
18
|
+
function searchDir(dir, keyword) {
|
|
21
19
|
if (!existsSync(dir)) return [];
|
|
22
20
|
|
|
23
21
|
const lowerKeyword = keyword.toLowerCase();
|
|
24
22
|
const files = readdirSync(dir)
|
|
25
23
|
.filter(f => f.endsWith('.md'))
|
|
26
24
|
.sort()
|
|
27
|
-
.reverse(); // newest first
|
|
25
|
+
.reverse(); // newest first within one directory
|
|
28
26
|
|
|
29
27
|
const results = [];
|
|
30
28
|
for (const file of files) {
|
|
31
|
-
if (results.length >= limit) break;
|
|
32
|
-
|
|
33
29
|
const raw = readFileSync(join(dir, file), 'utf8');
|
|
34
|
-
if (raw.toLowerCase().includes(lowerKeyword))
|
|
35
|
-
const msg = parseMessage(raw);
|
|
36
|
-
if (msg) results.push(msg);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
30
|
+
if (!raw.toLowerCase().includes(lowerKeyword)) continue;
|
|
39
31
|
|
|
32
|
+
const msg = parseMessage(raw);
|
|
33
|
+
if (msg) results.push(msg);
|
|
34
|
+
}
|
|
40
35
|
return results;
|
|
41
36
|
}
|
|
42
37
|
|
|
38
|
+
function compareNewestFirst(a, b) {
|
|
39
|
+
const sa = parseSeqFromId(a?.id);
|
|
40
|
+
const sb = parseSeqFromId(b?.id);
|
|
41
|
+
if (Number.isFinite(sa) && Number.isFinite(sb) && sa !== sb) return sb - sa;
|
|
42
|
+
return String(b?.time || '').localeCompare(String(a?.time || ''));
|
|
43
|
+
}
|
|
44
|
+
|
|
43
45
|
/**
|
|
44
|
-
* Search
|
|
46
|
+
* Search Yeaft history (chat + group + legacy conversation) for a keyword.
|
|
45
47
|
*
|
|
46
48
|
* @param {string} dir — Yeaft root directory (e.g. ~/.yeaft)
|
|
47
49
|
* @param {string} keyword — search term
|
|
@@ -51,15 +53,18 @@ function searchDir(dir, keyword, limit) {
|
|
|
51
53
|
export function searchMessages(dir, keyword, limit = 20) {
|
|
52
54
|
if (!keyword || !keyword.trim()) return [];
|
|
53
55
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
const dirs = [
|
|
57
|
+
join(dir, 'chat', 'messages'),
|
|
58
|
+
join(dir, 'chat', 'cold'),
|
|
59
|
+
join(dir, 'group', 'messages'),
|
|
60
|
+
join(dir, 'group', 'cold'),
|
|
61
|
+
// Compatibility for profiles created before chat/group split.
|
|
62
|
+
join(dir, 'conversation', 'messages'),
|
|
63
|
+
join(dir, 'conversation', 'cold'),
|
|
64
|
+
];
|
|
63
65
|
|
|
64
|
-
return
|
|
66
|
+
return dirs
|
|
67
|
+
.flatMap(d => searchDir(d, keyword))
|
|
68
|
+
.sort(compareNewestFirst)
|
|
69
|
+
.slice(0, limit);
|
|
65
70
|
}
|
|
@@ -401,7 +401,7 @@ export class DebugTrace {
|
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
/**
|
|
404
|
-
* Fetch the recent debug history for the
|
|
404
|
+
* Fetch the recent debug history for the YeaftDebugPanel. Returns one
|
|
405
405
|
* record per LLM loop (ordered oldest → newest) with the structured
|
|
406
406
|
* fields the panel expects. JSON columns are parsed; truncated /
|
|
407
407
|
* malformed payloads degrade to null instead of failing the call.
|
|
@@ -120,12 +120,28 @@ export function targetToScope(target) {
|
|
|
120
120
|
if (!target || typeof target !== 'string') throw new Error('apply.targetToScope: target required');
|
|
121
121
|
if (target === 'user') return { kind: 'user' };
|
|
122
122
|
const segs = target.split('/').filter(Boolean);
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
return { kind: '
|
|
123
|
+
// Legacy scopes — explicitly rejected. Old data lives under .legacy/.
|
|
124
|
+
if (segs[0] === 'vp' || segs[0] === 'feature' || segs[0] === 'topic') {
|
|
125
|
+
throw new Error(`apply.targetToScope: legacy root scope ${JSON.stringify(target)} rejected — use group/<g>/${segs[0]}/...`);
|
|
126
|
+
}
|
|
127
|
+
if (segs[0] === 'group') {
|
|
128
|
+
if (segs.length === 2) return { kind: 'group', id: segs[1] };
|
|
129
|
+
// group/<g>/user
|
|
130
|
+
if (segs.length === 3 && segs[2] === 'user') {
|
|
131
|
+
return { kind: 'group-user', groupId: segs[1] };
|
|
132
|
+
}
|
|
133
|
+
// group/<g>/vp/<v>
|
|
134
|
+
if (segs.length === 4 && segs[2] === 'vp') {
|
|
135
|
+
return { kind: 'group-vp', groupId: segs[1], id: segs[3] };
|
|
136
|
+
}
|
|
137
|
+
// group/<g>/feature/<f>
|
|
138
|
+
if (segs.length === 4 && segs[2] === 'feature') {
|
|
139
|
+
return { kind: 'group-feature', groupId: segs[1], id: segs[3] };
|
|
140
|
+
}
|
|
141
|
+
// group/<g>/topic/<l1>[/<l2>]
|
|
142
|
+
if (segs[2] === 'topic' && (segs.length === 4 || segs.length === 5)) {
|
|
143
|
+
return { kind: 'group-topic', groupId: segs[1], path: segs.slice(3) };
|
|
144
|
+
}
|
|
129
145
|
}
|
|
130
146
|
throw new Error(`apply.targetToScope: malformed target ${JSON.stringify(target)}`);
|
|
131
147
|
}
|
|
@@ -252,11 +268,12 @@ export async function applyMergedTarget(merged, opts) {
|
|
|
252
268
|
|
|
253
269
|
function scopeRelDir(scope) {
|
|
254
270
|
switch (scope.kind) {
|
|
255
|
-
case 'user':
|
|
256
|
-
case '
|
|
257
|
-
case 'group':
|
|
258
|
-
case '
|
|
259
|
-
case '
|
|
271
|
+
case 'user': return 'user';
|
|
272
|
+
case 'group': return `group/${scope.id}`;
|
|
273
|
+
case 'group-user': return `group/${scope.groupId}/user`;
|
|
274
|
+
case 'group-vp': return `group/${scope.groupId}/vp/${scope.id}`;
|
|
275
|
+
case 'group-feature': return `group/${scope.groupId}/feature/${scope.id}`;
|
|
276
|
+
case 'group-topic': return `group/${scope.groupId}/topic/${scope.path.join('/')}`;
|
|
260
277
|
default: throw new Error(`apply.scopeRelDir: unknown kind ${scope.kind}`);
|
|
261
278
|
}
|
|
262
279
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* callers that want to override one knob without restating the rest.
|
|
7
7
|
*
|
|
8
8
|
* `loadLimitsFromConfig(config)` merges a `~/.yeaft/config.json`
|
|
9
|
-
* `
|
|
9
|
+
* `yeaft.dream` block on top of defaults; unknown keys are ignored,
|
|
10
10
|
* malformed values fall back to default.
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -35,12 +35,12 @@ export const DEFAULT_LIMITS = Object.freeze({
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* Merge a config object's `
|
|
38
|
+
* Merge a config object's `yeaft.dream` block on top of defaults.
|
|
39
39
|
* @param {object} [config]
|
|
40
40
|
*/
|
|
41
41
|
export function loadLimitsFromConfig(config) {
|
|
42
42
|
const out = { ...DEFAULT_LIMITS };
|
|
43
|
-
const ud = config && config.
|
|
43
|
+
const ud = config && config.yeaft && config.yeaft.dream;
|
|
44
44
|
if (!ud || typeof ud !== 'object') return out;
|
|
45
45
|
for (const k of Object.keys(out)) {
|
|
46
46
|
if (Object.prototype.hasOwnProperty.call(ud, k)) {
|
|
@@ -41,8 +41,15 @@ const FILES = {
|
|
|
41
41
|
export function extractTemplateForScope(scope) {
|
|
42
42
|
if (!scope || typeof scope !== 'string') return 'extractTopic';
|
|
43
43
|
if (scope === 'user') return 'extractUser';
|
|
44
|
-
|
|
44
|
+
// Nested group-isolated scopes must be matched BEFORE the bare `group/<g>`
|
|
45
|
+
// branch so VPs/topics/features under a group don't get the group template.
|
|
46
|
+
if (/^group\/[^/]+\/vp\//.test(scope)) return 'extractVp';
|
|
47
|
+
if (/^group\/[^/]+\/topic\//.test(scope)) return 'extractTopic';
|
|
48
|
+
if (/^group\/[^/]+\/user(?:\/|$)/.test(scope)) return 'extractUser';
|
|
45
49
|
if (scope.startsWith('group/')) return 'extractGroup';
|
|
50
|
+
// Legacy top-level vp/topic scopes (archived to .legacy/ on boot — kept
|
|
51
|
+
// here defensively in case something still constructs the old strings).
|
|
52
|
+
if (scope.startsWith('vp/')) return 'extractVp';
|
|
46
53
|
if (scope.startsWith('topic/')) return 'extractTopic';
|
|
47
54
|
return 'extractTopic';
|
|
48
55
|
}
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* pruneOldSnapshots()
|
|
30
30
|
*
|
|
31
31
|
* onProgress emits dream_progress events that the web bridge forwards
|
|
32
|
-
* as `
|
|
32
|
+
* as `yeaft_output` messages so the debug panel can render live state
|
|
33
33
|
* . All events flow through the same channel; no new
|
|
34
34
|
* WebSocket message type is introduced.
|
|
35
35
|
*
|
|
@@ -95,9 +95,14 @@ export async function runDream(opts) {
|
|
|
95
95
|
const processedGroups = [];
|
|
96
96
|
|
|
97
97
|
// 2. per-group: skip / segment / triage
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
// Topic summaries are now per-group (group/<g>/topic/...), so resolve
|
|
99
|
+
// them inside the per-group loop instead of once up front.
|
|
100
|
+
const resolveTopicSummaries = async (groupId) => {
|
|
101
|
+
if (opts.listTopicSummaries) {
|
|
102
|
+
return await safeCall(() => opts.listTopicSummaries(groupId), []);
|
|
103
|
+
}
|
|
104
|
+
return await defaultListTopicSummaries(opts.root, groupId, opts.language).catch(() => []);
|
|
105
|
+
};
|
|
101
106
|
|
|
102
107
|
for (const groupId of groupIds) {
|
|
103
108
|
// Current-group manual dream passes are the one case where scopeFilter
|
|
@@ -152,6 +157,7 @@ export async function runDream(opts) {
|
|
|
152
157
|
|
|
153
158
|
let actions;
|
|
154
159
|
try {
|
|
160
|
+
const topicSummaries = await resolveTopicSummaries(groupId);
|
|
155
161
|
actions = await triageGroupSegments({
|
|
156
162
|
groupId,
|
|
157
163
|
segments,
|
|
@@ -304,11 +310,12 @@ async function safeCall(fn, fallback) {
|
|
|
304
310
|
}
|
|
305
311
|
}
|
|
306
312
|
|
|
307
|
-
async function defaultListTopicSummaries(root, language) {
|
|
313
|
+
async function defaultListTopicSummaries(root, groupId, language) {
|
|
308
314
|
const all = await listScopes({ root });
|
|
309
315
|
const out = [];
|
|
310
316
|
for (const sc of all) {
|
|
311
|
-
if (sc.kind !== 'topic') continue;
|
|
317
|
+
if (sc.kind !== 'group-topic') continue;
|
|
318
|
+
if (sc.groupId !== groupId) continue;
|
|
312
319
|
const summary = await readSummary(sc, { root, language });
|
|
313
320
|
out.push({ path: sc.path.join('/'), summary });
|
|
314
321
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* retired (task-710) — the wiring is unconditional.
|
|
10
10
|
*
|
|
11
11
|
* ─────────────────────────────────────────────────────────────────────
|
|
12
|
-
* Wire contract — DreamEvent (consumed by web/components/
|
|
12
|
+
* Wire contract — DreamEvent (consumed by web/components/YeaftDebugPanel.js)
|
|
13
13
|
* ─────────────────────────────────────────────────────────────────────
|
|
14
14
|
* The events persisted to trace_events with these event_type values are
|
|
15
15
|
* load-bearing for the debug panel. Renaming a field on this side
|
|
@@ -282,7 +282,7 @@ function persistDreamTrace(session, eventType, eventData) {
|
|
|
282
282
|
/**
|
|
283
283
|
* Create a v2 dream scheduler bound to a session and wire its progress
|
|
284
284
|
* events into the engine's sub-agent event sink (web-bridge translates
|
|
285
|
-
* `dream_progress` into `
|
|
285
|
+
* `dream_progress` into `yeaft_output` for the debug panel).
|
|
286
286
|
*
|
|
287
287
|
* @param {Object} session
|
|
288
288
|
* @returns {Object} — scheduler (start/stop/triggerNow)
|
|
@@ -62,20 +62,24 @@ export function applyHardRules({ groupId, messages }) {
|
|
|
62
62
|
const out = new Map();
|
|
63
63
|
const add = (scope) => { if (!out.has(scope)) out.set(scope, { kind: 'update', scope }); };
|
|
64
64
|
|
|
65
|
-
// user is always in.
|
|
65
|
+
// global user is always in.
|
|
66
66
|
add('user');
|
|
67
67
|
|
|
68
|
-
// active group, except the virtual _no-group bucket.
|
|
69
|
-
if (groupId && groupId !== '_no-group')
|
|
68
|
+
// active group + its per-group user layer, except the virtual _no-group bucket.
|
|
69
|
+
if (groupId && groupId !== '_no-group') {
|
|
70
|
+
add(`group/${groupId}`);
|
|
71
|
+
add(`group/${groupId}/user`);
|
|
72
|
+
}
|
|
70
73
|
|
|
71
74
|
for (const m of (messages || [])) {
|
|
72
75
|
if (!m || typeof m !== 'object') continue;
|
|
73
|
-
// Active VP: any assistant message's vpId.
|
|
76
|
+
// Active VP: any assistant message's vpId — now group-internal.
|
|
74
77
|
if (m.role === 'assistant') {
|
|
75
78
|
const vp = m.vpId || (m.author && /^vp:(.+)$/.exec(m.author)?.[1]);
|
|
76
|
-
if (vp && /^[A-Za-z0-9_\-.一-鿿]+$/.test(vp))
|
|
79
|
+
if (vp && /^[A-Za-z0-9_\-.一-鿿]+$/.test(vp) && groupId && groupId !== '_no-group') {
|
|
80
|
+
add(`group/${groupId}/vp/${vp}`);
|
|
81
|
+
}
|
|
77
82
|
}
|
|
78
|
-
// (Active feature scope was dropped 2026-05-13 with the Feature system.)
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
return Array.from(out.values());
|
|
@@ -161,8 +165,9 @@ export async function classifySoft({ groupId, messages, topicSummaries, llm, lan
|
|
|
161
165
|
const path = String(pass2.path || '').trim();
|
|
162
166
|
if (!path) continue;
|
|
163
167
|
const segs = path.split('/').filter(Boolean);
|
|
164
|
-
if (!
|
|
165
|
-
|
|
168
|
+
if (!groupId || groupId === '_no-group') continue;
|
|
169
|
+
if (!isValidTopic({ kind: 'group-topic', groupId, path: segs })) continue;
|
|
170
|
+
const scope = `group/${groupId}/topic/${segs.join('/')}`;
|
|
166
171
|
if (pass2.decision === 'match') {
|
|
167
172
|
out.push({ kind: 'update', scope });
|
|
168
173
|
} else if (pass2.decision === 'new') {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* effort.js — Scenario → effort decision tree for
|
|
2
|
+
* effort.js — Scenario → effort decision tree for Yeaft
|
|
3
3
|
*
|
|
4
4
|
* task-327b: given a per-query context (scenario tag, tool-loop depth,
|
|
5
5
|
* user override), pick the thinking/reasoning effort level that should be
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Red lines:
|
|
16
16
|
* • Never error on unknown scenario — default to 'max'.
|
|
17
|
-
* • Feature flag
|
|
17
|
+
* • Feature flag YEAFT_THINKING_V1 is enforced at the adapter/router
|
|
18
18
|
* layer; this module just computes the intended value. If the flag
|
|
19
19
|
* is off, adapters drop it anyway.
|
|
20
20
|
* • Unsupported models silently drop effort at the router — this
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Pattern derived from Claude Code's query loop (src/query.ts).
|
|
16
16
|
*
|
|
17
|
-
* Reference: yeaft-
|
|
17
|
+
* Reference: yeaft-yeaft-implementation-plan.md §3.1, §4 (Phase 2)
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { randomUUID } from 'crypto';
|
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
* task-324 — Turn cap removed.
|
|
61
61
|
*
|
|
62
62
|
* Previously MAX_TURNS=25 broke the query loop out of long tool-driven
|
|
63
|
-
* conversations (user report:
|
|
63
|
+
* conversations (user report: Yeaft loop errored at the cap). The engine
|
|
64
64
|
* now runs until the LLM itself returns stopReason='end_turn' or a
|
|
65
65
|
* non-retryable error surfaces. Real runaway loops are still bounded by:
|
|
66
66
|
* • provider rate limits / context window (LLMContextError → compact)
|
|
@@ -81,7 +81,7 @@ const MAX_CONTINUE_TURNS = 3;
|
|
|
81
81
|
* Content is passed through verbatim — never truncated. Debug traces must
|
|
82
82
|
* mirror exactly what we sent to the LLM; a truncated copy is misleading.
|
|
83
83
|
* If the resulting payload is too large for the client debug store the
|
|
84
|
-
* bound is per-loop-count (see `
|
|
84
|
+
* bound is per-loop-count (see `MAX_YEAFT_DEBUG_LOOPS` in
|
|
85
85
|
* `web/stores/chat.js`), not per-payload mutilation here.
|
|
86
86
|
*
|
|
87
87
|
* Pure function — no side effects on the input message.
|
|
@@ -569,8 +569,8 @@ export class Engine {
|
|
|
569
569
|
groupId
|
|
570
570
|
? readScopeSummary({ kind: 'group', id: groupId }, { root: memoryRoot, language }).catch(() => '')
|
|
571
571
|
: Promise.resolve(''),
|
|
572
|
-
vpId
|
|
573
|
-
? readScopeSummary({ kind: 'vp', id: vpId }, { root: memoryRoot, language }).catch(() => '')
|
|
572
|
+
vpId && groupId
|
|
573
|
+
? readScopeSummary({ kind: 'group-vp', groupId, id: vpId }, { root: memoryRoot, language }).catch(() => '')
|
|
574
574
|
: Promise.resolve(''),
|
|
575
575
|
];
|
|
576
576
|
const [user, group, vp] = await Promise.all(tasks);
|
|
@@ -790,7 +790,7 @@ export class Engine {
|
|
|
790
790
|
// Worker-shape harness is descriptive metadata for human inspection;
|
|
791
791
|
// production prompts skip it to save tokens. Re-enable via env when
|
|
792
792
|
// diagnosing prompt structure issues.
|
|
793
|
-
includeShape: process.env.
|
|
793
|
+
includeShape: process.env.YEAFT_PROMPT_INCLUDE_SHAPE === '1',
|
|
794
794
|
});
|
|
795
795
|
}
|
|
796
796
|
|
|
@@ -888,9 +888,9 @@ export class Engine {
|
|
|
888
888
|
// ViewImage (task-333b PR-B rev-3 P1-A): expose size cap + allowlist
|
|
889
889
|
// via tool ctx so hosts can override via ~/.yeaft/config.json without
|
|
890
890
|
// touching the tool impl.
|
|
891
|
-
maxImageBytes: this.#config?.
|
|
892
|
-
imageAllowlist: Array.isArray(this.#config?.
|
|
893
|
-
? this.#config.
|
|
891
|
+
maxImageBytes: this.#config?.yeaft?.maxImageBytes,
|
|
892
|
+
imageAllowlist: Array.isArray(this.#config?.yeaft?.imageAllowlist)
|
|
893
|
+
? this.#config.yeaft.imageAllowlist
|
|
894
894
|
: [],
|
|
895
895
|
// Bug 4 fix — VP / routing context for RouteForward (and any other
|
|
896
896
|
// VP-aware tool). Undefined when running in non-group / no-VP flows.
|
|
@@ -2735,7 +2735,7 @@ export class Engine {
|
|
|
2735
2735
|
/**
|
|
2736
2736
|
* Run a one-shot fast-model call to produce a compact summary.
|
|
2737
2737
|
* Used by the web bridge's in-memory history compactor
|
|
2738
|
-
* (`agent/
|
|
2738
|
+
* (`agent/yeaft/history-compact.js`) — kept on the engine so callers
|
|
2739
2739
|
* don't reach into the private adapter field.
|
|
2740
2740
|
*
|
|
2741
2741
|
* @param {{system: string, prompt: string, maxTokens?: number}} args
|
|
@@ -5,22 +5,22 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
7
|
* # Run all evals against default model (requires API key)
|
|
8
|
-
* node agent/
|
|
8
|
+
* node agent/yeaft/eval/run-eval.js
|
|
9
9
|
*
|
|
10
10
|
* # Run specific suite
|
|
11
|
-
* node agent/
|
|
11
|
+
* node agent/yeaft/eval/run-eval.js --suite tools
|
|
12
12
|
*
|
|
13
13
|
* # Compare multiple models
|
|
14
|
-
* node agent/
|
|
14
|
+
* node agent/yeaft/eval/run-eval.js --models claude-sonnet-4-20250514,gpt-5
|
|
15
15
|
*
|
|
16
16
|
* # Save baseline
|
|
17
|
-
* node agent/
|
|
17
|
+
* node agent/yeaft/eval/run-eval.js --save-baseline initial
|
|
18
18
|
*
|
|
19
19
|
* # Compare against baseline
|
|
20
|
-
* node agent/
|
|
20
|
+
* node agent/yeaft/eval/run-eval.js --compare-baseline baselines/initial.json
|
|
21
21
|
*
|
|
22
22
|
* # Dry run (MockAdapter, no API calls)
|
|
23
|
-
* node agent/
|
|
23
|
+
* node agent/yeaft/eval/run-eval.js --dry-run
|
|
24
24
|
*
|
|
25
25
|
* Environment:
|
|
26
26
|
* YEAFT_API_KEY — Anthropic API key
|
|
@@ -62,7 +62,7 @@ if (args.help) {
|
|
|
62
62
|
Yeaft Eval Runner
|
|
63
63
|
|
|
64
64
|
Usage:
|
|
65
|
-
node agent/
|
|
65
|
+
node agent/yeaft/eval/run-eval.js [options]
|
|
66
66
|
|
|
67
67
|
Options:
|
|
68
68
|
-s, --suite <name> Run specific suite: tools, memory, skills, e2e, all (default: all)
|
|
@@ -78,16 +78,16 @@ Environment:
|
|
|
78
78
|
|
|
79
79
|
Examples:
|
|
80
80
|
# Quick dry run to verify harness works
|
|
81
|
-
node agent/
|
|
81
|
+
node agent/yeaft/eval/run-eval.js --dry-run
|
|
82
82
|
|
|
83
83
|
# Run tool evals against Claude Sonnet
|
|
84
|
-
node agent/
|
|
84
|
+
node agent/yeaft/eval/run-eval.js --suite tools --models claude-sonnet-4-20250514
|
|
85
85
|
|
|
86
86
|
# Full eval, save baseline
|
|
87
|
-
node agent/
|
|
87
|
+
node agent/yeaft/eval/run-eval.js --save-baseline v0.1.411
|
|
88
88
|
|
|
89
89
|
# Check for regressions
|
|
90
|
-
node agent/
|
|
90
|
+
node agent/yeaft/eval/run-eval.js --compare-baseline baselines/v0.1.411.json
|
|
91
91
|
`);
|
|
92
92
|
process.exit(0);
|
|
93
93
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* feature-flag.js — Reads `config.
|
|
2
|
+
* feature-flag.js — Reads `config.yeaft.multiVp.enabled` from ~/.yeaft/config.json.
|
|
3
3
|
*
|
|
4
4
|
* Per architecture §11: multi-VP group mode is opt-in for MVP. The flag
|
|
5
5
|
* gates UI entry points and (later) migration. This module returns a plain
|
|
@@ -14,7 +14,7 @@ import { join } from 'path';
|
|
|
14
14
|
import { writeAtomic } from '../storage/index.js';
|
|
15
15
|
|
|
16
16
|
const CONFIG_FILE = 'config.json';
|
|
17
|
-
const FLAG_PATH = ['
|
|
17
|
+
const FLAG_PATH = ['yeaft', 'multiVp', 'enabled'];
|
|
18
18
|
|
|
19
19
|
function readConfig(yeaftDir) {
|
|
20
20
|
const path = join(yeaftDir, CONFIG_FILE);
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* - Roster mutation helpers
|
|
7
7
|
* - Coordinator that parses @-mentions on USER messages and dispatches to
|
|
8
8
|
* target RoleInstances via a caller-supplied `deliver(vpId, envelope)`.
|
|
9
|
-
* - Feature flag reader for `
|
|
9
|
+
* - Feature flag reader for `yeaft.multiVp.enabled`
|
|
10
10
|
* - First-boot default group seeder
|
|
11
11
|
*
|
|
12
|
-
* See agent/
|
|
12
|
+
* See agent/yeaft/groups/coordinator.js for the dispatch contract.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
export {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* groups/pre-flow.js — explicit pre-flow stage for
|
|
2
|
+
* groups/pre-flow.js — explicit pre-flow stage for Yeaft.
|
|
3
3
|
*
|
|
4
4
|
* Pre-flow is the "before any VP runs" stage. It owns:
|
|
5
5
|
*
|
|
@@ -167,6 +167,15 @@ export function selectRespondingVps(input) {
|
|
|
167
167
|
*/
|
|
168
168
|
function scopeHeading(scope) {
|
|
169
169
|
if (scope === 'user') return '## Memory: User';
|
|
170
|
+
// Nested group scopes first.
|
|
171
|
+
let m = /^group\/([^/]+)\/vp\/(.+)$/.exec(scope);
|
|
172
|
+
if (m) return `## Memory: VP ${m[2]}`;
|
|
173
|
+
m = /^group\/([^/]+)\/user$/.exec(scope);
|
|
174
|
+
if (m) return `## Memory: Group ${m[1]} (user)`;
|
|
175
|
+
m = /^group\/([^/]+)\/feature\/(.+)$/.exec(scope);
|
|
176
|
+
if (m) return `## Memory: Feature ${m[2]}`;
|
|
177
|
+
m = /^group\/([^/]+)\/topic\/(.+)$/.exec(scope);
|
|
178
|
+
if (m) return `## Memory: Topic ${m[2]}`;
|
|
170
179
|
if (scope.startsWith('group/')) return `## Memory: Group ${scope.slice(6)}`;
|
|
171
180
|
if (scope.startsWith('vp/')) return `## Memory: VP ${scope.slice(3)}`;
|
|
172
181
|
if (scope.startsWith('feature/')) return `## Memory: Feature ${scope.slice(8)}`;
|
|
@@ -238,8 +247,11 @@ export function formatPickedForInjection(picked) {
|
|
|
238
247
|
*/
|
|
239
248
|
export function buildRelevantScopes({ groupId, vpId, extra } = {}) {
|
|
240
249
|
const scopes = ['user'];
|
|
241
|
-
if (groupId)
|
|
242
|
-
|
|
250
|
+
if (groupId) {
|
|
251
|
+
scopes.push(`group/${groupId}`);
|
|
252
|
+
scopes.push(`group/${groupId}/user`);
|
|
253
|
+
if (vpId) scopes.push(`group/${groupId}/vp/${vpId}`);
|
|
254
|
+
}
|
|
243
255
|
if (Array.isArray(extra)) {
|
|
244
256
|
for (const s of extra) {
|
|
245
257
|
if (s && !scopes.includes(s)) scopes.push(s);
|
|
@@ -140,7 +140,7 @@ export function readProjectDoc(workDir, opts = {}) {
|
|
|
140
140
|
const text = buffer.toString('utf8', 0, useBytes).trim();
|
|
141
141
|
if (truncated) {
|
|
142
142
|
console.warn(
|
|
143
|
-
`[
|
|
143
|
+
`[yeaft/project-doc] ${picked.path} exceeds ${maxBytes} bytes — truncated.`,
|
|
144
144
|
);
|
|
145
145
|
}
|
|
146
146
|
if (!text) return null;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* history-compact.js — In-memory conversation history compaction for the
|
|
3
|
-
*
|
|
3
|
+
* Yeaft group-chat fan-out path.
|
|
4
4
|
*
|
|
5
5
|
* Problem this solves:
|
|
6
|
-
* `agent/
|
|
6
|
+
* `agent/yeaft/web-bridge.js` keeps a flat module-level array
|
|
7
7
|
* `conversationMessages` that grows unbounded across the lifetime of the
|
|
8
8
|
* agent process. Every fan-out turn snapshots the whole thing into
|
|
9
9
|
* `baseSnapshot` and feeds it to `engine.query` for every VP. Without a
|
|
10
10
|
* cap, prompt size and token cost grow linearly with conversation length.
|
|
11
11
|
*
|
|
12
|
-
* Existing infrastructure (`agent/
|
|
12
|
+
* Existing infrastructure (`agent/yeaft/compact/orchestrator.js`,
|
|
13
13
|
* `engine.js#runOrchestratorCompact`) compacts the on-disk
|
|
14
14
|
* `conversationStore` — a different surface. This helper compacts the
|
|
15
15
|
* in-memory array that actually gets passed to the LLM.
|
package/{unify → yeaft}/index.js
RENAMED
package/{unify → yeaft}/init.js
RENAMED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* init.js — Yeaft directory structure initialization
|
|
3
3
|
*
|
|
4
4
|
* Ensures ~/.yeaft/ and all required subdirectories exist.
|
|
5
|
-
* Creates default config.md, MEMORY.md, and
|
|
5
|
+
* Creates default config.md, MEMORY.md, chat/index.md, and group/index.md if missing.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { existsSync, mkdirSync, writeFileSync, accessSync, constants } from 'fs';
|
|
@@ -74,9 +74,13 @@ export const DEFAULT_YEAFT_DIR = join(homedir(), '.yeaft');
|
|
|
74
74
|
|
|
75
75
|
/** Subdirectories that must exist inside the Yeaft data directory. */
|
|
76
76
|
const SUBDIRS = [
|
|
77
|
-
'
|
|
78
|
-
'
|
|
79
|
-
'
|
|
77
|
+
'chat/messages',
|
|
78
|
+
'chat/cold',
|
|
79
|
+
'chat/blobs',
|
|
80
|
+
'group/messages',
|
|
81
|
+
'group/cold',
|
|
82
|
+
'group/blobs',
|
|
83
|
+
'group/compact',
|
|
80
84
|
'memory/entries',
|
|
81
85
|
'tasks',
|
|
82
86
|
'skills',
|
|
@@ -144,7 +148,7 @@ totalMessages: 0
|
|
|
144
148
|
|
|
145
149
|
# Conversation Index
|
|
146
150
|
|
|
147
|
-
This file tracks
|
|
151
|
+
This file tracks one Yeaft message-history mode.
|
|
148
152
|
`;
|
|
149
153
|
|
|
150
154
|
/**
|
|
@@ -196,10 +200,12 @@ export function initYeaftDir(dir) {
|
|
|
196
200
|
created.push(memoryPath);
|
|
197
201
|
}
|
|
198
202
|
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
+
for (const mode of ['chat', 'group']) {
|
|
204
|
+
const indexPath = join(root, mode, 'index.md');
|
|
205
|
+
if (!existsSync(indexPath)) {
|
|
206
|
+
safeWriteFile(indexPath, DEFAULT_CONVERSATION_INDEX, warnings);
|
|
207
|
+
created.push(indexPath);
|
|
208
|
+
}
|
|
203
209
|
}
|
|
204
210
|
|
|
205
211
|
// mcp.json.example — reference template for MCP server configuration
|
|
@@ -120,7 +120,7 @@ export class LLMAbortError extends Error {
|
|
|
120
120
|
* sent to the LLM. A truncated copy is worse than useless — it lies about
|
|
121
121
|
* what the model saw. If the resulting payload is too large for the debug
|
|
122
122
|
* store, the fix is to bound retention (drop oldest turns), not to mutilate
|
|
123
|
-
* individual payloads. See `
|
|
123
|
+
* individual payloads. See `MAX_YEAFT_DEBUG_LOOPS` in `web/stores/chat.js`.
|
|
124
124
|
*
|
|
125
125
|
* @param {{ url: string, method: string, headers: object, body: any }} req
|
|
126
126
|
* @returns {{ url: string, method: string, headers: object, body: any }}
|
|
@@ -24,10 +24,10 @@ import {
|
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* task-327a: feature-flag accessor. thinkingV1 is OFF by default; set
|
|
27
|
-
* env
|
|
27
|
+
* env YEAFT_THINKING_V1=1 to enable. Read lazily so tests can flip.
|
|
28
28
|
*/
|
|
29
29
|
function thinkingV1Enabled() {
|
|
30
|
-
return process.env.
|
|
30
|
+
return process.env.YEAFT_THINKING_V1 === '1';
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
const DEFAULT_BASE_URL = 'https://api.anthropic.com';
|