rivet-design 0.13.3 → 0.13.5

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 (106) hide show
  1. package/dist/agent-variants/SessionStore.d.ts +25 -4
  2. package/dist/agent-variants/SessionStore.d.ts.map +1 -1
  3. package/dist/agent-variants/SessionStore.js +32 -0
  4. package/dist/agent-variants/SessionStore.js.map +1 -1
  5. package/dist/agent-variants/VariantWorkspaceManager.d.ts +43 -0
  6. package/dist/agent-variants/VariantWorkspaceManager.d.ts.map +1 -0
  7. package/dist/agent-variants/VariantWorkspaceManager.js +216 -0
  8. package/dist/agent-variants/VariantWorkspaceManager.js.map +1 -0
  9. package/dist/agent-variants/WorktreeOrchestrator.d.ts +40 -3
  10. package/dist/agent-variants/WorktreeOrchestrator.d.ts.map +1 -1
  11. package/dist/agent-variants/WorktreeOrchestrator.js +424 -62
  12. package/dist/agent-variants/WorktreeOrchestrator.js.map +1 -1
  13. package/dist/agent-variants/contracts.d.ts +72 -14
  14. package/dist/agent-variants/contracts.d.ts.map +1 -1
  15. package/dist/agent-variants/contracts.js +17 -3
  16. package/dist/agent-variants/contracts.js.map +1 -1
  17. package/dist/agent-variants/runLabel.d.ts +5 -4
  18. package/dist/agent-variants/runLabel.d.ts.map +1 -1
  19. package/dist/agent-variants/runLabel.js +16 -8
  20. package/dist/agent-variants/runLabel.js.map +1 -1
  21. package/dist/agent-variants/workItemBriefing.d.ts +26 -0
  22. package/dist/agent-variants/workItemBriefing.d.ts.map +1 -0
  23. package/dist/agent-variants/workItemBriefing.js +110 -0
  24. package/dist/agent-variants/workItemBriefing.js.map +1 -0
  25. package/dist/cli/client.d.ts +2 -0
  26. package/dist/cli/client.d.ts.map +1 -1
  27. package/dist/cli/client.js +1 -1
  28. package/dist/cli/client.js.map +1 -1
  29. package/dist/cli/commands/mcpServe.d.ts.map +1 -1
  30. package/dist/cli/commands/mcpServe.js +47 -20
  31. package/dist/cli/commands/mcpServe.js.map +1 -1
  32. package/dist/cli/commands/status.d.ts.map +1 -1
  33. package/dist/cli/commands/status.js +3 -1
  34. package/dist/cli/commands/status.js.map +1 -1
  35. package/dist/cli/commands/variants.d.ts.map +1 -1
  36. package/dist/cli/commands/variants.js +118 -9
  37. package/dist/cli/commands/variants.js.map +1 -1
  38. package/dist/cli/hostWorkNextAction.d.ts +33 -26
  39. package/dist/cli/hostWorkNextAction.d.ts.map +1 -1
  40. package/dist/cli/hostWorkNextAction.js +47 -24
  41. package/dist/cli/hostWorkNextAction.js.map +1 -1
  42. package/dist/cli/router.js +2 -2
  43. package/dist/cli/router.js.map +1 -1
  44. package/dist/hosts/workerDefaults.d.ts +62 -0
  45. package/dist/hosts/workerDefaults.d.ts.map +1 -0
  46. package/dist/hosts/workerDefaults.js +77 -0
  47. package/dist/hosts/workerDefaults.js.map +1 -0
  48. package/dist/install/harnesses.d.ts.map +1 -1
  49. package/dist/install/harnesses.js +21 -2
  50. package/dist/install/harnesses.js.map +1 -1
  51. package/dist/install/mcpRegistration.d.ts +1 -1
  52. package/dist/install/mcpRegistration.d.ts.map +1 -1
  53. package/dist/install/mcpRegistration.js +23 -10
  54. package/dist/install/mcpRegistration.js.map +1 -1
  55. package/dist/mcp/changeBatchClassification.d.ts +2 -0
  56. package/dist/mcp/changeBatchClassification.d.ts.map +1 -1
  57. package/dist/mcp/changeBatchClassification.js +5 -0
  58. package/dist/mcp/changeBatchClassification.js.map +1 -1
  59. package/dist/routes/agentVariants.d.ts.map +1 -1
  60. package/dist/routes/agentVariants.js +11 -4
  61. package/dist/routes/agentVariants.js.map +1 -1
  62. package/dist/routes/emptyStatePage.d.ts +14 -0
  63. package/dist/routes/emptyStatePage.d.ts.map +1 -0
  64. package/dist/routes/emptyStatePage.js +601 -0
  65. package/dist/routes/emptyStatePage.js.map +1 -0
  66. package/dist/routes/static.d.ts.map +1 -1
  67. package/dist/routes/static.js +2 -73
  68. package/dist/routes/static.js.map +1 -1
  69. package/dist/services/TelemetryService.d.ts +8 -0
  70. package/dist/services/TelemetryService.d.ts.map +1 -1
  71. package/dist/services/TelemetryService.js +24 -0
  72. package/dist/services/TelemetryService.js.map +1 -1
  73. package/dist/services/VariantGenerationService.d.ts.map +1 -1
  74. package/dist/services/VariantGenerationService.js +14 -0
  75. package/dist/services/VariantGenerationService.js.map +1 -1
  76. package/dist/services/VariantRunService.d.ts +5 -2
  77. package/dist/services/VariantRunService.d.ts.map +1 -1
  78. package/dist/services/VariantRunService.js +40 -14
  79. package/dist/services/VariantRunService.js.map +1 -1
  80. package/dist/services/WorktreeManager.d.ts.map +1 -1
  81. package/dist/services/WorktreeManager.js +7 -2
  82. package/dist/services/WorktreeManager.js.map +1 -1
  83. package/dist/types/change-request-types.d.ts +5 -0
  84. package/dist/types/change-request-types.d.ts.map +1 -1
  85. package/dist/utils/cloneOrCopyDirSync.d.ts +14 -0
  86. package/dist/utils/cloneOrCopyDirSync.d.ts.map +1 -0
  87. package/dist/utils/cloneOrCopyDirSync.js +72 -0
  88. package/dist/utils/cloneOrCopyDirSync.js.map +1 -0
  89. package/dist/utils/skillWriter.d.ts +8 -0
  90. package/dist/utils/skillWriter.d.ts.map +1 -1
  91. package/dist/utils/skillWriter.js +35 -0
  92. package/dist/utils/skillWriter.js.map +1 -1
  93. package/dist/utils/skills/claude-skill.d.ts +2 -2
  94. package/dist/utils/skills/claude-skill.d.ts.map +1 -1
  95. package/dist/utils/skills/claude-skill.js +1 -1
  96. package/dist/utils/skills/cli-guidance.d.ts +2 -2
  97. package/dist/utils/skills/cli-guidance.d.ts.map +1 -1
  98. package/dist/utils/skills/cli-guidance.js +11 -10
  99. package/dist/utils/skills/cli-guidance.js.map +1 -1
  100. package/dist/utils/skills/cursor-rules.d.ts +2 -2
  101. package/dist/utils/skills/cursor-rules.d.ts.map +1 -1
  102. package/dist/utils/skills/cursor-rules.js +1 -1
  103. package/package.json +3 -1
  104. package/src/ui/dist/assets/main-Ct4AXMqD.js +422 -0
  105. package/src/ui/dist/index.html +1 -1
  106. package/src/ui/dist/assets/main-Bd9GWv8x.js +0 -419
@@ -17,6 +17,7 @@ const devServerError_1 = require("../utils/devServerError");
17
17
  const errors_1 = require("./errors");
18
18
  const createProjectArtifacts_1 = require("./createProjectArtifacts");
19
19
  const contracts_1 = require("./contracts");
20
+ const workItemBriefing_1 = require("./workItemBriefing");
20
21
  const briefContent_1 = require("./briefContent");
21
22
  const runLabel_1 = require("./runLabel");
22
23
  const runPlan_1 = require("./runPlan");
@@ -24,6 +25,7 @@ const viteReactTs_1 = require("../services/templates/viteReactTs");
24
25
  const StaticPreviewServer_1 = require("../services/StaticPreviewServer");
25
26
  const designCatalog_1 = require("../services/templates/designCatalog");
26
27
  const previewQa_1 = require("./previewQa");
28
+ const VariantWorkspaceManager_1 = require("./VariantWorkspaceManager");
27
29
  const diffQa_1 = require("./diffQa");
28
30
  const VariantHistoryService_1 = require("../services/VariantHistoryService");
29
31
  const variantContext_1 = require("./variantContext");
@@ -60,6 +62,16 @@ const BRIEF_LABEL_ALTERNATIVES = [
60
62
  'Density',
61
63
  'Color',
62
64
  ];
65
+ const FALLBACK_DIRECTION_FOCI = [
66
+ 'Emphasize hierarchy and one unmistakable primary action.',
67
+ 'Use stronger contrast, scale shifts, and a more expressive visual system.',
68
+ 'Favor a quieter, spacious composition with restrained supporting detail.',
69
+ 'Make information density and fast scanning the organizing principle.',
70
+ 'Lead with imagery and let content frame the visual story.',
71
+ 'Use modular cards and clear grouping to expose the product structure.',
72
+ 'Explore tactile depth, layering, and purposeful interaction cues.',
73
+ 'Reduce the concept to its most essential typography and geometry.',
74
+ ];
63
75
  // Upper bound on waiting for in-flight history persists during session
64
76
  // teardown. Generous enough for a full-repo snapshot on a large project;
65
77
  // small enough that a wedged git subprocess (which has its own 120s block
@@ -350,6 +362,12 @@ const buildViewDisabledReason = (args) => {
350
362
  return 'Variant is still running';
351
363
  };
352
364
  const NOOP_TELEMETRY = { track: () => undefined };
365
+ const isImplementationWorkItemKind = (kind) => kind === 'static_preview' || kind === 'refine_variant' || kind === 'code_gen';
366
+ const DEFAULT_EXECUTION_MODE_BY_KIND = {
367
+ static_preview: 'create',
368
+ refine_variant: 'refine',
369
+ code_gen: 'create',
370
+ };
353
371
  const DESIGN_CREATION_ACTION_RE = /\b(create|build|design|make|redesign|refresh|update)\b/i;
354
372
  const DESIGN_CREATION_SURFACE_RE = /\b(home\s?page|landing page|dashboard|screen|site|website|app|page|ui|interface|component|hero)\b/i;
355
373
  const NON_VISUAL_CHANGE_RE = /\b(api|backend|database|event|lint|query|server|test|tracking|typecheck)\b/i;
@@ -658,16 +676,71 @@ class AgentVariantsOrchestrator {
658
676
  }
