flyspace-cli 0.1.14 → 0.1.16
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/dist/{chunk-DA7SYYKU.js → chunk-2OGLCT7I.js} +555 -19
- package/dist/cli.js +907 -648
- package/dist/index.js +1 -1
- package/package.json +7 -4
|
@@ -163,9 +163,9 @@ async function refreshAutomationRegistry() {
|
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
165
|
const next = data ?? [];
|
|
166
|
-
const
|
|
167
|
-
if (
|
|
168
|
-
lastSnapshot =
|
|
166
|
+
const snapshot2 = JSON.stringify(next, (key, value) => key === "updated_at" ? void 0 : value);
|
|
167
|
+
if (snapshot2 === lastSnapshot) return;
|
|
168
|
+
lastSnapshot = snapshot2;
|
|
169
169
|
rows = next;
|
|
170
170
|
for (const cb of changeListeners) {
|
|
171
171
|
try {
|
|
@@ -263,7 +263,7 @@ function stopAutomationRegistryPolling() {
|
|
|
263
263
|
}
|
|
264
264
|
|
|
265
265
|
// src/channels.ts
|
|
266
|
-
import { existsSync as existsSync2, mkdirSync, statSync } from "fs";
|
|
266
|
+
import { existsSync as existsSync2, mkdirSync, realpathSync, statSync } from "fs";
|
|
267
267
|
import { homedir } from "os";
|
|
268
268
|
import { isAbsolute, resolve as resolve2, sep } from "path";
|
|
269
269
|
function isLogicalCwd(p) {
|
|
@@ -272,11 +272,22 @@ function isLogicalCwd(p) {
|
|
|
272
272
|
function logicalRoot() {
|
|
273
273
|
return process.env["FLYSPACE_ROOT"] || resolve2(homedir(), "Flyspace");
|
|
274
274
|
}
|
|
275
|
+
var workspaceRoots = /* @__PURE__ */ new Map();
|
|
276
|
+
async function refreshWorkspaceRoots() {
|
|
277
|
+
const { data, error } = await getSupabaseClient().from("workspaces").select("slug, root_path");
|
|
278
|
+
if (error) return;
|
|
279
|
+
workspaceRoots = new Map(
|
|
280
|
+
(data ?? []).filter((w) => w.root_path).map((w) => [w.slug, expandPath(w.root_path)])
|
|
281
|
+
);
|
|
282
|
+
}
|
|
275
283
|
function resolveLogicalCwd(p) {
|
|
276
|
-
|
|
284
|
+
const [slug, name] = p.split("/");
|
|
285
|
+
const root = workspaceRoots.get(slug);
|
|
286
|
+
return root ? resolve2(root, name) : resolve2(logicalRoot(), p);
|
|
277
287
|
}
|
|
278
288
|
function ensureLogicalCwdDir(cwd) {
|
|
279
|
-
|
|
289
|
+
const roots = [logicalRoot(), ...workspaceRoots.values()];
|
|
290
|
+
if (!roots.some((r) => cwd.startsWith(r + sep)) || existsSync2(cwd)) return;
|
|
280
291
|
mkdirSync(cwd, { recursive: true });
|
|
281
292
|
}
|
|
282
293
|
function expandPath(p) {
|
|
@@ -358,18 +369,21 @@ function indexRegistry(list) {
|
|
|
358
369
|
);
|
|
359
370
|
}
|
|
360
371
|
async function refreshChannelRegistry() {
|
|
372
|
+
const rootsBefore = JSON.stringify([...workspaceRoots]);
|
|
373
|
+
await refreshWorkspaceRoots();
|
|
374
|
+
if (JSON.stringify([...workspaceRoots]) !== rootsBefore) lastSnapshot2 = void 0;
|
|
361
375
|
const { data, error } = await getSupabaseClient().from("channels").select("*").order("position", { ascending: true });
|
|
362
376
|
if (error) {
|
|
363
377
|
console.error("[channels] refreshChannelRegistry failed:", error.message);
|
|
364
378
|
return;
|
|
365
379
|
}
|
|
366
380
|
const rows2 = data ?? [];
|
|
367
|
-
const
|
|
381
|
+
const snapshot2 = JSON.stringify(
|
|
368
382
|
rows2,
|
|
369
383
|
(key, value) => key === "updated_at" || key === "created_at" ? void 0 : value
|
|
370
384
|
);
|
|
371
|
-
if (
|
|
372
|
-
lastSnapshot2 =
|
|
385
|
+
if (snapshot2 === lastSnapshot2) return;
|
|
386
|
+
lastSnapshot2 = snapshot2;
|
|
373
387
|
indexRegistry(rows2.map(rowToChannel));
|
|
374
388
|
for (const cb of changeListeners2) {
|
|
375
389
|
try {
|
|
@@ -522,7 +536,44 @@ function toRow(input) {
|
|
|
522
536
|
if (input.archivedAt !== void 0) row["archived_at"] = input.archivedAt;
|
|
523
537
|
return row;
|
|
524
538
|
}
|
|
539
|
+
var ChannelCwdError = class extends Error {
|
|
540
|
+
constructor(message) {
|
|
541
|
+
super(message);
|
|
542
|
+
this.name = "ChannelCwdError";
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
function realpathOrSelf(p) {
|
|
546
|
+
try {
|
|
547
|
+
return realpathSync(p);
|
|
548
|
+
} catch {
|
|
549
|
+
return p;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
async function assertCwdInWorkspace(profile, cwd) {
|
|
553
|
+
if (isLogicalCwd(cwd)) {
|
|
554
|
+
if (cwd.split("/")[0] !== profile) {
|
|
555
|
+
throw new ChannelCwdError(`Folder "${cwd}" belongs to another workspace.`);
|
|
556
|
+
}
|
|
557
|
+
return cwd;
|
|
558
|
+
}
|
|
559
|
+
const { data, error } = await getSupabaseClient().from("workspaces").select("slug, root_path");
|
|
560
|
+
if (error) throw new Error(error.message);
|
|
561
|
+
const workspaces = data ?? [];
|
|
562
|
+
const ownRoot = workspaces.find((w) => w.slug === profile)?.root_path;
|
|
563
|
+
const home = ownRoot?.match(/^(\/Users\/[^/]+|\/home\/[^/]+|\/root)(?=\/|$)/)?.[1];
|
|
564
|
+
const absolute = home && (cwd === "~" || cwd.startsWith("~/")) ? resolve2(home, cwd.slice(2)) : expandPath(cwd);
|
|
565
|
+
const path = realpathOrSelf(absolute);
|
|
566
|
+
const roots = workspaces.filter((w) => w.root_path).map((w) => ({ slug: w.slug, root: realpathOrSelf(expandPath(w.root_path)) }));
|
|
567
|
+
const owner = roots.filter(({ root }) => path === root || path.startsWith(root + sep)).sort((a, b) => b.root.length - a.root.length)[0];
|
|
568
|
+
if (owner ? owner.slug !== profile : roots.some((r) => r.slug === profile)) {
|
|
569
|
+
throw new ChannelCwdError(
|
|
570
|
+
owner ? `Folder "${cwd}" belongs to the "${owner.slug}" workspace.` : `Folder "${cwd}" is outside this workspace's folder.`
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
return absolute;
|
|
574
|
+
}
|
|
525
575
|
async function createChannel(input) {
|
|
576
|
+
input = { ...input, cwd: await assertCwdInWorkspace(getProfile(), input.cwd) };
|
|
526
577
|
const { data, error } = await getSupabaseClient().from("channels").insert({ ...toRow(input), profile: getProfile() }).select().single();
|
|
527
578
|
if (error) throw new Error(error.message);
|
|
528
579
|
const channel = rowToChannel(data);
|
|
@@ -547,6 +598,9 @@ async function updateChannelById(id, patch) {
|
|
|
547
598
|
if (existing?.isDefault && (patch.visibility === "private" || patch.archivedAt || patch.isDefault === false || patch.name !== void 0 && patch.name.trim() !== existing.name)) {
|
|
548
599
|
throw new DefaultChannelError();
|
|
549
600
|
}
|
|
601
|
+
if (patch.cwd !== void 0) {
|
|
602
|
+
patch = { ...patch, cwd: await assertCwdInWorkspace(existing?.profile ?? getProfile(), patch.cwd) };
|
|
603
|
+
}
|
|
550
604
|
const { data, error } = await getSupabaseClient().from("channels").update({ ...toRow(patch), updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id).select().single();
|
|
551
605
|
if (error) throw new Error(error.message);
|
|
552
606
|
await refreshChannelRegistry();
|
|
@@ -1543,9 +1597,9 @@ function isAuthError(text) {
|
|
|
1543
1597
|
}
|
|
1544
1598
|
function authErrorMessage(raw) {
|
|
1545
1599
|
return `The agent can't sign in to Claude right now (the Host's login expired and could not be refreshed).
|
|
1546
|
-
|
|
1600
|
+
To fix it, a workspace admin can open Settings \u2192 Agent \u2192 Providers and click Sign in next to Claude Code.
|
|
1547
1601
|
|
|
1548
|
-
Original error: ${raw}`;
|
|
1602
|
+
> Original error: ${raw}`;
|
|
1549
1603
|
}
|
|
1550
1604
|
function isTransientError(text, apiErrorStatus) {
|
|
1551
1605
|
if (apiErrorStatus == null) {
|
|
@@ -2086,6 +2140,400 @@ Fix the channel's working directory in its settings (or recreate the directory),
|
|
|
2086
2140
|
}
|
|
2087
2141
|
}
|
|
2088
2142
|
|
|
2143
|
+
// src/feature-flags.ts
|
|
2144
|
+
var featureFlags = {
|
|
2145
|
+
"ai-cost": {
|
|
2146
|
+
title: "AI cost",
|
|
2147
|
+
description: "Shows admins what the agent has spent: a Cost section in a channel's AI overview and a per-thread cost in the thread menu. Estimated at API list rates, so on a Claude subscription it will not match your bill.",
|
|
2148
|
+
stage: "beta"
|
|
2149
|
+
},
|
|
2150
|
+
"read-aloud": {
|
|
2151
|
+
title: "Read aloud",
|
|
2152
|
+
description: "Listen to messages, threads and docs as audio. Still rough in places, and each playback uses text-to-speech.",
|
|
2153
|
+
stage: "beta"
|
|
2154
|
+
},
|
|
2155
|
+
excalidraw: {
|
|
2156
|
+
title: "Excalidraw",
|
|
2157
|
+
description: "Create and edit whiteboard drawings (.excalidraw files) in the Docs pane. Existing drawings stay viewable with this off.",
|
|
2158
|
+
stage: "beta"
|
|
2159
|
+
},
|
|
2160
|
+
sandbox: {
|
|
2161
|
+
title: "Sandbox runtime",
|
|
2162
|
+
description: "Run a channel agent in an isolated sandbox without Docker. Channels already on Sandbox keep working with this off.",
|
|
2163
|
+
stage: "beta"
|
|
2164
|
+
},
|
|
2165
|
+
dictation: {
|
|
2166
|
+
title: "Dictation",
|
|
2167
|
+
description: "A mic button in the message composer that turns speech into text as you talk. Each phrase uses speech-to-text.",
|
|
2168
|
+
stage: "beta"
|
|
2169
|
+
},
|
|
2170
|
+
"agent-memory": {
|
|
2171
|
+
title: "Agent memory",
|
|
2172
|
+
description: "Channel memory the agent reads every turn, saved by the agent and picked up from idle threads, with a Memory tab and an undo chip. While on, Claude's built-in auto memory is switched off so the two can be compared; turning this off keeps every memory.",
|
|
2173
|
+
stage: "beta"
|
|
2174
|
+
}
|
|
2175
|
+
};
|
|
2176
|
+
var featureFlagIds = Object.keys(featureFlags);
|
|
2177
|
+
function isFeatureFlagId(id) {
|
|
2178
|
+
return Object.hasOwn(featureFlags, id);
|
|
2179
|
+
}
|
|
2180
|
+
function isFeatureVisible(stage, devHost) {
|
|
2181
|
+
return stage === "beta" || stage === "dev" && devHost;
|
|
2182
|
+
}
|
|
2183
|
+
function resolveFeatureFlags(enabledIds, devHost) {
|
|
2184
|
+
const enabled = new Set(enabledIds);
|
|
2185
|
+
const out = {};
|
|
2186
|
+
for (const id of featureFlagIds) {
|
|
2187
|
+
const stage = featureFlags[id].stage;
|
|
2188
|
+
out[id] = stage === "ga" || isFeatureVisible(stage, devHost) && enabled.has(id);
|
|
2189
|
+
}
|
|
2190
|
+
return out;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
// src/store/feature-flag-store.ts
|
|
2194
|
+
async function listEnabledFeatureFlags(workspaceId) {
|
|
2195
|
+
const { data, error } = await getSupabaseClient().from("feature_flags").select("flag_id").eq("workspace_id", workspaceId);
|
|
2196
|
+
if (error) throw new Error(`listEnabledFeatureFlags(${workspaceId}): ${error.message}`);
|
|
2197
|
+
return (data ?? []).map((r) => r.flag_id);
|
|
2198
|
+
}
|
|
2199
|
+
async function listEnabledFeatureFlagsBySlug(slug) {
|
|
2200
|
+
const { data, error } = await getSupabaseClient().from("workspaces").select("id").eq("slug", slug).maybeSingle();
|
|
2201
|
+
if (error) throw new Error(`listEnabledFeatureFlagsBySlug(${slug}): ${error.message}`);
|
|
2202
|
+
return data ? listEnabledFeatureFlags(data.id) : [];
|
|
2203
|
+
}
|
|
2204
|
+
async function enableFeatureFlag(workspaceId, flagId, userId) {
|
|
2205
|
+
const { error } = await getSupabaseClient().from("feature_flags").upsert(
|
|
2206
|
+
{ workspace_id: workspaceId, flag_id: flagId, enabled_by: userId },
|
|
2207
|
+
{ onConflict: "workspace_id,flag_id" }
|
|
2208
|
+
);
|
|
2209
|
+
if (error) throw new Error(`enableFeatureFlag(${workspaceId}, ${flagId}): ${error.message}`);
|
|
2210
|
+
}
|
|
2211
|
+
async function disableFeatureFlag(workspaceId, flagId) {
|
|
2212
|
+
const { error } = await getSupabaseClient().from("feature_flags").delete().eq("workspace_id", workspaceId).eq("flag_id", flagId);
|
|
2213
|
+
if (error) throw new Error(`disableFeatureFlag(${workspaceId}, ${flagId}): ${error.message}`);
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
// src/store/memory-store.ts
|
|
2217
|
+
var MEMORY_TYPES = ["preference", "decision", "fact", "reference"];
|
|
2218
|
+
var channelTarget = (channelId) => ({ scope: "channel", channelId });
|
|
2219
|
+
var NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
2220
|
+
var DESCRIPTION_MAX = 200;
|
|
2221
|
+
var BODY_MAX = 16e3;
|
|
2222
|
+
var MEMORY_INDEX_MAX_CHARS = 8e3;
|
|
2223
|
+
function isMemoryType(value) {
|
|
2224
|
+
return MEMORY_TYPES.includes(value);
|
|
2225
|
+
}
|
|
2226
|
+
function validateMemoryInput(input) {
|
|
2227
|
+
if (!NAME_RE.test(input.name))
|
|
2228
|
+
throw new Error("name must be a kebab-case slug, e.g. deploy-flow");
|
|
2229
|
+
if (!isMemoryType(input.type)) throw new Error(`type must be one of ${MEMORY_TYPES.join(", ")}`);
|
|
2230
|
+
const description = input.description.trim();
|
|
2231
|
+
if (!description || description.includes("\n") || description.length > DESCRIPTION_MAX) {
|
|
2232
|
+
throw new Error(`description must be one line of at most ${DESCRIPTION_MAX} characters`);
|
|
2233
|
+
}
|
|
2234
|
+
const body = input.body.trim();
|
|
2235
|
+
if (!body || body.length > BODY_MAX) {
|
|
2236
|
+
throw new Error(`body must be 1 to ${BODY_MAX} characters`);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
function rowToMemory(row) {
|
|
2240
|
+
return {
|
|
2241
|
+
id: row["id"],
|
|
2242
|
+
scope: row["scope"] ?? "channel",
|
|
2243
|
+
workspaceId: row["workspace_id"],
|
|
2244
|
+
channelId: row["channel_id"] ?? null,
|
|
2245
|
+
userId: row["user_id"] ?? null,
|
|
2246
|
+
name: row["name"],
|
|
2247
|
+
description: row["description"],
|
|
2248
|
+
type: row["type"],
|
|
2249
|
+
body: row["body"],
|
|
2250
|
+
sourceThreadId: row["source_thread_id"] ?? null,
|
|
2251
|
+
createdBy: row["created_by"],
|
|
2252
|
+
createdAt: row["created_at"],
|
|
2253
|
+
updatedAt: row["updated_at"],
|
|
2254
|
+
lastUsedAt: row["last_used_at"] ?? null
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
function targetRow(target) {
|
|
2258
|
+
switch (target.scope) {
|
|
2259
|
+
case "channel":
|
|
2260
|
+
return { scope: "channel", channel_id: target.channelId };
|
|
2261
|
+
case "workspace":
|
|
2262
|
+
return { scope: "workspace", workspace_id: target.workspaceId };
|
|
2263
|
+
case "user":
|
|
2264
|
+
return { scope: "user", workspace_id: target.workspaceId, user_id: target.userId };
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
function inTarget(query, target) {
|
|
2268
|
+
let q = query;
|
|
2269
|
+
for (const [column, value] of Object.entries(targetRow(target))) q = q.eq(column, value);
|
|
2270
|
+
return q;
|
|
2271
|
+
}
|
|
2272
|
+
var describeTarget = (t) => t.scope === "channel" ? t.channelId : t.scope === "workspace" ? `workspace ${t.workspaceId}` : `user ${t.userId}`;
|
|
2273
|
+
async function listMemories(target) {
|
|
2274
|
+
const { data, error } = await inTarget(getSupabaseClient().from("memories").select("*"), target).order("updated_at", { ascending: false });
|
|
2275
|
+
if (error) throw new Error(`listMemories(${describeTarget(target)}): ${error.message}`);
|
|
2276
|
+
return (data ?? []).map(rowToMemory);
|
|
2277
|
+
}
|
|
2278
|
+
async function getMemory(target, name) {
|
|
2279
|
+
const { data, error } = await inTarget(getSupabaseClient().from("memories").select("*"), target).eq("name", name).maybeSingle();
|
|
2280
|
+
if (error) throw new Error(`getMemory(${describeTarget(target)}, ${name}): ${error.message}`);
|
|
2281
|
+
return data ? rowToMemory(data) : null;
|
|
2282
|
+
}
|
|
2283
|
+
async function saveMemory(target, input, actor) {
|
|
2284
|
+
validateMemoryInput(input);
|
|
2285
|
+
const supabase = getSupabaseClient();
|
|
2286
|
+
const existing = await getMemory(target, input.name);
|
|
2287
|
+
const fields = {
|
|
2288
|
+
description: input.description.trim(),
|
|
2289
|
+
type: input.type,
|
|
2290
|
+
body: input.body.trim(),
|
|
2291
|
+
source_thread_id: input.sourceThreadId ?? existing?.sourceThreadId ?? null,
|
|
2292
|
+
created_by: input.createdBy
|
|
2293
|
+
};
|
|
2294
|
+
if (existing && sameContent(existing, fields)) {
|
|
2295
|
+
return { memory: existing, created: false, changed: false };
|
|
2296
|
+
}
|
|
2297
|
+
const { data, error } = existing ? await supabase.from("memories").update({ ...fields, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", existing.id).select("*").single() : await supabase.from("memories").insert({ ...fields, ...targetRow(target), name: input.name }).select("*").single();
|
|
2298
|
+
if (error)
|
|
2299
|
+
throw new Error(`saveMemory(${describeTarget(target)}, ${input.name}): ${error.message}`);
|
|
2300
|
+
const memory = rowToMemory(data);
|
|
2301
|
+
await logMemoryEvent({
|
|
2302
|
+
memory,
|
|
2303
|
+
action: existing ? "update" : "create",
|
|
2304
|
+
before: existing ? snapshot(existing) : null,
|
|
2305
|
+
after: snapshot(memory),
|
|
2306
|
+
actor,
|
|
2307
|
+
threadId: input.sourceThreadId ?? null
|
|
2308
|
+
});
|
|
2309
|
+
return { memory, created: !existing, changed: true };
|
|
2310
|
+
}
|
|
2311
|
+
async function updateMemory(target, memoryId, patch, actor) {
|
|
2312
|
+
const existing = (await listMemories(target)).find((m) => m.id === memoryId);
|
|
2313
|
+
if (!existing) return null;
|
|
2314
|
+
const { memory } = await saveMemory(
|
|
2315
|
+
target,
|
|
2316
|
+
{
|
|
2317
|
+
name: existing.name,
|
|
2318
|
+
description: patch.description ?? existing.description,
|
|
2319
|
+
type: patch.type ?? existing.type,
|
|
2320
|
+
body: patch.body ?? existing.body,
|
|
2321
|
+
createdBy: existing.createdBy
|
|
2322
|
+
},
|
|
2323
|
+
actor
|
|
2324
|
+
);
|
|
2325
|
+
return memory;
|
|
2326
|
+
}
|
|
2327
|
+
async function searchMemories(target, query) {
|
|
2328
|
+
const words = query.replaceAll(/[,()*%\\]/g, " ").split(/\s+/).filter(Boolean);
|
|
2329
|
+
if (!words.length) return [];
|
|
2330
|
+
let request = inTarget(getSupabaseClient().from("memories").select("*"), target);
|
|
2331
|
+
for (const word of words) {
|
|
2332
|
+
request = request.or(`name.ilike.*${word}*,description.ilike.*${word}*,body.ilike.*${word}*`);
|
|
2333
|
+
}
|
|
2334
|
+
const { data, error } = await request.order("updated_at", { ascending: false }).limit(20);
|
|
2335
|
+
if (error) throw new Error(`searchMemories(${describeTarget(target)}): ${error.message}`);
|
|
2336
|
+
return (data ?? []).map(rowToMemory);
|
|
2337
|
+
}
|
|
2338
|
+
var USED_GRANULARITY_MS = 24 * 60 * 6e4;
|
|
2339
|
+
async function markMemoryUsed(memory) {
|
|
2340
|
+
const cutoff = new Date(Date.now() - USED_GRANULARITY_MS).toISOString();
|
|
2341
|
+
if (memory.lastUsedAt && memory.lastUsedAt > cutoff) return;
|
|
2342
|
+
const { error } = await getSupabaseClient().from("memories").update({ last_used_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", memory.id).or(`last_used_at.is.null,last_used_at.lt.${cutoff}`);
|
|
2343
|
+
if (error) console.error(`[memory] markMemoryUsed(${memory.id}) failed:`, error.message);
|
|
2344
|
+
}
|
|
2345
|
+
async function deleteMemory(target, idOrName, actor, threadId) {
|
|
2346
|
+
const { data, error } = await inTarget(getSupabaseClient().from("memories").delete(), target).eq(isUuid(idOrName) ? "id" : "name", idOrName).select("*");
|
|
2347
|
+
if (error)
|
|
2348
|
+
throw new Error(`deleteMemory(${describeTarget(target)}, ${idOrName}): ${error.message}`);
|
|
2349
|
+
const deleted = (data ?? []).map(rowToMemory);
|
|
2350
|
+
for (const memory of deleted) {
|
|
2351
|
+
await logMemoryEvent({
|
|
2352
|
+
memory,
|
|
2353
|
+
action: "delete",
|
|
2354
|
+
before: snapshot(memory),
|
|
2355
|
+
after: null,
|
|
2356
|
+
actor,
|
|
2357
|
+
threadId: threadId ?? null
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
2360
|
+
return deleted.length > 0;
|
|
2361
|
+
}
|
|
2362
|
+
async function promoteChannelMemory(channelId, memoryId) {
|
|
2363
|
+
const supabase = getSupabaseClient();
|
|
2364
|
+
const existing = (await listChannelMemories(channelId)).find((m) => m.id === memoryId);
|
|
2365
|
+
if (!existing) return { ok: false, reason: "not-found" };
|
|
2366
|
+
if (await getMemory({ scope: "workspace", workspaceId: existing.workspaceId }, existing.name)) {
|
|
2367
|
+
return { ok: false, reason: "name-taken" };
|
|
2368
|
+
}
|
|
2369
|
+
const { data, error } = await supabase.from("memories").update({ scope: "workspace", channel_id: null, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", memoryId).eq("scope", "channel").select("*").maybeSingle();
|
|
2370
|
+
if (error) throw new Error(`promoteChannelMemory(${memoryId}): ${error.message}`);
|
|
2371
|
+
if (!data) return { ok: false, reason: "not-found" };
|
|
2372
|
+
const memory = rowToMemory(data);
|
|
2373
|
+
await logMemoryEvent({ memory, action: "create", before: null, after: snapshot(memory), actor: "user", threadId: null });
|
|
2374
|
+
return { ok: true, memory };
|
|
2375
|
+
}
|
|
2376
|
+
var listChannelMemories = (channelId) => listMemories(channelTarget(channelId));
|
|
2377
|
+
var getChannelMemory = (channelId, name) => getMemory(channelTarget(channelId), name);
|
|
2378
|
+
var saveChannelMemory = ({ channelId, ...fields }, actor) => saveMemory(channelTarget(channelId), fields, actor);
|
|
2379
|
+
var updateChannelMemory = (channelId, memoryId, patch, actor) => updateMemory(channelTarget(channelId), memoryId, patch, actor);
|
|
2380
|
+
var deleteChannelMemory = (channelId, idOrName, actor, threadId) => deleteMemory(channelTarget(channelId), idOrName, actor, threadId);
|
|
2381
|
+
function unchangedSince(current, after) {
|
|
2382
|
+
return current && after ? sameContent(current, after) : !current && !after;
|
|
2383
|
+
}
|
|
2384
|
+
function snapshot(m) {
|
|
2385
|
+
return {
|
|
2386
|
+
description: m.description,
|
|
2387
|
+
type: m.type,
|
|
2388
|
+
body: m.body,
|
|
2389
|
+
sourceThreadId: m.sourceThreadId,
|
|
2390
|
+
createdBy: m.createdBy
|
|
2391
|
+
};
|
|
2392
|
+
}
|
|
2393
|
+
function sameContent(m, other) {
|
|
2394
|
+
return m.description === other.description && m.type === other.type && m.body === other.body;
|
|
2395
|
+
}
|
|
2396
|
+
async function logMemoryEvent(e) {
|
|
2397
|
+
const { error } = await getSupabaseClient().from("memory_events").insert({
|
|
2398
|
+
channel_id: e.memory.channelId,
|
|
2399
|
+
// A channel event's workspace comes from its channel (0100 trigger).
|
|
2400
|
+
...e.memory.channelId ? {} : { workspace_id: e.memory.workspaceId },
|
|
2401
|
+
scope: e.memory.scope,
|
|
2402
|
+
user_id: e.memory.userId,
|
|
2403
|
+
memory_id: e.action === "delete" ? null : e.memory.id,
|
|
2404
|
+
thread_id: e.threadId,
|
|
2405
|
+
name: e.memory.name,
|
|
2406
|
+
action: e.action,
|
|
2407
|
+
before: e.before,
|
|
2408
|
+
after: e.after,
|
|
2409
|
+
actor: e.actor
|
|
2410
|
+
});
|
|
2411
|
+
if (error) console.error(`[memory] event log failed for ${e.memory.name}:`, error.message);
|
|
2412
|
+
}
|
|
2413
|
+
function rowToEvent(row) {
|
|
2414
|
+
return {
|
|
2415
|
+
id: row["id"],
|
|
2416
|
+
channelId: row["channel_id"],
|
|
2417
|
+
memoryId: row["memory_id"] ?? null,
|
|
2418
|
+
threadId: row["thread_id"] ?? null,
|
|
2419
|
+
name: row["name"],
|
|
2420
|
+
action: row["action"],
|
|
2421
|
+
before: row["before"] ?? null,
|
|
2422
|
+
after: row["after"] ?? null,
|
|
2423
|
+
actor: row["actor"],
|
|
2424
|
+
createdAt: row["created_at"],
|
|
2425
|
+
undoneAt: row["undone_at"] ?? null
|
|
2426
|
+
};
|
|
2427
|
+
}
|
|
2428
|
+
async function listMemoryEvents(channelId, filter = {}) {
|
|
2429
|
+
const supabase = getSupabaseClient();
|
|
2430
|
+
let query = supabase.from("memory_events").select("*").eq("channel_id", channelId);
|
|
2431
|
+
if (filter.threadId) query = query.eq("thread_id", filter.threadId);
|
|
2432
|
+
if (filter.actor) query = query.eq("actor", filter.actor);
|
|
2433
|
+
const { data, error } = await query.order("created_at", { ascending: false }).limit(filter.limit ?? 50);
|
|
2434
|
+
if (error) throw new Error(`listMemoryEvents(${channelId}): ${error.message}`);
|
|
2435
|
+
const events = (data ?? []).map(rowToEvent);
|
|
2436
|
+
if (!events.length) return [];
|
|
2437
|
+
const names = [...new Set(events.map((e) => e.name))];
|
|
2438
|
+
const live = await supabase.from("memories").select("*").eq("scope", "channel").eq("channel_id", channelId).in("name", names);
|
|
2439
|
+
if (live.error) throw new Error(`listMemoryEvents(${channelId}): ${live.error.message}`);
|
|
2440
|
+
const byName = new Map(
|
|
2441
|
+
(live.data ?? []).map((r) => [
|
|
2442
|
+
r["name"],
|
|
2443
|
+
rowToMemory(r)
|
|
2444
|
+
])
|
|
2445
|
+
);
|
|
2446
|
+
return events.map((e) => {
|
|
2447
|
+
const current = byName.get(e.name) ?? null;
|
|
2448
|
+
const unchanged = unchangedSince(current, e.after);
|
|
2449
|
+
return {
|
|
2450
|
+
...e,
|
|
2451
|
+
currentState: unchanged ? "current" : current ? "changed" : "deleted",
|
|
2452
|
+
undoable: !e.undoneAt && unchanged
|
|
2453
|
+
};
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
var listThreadMemoryEvents = (channelId, threadId) => listMemoryEvents(channelId, { threadId, actor: "agent" });
|
|
2457
|
+
async function undoMemoryEvent(channelId, eventId) {
|
|
2458
|
+
const supabase = getSupabaseClient();
|
|
2459
|
+
const { data, error } = await supabase.from("memory_events").select("*").eq("id", eventId).eq("channel_id", channelId).maybeSingle();
|
|
2460
|
+
if (error) throw new Error(`undoMemoryEvent(${eventId}): ${error.message}`);
|
|
2461
|
+
if (!data) return "not-found";
|
|
2462
|
+
const event = rowToEvent(data);
|
|
2463
|
+
if (event.undoneAt) return "already-undone";
|
|
2464
|
+
const current = await getChannelMemory(channelId, event.name);
|
|
2465
|
+
if (!unchangedSince(current, event.after)) return "conflict";
|
|
2466
|
+
const claimed = await supabase.from("memory_events").update({ undone_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", eventId).is("undone_at", null).select("id");
|
|
2467
|
+
if (claimed.error) throw new Error(`undoMemoryEvent(${eventId}): ${claimed.error.message}`);
|
|
2468
|
+
if (!(claimed.data ?? []).length) return "already-undone";
|
|
2469
|
+
const before = event.before;
|
|
2470
|
+
const restore = before ? {
|
|
2471
|
+
description: before.description,
|
|
2472
|
+
type: before.type,
|
|
2473
|
+
body: before.body,
|
|
2474
|
+
source_thread_id: before.sourceThreadId,
|
|
2475
|
+
created_by: before.createdBy
|
|
2476
|
+
} : null;
|
|
2477
|
+
const { error: writeError } = !restore ? await supabase.from("memories").delete().eq("id", current.id) : current ? await supabase.from("memories").update({ ...restore, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", current.id) : await supabase.from("memories").insert({ ...restore, scope: "channel", channel_id: channelId, name: event.name });
|
|
2478
|
+
if (writeError) {
|
|
2479
|
+
await supabase.from("memory_events").update({ undone_at: null }).eq("id", eventId);
|
|
2480
|
+
throw new Error(`undoMemoryEvent(${eventId}): ${writeError.message}`);
|
|
2481
|
+
}
|
|
2482
|
+
return "undone";
|
|
2483
|
+
}
|
|
2484
|
+
function isUuid(value) {
|
|
2485
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
2486
|
+
}
|
|
2487
|
+
function formatMemoryPrompt(channelId, memories, extras = {}) {
|
|
2488
|
+
let used = 0;
|
|
2489
|
+
const indexOf = (list, searchHint) => {
|
|
2490
|
+
const lines = [];
|
|
2491
|
+
let omitted = 0;
|
|
2492
|
+
for (const m of list) {
|
|
2493
|
+
const line = `- \`${m.name}\` (${m.type}): ${m.description}`;
|
|
2494
|
+
if (used + line.length > MEMORY_INDEX_MAX_CHARS) {
|
|
2495
|
+
omitted++;
|
|
2496
|
+
continue;
|
|
2497
|
+
}
|
|
2498
|
+
lines.push(line);
|
|
2499
|
+
used += line.length + 1;
|
|
2500
|
+
}
|
|
2501
|
+
if (!lines.length && !omitted) return "(none yet)";
|
|
2502
|
+
return lines.join("\n") + (omitted ? `
|
|
2503
|
+
- (${omitted} older memories not listed; find them with \`${searchHint}\`)` : "");
|
|
2504
|
+
};
|
|
2505
|
+
const user = extras.user;
|
|
2506
|
+
const userFlags2 = user ? `--scope user --user ${user.userId}` : "";
|
|
2507
|
+
const userIndex = user ? indexOf(user.memories, `memory search --channel ${channelId} ${userFlags2}`) : "";
|
|
2508
|
+
const channelIndex = indexOf(memories, "memory search");
|
|
2509
|
+
const workspaceIndex = extras.workspace?.length ? indexOf(extras.workspace, `memory search --channel ${channelId} --scope workspace`) : "";
|
|
2510
|
+
return [
|
|
2511
|
+
`## Channel memory`,
|
|
2512
|
+
``,
|
|
2513
|
+
`This channel has a persistent memory shared by every thread in it and by every agent runner. Use it instead of any built-in memory directory of your own CLI.`,
|
|
2514
|
+
``,
|
|
2515
|
+
`- Read a memory: \`flyspace memory get --channel ${channelId} <name>\``,
|
|
2516
|
+
`- Search: \`flyspace memory search --channel ${channelId} <words>\``,
|
|
2517
|
+
`- Save or overwrite: \`flyspace memory save --channel ${channelId} --name <kebab-slug> --type <${MEMORY_TYPES.join("|")}> --description "<one line>" --body "<text>" [--thread <thread-id>]\``,
|
|
2518
|
+
`- Delete one that turned out wrong: \`flyspace memory delete --channel ${channelId} <name> [--thread <thread-id>]\``,
|
|
2519
|
+
``,
|
|
2520
|
+
`Save when someone asks you to remember something, corrects you, or a decision is made that later threads will need. Save what is not derivable from the code, git history or docs: decisions and why, preferences, corrections, where external things live. Do not save one-off details of this conversation. Check the index first and update an existing memory rather than adding a near-duplicate. Write memories in English. Pass \`--thread\` with the thread id from the \`[context: ...]\` header, so the thread shows what changed and it can be undone there.`,
|
|
2521
|
+
...user ? [
|
|
2522
|
+
``,
|
|
2523
|
+
`A personal preference of @${user.handle} (language, tone, reply length, time zone, how they like to work) belongs in their personal memory instead, which only their own turns see: add \`${userFlags2}\` to the same commands. Never save someone else's preferences there, and never put project knowledge there.`
|
|
2524
|
+
] : [],
|
|
2525
|
+
...workspaceIndex ? [
|
|
2526
|
+
``,
|
|
2527
|
+
`Workspace memories below apply to every channel. They are read-only to you: read one with \`--scope workspace\` on \`get\`; a Workspace Admin manages them.`
|
|
2528
|
+
] : [],
|
|
2529
|
+
``,
|
|
2530
|
+
...user ? [`Personal memory of @${user.handle}:`, userIndex, ``] : [],
|
|
2531
|
+
`Index:`,
|
|
2532
|
+
channelIndex,
|
|
2533
|
+
...workspaceIndex ? [``, `Workspace memory:`, workspaceIndex] : []
|
|
2534
|
+
].join("\n");
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2089
2537
|
// src/store/workspace-store.ts
|
|
2090
2538
|
function rowToMembership(row) {
|
|
2091
2539
|
return {
|
|
@@ -2097,7 +2545,8 @@ function rowToMembership(row) {
|
|
|
2097
2545
|
role: row["role"],
|
|
2098
2546
|
status: row["status"],
|
|
2099
2547
|
invitedAt: row["invited_at"],
|
|
2100
|
-
acceptedAt: row["accepted_at"] ?? null
|
|
2548
|
+
acceptedAt: row["accepted_at"] ?? null,
|
|
2549
|
+
memoryAutoPaused: row["memory_auto_paused"] === true
|
|
2101
2550
|
};
|
|
2102
2551
|
}
|
|
2103
2552
|
var WORKSPACE_CACHE_TTL_MS = 6e4;
|
|
@@ -2163,6 +2612,7 @@ async function deleteWorkspace(workspace) {
|
|
|
2163
2612
|
}
|
|
2164
2613
|
async function setWorkspaceStripeSubscription(workspaceId, fields) {
|
|
2165
2614
|
const { error } = await getSupabaseClient().from("workspaces").update({
|
|
2615
|
+
plan: fields.plan,
|
|
2166
2616
|
stripe_customer_id: fields.stripeCustomerId,
|
|
2167
2617
|
stripe_subscription_id: fields.stripeSubscriptionId,
|
|
2168
2618
|
plan_status: fields.planStatus
|
|
@@ -2198,11 +2648,12 @@ async function getWorkspaceByStripeCustomerId(stripeCustomerId) {
|
|
|
2198
2648
|
return data;
|
|
2199
2649
|
}
|
|
2200
2650
|
async function getWorkspaceBilling(workspaceId) {
|
|
2201
|
-
const { data, error } = await getSupabaseClient().from("workspaces").select("plan_status, stripe_customer_id, stripe_subscription_id").eq("id", workspaceId).maybeSingle();
|
|
2651
|
+
const { data, error } = await getSupabaseClient().from("workspaces").select("plan, plan_status, stripe_customer_id, stripe_subscription_id").eq("id", workspaceId).maybeSingle();
|
|
2202
2652
|
if (error) throw new Error(`getWorkspaceBilling(${workspaceId}): ${error.message}`);
|
|
2203
2653
|
const row = data ?? {};
|
|
2204
2654
|
return {
|
|
2205
2655
|
planStatus: row["plan_status"] ?? null,
|
|
2656
|
+
plan: row["plan"] ?? null,
|
|
2206
2657
|
stripeCustomerId: row["stripe_customer_id"] ?? null,
|
|
2207
2658
|
stripeSubscriptionId: row["stripe_subscription_id"] ?? null
|
|
2208
2659
|
};
|
|
@@ -2251,6 +2702,10 @@ async function listMembershipsByEmail(email) {
|
|
|
2251
2702
|
if (error) throw new Error(`listMembershipsByEmail: ${error.message}`);
|
|
2252
2703
|
return (data ?? []).map(rowToMembership);
|
|
2253
2704
|
}
|
|
2705
|
+
async function setMemoryAutoPaused(workspaceId, userId, paused) {
|
|
2706
|
+
const { error } = await getSupabaseClient().from("memberships").update({ memory_auto_paused: paused }).eq("workspace_id", workspaceId).eq("user_id", userId);
|
|
2707
|
+
if (error) throw new Error(`setMemoryAutoPaused(${workspaceId}): ${error.message}`);
|
|
2708
|
+
}
|
|
2254
2709
|
async function listMemberships(workspaceId) {
|
|
2255
2710
|
const { data, error } = await getSupabaseClient().from("memberships").select("*").eq("workspace_id", workspaceId).order("invited_at", { ascending: true });
|
|
2256
2711
|
if (error) throw new Error(`listMemberships(${workspaceId}): ${error.message}`);
|
|
@@ -2374,6 +2829,37 @@ async function setAgentPaused(workspaceId, paused) {
|
|
|
2374
2829
|
if (error) throw new Error(`setAgentPaused(${workspaceId}): ${error.message}`);
|
|
2375
2830
|
}
|
|
2376
2831
|
|
|
2832
|
+
// src/store/memory-context.ts
|
|
2833
|
+
async function loadAgentMemory(profile, channelId, opts = {}) {
|
|
2834
|
+
const enabled = await listEnabledFeatureFlagsBySlug(profile).then((ids) => resolveFeatureFlags(ids, false)["agent-memory"]).catch((err) => {
|
|
2835
|
+
console.error("[memory] feature flag lookup failed:", err.message);
|
|
2836
|
+
return false;
|
|
2837
|
+
});
|
|
2838
|
+
if (!enabled || opts.readable === false || !channelId || !isUuid(channelId)) return { enabled };
|
|
2839
|
+
const memoryPrompt = await buildPrompt(profile, channelId, opts).catch((err) => {
|
|
2840
|
+
console.error("[memory] memory lookup failed:", err.message);
|
|
2841
|
+
return void 0;
|
|
2842
|
+
});
|
|
2843
|
+
return memoryPrompt ? { enabled, memoryPrompt } : { enabled };
|
|
2844
|
+
}
|
|
2845
|
+
async function buildPrompt(profile, channelId, opts) {
|
|
2846
|
+
const workspace = await getWorkspaceBySlug(profile);
|
|
2847
|
+
const handle = opts.senderHandle?.toLowerCase();
|
|
2848
|
+
const sender = workspace && handle ? (await listMemberships(workspace.id)).find(
|
|
2849
|
+
(m) => m.userId && m.handle.toLowerCase() === handle
|
|
2850
|
+
) : void 0;
|
|
2851
|
+
const [channel, shared, own] = await Promise.all([
|
|
2852
|
+
listMemories({ scope: "channel", channelId }),
|
|
2853
|
+
workspace ? listMemories({ scope: "workspace", workspaceId: workspace.id }) : [],
|
|
2854
|
+
workspace && sender ? listMemories({ scope: "user", workspaceId: workspace.id, userId: sender.userId }) : []
|
|
2855
|
+
]);
|
|
2856
|
+
const extras = {
|
|
2857
|
+
workspace: shared,
|
|
2858
|
+
...sender ? { user: { userId: sender.userId, handle: sender.handle, memories: own } } : {}
|
|
2859
|
+
};
|
|
2860
|
+
return formatMemoryPrompt(channelId, channel, extras);
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2377
2863
|
// src/dashboard/settings.ts
|
|
2378
2864
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync9, mkdirSync as mkdirSync6, rmSync as rmSync2 } from "fs";
|
|
2379
2865
|
import { dirname, resolve as resolve5 } from "path";
|
|
@@ -3739,17 +4225,21 @@ async function runAutomation(opts) {
|
|
|
3739
4225
|
|
|
3740
4226
|
`;
|
|
3741
4227
|
try {
|
|
4228
|
+
const memory = await loadAgentMemory(getProfile(), channel && findChannel(channel)?.id);
|
|
3742
4229
|
const useCodex = (explicitModel ? runnerForModel(explicitModel) === "codex" : runnerForModel(loadDashboardSettings().agent) === "codex") && !requiresClaudeAutomationRunner(prompt);
|
|
3743
4230
|
const response = useCodex ? await askCodex(automationPrefix + prompt, cwd, {
|
|
3744
4231
|
...explicitModel && runnerForModel(explicitModel) === "codex" ? { model: explicitModel } : codexModel?.trim() ? { model: codexModel.trim() } : {},
|
|
3745
4232
|
// Same reason as the interactive paths: no --append-system-prompt on
|
|
3746
4233
|
// Codex, so the persona and channel instructions go in the prompt.
|
|
3747
|
-
systemPrompt:
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
4234
|
+
systemPrompt: [
|
|
4235
|
+
buildSystemPrompt(
|
|
4236
|
+
botName,
|
|
4237
|
+
void 0,
|
|
4238
|
+
resolveChannelPrompt(channel),
|
|
4239
|
+
resolveSelectedOutputStyle(claudeConfigDir)?.body
|
|
4240
|
+
),
|
|
4241
|
+
memory.memoryPrompt
|
|
4242
|
+
].filter(Boolean).join("\n\n"),
|
|
3753
4243
|
// Same attribution as the Claude branch below — no userId, the
|
|
3754
4244
|
// schedule's name is the label.
|
|
3755
4245
|
usageContext: { source: "automation", channelId: channel, label: name }
|
|
@@ -3763,6 +4253,8 @@ async function runAutomation(opts) {
|
|
|
3763
4253
|
model,
|
|
3764
4254
|
maxBudgetUsd,
|
|
3765
4255
|
channelPrompt: resolveChannelPrompt(channel),
|
|
4256
|
+
...memory.memoryPrompt ? { memoryPrompt: memory.memoryPrompt } : {},
|
|
4257
|
+
...memory.enabled ? { disableAutoMemory: true } : {},
|
|
3766
4258
|
// No userId: nobody triggered this. The schedule's name is the
|
|
3767
4259
|
// label, which is what makes "which automation is burning tokens"
|
|
3768
4260
|
// answerable without joining anything.
|
|
@@ -4085,6 +4577,11 @@ import { CronExpressionParser } from "cron-parser";
|
|
|
4085
4577
|
var Scheduler = class {
|
|
4086
4578
|
jobs = /* @__PURE__ */ new Map();
|
|
4087
4579
|
caughtUp = /* @__PURE__ */ new Set();
|
|
4580
|
+
// A run is only recorded when it finishes, so while one is in flight the
|
|
4581
|
+
// store still shows the previous tick. Without this guard, a registry
|
|
4582
|
+
// reload during a long run re-registers the schedule and catchup starts a
|
|
4583
|
+
// second, overlapping copy (sah-budget-fetch ran twice a day this way).
|
|
4584
|
+
running = /* @__PURE__ */ new Set();
|
|
4088
4585
|
store;
|
|
4089
4586
|
constructor(store) {
|
|
4090
4587
|
this.store = store;
|
|
@@ -4136,6 +4633,11 @@ var Scheduler = class {
|
|
|
4136
4633
|
wrappedRun();
|
|
4137
4634
|
}
|
|
4138
4635
|
async runAndRecord(name, runFn) {
|
|
4636
|
+
if (this.running.has(name)) {
|
|
4637
|
+
console.log(`[scheduler] ${name} is still running, skipping overlapping run`);
|
|
4638
|
+
return;
|
|
4639
|
+
}
|
|
4640
|
+
this.running.add(name);
|
|
4139
4641
|
const startedAt = /* @__PURE__ */ new Date();
|
|
4140
4642
|
const elapsed = () => Date.now() - startedAt.getTime();
|
|
4141
4643
|
try {
|
|
@@ -4144,6 +4646,8 @@ var Scheduler = class {
|
|
|
4144
4646
|
} catch (err) {
|
|
4145
4647
|
console.error(`[scheduler] Error in ${name}:`, err);
|
|
4146
4648
|
this.store?.recordRun(name, "error", startedAt, elapsed());
|
|
4649
|
+
} finally {
|
|
4650
|
+
this.running.delete(name);
|
|
4147
4651
|
}
|
|
4148
4652
|
}
|
|
4149
4653
|
unregister(name) {
|
|
@@ -8376,6 +8880,7 @@ export {
|
|
|
8376
8880
|
warnOnBrokenChannelCwds,
|
|
8377
8881
|
resolveCwd,
|
|
8378
8882
|
resolveChannelPrompt,
|
|
8883
|
+
ChannelCwdError,
|
|
8379
8884
|
createChannel,
|
|
8380
8885
|
DefaultChannelError,
|
|
8381
8886
|
updateChannelById,
|
|
@@ -8403,11 +8908,40 @@ export {
|
|
|
8403
8908
|
modelLabel,
|
|
8404
8909
|
usageLimitedUntil,
|
|
8405
8910
|
usageLimitedModels,
|
|
8911
|
+
clearClaudeAuthFailed,
|
|
8406
8912
|
claudeAuthFailed,
|
|
8407
8913
|
buildSystemPrompt,
|
|
8408
8914
|
docsAccessPromptNote,
|
|
8409
8915
|
pinnedClaudeConfigDir,
|
|
8410
8916
|
askClaude,
|
|
8917
|
+
featureFlags,
|
|
8918
|
+
featureFlagIds,
|
|
8919
|
+
isFeatureFlagId,
|
|
8920
|
+
isFeatureVisible,
|
|
8921
|
+
resolveFeatureFlags,
|
|
8922
|
+
listEnabledFeatureFlags,
|
|
8923
|
+
enableFeatureFlag,
|
|
8924
|
+
disableFeatureFlag,
|
|
8925
|
+
MEMORY_TYPES,
|
|
8926
|
+
isMemoryType,
|
|
8927
|
+
validateMemoryInput,
|
|
8928
|
+
listMemories,
|
|
8929
|
+
getMemory,
|
|
8930
|
+
saveMemory,
|
|
8931
|
+
updateMemory,
|
|
8932
|
+
searchMemories,
|
|
8933
|
+
markMemoryUsed,
|
|
8934
|
+
deleteMemory,
|
|
8935
|
+
promoteChannelMemory,
|
|
8936
|
+
listChannelMemories,
|
|
8937
|
+
getChannelMemory,
|
|
8938
|
+
saveChannelMemory,
|
|
8939
|
+
updateChannelMemory,
|
|
8940
|
+
deleteChannelMemory,
|
|
8941
|
+
listMemoryEvents,
|
|
8942
|
+
listThreadMemoryEvents,
|
|
8943
|
+
undoMemoryEvent,
|
|
8944
|
+
isUuid,
|
|
8411
8945
|
getWorkspaceBySlug,
|
|
8412
8946
|
getWorkspaceById,
|
|
8413
8947
|
listWorkspaces,
|
|
@@ -8427,6 +8961,7 @@ export {
|
|
|
8427
8961
|
getMembershipByEmail,
|
|
8428
8962
|
getMembershipByUserId,
|
|
8429
8963
|
listMembershipsByEmail,
|
|
8964
|
+
setMemoryAutoPaused,
|
|
8430
8965
|
listMemberships,
|
|
8431
8966
|
createMembership,
|
|
8432
8967
|
HANDLE_PATTERN,
|
|
@@ -8443,6 +8978,7 @@ export {
|
|
|
8443
8978
|
setAllowGuestAccess,
|
|
8444
8979
|
getAgentPaused,
|
|
8445
8980
|
setAgentPaused,
|
|
8981
|
+
loadAgentMemory,
|
|
8446
8982
|
DEFAULT_TITLE_RULES,
|
|
8447
8983
|
extractCategories,
|
|
8448
8984
|
buildAutoTitle,
|