flyspace-cli 0.1.14 → 0.1.15
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-KFT2YP5W.js} +376 -13
- package/dist/cli.js +328 -435
- package/dist/index.js +1 -1
- package/package.json +2 -1
|
@@ -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 {
|
|
@@ -364,12 +364,12 @@ async function refreshChannelRegistry() {
|
|
|
364
364
|
return;
|
|
365
365
|
}
|
|
366
366
|
const rows2 = data ?? [];
|
|
367
|
-
const
|
|
367
|
+
const snapshot2 = JSON.stringify(
|
|
368
368
|
rows2,
|
|
369
369
|
(key, value) => key === "updated_at" || key === "created_at" ? void 0 : value
|
|
370
370
|
);
|
|
371
|
-
if (
|
|
372
|
-
lastSnapshot2 =
|
|
371
|
+
if (snapshot2 === lastSnapshot2) return;
|
|
372
|
+
lastSnapshot2 = snapshot2;
|
|
373
373
|
indexRegistry(rows2.map(rowToChannel));
|
|
374
374
|
for (const cb of changeListeners2) {
|
|
375
375
|
try {
|
|
@@ -2086,6 +2086,340 @@ Fix the channel's working directory in its settings (or recreate the directory),
|
|
|
2086
2086
|
}
|
|
2087
2087
|
}
|
|
2088
2088
|
|
|
2089
|
+
// src/feature-flags.ts
|
|
2090
|
+
var featureFlags = {
|
|
2091
|
+
"ai-cost": {
|
|
2092
|
+
title: "AI cost",
|
|
2093
|
+
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.",
|
|
2094
|
+
stage: "beta"
|
|
2095
|
+
},
|
|
2096
|
+
"read-aloud": {
|
|
2097
|
+
title: "Read aloud",
|
|
2098
|
+
description: "Listen to messages, threads and docs as audio. Still rough in places, and each playback uses text-to-speech.",
|
|
2099
|
+
stage: "beta"
|
|
2100
|
+
},
|
|
2101
|
+
excalidraw: {
|
|
2102
|
+
title: "Excalidraw",
|
|
2103
|
+
description: "Create and edit whiteboard drawings (.excalidraw files) in the Docs pane. Existing drawings stay viewable with this off.",
|
|
2104
|
+
stage: "beta"
|
|
2105
|
+
},
|
|
2106
|
+
sandbox: {
|
|
2107
|
+
title: "Sandbox runtime",
|
|
2108
|
+
description: "Run a channel agent in an isolated sandbox without Docker. Channels already on Sandbox keep working with this off.",
|
|
2109
|
+
stage: "beta"
|
|
2110
|
+
},
|
|
2111
|
+
dictation: {
|
|
2112
|
+
title: "Dictation",
|
|
2113
|
+
description: "A mic button in the message composer that turns speech into text as you talk. Each phrase uses speech-to-text.",
|
|
2114
|
+
stage: "dev"
|
|
2115
|
+
},
|
|
2116
|
+
"agent-memory": {
|
|
2117
|
+
title: "Agent memory",
|
|
2118
|
+
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.",
|
|
2119
|
+
stage: "beta"
|
|
2120
|
+
}
|
|
2121
|
+
};
|
|
2122
|
+
var featureFlagIds = Object.keys(featureFlags);
|
|
2123
|
+
function isFeatureFlagId(id) {
|
|
2124
|
+
return Object.hasOwn(featureFlags, id);
|
|
2125
|
+
}
|
|
2126
|
+
function isFeatureVisible(stage, devHost) {
|
|
2127
|
+
return stage === "beta" || stage === "dev" && devHost;
|
|
2128
|
+
}
|
|
2129
|
+
function resolveFeatureFlags(enabledIds, devHost) {
|
|
2130
|
+
const enabled = new Set(enabledIds);
|
|
2131
|
+
const out = {};
|
|
2132
|
+
for (const id of featureFlagIds) {
|
|
2133
|
+
const stage = featureFlags[id].stage;
|
|
2134
|
+
out[id] = stage === "ga" || isFeatureVisible(stage, devHost) && enabled.has(id);
|
|
2135
|
+
}
|
|
2136
|
+
return out;
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
// src/store/feature-flag-store.ts
|
|
2140
|
+
async function listEnabledFeatureFlags(workspaceId) {
|
|
2141
|
+
const { data, error } = await getSupabaseClient().from("feature_flags").select("flag_id").eq("workspace_id", workspaceId);
|
|
2142
|
+
if (error) throw new Error(`listEnabledFeatureFlags(${workspaceId}): ${error.message}`);
|
|
2143
|
+
return (data ?? []).map((r) => r.flag_id);
|
|
2144
|
+
}
|
|
2145
|
+
async function listEnabledFeatureFlagsBySlug(slug) {
|
|
2146
|
+
const { data, error } = await getSupabaseClient().from("workspaces").select("id").eq("slug", slug).maybeSingle();
|
|
2147
|
+
if (error) throw new Error(`listEnabledFeatureFlagsBySlug(${slug}): ${error.message}`);
|
|
2148
|
+
return data ? listEnabledFeatureFlags(data.id) : [];
|
|
2149
|
+
}
|
|
2150
|
+
async function enableFeatureFlag(workspaceId, flagId, userId) {
|
|
2151
|
+
const { error } = await getSupabaseClient().from("feature_flags").upsert(
|
|
2152
|
+
{ workspace_id: workspaceId, flag_id: flagId, enabled_by: userId },
|
|
2153
|
+
{ onConflict: "workspace_id,flag_id" }
|
|
2154
|
+
);
|
|
2155
|
+
if (error) throw new Error(`enableFeatureFlag(${workspaceId}, ${flagId}): ${error.message}`);
|
|
2156
|
+
}
|
|
2157
|
+
async function disableFeatureFlag(workspaceId, flagId) {
|
|
2158
|
+
const { error } = await getSupabaseClient().from("feature_flags").delete().eq("workspace_id", workspaceId).eq("flag_id", flagId);
|
|
2159
|
+
if (error) throw new Error(`disableFeatureFlag(${workspaceId}, ${flagId}): ${error.message}`);
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
// src/store/memory-store.ts
|
|
2163
|
+
var MEMORY_TYPES = ["preference", "decision", "fact", "reference"];
|
|
2164
|
+
var NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
2165
|
+
var DESCRIPTION_MAX = 200;
|
|
2166
|
+
var BODY_MAX = 16e3;
|
|
2167
|
+
var MEMORY_INDEX_MAX_CHARS = 8e3;
|
|
2168
|
+
function isMemoryType(value) {
|
|
2169
|
+
return MEMORY_TYPES.includes(value);
|
|
2170
|
+
}
|
|
2171
|
+
function validateMemoryInput(input) {
|
|
2172
|
+
if (!NAME_RE.test(input.name))
|
|
2173
|
+
throw new Error("name must be a kebab-case slug, e.g. deploy-flow");
|
|
2174
|
+
if (!isMemoryType(input.type)) throw new Error(`type must be one of ${MEMORY_TYPES.join(", ")}`);
|
|
2175
|
+
const description = input.description.trim();
|
|
2176
|
+
if (!description || description.includes("\n") || description.length > DESCRIPTION_MAX) {
|
|
2177
|
+
throw new Error(`description must be one line of at most ${DESCRIPTION_MAX} characters`);
|
|
2178
|
+
}
|
|
2179
|
+
const body = input.body.trim();
|
|
2180
|
+
if (!body || body.length > BODY_MAX) {
|
|
2181
|
+
throw new Error(`body must be 1 to ${BODY_MAX} characters`);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
function rowToMemory(row) {
|
|
2185
|
+
return {
|
|
2186
|
+
id: row["id"],
|
|
2187
|
+
channelId: row["channel_id"],
|
|
2188
|
+
name: row["name"],
|
|
2189
|
+
description: row["description"],
|
|
2190
|
+
type: row["type"],
|
|
2191
|
+
body: row["body"],
|
|
2192
|
+
sourceThreadId: row["source_thread_id"] ?? null,
|
|
2193
|
+
createdBy: row["created_by"],
|
|
2194
|
+
createdAt: row["created_at"],
|
|
2195
|
+
updatedAt: row["updated_at"]
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
async function listChannelMemories(channelId) {
|
|
2199
|
+
const { data, error } = await getSupabaseClient().from("memories").select("*").eq("scope", "channel").eq("channel_id", channelId).order("updated_at", { ascending: false });
|
|
2200
|
+
if (error) throw new Error(`listChannelMemories(${channelId}): ${error.message}`);
|
|
2201
|
+
return (data ?? []).map(rowToMemory);
|
|
2202
|
+
}
|
|
2203
|
+
async function getChannelMemory(channelId, name) {
|
|
2204
|
+
const { data, error } = await getSupabaseClient().from("memories").select("*").eq("scope", "channel").eq("channel_id", channelId).eq("name", name).maybeSingle();
|
|
2205
|
+
if (error) throw new Error(`getChannelMemory(${channelId}, ${name}): ${error.message}`);
|
|
2206
|
+
return data ? rowToMemory(data) : null;
|
|
2207
|
+
}
|
|
2208
|
+
async function saveChannelMemory(input, actor) {
|
|
2209
|
+
validateMemoryInput(input);
|
|
2210
|
+
const supabase = getSupabaseClient();
|
|
2211
|
+
const existing = await getChannelMemory(input.channelId, input.name);
|
|
2212
|
+
const fields = {
|
|
2213
|
+
description: input.description.trim(),
|
|
2214
|
+
type: input.type,
|
|
2215
|
+
body: input.body.trim(),
|
|
2216
|
+
source_thread_id: input.sourceThreadId ?? existing?.sourceThreadId ?? null,
|
|
2217
|
+
created_by: input.createdBy
|
|
2218
|
+
};
|
|
2219
|
+
if (existing && sameContent(existing, fields)) {
|
|
2220
|
+
return { memory: existing, created: false, changed: false };
|
|
2221
|
+
}
|
|
2222
|
+
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, scope: "channel", channel_id: input.channelId, name: input.name }).select("*").single();
|
|
2223
|
+
if (error)
|
|
2224
|
+
throw new Error(`saveChannelMemory(${input.channelId}, ${input.name}): ${error.message}`);
|
|
2225
|
+
const memory = rowToMemory(data);
|
|
2226
|
+
await logMemoryEvent({
|
|
2227
|
+
memory,
|
|
2228
|
+
action: existing ? "update" : "create",
|
|
2229
|
+
before: existing ? snapshot(existing) : null,
|
|
2230
|
+
after: snapshot(memory),
|
|
2231
|
+
actor,
|
|
2232
|
+
threadId: input.sourceThreadId ?? null
|
|
2233
|
+
});
|
|
2234
|
+
return { memory, created: !existing, changed: true };
|
|
2235
|
+
}
|
|
2236
|
+
async function updateChannelMemory(channelId, memoryId, patch, actor) {
|
|
2237
|
+
const existing = (await listChannelMemories(channelId)).find((m) => m.id === memoryId);
|
|
2238
|
+
if (!existing) return null;
|
|
2239
|
+
const { memory } = await saveChannelMemory(
|
|
2240
|
+
{
|
|
2241
|
+
channelId,
|
|
2242
|
+
name: existing.name,
|
|
2243
|
+
description: patch.description ?? existing.description,
|
|
2244
|
+
type: patch.type ?? existing.type,
|
|
2245
|
+
body: patch.body ?? existing.body,
|
|
2246
|
+
createdBy: existing.createdBy
|
|
2247
|
+
},
|
|
2248
|
+
actor
|
|
2249
|
+
);
|
|
2250
|
+
return memory;
|
|
2251
|
+
}
|
|
2252
|
+
async function searchChannelMemories(channelId, query) {
|
|
2253
|
+
const words = query.replaceAll(/[,()*%\\]/g, " ").split(/\s+/).filter(Boolean);
|
|
2254
|
+
if (!words.length) return [];
|
|
2255
|
+
let request = getSupabaseClient().from("memories").select("*").eq("scope", "channel").eq("channel_id", channelId);
|
|
2256
|
+
for (const word of words) {
|
|
2257
|
+
request = request.or(`name.ilike.*${word}*,description.ilike.*${word}*,body.ilike.*${word}*`);
|
|
2258
|
+
}
|
|
2259
|
+
const { data, error } = await request.order("updated_at", { ascending: false }).limit(20);
|
|
2260
|
+
if (error) throw new Error(`searchChannelMemories(${channelId}): ${error.message}`);
|
|
2261
|
+
return (data ?? []).map(rowToMemory);
|
|
2262
|
+
}
|
|
2263
|
+
async function deleteChannelMemory(channelId, idOrName, actor, threadId) {
|
|
2264
|
+
const { data, error } = await getSupabaseClient().from("memories").delete().eq("scope", "channel").eq("channel_id", channelId).eq(isUuid(idOrName) ? "id" : "name", idOrName).select("*");
|
|
2265
|
+
if (error) throw new Error(`deleteChannelMemory(${channelId}, ${idOrName}): ${error.message}`);
|
|
2266
|
+
const deleted = (data ?? []).map(rowToMemory);
|
|
2267
|
+
for (const memory of deleted) {
|
|
2268
|
+
await logMemoryEvent({
|
|
2269
|
+
memory,
|
|
2270
|
+
action: "delete",
|
|
2271
|
+
before: snapshot(memory),
|
|
2272
|
+
after: null,
|
|
2273
|
+
actor,
|
|
2274
|
+
threadId: threadId ?? null
|
|
2275
|
+
});
|
|
2276
|
+
}
|
|
2277
|
+
return deleted.length > 0;
|
|
2278
|
+
}
|
|
2279
|
+
function unchangedSince(current, after) {
|
|
2280
|
+
return current && after ? sameContent(current, after) : !current && !after;
|
|
2281
|
+
}
|
|
2282
|
+
function snapshot(m) {
|
|
2283
|
+
return {
|
|
2284
|
+
description: m.description,
|
|
2285
|
+
type: m.type,
|
|
2286
|
+
body: m.body,
|
|
2287
|
+
sourceThreadId: m.sourceThreadId,
|
|
2288
|
+
createdBy: m.createdBy
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
function sameContent(m, other) {
|
|
2292
|
+
return m.description === other.description && m.type === other.type && m.body === other.body;
|
|
2293
|
+
}
|
|
2294
|
+
async function logMemoryEvent(e) {
|
|
2295
|
+
const { error } = await getSupabaseClient().from("memory_events").insert({
|
|
2296
|
+
channel_id: e.memory.channelId,
|
|
2297
|
+
memory_id: e.action === "delete" ? null : e.memory.id,
|
|
2298
|
+
thread_id: e.threadId,
|
|
2299
|
+
name: e.memory.name,
|
|
2300
|
+
action: e.action,
|
|
2301
|
+
before: e.before,
|
|
2302
|
+
after: e.after,
|
|
2303
|
+
actor: e.actor
|
|
2304
|
+
});
|
|
2305
|
+
if (error) console.error(`[memory] event log failed for ${e.memory.name}:`, error.message);
|
|
2306
|
+
}
|
|
2307
|
+
function rowToEvent(row) {
|
|
2308
|
+
return {
|
|
2309
|
+
id: row["id"],
|
|
2310
|
+
channelId: row["channel_id"],
|
|
2311
|
+
memoryId: row["memory_id"] ?? null,
|
|
2312
|
+
threadId: row["thread_id"] ?? null,
|
|
2313
|
+
name: row["name"],
|
|
2314
|
+
action: row["action"],
|
|
2315
|
+
before: row["before"] ?? null,
|
|
2316
|
+
after: row["after"] ?? null,
|
|
2317
|
+
actor: row["actor"],
|
|
2318
|
+
createdAt: row["created_at"],
|
|
2319
|
+
undoneAt: row["undone_at"] ?? null
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
async function listThreadMemoryEvents(channelId, threadId) {
|
|
2323
|
+
const supabase = getSupabaseClient();
|
|
2324
|
+
const { data, error } = await supabase.from("memory_events").select("*").eq("channel_id", channelId).eq("thread_id", threadId).order("created_at", { ascending: false }).limit(50);
|
|
2325
|
+
if (error) throw new Error(`listThreadMemoryEvents(${threadId}): ${error.message}`);
|
|
2326
|
+
const events = (data ?? []).map(rowToEvent);
|
|
2327
|
+
if (!events.length) return [];
|
|
2328
|
+
const names = [...new Set(events.map((e) => e.name))];
|
|
2329
|
+
const live = await supabase.from("memories").select("*").eq("scope", "channel").eq("channel_id", channelId).in("name", names);
|
|
2330
|
+
if (live.error) throw new Error(`listThreadMemoryEvents(${threadId}): ${live.error.message}`);
|
|
2331
|
+
const byName = new Map(
|
|
2332
|
+
(live.data ?? []).map((r) => [
|
|
2333
|
+
r["name"],
|
|
2334
|
+
rowToMemory(r)
|
|
2335
|
+
])
|
|
2336
|
+
);
|
|
2337
|
+
return events.map((e) => {
|
|
2338
|
+
const current = byName.get(e.name) ?? null;
|
|
2339
|
+
const unchanged = unchangedSince(current, e.after);
|
|
2340
|
+
return {
|
|
2341
|
+
...e,
|
|
2342
|
+
currentState: unchanged ? "current" : current ? "changed" : "deleted",
|
|
2343
|
+
undoable: !e.undoneAt && unchanged
|
|
2344
|
+
};
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
async function undoMemoryEvent(channelId, eventId) {
|
|
2348
|
+
const supabase = getSupabaseClient();
|
|
2349
|
+
const { data, error } = await supabase.from("memory_events").select("*").eq("id", eventId).eq("channel_id", channelId).maybeSingle();
|
|
2350
|
+
if (error) throw new Error(`undoMemoryEvent(${eventId}): ${error.message}`);
|
|
2351
|
+
if (!data) return "not-found";
|
|
2352
|
+
const event = rowToEvent(data);
|
|
2353
|
+
if (event.undoneAt) return "already-undone";
|
|
2354
|
+
const current = await getChannelMemory(channelId, event.name);
|
|
2355
|
+
if (!unchangedSince(current, event.after)) return "conflict";
|
|
2356
|
+
const claimed = await supabase.from("memory_events").update({ undone_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", eventId).is("undone_at", null).select("id");
|
|
2357
|
+
if (claimed.error) throw new Error(`undoMemoryEvent(${eventId}): ${claimed.error.message}`);
|
|
2358
|
+
if (!(claimed.data ?? []).length) return "already-undone";
|
|
2359
|
+
const before = event.before;
|
|
2360
|
+
const restore = before ? {
|
|
2361
|
+
description: before.description,
|
|
2362
|
+
type: before.type,
|
|
2363
|
+
body: before.body,
|
|
2364
|
+
source_thread_id: before.sourceThreadId,
|
|
2365
|
+
created_by: before.createdBy
|
|
2366
|
+
} : null;
|
|
2367
|
+
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 });
|
|
2368
|
+
if (writeError) {
|
|
2369
|
+
await supabase.from("memory_events").update({ undone_at: null }).eq("id", eventId);
|
|
2370
|
+
throw new Error(`undoMemoryEvent(${eventId}): ${writeError.message}`);
|
|
2371
|
+
}
|
|
2372
|
+
return "undone";
|
|
2373
|
+
}
|
|
2374
|
+
function isUuid(value) {
|
|
2375
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
2376
|
+
}
|
|
2377
|
+
function formatMemoryPrompt(channelId, memories) {
|
|
2378
|
+
const lines = [];
|
|
2379
|
+
let used = 0;
|
|
2380
|
+
let omitted = 0;
|
|
2381
|
+
for (const m of memories) {
|
|
2382
|
+
const line = `- \`${m.name}\` (${m.type}): ${m.description}`;
|
|
2383
|
+
if (used + line.length > MEMORY_INDEX_MAX_CHARS) {
|
|
2384
|
+
omitted++;
|
|
2385
|
+
continue;
|
|
2386
|
+
}
|
|
2387
|
+
lines.push(line);
|
|
2388
|
+
used += line.length + 1;
|
|
2389
|
+
}
|
|
2390
|
+
const index = lines.length ? lines.join("\n") + (omitted ? `
|
|
2391
|
+
- (${omitted} older memories not listed; find them with \`memory search\`)` : "") : "(none yet)";
|
|
2392
|
+
return [
|
|
2393
|
+
`## Channel memory`,
|
|
2394
|
+
``,
|
|
2395
|
+
`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.`,
|
|
2396
|
+
``,
|
|
2397
|
+
`- Read a memory: \`flyspace memory get --channel ${channelId} <name>\``,
|
|
2398
|
+
`- Search: \`flyspace memory search --channel ${channelId} <words>\``,
|
|
2399
|
+
`- Save or overwrite: \`flyspace memory save --channel ${channelId} --name <kebab-slug> --type <${MEMORY_TYPES.join("|")}> --description "<one line>" --body "<text>" [--thread <thread-id>]\``,
|
|
2400
|
+
`- Delete one that turned out wrong: \`flyspace memory delete --channel ${channelId} <name> [--thread <thread-id>]\``,
|
|
2401
|
+
``,
|
|
2402
|
+
`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.`,
|
|
2403
|
+
``,
|
|
2404
|
+
`Index:`,
|
|
2405
|
+
index
|
|
2406
|
+
].join("\n");
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
// src/store/memory-context.ts
|
|
2410
|
+
async function loadAgentMemory(profile, channelId, opts = {}) {
|
|
2411
|
+
const enabled = await listEnabledFeatureFlagsBySlug(profile).then((ids) => resolveFeatureFlags(ids, false)["agent-memory"]).catch((err) => {
|
|
2412
|
+
console.error("[memory] feature flag lookup failed:", err.message);
|
|
2413
|
+
return false;
|
|
2414
|
+
});
|
|
2415
|
+
if (!enabled || opts.readable === false || !channelId || !isUuid(channelId)) return { enabled };
|
|
2416
|
+
const memoryPrompt = await listChannelMemories(channelId).then((memories) => formatMemoryPrompt(channelId, memories)).catch((err) => {
|
|
2417
|
+
console.error("[memory] memory lookup failed:", err.message);
|
|
2418
|
+
return void 0;
|
|
2419
|
+
});
|
|
2420
|
+
return memoryPrompt ? { enabled, memoryPrompt } : { enabled };
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2089
2423
|
// src/store/workspace-store.ts
|
|
2090
2424
|
function rowToMembership(row) {
|
|
2091
2425
|
return {
|
|
@@ -2163,6 +2497,7 @@ async function deleteWorkspace(workspace) {
|
|
|
2163
2497
|
}
|
|
2164
2498
|
async function setWorkspaceStripeSubscription(workspaceId, fields) {
|
|
2165
2499
|
const { error } = await getSupabaseClient().from("workspaces").update({
|
|
2500
|
+
plan: fields.plan,
|
|
2166
2501
|
stripe_customer_id: fields.stripeCustomerId,
|
|
2167
2502
|
stripe_subscription_id: fields.stripeSubscriptionId,
|
|
2168
2503
|
plan_status: fields.planStatus
|
|
@@ -2198,11 +2533,12 @@ async function getWorkspaceByStripeCustomerId(stripeCustomerId) {
|
|
|
2198
2533
|
return data;
|
|
2199
2534
|
}
|
|
2200
2535
|
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();
|
|
2536
|
+
const { data, error } = await getSupabaseClient().from("workspaces").select("plan, plan_status, stripe_customer_id, stripe_subscription_id").eq("id", workspaceId).maybeSingle();
|
|
2202
2537
|
if (error) throw new Error(`getWorkspaceBilling(${workspaceId}): ${error.message}`);
|
|
2203
2538
|
const row = data ?? {};
|
|
2204
2539
|
return {
|
|
2205
2540
|
planStatus: row["plan_status"] ?? null,
|
|
2541
|
+
plan: row["plan"] ?? null,
|
|
2206
2542
|
stripeCustomerId: row["stripe_customer_id"] ?? null,
|
|
2207
2543
|
stripeSubscriptionId: row["stripe_subscription_id"] ?? null
|
|
2208
2544
|
};
|
|
@@ -3739,17 +4075,21 @@ async function runAutomation(opts) {
|
|
|
3739
4075
|
|
|
3740
4076
|
`;
|
|
3741
4077
|
try {
|
|
4078
|
+
const memory = await loadAgentMemory(getProfile(), channel && findChannel(channel)?.id);
|
|
3742
4079
|
const useCodex = (explicitModel ? runnerForModel(explicitModel) === "codex" : runnerForModel(loadDashboardSettings().agent) === "codex") && !requiresClaudeAutomationRunner(prompt);
|
|
3743
4080
|
const response = useCodex ? await askCodex(automationPrefix + prompt, cwd, {
|
|
3744
4081
|
...explicitModel && runnerForModel(explicitModel) === "codex" ? { model: explicitModel } : codexModel?.trim() ? { model: codexModel.trim() } : {},
|
|
3745
4082
|
// Same reason as the interactive paths: no --append-system-prompt on
|
|
3746
4083
|
// Codex, so the persona and channel instructions go in the prompt.
|
|
3747
|
-
systemPrompt:
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
4084
|
+
systemPrompt: [
|
|
4085
|
+
buildSystemPrompt(
|
|
4086
|
+
botName,
|
|
4087
|
+
void 0,
|
|
4088
|
+
resolveChannelPrompt(channel),
|
|
4089
|
+
resolveSelectedOutputStyle(claudeConfigDir)?.body
|
|
4090
|
+
),
|
|
4091
|
+
memory.memoryPrompt
|
|
4092
|
+
].filter(Boolean).join("\n\n"),
|
|
3753
4093
|
// Same attribution as the Claude branch below — no userId, the
|
|
3754
4094
|
// schedule's name is the label.
|
|
3755
4095
|
usageContext: { source: "automation", channelId: channel, label: name }
|
|
@@ -3763,6 +4103,8 @@ async function runAutomation(opts) {
|
|
|
3763
4103
|
model,
|
|
3764
4104
|
maxBudgetUsd,
|
|
3765
4105
|
channelPrompt: resolveChannelPrompt(channel),
|
|
4106
|
+
...memory.memoryPrompt ? { memoryPrompt: memory.memoryPrompt } : {},
|
|
4107
|
+
...memory.enabled ? { disableAutoMemory: true } : {},
|
|
3766
4108
|
// No userId: nobody triggered this. The schedule's name is the
|
|
3767
4109
|
// label, which is what makes "which automation is burning tokens"
|
|
3768
4110
|
// answerable without joining anything.
|
|
@@ -8408,6 +8750,27 @@ export {
|
|
|
8408
8750
|
docsAccessPromptNote,
|
|
8409
8751
|
pinnedClaudeConfigDir,
|
|
8410
8752
|
askClaude,
|
|
8753
|
+
featureFlags,
|
|
8754
|
+
featureFlagIds,
|
|
8755
|
+
isFeatureFlagId,
|
|
8756
|
+
isFeatureVisible,
|
|
8757
|
+
resolveFeatureFlags,
|
|
8758
|
+
listEnabledFeatureFlags,
|
|
8759
|
+
enableFeatureFlag,
|
|
8760
|
+
disableFeatureFlag,
|
|
8761
|
+
MEMORY_TYPES,
|
|
8762
|
+
isMemoryType,
|
|
8763
|
+
validateMemoryInput,
|
|
8764
|
+
listChannelMemories,
|
|
8765
|
+
getChannelMemory,
|
|
8766
|
+
saveChannelMemory,
|
|
8767
|
+
updateChannelMemory,
|
|
8768
|
+
searchChannelMemories,
|
|
8769
|
+
deleteChannelMemory,
|
|
8770
|
+
listThreadMemoryEvents,
|
|
8771
|
+
undoMemoryEvent,
|
|
8772
|
+
isUuid,
|
|
8773
|
+
loadAgentMemory,
|
|
8411
8774
|
getWorkspaceBySlug,
|
|
8412
8775
|
getWorkspaceById,
|
|
8413
8776
|
listWorkspaces,
|