659
677
  getHostWork(sessionId) {
660
678
  return this.store.getHostWork(sessionId).map((item) => {
661
- const worktreePath = this.getWorktreePath(sessionId, item.workId);
662
- const baseInput = item.input && typeof item.input === 'object'
663
- ? item.input
664
- : {};
665
- return {
666
- ...item,
667
- input: { ...baseInput, worktreePath },
679
+ const kind = item.kind;
680
+ if (!isImplementationWorkItemKind(kind)) {
681
+ const planningItem = {
682
+ ...item,
683
+ kind,
684
+ };
685
+ return planningItem;
686
+ }
687
+ const input = (item.input ?? {});
688
+ const workspace = this.getAssignedWorkspace({
689
+ sessionId,
690
+ workItemId: item.workId,
691
+ kind,
692
+ });
693
+ const mode = input.sourceVariantId
694
+ ? 'vary'
695
+ : DEFAULT_EXECUTION_MODE_BY_KIND[kind];
696
+ const sourceLabel = input.sourceLabel ?? (mode === 'refine' ? input.briefLabel : undefined);
697
+ const sourceBody = input.sourceBody ??
698
+ (mode === 'refine' ? input.briefBody : undefined) ??
699
+ sourceLabel;
700
+ const implementationItem = {
701
+ workId: item.workId,
702
+ variantId: item.variantId,
703
+ kind,
704
+ status: item.status,
705
+ leaseId: item.leaseId,
706
+ attempt: item.attempt,
707
+ leaseExpiresAt: item.leaseExpiresAt,
708
+ workspace,
709
+ mode,
710
+ direction: {
711
+ label: input.briefLabel ?? 'Variant',
712
+ brief: input.briefBody ?? input.instruction ?? 'Create this variant',
713
+ ...(input.briefNotes ? { notes: input.briefNotes } : {}),
714
+ },
715
+ ...(sourceLabel && sourceBody
716
+ ? { sourceDirection: { label: sourceLabel, brief: sourceBody } }
717
+ : {}),
718
+ ...(input.relevantFiles?.length
719
+ ? { relevantFiles: input.relevantFiles }
720
+ : {}),
721
+ ...(input.instruction ? { instruction: input.instruction } : {}),
722
+ ...(input.target ? { target: input.target } : {}),
723
+ ...(input.targetElement ? { targetElement: input.targetElement } : {}),
724
+ ...(input.context?.contextBundlePath
725
+ ? { contextBundlePath: input.context.contextBundlePath }
726
+ : {}),
727
+ completion: kind === 'code_gen'
728
+ ? { kind: 'worktree_diff' }
729
+ : { kind: 'static_artifact' },
668
730
  };
731
+ return implementationItem;
669
732
  });
670
733
  }
734
+ getAssignedWorkspace(args) {
735
+ const workspace = this.resources
736
+ .get(args.sessionId)
737
+ ?.workspaceManager.getAssignedWorkspace(args.workItemId);
738
+ const expectedKind = args.kind === 'code_gen' ? 'git_worktree' : 'artifact_directory';
739
+ if (!workspace || workspace.kind !== expectedKind) {
740
+ throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `Variant ${args.workItemId} has no assigned ${expectedKind} workspace`);
741
+ }
742
+ return workspace;
743
+ }
671
744
  getVariantsTelemetryMode(projectContext, _target) {
672
745
  if (projectContext.kind === 'fresh' && projectContext.variantContext) {
673
746
  return 'source_grounded';
@@ -967,6 +1040,35 @@ class AgentVariantsOrchestrator {
967
1040
  }
968
1041
  requestWork(args) {
969
1042
  const result = this.store.requestWork(args);
1043
+ try {
1044
+ this.ensureStaticWorkspaces(args.sessionId, result.leasedWorkItems);
1045
+ }
1046
+ catch (error) {
1047
+ for (const item of result.leasedWorkItems) {
1048
+ if (item.kind !== 'static_preview' && item.kind !== 'refine_variant') {
1049
+ continue;
1050
+ }
1051
+ try {
1052
+ this.store.reportComplete({
1053
+ sessionId: args.sessionId,
1054
+ workItemId: item.id,
1055
+ leaseId: result.leaseId,
1056
+ attempt: item.attempt,
1057
+ status: 'failed',
1058
+ error: {
1059
+ code: 'RUNTIME_VALIDATION_FAILED',
1060
+ message: error instanceof Error
1061
+ ? error.message
1062
+ : 'Failed to assign static variant workspace',
1063
+ },
1064
+ });
1065
+ }
1066
+ catch (reportError) {
1067
+ log.warn(`Failed to record static workspace provisioning failure for ${item.id}`, reportError);
1068
+ }
1069
+ }
1070
+ throw error;
1071
+ }
970
1072
  // Record lease times so variant_completed can report lease→report
971
1073
  // duration. (We don't fire a separate code_gen_leased event — the
972
1074
  // funnel signal we care about is per-variant completion duration.)
@@ -977,6 +1079,7 @@ class AgentVariantsOrchestrator {
977
1079
  resources.leasedAt.set(item.id, now);
978
1080
  }
979
1081
  }
1082
+ this.writeLeasedWorkItemBriefings(args.sessionId, result.leasedWorkItems.map((item) => item.id));
980
1083
  this.trackRefinementLeased({ sessionId: args.sessionId, lease: result });
981
1084
  // Decorate each leased item's input with the worktreePath provisioned
982
1085
  // by approve(). The skill instructs agents to write files to
@@ -998,6 +1101,94 @@ class AgentVariantsOrchestrator {
998
1101
  this.emitChange();
999
1102
  return decorated;
1000
1103
  }
1104
+ /**
1105
+ * @effect Writes each leased implementation item's server-authored briefing
1106
+ * BESIDE its workspace so the coordinating host dispatches workers with a
1107
+ * short pointer prompt instead of composing prose per worker. Best-effort:
1108
+ * a briefing write failure never blocks the lease (the host guidance's
1109
+ * packet fields still carry the same data).
1110
+ */
1111
+ writeLeasedWorkItemBriefings(sessionId, leasedWorkItemIds) {
1112
+ if (leasedWorkItemIds.length === 0)
1113
+ return;
1114
+ const leased = new Set(leasedWorkItemIds);
1115
+ for (const item of this.getHostWork(sessionId)) {
1116
+ if (!leased.has(item.workId))
1117
+ continue;
1118
+ if (!isImplementationWorkItemKind(item.kind))
1119
+ continue;
1120
+ const implementation = item;
1121
+ try {
1122
+ const briefingPath = (0, workItemBriefing_1.workItemBriefingPath)(sessionId, item.workId);
1123
+ fs_1.default.mkdirSync(path_1.default.dirname(briefingPath), { recursive: true });
1124
+ fs_1.default.writeFileSync(briefingPath, (0, workItemBriefing_1.renderWorkItemBriefing)(implementation), 'utf8');
1125
+ }
1126
+ catch (error) {
1127
+ log.warn(`Failed to write work-item briefing for ${item.workId}`, error);
1128
+ }
1129
+ }
1130
+ }
1131
+ /** @effect Assigns isolated, server-owned artifact workspaces to leased static work. */
1132
+ ensureStaticWorkspaces(sessionId, items) {
1133
+ const resources = this.ensureResources(sessionId);
1134
+ const projectContext = this.store.getProjectContext(sessionId);
1135
+ // This lane provisions by copying the source artifact tree (vary forks) or
1136
+ // seeding an entry file — it bypasses provisionWorktrees, so it gets its
1137
+ // own provisioning telemetry here.
1138
+ const provisionStartedAt = Date.now();
1139
+ let assignedCount = 0;
1140
+ for (const item of items) {
1141
+ if (item.kind !== 'static_preview' && item.kind !== 'refine_variant') {
1142
+ continue;
1143
+ }
1144
+ if (resources.workspaceManager.getAssignedWorkspace(item.id))
1145
+ continue;
1146
+ assignedCount += 1;
1147
+ const input = (item.input ?? {});
1148
+ const existingProjectPath = projectContext.kind === 'existing'
1149
+ ? projectContext.projectPath
1150
+ : undefined;
1151
+ const existingParent = existingProjectPath && fs_1.default.existsSync(existingProjectPath)
1152
+ ? path_1.default.join(existingProjectPath, '.rivet', 'variant-work', sessionId)
1153
+ : path_1.default.join(os_1.default.tmpdir(), 'rivet-variant-work', sessionId);
1154
+ const parent = projectContext.kind === 'fresh'
1155
+ ? projectContext.workspacePath
1156
+ : existingParent;
1157
+ fs_1.default.mkdirSync(parent, { recursive: true });
1158
+ const rootPath = projectContext.kind === 'fresh'
1159
+ ? path_1.default.join(parent, `.rivet-preview-${item.id}`)
1160
+ : path_1.default.join(parent, item.id);
1161
+ const sourceVariantId = input.sourceVariantId ?? input.variantId;
1162
+ const sourceDirectory = sourceVariantId
1163
+ ? resources.staticPreviews.get(sourceVariantId)?.assetBase
1164
+ : undefined;
1165
+ resources.workspaceManager.assignArtifactWorkspace({
1166
+ workItemId: item.id,
1167
+ rootPath,
1168
+ allowedRoot: parent,
1169
+ entryPath: 'index.html',
1170
+ sourceDirectory,
1171
+ seedHtml: input.currentHtml,
1172
+ });
1173
+ }
1174
+ if (assignedCount > 0) {
1175
+ this.telemetry.track('agent_variants.provisioning_completed', {
1176
+ source: 'mcp',
1177
+ sessionId,
1178
+ kind: 'artifact_directory',
1179
+ variantCount: assignedCount,
1180
+ durationMs: Date.now() - provisionStartedAt,
1181
+ });
1182
+ }
1183
+ }
1184
+ /** Reads a completed static artifact while enforcing its assigned directory boundary. */
1185
+ readAssignedStaticArtifact(sessionId, workItemId) {
1186
+ const workspaceManager = this.resources.get(sessionId)?.workspaceManager;
1187
+ if (!workspaceManager) {
1188
+ throw new errors_1.AgentVariantsError('SESSION_NOT_FOUND', `No live resources for session ${sessionId}`);
1189
+ }
1190
+ return workspaceManager.readArtifact(workItemId);
1191
+ }
1001
1192
  beginStaticPreviewRefinements(args) {
1002
1193
  const seenVariantIds = new Set();
1003
1194
  // Targets whose in-place refinement could neither be created nor folded
@@ -1039,6 +1230,7 @@ class AgentVariantsOrchestrator {
1039
1230
  instruction: target.instruction,
1040
1231
  currentHtml,
1041
1232
  targetElement: target.element,
1233
+ relevantFiles: target.relevantFiles,
1042
1234
  requestId: target.requestId,
1043
1235
  });
1044
1236
  createdWorkItemIds.push(refineItem.id);
@@ -1085,8 +1277,10 @@ class AgentVariantsOrchestrator {
1085
1277
  instruction: target.instruction,
1086
1278
  count: target.count,
1087
1279
  ...(target.briefs ? { briefs: target.briefs } : {}),
1280
+ ...(target.runLabel ? { runLabel: target.runLabel } : {}),
1088
1281
  currentHtml,
1089
1282
  targetElement: target.element,
1283
+ relevantFiles: target.relevantFiles,
1090
1284
  requestId: target.requestId,
1091
1285
  });
1092
1286
  createdWorkItemIds.push(...forks.map((item) => item.id));
@@ -1369,6 +1563,10 @@ class AgentVariantsOrchestrator {
1369
1563
  getWorkItemInput(sessionId, workItemId) {
1370
1564
  return this.store.getWorkItemInput(sessionId, workItemId);
1371
1565
  }
1566
+ /** Returns status after orchestration QA and refinement post-processing. */
1567
+ getWorkItemStatus(sessionId, workItemId) {
1568
+ return this.store.getWorkItemStatus(sessionId, workItemId);
1569
+ }
1372
1570
  getBriefs(sessionId) {
1373
1571
  return this.store.getBriefs(sessionId);
1374
1572
  }
@@ -1494,6 +1692,38 @@ class AgentVariantsOrchestrator {
1494
1692
  getDevServerPort(sessionId, workItemId) {
1495
1693
  return this.resources.get(sessionId)?.worktrees.get(workItemId)?.port;
1496
1694
  }
1695
+ /**
1696
+ * Restores a Rivet-owned variant preview that the active-session idle timer
1697
+ * reclaimed. Other missing-preview causes remain terminal so this path never
1698
+ * attempts to restart user-owned or repeatedly failing servers.
1699
+ *
1700
+ * @effect Clears the idle marker, starts the worktree dev server, and emits
1701
+ * preview-state snapshots.
1702
+ * @deps Uses the live session resources, project context, and dev-server
1703
+ * manager owned by this orchestrator.
1704
+ */
1705
+ async restoreVariantPreview(sessionId, workItemId) {
1706
+ const resources = this.resources.get(sessionId);
1707
+ const record = resources?.worktrees.get(workItemId);
1708
+ if (!resources || !record)
1709
+ return undefined;
1710
+ if (record.port !== undefined && record.devServerProcess) {
1711
+ this.touchSession(sessionId);
1712
+ return record.port;
1713
+ }
1714
+ if (resources.previewFailures.get(workItemId)?.reason !== 'idle_timeout') {
1715
+ return undefined;
1716
+ }
1717
+ resources.previewFailures.delete(workItemId);
1718
+ this.emitChange();
1719
+ const projectContext = this.store.getProjectContext(sessionId);
1720
+ return this.startVariantDevServer({
1721
+ sessionId,
1722
+ workItemId,
1723
+ record,
1724
+ isFresh: projectContext.kind === 'fresh',
1725
+ });
1726
+ }
1497
1727
  /**
1498
1728
  * @effect Resolves the dev server port for a preview request and refreshes
1499
1729
  * active-preview retention for that session while the port is still live.
@@ -1713,6 +1943,9 @@ class AgentVariantsOrchestrator {
1713
1943
  sessionId,
1714
1944
  briefs,
1715
1945
  runLabel: args.runLabel?.trim() || (0, runLabel_1.deriveRunLabel)(args.prompt),
1946
+ ...(args.relevantFiles?.length
1947
+ ? { relevantFiles: args.relevantFiles }
1948
+ : {}),
1716
1949
  ...(projectContext.kind === 'existing' && projectContext.variantContext
1717
1950
  ? { variantContext: projectContext.variantContext }
1718
1951
  : {}),
@@ -1755,6 +1988,7 @@ class AgentVariantsOrchestrator {
1755
1988
  prompt: args.prompt,
1756
1989
  count,
1757
1990
  target: args.target,
1991
+ relevantFiles: args.relevantFiles,
1758
1992
  projectContext,
1759
1993
  runLabel: args.runLabel,
1760
1994
  ...(args.sessionId ? { sessionId: args.sessionId } : {}),
@@ -1905,20 +2139,79 @@ class AgentVariantsOrchestrator {
1905
2139
  return null;
1906
2140
  return sessionId;
1907
2141
  }
2142
+ /** @effect Registers a provisioned git worktree with runtime and workspace state. */
2143
+ async registerWorktreeWorkspace(resources, record) {
2144
+ let projectPath = record.worktreePath;
2145
+ try {
2146
+ projectPath = await this.worktrees.getProjectCwdInWorktree(record.worktreePath);
2147
+ }
2148
+ catch (err) {
2149
+ log.warn(`Failed to resolve project path in worktree ${record.worktreePath}; using its root`, err);
2150
+ }
2151
+ resources.worktrees.set(record.workItemId, record);
2152
+ resources.workspaceManager.registerGitWorkspace({
2153
+ workItemId: record.workItemId,
2154
+ rootPath: record.worktreePath,
2155
+ projectPath,
2156
+ });
2157
+ }
1908
2158
  /**
1909
2159
  * Provision a worktree for each appended existing-project variant. Mirrors
1910
2160
  * {@link provisionExistingWorktrees} but only for the newly appended ids, and
1911
2161
  * passes a per-batch `instanceKeyPrefix` so the new worktree dirs do not
1912
2162
  * collide with the first batch's `${sessionId}-${i}` paths.
1913
2163
  */
2164
+ /** Emits provisioning started/completed/failed telemetry around `run` for
2165
+ * the vary-fork/append lanes that bypass provisionWorktrees (and its
2166
+ * telemetry). Rethrows failures untouched. */
2167
+ async withProvisioningTelemetry(args, run) {
2168
+ const startedAt = Date.now();
2169
+ this.telemetry.track('agent_variants.provisioning_started', {
2170
+ source: 'mcp',
2171
+ sessionId: args.sessionId,
2172
+ kind: args.kind,
2173
+ variantCount: args.variantCount,
2174
+ });
2175
+ try {
2176
+ const result = await run();
2177
+ this.telemetry.track('agent_variants.provisioning_completed', {
2178
+ source: 'mcp',
2179
+ sessionId: args.sessionId,
2180
+ kind: args.kind,
2181
+ variantCount: args.variantCount,
2182
+ durationMs: Date.now() - startedAt,
2183
+ });
2184
+ return result;
2185
+ }
2186
+ catch (err) {
2187
+ const timedOut = err instanceof ProvisionTimeoutError;
2188
+ this.telemetry.track('agent_variants.provisioning_failed', {
2189
+ source: 'mcp',
2190
+ sessionId: args.sessionId,
2191
+ kind: args.kind,
2192
+ variantCount: args.variantCount,
2193
+ durationMs: Date.now() - startedAt,
2194
+ timedOut,
2195
+ errorCode: timedOut
2196
+ ? 'PROVISION_TIMEOUT'
2197
+ : err instanceof errors_1.AgentVariantsError
2198
+ ? err.code
2199
+ : 'PROVISION_FAILED',
2200
+ });
2201
+ throw err;
2202
+ }
2203
+ }
1914
2204
  async provisionAppendedExistingWorktrees(sessionId, workItemIds) {
1915
2205
  if (workItemIds.length === 0)
1916
2206
  return;
1917
- const resources = this.ensureResources(sessionId);
1918
- const instanceKeyPrefix = `a${(0, crypto_1.randomUUID)().slice(0, 8)}-`;
1919
- const paths = await withProvisionTimeout(this.worktrees.createWorktrees(sessionId, workItemIds.length, instanceKeyPrefix), this.provisionTimeoutMs);
1920
- workItemIds.forEach((id, i) => {
1921
- resources.worktrees.set(id, { workItemId: id, worktreePath: paths[i] });
2207
+ await this.withProvisioningTelemetry({ sessionId, kind: 'appended_existing', variantCount: workItemIds.length }, async () => {
2208
+ const resources = this.ensureResources(sessionId);
2209
+ const instanceKeyPrefix = `a${(0, crypto_1.randomUUID)().slice(0, 8)}-`;
2210
+ const paths = await withProvisionTimeout(this.worktrees.createWorktrees(sessionId, workItemIds.length, instanceKeyPrefix), this.provisionTimeoutMs);
2211
+ await Promise.all(workItemIds.map((id, index) => this.registerWorktreeWorkspace(resources, {
2212
+ workItemId: id,
2213
+ worktreePath: paths[index],
2214
+ })));
1922
2215
  });
1923
2216
  }
1924
2217
  /**
@@ -1936,14 +2229,21 @@ class AgentVariantsOrchestrator {
1936
2229
  if (!this.worktrees.forkWorktreesFrom) {
1937
2230
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', 'worktreeManager does not implement forkWorktreesFrom');
1938
2231
  }
1939
- const resources = this.ensureResources(sessionId);
1940
- const instanceKeyPrefix = `f${(0, crypto_1.randomUUID)().slice(0, 8)}-`;
1941
- const paths = await withProvisionTimeout(this.worktrees.forkWorktreesFrom(sourcePath, sessionId, workItemIds.length, instanceKeyPrefix), this.provisionTimeoutMs);
1942
- workItemIds.forEach((id, i) => {
1943
- resources.worktrees.set(id, { workItemId: id, worktreePath: paths[i] });
2232
+ await this.withProvisioningTelemetry({ sessionId, kind: 'forked_variant', variantCount: workItemIds.length }, async () => {
2233
+ const resources = this.ensureResources(sessionId);
2234
+ const instanceKeyPrefix = `f${(0, crypto_1.randomUUID)().slice(0, 8)}-`;
2235
+ const paths = await withProvisionTimeout(this.worktrees.forkWorktreesFrom(sourcePath, sessionId, workItemIds.length, instanceKeyPrefix), this.provisionTimeoutMs);
2236
+ await Promise.all(workItemIds.map((id, index) => this.registerWorktreeWorkspace(resources, {
2237
+ workItemId: id,
2238
+ worktreePath: paths[index],
2239
+ })));
1944
2240
  });
1945
2241
  }
1946
2242
  async reportComplete(args) {
2243
+ // Phase-timing anchor: lease→report (agent work) vs report→recorded
2244
+ // (QA gate + post-processing) are attributed separately on
2245
+ // `variant_completed` so latency work can tell them apart.
2246
+ const reportReceivedAt = Date.now();
1947
2247
  // Cooperative abort short-circuit: if the work item was removed/cancelled
1948
2248
  // while in flight (delete-while-loading, cancel_variant, session cancel),
1949
2249
  // skip the schema and QA gates entirely — there is no point validating or
@@ -1956,18 +2256,23 @@ class AgentVariantsOrchestrator {
1956
2256
  const workItemKind = this.store.hasSession(args.sessionId)
1957
2257
  ? this.store.getWorkItemKind(args.sessionId, args.workItemId)
1958
2258
  : undefined;
1959
- // Contract validation: a `succeeded` static_preview report MUST carry the
1960
- // deliverable inline as `output.html` (with optional `css` / `js`). Agents
1961
- // sometimes default to writing files to disk and reporting back a file
1962
- // list — that leaves the orchestrator with no HTML to serve, the variant
1963
- // shows "Preview unavailable" in the panel, and `/api/variants/.../static`
1964
- // returns 404. Reject loudly so the agent self-corrects on retry instead
1965
- // of silently producing an empty-preview run.
1966
- if (args.status === 'succeeded' &&
2259
+ const resolvedArgs = args.status === 'succeeded' &&
2260
+ (workItemKind === 'static_preview' ||
2261
+ workItemKind === 'refine_variant') &&
2262
+ args.output === undefined
2263
+ ? {
2264
+ ...args,
2265
+ output: this.readAssignedStaticArtifact(args.sessionId, args.workItemId),
2266
+ }
2267
+ : args;
2268
+ // Contract validation keeps internal callers that provide an explicit
2269
+ // static output honest. Host workers complete assigned artifacts without
2270
+ // transporting the HTML through this report.
2271
+ if (resolvedArgs.status === 'succeeded' &&
1967
2272
  this.store.hasSession(args.sessionId) &&
1968
2273
  (workItemKind === 'static_preview' ||
1969
2274
  workItemKind === 'refine_variant') &&
1970
- !parseStaticPreviewOutput(normalizeOutput(args.output))) {
2275
+ !parseStaticPreviewOutput(normalizeOutput(resolvedArgs.output))) {
1971
2276
  const telemetry = this.refinementTelemetryForWorkItem({
1972
2277
  sessionId: args.sessionId,
1973
2278
  workItemId: args.workItemId,
@@ -1978,25 +2283,25 @@ class AgentVariantsOrchestrator {
1978
2283
  if (telemetry) {
1979
2284
  this.telemetry.trackAgentVariantsRefinementFailed?.(telemetry);
1980
2285
  }
1981
- throw new errors_1.AgentVariantsError('SCHEMA_VALIDATION_FAILED', `${workItemKind} report_variant_complete requires output.html (string, non-empty). ` +
1982
- 'Pass the full HTML inline as output: { html: "<!doctype html>...", css?: "...", js?: "..." }. ' +
1983
- 'Do NOT write the HTML to a file and report a file list — Rivet renders the HTML inline; on-disk files are not read.');
2286
+ throw new errors_1.AgentVariantsError('SCHEMA_VALIDATION_FAILED', `${workItemKind} output.html must be a string and non-empty when explicit output is provided. Host workers should write the assigned index.html instead.`);
1984
2287
  }
1985
2288
  // QA gate: for `succeeded` static_preview reports, run preview QA
1986
2289
  // synchronously before recording success in the store. A failed QA
1987
2290
  // verdict converts the report to `failed` with code `VARIANT_QA_FAILED`
1988
2291
  // so the variant never reaches `ready` and `getVariants` can disable
1989
2292
  // its commit action.
2293
+ const qaStartedAt = Date.now();
1990
2294
  const qaEval = workItemKind === 'refine_variant'
1991
2295
  ? null
1992
- : await this.evaluateQaForReport(args);
2296
+ : await this.evaluateQaForReport(resolvedArgs);
2297
+ const qaMs = qaEval === null ? null : Date.now() - qaStartedAt;
1993
2298
  // A `retry` verdict re-leases the variant for one regeneration (the
1994
2299
  // existing-project diff task-fit gate) instead of recording success —
1995
2300
  // short-circuit the normal record path.
1996
2301
  if (qaEval?.decision === 'retry') {
1997
- return this.handleQaRegenRetry(args, qaEval.qa);
2302
+ return this.handleQaRegenRetry(resolvedArgs, qaEval.qa);
1998
2303
  }
1999
- let effectiveArgs = qaEval?.decision === 'fail_terminal' ? qaEval.overrideArgs : args;
2304
+ let effectiveArgs = qaEval?.decision === 'fail_terminal' ? qaEval.overrideArgs : resolvedArgs;
2000
2305
  // For a successful refine_variant, swap the refined static preview into
2001
2306
  // place and persist history BEFORE recording success in the store. This
2002
2307
  // keeps the user-visible `refinement.status: succeeded` (and the snapshot
@@ -2016,7 +2321,7 @@ class AgentVariantsOrchestrator {
2016
2321
  await this.handleStaticPreviewRefinement({
2017
2322
  sessionId: args.sessionId,
2018
2323
  workItemId: args.workItemId,
2019
- output: args.output,
2324
+ output: effectiveArgs.output,
2020
2325
  });
2021
2326
  }
2022
2327
  catch (err) {
@@ -2062,8 +2367,15 @@ class AgentVariantsOrchestrator {
2062
2367
  const approveStartedAt = resources?.approveStartedAt;
2063
2368
  const approveToCompleteMs = approveStartedAt !== undefined ? Date.now() - approveStartedAt : null;
2064
2369
  // Byte size of generated output for HTML vs full-stack comparison.
2065
- const outputBytes = estimateOutputBytes(args.output);
2370
+ const outputBytes = estimateOutputBytes(effectiveArgs.output);
2066
2371
  const projectContextKind = this.store.getProjectContext(args.sessionId).kind;
2372
+ // Execution mode mirrors the host work-item snapshot derivation so
2373
+ // prod latency can finally segment vary vs create vs refine.
2374
+ const mode = workItemKind !== undefined && isImplementationWorkItemKind(workItemKind)
2375
+ ? this.store.isVaryForkWorkItem(args.sessionId, args.workItemId)
2376
+ ? 'vary'
2377
+ : DEFAULT_EXECUTION_MODE_BY_KIND[workItemKind]
2378
+ : null;
2067
2379
  this.telemetry.track('agent_variants.variant_completed', {
2068
2380
  source: 'mcp',
2069
2381
  sessionId: args.sessionId,
@@ -2072,6 +2384,12 @@ class AgentVariantsOrchestrator {
2072
2384
  attempt: args.attempt,
2073
2385
  durationMs,
2074
2386
  approveToCompleteMs,
2387
+ kind: workItemKind ?? null,
2388
+ mode,
2389
+ // Phase split of `durationMs`: agent work vs server-side gating.
2390
+ leaseToReportMs: leasedAt !== undefined ? reportReceivedAt - leasedAt : null,
2391
+ reportProcessingMs: Date.now() - reportReceivedAt,
2392
+ qaMs,
2075
2393
  hasError: Boolean(effectiveArgs.error),
2076
2394
  errorCode: effectiveArgs.error?.code ?? null,
2077
2395
  isScaffold: resources?.scaffoldBaseWorkItemId === args.workItemId,
@@ -2151,7 +2469,7 @@ class AgentVariantsOrchestrator {
2151
2469
  void this.handleSucceededReport({
2152
2470
  sessionId: args.sessionId,
2153
2471
  workItemId: args.workItemId,
2154
- output: args.output,
2472
+ output: effectiveArgs.output,
2155
2473
  }).catch((err) => {
2156
2474
  log.error(`handleSucceededReport failed for ${args.sessionId}/${args.workItemId}`, err);
2157
2475
  });
@@ -2947,7 +3265,7 @@ class AgentVariantsOrchestrator {
2947
3265
  let cursor = 0;
2948
3266
  if (approveResult.scaffoldBaseWorkItemId) {
2949
3267
  resources.scaffoldBaseWorkItemId = approveResult.scaffoldBaseWorkItemId;
2950
- resources.worktrees.set(approveResult.scaffoldBaseWorkItemId, {
3268
+ await this.registerWorktreeWorkspace(resources, {
2951
3269
  workItemId: approveResult.scaffoldBaseWorkItemId,
2952
3270
  worktreePath: paths[cursor],
2953
3271
  });
@@ -2956,7 +3274,10 @@ class AgentVariantsOrchestrator {
2956
3274
  for (const id of approveResult.codeGenWorkItemIds) {
2957
3275
  const p = paths[cursor];
2958
3276
  cursor += 1;
2959
- resources.worktrees.set(id, { workItemId: id, worktreePath: p });
3277
+ await this.registerWorktreeWorkspace(resources, {
3278
+ workItemId: id,
3279
+ worktreePath: p,
3280
+ });
2960
3281
  }
2961
3282
  }
2962
3283
  /**
@@ -3013,12 +3334,10 @@ class AgentVariantsOrchestrator {
3013
3334
  resources.freshDestinationParent = destinationParent;
3014
3335
  // Each code_gen item maps 1:1 to a fresh worktree. The scaffold_base
3015
3336
  // work item is internal — no dedicated worktree.
3016
- for (let i = 0; i < codeGenIds.length; i++) {
3017
- resources.worktrees.set(codeGenIds[i], {
3018
- workItemId: codeGenIds[i],
3019
- worktreePath: paths[i],
3020
- });
3021
- }
3337
+ await Promise.all(codeGenIds.map((workItemId, index) => this.registerWorktreeWorkspace(resources, {
3338
+ workItemId,
3339
+ worktreePath: paths[index],
3340
+ })));
3022
3341
  // Copy agent-planned assets (e.g. avatar.glb) into each worktree so
3023
3342
  // generated code can reference them via plan.referenceAs. Done before
3024
3343
  // the background install so the assets are on disk by the time the
@@ -3418,6 +3737,12 @@ class AgentVariantsOrchestrator {
3418
3737
  return;
3419
3738
  args.record.port = undefined;
3420
3739
  args.record.devServerProcess = undefined;
3740
+ this.resources
3741
+ .get(args.sessionId)
3742
+ ?.previewFailures.set(args.workItemId, {
3743
+ reason: 'exited',
3744
+ portInUse: false,
3745
+ });
3421
3746
  log.warn(`Variant ${args.workItemId} dev server exited unexpectedly (code=${code ?? 'null'}, signal=${signal ?? 'null'}); preview disabled`);
3422
3747
  this.emitChange();
3423
3748
  });
@@ -3565,6 +3890,11 @@ class AgentVariantsOrchestrator {
3565
3890
  hasAssets: Boolean(resolvedAssetBase),
3566
3891
  };
3567
3892
  resources.staticPreviews.set(workItemId, record);
3893
+ const assignedWorkspace = resources.workspaceManager.getAssignedWorkspace(workItemId);
3894
+ if (assignedWorkspace?.kind === 'artifact_directory' &&
3895
+ assignedWorkspace.rootPath !== record.assetBase) {
3896
+ resources.workspaceManager.removeArtifactWorkspace(workItemId);
3897
+ }
3568
3898
  if (this.store.getProjectContext(sessionId).kind === 'fresh') {
3569
3899
  // History at `<workspacePath>/.rivet/variants/<sessionId>/<variantId>/`
3570
3900
  // is the on-disk record for this generated project.
@@ -3721,6 +4051,14 @@ class AgentVariantsOrchestrator {
3721
4051
  hasAssets: Boolean(resolvedAssetBase) || Boolean(previousRecord?.hasAssets),
3722
4052
  });
3723
4053
  }
4054
+ const refinementWorkspace = resources.workspaceManager.getAssignedWorkspace(args.workItemId);
4055
+ if (refinementWorkspace?.kind === 'artifact_directory' &&
4056
+ dir === refinementWorkspace.rootPath) {
4057
+ resources.workspaceManager.promoteArtifactWorkspace({
4058
+ sourceWorkItemId: args.workItemId,
4059
+ targetWorkItemId: input.variantId,
4060
+ });
4061
+ }
3724
4062
  // History persistence is best-effort, mirroring handleSucceededReport's
3725
4063
  // fire-and-forget persistence for freshly completed variants. A failure
3726
4064
  // here (e.g. a transient filesystem race) must not fail the refinement or
@@ -4666,17 +5004,30 @@ class AgentVariantsOrchestrator {
4666
5004
  const resources = this.resources.get(sessionId);
4667
5005
  if (!resources || resources.cleanupStarted)
4668
5006
  return;
5007
+ if (this.uiClientCount > 0) {
5008
+ this.scheduleActivePreviewIdleTimer(resources);
5009
+ return;
5010
+ }
4669
5011
  if (resources.activePreviewIdleStop) {
4670
5012
  await resources.activePreviewIdleStop;
4671
5013
  return;
4672
5014
  }
4673
5015
  const stop = (async () => {
5016
+ const expiringWorkItemIds = Array.from(resources.worktrees.values())
5017
+ .filter((record) => Boolean(record.devServerProcess))
5018
+ .map((record) => record.workItemId);
4674
5019
  const stoppedCount = await this.stopAllDevServersForSession(resources);
4675
5020
  if (stoppedCount === 0)
4676
5021
  return;
5022
+ for (const workItemId of expiringWorkItemIds) {
5023
+ resources.previewFailures.set(workItemId, {
5024
+ reason: 'idle_timeout',
5025
+ portInUse: false,
5026
+ });
5027
+ }
4677
5028
  log.info(`Stopped ${stoppedCount} idle variant dev server(s) for session ${sessionId}`);
4678
- // Keep the current behavior bounded: expired previews become unavailable
4679
- // until fresh generation or another path explicitly starts servers again.
5029
+ // The next selection restores only the requested Rivet-owned preview;
5030
+ // siblings remain stopped until the user asks to view them.
4680
5031
  try {
4681
5032
  this.switchPreviewPort?.(null);
4682
5033
  }
@@ -4848,12 +5199,13 @@ class AgentVariantsOrchestrator {
4848
5199
  log.warn(`cleanupSession (worktree removal) failed for ${sessionId}`, err);
4849
5200
  }
4850
5201
  }
4851
- // Remove fresh static_preview staging dirs (the per-variant directories
4852
- // materialized under workspacePath at report time). On 'committed' the
4853
- // commit flatten already moved/removed them; on cancel/shutdown they'd
4854
- // otherwise orphan under `.rivet/<slug>/`. Existing-mode static previews
4855
- // point at worktree dirs (cleaned by cleanupSession above), so scope this
4856
- // to fresh sessions — where every staticPreviews dir is Rivet staging.
5202
+ // Assigned static workspaces are always Rivet-owned, including existing-
5203
+ // project refinement work staged under `.rivet/variant-work` or /tmp.
5204
+ // Fresh staticPreviews are also Rivet staging unless commit already moved
5205
+ // the selected directory into the destination.
5206
+ const ownedArtifactRoots = new Set(resources.workspaceManager.getOwnedArtifactRoots());
5207
+ resources.workspaceManager.removeOwnedArtifactWorkspaces();
5208
+ const staticDirectories = new Set();
4857
5209
  if (reason !== 'committed') {
4858
5210
  let isFresh = false;
4859
5211
  try {
@@ -4863,16 +5215,21 @@ class AgentVariantsOrchestrator {
4863
5215
  isFresh = false;
4864
5216
  }
4865
5217
  if (isFresh) {
4866
- for (const sp of resources.staticPreviews.values()) {
4867
- try {
4868
- fs_1.default.rmSync(sp.assetBase, { recursive: true, force: true });
4869
- }
4870
- catch (err) {
4871
- log.warn(`failed to remove static preview dir ${sp.assetBase} for ${sessionId}`, err);
5218
+ for (const preview of resources.staticPreviews.values()) {
5219
+ if (!ownedArtifactRoots.has(preview.assetBase)) {
5220
+ staticDirectories.add(preview.assetBase);
4872
5221
  }
4873
5222
  }
4874
5223
  }
4875
5224
  }
5225
+ for (const staticDirectory of staticDirectories) {
5226
+ try {
5227
+ fs_1.default.rmSync(staticDirectory, { recursive: true, force: true });
5228
+ }
5229
+ catch (err) {
5230
+ log.warn(`failed to remove static preview dir ${staticDirectory} for ${sessionId}`, err);
5231
+ }
5232
+ }
4876
5233
  this.resources.delete(sessionId);
4877
5234
  }
4878
5235
  ensureResources(sessionId) {
@@ -4882,6 +5239,7 @@ class AgentVariantsOrchestrator {
4882
5239
  sessionId,
4883
5240
  worktrees: new Map(),
4884
5241
  staticPreviews: new Map(),
5242
+ workspaceManager: new VariantWorkspaceManager_1.VariantWorkspaceManager(),
4885
5243
  cleanupStarted: false,
4886
5244
  committedVariantIds: new Set(),
4887
5245
  startedAt: Date.now(),
@@ -6023,12 +6381,16 @@ const defaultMaterializeProject = async (sourceWorktreePath, destinationPath) =>
6023
6381
  };
6024
6382
  /**
6025
6383
  * Synthesize a fallback brief body for a human-direct unified start slot.
6026
- * Agent callers provide structured briefs before starting. Index/total are
6027
- * unused for now but kept in the signature so future human-direct diversity
6028
- * hints can be added without a call-site change.
6384
+ * Agent callers provide structured briefs before starting. Human-direct
6385
+ * batches get stable, slot-specific focus statements without another model
6386
+ * call so workers do not independently invent overlapping directions.
6029
6387
  */
6030
- function synthesizeUnifiedBriefBody(prompt, _index, _total) {
6388
+ function synthesizeUnifiedBriefBody(prompt, index, total) {
6031
6389
  const trimmed = prompt.trim().replace(/\s+/g, ' ').slice(0, 200);
6032
- return trimmed.length > 0 ? trimmed : 'variant';
6390
+ const intent = trimmed.length > 0 ? trimmed : 'Create this variant';
6391
+ if (total <= 1)
6392
+ return intent;
6393
+ const focus = FALLBACK_DIRECTION_FOCI[(Math.max(index, 1) - 1) % FALLBACK_DIRECTION_FOCI.length];
6394
+ return `${intent} Direction ${index} of ${total}: ${focus}`;
6033
6395
  }
6034
6396
  //# sourceMappingURL=WorktreeOrchestrator.js.map