MCPcopy
hub / github.com/coder/mux / generateWorkspaceIdentity

Function generateWorkspaceIdentity

src/node/services/workspaceTitleGenerator.ts:181–287  ·  view source on GitHub ↗
(
  message: string,
  candidates: string[],
  aiService: AIService,
  /** Optional conversation turns context used for regenerate-title prompts. */
  conversationContext?: string,
  /** Optional most recent user message; included as additional context only — not given precedence over older turns. */
  latestUserMessage?: string
)

Source from the content-addressed store, hash-verified

179}
180
181export async function generateWorkspaceIdentity(
182 message: string,
183 candidates: string[],
184 aiService: AIService,
185 /** Optional conversation turns context used for regenerate-title prompts. */
186 conversationContext?: string,
187 /** Optional most recent user message; included as additional context only — not given precedence over older turns. */
188 latestUserMessage?: string
189): Promise<Result<GenerateWorkspaceIdentityResult, NameGenerationError>> {
190 if (candidates.length === 0) {
191 return Err({ type: "unknown", raw: "No model candidates provided for name generation" });
192 }
193
194 // Try up to 3 candidates
195 const maxAttempts = Math.min(candidates.length, 3);
196
197 // Track the last classified error to return if all candidates fail
198 let lastError: NameGenerationError | null = null;
199
200 for (let i = 0; i < maxAttempts; i++) {
201 const modelString = candidates[i];
202
203 const modelResult = await aiService.createModel(modelString, undefined, {
204 agentInitiated: true,
205 });
206 if (!modelResult.success) {
207 lastError = mapModelCreationError(modelResult.error, modelString);
208 log.debug(`Name generation: skipping ${modelString} (${modelResult.error.type})`);
209 continue;
210 }
211
212 try {
213 // Use streamText with a propose_name tool instead of Output.object().
214 // Tool calls are universally supported across LLM APIs and far more
215 // reliable than structured JSON output, eliminating all the fragile
216 // regex fallback parsing that was previously needed.
217 //
218 // streamText (not generateText): the Codex OAuth endpoint requires
219 // stream:true in the request body; streamText sets it automatically.
220 //
221 // No toolChoice — forced tool choice (toolChoice: "required" / "any" /
222 // { type: "tool" }) is incompatible with extended thinking models.
223 // Instead, the prompt instructs the model to call the tool, and the
224 // name_workspace builtin agent declares tools.require: [propose_name]
225 // which the StreamManager enforces via stopWhen for full agent sessions.
226 // For this direct streamText path, the candidate retry loop handles the
227 // (rare) case where the model ignores the instruction.
228 const currentStream = streamText({
229 model: modelResult.data,
230 prompt: buildWorkspaceIdentityPrompt(message, conversationContext, latestUserMessage),
231 tools: {
232 // Defined inline so TypeScript preserves full schema inference on
233 // toolResult.output (the propose_name tool is only used here).
234 propose_name: tool({
235 description: TOOL_DEFINITIONS.propose_name.description,
236 inputSchema: ProposeNameToolArgsSchema,
237 // eslint-disable-next-line @typescript-eslint/require-await -- AI SDK Tool.execute must return a Promise
238 execute: async (args) => ({ success: true as const, ...args }),

Callers 4

regenerateTitleMethod · 0.90
routerFunction · 0.90

Calls 11

ErrFunction · 0.90
OkFunction · 0.90
runLanguageModelCleanupFunction · 0.90
mapModelCreationErrorFunction · 0.85
generateNameSuffixFunction · 0.85
sanitizeBranchNameFunction · 0.85
mapNameGenerationErrorFunction · 0.85
debugMethod · 0.80
createModelMethod · 0.65
toolFunction · 0.50

Tested by

no test coverage detected