opencode-copilot-account-switcher 0.13.5 → 0.14.0

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.
Files changed (54) hide show
  1. package/dist/common-settings-actions.d.ts +1 -1
  2. package/dist/common-settings-actions.js +36 -0
  3. package/dist/common-settings-store.d.ts +4 -0
  4. package/dist/common-settings-store.js +32 -9
  5. package/dist/menu-runtime.js +12 -1
  6. package/dist/plugin-hooks.d.ts +8 -0
  7. package/dist/plugin-hooks.js +96 -0
  8. package/dist/providers/codex-menu-adapter.js +8 -1
  9. package/dist/providers/copilot-menu-adapter.js +7 -0
  10. package/dist/store-paths.d.ts +1 -0
  11. package/dist/store-paths.js +3 -0
  12. package/dist/ui/menu.d.ts +33 -0
  13. package/dist/ui/menu.js +85 -0
  14. package/dist/wechat/bridge.d.ts +69 -0
  15. package/dist/wechat/bridge.js +180 -0
  16. package/dist/wechat/broker-client.d.ts +33 -0
  17. package/dist/wechat/broker-client.js +257 -0
  18. package/dist/wechat/broker-entry.d.ts +17 -0
  19. package/dist/wechat/broker-entry.js +182 -0
  20. package/dist/wechat/broker-launcher.d.ts +27 -0
  21. package/dist/wechat/broker-launcher.js +191 -0
  22. package/dist/wechat/broker-server.d.ts +25 -0
  23. package/dist/wechat/broker-server.js +540 -0
  24. package/dist/wechat/command-parser.d.ts +7 -0
  25. package/dist/wechat/command-parser.js +16 -0
  26. package/dist/wechat/compat/openclaw-guided-smoke.d.ts +178 -0
  27. package/dist/wechat/compat/openclaw-guided-smoke.js +1133 -0
  28. package/dist/wechat/compat/openclaw-public-helpers.d.ts +101 -0
  29. package/dist/wechat/compat/openclaw-public-helpers.js +207 -0
  30. package/dist/wechat/compat/openclaw-smoke.d.ts +48 -0
  31. package/dist/wechat/compat/openclaw-smoke.js +100 -0
  32. package/dist/wechat/compat/slash-guard.d.ts +11 -0
  33. package/dist/wechat/compat/slash-guard.js +24 -0
  34. package/dist/wechat/handle.d.ts +8 -0
  35. package/dist/wechat/handle.js +46 -0
  36. package/dist/wechat/ipc-auth.d.ts +6 -0
  37. package/dist/wechat/ipc-auth.js +39 -0
  38. package/dist/wechat/operator-store.d.ts +8 -0
  39. package/dist/wechat/operator-store.js +59 -0
  40. package/dist/wechat/protocol.d.ts +29 -0
  41. package/dist/wechat/protocol.js +75 -0
  42. package/dist/wechat/request-store.d.ts +41 -0
  43. package/dist/wechat/request-store.js +215 -0
  44. package/dist/wechat/session-digest.d.ts +41 -0
  45. package/dist/wechat/session-digest.js +134 -0
  46. package/dist/wechat/state-paths.d.ts +14 -0
  47. package/dist/wechat/state-paths.js +45 -0
  48. package/dist/wechat/status-format.d.ts +14 -0
  49. package/dist/wechat/status-format.js +174 -0
  50. package/dist/wechat/token-store.d.ts +18 -0
  51. package/dist/wechat/token-store.js +100 -0
  52. package/dist/wechat/wechat-status-runtime.d.ts +24 -0
  53. package/dist/wechat/wechat-status-runtime.js +238 -0
  54. package/package.json +8 -3
@@ -1,5 +1,5 @@
1
1
  import type { CommonSettingsStore } from "./common-settings-store.js";
2
- export type CommonSettingsActionType = "toggle-loop-safety" | "toggle-loop-safety-provider-scope" | "toggle-experimental-slash-commands" | "toggle-network-retry";
2
+ export type CommonSettingsActionType = "toggle-loop-safety" | "toggle-loop-safety-provider-scope" | "toggle-experimental-slash-commands" | "toggle-network-retry" | "toggle-wechat-notifications" | "toggle-wechat-question-notify" | "toggle-wechat-permission-notify" | "toggle-wechat-session-error-notify";
3
3
  type WriteMeta = {
4
4
  reason?: string;
5
5
  source?: string;
@@ -38,5 +38,41 @@ export async function applyCommonSettingsAction(input) {
38
38
  });
39
39
  return true;
40
40
  }
41
+ if (input.action.type === "toggle-wechat-notifications") {
42
+ settings.wechatNotificationsEnabled = settings.wechatNotificationsEnabled !== true;
43
+ await input.writeSettings(settings, {
44
+ reason: "toggle-wechat-notifications",
45
+ source: "applyCommonSettingsAction",
46
+ actionType: "toggle-wechat-notifications",
47
+ });
48
+ return true;
49
+ }
50
+ if (input.action.type === "toggle-wechat-question-notify") {
51
+ settings.wechatQuestionNotifyEnabled = settings.wechatQuestionNotifyEnabled !== true;
52
+ await input.writeSettings(settings, {
53
+ reason: "toggle-wechat-question-notify",
54
+ source: "applyCommonSettingsAction",
55
+ actionType: "toggle-wechat-question-notify",
56
+ });
57
+ return true;
58
+ }
59
+ if (input.action.type === "toggle-wechat-permission-notify") {
60
+ settings.wechatPermissionNotifyEnabled = settings.wechatPermissionNotifyEnabled !== true;
61
+ await input.writeSettings(settings, {
62
+ reason: "toggle-wechat-permission-notify",
63
+ source: "applyCommonSettingsAction",
64
+ actionType: "toggle-wechat-permission-notify",
65
+ });
66
+ return true;
67
+ }
68
+ if (input.action.type === "toggle-wechat-session-error-notify") {
69
+ settings.wechatSessionErrorNotifyEnabled = settings.wechatSessionErrorNotifyEnabled !== true;
70
+ await input.writeSettings(settings, {
71
+ reason: "toggle-wechat-session-error-notify",
72
+ source: "applyCommonSettingsAction",
73
+ actionType: "toggle-wechat-session-error-notify",
74
+ });
75
+ return true;
76
+ }
41
77
  return false;
42
78
  }
@@ -4,6 +4,10 @@ export type CommonSettingsStore = {
4
4
  networkRetryEnabled?: boolean;
5
5
  experimentalSlashCommandsEnabled?: boolean;
6
6
  experimentalStatusSlashCommandEnabled?: boolean;
7
+ wechatNotificationsEnabled?: boolean;
8
+ wechatQuestionNotifyEnabled?: boolean;
9
+ wechatPermissionNotifyEnabled?: boolean;
10
+ wechatSessionErrorNotifyEnabled?: boolean;
7
11
  };
8
12
  export declare function parseCommonSettingsStore(raw: string): CommonSettingsStore;
9
13
  export declare function commonSettingsPath(): string;
@@ -15,6 +15,12 @@ function normalizeCommonSettingsStore(input) {
15
15
  : legacySlashCommandsEnabled === false
16
16
  ? false
17
17
  : true,
18
+ ...(source.wechatNotificationsEnabled === false ? { wechatNotificationsEnabled: false } : { wechatNotificationsEnabled: true }),
19
+ ...(source.wechatQuestionNotifyEnabled === false ? { wechatQuestionNotifyEnabled: false } : { wechatQuestionNotifyEnabled: true }),
20
+ ...(source.wechatPermissionNotifyEnabled === false ? { wechatPermissionNotifyEnabled: false } : { wechatPermissionNotifyEnabled: true }),
21
+ ...(source.wechatSessionErrorNotifyEnabled === false
22
+ ? { wechatSessionErrorNotifyEnabled: false }
23
+ : { wechatSessionErrorNotifyEnabled: true }),
18
24
  };
19
25
  }
20
26
  function parsePartialCommonSettingsStore(raw) {
@@ -35,6 +41,18 @@ function parsePartialCommonSettingsStore(raw) {
35
41
  if (parsed.experimentalStatusSlashCommandEnabled === true || parsed.experimentalStatusSlashCommandEnabled === false) {
36
42
  partial.experimentalStatusSlashCommandEnabled = parsed.experimentalStatusSlashCommandEnabled;
37
43
  }
44
+ if (parsed.wechatNotificationsEnabled === true || parsed.wechatNotificationsEnabled === false) {
45
+ partial.wechatNotificationsEnabled = parsed.wechatNotificationsEnabled;
46
+ }
47
+ if (parsed.wechatQuestionNotifyEnabled === true || parsed.wechatQuestionNotifyEnabled === false) {
48
+ partial.wechatQuestionNotifyEnabled = parsed.wechatQuestionNotifyEnabled;
49
+ }
50
+ if (parsed.wechatPermissionNotifyEnabled === true || parsed.wechatPermissionNotifyEnabled === false) {
51
+ partial.wechatPermissionNotifyEnabled = parsed.wechatPermissionNotifyEnabled;
52
+ }
53
+ if (parsed.wechatSessionErrorNotifyEnabled === true || parsed.wechatSessionErrorNotifyEnabled === false) {
54
+ partial.wechatSessionErrorNotifyEnabled = parsed.wechatSessionErrorNotifyEnabled;
55
+ }
38
56
  return partial;
39
57
  }
40
58
  function readLegacyCommonSettings(store) {
@@ -55,6 +73,10 @@ function mergeCommonSettings(current, legacy) {
55
73
  networkRetryEnabled: current.networkRetryEnabled ?? legacy.networkRetryEnabled,
56
74
  experimentalSlashCommandsEnabled: current.experimentalSlashCommandsEnabled ?? legacy.experimentalSlashCommandsEnabled,
57
75
  experimentalStatusSlashCommandEnabled: current.experimentalStatusSlashCommandEnabled ?? legacy.experimentalStatusSlashCommandEnabled,
76
+ wechatNotificationsEnabled: current.wechatNotificationsEnabled ?? legacy.wechatNotificationsEnabled,
77
+ wechatQuestionNotifyEnabled: current.wechatQuestionNotifyEnabled ?? legacy.wechatQuestionNotifyEnabled,
78
+ wechatPermissionNotifyEnabled: current.wechatPermissionNotifyEnabled ?? legacy.wechatPermissionNotifyEnabled,
79
+ wechatSessionErrorNotifyEnabled: current.wechatSessionErrorNotifyEnabled ?? legacy.wechatSessionErrorNotifyEnabled,
58
80
  });
59
81
  }
60
82
  export function parseCommonSettingsStore(raw) {
@@ -111,15 +133,16 @@ export function readCommonSettingsStoreSync(options) {
111
133
  export async function writeCommonSettingsStore(store, options) {
112
134
  const file = options?.filePath ?? commonSettingsPath();
113
135
  const normalized = normalizeCommonSettingsStore(store);
114
- const persisted = {};
115
- if (normalized.loopSafetyEnabled === false)
116
- persisted.loopSafetyEnabled = false;
117
- if (normalized.loopSafetyProviderScope === "all-models")
118
- persisted.loopSafetyProviderScope = "all-models";
119
- if (normalized.networkRetryEnabled === true)
120
- persisted.networkRetryEnabled = true;
121
- if (normalized.experimentalSlashCommandsEnabled === false)
122
- persisted.experimentalSlashCommandsEnabled = false;
136
+ const persisted = {
137
+ loopSafetyEnabled: normalized.loopSafetyEnabled,
138
+ loopSafetyProviderScope: normalized.loopSafetyProviderScope,
139
+ networkRetryEnabled: normalized.networkRetryEnabled,
140
+ experimentalSlashCommandsEnabled: normalized.experimentalSlashCommandsEnabled,
141
+ wechatNotificationsEnabled: normalized.wechatNotificationsEnabled,
142
+ wechatQuestionNotifyEnabled: normalized.wechatQuestionNotifyEnabled,
143
+ wechatPermissionNotifyEnabled: normalized.wechatPermissionNotifyEnabled,
144
+ wechatSessionErrorNotifyEnabled: normalized.wechatSessionErrorNotifyEnabled,
145
+ };
123
146
  await fs.mkdir(path.dirname(file), { recursive: true });
124
147
  await fs.writeFile(file, JSON.stringify(persisted, null, 2), { mode: 0o600 });
125
148
  }
@@ -10,6 +10,15 @@ function parseSharedActionResult(result) {
10
10
  persistHandled: false,
11
11
  };
12
12
  }
13
+ function providerActionReason(name) {
14
+ if (name.startsWith("wechat-")) {
15
+ return `wechat-action:${name}`;
16
+ }
17
+ return `provider-action:${name}`;
18
+ }
19
+ function isNonPersistentProviderAction(name) {
20
+ return name === "wechat-bind";
21
+ }
13
22
  export async function runProviderMenu(input) {
14
23
  const now = input.now ?? Date.now;
15
24
  const store = await input.adapter.loadStore();
@@ -99,8 +108,10 @@ export async function runProviderMenu(input) {
99
108
  continue;
100
109
  if (!await input.adapter.applyAction(store, action))
101
110
  continue;
111
+ if (isNonPersistentProviderAction(action.name))
112
+ continue;
102
113
  await input.adapter.writeStore(store, {
103
- reason: `provider-action:${action.name}`,
114
+ reason: providerActionReason(action.name),
104
115
  source: "menu-runtime",
105
116
  actionType: action.name,
106
117
  });
@@ -9,6 +9,7 @@ import { handleStatusCommand } from "./status-command.js";
9
9
  import { handleCodexStatusCommand } from "./codex-status-command.js";
10
10
  import { handleCompactCommand, handleStopToolCommand } from "./session-control-command.js";
11
11
  import { type AppendSessionTouchEventInput, type RouteDecisionEvent, type RoutingSnapshot, type RoutingEvent } from "./routing-state.js";
12
+ import { type WechatBridgeLifecycleInput } from "./wechat/bridge.js";
12
13
  type ChatHeadersHook = (input: {
13
14
  sessionID: string;
14
15
  agent: string;
@@ -81,8 +82,15 @@ export declare function buildPluginHooks(input: {
81
82
  }) => Promise<OfficialChatHeadersHook>;
82
83
  createRetryFetch?: (fetch: FetchLike, ctx?: CopilotRetryContext) => FetchLike;
83
84
  client?: CopilotRetryContext["client"];
85
+ project?: {
86
+ id?: string;
87
+ name?: string;
88
+ };
84
89
  directory?: CopilotRetryContext["directory"];
85
90
  serverUrl?: CopilotRetryContext["serverUrl"];
91
+ createWechatBridgeLifecycleImpl?: (input: WechatBridgeLifecycleInput) => Promise<{
92
+ close: () => Promise<void>;
93
+ }>;
86
94
  clearAccountSwitchContext?: (lastAccountSwitchAt?: number) => Promise<void>;
87
95
  now?: () => number;
88
96
  refreshQuota?: RefreshQuota;
@@ -19,6 +19,7 @@ import { handleStatusCommand, showStatusToast } from "./status-command.js";
19
19
  import { handleCodexStatusCommand } from "./codex-status-command.js";
20
20
  import { handleCompactCommand, handleStopToolCommand, } from "./session-control-command.js";
21
21
  import { appendRoutingEvent, appendRouteDecisionEvent, appendSessionTouchEvent, buildCandidateAccountLoads, isAccountRateLimitCooledDown, readRoutingState, routingStatePath, } from "./routing-state.js";
22
+ import { createWechatBridgeLifecycle, } from "./wechat/bridge.js";
22
23
  const SESSION_BINDING_IDLE_TTL_MS = 30 * 60 * 1000;
23
24
  const RATE_LIMIT_WINDOW_MS = 5 * 60 * 1000;
24
25
  const RATE_LIMIT_HIT_THRESHOLD = 3;
@@ -27,6 +28,70 @@ const MAX_SESSION_BINDINGS = 256;
27
28
  const TOUCH_WRITE_CACHE_IDLE_TTL_MS = 30 * 60 * 1000;
28
29
  const MAX_TOUCH_WRITE_CACHE_ENTRIES = 2048;
29
30
  const INTERNAL_DEBUG_LINK_HEADER = "x-opencode-debug-link-id";
31
+ let wechatBridgeLifecycleState;
32
+ let wechatBridgeAutoCloseAttached = false;
33
+ function buildWechatBridgeLifecycleKey(input) {
34
+ const projectName = typeof input.project?.name === "string" ? input.project.name : "";
35
+ const projectId = typeof input.project?.id === "string" ? input.project.id : "";
36
+ const directory = typeof input.directory === "string" ? input.directory : "";
37
+ const serverUrl = input.serverUrl?.href ?? "";
38
+ return `${serverUrl}|${directory}|${projectName}|${projectId}`;
39
+ }
40
+ function attachWechatBridgeAutoClose() {
41
+ if (wechatBridgeAutoCloseAttached) {
42
+ return;
43
+ }
44
+ wechatBridgeAutoCloseAttached = true;
45
+ const closeLifecycle = () => {
46
+ const state = wechatBridgeLifecycleState;
47
+ if (!state)
48
+ return;
49
+ closeWechatBridgeLifecycleState(state);
50
+ };
51
+ process.once("beforeExit", closeLifecycle);
52
+ process.once("SIGINT", closeLifecycle);
53
+ process.once("SIGTERM", closeLifecycle);
54
+ }
55
+ function closeWechatBridgeLifecycleState(state) {
56
+ if (state.closeRequested) {
57
+ return;
58
+ }
59
+ state.closeRequested = true;
60
+ void state.promise
61
+ .then((lifecycle) => lifecycle.close().catch(() => { }))
62
+ .catch(() => { });
63
+ }
64
+ function ensureWechatBridgeLifecycle(input) {
65
+ if (wechatBridgeLifecycleState?.key === input.key) {
66
+ return wechatBridgeLifecycleState.promise;
67
+ }
68
+ const previous = wechatBridgeLifecycleState;
69
+ const promise = input.create();
70
+ const state = {
71
+ key: input.key,
72
+ promise,
73
+ closeRequested: false,
74
+ };
75
+ wechatBridgeLifecycleState = state;
76
+ if (previous) {
77
+ closeWechatBridgeLifecycleState(previous);
78
+ }
79
+ void promise
80
+ .then((lifecycle) => {
81
+ if (wechatBridgeLifecycleState !== state) {
82
+ closeWechatBridgeLifecycleState(state);
83
+ return;
84
+ }
85
+ state.lifecycle = lifecycle;
86
+ })
87
+ .catch((error) => {
88
+ if (wechatBridgeLifecycleState === state) {
89
+ wechatBridgeLifecycleState = undefined;
90
+ }
91
+ console.warn("[plugin-hooks] failed to initialize wechat bridge lifecycle", error);
92
+ });
93
+ return promise;
94
+ }
30
95
  export class InjectCommandHandledError extends Error {
31
96
  constructor() {
32
97
  super("copilot-inject-handled");
@@ -249,6 +314,17 @@ function getFinalSentRequestHeadersRecord(request, init) {
249
314
  headers.delete(INTERNAL_DEBUG_LINK_HEADER);
250
315
  return sanitizeLoggedRequestHeadersRecord(Object.fromEntries(headers.entries()));
251
316
  }
317
+ function hasWechatBridgeClientShape(value) {
318
+ if (typeof value !== "object" || value === null)
319
+ return false;
320
+ const client = value;
321
+ return typeof client.session?.list === "function"
322
+ && typeof client.session?.status === "function"
323
+ && typeof client.session?.todo === "function"
324
+ && typeof client.session?.messages === "function"
325
+ && typeof client.question?.list === "function"
326
+ && typeof client.permission?.list === "function";
327
+ }
252
328
  function sanitizeLoggedRequestHeadersRecord(headers) {
253
329
  const sanitized = { ...headers };
254
330
  if (typeof sanitized.authorization === "string" && sanitized.authorization.length > 0) {
@@ -507,6 +583,26 @@ export function buildPluginHooks(input) {
507
583
  const appendRouteDecisionEventImpl = input.appendRouteDecisionEventImpl ?? appendRouteDecisionEvent;
508
584
  const readRoutingStateImpl = input.readRoutingStateImpl ?? readRoutingState;
509
585
  const triggerBillingCompensation = input.triggerBillingCompensation ?? (async () => { });
586
+ const createWechatBridgeLifecycleImpl = input.createWechatBridgeLifecycleImpl ?? createWechatBridgeLifecycle;
587
+ if (input.serverUrl && hasWechatBridgeClientShape(input.client)) {
588
+ const wechatBridgeClient = input.client;
589
+ const lifecycleKey = buildWechatBridgeLifecycleKey({
590
+ directory: input.directory,
591
+ serverUrl: input.serverUrl,
592
+ project: input.project,
593
+ });
594
+ attachWechatBridgeAutoClose();
595
+ void ensureWechatBridgeLifecycle({
596
+ key: lifecycleKey,
597
+ create: () => createWechatBridgeLifecycleImpl({
598
+ client: wechatBridgeClient,
599
+ project: input.project,
600
+ directory: input.directory,
601
+ serverUrl: input.serverUrl,
602
+ statusCollectionEnabled: true,
603
+ }),
604
+ }).catch(() => { });
605
+ }
510
606
  const loadCandidateAccountLoads = input.loadCandidateAccountLoads ?? (async (ctx) => {
511
607
  const snapshot = await readRoutingStateImpl(routingDirectory);
512
608
  return buildCandidateAccountLoads({
@@ -370,7 +370,11 @@ export function createCodexMenuAdapter(inputDeps) {
370
370
  if (action.name === "toggle-loop-safety"
371
371
  || action.name === "toggle-loop-safety-provider-scope"
372
372
  || action.name === "toggle-experimental-slash-commands"
373
- || action.name === "toggle-network-retry") {
373
+ || action.name === "toggle-network-retry"
374
+ || action.name === "toggle-wechat-notifications"
375
+ || action.name === "toggle-wechat-question-notify"
376
+ || action.name === "toggle-wechat-permission-notify"
377
+ || action.name === "toggle-wechat-session-error-notify") {
374
378
  await applyCommonSettingsAction({
375
379
  action: { type: action.name },
376
380
  readSettings: readCommonSettings,
@@ -378,6 +382,9 @@ export function createCodexMenuAdapter(inputDeps) {
378
382
  });
379
383
  return false;
380
384
  }
385
+ if (action.name === "wechat-bind") {
386
+ return true;
387
+ }
381
388
  return false;
382
389
  },
383
390
  };
@@ -726,6 +726,10 @@ export function createCopilotMenuAdapter(inputDeps) {
726
726
  || action.name === "toggle-loop-safety-provider-scope"
727
727
  || action.name === "toggle-experimental-slash-commands"
728
728
  || action.name === "toggle-network-retry"
729
+ || action.name === "toggle-wechat-notifications"
730
+ || action.name === "toggle-wechat-question-notify"
731
+ || action.name === "toggle-wechat-permission-notify"
732
+ || action.name === "toggle-wechat-session-error-notify"
729
733
  || action.name === "toggle-synthetic-agent-initiator") {
730
734
  await applyMenuAction({
731
735
  action: { type: action.name },
@@ -736,6 +740,9 @@ export function createCopilotMenuAdapter(inputDeps) {
736
740
  });
737
741
  return false;
738
742
  }
743
+ if (action.name === "wechat-bind") {
744
+ return true;
745
+ }
739
746
  if (action.name === "list-models") {
740
747
  const modelID = await select([
741
748
  { label: "Back", value: "" },
@@ -2,5 +2,6 @@ export declare function accountSwitcherConfigDir(): string;
2
2
  export declare function commonSettingsPath(): string;
3
3
  export declare function copilotAccountsPath(): string;
4
4
  export declare function codexAccountsPath(): string;
5
+ export declare function wechatConfigDir(): string;
5
6
  export declare function legacyCopilotStorePath(): string;
6
7
  export declare function legacyCodexStorePath(): string;
@@ -16,6 +16,9 @@ export function copilotAccountsPath() {
16
16
  export function codexAccountsPath() {
17
17
  return path.join(accountSwitcherConfigDir(), "codex-accounts.json");
18
18
  }
19
+ export function wechatConfigDir() {
20
+ return path.join(accountSwitcherConfigDir(), "wechat");
21
+ }
19
22
  export function legacyCopilotStorePath() {
20
23
  return path.join(configBaseDir(), "opencode", "copilot-accounts.json");
21
24
  }
package/dist/ui/menu.d.ts CHANGED
@@ -69,6 +69,16 @@ export type MenuAction = {
69
69
  type: "toggle-experimental-slash-commands";
70
70
  } | {
71
71
  type: "toggle-network-retry";
72
+ } | {
73
+ type: "wechat-bind";
74
+ } | {
75
+ type: "toggle-wechat-notifications";
76
+ } | {
77
+ type: "toggle-wechat-question-notify";
78
+ } | {
79
+ type: "toggle-wechat-permission-notify";
80
+ } | {
81
+ type: "toggle-wechat-session-error-notify";
72
82
  } | {
73
83
  type: "toggle-synthetic-agent-initiator";
74
84
  } | {
@@ -96,6 +106,7 @@ type MenuCapabilities = {
96
106
  experimentalSlashCommands: boolean;
97
107
  networkRetry: boolean;
98
108
  syntheticAgentInitiator: boolean;
109
+ wechatNotificationsMenu: boolean;
99
110
  };
100
111
  export declare function getMenuCopy(language?: MenuLanguage, provider?: MenuProvider): {
101
112
  menuTitle: string;
@@ -130,6 +141,16 @@ export declare function getMenuCopy(language?: MenuLanguage, provider?: MenuProv
130
141
  syntheticInitiatorOn: string;
131
142
  syntheticInitiatorOff: string;
132
143
  syntheticInitiatorHint: string;
144
+ wechatNotificationsHeading: string;
145
+ wechatBind: string;
146
+ wechatNotificationsOn: string;
147
+ wechatNotificationsOff: string;
148
+ wechatQuestionNotifyOn: string;
149
+ wechatQuestionNotifyOff: string;
150
+ wechatPermissionNotifyOn: string;
151
+ wechatPermissionNotifyOff: string;
152
+ wechatSessionErrorNotifyOn: string;
153
+ wechatSessionErrorNotifyOff: string;
133
154
  accountsHeading: string;
134
155
  dangerHeading: string;
135
156
  removeAll: string;
@@ -147,6 +168,10 @@ export declare function buildMenuItems(input: {
147
168
  loopSafetyEnabled: boolean;
148
169
  loopSafetyProviderScope?: "copilot-only" | "all-models";
149
170
  networkRetryEnabled: boolean;
171
+ wechatNotificationsEnabled?: boolean;
172
+ wechatQuestionNotifyEnabled?: boolean;
173
+ wechatPermissionNotifyEnabled?: boolean;
174
+ wechatSessionErrorNotifyEnabled?: boolean;
150
175
  syntheticAgentInitiatorEnabled?: boolean;
151
176
  experimentalSlashCommandsEnabled?: boolean;
152
177
  capabilities?: Partial<MenuCapabilities>;
@@ -164,6 +189,10 @@ export declare function showMenu(accounts: AccountInfo[], input?: {
164
189
  loopSafetyEnabled?: boolean;
165
190
  loopSafetyProviderScope?: "copilot-only" | "all-models";
166
191
  networkRetryEnabled?: boolean;
192
+ wechatNotificationsEnabled?: boolean;
193
+ wechatQuestionNotifyEnabled?: boolean;
194
+ wechatPermissionNotifyEnabled?: boolean;
195
+ wechatSessionErrorNotifyEnabled?: boolean;
167
196
  syntheticAgentInitiatorEnabled?: boolean;
168
197
  experimentalSlashCommandsEnabled?: boolean;
169
198
  capabilities?: Partial<MenuCapabilities>;
@@ -181,6 +210,10 @@ export declare function showMenuWithDeps(accounts: AccountInfo[], input?: {
181
210
  loopSafetyEnabled?: boolean;
182
211
  loopSafetyProviderScope?: "copilot-only" | "all-models";
183
212
  networkRetryEnabled?: boolean;
213
+ wechatNotificationsEnabled?: boolean;
214
+ wechatQuestionNotifyEnabled?: boolean;
215
+ wechatPermissionNotifyEnabled?: boolean;
216
+ wechatSessionErrorNotifyEnabled?: boolean;
184
217
  syntheticAgentInitiatorEnabled?: boolean;
185
218
  experimentalSlashCommandsEnabled?: boolean;
186
219
  capabilities?: Partial<MenuCapabilities>;
package/dist/ui/menu.js CHANGED
@@ -15,6 +15,7 @@ function defaultMenuCapabilities(provider) {
15
15
  experimentalSlashCommands: true,
16
16
  networkRetry: true,
17
17
  syntheticAgentInitiator: false,
18
+ wechatNotificationsMenu: true,
18
19
  };
19
20
  }
20
21
  return {
@@ -29,6 +30,7 @@ function defaultMenuCapabilities(provider) {
29
30
  experimentalSlashCommands: true,
30
31
  networkRetry: true,
31
32
  syntheticAgentInitiator: true,
33
+ wechatNotificationsMenu: true,
32
34
  };
33
35
  }
34
36
  export function getMenuCopy(language = "zh", provider = "copilot") {
@@ -67,6 +69,16 @@ export function getMenuCopy(language = "zh", provider = "copilot") {
67
69
  syntheticInitiatorOn: "Send synthetic messages as agent: On",
68
70
  syntheticInitiatorOff: "Send synthetic messages as agent: Off",
69
71
  syntheticInitiatorHint: "Changes upstream behavior; misuse may increase billing risk or trigger abuse signals",
72
+ wechatNotificationsHeading: "WeChat notifications",
73
+ wechatBind: "Bind / Rebind WeChat",
74
+ wechatNotificationsOn: "WeChat notifications: On",
75
+ wechatNotificationsOff: "WeChat notifications: Off",
76
+ wechatQuestionNotifyOn: "Question notifications: On",
77
+ wechatQuestionNotifyOff: "Question notifications: Off",
78
+ wechatPermissionNotifyOn: "Permission notifications: On",
79
+ wechatPermissionNotifyOff: "Permission notifications: Off",
80
+ wechatSessionErrorNotifyOn: "Session error notifications: On",
81
+ wechatSessionErrorNotifyOff: "Session error notifications: Off",
70
82
  accountsHeading: "Accounts",
71
83
  dangerHeading: "Danger zone",
72
84
  removeAll: "Remove all accounts",
@@ -105,6 +117,16 @@ export function getMenuCopy(language = "zh", provider = "copilot") {
105
117
  syntheticInitiatorOn: "synthetic 消息按 agent 身份发送:已开启",
106
118
  syntheticInitiatorOff: "synthetic 消息按 agent 身份发送:已关闭",
107
119
  syntheticInitiatorHint: "会改变与 upstream 的默认行为;误用可能带来异常计费或 abuse 风险",
120
+ wechatNotificationsHeading: "微信通知",
121
+ wechatBind: "绑定 / 重绑微信",
122
+ wechatNotificationsOn: "微信通知总开关:已开启",
123
+ wechatNotificationsOff: "微信通知总开关:已关闭",
124
+ wechatQuestionNotifyOn: "问题通知:已开启",
125
+ wechatQuestionNotifyOff: "问题通知:已关闭",
126
+ wechatPermissionNotifyOn: "权限通知:已开启",
127
+ wechatPermissionNotifyOff: "权限通知:已关闭",
128
+ wechatSessionErrorNotifyOn: "会话错误通知:已开启",
129
+ wechatSessionErrorNotifyOff: "会话错误通知:已关闭",
108
130
  accountsHeading: "账号",
109
131
  dangerHeading: "危险操作",
110
132
  removeAll: "删除全部账号",
@@ -144,6 +166,16 @@ export function getMenuCopy(language = "zh", provider = "copilot") {
144
166
  syntheticInitiatorOn: "Send synthetic messages as agent: On",
145
167
  syntheticInitiatorOff: "Send synthetic messages as agent: Off",
146
168
  syntheticInitiatorHint: "Changes upstream behavior; misuse may increase billing risk or trigger abuse signals",
169
+ wechatNotificationsHeading: "WeChat notifications",
170
+ wechatBind: "Bind / Rebind WeChat",
171
+ wechatNotificationsOn: "WeChat notifications: On",
172
+ wechatNotificationsOff: "WeChat notifications: Off",
173
+ wechatQuestionNotifyOn: "Question notifications: On",
174
+ wechatQuestionNotifyOff: "Question notifications: Off",
175
+ wechatPermissionNotifyOn: "Permission notifications: On",
176
+ wechatPermissionNotifyOff: "Permission notifications: Off",
177
+ wechatSessionErrorNotifyOn: "Session error notifications: On",
178
+ wechatSessionErrorNotifyOff: "Session error notifications: Off",
147
179
  accountsHeading: "Accounts",
148
180
  dangerHeading: "Danger zone",
149
181
  removeAll: "Remove all accounts",
@@ -182,6 +214,16 @@ export function getMenuCopy(language = "zh", provider = "copilot") {
182
214
  syntheticInitiatorOn: "synthetic 消息按 agent 身份发送:已开启",
183
215
  syntheticInitiatorOff: "synthetic 消息按 agent 身份发送:已关闭",
184
216
  syntheticInitiatorHint: "会改变与 upstream 的默认行为;误用可能带来异常计费或 abuse 风险",
217
+ wechatNotificationsHeading: "微信通知",
218
+ wechatBind: "绑定 / 重绑微信",
219
+ wechatNotificationsOn: "微信通知总开关:已开启",
220
+ wechatNotificationsOff: "微信通知总开关:已关闭",
221
+ wechatQuestionNotifyOn: "问题通知:已开启",
222
+ wechatQuestionNotifyOff: "问题通知:已关闭",
223
+ wechatPermissionNotifyOn: "权限通知:已开启",
224
+ wechatPermissionNotifyOff: "权限通知:已关闭",
225
+ wechatSessionErrorNotifyOn: "会话错误通知:已开启",
226
+ wechatSessionErrorNotifyOff: "会话错误通知:已关闭",
185
227
  accountsHeading: "账号",
186
228
  dangerHeading: "危险操作",
187
229
  removeAll: "删除全部账号",
@@ -228,6 +270,10 @@ export function buildMenuItems(input) {
228
270
  const quotaHint = input.lastQuotaRefresh ? `last ${formatRelativeTime(input.lastQuotaRefresh)}` : undefined;
229
271
  const loopSafetyProviderScope = input.loopSafetyProviderScope ?? "copilot-only";
230
272
  const experimentalSlashCommandsEnabled = input.experimentalSlashCommandsEnabled !== false;
273
+ const wechatNotificationsEnabled = input.wechatNotificationsEnabled !== false;
274
+ const wechatQuestionNotifyEnabled = input.wechatQuestionNotifyEnabled !== false;
275
+ const wechatPermissionNotifyEnabled = input.wechatPermissionNotifyEnabled !== false;
276
+ const wechatSessionErrorNotifyEnabled = input.wechatSessionErrorNotifyEnabled !== false;
231
277
  const providerActions = [
232
278
  { label: copy.actionsHeading, value: { type: "cancel" }, kind: "heading" },
233
279
  { label: copy.switchLanguageLabel, value: { type: "toggle-language" }, color: "cyan" },
@@ -310,6 +356,39 @@ export function buildMenuItems(input) {
310
356
  hint: copy.syntheticInitiatorHint,
311
357
  });
312
358
  }
359
+ const wechatNotifications = [
360
+ { label: copy.wechatNotificationsHeading, value: { type: "cancel" }, kind: "heading" },
361
+ {
362
+ label: copy.wechatBind,
363
+ value: { type: "wechat-bind" },
364
+ color: "cyan",
365
+ disabled: !capabilities.wechatNotificationsMenu,
366
+ },
367
+ {
368
+ label: wechatNotificationsEnabled ? copy.wechatNotificationsOn : copy.wechatNotificationsOff,
369
+ value: { type: "toggle-wechat-notifications" },
370
+ color: "cyan",
371
+ disabled: !capabilities.wechatNotificationsMenu,
372
+ },
373
+ {
374
+ label: wechatQuestionNotifyEnabled ? copy.wechatQuestionNotifyOn : copy.wechatQuestionNotifyOff,
375
+ value: { type: "toggle-wechat-question-notify" },
376
+ color: "cyan",
377
+ disabled: !capabilities.wechatNotificationsMenu,
378
+ },
379
+ {
380
+ label: wechatPermissionNotifyEnabled ? copy.wechatPermissionNotifyOn : copy.wechatPermissionNotifyOff,
381
+ value: { type: "toggle-wechat-permission-notify" },
382
+ color: "cyan",
383
+ disabled: !capabilities.wechatNotificationsMenu,
384
+ },
385
+ {
386
+ label: wechatSessionErrorNotifyEnabled ? copy.wechatSessionErrorNotifyOn : copy.wechatSessionErrorNotifyOff,
387
+ value: { type: "toggle-wechat-session-error-notify" },
388
+ color: "cyan",
389
+ disabled: !capabilities.wechatNotificationsMenu,
390
+ },
391
+ ];
313
392
  return [
314
393
  ...providerActions,
315
394
  { label: "", value: { type: "cancel" }, separator: true },
@@ -317,6 +396,8 @@ export function buildMenuItems(input) {
317
396
  { label: "", value: { type: "cancel" }, separator: true },
318
397
  ...providerSettings,
319
398
  { label: "", value: { type: "cancel" }, separator: true },
399
+ ...wechatNotifications,
400
+ { label: "", value: { type: "cancel" }, separator: true },
320
401
  { label: copy.accountsHeading, value: { type: "cancel" }, kind: "heading" },
321
402
  ...input.accounts.map((account) => {
322
403
  const statusBadge = getStatusBadge(account.status);
@@ -367,6 +448,10 @@ export async function showMenuWithDeps(accounts, input = {}, deps = {}) {
367
448
  loopSafetyEnabled: input.loopSafetyEnabled === true,
368
449
  loopSafetyProviderScope: input.loopSafetyProviderScope,
369
450
  networkRetryEnabled: input.networkRetryEnabled === true,
451
+ wechatNotificationsEnabled: input.wechatNotificationsEnabled,
452
+ wechatQuestionNotifyEnabled: input.wechatQuestionNotifyEnabled,
453
+ wechatPermissionNotifyEnabled: input.wechatPermissionNotifyEnabled,
454
+ wechatSessionErrorNotifyEnabled: input.wechatSessionErrorNotifyEnabled,
370
455
  syntheticAgentInitiatorEnabled: input.syntheticAgentInitiatorEnabled === true,
371
456
  experimentalSlashCommandsEnabled: input.experimentalSlashCommandsEnabled,
372
457
  capabilities: input.capabilities,