zenflo 0.11.6 → 0.11.7

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.
@@ -3,7 +3,7 @@
3
3
  var chalk = require('chalk');
4
4
  var os = require('node:os');
5
5
  var node_crypto = require('node:crypto');
6
- var types = require('./types-CJaqq466.cjs');
6
+ var types = require('./types-Dvhor4zW.cjs');
7
7
  var node_child_process = require('node:child_process');
8
8
  var node_path = require('node:path');
9
9
  var node_readline = require('node:readline');
@@ -216,18 +216,29 @@ function shouldIncludeCoAuthoredBy() {
216
216
  }
217
217
 
218
218
  const BASE_SYSTEM_PROMPT = (() => trimIdent(`
219
- ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
219
+ ALWAYS when you start a new chat - you must call a tool "mcp__zenflo__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
220
+
221
+ \u26A0\uFE0F CRITICAL: Use Zen Mode MCP, NOT TodoWrite tool
222
+
223
+ At the START of EVERY session, you MUST:
224
+ 1. Call mcp__zen_mode__list_tasks to see existing tasks
225
+ 2. Use mcp__zen_mode__create_task to create new tasks for the session
226
+ 3. Use mcp__zen_mode__update_task to update status as you work
227
+ 4. NEVER use TodoWrite tool - it doesn't sync across devices
228
+
229
+ Task Priorities: LOW, MEDIUM, HIGH, URGENT
230
+ Task Status: TODO, IN_PROGRESS, DONE, CANCELLED
220
231
  `))();
221
232
  const CO_AUTHORED_CREDITS = (() => trimIdent(`
222
- When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
233
+ When making commit messages, instead of just giving co-credit to Claude, also give credit to ZenFlo like so:
223
234
 
224
235
  <main commit message>
225
236
 
226
237
  Generated with [Claude Code](https://claude.ai/code)
227
- via [Happy](https://happy.engineering)
238
+ via [ZenFlo](https://zenflo.app)
228
239
 
229
240
  Co-Authored-By: Claude <noreply@anthropic.com>
230
- Co-Authored-By: Happy <yesreply@happy.engineering>
241
+ Co-Authored-By: ZenFlo <yesreply@zenflo.app>
231
242
  `))();
