@aion0/forge 0.9.11 → 0.9.12

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/RELEASE_NOTES.md CHANGED
@@ -1,8 +1,13 @@
1
- # Forge v0.9.11
1
+ # Forge v0.9.12
2
2
 
3
- Released: 2026-05-26
3
+ Released: 2026-05-27
4
4
 
5
- ## Changes since v0.9.10
5
+ ## Changes since v0.9.11
6
6
 
7
+ ### Other
8
+ - fix(settings): id-collision guards on agent + profile add
9
+ - fix(settings): functional setSettings in profile add — preserves new agent
10
+ - fix(settings): adding an Agent no longer creates a CLI Profile
7
11
 
8
- **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.9.10...v0.9.11
12
+
13
+ **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.9.11...v0.9.12
@@ -1379,8 +1379,16 @@ function AgentsSection({ settings, setSettings }: { settings: any; setSettings:
1379
1379
  const agentsCfg: Record<string, any> = { ...(prev.agents || {}) };
1380
1380
  for (const a of updated) {
1381
1381
  const existing = agentsCfg[a.id] || {};
1382
+ // NOTE: do NOT write `cliType` from the entry here. cliType is the
1383
+ // load-time discriminator between agents and CLI profiles (see the
1384
+ // filter ~line 1349 — `if (cfg.cliType || cfg.base) continue`).
1385
+ // The Agents-add form seeds newAgent.cliType for cliDefaults lookup
1386
+ // (taskFlags / resumeFlag presets), but persisting it would make
1387
+ // the new agent show up as a profile on reload. `...existing`
1388
+ // carries forward cliType for detected agents whose inline selector
1389
+ // wrote it via setSettings directly (line ~1540).
1382
1390
  agentsCfg[a.id] = {
1383
- ...existing, // preserve profile-specific fields
1391
+ ...existing, // preserve profile-specific fields + any prior cliType
1384
1392
  name: a.name,
1385
1393
  path: a.path,
1386
1394
  enabled: a.enabled,
@@ -1391,7 +1399,6 @@ function AgentsSection({ settings, setSettings }: { settings: any; setSettings:
1391
1399
  models: a.models,
1392
1400
  skipPermissionsFlag: a.skipPermissionsFlag,
1393
1401
  requiresTTY: a.requiresTTY,
1394
- cliType: (a as any).cliType || existing.cliType,
1395
1402
  };
1396
1403
  }
1397
1404
  const claude = updated.find(a => a.id === 'claude');
@@ -1437,6 +1444,15 @@ function AgentsSection({ settings, setSettings }: { settings: any; setSettings:
1437
1444
 
1438
1445
  const addAgent = () => {
1439
1446
  if (!newAgent.id || !newAgent.path) return;
1447
+ // settings.agents is one map keyed by id — agents + CLI profiles + API
1448
+ // profiles all coexist. A duplicate id silently overwrites whichever
1449
+ // entry was there first. Block the add and tell the user why.
1450
+ if (agents.some(a => a.id === newAgent.id) || settings.agents?.[newAgent.id]) {
1451
+ const existing = settings.agents?.[newAgent.id] as any;
1452
+ const kind = existing?.type === 'api' ? 'API profile' : existing?.cliType || existing?.base ? 'CLI profile' : 'agent';
1453
+ alert(`id "${newAgent.id}" is already in use by a ${kind}. Pick a different id.`);
1454
+ return;
1455
+ }
1440
1456
  const entry: AgentEntry = {
1441
1457
  ...newAgent,
1442
1458
  enabled: true,
@@ -1743,11 +1759,34 @@ function AgentsSection({ settings, setSettings }: { settings: any; setSettings:
1743
1759
  ))}
1744
1760
 
1745
1761
  <div className="flex gap-2 mt-1">
1762
+ {/* Functional setSettings — non-functional form captures a stale
1763
+ `settings` snapshot, which clobbers any agent that was saved
1764
+ between this component's last render and the Add click
1765
+ (debouncedSave for the Agents section fires 1s later, so the
1766
+ window is real). Also: settings.agents is a single map keyed
1767
+ by id — agents + CLI profiles + API profiles share it — so
1768
+ reusing an id silently overwrites. Block dups with an alert. */}
1746
1769
  <AddProfileForm type="cli" baseAgents={agents.filter(a => !a.isProfile && a.detected)} onAdd={(id, cfg) => {
1747
- setSettings({ ...settings, agents: { ...settings.agents, [id]: cfg } });
1770
+ setSettings((prev: any) => {
1771
+ if (prev.agents?.[id]) {
1772
+ const existing = prev.agents[id] as any;
1773
+ const kind = existing.type === 'api' ? 'API profile' : existing.cliType || existing.base ? 'CLI profile' : 'agent';
1774
+ alert(`id "${id}" is already in use by a ${kind}. Pick a different id.`);
1775
+ return prev;
1776
+ }
1777
+ return { ...prev, agents: { ...(prev.agents || {}), [id]: cfg } };
1778
+ });
1748
1779
  }} />
1749
1780
  <AddProfileForm type="api" baseAgents={[]} onAdd={(id, cfg) => {
1750
- setSettings({ ...settings, agents: { ...settings.agents, [id]: cfg } });
1781
+ setSettings((prev: any) => {
1782
+ if (prev.agents?.[id]) {
1783
+ const existing = prev.agents[id] as any;
1784
+ const kind = existing.type === 'api' ? 'API profile' : existing.cliType || existing.base ? 'CLI profile' : 'agent';
1785
+ alert(`id "${id}" is already in use by a ${kind}. Pick a different id.`);
1786
+ return prev;
1787
+ }
1788
+ return { ...prev, agents: { ...(prev.agents || {}), [id]: cfg } };
1789
+ });
1751
1790
  }} />
1752
1791
  </div>
1753
1792
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.9.11",
3
+ "version": "0.9.12",
4
4
  "description": "Unified AI workflow platform — multi-model task orchestration, persistent sessions, web terminal, remote access",
5
5
  "type": "module",
6
6
  "scripts": {