File size: 3,302 Bytes
d47b053
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import type {
  StudioAssistantMessage,
  StudioPermissionRequest,
  StudioProcessorStreamEvent,
  StudioRun,
  StudioRuntimeTurnPlan,
  StudioSession,
  StudioSessionStore,
  StudioTaskStore,
  StudioWorkResultStore,
  StudioWorkStore
} from '../../domain/types'
import type { StudioPermissionService } from '../../permissions/permission-service'
import type { StudioToolRegistry } from '../../tools/registry'
import { createStudioToolCallExecutionEvents } from '../tools/tool-call-adapter'
import type {
  StudioResolvedSkill,
  StudioRuntimeBackedToolContext,
  StudioSubagentRunRequest,
  StudioSubagentRunResult
} from '../tools/tool-runtime-context'
import type { CustomApiConfig } from '../../../types'
import { throwIfStudioRunCancelled } from './run-cancellation'

interface StudioTurnExecutionOptions {
  projectId: string
  session: StudioSession
  run: StudioRun
  assistantMessage: StudioAssistantMessage
  plan: StudioRuntimeTurnPlan
  registry: StudioToolRegistry
  eventBus: StudioRuntimeBackedToolContext['eventBus']
  permissionService?: StudioPermissionService
  sessionStore?: StudioSessionStore
  taskStore?: StudioTaskStore
  workStore?: StudioWorkStore
  workResultStore?: StudioWorkResultStore
  askForConfirmation?: (request: StudioPermissionRequest) => Promise<'once' | 'always' | 'reject'>
  runSubagent?: (input: StudioSubagentRunRequest) => Promise<StudioSubagentRunResult>
  resolveSkill?: (name: string, session: StudioSession) => Promise<StudioResolvedSkill>
  setToolMetadata: (callId: string, metadata: { title?: string; metadata?: Record<string, unknown> }) => void
  customApiConfig?: CustomApiConfig
  abortSignal?: AbortSignal
}

export async function* createStudioTurnExecutionStream(
  input: StudioTurnExecutionOptions
): AsyncGenerator<StudioProcessorStreamEvent> {
  const hasAssistantText = Boolean(input.plan.assistantText?.trim())

  if (hasAssistantText) {
    yield { type: 'text-start' }
    yield { type: 'text-delta', text: input.plan.assistantText ?? '' }
    yield { type: 'text-end' }
  }

  for (const toolCall of input.plan.toolCalls ?? []) {
    throwIfStudioRunCancelled(input.abortSignal)
    const toolInput = asToolInput(toolCall.input)
    yield* createStudioToolCallExecutionEvents({
      projectId: input.projectId,
      session: input.session,
      run: input.run,
      assistantMessage: input.assistantMessage,
      toolCallId: toolCall.callId,
      toolName: toolCall.toolName,
      toolInput,
      registry: input.registry,
      eventBus: input.eventBus,
      permissionService: input.permissionService,
      sessionStore: input.sessionStore,
      taskStore: input.taskStore,
      workStore: input.workStore,
      workResultStore: input.workResultStore,
      askForConfirmation: input.askForConfirmation,
      runSubagent: input.runSubagent,
      resolveSkill: input.resolveSkill,
      setToolMetadata: input.setToolMetadata,
      customApiConfig: input.customApiConfig,
      abortSignal: input.abortSignal,
      commentary: hasAssistantText ? null : undefined
    })
  }

  yield { type: 'finish-step' }
}

function asToolInput(input: unknown): Record<string, unknown> {
  if (input && typeof input === 'object' && !Array.isArray(input)) {
    return input as Record<string, unknown>
  }
  return {}
}