@molecule/app-ide-react 1.1.0 → 1.1.2
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/components/ChatPanel.d.ts.map +1 -1
- package/dist/components/ChatPanel.js +78 -52
- package/dist/components/ChatPanel.js.map +1 -1
- package/dist/components/chat-model-mode-utilities.d.ts +29 -0
- package/dist/components/chat-model-mode-utilities.d.ts.map +1 -1
- package/dist/components/chat-model-mode-utilities.js +40 -0
- package/dist/components/chat-model-mode-utilities.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatPanel.d.ts","sourceRoot":"","sources":["../../src/components/ChatPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAa,MAAM,OAAO,CAAA;AAY3C,OAAO,KAAK,EAAa,WAAW,EAAmB,MAAM,uBAAuB,CAAA;AAsCpF,OAAO,KAAK,EAAE,cAAc,EAAoB,eAAe,EAAE,MAAM,aAAa,CAAA;AACpF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AA0BvD,OAAO,KAAK,EAAsB,SAAS,EAAE,MAAM,gCAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"ChatPanel.d.ts","sourceRoot":"","sources":["../../src/components/ChatPanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -21,7 +21,7 @@ import { stripCommitCoauthorTrailer } from './chat-commit-utilities.js';
|
|
|
21
21
|
import { cachedPromptTokens, formatTokenTotal } from './chat-cost-utilities.js';
|
|
22
22
|
import { effortOptionsForModel, nativeEffortName, parseEffortCommand, resolveEffortArg, } from './chat-effort-utilities.js';
|
|
23
23
|
import { buildHelpText } from './chat-help-utilities.js';
|
|
24
|
-
import { freeTierLockReason, freeTierUsableMode, isModeModelLocked, modeSettingKey, parseModelModeCommand, resolveModeModel, } from './chat-model-mode-utilities.js';
|
|
24
|
+
import { effectiveModeModelId, freeTierLockReason, freeTierUsableMode, isModeModelLocked, modeSettingKey, parseModelModeCommand, resolveModeModel, } from './chat-model-mode-utilities.js';
|
|
25
25
|
import { modelUsageRate, sortModels } from './chat-models-utilities.js';
|
|
26
26
|
import { formatReportConfirmation, parseReportCommand } from './chat-report-utilities.js';
|
|
27
27
|
import { findScriptByName, formatRunOutput, parseRunCommand, parseScriptsCommand, runSucceeded, } from './chat-scripts-utilities.js';
|
|
@@ -745,7 +745,9 @@ function NoticeCard({ tone, text, content, action, icon: iconOverride, }) {
|
|
|
745
745
|
// colors — the ClassMap owns the look, including hover states.
|
|
746
746
|
// Colorless actions keep the legacy accent-outline treatment.
|
|
747
747
|
if (act.color) {
|
|
748
|
-
|
|
748
|
+
// touchTargetCompact (36px), not the full 44px touchTarget — these
|
|
749
|
+
// sit inside a dense chat card (the floor agreed for its actions).
|
|
750
|
+
const className = cm.cn(cm.button({ color: act.color, size: 'sm' }), cm.touchTargetCompact);
|
|
749
751
|
const coloredStyle = {
|
|
750
752
|
textDecoration: 'none',
|
|
751
753
|
fontFamily: act.code ? 'var(--mol-font-mono, monospace)' : 'inherit',
|
|
@@ -2482,6 +2484,13 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2482
2484
|
const DEFAULT_MODEL = FREE_TIER_MODEL;
|
|
2483
2485
|
const isFreeTier = !isPro;
|
|
2484
2486
|
const [currentModel, setCurrentModel] = useState('');
|
|
2487
|
+
// The PERSISTED legacy chatModel only — unlike currentModel, never seeded
|
|
2488
|
+
// with the catalog default. The distinction matters: a seeded default is not
|
|
2489
|
+
// a user choice, and treating it as one made every plan-mode surface label
|
|
2490
|
+
// the free-tier EXECUTOR as the active model while the server planned with
|
|
2491
|
+
// its own plan default (prod 2026-08-13: UI said "DeepSeek V4 Flash" for a
|
|
2492
|
+
// plan turn DeepSeek V4 Pro served).
|
|
2493
|
+
const [savedChatModel, setSavedChatModel] = useState('');
|
|
2485
2494
|
// Per-mode model overrides (SYN5). Empty string = unset; resolveModeModel then
|
|
2486
2495
|
// falls back to currentModel (the legacy single chatModel) for back-compat.
|
|
2487
2496
|
const [planModel, setPlanModel] = useState('');
|
|
@@ -2513,13 +2522,21 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2513
2522
|
setCurrentModel(DEFAULT_MODEL);
|
|
2514
2523
|
}
|
|
2515
2524
|
}, [currentModel, DEFAULT_MODEL]);
|
|
2525
|
+
// The model a conversation mode will ACTUALLY use, for every display surface
|
|
2526
|
+
// (picker pill, /model suffix, settings rows, fast-mode gating). Mirrors the
|
|
2527
|
+
// server's selection chain: per-mode setting → SAVED chatModel → the
|
|
2528
|
+
// server-sent per-mode default (owner account settings + tier already folded
|
|
2529
|
+
// in) → the catalog clamp.
|
|
2530
|
+
const effectiveModelForMode = (m) => effectiveModeModelId(m, { planModel, executeModel, chatModel: savedChatModel }, serverModelDefaults, isFreeTier, AVAILABLE_MODELS, FREE_TIER_MODEL);
|
|
2516
2531
|
useEffect(() => {
|
|
2517
2532
|
http
|
|
2518
2533
|
.get(`/projects/${projectId}`)
|
|
2519
2534
|
.then((res) => {
|
|
2520
2535
|
const s = res.data.settings;
|
|
2521
|
-
if (typeof s?.chatModel === 'string')
|
|
2536
|
+
if (typeof s?.chatModel === 'string') {
|
|
2522
2537
|
setCurrentModel(s.chatModel);
|
|
2538
|
+
setSavedChatModel(s.chatModel);
|
|
2539
|
+
}
|
|
2523
2540
|
if (typeof s?.planModel === 'string')
|
|
2524
2541
|
setPlanModel(s.planModel);
|
|
2525
2542
|
if (typeof s?.executeModel === 'string')
|
|
@@ -2652,6 +2669,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2652
2669
|
}));
|
|
2653
2670
|
if (fallback) {
|
|
2654
2671
|
setCurrentModel(fallback);
|
|
2672
|
+
setSavedChatModel(fallback);
|
|
2655
2673
|
http.patch(`/projects/${projectId}`, { settings: { chatModel: fallback } }).catch(() => {
|
|
2656
2674
|
/* persistence is best-effort; the in-memory switch is what matters */
|
|
2657
2675
|
});
|
|
@@ -2849,9 +2867,16 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2849
2867
|
// render as a normal user bubble: either it's suppressed entirely (hidden build
|
|
2850
2868
|
// kickoff) or it stays visible but flagged `automatic` so the chat shows it as sent
|
|
2851
2869
|
// by Synthase on the user's behalf (distinct avatar + accent), not typed by the user.
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2870
|
+
// Suppressed or not, a pending message is platform-composed, so it ALWAYS
|
|
2871
|
+
// carries `automatic` (+ `userInitiated` when a direct click requested it).
|
|
2872
|
+
// A suppressed send without `automatic` used to bypass the server's
|
|
2873
|
+
// user-stop gate — the hidden build kickoff resumed a plan turn ~37s
|
|
2874
|
+
// after an explicit Stop (prod 2026-08-13).
|
|
2875
|
+
const sendOpts = {
|
|
2876
|
+
...(pendingMessageSuppressUser ? { suppressUserMessage: true } : {}),
|
|
2877
|
+
automatic: true,
|
|
2878
|
+
...(pendingMessageUserInitiated ? { userInitiated: true } : {}),
|
|
2879
|
+
};
|
|
2855
2880
|
if (isLoading) {
|
|
2856
2881
|
// AI is busy — defer until streaming ends
|
|
2857
2882
|
deferredPendingRef.current = pendingMessage;
|
|
@@ -2883,11 +2908,13 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2883
2908
|
deferredPendingRef.current = null;
|
|
2884
2909
|
deferredPendingSuppressRef.current = false;
|
|
2885
2910
|
deferredPendingUserInitiatedRef.current = false;
|
|
2886
|
-
// Same rule as the immediate path: a deferred pending message is system-composed
|
|
2887
|
-
//
|
|
2888
|
-
sendMessage(msg, undefined,
|
|
2889
|
-
? { suppressUserMessage: true }
|
|
2890
|
-
|
|
2911
|
+
// Same rule as the immediate path: a deferred pending message is system-composed —
|
|
2912
|
+
// always `automatic` (suppressed or visible), never a plain user bubble.
|
|
2913
|
+
sendMessage(msg, undefined, {
|
|
2914
|
+
...(suppress ? { suppressUserMessage: true } : {}),
|
|
2915
|
+
automatic: true,
|
|
2916
|
+
...(userInitiated ? { userInitiated: true } : {}),
|
|
2917
|
+
});
|
|
2891
2918
|
}
|
|
2892
2919
|
}, [isLoading, sendMessage]);
|
|
2893
2920
|
// ── Auto-delete queued autofix messages when user edits a relevant file ────
|
|
@@ -3437,6 +3464,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3437
3464
|
else {
|
|
3438
3465
|
await http.patch(`/projects/${projectId}`, { settings: { chatModel: modelId } });
|
|
3439
3466
|
setCurrentModel(modelId);
|
|
3467
|
+
setSavedChatModel(modelId);
|
|
3440
3468
|
addSystemCard(t('ide.chat.modelSet', { name }, {
|
|
3441
3469
|
defaultValue: `Chat model set to ${name}`,
|
|
3442
3470
|
}));
|
|
@@ -4004,6 +4032,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4004
4032
|
try {
|
|
4005
4033
|
await http.patch(`/projects/${projectId}`, { settings: { chatModel: name } });
|
|
4006
4034
|
setCurrentModel(name);
|
|
4035
|
+
setSavedChatModel(name);
|
|
4007
4036
|
addSystemCard(t('ide.chat.modelSet', { name: resolved?.label ?? name }, {
|
|
4008
4037
|
defaultValue: `Chat model set to ${resolved?.label ?? name}`,
|
|
4009
4038
|
}));
|
|
@@ -4033,8 +4062,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4033
4062
|
// picker + slash-suffix resolveModeModel logic (P2-10: each mode's
|
|
4034
4063
|
// options come from ITS model's reasoning capabilities).
|
|
4035
4064
|
const modelForMode = (m) => {
|
|
4036
|
-
|
|
4037
|
-
return AVAILABLE_MODELS.find((model) => model.id === id);
|
|
4065
|
+
return AVAILABLE_MODELS.find((model) => model.id === effectiveModelForMode(m));
|
|
4038
4066
|
};
|
|
4039
4067
|
const effectiveEffort = (m) => effortByMode[m] ?? (effortLevel || undefined);
|
|
4040
4068
|
const modeStatusLine = (m) => {
|
|
@@ -4354,7 +4382,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4354
4382
|
// The composer toggle renders only when the model that will serve the CURRENT
|
|
4355
4383
|
// mode declares fast-tier pricing (`fastPricing` — e.g. Claude Opus 5). The
|
|
4356
4384
|
// server re-checks per iteration, so this is presentation-only gating.
|
|
4357
|
-
const activeModeModelId = (mode === 'plan' ?
|
|
4385
|
+
const activeModeModelId = effectiveModelForMode(mode === 'plan' ? 'plan' : 'execute');
|
|
4358
4386
|
const fastModeAvailable = Boolean(AVAILABLE_MODELS.find((m) => m.id === activeModeModelId)?.fastPricing);
|
|
4359
4387
|
// ── Model-picker mode dropdown ──────────────────────────────────────────────
|
|
4360
4388
|
// Options for the picker's mode selector; each shows the model currently
|
|
@@ -4362,32 +4390,33 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4362
4390
|
// re-scoping the open picker is informed. Cheap to recompute per render.
|
|
4363
4391
|
const pickerModeOptions = (() => {
|
|
4364
4392
|
const label = (id) => AVAILABLE_MODELS.find((m) => m.id === id)?.label || id;
|
|
4365
|
-
const followsDefault = t('ide.chat.settings.modelFollowsDefault', undefined, {
|
|
4366
|
-
defaultValue: 'Follows default model',
|
|
4367
|
-
});
|
|
4368
4393
|
// Name the model "default" resolves to when the server told us
|
|
4369
4394
|
// ("Default (DeepSeek V4 Flash)") — a bare "Fast default" is not decodable.
|
|
4370
4395
|
const auxDefault = (id) => id
|
|
4371
4396
|
? t('ide.chat.settings.modelDefaultNamed', { model: label(id) }, { defaultValue: 'Default ({{model}})' })
|
|
4372
4397
|
: t('ide.chat.settings.modelDefaultFast', undefined, { defaultValue: 'Fast default' });
|
|
4373
|
-
|
|
4398
|
+
// The named-default helper ("Default (<model>)") for a mode whose per-mode
|
|
4399
|
+
// value is unset — names what the mode ACTUALLY resolves to (saved default
|
|
4400
|
+
// model, else the server's per-mode default), never a vague phrase and
|
|
4401
|
+
// never the wrong mode's model.
|
|
4402
|
+
const modeDefault = (m) => t('ide.chat.settings.modelDefaultNamed', { model: label(effectiveModelForMode(m)) }, { defaultValue: 'Default ({{model}})' });
|
|
4374
4403
|
const rows = [
|
|
4375
4404
|
{
|
|
4376
4405
|
value: '',
|
|
4377
4406
|
mode: t('ide.chat.modelMode.default', undefined, { defaultValue: 'Default' }),
|
|
4378
|
-
model:
|
|
4379
|
-
? label(
|
|
4407
|
+
model: savedChatModel
|
|
4408
|
+
? label(savedChatModel)
|
|
4380
4409
|
: t('ide.chat.settings.modelUnset', undefined, { defaultValue: 'Not set' }),
|
|
4381
4410
|
},
|
|
4382
4411
|
{
|
|
4383
4412
|
value: 'plan',
|
|
4384
4413
|
mode: t('ide.chat.settings.modePlan', undefined, { defaultValue: 'Plan' }),
|
|
4385
|
-
model: planModel ? label(planModel) :
|
|
4414
|
+
model: planModel ? label(planModel) : modeDefault('plan'),
|
|
4386
4415
|
},
|
|
4387
4416
|
{
|
|
4388
4417
|
value: 'execute',
|
|
4389
4418
|
mode: t('ide.chat.settings.modeExecute', undefined, { defaultValue: 'Execute' }),
|
|
4390
|
-
model: executeModel ? label(executeModel) :
|
|
4419
|
+
model: executeModel ? label(executeModel) : modeDefault('execute'),
|
|
4391
4420
|
},
|
|
4392
4421
|
{
|
|
4393
4422
|
value: 'commit',
|
|
@@ -4406,12 +4435,16 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4406
4435
|
}));
|
|
4407
4436
|
})();
|
|
4408
4437
|
// The row that gets the "current" pill — the model the selected picker mode
|
|
4409
|
-
// actually uses (its per-mode value with the mode's fallback chain), not
|
|
4410
|
-
// unconditionally the legacy chatModel.
|
|
4438
|
+
// actually uses (its per-mode value with the mode's REAL fallback chain), not
|
|
4439
|
+
// unconditionally the legacy chatModel. The generic (no-mode) picker edits
|
|
4440
|
+
// the default slot, so its pill is the SAVED default only — a seeded catalog
|
|
4441
|
+
// default is not a choice and must not masquerade as one.
|
|
4411
4442
|
const pickerCurrentModelId = modelPicker
|
|
4412
|
-
? modelPicker.mode
|
|
4413
|
-
?
|
|
4414
|
-
:
|
|
4443
|
+
? modelPicker.mode === 'plan' || modelPicker.mode === 'execute'
|
|
4444
|
+
? effectiveModelForMode(modelPicker.mode)
|
|
4445
|
+
: modelPicker.mode
|
|
4446
|
+
? (resolveModeModel({ planModel, executeModel, commitModel, compactModel, chatModel: savedChatModel }, modelPicker.mode) ?? serverModelDefaults?.[modelPicker.mode])
|
|
4447
|
+
: savedChatModel || undefined
|
|
4415
4448
|
: undefined;
|
|
4416
4449
|
const filteredEntries = useMemo(() => {
|
|
4417
4450
|
if (!filePicker)
|
|
@@ -4708,11 +4741,12 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4708
4741
|
const soundsSummary = summarizeSounds(soundsConfig);
|
|
4709
4742
|
const notSet = t('ide.chat.settings.modelUnset', undefined, { defaultValue: 'Not set' });
|
|
4710
4743
|
const modelLabel = (id) => AVAILABLE_MODELS.find((m) => m.id === id)?.label || id;
|
|
4711
|
-
//
|
|
4712
|
-
//
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4744
|
+
// An unset per-mode model shows what the mode ACTUALLY resolves to
|
|
4745
|
+
// ("Default (<model>)"): the saved default model, else the server's
|
|
4746
|
+
// per-mode default — never the wrong mode's model (SYN11; the old
|
|
4747
|
+
// "Follows default model" phrasing implied plan follows the executor's
|
|
4748
|
+
// free-tier default while the server planned with its own plan default).
|
|
4749
|
+
const modeDefault = (m) => t('ide.chat.settings.modelDefaultNamed', { model: modelLabel(effectiveModelForMode(m)) }, { defaultValue: 'Default ({{model}})' });
|
|
4716
4750
|
// The auxiliary commit/compact jobs fall back to the server's own fast
|
|
4717
4751
|
// default when unset — NOT the default model — so their unset label differs.
|
|
4718
4752
|
// Name the resolved model when the server sent its defaults.
|
|
@@ -4720,9 +4754,9 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4720
4754
|
? t('ide.chat.settings.modelDefaultNamed', { model: modelLabel(id) }, { defaultValue: 'Default ({{model}})' })
|
|
4721
4755
|
: t('ide.chat.settings.modelDefaultFast', undefined, { defaultValue: 'Fast default' });
|
|
4722
4756
|
return buildSettingsList({
|
|
4723
|
-
model:
|
|
4724
|
-
planModel: planModel ? modelLabel(planModel) :
|
|
4725
|
-
executeModel: executeModel ? modelLabel(executeModel) :
|
|
4757
|
+
model: savedChatModel ? modelLabel(savedChatModel) : notSet,
|
|
4758
|
+
planModel: planModel ? modelLabel(planModel) : modeDefault('plan'),
|
|
4759
|
+
executeModel: executeModel ? modelLabel(executeModel) : modeDefault('execute'),
|
|
4726
4760
|
commitModel: commitModel ? modelLabel(commitModel) : auxDefault(serverModelDefaults?.commit),
|
|
4727
4761
|
compactModel: compactModel
|
|
4728
4762
|
? modelLabel(compactModel)
|
|
@@ -4734,15 +4768,12 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4734
4768
|
// reasoning model shows "fixed"). Falls back to the legacy single level.
|
|
4735
4769
|
effort: t('ide.chat.settings.effortValue', {
|
|
4736
4770
|
plan: (() => {
|
|
4737
|
-
const model = AVAILABLE_MODELS.find((m) => m.id ===
|
|
4738
|
-
(resolveModeModel({ planModel, executeModel, chatModel: currentModel }, 'plan') ??
|
|
4739
|
-
currentModel));
|
|
4771
|
+
const model = AVAILABLE_MODELS.find((m) => m.id === effectiveModelForMode('plan'));
|
|
4740
4772
|
return (nativeEffortName(model, effortByMode.plan ?? effortLevel) ??
|
|
4741
4773
|
t('ide.chat.settings.effortFixed', undefined, { defaultValue: 'fixed' }));
|
|
4742
4774
|
})(),
|
|
4743
4775
|
execute: (() => {
|
|
4744
|
-
const model = AVAILABLE_MODELS.find((m) => m.id ===
|
|
4745
|
-
(resolveModeModel({ planModel, executeModel, chatModel: currentModel }, 'execute') ?? currentModel));
|
|
4776
|
+
const model = AVAILABLE_MODELS.find((m) => m.id === effectiveModelForMode('execute'));
|
|
4746
4777
|
return (nativeEffortName(model, effortByMode.execute ?? effortLevel) ??
|
|
4747
4778
|
t('ide.chat.settings.effortFixed', undefined, { defaultValue: 'fixed' }));
|
|
4748
4779
|
})(),
|
|
@@ -5074,18 +5105,13 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
5074
5105
|
let suffix = '';
|
|
5075
5106
|
if (cmd.id === 'model') {
|
|
5076
5107
|
// Reflect the model the ACTIVE conversation mode will
|
|
5077
|
-
// actually use
|
|
5078
|
-
//
|
|
5079
|
-
//
|
|
5080
|
-
// the
|
|
5081
|
-
//
|
|
5082
|
-
// parentheses never render
|
|
5083
|
-
|
|
5084
|
-
// chatModel -> the default / free-tier model id (|| so an
|
|
5085
|
-
// empty string also falls through).
|
|
5086
|
-
const effId = resolveModeModel({ planModel, executeModel, chatModel: currentModel }, mode) ||
|
|
5087
|
-
currentModel ||
|
|
5088
|
-
DEFAULT_MODEL ||
|
|
5108
|
+
// actually use — the full server chain (per-mode →
|
|
5109
|
+
// saved default → server per-mode default → clamp),
|
|
5110
|
+
// never a UI-seeded default (which mislabeled plan
|
|
5111
|
+
// mode with the free-tier executor). Falls through to
|
|
5112
|
+
// the free-tier id only before the catalog loads so
|
|
5113
|
+
// the parentheses never render empty (P3-16).
|
|
5114
|
+
const effId = effectiveModelForMode(mode === 'plan' ? 'plan' : 'execute') ||
|
|
5089
5115
|
FREE_TIER_MODEL;
|
|
5090
5116
|
const modelLabel = AVAILABLE_MODELS.find((m) => m.id === effId)?.label ?? effId;
|
|
5091
5117
|
suffix = modelLabel ? ` (${modelLabel})` : '';
|