@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
|
@@ -25,6 +25,7 @@ const NETWORK_TIMEOUT_MS = 15000;
|
|
|
25
25
|
const STALE_GRACE_MS = 5 * 60 * 1000; // 5 minutes when serving stale-after-failure
|
|
26
26
|
|
|
27
27
|
let _memCache = null;
|
|
28
|
+
let _memCachePath = null;
|
|
28
29
|
let _memCacheTime = 0;
|
|
29
30
|
|
|
30
31
|
function getCachePath(yeaftDir) {
|
|
@@ -71,8 +72,10 @@ async function saveDiskCache(yeaftDir, data) {
|
|
|
71
72
|
* @returns {Promise<object>} provider-id → provider entry; {} on total failure.
|
|
72
73
|
*/
|
|
73
74
|
export async function fetchModelsDev({ forceRefresh = false, yeaftDir = null } = {}) {
|
|
75
|
+
const cachePath = getCachePath(yeaftDir);
|
|
76
|
+
|
|
74
77
|
// Stage 1: in-memory cache.
|
|
75
|
-
if (!forceRefresh && _memCache && Date.now() - _memCacheTime < CACHE_TTL_MS) {
|
|
78
|
+
if (!forceRefresh && _memCache && _memCachePath === cachePath && Date.now() - _memCacheTime < CACHE_TTL_MS) {
|
|
76
79
|
return _memCache;
|
|
77
80
|
}
|
|
78
81
|
|
|
@@ -83,6 +86,7 @@ export async function fetchModelsDev({ forceRefresh = false, yeaftDir = null } =
|
|
|
83
86
|
const data = await loadDiskCache(yeaftDir);
|
|
84
87
|
if (data && typeof data === 'object') {
|
|
85
88
|
_memCache = data;
|
|
89
|
+
_memCachePath = cachePath;
|
|
86
90
|
_memCacheTime = Date.now() - age;
|
|
87
91
|
return _memCache;
|
|
88
92
|
}
|
|
@@ -99,6 +103,7 @@ export async function fetchModelsDev({ forceRefresh = false, yeaftDir = null } =
|
|
|
99
103
|
const data = await res.json();
|
|
100
104
|
if (data && typeof data === 'object') {
|
|
101
105
|
_memCache = data;
|
|
106
|
+
_memCachePath = cachePath;
|
|
102
107
|
_memCacheTime = Date.now();
|
|
103
108
|
await saveDiskCache(yeaftDir, data);
|
|
104
109
|
return data;
|
|
@@ -111,15 +116,16 @@ export async function fetchModelsDev({ forceRefresh = false, yeaftDir = null } =
|
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
// Stage 4: stale disk cache fallback with short grace TTL so we retry soon.
|
|
114
|
-
if (!_memCache) {
|
|
119
|
+
if (!_memCache || _memCachePath !== cachePath) {
|
|
115
120
|
const data = await loadDiskCache(yeaftDir);
|
|
116
121
|
if (data && typeof data === 'object') {
|
|
117
122
|
_memCache = data;
|
|
123
|
+
_memCachePath = cachePath;
|
|
118
124
|
_memCacheTime = Date.now() - CACHE_TTL_MS + STALE_GRACE_MS;
|
|
119
125
|
}
|
|
120
126
|
}
|
|
121
127
|
|
|
122
|
-
return _memCache || {};
|
|
128
|
+
return _memCachePath === cachePath ? (_memCache || {}) : {};
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
/**
|
|
@@ -156,5 +162,6 @@ export async function listProviderModels(providerId, { yeaftDir = null } = {}) {
|
|
|
156
162
|
*/
|
|
157
163
|
export function _resetMemCache() {
|
|
158
164
|
_memCache = null;
|
|
165
|
+
_memCachePath = null;
|
|
159
166
|
_memCacheTime = 0;
|
|
160
167
|
}
|
|
@@ -43,12 +43,12 @@ import {
|
|
|
43
43
|
const DEFAULT_BASE_URL = 'https://api.openai.com/v1';
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
* Feature-flag accessor mirroring anthropic.js.
|
|
46
|
+
* Feature-flag accessor mirroring anthropic.js. YEAFT_THINKING_V1 is OFF by
|
|
47
47
|
* default; set env to '1' to enable thinking-mode field translation. Read
|
|
48
48
|
* lazily so tests can flip the flag between calls.
|
|
49
49
|
*/
|
|
50
50
|
function thinkingV1Enabled() {
|
|
51
|
-
return process.env.
|
|
51
|
+
return process.env.YEAFT_THINKING_V1 === '1';
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
@@ -78,7 +78,7 @@ export function inferProtocolFromModelId(modelId) {
|
|
|
78
78
|
* task-327a: feature-flag accessor. Read lazily so tests can flip.
|
|
79
79
|
*/
|
|
80
80
|
function thinkingV1Enabled() {
|
|
81
|
-
return process.env.
|
|
81
|
+
return process.env.YEAFT_THINKING_V1 === '1';
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
package/{unify → yeaft}/mcp.js
RENAMED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Each MCP server communicates via stdio JSON-RPC (MCP protocol).
|
|
21
21
|
*
|
|
22
|
-
* Reference: yeaft-
|
|
22
|
+
* Reference: yeaft-yeaft-design.md §8, yeaft-yeaft-core-systems.md §3.2
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { spawn } from 'child_process';
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import { approxTokens } from './budget.js';
|
|
31
|
+
import { isVpForeign } from './store-v2.js';
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* @typedef {object} AdjustTriggerInput
|
|
@@ -104,10 +105,7 @@ function firstSentence(body) {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
function isOwnOrNonVp(scope, ownVpId) {
|
|
107
|
-
|
|
108
|
-
if (!ownVpId) return true;
|
|
109
|
-
const other = scope.slice(3).split('/')[0];
|
|
110
|
-
return other === ownVpId;
|
|
108
|
+
return !isVpForeign(scope, ownVpId);
|
|
111
109
|
}
|
|
112
110
|
|
|
113
111
|
/**
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* the next time the group is opened. Resident layer is derived state
|
|
25
25
|
* (rebuilt every turn from `<scope>/summary.md`) — never persisted.
|
|
26
26
|
*
|
|
27
|
-
* For the single-VP
|
|
27
|
+
* For the single-VP Yeaft path (no group), the registry uses the literal
|
|
28
28
|
* key `"default"` so there's still a stable home for AMS state.
|
|
29
29
|
*/
|
|
30
30
|
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { approxTokens, packWithinBudget } from './budget.js';
|
|
20
|
+
import { isVpForeign } from './store-v2.js';
|
|
20
21
|
|
|
21
22
|
const RECENT_DEFAULT_CAPACITY = 64;
|
|
22
23
|
|
|
@@ -189,9 +190,6 @@ export class ActiveMemorySet {
|
|
|
189
190
|
// ────────────────────────── privacy ──────────────────────────
|
|
190
191
|
|
|
191
192
|
_isForeignVp(scope) {
|
|
192
|
-
|
|
193
|
-
if (!this.ownVpId) return false; // no own id → no filtering
|
|
194
|
-
const other = scope.slice(3).split('/')[0];
|
|
195
|
-
return other !== this.ownVpId;
|
|
193
|
+
return isVpForeign(scope, this.ownVpId);
|
|
196
194
|
}
|
|
197
195
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { extractKeywords } from './keywords.js';
|
|
19
19
|
import { approxTokens } from './budget.js';
|
|
20
|
+
import { isVpForeign } from './store-v2.js';
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* @typedef {object} PreflowOptions
|
|
@@ -118,12 +119,7 @@ export function buildFtsQuery(keywords) {
|
|
|
118
119
|
* @returns {string[]}
|
|
119
120
|
*/
|
|
120
121
|
export function filterScopes(scopes, ownVpId) {
|
|
121
|
-
return scopes.filter(s =>
|
|
122
|
-
if (!s.startsWith('vp/')) return true;
|
|
123
|
-
if (!ownVpId) return true;
|
|
124
|
-
const other = s.slice(3).split('/')[0];
|
|
125
|
-
return other === ownVpId;
|
|
126
|
-
});
|
|
122
|
+
return scopes.filter(s => !isVpForeign(s, ownVpId));
|
|
127
123
|
}
|
|
128
124
|
|
|
129
125
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* memory/seed-backfill.js — Run-once backfill of `summary.md` for VPs and
|
|
3
3
|
* groups that were created BEFORE the create-time seed was added (PR
|
|
4
|
-
* "fix-
|
|
4
|
+
* "fix-yeaft-context-and-memory"). Without backfill, an existing user's
|
|
5
5
|
* `grp_claude` group and `steve` VP never get a Layer-A resident summary —
|
|
6
6
|
* `engine.#prepareAms` then renders an empty memory section every turn,
|
|
7
7
|
* which is the user-visible Bug #2.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* a permission error must NEVER prevent the session from loading.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, statSync } from 'fs';
|
|
18
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, statSync, renameSync } from 'fs';
|
|
19
19
|
import { join } from 'path';
|
|
20
20
|
import { homedir } from 'os';
|
|
21
21
|
import { parseRoleMd } from '../vp/vp-store.js';
|
|
@@ -292,3 +292,43 @@ export function runSummaryBackfill({ yeaftDir, libDir, root = DEFAULT_MEMORY_ROO
|
|
|
292
292
|
}
|
|
293
293
|
return { migrate, vp, group };
|
|
294
294
|
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* archiveLegacyScopes(root) — one-shot migration for the group-isolated
|
|
298
|
+
* memory refactor. The legacy flat layout had `vp/<id>/`, `feature/<id>/`,
|
|
299
|
+
* and `topic/<l1>[/<l2>]/` directories at the memory root; the new layout
|
|
300
|
+
* tucks each into `group/<g>/{vp,feature,topic}/...`. Per user directive
|
|
301
|
+
* "硬切,老的就不要了" — we do NOT migrate per-record, we just move the
|
|
302
|
+
* top-level dirs to `<root>/.legacy/<kind>/` once. They are never read
|
|
303
|
+
* again; this is forensics-only.
|
|
304
|
+
*
|
|
305
|
+
* Idempotent: a second invocation is a no-op when no legacy dirs remain at
|
|
306
|
+
* the root. If `.legacy/<kind>/` already exists, the new move is suffixed
|
|
307
|
+
* with a timestamp so re-attempts after a partial first run don't clobber.
|
|
308
|
+
*
|
|
309
|
+
* @param {string} root memory root (typically <yeaftDir>/memory)
|
|
310
|
+
* @returns {{moved: string[]}}
|
|
311
|
+
*/
|
|
312
|
+
export function archiveLegacyScopes(root) {
|
|
313
|
+
const moved = [];
|
|
314
|
+
if (!root || !existsSync(root)) return { moved };
|
|
315
|
+
const legacyRoot = join(root, '.legacy');
|
|
316
|
+
for (const kind of ['vp', 'feature', 'topic']) {
|
|
317
|
+
const src = join(root, kind);
|
|
318
|
+
if (!existsSync(src)) continue;
|
|
319
|
+
try {
|
|
320
|
+
mkdirSync(legacyRoot, { recursive: true });
|
|
321
|
+
let dst = join(legacyRoot, kind);
|
|
322
|
+
if (existsSync(dst)) {
|
|
323
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
324
|
+
dst = `${dst}.${ts}`;
|
|
325
|
+
}
|
|
326
|
+
// eslint-disable-next-line global-require
|
|
327
|
+
renameSync(src, dst);
|
|
328
|
+
moved.push(kind);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
console.warn(`[seed-backfill] archiveLegacyScopes(${kind}) failed:`, err?.message || err);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return { moved };
|
|
334
|
+
}
|
|
@@ -51,7 +51,7 @@ export const KIND_VALUES = new Set([
|
|
|
51
51
|
'fact', 'preference', 'decision', 'lesson', 'relation', 'goal', 'context',
|
|
52
52
|
]);
|
|
53
53
|
|
|
54
|
-
const SCOPE_RE = /^(user|
|
|
54
|
+
const SCOPE_RE = /^(user|group\/[\w-]+(?:\/(?:user|vp\/[\w-]+|feature\/[\w-]+|topic\/[\w-]+(?:\/[\w-]+)?))?)$/;
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Compute a stable id from segment content. Same body + scope + kind →
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
* by `dream-v2/state.js`, not here.
|
|
38
38
|
* - No LLM calls, no extraction, no summarisation. Pure I/O.
|
|
39
39
|
*
|
|
40
|
-
* Reference: agent/
|
|
40
|
+
* Reference: agent/yeaft/memory/DESIGN-H2-AMS.md.
|
|
41
41
|
*/
|
|
42
42
|
|
|
43
43
|
import {
|
|
@@ -54,16 +54,24 @@ import { homedir } from 'os';
|
|
|
54
54
|
/** Default memory root. Tests override via `opts.root`. */
|
|
55
55
|
export const DEFAULT_MEMORY_ROOT = join(homedir(), '.yeaft', 'memory');
|
|
56
56
|
|
|
57
|
-
/** Scope kinds recognised by v2. */
|
|
58
|
-
export const SCOPE_KINDS = Object.freeze([
|
|
57
|
+
/** Scope kinds recognised by v2 (group-isolated layout). */
|
|
58
|
+
export const SCOPE_KINDS = Object.freeze([
|
|
59
|
+
'user',
|
|
60
|
+
'group',
|
|
61
|
+
'group-user',
|
|
62
|
+
'group-vp',
|
|
63
|
+
'group-feature',
|
|
64
|
+
'group-topic',
|
|
65
|
+
]);
|
|
59
66
|
|
|
60
|
-
/** @typedef {'user'|'
|
|
67
|
+
/** @typedef {'user'|'group'|'group-user'|'group-vp'|'group-feature'|'group-topic'} ScopeKind */
|
|
61
68
|
|
|
62
69
|
/**
|
|
63
70
|
* @typedef {Object} Scope
|
|
64
71
|
* @property {ScopeKind} kind
|
|
65
|
-
* @property {string} [id]
|
|
66
|
-
* @property {string
|
|
72
|
+
* @property {string} [id] — required for group; for group-vp / group-feature the per-kind id
|
|
73
|
+
* @property {string} [groupId] — required for every group-* kind
|
|
74
|
+
* @property {string[]} [path] — required for group-topic; 1–2 segments
|
|
67
75
|
*/
|
|
68
76
|
|
|
69
77
|
/**
|
|
@@ -81,25 +89,38 @@ export function scopeDir(scope) {
|
|
|
81
89
|
switch (scope.kind) {
|
|
82
90
|
case 'user':
|
|
83
91
|
return 'user';
|
|
84
|
-
case 'vp':
|
|
85
|
-
if (!scope.id) throw new Error('scopeDir: vp scope requires id');
|
|
86
|
-
assertSafeSegment(scope.id, 'vp.id');
|
|
87
|
-
return `vp/${scope.id}`;
|
|
88
92
|
case 'group':
|
|
89
93
|
if (!scope.id) throw new Error('scopeDir: group scope requires id');
|
|
90
94
|
assertSafeSegment(scope.id, 'group.id');
|
|
91
95
|
return `group/${scope.id}`;
|
|
92
|
-
case '
|
|
93
|
-
if (!scope.
|
|
94
|
-
assertSafeSegment(scope.
|
|
95
|
-
return `
|
|
96
|
-
|
|
96
|
+
case 'group-user': {
|
|
97
|
+
if (!scope.groupId) throw new Error('scopeDir: group-user scope requires groupId');
|
|
98
|
+
assertSafeSegment(scope.groupId, 'group-user.groupId');
|
|
99
|
+
return `group/${scope.groupId}/user`;
|
|
100
|
+
}
|
|
101
|
+
case 'group-vp': {
|
|
102
|
+
if (!scope.groupId) throw new Error('scopeDir: group-vp scope requires groupId');
|
|
103
|
+
if (!scope.id) throw new Error('scopeDir: group-vp scope requires id');
|
|
104
|
+
assertSafeSegment(scope.groupId, 'group-vp.groupId');
|
|
105
|
+
assertSafeSegment(scope.id, 'group-vp.id');
|
|
106
|
+
return `group/${scope.groupId}/vp/${scope.id}`;
|
|
107
|
+
}
|
|
108
|
+
case 'group-feature': {
|
|
109
|
+
if (!scope.groupId) throw new Error('scopeDir: group-feature scope requires groupId');
|
|
110
|
+
if (!scope.id) throw new Error('scopeDir: group-feature scope requires id');
|
|
111
|
+
assertSafeSegment(scope.groupId, 'group-feature.groupId');
|
|
112
|
+
assertSafeSegment(scope.id, 'group-feature.id');
|
|
113
|
+
return `group/${scope.groupId}/feature/${scope.id}`;
|
|
114
|
+
}
|
|
115
|
+
case 'group-topic': {
|
|
116
|
+
if (!scope.groupId) throw new Error('scopeDir: group-topic scope requires groupId');
|
|
117
|
+
assertSafeSegment(scope.groupId, 'group-topic.groupId');
|
|
97
118
|
const segs = Array.isArray(scope.path) ? scope.path : [];
|
|
98
119
|
if (segs.length === 0 || segs.length > 2) {
|
|
99
|
-
throw new Error('scopeDir: topic.path must have 1 or 2 segments');
|
|
120
|
+
throw new Error('scopeDir: group-topic.path must have 1 or 2 segments');
|
|
100
121
|
}
|
|
101
|
-
for (const s of segs) assertSafeSegment(s, 'topic.path');
|
|
102
|
-
return `topic/${segs.join('/')}`;
|
|
122
|
+
for (const s of segs) assertSafeSegment(s, 'group-topic.path');
|
|
123
|
+
return `group/${scope.groupId}/topic/${segs.join('/')}`;
|
|
103
124
|
}
|
|
104
125
|
default:
|
|
105
126
|
throw new Error(`scopeDir: unknown kind ${JSON.stringify(scope.kind)}`);
|
|
@@ -140,7 +161,8 @@ function assertSafeSegment(s, ctx) {
|
|
|
140
161
|
* @returns {boolean}
|
|
141
162
|
*/
|
|
142
163
|
export function isValidTopic(scope) {
|
|
143
|
-
if (!scope || scope.kind !== 'topic') return false;
|
|
164
|
+
if (!scope || scope.kind !== 'group-topic') return false;
|
|
165
|
+
if (!scope.groupId || typeof scope.groupId !== 'string') return false;
|
|
144
166
|
if (!Array.isArray(scope.path)) return false;
|
|
145
167
|
if (scope.path.length < 1 || scope.path.length > 2) return false;
|
|
146
168
|
for (const s of scope.path) {
|
|
@@ -155,7 +177,9 @@ export function isValidTopic(scope) {
|
|
|
155
177
|
// ─── ACL ───────────────────────────────────────────────────────
|
|
156
178
|
|
|
157
179
|
/**
|
|
158
|
-
* The single ACL: `vp/<other>` is foreign when `currentVpId` is given.
|
|
180
|
+
* The single ACL: `group/<g>/vp/<other>` is foreign when `currentVpId` is given.
|
|
181
|
+
* Across groups, every `group/<g>/vp/...` path is foreign by construction
|
|
182
|
+
* (the calling VP only runs inside its own group dir).
|
|
159
183
|
*
|
|
160
184
|
* @param {string} relPath
|
|
161
185
|
* @param {string} currentVpId
|
|
@@ -163,7 +187,7 @@ export function isValidTopic(scope) {
|
|
|
163
187
|
*/
|
|
164
188
|
export function isVpForeign(relPath, currentVpId) {
|
|
165
189
|
if (!relPath || !currentVpId) return false;
|
|
166
|
-
const m = /^vp\/([^/]+)(?:\/|$)/.exec(relPath);
|
|
190
|
+
const m = /^group\/[^/]+\/vp\/([^/]+)(?:\/|$)/.exec(relPath);
|
|
167
191
|
if (!m) return false;
|
|
168
192
|
return m[1] !== currentVpId;
|
|
169
193
|
}
|
|
@@ -403,18 +427,17 @@ export async function ensureScope(scope, opts = {}) {
|
|
|
403
427
|
|
|
404
428
|
/**
|
|
405
429
|
* Enumerate all scopes present on disk. Returns Scope shapes that round-trip
|
|
406
|
-
* back through `scopeDir`. Used by Triage to list candidate
|
|
407
|
-
* scopes for a group's diff.
|
|
430
|
+
* back through `scopeDir`. Used by Triage to list candidate scopes.
|
|
408
431
|
*
|
|
409
432
|
* Walks shallowly:
|
|
410
|
-
* user/
|
|
411
|
-
*
|
|
412
|
-
* group/<
|
|
413
|
-
*
|
|
414
|
-
*
|
|
433
|
+
* user/ → { kind: 'user' }
|
|
434
|
+
* group/<g>/ → { kind: 'group', id: g }
|
|
435
|
+
* group/<g>/user/ → { kind: 'group-user', groupId: g }
|
|
436
|
+
* group/<g>/vp/<v>/ → { kind: 'group-vp', groupId: g, id: v }
|
|
437
|
+
* group/<g>/feature/<f>/ → { kind: 'group-feature', groupId: g, id: f }
|
|
438
|
+
* group/<g>/topic/<l1>[/<l2>]/ → { kind: 'group-topic', groupId: g, path: [...] }
|
|
415
439
|
*
|
|
416
|
-
* Skips
|
|
417
|
-
* validation (e.g. accidental `.tmp.*` files at scope root, dotfiles).
|
|
440
|
+
* Skips `.legacy/` and any dotfile / unsafe segment.
|
|
418
441
|
*
|
|
419
442
|
* @param {{ root?: string }} [opts]
|
|
420
443
|
* @returns {Promise<Scope[]>}
|
|
@@ -427,53 +450,77 @@ export async function listScopes(opts = {}) {
|
|
|
427
450
|
// user/
|
|
428
451
|
if (existsSync(join(root, 'user'))) out.push({ kind: 'user' });
|
|
429
452
|
|
|
430
|
-
//
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
453
|
+
// group/<g>/...
|
|
454
|
+
const groupRoot = join(root, 'group');
|
|
455
|
+
let groups;
|
|
456
|
+
try { groups = await fsp.readdir(groupRoot, { withFileTypes: true }); }
|
|
457
|
+
catch (err) {
|
|
458
|
+
if (err && err.code === 'ENOENT') return out;
|
|
459
|
+
throw err;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
for (const gent of groups) {
|
|
463
|
+
if (!gent.isDirectory()) continue;
|
|
464
|
+
if (gent.name.startsWith('.')) continue;
|
|
465
|
+
if (!isSafeId(gent.name)) continue;
|
|
466
|
+
const g = gent.name;
|
|
467
|
+
out.push({ kind: 'group', id: g });
|
|
468
|
+
const gAbs = join(groupRoot, g);
|
|
469
|
+
|
|
470
|
+
// group/<g>/user/
|
|
471
|
+
if (existsSync(join(gAbs, 'user'))) {
|
|
472
|
+
out.push({ kind: 'group-user', groupId: g });
|
|
438
473
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
474
|
+
|
|
475
|
+
// group/<g>/vp/<v>/ and group/<g>/feature/<f>/
|
|
476
|
+
for (const kind of ['vp', 'feature']) {
|
|
477
|
+
const dir = join(gAbs, kind);
|
|
478
|
+
let names;
|
|
479
|
+
try { names = await fsp.readdir(dir, { withFileTypes: true }); }
|
|
480
|
+
catch (err) {
|
|
481
|
+
if (err && err.code === 'ENOENT') continue;
|
|
482
|
+
throw err;
|
|
483
|
+
}
|
|
484
|
+
for (const ent of names) {
|
|
485
|
+
if (!ent.isDirectory()) continue;
|
|
486
|
+
if (!isSafeId(ent.name)) continue;
|
|
487
|
+
out.push({
|
|
488
|
+
kind: kind === 'vp' ? 'group-vp' : 'group-feature',
|
|
489
|
+
groupId: g,
|
|
490
|
+
id: ent.name,
|
|
491
|
+
});
|
|
492
|
+
}
|
|
444
493
|
}
|
|
445
|
-
}
|
|
446
494
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}
|
|
455
|
-
for (const l1ent of l1s) {
|
|
456
|
-
if (!l1ent.isDirectory()) continue;
|
|
457
|
-
if (!isSafeId(l1ent.name)) continue;
|
|
458
|
-
const l1 = l1ent.name;
|
|
459
|
-
// Read l2 entries; if l1 itself contains memory.md, treat as 1-level topic
|
|
460
|
-
const l1abs = join(topicDir, l1);
|
|
461
|
-
let l2s;
|
|
462
|
-
try { l2s = await fsp.readdir(l1abs, { withFileTypes: true }); }
|
|
463
|
-
catch { l2s = []; }
|
|
464
|
-
let hasL2 = false;
|
|
465
|
-
for (const l2ent of l2s) {
|
|
466
|
-
if (!l2ent.isDirectory()) continue;
|
|
467
|
-
if (!isSafeId(l2ent.name)) continue;
|
|
468
|
-
out.push({ kind: 'topic', path: [l1, l2ent.name] });
|
|
469
|
-
hasL2 = true;
|
|
495
|
+
// group/<g>/topic/<l1>/[<l2>/]
|
|
496
|
+
const topicDir = join(gAbs, 'topic');
|
|
497
|
+
let l1s;
|
|
498
|
+
try { l1s = await fsp.readdir(topicDir, { withFileTypes: true }); }
|
|
499
|
+
catch (err) {
|
|
500
|
+
if (err && err.code === 'ENOENT') l1s = [];
|
|
501
|
+
else throw err;
|
|
470
502
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
503
|
+
for (const l1ent of l1s) {
|
|
504
|
+
if (!l1ent.isDirectory()) continue;
|
|
505
|
+
if (!isSafeId(l1ent.name)) continue;
|
|
506
|
+
const l1 = l1ent.name;
|
|
507
|
+
const l1abs = join(topicDir, l1);
|
|
508
|
+
let l2s;
|
|
509
|
+
try { l2s = await fsp.readdir(l1abs, { withFileTypes: true }); }
|
|
510
|
+
catch { l2s = []; }
|
|
511
|
+
let hasL2 = false;
|
|
512
|
+
for (const l2ent of l2s) {
|
|
513
|
+
if (!l2ent.isDirectory()) continue;
|
|
514
|
+
if (!isSafeId(l2ent.name)) continue;
|
|
515
|
+
out.push({ kind: 'group-topic', groupId: g, path: [l1, l2ent.name] });
|
|
516
|
+
hasL2 = true;
|
|
517
|
+
}
|
|
518
|
+
if (!hasL2) {
|
|
519
|
+
const hasMemory = existsSync(join(l1abs, 'memory.md'));
|
|
520
|
+
const hasSummary = existsSync(join(l1abs, 'summary.md'));
|
|
521
|
+
if (hasMemory || hasSummary) {
|
|
522
|
+
out.push({ kind: 'group-topic', groupId: g, path: [l1] });
|
|
523
|
+
}
|
|
477
524
|
}
|
|
478
525
|
}
|
|
479
526
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* models.js — Model ID registry for Yeaft
|
|
2
|
+
* models.js — Model ID registry for Yeaft Yeaft
|
|
3
3
|
*
|
|
4
4
|
* Maps model IDs (e.g. "gpt-5", "claude-sonnet-4-20250514") to their
|
|
5
5
|
* adapter type, API base URL, and capabilities.
|
|
@@ -305,7 +305,7 @@ export function parseModelRef(ref) {
|
|
|
305
305
|
// ─── task-327a: thinking / reasoning capability ─────────────────
|
|
306
306
|
|
|
307
307
|
/**
|
|
308
|
-
* Valid effort levels accepted by
|
|
308
|
+
* Valid effort levels accepted by Yeaft adapters.
|
|
309
309
|
* @typedef {'low' | 'medium' | 'high' | 'max'} Effort
|
|
310
310
|
*/
|
|
311
311
|
|
|
@@ -325,7 +325,7 @@ export const ANTHROPIC_THINKING_BUDGETS = {
|
|
|
325
325
|
};
|
|
326
326
|
|
|
327
327
|
/**
|
|
328
|
-
* Map a
|
|
328
|
+
* Map a Yeaft effort level to the OpenAI reasoning.effort enum. OpenAI does
|
|
329
329
|
* not expose a 'max' level — callers that pass 'max' get 'high' (the highest
|
|
330
330
|
* available on that protocol). The router/engine should log this downgrade
|
|
331
331
|
* but the adapter MUST NOT error.
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* slice so it can be safely fed to the LLM adapter.
|
|
4
4
|
*
|
|
5
5
|
* Why this exists:
|
|
6
|
-
* `agent/
|
|
7
|
-
* `agent/
|
|
6
|
+
* `agent/yeaft/conversation/persist.js#loadRecentByGroup` and
|
|
7
|
+
* `agent/yeaft/history-compact.js#compactHistory` both produce
|
|
8
8
|
* sub-slices of a longer message stream. Both paths can — depending on
|
|
9
9
|
* where the cut lands — produce one of two illegal shapes:
|
|
10
10
|
* 1. A `role: 'tool'` message whose owning assistant `tool_use` is
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* model tier it runs on, and what system prompt it carries.
|
|
6
6
|
*
|
|
7
7
|
* Presets live as markdown files with YAML frontmatter in
|
|
8
|
-
* `agent/
|
|
8
|
+
* `agent/yeaft/templates/personas/*.md`.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { readFileSync, readdirSync } from 'fs';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Single source of truth for system prompts. Both engine.js and cli.js
|
|
5
5
|
* import buildSystemPrompt() from here. Supports 'en' and 'zh'.
|
|
6
6
|
*
|
|
7
|
-
* Template files from agent/
|
|
7
|
+
* Template files from agent/yeaft/templates/ are loaded once at startup
|
|
8
8
|
* and used to enrich the system prompt beyond the hardcoded fallbacks.
|
|
9
9
|
*
|
|
10
10
|
* Concept layering (DESIGN-PROMPT §3):
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - router: route_forward → coordinator.ingest wrapper with guard,
|
|
7
7
|
* self-reject, task.members forwarding, and causedBy chain.
|
|
8
8
|
*
|
|
9
|
-
* See agent/
|
|
9
|
+
* See agent/yeaft/tools/route-forward.js for the VP-facing tool.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
export {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* (a) Does NOT touch RoleInstance state machine internals (that's 334c).
|
|
23
23
|
* (b) Does NOT touch live-diff (334h).
|
|
24
24
|
* (c) Persistence routes through group.appendMessage via Coordinator.
|
|
25
|
-
* (d) Tool schema uses defineTool (agent/
|
|
25
|
+
* (d) Tool schema uses defineTool (agent/yeaft/tools/types.js).
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { resolveMemberId } from '../groups/roster.js';
|