@vibes.diy/api-svc 14.0.21 → 14.0.23

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 (94) hide show
  1. package/create-handler.js +12 -0
  2. package/create-handler.js.map +1 -1
  3. package/evento-handler-manifest.js +2 -0
  4. package/evento-handler-manifest.js.map +1 -1
  5. package/intern/agent-assembly.d.ts +1 -0
  6. package/intern/agent-assembly.js +6 -1
  7. package/intern/agent-assembly.js.map +1 -1
  8. package/intern/agent-belt.d.ts +4 -1
  9. package/intern/agent-belt.js +12 -3
  10. package/intern/agent-belt.js.map +1 -1
  11. package/intern/agent-build-tools.d.ts +4 -0
  12. package/intern/agent-build-tools.js +6 -0
  13. package/intern/agent-build-tools.js.map +1 -1
  14. package/intern/agent-dispatcher.d.ts +1 -1
  15. package/intern/agent-dispatcher.js +4 -4
  16. package/intern/agent-dispatcher.js.map +1 -1
  17. package/intern/agent-evidence.d.ts +12 -0
  18. package/intern/agent-evidence.js +70 -0
  19. package/intern/agent-evidence.js.map +1 -0
  20. package/intern/agent-file-tools.d.ts +12 -0
  21. package/intern/agent-file-tools.js +162 -0
  22. package/intern/agent-file-tools.js.map +1 -0
  23. package/intern/agent-file-validation.d.ts +9 -0
  24. package/intern/agent-file-validation.js +81 -0
  25. package/intern/agent-file-validation.js.map +1 -0
  26. package/intern/agent-instruction.d.ts +1 -1
  27. package/intern/agent-instruction.js +1 -1
  28. package/intern/agent-lane-flag.d.ts +7 -0
  29. package/intern/agent-lane-flag.js +3 -0
  30. package/intern/agent-lane-flag.js.map +1 -1
  31. package/intern/agent-session-instruction.d.ts +1 -0
  32. package/intern/agent-session-instruction.js +43 -0
  33. package/intern/agent-session-instruction.js.map +1 -0
  34. package/intern/agent-session-runtime.d.ts +39 -0
  35. package/intern/agent-session-runtime.js +247 -0
  36. package/intern/agent-session-runtime.js.map +1 -0
  37. package/intern/agent-session-store.d.ts +6 -0
  38. package/intern/agent-session-store.js +52 -0
  39. package/intern/agent-session-store.js.map +1 -0
  40. package/intern/agent-session.d.ts +193 -0
  41. package/intern/agent-session.js +180 -0
  42. package/intern/agent-session.js.map +1 -0
  43. package/intern/agent-turn.d.ts +5 -1
  44. package/intern/agent-turn.js +7 -3
  45. package/intern/agent-turn.js.map +1 -1
  46. package/intern/agent-wake.d.ts +10 -6
  47. package/intern/agent-wake.js +3 -1
  48. package/intern/agent-wake.js.map +1 -1
  49. package/intern/llm-tool-loop.d.ts +11 -1
  50. package/intern/llm-tool-loop.js +79 -7
  51. package/intern/llm-tool-loop.js.map +1 -1
  52. package/intern/mint-fire-core.js +3 -3
  53. package/intern/mint-fire-core.js.map +1 -1
  54. package/intern/open-turn-store.js +2 -2
  55. package/intern/open-turn-store.js.map +1 -1
  56. package/intern/process-access-bindings.d.ts +1 -0
  57. package/intern/process-access-bindings.js +1 -1
  58. package/intern/process-access-bindings.js.map +1 -1
  59. package/intern/prompt-dispatch-context.d.ts +4 -0
  60. package/intern/prompt-dispatch-context.js +19 -0
  61. package/intern/prompt-dispatch-context.js.map +1 -0
  62. package/intern/work-tools.d.ts +1 -0
  63. package/intern/work-tools.js +1 -1
  64. package/intern/work-tools.js.map +1 -1
  65. package/package.json +12 -11
  66. package/public/fork-app.js +24 -26
  67. package/public/fork-app.js.map +1 -1
  68. package/public/fs-prompt.d.ts +2 -1
  69. package/public/fs-prompt.js +57 -2
  70. package/public/fs-prompt.js.map +1 -1
  71. package/public/llm-dispatch.d.ts +2 -2
  72. package/public/llm-dispatch.js +26 -1
  73. package/public/llm-dispatch.js.map +1 -1
  74. package/public/open-chat.d.ts +2 -2
  75. package/public/open-chat.js +25 -0
  76. package/public/open-chat.js.map +1 -1
  77. package/public/prompt-chat-section.js +302 -12
  78. package/public/prompt-chat-section.js.map +1 -1
  79. package/public/report-top-vibes-by-traffic.d.ts +5 -0
  80. package/public/report-top-vibes-by-traffic.js +69 -0
  81. package/public/report-top-vibes-by-traffic.js.map +1 -0
  82. package/public/vibe-traffic-source.d.ts +114 -0
  83. package/public/vibe-traffic-source.js +322 -0
  84. package/public/vibe-traffic-source.js.map +1 -0
  85. package/types.d.ts +5 -0
  86. package/types.js.map +1 -1
  87. package/usage-report/codegen-delivery-alert.js +2 -2
  88. package/usage-report/codegen-delivery-alert.js.map +1 -1
  89. package/usage-report/vibe-traffic-alert-util.d.ts +74 -0
  90. package/usage-report/vibe-traffic-alert-util.js +237 -0
  91. package/usage-report/vibe-traffic-alert-util.js.map +1 -0
  92. package/usage-report/vibe-traffic-alert.d.ts +2 -0
  93. package/usage-report/vibe-traffic-alert.js +99 -0
  94. package/usage-report/vibe-traffic-alert.js.map +1 -0