232
243
  const systemPrompt = (() => {
233
244
  const includeCoAuthored = shouldIncludeCoAuthoredBy();
@@ -279,7 +290,10 @@ async function claudeLocal(opts) {
279
290
  }
280
291
  };
281
292
  try {
282
- process.stdin.pause();
293
+ const isStreamJsonMode = opts.claudeArgs?.includes("--input-format") && opts.claudeArgs?.includes("stream-json");
294
+ if (!isStreamJsonMode) {
295
+ process.stdin.pause();
296
+ }
283
297
  await new Promise((r, reject) => {
284
298
  const args = [];
285
299
  if (startFrom) {
@@ -304,10 +318,16 @@ async function claudeLocal(opts) {
304
318
  };
305
319
  const child = node_child_process.spawn("node", [claudeCliPath, ...args], {
306
320
  stdio: ["inherit", "inherit", "inherit", "pipe"],
307
- signal: opts.abort,
308
321
  cwd: opts.path,
309
322
  env
310
323
  });
324
+ const abortHandler = () => {
325
+ if (!child.killed) {
326
+ types.logger.debug("[ClaudeLocal] Abort signal received, killing child process");
327
+ child.kill("SIGTERM");
328
+ }
329
+ };
330
+ opts.abort.addEventListener("abort", abortHandler);
311
331
  if (child.stdio[3]) {
312
332
  const rl = node_readline.createInterface({
313
333
  input: child.stdio[3],
@@ -379,7 +399,10 @@ async function claudeLocal(opts) {
379
399
  });
380
400
  } finally {
381
401
  watcher.close();
382
- process.stdin.resume();
402
+ const isStreamJsonMode = opts.claudeArgs?.includes("--input-format") && opts.claudeArgs?.includes("stream-json");
403
+ if (!isStreamJsonMode) {
404
+ process.stdin.resume();
405
+ }
383
406
  if (stopThinkingTimeout) {
384
407
  clearTimeout(stopThinkingTimeout);
385
408
  stopThinkingTimeout = null;
@@ -563,7 +586,7 @@ async function createSessionScanner(opts) {
563
586
  await sync.invalidateAndAwait();
564
587
  sync.stop();
565
588
  },
566
- onNewSession: (sessionId) => {
589
+ onNewSession: async (sessionId) => {
567
590
  if (currentSessionId === sessionId) {
568
591
  types.logger.debug(`[SESSION_SCANNER] New session: ${sessionId} is the same as the current session, skipping`);
569
592
  return;
@@ -581,6 +604,19 @@ async function createSessionScanner(opts) {
581
604
  }
582
605
  types.logger.debug(`[SESSION_SCANNER] New session: ${sessionId}`);
583
606
  currentSessionId = sessionId;
607
+ try {
608
+ const existingMessages = await readSessionLog(projectDir, sessionId);
609
+ types.logger.debug(`[SESSION_SCANNER] Found ${existingMessages.length} existing messages in new session ${sessionId}`);
610
+ for (const message of existingMessages) {
611
+ const key = messageKey(message);
612
+ if (!processedMessageKeys.has(key)) {
613
+ processedMessageKeys.add(key);
614
+ opts.onMessage(message);
615
+ }
616
+ }
617
+ } catch (err) {
618
+ types.logger.debug(`[SESSION_SCANNER] Error reading existing messages: ${err}`);
619
+ }
584
620
  sync.invalidate();
585
621
  }
586
622
  };
@@ -594,6 +630,8 @@ function messageKey(message) {
594
630
  return "summary: " + message.leafUuid + ": " + message.summary;
595
631
  } else if (message.type === "system") {
596
632
  return message.uuid;
633
+ } else if (message.type === "queue-operation") {
634
+ return "queue-operation: " + message.timestamp + ": " + message.operation;
597
635
  } else {
598
636
  throw Error();
599
637
  }
@@ -618,7 +656,8 @@ async function readSessionLog(projectDir, sessionId) {
618
656
  let message = JSON.parse(l);
619
657
  let parsed = types.RawJSONLinesSchema.safeParse(message);
620
658
  if (!parsed.success) {
621
- types.logger.debugLargeJson(`[SESSION_SCANNER] Failed to parse message`, message);
659
+ types.logger.debug(`[SESSION_SCANNER] Failed to parse message - Zod error:`, parsed.error.errors);
660
+ types.logger.debugLargeJson(`[SESSION_SCANNER] Failed message content:`, message);
622
661
  continue;
623
662
  }
624
663
  messages.push(parsed.data);
@@ -635,7 +674,7 @@ async function claudeLocalLauncher(session) {
635
674
  sessionId: session.sessionId,
636
675
  workingDirectory: session.path,
637
676
  onMessage: (message) => {
638
- if (message.type !== "summary") {
677
+ if (message.type !== "summary" && message.type !== "queue-operation") {
639
678
  session.client.sendClaudeSessionMessage(message);
640
679
  }
641
680
  }
@@ -981,7 +1020,7 @@ class AbortError extends Error {
981
1020
  }
982
1021
  }
983
1022
 
984
- const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-BtMdJglm.cjs', document.baseURI).href)));
1023
+ const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-HBSmEvnF.cjs', document.baseURI).href)));
985
1024
  const __dirname$1 = node_path.join(__filename$1, "..");
986
1025
  function getDefaultClaudeCodePath() {
987
1026
  return node_path.join(__dirname$1, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
@@ -3210,7 +3249,7 @@ function hashObject(obj, options, encoding = "hex") {
3210
3249
  let caffeinateProcess = null;
3211
3250
  function startCaffeinate() {
3212
3251
  if (types.configuration.disableCaffeinate) {
3213
- types.logger.debug("[caffeinate] Caffeinate disabled via HAPPY_DISABLE_CAFFEINATE environment variable");
3252
+ types.logger.debug("[caffeinate] Caffeinate disabled via ZENFLO_DISABLE_CAFFEINATE environment variable");
3214
3253
  return false;
3215
3254
  }
3216
3255
  if (process.platform !== "darwin") {
@@ -3354,7 +3393,7 @@ async function daemonPost(path, body) {
3354
3393
  };
3355
3394
  }
3356
3395
  try {
3357
- const timeout = process.env.HAPPY_DAEMON_HTTP_TIMEOUT ? parseInt(process.env.HAPPY_DAEMON_HTTP_TIMEOUT) : 1e4;
3396
+ const timeout = process.env.ZENFLO_DAEMON_HTTP_TIMEOUT ? parseInt(process.env.ZENFLO_DAEMON_HTTP_TIMEOUT) : 1e4;
3358
3397
  const response = await fetch(`http://127.0.0.1:${state.httpPort}${path}`, {
3359
3398
  method: "POST",
3360
3399
  headers: { "Content-Type": "application/json" },
@@ -3560,7 +3599,7 @@ function getEnvironmentInfo() {
3560
3599
  DEBUG: process.env.DEBUG,
3561
3600
  workingDirectory: process.cwd(),
3562
3601
  processArgv: process.argv,
3563
- happyDir: types.configuration?.happyHomeDir,
3602
+ happyDir: types.configuration?.zenfloHomeDir,
3564
3603
  serverUrl: types.configuration?.serverUrl,
3565
3604
  logsDir: types.configuration?.logsDir,
3566
3605
  processPid: process.pid,
@@ -3609,7 +3648,7 @@ async function runDoctorCommand(filter) {
3609
3648
  console.log(`CLI Exists: ${fs.existsSync(cliEntrypoint) ? chalk.green("\u2713 Yes") : chalk.red("\u274C No")}`);
3610
3649
  console.log("");
3611
3650
  console.log(chalk.bold("\u2699\uFE0F Configuration"));
3612
- console.log(`Happy Home: ${chalk.blue(types.configuration.happyHomeDir)}`);
3651
+ console.log(`Happy Home: ${chalk.blue(types.configuration.zenfloHomeDir)}`);
3613
3652
  console.log(`Server URL: ${chalk.blue(types.configuration.serverUrl)}`);
3614
3653
  console.log(`Logs Dir: ${chalk.blue(types.configuration.logsDir)}`);
3615
3654
  console.log(chalk.bold("\n\u{1F30D} Environment Variables"));
@@ -4032,7 +4071,7 @@ function startDaemonControlServer({
4032
4071
  stopSession,
4033
4072
  spawnSession,
4034
4073
  requestShutdown,
4035
- onHappySessionWebhook
4074
+ onZenfloSessionWebhook
4036
4075
  }) {
4037
4076
  return new Promise((resolve) => {
4038
4077
  const app = fastify({
@@ -4058,7 +4097,7 @@ function startDaemonControlServer({
4058
4097
  }, async (request) => {
4059
4098
  const { sessionId, metadata } = request.body;
4060
4099
  types.logger.debug(`[CONTROL SERVER] Session started: ${sessionId}`);
4061
- onHappySessionWebhook(sessionId, metadata);
4100
+ onZenfloSessionWebhook(sessionId, metadata);
4062
4101
  return { status: "ok" };
4063
4102
  });
4064
4103
  typed.post("/list", {
@@ -4197,10 +4236,10 @@ function startDaemonControlServer({
4197
4236
  const initialMachineMetadata = {
4198
4237
  host: os$1.hostname(),
4199
4238
  platform: os$1.platform(),
4200
- happyCliVersion: types.packageJson.version,
4239
+ zenfloCliVersion: types.packageJson.version,
4201
4240
  homeDir: os$1.homedir(),
4202
- happyHomeDir: types.configuration.happyHomeDir,
4203
- happyLibDir: types.projectPath()
4241
+ zenfloHomeDir: types.configuration.zenfloHomeDir,
4242
+ zenfloLibDir: types.projectPath()
4204
4243
  };
4205
4244
  async function startDaemon() {
4206
4245
  let requestShutdown;
@@ -4267,7 +4306,7 @@ async function startDaemon() {
4267
4306
  const pidToTrackedSession = /* @__PURE__ */ new Map();
4268
4307
  const pidToAwaiter = /* @__PURE__ */ new Map();
4269
4308
  const getCurrentChildren = () => Array.from(pidToTrackedSession.values());
4270
- const onHappySessionWebhook = (sessionId, sessionMetadata) => {
4309
+ const onZenfloSessionWebhook = (sessionId, sessionMetadata) => {
4271
4310
  types.logger.debugLargeJson(`[DAEMON RUN] Session reported`, sessionMetadata);
4272
4311
  const pid = sessionMetadata.hostPid;
4273
4312
  if (!pid) {
@@ -4289,7 +4328,7 @@ async function startDaemon() {
4289
4328
  }
4290
4329
  } else if (!existingSession) {
4291
4330
  const trackedSession = {
4292
- startedBy: "happy directly - likely by user from terminal",
4331
+ startedBy: "zenflo directly - likely by user from terminal",
4293
4332
  zenfloSessionId: sessionId,
4294
4333
  zenfloSessionMetadataFromLocalWebhook: sessionMetadata,
4295
4334
  pid
@@ -4354,8 +4393,9 @@ async function startDaemon() {
4354
4393
  }
4355
4394
  }
4356
4395
  const args = [
4357
- options.agent === "claude" ? "claude" : "codex",
4358
- "--happy-starting-mode",
4396
+ options.agent || "claude",
4397
+ // Use the agent directly (claude, codex, qwen, gemini)
4398
+ "--zenflo-starting-mode",
4359
4399
  "remote",
4360
4400
  "--started-by",
4361
4401
  "daemon"
@@ -4470,8 +4510,8 @@ async function startDaemon() {
4470
4510
  getChildren: getCurrentChildren,
4471
4511
  stopSession,
4472
4512
  spawnSession,
4473
- requestShutdown: () => requestShutdown("happy-cli"),
4474
- onHappySessionWebhook
4513
+ requestShutdown: () => requestShutdown("zenflo-cli"),
4514
+ onZenfloSessionWebhook
4475
4515
  });
4476
4516
  const fileState = {
4477
4517
  pid: process.pid,
@@ -4499,10 +4539,10 @@ async function startDaemon() {
4499
4539
  apiMachine.setRPCHandlers({
4500
4540
  spawnSession,
4501
4541
  stopSession,
4502
- requestShutdown: () => requestShutdown("happy-app")
4542
+ requestShutdown: () => requestShutdown("zenflo-app")
4503
4543
  });
4504
4544
  apiMachine.connect();
4505
- const heartbeatIntervalMs = parseInt(process.env.HAPPY_DAEMON_HEARTBEAT_INTERVAL || "60000");
4545
+ const heartbeatIntervalMs = parseInt(process.env.ZENFLO_DAEMON_HEARTBEAT_INTERVAL || "60000");
4506
4546
  let heartbeatRunning = false;
4507
4547
  const restartOnStaleVersionAndHeartbeat = setInterval(async () => {
4508
4548
  if (heartbeatRunning) {
@@ -4725,7 +4765,8 @@ function registerKillSessionHandler(rpcHandlerManager, killThisHappy) {
4725
4765
 
4726
4766
  async function runClaude(credentials, options = {}) {
4727
4767
  const workingDirectory = process.cwd();
4728
- const sessionTag = node_crypto.randomUUID();
4768
+ const isExtensionMode = options.claudeArgs?.includes("--input-format") && options.claudeArgs?.includes("stream-json");
4769
+ const sessionTag = isExtensionMode ? `extension-${hashObject({ workingDirectory, machineId: (await types.readSettings())?.machineId })}` : node_crypto.randomUUID();
4729
4770
  types.logger.debugLargeJson("[START] ZenFlo process started", getEnvironmentInfo());
4730
4771
  types.logger.debug(`[START] Options: startedBy=${options.startedBy}, startingMode=${options.startingMode}`);
4731
4772
  if (options.startedBy === "daemon" && options.startingMode === "local") {
@@ -4752,9 +4793,9 @@ async function runClaude(credentials, options = {}) {
4752
4793
  os: os.platform(),
4753
4794
  machineId,
4754
4795
  homeDir: os.homedir(),
4755
- happyHomeDir: types.configuration.happyHomeDir,
4756
- happyLibDir: types.projectPath(),
4757
- happyToolsDir: node_path.resolve(types.projectPath(), "tools", "unpacked"),
4796
+ zenfloHomeDir: types.configuration.zenfloHomeDir,
4797
+ zenfloLibDir: types.projectPath(),
4798
+ zenfloToolsDir: node_path.resolve(types.projectPath(), "tools", "unpacked"),
4758
4799
  startedFromDaemon: options.startedBy === "daemon",
4759
4800
  hostPid: process.pid,
4760
4801
  startedBy: options.startedBy || "terminal",
@@ -5025,7 +5066,7 @@ async function install$1() {
5025
5066
 
5026
5067
  <key>EnvironmentVariables</key>
5027
5068
  <dict>
5028
- <key>HAPPY_DAEMON_MODE</key>
5069
+ <key>ZENFLO_DAEMON_MODE</key>
5029
5070
  <string>true</string>
5030
5071
  </dict>
5031
5072
 
@@ -5190,7 +5231,7 @@ async function handleAuthLogin(args) {
5190
5231
  }
5191
5232
  }
5192
5233
  async function handleAuthLogout() {
5193
- const happyDir = types.configuration.happyHomeDir;
5234
+ const happyDir = types.configuration.zenfloHomeDir;
5194
5235
  const credentials = await types.readCredentials();
5195
5236
  if (!credentials) {
5196
5237
  console.log(chalk.yellow("Not currently authenticated"));
@@ -5246,7 +5287,7 @@ async function handleAuthStatus() {
5246
5287
  console.log(chalk.gray(' Run "happy auth login --force" to fix this'));
5247
5288
  }
5248
5289
  console.log(chalk.gray(`
5249
- Data directory: ${types.configuration.happyHomeDir}`));
5290
+ Data directory: ${types.configuration.zenfloHomeDir}`));
5250
5291
  try {
5251
5292
  const running = await checkIfDaemonRunningAndCleanupStaleState();
5252
5293
  if (running) {
@@ -5775,10 +5816,10 @@ ${chalk.bold("Examples:")}
5775
5816
  happy connect claude
5776
5817
  happy connect gemini
5777
5818
 
5778
- ${chalk.bold("Notes:")}
5819
+ ${chalk.bold("Notes:")}
5779
5820
  \u2022 You must be authenticated with Happy first (run 'happy auth login')
5780
5821
  \u2022 API keys are encrypted and stored securely in Happy cloud
5781
- \u2022 You can manage your stored keys at app.happy.engineering
5822
+ \u2022 You can manage your stored keys at app.combinedmemory.com
5782
5823
  `);
5783
5824
  }
5784
5825
  async function handleConnectVendor(vendor, displayName) {
@@ -5856,7 +5897,7 @@ async function handleConnectVendor(vendor, displayName) {
5856
5897
  return;
5857
5898
  } else if (subcommand === "codex") {
5858
5899
  try {
5859
- const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-Cg3lSta7.cjs'); });
5900
+ const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-cXLrsovg.cjs'); });
5860
5901
  let startedBy = void 0;
5861
5902
  for (let i = 1; i < args.length; i++) {
5862
5903
  if (args[i] === "--started-by") {
@@ -6014,7 +6055,7 @@ ${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("happy doctor c
6014
6055
  } else if (arg === "-v" || arg === "--version") {
6015
6056
  showVersion = true;
6016
6057
  unknownArgs.push(arg);
6017
- } else if (arg === "--happy-starting-mode") {
6058
+ } else if (arg === "--zenflo-starting-mode") {
6018
6059
  options.startingMode = z.z.enum(["local", "remote"]).parse(args[++i]);
6019
6060
  } else if (arg === "--yolo") {
6020
6061
  unknownArgs.push("--dangerously-skip-permissions");