@venturewild/workspace 0.6.18 → 0.6.20
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/package.json +1 -1
- package/server/src/bazaar/core.mjs +59 -0
- package/server/src/canvas/core.mjs +37 -0
- package/server/src/chat-sessions.mjs +97 -8
- package/server/src/config.mjs +1 -1
- package/server/src/index.mjs +252 -6
- package/server/src/lobby-browse.mjs +32 -2
- package/server/src/support/index.mjs +30 -0
- package/server/src/support/mcp-server.mjs +156 -0
- package/server/src/support/tickets.mjs +111 -0
- package/server/src/turn-mcp.mjs +3 -1
- package/server/src/usage.mjs +16 -5
- package/web/dist/assets/index-ABxdF5Rs.css +32 -0
- package/web/dist/assets/index-CrHQ6EW8.js +131 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-BPNfw1Sq.css +0 -32
- package/web/dist/assets/index-VXldPvIc.js +0 -131
package/package.json
CHANGED
|
@@ -73,6 +73,65 @@ export const SEED_THEMES = [
|
|
|
73
73
|
outcomeScore: 0.74, outcomeStats: { builds: 0, working: 0 }, safetyBadge: 'verified',
|
|
74
74
|
rating: { stars: 4, count: 6 }, reward: { model: 'one-time', unit: 'per apply', oneTimeValue: 1.5, perUseValue: 0 },
|
|
75
75
|
},
|
|
76
|
+
// Framework themes (item 5) — the structural engine in action. Mirror of
|
|
77
|
+
// FRAMEWORK_THEMES in web/src/theme.js (keep in sync). Each carries a `structure`
|
|
78
|
+
// bundle the client's normalizeStructure validates before applying.
|
|
79
|
+
{
|
|
80
|
+
id: 'theme-neo-brutalism', kind: 'theme', source: 'theme',
|
|
81
|
+
title: 'Neo-Brutalism', pitch: 'Raw & bold — square corners, thick black borders, hard shadows.',
|
|
82
|
+
summary: 'A loud, high-contrast look: zero radius, 3px black borders, hard offset shadows, a bold grotesk.',
|
|
83
|
+
tags: ['light', 'bold', 'brutalist', 'framework'],
|
|
84
|
+
producer: { name: 'VentureWild', handle: 'venturewild', kind: 'vendor' },
|
|
85
|
+
theme: {
|
|
86
|
+
mode: 'light', accent: '#2b59ff',
|
|
87
|
+
tokens: { bg: '#fffdf5', surface: '#ffffff', text: '#0a0a0a', textMuted: '#3a3a3a', border: '#0a0a0a', canvas1: '#ffe600', canvas2: '#ff90e8', canvas3: '#9df6c4' },
|
|
88
|
+
structure: { radius: 0, borderWidth: 3, shadow: 'hard', fontFamily: 'grotesk', fontWeight: 'bold', surfaceStyle: 'flat' },
|
|
89
|
+
},
|
|
90
|
+
outcomeScore: 0.88, outcomeStats: { builds: 0, working: 0 }, safetyBadge: 'verified',
|
|
91
|
+
rating: { stars: 5, count: 19 }, reward: { model: 'one-time', unit: 'per apply', oneTimeValue: 1.5, perUseValue: 0 },
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 'theme-glassmorphism', kind: 'theme', source: 'theme',
|
|
95
|
+
title: 'Glassmorphism', pitch: 'Frosted glass — translucent, blurred panels over a vivid gradient.',
|
|
96
|
+
summary: 'Soft, modern, translucent: blurred surfaces float over a violet-cyan gradient, big radius, gentle shadow.',
|
|
97
|
+
tags: ['light', 'glass', 'modern', 'framework'],
|
|
98
|
+
producer: { name: 'VentureWild', handle: 'venturewild', kind: 'vendor' },
|
|
99
|
+
theme: {
|
|
100
|
+
mode: 'light', accent: '#7c3aed',
|
|
101
|
+
tokens: { bg: '#f3f0ff', surface: '#ffffff', text: '#1a1530', textMuted: '#6b6486', border: '#e5e0f5', canvas1: '#a78bfa', canvas2: '#67e8f9', canvas3: '#f0abfc' },
|
|
102
|
+
structure: { radius: 22, borderWidth: 1, shadow: 'soft', fontFamily: 'inter', fontWeight: 'medium', surfaceStyle: 'glass' },
|
|
103
|
+
},
|
|
104
|
+
outcomeScore: 0.86, outcomeStats: { builds: 0, working: 0 }, safetyBadge: 'verified',
|
|
105
|
+
rating: { stars: 5, count: 22 }, reward: { model: 'one-time', unit: 'per apply', oneTimeValue: 1.5, perUseValue: 0 },
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'theme-swiss', kind: 'theme', source: 'theme',
|
|
109
|
+
title: 'Swiss / Minimal', pitch: 'International Typographic — flat, tight, no shadow, a single red.',
|
|
110
|
+
summary: 'Classic Swiss style: a clean grotesk, flat surfaces, near-zero radius, no shadow, one decisive red accent.',
|
|
111
|
+
tags: ['light', 'minimal', 'swiss', 'framework'],
|
|
112
|
+
producer: { name: 'VentureWild', handle: 'venturewild', kind: 'vendor' },
|
|
113
|
+
theme: {
|
|
114
|
+
mode: 'light', accent: '#e0322a',
|
|
115
|
+
tokens: { bg: '#ffffff', surface: '#ffffff', text: '#111111', textMuted: '#6b6b6b', border: '#e4e4e4', canvas1: '#f5f5f5', canvas2: '#fafafa', canvas3: '#ffffff' },
|
|
116
|
+
structure: { radius: 2, borderWidth: 1, shadow: 'none', fontFamily: 'grotesk', fontWeight: 'semibold', surfaceStyle: 'flat' },
|
|
117
|
+
},
|
|
118
|
+
outcomeScore: 0.83, outcomeStats: { builds: 0, working: 0 }, safetyBadge: 'verified',
|
|
119
|
+
rating: { stars: 5, count: 14 }, reward: { model: 'one-time', unit: 'per apply', oneTimeValue: 1.5, perUseValue: 0 },
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: 'theme-retro-terminal', kind: 'theme', source: 'theme',
|
|
123
|
+
title: 'Retro-Terminal', pitch: 'Phosphor CRT — dark, monospace, a green accent that glows.',
|
|
124
|
+
summary: 'A green-phosphor terminal: near-black green-tinted background, monospace, glowing accent ring.',
|
|
125
|
+
tags: ['dark', 'mono', 'retro', 'framework'],
|
|
126
|
+
producer: { name: 'VentureWild', handle: 'venturewild', kind: 'vendor' },
|
|
127
|
+
theme: {
|
|
128
|
+
mode: 'dark', accent: '#2ee66b',
|
|
129
|
+
tokens: { bg: '#04140a', surface: '#0a1f12', text: '#9bf7b8', textMuted: '#4f9e6a', border: '#15402a', canvas1: '#04160c', canvas2: '#06200f', canvas3: '#020a05' },
|
|
130
|
+
structure: { radius: 0, borderWidth: 1, shadow: 'glow', fontFamily: 'mono', fontWeight: 'normal', surfaceStyle: 'flat' },
|
|
131
|
+
},
|
|
132
|
+
outcomeScore: 0.81, outcomeStats: { builds: 0, working: 0 }, safetyBadge: 'verified',
|
|
133
|
+
rating: { stars: 4, count: 12 }, reward: { model: 'one-time', unit: 'per apply', oneTimeValue: 1.5, perUseValue: 0 },
|
|
134
|
+
},
|
|
76
135
|
];
|
|
77
136
|
|
|
78
137
|
// ~/.wild-workspace/bazaar — mirrors logpaths.globalDir() but kept dependency-free
|
|
@@ -37,6 +37,39 @@ export const TOKEN_KEYS = [
|
|
|
37
37
|
'bg', 'bgElev', 'surface', 'border', 'text', 'textMuted', 'canvas1', 'canvas2', 'canvas3',
|
|
38
38
|
];
|
|
39
39
|
|
|
40
|
+
// Structural theme tokens (item 5, 2026-06-21) — the SHAPE half of the theme engine.
|
|
41
|
+
// SAME security boundary as the color tokens: numbers CLAMP to a safe range, enums and
|
|
42
|
+
// font ids are ALLOWLISTED — a stored structure can only ever be one of these values,
|
|
43
|
+
// never a raw CSS string. Keep in lockstep with theme.js (sanitizeStructure + the
|
|
44
|
+
// *_IDS / *_STACKS / *_VALUES tables that map an id to its hard-coded CSS).
|
|
45
|
+
export const SHADOW_IDS = ['none', 'soft', 'hard', 'glow'];
|
|
46
|
+
export const SURFACE_STYLE_IDS = ['flat', 'glass', 'paper'];
|
|
47
|
+
export const FONT_WEIGHT_IDS = ['normal', 'medium', 'semibold', 'bold'];
|
|
48
|
+
export const UI_FONT_IDS = ['system', 'inter', 'grotesk', 'serif', 'mono'];
|
|
49
|
+
|
|
50
|
+
function clampInt(v, lo, hi) {
|
|
51
|
+
const n = Math.round(Number(v));
|
|
52
|
+
if (!Number.isFinite(n)) return null;
|
|
53
|
+
return Math.max(lo, Math.min(hi, n));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Validate a raw structure bundle to the allowlisted/clamped shape. Accepts a nested
|
|
57
|
+
// `structure` object OR flat fields on the raw theme (the MCP set_theme/publish_theme
|
|
58
|
+
// path passes flat input). Unknown values drop; out-of-range numbers clamp.
|
|
59
|
+
export function normalizeStructure(raw = {}) {
|
|
60
|
+
const r = raw && typeof raw === 'object' ? raw : {};
|
|
61
|
+
const out = {};
|
|
62
|
+
const radius = clampInt(r.radius, 0, 32);
|
|
63
|
+
if (radius !== null) out.radius = radius;
|
|
64
|
+
const bw = clampInt(r.borderWidth, 0, 6);
|
|
65
|
+
if (bw !== null) out.borderWidth = bw;
|
|
66
|
+
if (SHADOW_IDS.includes(r.shadow)) out.shadow = r.shadow;
|
|
67
|
+
if (UI_FONT_IDS.includes(r.fontFamily)) out.fontFamily = r.fontFamily;
|
|
68
|
+
if (FONT_WEIGHT_IDS.includes(r.fontWeight)) out.fontWeight = r.fontWeight;
|
|
69
|
+
if (SURFACE_STYLE_IDS.includes(r.surfaceStyle)) out.surfaceStyle = r.surfaceStyle;
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
|
|
40
73
|
// Reading typography (chat "Aa" controls). The server only ever stores allowlisted
|
|
41
74
|
// ids — never a raw font string — so a stored value can only ever be one the client
|
|
42
75
|
// maps to a hard-coded font stack (no CSS injection). Keep in lockstep with
|
|
@@ -222,6 +255,8 @@ export function normalizeTheme(raw = {}) {
|
|
|
222
255
|
if (hex) tokens[key] = hex;
|
|
223
256
|
}
|
|
224
257
|
theme.tokens = tokens;
|
|
258
|
+
// Structural tokens — nested `structure` wins, else read flat fields off the theme.
|
|
259
|
+
theme.structure = normalizeStructure(raw.structure || raw);
|
|
225
260
|
return theme;
|
|
226
261
|
}
|
|
227
262
|
|
|
@@ -407,6 +442,8 @@ export function createCanvas({ baseDir, personKey } = {}) {
|
|
|
407
442
|
if (hex) theme.tokens[key] = hex;
|
|
408
443
|
}
|
|
409
444
|
}
|
|
445
|
+
// Structural tokens — allowlisted/clamped, follows the user across devices.
|
|
446
|
+
theme.structure = normalizeStructure(raw?.structure);
|
|
410
447
|
ensureStateDir();
|
|
411
448
|
try { writeJsonAtomic(userThemeFile, theme); } catch { /* best-effort */ }
|
|
412
449
|
return theme;
|
|
@@ -68,18 +68,57 @@ function nextStamp(sessions) {
|
|
|
68
68
|
return Math.max(nowMs(), max + 1);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// Per-session provider (items 13/14) — Claude or GLM, LOCKED at creation (resume
|
|
72
|
+
// tokens don't cross providers, so a chat is one provider for its life).
|
|
73
|
+
const PROVIDERS = ['claude', 'glm'];
|
|
74
|
+
|
|
71
75
|
function sanitizeRec(s) {
|
|
72
76
|
const id = safeId(s?.id);
|
|
73
77
|
if (!id) return null;
|
|
74
78
|
return {
|
|
75
79
|
id,
|
|
76
80
|
title: typeof s.title === 'string' && s.title.trim() ? s.title.trim().slice(0, TITLE_CAP) : null,
|
|
81
|
+
// 'auto' = server-derived from the conversation; 'manual' = the user renamed it
|
|
82
|
+
// (sticky — auto-naming never overwrites a manual title). Default 'auto'.
|
|
83
|
+
titleSource: s.titleSource === 'manual' ? 'manual' : 'auto',
|
|
84
|
+
provider: PROVIDERS.includes(s.provider) ? s.provider : 'claude',
|
|
77
85
|
account: typeof s.account === 'string' && s.account ? s.account : 'local',
|
|
86
|
+
// Per-session context gauge (item 3) — the % of THIS session's provider window in
|
|
87
|
+
// use, persisted on turn completion so the sidebar shows it without a live turn.
|
|
88
|
+
contextPct: Number.isFinite(Number(s.contextPct)) ? Number(s.contextPct) : 0,
|
|
89
|
+
contextUsed: Number.isFinite(Number(s.contextUsed)) ? Number(s.contextUsed) : 0,
|
|
90
|
+
contextWindow: Number.isFinite(Number(s.contextWindow)) ? Number(s.contextWindow) : 0,
|
|
78
91
|
createdAt: Number(s.createdAt) || nowMs(),
|
|
79
92
|
lastActivityAt: Number(s.lastActivityAt) || Number(s.createdAt) || nowMs(),
|
|
80
93
|
};
|
|
81
94
|
}
|
|
82
95
|
|
|
96
|
+
// --- auto-naming + living summary (item 9) — derived from the conversation ---
|
|
97
|
+
// The old auto-naming "wasn't working"; this derives a stable title from the first
|
|
98
|
+
// user message and keeps a refreshing summary .md (the carry-the-gist handoff).
|
|
99
|
+
function userTexts(messages) {
|
|
100
|
+
return (Array.isArray(messages) ? messages : [])
|
|
101
|
+
.filter((m) => m && m.role === 'user' && typeof m.text === 'string' && m.text.trim())
|
|
102
|
+
.map((m) => m.text.trim().replace(/\s+/g, ' '));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function deriveTitle(messages) {
|
|
106
|
+
const users = userTexts(messages);
|
|
107
|
+
if (!users.length) return null;
|
|
108
|
+
let t = users[0]
|
|
109
|
+
.replace(/^(can you|could you|please|i want to|i'd like to|i would like to|help me|let's|lets|i need to|how do i|how can i|how to)\s+/i, '');
|
|
110
|
+
t = t.charAt(0).toUpperCase() + t.slice(1);
|
|
111
|
+
return t.slice(0, 60) || null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function buildSummary(messages) {
|
|
115
|
+
const users = userTexts(messages);
|
|
116
|
+
if (!users.length) return null;
|
|
117
|
+
const lines = ['# Session summary', '', `**Gist:** ${users[0].slice(0, 280)}`, '', '## What was asked'];
|
|
118
|
+
for (const u of users.slice(0, 12)) lines.push(`- ${u.slice(0, 120)}`);
|
|
119
|
+
return `${lines.join('\n')}\n`;
|
|
120
|
+
}
|
|
121
|
+
|
|
83
122
|
/**
|
|
84
123
|
* Per-workspace session store. Construct from the workspace's dataDir (the
|
|
85
124
|
* `.wild-workspace` folder) — `createChatSessions({ dataDir: workspaceFor(c).dataDir })`.
|
|
@@ -88,6 +127,7 @@ export function createChatSessions({ dataDir } = {}) {
|
|
|
88
127
|
const dir = path.join(dataDir, 'sessions');
|
|
89
128
|
const indexFile = path.join(dir, 'index.json');
|
|
90
129
|
const msgFile = (id) => path.join(dir, `${id}.json`);
|
|
130
|
+
const summaryFile = (id) => path.join(dir, `${id}.summary.md`);
|
|
91
131
|
// The Claude resume file for a session: Main → legacy un-suffixed; others suffixed.
|
|
92
132
|
const resumeFile = (id) =>
|
|
93
133
|
id === MAIN_SESSION_ID
|
|
@@ -136,8 +176,8 @@ export function createChatSessions({ dataDir } = {}) {
|
|
|
136
176
|
writeIndex(sessions);
|
|
137
177
|
}
|
|
138
178
|
|
|
139
|
-
/** Create a new session. Returns the record. */
|
|
140
|
-
function create({ title, account } = {}) {
|
|
179
|
+
/** Create a new session. Returns the record. `provider` is locked at creation. */
|
|
180
|
+
function create({ title, account, provider } = {}) {
|
|
141
181
|
const sessions = readIndex();
|
|
142
182
|
// Soft cap: evict the oldest non-Main session that has no stored messages.
|
|
143
183
|
if (sessions.length >= MAX_SESSIONS) {
|
|
@@ -150,6 +190,8 @@ export function createChatSessions({ dataDir } = {}) {
|
|
|
150
190
|
const rec = sanitizeRec({
|
|
151
191
|
id: nanoid(10),
|
|
152
192
|
title: title || null,
|
|
193
|
+
titleSource: title ? 'manual' : 'auto',
|
|
194
|
+
provider: provider || 'claude',
|
|
153
195
|
account: account || 'local',
|
|
154
196
|
createdAt: stamp,
|
|
155
197
|
lastActivityAt: stamp,
|
|
@@ -159,6 +201,13 @@ export function createChatSessions({ dataDir } = {}) {
|
|
|
159
201
|
return rec;
|
|
160
202
|
}
|
|
161
203
|
|
|
204
|
+
/** One session record (or null). */
|
|
205
|
+
function get(id) {
|
|
206
|
+
const safe = safeId(id);
|
|
207
|
+
if (!safe) return null;
|
|
208
|
+
return readIndex().find((s) => s.id === safe) || null;
|
|
209
|
+
}
|
|
210
|
+
|
|
162
211
|
function getMessages(id) {
|
|
163
212
|
const safe = safeId(id);
|
|
164
213
|
if (!safe) return [];
|
|
@@ -185,17 +234,30 @@ export function createChatSessions({ dataDir } = {}) {
|
|
|
185
234
|
} catch {
|
|
186
235
|
return { ok: false, count: 0, error: 'write_failed' };
|
|
187
236
|
}
|
|
188
|
-
//
|
|
237
|
+
// Refresh the living summary (item 9) — the carry-the-gist handoff .md.
|
|
238
|
+
const summary = buildSummary(capped);
|
|
239
|
+
if (summary) {
|
|
240
|
+
try {
|
|
241
|
+
const tmp = `${summaryFile(safe)}.${process.pid}.tmp`;
|
|
242
|
+
fs.writeFileSync(tmp, summary);
|
|
243
|
+
fs.renameSync(tmp, summaryFile(safe));
|
|
244
|
+
} catch { /* best-effort */ }
|
|
245
|
+
}
|
|
246
|
+
// Upsert + touch the registry record; auto-derive the title (unless manually set).
|
|
189
247
|
const sessions = readIndex();
|
|
190
248
|
const stamp = nextStamp(sessions);
|
|
191
249
|
const i = findIndex(sessions, safe);
|
|
250
|
+
const autoTitle = deriveTitle(capped);
|
|
192
251
|
if (i >= 0) {
|
|
193
|
-
|
|
252
|
+
const prev = sessions[i];
|
|
253
|
+
const title = prev.titleSource === 'manual' ? prev.title : (autoTitle || prev.title);
|
|
254
|
+
sessions[i] = { ...prev, title, lastActivityAt: stamp };
|
|
194
255
|
} else {
|
|
195
256
|
sessions.push(
|
|
196
257
|
sanitizeRec({
|
|
197
258
|
id: safe,
|
|
198
|
-
title: safe === MAIN_SESSION_ID ? 'Main' :
|
|
259
|
+
title: safe === MAIN_SESSION_ID ? 'Main' : autoTitle,
|
|
260
|
+
titleSource: safe === MAIN_SESSION_ID ? 'manual' : 'auto',
|
|
199
261
|
account: account || 'local',
|
|
200
262
|
createdAt: stamp,
|
|
201
263
|
lastActivityAt: stamp,
|
|
@@ -206,15 +268,42 @@ export function createChatSessions({ dataDir } = {}) {
|
|
|
206
268
|
return { ok: true, count: capped.length };
|
|
207
269
|
}
|
|
208
270
|
|
|
271
|
+
/** Read a session's living summary .md (or null). The fresh-chat handoff payload. */
|
|
272
|
+
function getSummary(id) {
|
|
273
|
+
const safe = safeId(id);
|
|
274
|
+
if (!safe) return null;
|
|
275
|
+
try { return fs.readFileSync(summaryFile(safe), 'utf8'); } catch { return null; }
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** Persist a session's context gauge after a turn (item 3). */
|
|
279
|
+
function setContext(id, { pct, used, window } = {}) {
|
|
280
|
+
const safe = safeId(id);
|
|
281
|
+
if (!safe) return;
|
|
282
|
+
const sessions = readIndex();
|
|
283
|
+
const i = findIndex(sessions, safe);
|
|
284
|
+
if (i < 0) return;
|
|
285
|
+
sessions[i] = {
|
|
286
|
+
...sessions[i],
|
|
287
|
+
contextPct: Number.isFinite(Number(pct)) ? Math.max(0, Math.min(100, Number(pct))) : sessions[i].contextPct,
|
|
288
|
+
contextUsed: Number.isFinite(Number(used)) ? Number(used) : sessions[i].contextUsed,
|
|
289
|
+
contextWindow: Number.isFinite(Number(window)) ? Number(window) : sessions[i].contextWindow,
|
|
290
|
+
};
|
|
291
|
+
writeIndex(sessions);
|
|
292
|
+
}
|
|
293
|
+
|
|
209
294
|
function rename(id, title) {
|
|
210
295
|
const safe = safeId(id);
|
|
211
296
|
if (!safe) return null;
|
|
212
297
|
const sessions = readIndex();
|
|
213
298
|
const i = findIndex(sessions, safe);
|
|
214
299
|
if (i < 0) return null;
|
|
300
|
+
const clean = typeof title === 'string' && title.trim() ? title.trim().slice(0, TITLE_CAP) : null;
|
|
215
301
|
sessions[i] = {
|
|
216
302
|
...sessions[i],
|
|
217
|
-
title:
|
|
303
|
+
title: clean,
|
|
304
|
+
// A manual rename is sticky — auto-naming won't overwrite it. Clearing the title
|
|
305
|
+
// (empty) hands naming back to the auto-deriver.
|
|
306
|
+
titleSource: clean ? 'manual' : 'auto',
|
|
218
307
|
};
|
|
219
308
|
writeIndex(sessions);
|
|
220
309
|
return sessions[i];
|
|
@@ -229,7 +318,7 @@ export function createChatSessions({ dataDir } = {}) {
|
|
|
229
318
|
if (i < 0) return false;
|
|
230
319
|
sessions.splice(i, 1);
|
|
231
320
|
writeIndex(sessions);
|
|
232
|
-
for (const f of [msgFile(safe), resumeFile(safe)]) {
|
|
321
|
+
for (const f of [msgFile(safe), summaryFile(safe), resumeFile(safe)]) {
|
|
233
322
|
try { fs.unlinkSync(f); } catch { /* already gone / read-only */ }
|
|
234
323
|
}
|
|
235
324
|
return true;
|
|
@@ -258,5 +347,5 @@ export function createChatSessions({ dataDir } = {}) {
|
|
|
258
347
|
writeIndex(sessions);
|
|
259
348
|
}
|
|
260
349
|
|
|
261
|
-
return { dir, list, ensureMain, create, getMessages, putMessages, rename, remove, touch };
|
|
350
|
+
return { dir, list, get, ensureMain, create, getMessages, putMessages, getSummary, setContext, rename, remove, touch };
|
|
262
351
|
}
|
package/server/src/config.mjs
CHANGED
|
@@ -225,7 +225,7 @@ export const DEFAULT_AGENTS = Object.freeze([
|
|
|
225
225
|
id: 'glm',
|
|
226
226
|
binary: 'glm',
|
|
227
227
|
label: 'GLM (Z.AI)',
|
|
228
|
-
description: 'GLM-
|
|
228
|
+
description: 'GLM-5.1 via Z.AI',
|
|
229
229
|
args: ['-p', '--permission-mode', 'bypassPermissions'],
|
|
230
230
|
streamFormat: 'text',
|
|
231
231
|
},
|