flyspace-cli 0.1.13 → 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.
@@ -163,9 +163,9 @@ async function refreshAutomationRegistry() {
163
163
  return;
164
164
  }
165
165
  const next = data ?? [];
166
- const snapshot = JSON.stringify(next, (key, value) => key === "updated_at" ? void 0 : value);
167
- if (snapshot === lastSnapshot) return;
168
- lastSnapshot = snapshot;
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 snapshot = JSON.stringify(
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 (snapshot === lastSnapshot2) return;
372
- lastSnapshot2 = snapshot;
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
@@ -2175,6 +2510,14 @@ async function markWorkspaceAgentConnected(workspaceId) {
2175
2510
  async function markWorkspaceProjectsSetup(workspaceId) {
2176
2511
  await stampOnce(workspaceId, "projects_setup_at");
2177
2512
  }
2513
+ async function markWorkspaceGreeted(workspaceId) {
2514
+ await stampOnce(workspaceId, "greeted_at");
2515
+ }
2516
+ async function getWorkspaceGreetedAt(workspaceId) {
2517
+ const { data, error } = await getSupabaseClient().from("workspaces").select("greeted_at").eq("id", workspaceId).maybeSingle();
2518
+ if (error) throw new Error(`getWorkspaceGreetedAt(${workspaceId}): ${error.message}`);
2519
+ return data?.greeted_at ?? null;
2520
+ }
2178
2521
  async function stampOnce(workspaceId, column) {
2179
2522
  const { error } = await getSupabaseClient().from("workspaces").update({ [column]: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", workspaceId).is(column, null);
2180
2523
  if (error) throw new Error(`stampOnce(${workspaceId}, ${column}): ${error.message}`);
@@ -2190,11 +2533,12 @@ async function getWorkspaceByStripeCustomerId(stripeCustomerId) {
2190
2533
  return data;
2191
2534
  }
2192
2535
  async function getWorkspaceBilling(workspaceId) {
2193
- 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();
2194
2537
  if (error) throw new Error(`getWorkspaceBilling(${workspaceId}): ${error.message}`);
2195
2538
  const row = data ?? {};
2196
2539
  return {
2197
2540
  planStatus: row["plan_status"] ?? null,
2541
+ plan: row["plan"] ?? null,
2198
2542
  stripeCustomerId: row["stripe_customer_id"] ?? null,
2199
2543
  stripeSubscriptionId: row["stripe_subscription_id"] ?? null
2200
2544
  };
@@ -3731,17 +4075,21 @@ async function runAutomation(opts) {
3731
4075
 
3732
4076
  `;
3733
4077
  try {
4078
+ const memory = await loadAgentMemory(getProfile(), channel && findChannel(channel)?.id);
3734
4079
  const useCodex = (explicitModel ? runnerForModel(explicitModel) === "codex" : runnerForModel(loadDashboardSettings().agent) === "codex") && !requiresClaudeAutomationRunner(prompt);
3735
4080
  const response = useCodex ? await askCodex(automationPrefix + prompt, cwd, {
3736
4081
  ...explicitModel && runnerForModel(explicitModel) === "codex" ? { model: explicitModel } : codexModel?.trim() ? { model: codexModel.trim() } : {},
3737
4082
  // Same reason as the interactive paths: no --append-system-prompt on
3738
4083
  // Codex, so the persona and channel instructions go in the prompt.
3739
- systemPrompt: buildSystemPrompt(
3740
- botName,
3741
- void 0,
3742
- resolveChannelPrompt(channel),
3743
- resolveSelectedOutputStyle(claudeConfigDir)?.body
3744
- ),
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"),
3745
4093
  // Same attribution as the Claude branch below — no userId, the
3746
4094
  // schedule's name is the label.
3747
4095
  usageContext: { source: "automation", channelId: channel, label: name }
@@ -3755,6 +4103,8 @@ async function runAutomation(opts) {
3755
4103
  model,
3756
4104
  maxBudgetUsd,
3757
4105
  channelPrompt: resolveChannelPrompt(channel),
4106
+ ...memory.memoryPrompt ? { memoryPrompt: memory.memoryPrompt } : {},
4107
+ ...memory.enabled ? { disableAutoMemory: true } : {},
3758
4108
  // No userId: nobody triggered this. The schedule's name is the
3759
4109
  // label, which is what makes "which automation is burning tokens"
3760
4110
  // answerable without joining anything.
@@ -8400,6 +8750,27 @@ export {
8400
8750
  docsAccessPromptNote,
8401
8751
  pinnedClaudeConfigDir,
8402
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,
8403
8774
  getWorkspaceBySlug,
8404
8775
  getWorkspaceById,
8405
8776
  listWorkspaces,
@@ -8409,6 +8780,8 @@ export {
8409
8780
  setWorkspaceStripeSubscription,
8410
8781
  markWorkspaceAgentConnected,
8411
8782
  markWorkspaceProjectsSetup,
8783
+ markWorkspaceGreeted,
8784
+ getWorkspaceGreetedAt,
8412
8785
  getWorkspaceByStripeCustomerId,
8413
8786
  getWorkspaceBilling,
8414
8787
  updateWorkspacePlanStatus,