@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.
Files changed (48) hide show
  1. package/connection/message-router.js +20 -13
  2. package/package.json +1 -1
  3. package/providers/copilot-models.js +105 -89
  4. package/providers/copilot.js +3 -1
  5. package/yeaft/attachments.js +2 -2
  6. package/yeaft/cli.js +13 -13
  7. package/yeaft/compact/compactor.js +20 -20
  8. package/yeaft/conversation/persist.js +95 -95
  9. package/yeaft/debug-trace.js +12 -12
  10. package/yeaft/dream-v2/apply.js +15 -15
  11. package/yeaft/dream-v2/merge.js +12 -12
  12. package/yeaft/dream-v2/prompts/{extract-group.md → extract-session.md} +12 -12
  13. package/yeaft/dream-v2/prompts/index.js +3 -3
  14. package/yeaft/dream-v2/prompts/triage-pass1.md +1 -1
  15. package/yeaft/dream-v2/runner.js +37 -37
  16. package/yeaft/dream-v2/segment.js +3 -3
  17. package/yeaft/dream-v2/session-wiring.js +22 -22
  18. package/yeaft/dream-v2/state.js +7 -7
  19. package/yeaft/dream-v2/triage.js +22 -22
  20. package/yeaft/engine.js +65 -65
  21. package/yeaft/memory/ams-registry.js +22 -22
  22. package/yeaft/memory/seed-backfill.js +9 -9
  23. package/yeaft/memory/store-v2.js +27 -27
  24. package/yeaft/prompts.js +9 -9
  25. package/yeaft/routing/loop-guard.js +14 -14
  26. package/yeaft/routing/router.js +5 -5
  27. package/yeaft/session.js +5 -5
  28. package/yeaft/sessions/coordinator.js +96 -20
  29. package/yeaft/{groups → sessions}/ids.js +3 -3
  30. package/yeaft/{groups → sessions}/index.js +28 -28
  31. package/yeaft/sessions/pre-flow.js +178 -42
  32. package/yeaft/{groups → sessions}/seed-default.js +19 -19
  33. package/yeaft/{groups/group-config.js → sessions/session-config.js} +29 -29
  34. package/yeaft/{groups/group-crud.js → sessions/session-crud.js} +113 -113
  35. package/yeaft/sessions/session-store.js +85 -154
  36. package/yeaft/stop-hooks.js +4 -4
  37. package/yeaft/tools/todo-write.js +1 -1
  38. package/yeaft/tools/types.js +2 -2
  39. package/yeaft/vp/registry.js +1 -1
  40. package/yeaft/vp/vp-crud.js +1 -1
  41. package/yeaft/vp-status-broker.js +28 -28
  42. package/yeaft/web-bridge.js +411 -398
  43. package/yeaft/groups/coordinator.js +0 -221
  44. package/yeaft/groups/group-store.js +0 -212
  45. package/yeaft/groups/pre-flow.js +0 -329
  46. /package/yeaft/{groups → sessions}/feature-flag.js +0 -0
  47. /package/yeaft/{groups → sessions}/project-doc.js +0 -0
  48. /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.createGroup:
9
- * - createGroup throws on duplicate; seed returns the existing handle.
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 { openGroup, createGroup, loadGroupMeta } from './group-store.js';
18
+ import { openSession, createSession, loadSessionMeta } from './session-store.js';
19
19
  import { seedSummaryIfMissingSync } from '../memory/store-v2.js';
20
20
 
21
- export const DEFAULT_GROUP_ID = 'grp_default';
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 `buildGroupSeedSummary` in
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 buildDefaultGroupSeedSummary(spec) {
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('./group-store.js').GroupHandle, created: boolean }}
54
+ * @returns {{ group: import('./session-store.js').GroupHandle, created: boolean }}
55
55
  */
