Skip to content

Commit 3f6be08

Browse files
committed
feat(managed-agent): name Anthropic sessions after the origin workflow node
Previously Claude Managed Agent sessions opened untitled, which made them hard to trace back from the Claude Platform side to the workflow that created them. Sessions now open with a human-legible title composed from the Sim workspace, workflow, and node names: sim.ai - <workspace name> - <workflow name> - <block name> Segments whose DB lookup fails (deleted workspace/workflow, unnamed block) are silently dropped so the session still opens with whatever context is available. If nothing beyond the "sim.ai" prefix resolves, we skip the title entirely rather than create a placeholder-titled session. Plumbing: - The generic block handler now also passes `blockId` and `blockName` on `_context` when it invokes a tool. `WorkflowToolExecutionContext` gains matching optional fields — opt-in for any tool that wants to emit externally-visible records tied to their origin node. - `run_session.server.ts` looks up the workspace/workflow names via `@sim/db` at execute time (a two-column SELECT per name; wrapped in a defensive try/catch so a DB blip never blocks session creation). - `buildSessionCreatePayload` already accepts `title` — no session- client change needed.
1 parent bd7dbda commit 3f6be08

3 files changed

Lines changed: 72 additions & 0 deletions

File tree

apps/sim/executor/handlers/generic/generic-handler.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ export class GenericBlockHandler implements BlockHandler {
7070
userId: ctx.userId,
7171
isDeployedContext: ctx.isDeployedContext,
7272
enforceCredentialAccess: ctx.enforceCredentialAccess,
73+
blockId: block.id,
74+
blockName: block.metadata?.name,
7375
},
7476
},
7577
{ executionContext: ctx }

apps/sim/tools/managed_agent/run_session.server.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import 'server-only'
22

3+
import { db } from '@sim/db'
4+
import { workflow as workflowTable, workspace as workspaceTable } from '@sim/db/schema'
35
import { createLogger } from '@sim/logger'
46
import { getErrorMessage } from '@sim/utils/errors'
57
import { sleep } from '@sim/utils/helpers'
8+
import { eq } from 'drizzle-orm'
69
import { readSSEEvents } from '@/lib/core/utils/sse'
710
import { getDecryptedApiKey } from '@/lib/managed-agents/connections'
811
import { env } from '@/lib/core/config/env'
@@ -133,11 +136,22 @@ const impl: ManagedAgentServerImpl = async (
133136
const files = normalizeFiles(params.files)
134137
const sessionParameters = normalizeSessionParameters(params.sessionParameters)
135138

139+
// Human-legible session title so runs are traceable from the Claude
140+
// Platform side back to the Sim workflow node that opened them.
141+
// Failure to look up either name is tolerated — we just drop that
142+
// segment from the title so the session still opens.
143+
const title = await buildSessionTitle({
144+
workspaceId,
145+
workflowId: context?.workflowId,
146+
blockName: context?.blockName,
147+
})
148+
136149
const createSessionInput = {
137150
apiKey,
138151
agentId: params.agent,
139152
environmentId: params.environment,
140153
envType,
154+
...(title ? { title } : {}),
141155
...(vaultIds.length > 0 ? { vaultIds } : {}),
142156
...(memoryStoreId ? { memoryStoreId } : {}),
143157
...(memoryStoreId && memoryAccess ? { memoryAccess } : {}),
@@ -478,3 +492,51 @@ function isPayloadDebugEnabled(): boolean {
478492
const normalized = String(raw).toLowerCase()
479493
return normalized === '1' || normalized === 'true' || normalized === 'yes'
480494
}
495+
496+
/**
497+
* Assemble the Anthropic session title from the Sim workspace, workflow,
498+
* and node names — so a session listed on Claude Platform links back to
499+
* the origin.
500+
*
501+
* Shape: `sim.ai - <workspace> - <workflow> - <block>`. Segments whose
502+
* lookups fail (workspace/workflow deleted, block unnamed) are dropped
503+
* so the title still opens something useful. If we can't build any
504+
* meaningful title, return `undefined` — the session creates untitled.
505+
*/
506+
async function buildSessionTitle(input: {
507+
workspaceId: string
508+
workflowId?: string
509+
blockName?: string
510+
}): Promise<string | undefined> {
511+
const [workspaceName, workflowName] = await Promise.all([
512+
fetchNameSafely(workspaceTable, workspaceTable.id, input.workspaceId),
513+
input.workflowId
514+
? fetchNameSafely(workflowTable, workflowTable.id, input.workflowId)
515+
: Promise.resolve(undefined),
516+
])
517+
const segments = ['sim.ai']
518+
if (workspaceName) segments.push(workspaceName)
519+
if (workflowName) segments.push(workflowName)
520+
if (input.blockName?.trim()) segments.push(input.blockName.trim())
521+
if (segments.length === 1) return undefined // only the "sim.ai" prefix — not useful
522+
return segments.join(' - ')
523+
}
524+
525+
async function fetchNameSafely<T extends { name: unknown }>(
526+
table: { name: unknown } & Record<string, unknown>,
527+
idColumn: unknown,
528+
id: string
529+
): Promise<string | undefined> {
530+
try {
531+
const rows = (await db
532+
.select({ name: (table as { name: unknown }).name })
533+
.from(table as never)
534+
.where(eq(idColumn as never, id))
535+
.limit(1)) as Array<{ name: string | null }>
536+
const name = rows[0]?.name
537+
return typeof name === 'string' && name.trim().length > 0 ? name.trim() : undefined
538+
} catch (err) {
539+
logger.warn('Failed to look up name for session title', { id, error: getErrorMessage(err) })
540+
return undefined
541+
}
542+
}

apps/sim/tools/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ export type WorkflowToolExecutionContext = {
5151
workflowId?: string
5252
executionId?: string
5353
userId?: string
54+
/**
55+
* Optional block identity injected by the generic handler at execute
56+
* time. Tools that create externally-visible records (e.g. a Claude
57+
* Platform Managed Agent session) use these to build human-legible
58+
* titles that link the record back to the source workflow node.
59+
*/
60+
blockId?: string
61+
blockName?: string
5462
}
5563

5664
export type OutputType =

0 commit comments

Comments
 (0)