@yeaft/webchat-agent 0.1.873 → 0.1.875
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/connection/message-router.js +20 -13
- package/package.json +1 -1
- package/providers/copilot-models.js +105 -89
- package/providers/copilot.js +3 -1
- package/yeaft/attachments.js +2 -2
- package/yeaft/cli.js +13 -13
- package/yeaft/compact/compactor.js +20 -20
- package/yeaft/conversation/persist.js +95 -95
- package/yeaft/debug-trace.js +12 -12
- package/yeaft/dream-v2/apply.js +15 -15
- package/yeaft/dream-v2/merge.js +12 -12
- package/yeaft/dream-v2/prompts/{extract-group.md → extract-session.md} +12 -12
- package/yeaft/dream-v2/prompts/index.js +3 -3
- package/yeaft/dream-v2/prompts/triage-pass1.md +1 -1
- package/yeaft/dream-v2/runner.js +37 -37
- package/yeaft/dream-v2/segment.js +3 -3
- package/yeaft/dream-v2/session-wiring.js +22 -22
- package/yeaft/dream-v2/state.js +7 -7
- package/yeaft/dream-v2/triage.js +22 -22
- package/yeaft/engine.js +65 -65
- package/yeaft/memory/ams-registry.js +22 -22
- package/yeaft/memory/seed-backfill.js +9 -9
- package/yeaft/memory/store-v2.js +27 -27
- package/yeaft/prompts.js +9 -9
- package/yeaft/routing/loop-guard.js +14 -14
- package/yeaft/routing/router.js +5 -5
- package/yeaft/session.js +5 -5
- package/yeaft/sessions/coordinator.js +96 -20
- package/yeaft/{groups → sessions}/ids.js +3 -3
- package/yeaft/{groups → sessions}/index.js +28 -28
- package/yeaft/sessions/pre-flow.js +178 -42
- package/yeaft/{groups → sessions}/seed-default.js +19 -19
- package/yeaft/{groups/group-config.js → sessions/session-config.js} +29 -29
- package/yeaft/{groups/group-crud.js → sessions/session-crud.js} +113 -113
- package/yeaft/sessions/session-store.js +85 -154
- package/yeaft/stop-hooks.js +4 -4
- package/yeaft/tools/todo-write.js +1 -1
- package/yeaft/tools/types.js +2 -2
- package/yeaft/vp/registry.js +1 -1
- package/yeaft/vp/vp-crud.js +1 -1
- package/yeaft/vp-status-broker.js +28 -28
- package/yeaft/web-bridge.js +411 -398
- package/yeaft/groups/coordinator.js +0 -221
- package/yeaft/groups/group-store.js +0 -212
- package/yeaft/groups/pre-flow.js +0 -329
- /package/yeaft/{groups → sessions}/feature-flag.js +0 -0
- /package/yeaft/{groups → sessions}/project-doc.js +0 -0
- /package/yeaft/{groups → sessions}/roster.js +0 -0
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* the provided roster (typically `[defaultVpId]`). Idempotent: if the group
|
|
6
6
|
* already exists on disk, returns the existing handle without overwriting.
|
|
7
7
|
*
|
|
8
|
-
* Separation from group-store.
|
|
9
|
-
* -
|
|
8
|
+
* Separation from group-store.createSession:
|
|
9
|
+
* - createSession throws on duplicate; seed returns the existing handle.
|
|
10
10
|
* - seed picks a stable id `grp_default` so UI can deep-link to it.
|
|
11
11
|
* - seed is the only place that writes the "default group exists" side
|
|
12
12
|
* effect during the bootstrap flow.
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
import { existsSync, mkdirSync } from 'fs';
|
|
16
16
|
import { join } from 'path';
|
|
17
17
|
import { homedir } from 'os';
|
|
18
|
-
import {
|
|
18
|
+
import { openSession, createSession, loadSessionMeta } from './session-store.js';
|
|
19
19
|
import { seedSummaryIfMissingSync } from '../memory/store-v2.js';
|
|
20
20
|
|
|
21
|
-
export const
|
|
21
|
+
export const DEFAULT_SESSION_ID = 'grp_default';
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Default memory root used when callers don't pass `options.memoryRoot`.
|
|
@@ -30,14 +30,14 @@ const DEFAULT_MEMORY_ROOT = join(homedir(), '.yeaft', 'memory');
|
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Build the default-group seed summary body. Pulled into a helper so
|
|
33
|
-
* tests can pin the exact format. Mirrors `
|
|
33
|
+
* tests can pin the exact format. Mirrors `buildSessionSeedSummary` in
|
|
34
34
|
* `group-crud.js` shape, with the "Default group" wording reserved for
|
|
35
35
|
* the bootstrap path.
|
|
36
36
|
*
|
|
37
37
|
* @param {{ name?: string, roster?: string[], defaultVpId?: string|null }} spec
|
|
38
38
|
* @returns {string}
|
|
39
39
|
*/
|
|
40
|
-
export function
|
|
40
|
+
export function buildDefaultSessionSeedSummary(spec) {
|
|
41
41
|
const name = String(spec?.name || 'Default').trim();
|
|
42
42
|
const roster = Array.isArray(spec?.roster) ? spec.roster : [];
|
|
43
43
|
const defaultVpId = spec?.defaultVpId || null;
|
|
@@ -51,16 +51,16 @@ export function buildDefaultGroupSeedSummary(spec) {
|
|
|
51
51
|
/**
|
|
52
52
|
* @param {string} yeaftDir
|
|
53
53
|
* @param {{ defaultVpId?: string|null, roster?: string[], name?: string, memoryRoot?: string }} [spec]
|
|
54
|
-
* @returns {{ group: import('./
|
|
54
|
+
* @returns {{ group: import('./session-store.js').GroupHandle, created: boolean }}
|
|
55
55
|
*/
|
|
56
|
-
export function
|
|
56
|
+
export function seedDefaultSession(yeaftDir, spec = {}) {
|
|
57
57
|
const memoryRoot = spec.memoryRoot || DEFAULT_MEMORY_ROOT;
|
|
58
|
-
const
|
|
59
|
-
if (!existsSync(
|
|
58
|
+
const sessionsRoot = join(yeaftDir, 'sessions');
|
|
59
|
+
if (!existsSync(sessionsRoot)) mkdirSync(sessionsRoot, { recursive: true });
|
|
60
60
|
|
|
61
|
-
const existingDir = join(
|
|
62
|
-
if (existsSync(existingDir) &&
|
|
63
|
-
return { group:
|
|
61
|
+
const existingDir = join(sessionsRoot, DEFAULT_SESSION_ID);
|
|
62
|
+
if (existsSync(existingDir) && loadSessionMeta(existingDir)) {
|
|
63
|
+
return { group: openSession(sessionsRoot, DEFAULT_SESSION_ID), created: false };
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
const roster = Array.isArray(spec.roster) && spec.roster.length
|
|
@@ -69,8 +69,8 @@ export function seedDefaultGroup(yeaftDir, spec = {}) {
|
|
|
69
69
|
const defaultVpId = spec.defaultVpId || roster[0] || null;
|
|
70
70
|
const name = spec.name || 'Default';
|
|
71
71
|
|
|
72
|
-
const group =
|
|
73
|
-
id:
|
|
72
|
+
const group = createSession(sessionsRoot, {
|
|
73
|
+
id: DEFAULT_SESSION_ID,
|
|
74
74
|
name,
|
|
75
75
|
roster,
|
|
76
76
|
defaultVpId,
|
|
@@ -79,16 +79,16 @@ export function seedDefaultGroup(yeaftDir, spec = {}) {
|
|
|
79
79
|
// Seed Layer-A resident summary so the very first session — even on a
|
|
80
80
|
// brand-new install where only `grp_default` exists — renders a non-
|
|
81
81
|
// empty memory section in the system prompt. No-op once Dream-v2 (or
|
|
82
|
-
//
|
|
82
|
+
// createSessionFromSpec) has already written one. Best-effort: a memory-
|
|
83
83
|
// root permission failure must NOT break the bootstrap flow.
|
|
84
84
|
try {
|
|
85
85
|
seedSummaryIfMissingSync(
|
|
86
|
-
{ kind: 'group', id:
|
|
87
|
-
|
|
86
|
+
{ kind: 'group', id: DEFAULT_SESSION_ID },
|
|
87
|
+
buildDefaultSessionSeedSummary({ name, roster, defaultVpId }),
|
|
88
88
|
{ root: memoryRoot },
|
|
89
89
|
);
|
|
90
90
|
} catch (err) {
|
|
91
|
-
console.warn(`[seed-default] failed to seed summary.md for ${
|
|
91
|
+
console.warn(`[seed-default] failed to seed summary.md for ${DEFAULT_SESSION_ID}:`, err?.message || err);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
return { group, created: true };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* group-config.js — Per-group selected model state.
|
|
3
3
|
*
|
|
4
4
|
* Each group may carry its header-selected model in `config.json` at
|
|
5
|
-
* ~/.yeaft/
|
|
5
|
+
* ~/.yeaft/sessions/<sessionId>/config.json
|
|
6
6
|
*
|
|
7
7
|
* v1 schema (intentionally tiny — extend via additive keys only):
|
|
8
8
|
* {
|
|
@@ -21,17 +21,17 @@
|
|
|
21
21
|
import { existsSync, readFileSync } from 'fs';
|
|
22
22
|
import { join } from 'path';
|
|
23
23
|
import { writeAtomic } from '../storage/index.js';
|
|
24
|
-
import {
|
|
24
|
+
import { sessionsRoot, resolveSessionYeaftDir } from './session-crud.js';
|
|
25
25
|
|
|
26
26
|
const CONFIG_FILE = 'config.json';
|
|
27
27
|
|
|
28
28
|
/** Whitelist of persisted group model-state fields. Reject everything else. */
|
|
29
29
|
const ALLOWED_KEYS = new Set(['model']);
|
|
30
30
|
|
|
31
|
-
export class
|
|
31
|
+
export class SessionConfigError extends Error {
|
|
32
32
|
constructor(code, message) {
|
|
33
33
|
super(message || code);
|
|
34
|
-
this.name = '
|
|
34
|
+
this.name = 'SessionConfigError';
|
|
35
35
|
this.code = code;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -41,24 +41,24 @@ export class GroupConfigError extends Error {
|
|
|
41
41
|
* per-group workDir registry so groups bound to a project directory
|
|
42
42
|
* keep their config alongside the group meta.
|
|
43
43
|
*/
|
|
44
|
-
export function
|
|
44
|
+
export function sessionConfigPath(yeaftDir, sessionId) {
|
|
45
45
|
if (!yeaftDir) return null;
|
|
46
|
-
const groupYeaftDir =
|
|
47
|
-
return join(
|
|
46
|
+
const groupYeaftDir = resolveSessionYeaftDir(yeaftDir, sessionId);
|
|
47
|
+
return join(sessionsRoot(groupYeaftDir), sessionId, CONFIG_FILE);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Read a group's config.json. Returns `{}` when the file is missing or
|
|
52
52
|
* corrupt — callers fall back to user-level defaults via
|
|
53
|
-
* `
|
|
53
|
+
* `resolveSessionConfig`. We never auto-write on read.
|
|
54
54
|
*
|
|
55
55
|
* @param {string} yeaftDir
|
|
56
|
-
* @param {string}
|
|
56
|
+
* @param {string} sessionId
|
|
57
57
|
* @returns {object}
|
|
58
58
|
*/
|
|
59
|
-
export function
|
|
60
|
-
if (!
|
|
61
|
-
const path =
|
|
59
|
+
export function loadSessionConfig(yeaftDir, sessionId) {
|
|
60
|
+
if (!sessionId || !yeaftDir) return {};
|
|
61
|
+
const path = sessionConfigPath(yeaftDir, sessionId);
|
|
62
62
|
if (!path || !existsSync(path)) return {};
|
|
63
63
|
try {
|
|
64
64
|
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
@@ -76,25 +76,25 @@ export function loadGroupConfig(yeaftDir, groupId) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
|
-
* Validate a partial group-config object. Throws
|
|
79
|
+
* Validate a partial group-config object. Throws SessionConfigError on
|
|
80
80
|
* any unknown key or malformed value. Empty / null values for known
|
|
81
81
|
* keys are allowed — they signal "fall back to user default".
|
|
82
82
|
*
|
|
83
83
|
* @param {object} cfg
|
|
84
84
|
*/
|
|
85
|
-
export function
|
|
85
|
+
export function validateSessionConfig(cfg) {
|
|
86
86
|
if (cfg === null || cfg === undefined) return;
|
|
87
87
|
if (typeof cfg !== 'object' || Array.isArray(cfg)) {
|
|
88
|
-
throw new
|
|
88
|
+
throw new SessionConfigError('invalid_shape', 'config must be an object');
|
|
89
89
|
}
|
|
90
90
|
for (const k of Object.keys(cfg)) {
|
|
91
91
|
if (!ALLOWED_KEYS.has(k)) {
|
|
92
|
-
throw new
|
|
92
|
+
throw new SessionConfigError('unknown_key', `unknown config key: ${k}`);
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
if ('model' in cfg && cfg.model !== null && cfg.model !== undefined && cfg.model !== '') {
|
|
96
96
|
if (typeof cfg.model !== 'string' || !cfg.model.trim()) {
|
|
97
|
-
throw new
|
|
97
|
+
throw new SessionConfigError('invalid_model', 'model must be a non-empty string');
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -106,14 +106,14 @@ export function validateGroupConfig(cfg) {
|
|
|
106
106
|
* back to the user default).
|
|
107
107
|
*
|
|
108
108
|
* @param {string} yeaftDir
|
|
109
|
-
* @param {string}
|
|
109
|
+
* @param {string} sessionId
|
|
110
110
|
* @param {object} partial
|
|
111
111
|
* @returns {object}
|
|
112
112
|
*/
|
|
113
|
-
export function
|
|
114
|
-
if (!
|
|
115
|
-
|
|
116
|
-
const current =
|
|
113
|
+
export function saveSessionConfig(yeaftDir, sessionId, partial) {
|
|
114
|
+
if (!sessionId) throw new SessionConfigError('missing_group_id', 'sessionId required');
|
|
115
|
+
validateSessionConfig(partial);
|
|
116
|
+
const current = loadSessionConfig(yeaftDir, sessionId);
|
|
117
117
|
const next = { ...current };
|
|
118
118
|
for (const [k, v] of Object.entries(partial || {})) {
|
|
119
119
|
if (!ALLOWED_KEYS.has(k)) continue;
|
|
@@ -123,17 +123,17 @@ export function saveGroupConfig(yeaftDir, groupId, partial) {
|
|
|
123
123
|
next[k] = typeof v === 'string' ? v.trim() : v;
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
const path =
|
|
126
|
+
const path = sessionConfigPath(yeaftDir, sessionId);
|
|
127
127
|
writeAtomic(path, `${JSON.stringify(next, null, 2)}\n`);
|
|
128
128
|
return next;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
132
|
* Initialise an empty config.json next to a brand-new group. Idempotent —
|
|
133
|
-
* leaves an existing file untouched. Called by `
|
|
133
|
+
* leaves an existing file untouched. Called by `createSessionFromSpec`.
|
|
134
134
|
*/
|
|
135
|
-
export function
|
|
136
|
-
const path =
|
|
135
|
+
export function ensureSessionConfigFile(yeaftDir, sessionId) {
|
|
136
|
+
const path = sessionConfigPath(yeaftDir, sessionId);
|
|
137
137
|
if (!path || existsSync(path)) return;
|
|
138
138
|
try {
|
|
139
139
|
writeAtomic(path, `${JSON.stringify({}, null, 2)}\n`);
|
|
@@ -152,12 +152,12 @@ export function ensureGroupConfigFile(yeaftDir, groupId) {
|
|
|
152
152
|
* verbatim from the user config.
|
|
153
153
|
*
|
|
154
154
|
* @param {object} userConfig — loadConfig() result
|
|
155
|
-
* @param {object}
|
|
155
|
+
* @param {object} sessionConfig — loadSessionConfig() result
|
|
156
156
|
* @returns {object} — A new config object safe to hand to the engine.
|
|
157
157
|
*/
|
|
158
|
-
export function
|
|
158
|
+
export function resolveSessionConfig(userConfig, sessionConfig) {
|
|
159
159
|
const base = userConfig ? { ...userConfig } : {};
|
|
160
|
-
const overrides =
|
|
160
|
+
const overrides = sessionConfig && typeof sessionConfig === 'object' ? sessionConfig : {};
|
|
161
161
|
if (overrides.model && typeof overrides.model === 'string' && overrides.model.trim()) {
|
|
162
162
|
const model = overrides.model.trim();
|
|
163
163
|
base.model = model;
|