File size: 4,674 Bytes
4c76b0d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import { stringifyRouteThreadId } from "../../../plugin-sdk/channel-route.js";
import type { BootstrapContextMode } from "../../bootstrap-files.js";
import { normalizeSpawnedRunMetadata } from "../../spawned-context.js";
import type { SubagentLaunchAuthorization } from "./subagent-launch-authorization.js";
import { resolveSubagentAgentGatewayTimeoutMs } from "./subagent-spawn-gateway.js";
import { AGENT_LANE_SUBAGENT } from "./subagent-spawn.runtime.js";
import type { SpawnSubagentMode } from "./subagent-spawn.types.js";
import type { SubagentCompletionMode } from "./subagent-system-prompt.js";

export function buildSubagentLaunchRequest(params: {
  completionMode: SubagentCompletionMode;
  spawnMode: SpawnSubagentMode;
  message: string;
  spawnedByKey: string;
  toolSpawnMetadata: Parameters<typeof normalizeSpawnedRunMetadata>[0];
  spawnedWorkspaceDir?: string;
  childSessionKey: string;
  childSessionOrigin?: {
    channel?: string;
    to?: string;
    accountId?: string;
    threadId?: string | number;
  };
  childIdem: string;
  outputSchema?: Record<string, unknown>;
  childSystemPrompt: string;
  thinkingOverride?: string;
  runTimeoutSeconds: number;
  lightContext: boolean;
  requesterOrigin?: {
    channel?: string;
    accountId?: string;
    to?: string;
    threadId?: string | number;
  };
  currentMessagingTarget?: string;
  currentChannelId?: string;
  currentMessageId?: string | number;
  launchAuthorization?: SubagentLaunchAuthorization;
  swarmSchedulerGroupKey?: string;
  swarmMaxConcurrent: number;
}): {
  childLaunch: {
    request: Record<string, unknown>;
    authorization?: SubagentLaunchAuthorization;
    timeoutMs: number;
  };
  queuedLaunch?: {
    request: Record<string, unknown>;
    authorization?: SubagentLaunchAuthorization;
    timeoutMs: number;
    schedulerGroupKey: string;
    maxConcurrent: number;
  };
  progressOrigin: {
    channel?: string;
    accountId?: string;
    to?: string;
    threadId?: string | number;
    channelId?: string;
    messageId?: string | number;
  };
  spawnedMetadata: ReturnType<typeof normalizeSpawnedRunMetadata>;
} {
  const bootstrapContextMode: BootstrapContextMode | undefined = params.lightContext
    ? "lightweight"
    : undefined;
  const collect = params.completionMode === "collector";
  const spawnedMetadata = normalizeSpawnedRunMetadata({
    spawnedBy: params.spawnedByKey,
    ...params.toolSpawnMetadata,
    workspaceDir: params.spawnedWorkspaceDir,
  });
  const {
    spawnedBy: _spawnedBy,
    workspaceDir: _workspaceDir,
    ...publicSpawnedMetadata
  } = spawnedMetadata;
  const request: Record<string, unknown> = {
    message: params.message,
    sessionKey: params.childSessionKey,
    ...(collect
      ? {}
      : {
          channel: params.childSessionOrigin?.channel,
          to: params.childSessionOrigin?.to ?? undefined,
          accountId: params.childSessionOrigin?.accountId ?? undefined,
          threadId:
            params.childSessionOrigin?.threadId != null
              ? stringifyRouteThreadId(params.childSessionOrigin.threadId)
              : undefined,
        }),
    idempotencyKey: params.childIdem,
    deliver: params.completionMode === "thread-direct",
    lane: AGENT_LANE_SUBAGENT,
    disableMessageTool: true,
    swarmCollector: collect,
    swarmOutputSchema: params.outputSchema,
    cleanupBundleMcpOnRunEnd: params.spawnMode !== "session",
    extraSystemPrompt: params.childSystemPrompt,
    thinking: params.thinkingOverride,
    timeout: params.runTimeoutSeconds,
    // Creation owns the label; delayed launches must preserve operator renames.
    ...(bootstrapContextMode
      ? {
          bootstrapContextMode,
          bootstrapContextRunKind: "default" as const,
        }
      : {}),
    ...publicSpawnedMetadata,
  };
  const childLaunch = {
    request,
    ...(params.launchAuthorization ? { authorization: params.launchAuthorization } : {}),
    timeoutMs: resolveSubagentAgentGatewayTimeoutMs(params.runTimeoutSeconds),
  };
  const queuedLaunch =
    collect && params.swarmSchedulerGroupKey
      ? {
          ...childLaunch,
          schedulerGroupKey: params.swarmSchedulerGroupKey,
          maxConcurrent: params.swarmMaxConcurrent,
        }
      : undefined;
  return {
    childLaunch,
    queuedLaunch,
    progressOrigin: {
      channel: params.requesterOrigin?.channel,
      accountId: params.requesterOrigin?.accountId,
      to: params.currentMessagingTarget ?? params.requesterOrigin?.to,
      threadId: params.requesterOrigin?.threadId,
      channelId: params.currentChannelId,
      messageId: params.currentMessageId,
    },
    spawnedMetadata,
  };
}