56
- export function seedDefaultGroup(yeaftDir, spec = {}) {
56
+ export function seedDefaultSession(yeaftDir, spec = {}) {
57
57
  const memoryRoot = spec.memoryRoot || DEFAULT_MEMORY_ROOT;
58
- const groupsRoot = join(yeaftDir, 'groups');
59
- if (!existsSync(groupsRoot)) mkdirSync(groupsRoot, { recursive: true });
58
+ const sessionsRoot = join(yeaftDir, 'sessions');
59
+ if (!existsSync(sessionsRoot)) mkdirSync(sessionsRoot, { recursive: true });
60
60
 
61
- const existingDir = join(groupsRoot, DEFAULT_GROUP_ID);
62
- if (existsSync(existingDir) && loadGroupMeta(existingDir)) {
63
- return { group: openGroup(groupsRoot, DEFAULT_GROUP_ID), created: false };
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 = createGroup(groupsRoot, {
73
- id: DEFAULT_GROUP_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
- // createGroupFromSpec) has already written one. Best-effort: a memory-
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: DEFAULT_GROUP_ID },
87
- buildDefaultGroupSeedSummary({ name, roster, defaultVpId }),
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 ${DEFAULT_GROUP_ID}:`, err?.message || err);
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/groups/<groupId>/config.json
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 { groupsRoot, resolveGroupYeaftDir } from './group-crud.js';
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 GroupConfigError extends Error {
31
+ export class SessionConfigError extends Error {
32
32
  constructor(code, message) {
33
33
  super(message || code);
34
- this.name = 'GroupConfigError';
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 groupConfigPath(yeaftDir, groupId) {
44
+ export function sessionConfigPath(yeaftDir, sessionId) {
45
45
  if (!yeaftDir) return null;
46
- const groupYeaftDir = resolveGroupYeaftDir(yeaftDir, groupId);
47
- return join(groupsRoot(groupYeaftDir), groupId, CONFIG_FILE);
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
- * `resolveGroupConfig`. We never auto-write on read.
53
+ * `resolveSessionConfig`. We never auto-write on read.
54
54
  *
55
55
  * @param {string} yeaftDir
56
- * @param {string} groupId
56
+ * @param {string} sessionId
57
57
  * @returns {object}
58
58
  */
59
- export function loadGroupConfig(yeaftDir, groupId) {
60
- if (!groupId || !yeaftDir) return {};
61
- const path = groupConfigPath(yeaftDir, groupId);
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 GroupConfigError on
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 validateGroupConfig(cfg) {
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 GroupConfigError('invalid_shape', 'config must be an object');
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 GroupConfigError('unknown_key', `unknown config key: ${k}`);
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 GroupConfigError('invalid_model', 'model must be a non-empty string');
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} groupId
109
+ * @param {string} sessionId
110
110
  * @param {object} partial
111
111
  * @returns {object}
112
112
  */
113
- export function saveGroupConfig(yeaftDir, groupId, partial) {
114
- if (!groupId) throw new GroupConfigError('missing_group_id', 'groupId required');
115
- validateGroupConfig(partial);
116
- const current = loadGroupConfig(yeaftDir, groupId);
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 = groupConfigPath(yeaftDir, groupId);
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 `createGroupFromSpec`.
133
+ * leaves an existing file untouched. Called by `createSessionFromSpec`.
134
134
  */
135
- export function ensureGroupConfigFile(yeaftDir, groupId) {
136
- const path = groupConfigPath(yeaftDir, groupId);
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} groupConfigloadGroupConfig() result
155
+ * @param {object} sessionConfigloadSessionConfig() result
156
156
  * @returns {object} — A new config object safe to hand to the engine.
157
157
  */
158
- export function resolveGroupConfig(userConfig, groupConfig) {
158
+ export function resolveSessionConfig(userConfig, sessionConfig) {
159
159
  const base = userConfig ? { ...userConfig } : {};
160
- const overrides = groupConfig && typeof groupConfig === 'object' ? groupConfig : {};
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;