@chrysb/alphaclaw 0.6.0-beta.2 → 0.6.0-beta.3

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.
@@ -100,6 +100,9 @@ const resolveProfileId = (mode, provider) => {
100
100
  return `${p}:${mode.profileSuffix || "default"}`;
101
101
  };
102
102
 
103
+ const getCredentialValue = (value) =>
104
+ String(value?.key || value?.token || value?.access || "").trim();
105
+
103
106
  const CodexOAuthSection = ({ codexStatus, onRefreshCodex }) => {
104
107
  const [authStarted, setAuthStarted] = useState(false);
105
108
  const [authWaiting, setAuthWaiting] = useState(false);
@@ -285,6 +288,18 @@ export const ProviderAuthCard = ({
285
288
 
286
289
  const effectiveOrder = getEffectiveOrder(provider);
287
290
  const activeProfileId = effectiveOrder?.[0] || null;
291
+ const savedOrder = authOrder[provider] || null;
292
+
293
+ const hasUnsavedProfileChanges = credentialModes.some((mode) => {
294
+ const profileId = resolveProfileId(mode, provider);
295
+ const savedValue = authProfiles.find((p) => p.id === profileId) || null;
296
+ const draftValue = getProfileValue(profileId);
297
+ return getCredentialValue(draftValue) !== getCredentialValue(savedValue);
298
+ });
299
+
300
+ const hasUnsavedOrderChanges =
301
+ JSON.stringify(effectiveOrder || null) !== JSON.stringify(savedOrder);
302
+ const hasUnsavedChanges = hasUnsavedProfileChanges || hasUnsavedOrderChanges;
288
303
 
289
304
  const isConnected =
290
305
  credentialModes.some((mode) => {
@@ -306,7 +321,9 @@ export const ProviderAuthCard = ({
306
321
  <h3 class="card-label">${meta.label}</h3>
307
322
  ${showsInlineOauthStatus && credentialModes.length === 0
308
323
  ? null
309
- : isConnected
324
+ : hasUnsavedChanges
325
+ ? html`<${Badge} tone="warning">Unsaved</${Badge}>`
326
+ : isConnected
310
327
  ? html`<${Badge} tone="success">Connected</${Badge}>`
311
328
  : html`<${Badge} tone="warning">Not configured</${Badge}>`}
312
329
  </div>
@@ -9,6 +9,8 @@ import {
9
9
  import { showToast } from "../toast.js";
10
10
 
11
11
  let kModelsTabCache = null;
12
+ const getCredentialValue = (value) =>
13
+ String(value?.key || value?.token || value?.access || "").trim();
12
14
 
13
15
  export const useModels = (agentId) => {
14
16
  const isScoped = !!agentId;
@@ -99,10 +101,7 @@ export const useModels = (agentId) => {
99
101
  const hasProfileChanges = Object.entries(profileEdits).some(
100
102
  ([id, cred]) => {
101
103
  const existing = authProfiles.find((p) => p.id === id);
102
- const newVal = cred?.key || cred?.token || cred?.access || "";
103
- const oldVal =
104
- existing?.key || existing?.token || existing?.access || "";
105
- return newVal !== oldVal && newVal !== "";
104
+ return getCredentialValue(cred) !== getCredentialValue(existing);
106
105
  },
107
106
  );
108
107
  const hasOrderChanges = Object.entries(orderEdits).some(
@@ -199,9 +198,9 @@ export const useModels = (agentId) => {
199
198
  setSaving(true);
200
199
  try {
201
200
  const changedProfiles = Object.entries(profileEdits)
202
- .filter(([, cred]) => {
203
- const val = cred?.key || cred?.token || cred?.access || "";
204
- return val !== "";
201
+ .filter(([id, cred]) => {
202
+ const existing = authProfiles.find((p) => p.id === id);
203
+ return getCredentialValue(cred) !== getCredentialValue(existing);
205
204
  })
206
205
  .map(([id, cred]) => ({ id, ...cred }));
207
206
 
@@ -225,7 +224,15 @@ export const useModels = (agentId) => {
225
224
  } finally {
226
225
  setSaving(false);
227
226
  }
228
- }, [saving, primary, configuredModels, profileEdits, orderEdits, refresh]);
227
+ }, [
228
+ saving,
229
+ primary,
230
+ configuredModels,
231
+ profileEdits,
232
+ orderEdits,
233
+ authProfiles,
234
+ refresh,
235
+ ]);
229
236
 
230
237
  const refreshCodexStatus = useCallback(async () => {
231
238
  try {
@@ -11,19 +11,23 @@ export const getAuthProviderFromModelProvider = (provider) => {
11
11
  export const kFeaturedModelDefs = [
12
12
  {
13
13
  label: "Opus 4.6",
14
- preferredKeys: ["anthropic/claude-opus-4-6", "anthropic/claude-opus-4-5"],
14
+ preferredKeys: ["anthropic/claude-opus-4-6"],
15
15
  },
16
16
  {
17
17
  label: "Sonnet 4.6",
18
- preferredKeys: ["anthropic/claude-sonnet-4-6", "anthropic/claude-sonnet-4-5"],
18
+ preferredKeys: ["anthropic/claude-sonnet-4-6"],
19
19
  },
20
20
  {
21
21
  label: "Codex 5.3",
22
- preferredKeys: ["openai-codex/gpt-5.3-codex", "openai-codex/gpt-5.2-codex"],
22
+ preferredKeys: ["openai-codex/gpt-5.3-codex"],
23
+ },
24
+ {
25
+ label: "GPT-5.4",
26
+ preferredKeys: ["openai-codex/gpt-5.4"],
23
27
  },
24
28
  {
25
29
  label: "Gemini 3.1 Pro",
26
- preferredKeys: ["google/gemini-3.1-pro-preview", "google/gemini-3-flash-preview"],
30
+ preferredKeys: ["google/gemini-3.1-pro-preview"],
27
31
  },
28
32
  ];
29
33
 
@@ -106,6 +106,11 @@ const kFallbackOnboardingModels = [
106
106
  provider: "anthropic",
107
107
  label: "Claude Haiku 4.6",
108
108
  },
109
+ {
110
+ key: "openai-codex/gpt-5.4",
111
+ provider: "openai-codex",
112
+ label: "GPT-5.4",
113
+ },
109
114
  {
110
115
  key: "openai-codex/gpt-5.3-codex",
111
116
  provider: "openai-codex",
@@ -8,6 +8,7 @@ const kGlobalModelPricing = {
8
8
  "claude-sonnet-4-6": { input: 3.0, output: 15.0 },
9
9
  "claude-haiku-4-6": { input: 0.8, output: 4.0 },
10
10
  "gpt-5": { input: 1.25, output: 10.0 },
11
+ "gpt-5.4": { input: 2.5, output: 10.0 },
11
12
  "gpt-5.1-codex": { input: 2.5, output: 10.0 },
12
13
  "gpt-5.3-codex": { input: 2.5, output: 10.0 },
13
14
  "gpt-4.1": { input: 2.0, output: 8.0 },
@@ -34,6 +34,11 @@ const registerModelRoutes = ({
34
34
  return next;
35
35
  };
36
36
 
37
+ const removeEnvVar = (items, key) => {
38
+ const next = Array.isArray(items) ? [...items] : [];
39
+ return next.filter((entry) => entry.key !== key);
40
+ };
41
+
37
42
  const readEnvVarMap = () => {
38
43
  if (typeof readEnvFile !== "function") return new Map();
39
44
  return new Map(
@@ -105,10 +110,16 @@ const registerModelRoutes = ({
105
110
  if (profile?.type !== "api_key") continue;
106
111
  const envKey = authProfiles.getEnvVarForApiKeyProvider?.(profile.provider);
107
112
  const envValue = String(profile?.key || "").trim();
108
- if (!envKey || !envValue) continue;
113
+ if (!envKey) continue;
109
114
  const prevValue = String(
110
115
  nextEnvVars.find((entry) => entry.key === envKey)?.value || "",
111
116
  );
117
+ if (!envValue) {
118
+ if (!prevValue) continue;
119
+ nextEnvVars = removeEnvVar(nextEnvVars, envKey);
120
+ changed = true;
121
+ continue;
122
+ }
112
123
  if (prevValue === envValue) continue;
113
124
  nextEnvVars = upsertEnvVar(nextEnvVars, envKey, envValue);
114
125
  changed = true;
@@ -88,8 +88,12 @@ const syncConfigForTelegram = ({
88
88
  const promptTopics = {};
89
89
  for (const [threadId, topic] of Object.entries(registryTopics)) {
90
90
  const systemPrompt = String(topic?.systemInstructions || "").trim();
91
- if (!systemPrompt) continue;
92
- promptTopics[threadId] = { systemPrompt };
91
+ const topicAgentId = String(topic?.agentId || "").trim();
92
+ if (!systemPrompt && !topicAgentId) continue;
93
+ promptTopics[threadId] = {
94
+ ...(systemPrompt ? { systemPrompt } : {}),
95
+ ...(topicAgentId ? { agentId: topicAgentId } : {}),
96
+ };
93
97
  }
94
98
  if (Object.keys(promptTopics).length > 0) {
95
99
  targetConfig.groups[groupId].topics = promptTopics;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.6.0-beta.2",
3
+ "version": "0.6.0-beta.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },