@vibes.diy/api-svc 14.3.16 → 14.3.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/intern/agent-dispatcher.d.ts +1 -0
  2. package/intern/agent-dispatcher.js +2 -0
  3. package/intern/agent-dispatcher.js.map +1 -1
  4. package/intern/agent-session.d.ts +22 -1
  5. package/intern/agent-session.js +19 -1
  6. package/intern/agent-session.js.map +1 -1
  7. package/intern/agent-wake.d.ts +1 -0
  8. package/intern/agent-wake.js +1 -0
  9. package/intern/agent-wake.js.map +1 -1
  10. package/intern/builder-model.d.ts +4 -1
  11. package/intern/builder-model.js +12 -6
  12. package/intern/builder-model.js.map +1 -1
  13. package/intern/eval-handles.d.ts +1 -1
  14. package/intern/eval-handles.js +3 -3
  15. package/intern/eval-handles.js.map +1 -1
  16. package/intern/get-model-defaults.d.ts +1 -0
  17. package/intern/get-model-defaults.js +13 -0
  18. package/intern/get-model-defaults.js.map +1 -1
  19. package/intern/in-flight-turns.d.ts +1 -1
  20. package/intern/in-flight-turns.js +1 -1
  21. package/intern/in-flight-turns.js.map +1 -1
  22. package/intern/mint-fire-core.d.ts +2 -0
  23. package/intern/mint-fire-core.js +2 -0
  24. package/intern/mint-fire-core.js.map +1 -1
  25. package/intern/process-access-bindings.js +1 -1
  26. package/intern/process-access-bindings.js.map +1 -1
  27. package/intern/prompt-usage.js +20 -0
  28. package/intern/prompt-usage.js.map +1 -1
  29. package/intern/subagent-model.d.ts +20 -0
  30. package/intern/subagent-model.js +30 -0
  31. package/intern/subagent-model.js.map +1 -0
  32. package/intern/usage-rounds.d.ts +7 -0
  33. package/intern/usage-rounds.js +20 -0
  34. package/intern/usage-rounds.js.map +1 -0
  35. package/models.json +63 -1
  36. package/package.json +12 -12
  37. package/public/agent-turn-action.d.ts +30 -0
  38. package/public/agent-turn-action.js +374 -0
  39. package/public/agent-turn-action.js.map +1 -0
  40. package/public/agent-turn-assembly.d.ts +34 -0
  41. package/public/agent-turn-assembly.js +199 -0
  42. package/public/agent-turn-assembly.js.map +1 -0
  43. package/public/append-block-event.d.ts +31 -0
  44. package/public/append-block-event.js +121 -0
  45. package/public/append-block-event.js.map +1 -0
  46. package/public/auto-publish-on-persist.d.ts +10 -0
  47. package/public/auto-publish-on-persist.js +40 -0
  48. package/public/auto-publish-on-persist.js.map +1 -0
  49. package/public/codegen-turn-route.d.ts +40 -0
  50. package/public/codegen-turn-route.js +177 -0
  51. package/public/codegen-turn-route.js.map +1 -0
  52. package/public/draft-runtime.js +12 -2
  53. package/public/draft-runtime.js.map +1 -1
  54. package/public/guest-build-generation.js +2 -1
  55. package/public/guest-build-generation.js.map +1 -1
  56. package/public/handle-prompt-context.d.ts +41 -0
  57. package/public/handle-prompt-context.js +397 -0
  58. package/public/handle-prompt-context.js.map +1 -0
  59. package/public/handler-llm-request.d.ts +48 -0
  60. package/public/handler-llm-request.js +363 -0
  61. package/public/handler-llm-request.js.map +1 -0
  62. package/public/image-gen.js +1 -1
  63. package/public/image-gen.js.map +1 -1
  64. package/public/llm-dispatch-fallback.d.ts +46 -0
  65. package/public/llm-dispatch-fallback.js +293 -0
  66. package/public/llm-dispatch-fallback.js.map +1 -0
  67. package/public/llm-dispatch.d.ts +5 -180
  68. package/public/llm-dispatch.js +5 -1158
  69. package/public/llm-dispatch.js.map +1 -1
  70. package/public/open-chat.d.ts +2 -0
  71. package/public/open-chat.js +51 -7
  72. package/public/open-chat.js.map +1 -1
  73. package/public/prompt-chat-section.js +119 -1167
  74. package/public/prompt-chat-section.js.map +1 -1
  75. package/public/prompt-section-actions.d.ts +41 -0
  76. package/public/prompt-section-actions.js +161 -0
  77. package/public/prompt-section-actions.js.map +1 -0
  78. package/public/prompt-turn-envelope.d.ts +26 -0
  79. package/public/prompt-turn-envelope.js +120 -0
  80. package/public/prompt-turn-envelope.js.map +1 -0
  81. package/public/prompt-turn-settled.d.ts +32 -0
  82. package/public/prompt-turn-settled.js +157 -0
  83. package/public/prompt-turn-settled.js.map +1 -0
  84. package/public/prompt-turn-terminal.d.ts +22 -0
  85. package/public/prompt-turn-terminal.js +135 -0
  86. package/public/prompt-turn-terminal.js.map +1 -0
  87. package/public/prompt-turn-types.d.ts +37 -0
  88. package/public/prompt-turn-types.js +2 -0
  89. package/public/prompt-turn-types.js.map +1 -0
  90. package/public/social-store.js.map +1 -1
  91. package/public/stream-assembly.js +33 -51
  92. package/public/stream-assembly.js.map +1 -1
@@ -2,130 +2,32 @@ import { checkBuildAuth } from "../check-build-auth.js";
2
2
  import { handleGuestPrompt } from "./guest-build-generation.js";
3
3
  import { Result, Option, EventoResult, exception2Result } from "@adviser/cement";
4
4
  import { scopey } from "@adviser/scopey";
5
- import { isReqCreationPromptChatSection, isReqPromptApplicationChatSection, isReqPromptFSChatSection, isReqPromptImageChatSection, isReqPromptLLMChatSection, reqPromptChatSection, canonicalModelUsage, isBuildNotifyMode, isSourceChangingMode, shouldNotify, RESEARCH_BLOCK_SEQ, } from "@vibes.diy/api-types";
5
+ import { isReqCreationPromptChatSection, reqPromptChatSection, isBuildNotifyMode, isSourceChangingMode, } from "@vibes.diy/api-types";
6
6
  import { type } from "arktype";
7
- import { captureAccessVerdicts, captureCodegenTurnSettled } from "../analytics.js";
8
- import { releaseAccessVerdicts } from "../intern/access-verdict-docs.js";
9
- import { renderAccessVerdictLine } from "../intern/access-verdicts.js";
10
- import { dispatchLookLint, loadTurnFilesByFsId, lookLintPlanned } from "../intern/look-lint.js";
11
- import { postMakerFact } from "../intern/post-maker-fact.js";
12
- import { postCapiBuildEvent } from "../intern/capi-build-event.js";
13
- import { classifyTurnReach, projectTurnReach } from "../intern/turn-reach.js";
14
- import { loadNotificationPrefs } from "../intern/notification-prefs.js";
15
- import { resolveBuildOrdinal } from "../intern/build-version-ordinal.js";
16
7
  import { checkAuth } from "../check-auth.js";
17
8
  import { unwrapMsgBase, wrapMsgBase } from "../unwrap-msg-base.js";
18
9
  import { assertChatShardIdentity } from "../shard-gate.js";
19
- import { and, eq, ne } from "drizzle-orm/sql/expressions";
20
- import { isChatMessage, isUserChatMessage } from "@vibes.diy/call-ai-v2";
21
- import { getModelDefaults } from "../intern/get-model-defaults.js";
22
- import { resolveSlotConfig } from "../intern/slot-assembler.js";
23
- import { dryRunHandleIsForeign } from "../intern/ensure-chat-id.js";
24
- import { resolveDeveloperAccess } from "./developer-grants.js";
25
10
  import { createPromptAssetFetch, promptsPkgBaseUrl } from "../intern/prompt-asset-fetch.js";
26
11
  import { resolveCodeBlocksToFileSystem, resolveCodeBlockChangedPaths, createStreamingResolver, createBlockAccumulator, logApplyError, } from "../intern/prompt-streaming.js";
27
12
  import { reconstructConversationMessages, assemblePromptPayload, persistCodegenThemeMarker, ELIDED_CODE_NOTICE, } from "../intern/prompt-assembly.js";
28
13
  import { bumpAppRecency } from "../intern/bump-app-recency.js";
29
14
  import { waitChatPersistIdle } from "../intern/chat-persist-gate.js";
30
- import { peekInFlightTurns } from "../intern/in-flight-turns.js";
31
- import { acquireTurnBuildLock, peekBuildLockEntry, releaseTurnBuildLock } from "../intern/codegen-build-lock.js";
32
- import { deliverAgentSession } from "../intern/agent-session-runtime.js";
33
- import { readAgentSession } from "../intern/agent-session-store.js";
34
- import { runEditVibeTool } from "../intern/agent-build-tools.js";
35
- import { claimFirstTurnMint, claimMintJob, getMintJob } from "../intern/mint-job-store.js";
36
- import { codegenTurnLane, mintRowSaysFirstTurnIsAgents, routeAgentTurn } from "../intern/agent-router.js";
37
- import { assembleAgentPayload } from "../intern/agent-assembly.js";
38
- import { claimAgentAppContext, composeAgentRequest, isAppContextMessage, markJournalCacheTail, } from "../intern/agent-request-frame.js";
39
- import { createAgentBelt } from "../intern/agent-belt.js";
40
- import { createAgentTurnStream, agentTotalBudgetMs } from "../intern/agent-turn.js";
41
- import { createAgentBuildDispatcher } from "../intern/agent-dispatcher.js";
42
- import { decideAgentWake, fireAgentWakeTurn, readAgentWakeBudget, renderAgentWakeNote } from "../intern/agent-wake.js";
43
- import { settleTurnTerminal } from "../intern/turn-terminal.js";
44
- import { resolveAgentActor, resolveChatApp } from "../intern/agent-actor.js";
45
- import { langForFilename } from "../intern/ensure-push-seeded-chat.js";
46
- import { promptDispatchContext } from "../intern/prompt-dispatch-context.js";
47
- import { dispatchLlmRequestWithFallback } from "./llm-dispatch.js";
48
- import { scheduleUrlResearch } from "../intern/url-research-queue.js";
49
- import { createProviderSseStream } from "../intern/provider-sse.js";
50
- import { activateWorkUrls, loadWorkFetchUrlContext, loadWorkVfs, withoutServerTools } from "../intern/work-tools.js";
51
- import { closeTurn, extendOpenTurnDeadline, OPEN_TURN_AWAIT_BUILD_EXTENSION_MS, shouldCloseTurnRecord, } from "../intern/open-turn-store.js";
52
- import { maxPersistedBlockSeq } from "../intern/persisted-block-seq.js";
53
- import { appendBlockEvent, currentUserMessageText, handlerLlmRequest } from "./llm-dispatch.js";
54
- import { createAgentEvidenceWriter, nextAgentSessionMessages } from "../intern/agent-evidence.js";
55
- import { msSinceOrigin, observeEvidenceLine } from "../intern/turn-latency.js";
56
- import { handleLlmResponse } from "./stream-assembly.js";
57
- import { handleProdiaImageRequest } from "./image-gen.js";
15
+ import { acquireTurnBuildLock, releaseTurnBuildLock } from "../intern/codegen-build-lock.js";
16
+ import { agentSessionPinsFor } from "../intern/agent-session.js";
17
+ import { decideAgentWake } from "../intern/agent-wake.js";
18
+ import { appendBlockEvent } from "./llm-dispatch.js";
58
19
  import { handleFSPrompt } from "./fs-prompt.js";
59
- import { admitBuilderModel, dispatchCarryFor } from "../intern/builder-model.js";
60
- import { admitInstructionVariant } from "../intern/instruction-variant.js";
61
- import { evalWakeAgentModel } from "../intern/eval-handles.js";
62
- import { promptStepFor } from "../intern/prompt-step.js";
63
- function clientWsSend(ctx) {
64
- return ctx.send.provider;
65
- }
20
+ import { admitTurnOptions, getResChatFromMode, readTurnEnvelope } from "./prompt-turn-envelope.js";
21
+ import { assembleCodegenTurn, routeCodegenTurn } from "./codegen-turn-route.js";
22
+ import { assembleAgentTurn } from "./agent-turn-assembly.js";
23
+ import { resolveImageModel, selectPromptSectionAction } from "./prompt-section-actions.js";
24
+ import { finalizePromptTurn, recordTurnError } from "./prompt-turn-terminal.js";
25
+ import { settlePromptTurn } from "./prompt-turn-settled.js";
66
26
  export { createPromptAssetFetch, promptsPkgBaseUrl };
67
27
  export { resolveCodeBlocksToFileSystem, resolveCodeBlockChangedPaths, createStreamingResolver, createBlockAccumulator, logApplyError, };
68
28
  export { reconstructConversationMessages, assemblePromptPayload, persistCodegenThemeMarker, ELIDED_CODE_NOTICE };
69
29
  export { handlePromptContext } from "./llm-dispatch.js";
70
30
  export { handleFSPrompt };
71
- async function getResChatFromMode(vctx, req, orig) {
72
- if (isReqCreationPromptChatSection(orig) &&
73
- orig.dryRun === true &&
74
- orig.ownerHandle !== undefined &&
75
- orig.appSlug !== undefined) {
76
- const anyRow = await vctx.sql.db
77
- .select({ chatId: vctx.sql.tables.chatContexts.chatId })
78
- .from(vctx.sql.tables.chatContexts)
79
- .where(eq(vctx.sql.tables.chatContexts.chatId, req.chatId))
80
- .limit(1)
81
- .then((r) => r[0]);
82
- if (!anyRow) {
83
- const userId = req._auth.verifiedAuth.claims.userId;
84
- const rForeign = await dryRunHandleIsForeign(vctx, orig.ownerHandle, userId);
85
- if (rForeign.isErr())
86
- return Result.Err(rForeign);
87
- if (rForeign.Ok()) {
88
- const dev = orig.appSlug !== undefined
89
- ? await resolveDeveloperAccess(vctx, { ownerHandle: orig.ownerHandle, appSlug: orig.appSlug, callerUserId: userId })
90
- : null;
91
- if (dev === null) {
92
- return Result.Err(`ownerHandle "${orig.ownerHandle}" is owned by another user`);
93
- }
94
- }
95
- return Result.Ok({ appSlug: orig.appSlug, ownerHandle: orig.ownerHandle, mode: orig.mode });
96
- }
97
- }
98
- let iResChat;
99
- if (isReqPromptApplicationChatSection(orig) || isReqPromptImageChatSection(orig)) {
100
- iResChat = await vctx.sql.db
101
- .select()
102
- .from(vctx.sql.tables.applicationChats)
103
- .where(and(eq(vctx.sql.tables.applicationChats.userId, req._auth.verifiedAuth.claims.userId), eq(vctx.sql.tables.applicationChats.chatId, req.chatId)))
104
- .limit(1)
105
- .then((r) => r[0]);
106
- }
107
- else {
108
- iResChat = await vctx.sql.db
109
- .select()
110
- .from(vctx.sql.tables.chatContexts)
111
- .where(and(eq(vctx.sql.tables.chatContexts.userId, req._auth.verifiedAuth.claims.userId), eq(vctx.sql.tables.chatContexts.chatId, req.chatId)))
112
- .limit(1)
113
- .then((r) => r[0]);
114
- }
115
- if (!iResChat) {
116
- if (isReqCreationPromptChatSection(orig)) {
117
- return Result.Err(`Creation Chat ID ${req.chatId} not found`);
118
- }
119
- else if (isReqPromptApplicationChatSection(orig)) {
120
- return Result.Err(`Application Chat ID ${req.chatId} not found`);
121
- }
122
- else if (isReqPromptImageChatSection(orig)) {
123
- return Result.Err(`Image Chat ID ${req.chatId} not found`);
124
- }
125
- }
126
- const resChat = { ...iResChat, mode: orig.mode };
127
- return Result.Ok(resChat);
128
- }
129
31
  const accountPromptChatSection = {
130
32
  hash: "prompt-chat-section-handler",
131
33
  validate: unwrapMsgBase(async (msg) => {
@@ -143,829 +45,117 @@ const accountPromptChatSection = {
143
45
  const orig = ctx.enRequest.payload;
144
46
  const vctx = ctx.ctx.getOrThrow("vibesApiCtx");
145
47
  const turnStartedAt = Date.now();
146
- const submittedAtRaw = orig.submittedAt;
147
- const submittedAt = typeof submittedAtRaw === "number" &&
148
- Number.isFinite(submittedAtRaw) &&
149
- submittedAtRaw <= turnStartedAt &&
150
- turnStartedAt - submittedAtRaw <= 3_600_000
151
- ? submittedAtRaw
152
- : undefined;
153
- const directBuildRequested = orig.directBuild === true;
154
- const agentDispatchedBuild = orig.agentDispatched === true;
155
- const agentWakeTurn = orig.agentWake === true;
156
- const agentWakeBudget = readAgentWakeBudget(orig.agentWakeBudget);
157
- const agentWakeNoteRaw = orig.agentWakeNote;
158
- const agentWakeNote = agentWakeTurn === true && typeof agentWakeNoteRaw === "string" ? agentWakeNoteRaw : undefined;
159
- const firstTurnAgent = orig.firstTurnAgent === true;
160
- const nonInteractive = orig.nonInteractive === true;
161
- let agentFetchUrlContext;
48
+ const envelope = readTurnEnvelope(orig, turnStartedAt);
49
+ const { agentDispatchedBuild, agentWakeBudget } = envelope;
162
50
  await waitChatPersistIdle(vctx.connections, req.chatId, { logger: vctx.logger });
163
51
  const rResChat = await getResChatFromMode(vctx, req, orig);
164
52
  if (rResChat.isErr()) {
165
53
  return Result.Err(rResChat);
166
54
  }
167
55
  const resChat = rResChat.Ok();
168
- const rInstructionVariant = admitInstructionVariant(orig.instructionVariant, resChat.ownerHandle);
169
- if (rInstructionVariant.isErr())
170
- return Result.Err(rInstructionVariant);
171
- const instructionVariant = rInstructionVariant.Ok();
172
- const rBuilderModel = await admitBuilderModel(vctx, orig.builderModel, resChat.ownerHandle);
173
- if (rBuilderModel.isErr())
174
- return Result.Err(rBuilderModel);
175
- const builderModel = rBuilderModel.Ok();
176
- const dispatchCarry = dispatchCarryFor({
177
- apiKey: orig.apiKey,
178
- model: orig.prompt?.model,
179
- ...(builderModel === undefined ? {} : { builderModel }),
180
- focusPath: orig.focusPath,
181
- ...(instructionVariant === undefined ? {} : { instructionVariant }),
182
- });
183
- const wakeAgentModel = evalWakeAgentModel(resChat.ownerHandle, orig.agentModel);
56
+ const rAdmitted = await admitTurnOptions(vctx, orig, resChat);
57
+ if (rAdmitted.isErr())
58
+ return Result.Err(rAdmitted);
59
+ const { instructionVariant, builderModel, subagentModel, dispatchCarry, wakeAgentModel } = rAdmitted.Ok();
184
60
  const oShardErr = assertChatShardIdentity(ctx, resChat.ownerHandle, resChat.appSlug);
185
61
  if (oShardErr.IsSome()) {
186
62
  await ctx.send.send(ctx, oShardErr.unwrap());
187
63
  return Result.Ok(EventoResult.Continue);
188
64
  }
189
65
  let preAssembled;
190
- let turnToolsOffered = [];
191
66
  let agentAssembled;
192
67
  let agentSession;
193
68
  let agentEvidence;
69
+ let agentFetchUrlContext;
194
70
  const turnLatency = {};
195
71
  let agentFirstTurnMint = false;
196
- let lightMintedChat = false;
197
- let mintUnclaimed = false;
72
+ let subagentCall;
73
+ const facts = { toolsOffered: [], errored: false };
198
74
  if (isReqCreationPromptChatSection(orig)) {
199
- if (agentWakeTurn === false && !orig.prompt.messages.some((m) => m.role === "user")) {
200
- return Result.Err(`prompt.messages must include at least one user message`);
201
- }
202
- const rDefaults = await getModelDefaults(vctx, { appSlug: resChat.appSlug, ownerHandle: resChat.ownerHandle });
203
- if (rDefaults.isErr())
204
- return Result.Err(rDefaults);
205
- const modelId = orig.prompt.model ?? rDefaults.Ok().codegen.model.id;
206
- let hasPriorTurn = false;
207
- if (orig.dryRun !== true) {
208
- const rPrior = await exception2Result(() => vctx.sql.db
209
- .select({ chatId: vctx.sql.tables.chatSections.chatId })
210
- .from(vctx.sql.tables.chatSections)
211
- .where(and(eq(vctx.sql.tables.chatSections.chatId, req.chatId), ne(vctx.sql.tables.chatSections.blockSeq, RESEARCH_BLOCK_SEQ)))
212
- .limit(1)
213
- .then((r) => r[0]));
214
- if (rPrior.isErr()) {
215
- vctx.logger.Warn().Err(rPrior).Msg("follow-up probe: chatSections read failed; treating turn as initial");
216
- }
217
- else {
218
- hasPriorTurn = rPrior.Ok() !== undefined;
219
- }
220
- }
221
- const routeInput = {
222
- codegenChatTurn: true,
223
- dryRun: orig.dryRun === true,
224
- directBuildRequested,
225
- agentWake: agentWakeTurn,
226
- hasPriorTurn,
227
- firstTurnAgent,
228
- };
229
- let agentRoute = routeAgentTurn(routeInput);
230
- const mintRowWorthReading = (agentRoute.route === "codegen" && agentRoute.skip === "first-turn") ||
231
- (agentRoute.route === "agent" && hasPriorTurn === true);
232
- if (mintRowWorthReading) {
233
- const rJob = await getMintJob(vctx, req.chatId);
234
- if (rJob.isErr()) {
235
- vctx.logger
236
- .Warn()
237
- .Err(rJob)
238
- .Str("chatId", req.chatId)
239
- .Msg("first-turn mint-job read failed; keeping the codegen route");
240
- }
241
- else if (mintRowSaysFirstTurnIsAgents(rJob.Ok()?.status)) {
242
- mintUnclaimed = true;
243
- if (agentRoute.route === "codegen") {
244
- lightMintedChat = true;
245
- agentRoute = routeAgentTurn({ ...routeInput, firstTurnAgent: true });
246
- }
247
- }
248
- }
249
- agentFirstTurnMint =
250
- agentRoute.route === "agent" &&
251
- (firstTurnAgent || lightMintedChat || mintUnclaimed) &&
252
- (hasPriorTurn === false || mintUnclaimed);
253
- if (agentRoute.route === "agent") {
254
- agentFetchUrlContext = await loadWorkFetchUrlContext(vctx, {
255
- userId: req._auth.verifiedAuth.claims.userId,
256
- chatId: req.chatId,
257
- ownerHandle: resChat.ownerHandle,
258
- appSlug: resChat.appSlug,
259
- activatedUrls: activateWorkUrls(currentUserMessageText(orig.prompt.messages.filter(isChatMessage))),
260
- });
261
- const buildInFlight = peekBuildLockEntry(vctx.connections, req.chatId);
262
- const rSeenHead = await readAgentSession(vctx.sql, req.chatId);
263
- if (rSeenHead.isErr())
264
- vctx.logger.Warn().Err(rSeenHead).Str("chatId", req.chatId).Msg("agent-session-head-read-failed");
265
- const lastSeenHeadFsId = rSeenHead.isOk() ? rSeenHead.Ok().snapshot?.headFsId : undefined;
266
- const rAgent = await assembleAgentPayload(vctx, {
267
- chatId: req.chatId,
268
- userId: req._auth.verifiedAuth.claims.userId,
269
- model: orig.prompt.model ?? rDefaults.Ok().agent.model.id,
270
- ownerHandle: resChat.ownerHandle,
271
- appSlug: resChat.appSlug,
272
- newUserMessages: orig.prompt.messages.filter(isChatMessage),
273
- ...(agentWakeNote === undefined ? {} : { wakeNote: agentWakeNote }),
274
- ...(buildInFlight === undefined ? {} : { buildInFlight: { promptId: buildInFlight.promptId } }),
275
- ...(agentFirstTurnMint ? { noAppYet: true } : {}),
276
- ...(nonInteractive ? { nonInteractive: true } : {}),
277
- ...(lastSeenHeadFsId === undefined ? {} : { lastSeenHeadFsId }),
278
- ...(instructionVariant === undefined ? {} : { instructionVariant }),
75
+ const rRoute = await routeCodegenTurn(vctx, {
76
+ req,
77
+ orig,
78
+ resChat,
79
+ envelope,
80
+ ...(subagentModel === undefined ? {} : { subagentModel }),
81
+ });
82
+ if (rRoute.isErr())
83
+ return Result.Err(rRoute);
84
+ const route = rRoute.Ok();
85
+ agentFirstTurnMint = route.agentFirstTurnMint;
86
+ subagentCall = route.subagentCall;
87
+ if (route.agentRoute.route === "agent") {
88
+ const rAgent = await assembleAgentTurn({
89
+ ctx,
90
+ vctx,
91
+ req,
92
+ orig,
93
+ resChat,
94
+ defaults: route.defaults,
95
+ envelope,
96
+ agentFirstTurnMint,
97
+ instructionVariant,
98
+ turnLatency,
99
+ pins: agentSessionPinsFor(dispatchCarry),
279
100
  });
280
101
  if (rAgent.isErr())
281
102
  return Result.Err(rAgent);
282
- agentAssembled = { model: rAgent.Ok().model, messages: rAgent.Ok().messages };
283
- const rawIncoming = orig.prompt.messages.filter(isUserChatMessage);
284
- const newMessageCount = orig.prompt.messages.filter(isChatMessage).length;
285
- const incoming = newMessageCount === 0 ? [] : rAgent.Ok().messages.slice(-newMessageCount).filter(isUserChatMessage);
286
- const delivered = incoming.length === 0 && agentWakeTurn
287
- ? [
288
- {
289
- role: "user",
290
- content: [
291
- {
292
- type: "text",
293
- text: agentWakeNote ??
294
- "[session resumed] Continue from your persisted context and pending results. Inspect outcomes before retrying interrupted tools.",
295
- },
296
- ],
297
- },
298
- ]
299
- : incoming;
300
- const acquired = await deliverAgentSession(vctx, {
301
- chatId: req.chatId,
302
- userId: req._auth.verifiedAuth.claims.userId,
303
- ownerHandle: resChat.ownerHandle,
304
- appSlug: resChat.appSlug,
305
- }, { id: ctx.validated.tid, messages: delivered }, rAgent.Ok().messages.filter((message) => message.role === "system" || isAppContextMessage(message)));
306
- if (acquired.isErr())
307
- return Result.Err(acquired.Err());
308
- const { live, created } = acquired.Ok();
309
- const composedHead = rAgent.Ok().headFsId;
310
- if (composedHead !== undefined) {
311
- const noted = await live.session.observeHead(composedHead);
312
- if (noted.isErr())
313
- vctx.logger.Warn().Err(noted).Str("chatId", req.chatId).Msg("agent-session-head-note-failed");
314
- }
315
- if (created === false) {
316
- const receiptId = vctx.sthis.nextId(12).str;
317
- const receiptEvidence = createAgentEvidenceWriter({
318
- chatId: req.chatId,
319
- promptId: receiptId,
320
- sessionId: live.promptId,
321
- append: (evt, blockSeq) => appendBlockEvent({ ctx, vctx, req, promptId: receiptId, blockSeq, evt }),
322
- });
323
- const linked = await receiptEvidence({ kind: "active" });
324
- if (linked.isErr())
325
- return Result.Err(linked.Err());
326
- await ctx.send.send(ctx, wrapMsgBase(ctx.validated, {
327
- payload: {
328
- type: "vibes.diy.res-prompt-chat-section",
329
- chatId: req.chatId,
330
- ownerHandle: resChat.ownerHandle,
331
- appSlug: resChat.appSlug,
332
- promptId: receiptId,
333
- outerTid: req.outerTid,
334
- mode: req.mode,
335
- },
336
- tid: ctx.validated.tid,
337
- src: "agentSession",
338
- }));
339
- const echo = await appendBlockEvent({
340
- ctx,
341
- vctx,
342
- req,
343
- promptId: receiptId,
344
- blockSeq: 0,
345
- evt: {
346
- type: "prompt.req",
347
- ...(orig.mode === "codegen" && orig.source === "suggestion" ? { source: "suggestion" } : {}),
348
- streamId: receiptId,
349
- chatId: req.chatId,
350
- seq: 0,
351
- request: { messages: rawIncoming },
352
- timestamp: new Date(),
353
- },
354
- });
355
- if (echo.isErr())
356
- return Result.Err(echo.Err());
357
- const terminal = await appendBlockEvent({
358
- ctx,
359
- vctx,
360
- req,
361
- promptId: receiptId,
362
- blockSeq: 1,
363
- evt: {
364
- type: "block.end",
365
- streamId: receiptId,
366
- blockId: receiptId,
367
- blockNr: 0,
368
- seq: 1,
369
- timestamp: new Date(),
370
- stats: {
371
- toplevel: { lines: 0, bytes: 0 },
372
- code: { lines: 0, bytes: 0 },
373
- image: { lines: 0, bytes: 0 },
374
- total: { lines: 0, bytes: 0 },
375
- },
376
- usage: { given: [], calculated: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 } },
377
- },
378
- });
379
- if (terminal.isErr())
380
- return Result.Err(terminal.Err());
381
- const ended = await appendBlockEvent({
382
- ctx,
383
- vctx,
384
- req,
385
- promptId: receiptId,
386
- blockSeq: 2,
387
- evt: { type: "prompt.block-end", streamId: receiptId, chatId: req.chatId, seq: 2, timestamp: new Date() },
388
- });
389
- if (ended.isErr())
390
- return Result.Err(ended.Err());
103
+ const agent = rAgent.Ok();
104
+ if (agent.kind === "receipt")
391
105
  return Result.Ok(EventoResult.Continue);
392
- }
393
- agentSession = live;
394
- agentEvidence = observeEvidenceLine(createAgentEvidenceWriter({
395
- chatId: req.chatId,
396
- promptId: live.promptId,
397
- sessionId: live.promptId,
398
- append: (evt, blockSeq) => appendBlockEvent({ ctx, vctx, req, promptId: live.promptId, blockSeq, evt }),
399
- }), turnLatency);
400
- const active = await agentEvidence({
401
- kind: "active",
402
- ...(live.supersedes === undefined ? {} : { supersedes: live.supersedes }),
403
- });
404
- if (active.isErr()) {
405
- live.close();
406
- return Result.Err(active.Err());
407
- }
408
- if (live.session.messages().length === 0) {
409
- const initial = agentAssembled.messages.slice(0, agentAssembled.messages.length - newMessageCount - (agentWakeNote === undefined ? 0 : 1));
410
- const saved = await live.session.checkpoint(initial);
411
- if (saved.isErr()) {
412
- live.close();
413
- return Result.Err(saved.Err());
414
- }
415
- }
416
- const events = await nextAgentSessionMessages(live.session, agentEvidence);
417
- if (events.isErr()) {
418
- live.close();
419
- return Result.Err(events.Err());
420
- }
421
- agentAssembled = {
422
- ...agentAssembled,
423
- messages: composeAgentRequest({
424
- system: agentAssembled.messages.filter((message) => message.role === "system"),
425
- journal: markJournalCacheTail(live.session.messages()),
426
- context: agentAssembled.messages.filter(isAppContextMessage),
427
- tail: events.Ok() ?? [],
428
- }),
429
- };
430
- const saved = await live.session.checkpoint(agentAssembled.messages);
431
- if (saved.isErr()) {
432
- live.close();
433
- return Result.Err(saved.Err());
434
- }
106
+ agentAssembled = agent.agentAssembled;
107
+ agentSession = agent.agentSession;
108
+ agentEvidence = agent.agentEvidence;
109
+ agentFetchUrlContext = agent.agentFetchUrlContext;
435
110
  }
436
111
  else {
437
- const rAssembled = await assemblePromptPayload(vctx, {
438
- chatId: req.chatId,
439
- userId: req._auth.verifiedAuth.claims.userId,
440
- model: modelId,
441
- newUserMessages: orig.prompt.messages.filter(isChatMessage),
442
- selected: orig.selected,
443
- slots: resolveSlotConfig(orig.slots, {
444
- SLOTS_ORIGINAL: vctx.sthis.env.get("SLOTS_ORIGINAL"),
445
- SLOTS_SELECTED: vctx.sthis.env.get("SLOTS_SELECTED"),
446
- SLOTS_LAST_EDIT: vctx.sthis.env.get("SLOTS_LAST_EDIT"),
447
- SLOTS_PREVIOUS: vctx.sthis.env.get("SLOTS_PREVIOUS"),
448
- SLOTS_COMPACTION: vctx.sthis.env.get("SLOTS_COMPACTION"),
449
- }),
450
- focusPath: orig.focusPath,
451
- attachScreenshot: orig.dryRun !== true,
452
- degradeAttachments: orig.dryRun === true,
453
- ...(directBuildRequested && Array.isArray(orig.agentSkills) && orig.agentSkills.length > 0
454
- ? { forceSkills: orig.agentSkills }
455
- : {}),
456
- });
457
- if (rAssembled.isErr())
458
- return Result.Err(rAssembled);
459
- preAssembled = rAssembled.Ok();
460
- if (orig.dryRun === true) {
461
- const dryRunPromptId = `dry-run-${vctx.sthis.nextId(12).str}`;
462
- await ctx.send.send(ctx, wrapMsgBase(ctx.validated, {
463
- payload: {
464
- type: "vibes.diy.res-prompt-chat-section",
465
- chatId: req.chatId,
466
- ownerHandle: resChat.ownerHandle,
467
- appSlug: resChat.appSlug,
468
- promptId: dryRunPromptId,
469
- outerTid: req.outerTid,
470
- mode: req.mode,
471
- },
472
- tid: ctx.validated.tid,
473
- src: "promptChatSection",
474
- }));
475
- const now = () => new Date();
476
- let seq = 0;
477
- await appendBlockEvent({
478
- ctx,
479
- vctx,
480
- req,
481
- promptId: dryRunPromptId,
482
- blockSeq: seq,
483
- emitMode: "emit-only",
484
- evt: { type: "prompt.block-begin", streamId: dryRunPromptId, chatId: req.chatId, seq, timestamp: now() },
485
- });
486
- seq++;
487
- await appendBlockEvent({
488
- ctx,
489
- vctx,
490
- req,
491
- promptId: dryRunPromptId,
492
- blockSeq: seq,
493
- emitMode: "emit-only",
494
- evt: {
495
- type: "prompt.dry-run-payload",
496
- streamId: dryRunPromptId,
497
- chatId: req.chatId,
498
- seq,
499
- timestamp: now(),
500
- request: { ...orig.prompt, model: preAssembled.model, messages: preAssembled.messages },
501
- },
502
- });
503
- seq++;
504
- await appendBlockEvent({
505
- ctx,
506
- vctx,
507
- req,
508
- promptId: dryRunPromptId,
509
- blockSeq: seq,
510
- emitMode: "emit-only",
511
- evt: { type: "prompt.block-end", streamId: dryRunPromptId, chatId: req.chatId, seq, timestamp: now() },
512
- });
513
- return Result.Ok(EventoResult.Continue);
514
- }
515
- }
516
- }
517
- let resolvedImgModel;
518
- if (isReqPromptImageChatSection(orig)) {
519
- const override = orig.prompt.model;
520
- if (override) {
521
- resolvedImgModel = override;
522
- }
523
- else {
524
- const rDefaults = await getModelDefaults(vctx, { appSlug: resChat.appSlug, ownerHandle: resChat.ownerHandle });
525
- if (rDefaults.isOk()) {
526
- const defaults = rDefaults.Ok();
527
- const hasInputImage = !!orig.inputImageBase64;
528
- const editDefault = hasInputImage ? defaults["img-edit"] : undefined;
529
- resolvedImgModel = (editDefault ?? defaults.img).model.id;
530
- orig.prompt.model = resolvedImgModel;
531
- }
532
- }
533
- }
534
- if (resolvedImgModel?.startsWith("prodia/") && !vctx.prodiaToken) {
535
- resolvedImgModel = "google/gemini-2.5-flash-image";
536
- if (isReqPromptImageChatSection(orig)) {
537
- orig.prompt.model = resolvedImgModel;
538
- }
539
- }
540
- const useProdia = !!(isReqPromptImageChatSection(orig) && vctx.prodiaToken && resolvedImgModel?.startsWith("prodia/"));
541
- const terminal = { promptBlockEndEmitted: false, latency: turnLatency };
542
- let prompSectionAction;
543
- if (isReqPromptImageChatSection(orig) && useProdia) {
544
- prompSectionAction = async (scope, blockSeq) => {
545
- const reqWithAuth = { ...orig, _auth: req._auth };
546
- return handleProdiaImageRequest({
547
- scope,
548
- ctx,
549
- vctx,
550
- req: reqWithAuth,
551
- promptId,
552
- blockSeq,
553
- resolvedModel: resolvedImgModel,
554
- resChat,
555
- });
556
- };
557
- }
558
- else if (agentAssembled !== undefined && isReqPromptLLMChatSection(orig)) {
559
- const agentPayload = agentAssembled;
560
- const liveSession = agentSession;
561
- if (liveSession === undefined)
562
- return Result.Err("Agent turn has no session");
563
- prompSectionAction = async (scope, blockSeq) => {
564
- const rActor = await resolveAgentActor(vctx, {
565
- userId: req._auth.verifiedAuth.claims.userId,
566
- scope: agentFirstTurnMint
567
- ? { ownerHandle: resChat.ownerHandle }
568
- : { ownerHandle: resChat.ownerHandle, appSlug: resChat.appSlug },
569
- });
570
- if (rActor.isErr())
571
- return Result.Err(rActor.Err());
572
- let liveAppSlug = resChat.appSlug;
573
- let firstTurnAppClaimed = agentFirstTurnMint === false;
574
- const rVfs = await loadWorkVfs(vctx, {
575
- chatId: req.chatId,
576
- ownerHandle: resChat.ownerHandle,
577
- appSlug: resChat.appSlug,
578
- editBaseFsId: undefined,
579
- });
580
- if (rVfs.isErr())
581
- return Result.Err(rVfs.Err());
582
- const vfs = new Map(rVfs.Ok());
583
- function extendDeadlineForAwaitedBuild(awaiting) {
584
- void extendOpenTurnDeadline(vctx.sql, {
585
- chatId: req.chatId,
586
- promptId,
587
- deadlineAtMs: Date.now() + OPEN_TURN_AWAIT_BUILD_EXTENSION_MS,
588
- }).then((r) => {
589
- if (r.isErr()) {
590
- vctx.logger
591
- .Warn()
592
- .Err(r)
593
- .Any("event", { chatId: req.chatId, promptId, awaiting })
594
- .Msg("agent-await-deadline-extend-failed");
595
- }
596
- });
597
- }
598
- const firstTurnFileBlocks = orig.prompt.messages
599
- .filter(isUserChatMessage)
600
- .flatMap((m) => m.content.filter((c) => c.type === "vibes_file"));
601
- const dispatcher = createAgentBuildDispatcher({
602
- vctx,
603
- host: ctx,
604
- auth: req.auth,
605
- outerTid: req.outerTid,
606
- handle: (dispatchCtx) => promptChatSection.handle(dispatchCtx),
607
- wakeBudget: 0,
608
- onPendingBuild: (t) => {
609
- extendDeadlineForAwaitedBuild(t.promptId);
610
- return liveSession.watch(t.promptId);
611
- },
612
- ...(agentFirstTurnMint
613
- ? {
614
- firstTurn: {
615
- chatId: req.chatId,
616
- ...(submittedAt === undefined ? {} : { submittedAt }),
617
- ...(firstTurnFileBlocks.length === 0 ? {} : { fileBlocks: firstTurnFileBlocks }),
618
- ...(orig.varietyPin === undefined
619
- ? {}
620
- : { varietyPin: orig.varietyPin }),
621
- ...(typeof orig.stylePin === "string"
622
- ? { stylePin: orig.stylePin }
623
- : {}),
624
- },
625
- }
626
- : {}),
627
- ...(Object.keys(dispatchCarry).length === 0 ? {} : { carry: dispatchCarry }),
628
- });
629
- const belt = createAgentBelt(vctx, {
630
- actor: rActor.Ok(),
631
- chatId: req.chatId,
632
- vfs,
633
- ...(agentFirstTurnMint
634
- ? {
635
- firstTurn: {
636
- chatId: req.chatId,
637
- claim: () => claimFirstTurnMint(vctx, req.chatId),
638
- release: async () => {
639
- await claimMintJob(vctx, req.chatId, ["pending"], "agent");
640
- },
641
- },
642
- }
643
- : {}),
644
- ...(agentFetchUrlContext === undefined ? {} : { fetchUrl: agentFetchUrlContext }),
645
- dispatcher,
646
- queueEdit: (input) => liveSession.queue(input),
647
- fileSession: liveSession,
648
- ...(instructionVariant === undefined ? {} : { instructionVariant }),
649
- onStyleApplied: async (applied) => {
650
- await appendBlockEvent({
651
- ctx,
652
- vctx,
653
- req,
654
- promptId,
655
- blockSeq,
656
- evt: {
657
- type: "prompt.restyle",
658
- streamId: promptId,
659
- chatId: req.chatId,
660
- seq: blockSeq,
661
- timestamp: new Date(),
662
- style: applied.style,
663
- description: applied.description,
664
- },
665
- emitMode: "emit-only",
666
- });
667
- },
668
- onDispatch: (dispatched) => {
669
- liveAppSlug = dispatched.appSlug;
670
- liveSession.claimAppAddress(dispatched.appSlug);
671
- firstTurnAppClaimed = true;
672
- vctx.logger
673
- .Info()
674
- .Any("event", { chatId: dispatched.chatId, appSlug: dispatched.appSlug, promptId })
675
- .Msg("agent-turn-dispatched-build");
676
- },
677
- });
678
- turnToolsOffered = belt.definitions.map((d) => d.function.name);
679
- await liveSession.bind((input) => runEditVibeTool(vctx, {
680
- actor: rActor.Ok(),
681
- chatId: req.chatId,
682
- input,
683
- dispatcher,
684
- toolsOffered: belt.definitions.map((d) => d.function.name),
685
- }), async (commit) => {
686
- if (firstTurnAppClaimed === false)
687
- return Result.Ok({
688
- ok: false,
689
- reason: "no-app-yet",
690
- message: "Call create_vibe first; there is no app to edit until it has been created.",
691
- });
692
- const currentActor = await resolveAgentActor(vctx, {
693
- userId: rActor.Ok().userId,
694
- requestedHandle: rActor.Ok().ownerHandle,
695
- scope: rActor.Ok().scope,
696
- });
697
- if (currentActor.isErr())
698
- return Result.Err(currentActor.Err());
699
- const scoped = await resolveChatApp(vctx, currentActor.Ok(), req.chatId);
700
- if (scoped.isErr())
701
- return Result.Err(scoped.Err());
702
- const commitId = vctx.sthis.nextId(12).str;
703
- const lock = acquireTurnBuildLock({
704
- vctx,
705
- chatId: req.chatId,
706
- promptId: commitId,
707
- movesFilesystem: true,
708
- wakesOnTerminal: false,
709
- });
710
- if (lock.acquired === false)
711
- return Result.Err("A build is running; the staged commit could not acquire the write lane.");
712
- const committed = { promptBlockEndEmitted: false };
713
- const saved = await exception2Result(() => handleFSPrompt({
714
- scope,
715
- vctx,
716
- ctx,
717
- promptId: commitId,
718
- blockSeq: 0,
719
- terminal: committed,
720
- beginDirect: true,
721
- resChat: { ...resChat, mode: "fs-update" },
722
- req: {
723
- type: "vibes.diy.req-prompt-chat-section",
724
- mode: "fs-update",
725
- auth: req.auth,
726
- _auth: req._auth,
727
- chatId: req.chatId,
728
- outerTid: req.outerTid,
729
- agentFileCommit: commit,
730
- fsUpdate: {
731
- update: commit.changes.map((file) => ({
732
- type: "code-block",
733
- filename: file.path,
734
- content: file.contents,
735
- lang: langForFilename(file.path),
736
- })),
737
- remove: [],
738
- },
739
- },
740
- })).finally(() => {
741
- committed.releasePersistGate?.();
742
- releaseTurnBuildLock(vctx, req.chatId, commitId);
743
- });
744
- if (saved.isErr()) {
745
- const last = await maxPersistedBlockSeq(vctx.sql, { chatId: req.chatId, promptId: commitId });
746
- if (last.isErr())
747
- return Result.Err(last.Err());
748
- const seq = (last.Ok() ?? -1) + 1;
749
- await appendBlockEvent({
750
- ctx,
751
- vctx,
752
- req,
753
- promptId: commitId,
754
- blockSeq: seq,
755
- evt: {
756
- type: "prompt.error",
757
- streamId: commitId,
758
- chatId: req.chatId,
759
- seq,
760
- timestamp: new Date(),
761
- error: saved.Err().message,
762
- },
763
- });
764
- return Result.Err(saved.Err());
765
- }
766
- if (committed.fsRef === undefined)
767
- return Result.Err("The commit did not persist a version.");
768
- const committedPaths = commit.changes.map((file) => file.path);
769
- return Result.Ok({
770
- ok: true,
771
- saved: true,
772
- fsRef: committed.fsRef,
773
- paths: committedPaths,
774
- entryTouched: classifyTurnReach(committedPaths).entryTouched,
775
- });
776
- });
777
- const res = await handlerLlmRequest({
112
+ const rCodegen = await assembleCodegenTurn({
778
113
  ctx,
779
- blockSeq,
780
- scope,
781
- vctx,
782
- req: req,
783
- resChat,
784
- promptId,
785
- preAssembled: { ...agentPayload, isInitialTurn: false },
786
- modelUsage: "agent",
787
- serverTools: belt.definitions,
788
- });
789
- const evidence = agentEvidence ??
790
- observeEvidenceLine(createAgentEvidenceWriter({
791
- chatId: req.chatId,
792
- promptId,
793
- sessionId: liveSession.promptId,
794
- append: (evt, blockSeq) => appendBlockEvent({ ctx, vctx, req, promptId, blockSeq, evt }),
795
- }), turnLatency);
796
- let providerStream;
797
- const rSse = createProviderSseStream(res.res, promptId);
798
- if (rSse.isErr()) {
799
- vctx.logger.Warn().Err(rSse).Str("chatId", req.chatId).Msg("agent-turn-provider-stream-unavailable");
800
- }
801
- else {
802
- providerStream = createAgentTurnStream({
803
- vctx,
804
- belt,
805
- actor: rActor.Ok(),
806
- chatId: req.chatId,
807
- vfs,
808
- dispatcher,
809
- request: res.llmReq,
810
- stream: rSse.Ok(),
811
- abort: res.abort,
812
- onToolEvidence: ({ kind, call, text, round, epoch }) => evidence({ kind, callId: call.id, name: call.name, text, round, epoch }),
813
- session: {
814
- checkpoint: async (messages) => {
815
- const saved = await liveSession.session.checkpoint(messages);
816
- if (saved.isErr())
817
- return saved;
818
- const extended = await extendOpenTurnDeadline(vctx.sql, {
819
- chatId: req.chatId,
820
- promptId,
821
- deadlineAtMs: Date.now() + agentTotalBudgetMs(vctx) + OPEN_TURN_AWAIT_BUILD_EXTENSION_MS,
822
- });
823
- if (extended.isErr())
824
- return Result.Err(extended.Err());
825
- return Result.Ok();
826
- },
827
- next: () => nextAgentSessionMessages(liveSession.session, evidence),
828
- head: () => liveSession.session.head(),
829
- dispatchBudgetMs: agentTotalBudgetMs(vctx),
830
- },
831
- dispatch: async ({ request, budget }) => {
832
- const messages = agentFirstTurnMint && firstTurnAppClaimed
833
- ? claimAgentAppContext(request.messages, { ownerHandle: resChat.ownerHandle, appSlug: liveAppSlug })
834
- : request.messages;
835
- {
836
- const latest = await loadWorkVfs(vctx, {
837
- chatId: req.chatId,
838
- ownerHandle: resChat.ownerHandle,
839
- appSlug: liveAppSlug,
840
- editBaseFsId: undefined,
841
- });
842
- if (latest.isErr())
843
- return Result.Err(latest.Err());
844
- vfs.clear();
845
- for (const [path, content] of latest.Ok())
846
- vfs.set(path, content);
847
- }
848
- const dispatch = await dispatchLlmRequestWithFallback({
849
- vctx,
850
- mode: req.mode,
851
- llmReq: { ...res.llmReq, messages },
852
- chatId: req.chatId,
853
- promptId,
854
- phase: "tool-resume",
855
- budget,
856
- });
857
- if (dispatch.ok === false)
858
- return Result.Err(dispatch.error);
859
- const rRound = createProviderSseStream(dispatch.res, promptId);
860
- if (rRound.isErr())
861
- return Result.Err(rRound);
862
- return Result.Ok({
863
- request: { ...res.llmReq, messages },
864
- stream: rRound.Ok(),
865
- abort: dispatch.abort,
866
- });
867
- },
868
- onError: (error) => {
869
- vctx.logger.Warn().Any("event", { chatId: req.chatId, promptId, error }).Msg("agent-turn-loop-failed");
870
- },
871
- });
872
- }
873
- const finalBlockSeq = await handleLlmResponse({
874
- scope,
875
114
  vctx,
876
- req: req,
877
- ctx,
878
- res: res.res,
879
- resChat,
880
- promptId,
881
- blockSeq: res.blockSeq,
882
- llmReq: res.llmReq,
883
- abort: res.abort,
884
- ...(providerStream === undefined ? {} : { providerStream }),
885
- stripServerTools: withoutServerTools,
886
- proseOnly: true,
887
- terminal,
888
- researchRequested: false,
889
- platformInitiated: agentWakeTurn,
890
- step: agentWakeTurn ? "wake" : "agent",
891
- });
892
- return Result.Ok(finalBlockSeq);
893
- };
894
- }
895
- else if (isReqPromptLLMChatSection(orig)) {
896
- const buildStep = promptStepFor({
897
- agentTurn: false,
898
- agentWake: false,
899
- agentDispatched: agentDispatchedBuild,
900
- agentStep: orig.agentStep,
901
- agentSkills: orig.agentSkills,
902
- });
903
- prompSectionAction = async (scope, blockSeq) => {
904
- const res = await handlerLlmRequest({
905
- ctx,
906
- blockSeq,
907
- scope,
908
- vctx,
909
- req: req,
910
- resChat,
911
- promptId,
912
- preAssembled,
913
- ...(agentDispatchedBuild ? { initiator: "platform" } : {}),
914
- ...(agentDispatchedBuild ? { agentDispatched: true } : {}),
915
- });
916
- if (res.urlResearchJob !== undefined) {
917
- scheduleUrlResearch((message) => exception2Result(() => Result.Ok(vctx.postQueue(message))), res.urlResearchJob, {
918
- waitUntil: vctx.waitUntil,
919
- onError: (error) => {
920
- vctx.logger
921
- .Warn()
922
- .Any("event", { chatId: req.chatId, promptId, error: String(error) })
923
- .Msg("url-research-enqueue-failed");
924
- },
925
- });
926
- }
927
- const finalBlockSeq = await handleLlmResponse({
928
- scope,
929
- vctx,
930
- req: req,
931
- ctx,
932
- res: res.res,
933
- resChat,
934
- promptId,
935
- blockSeq: res.blockSeq,
936
- llmReq: res.llmReq,
937
- abort: res.abort,
938
- terminal,
939
- researchRequested: res.urlResearchJob !== undefined,
940
- frontendDraft: preAssembled?.frontendDraft === true && req.mode === "codegen",
941
- codegenTheme: preAssembled?.codegenTheme,
942
- codegenVariety: preAssembled?.codegenVariety,
943
- editBaseFsId: preAssembled?.editBaseFsId,
944
- agentDispatched: agentDispatchedBuild,
945
- ...(buildStep === undefined ? {} : { step: buildStep }),
946
- ...(res.urlResearchJob === undefined ? {} : { researchModel: res.urlResearchJob.model }),
947
- });
948
- return Result.Ok(finalBlockSeq);
949
- };
950
- }
951
- if (isReqPromptFSChatSection(orig)) {
952
- prompSectionAction = async (scope, blockSeq) => {
953
- const r = await handleFSPrompt({
954
- scope,
955
- vctx,
956
- req: req,
957
- ctx,
115
+ req,
116
+ orig,
958
117
  resChat,
959
- promptId,
960
- blockSeq,
961
- terminal,
118
+ modelId: route.modelId,
119
+ directBuildRequested: envelope.directBuildRequested,
962
120
  });
963
- return r;
964
- };
965
- }
966
- if (!prompSectionAction) {
967
- return Result.Err(`Unsupported prompt chat section mode: ${orig.mode}`);
121
+ if (rCodegen.isErr())
122
+ return Result.Err(rCodegen);
123
+ const codegen = rCodegen.Ok();
124
+ if (codegen.kind === "dry-run-done")
125
+ return Result.Ok(EventoResult.Continue);
126
+ preAssembled = codegen.preAssembled;
127
+ }
968
128
  }
129
+ const { resolvedImgModel, useProdia } = await resolveImageModel(vctx, orig, resChat);
130
+ const terminal = { promptBlockEndEmitted: false, latency: turnLatency };
131
+ const reenter = (reentry) => promptChatSection.handle(reentry);
132
+ const rSectionAction = selectPromptSectionAction({
133
+ ctx,
134
+ vctx,
135
+ req,
136
+ orig,
137
+ resChat,
138
+ resolvedImgModel,
139
+ useProdia,
140
+ preAssembled,
141
+ agentAssembled,
142
+ agentSession,
143
+ agentEvidence,
144
+ agentFetchUrlContext,
145
+ agentFirstTurnMint,
146
+ envelope,
147
+ dispatchCarry,
148
+ instructionVariant,
149
+ subagentModel,
150
+ subagentCall,
151
+ turnLatency,
152
+ terminal,
153
+ facts,
154
+ reenter,
155
+ });
156
+ if (rSectionAction.isErr())
157
+ return Result.Err(rSectionAction);
158
+ const prompSectionAction = rSectionAction.Ok();
969
159
  const rBump = await bumpAppRecency(vctx, { ownerHandle: resChat.ownerHandle, appSlug: resChat.appSlug });
970
160
  if (rBump.isErr()) {
971
161
  vctx.logger.Warn().Err(rBump).Msg("bumpAppRecency failed");
@@ -1006,64 +196,8 @@ const accountPromptChatSection = {
1006
196
  await scopey(async (scope) => {
1007
197
  const seq = { val: 0 };
1008
198
  const notifiesBuild = isBuildNotifyMode(req.mode);
1009
- let turnErrored = false;
1010
- let turnErrorReason;
1011
- scope.onCatch(async (e) => {
1012
- turnErrored = true;
1013
- const failureMessage = e.message;
1014
- if (failureMessage !== "")
1015
- turnErrorReason = failureMessage;
1016
- const rMaxSeq = await maxPersistedBlockSeq(vctx.sql, { chatId: req.chatId, promptId });
1017
- const maxSeq = rMaxSeq.isOk() ? rMaxSeq.Ok() : undefined;
1018
- if (maxSeq !== undefined) {
1019
- seq.val = Math.max(seq.val, maxSeq + 1);
1020
- }
1021
- const errorSeq = seq.val;
1022
- seq.val++;
1023
- const rError = await appendBlockEvent({
1024
- ctx,
1025
- vctx,
1026
- req,
1027
- promptId,
1028
- blockSeq: errorSeq,
1029
- evt: {
1030
- type: "prompt.error",
1031
- streamId: promptId,
1032
- chatId: req.chatId,
1033
- seq: errorSeq,
1034
- timestamp: new Date(),
1035
- error: e.message,
1036
- },
1037
- });
1038
- if (rError.isErr()) {
1039
- vctx.logger
1040
- .Error()
1041
- .Err(rError)
1042
- .Any("event", { chatId: req.chatId, promptId, blockSeq: errorSeq })
1043
- .Msg("prompt-error-persist-failed");
1044
- }
1045
- if (vctx.notifyUser && notifiesBuild) {
1046
- const userId = req._auth.verifiedAuth.claims.userId;
1047
- const connId = clientWsSend(ctx).connId;
1048
- const prefs = await loadNotificationPrefs(vctx, userId);
1049
- if (shouldNotify(prefs, {
1050
- notificationType: "build-failed",
1051
- ownerHandle: resChat.ownerHandle,
1052
- appSlug: resChat.appSlug,
1053
- })) {
1054
- void vctx
1055
- .notifyUser(userId, {
1056
- type: "vibes.diy.evt-user-notification",
1057
- notificationType: "build-failed",
1058
- ownerHandle: resChat.ownerHandle,
1059
- appSlug: resChat.appSlug,
1060
- promptId,
1061
- }, connId)
1062
- .catch((_e) => {
1063
- });
1064
- }
1065
- }
1066
- }, 0);
199
+ const terminalArgs = { ctx, vctx, req, resChat, promptId, seq, facts, notifiesBuild };
200
+ scope.onCatch(async (e) => recordTurnError(terminalArgs, e), 0);
1067
201
  await scope
1068
202
  .evalResult(async () => {
1069
203
  const res = await appendBlockEvent({
@@ -1081,7 +215,7 @@ const accountPromptChatSection = {
1081
215
  },
1082
216
  });
1083
217
  seq.val++;
1084
- const rAction = await prompSectionAction(scope, seq.val);
218
+ const rAction = await prompSectionAction({ scope, blockSeq: seq.val, promptId });
1085
219
  if (rAction.isErr())
1086
220
  return rAction;
1087
221
  seq.val = rAction.Ok();
@@ -1089,218 +223,36 @@ const accountPromptChatSection = {
1089
223
  })
1090
224
  .finally(async () => {
1091
225
  const turnBegan = seq.val > 0;
1092
- let settledArgs;
1093
- let lookLintPlan;
1094
226
  try {
1095
- await finalizePromptTurn();
227
+ await finalizePromptTurn({ ...terminalArgs, terminal });
1096
228
  }
1097
229
  finally {
1098
- if (turnBegan && canonicalModelUsage(req.mode) === "codegen") {
1099
- lookLintPlan = {
1100
- theme: preAssembled?.codegenTheme,
1101
- committed: terminal.fsRef !== undefined,
1102
- };
1103
- const turnOrigin = submittedAt ?? turnStartedAt;
1104
- const msToFirstToken = msSinceOrigin(turnOrigin, turnLatency.firstTokenAt);
1105
- const msToFirstEvidence = msSinceOrigin(turnOrigin, turnLatency.firstEvidenceLineAt);
1106
- settledArgs = {
1107
- outcome: turnErrored ? "error" : terminal.settled?.applied === true ? "applied" : "no-code",
1108
- userId: req._auth.verifiedAuth.claims.userId,
1109
- ownerHandle: resChat.ownerHandle,
1110
- appSlug: resChat.appSlug,
1111
- reach: projectTurnReach(terminal.settled, terminal.touchedPaths),
1112
- lane: codegenTurnLane({ agentAssembled: agentAssembled !== undefined, agentDispatched: agentDispatchedBuild }),
1113
- firstBuild: preAssembled?.isInitialTurn === true,
1114
- msFromSubmit: Math.max(0, Date.now() - turnOrigin),
1115
- ...(msToFirstToken === undefined ? {} : { msToFirstToken }),
1116
- ...(msToFirstEvidence === undefined ? {} : { msToFirstEvidence }),
1117
- toolsOffered: turnToolsOffered,
1118
- skills: preAssembled?.skills ?? [],
1119
- ...(terminal.recovery === undefined ? {} : { recovery: terminal.recovery }),
1120
- };
1121
- if (lookLintPlanned(lookLintPlan) === false)
1122
- captureCodegenTurnSettled(vctx, settledArgs);
1123
- postMakerFact(vctx, {
1124
- eventId: `turn:${promptId}`,
1125
- userId: req._auth.verifiedAuth.claims.userId,
1126
- at: new Date().toISOString(),
1127
- kind: "codegen-turn",
1128
- }, "codegen-turn-settled");
1129
- const ownWords = agentDispatchedBuild === false && agentWakeTurn === false && isReqPromptLLMChatSection(orig)
1130
- ? currentUserMessageText(orig.prompt.messages.filter(isChatMessage))
1131
- : undefined;
1132
- postCapiBuildEvent(vctx, {
1133
- outcome: settledArgs.outcome,
1134
- firstBuild: settledArgs.firstBuild,
1135
- promptId,
1136
- userId: req._auth.verifiedAuth.claims.userId,
1137
- promptText: ownWords,
1138
- });
1139
- }
1140
- peekInFlightTurns(req.chatId)?.delete(promptId);
1141
- await agentSession?.settle();
1142
- releaseTurnBuildLock(vctx, req.chatId, promptId);
1143
- terminal.releasePersistGate?.();
1144
- if (settledArgs !== undefined && lookLintPlan !== undefined) {
1145
- const args = settledArgs;
1146
- dispatchLookLint(vctx, {
1147
- ...lookLintPlan,
1148
- files: async () => (terminal.fsRef === undefined ? {} : loadTurnFilesByFsId(vctx, terminal.fsRef.fsId)),
1149
- onResult: (lookLint) => {
1150
- captureCodegenTurnSettled(vctx, lookLint === undefined ? args : { ...args, lookLint });
1151
- },
1152
- });
1153
- }
1154
- const accessTouched = (terminal.touchedPaths ?? []).some((path) => /\/?(access\.js|seed\.json)$/.test(path)) &&
1155
- terminal.fsRef !== undefined;
1156
- const accessFsRef = terminal.fsRef;
1157
- const rAccessReport = await exception2Result(async () => {
1158
- if (!accessTouched || accessFsRef === undefined)
1159
- return undefined;
1160
- const report = await releaseAccessVerdicts(vctx, {
1161
- ownerHandle: resChat.ownerHandle,
1162
- appSlug: resChat.appSlug,
1163
- files: () => loadTurnFilesByFsId(vctx, accessFsRef.fsId),
1164
- });
1165
- if (report !== undefined) {
1166
- captureAccessVerdicts(vctx, {
1167
- lane: "build",
1168
- userId: req._auth.verifiedAuth.claims.userId,
1169
- ownerHandle: resChat.ownerHandle,
1170
- appSlug: resChat.appSlug,
1171
- report,
1172
- accessChanged: (terminal.touchedPaths ?? []).some((path) => /\/?access\.js$/.test(path)),
1173
- });
1174
- }
1175
- return report;
1176
- });
1177
- if (rAccessReport.isErr()) {
1178
- vctx.logger.Warn().Err(rAccessReport.Err()).Str("promptId", promptId).Msg("access-verdicts-terminal-failed");
1179
- }
1180
- const accessReport = rAccessReport.isErr() ? undefined : rAccessReport.Ok();
1181
- const landingOutcome = {
1182
- outcome: turnErrored
1183
- ? "error"
1184
- : terminal.settled?.applied === true
1185
- ? "applied"
1186
- : "no-code",
1187
- ...(terminal.touchedPaths === undefined ? {} : { touchedPaths: terminal.touchedPaths }),
1188
- ...(terminal.settled === undefined || turnErrored ? {} : { settled: terminal.settled }),
1189
- ...(terminal.fsRef === undefined ? {} : { fsId: terminal.fsRef.fsId }),
1190
- ...(accessReport === undefined ? {} : { accessLine: renderAccessVerdictLine(accessReport) }),
1191
- ...(turnErrorReason === undefined ? {} : { errorReason: turnErrorReason }),
1192
- };
1193
- const { consumed } = settleTurnTerminal(vctx.connections, req.chatId, promptId, landingOutcome);
1194
- const wake = decideAgentWake({
1195
- agentDispatched: agentDispatchedBuild,
1196
- movedFilesystem: movesFilesystem,
1197
- budget: 1,
230
+ await settlePromptTurn({
231
+ ctx,
232
+ vctx,
233
+ req,
234
+ orig,
235
+ resChat,
236
+ promptId,
237
+ terminal,
238
+ facts,
239
+ turnBegan,
240
+ preAssembled,
241
+ agentAssembled,
242
+ agentSession,
243
+ envelope,
244
+ turnStartedAt,
245
+ turnLatency,
246
+ movesFilesystem,
247
+ builderModel,
248
+ subagentModel,
249
+ instructionVariant,
250
+ wakeAgentModel,
251
+ reenter,
1198
252
  });
1199
- if (consumed === true) {
1200
- vctx.logger
1201
- .Info()
1202
- .Any("event", { chatId: req.chatId, promptId, outcome: landingOutcome.outcome })
1203
- .Msg("agent-build-landing-consumed");
1204
- }
1205
- else if (wake.wake === true) {
1206
- vctx.logger.Info().Any("event", { chatId: req.chatId, promptId, budget: wake.budget }).Msg("agent-wake-firing");
1207
- fireAgentWakeTurn({
1208
- vctx,
1209
- host: ctx,
1210
- auth: req.auth,
1211
- outerTid: req.outerTid,
1212
- chatId: req.chatId,
1213
- budget: wake.budget,
1214
- note: renderAgentWakeNote(landingOutcome),
1215
- ...(builderModel === undefined ? {} : { builderModel }),
1216
- ...(instructionVariant === undefined ? {} : { instructionVariant }),
1217
- ...(wakeAgentModel === undefined ? {} : { agentModel: wakeAgentModel }),
1218
- handle: (wakeCtx) => promptChatSection.handle(promptDispatchContext(ctx, wakeCtx.validated)),
1219
- });
1220
- }
1221
- else {
1222
- vctx.logger
1223
- .Info()
1224
- .Any("event", {
1225
- chatId: req.chatId,
1226
- promptId,
1227
- skip: wake.skip,
1228
- agentDispatched: agentDispatchedBuild,
1229
- movedFilesystem: movesFilesystem,
1230
- budget: agentWakeBudget,
1231
- })
1232
- .Msg("agent-wake-skipped");
1233
- }
1234
253
  }
1235
254
  })
1236
255
  .do();
1237
- async function finalizePromptTurn() {
1238
- if (seq.val === 0)
1239
- return;
1240
- const rEnd = await appendBlockEvent({
1241
- ctx,
1242
- vctx,
1243
- req,
1244
- promptId,
1245
- blockSeq: seq.val,
1246
- evt: {
1247
- type: "prompt.block-end",
1248
- streamId: promptId,
1249
- chatId: req.chatId,
1250
- seq: seq.val,
1251
- timestamp: new Date(),
1252
- },
1253
- emitMode: terminal.promptBlockEndEmitted ? "store-only" : "store",
1254
- });
1255
- if (rEnd.isErr()) {
1256
- vctx.logger
1257
- .Error()
1258
- .Err(rEnd)
1259
- .Any("event", { chatId: req.chatId, promptId, blockSeq: seq.val })
1260
- .Msg("prompt-block-end-persist-failed");
1261
- }
1262
- if (shouldCloseTurnRecord({ canonicalMode: canonicalModelUsage(req.mode), terminalPersisted: rEnd.isOk() })) {
1263
- const rClose = await closeTurn(vctx.sql, { chatId: req.chatId, promptId });
1264
- if (rClose.isErr()) {
1265
- vctx.logger.Warn().Err(rClose).Any("event", { chatId: req.chatId, promptId }).Msg("close-turn-record-failed");
1266
- }
1267
- else if (rClose.Ok() === false) {
1268
- vctx.logger.Warn().Any("event", { chatId: req.chatId, promptId, turnErrored }).Msg("turn-closed-after-sweep");
1269
- }
1270
- }
1271
- if (vctx.notifyUser && notifiesBuild && turnErrored === false) {
1272
- const userId = req._auth.verifiedAuth.claims.userId;
1273
- const connId = clientWsSend(ctx).connId;
1274
- const prefs = await loadNotificationPrefs(vctx, userId);
1275
- if (shouldNotify(prefs, {
1276
- notificationType: "build-complete",
1277
- ownerHandle: resChat.ownerHandle,
1278
- appSlug: resChat.appSlug,
1279
- })) {
1280
- const persisted = terminal.fsRef;
1281
- const version = persisted === undefined
1282
- ? undefined
1283
- : await resolveBuildOrdinal(vctx, {
1284
- ownerHandle: persisted.ownerHandle,
1285
- appSlug: persisted.appSlug,
1286
- fsId: persisted.fsId,
1287
- });
1288
- const fsId = persisted?.fsId;
1289
- void vctx
1290
- .notifyUser(userId, {
1291
- type: "vibes.diy.evt-user-notification",
1292
- notificationType: "build-complete",
1293
- ownerHandle: resChat.ownerHandle,
1294
- appSlug: resChat.appSlug,
1295
- promptId,
1296
- ...(fsId === undefined ? {} : { fsId }),
1297
- ...(version === undefined ? {} : { version }),
1298
- }, connId)
1299
- .catch((_e) => {
1300
- });
1301
- }
1302
- }
1303
- }
1304
256
  });
1305
257
  return Result.Ok(EventoResult.Continue);
1306
258
  }),