diff --git a/sidecar/scripts/prepare-node-binary.mjs b/sidecar/scripts/prepare-node-binary.mjs
index 9c45cbc..a59f935 100644
--- a/sidecar/scripts/prepare-node-binary.mjs
+++ b/sidecar/scripts/prepare-node-binary.mjs
@@ -109,13 +109,21 @@ function main() {
copyFileSync(sysNode, dest);
}
- // Strip whatever signature came from Node Foundation; Tauri will re-sign
- // with our Developer ID at bundle time.
+ // Strip Node Foundation's signature, then ad-hoc sign so the binary can
+ // actually execute. Background: on macOS 14.4+ (and 15+ more strictly),
+ // `amfid` SIGKILLs unsigned arm64 Mach-O binaries the moment they exec.
+ // The original Node Foundation signature would work for execution but
+ // doesn't match our final .app's signing chain, so production builds
+ // need it stripped. Replacing it with an ad-hoc signature (`--sign -`)
+ // is the only way to have the binary be both executable *now* (for
+ // `tauri dev`) and clean-slate for tauri-action's later `codesign
+ // --force` re-sign with the Developer ID cert.
try {
execSync(`codesign --remove-signature "${dest}"`, { stdio: "ignore" });
} catch {
// already unsigned — fine
}
+ execSync(`codesign --sign - --force --timestamp=none "${dest}"`, { stdio: "ignore" });
execSync(`chmod +x "${dest}"`);
const size = (statSync(dest).size / 1024 / 1024).toFixed(1);
diff --git a/src/renderer/components/SettingsPanel.tsx b/src/renderer/components/SettingsPanel.tsx
index 4158374..c6e14e0 100644
--- a/src/renderer/components/SettingsPanel.tsx
+++ b/src/renderer/components/SettingsPanel.tsx
@@ -1325,142 +1325,8 @@ export function SettingsPanel({ onClose, initialTab }: SettingsPanelProps) {
)}
- {/* AI Models */}
-
-
-
AI Models
-
- Choose which Claude model to use for each feature. Haiku is fastest and
- cheapest, Opus is most capable.
-
-
-
- {[
- {
- key: "analysis" as const,
- label: "Email Analysis",
- description: "Triaging which emails need replies",
- },
- {
- key: "drafts" as const,
- label: "Draft Generation",
- description: "Writing reply drafts",
- },
- {
- key: "refinement" as const,
- label: "Draft Refinement",
- description: "Improving drafts based on feedback",
- },
- {
- key: "calendaring" as const,
- label: "Scheduling Detection",
- description: "Identifying calendar-related emails",
- },
- {
- key: "archiveReady" as const,
- label: "Archive-Ready Analysis",
- description: "Detecting completed conversations",
- },
- {
- key: "senderLookup" as const,
- label: "Sender Lookup",
- description: "Web search for sender info",
- anthropicOnly: true,
- },
- {
- key: "agentDrafter" as const,
- label: "Agent Drafter",
- description: "Background auto-draft generation",
- },
- {
- key: "agentChat" as const,
- label: "Agent Chat",
- description: "Interactive agent sidebar conversations",
- },
- ].map(({ key, label, description, ...rest }) => {
- // Mirror the Agents tab picker shape: full Anthropic
- // model list + OpenRouter free models, auto-save on
- // change. anthropicOnly features (Sender Lookup uses
- // Anthropic web_search; no OpenRouter equivalent) keep
- // the Anthropic-only chip and hide the OpenRouter
- // optgroup. The sidecar's resolveModelFor() accepts
- // both legacy tier names ("haiku") and concrete model
- // ids ("claude-haiku-4-5-…", "deepseek/…:free"), so
- // either value space round-trips correctly.
- const anthropicOnly =
- "anthropicOnly" in rest ? rest.anthropicOnly : false;
- const isNonClaude =
- anthropicOnly && !modelConfig[key].startsWith("claude-");
- return (
-
- Configured model is not a Claude model — this feature will fail
- until a Claude model is selected.
-
- )}
-
-
-
- );
- })}
-
-
- Picking a non-Anthropic model routes that feature through OpenRouter — make
- sure an OpenRouter API key is configured in Agent Tools → AI Models, otherwise
- the call will surface a clear “OpenRouter API key required” error.
- Sender Lookup is the exception: it uses Anthropic’s web_search tool,
- which has no OpenRouter equivalent, so the picker only offers Claude models
- for that feature.
-
-
+ {/* AI Models lives in Agent Tools → AI Models now — single
+ source of truth, directly under the Anthropic auth section. */}
{/* Updates */}
@@ -2292,6 +2158,11 @@ export function SettingsPanel({ onClose, initialTab }: SettingsPanelProps) {
description:
"Iterating on a draft from your feedback (falls back to Draft Generation)",
},
+ {
+ key: "calendaring" as const,
+ label: "Scheduling Detection",
+ description: "Identifying calendar-related emails",
+ },
{
key: "summary" as const,
label: "Thread Summary",
@@ -2308,6 +2179,16 @@ export function SettingsPanel({ onClose, initialTab }: SettingsPanelProps) {
description: "Web-search-backed sender profiles",
anthropicOnly: true,
},
+ {
+ key: "agentDrafter" as const,
+ label: "Agent Drafter",
+ description: "Background auto-draft generation",
+ },
+ {
+ key: "agentChat" as const,
+ label: "Agent Chat",
+ description: "Interactive agent sidebar conversations",
+ },
] as const
).map(({ key, label, description, ...rest }) => {
const anthropicOnly = "anthropicOnly" in rest ? rest.anthropicOnly : false;