@@ -24,16 +24,20 @@ import { bumpAppRecency } from "../intern/bump-app-recency.js";
24
24
  import { waitChatPersistIdle } from "../intern/chat-persist-gate.js";
25
25
  import { peekInFlightTurns } from "../intern/in-flight-turns.js";
26
26
  import { acquireTurnBuildLock, peekBuildLockEntry, releaseTurnBuildLock } from "../intern/codegen-build-lock.js";
27
- import { agentAwaitBuildEnabled, agentFirstTurnEnabled, agentLaneEnabled } from "../intern/agent-lane-flag.js";
27
+ import { agentAwaitBuildEnabled, agentFirstTurnEnabled, agentLaneEnabled, agentSessionEnabled } from "../intern/agent-lane-flag.js";
28
+ import { deliverAgentSession } from "../intern/agent-session-runtime.js";
29
+ import { runEditVibeTool } from "../intern/agent-build-tools.js";
28
30
  import { claimFirstTurnMint, claimMintJob } from "../intern/mint-job-store.js";
29
31
  import { codegenTurnLane, legacyVariantHint, routeAgentTurn } from "../intern/agent-router.js";
30
32
  import { assembleAgentPayload } from "../intern/agent-assembly.js";
31
33
  import { createAgentBelt } from "../intern/agent-belt.js";
32
- import { createAgentTurnStream } from "../intern/agent-turn.js";
34
+ import { createAgentTurnStream, agentTotalBudgetMs } from "../intern/agent-turn.js";
33
35
  import { createAgentBuildDispatcher } from "../intern/agent-dispatcher.js";
34
36
  import { decideAgentWake, fireAgentWakeTurn, readAgentWakeBudget, renderAgentWakeNote, renderBuildSettledSentence, } from "../intern/agent-wake.js";
35
37
  import { createPendingBuildLandings, renderBuildLandingNote, settleTurnTerminal } from "../intern/turn-terminal.js";
36
- import { resolveAgentActor } from "../intern/agent-actor.js";
38
+ import { resolveAgentActor, resolveChatApp } from "../intern/agent-actor.js";
39
+ import { langForFilename } from "../intern/ensure-push-seeded-chat.js";
40
+ import { promptDispatchContext } from "../intern/prompt-dispatch-context.js";
37
41
  import { dispatchLlmRequestWithFallback } from "./llm-dispatch.js";
38
42
  import { scheduleUrlResearch } from "../intern/url-research-queue.js";
39
43
  import { createProviderSseStream } from "../intern/provider-sse.js";
