Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
b7e377e
v0.5.91: docs i18n, turborepo upgrade
waleedlatif1 Feb 16, 2026
da46a38
v0.5.92: shortlinks, copilot scrolling stickiness, pagination
waleedlatif1 Feb 17, 2026
fdca736
v0.5.93: NextJS config changes, MCP and Blocks whitelisting, copilot …
waleedlatif1 Feb 18, 2026
15ace5e
v0.5.94: vercel integration, folder insertion, migrated tracking redi…
waleedlatif1 Feb 19, 2026
67aa4bb
v0.5.95: gemini 3.1 pro, cloudflare, dataverse, revenuecat, redis, up…
waleedlatif1 Feb 20, 2026
34d92fa
v0.5.96: sim oauth provider, slack ephemeral message tool and blockki…
waleedlatif1 Feb 21, 2026
115f04e
v0.5.97: oidc discovery for copilot mcp
waleedlatif1 Feb 21, 2026
0d86ea0
v0.5.98: change detection improvements, rate limit and code execution…
waleedlatif1 Feb 22, 2026
af59234
v0.5.99: local dev improvements, live workflow logs in terminal
waleedlatif1 Feb 23, 2026
67f8a68
v0.5.100: multiple credentials, 40% speedup, gong, attio, audit log i…
waleedlatif1 Feb 25, 2026
4fd0989
v0.5.101: circular dependency mitigation, confluence enhancements, go…
waleedlatif1 Feb 26, 2026
0d2e6ff
v0.5.102: new integrations, new tools, ci speedups, memory leak instr…
waleedlatif1 Feb 28, 2026
e07e3c3
v0.5.103: memory util instrumentation, API docs, amplitude, google pa…
waleedlatif1 Mar 2, 2026
f1ec5fe
v0.5.104: memory improvements, nested subflows, careers page redirect…
waleedlatif1 Mar 4, 2026
70c36cb
v0.5.105: slack remove reaction, nested subflow locks fix, servicenow…
waleedlatif1 Mar 5, 2026
3ce9475
v0.5.106: condition block and legacy kbs fixes, GPT 5.4
icecrasher321 Mar 6, 2026
6586c5c
v0.5.107: new reddit, slack tools
waleedlatif1 Mar 6, 2026
8c0a2e0
v0.5.108: workflow input params in agent tools, bun upgrade, dropdown…
icecrasher321 Mar 7, 2026
ecd3536
v0.5.109: obsidian and evernote integrations, slack fixes, remove mem…
waleedlatif1 Mar 9, 2026
1c2c2c6
v0.5.110: webhook execution speedups, SSRF patches
waleedlatif1 Mar 11, 2026
36612ae
v0.5.111: non-polling webhook execs off trigger.dev, gmail subject he…
icecrasher321 Mar 12, 2026
e9bdc57
v0.5.112: trace spans improvements, fathom integration, jira fixes, c…
waleedlatif1 Mar 12, 2026
4c12914
v0.5.113: jira, ashby, google ads, grain updates
icecrasher321 Mar 13, 2026
84d6fdc
v0.6: mothership, tables, connectors
waleedlatif1 Mar 17, 2026
4f3bc37
v0.6.1: added better auth admin plugin
waleedlatif1 Mar 17, 2026
4bd0731
v0.6.2: mothership stability, chat iframe embedding, KB upserts, new …
waleedlatif1 Mar 18, 2026
30f2d1a
v0.6.3: hubspot integration, kb block improvements
waleedlatif1 Mar 18, 2026
ff7b5b5
v0.6.4: subflows, docusign, ashby new tools, box, workday, billing bu…
waleedlatif1 Mar 19, 2026
9fcd02f
v0.6.5: email validation, integrations page, mothership and custom to…
waleedlatif1 Mar 19, 2026
1731a4d
v0.6.6: landing improvements, styling consistency, mothership table r…
waleedlatif1 Mar 20, 2026
19442f1
v0.6.7: kb improvements, edge z index fix, captcha, new trust center,…
waleedlatif1 Mar 21, 2026
704d06f
v0
Sg312 Mar 22, 2026
8abb884
Fix ppt load
Sg312 Mar 22, 2026
b28556f
Fixes
Sg312 Mar 22, 2026
dde64aa
Fixes
Sg312 Mar 22, 2026
4a537ff
Fix lint
Sg312 Mar 22, 2026
aa9fc10
Fix wid
Sg312 Mar 22, 2026
5954abd
Download image
Sg312 Mar 22, 2026
77a4f2f
Update tools
Sg312 Mar 23, 2026
d071248
Fix lint
Sg312 Mar 23, 2026
844c9a2
Fix error msg
Sg312 Mar 23, 2026
46e8964
Tool fixes
Sg312 Mar 23, 2026
6549a50
Reenable subagent stream
Sg312 Mar 23, 2026
98f4dfd
Subagent stream
Sg312 Mar 23, 2026
1e7a987
Fix edit workflow hydration
Sg312 Mar 23, 2026
0b3000a
Throw func execute error on error
Sg312 Mar 23, 2026
e2d5d27
Rewrite
Sg312 Mar 23, 2026
8934206
Remove promptForToolApproval flag, fix workflow terminal logs
Mar 23, 2026
24fbf41
Fixes
Sg312 Mar 23, 2026
a573ec8
Rebase
Sg312 Mar 23, 2026
7d59763
Fix buffer
Sg312 Mar 23, 2026
035c614
Fix
Sg312 Mar 23, 2026
7dce59e
Fix claimed by
Sg312 Mar 23, 2026
f77c8b4
Cleanup v1
Sg312 Mar 23, 2026
1c0697a
Tool call loop
Sg312 Mar 23, 2026
df7e635
Fixes
Sg312 Mar 23, 2026
39ff907
Fixes
Sg312 Mar 23, 2026
7f83a0c
Fix subaget aborts
Sg312 Mar 24, 2026
772929c
Fix diff
Sg312 Mar 24, 2026
5b7a155
Add delegating state to subagents
Mar 24, 2026
a943010
Fix build
Sg312 Mar 24, 2026
63011d9
Fix sandbox
Sg312 Mar 24, 2026
405f57c
Fix lint
Sg312 Mar 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions apps/sim/app/api/copilot/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,6 @@ export async function POST(req: NextRequest) {
goRoute: '/api/copilot',
autoExecuteTools: true,
interactive: true,
promptForToolApproval: false,
onComplete: async (result: OrchestratorResult) => {
if (!actualChatId) return

Expand All @@ -365,16 +364,25 @@ export async function POST(req: NextRequest) {
const stored: Record<string, unknown> = { type: block.type }
if (block.content) stored.content = block.content
if (block.type === 'tool_call' && block.toolCall) {
const state =
block.toolCall.result?.success !== undefined
? block.toolCall.result.success
? 'success'
: 'error'
: block.toolCall.status
const isSubagentTool = !!block.calledBy
const isNonTerminal =
state === 'cancelled' || state === 'pending' || state === 'executing'
stored.toolCall = {
id: block.toolCall.id,
name: block.toolCall.name,
state:
block.toolCall.result?.success !== undefined
? block.toolCall.result.success
? 'success'
: 'error'
: block.toolCall.status,
result: block.toolCall.result,
state,
...(isSubagentTool && isNonTerminal ? {} : { result: block.toolCall.result }),
...(isSubagentTool && isNonTerminal
? {}
: block.toolCall.params
? { params: block.toolCall.params }
: {}),
...(block.calledBy ? { calledBy: block.calledBy } : {}),
}
}
Expand Down Expand Up @@ -426,7 +434,6 @@ export async function POST(req: NextRequest) {
goRoute: '/api/copilot',
autoExecuteTools: true,
interactive: true,
promptForToolApproval: false,
})

const responseData = {
Expand Down
59 changes: 59 additions & 0 deletions apps/sim/app/api/copilot/chat/stream/route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @vitest-environment node
*/

import { NextRequest } from 'next/server'
import { beforeEach, describe, expect, it, vi } from 'vitest'

const { getStreamMeta, readStreamEvents, authenticateCopilotRequestSessionOnly } = vi.hoisted(
() => ({
getStreamMeta: vi.fn(),
readStreamEvents: vi.fn(),
authenticateCopilotRequestSessionOnly: vi.fn(),
})
)

vi.mock('@/lib/copilot/orchestrator/stream/buffer', () => ({
getStreamMeta,
readStreamEvents,
}))

vi.mock('@/lib/copilot/request-helpers', () => ({
authenticateCopilotRequestSessionOnly,
}))

import { GET } from '@/app/api/copilot/chat/stream/route'

describe('copilot chat stream replay route', () => {
beforeEach(() => {
vi.clearAllMocks()
authenticateCopilotRequestSessionOnly.mockResolvedValue({
userId: 'user-1',
isAuthenticated: true,
})
readStreamEvents.mockResolvedValue([])
})

it('stops replay polling when stream meta becomes cancelled', async () => {
getStreamMeta
.mockResolvedValueOnce({
status: 'active',
userId: 'user-1',
})
.mockResolvedValueOnce({
status: 'cancelled',
userId: 'user-1',
})

const response = await GET(
new NextRequest('http://localhost:3000/api/copilot/chat/stream?streamId=stream-1')
)

const reader = response.body?.getReader()
expect(reader).toBeTruthy()

const first = await reader!.read()
expect(first.done).toBe(true)
expect(getStreamMeta).toHaveBeenCalledTimes(2)
})
})
56 changes: 48 additions & 8 deletions apps/sim/app/api/copilot/chat/stream/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,33 @@ export async function GET(request: NextRequest) {
async start(controller) {
let lastEventId = Number.isFinite(fromEventId) ? fromEventId : 0
let latestMeta = meta
let controllerClosed = false

const closeController = () => {
if (controllerClosed) return
controllerClosed = true
try {
controller.close()
} catch {
// Controller already closed by runtime/client - treat as normal.
}
}

const enqueueEvent = (payload: Record<string, any>) => {
if (controllerClosed) return false
try {
controller.enqueue(encodeEvent(payload))
return true
} catch {
controllerClosed = true
return false
}
}

const abortListener = () => {
controllerClosed = true
}
request.signal.addEventListener('abort', abortListener, { once: true })

const flushEvents = async () => {
const events = await readStreamEvents(streamId, lastEventId)
Expand All @@ -99,37 +126,50 @@ export async function GET(request: NextRequest) {
executionId: latestMeta?.executionId,
runId: latestMeta?.runId,
}
controller.enqueue(encodeEvent(payload))
if (!enqueueEvent(payload)) {
break
}
}
}

try {
await flushEvents()

while (Date.now() - startTime < MAX_STREAM_MS) {
while (!controllerClosed && Date.now() - startTime < MAX_STREAM_MS) {
const currentMeta = await getStreamMeta(streamId)
if (!currentMeta) break
latestMeta = currentMeta

await flushEvents()

if (currentMeta.status === 'complete' || currentMeta.status === 'error') {
if (controllerClosed) {
break
}
if (
currentMeta.status === 'complete' ||
currentMeta.status === 'error' ||
currentMeta.status === 'cancelled'
) {
break
}

if (request.signal.aborted) {
controllerClosed = true
break
}

await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS))
}
} catch (error) {
logger.warn('Stream replay failed', {
streamId,
error: error instanceof Error ? error.message : String(error),
})
if (!controllerClosed && !request.signal.aborted) {
logger.warn('Stream replay failed', {
streamId,
error: error instanceof Error ? error.message : String(error),
})
}
} finally {
controller.close()
request.signal.removeEventListener('abort', abortListener)
closeController()
}
},
})
Expand Down
Loading
Loading