Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1b597b0
fix(sandbox): upgrade isolated-vm to 6.2.0 (#7125)
icecrasher321 Aug 26, 2026
adbb9b6
fix(fireflies): handle nullable transcript metadata (#7129)
waleedlatif1 Aug 26, 2026
2d4dfcf
fix(knowledge): stabilize skipped connector documents (#7130)
waleedlatif1 Aug 26, 2026
c930830
fix(v2,cli): second audit pass over the v2 API and CLI (#7126)
waleedlatif1 Aug 26, 2026
af7a12c
improvement(cli): automate npm package version bumps (#7127)
TheodoreSpeaks Aug 26, 2026
cc5a39a
fix(landing): stabilize previews across browsers (#7131)
waleedlatif1 Aug 26, 2026
562d918
refactor(billing): retire protocol rollout flags (#7132)
icecrasher321 Aug 26, 2026
1afaf46
fix(knowledge): harden OCR and SharePoint ingestion limits (#7135)
waleedlatif1 Aug 26, 2026
c2d9423
improvement(canvas): improve code tooltip readability (#7138)
waleedlatif1 Aug 26, 2026
d9b08e9
feat(library): Best Dify Alternatives in 2026 (#7140)
icecrasher321 Aug 27, 2026
18e0532
fix(landing): harden cross-browser compatibility (#7139)
waleedlatif1 Aug 27, 2026
40ca081
docs(blog): update enterprise (#7141)
icecrasher321 Aug 27, 2026
f02ee99
feat(desktop,cli): support self-hosted desktop installs (#7136)
waleedlatif1 Aug 27, 2026
6bbabbb
fix(cli): close follow-up gaps (#7137)
waleedlatif1 Aug 27, 2026
4e1e291
fix(landing): normalize typography weights (#7144)
waleedlatif1 Aug 27, 2026
ea93f6b
fix(desktop-browser): add recoverable page failure states (#7142)
waleedlatif1 Aug 27, 2026
895d40e
improvement(blocks): document child workflow deployment behavior (#7145)
TheodoreSpeaks Aug 27, 2026
be14d27
fix(copilot): persist workflow drafts across navigation (#6807)
BillLeoutsakosvl346 Aug 27, 2026
0d30c6d
feat(chat): add live voice input waveform (#7146)
waleedlatif1 Aug 27, 2026
5112b07
improvement(db): stop declaring retired usage columns ahead of their …
icecrasher321 Aug 27, 2026
76c7be5
fix(ui): preserve resource view collapse state (#7147)
waleedlatif1 Aug 27, 2026
27b97cb
fix(ui): optimize universal paste handling (#7148)
waleedlatif1 Aug 27, 2026
752c21c
fix(landing): reset unmeasurable preview stages (#7153)
waleedlatif1 Aug 27, 2026
8142208
fix(mistral): distinguish response size failures (#7154)
waleedlatif1 Aug 27, 2026
a3705a7
fix(chat): guide blocked browser microphone access (#7155)
waleedlatif1 Aug 27, 2026
b44b537
docs(self-hosting): correct what a server change clears, and the cert…
waleedlatif1 Aug 27, 2026
286b30e
fix(ui): close paste admission edge cases (#7156)
waleedlatif1 Aug 27, 2026
e906190
fix(cli): refuse what the help already says is invalid (#7157)
waleedlatif1 Aug 27, 2026
2fb768d
fix(copilot): scope panel chat drafts per chat (#7159)
waleedlatif1 Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
21 changes: 7 additions & 14 deletions .github/workflows/publish-sim-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:

concurrency:
group: publish-sim-cli-${{ github.ref }}
cancel-in-progress: true
cancel-in-progress: false

jobs:
publish-npm:
Expand Down Expand Up @@ -50,6 +50,9 @@ jobs:
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
run: bun pm whoami

- name: Auto-bump package version
run: bun run bump:npm-packages sim-cli

- name: Run tests
working-directory: packages/sim-cli
run: bun run test
Expand Down Expand Up @@ -115,35 +118,25 @@ jobs:
tar -xzf "$PACKAGE_PATH" -C "$SMOKE_DIR"
"$SMOKE_DIR/package/dist/index.js" --version

- name: Check if version already exists
id: version_check
- name: Verify version is unpublished
working-directory: packages/sim-cli
env:
VERSION: ${{ steps.release.outputs.version }}
run: |
if bun pm view "sim@$VERSION" version > /dev/null 2>&1; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "sim@$VERSION is already published. The automatic version bump did not produce a unique release." >&2
exit 1
fi

- name: Publish to npm
if: steps.version_check.outputs.exists == 'false'
working-directory: packages/sim-cli
env:
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TAG: ${{ steps.release.outputs.tag }}
run: bun publish --access public --tag "$NPM_TAG" --no-save

- name: Summarize release
if: steps.version_check.outputs.exists == 'false'
env:
VERSION: ${{ steps.release.outputs.version }}
NPM_TAG: ${{ steps.release.outputs.tag }}
run: echo "Published sim@$VERSION with the '$NPM_TAG' tag."

- name: Summarize skipped release
if: steps.version_check.outputs.exists == 'true'
env:
VERSION: ${{ steps.release.outputs.version }}
run: echo "Skipped sim@$VERSION because that version is already published."
5 changes: 4 additions & 1 deletion .github/workflows/publish-sim-setup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ jobs:
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
run: bun pm whoami

- name: Auto-bump package version
run: bun run bump:npm-packages sim-setup

- name: Check generated deployment config
run: bun run deployment-config:check

Expand Down Expand Up @@ -156,7 +159,7 @@ jobs:
VERSION: ${{ steps.release.outputs.version }}
run: |
if bun pm view "sim-setup@$VERSION" version > /dev/null 2>&1; then
echo "sim-setup@$VERSION is already published. Bump packages/sim-setup/package.json before releasing another build." >&2
echo "sim-setup@$VERSION is already published. The automatic version bump did not produce a unique release." >&2
exit 1
fi

Expand Down
10 changes: 9 additions & 1 deletion apps/desktop/e2e/smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,15 @@ test.describe('desktop shell smoke', () => {
await expect(window.locator('.wordmark')).toBeVisible()
await expect(window.locator('.wordmark')).toHaveAttribute('aria-label', 'Sim')
await expect(window.locator('#title')).toHaveText('Can’t connect to Sim')
await expect(window.locator('#status')).toHaveText('Check status')
// The recovery path for a self-hosted shell pointed at a server it cannot
// reach. Exercised end to end here because it is the only coverage of the
// `server:` local-page IPC gate: the bundled page reads the configuration
// over the real preload bridge, and status.sim.ai is withheld because this
// origin is not one of Sim's own. `toBeHidden` is load-bearing — the page's
// own `button { display: inline-flex }` outranks the UA `[hidden]` rule, so
// the attribute alone does not hide it.
await expect(window.locator('#server')).toBeVisible()
await expect(window.locator('#status')).toBeHidden()
await expect
.poll(() => window.evaluate(() => document.fonts.check('16px "Season Sans"')))
.toBe(true)
Expand Down
53 changes: 53 additions & 0 deletions apps/desktop/src/main/browser-agent/driver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,59 @@ describe('executeTool', () => {
)
})

it('publishes main-frame load failures and retries their uncommitted URL', async () => {
const onPageState = vi.fn()
const win = new BrowserWindow()
driver.initDriver(
{
onPageState,
onTabsState: vi.fn(),
onSessionStatus: vi.fn(),
onFillAvailability: vi.fn(),
},
() => win
)
driver.activateBrowserScope('chat-test')
await driver.executeTool('chat-test', 'browser_open_tab', {})
const contents = session.requireTab().view.webContents
const eventHandlers = (contents.on as unknown as ReturnType<typeof vi.fn>).mock.calls
const failLoad = eventHandlers.find(([eventName]) => eventName === 'did-fail-load')?.[1] as
| ((...args: unknown[]) => void)
| undefined
const failedUrl = 'http://localhost:3004/login'

onPageState.mockClear()
failLoad?.({}, -102, 'ERR_CONNECTION_REFUSED', failedUrl, false)
failLoad?.({}, -3, 'ERR_ABORTED', failedUrl, true)
expect(onPageState).not.toHaveBeenCalled()

failLoad?.({}, -102, 'ERR_CONNECTION_REFUSED', failedUrl, true)

expect(onPageState).toHaveBeenLastCalledWith(
expect.objectContaining({
url: failedUrl,
issue: {
kind: 'load-error',
code: -102,
description: 'ERR_CONNECTION_REFUSED',
url: failedUrl,
},
})
)

vi.mocked(contents.loadURL).mockClear()
await driver.handlePanelAction('chat-test', { action: 'reload' })
expect(contents.loadURL).toHaveBeenCalledWith(failedUrl)

vi.mocked(contents.loadURL).mockClear()
await driver.executeTool('chat-test', 'browser_go_back', {})
expect(session.pageIssueForContents(contents)).toBeUndefined()
expect(session.canGoForward(contents)).toBe(true)

await driver.executeTool('chat-test', 'browser_go_forward', {})
expect(contents.loadURL).toHaveBeenCalledWith(failedUrl)
})

it('forces fill availability to replay on scope activation and tab switches', async () => {
const refreshAvailability = vi
.spyOn(fillCoordinator()!, 'refreshAvailability')
Expand Down
51 changes: 37 additions & 14 deletions apps/desktop/src/main/browser-agent/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,16 @@ function recordNotice(notice: string): void {
* navigations and tab switches.
*/
function pageStateFor(contents: WebContents, tabId: string): BrowserPageState {
const issue = session.pageIssueForContents(contents)
return {
scopeId: session.getBrowserScopeId(),
tabId,
url: contents.getURL(),
title: contents.getTitle(),
loading: contents.isLoadingMainFrame(),
canGoBack: contents.navigationHistory.canGoBack(),
canGoForward: contents.navigationHistory.canGoForward(),
url: issue?.url ?? contents.getURL(),
title: issue?.kind === 'load-error' ? '' : contents.getTitle(),
loading: issue ? false : contents.isLoadingMainFrame(),
canGoBack: session.canGoBack(contents),
canGoForward: session.canGoForward(contents),
...(issue ? { issue } : {}),
}
}

Expand Down Expand Up @@ -346,6 +348,18 @@ function instrumentTab(contents: WebContents): void {
pushTabsState()
})
)
contents.on(
'did-fail-load',
inScope((_event, errorCode, errorDescription, validatedURL, isMainFrame) => {
if (!isMainFrame || errorCode === 0 || errorCode === -3) return
session.recordPageLoadFailure(contents, {
kind: 'load-error',
code: errorCode,
description: errorDescription,
url: validatedURL || contents.getURL(),
})
})
)
contents.on(
'did-frame-navigate',
inScope(
Expand All @@ -364,7 +378,6 @@ function instrumentTab(contents: WebContents): void {
for (const event of [
'did-navigate-in-page',
'page-title-updated',
'did-start-loading',
'did-finish-load',
'did-stop-loading',
] as const) {
Expand All @@ -376,6 +389,14 @@ function instrumentTab(contents: WebContents): void {
})
)
}
contents.on(
'did-start-loading',
inScope(() => {
session.notePageLoadStarted(contents)
pushPageState(contents)
pushTabsState()
})
)
driverCallbacks?.onSessionStatus(true, scopeId)
}

Expand Down Expand Up @@ -435,6 +456,7 @@ export function initDriver(
// The fill affordance belongs to whichever page is in front.
void fillCoordinator()?.refreshAvailability(true)
},
onPageStateChanged: pushPageState,
onTabsChanged: pushTabsState,
onTabThemeChanged: (contents, theme) => {
void cdp.setColorScheme(contents, theme).catch((error) => {
Expand Down Expand Up @@ -1963,19 +1985,20 @@ async function executeToolInner(
case 'browser_go_forward': {
invalidateSnapshot()
const contents = session.requireAutomationTab().view.webContents
const history = contents.navigationHistory
assertCurrentExecution()
let completion: Promise<void>
if (tool === 'browser_go_back') {
if (!history.canGoBack()) throw new ToolError('Cannot go back — no earlier history entry.')
if (!session.canGoBack(contents)) {
throw new ToolError('Cannot go back — no earlier history entry.')
}
completion = waitForLoadComplete(contents, NAVIGATION_TIMEOUT_MS)
history.goBack()
session.goBack(contents)
} else {
if (!history.canGoForward()) {
if (!session.canGoForward(contents)) {
throw new ToolError('Cannot go forward — no later history entry.')
}
completion = waitForLoadComplete(contents, NAVIGATION_TIMEOUT_MS)
history.goForward()
session.goForward(contents)
}
return await navigationResult(contents, completion)
}
Expand Down Expand Up @@ -3814,13 +3837,13 @@ export async function handlePanelAction(
const contents = tab.view.webContents
switch (action.action) {
case 'reload':
contents.reload()
session.reloadPage(contents)
return
case 'back':
if (contents.navigationHistory.canGoBack()) contents.navigationHistory.goBack()
session.goBack(contents)
return
case 'forward':
if (contents.navigationHistory.canGoForward()) contents.navigationHistory.goForward()
session.goForward(contents)
return
case 'print':
contents.print({ printBackground: true })
Expand Down
Loading
Loading