@@ -42,6 +46,7 @@ import { emitWorkTurnStatus } from "../intern/work-turn-status.js";
42
46
  import { closeTurn, extendOpenTurnDeadline, OPEN_TURN_AWAIT_BUILD_EXTENSION_MS, shouldCloseTurnRecord, } from "../intern/open-turn-store.js";
43
47
  import { maxPersistedBlockSeq } from "../intern/persisted-block-seq.js";
44
48
  import { appendBlockEvent, currentUserMessageText, handlerLlmRequest } from "./llm-dispatch.js";
49
+ import { createAgentEvidenceWriter, nextAgentSessionMessages } from "../intern/agent-evidence.js";
45
50
  import { handleLlmResponse, } from "./stream-assembly.js";
46
51
  import { handleProdiaImageRequest } from "./image-gen.js";
47
52
  import { handleFSPrompt } from "./fs-prompt.js";
@@ -187,6 +192,8 @@ export const promptChatSection = {
187
192
  }
188
193
  let preAssembled;
189
194
  let agentAssembled;
195
+ let agentSession;
196
+ let agentEvidence;
190
197
  let agentFirstTurnMint = false;
191
198
  if (isReqCreationPromptChatSection(orig)) {
192
199
  if (agentWakeTurn === false && !orig.prompt.messages.some((m) => m.role === "user")) {
@@ -284,10 +291,157 @@ export const promptChatSection = {
284
291
  ...(agentWakeNote === undefined ? {} : { wakeNote: agentWakeNote }),
285
292
  ...(buildInFlight === undefined ? {} : { buildInFlight: { promptId: buildInFlight.promptId } }),
286
293
  ...(agentFirstTurnMint ? { noAppYet: true } : {}),
294
+ ...(agentSessionEnabled(vctx) && agentFirstTurnMint === false ? { session: true } : {}),
287
295
  });
288
296
  if (rAgent.isErr())
289
297
  return Result.Err(rAgent);
290
298
  agentAssembled = { model: rAgent.Ok().model, messages: rAgent.Ok().messages };
299
+ if (agentSessionEnabled(vctx) && agentFirstTurnMint === false) {
300
+ const rawIncoming = orig.prompt.messages.filter(isUserChatMessage);
301
+ const newMessageCount = orig.prompt.messages.filter(isChatMessage).length;
302
+ const incoming = newMessageCount === 0 ? [] : rAgent.Ok().messages.slice(-newMessageCount).filter(isUserChatMessage);
303
+ const delivered = incoming.length === 0 && agentWakeTurn
304
+ ? [
305
+ {
306
+ role: "user",
307
+ content: [
308
+ {
309
+ type: "text",
310
+ text: agentWakeNote ??
311
+ "[session resumed] Continue from your persisted context and pending results. Inspect outcomes before retrying interrupted tools.",
312
+ },
313
+ ],
314
+ },
315
+ ]
316
+ : incoming;
317
+ const acquired = await deliverAgentSession(vctx, {
318
+ chatId: req.chatId,
319
+ userId: req._auth.verifiedAuth.claims.userId,
320
+ ownerHandle: resChat.ownerHandle,
321
+ appSlug: resChat.appSlug,
322
+ }, { id: ctx.validated.tid, messages: delivered });
323
+ if (acquired.isErr())
324
+ return Result.Err(acquired.Err());
325
+ const { live, created } = acquired.Ok();
326
+ if (created === false) {
327
+ const receiptId = vctx.sthis.nextId(12).str;
328
+ const receiptEvidence = createAgentEvidenceWriter({
329
+ chatId: req.chatId,
330
+ promptId: receiptId,
331
+ sessionId: live.promptId,
332
+ append: (evt, blockSeq) => appendBlockEvent({ ctx, vctx, req, promptId: receiptId, blockSeq, evt }),
333
+ });
334
+ const linked = await receiptEvidence({ kind: "active" });
335
+ if (linked.isErr())
336
+ return Result.Err(linked.Err());
337
+ await ctx.send.send(ctx, wrapMsgBase(ctx.validated, {
338
+ payload: {
339
+ type: "vibes.diy.res-prompt-chat-section",
340
+ chatId: req.chatId,
341
+ ownerHandle: resChat.ownerHandle,
342
+ appSlug: resChat.appSlug,
343
+ promptId: receiptId,
344
+ outerTid: req.outerTid,
345
+ mode: req.mode,
346
+ },
347
+ tid: ctx.validated.tid,
348
+ src: "agentSession",
349
+ }));
350
+ const echo = await appendBlockEvent({
351
+ ctx,
352
+ vctx,
353
+ req,
354
+ promptId: receiptId,
355
+ blockSeq: 0,
356
+ evt: {
357
+ type: "prompt.req",
358
+ streamId: receiptId,
359
+ chatId: req.chatId,
360
+ seq: 0,
361
+ request: { messages: rawIncoming },
362
+ timestamp: new Date(),
363
+ },
364
+ });
365
+ if (echo.isErr())
366
+ return Result.Err(echo.Err());
367
+ const terminal = await appendBlockEvent({
368
+ ctx,
369
+ vctx,
370
+ req,
371
+ promptId: receiptId,
372
+ blockSeq: 1,
373
+ evt: {
374
+ type: "block.end",
375
+ streamId: receiptId,
376
+ blockId: receiptId,
377
+ blockNr: 0,
378
+ seq: 1,
379
+ timestamp: new Date(),
380
+ stats: {
381
+ toplevel: { lines: 0, bytes: 0 },
382
+ code: { lines: 0, bytes: 0 },
383
+ image: { lines: 0, bytes: 0 },
384
+ total: { lines: 0, bytes: 0 },
385
+ },
386
+ usage: { given: [], calculated: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 } },
387
+ },
388
+ });
389
+ if (terminal.isErr())
390
+ return Result.Err(terminal.Err());
391
+ const ended = await appendBlockEvent({
392
+ ctx,
393
+ vctx,
394
+ req,
395
+ promptId: receiptId,
396
+ blockSeq: 2,
397
+ evt: { type: "prompt.block-end", streamId: receiptId, chatId: req.chatId, seq: 2, timestamp: new Date() },
398
+ });
399
+ if (ended.isErr())
400
+ return Result.Err(ended.Err());
401
+ return Result.Ok(EventoResult.Continue);
402
+ }
403
+ agentSession = live;
404
+ agentEvidence = createAgentEvidenceWriter({
405
+ chatId: req.chatId,
406
+ promptId: live.promptId,
407
+ sessionId: live.promptId,
408
+ append: (evt, blockSeq) => appendBlockEvent({ ctx, vctx, req, promptId: live.promptId, blockSeq, evt }),
409
+ });
410
+ const active = await agentEvidence({
411
+ kind: "active",
412
+ ...(live.supersedes === undefined ? {} : { supersedes: live.supersedes }),
413
+ });
414
+ if (active.isErr()) {
415
+ live.close();
416
+ return Result.Err(active.Err());
417
+ }
418
+ if (live.session.messages().length === 0) {
419
+ const initial = agentAssembled.messages.slice(0, agentAssembled.messages.length - newMessageCount - (agentWakeNote === undefined ? 0 : 1));
420
+ const saved = await live.session.checkpoint(initial);
421
+ if (saved.isErr()) {
422
+ live.close();
423
+ return Result.Err(saved.Err());
424
+ }
425
+ }
426
+ const events = await nextAgentSessionMessages(live.session, agentEvidence);
427
+ if (events.isErr()) {
428
+ live.close();
429
+ return Result.Err(events.Err());
430
+ }
431
+ agentAssembled = {
432
+ ...agentAssembled,
433
+ messages: [
434
+ ...agentAssembled.messages.filter((message) => message.role === "system"),
435
+ ...live.session.messages().filter((message) => message.role !== "system"),
436
+ ...(events.Ok() ?? []),
437
+ ],
438
+ };
439
+ const saved = await live.session.checkpoint(agentAssembled.messages);
440
+ if (saved.isErr()) {
441
+ live.close();
442
+ return Result.Err(saved.Err());
443
+ }
444
+ }
291
445
  }
292
446
  else {
293
447
  const rAssembled = await assemblePromptPayload(vctx, {
@@ -420,6 +574,7 @@ export const promptChatSection = {
420
574
  else if (agentAssembled !== undefined && isReqPromptLLMChatSection(orig)) {
421
575
  const agentPayload = agentAssembled;
422
576
  prompSectionAction = async (scope, blockSeq) => {
577
+ const liveSession = agentSession;
423
578
  const rActor = await resolveAgentActor(vctx, {
424
579
  userId: req._auth.verifiedAuth.claims.userId,
425
580
  scope: { ownerHandle: resChat.ownerHandle, appSlug: resChat.appSlug },
@@ -432,11 +587,13 @@ export const promptChatSection = {
432
587
  appSlug: resChat.appSlug,
433
588
  editBaseFsId: undefined,
434
589
  });
590
+ if (liveSession !== undefined && rVfs.isErr())
591
+ return Result.Err(rVfs.Err());
435
592
  if (rVfs.isErr()) {
436
593
  vctx.logger.Warn().Err(rVfs).Str("chatId", req.chatId).Msg("agent-turn-vfs-load-failed");
437
594
  }
438
- const vfs = rVfs.isOk() ? rVfs.Ok() : new Map();
439
- const pendingBuilds = agentAwaitBuildEnabled(vctx)
595
+ const vfs = new Map(rVfs.isOk() ? rVfs.Ok() : []);
596
+ const pendingBuilds = agentSessionEnabled(vctx) === false && agentAwaitBuildEnabled(vctx)
440
597
  ? createPendingBuildLandings({
441
598
  scope: vctx.connections,
442
599
  renderLanding: (outcome) => renderBuildLandingNote(renderBuildSettledSentence(outcome)),
@@ -466,8 +623,8 @@ export const promptChatSection = {
466
623
  outerTid: req.outerTid,
467
624
  chatId: req.chatId,
468
625
  budget: missed.budget,
469
- note: renderAgentWakeNote(landing.outcome),
470
- handle: (wakeCtx) => promptChatSection.handle(wakeCtx),
626
+ note: renderAgentWakeNote(landing.outcome, agentSessionEnabled(vctx)),
627
+ handle: (wakeCtx) => promptChatSection.handle(promptDispatchContext(ctx, wakeCtx.validated)),
471
628
  });
472
629
  },
473
630
  onRegister: (target) => {
@@ -493,8 +650,9 @@ export const promptChatSection = {
493
650
  auth: orig.auth,
494
651
  outerTid: req.outerTid,
495
652
  handle: (dispatchCtx) => promptChatSection.handle(dispatchCtx),
496
- wakeBudget: agentWakeBudget,
653
+ wakeBudget: liveSession === undefined ? agentWakeBudget : 0,
497
654
  ...(pendingBuilds === undefined ? {} : { onPendingBuild: (t) => pendingBuilds.register(t) }),
655
+ ...(agentSession === undefined ? {} : { onPendingBuild: (t) => agentSession?.watch(t.promptId) }),
498
656
  ...(agentFirstTurnMint
499
657
  ? { firstTurn: { chatId: req.chatId, ...(submittedAt === undefined ? {} : { submittedAt }) } }
500
658
  : {}),
@@ -516,6 +674,8 @@ export const promptChatSection = {
516
674
  : {}),
517
675
  ...(agentFetchUrlContext === undefined ? {} : { fetchUrl: agentFetchUrlContext }),
518
676
  dispatcher,
677
+ ...(liveSession === undefined ? {} : { queueEdit: (input) => liveSession.queue(input) }),
678
+ ...(liveSession === undefined ? {} : { fileSession: liveSession }),
519
679
  onStyleApplied: async (applied) => {
520
680
  await appendBlockEvent({
521
681
  ctx,
@@ -542,6 +702,92 @@ export const promptChatSection = {
542
702
  .Msg("agent-turn-dispatched-build");
543
703
  },
544
704
  });
705
+ if (agentSession !== undefined) {
706
+ await agentSession.bind((input) => runEditVibeTool(vctx, {
707
+ actor: rActor.Ok(),
708
+ chatId: req.chatId,
709
+ input,
710
+ dispatcher,
711
+ }), async (commit) => {
712
+ const currentActor = await resolveAgentActor(vctx, {
713
+ userId: rActor.Ok().userId,
714
+ requestedHandle: rActor.Ok().ownerHandle,
715
+ scope: rActor.Ok().scope,
716
+ });
717
+ if (currentActor.isErr())
718
+ return Result.Err(currentActor.Err());
719
+ const scoped = await resolveChatApp(vctx, currentActor.Ok(), req.chatId);
720
+ if (scoped.isErr())
721
+ return Result.Err(scoped.Err());
722
+ const commitId = vctx.sthis.nextId(12).str;
723
+ const lock = acquireTurnBuildLock({
724
+ vctx,
725
+ chatId: req.chatId,
726
+ promptId: commitId,
727
+ movesFilesystem: true,
728
+ wakesOnTerminal: false,
729
+ });
730
+ if (lock.acquired === false)
731
+ return Result.Err("A build is running; the staged commit could not acquire the write lane.");
732
+ const committed = { promptBlockEndEmitted: false };
733
+ const saved = await exception2Result(() => handleFSPrompt({
734
+ scope,
735
+ vctx,
736
+ ctx,
737
+ promptId: commitId,
738
+ blockSeq: 0,
739
+ terminal: committed,
740
+ beginDirect: true,
741
+ resChat: { ...resChat, mode: "fs-update" },
742
+ req: {
743
+ type: "vibes.diy.req-prompt-chat-section",
744
+ mode: "fs-update",
745
+ auth: orig.auth,
746
+ _auth: req._auth,
747
+ chatId: req.chatId,
748
+ outerTid: req.outerTid,
749
+ agentFileCommit: commit,
750
+ fsUpdate: {
751
+ update: commit.changes.map((file) => ({
752
+ type: "code-block",
753
+ filename: file.path,
754
+ content: file.contents,
755
+ lang: langForFilename(file.path),
756
+ })),
757
+ remove: [],
758
+ },
759
+ },
760
+ })).finally(() => {
761
+ committed.releasePersistGate?.();
762
+ releaseTurnBuildLock(vctx, req.chatId, commitId);
763
+ });
764
+ if (saved.isErr()) {
765
+ const last = await maxPersistedBlockSeq(vctx.sql, { chatId: req.chatId, promptId: commitId });
766
+ if (last.isErr())
767
+ return Result.Err(last.Err());
768
+ const seq = (last.Ok() ?? -1) + 1;
769
+ await appendBlockEvent({
770
+ ctx,
771
+ vctx,
772
+ req,
773
+ promptId: commitId,
774
+ blockSeq: seq,
775
+ evt: {
776
+ type: "prompt.error",
777
+ streamId: commitId,
778
+ chatId: req.chatId,
779
+ seq,
780
+ timestamp: new Date(),
781
+ error: saved.Err().message,
782
+ },
783
+ });
784
+ return Result.Err(saved.Err());
785
+ }
786
+ if (committed.fsRef === undefined)
787
+ return Result.Err("The commit did not persist a version.");
788
+ return Result.Ok({ ok: true, saved: true, fsRef: committed.fsRef, paths: commit.changes.map((file) => file.path) });
789
+ });
790
+ }
545
791
  const res = await handlerLlmRequest({
546
792
  ctx,
547
793
  blockSeq,
@@ -553,6 +799,13 @@ export const promptChatSection = {
553
799
  preAssembled: { ...agentPayload, isInitialTurn: false },
554
800
  serverTools: belt.definitions,
555
801
  });
802
+ const evidence = agentEvidence ??
803
+ createAgentEvidenceWriter({
804
+ chatId: req.chatId,
805
+ promptId,
806
+ ...(liveSession === undefined ? {} : { sessionId: liveSession.promptId }),
807
+ append: (evt, blockSeq) => appendBlockEvent({ ctx, vctx, req, promptId, blockSeq, evt }),
808
+ });
556
809
  let providerStream;
557
810
  const rSse = createProviderSseStream(res.res, promptId);
558
811
  if (rSse.isErr()) {
@@ -569,8 +822,43 @@ export const promptChatSection = {
569
822
  request: res.llmReq,
570
823
  stream: rSse.Ok(),
571
824
  abort: res.abort,
825
+ onToolEvidence: ({ kind, call, text }) => evidence({ kind, callId: call.id, name: call.name, text }),
572
826
  ...(pendingBuilds === undefined ? {} : { pending: pendingBuilds.pending }),
827
+ ...(liveSession === undefined
828
+ ? {}
829
+ : {
830
+ session: {
831
+ checkpoint: async (messages) => {
832
+ const saved = await liveSession.session.checkpoint(messages);
833
+ if (saved.isErr())
834
+ return saved;
835
+ const extended = await extendOpenTurnDeadline(vctx.sql, {
836
+ chatId: req.chatId,
837
+ promptId,
838
+ deadlineAtMs: Date.now() + agentTotalBudgetMs(vctx) + OPEN_TURN_AWAIT_BUILD_EXTENSION_MS,
839
+ });
840
+ if (extended.isErr())
841
+ return Result.Err(extended.Err());
842
+ return Result.Ok();
843
+ },
844
+ next: () => nextAgentSessionMessages(liveSession.session, evidence),
845
+ dispatchBudgetMs: agentTotalBudgetMs(vctx),
846
+ },
847
+ }),
573
848
  dispatch: async ({ request, budget }) => {
849
+ if (agentSession !== undefined) {
850
+ const latest = await loadWorkVfs(vctx, {
851
+ chatId: req.chatId,
852
+ ownerHandle: resChat.ownerHandle,
853
+ appSlug: resChat.appSlug,
854
+ editBaseFsId: undefined,
855
+ });
856
+ if (latest.isErr())
857
+ return Result.Err(latest.Err());
858
+ vfs.clear();
859
+ for (const [path, content] of latest.Ok())
860
+ vfs.set(path, content);
861
+ }
574
862
  const dispatch = await dispatchLlmRequestWithFallback({
575
863
  vctx,
576
864
  mode: req.mode,
@@ -784,7 +1072,7 @@ export const promptChatSection = {
784
1072
  if (rBump.isErr()) {
785
1073
  vctx.logger.Warn().Err(rBump).Msg("bumpAppRecency failed");
786
1074
  }
787
- const promptId = vctx.sthis.nextId(96 / 8).str;
1075
+ const promptId = agentSession?.promptId ?? vctx.sthis.nextId(96 / 8).str;
788
1076
  const movesFilesystem = isSourceChangingMode(req.mode) && discuss === false && agentAssembled === undefined;
789
1077
  const wakesOnTerminal = decideAgentWake({
790
1078
  agentLane,
@@ -814,6 +1102,7 @@ export const promptChatSection = {
814
1102
  src: "promptChatSection",
815
1103
  })));
816
1104
  if (rAck.isErr()) {
1105
+ agentSession?.close();
817
1106
  releaseTurnBuildLock(vctx, req.chatId, promptId);
818
1107
  return Result.Err(rAck);
819
1108
  }
@@ -916,6 +1205,7 @@ export const promptChatSection = {
916
1205
  });
917
1206
  }
918
1207
  peekInFlightTurns(req.chatId)?.delete(promptId);
1208
+ agentSession?.close();
919
1209
  releaseTurnBuildLock(vctx, req.chatId, promptId);
920
1210
  terminal.releasePersistGate?.();
921
1211
  const landingOutcome = {
@@ -931,7 +1221,7 @@ export const promptChatSection = {
931
1221
  agentLane,
932
1222
  agentDispatched: agentDispatchedBuild,
933
1223
  movedFilesystem: movesFilesystem,
934
- budget: agentWakeBudget,
1224
+ budget: agentSessionEnabled(vctx) ? 1 : agentWakeBudget,
935
1225
  });
936
1226
  if (consumed === true) {
937
1227
  vctx.logger
@@ -951,8 +1241,8 @@ export const promptChatSection = {
951
1241
  note: renderAgentWakeNote({
952
1242
  outcome: turnErrored ? "error" : terminal.settled?.applied === true ? "applied" : "no-code",
953
1243
  ...(terminal.touchedPaths === undefined ? {} : { touchedPaths: terminal.touchedPaths }),
954
- }),
955
- handle: (wakeCtx) => promptChatSection.handle(wakeCtx),
1244
+ }, agentSessionEnabled(vctx)),
1245
+ handle: (wakeCtx) => promptChatSection.handle(promptDispatchContext(ctx, wakeCtx.validated)),
956
1246
  });
957
1247
  }
958
1248
  else {