@algolia/wizard 0.98.0 → 0.100.0

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 (2) hide show
  1. package/dist/main.js +144 -31
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -1581,28 +1581,38 @@ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, rm } f
1581
1581
  import { join as join4 } from "node:path";
1582
1582
  var isStepVisible = (s) => s.visible !== false;
1583
1583
  var stateFile = (workflowId) => join4(stateDir(), `state-${workflowId}.json`);
1584
+ var pendingWrite = Promise.resolve();
1584
1585
  function runAppId(state) {
1585
1586
  const appId = state.algoliaState.appId;
1586
1587
  return typeof appId === "string" && appId ? appId : void 0;
1587
1588
  }
1588
1589
  async function loadWorkflowState(workflowId) {
1590
+ const file = stateFile(workflowId);
1591
+ await pendingWrite;
1589
1592
  try {
1590
- const raw = await readFile2(stateFile(workflowId), "utf8");
1593
+ const raw = await readFile2(file, "utf8");
1591
1594
  return JSON.parse(raw);
1592
1595
  } catch {
1593
1596
  return null;
1594
1597
  }
1595
1598
  }
1599
+ function enqueueWrite(write) {
1600
+ const next = pendingWrite.then(write, write);
1601
+ pendingWrite = next.catch(() => void 0);
1602
+ return next;
1603
+ }
1596
1604
  async function saveWorkflowState(state) {
1597
- await mkdir2(stateDir(), { recursive: true });
1598
- await writeFile2(
1599
- stateFile(state.workflowId),
1600
- JSON.stringify(state, null, 2),
1601
- "utf8"
1602
- );
1605
+ const dir = stateDir();
1606
+ const file = stateFile(state.workflowId);
1607
+ const json = JSON.stringify(state, null, 2);
1608
+ return enqueueWrite(async () => {
1609
+ await mkdir2(dir, { recursive: true });
1610
+ await writeFile2(file, json, "utf8");
1611
+ });
1603
1612
  }
1604
1613
  async function clearWorkflowState(workflowId) {
1605
- await rm(stateFile(workflowId), { force: true });
1614
+ const file = stateFile(workflowId);
1615
+ return enqueueWrite(() => rm(file, { force: true }));
1606
1616
  }
1607
1617
 
1608
1618
  // src/lib/modelProfiles.ts
@@ -1701,10 +1711,45 @@ var COUNT_METRIC = 1;
1701
1711
  var GAUGE_METRIC = 3;
1702
1712
  var FIRST_ATTEMPT = 1;
1703
1713
  var NO_TOKENS2 = 0;
1714
+ var NO_COST = 0;
1704
1715
  var AGENT_TELEMETRY_CONTEXT = {
1705
1716
  appId: void 0,
1706
- workflowId: void 0
1717
+ workflowId: void 0,
1718
+ agentRunId: void 0,
1719
+ sessionCostUsd: NO_COST
1707
1720
  };
1721
+ function resetTelemetryContext() {
1722
+ AGENT_TELEMETRY_CONTEXT.appId = void 0;
1723
+ AGENT_TELEMETRY_CONTEXT.workflowId = void 0;
1724
+ AGENT_TELEMETRY_CONTEXT.agentRunId = void 0;
1725
+ AGENT_TELEMETRY_CONTEXT.sessionCostUsd = NO_COST;
1726
+ }
1727
+ function sessionCostUsd(agentRunId) {
1728
+ return agentRunId && agentRunId === AGENT_TELEMETRY_CONTEXT.agentRunId ? AGENT_TELEMETRY_CONTEXT.sessionCostUsd : void 0;
1729
+ }
1730
+ var sessionCostListeners = /* @__PURE__ */ new Set();
1731
+ function onSessionCost(listener) {
1732
+ sessionCostListeners.add(listener);
1733
+ return () => {
1734
+ sessionCostListeners.delete(listener);
1735
+ };
1736
+ }
1737
+ function sessionTags() {
1738
+ const { agentRunId } = AGENT_TELEMETRY_CONTEXT;
1739
+ return agentRunId ? [`agent_run_id:${agentRunId}`] : [];
1740
+ }
1741
+ var UNKNOWN_STACK = "unknown";
1742
+ function sessionSummary(outcome, session) {
1743
+ const completedSteps = session?.completedSteps ?? [];
1744
+ return {
1745
+ outcome,
1746
+ estimated_cost_usd: AGENT_TELEMETRY_CONTEXT.sessionCostUsd,
1747
+ completed_steps: truncate2(completedSteps.join(", ")),
1748
+ completed_step_count: completedSteps.length,
1749
+ language: truncate2(session?.language ?? UNKNOWN_STACK),
1750
+ framework: truncate2(session?.framework ?? UNKNOWN_STACK)
1751
+ };
1752
+ }
1708
1753
  function isTelemetryOptedOut() {
1709
1754
  return process.env.WIZARD_TELEMETRY === "false";
1710
1755
  }
@@ -1837,13 +1882,15 @@ function agentRunMetrics(event, values, tags) {
1837
1882
  function trackAgentRun(event) {
1838
1883
  const profile = getModelProfile(event.profile);
1839
1884
  const values = agentRunValues(event);
1885
+ AGENT_TELEMETRY_CONTEXT.sessionCostUsd += values.estimatedCostUsd;
1840
1886
  const tagsForMetrics = agentMetricTags(
1841
1887
  event,
1842
1888
  AGENT_TELEMETRY_CONTEXT.workflowId
1843
1889
  );
1844
1890
  const tags = [
1845
1891
  ...tagsForMetrics,
1846
- ...AGENT_TELEMETRY_CONTEXT.appId ? [`app_id:${AGENT_TELEMETRY_CONTEXT.appId}`] : []
1892
+ ...AGENT_TELEMETRY_CONTEXT.appId ? [`app_id:${AGENT_TELEMETRY_CONTEXT.appId}`] : [],
1893
+ ...sessionTags()
1847
1894
  ];
1848
1895
  sendTelemetry({
1849
1896
  logs: [
@@ -1864,6 +1911,7 @@ function trackAgentRun(event) {
1864
1911
  finishReason: event.finishReason,
1865
1912
  workflow_id: AGENT_TELEMETRY_CONTEXT.workflowId,
1866
1913
  app_id: AGENT_TELEMETRY_CONTEXT.appId,
1914
+ agent_run_id: AGENT_TELEMETRY_CONTEXT.agentRunId,
1867
1915
  ...values
1868
1916
  },
1869
1917
  tags
@@ -1871,14 +1919,26 @@ function trackAgentRun(event) {
1871
1919
  ],
1872
1920
  metrics: agentRunMetrics(event, values, tagsForMetrics)
1873
1921
  });
1922
+ for (const listener of sessionCostListeners) {
1923
+ try {
1924
+ listener();
1925
+ } catch (err) {
1926
+ logger.warn({ err }, "session cost listener failed");
1927
+ }
1928
+ }
1874
1929
  }
1875
1930
  function logTags(workflowId, actionId, appId) {
1876
1931
  return [
1877
1932
  ...metricTags(workflowId, actionId),
1878
- ...appId ? [`app_id:${appId}`] : []
1933
+ ...appId ? [`app_id:${appId}`] : [],
1934
+ ...sessionTags()
1879
1935
  ];
1880
1936
  }
1881
- function emitTelemetryLog(level, message, attributes, tags) {
1937
+ function emitTelemetryLog(level, message, fields, tags) {
1938
+ const attributes = {
1939
+ ...fields,
1940
+ agent_run_id: AGENT_TELEMETRY_CONTEXT.agentRunId
1941
+ };
1882
1942
  if (level === "info") {
1883
1943
  logger.info(attributes, message);
1884
1944
  } else {
@@ -1889,6 +1949,8 @@ function emitTelemetryLog(level, message, attributes, tags) {
1889
1949
  function trackWorkflowStart(ctx) {
1890
1950
  AGENT_TELEMETRY_CONTEXT.appId = ctx.appId;
1891
1951
  AGENT_TELEMETRY_CONTEXT.workflowId = ctx.workflowId;
1952
+ AGENT_TELEMETRY_CONTEXT.agentRunId = ctx.agentRunId;
1953
+ AGENT_TELEMETRY_CONTEXT.sessionCostUsd = ctx.estimatedCostUsd ?? NO_COST;
1892
1954
  const attributes = {
1893
1955
  event: "wizard.workflow.start",
1894
1956
  workflow_id: ctx.workflowId,
@@ -1907,7 +1969,8 @@ function trackWorkflowAbandoned(ctx) {
1907
1969
  event: "wizard.workflow.abandoned",
1908
1970
  workflow_id: ctx.workflowId,
1909
1971
  app_id: ctx.appId,
1910
- reason: ctx.reason
1972
+ reason: ctx.reason,
1973
+ ...sessionSummary("abandoned", ctx.session)
1911
1974
  };
1912
1975
  emitTelemetryLog(
1913
1976
  "info",
@@ -2037,7 +2100,8 @@ function trackWizardComplete(event) {
2037
2100
  workflow_id: event.workflowId,
2038
2101
  app_id: event.appId,
2039
2102
  total_duration: event.total_duration,
2040
- total_steps: event.total_steps
2103
+ total_steps: event.total_steps,
2104
+ ...sessionSummary("success", event)
2041
2105
  };
2042
2106
  emitTelemetryLog(
2043
2107
  "info",
@@ -2045,8 +2109,7 @@ function trackWizardComplete(event) {
2045
2109
  attributes,
2046
2110
  logTags(event.workflowId, void 0, event.appId)
2047
2111
  );
2048
- AGENT_TELEMETRY_CONTEXT.appId = void 0;
2049
- AGENT_TELEMETRY_CONTEXT.workflowId = void 0;
2112
+ resetTelemetryContext();
2050
2113
  }
2051
2114
  function trackWorkflowError(ctx) {
2052
2115
  const attributes = {
@@ -2055,7 +2118,8 @@ function trackWorkflowError(ctx) {
2055
2118
  action_id: ctx.actionId,
2056
2119
  error: ctx.error,
2057
2120
  app_id: ctx.appId,
2058
- ...errorAttributes(ctx.cause)
2121
+ ...errorAttributes(ctx.cause),
2122
+ ...sessionSummary("failed", ctx.session)
2059
2123
  };
2060
2124
  emitTelemetryLog(
2061
2125
  "error",
@@ -2063,8 +2127,7 @@ function trackWorkflowError(ctx) {
2063
2127
  attributes,
2064
2128
  logTags(ctx.workflowId, ctx.actionId, ctx.appId)
2065
2129
  );
2066
- AGENT_TELEMETRY_CONTEXT.appId = void 0;
2067
- AGENT_TELEMETRY_CONTEXT.workflowId = void 0;
2130
+ resetTelemetryContext();
2068
2131
  }
2069
2132
 
2070
2133
  // src/lib/events.ts
@@ -2384,7 +2447,7 @@ async function ensureApplication(resumeAppId) {
2384
2447
  // package.json
2385
2448
  var package_default = {
2386
2449
  name: "@algolia/wizard",
2387
- version: "0.98.0",
2450
+ version: "0.100.0",
2388
2451
  description: "Magically implement Algolia functionality in your codebase",
2389
2452
  type: "module",
2390
2453
  engines: {
@@ -2528,6 +2591,36 @@ function initWorkflowState(workflow, now, appId) {
2528
2591
  function ensureAgentRunId(state) {
2529
2592
  if (!state.agentRunId) state.agentRunId = crypto.randomUUID();
2530
2593
  }
2594
+ async function persistState(state) {
2595
+ state.estimatedCostUsd = sessionCostUsd(state.agentRunId) ?? state.estimatedCostUsd;
2596
+ await saveWorkflowState(state);
2597
+ }
2598
+ function saveCostDuringStep(state) {
2599
+ const snapshot = JSON.parse(JSON.stringify(state));
2600
+ return onSessionCost(() => {
2601
+ const cost = sessionCostUsd(state.agentRunId);
2602
+ if (cost === void 0) return;
2603
+ snapshot.estimatedCostUsd = cost;
2604
+ saveWorkflowState(snapshot).catch((err) => {
2605
+ logger.warn({ err }, "failed to save the session cost");
2606
+ });
2607
+ });
2608
+ }
2609
+ function stepOutput(state, stepId) {
2610
+ const record = state.steps.find((s) => s.id === stepId);
2611
+ return record?.status === "done" ? record.output : void 0;
2612
+ }
2613
+ function stackName(state, confirmStepId, key) {
2614
+ const name = stepOutput(state, confirmStepId)?.[key]?.[0]?.name ?? stepOutput(state, "project-scan")?.[key]?.[0]?.name;
2615
+ return name ? name.toLowerCase() : "unknown";
2616
+ }
2617
+ function sessionReport(state) {
2618
+ return {
2619
+ completedSteps: state.steps.filter((s) => s.status === "done").map((s) => s.id),
2620
+ language: stackName(state, "confirm-language", "languages"),
2621
+ framework: stackName(state, "confirm-framework", "frameworks")
2622
+ };
2623
+ }
2531
2624
  function completeEventProperties(state, totalDurationMs, stepCount) {
2532
2625
  const indexName = state.steps.find((s) => s.id === "select-index")?.output?.selection;
2533
2626
  const framework = state.steps.find((s) => s.id === "confirm-framework")?.output?.frameworks?.[0]?.name;
@@ -2632,7 +2725,7 @@ async function rewindTo(state, targetIndex, fromIndex) {
2632
2725
  const store = useWizard.getState();
2633
2726
  store.setActiveStep(targetIndex);
2634
2727
  store.syncSteps([...state.steps], targetIndex);
2635
- await saveWorkflowState(state);
2728
+ await persistState(state);
2636
2729
  }
2637
2730
  async function runStep(state, index, step) {
2638
2731
  const store = useWizard.getState();
@@ -2653,9 +2746,10 @@ async function runStep(state, index, step) {
2653
2746
  state.updatedAt = nowIso();
2654
2747
  store.setActiveStep(index);
2655
2748
  store.syncSteps([...state.steps], index);
2656
- await saveWorkflowState(state);
2749
+ await persistState(state);
2657
2750
  await markInteraction();
2658
2751
  const ctx = await makeContext(state);
2752
+ const stopSavingCost = saveCostDuringStep(state);
2659
2753
  let output;
2660
2754
  try {
2661
2755
  const raw = await step.run(ctx);
@@ -2663,6 +2757,7 @@ async function runStep(state, index, step) {
2663
2757
  await step.onStepComplete?.(output, ctx);
2664
2758
  } catch (err) {
2665
2759
  if (err instanceof ReturnToStepSignal) {
2760
+ stopSavingCost();
2666
2761
  await rewindTo(state, err.targetIndex, index);
2667
2762
  return err.targetIndex;
2668
2763
  }
@@ -2677,12 +2772,14 @@ async function runStep(state, index, step) {
2677
2772
  });
2678
2773
  }
2679
2774
  throw err;
2775
+ } finally {
2776
+ stopSavingCost();
2680
2777
  }
2681
2778
  record.status = "done";
2682
2779
  record.output = output;
2683
2780
  state.updatedAt = nowIso();
2684
2781
  store.syncSteps([...state.steps], index);
2685
- await saveWorkflowState(state);
2782
+ await persistState(state);
2686
2783
  trackActionEnd({
2687
2784
  workflowId: state.workflowId,
2688
2785
  appId,
@@ -2740,13 +2837,19 @@ async function beginRun(workflow, state) {
2740
2837
  [...state.steps]
2741
2838
  );
2742
2839
  await ensureConsent();
2743
- trackWorkflowStart({ workflowId: workflow.id, appId: runAppId(state) });
2840
+ trackWorkflowStart({
2841
+ workflowId: workflow.id,
2842
+ appId: runAppId(state),
2843
+ agentRunId: state.agentRunId,
2844
+ estimatedCostUsd: state.estimatedCostUsd
2845
+ });
2744
2846
  }
2745
2847
  async function handleRestartWorkflow(workflow, previous, newAppId) {
2746
2848
  trackWorkflowAbandoned({
2747
2849
  workflowId: workflow.id,
2748
2850
  appId: runAppId(previous),
2749
- reason: "application_changed"
2851
+ reason: "application_changed",
2852
+ session: sessionReport(previous)
2750
2853
  });
2751
2854
  await clearWorkflowState(workflow.id);
2752
2855
  const next = initWorkflowState(workflow, nowIso(), newAppId);
@@ -2795,7 +2898,8 @@ async function runWorkflow(workflow, startWorkflowAppId, options) {
2795
2898
  workflowId: workflow.id,
2796
2899
  appId: runAppId(state),
2797
2900
  total_duration: Math.round(totalDurationMs / 1e3),
2798
- total_steps: stepCount
2901
+ total_steps: stepCount,
2902
+ ...sessionReport(state)
2799
2903
  });
2800
2904
  track(
2801
2905
  "AI Wizard Completed",
@@ -2819,12 +2923,12 @@ async function runWorkflow(workflow, startWorkflowAppId, options) {
2819
2923
  if (record.status === "running") {
2820
2924
  record.status = "error";
2821
2925
  record.error = message;
2822
- await saveWorkflowState(failed);
2823
2926
  store.syncSteps([...failed.steps], failed.currentStepIndex);
2824
2927
  }
2825
2928
  failedActionId = record.id;
2826
2929
  failedActionTitle = record.title;
2827
2930
  }
2931
+ await persistState(failed);
2828
2932
  }
2829
2933
  if (failedActionId && failedActionTitle) {
2830
2934
  trackActionError({
@@ -2841,7 +2945,8 @@ async function runWorkflow(workflow, startWorkflowAppId, options) {
2841
2945
  appId: failedAppId,
2842
2946
  error: message,
2843
2947
  actionId: failedActionId,
2844
- cause: err
2948
+ cause: err,
2949
+ session: failed ? sessionReport(failed) : void 0
2845
2950
  });
2846
2951
  const errorType = wizardErrorType(failedActionId, step, message);
2847
2952
  const errorCode2 = errorCodeFrom(err);
@@ -6528,7 +6633,8 @@ function searchInstructions(input) {
6528
6633
  "Style the search surfaces through the widget's class and CSS-variable overrides (or, when hand-built, the app's existing theme tokens): give the results panel and the element that actually draws the input's border and background \u2014 for a widget, the form or wrapper it renders around the bare input \u2014 non-transparent surfaces, keep that frame distinguishable from the background behind it in every supported theme, and never assume a light surface or reuse the surrounding page's colors unchanged inside the panel.",
6529
6634
  "The panel takes its width from the input by default, so a small input makes it unreadably narrow: give it `min-width: min(320px, calc(100vw - 2rem))` independent of the input, cap its maximum width to the viewport, and anchor it to the input edge it opens from so widening does not push it off-screen.",
6530
6635
  "Match the application styles closely, including light, dark, and high-contrast themes the project already supports.",
6531
- "Before reporting, mount the component. Do not run a type check, build, or test command: the validation pass runs the type check, after the developer agrees to it. If a tool limit is reached or an error remains unresolved, keep the implementation, report status=success, and name the remaining issue in the summary so validation can drive the next pass. Report status=fail only when no search implementation can be written, with the concrete blocker as the reason.",
6636
+ "When you add UI text to a localized dictionary or message file, add the same keys, translated, to every locale the project supports.",
6637
+ "Before reporting, mount the component. Do not run a type check, build, or test command unless a later instruction allows it: the validation pass runs the type check, after the developer agrees to it. If a tool limit is reached or an error remains unresolved, keep the implementation, report status=success, and name the remaining issue in the summary so validation can drive the next pass. Report status=fail only when no search implementation can be written, with the concrete blocker as the reason.",
6532
6638
  "The summary should be extremely concise; do not mention manual testing steps."
6533
6639
  ];
6534
6640
  }
@@ -6954,10 +7060,16 @@ function validationRunInstructions(plan, attempt) {
6954
7060
  "The developer approved the optional build checks.",
6955
7061
  ...installCheckInstructions(plan, attempt),
6956
7062
  "Do not use npx, dlx, or another package runner that can download an undeclared tool.",
6957
- "Run the type check once with the project's existing command, when one is available.",
7063
+ "Run the type check once with the project's existing command, when one is available. Run it even when you already found other failures, and put every type error that the changes cause in additionalInstructions, so the next pass can fix them all at once.",
6958
7064
  "Do not use an autofix option during validation."
6959
7065
  ];
6960
7066
  }
7067
+ function retryTypeCheckInstructions(plan) {
7068
+ if (!plan.run) return [];
7069
+ return [
7070
+ "The developer approved the type check. After your edits, run it once with the project's existing command. Do not install dependencies, use npx, dlx, or another package runner, or use an autofix option. Fix the errors that your changes cause, and name any error that remains in the summary."
7071
+ ];
7072
+ }
6961
7073
  async function resolveIngestionSource(ctx, repoRoot) {
6962
7074
  let error = void 0;
6963
7075
  for (; ; ) {
@@ -7493,6 +7605,7 @@ ${detail}` : ""}`
7493
7605
  `The developer asked you to investigate this check failure: ${validation.unrelatedFailure}`
7494
7606
  );
7495
7607
  }
7608
+ extraInstructions.push(...retryTypeCheckInstructions(buildCheckPlan));
7496
7609
  }
7497
7610
  if (searchConfigFile) {
7498
7611
  const ignoreStatus = await gitIgnoreStatus(
@@ -9911,7 +10024,7 @@ async function resetProjectState() {
9911
10024
  // package.json with { type: 'json' }
9912
10025
  var package_default2 = {
9913
10026
  name: "@algolia/wizard",
9914
- version: "0.98.0",
10027
+ version: "0.100.0",
9915
10028
  description: "Magically implement Algolia functionality in your codebase",
9916
10029
  type: "module",
9917
10030
  engines: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.98.0",
3
+ "version": "0.100.0",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {