@aion0/forge 0.10.41 → 0.10.42
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 +3 -84
- package/app/api/onboarding/route.ts +24 -1
- package/app/chat/page.tsx +8 -5
- package/lib/chat/llm/anthropic.ts +6 -1
- package/package.json +1 -1
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,89 +1,8 @@
|
|
|
1
|
-
# Forge v0.10.
|
|
1
|
+
# Forge v0.10.42
|
|
2
2
|
|
|
3
3
|
Released: 2026-06-07
|
|
4
4
|
|
|
5
|
-
## Changes since v0.10.
|
|
5
|
+
## Changes since v0.10.41
|
|
6
6
|
|
|
7
|
-
### Features
|
|
8
|
-
- fix: agent path resolution + wizard writes self-contained settings
|
|
9
|
-
- feat: pipelines auto-install on Reinstall + monitor self-detect
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
- fix: agent path resolution + wizard writes self-contained settings
|
|
13
|
-
|
|
14
|
-
### Other
|
|
15
|
-
- chat: guard against silent project guessing for ambiguous MR / bug ids
|
|
16
|
-
- feat(marketplace): single unified Sync all — covers every type + every source
|
|
17
|
-
- ui: restore Re-sync registry button in Enterprise section
|
|
18
|
-
- ui: consolidate sync into Marketplace Connectors → Refresh
|
|
19
|
-
- ui(settings): per-source ↻ Sync button on each enterprise row
|
|
20
|
-
- ui(settings): re-run wizard moves next to Re-sync, shorter copy
|
|
21
|
-
- fix(settings): remove "Reinstall all" button — too many footguns
|
|
22
|
-
- fix(login-status,reinstall): badge polls, Reinstall force-overwrites instance fields
|
|
23
|
-
- fix(login-status): panel re-probes on mount, drops stale-cache UX
|
|
24
|
-
- fix(login-status): Test button writes result to login-status cache
|
|
25
|
-
- feat(wizard,pipeline): temper auto-provision + scratch-first project resolution + login-status cache invalidate
|
|
26
|
-
- fix(wizard): sequential section numbers + always show Pipelines
|
|
27
|
-
- feat(wizard): show template-baked defaults under each connector
|
|
28
|
-
- fix(wizard): profile-as-defaults — open with user's company+dept
|
|
29
|
-
- revert(wizard): drop settings.company override on source default
|
|
30
|
-
- fix(wizard): tenant selector defaults to settings.company
|
|
31
|
-
- fix(wizard): revert force-apply complexity, dept default reads settings.dept
|
|
32
|
-
- fix(wizard): re-run with different dept actually rotates defaults
|
|
33
|
-
- ui(profile): dept edit just updates label, no template re-apply
|
|
34
|
-
- feat(profile): company + dept as user-profile fields
|
|
35
|
-
- ui(enterprise): dept chip is display-only, picker lives in wizard
|
|
36
|
-
- feat(enterprise): dept chip becomes a one-click selector
|
|
37
|
-
- feat(wizard): support template-less department entries
|
|
38
|
-
- ui(enterprise): show single active dept, not the full list
|
|
39
|
-
- feat(enterprise): highlight currently-active dept chip
|
|
40
|
-
- feat(enterprise): surface dept list under each tenant
|
|
41
|
-
- ui(settings): merge Onboarding into Enterprise section at top
|
|
42
|
-
- fix(wizard): default dept picker to last-applied dept
|
|
43
|
-
- fix(wizard): restore auto-open + add cold-boot tenant splash
|
|
44
|
-
- fix(wizard): hasCache check accepts multi-dept index too
|
|
45
|
-
- fix(wizard): priority chain reads multi-dept cache, not just legacy file
|
|
46
|
-
- feat(wizard): add-key → wizard handoff + cold-boot auto-open gate (E4)
|
|
47
|
-
- feat(wizard): multi-department templates per source (E3)
|
|
48
|
-
- feat(wizard): tenant-scoped wizard via ?source_id (E2)
|
|
49
|
-
- feat(enterprise): editable keys (E1 of wizard redesign)
|
|
50
|
-
- feat(chat): surface connector defaults + stem-match arg fallback
|
|
51
|
-
- fix(ui): anchor enterprise popover to left edge
|
|
52
|
-
- fix(ui): drop displayName suffix from top-left title
|
|
53
|
-
- feat(ui): enterprise badge popover next to Forge title + settings reorder
|
|
54
|
-
- ui(settings): split 'Add tenant' from sync/reinstall actions
|
|
55
|
-
- feat(marketplace): Reinstall also applies template defaults to connector configs
|
|
56
|
-
- feat(dispatcher): auto-fill missing tool args from settings.default_<name>
|
|
57
|
-
- fix(http): expand tokens in body_form_inject_from values + keys
|
|
58
|
-
- fix(onboarding): fall back to displayName when email is empty for {user.login}
|
|
59
|
-
- feat(api): /api/bridge-info — expose browser-bridge port for the extension
|
|
60
|
-
- feat(auth): first-time admin password setup — no current-password required
|
|
61
|
-
- cli: forge --reset-password forwards trailing args (so --dir picks target instance)
|
|
62
|
-
- fix(onboarding): synchronously sync enterprise template on first launch
|
|
63
|
-
- fix(db): silence expected 'no such table' on fresh DB init
|
|
64
|
-
- onboarding: orphan instance rows drop unconditionally — template is canonical
|
|
65
|
-
- fix(connectors): cross-ref regex must also accept single-token refs like {base_url}
|
|
66
|
-
- fix(connectors): jenkins template — preserve cross-refs, refill empty, drop orphans
|
|
67
|
-
- feat(chat): preflight required connector settings with wizard prompt URL
|
|
68
|
-
- template: drop hardcoded default-jenkins from bundled fallback
|
|
69
|
-
- fix(onboarding): bake {user.X} tokens into persisted connector config
|
|
70
|
-
- marketplace: surface enterprise availability even when versions match
|
|
71
|
-
- fix(marketplace): surface update_source_id so Update button shows where it pulls from
|
|
72
|
-
- feat(marketplace): Reinstall-all button — force-flip installed connectors to enterprise
|
|
73
|
-
- feat(connectors): {user.*} global identity tokens + instances merge by name
|
|
74
|
-
- fix(wizard): minimal mode no longer hides required-prompt connector cards
|
|
75
|
-
- feat(marketplace): per-source sync status + PAT-scope diagnostic
|
|
76
|
-
- feat(onboarding): _wizard.minimal flag — hide non-required steps
|
|
77
|
-
- fix(enterprise): dedupe sources, scope monitor, tighten browser probe, default chat agent
|
|
78
|
-
- feat(wizard): data-driven UI — collapse hardcoded sections when template fills them
|
|
79
|
-
- feat(templates): {connector:id.field} cross-ref + _derive in wizard apply
|
|
80
|
-
- refactor(enterprise): unify ftnt → fortinet + dev-test passthrough + secret-scan guard
|
|
81
|
-
- feat(enterprise): obfuscated secrets + _agents preset in wizard template
|
|
82
|
-
- docs(help): enterprise marketplace + multi-source connector flow
|
|
83
|
-
- feat(wizard): per-enterprise onboarding template via resolver chain
|
|
84
|
-
- feat(enterprise): Dashboard badge + add_enterprise_key chat tool
|
|
85
|
-
- feat(ui): enterprise sources in marketplace + Settings panel
|
|
86
|
-
- feat(marketplace): multi-source connector + workflow registries (enterprise keys)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
**Full Changelog**: https://github.com/aiwatching/forge/compare/v0.10.40...v0.10.41
|
|
8
|
+
**Full Changelog**: https://github.com/aiwatching/forge/compare/v0.10.41...v0.10.42
|
|
@@ -449,7 +449,15 @@ function preprocessTemplate(
|
|
|
449
449
|
if (changed) saveSettings({ ...fresh, agents: current });
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
-
// _apiProfiles — same shape rules
|
|
452
|
+
// _apiProfiles — same shape rules + dedup-by-backend.
|
|
453
|
+
//
|
|
454
|
+
// Dedup: if the user already has an apiProfile pointing at the same
|
|
455
|
+
// (baseUrl, model) tuple, skip the template entry even when the IDs
|
|
456
|
+
// differ. This handles wizard-template rename scenarios — e.g. the
|
|
457
|
+
// template changed `forti-k2-chat` (provider: anthropic) into
|
|
458
|
+
// `fortinac-qwen-chat` (provider: litellm). Without this check we'd
|
|
459
|
+
// add the renamed entry alongside the user's existing one, leaving
|
|
460
|
+
// two profiles for the same backend.
|
|
453
461
|
const profilesApplied: string[] = [];
|
|
454
462
|
const profilesDict = template._apiProfiles as Record<string, any> | undefined;
|
|
455
463
|
if (profilesDict && typeof profilesDict === 'object') {
|
|
@@ -457,12 +465,27 @@ function preprocessTemplate(
|
|
|
457
465
|
const current = { ...(fresh.apiProfiles || {}) };
|
|
458
466
|
let changed = false;
|
|
459
467
|
let defaultPick: string | undefined;
|
|
468
|
+
// Build (baseUrl|model) → existing id index for dedup lookup.
|
|
469
|
+
const backendKey = (p: any): string =>
|
|
470
|
+
`${String(p?.baseUrl || '').toLowerCase().replace(/\/+$/, '')}|${String(p?.model || '').toLowerCase()}`;
|
|
471
|
+
const existingByBackend = new Map<string, string>();
|
|
472
|
+
for (const [id, p] of Object.entries(current)) {
|
|
473
|
+
const k = backendKey(p);
|
|
474
|
+
if (k !== '|') existingByBackend.set(k, id);
|
|
475
|
+
}
|
|
460
476
|
for (const [profileId, raw] of Object.entries(profilesDict)) {
|
|
461
477
|
if (!raw || typeof raw !== 'object') continue;
|
|
462
478
|
const overwrite = (raw as any)._overwrite === true;
|
|
463
479
|
if (current[profileId] && !overwrite) continue;
|
|
480
|
+
// Skip if a different-id profile already targets the same (baseUrl, model).
|
|
481
|
+
const tplKey = backendKey(raw);
|
|
482
|
+
if (!overwrite && tplKey !== '|' && existingByBackend.has(tplKey)
|
|
483
|
+
&& existingByBackend.get(tplKey) !== profileId) {
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
464
486
|
const { _overwrite, _default, ...entry } = raw as Record<string, unknown>;
|
|
465
487
|
current[profileId] = entry as unknown as ApiProfile;
|
|
488
|
+
existingByBackend.set(tplKey, profileId);
|
|
466
489
|
profilesApplied.push(profileId);
|
|
467
490
|
changed = true;
|
|
468
491
|
// First entry flagged `_default: true` (or the first applied entry
|
package/app/chat/page.tsx
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
'use client';
|
|
21
21
|
|
|
22
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
22
|
+
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
23
23
|
import MarkdownContent from '@/components/MarkdownContent';
|
|
24
24
|
import WatchesPanel from '@/components/WatchesPanel';
|
|
25
25
|
import type { ContentBlock, Message, Session } from '@/lib/chat/types';
|
|
@@ -497,7 +497,10 @@ function RoleBlock({ role, ts, children }: { role: 'user' | 'assistant'; ts?: nu
|
|
|
497
497
|
);
|
|
498
498
|
}
|
|
499
499
|
|
|
500
|
-
|
|
500
|
+
// Memoized — keystroke in the composer re-renders the page, and without
|
|
501
|
+
// memo every prior message re-runs its markdown parse → typing in a long
|
|
502
|
+
// chat became visibly laggy.
|
|
503
|
+
const MessageView = memo(function MessageView({ m }: { m: Message }) {
|
|
501
504
|
return (
|
|
502
505
|
<RoleBlock role={m.role} ts={m.ts}>
|
|
503
506
|
{m.blocks.map((b, i) => (
|
|
@@ -510,9 +513,9 @@ function MessageView({ m }: { m: Message }) {
|
|
|
510
513
|
)}
|
|
511
514
|
</RoleBlock>
|
|
512
515
|
);
|
|
513
|
-
}
|
|
516
|
+
});
|
|
514
517
|
|
|
515
|
-
function BlockView({ b, role }: { b: ContentBlock; role?: 'user' | 'assistant' }) {
|
|
518
|
+
const BlockView = memo(function BlockView({ b, role }: { b: ContentBlock; role?: 'user' | 'assistant' }) {
|
|
516
519
|
if (b.type === 'text') {
|
|
517
520
|
return <MarkdownContent content={b.text} linkify={role === 'assistant'} />;
|
|
518
521
|
}
|
|
@@ -524,7 +527,7 @@ function BlockView({ b, role }: { b: ContentBlock; role?: 'user' | 'assistant' }
|
|
|
524
527
|
return <ToolResultBlockView content={txt} isError={!!b.is_error} />;
|
|
525
528
|
}
|
|
526
529
|
return null;
|
|
527
|
-
}
|
|
530
|
+
});
|
|
528
531
|
|
|
529
532
|
function ToolUseBlockView({ name, input }: { name: string; input: unknown }) {
|
|
530
533
|
const [open, setOpen] = useState(false);
|
|
@@ -167,7 +167,12 @@ export const anthropicAdapter: LlmAdapter = {
|
|
|
167
167
|
const content: ContentBlock[] = [];
|
|
168
168
|
let textBuf = '';
|
|
169
169
|
for await (const part of result.fullStream) {
|
|
170
|
-
if (part.type === 'text-delta') {
|
|
170
|
+
if (part.type === 'text-delta' || part.type === 'reasoning-delta') {
|
|
171
|
+
// Treat reasoning-delta the same as text-delta — Fortinet
|
|
172
|
+
// relay endpoints (forti-k2, forti-coder, etc.) return their
|
|
173
|
+
// entire response as reasoning blocks instead of regular text.
|
|
174
|
+
// Without this branch the model output silently disappears
|
|
175
|
+
// (assistant message saved with blocks=[]).
|
|
171
176
|
textBuf += part.text;
|
|
172
177
|
cb.onTextDelta(part.text);
|
|
173
178
|
} else if (part.type === 'tool-call') {
|
package/package.json
CHANGED