sessix-server 0.3.6 → 0.3.8

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/index.js CHANGED
@@ -403,7 +403,7 @@ var ProcessProvider = class {
403
403
  * 并开始监听 stdout 的 NDJSON 输出。
404
404
  */
405
405
  async startSession(opts) {
406
- const { projectPath, message, sessionId: existingSessionId, model, permissionMode, effort, images } = opts;
406
+ const { projectPath, message, sessionId: existingSessionId, model, permissionMode, effort, images, fallbackModel, maxBudgetUsd } = opts;
407
407
  const sessionId = existingSessionId ?? (0, import_uuid.v4)();
408
408
  if (this.activeSessions.has(sessionId)) {
409
409
  await this.killSession(sessionId);
@@ -419,10 +419,10 @@ var ProcessProvider = class {
419
419
  summary: message.slice(0, 80)
420
420
  };
421
421
  const resume = opts.resume ?? !!existingSessionId;
422
- const proc = this.spawnClaudeProcess(sessionId, projectPath, resume, model, permissionMode, effort);
422
+ const proc = this.spawnClaudeProcess(sessionId, projectPath, resume, model, permissionMode, effort, fallbackModel, maxBudgetUsd);
423
423
  this.writeUserMessage(proc, message, sessionId, images);
424
424
  session.pid = proc.pid;
425
- this.activeSessions.set(sessionId, { session, process: proc, model, permissionMode, effort });
425
+ this.activeSessions.set(sessionId, { session, process: proc, model, permissionMode, effort, fallbackModel, maxBudgetUsd });
426
426
  proc.on("error", (err) => {
427
427
  console.error(`[ProcessProvider] Session ${sessionId} process error:`, err.message);
428
428
  this.activeSessions.delete(sessionId);
@@ -494,7 +494,7 @@ var ProcessProvider = class {
494
494
  }
495
495
  const savedPendingQuestion = entry.pendingQuestion;
496
496
  const newMode = permissionMode ?? entry.permissionMode;
497
- const proc = this.spawnClaudeProcess(sessionId, entry.session.projectPath, true, entry.model, newMode, entry.effort);
497
+ const proc = this.spawnClaudeProcess(sessionId, entry.session.projectPath, true, entry.model, newMode, entry.effort, entry.fallbackModel, entry.maxBudgetUsd);
498
498
  this.writeUserMessage(proc, message, sessionId, images);
499
499
  entry.session.status = "running";
500
500
  entry.session.lastActiveAt = Date.now();
@@ -544,14 +544,15 @@ var ProcessProvider = class {
544
544
  /**
545
545
  * 启动 claude CLI 进程(持久模式,stdin 保持开放接收多条消息)
546
546
  */
547
- spawnClaudeProcess(sessionId, projectPath, resume = false, model, permissionMode, effort) {
547
+ spawnClaudeProcess(sessionId, projectPath, resume = false, model, permissionMode, effort, fallbackModel, maxBudgetUsd) {
548
548
  const args = [
549
549
  "--input-format",
550
550
  "stream-json",
551
551
  "--output-format",
552
552
  "stream-json",
553
553
  "--verbose",
554
- "--include-partial-messages"
554
+ "--include-partial-messages",
555
+ "--include-hook-events"
555
556
  ];
556
557
  if (resume) {
557
558
  args.push("--resume", sessionId);
@@ -567,6 +568,12 @@ var ProcessProvider = class {
567
568
  if (effort) {
568
569
  args.push("--effort", effort);
569
570
  }
571
+ if (fallbackModel) {
572
+ args.push("--fallback-model", fallbackModel);
573
+ }
574
+ if (maxBudgetUsd != null) {
575
+ args.push("--max-budget-usd", String(maxBudgetUsd));
576
+ }
570
577
  const env = { ...process.env, SESSIX_SESSION_ID: sessionId };
571
578
  delete env.CLAUDECODE;
572
579
  const proc = (0, import_child_process.spawn)(CLAUDE_PATH, args, {
@@ -1677,7 +1684,7 @@ var SessionManager = class {
1677
1684
  * 调用 provider.startSession(),订阅事件流,
1678
1685
  * 将 ClaudeStreamEvent 包装为 ServerEvent 转发。
1679
1686
  */
1680
- async createSession(projectPath, message, resumeSessionId, newSessionId, model, permissionMode, effort, images, agentType) {
1687
+ async createSession(projectPath, message, resumeSessionId, newSessionId, model, permissionMode, effort, images, agentType, fallbackModel, maxBudgetUsd) {
1681
1688
  let resolvedAgentType = agentType ?? "claude-code";
1682
1689
  if (!agentType && resumeSessionId && this.providerFactory) {
1683
1690
  const codexProvider = this.providerFactory.getProvider("codex");
@@ -1694,7 +1701,9 @@ var SessionManager = class {
1694
1701
  model,
1695
1702
  permissionMode,
1696
1703
  effort,
1697
- images
1704
+ images,
1705
+ fallbackModel,
1706
+ maxBudgetUsd
1698
1707
  });
1699
1708
  this.sessionAgentType.set(session.id, resolvedAgentType);
1700
1709
  this.lastBroadcastStatus.set(session.id, session.status);
@@ -2335,7 +2344,7 @@ var WsBridge = class _WsBridge {
2335
2344
  onConnection(callback) {
2336
2345
  this.connectionCallbacks.push(callback);
2337
2346
  }
2338
- /** 注册断开连接回调(任意客户端断开时触发,传入断开的 ws 对象) */
2347
+ /** 注册断开连接回调(任意客户端断开时触发,可通过 getConnectionCount() 判断是否全部断开) */
2339
2348
  onDisconnect(callback) {
2340
2349
  this.disconnectCallbacks.push(callback);
2341
2350
  }
@@ -2449,7 +2458,7 @@ var WsBridge = class _WsBridge {
2449
2458
  console.log(`[WsBridge] Client disconnected, connections: ${this.getConnectionCount()}`);
2450
2459
  for (const cb of this.disconnectCallbacks) {
2451
2460
  try {
2452
- cb(ws);
2461
+ cb();
2453
2462
  } catch (err) {
2454
2463
  console.error("[WsBridge] Disconnect callback error:", err);
2455
2464
  }
@@ -3275,8 +3284,6 @@ var NotificationService = class {
3275
3284
  latestAssistantText = /* @__PURE__ */ new Map();
3276
3285
  /** 获取全局待审批总数的回调(跨所有会话) */
3277
3286
  globalPendingCountProvider = null;
3278
- /** 获取当前 WS 连接数的回调(用于在线抑制推送) */
3279
- connectionCountProvider = null;
3280
3287
  /** 添加通知渠道(id 唯一,可用于后续动态开关) */
3281
3288
  addChannel(id, channel, enabled = true) {
3282
3289
  this.channelMap.set(id, { channel, enabled });
@@ -3314,14 +3321,6 @@ var NotificationService = class {
3314
3321
  setGlobalPendingCountProvider(provider) {
3315
3322
  this.globalPendingCountProvider = provider;
3316
3323
  }
3317
- /** 设置 WS 连接数提供者(用于 status_change 通知的在线抑制) */
3318
- setConnectionCountProvider(provider) {
3319
- this.connectionCountProvider = provider;
3320
- }
3321
- /** 移除指定 WebSocket 关联的所有 push token */
3322
- removeTokensByWs(ws) {
3323
- this.expoChannel?.removeTokensByWs(ws);
3324
- }
3325
3324
  /** 获取全局待审批总数 */
3326
3325
  getGlobalPendingCount() {
3327
3326
  return this.globalPendingCountProvider?.() ?? 0;
@@ -3464,7 +3463,7 @@ var NotificationService = class {
3464
3463
  isYoloMode,
3465
3464
  updatedAt: Date.now()
3466
3465
  });
3467
- } else if ((this.connectionCountProvider?.() ?? 0) === 0) {
3466
+ } else {
3468
3467
  this.notify({
3469
3468
  title: sessionTitle,
3470
3469
  body,
@@ -3486,7 +3485,7 @@ var NotificationService = class {
3486
3485
  isYoloMode,
3487
3486
  updatedAt: Date.now()
3488
3487
  });
3489
- } else if ((this.connectionCountProvider?.() ?? 0) === 0) {
3488
+ } else {
3490
3489
  this.notify({
3491
3490
  title: sessionTitle,
3492
3491
  body,
@@ -3574,17 +3573,6 @@ var ExpoNotificationChannel = class {
3574
3573
  this.soundPreferences.delete(token);
3575
3574
  console.log(`[ExpoNotificationChannel] ${t("notification.tokenRemoved", { count: this.tokens.size })}`);
3576
3575
  }
3577
- /** 移除指定 WebSocket 关联的所有 token(设备断开时调用) */
3578
- removeTokensByWs(ws) {
3579
- for (const [token, tokenWs] of this.tokenWsMap) {
3580
- if (tokenWs === ws) {
3581
- this.tokens.delete(token);
3582
- this.tokenWsMap.delete(token);
3583
- this.soundPreferences.delete(token);
3584
- console.log(`[ExpoNotificationChannel] Token removed on WS disconnect, remaining: ${this.tokens.size}`);
3585
- }
3586
- }
3587
- }
3588
3576
  /** 更新某个 token 的音效偏好 */
3589
3577
  setSoundPreferences(prefs) {
3590
3578
  for (const token of this.tokens) {
@@ -4434,7 +4422,7 @@ var TerminalExecutor = class {
4434
4422
  // src/utils/cliCapabilities.ts
4435
4423
  var import_node_child_process8 = require("child_process");
4436
4424
  var DEFAULT_CAPABILITIES = {
4437
- effortLevels: ["low", "medium", "high", "max"]
4425
+ effortLevels: ["low", "medium", "high", "xhigh", "max"]
4438
4426
  };
4439
4427
  async function parseCliCapabilities() {
4440
4428
  const claudePath = findClaudePath();
@@ -4596,7 +4584,6 @@ async function start(opts = {}) {
4596
4584
  notificationService.setGlobalPendingCountProvider(
4597
4585
  () => approvalProxy.getPendingCount() + sessionManager.getAllPendingQuestions().length + unreadSessionIds.size
4598
4586
  );
4599
- notificationService.setConnectionCountProvider(() => wsBridge.getConnectionCount());
4600
4587
  let cliCapabilities = null;
4601
4588
  parseCliCapabilities().then((caps) => {
4602
4589
  cliCapabilities = caps;
@@ -4643,7 +4630,9 @@ async function start(opts = {}) {
4643
4630
  event.permissionMode,
4644
4631
  event.effort,
4645
4632
  event.images,
4646
- event.agentType
4633
+ event.agentType,
4634
+ event.fallbackModel,
4635
+ event.maxBudgetUsd
4647
4636
  );
4648
4637
  wsBridge.broadcast({
4649
4638
  type: "session_list",
@@ -4961,8 +4950,7 @@ async function start(opts = {}) {
4961
4950
  terminalExecutor.onEvent((event) => {
4962
4951
  wsBridge.broadcast(event);
4963
4952
  });
4964
- wsBridge.onDisconnect((ws) => {
4965
- notificationService.removeTokensByWs(ws);
4953
+ wsBridge.onDisconnect(() => {
4966
4954
  if (wsBridge.getConnectionCount() === 0 && approvalProxy.getPendingCount() > 0) {
4967
4955
  approvalProxy.approveAll(t("server.phoneDisconnected"));
4968
4956
  }
package/dist/server.js CHANGED
@@ -408,7 +408,7 @@ var ProcessProvider = class {
408
408
  * 并开始监听 stdout 的 NDJSON 输出。
409
409
  */
410
410
  async startSession(opts) {
411
- const { projectPath, message, sessionId: existingSessionId, model, permissionMode, effort, images } = opts;
411
+ const { projectPath, message, sessionId: existingSessionId, model, permissionMode, effort, images, fallbackModel, maxBudgetUsd } = opts;
412
412
  const sessionId = existingSessionId ?? (0, import_uuid.v4)();
413
413
  if (this.activeSessions.has(sessionId)) {
414
414
  await this.killSession(sessionId);
@@ -424,10 +424,10 @@ var ProcessProvider = class {
424
424
  summary: message.slice(0, 80)
425
425
  };
426
426
  const resume = opts.resume ?? !!existingSessionId;
427
- const proc = this.spawnClaudeProcess(sessionId, projectPath, resume, model, permissionMode, effort);
427
+ const proc = this.spawnClaudeProcess(sessionId, projectPath, resume, model, permissionMode, effort, fallbackModel, maxBudgetUsd);
428
428
  this.writeUserMessage(proc, message, sessionId, images);
429
429
  session.pid = proc.pid;
430
- this.activeSessions.set(sessionId, { session, process: proc, model, permissionMode, effort });
430
+ this.activeSessions.set(sessionId, { session, process: proc, model, permissionMode, effort, fallbackModel, maxBudgetUsd });
431
431
  proc.on("error", (err) => {
432
432
  console.error(`[ProcessProvider] Session ${sessionId} process error:`, err.message);
433
433
  this.activeSessions.delete(sessionId);
@@ -499,7 +499,7 @@ var ProcessProvider = class {
499
499
  }
500
500
  const savedPendingQuestion = entry.pendingQuestion;
501
501
  const newMode = permissionMode ?? entry.permissionMode;
502
- const proc = this.spawnClaudeProcess(sessionId, entry.session.projectPath, true, entry.model, newMode, entry.effort);
502
+ const proc = this.spawnClaudeProcess(sessionId, entry.session.projectPath, true, entry.model, newMode, entry.effort, entry.fallbackModel, entry.maxBudgetUsd);
503
503
  this.writeUserMessage(proc, message, sessionId, images);
504
504
  entry.session.status = "running";
505
505
  entry.session.lastActiveAt = Date.now();
@@ -549,14 +549,15 @@ var ProcessProvider = class {
549
549
  /**
550
550
  * 启动 claude CLI 进程(持久模式,stdin 保持开放接收多条消息)
551
551
  */
552
- spawnClaudeProcess(sessionId, projectPath, resume = false, model, permissionMode, effort) {
552
+ spawnClaudeProcess(sessionId, projectPath, resume = false, model, permissionMode, effort, fallbackModel, maxBudgetUsd) {
553
553
  const args = [
554
554
  "--input-format",
555
555
  "stream-json",
556
556
  "--output-format",
557
557
  "stream-json",
558
558
  "--verbose",
559
- "--include-partial-messages"
559
+ "--include-partial-messages",
560
+ "--include-hook-events"
560
561
  ];
561
562
  if (resume) {
562
563
  args.push("--resume", sessionId);
@@ -572,6 +573,12 @@ var ProcessProvider = class {
572
573
  if (effort) {
573
574
  args.push("--effort", effort);
574
575
  }
576
+ if (fallbackModel) {
577
+ args.push("--fallback-model", fallbackModel);
578
+ }
579
+ if (maxBudgetUsd != null) {
580
+ args.push("--max-budget-usd", String(maxBudgetUsd));
581
+ }
575
582
  const env = { ...process.env, SESSIX_SESSION_ID: sessionId };
576
583
  delete env.CLAUDECODE;
577
584
  const proc = (0, import_child_process.spawn)(CLAUDE_PATH, args, {
@@ -1682,7 +1689,7 @@ var SessionManager = class {
1682
1689
  * 调用 provider.startSession(),订阅事件流,
1683
1690
  * 将 ClaudeStreamEvent 包装为 ServerEvent 转发。
1684
1691
  */
1685
- async createSession(projectPath, message, resumeSessionId, newSessionId, model, permissionMode, effort, images, agentType) {
1692
+ async createSession(projectPath, message, resumeSessionId, newSessionId, model, permissionMode, effort, images, agentType, fallbackModel, maxBudgetUsd) {
1686
1693
  let resolvedAgentType = agentType ?? "claude-code";
1687
1694
  if (!agentType && resumeSessionId && this.providerFactory) {
1688
1695
  const codexProvider = this.providerFactory.getProvider("codex");
@@ -1699,7 +1706,9 @@ var SessionManager = class {
1699
1706
  model,
1700
1707
  permissionMode,
1701
1708
  effort,
1702
- images
1709
+ images,
1710
+ fallbackModel,
1711
+ maxBudgetUsd
1703
1712
  });
1704
1713
  this.sessionAgentType.set(session.id, resolvedAgentType);
1705
1714
  this.lastBroadcastStatus.set(session.id, session.status);
@@ -2340,7 +2349,7 @@ var WsBridge = class _WsBridge {
2340
2349
  onConnection(callback) {
2341
2350
  this.connectionCallbacks.push(callback);
2342
2351
  }
2343
- /** 注册断开连接回调(任意客户端断开时触发,传入断开的 ws 对象) */
2352
+ /** 注册断开连接回调(任意客户端断开时触发,可通过 getConnectionCount() 判断是否全部断开) */
2344
2353
  onDisconnect(callback) {
2345
2354
  this.disconnectCallbacks.push(callback);
2346
2355
  }
@@ -2454,7 +2463,7 @@ var WsBridge = class _WsBridge {
2454
2463
  console.log(`[WsBridge] Client disconnected, connections: ${this.getConnectionCount()}`);
2455
2464
  for (const cb of this.disconnectCallbacks) {
2456
2465
  try {
2457
- cb(ws);
2466
+ cb();
2458
2467
  } catch (err) {
2459
2468
  console.error("[WsBridge] Disconnect callback error:", err);
2460
2469
  }
@@ -3280,8 +3289,6 @@ var NotificationService = class {
3280
3289
  latestAssistantText = /* @__PURE__ */ new Map();
3281
3290
  /** 获取全局待审批总数的回调(跨所有会话) */
3282
3291
  globalPendingCountProvider = null;
3283
- /** 获取当前 WS 连接数的回调(用于在线抑制推送) */
3284
- connectionCountProvider = null;
3285
3292
  /** 添加通知渠道(id 唯一,可用于后续动态开关) */
3286
3293
  addChannel(id, channel, enabled = true) {
3287
3294
  this.channelMap.set(id, { channel, enabled });
@@ -3319,14 +3326,6 @@ var NotificationService = class {
3319
3326
  setGlobalPendingCountProvider(provider) {
3320
3327
  this.globalPendingCountProvider = provider;
3321
3328
  }
3322
- /** 设置 WS 连接数提供者(用于 status_change 通知的在线抑制) */
3323
- setConnectionCountProvider(provider) {
3324
- this.connectionCountProvider = provider;
3325
- }
3326
- /** 移除指定 WebSocket 关联的所有 push token */
3327
- removeTokensByWs(ws) {
3328
- this.expoChannel?.removeTokensByWs(ws);
3329
- }
3330
3329
  /** 获取全局待审批总数 */
3331
3330
  getGlobalPendingCount() {
3332
3331
  return this.globalPendingCountProvider?.() ?? 0;
@@ -3469,7 +3468,7 @@ var NotificationService = class {
3469
3468
  isYoloMode,
3470
3469
  updatedAt: Date.now()
3471
3470
  });
3472
- } else if ((this.connectionCountProvider?.() ?? 0) === 0) {
3471
+ } else {
3473
3472
  this.notify({
3474
3473
  title: sessionTitle,
3475
3474
  body,
@@ -3491,7 +3490,7 @@ var NotificationService = class {
3491
3490
  isYoloMode,
3492
3491
  updatedAt: Date.now()
3493
3492
  });
3494
- } else if ((this.connectionCountProvider?.() ?? 0) === 0) {
3493
+ } else {
3495
3494
  this.notify({
3496
3495
  title: sessionTitle,
3497
3496
  body,
@@ -3579,17 +3578,6 @@ var ExpoNotificationChannel = class {
3579
3578
  this.soundPreferences.delete(token);
3580
3579
  console.log(`[ExpoNotificationChannel] ${t("notification.tokenRemoved", { count: this.tokens.size })}`);
3581
3580
  }
3582
- /** 移除指定 WebSocket 关联的所有 token(设备断开时调用) */
3583
- removeTokensByWs(ws) {
3584
- for (const [token, tokenWs] of this.tokenWsMap) {
3585
- if (tokenWs === ws) {
3586
- this.tokens.delete(token);
3587
- this.tokenWsMap.delete(token);
3588
- this.soundPreferences.delete(token);
3589
- console.log(`[ExpoNotificationChannel] Token removed on WS disconnect, remaining: ${this.tokens.size}`);
3590
- }
3591
- }
3592
- }
3593
3581
  /** 更新某个 token 的音效偏好 */
3594
3582
  setSoundPreferences(prefs) {
3595
3583
  for (const token of this.tokens) {
@@ -4439,7 +4427,7 @@ var TerminalExecutor = class {
4439
4427
  // src/utils/cliCapabilities.ts
4440
4428
  var import_node_child_process8 = require("child_process");
4441
4429
  var DEFAULT_CAPABILITIES = {
4442
- effortLevels: ["low", "medium", "high", "max"]
4430
+ effortLevels: ["low", "medium", "high", "xhigh", "max"]
4443
4431
  };
4444
4432
  async function parseCliCapabilities() {
4445
4433
  const claudePath = findClaudePath();
@@ -4601,7 +4589,6 @@ async function start(opts = {}) {
4601
4589
  notificationService.setGlobalPendingCountProvider(
4602
4590
  () => approvalProxy.getPendingCount() + sessionManager.getAllPendingQuestions().length + unreadSessionIds.size
4603
4591
  );
4604
- notificationService.setConnectionCountProvider(() => wsBridge.getConnectionCount());
4605
4592
  let cliCapabilities = null;
4606
4593
  parseCliCapabilities().then((caps) => {
4607
4594
  cliCapabilities = caps;
@@ -4648,7 +4635,9 @@ async function start(opts = {}) {
4648
4635
  event.permissionMode,
4649
4636
  event.effort,
4650
4637
  event.images,
4651
- event.agentType
4638
+ event.agentType,
4639
+ event.fallbackModel,
4640
+ event.maxBudgetUsd
4652
4641
  );
4653
4642
  wsBridge.broadcast({
4654
4643
  type: "session_list",
@@ -4966,8 +4955,7 @@ async function start(opts = {}) {
4966
4955
  terminalExecutor.onEvent((event) => {
4967
4956
  wsBridge.broadcast(event);
4968
4957
  });
4969
- wsBridge.onDisconnect((ws) => {
4970
- notificationService.removeTokensByWs(ws);
4958
+ wsBridge.onDisconnect(() => {
4971
4959
  if (wsBridge.getConnectionCount() === 0 && approvalProxy.getPendingCount() > 0) {
4972
4960
  approvalProxy.approveAll(t("server.phoneDisconnected"));
4973
4961
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sessix-server",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "bin": {
5
5
  "sessix-server": "dist/index.js"
6
6
  },